root/trunk/src/shared/Config/dotconfpp/dotconfpp.cpp @ 229

Revision 44, 16.0 kB (checked in by yumileroy, 17 years ago)

[svn] * Merge Temp dev SVN with Assembla.
* Changes include:

  • Implementation of w12x's Outdoor PvP and Game Event Systems.
  • Temporary removal of IRC Chat Bot (until infinite loop when disabled is fixed).
  • All mangos -> trinity (to convert your mangos_string table, please run mangos_string_to_trinity_string.sql).
  • Improved Config cleanup.
  • And many more changes.

Original author: Seline
Date: 2008-10-14 11:57:03-05:00

Line 
1
2#include "Common.h"
3
4#include "dotconfpp.h"
5
6#if !defined(R_OK)
7#define R_OK 04
8#endif
9
10DOTCONFDocumentNode::DOTCONFDocumentNode():previousNode(NULL), nextNode(NULL), parentNode(NULL), childNode(NULL),
11    values(NULL), valuesCount(0),
12    name(NULL), lineNum(0), fileName(NULL), closed(true)
13{
14}
15
16DOTCONFDocumentNode::~DOTCONFDocumentNode()
17{
18    free(name);
19    if(values != NULL){
20        for(int i = 0 ; i < valuesCount; i++){
21            free(values[i]);
22        }
23        free(values);
24    }
25}
26
27void DOTCONFDocumentNode::pushValue(char * _value)
28{
29    ++valuesCount;
30    values = (char**)realloc(values, valuesCount*sizeof(char*));
31    values[valuesCount-1] = strdup(_value);
32}
33
34const char* DOTCONFDocumentNode::getValue(int index) const
35{
36    if(index >= valuesCount){
37        return NULL;
38    }
39    return values[index];
40}
41
42DOTCONFDocument::DOTCONFDocument(DOTCONFDocument::CaseSensitive caseSensitivity):
43    mempool(NULL),
44    curParent(NULL), curPrev(NULL), curLine(0), file(NULL), fileName(NULL)
45{
46    if(caseSensitivity == CASESENSETIVE){
47        cmp_func = strcmp;
48    } else {
49        cmp_func = strcasecmp;
50    }
51
52    mempool = new AsyncDNSMemPool(1024);
53    mempool->initialize();
54}
55
56DOTCONFDocument::~DOTCONFDocument()
57{
58    for(std::list<DOTCONFDocumentNode*>::iterator i = nodeTree.begin(); i != nodeTree.end(); i++){
59        delete(*i);
60    }
61    for(std::list<char*>::iterator i = requiredOptions.begin(); i != requiredOptions.end(); i++){
62        free(*i);
63    }
64    for(std::list<char*>::iterator i = processedFiles.begin(); i != processedFiles.end(); i++){
65        free(*i);
66    }
67    free(fileName);
68    delete mempool;
69}
70
71int DOTCONFDocument::cleanupLine(char * line)
72{
73    char * start = line;
74    char * bg = line;
75    bool multiline = false;
76    bool concat = false;
77    char * word = NULL;
78
79    if(!words.empty() && quoted)
80        concat = true;
81
82    while(*line){
83        if((*line == '#' || *line == ';') && !quoted){
84            *bg = 0;
85            if(strlen(start)){
86
87                if(concat){
88                    word = (char*)mempool->alloc(strlen(words.back())+strlen(start)+1);
89                    strcpy(word, words.back());
90                    strcat(word, start);
91                    words.pop_back();
92                    concat = false;
93                } else {
94                    word = mempool->strdup(start);
95                }
96                words.push_back(word);
97            }
98            break;
99        }
100        if(*line == '=' && !quoted){
101            *line = ' ';continue;
102        }
103
104        // Allowing \" in there causes problems with directory paths
105        // like "C:\TrinIty\"
106        //if(*line == '\\' && (*(line+1) == '"' || *(line+1) == '\'')){
107        if(*line == '\\' && (*(line+1) == '\'')) {
108            *bg++ = *(line+1);
109            line+=2; continue;
110        }
111        if(*line == '\\' && *(line+1) == 'n'){
112            *bg++ = '\n';
113            line+=2; continue;
114        }
115        if(*line == '\\' && *(line+1) == 'r'){
116            *bg++ = '\r';
117            line+=2; continue;
118        }
119        if(*line == '\\' && (*(line+1) == '\n' || *(line+1) == '\r')){
120            *bg = 0;
121            if(strlen(start)){
122
123                if(concat){
124                    word = (char*)mempool->alloc(strlen(words.back())+strlen(start)+1);
125                    strcpy(word, words.back());
126                    strcat(word, start);
127                    words.pop_back();
128                    concat = false;
129                } else {
130                    word = mempool->strdup(start);
131                }
132                words.push_back(word);
133            }
134            multiline = true;
135            break;
136        }
137        if(*line == '"' || *line == '\''){
138            quoted = !quoted;
139            ++line; continue;
140        }
141        if(isspace(*line) && !quoted){
142            *bg++ = 0;
143            if(strlen(start)){
144
145                if(concat){
146                    word = (char*)mempool->alloc(strlen(words.back())+strlen(start)+1);
147                    strcpy(word, words.back());
148                    strcat(word, start);
149                    words.pop_back();
150                    concat = false;
151                } else {
152                    word = mempool->strdup(start);
153                }
154                words.push_back(word);
155            }
156            start = bg;
157            while(isspace(*++line)) {}
158
159            continue;
160        }
161        *bg++ = *line++;
162    }
163
164    if(quoted && !multiline){
165        error(curLine, fileName, "unterminated quote");
166        return -1;
167    }
168
169    return multiline?1:0;
170}
171
172int DOTCONFDocument::parseLine()
173{
174    char * word = NULL;
175    char * nodeName = NULL;
176    char * nodeValue = NULL;
177    DOTCONFDocumentNode * tagNode = NULL;
178    bool newNode = false;
179
180    for(std::list<char*>::iterator i = words.begin(); i != words.end(); i++) {
181        word = *i;
182
183        if(*word == '<'){
184            newNode = true;
185        }
186
187        if(newNode){
188            nodeValue = NULL;
189            nodeName = NULL;
190            newNode = false;
191        }
192
193        size_t wordLen = strlen(word);
194        if(word[wordLen-1] == '>'){
195            word[wordLen-1] = 0;
196            newNode = true;
197        }
198
199        if(nodeName == NULL){
200            nodeName = word;
201            bool closed = true;
202            if(*nodeName == '<'){
203                if(*(nodeName+1) != '/'){
204                    ++nodeName;
205                    closed = false;
206                } else {
207                    nodeName+=2;
208                    std::list<DOTCONFDocumentNode*>::reverse_iterator itr=nodeTree.rbegin();
209                    for(; itr!=nodeTree.rend(); ++itr){
210                        if(!cmp_func(nodeName, (*itr)->name) && !(*itr)->closed){
211                            (*itr)->closed = true;
212                            curParent = (*itr)->parentNode;
213                            curPrev = *itr;
214                            break;
215                        }
216                    }
217                    if(itr==nodeTree.rend()){
218                        error(curLine, fileName, "not matched closing tag </%s>", nodeName);
219                        return -1;
220                    }
221                    continue;
222                }
223            }
224            tagNode = new DOTCONFDocumentNode;
225            tagNode->name = strdup(nodeName);
226            tagNode->document = this;
227            tagNode->fileName = processedFiles.back();
228            tagNode->lineNum = curLine;
229            tagNode->closed = closed;
230            if(!nodeTree.empty()){
231                DOTCONFDocumentNode * prev = nodeTree.back();
232                if(prev->closed){
233
234                    curPrev->nextNode = tagNode;
235                    tagNode->previousNode = curPrev;
236                    tagNode->parentNode = curParent;
237
238                } else {
239                    prev->childNode = tagNode;
240                    tagNode->parentNode = prev;
241                    curParent = prev;
242                }
243            }
244            nodeTree.push_back(tagNode);
245            curPrev = tagNode;
246        } else {
247            nodeValue = word;
248            tagNode->pushValue(nodeValue);
249        }
250    }
251
252    return 0;
253}
254int DOTCONFDocument::parseFile(DOTCONFDocumentNode * _parent)
255{
256    char str[512];
257    int ret = 0;
258    curLine = 0;
259    curParent = _parent;
260
261    quoted = false;
262    size_t slen = 0;
263
264    while(fgets(str, 511, file)){
265        ++curLine;
266    slen = strlen(str);
267        if( slen >= 510 ){
268            error(curLine, fileName, "warning: line too long");
269        }
270    if(str[slen-1] != '\n'){
271        str[slen] = '\n';
272        str[slen+1] = 0;
273    }
274        if((ret = cleanupLine(str)) == -1){
275            break;
276        }
277        if(ret == 0){
278            if(!words.empty()){
279                ret = parseLine();
280                mempool->free();
281                words.clear();
282                if(ret == -1){
283                    break;
284                }
285            }
286        }
287    }
288
289    return ret;
290}
291
292int DOTCONFDocument::checkConfig(const std::list<DOTCONFDocumentNode*>::iterator & from)
293{
294    int ret = 0;
295
296    DOTCONFDocumentNode * tagNode = NULL;
297    int vi = 0;
298    for(std::list<DOTCONFDocumentNode*>::iterator i = from; i != nodeTree.end(); i++){
299        tagNode = *i;
300        if(!tagNode->closed){
301            error(tagNode->lineNum, tagNode->fileName, "unclosed tag %s", tagNode->name);
302            ret = -1;
303            break;
304        }
305        vi = 0;
306        while( vi < tagNode->valuesCount ){
307
308            if(strstr(tagNode->values[vi], "${") && strchr(tagNode->values[vi], '}') ){
309                ret = macroSubstitute(tagNode, vi );
310                mempool->free();
311                if(ret == -1){
312                    break;
313                }
314            }
315            ++vi;
316        }
317        if(ret == -1){
318            break;
319        }
320    }
321
322    return ret;
323}
324
325int DOTCONFDocument::setContent(const char * _fileName)
326{
327    int ret = 0;
328    char realpathBuf[PATH_MAX];
329
330    if(realpath(_fileName, realpathBuf) == NULL){
331        error(0, NULL, "realpath(%s) failed: %s", _fileName, strerror(errno));
332        return -1;
333    }
334
335    fileName = strdup(realpathBuf);
336
337    processedFiles.push_back(strdup(realpathBuf));
338
339    if(( file = fopen(fileName, "r")) == NULL){
340        error(0, NULL, "failed to open file '%s': %s", fileName, strerror(errno));
341        return -1;
342    }
343
344    ret = parseFile();
345
346    (void) fclose(file);
347
348    if(!ret){
349
350        if( (ret = checkConfig(nodeTree.begin())) == -1){
351            return -1;
352        }
353
354        std::list<DOTCONFDocumentNode*>::iterator from;
355        DOTCONFDocumentNode * tagNode = NULL;
356        int vi = 0;
357        for(std::list<DOTCONFDocumentNode*>::iterator i = nodeTree.begin(); i!=nodeTree.end(); i++){
358            tagNode = *i;
359            if(!cmp_func("DOTCONFPPIncludeFile", tagNode->name)){
360                vi = 0;
361                while( vi < tagNode->valuesCount ){
362                    if(access(tagNode->values[vi], R_OK) == -1){
363                        error(tagNode->lineNum, tagNode->fileName, "%s: %s", tagNode->values[vi], strerror(errno));
364                        return -1;
365                    }
366                    if(realpath(tagNode->values[vi], realpathBuf) == NULL){
367                        error(tagNode->lineNum, tagNode->fileName, "realpath(%s) failed: %s", tagNode->values[vi], strerror(errno));
368                        return -1;
369                    }
370
371                    bool processed = false;
372                    for(std::list<char*>::const_iterator itInode = processedFiles.begin(); itInode != processedFiles.end(); itInode++){
373                        if(!strcmp(*itInode, realpathBuf)){
374                            processed = true;
375                            break;
376                        }
377                    }
378                    if(processed){
379                        break;
380                    }
381
382                    processedFiles.push_back(strdup(realpathBuf));
383
384                    file = fopen(tagNode->values[vi], "r");
385                    if(file == NULL){
386                        error(tagNode->lineNum, fileName, "failed to open file '%s': %s", tagNode->values[vi], strerror(errno));
387                        return -1;
388                    }
389
390                    fileName = strdup(realpathBuf);
391                    from = nodeTree.end(); --from;
392
393                    ret = parseFile();
394                    (void) fclose(file);
395                    if(ret == -1)
396                        return -1;
397                    if(checkConfig(++from) == -1){
398                        return -1;
399                    }
400                    ++vi;
401                }
402            }
403        }
404
405
406        if(!requiredOptions.empty())
407            ret = checkRequiredOptions();
408    }
409
410    return ret;
411}
412
413int DOTCONFDocument::checkRequiredOptions()
414{
415    for(std::list<char*>::const_iterator ci = requiredOptions.begin(); ci != requiredOptions.end(); ci++){
416        bool matched = false;
417        for(std::list<DOTCONFDocumentNode*>::iterator i = nodeTree.begin(); i!=nodeTree.end(); i++){
418            if(!cmp_func((*i)->name, *ci)){
419                matched = true;
420                break;
421            }
422        }
423        if(!matched){
424            error(0, NULL, "required option '%s' not specified", *ci);
425            return -1;
426        }
427    }
428    return 0;
429}
430
431void DOTCONFDocument::error(int lineNum, const char * fileName_, const char * fmt, ...)
432{
433    va_list args;
434    va_start(args, fmt);
435
436    size_t len = (lineNum!=0?strlen(fileName_):0) + strlen(fmt) + 50;
437    char * buf = (char*)mempool->alloc(len);
438
439    if(lineNum)
440        (void) snprintf(buf, len, "DOTCONF++: file '%s', line %d: %s\n", fileName_, lineNum, fmt);
441    else
442        (void) snprintf(buf, len, "DOTCONF++: %s\n", fmt);
443
444    (void) vfprintf(stderr, buf, args);
445
446    va_end(args);
447}
448
449char * DOTCONFDocument::getSubstitution(char * macro, int lineNum)
450{
451    char * buf = NULL;
452    char * variable = macro+2;
453
454    char * endBr = strchr(macro, '}');
455
456    if(!endBr){
457        error(lineNum, fileName, "unterminated '{'");
458        return NULL;
459    }
460    *endBr = 0;
461
462    char * defaultValue = strchr(variable, ':');
463
464    if(defaultValue){
465        *defaultValue++ = 0;
466        if(*defaultValue != '-'){
467            error(lineNum, fileName, "incorrect macro substitution syntax");
468            return NULL;
469        }
470        ++defaultValue;
471        if(*defaultValue == '"' || *defaultValue == '\''){
472            ++defaultValue;
473            defaultValue[strlen(defaultValue)-1] = 0;
474        }
475    } else {
476        defaultValue = NULL;
477    }
478
479    char * subs = getenv(variable);
480    if( subs ){
481        buf = mempool->strdup(subs);
482    } else {
483        std::list<DOTCONFDocumentNode*>::iterator i = nodeTree.begin();
484        DOTCONFDocumentNode * tagNode = NULL;
485        for(; i!=nodeTree.end(); i++){
486            tagNode = *i;
487            if(!cmp_func(tagNode->name, variable)){
488                if(tagNode->valuesCount != 0){
489                    buf = mempool->strdup(tagNode->values[0]);
490                    break;
491                }
492            }
493        }
494        if( i == nodeTree.end() ){
495            if( defaultValue ){
496                buf = mempool->strdup(defaultValue);
497            } else {
498                error(lineNum, fileName, "substitution not found and default value not given");
499                return NULL;
500            }
501        }
502    }
503    return buf;
504}
505
506int DOTCONFDocument::macroSubstitute(DOTCONFDocumentNode * tagNode, int valueIndex)
507{
508    int ret = 0;
509    char * macro = tagNode->values[valueIndex];
510    size_t valueLen = strlen(tagNode->values[valueIndex])+1;
511    char * value = (char*)mempool->alloc(valueLen);
512    char * v = value;
513    char * subs = NULL;
514
515    while(*macro){
516        if(*macro == '$' && *(macro+1) == '{'){
517            char * m = strchr(macro, '}');
518            subs = getSubstitution(macro, tagNode->lineNum);
519            if(subs == NULL){
520                ret = -1;
521                break;
522            }
523            macro = m + 1;
524            *v = 0;
525            v = (char*)mempool->alloc(strlen(value)+strlen(subs)+valueLen);
526            strcpy(v, value);
527            value = strcat(v, subs);
528            v = value + strlen(value);
529            continue;
530        }
531        *v++ = *macro++;
532    }
533    *v = 0;
534
535    free(tagNode->values[valueIndex]);
536    tagNode->values[valueIndex] = strdup(value);
537    return ret;
538}
539
540const DOTCONFDocumentNode * DOTCONFDocument::getFirstNode() const
541{
542    if ( !nodeTree.empty() ) {
543        return *nodeTree.begin();
544    } else {
545        return NULL;
546    }
547}
548
549const DOTCONFDocumentNode * DOTCONFDocument::findNode(const char * nodeName, const DOTCONFDocumentNode * parentNode, const DOTCONFDocumentNode * startNode) const
550{
551
552
553    std::list<DOTCONFDocumentNode*>::const_iterator i = nodeTree.begin();
554
555    if(startNode == NULL)
556        startNode = parentNode;
557
558    if(startNode != NULL){
559        while( i != nodeTree.end() && (*i) != startNode ){
560            ++i;
561        }
562        if( i != nodeTree.end() ) ++i;
563    }
564
565    for(; i!=nodeTree.end(); i++){
566
567    if((*i)->parentNode != parentNode){
568            continue;
569        }
570        if(!cmp_func(nodeName, (*i)->name)){
571            return *i;
572        }
573    }
574    return NULL;
575}
576
577void DOTCONFDocument::setRequiredOptionNames(const char ** requiredOptionNames)
578{
579    while(*requiredOptionNames){
580        requiredOptions.push_back(strdup( *requiredOptionNames ));
581        ++requiredOptionNames;
582    }
583}
Note: See TracBrowser for help on using the browser.