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<*>. { ConfigImpl::config_warn("ignoring unknown character '{:c}' at line {}, file {}\n",yytext[0],g_yyLineNr,g_yyFileName); }
1306
1307%%
1308
1309/*@ ----------------------------------------------------------------------------
1310 */
1311
1312void ConfigImpl::writeTemplate(TextStream &t,bool sl,bool upd)
1313{
1314 /* print first lines of user comment that were at the beginning of the file, might have special meaning for editors */
1315 if (!m_startComment.isEmpty())
1316 {
1317 t << takeStartComment() << "\n";
1318 }
1319 t << "# Doxyfile " << getDoxygenVersion() << "\n\n";
1320 if (!sl)
1321 {
1322 t << convertToComment(m_header,"");
1323 }
1324 for (const auto &option : m_options)
1325 {
1326 option->writeTemplate(t,sl,upd);
1327 }
1328 /* print last lines of user comment that were at the end of the file */
1329 if (!m_userComment.isEmpty())
1330 {
1331 t << "\n";
1332 t << takeUserComment();
1333 }
1334}
1335
1337{
1338 t << "# Difference with default Doxyfile " << getFullVersion();
1339 t << "\n";
1340 for (const auto &option : m_options)
1341 {
1342 option->m_userComment = "";
1343 option->compareDoxyfile(t,compareMode);
1344 }
1345 if (!m_storeRepl.isEmpty())
1346 {
1347 t << "\n";
1348 t << takeStoreRepl() << "\n";
1349 }
1350}
1351
1353{
1354 t << "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n";
1355 t << "<doxyfile xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"doxyfile.xsd\" version=\"" << getDoxygenVersion() << "\" xml:lang=\"" << theTranslator->trISOLang() << "\">\n";
1356 for (const auto &option : m_options)
1357 {
1358 option->writeXMLDoxyfile(t);
1359 }
1360 t << "</doxyfile>\n";
1361}
1362
1364{
1365 for (const auto &option : m_options)
1366 {
1367 option->writeXSDDoxyfile(t);
1368 }
1369 for (const auto &option : m_disabled)
1370 {
1371 option->writeXSDDoxyfile(t);
1372 }
1373}
1374
1376{
1377 for (const auto &option : m_options)
1378 {
1379 option->convertStrToVal(compareMode);
1380 }
1381}
1383{
1384 for (const auto &option : m_options)
1385 {
1386 option->emptyValueToDefault();
1387 }
1388}
1389
1390static const reg::Ex reEnvVar(R"(\$\‍((\a[\w.-]*)\))"); // e.g. $(HOME)
1391static const reg::Ex reEnvVarExt(R"(\$\‍((\a[\w.-]*\‍(\a[\w.-]*\))\))"); // e.g. $(PROGRAMFILES(X86))
1392static const reg::Ex reEnvVarCMake(R"(@\a\w*@)"); // CMake type replacement (@...@)
1393static const reg::Ex reEnvVar1CMake(R"(\${\a\w*})"); // CMake type replacement (${...})
1395static bool containsEnvVar(QCString &str)
1396{
1398 std::string s = str.str();
1400}
1401
1402static void substEnvVarsInString(QCString &str)
1403{
1404 if (str.isEmpty()) return;
1405 auto replace = [](const std::string &s, const reg::Ex &re) -> std::string
1406 {
1407 reg::Iterator it(s,re);
1409 std::string result;
1410 size_t p = 0;
1411 for (; it!=end ; ++it)
1412 {
1413 const auto &match = *it;
1414 size_t i = match.position();
1415 size_t l = match.length();
1416 result+=s.substr(p,i-p);
1417 std::string matchContents = match[1].str();
1418 QCString env=Portable::getenv(matchContents); // get content of $(..) match
1419 substEnvVarsInString(env); // recursively expand variables if needed.
1420 result+=env.str();
1421 p=i+l;
1422 }
1423 result+=s.substr(p);
1424 return result;
1425 };
1426
1428}
1429
1430static void substEnvVarsInStrList(StringVector &sl)
1431{
1433 for (const auto &s : sl)
1434 {
1435 QCString result = s;
1436 bool wasQuoted = ((result.at(0)=='"') && (result.at(result.length()-1)=='"'));
1437 if (wasQuoted)
1438 {
1439 result = result.mid(1,result.length()-2);
1440 }
1441 else
1442 {
1443 wasQuoted = (result.find(' ')!=-1) || (result.find('\t')!=-1) || (result.find('"')!=-1);
1444 }
1445 // here we strip the quote again
1446 substEnvVarsInString(result);
1447
1448 //printf("Result %s was quoted=%d\n",qPrint(result),wasQuoted);
1449
1450 if (!wasQuoted) /* as a result of the expansion, a single string
1451 may have expanded into a list, which we'll
1452 add to sl. If the original string already
1453 contained multiple elements no further
1454 splitting is done to allow quoted items with spaces! */
1455 {
1456 int l = static_cast<int>(result.length());
1457 int p = 0;
1458 // skip spaces
1459 // search for a "word"
1460 for (int i=0;i<l;i++)
1461 {
1462 char c=0;
1463 // skip until start of new word
1464 while (i<l && ((c=result.at(i))==' ' || c=='\t')) i++;
1465 p=i; // p marks the start index of the word
1466 // skip until end of a word
1467 while (i<l && ((c=result.at(i))!=' ' && c!='\t' && c!='"')) i++;
1468 if (i<l) // not at the end of the string
1469 {
1470 if (c=='"') // word within quotes
1471 {
1472 p=i+1;
1473 for (i++;i<l;i++)
1474 {
1475 c=result.at(i);
1476 if (c=='"') // end quote
1477 {
1478 results.push_back(result.mid(p,i-p).str());
1479 p=i+1;
1480 break;
1481 }
1482 else if (c=='\\') // skip escaped stuff
1483 {
1484 i++;
1485 }
1486 }
1487 }
1488 else if (c==' ' || c=='\t') // separator
1489 {
1490 if (i>p) results.push_back(result.mid(p,i-p).str());
1491 p=i+1;
1492 }
1493 }
1494 }
1495 if (p!=l) // add the leftover as a string
1496 {
1497 results.push_back(result.right(l-p).str());
1498 }
1499 }
1500 else // just goto the next element in the list
1501 {
1502 if (!result.isEmpty()) results.push_back(result.str());
1503 }
1504 }
1505 sl = results;
1506}
1507
1509{
1514{
1519{
1524{
1529{
1531}
1532
1533//---------------------------------------------
1534
1536{
1537 for (const auto &option : m_options)
1538 {
1539 option->substEnvVars();
1540 }
1541}
1542
1543void ConfigImpl::init()
1544{
1545 for (const auto &option : m_options)
1546 {
1547 option->init();
1548 }
1549
1550 // sanity check if all depends relations are valid
1551 for (const auto &option : m_options)
1552 {
1553 QCString depName = option->dependsOn();
1554 if (!depName.isEmpty())
1555 {
1556 ConfigOption * opt = ConfigImpl::instance()->get(depName);
1557 if (opt==0)
1558 {
1559 config_term("Config option '{}' has invalid depends relation on unknown option '{}'\n",
1560 option->name(),depName);
1561 }
1562 }
1563 }
1564}
1565
1567{
1569}
1570
1571static QCString configFileToString(const QCString &name)
1572{
1573 if (name.isEmpty()) return QCString();
1574
1575 auto stream2string = [](std::istream &in) -> std::string
1576 {
1577 std::string ret;
1578 char buffer[4096];
1579 while (in.read(buffer, sizeof(buffer))) ret.append(buffer, sizeof(buffer));
1580 ret.append(buffer, static_cast<uint32_t>(in.gcount()));
1581 if (!ret.empty() && ret[ret.length()-1]!='\n') ret+='\n'; // to help the scanner
1582 return ret;
1583 };
1584
1585 if (name=="-") // read from stdin
1586 {
1587 // read contents from stdin into contents string
1588 return stream2string(std::cin);
1589 }
1590 else // read from file
1591 {
1592 std::ifstream f = Portable::openInputStream(name);
1593 if (!f.is_open())
1594 {
1595 ConfigImpl::config_term("file '{}' not found or could not be opened\n",name);
1596 return "";
1597 }
1598 return stream2string(f);
1599 }
1600}
1601
1602bool ConfigImpl::parseString(const QCString &fn,const QCString &str,bool update)
1603{
1604#ifdef FLEX_DEBUG
1605 configimplYYset_debug(Debug::isFlagSet(Debug::Lex_configimpl)?1:0);
1606#endif
1608 g_inputString = str.data();
1609 g_inputPosition = 0;
1610 g_yyFileName = fn;
1611 g_yyLineNr = 1;
1612 g_includeStack.clear();
1613 configimplYYrestart( configimplYYin );
1614 BEGIN( Start );
1615 g_configUpdate = update;
1616 configimplYYlex();
1618 g_inputString = 0;
1619 return TRUE;
1620}
1621
1622bool ConfigImpl::parse(const QCString &fn,bool update)
1623{
1624 g_encoding = "UTF-8";
1625 DebugLex debugLex(Debug::Lex_configimpl, __FILE__, qPrint(fn));
1626 bool retval = parseString(fn,configFileToString(fn), update);
1627 return retval;
1628}
1629
1630//----------------------------------------------------------------------
1631
1632static void cleanUpPaths(StringVector &str)
1633{
1634 for (size_t i=0;i<str.size();i++)
1635 {
1636 std::string path = str[i];
1637 std::replace(path.begin(),path.end(),'\\','/');
1638 if ((path[0]!='/' && (path.size()<=2 || path[1]!=':')) || path[path.size()-1]!='/')
1639 {
1640 FileInfo fi(path);
1641 if (fi.exists() && fi.isDir())
1642 {
1643 path = fi.absFilePath();
1644 if (path[path.size()-1]!='/') path+='/';
1645 }
1646 }
1647 str[i]=path;
1648 }
1649}
1650
1651static bool checkFileName(const QCString &s,const char *optionName)
1652{
1654 if ((val=="yes" || val=="true" || val=="1" || val=="all") ||
1655 (val=="no" || val=="false" || val=="0" || val=="none"))
1656 {
1657 err("file name expected for option {}, got {} instead. Ignoring...\n",optionName,s);
1658 return false;
1659 }
1660 return true;
1661}
1662
1663
1664void Config::init()
1665{
1667}
1668
1669static void checkList(const StringVector &list,const char *name, bool equalRequired,bool valueRequired)
1670{
1671 for (const auto &s: list)
1672 {
1673 QCString item = s;
1674 item=item.stripWhiteSpace();
1675 int i=item.find('=');
1676 if (i==-1 && equalRequired)
1677 {
1678 err("Illegal format for option {}, no equal sign ('=') specified for item '{}'\n",name,item);
1679 }
1680 if (i!=-1)
1681 {
1682 QCString myName=item.left(i).stripWhiteSpace();
1683 if (myName.isEmpty())
1684 {
1685 err("Illegal format for option {}, no name specified for item '{}'\n",name,item);
1686 }
1687 else if (valueRequired)
1688 {
1689 QCString myValue=item.right(item.length()-i-1).stripWhiteSpace();
1690 if (myValue.isEmpty())
1691 {
1692 err("Illegal format for option {}, no value specified for item '{}'\n",name,item);
1693 }
1694 }
1695 }
1696 }
1697}
1698
1699static void adjustBoolSetting(const char *depOption, const char *optionName,bool expectedValue)
1700{
1701 // lookup option by name
1702 const ConfigValues::Info *option = ConfigValues::instance().get(optionName);
1703 if (option && option->type==ConfigValues::Info::Bool) // safety check
1704 {
1705 if (ConfigValues::instance().*(option->value.b)!=expectedValue) // current value differs from expectation
1706 {
1707 err("When enabling {} the {} option should be {}. I'll adjust it for you.\n",depOption,optionName,expectedValue? "enabled" : "disabled");
1708 ConfigValues::instance().*(option->value.b)=expectedValue; // adjust option
1709 }
1710 }
1711}
1712
1713static void adjustStringSetting(const char *depOption, const char *optionName,const QCString &expectedValue)
1714{
1715 // lookup option by name
1716 const ConfigValues::Info *option = ConfigValues::instance().get(optionName);
1717 if (option && option->type==ConfigValues::Info::String) // safety check
1718 {
1719 if (ConfigValues::instance().*(option->value.s)!=expectedValue) // current value differs from expectation
1720 {
1721 err("When enabling {} the {} option should have value '{}'. I'll adjust it for you.\n",depOption,optionName,expectedValue);
1722 ConfigValues::instance().*(option->value.s)=expectedValue; // adjust option
1723 }
1724 }
1725}
1726
1727static void adjustColorStyleSetting(const char *depOption)
1728{
1729 auto updateColorStyle = [&depOption](HTML_COLORSTYLE_t curStyle,HTML_COLORSTYLE_t newStyle)
1730 {
1731 err("When enabling '{}' the 'HTML_COLORSTYLE' option should be either 'LIGHT' or 'DARK' but has value '{}'. I'll adjust it for you to '{}'.\n",
1732 depOption,
1733 HTML_COLORSTYLE_enum2str(curStyle),
1734 HTML_COLORSTYLE_enum2str(newStyle));
1735 Config_updateEnum(HTML_COLORSTYLE,newStyle);
1736 };
1737 auto colorStyle = Config_getEnum(HTML_COLORSTYLE);
1738 switch (colorStyle)
1739 {
1740 case HTML_COLORSTYLE_t::LIGHT:
1741 case HTML_COLORSTYLE_t::DARK:
1742 // no adjustment needed
1743 break;
1744 case HTML_COLORSTYLE_t::AUTO_LIGHT:
1745 case HTML_COLORSTYLE_t::TOGGLE:
1746 updateColorStyle(colorStyle,HTML_COLORSTYLE_t::LIGHT);
1747 break;
1748 case HTML_COLORSTYLE_t::AUTO_DARK:
1749 updateColorStyle(colorStyle,HTML_COLORSTYLE_t::DARK);
1750 break;
1751 }
1752}
1753
1754
1755void Config::checkAndCorrect(bool quiet, const bool check)
1756{
1757 ConfigValues::instance().init();
1758
1759 Config_updateBool(QUIET,quiet || Config_getBool(QUIET));
1760 //------------------------
1761 // check WARN_FORMAT
1762 QCString warnFormat = Config_getString(WARN_FORMAT);
1763 if (warnFormat.find("$file")==-1)
1764 {
1765 warn_uncond("warning format does not contain a $file tag!\n");
1766 }
1767 if (warnFormat.find("$line")==-1)
1768 {
1769 warn_uncond("warning format does not contain a $line tag!\n");
1770 }
1771 if (warnFormat.find("$text")==-1)
1772 {
1773 warn_uncond("warning format does not contain a $text tag!\n");
1774 }
1775
1776 //------------------------
1777 // check and correct PAPER_TYPE
1778 QCString paperType = Config_getEnumAsString(PAPER_TYPE);
1779 paperType=paperType.lower().stripWhiteSpace();
1780 if (paperType.isEmpty() || paperType=="a4wide")
1781 {
1782 // use a4
1783 Config_updateEnum(PAPER_TYPE,PAPER_TYPE_t::a4);
1784 }
1785 else if (paperType!="a4" && paperType!="letter" &&
1786 paperType!="legal" && paperType!="executive")
1787 {
1788 err("Unknown page type '{}' specified\n",paperType);
1789 Config_updateEnum(PAPER_TYPE,PAPER_TYPE_t::a4);
1790 }
1791
1792 //------------------------
1793 // check & correct STRIP_FROM_PATH
1794 StringVector stripFromPath = Config_getList(STRIP_FROM_PATH);
1795 if (stripFromPath.empty()) // by default use the current path
1796 {
1797 std::string p = Dir::currentDirPath()+"/";
1798 stripFromPath.push_back(p);
1799 }
1800 else
1801 {
1803 }
1804 Config_updateList(STRIP_FROM_PATH,stripFromPath);
1805
1806 //------------------------
1807 // check & correct STRIP_FROM_INC_PATH
1808 StringVector stripFromIncPath = Config_getList(STRIP_FROM_INC_PATH);
1809 cleanUpPaths(stripFromIncPath);
1810 Config_updateList(STRIP_FROM_INC_PATH,stripFromIncPath);
1811
1812 //------------------------
1813 // Test to see if HTML header is valid
1814 QCString headerFile = Config_getString(HTML_HEADER);
1815 if (check && !headerFile.isEmpty())
1816 {
1817 FileInfo fi(headerFile.str());
1818 if (!fi.exists())
1819 {
1820 ConfigImpl::config_term("tag HTML_HEADER: header file '{}' "
1821 "does not exist\n",headerFile);
1822 }
1823 }
1824
1825 //------------------------
1826 // Test to see if HTML footer is valid
1827 QCString footerFile = Config_getString(HTML_FOOTER);
1828 if (check && !footerFile.isEmpty())
1829 {
1830 FileInfo fi(footerFile.str());
1831 if (!fi.exists())
1832 {
1833 ConfigImpl::config_term("tag HTML_FOOTER: footer file '{}' "
1834 "does not exist\n",footerFile);
1835 }
1836 }
1837
1838 //------------------------
1839 // Test to see if MathJax code file is valid
1840 if (Config_getBool(USE_MATHJAX))
1841 {
1842 auto mathJaxFormat = Config_getEnum(MATHJAX_FORMAT);
1843 auto mathjaxVersion = Config_getEnum(MATHJAX_VERSION);
1844 switch (mathjaxVersion)
1845 {
1846 case MATHJAX_VERSION_t::MathJax_2:
1847 if (mathJaxFormat==MATHJAX_FORMAT_t::chtml)
1848 {
1849 Config_updateEnum(MATHJAX_FORMAT,MATHJAX_FORMAT_t::HTML_CSS);
1850 }
1851 break;
1852 case MATHJAX_VERSION_t::MathJax_3:
1853 if (mathJaxFormat==MATHJAX_FORMAT_t::HTML_CSS || mathJaxFormat==MATHJAX_FORMAT_t::NativeMML)
1854 {
1855 Config_updateEnum(MATHJAX_FORMAT,MATHJAX_FORMAT_t::chtml);
1856 }
1857 break;
1858 case MATHJAX_VERSION_t::MathJax_4:
1859 if (mathJaxFormat==MATHJAX_FORMAT_t::HTML_CSS || mathJaxFormat==MATHJAX_FORMAT_t::NativeMML)
1860 {
1861 Config_updateEnum(MATHJAX_FORMAT,MATHJAX_FORMAT_t::chtml);
1862 }
1863 break;
1864 }
1865
1866 QCString mathJaxCodefile = Config_getString(MATHJAX_CODEFILE);
1867 if (check && !mathJaxCodefile.isEmpty())
1868 {
1869 FileInfo fi(mathJaxCodefile.str());
1870 if (!fi.exists())
1871 {
1872 ConfigImpl::config_term("tag MATHJAX_CODEFILE file '{}' "
1873 "does not exist\n",mathJaxCodefile);
1874 }
1875 }
1876 QCString path = Config_getString(MATHJAX_RELPATH);
1877 if (path.isEmpty())
1878 {
1879 path = "https://cdn.jsdelivr.net/npm/mathjax@";
1880 switch (mathjaxVersion)
1881 {
1882 case MATHJAX_VERSION_t::MathJax_2: path += "2"; break;
1883 case MATHJAX_VERSION_t::MathJax_3: path += "3"; break;
1884 case MATHJAX_VERSION_t::MathJax_4: path += "4"; break;
1885 }
1886 }
1887
1888 if (path.at(path.length()-1)!='/')
1889 {
1890 path+="/";
1891 }
1892 Config_updateString(MATHJAX_RELPATH,path);
1893 }
1894
1895 //------------------------
1896 // Test to see if LaTeX header is valid
1897 QCString latexHeaderFile = Config_getString(LATEX_HEADER);
1898 if (check && !latexHeaderFile.isEmpty())
1899 {
1900 FileInfo fi(latexHeaderFile.str());
1901 if (!fi.exists())
1902 {
1903 ConfigImpl::config_term("tag LATEX_HEADER: header file '{}' "
1904 "does not exist\n",latexHeaderFile);
1905 }
1906 }
1907
1908 //------------------------
1909 // Test to see if LaTeX footer is valid
1910 QCString latexFooterFile = Config_getString(LATEX_FOOTER);
1911 if (check && !latexFooterFile.isEmpty())
1912 {
1913 FileInfo fi(latexFooterFile.str());
1914 if (!fi.exists())
1915 {
1916 ConfigImpl::config_term("tag LATEX_FOOTER: footer file '{}' "
1917 "does not exist\n",latexFooterFile);
1918 }
1919 }
1920
1921 //------------------------
1922 // check include path
1923 const StringVector &includePath = Config_getList(INCLUDE_PATH);
1924 for (const auto &s : includePath)
1925 {
1926 FileInfo fi(s);
1927 if (!fi.exists())
1928 {
1929 warn_uncond("tag INCLUDE_PATH: include path '{}' does not exist\n",s);
1930 }
1931 }
1932
1933 //------------------------
1934 // check PREDEFINED
1935 if (Config_getBool(ENABLE_PREPROCESSING))
1936 {
1937 const StringVector &predefList = Config_getList(PREDEFINED);
1938 for (const auto &s : predefList)
1939 {
1940 QCString predef = s;
1941 predef=predef.stripWhiteSpace();
1942 int i_equals=predef.find('=');
1943 int i_obrace=predef.find('(');
1944 if ((i_obrace==0) || (i_equals==0) || (i_equals==1 && predef.at(i_equals-1)==':'))
1945 {
1946 err("Illegal PREDEFINED format '{}', no define name specified\n",predef);
1947 }
1948 }
1949 }
1950
1951 //------------------------
1952 // check EXTENSION_MAPPING
1953 checkList(Config_getList(EXTENSION_MAPPING),"EXTENSION_MAPPING",TRUE,TRUE);
1954
1955 //------------------------
1956 // check FILTER_PATTERNS
1957 checkList(Config_getList(FILTER_PATTERNS),"FILTER_PATTERNS",TRUE,TRUE);
1958
1959 //------------------------
1960 // check FILTER_SOURCE_PATTERNS
1961 checkList(Config_getList(FILTER_SOURCE_PATTERNS),"FILTER_SOURCE_PATTERNS",FALSE,FALSE);
1962
1963 //------------------------
1964 // check INPUT_FILE_ENCODING
1965 checkList(Config_getList(INPUT_FILE_ENCODING),"INPUT_FILE_ENCODING",TRUE,TRUE);
1966
1967 //------------------------
1968 // check TAGFILES
1969 checkList(Config_getList(TAGFILES),"TAGFILES",FALSE,TRUE);
1970
1971 //------------------------
1972 // check EXTRA_SEARCH_MAPPINGS
1973 if (Config_getBool(SEARCHENGINE) && Config_getBool(GENERATE_HTML))
1974 {
1975 checkList(Config_getList(EXTRA_SEARCH_MAPPINGS),"EXTRA_SEARCH_MAPPING",TRUE,TRUE);
1976 }
1977
1978 int numThreads = Config_getInt(NUM_PROC_THREADS);
1979 if (numThreads==0)
1980 {
1981 numThreads = static_cast<int>(std::thread::hardware_concurrency());
1982 Config_updateInt(NUM_PROC_THREADS,numThreads);
1983 }
1984
1985 //------------------------
1986
1987 // check for settings that are inconsistent with having GENERATE_HTMLHELP enabled
1988 if (Config_getBool(GENERATE_HTMLHELP))
1989 {
1990 const char *depOption = "GENERATE_HTMLHELP";
1991 adjustBoolSetting( depOption, "GENERATE_TREEVIEW", false );
1992 adjustBoolSetting( depOption, "SEARCHENGINE", false );
1993 adjustBoolSetting( depOption, "HTML_DYNAMIC_MENUS", false );
1994 adjustBoolSetting( depOption, "HTML_DYNAMIC_SECTIONS",false );
1995 adjustBoolSetting( depOption, "HTML_COPY_CLIPBOARD", false );
1996 adjustStringSetting(depOption, "HTML_FILE_EXTENSION", ".html");
1997 adjustColorStyleSetting(depOption);
1998 const StringVector &tagFileList = Config_getList(TAGFILES);
1999 StringVector filteredTagFileList;
2000 for (const auto &s : tagFileList)
2001 {
2002 bool validUrl = false;
2003 size_t eqPos = s.find('=');
2004 if (eqPos!=std::string::npos) // tag command contains a destination
2005 {
2006 QCString url = QCString(s.substr(eqPos+1)).stripWhiteSpace().lower();
2007 validUrl = url.startsWith("http:") || url.startsWith("https:") ||
2008 url.startsWith("ms-its:");
2009 }
2010 if (validUrl)
2011 {
2012 filteredTagFileList.push_back(s);
2013 }
2014 else
2015 {
2016 err("When enabling GENERATE_HTMLHELP the TAGFILES option should only contain destinations "
2017 "with https / http addresses (not: {}). I'll adjust it for you.\n",s);
2018 }
2019 }
2020 Config_updateList(TAGFILES,filteredTagFileList);
2021 }
2022
2023 // check for settings that are inconsistent with having INLINE_GROUPED_CLASSES enabled
2024 if (Config_getBool(INLINE_GROUPED_CLASSES))
2025 {
2026 const char *depOption = "INLINE_GROUPED_CLASSES";
2027 adjustBoolSetting(depOption, "SEPARATE_MEMBER_PAGES", false);
2028 }
2029
2030 //------------------------
2031 int dotNumThreads = Config_getInt(DOT_NUM_THREADS);
2032 if (dotNumThreads<=0)
2033 {
2034 dotNumThreads=std::max(2u,std::thread::hardware_concurrency()+1);
2035 Config_updateInt(DOT_NUM_THREADS,dotNumThreads);
2036 }
2037
2038 //------------------------
2039 // check plantuml path
2040 QCString plantumlJarPath = Config_getString(PLANTUML_JAR_PATH);
2041 if (!plantumlJarPath.isEmpty())
2042 {
2043 FileInfo pu(plantumlJarPath.str());
2044 if (pu.exists() && pu.isDir()) // PLANTUML_JAR_PATH is directory
2045 {
2046 QCString plantumlJar = plantumlJarPath+Portable::pathSeparator()+"plantuml.jar";
2047 FileInfo jar(plantumlJar.str());
2048 if (jar.exists() && jar.isFile())
2049 {
2050 plantumlJarPath = plantumlJar;
2051 }
2052 else
2053 {
2054 err("Jar file 'plantuml.jar' not found at location specified via PLANTUML_JAR_PATH: '{}'\n",plantumlJarPath);
2055 plantumlJarPath="";
2056 }
2057 }
2058 else if (pu.exists() && pu.isFile()) // PLANTUML_JAR_PATH is file
2059 {
2060 // Nothing to be done
2061 }
2062 else
2063 {
2064 err("PLANTUML_JAR_PATH is not a directory with a 'plantuml.jar' file or is not an existing file: {}\n",plantumlJarPath);
2065 plantumlJarPath="";
2066 }
2067 Config_updateString(PLANTUML_JAR_PATH,plantumlJarPath);
2068 }
2069
2070 //------------------------
2071 // check dia path
2072 QCString diaPath = Config_getString(DIA_PATH);
2073 if (!diaPath.isEmpty())
2074 {
2075 QCString diaExe = diaPath+"/dia"+Portable::commandExtension();
2076 FileInfo dp(diaExe.str());
2077 if (!dp.exists() || !dp.isFile())
2078 {
2079 warn_uncond("dia could not be found at {}\n",diaPath);
2080 diaPath="";
2081 }
2082 else
2083 {
2084 diaPath=dp.dirPath(TRUE)+"/";
2085#if defined(_WIN32) // convert slashes
2086 size_t i=0,l=diaPath.length();
2087 for (i=0;i<l;i++) if (diaPath.at(i)=='/') diaPath.at(i)='\\';
2088#endif
2089 }
2090 Config_updateString(DIA_PATH,diaPath);
2091 }
2092
2093 //------------------------
2094 // check INPUT
2095 StringVector inputSources=Config_getList(INPUT);
2096 if (inputSources.empty())
2097 {
2098 // use current dir as the default
2099 inputSources.push_back(Dir::currentDirPath());
2100 }
2101 else
2102 {
2103 for (const auto &s : inputSources)
2104 {
2105 FileInfo fi(s);
2106 if (!fi.exists())
2107 {
2108 warn_uncond("tag INPUT: input source '{}' does not exist\n",s);
2109 }
2110 }
2111 }
2112 Config_updateList(INPUT,inputSources);
2113
2114 //------------------------
2115 // if no output format is enabled, warn the user
2116 if (!Config_getBool(GENERATE_HTML) &&
2117 !Config_getBool(GENERATE_LATEX) &&
2118 !Config_getBool(GENERATE_MAN) &&
2119 !Config_getBool(GENERATE_RTF) &&
2120 !Config_getBool(GENERATE_XML) &&
2121 !Config_getBool(GENERATE_PERLMOD) &&
2122 !Config_getBool(GENERATE_RTF) &&
2123 !Config_getBool(GENERATE_DOCBOOK) &&
2124 !Config_getBool(GENERATE_AUTOGEN_DEF) &&
2125 Config_getString(GENERATE_TAGFILE).isEmpty()
2126 )
2127 {
2128 warn_uncond("No output formats selected! Set at least one of the main GENERATE_* options to YES.\n");
2129 }
2130
2131 //------------------------
2132 // check HTMLHELP creation requirements
2133 if (!Config_getBool(GENERATE_HTML) &&
2134 Config_getBool(GENERATE_HTMLHELP))
2135 {
2136 warn_uncond("GENERATE_HTMLHELP=YES requires GENERATE_HTML=YES.\n");
2137 }
2138
2139 //------------------------
2140 // check sitemap creation requirements
2141 if (!Config_getBool(GENERATE_HTML) &&
2142 !Config_getString(SITEMAP_URL).isEmpty())
2143 {
2144 warn_uncond("Setting SITEMAP_URL requires GENERATE_HTML=YES.\n");
2145 }
2146
2147 //------------------------
2148 // check QHP creation requirements
2149 if (Config_getBool(GENERATE_QHP))
2150 {
2151 if (!Config_getBool(GENERATE_HTML))
2152 {
2153 warn_uncond("GENERATE_QHP=YES requires GENERATE_HTML=YES.\n");
2154 }
2155 if (Config_getString(QHP_NAMESPACE).isEmpty())
2156 {
2157 err("GENERATE_QHP=YES requires QHP_NAMESPACE to be set. Using 'org.doxygen.doc' as default!.\n");
2158 Config_updateString(QHP_NAMESPACE,"org.doxygen.doc");
2159 }
2160
2161 if (Config_getString(QHP_VIRTUAL_FOLDER).isEmpty())
2162 {
2163 err("GENERATE_QHP=YES requires QHP_VIRTUAL_FOLDER to be set. Using 'doc' as default!\n");
2164 Config_updateString(QHP_VIRTUAL_FOLDER,"doc");
2165 }
2166 const StringVector &tagFileList = Config_getList(TAGFILES);
2167 if (!tagFileList.empty())
2168 {
2169 err("When enabling GENERATE_QHP the TAGFILES option should be empty. I'll adjust it for you.\n");
2170 Config_updateList(TAGFILES,StringVector());
2171 }
2172 }
2173
2174 //------------------------
2175 if (Config_getBool(OPTIMIZE_OUTPUT_JAVA) && Config_getBool(INLINE_INFO))
2176 {
2177 // don't show inline info for Java output, since Java has no inline
2178 // concept.
2179 Config_updateBool(INLINE_INFO,FALSE);
2180 }
2181
2182 //------------------------
2183 int depth = Config_getInt(MAX_DOT_GRAPH_DEPTH);
2184 if (depth==0)
2185 {
2186 Config_updateInt(MAX_DOT_GRAPH_DEPTH,1000);
2187 }
2188
2189 //------------------------
2190 if (Config_getBool(INTERACTIVE_SVG))
2191 {
2192 // issue 11308
2193 if ((Config_getEnum(DOT_IMAGE_FORMAT) == DOT_IMAGE_FORMAT_t::svg_cairo) ||
2194 (Config_getEnum(DOT_IMAGE_FORMAT) == DOT_IMAGE_FORMAT_t::svg_cairo_cairo))
2195 {
2196 err("When using DOT_IMAGE_FORMAT with {} the INTERACTIVE_SVG option should be disabled. I'll adjust it for you.\n",
2197 Config_getEnumAsString(DOT_IMAGE_FORMAT));
2198 Config_updateBool(INTERACTIVE_SVG,FALSE);
2199 }
2200 }
2201
2202 //------------------------
2203 // check for settings that are inconsistent with having OPTIMIZED_OUTPUT_VHDL enabled
2204 if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
2205 {
2206 const char *depOption = "OPTIMIZE_OUTPUT_VHDL";
2207 adjustBoolSetting(depOption,"INLINE_INHERITED_MEMB",false);
2208 adjustBoolSetting(depOption,"INHERIT_DOCS", false);
2209 adjustBoolSetting(depOption,"HIDE_SCOPE_NAMES", true );
2210 adjustBoolSetting(depOption,"EXTRACT_PRIVATE", true );
2211 adjustBoolSetting(depOption,"ENABLE_PREPROCESSING", false);
2212 adjustBoolSetting(depOption,"EXTRACT_PACKAGE", true );
2213 }
2214
2215 if (!checkFileName(Config_getString(GENERATE_TAGFILE),"GENERATE_TAGFILE"))
2216 {
2217 Config_updateString(GENERATE_TAGFILE,"");
2218 }
2219
2220#if 0 // TODO: this breaks test 25; SOURCEBROWSER = NO and SOURCE_TOOLTIPS = YES.
2221 // So this and other regressions should be analyzed and fixed before this can be enabled
2222 // disable any boolean options that depend on disabled options
2223 for (const auto &option : m_options)
2224 {
2225 QCString depName = option->dependsOn(); // option has a dependency
2226 if (!depName.isEmpty())
2227 {
2228 ConfigOption * dep = Config::instance()->get(depName);
2229 if (dep->kind()==ConfigOption::O_Bool &&
2230 ConfigImpl_getBool("depName")==FALSE) // dependent option is disabled
2231 {
2232 if (option->kind()==ConfigOption::O_Bool)
2233 {
2234 printf("disabling option %s\n",qPrint(option->name()));
2235 ConfigImpl_getBool("option->name("))=FALSE; // also disable this option
2236 }
2237 }
2238 }
2239 }
2240#endif
2241
2242}
2243
2244static void updateAttribute(DotAttributes& attr, QCString name, ConfigObsolete* value)
2245{
2246 attr.updateValue(name,*value->valueStringRef());
2247}
2248
2250{
2251 //------------------------
2252 // check for presence of obsolete CLASS_DIAGRAM option and correct CLASS_GRAPH if needed
2253 ConfigOption *classDiagramsOpt = ConfigImpl::instance()->get("CLASS_DIAGRAMS");
2254 ConfigOption *haveDotOpt = ConfigImpl::instance()->get("HAVE_DOT");
2255 ConfigOption *classGraphOpt = ConfigImpl::instance()->get("CLASS_GRAPH");
2256 if (classDiagramsOpt && classDiagramsOpt->kind()==ConfigOption::O_Obsolete &&
2257 haveDotOpt && classGraphOpt)
2258 {
2259 ConfigObsolete *classDiagramsOpt_ = dynamic_cast<ConfigObsolete*>(classDiagramsOpt);
2260 ConfigBool *haveDotOpt_ = dynamic_cast<ConfigBool*>(haveDotOpt);
2261 ConfigEnum *classGraphOpt_ = dynamic_cast<ConfigEnum*>(classGraphOpt);
2262 if (classDiagramsOpt_ && haveDotOpt_ && classGraphOpt_ &&
2263 classDiagramsOpt_->isPresent() && classDiagramsOpt_->orgType()==ConfigOption::O_Bool)
2264 {
2265 QCString classDiagramValue = *classDiagramsOpt_->valueStringRef();
2266 QCString haveDotValue = *haveDotOpt_->valueStringRef();
2267 QCString &classGraphValue = *classGraphOpt_->valueRef();
2268 bool isValid1=true, isValid2=true;
2269 bool bClassDiagrams = convertStringToBool(classDiagramValue,isValid1);
2270 bool bHaveDot = haveDotValue.isEmpty() ? false : convertStringToBool(haveDotValue, isValid2);
2271 if (isValid1 && isValid2 && !bClassDiagrams && !bHaveDot && classGraphValue.lower()=="yes")
2272 {
2273 warn_uncond("Changing CLASS_GRAPH option to TEXT because obsolete option CLASS_DIAGRAM was found and set to NO.\n");
2274 classGraphValue="TEXT";
2275 }
2276 }
2277 }
2278
2279 // update TIMESTAMP based on HTML_TIMESTAMP and LATEX_TIMESTAMP
2280 ConfigOption *HtmlTimestamp = ConfigImpl::instance()->get("HTML_TIMESTAMP");
2281 ConfigOption *timestampOpt = ConfigImpl::instance()->get("TIMESTAMP");
2282 bool reset = false;
2283 if (HtmlTimestamp && HtmlTimestamp->kind()==ConfigOption::O_Obsolete && timestampOpt)
2284 {
2285 ConfigObsolete *htmlTimestamp_ = dynamic_cast<ConfigObsolete*>(HtmlTimestamp);
2286 ConfigEnum *timestampOpt_ = dynamic_cast<ConfigEnum*>(timestampOpt);
2287 if (htmlTimestamp_ && timestampOpt_ &&
2288 htmlTimestamp_->isPresent() && htmlTimestamp_->orgType()==ConfigOption::O_Bool)
2289 {
2290 QCString &timestampValue = *timestampOpt_->valueRef();
2291 QCString htmlTimestampValue = *htmlTimestamp_->valueStringRef();
2292 bool isValid=true;
2293 bool bTimestamp = convertStringToBool(htmlTimestampValue,isValid);
2294 if (isValid && bTimestamp)
2295 {
2296 reset = true;
2297 timestampValue = "YES";
2298 }
2299 }
2300 }
2301 ConfigOption *LatexTimestamp = ConfigImpl::instance()->get("LATEX_TIMESTAMP");
2302 if (!reset && LatexTimestamp && LatexTimestamp->kind()==ConfigOption::O_Obsolete && timestampOpt)
2303 {
2304 ConfigObsolete *latexTimestamp_ = dynamic_cast<ConfigObsolete*>(LatexTimestamp);
2305 ConfigEnum *timestampOpt_ = dynamic_cast<ConfigEnum*>(timestampOpt);
2306 if (latexTimestamp_ && timestampOpt_ &&
2307 latexTimestamp_->isPresent() && latexTimestamp_->orgType()==ConfigOption::O_Bool)
2308 {
2309 QCString &timestampValue = *timestampOpt_->valueRef();
2310 QCString latexTimestampValue = *latexTimestamp_->valueStringRef();
2311 bool isValid=true;
2312 bool bTimestamp = convertStringToBool(latexTimestampValue,isValid);
2313 if (isValid && bTimestamp) timestampValue = "YES";
2314 }
2315 }
2316
2317 auto fontname = dynamic_cast<ConfigObsolete*>(ConfigImpl::instance()->get("DOT_FONTNAME"));
2318 auto fontsize = dynamic_cast<ConfigObsolete*>(ConfigImpl::instance()->get("DOT_FONTSIZE"));
2319
2320 // correct DOT_FONTNAME if needed
2321 if (fontname &&
2322 (*fontname->valueStringRef() == "FreeSans"
2323 || *fontname->valueStringRef() == "FreeSans.ttf"))
2324 warn_uncond("doxygen no longer ships with the FreeSans font.\n"
2325 " You may want to clear or change DOT_FONTNAME.\n"
2326 " Otherwise you run the risk that the wrong font is being used for dot generated graphs.\n");
2327
2328 auto commonAttrOpt = dynamic_cast<ConfigString*>(ConfigImpl::instance()->get("DOT_COMMON_ATTR"));
2329 if (commonAttrOpt)
2330 {
2331 QCString& commonAttrStr = *commonAttrOpt->valueRef();
2332 DotAttributes commonAttr(commonAttrStr);
2333 updateAttribute(commonAttr, "fontname", fontname);
2334 updateAttribute(commonAttr, "fontsize", fontsize);
2335 commonAttrStr = commonAttr.str();
2336 }
2337
2338 auto edgeAttrOpt = dynamic_cast<ConfigString*>(ConfigImpl::instance()->get("DOT_EDGE_ATTR"));
2339 if (edgeAttrOpt)
2340 {
2341 QCString& edgeAttrStr = *edgeAttrOpt->valueRef();
2342 DotAttributes edgeAttr(edgeAttrStr);
2343 updateAttribute(edgeAttr, "labelfontname", fontname);
2344 updateAttribute(edgeAttr, "labelfontsize", fontsize);
2345 edgeAttrStr = edgeAttr.str();
2346 }
2347}
2348
2349void Config::writeTemplate(TextStream &t,bool shortList,bool update)
2350{
2351 ConfigImpl::instance()->writeTemplate(t,shortList,update);
2352}
2353
2355{
2356 postProcess(FALSE, compareMode);
2357 ConfigImpl::instance()->compareDoxyfile(t, compareMode);
2358}
2359
2361{
2366{
2368}
2369
2370bool Config::parse(const QCString &fileName,bool update, Config::CompareMode compareMode)
2371{
2372 g_compareMode = compareMode;
2373 bool parseRes = ConfigImpl::instance()->parse(fileName,update);
2374 if (!parseRes) return parseRes;
2375
2376 // Internally we use the default format UTF-8 and
2377 // when updating etc. the output is in this format as well and not in the read format
2378 ConfigString *option = dynamic_cast<ConfigString*>(g_config->get("DOXYFILE_ENCODING"));
2379 option->init();
2380
2381 return parseRes;
2382}
2383
2384void Config::postProcess(bool clearHeaderAndFooter, Config::CompareMode compareMode)
2385{
2386 auto configInst = ConfigImpl::instance();
2387 if (compareMode != CompareMode::CompressedNoEnv) configInst->substituteEnvironmentVars();
2388 if (compareMode == CompareMode::Full) configInst->emptyValueToDefault();
2389 configInst->convertStrToVal(compareMode);
2390
2391 // avoid bootstrapping issues when the g_config file already
2392 // refers to the files that we are supposed to parse.
2393 if (clearHeaderAndFooter)
2394 {
2395 Config_updateString(HTML_HEADER ,"");
2396 Config_updateString(HTML_FOOTER ,"");
2397 Config_updateString(LATEX_HEADER,"");
2398 Config_updateString(LATEX_FOOTER,"");
2399 }
2400}
2401
2402void Config::deinit()
2403{
2405}
2406
2407#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