1 /******************************************************************************
3 * Copyright (C) 1997-2015 by Dimitri van Heesch.
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
12 %option never-interactive
13 %option prefix="configimplYY"
27 #include <qfileinfo.h>
29 #include <qtextstream.h>
34 #include "configimpl.h"
41 #include "configoptions.h"
44 #define YY_NO_UNISTD_H 1
46 static const char *warning_str = "warning: ";
47 static const char *error_str = "error: ";
49 void config_err(const char *fmt, ...)
53 vfprintf(stderr, (QCString(error_str) + fmt).data(), args);
56 void config_warn(const char *fmt, ...)
60 vfprintf(stderr, (QCString(warning_str) + fmt).data(), args);
64 static QCString configStringRecode(
66 const char *fromEncoding,
67 const char *toEncoding);
69 #define MAX_INCLUDE_DEPTH 10
70 #define YY_NEVER_INTERACTIVE 1
72 /* -----------------------------------------------------------------
74 static QCString convertToComment(const QCString &s, const QCString &u)
76 //printf("convertToComment(%s)=%s\n",s.data(),u.data());
80 QCString tmp=s.stripWhiteSpace();
81 const char *p=tmp.data();
100 if (!result.isEmpty()) result+='\n';
106 void ConfigOption::writeBoolValue(FTextStream &t,bool v)
109 if (v) t << "YES"; else t << "NO";
112 void ConfigOption::writeIntValue(FTextStream &t,int i)
117 void ConfigOption::writeStringValue(FTextStream &t,QCString &s)
120 bool needsEscaping=FALSE;
121 // convert the string back to it original encoding
122 QCString se = configStringRecode(s,"UTF-8",m_encoding);
123 const char *p=se.data();
127 while ((c=*p++)!=0 && !needsEscaping)
128 needsEscaping = (c==' ' || c=='\n' || c=='\t' || c=='"' || c=='#');
135 if (*p==' ' && *(p+1)=='\0') break; // skip inserted space at the end
136 if (*p=='"') t << "\\"; // escape quotes
148 void ConfigOption::writeStringList(FTextStream &t,QStrList &l)
150 const char *p = l.first();
158 writeStringValue(t,s);
160 if (p) t << " \\" << endl;
164 /* -----------------------------------------------------------------
167 ConfigImpl *ConfigImpl::m_instance = 0;
169 void ConfigInt::convertStrToVal()
171 if (!m_valueString.isEmpty())
174 int val = m_valueString.toInt(&ok);
175 if (!ok || val<m_minVal || val>m_maxVal)
177 config_warn("argument `%s' for option %s is not a valid number in the range [%d..%d]!\n"
178 "Using the default: %d!\n",m_valueString.data(),m_name.data(),m_minVal,m_maxVal,m_value);
187 void ConfigBool::convertStrToVal()
189 QCString val = m_valueString.stripWhiteSpace().lower();
192 if (val=="yes" || val=="true" || val=="1" || val=="all")
196 else if (val=="no" || val=="false" || val=="0" || val=="none")
202 config_warn("argument `%s' for option %s is not a valid boolean value\n"
203 "Using the default: %s!\n",m_valueString.data(),m_name.data(),m_value?"YES":"NO");
208 QCString &ConfigImpl::getString(const char *fileName,int num,const char *name) const
210 ConfigOption *opt = m_dict->find(name);
213 config_err("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
216 else if (opt->kind()!=ConfigOption::O_String)
218 config_err("%s<%d>: Internal error: Requested option %s not of string type!\n",fileName,num,name);
221 return *((ConfigString *)opt)->valueRef();
224 QStrList &ConfigImpl::getList(const char *fileName,int num,const char *name) const
226 ConfigOption *opt = m_dict->find(name);
229 config_err("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
232 else if (opt->kind()!=ConfigOption::O_List)
234 config_err("%s<%d>: Internal error: Requested option %s not of list type!\n",fileName,num,name);
237 return *((ConfigList *)opt)->valueRef();
240 QCString &ConfigImpl::getEnum(const char *fileName,int num,const char *name) const
242 ConfigOption *opt = m_dict->find(name);
245 config_err("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
248 else if (opt->kind()!=ConfigOption::O_Enum)
250 config_err("%s<%d>: Internal error: Requested option %s not of enum type!\n",fileName,num,name);
253 return *((ConfigEnum *)opt)->valueRef();
256 int &ConfigImpl::getInt(const char *fileName,int num,const char *name) const
258 ConfigOption *opt = m_dict->find(name);
261 config_err("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
264 else if (opt->kind()!=ConfigOption::O_Int)
266 config_err("%s<%d>: Internal error: Requested option %s not of integer type!\n",fileName,num,name);
269 return *((ConfigInt *)opt)->valueRef();
272 bool &ConfigImpl::getBool(const char *fileName,int num,const char *name) const
274 ConfigOption *opt = m_dict->find(name);
277 config_err("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
280 else if (opt->kind()!=ConfigOption::O_Bool)
282 config_err("%s<%d>: Internal error: Requested option %s not of boolean type!\n",fileName,num,name);
285 return *((ConfigBool *)opt)->valueRef();
288 /* ------------------------------------------ */
290 void ConfigInfo::writeTemplate(FTextStream &t, bool sl,bool)
296 t << "#---------------------------------------------------------------------------\n";
297 t << "# " << m_doc << endl;
298 t << "#---------------------------------------------------------------------------\n";
301 void ConfigList::writeTemplate(FTextStream &t,bool sl,bool)
306 t << convertToComment(m_doc, m_userComment);
309 else if (!m_userComment.isEmpty())
311 t << convertToComment("", m_userComment);
313 t << m_name << m_spaces.left(MAX_OPTION_LENGTH-m_name.length()) << "=";
314 writeStringList(t,m_value);
318 void ConfigList::compareDoxyfile(FTextStream &t)
320 const char *p = m_value.first();
321 const char *q = m_defaultValue.first();
325 // count non empty elements
329 if (!s.stripWhiteSpace().isEmpty()) valCnt += 1;
336 if (!s.stripWhiteSpace().isEmpty()) defCnt += 1;
337 q = m_defaultValue.next();
339 if ( valCnt != defCnt)
341 writeTemplate(t,TRUE,TRUE);
345 // get first non empry element
346 q = m_defaultValue.first();
349 while (p && sp.stripWhiteSpace().isEmpty())
355 while (q && sq.stripWhiteSpace().isEmpty())
362 // skip empty elements
364 while (p && sp.stripWhiteSpace().isEmpty())
370 while (q && sq.stripWhiteSpace().isEmpty())
375 // be sure we have still an element (p and q have same number of 'filled' elements)
378 if (sp.stripWhiteSpace() != sq.stripWhiteSpace())
380 writeTemplate(t,TRUE,TRUE);
384 q = m_defaultValue.next();
389 void ConfigEnum::writeTemplate(FTextStream &t,bool sl,bool)
394 t << convertToComment(m_doc, m_userComment);
397 else if (!m_userComment.isEmpty())
399 t << convertToComment("", m_userComment);
401 t << m_name << m_spaces.left(MAX_OPTION_LENGTH-m_name.length()) << "=";
402 writeStringValue(t,m_value);
406 void ConfigEnum::compareDoxyfile(FTextStream &t)
408 if (m_value != m_defValue) writeTemplate(t,TRUE,TRUE);
411 void ConfigString::writeTemplate(FTextStream &t,bool sl,bool)
416 t << convertToComment(m_doc, m_userComment);
419 else if (!m_userComment.isEmpty())
421 t << convertToComment("", m_userComment);
423 t << m_name << m_spaces.left(MAX_OPTION_LENGTH-m_name.length()) << "=";
424 writeStringValue(t,m_value);
428 void ConfigString::compareDoxyfile(FTextStream &t)
430 if (m_value.stripWhiteSpace() != m_defValue.stripWhiteSpace()) writeTemplate(t,TRUE,TRUE);
433 void ConfigInt::writeTemplate(FTextStream &t,bool sl,bool upd)
438 t << convertToComment(m_doc, m_userComment);
441 else if (!m_userComment.isEmpty())
443 t << convertToComment("", m_userComment);
445 t << m_name << m_spaces.left(MAX_OPTION_LENGTH-m_name.length()) << "=";
446 if (upd && !m_valueString.isEmpty())
448 writeStringValue(t,m_valueString);
452 writeIntValue(t,m_value);
457 void ConfigInt::compareDoxyfile(FTextStream &t)
459 if (m_value != m_defValue) writeTemplate(t,TRUE,TRUE);
462 void ConfigBool::writeTemplate(FTextStream &t,bool sl,bool upd)
467 t << convertToComment(m_doc, m_userComment);
470 else if (!m_userComment.isEmpty())
472 t << convertToComment("", m_userComment);
474 t << m_name << m_spaces.left(MAX_OPTION_LENGTH-m_name.length()) << "=";
475 if (upd && !m_valueString.isEmpty())
477 writeStringValue(t,m_valueString);
481 writeBoolValue(t,m_value);
486 void ConfigBool::compareDoxyfile(FTextStream &t)
488 if (m_value != m_defValue) writeTemplate(t,TRUE,TRUE);
491 void ConfigObsolete::writeTemplate(FTextStream &,bool,bool) {}
492 void ConfigDisabled::writeTemplate(FTextStream &,bool,bool) {}
494 /* -----------------------------------------------------------------
499 struct ConfigFileState
503 YY_BUFFER_STATE oldState;
504 YY_BUFFER_STATE newState;
508 static const char *inputString;
509 static int inputPosition;
511 static QCString yyFileName;
512 static QCString tmpString;
513 static QCString *s=0;
515 static QStrList *l=0;
516 static int lastState;
517 static QCString elemStr;
518 static QStrList includePathList;
519 static QStack<ConfigFileState> includeStack;
520 static int includeDepth;
521 static bool config_upd = FALSE;
522 static QCString encoding;
523 static ConfigImpl *config;
525 /* -----------------------------------------------------------------
528 #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size);
530 static int yyread(char *buf,int max_size)
533 if (includeStack.isEmpty())
536 if (inputString==0) return c;
537 while( c < max_size && inputString[inputPosition] )
539 *buf = inputString[inputPosition++] ;
546 //assert(includeStack.current()->newState==YY_CURRENT_BUFFER);
547 return (int)fread(buf,1,max_size,includeStack.current()->filePtr);
552 static QCString configStringRecode(
554 const char *fromEncoding,
555 const char *toEncoding)
557 QCString inputEncoding = fromEncoding;
558 QCString outputEncoding = toEncoding;
559 if (inputEncoding.isEmpty() || outputEncoding.isEmpty() || inputEncoding==outputEncoding) return str;
560 int inputSize=str.length();
561 int outputSize=inputSize*4+1;
562 QCString output(outputSize);
563 void *cd = portable_iconv_open(outputEncoding,inputEncoding);
564 if (cd==(void *)(-1))
566 fprintf(stderr,"Error: unsupported character conversion: '%s'->'%s'\n",
567 inputEncoding.data(),outputEncoding.data());
570 size_t iLeft=(size_t)inputSize;
571 size_t oLeft=(size_t)outputSize;
572 char *inputPtr = str.rawData();
573 char *outputPtr = output.rawData();
574 if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft))
576 outputSize-=(int)oLeft;
577 output.resize(outputSize+1);
578 output.at(outputSize)='\0';
579 //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
583 fprintf(stderr,"Error: failed to translate characters from %s to %s: %s\n",
584 inputEncoding.data(),outputEncoding.data(),strerror(errno));
587 portable_iconv_close(cd);
591 static void checkEncoding()
593 ConfigString *option = (ConfigString*)config->get("DOXYFILE_ENCODING");
594 encoding = *option->valueRef();
597 static FILE *tryPath(const char *path,const char *fileName)
599 QCString absName=(path ? (QCString)path+"/"+fileName : (QCString)fileName);
600 QFileInfo fi(absName);
601 if (fi.exists() && fi.isFile())
603 FILE *f=portable_fopen(absName,"r");
604 if (!f) config_err("could not open file %s for reading\n",absName.data());
610 static void substEnvVarsInStrList(QStrList &sl);
611 static void substEnvVarsInString(QCString &s);
613 static FILE *findFile(const char *fileName)
619 if (portable_isAbsolutePath(fileName))
621 return tryPath(NULL, fileName);
623 substEnvVarsInStrList(includePathList);
624 char *s=includePathList.first();
625 while (s) // try each of the include paths
627 FILE *f = tryPath(s,fileName);
629 s=includePathList.next();
631 // try cwd if includePathList fails
632 return tryPath(".",fileName);
635 static void readIncludeFile(const char *incName)
637 if (includeDepth==MAX_INCLUDE_DEPTH) {
638 config_err("maximum include depth (%d) reached, %s is not included. Aborting...\n",
639 MAX_INCLUDE_DEPTH,incName);
643 QCString inc = incName;
644 substEnvVarsInString(inc);
645 inc = inc.stripWhiteSpace();
646 uint incLen = inc.length();
647 if (incLen>0 && inc.at(0)=='"' && inc.at(incLen-1)=='"') // strip quotes
649 inc=inc.mid(1,incLen-2);
654 if ((f=findFile(inc))) // see if the include file can be found
658 for (i=0;i<includeStack.count();i++) msg(" ");
659 msg("@INCLUDE = %s: parsing...\n",inc.data());
662 // store the state of the old file
663 ConfigFileState *fs=new ConfigFileState;
664 fs->oldState=YY_CURRENT_BUFFER;
666 fs->fileName=yyFileName;
668 // push the state on the stack
669 includeStack.push(fs);
670 // set the scanner to the include file
671 yy_switch_to_buffer(yy_create_buffer(f, YY_BUF_SIZE));
672 fs->newState=YY_CURRENT_BUFFER;
678 config_err("@INCLUDE = %s: not found!\n",inc.data());
702 <PreStart>"##".*"\n" { config->appendStartComment(yytext);}
707 <Start,GetString,GetStrList,GetBool,SkipInvalid>"##".*"\n" { config->appendUserComment(yytext);}
708 <Start,GetString,GetStrList,GetBool,SkipInvalid>"#" { BEGIN(SkipComment); }
709 <Start>[a-z_A-Z][a-z_A-Z0-9]*[ \t]*"=" { QCString cmd=yytext;
710 cmd=cmd.left(cmd.length()-1).stripWhiteSpace();
711 ConfigOption *option = config->get(cmd);
712 if (option==0) // oops not known
714 config_warn("ignoring unsupported tag `%s' at line %d, file %s\n",
715 cmd.data(),yyLineNr,yyFileName.data());
720 option->setUserComment(config->takeUserComment());
721 option->setEncoding(encoding);
722 switch(option->kind())
724 case ConfigOption::O_Info:
725 // shouldn't get here!
728 case ConfigOption::O_List:
729 l = ((ConfigList *)option)->valueRef();
734 case ConfigOption::O_Enum:
735 s = ((ConfigEnum *)option)->valueRef();
739 case ConfigOption::O_String:
740 s = ((ConfigString *)option)->valueRef();
744 case ConfigOption::O_Int:
745 s = ((ConfigInt *)option)->valueStringRef();
749 case ConfigOption::O_Bool:
750 s = ((ConfigBool *)option)->valueStringRef();
754 case ConfigOption::O_Obsolete:
757 config_warn("Tag `%s' at line %d of file `%s' has become obsolete.\n"
758 " This tag has been removed.\n", cmd.data(),yyLineNr,yyFileName.data());
762 config_warn("Tag `%s' at line %d of file `%s' has become obsolete.\n"
763 " To avoid this warning please remove this line from your configuration "
764 "file or upgrade it using \"doxygen -u\"\n", cmd.data(),yyLineNr,yyFileName.data());
768 case ConfigOption::O_Disabled:
771 config_warn("Tag `%s' at line %d of file `%s' belongs to an option that was not enabled at compile time.\n"
772 " This tag has been removed.\n", cmd.data(),yyLineNr,yyFileName.data());
776 config_warn("Tag `%s' at line %d of file `%s' belongs to an option that was not enabled at compile time.\n"
777 " To avoid this warning please remove this line from your configuration "
778 "file or upgrade it using \"doxygen -u\", or recompile doxygen with this feature enabled.\n", cmd.data(),yyLineNr,yyFileName.data());
785 <Start>[a-z_A-Z][a-z_A-Z0-9]*[ \t]*"+=" { QCString cmd=yytext;
786 cmd=cmd.left(cmd.length()-2).stripWhiteSpace();
787 ConfigOption *option = config->get(cmd);
788 if (option==0) // oops not known
790 config_warn("ignoring unsupported tag `%s' at line %d, file %s\n",
791 cmd.data(),yyLineNr,yyFileName.data());
796 option->setUserComment(config->takeUserComment());
797 switch(option->kind())
799 case ConfigOption::O_Info:
800 // shouldn't get here!
803 case ConfigOption::O_List:
804 l = ((ConfigList *)option)->valueRef();
808 case ConfigOption::O_Enum:
809 case ConfigOption::O_String:
810 case ConfigOption::O_Int:
811 case ConfigOption::O_Bool:
812 config_warn("operator += not supported for `%s'. Ignoring line at line %d, file %s\n",
813 yytext,yyLineNr,yyFileName.data());
816 case ConfigOption::O_Obsolete:
817 config_warn("Tag `%s' at line %d of file %s has become obsolete.\n"
818 "To avoid this warning please update your configuration "
819 "file using \"doxygen -u\"\n", cmd.data(),yyLineNr,yyFileName.data());
822 case ConfigOption::O_Disabled:
823 config_warn("Tag `%s' at line %d of file %s belongs to an option that was not enabled at compile time.\n"
824 "To avoid this warning please remove this line from your configuration "
825 "file, upgrade it using \"doxygen -u\", or recompile doxygen with this feature enabled.\n", cmd.data(),yyLineNr,yyFileName.data());
831 <Start>"@INCLUDE_PATH"[ \t]*"=" { BEGIN(GetStrList); l=&includePathList; l->clear(); elemStr=""; }
832 /* include a config file */
833 <Start>"@INCLUDE"[ \t]*"=" { BEGIN(Include);}
834 <Include>([^ \"\t\r\n]+)|("\""[^\n\"]+"\"") {
835 readIncludeFile(configStringRecode(yytext,encoding,"UTF-8"));
839 //printf("End of include file\n");
840 //printf("Include stack depth=%d\n",g_includeStack.count());
841 if (includeStack.isEmpty())
843 //printf("Terminating scanner!\n");
848 ConfigFileState *fs=includeStack.pop();
850 YY_BUFFER_STATE oldBuf = YY_CURRENT_BUFFER;
851 yy_switch_to_buffer( fs->oldState );
852 yy_delete_buffer( oldBuf );
854 yyFileName=fs->fileName;
860 <Start>[a-z_A-Z0-9]+ { config_warn("ignoring unknown tag `%s' at line %d, file %s\n",yytext,yyLineNr,yyFileName.data()); }
861 <GetString,GetBool,SkipInvalid>\n { yyLineNr++; BEGIN(Start); }
864 if (!elemStr.isEmpty())
866 //printf("elemStr1=`%s'\n",elemStr.data());
871 <GetStrList>[ \t,]+ {
872 if (!elemStr.isEmpty())
874 //printf("elemStr2=`%s'\n",elemStr.data());
879 <GetString>[^ \"\t\r\n]+ { (*s)+=configStringRecode(yytext,encoding,"UTF-8");
882 <GetString,GetStrList,SkipInvalid>"\"" { lastState=YY_START;
883 BEGIN(GetQuotedString);
886 <GetQuotedString>"\""|"\n" {
887 // we add a bogus space to signal that the string was quoted. This space will be stripped later on.
889 //printf("Quoted String = `%s'\n",tmpString.data());
890 if (lastState==GetString)
892 (*s)+=configStringRecode(tmpString,encoding,"UTF-8");
897 elemStr+=configStringRecode(tmpString,encoding,"UTF-8");
901 config_warn("Missing end quote (\") on line %d, file %s\n",yyLineNr,yyFileName.data());
906 <GetQuotedString>"\\\"" {
909 <GetQuotedString>. { tmpString+=*yytext; }
913 if (bs=="YES" || bs=="1")
915 else if (bs=="NO" || bs=="0")
920 config_warn("Invalid value `%s' for "
921 "boolean tag in line %d, file %s; use YES or NO\n",
922 bs.data(),yyLineNr,yyFileName.data());
925 <GetStrList>[^ \#\"\t\r\n,]+ {
926 elemStr+=configStringRecode(yytext,encoding,"UTF-8");
928 <SkipComment>\n { yyLineNr++; BEGIN(Start); }
929 <SkipComment>\\[ \r\t]*\n { yyLineNr++; BEGIN(Start); }
930 <*>\\[ \r\t]*\n { yyLineNr++; }
932 <*>\n { yyLineNr++ ; }
936 /*@ ----------------------------------------------------------------------------
939 void ConfigImpl::writeTemplate(FTextStream &t,bool sl,bool upd)
941 /* print first lines of user comment that were at the beginning of the file, might have special meaning for editors */
944 t << takeStartComment() << endl;
946 t << "# Doxyfile " << versionString << endl << endl;
949 t << convertToComment(m_header,"");
951 QListIterator<ConfigOption> it = iterator();
952 ConfigOption *option;
953 for (;(option=it.current());++it)
955 option->writeTemplate(t,sl,upd);
957 /* print last lines of user comment that were at the end of the file */
961 t << takeUserComment();
965 void ConfigImpl::compareDoxyfile(FTextStream &t)
967 t << "# Difference with default Doxyfile " << versionString << endl;
968 QListIterator<ConfigOption> it = iterator();
969 ConfigOption *option;
970 for (;(option=it.current());++it)
972 option->m_userComment = "";
973 option->compareDoxyfile(t);
977 void ConfigImpl::convertStrToVal()
979 QListIterator<ConfigOption> it = iterator();
980 ConfigOption *option;
981 for (;(option=it.current());++it)
983 option->convertStrToVal();
986 void ConfigImpl::emptyValueToDefault()
988 QListIterator<ConfigOption> it = iterator();
989 ConfigOption *option;
990 for (;(option=it.current());++it)
992 option->emptyValueToDefault();
996 static void substEnvVarsInString(QCString &s)
998 static QRegExp re("\\$\\([a-z_A-Z0-9.-]+\\)");
999 static QRegExp re2("\\$\\([a-z_A-Z0-9.-]+\\([a-z_A-Z0-9.-]+\\)\\)"); // For e.g. PROGRAMFILES(X86)
1000 if (s.isEmpty()) return;
1003 //printf("substEnvVarInString(%s) start\n",s.data());
1004 while ((i=re.match(s,p,&l))!=-1 || (i=re2.match(s,p,&l))!=-1)
1006 //printf("Found environment var s.mid(%d,%d)=`%s'\n",i+2,l-3,s.mid(i+2,l-3).data());
1007 QCString env=portable_getenv(s.mid(i+2,l-3));
1008 substEnvVarsInString(env); // recursively expand variables if needed.
1009 s = s.left(i)+env+s.right(s.length()-i-l);
1010 p=i+env.length(); // next time start at the end of the expanded string
1012 s=s.stripWhiteSpace(); // to strip the bogus space that was added when an argument
1014 //printf("substEnvVarInString(%s) end\n",s.data());
1017 static void substEnvVarsInStrList(QStrList &sl)
1019 char *s = sl.first();
1023 // an argument with quotes will have an extra space at the end, so wasQuoted will be TRUE.
1024 bool wasQuoted = (result.find(' ')!=-1) || (result.find('\t')!=-1);
1025 // here we strip the quote again
1026 substEnvVarsInString(result);
1028 //printf("Result %s was quoted=%d\n",result.data(),wasQuoted);
1030 if (!wasQuoted) /* as a result of the expansion, a single string
1031 may have expanded into a list, which we'll
1032 add to sl. If the original string already
1033 contained multiple elements no further
1034 splitting is done to allow quoted items with spaces! */
1036 int l=result.length();
1039 // search for a "word"
1043 // skip until start of new word
1044 while (i<l && ((c=result.at(i))==' ' || c=='\t')) i++;
1045 p=i; // p marks the start index of the word
1046 // skip until end of a word
1047 while (i<l && ((c=result.at(i))!=' ' && c!='\t' && c!='"')) i++;
1048 if (i<l) // not at the end of the string
1050 if (c=='"') // word within quotes
1056 if (c=='"') // end quote
1058 // replace the string in the list and go to the next item.
1059 sl.insert(sl.at(),result.mid(p,i-p)); // insert new item before current item.
1060 sl.next(); // current item is now the old item
1064 else if (c=='\\') // skip escaped stuff
1070 else if (c==' ' || c=='\t') // separator
1072 // replace the string in the list and go to the next item.
1073 sl.insert(sl.at(),result.mid(p,i-p)); // insert new item before current item.
1074 sl.next(); // current item is now the old item
1079 if (p!=l) // add the leftover as a string
1081 // replace the string in the list and go to the next item.
1082 sl.insert(sl.at(),result.right(l-p)); // insert new item before current item.
1083 sl.next(); // current item is now the old item
1086 else // just goto the next element in the list
1088 sl.insert(sl.at(),result);
1091 // remove the old unexpanded string from the list
1093 sl.remove(); // current item index changes if the last element is removed.
1094 if (sl.at()==i) // not last item
1096 else // just removed last item
1101 void ConfigString::substEnvVars()
1103 substEnvVarsInString(m_value);
1106 void ConfigList::substEnvVars()
1108 substEnvVarsInStrList(m_value);
1111 void ConfigBool::substEnvVars()
1113 substEnvVarsInString(m_valueString);
1116 void ConfigInt::substEnvVars()
1118 substEnvVarsInString(m_valueString);
1121 void ConfigEnum::substEnvVars()
1123 substEnvVarsInString(m_value);
1126 //---------------------------------------------
1128 void ConfigImpl::substituteEnvironmentVars()
1130 QListIterator<ConfigOption> it = iterator();
1131 ConfigOption *option;
1132 for (;(option=it.current());++it)
1134 option->substEnvVars();
1138 void ConfigImpl::init()
1140 QListIterator<ConfigOption> it = iterator();
1141 ConfigOption *option;
1142 for (;(option=it.current());++it)
1147 // sanity check if all depends relations are valid
1148 for (it.toFirst();(option=it.current());++it)
1150 QCString depName = option->dependsOn();
1151 if (!depName.isEmpty())
1153 ConfigOption * opt = ConfigImpl::instance()->get(depName);
1156 config_warn("Config option '%s' has invalid depends relation on unknown option '%s'\n",
1157 option->name().data(),depName.data());
1164 void ConfigImpl::create()
1166 if (m_initialized) return;
1167 m_initialized = TRUE;
1168 addConfigOptions(this);
1171 static QCString configFileToString(const char *name)
1173 if (name==0 || name[0]==0) return 0;
1176 bool fileOpened=FALSE;
1177 if (name[0]=='-' && name[1]==0) // read from stdin
1179 fileOpened=f.open(IO_ReadOnly,stdin);
1182 const int bSize=4096;
1183 QCString contents(bSize);
1186 while ((size=f.readBlock(contents.rawData()+totalSize,bSize))==bSize)
1189 contents.resize(totalSize+bSize);
1192 contents.resize(totalSize);
1193 contents.at(totalSize-2)='\n'; // to help the scanner
1194 contents.at(totalSize-1)='\0';
1198 else // read from file
1201 if (!fi.exists() || !fi.isFile())
1203 config_err("file `%s' not found\n",name);
1207 fileOpened=f.open(IO_ReadOnly);
1211 QCString contents(fsize+2);
1212 f.readBlock(contents.rawData(),fsize);
1214 if (fsize==0 || contents[fsize-1]=='\n')
1215 contents[fsize]='\0';
1217 contents[fsize]='\n'; // to help the scanner
1218 contents[fsize+1]='\0';
1224 config_err("cannot open file `%s' for reading\n",name);
1230 bool ConfigImpl::parseString(const char *fn,const char *str,bool update)
1232 config = ConfigImpl::instance();
1237 includeStack.setAutoDelete(TRUE);
1238 includeStack.clear();
1240 configimplYYrestart( configimplYYin );
1242 config_upd = update;
1249 bool ConfigImpl::parse(const char *fn,bool update)
1253 printlex(yy_flex_debug, TRUE, __FILE__, fn);
1254 retval = parseString(fn,configFileToString(fn), update);
1255 printlex(yy_flex_debug, FALSE, __FILE__, fn);
1259 //----------------------------------------------------------------------
1261 static void cleanUpPaths(QStrList &str)
1263 char *sfp = str.first();
1272 if (c=='\\') *p='/';
1276 QCString path = sfp;
1277 if ((path.at(0)!='/' && (path.length()<=2 || path.at(1)!=':')) ||
1278 path.at(path.length()-1)!='/'
1282 if (fi.exists() && fi.isDir())
1285 QString p = fi.absFilePath();
1286 if (p.at(p.length()-1)!='/')
1289 if (str.at()==i) // did not remove last item
1290 str.insert(i,p.utf8());
1292 str.append(p.utf8());
1299 static void checkFileName(QCString &s,const char *optionName)
1301 QCString val = s.stripWhiteSpace().lower();
1302 if ((val=="yes" || val=="true" || val=="1" || val=="all") ||
1303 (val=="no" || val=="false" || val=="0" || val=="none"))
1305 err("file name expected for option %s, got %s instead. Ignoring...\n",optionName,s.data());
1306 s=""; // note the use of &s above: this will change the option value!
1314 ConfigImpl::instance()->init();
1317 void Config::checkAndCorrect()
1319 QCString &warnFormat = ConfigImpl_getString("WARN_FORMAT");
1320 if (warnFormat.stripWhiteSpace().isEmpty())
1322 warnFormat="$file:$line $text";
1326 if (warnFormat.find("$file")==-1)
1328 warn_uncond("warning format does not contain a $file tag!\n");
1331 if (warnFormat.find("$line")==-1)
1333 warn_uncond("warning format does not contain a $line tag!\n");
1335 if (warnFormat.find("$text")==-1)
1337 warn_uncond("warning format foes not contain a $text tag!\n");
1341 QCString &manExtension = ConfigImpl_getString("MAN_EXTENSION");
1343 // set default man page extension if non is given by the user
1344 if (manExtension.isEmpty())
1349 QCString &paperType = ConfigImpl_getEnum("PAPER_TYPE");
1350 paperType=paperType.lower().stripWhiteSpace();
1351 if (paperType.isEmpty() || paperType=="a4wide")
1355 if (paperType!="a4" && paperType!="letter" &&
1356 paperType!="legal" && paperType!="executive")
1358 err("Unknown page type specified\n");
1362 QCString &outputLanguage=ConfigImpl_getEnum("OUTPUT_LANGUAGE");
1363 outputLanguage=outputLanguage.stripWhiteSpace();
1364 if (outputLanguage.isEmpty())
1366 outputLanguage = "English";
1369 QCString &htmlFileExtension=ConfigImpl_getString("HTML_FILE_EXTENSION");
1370 htmlFileExtension=htmlFileExtension.stripWhiteSpace();
1371 if (htmlFileExtension.isEmpty())
1373 htmlFileExtension = ".html";
1376 // expand the relative stripFromPath values
1377 QStrList &stripFromPath = ConfigImpl_getList("STRIP_FROM_PATH");
1378 char *sfp = stripFromPath.first();
1379 if (sfp==0) // by default use the current path
1381 QString p = QDir::currentDirPath();
1382 if (p.at(p.length()-1)!='/')
1384 stripFromPath.append(p.utf8());
1388 cleanUpPaths(stripFromPath);
1391 // expand the relative stripFromPath values
1392 QStrList &stripFromIncPath = ConfigImpl_getList("STRIP_FROM_INC_PATH");
1393 cleanUpPaths(stripFromIncPath);
1395 // Test to see if HTML header is valid
1396 QCString &headerFile = ConfigImpl_getString("HTML_HEADER");
1397 if (!headerFile.isEmpty())
1399 QFileInfo fi(headerFile);
1402 err("tag HTML_HEADER: header file `%s' "
1403 "does not exist\n",headerFile.data());
1407 // Test to see if HTML footer is valid
1408 QCString &footerFile = ConfigImpl_getString("HTML_FOOTER");
1409 if (!footerFile.isEmpty())
1411 QFileInfo fi(footerFile);
1414 err("tag HTML_FOOTER: footer file `%s' "
1415 "does not exist\n",footerFile.data());
1420 // Test to see if MathJax code file is valid
1421 if (ConfigImpl_getBool("USE_MATHJAX"))
1423 QCString &MathJaxCodefile = ConfigImpl_getString("MATHJAX_CODEFILE");
1424 if (!MathJaxCodefile.isEmpty())
1426 QFileInfo fi(MathJaxCodefile);
1429 err("tag MATHJAX_CODEFILE file `%s' "
1430 "does not exist\n",MathJaxCodefile.data());
1434 QCString &path = ConfigImpl_getString("MATHJAX_RELPATH");
1435 if (!path.isEmpty() && path.at(path.length()-1)!='/')
1442 // Test to see if LaTeX header is valid
1443 QCString &latexHeaderFile = ConfigImpl_getString("LATEX_HEADER");
1444 if (!latexHeaderFile.isEmpty())
1446 QFileInfo fi(latexHeaderFile);
1449 err("tag LATEX_HEADER: header file `%s' "
1450 "does not exist\n",latexHeaderFile.data());
1454 // Test to see if LaTeX footer is valid
1455 QCString &latexFooterFile = ConfigImpl_getString("LATEX_FOOTER");
1456 if (!latexFooterFile.isEmpty())
1458 QFileInfo fi(latexFooterFile);
1461 err("tag LATEX_FOOTER: footer file `%s' "
1462 "does not exist\n",latexFooterFile.data());
1467 // check include path
1468 QStrList &includePath = ConfigImpl_getList("INCLUDE_PATH");
1469 char *s=includePath.first();
1473 if (!fi.exists()) warn_uncond("tag INCLUDE_PATH: include path `%s' "
1474 "does not exist\n",s);
1475 s=includePath.next();
1479 QStrList &aliasList = ConfigImpl_getList("ALIASES");
1480 s=aliasList.first();
1483 QRegExp re1("[a-z_A-Z][a-z_A-Z0-9]*[ \t]*="); // alias without argument
1484 QRegExp re2("[a-z_A-Z][a-z_A-Z0-9]*{[0-9]*}[ \t]*="); // alias with argument
1486 alias=alias.stripWhiteSpace();
1487 if (alias.find(re1)!=0 && alias.find(re2)!=0)
1489 err("Illegal alias format `%s'. Use \"name=value\" or \"name(n)=value\", where n is the number of arguments\n",
1495 // check if GENERATE_TREEVIEW and GENERATE_HTMLHELP are both enabled
1496 if (ConfigImpl_getBool("GENERATE_TREEVIEW") && ConfigImpl_getBool("GENERATE_HTMLHELP"))
1498 err("When enabling GENERATE_HTMLHELP the tree view (GENERATE_TREEVIEW) should be disabled. I'll do it for you.\n");
1499 ConfigImpl_getBool("GENERATE_TREEVIEW")=FALSE;
1501 if (ConfigImpl_getBool("SEARCHENGINE") && ConfigImpl_getBool("GENERATE_HTMLHELP"))
1503 err("When enabling GENERATE_HTMLHELP the search engine (SEARCHENGINE) should be disabled. I'll do it for you.\n");
1504 ConfigImpl_getBool("SEARCHENGINE")=FALSE;
1507 // check if SEPARATE_MEMBER_PAGES and INLINE_GROUPED_CLASSES are both enabled
1508 if (ConfigImpl_getBool("SEPARATE_MEMBER_PAGES") && ConfigImpl_getBool("INLINE_GROUPED_CLASSES"))
1510 err("When enabling INLINE_GROUPED_CLASSES the SEPARATE_MEMBER_PAGES option should be disabled. I'll do it for you.\n");
1511 ConfigImpl_getBool("SEPARATE_MEMBER_PAGES")=FALSE;
1514 // check dot image format
1515 QCString &dotImageFormat=ConfigImpl_getEnum("DOT_IMAGE_FORMAT");
1516 dotImageFormat=dotImageFormat.stripWhiteSpace();
1517 if (dotImageFormat.isEmpty())
1519 dotImageFormat = "png";
1521 //else if (dotImageFormat!="gif" && dotImageFormat!="png" && dotImageFormat!="jpg")
1523 // err("Invalid value for DOT_IMAGE_FORMAT: `%s'. Using the default.\n",dotImageFormat.data());
1524 // dotImageFormat = "png";
1527 QCString &dotFontName=ConfigImpl_getString("DOT_FONTNAME");
1528 if (dotFontName=="FreeSans" || dotFontName=="FreeSans.ttf")
1530 warn_uncond("doxygen no longer ships with the FreeSans font.\n"
1531 "You may want to clear or change DOT_FONTNAME.\n"
1532 "Otherwise you run the risk that the wrong font is being used for dot generated graphs.\n");
1537 QCString &dotPath = ConfigImpl_getString("DOT_PATH");
1538 if (!dotPath.isEmpty())
1540 QFileInfo fi(dotPath);
1541 if (fi.exists() && fi.isFile()) // user specified path + exec
1543 dotPath=fi.dirPath(TRUE).utf8()+"/";
1547 QFileInfo dp(dotPath+"/dot"+portable_commandExtension());
1548 if (!dp.exists() || !dp.isFile())
1550 warn_uncond("the dot tool could not be found at %s\n",dotPath.data());
1555 dotPath=dp.dirPath(TRUE).utf8()+"/";
1558 #if defined(_WIN32) // convert slashes
1559 uint i=0,l=dotPath.length();
1560 for (i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
1563 else // make sure the string is empty but not null!
1568 // check mscgen path
1569 QCString &mscgenPath = ConfigImpl_getString("MSCGEN_PATH");
1570 if (!mscgenPath.isEmpty())
1572 QFileInfo dp(mscgenPath+"/mscgen"+portable_commandExtension());
1573 if (!dp.exists() || !dp.isFile())
1575 warn_uncond("the mscgen tool could not be found at %s\n",mscgenPath.data());
1580 mscgenPath=dp.dirPath(TRUE).utf8()+"/";
1581 #if defined(_WIN32) // convert slashes
1582 uint i=0,l=mscgenPath.length();
1583 for (i=0;i<l;i++) if (mscgenPath.at(i)=='/') mscgenPath.at(i)='\\';
1587 else // make sure the string is empty but not null!
1592 // check plantuml path
1593 QCString &plantumlJarPath = ConfigImpl_getString("PLANTUML_JAR_PATH");
1594 if (!plantumlJarPath.isEmpty())
1596 QFileInfo pu(plantumlJarPath);
1597 if (pu.exists() && pu.isDir()) // PLANTUML_JAR_PATH is directory
1599 QFileInfo jar(plantumlJarPath+portable_pathSeparator()+"plantuml.jar");
1600 if (jar.exists() && jar.isFile())
1602 plantumlJarPath = jar.dirPath(TRUE).utf8()+portable_pathSeparator();
1606 err("Jar file plantuml.jar not found at location "
1607 "specified via PLANTUML_JAR_PATH: '%s'\n",plantumlJarPath.data());
1611 else if (pu.exists() && pu.isFile() && plantumlJarPath.right(4)==".jar") // PLANTUML_JAR_PATH is file
1613 plantumlJarPath = pu.dirPath(TRUE).utf8()+portable_pathSeparator();
1617 err("path specified via PLANTUML_JAR_PATH does not exist or not a directory: %s\n",
1618 plantumlJarPath.data());
1624 QCString &diaPath = ConfigImpl_getString("DIA_PATH");
1625 if (!diaPath.isEmpty())
1627 QFileInfo dp(diaPath+"/dia"+portable_commandExtension());
1628 if (!dp.exists() || !dp.isFile())
1630 warn_uncond("dia could not be found at %s\n",diaPath.data());
1635 diaPath=dp.dirPath(TRUE).utf8()+"/";
1636 #if defined(_WIN32) // convert slashes
1637 uint i=0,l=diaPath.length();
1638 for (i=0;i<l;i++) if (diaPath.at(i)=='/') diaPath.at(i)='\\';
1642 else // make sure the string is empty but not null!
1648 QStrList &inputSources=ConfigImpl_getList("INPUT");
1649 if (inputSources.count()==0)
1651 // use current dir as the default
1652 inputSources.append(QDir::currentDirPath().utf8());
1656 s=inputSources.first();
1662 warn_uncond("tag INPUT: input source `%s' does not exist\n",s);
1664 s=inputSources.next();
1668 // add default file patterns if needed
1669 QStrList &filePatternList = ConfigImpl_getList("FILE_PATTERNS");
1670 if (filePatternList.isEmpty())
1672 ConfigOption * opt = ConfigImpl::instance()->get("FILE_PATTERNS");
1673 if (opt->kind()==ConfigOption::O_List)
1675 ((ConfigList*)opt)->init();
1679 // add default pattern if needed
1680 QStrList &examplePatternList = ConfigImpl_getList("EXAMPLE_PATTERNS");
1681 if (examplePatternList.isEmpty())
1683 examplePatternList.append("*");
1686 // if no output format is enabled, warn the user
1687 if (!ConfigImpl_getBool("GENERATE_HTML") &&
1688 !ConfigImpl_getBool("GENERATE_LATEX") &&
1689 !ConfigImpl_getBool("GENERATE_MAN") &&
1690 !ConfigImpl_getBool("GENERATE_RTF") &&
1691 !ConfigImpl_getBool("GENERATE_XML") &&
1692 !ConfigImpl_getBool("GENERATE_PERLMOD") &&
1693 !ConfigImpl_getBool("GENERATE_RTF") &&
1694 !ConfigImpl_getBool("GENERATE_DOCBOOK") &&
1695 !ConfigImpl_getBool("GENERATE_AUTOGEN_DEF") &&
1696 ConfigImpl_getString("GENERATE_TAGFILE").isEmpty()
1699 warn_uncond("No output formats selected! Set at least one of the main GENERATE_* options to YES.\n");
1702 // check HTMLHELP creation requirements
1703 if (!ConfigImpl_getBool("GENERATE_HTML") &&
1704 ConfigImpl_getBool("GENERATE_HTMLHELP"))
1706 warn_uncond("GENERATE_HTMLHELP=YES requires GENERATE_HTML=YES.\n");
1709 // check QHP creation requirements
1710 if (ConfigImpl_getBool("GENERATE_QHP"))
1712 if (ConfigImpl_getString("QHP_NAMESPACE").isEmpty())
1714 err("GENERATE_QHP=YES requires QHP_NAMESPACE to be set. Using 'org.doxygen.doc' as default!.\n");
1715 ConfigImpl_getString("QHP_NAMESPACE")="org.doxygen.doc";
1718 if (ConfigImpl_getString("QHP_VIRTUAL_FOLDER").isEmpty())
1720 err("GENERATE_QHP=YES requires QHP_VIRTUAL_FOLDER to be set. Using 'doc' as default!\n");
1721 ConfigImpl_getString("QHP_VIRTUAL_FOLDER")="doc";
1725 if (ConfigImpl_getBool("OPTIMIZE_OUTPUT_JAVA") && ConfigImpl_getBool("INLINE_INFO"))
1727 // don't show inline info for Java output, since Java has no inline
1729 ConfigImpl_getBool("INLINE_INFO")=FALSE;
1732 int &depth = ConfigImpl_getInt("MAX_DOT_GRAPH_DEPTH");
1738 int &hue = ConfigImpl_getInt("HTML_COLORSTYLE_HUE");
1748 int &sat = ConfigImpl_getInt("HTML_COLORSTYLE_SAT");
1757 int &gamma = ConfigImpl_getInt("HTML_COLORSTYLE_GAMMA");
1767 QCString mathJaxFormat = ConfigImpl_getEnum("MATHJAX_FORMAT");
1768 if (!mathJaxFormat.isEmpty() && mathJaxFormat!="HTML-CSS" &&
1769 mathJaxFormat!="NativeMML" && mathJaxFormat!="SVG")
1771 err("Unsupported value for MATHJAX_FORMAT: Should be one of HTML-CSS, NativeMML, or SVG\n");
1772 ConfigImpl_getEnum("MATHJAX_FORMAT")="HTML-CSS";
1775 // add default words if needed
1776 QStrList &annotationFromBrief = ConfigImpl_getList("ABBREVIATE_BRIEF");
1777 if (annotationFromBrief.isEmpty())
1779 annotationFromBrief.append("The $name class");
1780 annotationFromBrief.append("The $name widget");
1781 annotationFromBrief.append("The $name file");
1782 annotationFromBrief.append("is");
1783 annotationFromBrief.append("provides");
1784 annotationFromBrief.append("specifies");
1785 annotationFromBrief.append("contains");
1786 annotationFromBrief.append("represents");
1787 annotationFromBrief.append("a");
1788 annotationFromBrief.append("an");
1789 annotationFromBrief.append("the");
1792 // some default settings for vhdl
1793 if (ConfigImpl_getBool("OPTIMIZE_OUTPUT_VHDL") &&
1794 (ConfigImpl_getBool("INLINE_INHERITED_MEMB") ||
1795 ConfigImpl_getBool("INHERIT_DOCS") ||
1796 !ConfigImpl_getBool("HIDE_SCOPE_NAMES") ||
1797 !ConfigImpl_getBool("EXTRACT_PRIVATE") ||
1798 !ConfigImpl_getBool("EXTRACT_PACKAGE")
1802 bool b1 = ConfigImpl_getBool("INLINE_INHERITED_MEMB");
1803 bool b2 = ConfigImpl_getBool("INHERIT_DOCS");
1804 bool b3 = ConfigImpl_getBool("HIDE_SCOPE_NAMES");
1805 bool b4 = ConfigImpl_getBool("EXTRACT_PRIVATE");
1806 bool b5 = ConfigImpl_getBool("SKIP_FUNCTION_MACROS");
1807 bool b6 = ConfigImpl_getBool("EXTRACT_PACKAGE");
1808 const char *s1,*s2,*s3,*s4,*s5,*s6;
1809 if (b1) s1=" INLINE_INHERITED_MEMB = NO (was YES)\n"; else s1="";
1810 if (b2) s2=" INHERIT_DOCS = NO (was YES)\n"; else s2="";
1811 if (!b3) s3=" HIDE_SCOPE_NAMES = YES (was NO)\n"; else s3="";
1812 if (!b4) s4=" EXTRACT_PRIVATE = YES (was NO)\n"; else s4="";
1813 if (b5) s5=" ENABLE_PREPROCESSING = NO (was YES)\n"; else s5="";
1814 if (!b6) s6=" EXTRACT_PACKAGE = YES (was NO)\n"; else s6="";
1817 warn_uncond("enabling OPTIMIZE_OUTPUT_VHDL assumes the following settings:\n"
1818 "%s%s%s%s%s%s",s1,s2,s3,s4,s5,s6
1821 ConfigImpl_getBool("INLINE_INHERITED_MEMB") = FALSE;
1822 ConfigImpl_getBool("INHERIT_DOCS") = FALSE;
1823 ConfigImpl_getBool("HIDE_SCOPE_NAMES") = TRUE;
1824 ConfigImpl_getBool("EXTRACT_PRIVATE") = TRUE;
1825 ConfigImpl_getBool("ENABLE_PREPROCESSING") = FALSE;
1826 ConfigImpl_getBool("EXTRACT_PACKAGE") = TRUE;
1829 checkFileName(ConfigImpl_getString("GENERATE_TAGFILE"),"GENERATE_TAGFILE");
1831 #if 0 // TODO: this breaks test 25; SOURCEBROWSER = NO and SOURCE_TOOLTIPS = YES.
1832 // So this and other regressions should be analysed and fixed before this can be enabled
1833 // disable any boolean options that depend on disabled options
1834 QListIterator<ConfigOption> it = iterator();
1835 ConfigOption *option;
1836 for (it.toFirst();(option=it.current());++it)
1838 QCString depName = option->dependsOn(); // option has a dependency
1839 if (!depName.isEmpty())
1841 ConfigOption * dep = Config::instance()->get(depName);
1842 if (dep->kind()==ConfigOption::O_Bool &&
1843 ConfigImpl_getBool("depName")==FALSE) // dependent option is disabled
1845 if (option->kind()==ConfigOption::O_Bool)
1847 printf("disabling option %s\n",option->name().data());
1848 ConfigImpl_getBool("option->name("))=FALSE; // also disable this option
1855 ConfigValues::instance().init();
1859 void Config::writeTemplate(FTextStream &t,bool shortList,bool update)
1861 ConfigImpl::instance()->writeTemplate(t,shortList,update);
1864 void Config::compareDoxyfile(FTextStream &t)
1866 postProcess(FALSE, TRUE);
1867 ConfigImpl::instance()->compareDoxyfile(t);
1870 bool Config::parse(const char *fileName,bool update)
1872 return ConfigImpl::instance()->parse(fileName,update);
1875 void Config::postProcess(bool clearHeaderAndFooter, bool compare)
1877 ConfigImpl::instance()->substituteEnvironmentVars();
1878 if (!compare)ConfigImpl::instance()->emptyValueToDefault();
1879 ConfigImpl::instance()->convertStrToVal();
1881 // avoid bootstrapping issues when the config file already
1882 // refers to the files that we are supposed to parse.
1883 if (clearHeaderAndFooter)
1885 ConfigImpl_getString("HTML_HEADER")="";
1886 ConfigImpl_getString("HTML_FOOTER")="";
1887 ConfigImpl_getString("LATEX_HEADER")="";
1888 ConfigImpl_getString("LATEX_FOOTER")="";
1892 void Config::deinit()
1894 ConfigImpl::instance()->deleteInstance();