diff options
Diffstat (limited to 'addon/doxywizard/config_doxyw.l')
-rw-r--r-- | addon/doxywizard/config_doxyw.l | 458 |
1 files changed, 318 insertions, 140 deletions
diff --git a/addon/doxywizard/config_doxyw.l b/addon/doxywizard/config_doxyw.l index 59fa427..b93a79d 100644 --- a/addon/doxywizard/config_doxyw.l +++ b/addon/doxywizard/config_doxyw.l @@ -3,8 +3,8 @@ * 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 + * 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. * @@ -12,6 +12,10 @@ %option never-interactive %option prefix="config_doxywYY" +%top{ +#include <stdint.h> +} + %{ /* @@ -19,6 +23,8 @@ */ #include "config.h" #include "input.h" +#include "inputstring.h" +#include "config_msg.h" #include <QString> #include <QVariant> @@ -29,6 +35,7 @@ #include <QStringList> #include <QRegExp> #include <QTextStream> +#include <QMessageBox> #define YY_NO_UNISTD_H 1 @@ -47,22 +54,22 @@ struct ConfigFileState YY_BUFFER_STATE oldState; YY_BUFFER_STATE newState; QString fileName; -}; +}; static const QHash<QString,Input*> *g_options; static FILE *g_file; static QString g_yyFileName; static QString g_includeName; static QVariant g_includePathList; -static QStack<ConfigFileState*> g_includeStack; +static QStack<ConfigFileState*> g_includeStack; static int g_includeDepth; static QVariant *g_arg; static Input *g_curOption=0; -static QString g_elemStr; +static QByteArray g_str; static QTextCodec *g_codec = QTextCodec::codecForName("UTF-8"); static QString g_codecName = QString::fromLatin1("UTF-8"); -static int g_lastState; -static QByteArray g_tmpString; +static QString g_cmd; +static bool g_isEnum; static const char *stateToString(int state); @@ -71,41 +78,19 @@ static const char *stateToString(int state); #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size); -static int yyread(char *buf,int maxSize) +static yy_size_t yyread(char *buf,yy_size_t maxSize) { // no file included - if (g_includeStack.isEmpty()) + if (g_includeStack.isEmpty()) { return fread(buf,1,maxSize,g_file); - } - else + } + 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); @@ -123,6 +108,191 @@ static void checkEncoding() } } +static QByteArray stripComment(const QByteArray &s) +{ + // check if there is a comment at the end of the string + bool insideQuote=false; + int l = s.length(); + for (int i=0;i<l;i++) + { + char c = s.at(i); + if (c=='\\') // skip over escaped characters + { + i++; + } + else if (c=='"') // toggle inside/outside quotation + { + insideQuote=!insideQuote; + } + else if (!insideQuote && c=='#') // found start of a comment + { + return s.left(i).trimmed(); + } + } + return s; +} + + +static void processString() +{ + // strip leading and trailing whitespace + QByteArray s = stripComment(g_str.trimmed()); + int l = s.length(); + + // remove surrounding quotes if present (and not escaped) + if (l>=2 && s.at(0)=='"' && s.at(l-1)=='"' && // remove quotes + (s.at(l-2)!='\\' || (s.at(l-2)=='\\' && s.at(l-3)=='\\'))) + { + s=s.mid(1,s.length()-2); + l=s.length(); + } + + // check for invalid and/or escaped quotes + bool warned=false; + QByteArray result; + for (int i=0;i<l;i++) + { + char c = s.at(i); + if (c=='\\') // escaped character + { + if (i<l-1 && s.at(i+1)=='"') // unescape the quote character + { + result+='"'; + } + else // keep other escaped characters in escaped form + { + result+=c; + if (i<l-1) + { + result+=s.at(i+1); + } + } + i++; // skip over the escaped character + } + else if (c=='"') // unescaped quote + { + if (!warned) + { + std::string str = g_str.trimmed().data(); + config_warn("Invalid value for '%s' tag at line %d, file %s: Value '%s' is not properly quoted\n", + qPrintable(g_cmd),yylineno-1,qPrintable(g_yyFileName),str.c_str()); + } + warned=true; + } + else // normal character + { + result+=c; + } + } + + // recode the string + if (g_isEnum) + { + InputString *cur = dynamic_cast<InputString *>(g_curOption); + *g_arg = cur->checkEnumVal(g_codec->toUnicode(result)); + } + else + { + *g_arg = QVariant(g_codec->toUnicode(result)); + } + + // update encoding + checkEncoding(); + + //printf("Processed string '%s'\n",g_string->data()); +} + +static void processList() +{ + bool allowCommaAsSeparator = g_cmd!=QString::fromLatin1("PREDEFINED"); + + const QByteArray s = stripComment(g_str.trimmed()); + int l = s.length(); + + QByteArray elemStr; + + // helper to push elemStr to the list and clear it + auto addElem = [&elemStr]() + { + if (!elemStr.isEmpty()) + { + //printf("Processed list element '%s'\n",e.data()); + *g_arg = QVariant(g_arg->toStringList() << g_codec->toUnicode(elemStr)); + elemStr=""; + } + }; + + bool needsSeparator=false; + int insideQuote=false; + bool warned=false; + for (int i=0;i<l;i++) + { + char c = s.at(i); + if (!needsSeparator && c=='\\') // escaped character + { + if (i<l-1 && s.at(i+1)=='"') // unescape the quote character + { + elemStr+='"'; + } + else // keep other escaped characters in escaped form + { + elemStr+=c; + if (i<l-1) + { + elemStr+=s.at(i+1); + } + } + i++; // skip over the escaped character + } + else if (!needsSeparator && c=='"') // quote character + { + if (!insideQuote) + { + insideQuote=true; + } + else // this quote ends an element + { + insideQuote=false; + needsSeparator=true; + } + } + else if (!insideQuote && ((c==',' && allowCommaAsSeparator) || isspace(c))) // separator + { + needsSeparator=false; + addElem(); + } + else // normal content character + { + if (needsSeparator) + { + if (!warned) + { + std::string str = g_str.trimmed().data(); + config_warn("Invalid value for '%s' tag at line %d, file %s: Values in list '%s' are not properly space %sseparated\n", + qPrintable(g_cmd),yylineno-1,qPrintable(g_yyFileName),str.c_str(),allowCommaAsSeparator?"or comma ":""); + warned=true; + } + needsSeparator=false; + i--; // try the character again as part of a new element + addElem(); + } + else + { + elemStr+=c; + } + } + } + // add last part + addElem(); + if (insideQuote) + { + std::string str = g_str.trimmed().data(); + config_warn("Invalid value for '%s' tag at line %d, file %s: Values in list '%s' are not properly quoted\n", + qPrintable(g_cmd),yylineno-1,qPrintable(g_yyFileName),str.c_str()); + } +} + + static FILE *tryPath(const QString &path,const QString &fileName) { QString absName=!path.isEmpty() ? path+QString::fromLatin1("/")+fileName : fileName; @@ -132,7 +302,7 @@ static FILE *tryPath(const QString &path,const QString &fileName) FILE *f = fopen(absName.toLocal8Bit(),"r"); if (f==NULL) config_err("could not open file %s for reading\n",qPrintable(absName)); - else + else return f; } return NULL; @@ -148,7 +318,7 @@ static FILE *findFile(const QString &fileName) // relative path, try with include paths in the list QStringList sl = g_includePathList.toStringList(); substEnvVarsInStrList(sl); - foreach (QString s, sl) + foreach (QString s, sl) { FILE *f = tryPath(s,fileName); if (f) return f; @@ -159,18 +329,17 @@ static FILE *findFile(const QString &fileName) static void readIncludeFile(const QString &incName) { - if (g_includeDepth==MAX_INCLUDE_DEPTH) + if (g_includeDepth==MAX_INCLUDE_DEPTH) { - config_err("maximum include depth (%d) reached, %s is not included. Aborting...\n", + config_err("maximum include depth (%d) reached, %s is not included.", 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('"') && + if (inc.at(0)==QChar::fromLatin1('"') && inc.at(incLen-1)==QChar::fromLatin1('"')) // strip quotes { inc=inc.mid(1,incLen-2); @@ -185,7 +354,7 @@ static void readIncludeFile(const QString &incName) msg("@INCLUDE = %s: parsing...\n",qPrintable(inc)); #endif - // store the state of the old file + // store the state of the old file ConfigFileState *fs=new ConfigFileState; fs->oldState=YY_CURRENT_BUFFER; fs->fileName=g_yyFileName; @@ -197,11 +366,10 @@ static void readIncludeFile(const QString &incName) fs->newState=YY_CURRENT_BUFFER; g_yyFileName=inc; g_includeDepth++; - } + } else { config_err("@INCLUDE = %s: not found!\n",qPrintable(inc)); - exit(1); } } @@ -213,40 +381,46 @@ static void readIncludeFile(const QString &incName) %option yylineno %x Start -%x SkipComment %x SkipInvalid %x GetString +%x GetEnum %x GetStrList %x GetQuotedString -%x GetEnvVar %x Include %% <*>\0x0d -<Start,GetString,GetStrList,SkipInvalid>"#" { BEGIN(SkipComment); } -<Start>[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); + + /*-------------- Comments ---------------*/ + +<Start>"#".*\n { /* Skip comment */ } + + /*-------------- TAG start ---------------*/ + +<Start>[a-z_A-Z][a-z_A-Z0-9]*[ \t]*"=" { g_cmd = g_codec->toUnicode(yytext); + g_cmd=g_cmd.left(g_cmd.length()-1).trimmed(); + g_curOption = g_options->value(g_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)); + qPrintable(g_cmd),yylineno,qPrintable(g_yyFileName)); BEGIN(SkipInvalid); } else // known tag { - //option->setEncoding(encoding); g_arg = &g_curOption->value(); + g_str = QByteArray(); + g_isEnum = false; switch(g_curOption->kind()) { case Input::StrList: - g_elemStr = QString(); *g_arg = QStringList(); BEGIN(GetStrList); break; case Input::String: - BEGIN(GetString); + g_isEnum = dynamic_cast<InputString *>(g_curOption)->stringMode() == InputString::StringFixed; + BEGIN(GetString); break; case Input::Int: BEGIN(GetString); @@ -257,20 +431,20 @@ static void readIncludeFile(const QString &incName) 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)); + "file using \"doxygen -u\"\n", qPrintable(g_cmd), + yylineno,qPrintable(g_yyFileName)); BEGIN(SkipInvalid); break; } } } -<Start>[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); +<Start>[a-z_A-Z][a-z_A-Z0-9]*[ \t]*"+=" { g_cmd=g_codec->toUnicode(yytext); + g_cmd=g_cmd.left(g_cmd.length()-2).trimmed(); + g_curOption = g_options->value(g_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)); + yytext,yylineno,qPrintable(g_yyFileName)); BEGIN(SkipInvalid); } else // known tag @@ -279,31 +453,34 @@ static void readIncludeFile(const QString &incName) { case Input::StrList: g_arg = &g_curOption->value(); - g_elemStr=QString(); + g_str=QByteArray(); 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)); + config_warn("operator += not supported for '%s'. Ignoring line %d, file %s\n", + qPrintable(g_cmd),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)); + "file using \"doxygen -u\"\n", + qPrintable(g_cmd),yylineno,qPrintable(g_yyFileName)); BEGIN(SkipInvalid); break; } } } -<Start>"@INCLUDE_PATH"[ \t]*"=" { BEGIN(GetStrList); g_arg=&g_includePathList; *g_arg = QStringList(); g_elemStr=QString(); } + + /*-------------- INCLUDE* ---------------*/ + +<Start>"@INCLUDE_PATH"[ \t]*"=" { BEGIN(GetStrList); g_arg=&g_includePathList; *g_arg = QStringList(); g_str=QByteArray(); } /* include a config file */ <Start>"@INCLUDE"[ \t]*"=" { BEGIN(Include);} -<Include>([^ \"\t\r\n]+)|("\""[^\n\"]+"\"") { - readIncludeFile(g_codec->toUnicode(yytext)); +<Include>([^ \"\t\r\n]+)|("\""[^\n\"]+"\"") { + readIncludeFile(g_codec->toUnicode(yytext)); BEGIN(Start); } <<EOF>> { @@ -322,69 +499,66 @@ static void readIncludeFile(const QString &incName) yy_switch_to_buffer( fs->oldState ); yy_delete_buffer( oldBuf ); g_yyFileName=fs->fileName; - delete fs; + delete fs; g_includeDepth--; } } <Start>[a-z_A-Z0-9]+ { config_warn("ignoring unknown tag '%s' at line %d, file %s\n",yytext,yylineno,qPrintable(g_yyFileName)); } -<GetString,SkipInvalid>\n { BEGIN(Start); } -<GetStrList>\n { - if (!g_elemStr.isEmpty()) - { - //printf("elemStr1='%s'\n",qPrintable(elemStr)); - *g_arg = QVariant(g_arg->toStringList() << g_elemStr); - } - BEGIN(Start); - } -<GetStrList>[ \t]+ { - if (!g_elemStr.isEmpty()) - { - //printf("elemStr2='%s'\n",qPrintable(elemStr)); - *g_arg = QVariant(g_arg->toStringList() << g_elemStr); - } - g_elemStr = QString(); - } -<GetString>[^ \"\t\r\n]+ { - *g_arg = QVariant(g_codec->toUnicode(yytext)); - checkEncoding(); + + /*-------------- GetString ---------------*/ + +<GetString>\n { // end of string + processString(); + BEGIN(Start); } -<GetString,GetStrList,SkipInvalid>"\"" { g_lastState=YY_START; - BEGIN(GetQuotedString); - g_tmpString=""; - } -<GetQuotedString>"\""|"\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); - } -<GetQuotedString>"\\\"" { - g_tmpString+='"'; - } -<GetQuotedString>. { g_tmpString+=*yytext; } -<GetStrList>[^ \#\"\t\r\n]+ { - g_elemStr+=g_codec->toUnicode(yytext); - } -<SkipComment>\n { BEGIN(Start); } -<SkipComment>\\[ \r\t]*\n { BEGIN(Start); } +<GetString>\\[ \r\t]*\n { // line continuation + g_str+=' '; + } +<GetString>"\\" { // escape character + g_str+=yytext; + } +<GetString>[^\n\\]+ { // string part without escape characters + g_str+=yytext; + } + + /*-------------- GetStrList ---------------*/ + +<GetStrList>\n { // end of list + processList(); + BEGIN(Start); + } +<GetStrList>\\[ \r\t]*\n { // line continuation + g_str+=' '; + } +<GetStrList>"\\" { // escape character + g_str+=yytext; + } +<GetStrList>[^\n\\]+ { // string part without escape characters + g_str+=yytext; + } + + /*-------------- SkipInvalid ---------------*/ + +<SkipInvalid>\n { // end of skipped part + BEGIN(Start); + } +<SkipInvalid>\\[ \r\t]*\n { // line continuation + g_str+=' '; + } +<SkipInvalid>"\\" { // escape character + g_str+=yytext; + } +<SkipInvalid>[^\n\\]+ { // string part without escape characters + g_str+=yytext; + } + + /*-------------- fall through -------------*/ + <*>\\[ \r\t]*\n { } +<*>[ \r\t] { } <*>\n -<*>. +<*>. { config_warn("ignoring unknown character '%c' at line %d, file %s\n",yytext[0],yylineno,qPrintable(g_yyFileName)); } %% @@ -419,7 +593,7 @@ static void substEnvVarsInStrList(QStringList &sl) 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) || + bool wasQuoted = (result.indexOf(QChar::fromLatin1(' '))!=-1) || (result.indexOf(QChar::fromLatin1('\t'))!=-1); // here we strip the quote again substEnvVarsInString(result); @@ -429,7 +603,7 @@ static void substEnvVarsInStrList(QStringList &sl) 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 + contained multiple elements no further splitting is done to allow quoted items with spaces! */ { int l=result.length(); @@ -440,11 +614,11 @@ static void substEnvVarsInStrList(QStringList &sl) { QChar c=0; // skip until start of new word - while (i<l && ((c=result.at(i))==QChar::fromLatin1(' ') || c==QChar::fromLatin1('\t'))) i++; + while (i<l && ((c=result.at(i))==QChar::fromLatin1(' ') || c==QChar::fromLatin1('\t'))) i++; p=i; // p marks the start index of the word // skip until end of a word - while (i<l && ((c=result.at(i))!=QChar::fromLatin1(' ') && - c!=QChar::fromLatin1('\t') && + while (i<l && ((c=result.at(i))!=QChar::fromLatin1(' ') && + c!=QChar::fromLatin1('\t') && c!=QChar::fromLatin1('"'))) i++; if (i<l) // not at the end of the string { @@ -458,7 +632,7 @@ static void substEnvVarsInStrList(QStringList &sl) { out += result.mid(p,i-p); p=i+1; - break; + break; } else if (c==QChar::fromLatin1('\\')) // skip escaped stuff { @@ -493,13 +667,15 @@ bool parseConfig( const QHash<QString,Input *> &options ) { + yylineno = 1; + config_open(); QHashIterator<QString, Input*> i(options); g_file = fopen(fileName.toLocal8Bit(),"r"); if (g_file==NULL) return false; // reset all values i.toFront(); - while (i.hasNext()) + while (i.hasNext()) { i.next(); if (i.value()) @@ -519,7 +695,7 @@ bool parseConfig( // update the values in the UI i.toFront(); - while (i.hasNext()) + while (i.hasNext()) { i.next(); if (i.value()) @@ -531,8 +707,9 @@ bool parseConfig( { printf("Invalid option: %s\n",qPrintable(i.key())); } - } + } fclose(g_file); + config_finish(); return true; } @@ -549,29 +726,30 @@ void writeStringValue(QTextStream &t,QTextCodec *codec,const QString &s) { if (*p != QChar::fromLatin1('"')) { - while (!(c=*p++).isNull() && !needsEscaping) + while (!(c=*p++).isNull() && !needsEscaping) { - needsEscaping = (c==QChar::fromLatin1(' ') || - c==QChar::fromLatin1('\n') || - c==QChar::fromLatin1('\t') || + needsEscaping = (c==QChar::fromLatin1(' ') || + c==QChar::fromLatin1(',') || + c==QChar::fromLatin1('\n') || + c==QChar::fromLatin1('\t') || c==QChar::fromLatin1('"')); } p=s.data(); - while (!(c=*p++).isNull() && !needsHashEscaping) + 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(' ') && + 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++; @@ -582,7 +760,7 @@ void writeStringValue(QTextStream &t,QTextCodec *codec,const QString &s) t << s; } if (needsHashEscaping || needsEscaping) - { + { t << "\""; } } |