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