1 /******************************************************************************
3 * Copyright (C) 1997-2012 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.
25 #include <qfileinfo.h>
27 #include <qtextstream.h>
38 #include "configoptions.h"
40 #undef Config_getString
48 // use in-class definitions
49 #define Config_getString(val) getString(__FILE__,__LINE__,val)
50 #define Config_getInt(val) getInt(__FILE__,__LINE__,val)
51 #define Config_getList(val) getList(__FILE__,__LINE__,val)
52 #define Config_getEnum(val) getEnum(__FILE__,__LINE__,val)
53 #define Config_getBool(val) getBool(__FILE__,__LINE__,val)
55 void config_err(const char *fmt, ...)
59 vfprintf(stderr, fmt, args);
62 void config_warn(const char *fmt, ...)
66 vfprintf(stderr, fmt, args);
70 static QCString configStringRecode(
72 const char *fromEncoding,
73 const char *toEncoding);
75 #define MAX_INCLUDE_DEPTH 10
76 #define YY_NEVER_INTERACTIVE 1
78 /* -----------------------------------------------------------------
80 QCString ConfigOption::convertToComment(const QCString &s)
83 if (s.isEmpty()) return result;
86 QCString tmp=s.stripWhiteSpace();
107 void ConfigOption::writeBoolValue(FTextStream &t,bool v)
110 if (v) t << "YES"; else t << "NO";
113 void ConfigOption::writeIntValue(FTextStream &t,int i)
118 void ConfigOption::writeStringValue(FTextStream &t,QCString &s)
121 bool needsEscaping=FALSE;
122 // convert the string back to it original encoding
123 QCString se = configStringRecode(s,"UTF-8",m_encoding);
124 const char *p=se.data();
128 while ((c=*p++)!=0 && !needsEscaping)
129 needsEscaping = (c==' ' || c=='\n' || c=='\t' || c=='"' || c=='#');
136 if (*p==' ' && *(p+1)=='\0') break; // skip inserted space at the end
137 if (*p=='"') t << "\\"; // escape quotes
149 void ConfigOption::writeStringList(FTextStream &t,QStrList &l)
151 const char *p = l.first();
159 writeStringValue(t,s);
161 if (p) t << " \\" << endl;
165 /* -----------------------------------------------------------------
168 Config *Config::m_instance = 0;
170 void ConfigInt::convertStrToVal()
172 if (!m_valueString.isEmpty())
175 int val = m_valueString.toInt(&ok);
176 if (!ok || val<m_minVal || val>m_maxVal)
178 config_warn("warning: argument `%s' for option %s is not a valid number in the range [%d..%d]!\n"
179 "Using the default: %d!\n",m_valueString.data(),m_name.data(),m_minVal,m_maxVal,m_value);
185 void ConfigBool::convertStrToVal()
187 QCString val = m_valueString.stripWhiteSpace().lower();
190 if (val=="yes" || val=="true" || val=="1" || val=="all")
194 else if (val=="no" || val=="false" || val=="0" || val=="none")
200 config_warn("warning: argument `%s' for option %s is not a valid boolean value\n"
201 "Using the default: %s!\n",m_valueString.data(),m_name.data(),m_value?"YES":"NO");
206 QCString &Config::getString(const char *fileName,int num,const char *name) const
208 ConfigOption *opt = m_dict->find(name);
211 config_err("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
214 else if (opt->kind()!=ConfigOption::O_String)
216 config_err("%s<%d>: Internal error: Requested option %s not of string type!\n",fileName,num,name);
219 return *((ConfigString *)opt)->valueRef();
222 QStrList &Config::getList(const char *fileName,int num,const char *name) const
224 ConfigOption *opt = m_dict->find(name);
227 config_err("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
230 else if (opt->kind()!=ConfigOption::O_List)
232 config_err("%d<%d>: Internal error: Requested option %s not of list type!\n",fileName,num,name);
235 return *((ConfigList *)opt)->valueRef();
238 QCString &Config::getEnum(const char *fileName,int num,const char *name) const
240 ConfigOption *opt = m_dict->find(name);
243 config_err("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
246 else if (opt->kind()!=ConfigOption::O_Enum)
248 config_err("%s<%d>: Internal error: Requested option %s not of enum type!\n",fileName,num,name);
251 return *((ConfigEnum *)opt)->valueRef();
254 int &Config::getInt(const char *fileName,int num,const char *name) const
256 ConfigOption *opt = m_dict->find(name);
259 config_err("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
262 else if (opt->kind()!=ConfigOption::O_Int)
264 config_err("%s<%d>: Internal error: Requested option %s not of integer type!\n",fileName,num,name);
267 return *((ConfigInt *)opt)->valueRef();
270 bool &Config::getBool(const char *fileName,int num,const char *name) const
272 ConfigOption *opt = m_dict->find(name);
275 config_err("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
278 else if (opt->kind()!=ConfigOption::O_Bool)
280 config_err("%s<%d>: Internal error: Requested option %s not of integer type!\n",fileName,num,name);
283 return *((ConfigBool *)opt)->valueRef();
286 /* -----------------------------------------------------------------
289 void ConfigInt::writeXML(FTextStream& t)
291 t << " <option type='int' "
292 "id='" << convertToXML(name()) << "' "
293 "docs='\n" << convertToXML(docs()) << "' "
294 "minval='" << m_minVal << "' "
295 "maxval='" << m_maxVal << "' "
296 "defval='" << m_defValue << "'";
297 if (!m_dependency.isEmpty()) t << " depends='" << m_dependency << "'";
301 void ConfigBool::writeXML(FTextStream& t)
303 t << " <option type='bool' "
304 "id='" << convertToXML(name()) << "' "
305 "docs='\n" << convertToXML(docs()) << "' "
306 "defval='" << m_defValue << "'";
307 if (!m_dependency.isEmpty()) t << " depends='" << m_dependency << "'";
311 void ConfigString::writeXML(FTextStream& t)
314 switch (m_widgetType)
316 case String: format="string"; break;
317 case File: format="file"; break;
318 case Dir: format="dir"; break;
320 t << " <option type='string' "
321 "id='" << convertToXML(name()) << "' "
322 "format='" << format << "' "
323 "docs='\n" << convertToXML(docs()) << "' "
324 "defval='" << convertToXML(m_defValue) << "'";
325 if (!m_dependency.isEmpty()) t << " depends='" << m_dependency << "'";
329 void ConfigEnum::writeXML(FTextStream &t)
331 t << " <option type='enum' "
332 "id='" << convertToXML(name()) << "' "
333 "defval='" << convertToXML(m_defValue) << "' "
334 "docs='\n" << convertToXML(docs()) << "'";
335 if (!m_dependency.isEmpty()) t << " depends='" << m_dependency << "'";
338 char *enumVal = m_valueRange.first();
341 t << " <value name='" << convertToXML(enumVal) << "'/>" << endl;
342 enumVal = m_valueRange.next();
345 t << " </option>" << endl;
348 void ConfigList::writeXML(FTextStream &t)
351 switch (m_widgetType)
353 case String: format="string"; break;
354 case File: format="file"; break;
355 case Dir: format="dir"; break;
356 case FileAndDir: format="filedir"; break;
358 t << " <option type='list' "
359 "id='" << convertToXML(name()) << "' "
360 "format='" << format << "' "
361 "docs='\n" << convertToXML(docs()) << "'";
362 if (!m_dependency.isEmpty()) t << " depends='" << m_dependency << "'";
364 char *enumVal = m_value.first();
367 t << " <value name='" << convertToXML(enumVal) << "'/>" << endl;
368 enumVal = m_value.next();
371 t << " </option>" << endl;
374 void ConfigObsolete::writeXML(FTextStream &t)
376 t << " <option type='obsolete' "
377 "id='" << convertToXML(name()) << "'/>" << endl;
381 /* -----------------------------------------------------------------
386 struct ConfigFileState
390 YY_BUFFER_STATE oldState;
391 YY_BUFFER_STATE newState;
395 static const char *inputString;
396 static int inputPosition;
398 static QCString yyFileName;
399 static QCString tmpString;
400 static QCString *s=0;
402 static QStrList *l=0;
403 static int lastState;
404 static QCString elemStr;
405 static QCString includeName;
406 static QStrList includePathList;
407 static QStack<ConfigFileState> includeStack;
408 static int includeDepth;
410 static QCString tabSizeString;
411 static QCString maxInitLinesString;
412 static QCString colsInAlphaIndexString;
413 static QCString enumValuesPerLineString;
414 static QCString treeViewWidthString;
415 static QCString maxDotGraphWidthString;
416 static QCString maxDotGraphHeightString;
417 static QCString encoding;
419 static Config *config;
421 /* -----------------------------------------------------------------
424 #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size);
426 static int yyread(char *buf,int max_size)
429 if (includeStack.isEmpty())
432 if (inputString==0) return c;
433 while( c < max_size && inputString[inputPosition] )
435 *buf = inputString[inputPosition++] ;
442 //assert(includeStack.current()->newState==YY_CURRENT_BUFFER);
443 return (int)fread(buf,1,max_size,includeStack.current()->filePtr);
448 static QCString configStringRecode(
450 const char *fromEncoding,
451 const char *toEncoding)
453 QCString inputEncoding = fromEncoding;
454 QCString outputEncoding = toEncoding;
455 if (inputEncoding.isEmpty() || outputEncoding.isEmpty() || inputEncoding==outputEncoding) return str;
456 int inputSize=str.length();
457 int outputSize=inputSize*4+1;
458 QCString output(outputSize);
459 void *cd = portable_iconv_open(outputEncoding,inputEncoding);
460 if (cd==(void *)(-1))
462 fprintf(stderr,"error: unsupported character conversion: '%s'->'%s'\n",
463 inputEncoding.data(),outputEncoding.data());
466 size_t iLeft=(size_t)inputSize;
467 size_t oLeft=(size_t)outputSize;
468 char *inputPtr = str.data();
469 char *outputPtr = output.data();
470 if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft))
472 outputSize-=(int)oLeft;
473 output.resize(outputSize+1);
474 output.at(outputSize)='\0';
475 //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,srcBuf.data());
479 fprintf(stderr,"error: failed to translate characters from %s to %s: %s\n",
480 inputEncoding.data(),outputEncoding.data(),strerror(errno));
483 portable_iconv_close(cd);
487 static void checkEncoding()
489 ConfigString *option = (ConfigString*)config->get("DOXYFILE_ENCODING");
490 encoding = *option->valueRef();
493 static FILE *tryPath(const char *path,const char *fileName)
495 QCString absName=(path ? (QCString)path+"/"+fileName : (QCString)fileName);
496 QFileInfo fi(absName);
497 if (fi.exists() && fi.isFile())
499 FILE *f=portable_fopen(absName,"r");
500 if (!f) config_err("error: could not open file %s for reading\n",absName.data());
506 static void substEnvVarsInStrList(QStrList &sl);
507 static void substEnvVarsInString(QCString &s);
509 static bool isAbsolute(const char * fileName)
512 if (isalpha (fileName [0]) && fileName[1] == ':')
515 char const fst = fileName [0];
526 static FILE *findFile(const char *fileName)
528 if(isAbsolute(fileName))
529 return tryPath(NULL, fileName);
530 substEnvVarsInStrList(includePathList);
531 char *s=includePathList.first();
532 while (s) // try each of the include paths
534 FILE *f = tryPath(s,fileName);
536 s=includePathList.next();
538 // try cwd if includePathList fails
539 return tryPath(".",fileName);
542 static void readIncludeFile(const char *incName)
544 if (includeDepth==MAX_INCLUDE_DEPTH) {
545 config_err("error: maximum include depth (%d) reached, %s is not included. Aborting...\n",
546 MAX_INCLUDE_DEPTH,incName);
550 QCString inc = incName;
551 substEnvVarsInString(inc);
552 inc = inc.stripWhiteSpace();
553 uint incLen = inc.length();
554 if (inc.at(0)=='"' && inc.at(incLen-1)=='"') // strip quotes
556 inc=inc.mid(1,incLen-2);
561 if ((f=findFile(inc))) // see if the include file can be found
565 for (i=0;i<includeStack.count();i++) msg(" ");
566 msg("@INCLUDE = %s: parsing...\n",inc.data());
569 // store the state of the old file
570 ConfigFileState *fs=new ConfigFileState;
571 fs->oldState=YY_CURRENT_BUFFER;
573 fs->fileName=yyFileName;
575 // push the state on the stack
576 includeStack.push(fs);
577 // set the scanner to the include file
578 yy_switch_to_buffer(yy_create_buffer(f, YY_BUF_SIZE));
579 fs->newState=YY_CURRENT_BUFFER;
585 config_err("error: @INCLUDE = %s: not found!\n",inc.data());
609 <Start,GetString,GetStrList,GetBool,SkipInvalid>"#" { BEGIN(SkipComment); }
610 <Start>[a-z_A-Z][a-z_A-Z0-9]*[ \t]*"=" { QCString cmd=yytext;
611 cmd=cmd.left(cmd.length()-1).stripWhiteSpace();
612 ConfigOption *option = config->get(cmd);
613 if (option==0) // oops not known
615 config_err("warning: ignoring unsupported tag `%s' at line %d, file %s\n",
616 yytext,yyLineNr,yyFileName.data());
621 option->setEncoding(encoding);
622 switch(option->kind())
624 case ConfigOption::O_Info:
625 // shouldn't get here!
628 case ConfigOption::O_List:
629 l = ((ConfigList *)option)->valueRef();
634 case ConfigOption::O_Enum:
635 s = ((ConfigEnum *)option)->valueRef();
639 case ConfigOption::O_String:
640 s = ((ConfigString *)option)->valueRef();
644 case ConfigOption::O_Int:
645 s = ((ConfigInt *)option)->valueStringRef();
649 case ConfigOption::O_Bool:
650 s = ((ConfigBool *)option)->valueStringRef();
654 case ConfigOption::O_Obsolete:
655 config_err("warning: Tag `%s' at line %d of file %s has become obsolete.\n"
656 "To avoid this warning please remove this line from your configuration "
657 "file or upgrade it using \"doxygen -u\"\n", cmd.data(),yyLineNr,yyFileName.data());
663 <Start>[a-z_A-Z][a-z_A-Z0-9]*[ \t]*"+=" { QCString cmd=yytext;
664 cmd=cmd.left(cmd.length()-2).stripWhiteSpace();
665 ConfigOption *option = config->get(cmd);
666 if (option==0) // oops not known
668 config_err("warning: ignoring unsupported tag `%s' at line %d, file %s\n",
669 yytext,yyLineNr,yyFileName.data());
674 switch(option->kind())
676 case ConfigOption::O_Info:
677 // shouldn't get here!
680 case ConfigOption::O_List:
681 l = ((ConfigList *)option)->valueRef();
685 case ConfigOption::O_Enum:
686 case ConfigOption::O_String:
687 case ConfigOption::O_Int:
688 case ConfigOption::O_Bool:
689 config_err("warning: operator += not supported for `%s'. Ignoring line at line %d, file %s\n",
690 yytext,yyLineNr,yyFileName.data());
693 case ConfigOption::O_Obsolete:
694 config_err("warning: Tag `%s' at line %d of file %s has become obsolete.\n"
695 "To avoid this warning please update your configuration "
696 "file using \"doxygen -u\"\n", cmd.data(),yyLineNr,yyFileName.data());
702 <Start>"@INCLUDE_PATH"[ \t]*"=" { BEGIN(GetStrList); l=&includePathList; l->clear(); elemStr=""; }
703 /* include a config file */
704 <Start>"@INCLUDE"[ \t]*"=" { BEGIN(Include);}
705 <Include>([^ \"\t\r\n]+)|("\""[^\n\"]+"\"") {
706 readIncludeFile(configStringRecode(yytext,encoding,"UTF-8"));
710 //printf("End of include file\n");
711 //printf("Include stack depth=%d\n",g_includeStack.count());
712 if (includeStack.isEmpty())
714 //printf("Terminating scanner!\n");
719 ConfigFileState *fs=includeStack.pop();
721 YY_BUFFER_STATE oldBuf = YY_CURRENT_BUFFER;
722 yy_switch_to_buffer( fs->oldState );
723 yy_delete_buffer( oldBuf );
725 yyFileName=fs->fileName;
731 <Start>[a-z_A-Z0-9]+ { config_err("warning: ignoring unknown tag `%s' at line %d, file %s\n",yytext,yyLineNr,yyFileName.data()); }
732 <GetString,GetBool,SkipInvalid>\n { yyLineNr++; BEGIN(Start); }
735 if (!elemStr.isEmpty())
737 //printf("elemStr1=`%s'\n",elemStr.data());
743 if (!elemStr.isEmpty())
745 //printf("elemStr2=`%s'\n",elemStr.data());
750 <GetString>[^ \"\t\r\n]+ { (*s)+=configStringRecode(yytext,encoding,"UTF-8");
753 <GetString,GetStrList,SkipInvalid>"\"" { lastState=YY_START;
754 BEGIN(GetQuotedString);
757 <GetQuotedString>"\""|"\n" {
758 // we add a bogus space to signal that the string was quoted. This space will be stripped later on.
760 //printf("Quoted String = `%s'\n",tmpString.data());
761 if (lastState==GetString)
763 (*s)+=configStringRecode(tmpString,encoding,"UTF-8");
768 elemStr+=configStringRecode(tmpString,encoding,"UTF-8");
772 config_err("warning: Missing end quote (\") on line %d, file %s\n",yyLineNr,yyFileName.data());
777 <GetQuotedString>"\\\"" {
780 <GetQuotedString>. { tmpString+=*yytext; }
784 if (bs=="YES" || bs=="1")
786 else if (bs=="NO" || bs=="0")
791 config_warn("warning: Invalid value `%s' for "
792 "boolean tag in line %d, file %s; use YES or NO\n",
793 bs.data(),yyLineNr,yyFileName.data());
796 <GetStrList>[^ \#\"\t\r\n]+ {
797 elemStr+=configStringRecode(yytext,encoding,"UTF-8");
799 <SkipComment>\n { yyLineNr++; BEGIN(Start); }
800 <SkipComment>\\[ \r\t]*\n { yyLineNr++; BEGIN(Start); }
801 <*>\\[ \r\t]*\n { yyLineNr++; }
803 <*>\n { yyLineNr++ ; }
807 /*@ ----------------------------------------------------------------------------
810 void Config::writeTemplate(FTextStream &t,bool sl,bool upd)
812 t << "# Doxyfile " << versionString << endl << endl;
815 t << "# This file describes the settings to be used by the documentation system\n";
816 t << "# doxygen (www.doxygen.org) for a project.\n";
818 t << "# All text after a hash (#) is considered a comment and will be ignored.\n";
819 t << "# The format is:\n";
820 t << "# TAG = value [value, ...]\n";
821 t << "# For lists items can also be appended using:\n";
822 t << "# TAG += value [value, ...]\n";
823 t << "# Values that contain spaces should be placed between quotes (\" \").\n";
825 ConfigOption *option = m_options->first();
828 option->writeTemplate(t,sl,upd);
829 option = m_options->next();
833 void Config::writeXML(FTextStream &t)
835 t << "<doxygenconfig>" << endl;
837 ConfigOption *option = m_options->first();
840 if (option->kind()==ConfigOption::O_Info)
842 if (!first) t << " </group>" << endl;
843 t << " <group name='" << option->name() << "' "
844 "docs='" << option->docs() << "'>" << endl;
851 option = m_options->next();
853 option = m_obsolete->first();
857 option = m_obsolete->next();
859 if (!first) t << " </group>" << endl;
860 t << "</doxygenconfig>" << endl;
863 void Config::convertStrToVal()
865 ConfigOption *option = m_options->first();
868 option->convertStrToVal();
869 option = m_options->next();
873 static void substEnvVarsInString(QCString &s)
875 static QRegExp re("\\$\\([a-z_A-Z0-9.-]+\\)");
876 static QRegExp re2("\\$\\([a-z_A-Z0-9.-]+\\([a-z_A-Z0-9.-]+\\)\\)"); // For e.g. PROGRAMFILES(X86)
877 if (s.isEmpty()) return;
880 //printf("substEnvVarInString(%s) start\n",s.data());
881 while ((i=re.match(s,p,&l))!=-1 || (i=re2.match(s,p,&l))!=-1)
883 //printf("Found environment var s.mid(%d,%d)=`%s'\n",i+2,l-3,s.mid(i+2,l-3).data());
884 QCString env=portable_getenv(s.mid(i+2,l-3));
885 substEnvVarsInString(env); // recursively expand variables if needed.
886 s = s.left(i)+env+s.right(s.length()-i-l);
887 p=i+env.length(); // next time start at the end of the expanded string
889 s=s.stripWhiteSpace(); // to strip the bogus space that was added when an argument
891 //printf("substEnvVarInString(%s) end\n",s.data());
894 static void substEnvVarsInStrList(QStrList &sl)
896 char *s = sl.first();
900 // an argument with quotes will have an extra space at the end, so wasQuoted will be TRUE.
901 bool wasQuoted = (result.find(' ')!=-1) || (result.find('\t')!=-1);
902 // here we strip the quote again
903 substEnvVarsInString(result);
905 //printf("Result %s was quoted=%d\n",result.data(),wasQuoted);
907 if (!wasQuoted) /* as a result of the expansion, a single string
908 may have expanded into a list, which we'll
909 add to sl. If the original string already
910 contained multiple elements no further
911 splitting is done to allow quoted items with spaces! */
913 int l=result.length();
916 // search for a "word"
920 // skip until start of new word
921 while (i<l && ((c=result.at(i))==' ' || c=='\t')) i++;
922 p=i; // p marks the start index of the word
923 // skip until end of a word
924 while (i<l && ((c=result.at(i))!=' ' && c!='\t' && c!='"')) i++;
925 if (i<l) // not at the end of the string
927 if (c=='"') // word within quotes
933 if (c=='"') // end quote
935 // replace the string in the list and go to the next item.
936 sl.insert(sl.at(),result.mid(p,i-p)); // insert new item before current item.
937 sl.next(); // current item is now the old item
941 else if (c=='\\') // skip escaped stuff
947 else if (c==' ' || c=='\t') // separator
949 // replace the string in the list and go to the next item.
950 sl.insert(sl.at(),result.mid(p,i-p)); // insert new item before current item.
951 sl.next(); // current item is now the old item
956 if (p!=l) // add the leftover as a string
958 // replace the string in the list and go to the next item.
959 sl.insert(sl.at(),result.right(l-p)); // insert new item before current item.
960 sl.next(); // current item is now the old item
963 else // just goto the next element in the list
965 sl.insert(sl.at(),result);
968 // remove the old unexpanded string from the list
970 sl.remove(); // current item index changes if the last element is removed.
971 if (sl.at()==i) // not last item
973 else // just removed last item
978 void ConfigString::substEnvVars()
980 substEnvVarsInString(m_value);
983 void ConfigList::substEnvVars()
985 substEnvVarsInStrList(m_value);
988 void ConfigBool::substEnvVars()
990 substEnvVarsInString(m_valueString);
993 void ConfigInt::substEnvVars()
995 substEnvVarsInString(m_valueString);
998 void ConfigEnum::substEnvVars()
1000 substEnvVarsInString(m_value);
1003 void Config::substituteEnvironmentVars()
1005 ConfigOption *option = m_options->first();
1008 option->substEnvVars();
1009 option = m_options->next();
1013 static void cleanUpPaths(QStrList &str)
1015 char *sfp = str.first();
1018 register char *p = sfp;
1024 if (c=='\\') *p='/';
1028 QCString path = sfp;
1029 if ((path.at(0)!='/' && (path.length()<=2 || path.at(1)!=':')) ||
1030 path.at(path.length()-1)!='/'
1034 if (fi.exists() && fi.isDir())
1038 if (str.at()==i) // did not remove last item
1039 str.insert(i,fi.absFilePath().utf8()+"/");
1041 str.append(fi.absFilePath().utf8()+"/");
1048 void Config::check()
1050 //if (!projectName.isEmpty())
1052 // projectName[0]=toupper(projectName[0]);
1055 QCString &warnFormat = Config_getString("WARN_FORMAT");
1056 if (warnFormat.stripWhiteSpace().isEmpty())
1058 warnFormat="$file:$line $text";
1062 if (warnFormat.find("$file")==-1)
1064 config_err("warning: warning format does not contain a $file tag!\n");
1066 if (warnFormat.find("$line")==-1)
1068 config_err("warning: warning format does not contain a $line tag!\n");
1070 if (warnFormat.find("$text")==-1)
1072 config_err("warning: warning format foes not contain a $text tag!\n");
1076 QCString &manExtension = Config_getString("MAN_EXTENSION");
1078 // set default man page extension if non is given by the user
1079 if (manExtension.isEmpty())
1084 QCString &paperType = Config_getEnum("PAPER_TYPE");
1085 paperType=paperType.lower().stripWhiteSpace();
1086 if (paperType.isEmpty())
1090 if (paperType!="a4" && paperType!="a4wide" && paperType!="letter" &&
1091 paperType!="legal" && paperType!="executive")
1093 config_err("error: Unknown page type specified");
1096 QCString &outputLanguage=Config_getEnum("OUTPUT_LANGUAGE");
1097 outputLanguage=outputLanguage.stripWhiteSpace();
1098 if (outputLanguage.isEmpty())
1100 outputLanguage = "English";
1103 QCString &htmlFileExtension=Config_getString("HTML_FILE_EXTENSION");
1104 htmlFileExtension=htmlFileExtension.stripWhiteSpace();
1105 if (htmlFileExtension.isEmpty())
1107 htmlFileExtension = ".html";
1110 // expand the relative stripFromPath values
1111 QStrList &stripFromPath = Config_getList("STRIP_FROM_PATH");
1112 char *sfp = stripFromPath.first();
1113 if (sfp==0) // by default use the current path
1115 stripFromPath.append(QDir::currentDirPath().utf8()+"/");
1119 cleanUpPaths(stripFromPath);
1122 // expand the relative stripFromPath values
1123 QStrList &stripFromIncPath = Config_getList("STRIP_FROM_INC_PATH");
1124 cleanUpPaths(stripFromIncPath);
1126 // Test to see if HTML header is valid
1127 QCString &headerFile = Config_getString("HTML_HEADER");
1128 if (!headerFile.isEmpty())
1130 QFileInfo fi(headerFile);
1133 config_err("error: tag HTML_HEADER: header file `%s' "
1134 "does not exist\n",headerFile.data());
1138 // Test to see if HTML footer is valid
1139 QCString &footerFile = Config_getString("HTML_FOOTER");
1140 if (!footerFile.isEmpty())
1142 QFileInfo fi(footerFile);
1145 config_err("error: tag HTML_FOOTER: footer file `%s' "
1146 "does not exist\n",footerFile.data());
1150 // Test to see if LaTeX header is valid
1151 QCString &latexHeaderFile = Config_getString("LATEX_HEADER");
1152 if (!latexHeaderFile.isEmpty())
1154 QFileInfo fi(latexHeaderFile);
1157 config_err("error: tag LATEX_HEADER: header file `%s' "
1158 "does not exist\n",latexHeaderFile.data());
1162 // check include path
1163 QStrList &includePath = Config_getList("INCLUDE_PATH");
1164 char *s=includePath.first();
1168 if (!fi.exists()) config_err("warning: tag INCLUDE_PATH: include path `%s' "
1169 "does not exist\n",s);
1170 s=includePath.next();
1174 QStrList &aliasList = Config_getList("ALIASES");
1175 s=aliasList.first();
1178 QRegExp re1("[a-z_A-Z][a-z_A-Z0-9]*[ \t]*="); // alias without argument
1179 QRegExp re2("[a-z_A-Z][a-z_A-Z0-9]*{[0-9]*}[ \t]*="); // alias with argument
1181 alias=alias.stripWhiteSpace();
1182 if (alias.find(re1)!=0 && alias.find(re2)!=0)
1184 config_err("Illegal alias format `%s'. Use \"name=value\" or \"name(n)=value\", where n is the number of arguments\n",
1190 // check if GENERATE_TREEVIEW and GENERATE_HTMLHELP are both enabled
1191 if (Config_getBool("GENERATE_TREEVIEW") && Config_getBool("GENERATE_HTMLHELP"))
1193 config_err("When enabling GENERATE_HTMLHELP the tree view (GENERATE_TREEVIEW) should be disabled. I'll do it for you.\n");
1194 Config_getBool("GENERATE_TREEVIEW")=FALSE;
1196 if (Config_getBool("SEARCHENGINE") && Config_getBool("GENERATE_HTMLHELP"))
1198 config_err("When enabling GENERATE_HTMLHELP the search engine (SEARCHENGINE) should be disabled. I'll do it for you.\n");
1199 Config_getBool("SEARCHENGINE")=FALSE;
1202 // check if SEPARATE_MEMBER_PAGES and INLINE_GROUPED_CLASSES are both enabled
1203 if (Config_getBool("SEPARATE_MEMBER_PAGES") && Config_getBool("INLINE_GROUPED_CLASSES"))
1205 config_err("When enabling INLINE_GROUPED_CLASSES the SEPARATE_MEMBER_PAGES option should be disabled. I'll do it for you.\n");
1206 Config_getBool("SEPARATE_MEMBER_PAGES")=FALSE;
1209 // check dot image format
1210 QCString &dotImageFormat=Config_getEnum("DOT_IMAGE_FORMAT");
1211 dotImageFormat=dotImageFormat.stripWhiteSpace();
1212 if (dotImageFormat.isEmpty())
1214 dotImageFormat = "png";
1216 //else if (dotImageFormat!="gif" && dotImageFormat!="png" && dotImageFormat!="jpg")
1218 // config_err("Invalid value for DOT_IMAGE_FORMAT: `%s'. Using the default.\n",dotImageFormat.data());
1219 // dotImageFormat = "png";
1224 QCString &dotPath = Config_getString("DOT_PATH");
1225 if (!dotPath.isEmpty())
1227 QFileInfo fi(dotPath);
1228 if (fi.exists() && fi.isFile()) // user specified path + exec
1230 dotPath=fi.dirPath(TRUE).utf8()+"/";
1234 QFileInfo dp(dotPath+"/dot"+portable_commandExtension());
1235 if (!dp.exists() || !dp.isFile())
1237 config_err("warning: the dot tool could not be found at %s\n",dotPath.data());
1242 dotPath=dp.dirPath(TRUE).utf8()+"/";
1245 #if defined(_WIN32) // convert slashes
1246 uint i=0,l=dotPath.length();
1247 for (i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
1250 else // make sure the string is empty but not null!
1255 // check mscgen path
1256 QCString &mscgenPath = Config_getString("MSCGEN_PATH");
1257 if (!mscgenPath.isEmpty())
1259 QFileInfo dp(mscgenPath+"/mscgen"+portable_commandExtension());
1260 if (!dp.exists() || !dp.isFile())
1262 config_err("warning: the mscgen tool could not be found at %s\n",mscgenPath.data());
1267 mscgenPath=dp.dirPath(TRUE).utf8()+"/";
1268 #if defined(_WIN32) // convert slashes
1269 uint i=0,l=mscgenPath.length();
1270 for (i=0;i<l;i++) if (mscgenPath.at(i)=='/') mscgenPath.at(i)='\\';
1274 else // make sure the string is empty but not null!
1281 QStrList &inputSources=Config_getList("INPUT");
1282 if (inputSources.count()==0)
1284 // use current dir as the default
1285 inputSources.append(QDir::currentDirPath().utf8());
1289 s=inputSources.first();
1295 config_err("warning: tag INPUT: input source `%s' does not exist\n",s);
1297 s=inputSources.next();
1301 // add default pattern if needed
1302 QStrList &filePatternList = Config_getList("FILE_PATTERNS");
1303 if (filePatternList.isEmpty())
1305 filePatternList.append("*.c");
1306 filePatternList.append("*.cc");
1307 filePatternList.append("*.cxx");
1308 filePatternList.append("*.cpp");
1309 filePatternList.append("*.c++");
1310 filePatternList.append("*.d");
1311 filePatternList.append("*.java");
1312 filePatternList.append("*.ii");
1313 filePatternList.append("*.ixx");
1314 filePatternList.append("*.ipp");
1315 filePatternList.append("*.i++");
1316 filePatternList.append("*.inl");
1317 filePatternList.append("*.h");
1318 filePatternList.append("*.hh");
1319 filePatternList.append("*.hxx");
1320 filePatternList.append("*.hpp");
1321 filePatternList.append("*.h++");
1322 filePatternList.append("*.idl");
1323 filePatternList.append("*.odl");
1324 filePatternList.append("*.cs");
1325 filePatternList.append("*.php");
1326 filePatternList.append("*.php3");
1327 filePatternList.append("*.inc");
1328 filePatternList.append("*.m");
1329 filePatternList.append("*.mm");
1330 filePatternList.append("*.dox");
1331 filePatternList.append("*.py");
1332 filePatternList.append("*.f90");
1333 filePatternList.append("*.f");
1334 filePatternList.append("*.for");
1335 filePatternList.append("*.vhd");
1336 filePatternList.append("*.vhdl");
1337 filePatternList.append("*.tcl");
1338 filePatternList.append("*.md");
1339 filePatternList.append("*.markdown");
1340 if (portable_fileSystemIsCaseSensitive())
1342 // unix => case sensitive match => also include useful uppercase versions
1343 filePatternList.append("*.C");
1344 filePatternList.append("*.CC");
1345 filePatternList.append("*.C++");
1346 filePatternList.append("*.II");
1347 filePatternList.append("*.I++");
1348 filePatternList.append("*.H");
1349 filePatternList.append("*.HH");
1350 filePatternList.append("*.H++");
1351 filePatternList.append("*.CS");
1352 filePatternList.append("*.PHP");
1353 filePatternList.append("*.PHP3");
1354 filePatternList.append("*.M");
1355 filePatternList.append("*.MM");
1356 filePatternList.append("*.PY");
1357 filePatternList.append("*.F90");
1358 filePatternList.append("*.F");
1359 filePatternList.append("*.VHD");
1360 filePatternList.append("*.VHDL");
1361 filePatternList.append("*.TCL");
1362 filePatternList.append("*.MD");
1363 filePatternList.append("*.MARKDOWN");
1367 // add default pattern if needed
1368 QStrList &examplePatternList = Config_getList("EXAMPLE_PATTERNS");
1369 if (examplePatternList.isEmpty())
1371 examplePatternList.append("*");
1374 // if no output format is enabled, warn the user
1375 if (!Config_getBool("GENERATE_HTML") &&
1376 !Config_getBool("GENERATE_LATEX") &&
1377 !Config_getBool("GENERATE_MAN") &&
1378 !Config_getBool("GENERATE_RTF") &&
1379 !Config_getBool("GENERATE_XML") &&
1380 !Config_getBool("GENERATE_PERLMOD") &&
1381 !Config_getBool("GENERATE_RTF") &&
1382 !Config_getBool("GENERATE_AUTOGEN_DEF") &&
1383 Config_getString("GENERATE_TAGFILE").isEmpty()
1386 config_err("warning: No output formats selected! Set at least one of the main GENERATE_* options to YES.\n");
1389 // check HTMLHELP creation requirements
1390 if (!Config_getBool("GENERATE_HTML") &&
1391 Config_getBool("GENERATE_HTMLHELP"))
1393 config_err("warning: GENERATE_HTMLHELP=YES requires GENERATE_HTML=YES.\n");
1396 // check QHP creation requirements
1397 if (Config_getBool("GENERATE_QHP"))
1399 if (Config_getString("QHP_NAMESPACE").isEmpty())
1401 config_err("error: GENERATE_QHP=YES requires QHP_NAMESPACE to be set. Using 'org.doxygen.doc' as default!.\n");
1402 Config_getString("QHP_NAMESPACE")="org.doxygen.doc";
1405 if (Config_getString("QHP_VIRTUAL_FOLDER").isEmpty())
1407 config_err("error: GENERATE_QHP=YES requires QHP_VIRTUAL_FOLDER to be set. Using 'doc' as default!\n");
1408 Config_getString("QHP_VIRTUAL_FOLDER")="doc";
1412 if (Config_getBool("OPTIMIZE_OUTPUT_JAVA") && Config_getBool("INLINE_INFO"))
1414 // don't show inline info for Java output, since Java has no inline
1416 Config_getBool("INLINE_INFO")=FALSE;
1419 int &depth = Config_getInt("MAX_DOT_GRAPH_DEPTH");
1425 int &hue = Config_getInt("HTML_COLORSTYLE_HUE");
1435 int &sat = Config_getInt("HTML_COLORSTYLE_SAT");
1444 int &gamma = Config_getInt("HTML_COLORSTYLE_GAMMA");
1455 // add default words if needed
1456 QStrList &annotationFromBrief = Config_getList("ABBREVIATE_BRIEF");
1457 if (annotationFromBrief.isEmpty())
1459 annotationFromBrief.append("The $name class");
1460 annotationFromBrief.append("The $name widget");
1461 annotationFromBrief.append("The $name file");
1462 annotationFromBrief.append("is");
1463 annotationFromBrief.append("provides");
1464 annotationFromBrief.append("specifies");
1465 annotationFromBrief.append("contains");
1466 annotationFromBrief.append("represents");
1467 annotationFromBrief.append("a");
1468 annotationFromBrief.append("an");
1469 annotationFromBrief.append("the");
1472 // some default settings for vhdl
1473 if (Config_getBool("OPTIMIZE_OUTPUT_VHDL") &&
1474 (Config_getBool("INLINE_INHERITED_MEMB") ||
1475 Config_getBool("INHERIT_DOCS") ||
1476 !Config_getBool("HIDE_SCOPE_NAMES") ||
1477 !Config_getBool("EXTRACT_PRIVATE") ||
1478 !Config_getBool("EXTRACT_PACKAGE")
1482 bool b1 = Config_getBool("INLINE_INHERITED_MEMB");
1483 bool b2 = Config_getBool("INHERIT_DOCS");
1484 bool b3 = Config_getBool("HIDE_SCOPE_NAMES");
1485 bool b4 = Config_getBool("EXTRACT_PRIVATE");
1486 bool b5 = Config_getBool("SKIP_FUNCTION_MACROS");
1487 bool b6 = Config_getBool("EXTRACT_PACKAGE");
1488 const char *s1,*s2,*s3,*s4,*s5,*s6;
1489 if (b1) s1=" INLINE_INHERITED_MEMB = NO (was YES)\n"; else s1="";
1490 if (b2) s2=" INHERIT_DOCS = NO (was YES)\n"; else s2="";
1491 if (!b3) s3=" HIDE_SCOPE_NAMES = YES (was NO)\n"; else s3="";
1492 if (!b4) s4=" EXTRACT_PRIVATE = YES (was NO)\n"; else s4="";
1493 if (b5) s5=" ENABLE_PREPROCESSING = NO (was YES)\n"; else s5="";
1494 if (!b6) s6=" EXTRACT_PACKAGE = YES (was NO)\n"; else s6="";
1497 config_err("warning: enabling OPTIMIZE_OUTPUT_VHDL assumes the following settings:\n"
1498 "%s%s%s%s%s%s",s1,s2,s3,s4,s5,s6
1501 Config_getBool("INLINE_INHERITED_MEMB") = FALSE;
1502 Config_getBool("INHERIT_DOCS") = FALSE;
1503 Config_getBool("HIDE_SCOPE_NAMES") = TRUE;
1504 Config_getBool("EXTRACT_PRIVATE") = TRUE;
1505 Config_getBool("ENABLE_PREPROCESSING") = FALSE;
1506 Config_getBool("EXTRACT_PACKAGE") = TRUE;
1513 ConfigOption *option = m_options->first();
1517 option = m_options->next();
1521 void Config::create()
1523 if (m_initialized) return;
1524 m_initialized = TRUE;
1525 addConfigOptions(this);
1528 static QCString configFileToString(const char *name)
1530 if (name==0 || name[0]==0) return 0;
1533 bool fileOpened=FALSE;
1534 if (name[0]=='-' && name[1]==0) // read from stdin
1536 fileOpened=f.open(IO_ReadOnly,stdin);
1539 const int bSize=4096;
1540 QCString contents(bSize);
1543 while ((size=f.readBlock(contents.data()+totalSize,bSize))==bSize)
1546 contents.resize(totalSize+bSize);
1549 contents.resize(totalSize);
1550 contents.at(totalSize-2)='\n'; // to help the scanner
1551 contents.at(totalSize-1)='\0';
1555 else // read from file
1558 if (!fi.exists() || !fi.isFile())
1560 config_err("error: file `%s' not found\n",name);
1564 fileOpened=f.open(IO_ReadOnly);
1568 QCString contents(fsize+2);
1569 f.readBlock(contents.data(),fsize);
1571 if (fsize==0 || contents[fsize-1]=='\n')
1572 contents[fsize]='\0';
1574 contents[fsize]='\n'; // to help the scanner
1575 contents[fsize+1]='\0';
1581 config_err("error: cannot open file `%s' for reading\n",name);
1586 bool Config::parseString(const char *fn,const char *str)
1588 config = Config::instance();
1593 includeStack.setAutoDelete(TRUE);
1594 includeStack.clear();
1596 configYYrestart( configYYin );
1603 bool Config::parse(const char *fn)
1606 return parseString(fn,configFileToString(fn));
1609 extern "C" { // some bogus code to keep the compiler happy
1610 //int configYYwrap() { return 1 ; }