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