Doxygen
Loading...
Searching...
No Matches
configimpl.l
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2020 by Dimitri van Heesch.
4 *
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.
10 *
11 */
12%option never-interactive
13%option prefix="configimplYY"
14%top{
15#include <stdint.h>
16}
17
18%{
19
20/*
21 * includes
22 */
23#include <stdio.h>
24#include <stdlib.h>
25#include <assert.h>
26#include <ctype.h>
27#include <stdarg.h>
28#include <errno.h>
29#include <thread>
30#include <algorithm>
31#include <iostream>
32#include <iomanip>
33#include <cstdint>
34
35#include "config.h"
36#include "regex.h"
37#include "configimpl.h"
38#include "version.h"
39#include "portable.h"
40#include "message.h"
41#include "language.h"
42#include "configoptions.h"
43#include "fileinfo.h"
44#include "dir.h"
45#include "textstream.h"
46#include "dotattributes.h"
47#include "debug.h"
48
49#define YY_NO_INPUT 1
50#define YY_NO_UNISTD_H 1
51
52// For debugging
53#define SHOW_INCLUDES 0
54
55[[maybe_unused]] static const char *stateToString(int state);
56
57static const char *warning_str = "warning: ";
58static const char *error_str = "error: ";
59
60void ConfigImpl::config_err(const char *fmt, ...)
61{
62 va_list args;
63 va_start(args, fmt);
64 vfprintf(stderr, qPrint(QCString(error_str) + fmt), args);
65 va_end(args);
66}
67
68void ConfigImpl::config_term(const char *fmt, ...)
69{
70 va_list args;
71 va_start(args, fmt);
72 vfprintf(stderr, qPrint(QCString(error_str) + fmt), args);
73 va_end(args);
74 fprintf(stderr, "%s\n", "Exiting...");
75 exit(1);
76}
77
78void ConfigImpl::config_warn(const char *fmt, ...)
79{
80 va_list args;
81 va_start(args, fmt);
82 vfprintf(stderr, qPrint(QCString(warning_str) + fmt), args);
83 va_end(args);
84}
85
87 const QCString &str,
88 const QCString &fromEncoding,
89 const QCString &toEncoding);
90
91static bool containsEnvVar(QCString &str);
92
93#define MAX_INCLUDE_DEPTH 10
94#define YY_NEVER_INTERACTIVE 1
95
96/* -----------------------------------------------------------------
97 */
98static QCString convertToComment(const QCString &s, const QCString &u)
99{
100 //printf("convertToComment(%s)=%s\n",qPrint(s),qPrint(u));
101 QCString result;
102 if (!s.isEmpty())
103 {
105 const char *p=tmp.data();
106 char c = 0;
107 if (p)
108 {
109 result+="#";
110 if (*p && *p!='\n')
111 {
112 result+=" ";
113 }
114 while ((c=*p++))
115 {
116 if (c=='\n')
117 {
118 result+="\n#";
119 if (*p && *p!='\n')
120 {
121 result+=" ";
122 }
123 }
124 else result+=c;
125 }
126 result+='\n';
127 }
128 }
129 if (!u.isEmpty())
130 {
131 if (!result.isEmpty()) result+='\n';
132 result+= u;
133 }
134 return result;
135}
136
137void ConfigOption::writeBoolValue(TextStream &t,bool v,bool initSpace)
138{
139 if (initSpace) t << " ";
140 if (v) t << "YES"; else t << "NO";
141}
142
143void ConfigOption::writeIntValue(TextStream &t,int i,bool initSpace)
144{
145 if (initSpace) t << " ";
146 t << i;
147}
148
149void ConfigOption::writeStringValue(TextStream &t,const QCString &s,bool initSpace, bool wasQuoted)
150{
151 char c = 0;
152 bool needsEscaping=wasQuoted;
153 // convert the string back to it original g_encoding
154 QCString se = configStringRecode(s,"UTF-8",m_encoding);
155 if (se.isEmpty()) return;
156 const char *p=se.data();
157 if (p)
158 {
159 if (initSpace) t << " ";
160 while ((c=*p++)!=0 && !needsEscaping)
161 needsEscaping = (c==' ' || c== ',' || c=='\n' || c=='\t' || c=='"' || c=='#');
162 if (needsEscaping)
163 {
164 t << "\"";
165 p=se.data();
166 while (*p)
167 {
168 if (*p==' ' && *(p+1)=='\0') break; // skip inserted space at the end
169 if (*p=='"') t << "\\"; // escape quotes
170 t << *p++;
171 }
172 t << "\"";
173 }
174 else
175 {
176 t << se;
177 }
178 }
179}
180
182{
183 bool first=TRUE;
184 for (const auto &p : l)
185 {
186 if (!first) t << " \\\n";
187 QCString s=p.c_str();
188 if (!first)
189 t << " ";
190 bool wasQuoted = ((s.at(0)=='"') && (s.at(s.length()-1)=='"'));
191 if (wasQuoted)
192 {
193 s = s.mid(1,s.length()-2);
194 }
195 writeStringValue(t,s,true,wasQuoted);
196 first=FALSE;
197 }
198}
199
200/* -----------------------------------------------------------------
201 */
202
203std::unique_ptr<ConfigImpl> ConfigImpl::m_instance;
204
206{
207 if (!m_valueString.isEmpty())
208 {
209 if (compareMode == Config::CompareMode::CompressedNoEnv)
210 {
211 if (containsEnvVar(m_valueString)) return;
212 }
213 bool ok = false;
214 int val = m_valueString.toInt(&ok);
215 if (!ok || val<m_minVal || val>m_maxVal)
216 {
217 ConfigImpl::config_warn("argument '%s' for option %s is not a valid number in the range [%d..%d]!\n"
218 "Using the default: %d!\n",qPrint(m_valueString),qPrint(m_name),m_minVal,m_maxVal,m_value);
219 }
220 else
221 {
222 m_value=val;
223 }
224 }
225}
226
227static bool convertStringToBool(const QCString &str,bool &isValid)
228{
229 isValid=false;
230 QCString val = str.stripWhiteSpace().lower();
231 if (!val.isEmpty())
232 {
233 if (val=="yes" || val=="true" || val=="1" || val=="all")
234 {
235 isValid=true;
236 return true;
237 }
238 else if (val=="no" || val=="false" || val=="0" || val=="none")
239 {
240 isValid=true;
241 return false;
242 }
243 }
244 return false;
245}
246
248{
249 if (!m_valueString.stripWhiteSpace().isEmpty())
250 {
251 if (compareMode == Config::CompareMode::CompressedNoEnv)
252 {
253 if (containsEnvVar(m_valueString)) return;
254 }
255 bool isValid=false;
256 bool b = convertStringToBool(m_valueString,isValid);
257 if (isValid)
258 {
259 m_value=b;
260 }
261 else
262 {
263 ConfigImpl::config_warn("argument '%s' for option %s is not a valid boolean value\n"
264 "Using the default: %s!\n",qPrint(m_valueString),qPrint(m_name),m_value?"YES":"NO");
265 }
266 }
267}
268
270{
271 if (m_value.isEmpty())
272 {
274 return;
275 }
276 if (compareMode == Config::CompareMode::CompressedNoEnv)
277 {
278 if (containsEnvVar(m_value)) return;
279 }
280 QCString val = m_value.stripWhiteSpace().lower();
281 for (const auto &s : m_valueRange)
282 {
283 if (s.lower() == val)
284 {
285 m_value = s;
286 return;
287 }
288 }
289
290 ConfigImpl::config_warn("argument '%s' for option %s is not a valid enum value\n"
291 "Using the default: %s!\n",qPrint(m_value),qPrint(m_name),qPrint(m_defValue));
293}
294
295QCString &ConfigImpl::getString(const char *fileName,int num,const char *name) const
296{
297 auto it = m_dict.find(name);
298 if (it==m_dict.end())
299 {
300 config_term("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
301 }
302 else if (it->second->kind()!=ConfigOption::O_String)
303 {
304 config_term("%s<%d>: Internal error: Requested option %s not of string type!\n",fileName,num,name);
305 }
306 return *(dynamic_cast<ConfigString *>(it->second))->valueRef();
307}
308
309StringVector &ConfigImpl::getList(const char *fileName,int num,const char *name) const
310{
311 auto it = m_dict.find(name);
312 if (it==m_dict.end())
313 {
314 config_term("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
315 }
316 else if (it->second->kind()!=ConfigOption::O_List)
317 {
318 config_term("%s<%d>: Internal error: Requested option %s not of list type!\n",fileName,num,name);
319 }
320 return *(dynamic_cast<ConfigList *>(it->second))->valueRef();
321}
322
323QCString &ConfigImpl::getEnum(const char *fileName,int num,const char *name) const
324{
325 auto it = m_dict.find(name);
326 if (it==m_dict.end())
327 {
328 config_term("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
329 }
330 else if (it->second->kind()!=ConfigOption::O_Enum)
331 {
332 config_term("%s<%d>: Internal error: Requested option %s not of enum type!\n",fileName,num,name);
333 }
334 return *(dynamic_cast<ConfigEnum *>(it->second))->valueRef();
335}
336
337int &ConfigImpl::getInt(const char *fileName,int num,const char *name) const
338{
339 auto it = m_dict.find(name);
340 if (it==m_dict.end())
341 {
342 config_term("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
343 }
344 else if (it->second->kind()!=ConfigOption::O_Int)
345 {
346 config_term("%s<%d>: Internal error: Requested option %s not of integer type!\n",fileName,num,name);
347 }
348 return *(dynamic_cast<ConfigInt *>(it->second))->valueRef();
349}
350
351bool &ConfigImpl::getBool(const char *fileName,int num,const char *name) const
352{
353 auto it = m_dict.find(name);
354 if (it==m_dict.end())
355 {
356 config_term("%s<%d>: Internal error: Requested unknown option %s!\n",fileName,num,name);
357 }
358 else if (it->second->kind()!=ConfigOption::O_Bool)
359 {
360 config_term("%s<%d>: Internal error: Requested option %s not of boolean type!\n",fileName,num,name);
361 }
362 return *(dynamic_cast<ConfigBool *>(it->second))->valueRef();
363}
364
365/* ------------------------------------------ */
366
368{
369 if (!sl)
370 {
371 t << "\n";
372 }
373 t << "#---------------------------------------------------------------------------\n";
374 t << "# " << m_doc << "\n";
375 t << "#---------------------------------------------------------------------------\n";
376}
377
379{
380 if (!sl)
381 {
382 t << "\n";
384 t << "\n";
385 }
386 else if (!m_userComment.isEmpty())
387 {
389 }
390 t << m_name << m_spaces.left(MAX_OPTION_LENGTH-m_name.length()) << "=";
392 t << "\n";
393}
394
396{
397 auto get_stripped = [](const std::string &s) { return QCString(s.c_str()).stripWhiteSpace(); };
398 auto is_not_empty = [get_stripped](const std::string &s) { return !get_stripped(s).isEmpty(); };
399 size_t defCnt = std::count_if( m_value.begin(), m_value.end(),is_not_empty);
400 size_t valCnt = std::count_if(m_defaultValue.begin(),m_defaultValue.end(),is_not_empty);
401 if ( valCnt != defCnt)
402 {
403 return false;
404 }
405 auto it1 = m_value.begin();
406 auto it2 = m_defaultValue.begin();
407 while (it1!=m_value.end() && it2!=m_defaultValue.end())
408 {
409 // skip over empty values
410 while (it1!=m_value.end() && !is_not_empty(*it1))
411 {
412 ++it1;
413 }
414 if (it1!=m_value.end()) // non-empty value
415 {
416 if (get_stripped(*it1) != get_stripped(*it2)) // not the default, write as difference
417 {
418 return false;
419 }
420 ++it1;
421 ++it2;
422 }
423 }
424 return true;
425}
426
431
433{
434 t << " <option id='" << m_name << "'";
435 t << " default='" << (isDefault() ? "yes" : "no") << "'";
436 t << " type='stringlist'";
437 t << ">";
438 t << "\n";
439 for (const auto &p : m_value)
440 {
441 QCString s=p.c_str();
442 t << " <value>";
443 t << "<![CDATA[";
444 writeStringValue(t,s,false);
445 t << "]]>";
446 t << "</value>\n";
447 }
448 t << " </option>\n";
449}
450
452{
453 t << " <xsd:enumeration value=\"" << m_name << "\"/>\n";
454}
455
457{
458 if (!sl)
459 {
460 t << "\n";
462 t << "\n";
463 }
464 else if (!m_userComment.isEmpty())
465 {
467 }
468 t << m_name << m_spaces.left(MAX_OPTION_LENGTH-m_name.length()) << "=";
470 t << "\n";
471}
472
477
479{
480 t << " <option id='" << m_name << "'";
481 t << " default='" << (isDefault() ? "yes" : "no") << "'";
482 t << " type='string'";
483 t << ">";
484 t << "<value>";
485 writeStringValue(t,m_value,false);
486 t << "</value>";
487 t << "</option>\n";
488}
489
491{
492 t << " <xsd:enumeration value=\"" << m_name << "\"/>\n";
493}
494
496{
497 if (!sl)
498 {
499 t << "\n";
501 t << "\n";
502 }
503 else if (!m_userComment.isEmpty())
504 {
506 }
507 t << m_name << m_spaces.left(MAX_OPTION_LENGTH-m_name.length()) << "=";
509 t << "\n";
510}
511
516
518{
519 t << " <option id='" << m_name << "'";
520 t << " default='" << (isDefault() ? "yes" : "no") << "'";
521 t << " type='string'";
522 t << ">";
523 t << "<value>";
524 t << "<![CDATA[";
525 writeStringValue(t,m_value,false);
526 t << "]]>";
527 t << "</value>";
528 t << "</option>\n";
529}
530
532{
533 t << " <xsd:enumeration value=\"" << m_name << "\"/>\n";
534}
535
536void ConfigInt::writeTemplate(TextStream &t,bool sl,bool upd)
537{
538 if (!sl)
539 {
540 t << "\n";
542 t << "\n";
543 }
544 else if (!m_userComment.isEmpty())
545 {
547 }
548 t << m_name << m_spaces.left(MAX_OPTION_LENGTH-m_name.length()) << "=";
549 if (upd && !m_valueString.isEmpty())
550 {
552 }
553 else
554 {
556 }
557 t << "\n";
558}
559
564
566{
567 t << " <option id='" << m_name << "'";
568 t << " default='" << (isDefault() ? "yes" : "no") << "'";
569 t << " type='int'";
570 t << ">";
571 t << "<value>";
572 writeIntValue(t,m_value,false);
573 t << "</value>";
574 t << "</option>\n";
575}
576
578{
579 t << " <xsd:enumeration value=\"" << m_name << "\"/>\n";
580}
581
582void ConfigBool::writeTemplate(TextStream &t,bool sl,bool upd)
583{
584 if (!sl)
585 {
586 t << "\n";
588 t << "\n";
589 }
590 else if (!m_userComment.isEmpty())
591 {
593 }
594 QCString spaces = m_spaces.left(MAX_OPTION_LENGTH-m_name.length());
595 t << m_name << spaces << "=";
596 if (upd && !m_valueString.isEmpty())
597 {
599 }
600 else
601 {
603 }
604 t << "\n";
605}
606
611
613{
614 t << " <option id='" << m_name << "'";
615 t << " default='" << (isDefault() ? "yes" : "no") << "'";
616 t << " type='bool'";
617 t << ">";
618 t << "<value>";
619 writeBoolValue(t,m_value,false);
620 t << "</value>";
621 t << "</option>\n";
622}
623
625{
626 t << " <xsd:enumeration value=\"" << m_name << "\"/>\n";
627}
628
630
633{
634 t << " <xsd:enumeration value=\"" << m_name << "\"/>\n";
635}
636
637/* -----------------------------------------------------------------
638 *
639 * static variables
640 */
641
643{
644 int lineNr = 1;
645 FILE *filePtr = nullptr;
646 YY_BUFFER_STATE oldState;
647 YY_BUFFER_STATE newState;
648 QCString fileName;
649};
650
651static const char *g_inputString = nullptr;
652static int g_inputPosition = 0;
653static int g_yyLineNr = 1;
656static QCString *g_string = nullptr;
657static StringVector *g_list = nullptr;
660static std::vector< std::unique_ptr<ConfigFileState> > g_includeStack;
661static bool g_configUpdate = FALSE;
663static ConfigImpl *g_config = nullptr;
666
667#define unput_string(yytext,yyleng) do { for (int i=(int)yyleng-1;i>=0;i--) unput(yytext[i]); } while(0)
668/* -----------------------------------------------------------------
669 */
670#undef YY_INPUT
671#define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size);
672
673// otherwise the filename would be the name of the converted file (*.cpp instead of *.l)
674static inline const char *getLexerFILE() {return __FILE__;}
675#define LEX_NO_REENTRANT
676#include "doxygen_lex.h"
677
678static int yyread(char *buf,int max_size)
679{
680 // no file included
681 if (g_includeStack.empty())
682 {
683 int c=0;
684 if (g_inputString==0) return c;
685 while( c < max_size && g_inputString[g_inputPosition] )
686 {
688 c++; buf++;
689 }
690 return c;
691 }
692 else
693 {
694 //assert(g_includeStack.current()->newState==YY_CURRENT_BUFFER);
695 return static_cast<int>(fread(buf,1,max_size,g_includeStack.back()->filePtr));
696 }
697}
698
699
701 const QCString &str,
702 const QCString &inputEncoding,
703 const QCString &outputEncoding)
704{
705 if (inputEncoding.isEmpty() || outputEncoding.isEmpty() || inputEncoding==outputEncoding) return str;
706 size_t inputSize=str.length();
707 size_t outputSize=inputSize*4;
708 QCString output(outputSize, QCString::ExplicitSize);
709 void *cd = portable_iconv_open(outputEncoding.data(),inputEncoding.data());
710 if (cd==reinterpret_cast<void *>(-1))
711 {
712 ConfigImpl::config_term("Error: unsupported character conversion: '%s'->'%s'\n"
713 "Check the 'DOXYFILE_ENCODING' setting in the config file!\n",
714 qPrint(inputEncoding),qPrint(outputEncoding));
715 }
716 size_t iLeft=inputSize;
717 size_t oLeft=outputSize;
718 const char *inputPtr = str.data();
719 char *outputPtr = output.rawData();
720 if (!portable_iconv(cd, &inputPtr, &iLeft, &outputPtr, &oLeft))
721 {
722 outputSize-=oLeft;
723 output.resize(outputSize);
724 output.at(outputSize)='\0';
725 //printf("iconv: input size=%d output size=%d\n[%s]\n",size,newSize,qPrint(srcBuf));
726 }
727 else
728 {
729 ConfigImpl::config_term("Error: failed to translate characters from %s to %s: %s\n",
730 qPrint(inputEncoding),qPrint(outputEncoding),strerror(errno));
731 }
733 return output;
734}
735
736static void checkEncoding()
737{
738 ConfigString *option = dynamic_cast<ConfigString*>(g_config->get("DOXYFILE_ENCODING"));
739 g_encoding = *option->valueRef();
740}
741
743{
744 // check if there is a comment at the end of the string
745 bool insideQuote=false;
746 size_t l = s.length();
747 for (size_t i=0;i<l;i++)
748 {
749 char c = s.at(i);
750 if (c=='\\') // skip over escaped characters
751 {
752 i++;
753 }
754 else if (c=='"') // toggle inside/outside quotation
755 {
756 insideQuote=!insideQuote;
757 }
758 else if (!insideQuote && c=='#') // found start of a comment
759 {
760 if (i<l-1 && s.at(i+1)=='#') // ## -> user comment
761 {
762 g_config->appendUserComment(s.mid(i)+"\n");
763 }
764 return s.left(i).stripWhiteSpace();
765 }
766 }
767 return s;
768}
769
770static void processStoreRepl(QCString &storeReplStr)
771{
772 // strip leading and trailing whitespace
773 QCString s = stripComment(storeReplStr.stripWhiteSpace());
774 // recode the string
775 storeReplStr=configStringRecode(s,g_encoding,"UTF-8");
776}
777
778static void processString()
779{
780 // strip leading and trailing whitespace
781 QCString s = stripComment(g_string->stripWhiteSpace());
782 size_t l = s.length();
783
784 // remove surrounding quotes if present (and not escaped)
785 if (l>=2 && s.at(0)=='"' && s.at(l-1)=='"' && // remove quotes
786 (s.at(l-2)!='\\' || (s.at(l-2)=='\\' && s.at(l-3)=='\\')))
787 {
788 s=s.mid(1,s.length()-2);
789 l=s.length();
790 }
791
792 // check for invalid and/or escaped quotes
793 bool warned=false;
794 QCString result;
795 for (size_t i=0;i<l;i++)
796 {
797 char c = s.at(i);
798 if (c=='\\') // escaped character
799 {
800 if (i<l-1 && s.at(i+1)=='"') // unescape the quote character
801 {
802 result+='"';
803 }
804 else // keep other escaped characters in escaped form
805 {
806 result+=c;
807 if (i<l-1)
808 {
809 result+=s.at(i+1);
810 }
811 }
812 i++; // skip over the escaped character
813 }
814 else if (c=='"') // unescaped quote
815 {
816 if (!warned)
817 {
818 ConfigImpl::config_warn("Invalid value for '%s' tag at line %d, file %s: Value '%s' is not properly quoted\n",
820 }
821 warned=true;
822 }
823 else // normal character
824 {
825 result+=c;
826 }
827 }
828
829 // recode the string
830 *g_string=configStringRecode(result,g_encoding,"UTF-8");
831
832 // update encoding
834
835 //printf("Processed string '%s'\n",qPrint(g_string));
836}
837
838static void processList()
839{
840 bool allowCommaAsSeparator = g_cmd!="PREDEFINED";
841
842 QCString s = stripComment(g_listStr.stripWhiteSpace());
843 size_t l = s.length();
844
845 QCString elemStr;
846 bool wasQuote=false;
847
848 // helper to push elemStr to the list and clear it
849 auto addElem = [&elemStr,&wasQuote]()
850 {
851 if (!elemStr.isEmpty())
852 {
853 QCString e = configStringRecode(elemStr,g_encoding,"UTF-8");
854 //printf("Processed list element '%s'\n",qPrint(e));
855 if (wasQuote) e = "\""+e+"\"";
856 wasQuote = false;
857 g_list->push_back(e.str());
858 elemStr="";
859 }
860 };
861
862 bool needsSeparator=false;
863 bool insideQuote=false;
864 bool warned=false;
865 for (size_t i=0;i<l;i++)
866 {
867 char c = s.at(i);
868 if (!needsSeparator && c=='\\') // escaped character
869 {
870 if (i<l-1 && s.at(i+1)=='"') // unescape the quote character
871 {
872 elemStr+='"';
873 }
874 else // keep other escaped characters in escaped form
875 {
876 elemStr+=c;
877 if (i<l-1)
878 {
879 elemStr+=s.at(i+1);
880 }
881 }
882 i++; // skip over the escaped character
883 }
884 else if (!needsSeparator && c=='"') // quote character
885 {
886 if (!insideQuote)
887 {
888 insideQuote=true;
889 wasQuote=true;
890 }
891 else // this quote ends an element
892 {
893 insideQuote=false;
894 needsSeparator=true;
895 }
896 }
897 else if (!insideQuote && ((c==',' && allowCommaAsSeparator) || isspace(c))) // separator
898 {
899 needsSeparator=false;
900 addElem();
901 }
902 else // normal content character
903 {
904 if (needsSeparator)
905 {
906 if (!warned)
907 {
908 ConfigImpl::config_warn("Invalid value for '%s' tag at line %d, file %s: Values in list '%s' are not properly space %sseparated\n",
909 qPrint(g_cmd),g_yyLineNr,qPrint(g_yyFileName),qPrint(g_listStr.stripWhiteSpace()),allowCommaAsSeparator?"or comma ":"");
910 warned=true;
911 }
912 needsSeparator=false;
913 i--; // try the character again as part of a new element
914 addElem();
915 }
916 else
917 {
918 elemStr+=c;
919 }
920 }
921 }
922 // add last part
923 addElem();
924 if (insideQuote)
925 {
926 ConfigImpl::config_warn("Invalid value for '%s' tag at line %d, file %s: Values in list '%s' are not properly quoted\n",
928 }
929}
930
931static FILE *tryPath(const QCString &path,const QCString &fileName)
932{
933 QCString absName=(!path.isEmpty() ? path+"/"+fileName : fileName);
934 FileInfo fi(absName.str());
935 if (fi.exists() && fi.isFile())
936 {
937 FILE *f=Portable::fopen(absName,"r");
938 if (!f) ConfigImpl::config_err("could not open file %s for reading\n",qPrint(absName));
939 return f;
940 }
941 return 0;
942}
943
944static void substEnvVarsInStrList(StringVector &sl);
945static void substEnvVarsInString(QCString &s);
946
947static FILE *findFile(const QCString &fileName)
948{
949 if (fileName.isEmpty())
950 {
951 return 0;
952 }
953 if (Portable::isAbsolutePath(fileName))
954 {
955 return tryPath(QCString(), fileName);
956 }
958 for (const auto &s : g_includePathList)
959 {
960 FILE *f = tryPath(s.c_str(),fileName);
961 if (f) return f;
962 }
963 // try cwd if g_includePathList fails
964 return tryPath(".",fileName);
965}
966
967static void readIncludeFile(const QCString &incName)
968{
969 if (g_includeStack.size()==MAX_INCLUDE_DEPTH) {
970 ConfigImpl::config_term("maximum include depth (%d) reached, %s is not included. Aborting...\n",
971 MAX_INCLUDE_DEPTH,qPrint(incName));
972 }
973
974 QCString inc = incName;
976 inc = inc.stripWhiteSpace();
977 size_t incLen = inc.length();
978 if (incLen>0 && inc.at(0)=='"' && inc.at(incLen-1)=='"') // strip quotes
979 {
980 inc=inc.mid(1,incLen-2);
981 }
982
983 FILE *f;
984
985 if ((f=findFile(inc))) // see if the include file can be found
986 {
987 // For debugging
988#if SHOW_INCLUDES
989 for (size_t i=0;i<g_includeStack.size();i++) msg(" ");
990 msg("@INCLUDE = %s: parsing...\n",qPrint(inc));
991#endif
992
993 // store the state of the old file
995 fs->oldState=YY_CURRENT_BUFFER;
996 fs->lineNr=g_yyLineNr;
998 fs->filePtr=f;
999 // push the state on the stack
1000 g_includeStack.push_back(std::unique_ptr<ConfigFileState>(fs));
1001 // set the scanner to the include file
1002 yy_switch_to_buffer(yy_create_buffer(f, YY_BUF_SIZE));
1003 fs->newState=YY_CURRENT_BUFFER;
1004 g_yyFileName=inc;
1005 }
1006 else
1007 {
1008 ConfigImpl::config_term("@INCLUDE = %s: not found!\n",qPrint(inc));
1009 }
1010}
1011
1012
void writeTemplate(TextStream &t, bool sl, bool upd) override
Definition configimpl.l:582
bool isDefault() override
Definition configimpl.h:274
void writeXMLDoxyfile(TextStream &t) override
Definition configimpl.l:612
void writeXSDDoxyfile(TextStream &t) override
Definition configimpl.l:624
void compareDoxyfile(TextStream &t, Config::CompareMode compareMode) override
Definition configimpl.l:607
void convertStrToVal(Config::CompareMode compareMode) override
Definition configimpl.l:247
QCString m_valueString
Definition configimpl.h:278
void writeXSDDoxyfile(TextStream &) override
Definition configimpl.l:632
void writeTemplate(TextStream &, bool, bool) override
Definition configimpl.l:631
void writeTemplate(TextStream &t, bool sl, bool) override
Definition configimpl.l:456
QCString m_defValue
Definition configimpl.h:181
void writeXSDDoxyfile(TextStream &t) override
Definition configimpl.l:490
void compareDoxyfile(TextStream &t, Config::CompareMode compareMode) override
Definition configimpl.l:473
bool isDefault() override
Definition configimpl.h:176
void writeXMLDoxyfile(TextStream &t) override
Definition configimpl.l:478
std::vector< QCString > m_valueRange
Definition configimpl.h:179
void convertStrToVal(Config::CompareMode compareMode) override
Definition configimpl.l:269
QCString m_value
Definition configimpl.h:180
Singleton for configuration variables.
Definition configimpl.h:342
static void config_term(const char *fmt,...)
Definition configimpl.l:68
ConfigOptionMap m_dict
Definition configimpl.h:617
static void config_warn(const char *fmt,...)
Definition configimpl.l:78
QCString & getEnum(const char *fileName, int num, const char *name) const
Definition configimpl.l:323
static void config_err(const char *fmt,...)
Definition configimpl.l:60
StringVector & getList(const char *fileName, int num, const char *name) const
Definition configimpl.l:309
static std::unique_ptr< ConfigImpl > m_instance
Definition configimpl.h:618
bool & getBool(const char *fileName, int num, const char *name) const
Definition configimpl.l:351
int & getInt(const char *fileName, int num, const char *name) const
Definition configimpl.l:337
QCString & getString(const char *fileName, int num, const char *name) const
Definition configimpl.l:295
void writeTemplate(TextStream &t, bool sl, bool) override
Definition configimpl.l:367
bool isDefault() override
Definition configimpl.h:242
void writeXMLDoxyfile(TextStream &t) override
Definition configimpl.l:565
void convertStrToVal(Config::CompareMode compareMode) override
Definition configimpl.l:205
void writeXSDDoxyfile(TextStream &t) override
Definition configimpl.l:577
void compareDoxyfile(TextStream &t, Config::CompareMode compareMode) override
Definition configimpl.l:560
QCString m_valueString
Definition configimpl.h:248
void writeTemplate(TextStream &t, bool sl, bool upd) override
Definition configimpl.l:536
void writeTemplate(TextStream &t, bool sl, bool) override
Definition configimpl.l:378
bool isDefault() override
Definition configimpl.l:395
StringVector m_defaultValue
Definition configimpl.h:149
StringVector m_value
Definition configimpl.h:148
void writeXMLDoxyfile(TextStream &t) override
Definition configimpl.l:432
void writeXSDDoxyfile(TextStream &t) override
Definition configimpl.l:451
void compareDoxyfile(TextStream &t, Config::CompareMode compareMode) override
Definition configimpl.l:427
void writeTemplate(TextStream &, bool, bool) override
Definition configimpl.l:629
QCString m_doc
Definition configimpl.h:96
void writeStringValue(TextStream &t, const QCString &s, bool initSpace=true, bool wasQuoted=false)
Definition configimpl.l:149
QCString m_encoding
Definition configimpl.h:98
QCString m_name
Definition configimpl.h:95
QCString m_userComment
Definition configimpl.h:99
void writeIntValue(TextStream &t, int i, bool initSpace=true)
Definition configimpl.l:143
void writeStringList(TextStream &t, const StringVector &l)
Definition configimpl.l:181
QCString m_spaces
Definition configimpl.h:94
@ O_List
A list of items.
Definition configimpl.h:48
@ O_Enum
A fixed set of items.
Definition configimpl.h:49
@ O_Bool
A boolean value.
Definition configimpl.h:52
@ O_String
A single item.
Definition configimpl.h:50
@ O_Int
An integer value.
Definition configimpl.h:51
void writeBoolValue(TextStream &t, bool v, bool initSpace=true)
Definition configimpl.l:137
Class representing a string type option.
Definition configimpl.h:187
QCString m_value
Definition configimpl.h:211
bool isDefault() override
Definition configimpl.h:208
void writeXSDDoxyfile(TextStream &t) override
Definition configimpl.l:531
void writeXMLDoxyfile(TextStream &t) override
Definition configimpl.l:517
QCString * valueRef()
Definition configimpl.h:200
void writeTemplate(TextStream &t, bool sl, bool) override
Definition configimpl.l:495
void compareDoxyfile(TextStream &t, Config::CompareMode compareMode) override
Definition configimpl.l:512
Minimal replacement for QFileInfo.
Definition fileinfo.h:23
bool exists() const
Definition fileinfo.cpp:30
bool isFile() const
Definition fileinfo.cpp:63
This is an alternative implementation of QCString.
Definition qcstring.h:101
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:153
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:226
QCString lower() const
Definition qcstring.h:234
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:567
char * rawData()
Returns a writable pointer to the data.
Definition qcstring.h:165
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:150
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition qcstring.h:245
void resize(size_t newlen)
Definition qcstring.h:167
const std::string & str() const
Definition qcstring.h:526
@ ExplicitSize
Definition qcstring.h:133
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition qcstring.h:159
QCString left(size_t len) const
Definition qcstring.h:214
Text streaming class that buffers data.
Definition textstream.h:36
#define YY_BUF_SIZE
Definition commentcnv.l:19
static QCString * g_string
Definition configimpl.l:656
static void processList()
Definition configimpl.l:838
static QCString g_encoding
Definition configimpl.l:662
static QCString g_localStoreRepl
Definition configimpl.l:665
static void checkEncoding()
Definition configimpl.l:736
static QCString convertToComment(const QCString &s, const QCString &u)
Definition configimpl.l:98
static const char * g_inputString
Definition configimpl.l:651
static void processStoreRepl(QCString &storeReplStr)
Definition configimpl.l:770
static const char * warning_str
Definition configimpl.l:57
static bool containsEnvVar(QCString &str)
static std::vector< std::unique_ptr< ConfigFileState > > g_includeStack
Definition configimpl.l:660
static void processString()
Definition configimpl.l:778
static int g_inputPosition
Definition configimpl.l:652
static FILE * findFile(const QCString &fileName)
Definition configimpl.l:947
#define MAX_INCLUDE_DEPTH
Definition configimpl.l:93
static const char * stateToString(int state)
static void readIncludeFile(const QCString &incName)
Definition configimpl.l:967
static QCString configStringRecode(const QCString &str, const QCString &fromEncoding, const QCString &toEncoding)
Definition configimpl.l:700
static StringVector * g_list
Definition configimpl.l:657
static bool g_configUpdate
Definition configimpl.l:661
static bool convertStringToBool(const QCString &str, bool &isValid)
Definition configimpl.l:227
static QCString g_cmd
Definition configimpl.l:655
static StringVector g_includePathList
Definition configimpl.l:659
static QCString stripComment(const QCString &s)
Definition configimpl.l:742
static void substEnvVarsInStrList(StringVector &sl)
static FILE * tryPath(const QCString &path, const QCString &fileName)
Definition configimpl.l:931
static Config::CompareMode g_compareMode
Definition configimpl.l:664
static ConfigImpl * g_config
Definition configimpl.l:663
static const char * getLexerFILE()
Definition configimpl.l:674
static QCString g_listStr
Definition configimpl.l:658
static int yyread(char *buf, int max_size)
Definition configimpl.l:678
static int g_yyLineNr
Definition configimpl.l:653
static const char * error_str
Definition configimpl.l:58
static void substEnvVarsInString(QCString &s)
static QCString g_yyFileName
Definition configimpl.l:654
std::vector< std::string > StringVector
Definition containers.h:33
void msg(const char *fmt,...)
Definition message.cpp:98
CompareMode
Definition config.h:54
bool isAbsolutePath(const QCString &fileName)
Definition portable.cpp:514
FILE * fopen(const QCString &fileName, const QCString &mode)
Definition portable.cpp:366
Definition trace.h:153
Portable versions of functions that are platform dependent.
int portable_iconv_close(void *cd)
size_t portable_iconv(void *cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
void * portable_iconv_open(const char *tocode, const char *fromcode)
const char * qPrint(const char *s)
Definition qcstring.h:661
#define TRUE
Definition qcstring.h:37
#define FALSE
Definition qcstring.h:34
YY_BUFFER_STATE newState
Definition configimpl.l:647
QCString fileName
Definition configimpl.l:648
YY_BUFFER_STATE oldState
Definition configimpl.l:646
1013%}
1014
1015%option noyywrap
1016
1017REGEX_a [a-z_A-Z\x80-\xFF]
1018REGEX_w [a-z_A-Z0-9\x80-\xFF]
1019
1020%x Start
1021%x SkipInvalid
1022%x GetString
1023%x GetStrList
1024%x Include
1025%x StoreRepl
1026
1028
1029<*>\0x0d
1030
1031 /*-------------- Comments ---------------*/
1032
1033<Start>"##".*"\n" {
1034 g_config->appendUserComment(yytext);
1035 g_yyLineNr++;
1036 }
1037<Start>"#".*"\n" { /* normal comment */
1038 g_yyLineNr++;
1039 }
1040
1041 /*-------------- TAG start ---------------*/
1042
1043<Start>[a-z_A-Z][a-z_A-Z0-9]*[ \t]*"=" { g_cmd=yytext;
1044 g_cmd=g_cmd.left(g_cmd.length()-1).stripWhiteSpace();
1045 ConfigOption *option = g_config->get(g_cmd);
1046 if (option==0) // oops not known
1047 {
1048 ConfigImpl::config_warn("ignoring unsupported tag '%s' at line %d, file %s\n",
1050 BEGIN(SkipInvalid);
1051 }
1052 else // known tag
1053 {
1054 option->setUserComment(g_config->takeUserComment());
1055 option->setEncoding(g_encoding);
1056 switch(option->kind())
1057 {
1059 // shouldn't get here!
1060 BEGIN(SkipInvalid);
1061 break;
1063 g_list = dynamic_cast<ConfigList *>(option)->valueRef();
1064 g_list->clear();
1065 g_listStr="";
1066 BEGIN(GetStrList);
1067 break;
1069 g_string = dynamic_cast<ConfigEnum *>(option)->valueRef();
1070 g_string->clear();
1071 BEGIN(GetString);
1072 break;
1074 g_string = dynamic_cast<ConfigString *>(option)->valueRef();
1075 g_string->clear();
1076 BEGIN(GetString);
1077 break;
1079 g_string = dynamic_cast<ConfigInt *>(option)->valueStringRef();
1080 g_string->clear();
1081 BEGIN(GetString);
1082 break;
1084 g_string = dynamic_cast<ConfigBool *>(option)->valueStringRef();
1085 g_string->clear();
1086 BEGIN(GetString);
1087 break;
1089 if (g_configUpdate)
1090 {
1091 ConfigImpl::config_warn("Tag '%s' at line %d of file '%s' has become obsolete.\n"
1092 " This tag has been removed.\n", qPrint(g_cmd),g_yyLineNr,qPrint(g_yyFileName));
1093 }
1094 else
1095 {
1096 ConfigImpl::config_warn("Tag '%s' at line %d of file '%s' has become obsolete.\n"
1097 " To avoid this warning please remove this line from your configuration "
1098 "file or upgrade it using \"doxygen -u\"\n", qPrint(g_cmd),g_yyLineNr,qPrint(g_yyFileName));
1099 }
1100 dynamic_cast<ConfigObsolete*>(option)->markAsPresent();
1101 if (dynamic_cast<ConfigObsolete*>(option)->orgType()==ConfigOption::O_List)
1102 {
1103 g_list = dynamic_cast<ConfigObsolete*>(option)->valueListRef();
1104 g_list->clear();
1105 g_listStr="";
1106 BEGIN(GetStrList);
1107 }
1108 else
1109 {
1110 g_string = dynamic_cast<ConfigObsolete*>(option)->valueStringRef();
1111 g_string->clear();
1112 BEGIN(GetString);
1113 }
1114 break;
1116 if (g_configUpdate)
1117 {
1118 ConfigImpl::config_warn("Tag '%s' at line %d of file '%s' belongs to an option that was not enabled at compile time.\n"
1119 " This tag has been removed.\n", qPrint(g_cmd),g_yyLineNr,qPrint(g_yyFileName));
1120 }
1121 else
1122 {
1123 ConfigImpl::config_warn("Tag '%s' at line %d of file '%s' belongs to an option that was not enabled at compile time.\n"
1124 " To avoid this warning please remove this line from your configuration "
1125 "file or upgrade it using \"doxygen -u\", or recompile doxygen with this feature enabled.\n", qPrint(g_cmd),g_yyLineNr,qPrint(g_yyFileName));
1126 }
1127 BEGIN(SkipInvalid);
1128 break;
1129 }
1130 }
1131 }
Class representing a Boolean type option.
Definition configimpl.h:254
Class representing an enum type option.
Definition configimpl.h:156
Class representing an integer type option.
Definition configimpl.h:219
Class representing a list type option.
Definition configimpl.h:124
Section marker for obsolete options.
Definition configimpl.h:284
Abstract base class for any configuration option.
Definition configimpl.h:38
void setEncoding(const QCString &e)
Definition configimpl.h:75
void setUserComment(const QCString &u)
Definition configimpl.h:76
@ O_Disabled
Disabled compile time option.
Definition configimpl.h:54
@ O_Obsolete
An obsolete option.
Definition configimpl.h:53
@ O_Info
A section header.
Definition configimpl.h:47
OptionType kind() const
Definition configimpl.h:69
1132<Start>[a-z_A-Z][a-z_A-Z0-9]*[ \t]*"+=" { g_cmd=yytext;
1133 g_cmd=g_cmd.left(g_cmd.length()-2).stripWhiteSpace();
1134 ConfigOption *option = g_config->get(g_cmd);
1135 if (option==0) // oops not known
1136 {
1137 ConfigImpl::config_warn("ignoring unsupported tag '%s' at line %d, file %s\n",
1139 BEGIN(SkipInvalid);
1140 }
1141 else // known tag
1142 {
1143 option->setUserComment(g_config->takeUserComment());
1144 switch(option->kind())
1145 {
1147 // shouldn't get here!
1148 BEGIN(SkipInvalid);
1149 break;
1151 g_list = dynamic_cast<ConfigList *>(option)->valueRef();
1152 g_listStr="";
1153 BEGIN(GetStrList);
1154 break;
1159 ConfigImpl::config_warn("operator += not supported for '%s'. Ignoring line at line %d, file %s\n",
1161 BEGIN(SkipInvalid);
1162 break;
1164 ConfigImpl::config_warn("Tag '%s' at line %d of file %s has become obsolete.\n"
1165 "To avoid this warning please update your configuration "
1166 "file using \"doxygen -u\"\n", qPrint(g_cmd),g_yyLineNr,qPrint(g_yyFileName));
1167 if (dynamic_cast<ConfigObsolete*>(option)->orgType()==ConfigOption::O_List)
1168 {
1169 g_list = dynamic_cast<ConfigObsolete*>(option)->valueListRef();
1170 g_listStr="";
1171 BEGIN(GetStrList);
1172 }
1173 else
1174 {
1175 BEGIN(SkipInvalid);
1176 }
1177 break;
1179 ConfigImpl::config_warn("Tag '%s' at line %d of file %s belongs to an option that was not enabled at compile time.\n"
1180 "To avoid this warning please remove this line from your configuration "
1181 "file, upgrade it using \"doxygen -u\", or recompile doxygen with this feature enabled.\n", qPrint(g_cmd),g_yyLineNr,qPrint(g_yyFileName));
1182 BEGIN(SkipInvalid);
1183 break;
1184 }
1185 }
1186 }
1187
1188 /*-------------- INCLUDE* ---------------*/
1189
1190<Start>"@INCLUDE_PATH"[ \t]*"=" { BEGIN(GetStrList); g_list=&g_includePathList; g_list->clear(); g_listStr=""; }
1191 /* include a g_config file */
1192<Start>"@INCLUDE"[ \t]*"=" { BEGIN(Include);}
1193<Start>"$("{REGEX_a}({REGEX_w}|[.-])*")" | // e.g. $(HOME)
1194<Start>"$("{REGEX_a}({REGEX_w}|[.-])*"("{REGEX_a}({REGEX_w}|[.-])*"))" { // e.g. $(PROGRAMFILES(X86))
1195 g_localStoreRepl = yytext;
1197 {
1198 BEGIN(StoreRepl);
1199 }
1200 else
1201 {
1204 }
1205 }
#define unput_string(yytext, yyleng)
1206<Start>"@"{REGEX_a}{REGEX_w}*"@" {
1208 {
1209 g_localStoreRepl = yytext;
1210 BEGIN(StoreRepl);
1211 }
1212 else
1213 {
1214 ConfigImpl::config_warn("ignoring unknown '%s' at line %d, file %s\n",
1216 }
1217 }
1218<Include>([^ \"\t\r\n]+)|("\""[^\n\"]+"\"") {
1220 BEGIN(Start);
1221 }
static bool readIncludeFile(yyscan_t yyscanner, const QCString &inc, const QCString &blockId)
1222<<EOF>> {
1223 //printf("End of include file\n");
1224 //printf("Include stack depth=%d\n",g_includeStack.count());
1225 if (g_includeStack.empty())
1226 {
1227 //printf("Terminating scanner!\n");
1228 yyterminate();
1229 }
1230 else
1231 {
1232 auto &fs=g_includeStack.back();
1233 fclose(fs->filePtr);
1234 YY_BUFFER_STATE oldBuf = YY_CURRENT_BUFFER;
1235 yy_switch_to_buffer( fs->oldState );
1236 yy_delete_buffer( oldBuf );
1237 g_yyLineNr=fs->lineNr;
1238 g_yyFileName=fs->fileName;
1239 g_includeStack.pop_back();
1240 }
1241 }
#define yyterminate()
int fclose(FILE *f)
Definition portable.cpp:386
1242
1243<Start>[a-z_A-Z0-9]+ { ConfigImpl::config_warn("ignoring unknown tag '%s' at line %d, file %s\n",yytext,g_yyLineNr,qPrint(g_yyFileName)); }
1244 /*-------------- GetString ---------------*/
1245
1246<StoreRepl>\n {
1247 g_localStoreRepl += yytext;
1249 g_config->appendStoreRepl(g_localStoreRepl + "\n");
1250 g_localStoreRepl.clear();
1251 g_yyLineNr++; // end of string
1252 BEGIN(Start);
1253 }
1254<StoreRepl>\\[ \r\t]*\n { g_yyLineNr++; // line continuation
1255 g_localStoreRepl += yytext;
1256 }
1257<StoreRepl>"\\" { // escape character
1258 g_localStoreRepl += yytext;
1259 }
1260<StoreRepl>[^\n\\]+ { // string part without escape characters
1261 g_localStoreRepl += yytext;
1262 }
1263 /*-------------- GetString ---------------*/
1264
1265<GetString>\n { processString();
1266 g_yyLineNr++; // end of string
1267 BEGIN(Start);
1268 }
1269<GetString>\\[ \r\t]*\n { g_yyLineNr++; // line continuation
1270 *g_string+=' ';
1271 }
1272<GetString>"\\" { // escape character
1273 *g_string+=yytext;
1274 }
1275<GetString>[^\n\\]+ { // string part without escape characters
1276 *g_string+=yytext;
1277 }
1278
1279 /*-------------- GetStrList --------------*/
1280
1281<GetStrList>\n { processList();
1282 g_yyLineNr++; // end of list
1283 BEGIN(Start);
1284 }
1285<GetStrList>\\[ \r\t]*\n { g_yyLineNr++; // line continuation
1286 g_listStr+=' ';
1287 }
1288<GetStrList>"\\" { // escape character
1289 g_listStr+=yytext;
1290 }
1291<GetStrList>[^\n\\]+ { // string part without escape characters
1292 g_listStr+=yytext;
1293 }
1294
1295 /*-------------- SkipInvalid --------------*/
1296
1297<SkipInvalid>\n { g_yyLineNr++; // end of list
1298 BEGIN(Start);
1299 }
1300<SkipInvalid>\\[ \r\t]*\n { g_yyLineNr++; // line continuation
1301 }
1302<SkipInvalid>"\\" { // escape character
1303 }
1304<SkipInvalid>[^\n\\]+ { // string part without escape characters
1305 }
1306
1307 /*-------------- fall through -------------*/
1308
1309<*>\\[ \r\t]*\n { g_yyLineNr++; }
1310<*>[ \t\r]
1311<*>\n { g_yyLineNr++ ; }
1312<*>. { ConfigImpl::config_warn("ignoring unknown character '%c' at line %d, file %s\n",yytext[0],g_yyLineNr,qPrint(g_yyFileName)); }
1313
1314%%
1315
1316/*@ ----------------------------------------------------------------------------
1317 */
1318
1319void ConfigImpl::writeTemplate(TextStream &t,bool sl,bool upd)
1320{
1321 /* print first lines of user comment that were at the beginning of the file, might have special meaning for editors */
1322 if (!m_startComment.isEmpty())
1323 {
1324 t << takeStartComment() << "\n";
1325 }
1326 t << "# Doxyfile " << getDoxygenVersion() << "\n\n";
1327 if (!sl)
1328 {
1329 t << convertToComment(m_header,"");
1330 }
1331 for (const auto &option : m_options)
1332 {
1333 option->writeTemplate(t,sl,upd);
1334 }
1335 /* print last lines of user comment that were at the end of the file */
1336 if (!m_userComment.isEmpty())
1337 {
1338 t << "\n";
1339 t << takeUserComment();
1340 }
1341}
1342
1344{
1345 t << "# Difference with default Doxyfile " << getFullVersion();
1346 t << "\n";
1347 for (const auto &option : m_options)
1348 {
1349 option->m_userComment = "";
1350 option->compareDoxyfile(t,compareMode);
1351 }
1352 if (!m_storeRepl.isEmpty())
1353 {
1354 t << "\n";
1355 t << takeStoreRepl() << "\n";
1356 }
1357}
1358
1360{
1361 t << "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n";
1362 t << "<doxyfile xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"doxyfile.xsd\" version=\"" << getDoxygenVersion() << "\" xml:lang=\"" << theTranslator->trISOLang() << "\">\n";
1363 for (const auto &option : m_options)
1364 {
1365 option->writeXMLDoxyfile(t);
1366 }
1367 t << "</doxyfile>\n";
1368}
1369
1371{
1372 for (const auto &option : m_options)
1373 {
1374 option->writeXSDDoxyfile(t);
1375 }
1376 for (const auto &option : m_disabled)
1377 {
1378 option->writeXSDDoxyfile(t);
1379 }
1380}
1381
1383{
1384 for (const auto &option : m_options)
1385 {
1386 option->convertStrToVal(compareMode);
1387 }
1388}
1390{
1391 for (const auto &option : m_options)
1392 {
1393 option->emptyValueToDefault();
1394 }
1395}
1396
1397static const reg::Ex reEnvVar(R"(\$\‍((\a[\w.-]*)\))"); // e.g. $(HOME)
1398static const reg::Ex reEnvVarExt(R"(\$\‍((\a[\w.-]*\‍(\a[\w.-]*\))\))"); // e.g. $(PROGRAMFILES(X86))
1399static const reg::Ex reEnvVarCMake(R"(@\a\w*@)"); // CMake type replacement
1400
1401static bool containsEnvVar(QCString &str)
1402{
1403 reg::Match m;
1404 std::string s = str.str();
1406}
1407
1409{
1410 if (str.isEmpty()) return;
1411 auto replace = [](const std::string &s, const reg::Ex &re) -> std::string
1412 {
1413 reg::Iterator it(s,re);
1415 std::string result;
1416 size_t p = 0;
1417 for (; it!=end ; ++it)
1418 {
1419 const auto &match = *it;
1420 size_t i = match.position();
1421 size_t l = match.length();
1422 result+=s.substr(p,i-p);
1423 std::string matchContents = match[1].str();
1424 QCString env=Portable::getenv(matchContents.c_str()); // get content of $(..) match
1425 substEnvVarsInString(env); // recursively expand variables if needed.
1426 result+=env.str();
1427 p=i+l;
1428 }
1429 result+=s.substr(p);
1430 return result;
1431 };
1432
1433 str = QCString(replace(replace(str.str(),reEnvVar),reEnvVarExt)).stripWhiteSpace();
1434}
1435
1437{
1438 StringVector results;
1439 for (const auto &s : sl)
1440 {
1441 QCString result = s.c_str();
1442 bool wasQuoted = ((result.at(0)=='"') && (result.at(result.length()-1)=='"'));
1443 if (wasQuoted)
1444 {
1445 result = result.mid(1,result.length()-2);
1446 }
1447 else
1448 {
1449 wasQuoted = (result.find(' ')!=-1) || (result.find('\t')!=-1) || (result.find('"')!=-1);
1450 }
1451 // here we strip the quote again
1452 substEnvVarsInString(result);
1453
1454 //printf("Result %s was quoted=%d\n",qPrint(result),wasQuoted);
1455
1456 if (!wasQuoted) /* as a result of the expansion, a single string
1457 may have expanded into a list, which we'll
1458 add to sl. If the original string already
1459 contained multiple elements no further
1460 splitting is done to allow quoted items with spaces! */
1461 {
1462 int l = static_cast<int>(result.length());
1463 int p = 0;
1464 // skip spaces
1465 // search for a "word"
1466 for (int i=0;i<l;i++)
1467 {
1468 char c=0;
1469 // skip until start of new word
1470 while (i<l && ((c=result.at(i))==' ' || c=='\t')) i++;
1471 p=i; // p marks the start index of the word
1472 // skip until end of a word
1473 while (i<l && ((c=result.at(i))!=' ' && c!='\t' && c!='"')) i++;
1474 if (i<l) // not at the end of the string
1475 {
1476 if (c=='"') // word within quotes
1477 {
1478 p=i+1;
1479 for (i++;i<l;i++)
1480 {
1481 c=result.at(i);
1482 if (c=='"') // end quote
1483 {
1484 results.push_back(result.mid(p,i-p).str());
1485 p=i+1;
1486 break;
1487 }
1488 else if (c=='\\') // skip escaped stuff
1489 {
1490 i++;
1491 }
1492 }
1493 }
1494 else if (c==' ' || c=='\t') // separator
1495 {
1496 if (i>p) results.push_back(result.mid(p,i-p).str());
1497 p=i+1;
1498 }
1499 }
1500 }
1501 if (p!=l) // add the leftover as a string
1502 {
1503 results.push_back(result.right(l-p).str());
1504 }
1505 }
1506 else // just goto the next element in the list
1507 {
1508 if (!result.isEmpty()) results.push_back(result.str());
1509 }
1510 }
1511 sl = results;
1512}
1513
1518
1523
1528
1533
1538
1539//---------------------------------------------
1540
1542{
1543 for (const auto &option : m_options)
1544 {
1545 option->substEnvVars();
1546 }
1547}
1548
1550{
1551 for (const auto &option : m_options)
1552 {
1553 option->init();
1554 }
1555
1556 // sanity check if all depends relations are valid
1557 for (const auto &option : m_options)
1558 {
1559 QCString depName = option->dependsOn();
1560 if (!depName.isEmpty())
1561 {
1562 ConfigOption * opt = ConfigImpl::instance()->get(depName);
1563 if (opt==0)
1564 {
1565 config_term("Config option '%s' has invalid depends relation on unknown option '%s'\n",
1566 qPrint(option->name()),qPrint(depName));
1567 }
1568 }
1569 }
1570}
1571
1576
1578{
1579 if (name.isEmpty()) return QCString();
1580
1581 auto stream2string = [](std::istream &in) -> std::string
1582 {
1583 std::string ret;
1584 char buffer[4096];
1585 while (in.read(buffer, sizeof(buffer))) ret.append(buffer, sizeof(buffer));
1586 ret.append(buffer, static_cast<uint32_t>(in.gcount()));
1587 if (!ret.empty() && ret[ret.length()-1]!='\n') ret+='\n'; // to help the scanner
1588 return ret;
1589 };
1590
1591 if (name=="-") // read from stdin
1592 {
1593 // read contents from stdin into contents string
1594 return stream2string(std::cin);
1595 }
1596 else // read from file
1597 {
1598 std::ifstream f = Portable::openInputStream(name);
1599 if (!f.is_open())
1600 {
1601 ConfigImpl::config_term("file '%s' not found or could not be opened\n",qPrint(name));
1602 return "";
1603 }
1604 return stream2string(f);
1605 }
1606}
1607
1608bool ConfigImpl::parseString(const QCString &fn,const QCString &str,bool update)
1609{
1610#ifdef FLEX_DEBUG
1611 configimplYYset_debug(Debug::isFlagSet(Debug::Lex_configimpl)?1:0);
1612#endif
1614 g_inputString = str.data();
1615 g_inputPosition = 0;
1616 g_yyFileName = fn;
1617 g_yyLineNr = 1;
1619 configimplYYrestart( configimplYYin );
1620 BEGIN( Start );
1621 g_configUpdate = update;
1622 configimplYYlex();
1624 g_inputString = 0;
1625 return TRUE;
1626}
1627
1628bool ConfigImpl::parse(const QCString &fn,bool update)
1629{
1630 g_encoding = "UTF-8";
1631 DebugLex debugLex(Debug::Lex_configimpl, __FILE__, qPrint(fn));
1632 bool retval = parseString(fn,configFileToString(fn), update);
1633 return retval;
1634}
1635
1636//----------------------------------------------------------------------
1637
1639{
1640 for (size_t i=0;i<str.size();i++)
1641 {
1642 std::string path = str[i];
1643 std::replace(path.begin(),path.end(),'\\','/');
1644 if ((path[0]!='/' && (path.size()<=2 || path[1]!=':')) || path[path.size()-1]!='/')
1645 {
1646 FileInfo fi(path);
1647 if (fi.exists() && fi.isDir())
1648 {
1649 path = fi.absFilePath();
1650 if (path[path.size()-1]!='/') path+='/';
1651 }
1652 }
1653 str[i]=path;
1654 }
1655}
1656
1657static bool checkFileName(const QCString &s,const char *optionName)
1658{
1659 QCString val = s.stripWhiteSpace().lower();
1660 if ((val=="yes" || val=="true" || val=="1" || val=="all") ||
1661 (val=="no" || val=="false" || val=="0" || val=="none"))
1662 {
1663 err("file name expected for option %s, got %s instead. Ignoring...\n",optionName,qPrint(s));
1664 return false;
1665 }
1666 return true;
1667}
1668
1669
1671{
1673}
1674
1675static void checkList(const StringVector &list,const char *name, bool equalRequired,bool valueRequired)
1676{
1677 for (const auto &s: list)
1678 {
1679 QCString item=s.c_str();
1680 item=item.stripWhiteSpace();
1681 int i=item.find('=');
1682 if (i==-1 && equalRequired)
1683 {
1684 err("Illegal format for option %s, no equal sign ('=') specified for item '%s'\n",name,qPrint(item));
1685 }
1686 if (i!=-1)
1687 {
1688 QCString myName=item.left(i).stripWhiteSpace();
1689 if (myName.isEmpty())
1690 {
1691 err("Illegal format for option %s, no name specified for item '%s'\n",name,qPrint(item));
1692 }
1693 else if (valueRequired)
1694 {
1695 QCString myValue=item.right(item.length()-i-1).stripWhiteSpace();
1696 if (myValue.isEmpty())
1697 {
1698 err("Illegal format for option %s, no value specified for item '%s'\n",name,qPrint(item));
1699 }
1700 }
1701 }
1702 }
1703}
1704
1705static void adjustBoolSetting(const char *depOption, const char *optionName,bool expectedValue)
1706{
1707 // lookup option by name
1708 const ConfigValues::Info *option = ConfigValues::instance().get(optionName);
1709 if (option && option->type==ConfigValues::Info::Bool) // safety check
1710 {
1711 if (ConfigValues::instance().*(option->value.b)!=expectedValue) // current value differs from expectation
1712 {
1713 err("When enabling %s the %s option should be %s. I'll adjust it for you.\n",depOption,optionName,expectedValue? "enabled" : "disabled");
1714 ConfigValues::instance().*(option->value.b)=expectedValue; // adjust option
1715 }
1716 }
1717}
1718
1719static void adjustStringSetting(const char *depOption, const char *optionName,const QCString &expectedValue)
1720{
1721 // lookup option by name
1722 const ConfigValues::Info *option = ConfigValues::instance().get(optionName);
1723 if (option && option->type==ConfigValues::Info::String) // safety check
1724 {
1725 if (ConfigValues::instance().*(option->value.s)!=expectedValue) // current value differs from expectation
1726 {
1727 err("When enabling %s the %s option should have value '%s'. I'll adjust it for you.\n",depOption,optionName,qPrint(expectedValue));
1728 ConfigValues::instance().*(option->value.s)=expectedValue; // adjust option
1729 }
1730 }
1731}
1732
1733static void adjustColorStyleSetting(const char *depOption)
1734{
1735 auto updateColorStyle = [&depOption](HTML_COLORSTYLE_t curStyle,HTML_COLORSTYLE_t newStyle)
1736 {
1737 err("When enabling '%s' the 'HTML_COLORSTYLE' option should be either 'LIGHT' or 'DARK' but has value '%s'. I'll adjust it for you to '%s'.\n",
1738 depOption,
1739 qPrint(HTML_COLORSTYLE_enum2str(curStyle)),
1740 qPrint(HTML_COLORSTYLE_enum2str(newStyle)));
1741 Config_updateEnum(HTML_COLORSTYLE,newStyle);
1742 };
1743 auto colorStyle = Config_getEnum(HTML_COLORSTYLE);
1744 switch (colorStyle)
1745 {
1746 case HTML_COLORSTYLE_t::LIGHT:
1747 case HTML_COLORSTYLE_t::DARK:
1748 // no adjustment needed
1749 break;
1750 case HTML_COLORSTYLE_t::AUTO_LIGHT:
1751 case HTML_COLORSTYLE_t::TOGGLE:
1752 updateColorStyle(colorStyle,HTML_COLORSTYLE_t::LIGHT);
1753 break;
1754 case HTML_COLORSTYLE_t::AUTO_DARK:
1755 updateColorStyle(colorStyle,HTML_COLORSTYLE_t::DARK);
1756 break;
1757 }
1758}
1759
1760
1761void Config::checkAndCorrect(bool quiet, const bool check)
1762{
1763 ConfigValues::instance().init();
1764
1765 Config_updateBool(QUIET,quiet || Config_getBool(QUIET));
1766 //------------------------
1767 // check WARN_FORMAT
1768 QCString warnFormat = Config_getString(WARN_FORMAT);
1769 if (warnFormat.find("$file")==-1)
1770 {
1771 warn_uncond("warning format does not contain a $file tag!\n");
1772 }
1773 if (warnFormat.find("$line")==-1)
1774 {
1775 warn_uncond("warning format does not contain a $line tag!\n");
1776 }
1777 if (warnFormat.find("$text")==-1)
1778 {
1779 warn_uncond("warning format does not contain a $text tag!\n");
1780 }
1781
1782 //------------------------
1783 // check and correct PAPER_TYPE
1784 QCString paperType = Config_getEnumAsString(PAPER_TYPE);
1785 paperType=paperType.lower().stripWhiteSpace();
1786 if (paperType.isEmpty() || paperType=="a4wide")
1787 {
1788 // use a4
1789 Config_updateEnum(PAPER_TYPE,PAPER_TYPE_t::a4);
1790 }
1791 else if (paperType!="a4" && paperType!="letter" &&
1792 paperType!="legal" && paperType!="executive")
1793 {
1794 err("Unknown page type '%s' specified\n",qPrint(paperType));
1795 Config_updateEnum(PAPER_TYPE,PAPER_TYPE_t::a4);
1796 }
1797
1798 //------------------------
1799 // check & correct STRIP_FROM_PATH
1800 StringVector stripFromPath = Config_getList(STRIP_FROM_PATH);
1801 if (stripFromPath.empty()) // by default use the current path
1802 {
1803 std::string p = Dir::currentDirPath()+"/";
1804 stripFromPath.push_back(p);
1805 }
1806 else
1807 {
1809 }
1810 Config_updateList(STRIP_FROM_PATH,stripFromPath);
1811
1812 //------------------------
1813 // check & correct STRIP_FROM_INC_PATH
1814 StringVector stripFromIncPath = Config_getList(STRIP_FROM_INC_PATH);
1815 cleanUpPaths(stripFromIncPath);
1816 Config_updateList(STRIP_FROM_INC_PATH,stripFromIncPath);
1817
1818 //------------------------
1819 // Test to see if HTML header is valid
1820 QCString headerFile = Config_getString(HTML_HEADER);
1821 if (check && !headerFile.isEmpty())
1822 {
1823 FileInfo fi(headerFile.str());
1824 if (!fi.exists())
1825 {
1826 ConfigImpl::config_term("tag HTML_HEADER: header file '%s' "
1827 "does not exist\n",qPrint(headerFile));
1828 }
1829 }
1830
1831 //------------------------
1832 // Test to see if HTML footer is valid
1833 QCString footerFile = Config_getString(HTML_FOOTER);
1834 if (check && !footerFile.isEmpty())
1835 {
1836 FileInfo fi(footerFile.str());
1837 if (!fi.exists())
1838 {
1839 ConfigImpl::config_term("tag HTML_FOOTER: footer file '%s' "
1840 "does not exist\n",qPrint(footerFile));
1841 }
1842 }
1843
1844 //------------------------
1845 // Test to see if MathJax code file is valid
1846 if (Config_getBool(USE_MATHJAX))
1847 {
1848 auto mathJaxFormat = Config_getEnum(MATHJAX_FORMAT);
1849 auto mathjaxVersion = Config_getEnum(MATHJAX_VERSION);
1850 if (mathjaxVersion == MATHJAX_VERSION_t::MathJax_2)
1851 {
1852 if (mathJaxFormat==MATHJAX_FORMAT_t::chtml)
1853 {
1854 Config_updateEnum(MATHJAX_FORMAT,MATHJAX_FORMAT_t::HTML_CSS);
1855 }
1856 }
1857 else
1858 {
1859 if (mathJaxFormat==MATHJAX_FORMAT_t::HTML_CSS || mathJaxFormat==MATHJAX_FORMAT_t::NativeMML)
1860 {
1861 Config_updateEnum(MATHJAX_FORMAT,MATHJAX_FORMAT_t::chtml);
1862 }
1863 }
1864
1865 QCString mathJaxCodefile = Config_getString(MATHJAX_CODEFILE);
1866 if (check && !mathJaxCodefile.isEmpty())
1867 {
1868 FileInfo fi(mathJaxCodefile.str());
1869 if (!fi.exists())
1870 {
1871 ConfigImpl::config_term("tag MATHJAX_CODEFILE file '%s' "
1872 "does not exist\n",qPrint(mathJaxCodefile));
1873 }
1874 }
1875 QCString path = Config_getString(MATHJAX_RELPATH);
1876 if (path.isEmpty())
1877 {
1878 path = "https://cdn.jsdelivr.net/npm/mathjax@";
1879 switch (mathjaxVersion)
1880 {
1881 case MATHJAX_VERSION_t::MathJax_2: path += "2"; break;
1882 case MATHJAX_VERSION_t::MathJax_3: path += "3"; break;
1883 }
1884 }
1885
1886 if (path.at(path.length()-1)!='/')
1887 {
1888 path+="/";
1889 }
1890 Config_updateString(MATHJAX_RELPATH,path);
1891 }
1892
1893 //------------------------
1894 // Test to see if LaTeX header is valid
1895 QCString latexHeaderFile = Config_getString(LATEX_HEADER);
1896 if (check && !latexHeaderFile.isEmpty())
1897 {
1898 FileInfo fi(latexHeaderFile.str());
1899 if (!fi.exists())
1900 {
1901 ConfigImpl::config_term("tag LATEX_HEADER: header file '%s' "
1902 "does not exist\n",qPrint(latexHeaderFile));
1903 }
1904 }
1905
1906 //------------------------
1907 // Test to see if LaTeX footer is valid
1908 QCString latexFooterFile = Config_getString(LATEX_FOOTER);
1909 if (check && !latexFooterFile.isEmpty())
1910 {
1911 FileInfo fi(latexFooterFile.str());
1912 if (!fi.exists())
1913 {
1914 ConfigImpl::config_term("tag LATEX_FOOTER: footer file '%s' "
1915 "does not exist\n",qPrint(latexFooterFile));
1916 }
1917 }
1918
1919 //------------------------
1920 // check include path
1921 const StringVector &includePath = Config_getList(INCLUDE_PATH);
1922 for (const auto &s : includePath)
1923 {
1924 FileInfo fi(s);
1925 if (!fi.exists()) warn_uncond("tag INCLUDE_PATH: include path '%s' "
1926 "does not exist\n",s.c_str());
1927 }
1928
1929 //------------------------
1930 // check PREDEFINED
1931 if (Config_getBool(ENABLE_PREPROCESSING))
1932 {
1933 const StringVector &predefList = Config_getList(PREDEFINED);
1934 for (const auto &s : predefList)
1935 {
1936 QCString predef=s.c_str();
1937 predef=predef.stripWhiteSpace();
1938 int i_equals=predef.find('=');
1939 int i_obrace=predef.find('(');
1940 if ((i_obrace==0) || (i_equals==0) || (i_equals==1 && predef.at(i_equals-1)==':'))
1941 {
1942 err("Illegal PREDEFINED format '%s', no define name specified\n",qPrint(predef));
1943 }
1944 }
1945 }
1946
1947 //------------------------
1948 // check EXTENSION_MAPPING
1949 checkList(Config_getList(EXTENSION_MAPPING),"EXTENSION_MAPPING",TRUE,TRUE);
1950
1951 //------------------------
1952 // check FILTER_PATTERNS
1953 checkList(Config_getList(FILTER_PATTERNS),"FILTER_PATTERNS",TRUE,TRUE);
1954
1955 //------------------------
1956 // check FILTER_SOURCE_PATTERNS
1957 checkList(Config_getList(FILTER_SOURCE_PATTERNS),"FILTER_SOURCE_PATTERNS",FALSE,FALSE);
1958
1959 //------------------------
1960 // check INPUT_FILE_ENCODING
1961 checkList(Config_getList(INPUT_FILE_ENCODING),"INPUT_FILE_ENCODING",TRUE,TRUE);
1962
1963 //------------------------
1964 // check TAGFILES
1965 checkList(Config_getList(TAGFILES),"TAGFILES",FALSE,TRUE);
1966
1967 //------------------------
1968 // check EXTRA_SEARCH_MAPPINGS
1969 if (Config_getBool(SEARCHENGINE) && Config_getBool(GENERATE_HTML))
1970 {
1971 checkList(Config_getList(EXTRA_SEARCH_MAPPINGS),"EXTRA_SEARCH_MAPPING",TRUE,TRUE);
1972 }
1973
1974 int numThreads = Config_getInt(NUM_PROC_THREADS);
1975 if (numThreads==0)
1976 {
1977 numThreads = static_cast<int>(std::thread::hardware_concurrency());
1978 Config_updateInt(NUM_PROC_THREADS,numThreads);
1979 }
1980
1981 //------------------------
1982
1983 // check for settings that are inconsistent with having GENERATE_HTMLHELP enabled
1984 if (Config_getBool(GENERATE_HTMLHELP))
1985 {
1986 const char *depOption = "GENERATE_HTMLHELP";
1987 adjustBoolSetting( depOption, "GENERATE_TREEVIEW", false );
1988 adjustBoolSetting( depOption, "SEARCHENGINE", false );
1989 adjustBoolSetting( depOption, "HTML_DYNAMIC_MENUS", false );
1990 adjustBoolSetting( depOption, "HTML_DYNAMIC_SECTIONS",false );
1991 adjustBoolSetting( depOption, "HTML_COPY_CLIPBOARD", false );
1992 adjustStringSetting(depOption, "HTML_FILE_EXTENSION", ".html");
1993 adjustColorStyleSetting(depOption);
1994 const StringVector &tagFileList = Config_getList(TAGFILES);
1995 StringVector filteredTagFileList;
1996 for (const auto &s : tagFileList)
1997 {
1998 bool validUrl = false;
1999 size_t eqPos = s.find('=');
2000 if (eqPos!=std::string::npos) // tag command contains a destination
2001 {
2002 QCString url = QCString(s.substr(eqPos+1)).stripWhiteSpace().lower();
2003 validUrl = url.startsWith("http:") || url.startsWith("https:");
2004 }
2005 if (validUrl)
2006 {
2007 filteredTagFileList.push_back(s);
2008 }
2009 else
2010 {
2011 err("When enabling GENERATE_HTMLHELP the TAGFILES option should only contain destinations "
2012 "with https / http addresses (not: %s). I'll adjust it for you.\n",s.c_str());
2013 }
2014 }
2015 Config_updateList(TAGFILES,filteredTagFileList);
2016 }
2017
2018 // check for settings that are inconsistent with having INLINE_GROUPED_CLASSES enabled
2019 if (Config_getBool(INLINE_GROUPED_CLASSES))
2020 {
2021 const char *depOption = "INLINE_GROUPED_CLASSES";
2022 adjustBoolSetting(depOption, "SEPARATE_MEMBER_PAGES", false);
2023 }
2024
2025 //------------------------
2026 // clip number of threads
2027 int dotNumThreads = Config_getInt(DOT_NUM_THREADS);
2028 if (dotNumThreads>32)
2029 {
2030 dotNumThreads=32;
2031 }
2032 else if (dotNumThreads<=0)
2033 {
2034 dotNumThreads=std::max(2u,std::thread::hardware_concurrency()+1);
2035 }
2036 Config_updateInt(DOT_NUM_THREADS,dotNumThreads);
2037
2038 //------------------------
2039 // check plantuml path
2040 QCString plantumlJarPath = Config_getString(PLANTUML_JAR_PATH);
2041 if (!plantumlJarPath.isEmpty())
2042 {
2043 FileInfo pu(plantumlJarPath.str());
2044 if (pu.exists() && pu.isDir()) // PLANTUML_JAR_PATH is directory
2045 {
2046 QCString plantumlJar = plantumlJarPath+Portable::pathSeparator()+"plantuml.jar";
2047 FileInfo jar(plantumlJar.str());
2048 if (jar.exists() && jar.isFile())
2049 {
2050 plantumlJarPath = plantumlJar;
2051 }
2052 else
2053 {
2054 err("Jar file 'plantuml.jar' not found at location "
2055 "specified via PLANTUML_JAR_PATH: '%s'\n",qPrint(plantumlJarPath));
2056 plantumlJarPath="";
2057 }
2058 }
2059 else if (pu.exists() && pu.isFile()) // PLANTUML_JAR_PATH is file
2060 {
2061 // Nothing to be done
2062 }
2063 else
2064 {
2065 err("PLANTUML_JAR_PATH is not a directory with a 'plantuml.jar' file or is not an existing file: %s\n",
2066 qPrint(plantumlJarPath));
2067 plantumlJarPath="";
2068 }
2069 Config_updateString(PLANTUML_JAR_PATH,plantumlJarPath);
2070 }
2071
2072 //------------------------
2073 // check dia path
2074 QCString diaPath = Config_getString(DIA_PATH);
2075 if (!diaPath.isEmpty())
2076 {
2077 QCString diaExe = diaPath+"/dia"+Portable::commandExtension();
2078 FileInfo dp(diaExe.str());
2079 if (!dp.exists() || !dp.isFile())
2080 {
2081 warn_uncond("dia could not be found at %s\n",qPrint(diaPath));
2082 diaPath="";
2083 }
2084 else
2085 {
2086 diaPath=dp.dirPath(TRUE)+"/";
2087#if defined(_WIN32) // convert slashes
2088 size_t i=0,l=diaPath.length();
2089 for (i=0;i<l;i++) if (diaPath.at(i)=='/') diaPath.at(i)='\\';
2090#endif
2091 }
2092 Config_updateString(DIA_PATH,diaPath);
2093 }
2094
2095 //------------------------
2096 // check INPUT
2097 StringVector inputSources=Config_getList(INPUT);
2098 if (inputSources.empty())
2099 {
2100 // use current dir as the default
2101 inputSources.push_back(Dir::currentDirPath());
2102 }
2103 else
2104 {
2105 for (const auto &s : inputSources)
2106 {
2107 FileInfo fi(s.c_str());
2108 if (!fi.exists())
2109 {
2110 warn_uncond("tag INPUT: input source '%s' does not exist\n",s.c_str());
2111 }
2112 }
2113 }
2114 Config_updateList(INPUT,inputSources);
2115
2116 //------------------------
2117 // if no output format is enabled, warn the user
2118 if (!Config_getBool(GENERATE_HTML) &&
2119 !Config_getBool(GENERATE_LATEX) &&
2120 !Config_getBool(GENERATE_MAN) &&
2121 !Config_getBool(GENERATE_RTF) &&
2122 !Config_getBool(GENERATE_XML) &&
2123 !Config_getBool(GENERATE_PERLMOD) &&
2124 !Config_getBool(GENERATE_RTF) &&
2125 !Config_getBool(GENERATE_DOCBOOK) &&
2126 !Config_getBool(GENERATE_AUTOGEN_DEF) &&
2127 Config_getString(GENERATE_TAGFILE).isEmpty()
2128 )
2129 {
2130 warn_uncond("No output formats selected! Set at least one of the main GENERATE_* options to YES.\n");
2131 }
2132
2133 //------------------------
2134 // check HTMLHELP creation requirements
2135 if (!Config_getBool(GENERATE_HTML) &&
2136 Config_getBool(GENERATE_HTMLHELP))
2137 {
2138 warn_uncond("GENERATE_HTMLHELP=YES requires GENERATE_HTML=YES.\n");
2139 }
2140
2141 //------------------------
2142 // check sitemap creation requirements
2143 if (!Config_getBool(GENERATE_HTML) &&
2144 !Config_getString(SITEMAP_URL).isEmpty())
2145 {
2146 warn_uncond("Setting SITEMAP_URL requires GENERATE_HTML=YES.\n");
2147 }
2148
2149 //------------------------
2150 // check QHP creation requirements
2151 if (Config_getBool(GENERATE_QHP))
2152 {
2153 if (!Config_getBool(GENERATE_HTML))
2154 {
2155 warn_uncond("GENERATE_QHP=YES requires GENERATE_HTML=YES.\n");
2156 }
2157 if (Config_getString(QHP_NAMESPACE).isEmpty())
2158 {
2159 err("GENERATE_QHP=YES requires QHP_NAMESPACE to be set. Using 'org.doxygen.doc' as default!.\n");
2160 Config_updateString(QHP_NAMESPACE,"org.doxygen.doc");
2161 }
2162
2163 if (Config_getString(QHP_VIRTUAL_FOLDER).isEmpty())
2164 {
2165 err("GENERATE_QHP=YES requires QHP_VIRTUAL_FOLDER to be set. Using 'doc' as default!\n");
2166 Config_updateString(QHP_VIRTUAL_FOLDER,"doc");
2167 }
2168 const StringVector &tagFileList = Config_getList(TAGFILES);
2169 if (!tagFileList.empty())
2170 {
2171 err("When enabling GENERATE_QHP the TAGFILES option should be empty. I'll adjust it for you.\n");
2172 Config_updateList(TAGFILES,StringVector());
2173 }
2174 }
2175
2176 //------------------------
2177 if (Config_getBool(OPTIMIZE_OUTPUT_JAVA) && Config_getBool(INLINE_INFO))
2178 {
2179 // don't show inline info for Java output, since Java has no inline
2180 // concept.
2181 Config_updateBool(INLINE_INFO,FALSE);
2182 }
2183
2184 //------------------------
2185 int depth = Config_getInt(MAX_DOT_GRAPH_DEPTH);
2186 if (depth==0)
2187 {
2188 Config_updateInt(MAX_DOT_GRAPH_DEPTH,1000);
2189 }
2190
2191 //------------------------
2192 // check for settings that are inconsistent with having OPTIMIZED_OUTPUT_VHDL enabled
2193 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
2194 {
2195 const char *depOption = "OPTIMIZE_OUTPUT_VHDL";
2196 adjustBoolSetting(depOption,"INLINE_INHERITED_MEMB",false);
2197 adjustBoolSetting(depOption,"INHERIT_DOCS", false);
2198 adjustBoolSetting(depOption,"HIDE_SCOPE_NAMES", true );
2199 adjustBoolSetting(depOption,"EXTRACT_PRIVATE", true );
2200 adjustBoolSetting(depOption,"ENABLE_PREPROCESSING", false);
2201 adjustBoolSetting(depOption,"EXTRACT_PACKAGE", true );
2202 }
2203
2204 if (!checkFileName(Config_getString(GENERATE_TAGFILE),"GENERATE_TAGFILE"))
2205 {
2206 Config_updateString(GENERATE_TAGFILE,"");
2207 }
2208
2209#if 0 // TODO: this breaks test 25; SOURCEBROWSER = NO and SOURCE_TOOLTIPS = YES.
2210 // So this and other regressions should be analyzed and fixed before this can be enabled
2211 // disable any boolean options that depend on disabled options
2212 for (const auto &option : m_options)
2213 {
2214 QCString depName = option->dependsOn(); // option has a dependency
2215 if (!depName.isEmpty())
2216 {
2217 ConfigOption * dep = Config::instance()->get(depName);
2218 if (dep->kind()==ConfigOption::O_Bool &&
2219 ConfigImpl_getBool("depName")==FALSE) // dependent option is disabled
2220 {
2221 if (option->kind()==ConfigOption::O_Bool)
2222 {
2223 printf("disabling option %s\n",qPrint(option->name()));
2224 ConfigImpl_getBool("option->name("))=FALSE; // also disable this option
2225 }
2226 }
2227 }
2228 }
2229#endif
2230
2231}
2232
2234{
2235 attr.updateValue(name,*value->valueStringRef());
2236}
2237
2239{
2240 //------------------------
2241 // check for presence of obsolete CLASS_DIAGRAM option and correct CLASS_GRAPH if needed
2242 ConfigOption *classDiagramsOpt = ConfigImpl::instance()->get("CLASS_DIAGRAMS");
2243 ConfigOption *haveDotOpt = ConfigImpl::instance()->get("HAVE_DOT");
2244 ConfigOption *classGraphOpt = ConfigImpl::instance()->get("CLASS_GRAPH");
2245 if (classDiagramsOpt && classDiagramsOpt->kind()==ConfigOption::O_Obsolete &&
2246 haveDotOpt && classGraphOpt)
2247 {
2248 ConfigObsolete *classDiagramsOpt_ = dynamic_cast<ConfigObsolete*>(classDiagramsOpt);
2249 ConfigBool *haveDotOpt_ = dynamic_cast<ConfigBool*>(haveDotOpt);
2250 ConfigEnum *classGraphOpt_ = dynamic_cast<ConfigEnum*>(classGraphOpt);
2251 if (classDiagramsOpt_ && haveDotOpt_ && classGraphOpt_ &&
2252 classDiagramsOpt_->isPresent() && classDiagramsOpt_->orgType()==ConfigOption::O_Bool)
2253 {
2254 QCString classDiagramValue = *classDiagramsOpt_->valueStringRef();
2255 QCString haveDotValue = *haveDotOpt_->valueStringRef();
2256 QCString &classGraphValue = *classGraphOpt_->valueRef();
2257 bool isValid1=true, isValid2=true;
2258 bool bClassDiagrams = convertStringToBool(classDiagramValue,isValid1);
2259 bool bHaveDot = haveDotValue.isEmpty() ? false : convertStringToBool(haveDotValue, isValid2);
2260 if (isValid1 && isValid2 && !bClassDiagrams && !bHaveDot && classGraphValue.lower()=="yes")
2261 {
2262 warn_uncond("Changing CLASS_GRAPH option to TEXT because obsolete option CLASS_DIAGRAM was found and set to NO.\n");
2263 classGraphValue="TEXT";
2264 }
2265 }
2266 }
2267
2268 // update TIMESTAMP based on HTML_TIMESTAMP and LATEX_TIMESTAMP
2269 ConfigOption *HtmlTimestamp = ConfigImpl::instance()->get("HTML_TIMESTAMP");
2270 ConfigOption *timestampOpt = ConfigImpl::instance()->get("TIMESTAMP");
2271 bool reset = false;
2272 if (HtmlTimestamp && HtmlTimestamp->kind()==ConfigOption::O_Obsolete && timestampOpt)
2273 {
2274 ConfigObsolete *htmlTimestamp_ = dynamic_cast<ConfigObsolete*>(HtmlTimestamp);
2275 ConfigEnum *timestampOpt_ = dynamic_cast<ConfigEnum*>(timestampOpt);
2276 if (htmlTimestamp_ && timestampOpt_ &&
2277 htmlTimestamp_->isPresent() && htmlTimestamp_->orgType()==ConfigOption::O_Bool)
2278 {
2279 QCString &timestampValue = *timestampOpt_->valueRef();
2280 QCString htmlTimestampValue = *htmlTimestamp_->valueStringRef();
2281 bool isValid=true;
2282 bool bTimestamp = convertStringToBool(htmlTimestampValue,isValid);
2283 if (isValid && bTimestamp)
2284 {
2285 reset = true;
2286 timestampValue = "YES";
2287 }
2288 }
2289 }
2290 ConfigOption *LatexTimestamp = ConfigImpl::instance()->get("LATEX_TIMESTAMP");
2291 if (!reset && LatexTimestamp && LatexTimestamp->kind()==ConfigOption::O_Obsolete && timestampOpt)
2292 {
2293 ConfigObsolete *latexTimestamp_ = dynamic_cast<ConfigObsolete*>(LatexTimestamp);
2294 ConfigEnum *timestampOpt_ = dynamic_cast<ConfigEnum*>(timestampOpt);
2295 if (latexTimestamp_ && timestampOpt_ &&
2296 latexTimestamp_->isPresent() && latexTimestamp_->orgType()==ConfigOption::O_Bool)
2297 {
2298 QCString &timestampValue = *timestampOpt_->valueRef();
2299 QCString latexTimestampValue = *latexTimestamp_->valueStringRef();
2300 bool isValid=true;
2301 bool bTimestamp = convertStringToBool(latexTimestampValue,isValid);
2302 if (isValid && bTimestamp) timestampValue = "YES";
2303 }
2304 }
2305
2306 auto fontname = dynamic_cast<ConfigObsolete*>(ConfigImpl::instance()->get("DOT_FONTNAME"));
2307 auto fontsize = dynamic_cast<ConfigObsolete*>(ConfigImpl::instance()->get("DOT_FONTSIZE"));
2308
2309 // correct DOT_FONTNAME if needed
2310 if (fontname &&
2311 (*fontname->valueStringRef() == "FreeSans"
2312 || *fontname->valueStringRef() == "FreeSans.ttf"))
2313 warn_uncond("doxygen no longer ships with the FreeSans font.\n"
2314 " You may want to clear or change DOT_FONTNAME.\n"
2315 " Otherwise you run the risk that the wrong font is being used for dot generated graphs.\n");
2316
2317 auto commonAttrOpt = dynamic_cast<ConfigString*>(ConfigImpl::instance()->get("DOT_COMMON_ATTR"));
2318 if (commonAttrOpt)
2319 {
2320 QCString& commonAttrStr = *commonAttrOpt->valueRef();
2321 DotAttributes commonAttr(commonAttrStr);
2322 updateAttribute(commonAttr, "fontname", fontname);
2323 updateAttribute(commonAttr, "fontsize", fontsize);
2324 commonAttrStr = commonAttr.str();
2325 }
2326
2327 auto edgeAttrOpt = dynamic_cast<ConfigString*>(ConfigImpl::instance()->get("DOT_EDGE_ATTR"));
2328 if (edgeAttrOpt)
2329 {
2330 QCString& edgeAttrStr = *edgeAttrOpt->valueRef();
2331 DotAttributes edgeAttr(edgeAttrStr);
2332 updateAttribute(edgeAttr, "labelfontname", fontname);
2333 updateAttribute(edgeAttr, "labelfontsize", fontsize);
2334 edgeAttrStr = edgeAttr.str();
2335 }
2336}
2337
2338void Config::writeTemplate(TextStream &t,bool shortList,bool update)
2339{
2340 ConfigImpl::instance()->writeTemplate(t,shortList,update);
2341}
2342
2344{
2345 postProcess(FALSE, compareMode);
2346 ConfigImpl::instance()->compareDoxyfile(t, compareMode);
2347}
2348
2353
2358
2359bool Config::parse(const QCString &fileName,bool update, Config::CompareMode compareMode)
2360{
2361 g_compareMode = compareMode;
2362 bool parseRes = ConfigImpl::instance()->parse(fileName,update);
2363 if (!parseRes) return parseRes;
2364
2365 // Internally we use the default format UTF-8 and
2366 // when updating etc. the output is in this format as well and not in the read format
2367 ConfigString *option = dynamic_cast<ConfigString*>(g_config->get("DOXYFILE_ENCODING"));
2368 option->init();
2369
2370 return parseRes;
2371}
2372
2373void Config::postProcess(bool clearHeaderAndFooter, Config::CompareMode compareMode)
2374{
2375 auto configInst = ConfigImpl::instance();
2376 if (compareMode != CompareMode::CompressedNoEnv) configInst->substituteEnvironmentVars();
2377 if (compareMode == CompareMode::Full) configInst->emptyValueToDefault();
2378 configInst->convertStrToVal(compareMode);
2379
2380 // avoid bootstrapping issues when the g_config file already
2381 // refers to the files that we are supposed to parse.
2382 if (clearHeaderAndFooter)
2383 {
2384 Config_updateString(HTML_HEADER ,"");
2385 Config_updateString(HTML_FOOTER ,"");
2386 Config_updateString(LATEX_HEADER,"");
2387 Config_updateString(LATEX_FOOTER,"");
2388 }
2389}
2390
2395
2396#include "configimpl.l.h"
void substEnvVars() override
QCString * valueStringRef()
Definition configimpl.h:264
QCString * valueRef()
Definition configimpl.h:168
void substEnvVars() override
QCString takeStoreRepl()
Definition configimpl.h:602
ConfigOptionList m_options
Definition configimpl.h:614
static void deleteInstance()
Definition configimpl.h:356
QCString m_storeRepl
Definition configimpl.h:621
void compareDoxyfile(TextStream &t, Config::CompareMode compareMode)
void convertStrToVal(Config::CompareMode compareMode)
static ConfigImpl * instance()
Definition configimpl.h:350
void init()
void substituteEnvironmentVars()
bool parseString(const QCString &fn, const QCString &str, bool upd=FALSE)
bool parse(const QCString &fn, bool upd=FALSE)
QCString takeStartComment()
Definition configimpl.h:584
ConfigOption * get(const QCString &name) const
Definition configimpl.h:399
void writeXMLDoxyfile(TextStream &t)
void writeXSDDoxyfile(TextStream &t)
QCString takeUserComment()
Definition configimpl.h:593
void writeTemplate(TextStream &t, bool shortIndex, bool updateOnly)
QCString m_startComment
Definition configimpl.h:619
ConfigOptionList m_disabled
Definition configimpl.h:616
QCString m_userComment
Definition configimpl.h:620
void emptyValueToDefault()
QCString m_header
Definition configimpl.h:622
void substEnvVars() override
void substEnvVars() override
bool isPresent() const
Definition configimpl.h:297
QCString * valueStringRef()
Definition configimpl.h:295
OptionType orgType() const
Definition configimpl.h:293
virtual void convertStrToVal(Config::CompareMode)
Definition configimpl.h:83
virtual void init()
Definition configimpl.h:86
virtual void writeXMLDoxyfile(TextStream &t)=0
virtual void writeTemplate(TextStream &t, bool sl, bool upd)=0
virtual void writeXSDDoxyfile(TextStream &t)=0
QCString dependsOn() const
Definition configimpl.h:73
virtual void emptyValueToDefault()
Definition configimpl.h:84
QCString name() const
Definition configimpl.h:70
virtual void compareDoxyfile(TextStream &t, Config::CompareMode compareMode)=0
virtual void substEnvVars()=0
void substEnvVars() override
void init() override
Definition configimpl.h:206
@ Lex_configimpl
Definition debug.h:54
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:135
static std::string currentDirPath()
Definition dir.cpp:340
Class representing an attribute list of a dot graph object.
void updateValue(const QCString &key, const QCString &inpValue)
update a given attribute with a new value.
QCString str() const
Return the string representation of the attribute list.
bool isDir() const
Definition fileinfo.cpp:70
std::string dirPath(bool absPath=true) const
Definition fileinfo.cpp:137
std::string absFilePath() const
Definition fileinfo.cpp:101
int find(char c, int index=0, bool cs=TRUE) const
Definition qcstring.cpp:43
bool startsWith(const char *s) const
Definition qcstring.h:492
QCString right(size_t len) const
Definition qcstring.h:219
void clear()
Definition qcstring.h:169
virtual QCString trISOLang()=0
Class representing a regular expression.
Definition regex.h:39
Iterator class to iterator through matches.
Definition regex.h:232
Object representing the matching results.
Definition regex.h:153
#define Config_getInt(name)
Definition config.h:34
#define Config_getList(name)
Definition config.h:38
#define Config_updateString(name, value)
Definition config.h:39
#define Config_updateInt(name, value)
Definition config.h:41
#define Config_getEnumAsString(name)
Definition config.h:36
#define Config_updateBool(name, value)
Definition config.h:40
#define Config_getBool(name)
Definition config.h:33
#define Config_getString(name)
Definition config.h:32
#define Config_updateList(name,...)
Definition config.h:43
#define Config_updateEnum(name, value)
Definition config.h:42
#define Config_getEnum(name)
Definition config.h:35
#define ConfigImpl_getBool(val)
Definition configimpl.h:324
static void adjustStringSetting(const char *depOption, const char *optionName, const QCString &expectedValue)
static void adjustColorStyleSetting(const char *depOption)
static void updateAttribute(DotAttributes &attr, QCString name, ConfigObsolete *value)
static QCString configFileToString(const QCString &name)
static const reg::Ex reEnvVarExt(R"(\$\‍((\a[\w.-]*\‍(\a[\w.-]*\‍))\‍))")
static const reg::Ex reEnvVar(R"(\$\‍((\a[\w.-]*)\‍))")
static void cleanUpPaths(StringVector &str)
static void checkList(const StringVector &list, const char *name, bool equalRequired, bool valueRequired)
static bool checkFileName(const QCString &s, const char *optionName)
static const reg::Ex reEnvVarCMake(R"(@\a\w*@)")
static void adjustBoolSetting(const char *depOption, const char *optionName, bool expectedValue)
void addConfigOptions(ConfigImpl *cfg)
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:175
Translator * theTranslator
Definition language.cpp:71
#define warn_uncond(fmt,...)
Definition message.h:79
#define err(fmt,...)
Definition message.h:84
void postProcess(bool clearHeaderAndFooter, CompareMode compareMode=CompareMode::Full)
void checkAndCorrect(bool quiet, const bool check)
void writeXMLDoxyfile(TextStream &t)
void compareDoxyfile(TextStream &t, CompareMode compareMode)
void deinit()
bool parse(const QCString &fileName, bool update=FALSE, CompareMode compareMode=CompareMode::Full)
void writeXSDDoxyfile(TextStream &t)
void init()
void writeTemplate(TextStream &t, bool shortList, bool updateOnly=FALSE)
void updateObsolete()
std::ifstream openInputStream(const QCString &name, bool binary=false, bool openAtEnd=false)
Definition portable.cpp:676
QCString pathSeparator()
Definition portable.cpp:391
const char * commandExtension()
Definition portable.cpp:478
QCString getenv(const QCString &variable)
Definition portable.cpp:338
bool search(std::string_view str, Match &match, const Ex &re, size_t pos)
Search in a given string str starting at position pos for a match against regular expression re.
Definition regex.cpp:748
static QCString stripFromPath(const QCString &p, const StringVector &l)
Definition util.cpp:309