/****************************************************************************** * * Copyright (C) 1997-2019 by Dimitri van Heesch. * * Permission to use, copy, modify, and distribute this software and its * documentation under the terms of the GNU General Public License is hereby * granted. No representations are made about the suitability of this software * for any purpose. It is provided "as is" without express or implied warranty. * See the GNU General Public License for more details. * */ %option never-interactive %option prefix="config_doxywYY" %{ /* * includes */ #include "config.h" #include "input.h" #include #include #include #include #include #include #include #include #include #define YY_NO_UNISTD_H 1 #define MAX_INCLUDE_DEPTH 10 /* ----------------------------------------------------------------- * * static variables */ struct ConfigFileState { int lineNr; FILE *file; YY_BUFFER_STATE oldState; YY_BUFFER_STATE newState; QString fileName; }; static const QHash *g_options; static FILE *g_file; static QString g_yyFileName; static QString g_includeName; static QVariant g_includePathList; static QStack g_includeStack; static int g_includeDepth; static QVariant *g_arg; static Input *g_curOption=0; static QString g_elemStr; static QTextCodec *g_codec = QTextCodec::codecForName("UTF-8"); static QString g_codecName = QString::fromLatin1("UTF-8"); static int g_lastState; static QByteArray g_tmpString; /* ----------------------------------------------------------------- */ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size); static int yyread(char *buf,int maxSize) { // no file included if (g_includeStack.isEmpty()) { return fread(buf,1,maxSize,g_file); } else { return fread(buf,1,maxSize,g_includeStack.top()->file); } } static QString warning_str = QString::fromLatin1("warning: "); static QString error_str = QString::fromLatin1("error: "); void config_err(const char *fmt, ...) { QString msg = error_str; msg.append(QString::fromLatin1(fmt)); va_list args; va_start(args, fmt); vfprintf(stderr, qPrintable(msg), args); va_end(args); } void config_warn(const char *fmt, ...) { QString msg = warning_str; msg.append(QString::fromLatin1(fmt)); va_list args; va_start(args, fmt); vfprintf(stderr, qPrintable(msg), args); va_end(args); } static void substEnvVarsInStrList(QStringList &sl); static void substEnvVarsInString(QString &s); static void checkEncoding() { Input *option = g_options->value(QString::fromLatin1("DOXYFILE_ENCODING")); if (option && option->value().toString()!=g_codecName) { QTextCodec *newCodec = QTextCodec::codecForName(option->value().toString().toLatin1()); if (newCodec) { g_codec = newCodec; g_codecName = option->value().toString(); } } } static FILE *tryPath(const QString &path,const QString &fileName) { QString absName=!path.isEmpty() ? path+QString::fromLatin1("/")+fileName : fileName; QFileInfo fi(absName); if (fi.exists() && fi.isFile()) { FILE *f = fopen(absName.toLocal8Bit(),"r"); if (f==NULL) config_err("could not open file %s for reading\n",qPrintable(absName)); else return f; } return NULL; } static FILE *findFile(const QString &fileName) { if (QFileInfo(fileName).isAbsolute()) // absolute path { return tryPath(QString(), fileName); } // relative path, try with include paths in the list QStringList sl = g_includePathList.toStringList(); substEnvVarsInStrList(sl); foreach (QString s, sl) { FILE *f = tryPath(s,fileName); if (f) return f; } // try cwd if g_includePathList fails return tryPath(QString::fromLatin1("."),fileName); } static void readIncludeFile(const QString &incName) { if (g_includeDepth==MAX_INCLUDE_DEPTH) { config_err("maximum include depth (%d) reached, %s is not included. Aborting...\n", MAX_INCLUDE_DEPTH,qPrintable(incName)); exit(1); } QString inc = incName; substEnvVarsInString(inc); inc = inc.trimmed(); uint incLen = inc.length(); if (inc.at(0)==QChar::fromLatin1('"') && inc.at(incLen-1)==QChar::fromLatin1('"')) // strip quotes { inc=inc.mid(1,incLen-2); } FILE *f = findFile(inc); if (f) // see if the include file can be found { // For debugging #if SHOW_INCLUDES for (i=0;ioldState=YY_CURRENT_BUFFER; fs->fileName=g_yyFileName; fs->file=f; // push the state on the stack g_includeStack.push(fs); // set the scanner to the include file yy_switch_to_buffer(yy_create_buffer(f, YY_BUF_SIZE)); fs->newState=YY_CURRENT_BUFFER; g_yyFileName=inc; g_includeDepth++; } else { config_err("@INCLUDE = %s: not found!\n",qPrintable(inc)); exit(1); } } %} %option nounput %option noyywrap %option yylineno %x Start %x SkipComment %x SkipInvalid %x GetString %x GetStrList %x GetQuotedString %x GetEnvVar %x Include %% <*>\0x0d "#" { BEGIN(SkipComment); } [a-z_A-Z][a-z_A-Z0-9]*[ \t]*"=" { QString cmd = g_codec->toUnicode(yytext); cmd=cmd.left(cmd.length()-1).trimmed(); g_curOption = g_options->value(cmd); if (g_curOption==0) // oops not known { config_warn("ignoring unsupported tag '%s' at line %d, file %s\n", qPrintable(cmd),yylineno,qPrintable(g_yyFileName)); BEGIN(SkipInvalid); } else // known tag { //option->setEncoding(encoding); g_arg = &g_curOption->value(); switch(g_curOption->kind()) { case Input::StrList: g_elemStr = QString(); *g_arg = QStringList(); BEGIN(GetStrList); break; case Input::String: BEGIN(GetString); break; case Input::Int: BEGIN(GetString); break; case Input::Bool: BEGIN(GetString); break; case Input::Obsolete: config_warn("Tag '%s' at line %d of file %s has become obsolete.\n" "To avoid this warning please update your configuration " "file using \"doxygen -u\"\n", qPrintable(cmd), yylineno,qPrintable(g_yyFileName)); BEGIN(SkipInvalid); break; } } } [a-z_A-Z][a-z_A-Z0-9]*[ \t]*"+=" { QString cmd=g_codec->toUnicode(yytext); cmd=cmd.left(cmd.length()-2).trimmed(); g_curOption = g_options->value(cmd); if (g_curOption==0) // oops not known { config_warn("ignoring unsupported tag '%s' at line %d, file %s\n", yytext,yylineno,qPrintable(g_yyFileName)); BEGIN(SkipInvalid); } else // known tag { switch(g_curOption->kind()) { case Input::StrList: g_arg = &g_curOption->value(); g_elemStr=QString(); BEGIN(GetStrList); break; case Input::String: case Input::Int: case Input::Bool: config_warn("operator += not supported for '%s'. Ignoring line at line %d, file %s\n", yytext,yylineno,qPrintable(g_yyFileName)); BEGIN(SkipInvalid); break; case Input::Obsolete: config_warn("Tag '%s' at line %d of file %s has become obsolete.\n" "To avoid this warning please update your configuration " "file using \"doxygen -u\"\n", qPrintable(cmd),yylineno,qPrintable(g_yyFileName)); BEGIN(SkipInvalid); break; } } } "@INCLUDE_PATH"[ \t]*"=" { BEGIN(GetStrList); g_arg=&g_includePathList; *g_arg = QStringList(); g_elemStr=QString(); } /* include a config file */ "@INCLUDE"[ \t]*"=" { BEGIN(Include);} ([^ \"\t\r\n]+)|("\""[^\n\"]+"\"") { readIncludeFile(g_codec->toUnicode(yytext)); BEGIN(Start); } <> { //printf("End of include file\n"); //printf("Include stack depth=%d\n",g_includeStack.count()); if (g_includeStack.isEmpty()) { //printf("Terminating scanner!\n"); yyterminate(); } else { ConfigFileState *fs = g_includeStack.pop(); fclose(fs->file); YY_BUFFER_STATE oldBuf = YY_CURRENT_BUFFER; yy_switch_to_buffer( fs->oldState ); yy_delete_buffer( oldBuf ); g_yyFileName=fs->fileName; delete fs; g_includeDepth--; } } [a-z_A-Z0-9]+ { config_warn("ignoring unknown tag '%s' at line %d, file %s\n",yytext,yylineno,qPrintable(g_yyFileName)); } \n { BEGIN(Start); } \n { if (!g_elemStr.isEmpty()) { //printf("elemStr1='%s'\n",qPrintable(elemStr)); *g_arg = QVariant(g_arg->toStringList() << g_elemStr); } BEGIN(Start); } [ \t]+ { if (!g_elemStr.isEmpty()) { //printf("elemStr2='%s'\n",qPrintable(elemStr)); *g_arg = QVariant(g_arg->toStringList() << g_elemStr); } g_elemStr = QString(); } [^ \"\t\r\n]+ { *g_arg = QVariant(g_codec->toUnicode(yytext)); checkEncoding(); } "\"" { g_lastState=YY_START; BEGIN(GetQuotedString); g_tmpString=""; } "\""|"\n" { // we add a bogus space to signal that the string was quoted. This space will be stripped later on. g_tmpString+=" "; //printf("Quoted String = '%s'\n",qPrintable(tmpString)); if (g_lastState==GetString) { *g_arg = g_codec->toUnicode(g_tmpString); checkEncoding(); } else { g_elemStr+=g_codec->toUnicode(g_tmpString); } if (*yytext=='\n') { config_warn("Missing end quote (\") on line %d, file %s\n",yylineno, qPrintable(g_yyFileName)); } BEGIN(g_lastState); } "\\\"" { g_tmpString+='"'; } . { g_tmpString+=*yytext; } [^ \#\"\t\r\n]+ { g_elemStr+=g_codec->toUnicode(yytext); } \n { BEGIN(Start); } \\[ \r\t]*\n { BEGIN(Start); } <*>\\[ \r\t]*\n { } <*>\n <*>. %% /*@ ---------------------------------------------------------------------------- */ static void substEnvVarsInString(QString &s) { static QRegExp re(QString::fromLatin1("\\$\\([a-z_A-Z0-9]+\\)")); if (s.isEmpty()) return; int p=0; int i,l; //printf("substEnvVarInString(%s) start\n",qPrintable(s)); while ((i=re.indexIn(s,p))!=-1) { l = re.matchedLength(); //printf("Found environment var s.mid(%d,%d)='%s'\n",i+2,l-3,qPrintable(s.mid(i+2,l-3))); QString env=g_codec->toUnicode(getenv(s.mid(i+2,l-3).toLatin1())); substEnvVarsInString(env); // recursively expand variables if needed. s = s.left(i)+env+s.right(s.length()-i-l); p=i+env.length(); // next time start at the end of the expanded string } s=s.trimmed(); // to strip the bogus space that was added when an argument // has quotes //printf("substEnvVarInString(%s) end\n",qPrintable(s)); } static void substEnvVarsInStrList(QStringList &sl) { QStringList out; foreach (QString result, sl) { // an argument with quotes will have an extra space at the end, so wasQuoted will be TRUE. bool wasQuoted = (result.indexOf(QChar::fromLatin1(' '))!=-1) || (result.indexOf(QChar::fromLatin1('\t'))!=-1); // here we strip the quote again substEnvVarsInString(result); //printf("Result %s was quoted=%d\n",qPrintable(result),wasQuoted); if (!wasQuoted) /* as a result of the expansion, a single string may have expanded into a list, which we'll add to sl. If the original string already contained multiple elements no further splitting is done to allow quoted items with spaces! */ { int l=result.length(); int i,p=0; // skip spaces // search for a "word" for (i=0;i &options ) { QHashIterator i(options); g_file = fopen(fileName.toLocal8Bit(),"r"); if (g_file==NULL) return false; // reset all values i.toFront(); while (i.hasNext()) { i.next(); if (i.value()) { i.value()->reset(); } } // parse config file g_options = &options; g_yyFileName = fileName; g_includeStack.clear(); g_includeDepth = 0; config_doxywYYrestart( config_doxywYYin ); BEGIN( Start ); config_doxywYYlex(); // update the values in the UI i.toFront(); while (i.hasNext()) { i.next(); if (i.value()) { //printf("Updating: %s\n",qPrintable(i.key())); i.value()->update(); } else { printf("Invalid option: %s\n",qPrintable(i.key())); } } fclose(g_file); return true; } void writeStringValue(QTextStream &t,QTextCodec *codec,const QString &s) { QChar c; bool needsEscaping=false; bool needsHashEscaping=false; // convert the string back to it original encoding //QByteArray se = codec->fromUnicode(s); t.setCodec(codec); const QChar *p=s.data(); if (!s.isEmpty() && !p->isNull()) { if (*p != QChar::fromLatin1('"')) { while (!(c=*p++).isNull() && !needsEscaping) { needsEscaping = (c==QChar::fromLatin1(' ') || c==QChar::fromLatin1('\n') || c==QChar::fromLatin1('\t') || c==QChar::fromLatin1('"')); } p=s.data(); while (!(c=*p++).isNull() && !needsHashEscaping) { needsHashEscaping = (c==QChar::fromLatin1('#')); } } if (needsHashEscaping || needsEscaping) { t << "\""; } if (needsEscaping) { p=s.data(); while (!p->isNull()) { if (*p ==QChar::fromLatin1(' ') && *(p+1)==QChar::fromLatin1('\0')) break; // skip inserted space at the end if (*p ==QChar::fromLatin1('"')) t << "\\"; // escape quotes t << *p++; } } else { t << s; } if (needsHashEscaping || needsEscaping) { t << "\""; } } }