1 /******************************************************************************
3 * Copyright (C) 1997-2020 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"
35 #include "configimpl.h"
41 #include "configoptions.h"
44 #include "textstream.h"
47 #define YY_NO_UNISTD_H 1
49 #define USE_STATE2STRING 0
52 static const char *stateToString(int state);
55 static const char *warning_str = "warning: ";
56 static const char *error_str = "error: ";
58 void config_err(const char *fmt, ...)
62 vfprintf(stderr, qPrint(QCString(error_str) + fmt), args);
65 void config_term(const char *fmt, ...)
69 vfprintf(stderr, qPrint(QCString(error_str) + fmt), args);
71 fprintf(stderr, "%s\n", "Exiting...");
75 void config_warn(const char *fmt, ...)
79 vfprintf(stderr, qPrint(QCString(warning_str) + fmt), args);
83 static QCString configStringRecode(
85 const QCString &fromEncoding,
86 const QCString &toEncoding);
88 #define MAX_INCLUDE_DEPTH 10
89 #define YY_NEVER_INTERACTIVE 1
91 /* -----------------------------------------------------------------
93 static QCString convertToComment(const QCString &s, const QCString &u)
95 //printf("convertToComment(%s)=%s\n",qPrint(s),qPrint(u));
99 QCString tmp=s.stripWhiteSpace();
100 const char *p=tmp.data();
126 if (!result.isEmpty()) result+='\n';
132 void ConfigOption::writeBoolValue(TextStream &t,bool v,bool initSpace)
134 if (initSpace) t << " ";
135 if (v) t << "YES"; else t << "NO";
138 void ConfigOption::writeIntValue(TextStream &t,int i,bool initSpace)
140 if (initSpace) t << " ";
144 void ConfigOption::writeStringValue(TextStream &t,const QCString &s,bool initSpace)
147 bool needsEscaping=FALSE;
148 // convert the string back to it original g_encoding
149 QCString se = configStringRecode(s,"UTF-8",m_encoding);
150 if (se.isEmpty()) return;
151 const char *p=se.data();
154 if (initSpace) t << " ";
155 while ((c=*p++)!=0 && !needsEscaping)
156 needsEscaping = (c==' ' || c== ',' || c=='\n' || c=='\t' || c=='"' || c=='#');
163 if (*p==' ' && *(p+1)=='\0') break; // skip inserted space at the end
164 if (*p=='"') t << "\\"; // escape quotes
176 void ConfigOption::writeStringList(TextStream &t,const StringVector &l)
179 for (const auto &p : l)
181 if (!first) t << " \\\n";
182 QCString s=p.c_str();
185 writeStringValue(t,s);
190 /* -----------------------------------------------------------------
193 ConfigImpl *ConfigImpl::m_instance = 0;
195 void ConfigInt::convertStrToVal()
197 if (!m_valueString.isEmpty())
200 int val = m_valueString.toInt(&ok);
201 if (!ok || val<m_minVal || val>m_maxVal)
203 config_warn("argument '%s' for option %s is not a valid number in the range [%d..%d]!\n"
204 "Using the default: %d!\n",qPrint(m_valueString),qPrint(m_name),m_minVal,m_maxVal,m_value);
213 static bool convertStringToBool(const QCString &str,bool &isValid)
216 QCString val = str.stripWhiteSpace().lower();
219 if (val=="yes" || val=="true" || val=="1" || val=="all")
224 else if (val=="no" || val=="false" || val=="0" || val=="none")
233 void ConfigBool::convertStrToVal()
235 if (!m_valueString.stripWhiteSpace().isEmpty())
238 bool b = convertStringToBool(m_valueString,isValid);
245 config_warn("argument '%s' for option %s is not a valid boolean value\n"
246 "Using the default: %s!\n",qPrint(m_valueString),qPrint(m_name),m_value?"YES":"NO");
251 void ConfigEnum::convertStrToVal()
253 if (m_value.isEmpty())
255 m_value = m_defValue;
258 QCString val = m_value.stripWhiteSpace().lower();
259 for (const auto &s : m_valueRange)
261 if (s.lower() == val)
268 config_warn("argument '%s' for option %s is not a valid enum value\n"
269 "Using the default: %s!\n",qPrint(m_value),qPrint(m_name),qPrint(m_defValue));
270 m_value = m_defValue;
273 QCString &ConfigImpl::getString(const char *fileName,int num,const char *name) const
275 auto it = m_dict.find(name);
276 if (it==m_dict.end())
278 config_term("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
280 else if (it->second->kind()!=ConfigOption::O_String)
282 config_term("%s<%d>: Internal error: Requested option %s not of string type!\n",fileName,num,name);
284 return *((ConfigString *)it->second)->valueRef();
287 StringVector &ConfigImpl::getList(const char *fileName,int num,const char *name) const
289 auto it = m_dict.find(name);
290 if (it==m_dict.end())
292 config_term("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
294 else if (it->second->kind()!=ConfigOption::O_List)
296 config_term("%s<%d>: Internal error: Requested option %s not of list type!\n",fileName,num,name);
298 return *((ConfigList *)it->second)->valueRef();
301 QCString &ConfigImpl::getEnum(const char *fileName,int num,const char *name) const
303 auto it = m_dict.find(name);
304 if (it==m_dict.end())
306 config_term("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
308 else if (it->second->kind()!=ConfigOption::O_Enum)
310 config_term("%s<%d>: Internal error: Requested option %s not of enum type!\n",fileName,num,name);
312 return *((ConfigEnum *)it->second)->valueRef();
315 int &ConfigImpl::getInt(const char *fileName,int num,const char *name) const
317 auto it = m_dict.find(name);
318 if (it==m_dict.end())
320 config_term("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
322 else if (it->second->kind()!=ConfigOption::O_Int)
324 config_term("%s<%d>: Internal error: Requested option %s not of integer type!\n",fileName,num,name);
326 return *((ConfigInt *)it->second)->valueRef();
329 bool &ConfigImpl::getBool(const char *fileName,int num,const char *name) const
331 auto it = m_dict.find(name);
332 if (it==m_dict.end())
334 config_term("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
336 else if (it->second->kind()!=ConfigOption::O_Bool)
338 config_term("%s<%d>: Internal error: Requested option %s not of boolean type!\n",fileName,num,name);
340 return *((ConfigBool *)it->second)->valueRef();
343 /* ------------------------------------------ */
345 void ConfigInfo::writeTemplate(TextStream &t, bool sl,bool)
351 t << "#---------------------------------------------------------------------------\n";
352 t << "# " << m_doc << "\n";
353 t << "#---------------------------------------------------------------------------\n";
356 void ConfigList::writeTemplate(TextStream &t,bool sl,bool)
361 t << convertToComment(m_doc, m_userComment);
364 else if (!m_userComment.isEmpty())
366 t << convertToComment("", m_userComment);
368 t << m_name << m_spaces.left(MAX_OPTION_LENGTH-m_name.length()) << "=";
369 writeStringList(t,m_value);
373 bool ConfigList::isDefault()
375 auto get_stripped = [](std::string s) { return QCString(s.c_str()).stripWhiteSpace(); };
376 auto is_not_empty = [get_stripped](std::string s) { return !get_stripped(s).isEmpty(); };
377 size_t defCnt = std::count_if( m_value.begin(), m_value.end(),is_not_empty);
378 size_t valCnt = std::count_if(m_defaultValue.begin(),m_defaultValue.end(),is_not_empty);
379 if ( valCnt != defCnt)
383 auto it1 = m_value.begin();
384 auto it2 = m_defaultValue.begin();
385 while (it1!=m_value.end() && it2!=m_defaultValue.end())
387 // skip over empty values
388 while (it1!=m_value.end() && !is_not_empty(*it1))
392 if (it1!=m_value.end()) // non-empty value
394 if (get_stripped(*it1) != get_stripped(*it2)) // not the default, write as difference
405 void ConfigList::compareDoxyfile(TextStream &t)
407 if (!isDefault()) writeTemplate(t,TRUE,TRUE);
410 void ConfigList::writeXMLDoxyfile(TextStream &t)
412 t << " <option id='" << m_name << "'";
413 t << " default='" << (isDefault() ? "yes" : "no") << "'";
414 t << " type='stringlist'";
417 for (const auto &p : m_value)
419 QCString s=p.c_str();
422 writeStringValue(t,s,false);
429 void ConfigEnum::writeTemplate(TextStream &t,bool sl,bool)
434 t << convertToComment(m_doc, m_userComment);
437 else if (!m_userComment.isEmpty())
439 t << convertToComment("", m_userComment);
441 t << m_name << m_spaces.left(MAX_OPTION_LENGTH-m_name.length()) << "=";
442 writeStringValue(t,m_value);
446 void ConfigEnum::compareDoxyfile(TextStream &t)
448 if (!isDefault()) writeTemplate(t,TRUE,TRUE);
451 void ConfigEnum::writeXMLDoxyfile(TextStream &t)
453 t << " <option id='" << m_name << "'";
454 t << " default='" << (isDefault() ? "yes" : "no") << "'";
455 t << " type='string'";
458 writeStringValue(t,m_value,false);
463 void ConfigString::writeTemplate(TextStream &t,bool sl,bool)
468 t << convertToComment(m_doc, m_userComment);
471 else if (!m_userComment.isEmpty())
473 t << convertToComment("", m_userComment);
475 t << m_name << m_spaces.left(MAX_OPTION_LENGTH-m_name.length()) << "=";
476 writeStringValue(t,m_value);
480 void ConfigString::compareDoxyfile(TextStream &t)
482 if (!isDefault()) writeTemplate(t,TRUE,TRUE);
485 void ConfigString::writeXMLDoxyfile(TextStream &t)
487 t << " <option id='" << m_name << "'";
488 t << " default='" << (isDefault() ? "yes" : "no") << "'";
489 t << " type='string'";
493 writeStringValue(t,m_value,false);
499 void ConfigInt::writeTemplate(TextStream &t,bool sl,bool upd)
504 t << convertToComment(m_doc, m_userComment);
507 else if (!m_userComment.isEmpty())
509 t << convertToComment("", m_userComment);
511 t << m_name << m_spaces.left(MAX_OPTION_LENGTH-m_name.length()) << "=";
512 if (upd && !m_valueString.isEmpty())
514 writeStringValue(t,m_valueString);
518 writeIntValue(t,m_value);
523 void ConfigInt::compareDoxyfile(TextStream &t)
525 if (!isDefault()) writeTemplate(t,TRUE,TRUE);
528 void ConfigInt::writeXMLDoxyfile(TextStream &t)
530 t << " <option id='" << m_name << "'";
531 t << " default='" << (isDefault() ? "yes" : "no") << "'";
535 writeIntValue(t,m_value,false);
540 void ConfigBool::writeTemplate(TextStream &t,bool sl,bool upd)
545 t << convertToComment(m_doc, m_userComment);
548 else if (!m_userComment.isEmpty())
550 t << convertToComment("", m_userComment);
552 QCString spaces = m_spaces.left(MAX_OPTION_LENGTH-m_name.length());
553 t << m_name << spaces << "=";
554 if (upd && !m_valueString.isEmpty())
556 writeStringValue(t,m_valueString);
560 writeBoolValue(t,m_value);
565 void ConfigBool::compareDoxyfile(TextStream &t)
567 if (!isDefault()) writeTemplate(t,TRUE,TRUE);
570 void ConfigBool::writeXMLDoxyfile(TextStream &t)
572 t << " <option id='" << m_name << "'";
573 t << " default='" << (isDefault() ? "yes" : "no") << "'";
577 writeBoolValue(t,m_value,false);
582 void ConfigObsolete::writeTemplate(TextStream &,bool,bool) {}
583 void ConfigDisabled::writeTemplate(TextStream &,bool,bool) {}
585 /* -----------------------------------------------------------------
590 struct ConfigFileState
594 YY_BUFFER_STATE oldState;
595 YY_BUFFER_STATE newState;
599 static const char *g_inputString;
600 static int g_inputPosition;
601 static int g_yyLineNr;
602 static QCString g_yyFileName;
603 static QCString g_cmd;
604 static QCString *g_string=0;
605 static StringVector *g_list=0;
606 static QCString g_listStr;
607 static StringVector g_includePathList;
608 static std::vector< std::unique_ptr<ConfigFileState> > g_includeStack;
609 static bool g_configUpdate = FALSE;
610 static QCString g_encoding;
611 static ConfigImpl *g_config;
613 /* -----------------------------------------------------------------
616 #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size);
618 static yy_size_t yyread(char *buf,yy_size_t max_size)
621 if (g_includeStack.empty())
624 if (g_inputString==0) return c;
625 while( c < max_size && g_inputString[g_inputPosition] )
627 *buf = g_inputString[g_inputPosition++] ;
634 //assert(g_includeStack.current()->newState==YY_CURRENT_BUFFER);
635 return (yy_size_t)fread(buf,1,max_size,g_includeStack.back()->filePtr);
640 static QCString configStringRecode(
642 const QCString &inputEncoding,
643 const QCString &outputEncoding)
645 if (inputEncoding.isEmpty() || outputEncoding.isEmpty() || inputEncoding==outputEncoding) return str;
646 int inputSize=str.length();
647 int outputSize=inputSize*4+1;
648 QCString output(outputSize);
649 void *cd = portable_iconv_open(outputEncoding.data(),inputEncoding.data());
650 if (cd==(void *)(-1))
652 config_term("Error: unsupported character conversion: '%s'->'%s'\n",
653 qPrint(inputEncoding),qPrint(outputEncoding));
655 size_t iLeft=(size_t)inputSize;
656 size_t oLeft=(size_t)outputSize;
657 const char *inputPtr = str.data();
658 char *outputPtr = output.rawData();
659 if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft))
661 outputSize-=(int)oLeft;
662 output.resize(outputSize+1);
663 output.at(outputSize)='\0';
664 //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,qPrint(srcBuf));
668 config_term("Error: failed to translate characters from %s to %s: %s\n",
669 qPrint(inputEncoding),qPrint(outputEncoding),strerror(errno));
671 portable_iconv_close(cd);
675 static void checkEncoding()
677 ConfigString *option = (ConfigString*)g_config->get("DOXYFILE_ENCODING");
678 g_encoding = *option->valueRef();
681 static QCString stripComment(const QCString &s)
683 // check if there is a comment at the end of the string
684 bool insideQuote=false;
686 for (int i=0;i<l;i++)
689 if (c=='\\') // skip over escaped characters
693 else if (c=='"') // toggle inside/outside quotation
695 insideQuote=!insideQuote;
697 else if (!insideQuote && c=='#') // found start of a comment
699 if (i<l-1 && s.at(i+1)=='#') // ## -> user comment
701 g_config->appendUserComment(s.mid(i)+"\n");
703 return s.left(i).stripWhiteSpace();
709 static void processString()
711 // strip leading and trailing whitespace
712 QCString s = stripComment(g_string->stripWhiteSpace());
715 // remove surrounding quotes if present (and not escaped)
716 if (l>=2 && s.at(0)=='"' && s.at(l-1)=='"' && // remove quotes
717 (s.at(l-2)!='\\' || (s.at(l-2)=='\\' && s.at(l-3)=='\\')))
719 s=s.mid(1,s.length()-2);
723 // check for invalid and/or escaped quotes
726 for (int i=0;i<l;i++)
729 if (c=='\\') // escaped character
731 if (i<l-1 && s.at(i+1)=='"') // unescape the quote character
735 else // keep other escaped characters in escaped form
743 i++; // skip over the escaped character
745 else if (c=='"') // unescaped quote
749 config_warn("Invalid value for '%s' tag at line %d, file %s: Value '%s' is not properly quoted\n",
750 qPrint(g_cmd),g_yyLineNr,qPrint(g_yyFileName),qPrint(g_string->stripWhiteSpace()));
754 else // normal character
761 *g_string=configStringRecode(result,g_encoding,"UTF-8");
766 //printf("Processed string '%s'\n",qPrint(g_string));
769 static void processList()
771 bool allowCommaAsSeparator = g_cmd!="PREDEFINED";
773 QCString s = stripComment(g_listStr.stripWhiteSpace());
778 // helper to push elemStr to the list and clear it
779 auto addElem = [&elemStr]()
781 if (!elemStr.isEmpty())
783 QCString e = configStringRecode(elemStr,g_encoding,"UTF-8");
784 //printf("Processed list element '%s'\n",qPrint(e));
785 g_list->push_back(e.str());
790 bool needsSeparator=false;
791 int insideQuote=false;
793 for (int i=0;i<l;i++)
796 if (!needsSeparator && c=='\\') // escaped character
798 if (i<l-1 && s.at(i+1)=='"') // unescape the quote character
802 else // keep other escaped characters in escaped form
810 i++; // skip over the escaped character
812 else if (!needsSeparator && c=='"') // quote character
818 else // this quote ends an element
824 else if (!insideQuote && ((c==',' && allowCommaAsSeparator) || isspace(c))) // separator
826 needsSeparator=false;
829 else // normal content character
835 config_warn("Invalid value for '%s' tag at line %d, file %s: Values in list '%s' are not properly space %sseparated\n",
836 qPrint(g_cmd),g_yyLineNr,qPrint(g_yyFileName),qPrint(g_listStr.stripWhiteSpace()),allowCommaAsSeparator?"or comma ":"");
839 needsSeparator=false;
840 i--; // try the character again as part of a new element
853 config_warn("Invalid value for '%s' tag at line %d, file %s: Values in list '%s' are not properly quoted\n",
854 qPrint(g_cmd),g_yyLineNr,qPrint(g_yyFileName),qPrint(g_listStr.stripWhiteSpace()));
858 static FILE *tryPath(const QCString &path,const QCString &fileName)
860 QCString absName=(!path.isEmpty() ? path+"/"+fileName : fileName);
861 FileInfo fi(absName.str());
862 if (fi.exists() && fi.isFile())
864 FILE *f=Portable::fopen(absName,"r");
865 if (!f) config_err("could not open file %s for reading\n",qPrint(absName));
871 static void substEnvVarsInStrList(StringVector &sl);
872 static void substEnvVarsInString(QCString &s);
874 static FILE *findFile(const QCString &fileName)
876 if (fileName.isEmpty())
880 if (Portable::isAbsolutePath(fileName))
882 return tryPath(QCString(), fileName);
884 substEnvVarsInStrList(g_includePathList);
885 for (const auto &s : g_includePathList)
887 FILE *f = tryPath(s.c_str(),fileName);
890 // try cwd if g_includePathList fails
891 return tryPath(".",fileName);
894 static void readIncludeFile(const QCString &incName)
896 if (g_includeStack.size()==MAX_INCLUDE_DEPTH) {
897 config_term("maximum include depth (%d) reached, %s is not included. Aborting...\n",
898 MAX_INCLUDE_DEPTH,qPrint(incName));
901 QCString inc = incName;
902 substEnvVarsInString(inc);
903 inc = inc.stripWhiteSpace();
904 uint incLen = inc.length();
905 if (incLen>0 && inc.at(0)=='"' && inc.at(incLen-1)=='"') // strip quotes
907 inc=inc.mid(1,incLen-2);
912 if ((f=findFile(inc))) // see if the include file can be found
916 for (size_t i=0;i<g_includeStack.size();i++) msg(" ");
917 msg("@INCLUDE = %s: parsing...\n",qPrint(inc));
920 // store the state of the old file
921 ConfigFileState *fs=new ConfigFileState;
922 fs->oldState=YY_CURRENT_BUFFER;
923 fs->lineNr=g_yyLineNr;
924 fs->fileName=g_yyFileName;
926 // push the state on the stack
927 g_includeStack.push_back(std::unique_ptr<ConfigFileState>(fs));
928 // set the scanner to the include file
929 yy_switch_to_buffer(yy_create_buffer(f, YY_BUF_SIZE));
930 fs->newState=YY_CURRENT_BUFFER;
935 config_term("@INCLUDE = %s: not found!\n",qPrint(inc));
955 /*-------------- Comments ---------------*/
958 g_config->appendUserComment(yytext);
961 <Start>"#".*"\n" { /* normal comment */
965 /*-------------- TAG start ---------------*/
967 <Start>[a-z_A-Z][a-z_A-Z0-9]*[ \t]*"=" { g_cmd=yytext;
968 g_cmd=g_cmd.left(g_cmd.length()-1).stripWhiteSpace();
969 ConfigOption *option = g_config->get(g_cmd);
970 if (option==0) // oops not known
972 config_warn("ignoring unsupported tag '%s' at line %d, file %s\n",
973 qPrint(g_cmd),g_yyLineNr,qPrint(g_yyFileName));
978 option->setUserComment(g_config->takeUserComment());
979 option->setEncoding(g_encoding);
980 switch(option->kind())
982 case ConfigOption::O_Info:
983 // shouldn't get here!
986 case ConfigOption::O_List:
987 g_list = ((ConfigList *)option)->valueRef();
992 case ConfigOption::O_Enum:
993 g_string = ((ConfigEnum *)option)->valueRef();
997 case ConfigOption::O_String:
998 g_string = ((ConfigString *)option)->valueRef();
1002 case ConfigOption::O_Int:
1003 g_string = ((ConfigInt *)option)->valueStringRef();
1004 g_string->resize(0);
1007 case ConfigOption::O_Bool:
1008 g_string = ((ConfigBool *)option)->valueStringRef();
1009 g_string->resize(0);
1012 case ConfigOption::O_Obsolete:
1015 config_warn("Tag '%s' at line %d of file '%s' has become obsolete.\n"
1016 " This tag has been removed.\n", qPrint(g_cmd),g_yyLineNr,qPrint(g_yyFileName));
1020 config_warn("Tag '%s' at line %d of file '%s' has become obsolete.\n"
1021 " To avoid this warning please remove this line from your configuration "
1022 "file or upgrade it using \"doxygen -u\"\n", qPrint(g_cmd),g_yyLineNr,qPrint(g_yyFileName));
1024 ((ConfigObsolete*)option)->markAsPresent();
1025 if (((ConfigObsolete*)option)->orgType()==ConfigOption::O_List)
1027 g_list = ((ConfigObsolete*)option)->valueListRef();
1034 g_string = ((ConfigObsolete*)option)->valueStringRef();
1035 g_string->resize(0);
1039 case ConfigOption::O_Disabled:
1042 config_warn("Tag '%s' at line %d of file '%s' belongs to an option that was not enabled at compile time.\n"
1043 " This tag has been removed.\n", qPrint(g_cmd),g_yyLineNr,qPrint(g_yyFileName));
1047 config_warn("Tag '%s' at line %d of file '%s' belongs to an option that was not enabled at compile time.\n"
1048 " To avoid this warning please remove this line from your configuration "
1049 "file or upgrade it using \"doxygen -u\", or recompile doxygen with this feature enabled.\n", qPrint(g_cmd),g_yyLineNr,qPrint(g_yyFileName));
1056 <Start>[a-z_A-Z][a-z_A-Z0-9]*[ \t]*"+=" { g_cmd=yytext;
1057 g_cmd=g_cmd.left(g_cmd.length()-2).stripWhiteSpace();
1058 ConfigOption *option = g_config->get(g_cmd);
1059 if (option==0) // oops not known
1061 config_warn("ignoring unsupported tag '%s' at line %d, file %s\n",
1062 qPrint(g_cmd),g_yyLineNr,qPrint(g_yyFileName));
1067 option->setUserComment(g_config->takeUserComment());
1068 switch(option->kind())
1070 case ConfigOption::O_Info:
1071 // shouldn't get here!
1074 case ConfigOption::O_List:
1075 g_list = ((ConfigList *)option)->valueRef();
1079 case ConfigOption::O_Enum:
1080 case ConfigOption::O_String:
1081 case ConfigOption::O_Int:
1082 case ConfigOption::O_Bool:
1083 config_warn("operator += not supported for '%s'. Ignoring line at line %d, file %s\n",
1084 yytext,g_yyLineNr,qPrint(g_yyFileName));
1087 case ConfigOption::O_Obsolete:
1088 config_warn("Tag '%s' at line %d of file %s has become obsolete.\n"
1089 "To avoid this warning please update your configuration "
1090 "file using \"doxygen -u\"\n", qPrint(g_cmd),g_yyLineNr,qPrint(g_yyFileName));
1091 if (((ConfigObsolete*)option)->orgType()==ConfigOption::O_List)
1093 g_list = ((ConfigObsolete*)option)->valueListRef();
1102 case ConfigOption::O_Disabled:
1103 config_warn("Tag '%s' at line %d of file %s belongs to an option that was not enabled at compile time.\n"
1104 "To avoid this warning please remove this line from your configuration "
1105 "file, upgrade it using \"doxygen -u\", or recompile doxygen with this feature enabled.\n", qPrint(g_cmd),g_yyLineNr,qPrint(g_yyFileName));
1112 /*-------------- INCLUDE* ---------------*/
1114 <Start>"@INCLUDE_PATH"[ \t]*"=" { BEGIN(GetStrList); g_list=&g_includePathList; g_list->clear(); g_listStr=""; }
1115 /* include a g_config file */
1116 <Start>"@INCLUDE"[ \t]*"=" { BEGIN(Include);}
1117 <Include>([^ \"\t\r\n]+)|("\""[^\n\"]+"\"") {
1118 readIncludeFile(configStringRecode(yytext,g_encoding,"UTF-8"));
1122 //printf("End of include file\n");
1123 //printf("Include stack depth=%d\n",g_includeStack.count());
1124 if (g_includeStack.empty())
1126 //printf("Terminating scanner!\n");
1131 auto &fs=g_includeStack.back();
1132 fclose(fs->filePtr);
1133 YY_BUFFER_STATE oldBuf = YY_CURRENT_BUFFER;
1134 yy_switch_to_buffer( fs->oldState );
1135 yy_delete_buffer( oldBuf );
1136 g_yyLineNr=fs->lineNr;
1137 g_yyFileName=fs->fileName;
1138 g_includeStack.pop_back();
1142 <Start>[a-z_A-Z0-9]+ { config_warn("ignoring unknown tag '%s' at line %d, file %s\n",yytext,g_yyLineNr,qPrint(g_yyFileName)); }
1143 /*-------------- GetString ---------------*/
1145 <GetString>\n { processString();
1146 g_yyLineNr++; // end of string
1149 <GetString>\\[ \r\t]*\n { g_yyLineNr++; // line continuation
1152 <GetString>"\\" { // escape character
1155 <GetString>[^\n\\]+ { // string part without escape characters
1159 /*-------------- GetStrList --------------*/
1161 <GetStrList>\n { processList();
1162 g_yyLineNr++; // end of list
1165 <GetStrList>\\[ \r\t]*\n { g_yyLineNr++; // line continuation
1168 <GetStrList>"\\" { // escape character
1171 <GetStrList>[^\n\\]+ { // string part without escape characters
1175 /*-------------- SkipInvalid --------------*/
1177 <SkipInvalid>\n { g_yyLineNr++; // end of list
1180 <SkipInvalid>\\[ \r\t]*\n { g_yyLineNr++; // line continuation
1182 <SkipInvalid>"\\" { // escape character
1184 <SkipInvalid>[^\n\\]+ { // string part without escape characters
1187 /*-------------- fall through -------------*/
1189 <*>\\[ \r\t]*\n { g_yyLineNr++; }
1191 <*>\n { g_yyLineNr++ ; }
1192 <*>. { config_warn("ignoring unknown character '%c' at line %d, file %s\n",yytext[0],g_yyLineNr,qPrint(g_yyFileName)); }
1196 /*@ ----------------------------------------------------------------------------
1199 void ConfigImpl::writeTemplate(TextStream &t,bool sl,bool upd)
1201 /* print first lines of user comment that were at the beginning of the file, might have special meaning for editors */
1202 if (!m_startComment.isEmpty())
1204 t << takeStartComment() << "\n";
1206 t << "# Doxyfile " << getDoxygenVersion() << "\n\n";
1209 t << convertToComment(m_header,"");
1211 for (const auto &option : m_options)
1213 option->writeTemplate(t,sl,upd);
1215 /* print last lines of user comment that were at the end of the file */
1216 if (!m_userComment.isEmpty())
1219 t << takeUserComment();
1223 void ConfigImpl::compareDoxyfile(TextStream &t)
1225 t << "# Difference with default Doxyfile " << getFullVersion();
1227 for (const auto &option : m_options)
1229 option->m_userComment = "";
1230 option->compareDoxyfile(t);
1234 void ConfigImpl::writeXMLDoxyfile(TextStream &t)
1236 t << "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n";
1237 t << "<doxyfile xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"doxyfile.xsd\" version=\"" << getDoxygenVersion() << "\" xml:lang=\"" << theTranslator->trISOLang() << "\">\n";
1238 for (const auto &option : m_options)
1240 option->writeXMLDoxyfile(t);
1242 t << "</doxyfile>\n";
1245 void ConfigImpl::convertStrToVal()
1247 for (const auto &option : m_options)
1249 option->convertStrToVal();
1252 void ConfigImpl::emptyValueToDefault()
1254 for (const auto &option : m_options)
1256 option->emptyValueToDefault();
1260 static void substEnvVarsInString(QCString &str)
1262 if (str.isEmpty()) return;
1263 auto replace = [](const std::string &s, const reg::Ex &re) -> std::string
1265 reg::Iterator it(s,re);
1269 for (; it!=end ; ++it)
1271 const auto &match = *it;
1272 size_t i = match.position();
1273 size_t l = match.length();
1274 result+=s.substr(p,i-p);
1275 std::string matchContents = match[1].str();
1276 QCString env=Portable::getenv(matchContents.c_str()); // get content of $(..) match
1277 substEnvVarsInString(env); // recursively expand variables if needed.
1281 result+=s.substr(p);
1285 // match e.g. re1=$(HOME) but also re2=$(PROGRAMFILES(X86))
1286 static const reg::Ex re1(R"(\$\((\a[\w.-]*)\))");
1287 static const reg::Ex re2(R"(\$\((\a[\w.-]*\(\a[\w.-]*\))\))");
1288 str = QCString(replace(replace(str.str(),re1),re2)).stripWhiteSpace();
1291 static void substEnvVarsInStrList(StringVector &sl)
1293 StringVector results;
1294 for (const auto &s : sl)
1296 QCString result = s.c_str();
1297 bool wasQuoted = (result.find(' ')!=-1) || (result.find('\t')!=-1) || (result.find('"')!=-1);
1298 // here we strip the quote again
1299 substEnvVarsInString(result);
1301 //printf("Result %s was quoted=%d\n",qPrint(result),wasQuoted);
1303 if (!wasQuoted) /* as a result of the expansion, a single string
1304 may have expanded into a list, which we'll
1305 add to sl. If the original string already
1306 contained multiple elements no further
1307 splitting is done to allow quoted items with spaces! */
1309 int l=result.length();
1312 // search for a "word"
1316 // skip until start of new word
1317 while (i<l && ((c=result.at(i))==' ' || c=='\t')) i++;
1318 p=i; // p marks the start index of the word
1319 // skip until end of a word
1320 while (i<l && ((c=result.at(i))!=' ' && c!='\t' && c!='"')) i++;
1321 if (i<l) // not at the end of the string
1323 if (c=='"') // word within quotes
1329 if (c=='"') // end quote
1331 results.push_back(result.mid(p,i-p).str());
1335 else if (c=='\\') // skip escaped stuff
1341 else if (c==' ' || c=='\t') // separator
1343 if (i>p) results.push_back(result.mid(p,i-p).str());
1348 if (p!=l) // add the leftover as a string
1350 results.push_back(result.right(l-p).str());
1353 else // just goto the next element in the list
1355 if (!result.isEmpty()) results.push_back(result.str());
1361 void ConfigString::substEnvVars()
1363 substEnvVarsInString(m_value);
1366 void ConfigList::substEnvVars()
1368 substEnvVarsInStrList(m_value);
1371 void ConfigBool::substEnvVars()
1373 substEnvVarsInString(m_valueString);
1376 void ConfigInt::substEnvVars()
1378 substEnvVarsInString(m_valueString);
1381 void ConfigEnum::substEnvVars()
1383 substEnvVarsInString(m_value);
1386 //---------------------------------------------
1388 void ConfigImpl::substituteEnvironmentVars()
1390 for (const auto &option : m_options)
1392 option->substEnvVars();
1396 void ConfigImpl::init()
1398 for (const auto &option : m_options)
1403 // sanity check if all depends relations are valid
1404 for (const auto &option : m_options)
1406 QCString depName = option->dependsOn();
1407 if (!depName.isEmpty())
1409 ConfigOption * opt = ConfigImpl::instance()->get(depName);
1412 config_term("Config option '%s' has invalid depends relation on unknown option '%s'\n",
1413 qPrint(option->name()),qPrint(depName));
1419 void ConfigImpl::create()
1421 if (m_initialized) return;
1422 m_initialized = TRUE;
1423 addConfigOptions(this);
1426 static QCString configFileToString(const QCString &name)
1428 if (name.isEmpty()) return QCString();
1430 auto stream2string = [](std::istream &in) -> std::string
1434 while (in.read(buffer, sizeof(buffer))) ret.append(buffer, sizeof(buffer));
1435 ret.append(buffer, static_cast<uint>(in.gcount()));
1436 if (!ret.empty() && ret[ret.length()-1]!='\n') ret+='\n'; // to help the scanner
1440 if (name=="-") // read from stdin
1442 // read contents from stdin into contents string
1443 return stream2string(std::cin);
1445 else // read from file
1447 std::ifstream f(name.str(),std::istream::in);
1450 config_term("file '%s' not found or could not be opened\n",qPrint(name));
1453 return stream2string(f);
1457 bool ConfigImpl::parseString(const QCString &fn,const QCString &str,bool update)
1459 g_config = ConfigImpl::instance();
1460 g_inputString = str.data();
1461 g_inputPosition = 0;
1464 g_includeStack.clear();
1465 configimplYYrestart( configimplYYin );
1467 g_configUpdate = update;
1469 g_configUpdate = FALSE;
1474 bool ConfigImpl::parse(const QCString &fn,bool update)
1477 g_encoding = "UTF-8";
1478 printlex(yy_flex_debug, TRUE, __FILE__, qPrint(fn));
1479 retval = parseString(fn,configFileToString(fn), update);
1480 printlex(yy_flex_debug, FALSE, __FILE__, qPrint(fn));
1484 //----------------------------------------------------------------------
1486 static void cleanUpPaths(StringVector &str)
1488 for (size_t i=0;i<str.size();i++)
1490 std::string path = str[i];
1491 std::replace(path.begin(),path.end(),'\\','/');
1492 if ((path[0]!='/' && (path.size()<=2 || path[1]!=':')) || path[path.size()-1]!='/')
1495 if (fi.exists() && fi.isDir())
1497 path = fi.absFilePath();
1498 if (path[path.size()-1]!='/') path+='/';
1505 static bool checkFileName(const QCString &s,const char *optionName)
1507 QCString val = s.stripWhiteSpace().lower();
1508 if ((val=="yes" || val=="true" || val=="1" || val=="all") ||
1509 (val=="no" || val=="false" || val=="0" || val=="none"))
1511 err("file name expected for option %s, got %s instead. Ignoring...\n",optionName,qPrint(s));
1521 ConfigImpl::instance()->init();
1524 static void checkList(const StringVector &list,const char *name, bool equalRequired,bool valueRequired)
1526 for (const auto &s: list)
1528 QCString item=s.c_str();
1529 item=item.stripWhiteSpace();
1530 int i=item.find('=');
1531 if (i==-1 && equalRequired)
1533 err("Illegal format for option %s, no equal sign ('=') specified for item '%s'\n",name,qPrint(item));
1537 QCString myName=item.left(i).stripWhiteSpace();
1538 if (myName.isEmpty())
1540 err("Illegal format for option %s, no name specified for item '%s'\n",name,qPrint(item));
1542 else if (valueRequired)
1544 QCString myValue=item.right(item.length()-i-1).stripWhiteSpace();
1545 if (myValue.isEmpty())
1547 err("Illegal format for option %s, no value specified for item '%s'\n",name,qPrint(item));
1554 void Config::checkAndCorrect(bool quiet)
1556 ConfigValues::instance().init();
1558 Config_updateBool(QUIET,quiet || Config_getBool(QUIET));
1559 //------------------------
1560 // check WARN_FORMAT
1561 QCString warnFormat = Config_getString(WARN_FORMAT);
1562 if (warnFormat.find("$file")==-1)
1564 warn_uncond("warning format does not contain a $file tag!\n");
1566 if (warnFormat.find("$line")==-1)
1568 warn_uncond("warning format does not contain a $line tag!\n");
1570 if (warnFormat.find("$text")==-1)
1572 warn_uncond("warning format foes not contain a $text tag!\n");
1575 //------------------------
1576 // check and correct PAPER_TYPE
1577 QCString paperType = Config_getEnumAsString(PAPER_TYPE);
1578 paperType=paperType.lower().stripWhiteSpace();
1579 if (paperType.isEmpty() || paperType=="a4wide")
1582 Config_updateEnum(PAPER_TYPE,PAPER_TYPE_t::a4);
1584 else if (paperType!="a4" && paperType!="letter" &&
1585 paperType!="legal" && paperType!="executive")
1587 err("Unknown page type '%s' specified\n",qPrint(paperType));
1588 Config_updateEnum(PAPER_TYPE,PAPER_TYPE_t::a4);
1591 //------------------------
1592 // check & correct STRIP_FROM_PATH
1593 StringVector stripFromPath = Config_getList(STRIP_FROM_PATH);
1594 if (stripFromPath.empty()) // by default use the current path
1596 std::string p = Dir::currentDirPath()+"/";
1597 stripFromPath.push_back(p);
1601 cleanUpPaths(stripFromPath);
1603 Config_updateList(STRIP_FROM_PATH,stripFromPath);
1605 //------------------------
1606 // check & correct STRIP_FROM_INC_PATH
1607 StringVector stripFromIncPath = Config_getList(STRIP_FROM_INC_PATH);
1608 cleanUpPaths(stripFromIncPath);
1609 Config_updateList(STRIP_FROM_INC_PATH,stripFromIncPath);
1611 //------------------------
1612 // Test to see if HTML header is valid
1613 QCString headerFile = Config_getString(HTML_HEADER);
1614 if (!headerFile.isEmpty())
1616 FileInfo fi(headerFile.str());
1619 config_term("tag HTML_HEADER: header file '%s' "
1620 "does not exist\n",qPrint(headerFile));
1624 //------------------------
1625 // Test to see if HTML footer is valid
1626 QCString footerFile = Config_getString(HTML_FOOTER);
1627 if (!footerFile.isEmpty())
1629 FileInfo fi(footerFile.str());
1632 config_term("tag HTML_FOOTER: footer file '%s' "
1633 "does not exist\n",qPrint(footerFile));
1637 //------------------------
1638 // Test to see if MathJax code file is valid
1639 if (Config_getBool(USE_MATHJAX))
1641 auto mathJaxFormat = Config_getEnum(MATHJAX_FORMAT);
1642 auto mathjaxVersion = Config_getEnum(MATHJAX_VERSION);
1643 if (mathjaxVersion == MATHJAX_VERSION_t::MathJax_2)
1645 if (mathJaxFormat==MATHJAX_FORMAT_t::chtml)
1647 Config_updateEnum(MATHJAX_FORMAT,MATHJAX_FORMAT_t::HTML_CSS);
1652 if (mathJaxFormat==MATHJAX_FORMAT_t::HTML_CSS || mathJaxFormat==MATHJAX_FORMAT_t::NativeMML)
1654 Config_updateEnum(MATHJAX_FORMAT,MATHJAX_FORMAT_t::chtml);
1658 QCString mathJaxCodefile = Config_getString(MATHJAX_CODEFILE);
1659 if (!mathJaxCodefile.isEmpty())
1661 FileInfo fi(mathJaxCodefile.str());
1664 config_term("tag MATHJAX_CODEFILE file '%s' "
1665 "does not exist\n",qPrint(mathJaxCodefile));
1668 QCString path = Config_getString(MATHJAX_RELPATH);
1671 path = "https://cdn.jsdelivr.net/npm/mathjax@";
1672 switch (mathjaxVersion)
1674 case MATHJAX_VERSION_t::MathJax_2: path += "2"; break;
1675 case MATHJAX_VERSION_t::MathJax_3: path += "3"; break;
1679 if (path.at(path.length()-1)!='/')
1683 Config_updateString(MATHJAX_RELPATH,path);
1686 //------------------------
1687 // Test to see if LaTeX header is valid
1688 QCString latexHeaderFile = Config_getString(LATEX_HEADER);
1689 if (!latexHeaderFile.isEmpty())
1691 FileInfo fi(latexHeaderFile.str());
1694 config_term("tag LATEX_HEADER: header file '%s' "
1695 "does not exist\n",qPrint(latexHeaderFile));
1699 //------------------------
1700 // Test to see if LaTeX footer is valid
1701 QCString latexFooterFile = Config_getString(LATEX_FOOTER);
1702 if (!latexFooterFile.isEmpty())
1704 FileInfo fi(latexFooterFile.str());
1707 config_term("tag LATEX_FOOTER: footer file '%s' "
1708 "does not exist\n",qPrint(latexFooterFile));
1712 //------------------------
1713 // check include path
1714 const StringVector &includePath = Config_getList(INCLUDE_PATH);
1715 for (const auto &s : includePath)
1718 if (!fi.exists()) warn_uncond("tag INCLUDE_PATH: include path '%s' "
1719 "does not exist\n",s.c_str());
1722 //------------------------
1724 if (Config_getBool(ENABLE_PREPROCESSING))
1726 const StringVector &predefList = Config_getList(PREDEFINED);
1727 for (const auto &s : predefList)
1729 QCString predef=s.c_str();
1730 predef=predef.stripWhiteSpace();
1731 int i_equals=predef.find('=');
1732 int i_obrace=predef.find('(');
1733 if ((i_obrace==0) || (i_equals==0) || (i_equals==1 && predef.at(i_equals-1)==':'))
1735 err("Illegal PREDEFINED format '%s', no define name specified\n",qPrint(predef));
1740 //------------------------
1742 const StringVector &aliasList = Config_getList(ALIASES);
1743 for (const auto &alias : aliasList)
1745 // match aliases of the form re1='name=' and re2='name{2} ='
1746 static const reg::Ex re1(R"(^\a\w*\s*=)");
1747 static const reg::Ex re2(R"(^\a\w*{\d+}\s*=)");
1748 if (!reg::search(alias,re1) && !reg::search(alias,re2))
1750 err("Illegal ALIASES format '%s'. Use \"name=value\" or \"name{n}=value\", where n is the number of arguments\n",
1755 //------------------------
1756 // check EXTENSION_MAPPING
1757 checkList(Config_getList(EXTENSION_MAPPING),"EXTENSION_MAPPING",TRUE,TRUE);
1759 //------------------------
1760 // check FILTER_PATTERNS
1761 checkList(Config_getList(FILTER_PATTERNS),"FILTER_PATTERNS",TRUE,TRUE);
1763 //------------------------
1764 // check FILTER_SOURCE_PATTERNS
1765 checkList(Config_getList(FILTER_SOURCE_PATTERNS),"FILTER_SOURCE_PATTERNS",FALSE,FALSE);
1767 //------------------------
1769 checkList(Config_getList(TAGFILES),"TAGFILES",FALSE,TRUE);
1771 //------------------------
1772 // check EXTRA_SEARCH_MAPPINGS
1773 if (Config_getBool(SEARCHENGINE) && Config_getBool(GENERATE_HTML))
1775 checkList(Config_getList(EXTRA_SEARCH_MAPPINGS),"EXTRA_SEARCH_MAPPING",TRUE,TRUE);
1778 //------------------------
1779 // check if GENERATE_TREEVIEW and GENERATE_HTMLHELP are both enabled
1780 if (Config_getBool(GENERATE_TREEVIEW) && Config_getBool(GENERATE_HTMLHELP))
1782 err("When enabling GENERATE_HTMLHELP the tree view (GENERATE_TREEVIEW) should be disabled. I'll do it for you.\n");
1783 Config_updateBool(GENERATE_TREEVIEW,FALSE);
1786 //------------------------
1787 // check if GENERATE_HTMLHELP and HTML_FILE_EXTENSION is not .html
1788 if (Config_getString(HTML_FILE_EXTENSION)!=".html" && Config_getBool(GENERATE_HTMLHELP))
1790 err("When enabling GENERATE_HTMLHELP the HTML_FILE_EXTENSION should be \".html\". I'll do it for you.\n");
1791 Config_updateString(HTML_FILE_EXTENSION,".html");
1794 //------------------------
1795 // check if SEARCHENGINE and GENERATE_HTMLHELP are both enabled
1796 if (Config_getBool(SEARCHENGINE) && Config_getBool(GENERATE_HTMLHELP))
1798 err("When enabling GENERATE_HTMLHELP the search engine (SEARCHENGINE) should be disabled. I'll do it for you.\n");
1799 Config_updateBool(SEARCHENGINE,FALSE);
1802 //------------------------
1803 // check if SEPARATE_MEMBER_PAGES and INLINE_GROUPED_CLASSES are both enabled
1804 if (Config_getBool(SEPARATE_MEMBER_PAGES) && Config_getBool(INLINE_GROUPED_CLASSES))
1806 err("When enabling INLINE_GROUPED_CLASSES the SEPARATE_MEMBER_PAGES option should be disabled. I'll do it for you.\n");
1807 Config_updateBool(SEPARATE_MEMBER_PAGES,FALSE);
1810 //------------------------
1811 // correct DOT_FONTNAME if needed
1812 QCString dotFontName=Config_getString(DOT_FONTNAME);
1813 if (dotFontName=="FreeSans" || dotFontName=="FreeSans.ttf")
1815 warn_uncond("doxygen no longer ships with the FreeSans font.\n"
1816 " You may want to clear or change DOT_FONTNAME.\n"
1817 " Otherwise you run the risk that the wrong font is being used for dot generated graphs.\n");
1820 //------------------------
1821 // clip number of threads
1822 int dotNumThreads = Config_getInt(DOT_NUM_THREADS);
1823 if (dotNumThreads>32)
1827 else if (dotNumThreads<=0)
1829 dotNumThreads=std::max(2u,std::thread::hardware_concurrency()+1);
1831 Config_updateInt(DOT_NUM_THREADS,dotNumThreads);
1833 //------------------------
1835 QCString dotPath = Config_getString(DOT_PATH);
1836 if (!dotPath.isEmpty())
1838 FileInfo fi(dotPath.str());
1839 if (fi.exists() && fi.isFile()) // user specified path + exec
1841 dotPath=fi.dirPath(TRUE)+"/";
1845 QCString dotExe = dotPath+"/dot"+Portable::commandExtension();
1846 FileInfo dp(dotExe.str());
1847 if (!dp.exists() || !dp.isFile())
1849 warn_uncond("the dot tool could not be found at %s\n",qPrint(dotPath));
1854 dotPath=dp.dirPath(TRUE)+"/";
1857 #if defined(_WIN32) // convert slashes
1858 uint i=0,l=dotPath.length();
1859 for (i=0;i<l;i++) if (dotPath.at(i)=='/') dotPath.at(i)='\\';
1861 Config_updateString(DOT_PATH,dotPath);
1864 //------------------------
1865 // check plantuml path
1866 QCString plantumlJarPath = Config_getString(PLANTUML_JAR_PATH);
1867 if (!plantumlJarPath.isEmpty())
1869 FileInfo pu(plantumlJarPath.str());
1870 if (pu.exists() && pu.isDir()) // PLANTUML_JAR_PATH is directory
1872 QCString plantumlJar = plantumlJarPath+Portable::pathSeparator()+"plantuml.jar";
1873 FileInfo jar(plantumlJar.str());
1874 if (jar.exists() && jar.isFile())
1876 plantumlJarPath = jar.dirPath(TRUE)+Portable::pathSeparator();
1880 err("Jar file plantuml.jar not found at location "
1881 "specified via PLANTUML_JAR_PATH: '%s'\n",qPrint(plantumlJarPath));
1885 else if (pu.exists() && pu.isFile() && plantumlJarPath.right(4)==".jar") // PLANTUML_JAR_PATH is file
1887 plantumlJarPath = pu.dirPath(TRUE)+Portable::pathSeparator();
1891 err("path specified via PLANTUML_JAR_PATH does not exist or not a directory: %s\n",
1892 qPrint(plantumlJarPath));
1895 Config_updateString(PLANTUML_JAR_PATH,plantumlJarPath);
1898 //------------------------
1900 QCString diaPath = Config_getString(DIA_PATH);
1901 if (!diaPath.isEmpty())
1903 QCString diaExe = diaPath+"/dia"+Portable::commandExtension();
1904 FileInfo dp(diaExe.str());
1905 if (!dp.exists() || !dp.isFile())
1907 warn_uncond("dia could not be found at %s\n",qPrint(diaPath));
1912 diaPath=dp.dirPath(TRUE)+"/";
1913 #if defined(_WIN32) // convert slashes
1914 uint i=0,l=diaPath.length();
1915 for (i=0;i<l;i++) if (diaPath.at(i)=='/') diaPath.at(i)='\\';
1918 Config_updateString(DIA_PATH,diaPath);
1921 //------------------------
1923 StringVector inputSources=Config_getList(INPUT);
1924 if (inputSources.empty())
1926 // use current dir as the default
1927 inputSources.push_back(Dir::currentDirPath());
1931 for (const auto &s : inputSources)
1933 FileInfo fi(s.c_str());
1936 warn_uncond("tag INPUT: input source '%s' does not exist\n",s.c_str());
1940 Config_updateList(INPUT,inputSources);
1942 //------------------------
1943 // if no output format is enabled, warn the user
1944 if (!Config_getBool(GENERATE_HTML) &&
1945 !Config_getBool(GENERATE_LATEX) &&
1946 !Config_getBool(GENERATE_MAN) &&
1947 !Config_getBool(GENERATE_RTF) &&
1948 !Config_getBool(GENERATE_XML) &&
1949 !Config_getBool(GENERATE_PERLMOD) &&
1950 !Config_getBool(GENERATE_RTF) &&
1951 !Config_getBool(GENERATE_DOCBOOK) &&
1952 !Config_getBool(GENERATE_AUTOGEN_DEF) &&
1953 Config_getString(GENERATE_TAGFILE).isEmpty()
1956 warn_uncond("No output formats selected! Set at least one of the main GENERATE_* options to YES.\n");
1959 //------------------------
1960 // check HTMLHELP creation requirements
1961 if (!Config_getBool(GENERATE_HTML) &&
1962 Config_getBool(GENERATE_HTMLHELP))
1964 warn_uncond("GENERATE_HTMLHELP=YES requires GENERATE_HTML=YES.\n");
1967 //------------------------
1968 // check QHP creation requirements
1969 if (Config_getBool(GENERATE_QHP))
1971 if (Config_getString(QHP_NAMESPACE).isEmpty())
1973 err("GENERATE_QHP=YES requires QHP_NAMESPACE to be set. Using 'org.doxygen.doc' as default!.\n");
1974 Config_updateString(QHP_NAMESPACE,"org.doxygen.doc");
1977 if (Config_getString(QHP_VIRTUAL_FOLDER).isEmpty())
1979 err("GENERATE_QHP=YES requires QHP_VIRTUAL_FOLDER to be set. Using 'doc' as default!\n");
1980 Config_updateString(QHP_VIRTUAL_FOLDER,"doc");
1984 //------------------------
1985 if (Config_getBool(OPTIMIZE_OUTPUT_JAVA) && Config_getBool(INLINE_INFO))
1987 // don't show inline info for Java output, since Java has no inline
1989 Config_updateBool(INLINE_INFO,FALSE);
1992 //------------------------
1993 int depth = Config_getInt(MAX_DOT_GRAPH_DEPTH);
1996 Config_updateInt(MAX_DOT_GRAPH_DEPTH,1000);
1999 //------------------------
2000 // some default settings for vhdl
2001 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL) &&
2002 (Config_getBool(INLINE_INHERITED_MEMB) ||
2003 Config_getBool(INHERIT_DOCS) ||
2004 !Config_getBool(HIDE_SCOPE_NAMES) ||
2005 !Config_getBool(EXTRACT_PRIVATE) ||
2006 !Config_getBool(EXTRACT_PACKAGE)
2010 bool b1 = Config_getBool(INLINE_INHERITED_MEMB);
2011 bool b2 = Config_getBool(INHERIT_DOCS);
2012 bool b3 = Config_getBool(HIDE_SCOPE_NAMES);
2013 bool b4 = Config_getBool(EXTRACT_PRIVATE);
2014 bool b5 = Config_getBool(SKIP_FUNCTION_MACROS);
2015 bool b6 = Config_getBool(EXTRACT_PACKAGE);
2016 const char *s1,*s2,*s3,*s4,*s5,*s6;
2017 if (b1) s1=" INLINE_INHERITED_MEMB = NO (was YES)\n"; else s1="";
2018 if (b2) s2=" INHERIT_DOCS = NO (was YES)\n"; else s2="";
2019 if (!b3) s3=" HIDE_SCOPE_NAMES = YES (was NO)\n"; else s3="";
2020 if (!b4) s4=" EXTRACT_PRIVATE = YES (was NO)\n"; else s4="";
2021 if (b5) s5=" ENABLE_PREPROCESSING = NO (was YES)\n"; else s5="";
2022 if (!b6) s6=" EXTRACT_PACKAGE = YES (was NO)\n"; else s6="";
2025 warn_uncond("enabling OPTIMIZE_OUTPUT_VHDL assumes the following settings:\n"
2026 "%s%s%s%s%s%s",s1,s2,s3,s4,s5,s6
2029 Config_updateBool(INLINE_INHERITED_MEMB, FALSE);
2030 Config_updateBool(INHERIT_DOCS, FALSE);
2031 Config_updateBool(HIDE_SCOPE_NAMES, TRUE);
2032 Config_updateBool(EXTRACT_PRIVATE, TRUE);
2033 Config_updateBool(ENABLE_PREPROCESSING, FALSE);
2034 Config_updateBool(EXTRACT_PACKAGE, TRUE);
2037 if (!checkFileName(Config_getString(GENERATE_TAGFILE),"GENERATE_TAGFILE"))
2039 Config_updateString(GENERATE_TAGFILE,"");
2042 #if 0 // TODO: this breaks test 25; SOURCEBROWSER = NO and SOURCE_TOOLTIPS = YES.
2043 // So this and other regressions should be analysed and fixed before this can be enabled
2044 // disable any boolean options that depend on disabled options
2045 for (const auto &option : m_options)
2047 QCString depName = option->dependsOn(); // option has a dependency
2048 if (!depName.isEmpty())
2050 ConfigOption * dep = Config::instance()->get(depName);
2051 if (dep->kind()==ConfigOption::O_Bool &&
2052 ConfigImpl_getBool("depName")==FALSE) // dependent option is disabled
2054 if (option->kind()==ConfigOption::O_Bool)
2056 printf("disabling option %s\n",qPrint(option->name()));
2057 ConfigImpl_getBool("option->name("))=FALSE; // also disable this option
2066 void Config::updateObsolete()
2068 //------------------------
2069 // check for presence of obsolete CLASS_DIAGRAM option and correct CLASS_GRAPH if needed
2070 ConfigOption *classDiagramsOpt = ConfigImpl::instance()->get("CLASS_DIAGRAMS");
2071 ConfigOption *haveDotOpt = ConfigImpl::instance()->get("HAVE_DOT");
2072 ConfigOption *classGraphOpt = ConfigImpl::instance()->get("CLASS_GRAPH");
2073 if (classDiagramsOpt && classDiagramsOpt->kind()==ConfigOption::O_Obsolete &&
2074 haveDotOpt && classGraphOpt)
2076 ConfigObsolete *classDiagramsOpt_ = dynamic_cast<ConfigObsolete*>(classDiagramsOpt);
2077 ConfigBool *haveDotOpt_ = dynamic_cast<ConfigBool*>(haveDotOpt);
2078 ConfigEnum *classGraphOpt_ = dynamic_cast<ConfigEnum*>(classGraphOpt);
2079 if (classDiagramsOpt_->isPresent() && classDiagramsOpt_->orgType()==ConfigOption::O_Bool)
2081 QCString classDiagramValue = *classDiagramsOpt_->valueStringRef();
2082 QCString haveDotValue = *haveDotOpt_->valueStringRef();
2083 QCString &classGraphValue = *classGraphOpt_->valueRef();
2084 bool isValid1=true, isValid2=true;
2085 bool bClassDiagrams = convertStringToBool(classDiagramValue,isValid1);
2086 bool bHaveDot = haveDotValue.isEmpty() ? false : convertStringToBool(haveDotValue, isValid2);
2087 if (isValid1 && isValid2 && !bClassDiagrams && !bHaveDot && classGraphValue.lower()=="yes")
2089 warn_uncond("Changing CLASS_GRAPH option to TEXT because obsolete option CLASS_DIAGRAM was found and set to NO.\n");
2090 classGraphValue="TEXT";
2096 void Config::writeTemplate(TextStream &t,bool shortList,bool update)
2098 ConfigImpl::instance()->writeTemplate(t,shortList,update);
2101 void Config::compareDoxyfile(TextStream &t)
2103 postProcess(FALSE, TRUE);
2104 ConfigImpl::instance()->compareDoxyfile(t);
2107 void Config::writeXMLDoxyfile(TextStream &t)
2109 ConfigImpl::instance()->writeXMLDoxyfile(t);
2112 bool Config::parse(const QCString &fileName,bool update)
2114 bool parseRes = ConfigImpl::instance()->parse(fileName,update);
2115 if (!parseRes) return parseRes;
2117 // Internally we use the default format UTF-8 and
2118 // when updating etc. the output is in this format as well and not in the read format
2119 ConfigString *option = (ConfigString*)g_config->get("DOXYFILE_ENCODING");
2125 void Config::postProcess(bool clearHeaderAndFooter, bool compare)
2127 ConfigImpl::instance()->substituteEnvironmentVars();
2128 if (!compare)ConfigImpl::instance()->emptyValueToDefault();
2129 ConfigImpl::instance()->convertStrToVal();
2131 // avoid bootstrapping issues when the g_config file already
2132 // refers to the files that we are supposed to parse.
2133 if (clearHeaderAndFooter)
2135 Config_updateString(HTML_HEADER ,"");
2136 Config_updateString(HTML_FOOTER ,"");
2137 Config_updateString(LATEX_HEADER,"");
2138 Config_updateString(LATEX_FOOTER,"");
2142 void Config::deinit()
2144 ConfigImpl::instance()->deleteInstance();
2147 #if USE_STATE2STRING
2148 #include "configimpl.l.h"