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