tools/qdoc3/doc.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the tools applications of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "config.h"
       
    43 #include "doc.h"
       
    44 #include "codemarker.h"
       
    45 #include "editdistance.h"
       
    46 #include "openedlist.h"
       
    47 #include "quoter.h"
       
    48 #include "text.h"
       
    49 #include "tokenizer.h"
       
    50 #include <qdatetime.h>
       
    51 #include <qdebug.h>
       
    52 #include <qfile.h>
       
    53 #include <qfileinfo.h>
       
    54 #include <qhash.h>
       
    55 #include <qtextstream.h>
       
    56 #include <qregexp.h>
       
    57 #include <ctype.h>
       
    58 #include <limits.h>
       
    59 
       
    60 QT_BEGIN_NAMESPACE
       
    61 
       
    62 Q_GLOBAL_STATIC(QSet<QString>, null_Set_QString)
       
    63 Q_GLOBAL_STATIC(QStringList, null_QStringList)
       
    64 Q_GLOBAL_STATIC(QList<Text>, null_QList_Text)
       
    65 Q_GLOBAL_STATIC(QStringMap, null_QStringMap)
       
    66 
       
    67 struct Macro
       
    68 {
       
    69     QString defaultDef;
       
    70     Location defaultDefLocation;
       
    71     QStringMap otherDefs;
       
    72     int numParams;
       
    73 };
       
    74 
       
    75 enum {
       
    76     CMD_A, CMD_ABSTRACT, CMD_ANNOTATEDLIST, CMD_BADCODE,
       
    77     CMD_BASENAME, CMD_BOLD, CMD_BRIEF, CMD_C, CMD_CAPTION,
       
    78     CMD_CHAPTER, CMD_CODE, CMD_CODELINE, CMD_DOTS, CMD_ELSE,
       
    79     CMD_ENDABSTRACT, CMD_ENDCHAPTER, CMD_ENDCODE,
       
    80     CMD_ENDFOOTNOTE, CMD_ENDIF, CMD_ENDLEGALESE, CMD_ENDLINK,
       
    81     CMD_ENDLIST, CMD_ENDOMIT, CMD_ENDPART, CMD_ENDQUOTATION,
       
    82     CMD_ENDRAW, CMD_ENDSECTION1, CMD_ENDSECTION2,
       
    83     CMD_ENDSECTION3, CMD_ENDSECTION4, CMD_ENDSIDEBAR,
       
    84     CMD_ENDTABLE, CMD_EXPIRE, CMD_FOOTNOTE, CMD_GENERATELIST,
       
    85     CMD_GRANULARITY, CMD_HEADER, CMD_I, CMD_IF, CMD_IMAGE,
       
    86     CMD_INCLUDE, CMD_INLINEIMAGE, CMD_INDEX, CMD_KEYWORD,
       
    87     CMD_L, CMD_LEGALESE, CMD_LINK, CMD_LIST, CMD_META,
       
    88     CMD_NEWCODE, CMD_O, CMD_OLDCODE, CMD_OMIT, CMD_OMITVALUE,
       
    89     CMD_OVERLOAD, CMD_PART, CMD_PRINTLINE, CMD_PRINTTO,
       
    90     CMD_PRINTUNTIL, CMD_QUOTATION, CMD_QUOTEFILE,
       
    91     CMD_QUOTEFROMFILE, CMD_QUOTEFUNCTION, CMD_RAW, CMD_ROW,
       
    92     CMD_SA, CMD_SECTION1, CMD_SECTION2, CMD_SECTION3,
       
    93     CMD_SECTION4, CMD_SIDEBAR, CMD_SINCELIST, CMD_SKIPLINE,
       
    94     CMD_SKIPTO, CMD_SKIPUNTIL, CMD_SNIPPET, CMD_SUB, CMD_SUP,
       
    95     CMD_TABLE, CMD_TABLEOFCONTENTS, CMD_TARGET, CMD_TT,
       
    96     CMD_UNDERLINE, CMD_UNICODE, CMD_VALUE, CMD_WARNING,
       
    97 #ifdef QDOC_QML    
       
    98     CMD_QML, CMD_ENDQML, CMD_CPP, CMD_ENDCPP, CMD_QMLTEXT,
       
    99     CMD_ENDQMLTEXT, CMD_CPPTEXT, CMD_ENDCPPTEXT,
       
   100 #endif    
       
   101     NOT_A_CMD
       
   102 };
       
   103 
       
   104 static struct {
       
   105     const char *english;
       
   106     int no;
       
   107     QString *alias;
       
   108 } cmds[] = {
       
   109     { "a", CMD_A, 0 },
       
   110     { "abstract", CMD_ABSTRACT, 0 },
       
   111     { "annotatedlist", CMD_ANNOTATEDLIST, 0 },
       
   112     { "badcode", CMD_BADCODE, 0 },
       
   113     { "basename", CMD_BASENAME, 0 }, // ### don't document for now
       
   114     { "bold", CMD_BOLD, 0 },
       
   115     { "brief", CMD_BRIEF, 0 },
       
   116     { "c", CMD_C, 0 },
       
   117     { "caption", CMD_CAPTION, 0 },
       
   118     { "chapter", CMD_CHAPTER, 0 },
       
   119     { "code", CMD_CODE, 0 },
       
   120     { "codeline", CMD_CODELINE, 0},
       
   121     { "dots", CMD_DOTS, 0 },
       
   122     { "else", CMD_ELSE, 0 },
       
   123     { "endabstract", CMD_ENDABSTRACT, 0 },
       
   124     { "endchapter", CMD_ENDCHAPTER, 0 },
       
   125     { "endcode", CMD_ENDCODE, 0 },
       
   126     { "endfootnote", CMD_ENDFOOTNOTE, 0 },
       
   127     { "endif", CMD_ENDIF, 0 },
       
   128     { "endlegalese", CMD_ENDLEGALESE, 0 },
       
   129     { "endlink", CMD_ENDLINK, 0 },
       
   130     { "endlist", CMD_ENDLIST, 0 },
       
   131     { "endomit", CMD_ENDOMIT, 0 },
       
   132     { "endpart", CMD_ENDPART, 0 },
       
   133     { "endquotation", CMD_ENDQUOTATION, 0 },
       
   134     { "endraw", CMD_ENDRAW, 0 },
       
   135     { "endsection1", CMD_ENDSECTION1, 0 },  // ### don't document for now
       
   136     { "endsection2", CMD_ENDSECTION2, 0 },  // ### don't document for now
       
   137     { "endsection3", CMD_ENDSECTION3, 0 },  // ### don't document for now
       
   138     { "endsection4", CMD_ENDSECTION4, 0 },  // ### don't document for now
       
   139     { "endsidebar", CMD_ENDSIDEBAR, 0 },
       
   140     { "endtable", CMD_ENDTABLE, 0 },
       
   141     { "expire", CMD_EXPIRE, 0 },
       
   142     { "footnote", CMD_FOOTNOTE, 0 },
       
   143     { "generatelist", CMD_GENERATELIST, 0 },
       
   144     { "granularity", CMD_GRANULARITY, 0 }, // ### don't document for now
       
   145     { "header", CMD_HEADER, 0 },
       
   146     { "i", CMD_I, 0 },
       
   147     { "if", CMD_IF, 0 },
       
   148     { "image", CMD_IMAGE, 0 },
       
   149     { "include", CMD_INCLUDE, 0 },
       
   150     { "inlineimage", CMD_INLINEIMAGE, 0 },
       
   151     { "index", CMD_INDEX, 0 }, // ### don't document for now
       
   152     { "keyword", CMD_KEYWORD, 0 },
       
   153     { "l", CMD_L, 0 },
       
   154     { "legalese", CMD_LEGALESE, 0 },
       
   155     { "link", CMD_LINK, 0 },
       
   156     { "list", CMD_LIST, 0 },
       
   157     { "meta", CMD_META, 0 },
       
   158     { "newcode", CMD_NEWCODE, 0 },
       
   159     { "o", CMD_O, 0 },
       
   160     { "oldcode", CMD_OLDCODE, 0 },
       
   161     { "omit", CMD_OMIT, 0 },
       
   162     { "omitvalue", CMD_OMITVALUE, 0 },
       
   163     { "overload", CMD_OVERLOAD, 0 },
       
   164     { "part", CMD_PART, 0 },
       
   165     { "printline", CMD_PRINTLINE, 0 },
       
   166     { "printto", CMD_PRINTTO, 0 },
       
   167     { "printuntil", CMD_PRINTUNTIL, 0 },
       
   168     { "quotation", CMD_QUOTATION, 0 },
       
   169     { "quotefile", CMD_QUOTEFILE, 0 },
       
   170     { "quotefromfile", CMD_QUOTEFROMFILE, 0 },
       
   171     { "quotefunction", CMD_QUOTEFUNCTION, 0 }, // ### don't document for now
       
   172     { "raw", CMD_RAW, 0 },
       
   173     { "row", CMD_ROW, 0 },
       
   174     { "sa", CMD_SA, 0 },
       
   175     { "section1", CMD_SECTION1, 0 },
       
   176     { "section2", CMD_SECTION2, 0 },
       
   177     { "section3", CMD_SECTION3, 0 },
       
   178     { "section4", CMD_SECTION4, 0 },
       
   179     { "sidebar", CMD_SIDEBAR, 0 }, // ### don't document for now
       
   180     { "sincelist", CMD_SINCELIST, 0 },
       
   181     { "skipline", CMD_SKIPLINE, 0 },
       
   182     { "skipto", CMD_SKIPTO, 0 },
       
   183     { "skipuntil", CMD_SKIPUNTIL, 0 },
       
   184     { "snippet", CMD_SNIPPET, 0 },
       
   185     { "sub", CMD_SUB, 0 },
       
   186     { "sup", CMD_SUP, 0 },
       
   187     { "table", CMD_TABLE, 0 },
       
   188     { "tableofcontents", CMD_TABLEOFCONTENTS, 0 },
       
   189     { "target", CMD_TARGET, 0 },
       
   190     { "tt", CMD_TT, 0 },
       
   191     { "underline", CMD_UNDERLINE, 0 },
       
   192     { "unicode", CMD_UNICODE, 0 },
       
   193     { "value", CMD_VALUE, 0 },
       
   194     { "warning", CMD_WARNING, 0 },
       
   195 #ifdef QDOC_QML
       
   196     { "qml", CMD_QML, 0 },
       
   197     { "endqml", CMD_ENDQML, 0 },
       
   198     { "cpp", CMD_CPP, 0 },
       
   199     { "endcpp", CMD_ENDCPP, 0 },
       
   200     { "qmltext", CMD_QMLTEXT, 0 },
       
   201     { "endqmltext", CMD_ENDQMLTEXT, 0 },
       
   202     { "cpptext", CMD_CPPTEXT, 0 },
       
   203     { "endcpptext", CMD_ENDCPPTEXT, 0 },
       
   204 #endif
       
   205     { 0, 0, 0 }
       
   206 };
       
   207 
       
   208 typedef QHash<QString, int> QHash_QString_int;
       
   209 typedef QHash<QString, Macro> QHash_QString_Macro;
       
   210 
       
   211 Q_GLOBAL_STATIC(QStringMap, aliasMap)
       
   212 Q_GLOBAL_STATIC(QHash_QString_int, cmdHash)
       
   213 Q_GLOBAL_STATIC(QHash_QString_Macro, macroHash)
       
   214 
       
   215 class DocPrivateExtra
       
   216 {
       
   217   public:
       
   218     QString             baseName;
       
   219     Doc::SectioningUnit granularity;
       
   220     Doc::SectioningUnit sectioningUnit; // ###
       
   221     QList<Atom*>        tableOfContents;
       
   222     QList<int>          tableOfContentsLevels;
       
   223     QList<Atom*>        keywords;
       
   224     QList<Atom*>        targets;
       
   225     QStringMap          metaMap;
       
   226 
       
   227     DocPrivateExtra()
       
   228 	: granularity(Doc::Part) { }
       
   229 };
       
   230 
       
   231 struct Shared // ### get rid of
       
   232 {
       
   233     Shared()
       
   234 	: count(1) { }
       
   235     void ref() { ++count; }
       
   236     bool deref() { return (--count == 0); }
       
   237 
       
   238     int count;
       
   239 };
       
   240 
       
   241 static QString cleanLink(const QString &link)
       
   242 {
       
   243     int colonPos = link.indexOf(':');
       
   244     if ((colonPos == -1) ||
       
   245         (!link.startsWith("file:") && !link.startsWith("mailto:")))
       
   246         return link;
       
   247     return link.mid(colonPos + 1).simplified();
       
   248 }
       
   249 
       
   250 class DocPrivate : public Shared
       
   251 {
       
   252   public:
       
   253     DocPrivate(const Location& start = Location::null,
       
   254                const Location& end = Location::null,
       
   255                const QString& source = "");
       
   256     ~DocPrivate();
       
   257 
       
   258     void addAlso(const Text& also);
       
   259     void constructExtra();
       
   260     bool isEnumDocSimplifiable() const;
       
   261 
       
   262     // ### move some of this in DocPrivateExtra
       
   263     Location start_loc;
       
   264     Location end_loc;
       
   265     QString src;
       
   266     Text text;
       
   267     QSet<QString> params;
       
   268     QList<Text> alsoList;
       
   269     QStringList enumItemList;
       
   270     QStringList omitEnumItemList;
       
   271     QSet<QString> metacommandsUsed;
       
   272     QCommandMap metaCommandMap;
       
   273     bool hasLegalese : 1;
       
   274     bool hasSectioningUnits : 1;
       
   275     DocPrivateExtra *extra;
       
   276 };
       
   277 
       
   278 DocPrivate::DocPrivate(const Location& start,
       
   279                        const Location& end,
       
   280                        const QString& source)
       
   281     : start_loc(start),
       
   282       end_loc(end),
       
   283       src(source),
       
   284       hasLegalese(false),
       
   285       hasSectioningUnits(false),
       
   286       extra(0)
       
   287 {
       
   288     // nothing.
       
   289 }
       
   290 
       
   291 DocPrivate::~DocPrivate()
       
   292 {
       
   293     delete extra;
       
   294 }
       
   295 
       
   296 void DocPrivate::addAlso(const Text& also)
       
   297 {
       
   298     alsoList.append(also);
       
   299 }
       
   300 
       
   301 void DocPrivate::constructExtra()
       
   302 {
       
   303     if (extra == 0)
       
   304 	extra = new DocPrivateExtra;
       
   305 }
       
   306 
       
   307 bool DocPrivate::isEnumDocSimplifiable() const
       
   308 {
       
   309     bool justMetColon = false;
       
   310     int numValueTables = 0;
       
   311 
       
   312     const Atom *atom = text.firstAtom();
       
   313     while (atom) {
       
   314         if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) {
       
   315             justMetColon = atom->string().endsWith(":");
       
   316         }
       
   317         else if ((atom->type() == Atom::ListLeft) &&
       
   318                    (atom->string() == ATOM_LIST_VALUE)) {
       
   319             if (justMetColon || numValueTables > 0)
       
   320                 return false;
       
   321             ++numValueTables;
       
   322         }
       
   323         atom = atom->next();
       
   324     }
       
   325     return true;
       
   326 }
       
   327 
       
   328 class DocParser
       
   329 {
       
   330   public:
       
   331     void parse(const QString &source,
       
   332                DocPrivate *docPrivate,
       
   333                const QSet<QString> &metaCommandSet);
       
   334 
       
   335     static int endCmdFor(int cmd);
       
   336     static QString cmdName(int cmd);
       
   337     static QString endCmdName(int cmd);
       
   338     static QString untabifyEtc(const QString& str);
       
   339     static int indentLevel(const QString& str);
       
   340     static QString unindent(int level, const QString& str);
       
   341     static QString slashed(const QString& str);
       
   342 
       
   343     static int tabSize;
       
   344     static QStringList exampleFiles;
       
   345     static QStringList exampleDirs;
       
   346     static QStringList sourceFiles;
       
   347     static QStringList sourceDirs;
       
   348     static bool quoting;
       
   349 
       
   350   private:
       
   351     Location& location();
       
   352     QString detailsUnknownCommand(const QSet<QString>& metaCommandSet,
       
   353 				   const QString& str);
       
   354     void checkExpiry(const QString& date);
       
   355     void insertBaseName(const QString &baseName);
       
   356     void insertTarget(const QString& target, bool keyword);
       
   357     void include(const QString& fileName);
       
   358     void startFormat(const QString& format, int cmd);
       
   359     bool openCommand(int cmd);
       
   360     bool closeCommand(int endCmd);
       
   361     void startSection(Doc::SectioningUnit unit, int cmd);
       
   362     void endSection(int unit, int endCmd);
       
   363     void parseAlso();
       
   364     void append(Atom::Type type, const QString& string = "");
       
   365     void appendChar(QChar ch);
       
   366     void appendWord(const QString &word);
       
   367     void appendToCode(const QString &code);
       
   368     void startNewPara();
       
   369     void enterPara(Atom::Type leftType = Atom::ParaLeft,
       
   370 		    Atom::Type rightType = Atom::ParaRight,
       
   371 		    const QString& string = "");
       
   372     void leavePara();
       
   373     void leaveValue();
       
   374     void leaveValueList();
       
   375     void leaveTableRow();
       
   376     CodeMarker *quoteFromFile();
       
   377     void expandMacro(const QString& name, const QString& def, int numParams);
       
   378     Doc::SectioningUnit getSectioningUnit();
       
   379     QString getArgument(bool verbatim = false);
       
   380     QString getOptionalArgument();
       
   381     QString getRestOfLine();
       
   382     QString getMetaCommandArgument(const QString &cmdStr);
       
   383     QString getUntilEnd(int cmd);
       
   384     QString getCode(int cmd, CodeMarker *marker);
       
   385     QString getUnmarkedCode(int cmd);
       
   386 
       
   387     bool isBlankLine();
       
   388     bool isLeftBraceAhead();
       
   389     void skipSpacesOnLine();
       
   390     void skipSpacesOrOneEndl();
       
   391     void skipAllSpaces();
       
   392     void skipToNextPreprocessorCommand();
       
   393 
       
   394     QStack<int> openedInputs;
       
   395 
       
   396     QString in;
       
   397     int pos;
       
   398     int len;
       
   399     Location cachedLoc;
       
   400     int cachedPos;
       
   401 
       
   402     DocPrivate *priv;
       
   403     enum ParaState { OutsidePara, InsideSingleLinePara, InsideMultiLinePara };
       
   404     ParaState paraState;
       
   405     bool inTableHeader;
       
   406     bool inTableRow;
       
   407     bool inTableItem;
       
   408     bool indexStartedPara; // ### rename
       
   409     Atom::Type pendingParaLeftType;
       
   410     Atom::Type pendingParaRightType;
       
   411     QString pendingParaString;
       
   412 
       
   413     int braceDepth;
       
   414     int minIndent;
       
   415     Doc::SectioningUnit currentSectioningUnit;
       
   416     QMap<QString, Location> targetMap;
       
   417     QMap<int, QString> pendingFormats;
       
   418     QStack<int> openedCommands;
       
   419     QStack<OpenedList> openedLists;
       
   420     Quoter quoter;
       
   421 };
       
   422 
       
   423 int DocParser::tabSize;
       
   424 QStringList DocParser::exampleFiles;
       
   425 QStringList DocParser::exampleDirs;
       
   426 QStringList DocParser::sourceFiles;
       
   427 QStringList DocParser::sourceDirs;
       
   428 bool DocParser::quoting;
       
   429 
       
   430 /*!
       
   431   Parse the \a source string to build a Text data structure
       
   432   in \a docPrivate. The Text data structure is a linked list
       
   433   of Atoms.
       
   434 
       
   435   \a metaCommandSet is the set of metacommands that may be
       
   436   found in \a source. These metacommands are not markup text
       
   437   commands. They are topic commands and related metacommands.
       
   438  */
       
   439 void DocParser::parse(const QString& source,
       
   440                       DocPrivate *docPrivate,
       
   441                       const QSet<QString>& metaCommandSet)
       
   442 {
       
   443     in = source;
       
   444     pos = 0;
       
   445     len = in.length();
       
   446     cachedLoc = docPrivate->start_loc;
       
   447     cachedPos = 0;
       
   448     priv = docPrivate;
       
   449     priv->text << Atom::Nop;
       
   450 
       
   451     paraState = OutsidePara;
       
   452     inTableHeader = false;
       
   453     inTableRow = false;
       
   454     inTableItem = false;
       
   455     indexStartedPara = false;
       
   456     pendingParaLeftType = Atom::Nop;
       
   457     pendingParaRightType = Atom::Nop;
       
   458 
       
   459     braceDepth = 0;
       
   460     minIndent = INT_MAX;
       
   461     currentSectioningUnit = Doc::Book;
       
   462     openedCommands.push(CMD_OMIT);
       
   463     quoter.reset();
       
   464 
       
   465     CodeMarker *marker = 0;
       
   466     Atom *currentLinkAtom = 0;
       
   467     QString x;
       
   468     QStack<bool> preprocessorSkipping;
       
   469     int numPreprocessorSkipping = 0;
       
   470 
       
   471     while (pos < len) {
       
   472         QChar ch = in.at(pos);
       
   473 
       
   474         switch (ch.unicode()) {
       
   475         case '\\':
       
   476             {
       
   477                 QString cmdStr;
       
   478                 pos++;
       
   479                 while (pos < len) {
       
   480                     ch = in.at(pos);
       
   481                     if (ch.isLetterOrNumber()) {
       
   482                         cmdStr += ch;
       
   483                         pos++;
       
   484                     }
       
   485                     else {
       
   486                         break;
       
   487                     }
       
   488                 }
       
   489                 if (cmdStr.isEmpty()) {
       
   490                     if (pos < len) {
       
   491                         enterPara();
       
   492                         if (in.at(pos).isSpace()) {
       
   493                             skipAllSpaces();
       
   494                             appendChar(QLatin1Char(' '));
       
   495                         }
       
   496                         else {
       
   497                             appendChar(in.at(pos++));
       
   498                         }
       
   499                     }
       
   500                 }
       
   501                 else {
       
   502                     int cmd = cmdHash()->value(cmdStr,NOT_A_CMD);
       
   503                     switch (cmd) {
       
   504                     case CMD_A:
       
   505                         enterPara();
       
   506                         x = getArgument();
       
   507                         append(Atom::FormattingLeft,ATOM_FORMATTING_PARAMETER);
       
   508                         append(Atom::String, x);
       
   509                         append(Atom::FormattingRight,ATOM_FORMATTING_PARAMETER);
       
   510                         priv->params.insert(x);
       
   511                         break;
       
   512                     case CMD_ABSTRACT:
       
   513                         if (openCommand(cmd)) {
       
   514                             leavePara();
       
   515                             append(Atom::AbstractLeft);
       
   516                         }
       
   517                         break;
       
   518                     case CMD_BADCODE:
       
   519                         leavePara();
       
   520                         append(Atom::CodeBad,getCode(CMD_BADCODE, marker));
       
   521                         break;
       
   522                     case CMD_BASENAME:
       
   523                         leavePara();
       
   524                         insertBaseName(getArgument());
       
   525                         break;
       
   526                     case CMD_BOLD:
       
   527                         startFormat(ATOM_FORMATTING_BOLD, cmd);
       
   528                         break;
       
   529                     case CMD_BRIEF:
       
   530                         leavePara();
       
   531                         enterPara(Atom::BriefLeft, Atom::BriefRight);
       
   532                         break;
       
   533                     case CMD_C:
       
   534                         enterPara();
       
   535                         x = untabifyEtc(getArgument(true));
       
   536                         marker = CodeMarker::markerForCode(x);
       
   537                         append(Atom::C, marker->markedUpCode(x, 0, ""));
       
   538                         break;
       
   539                     case CMD_CAPTION:
       
   540                         leavePara();
       
   541                         /* ... */
       
   542                         break;
       
   543                     case CMD_CHAPTER:
       
   544                         startSection(Doc::Chapter, cmd);
       
   545                         break;
       
   546                     case CMD_CODE:
       
   547                         leavePara();
       
   548                         append(Atom::Code, getCode(CMD_CODE, marker));
       
   549                         break;
       
   550 #ifdef QDOC_QML        
       
   551                     case CMD_QML:
       
   552                         leavePara();
       
   553                         append(Atom::Qml, getCode(CMD_QML, marker));
       
   554                         break;
       
   555                     case CMD_QMLTEXT:
       
   556                         append(Atom::QmlText);
       
   557                         break;
       
   558 #endif                        
       
   559                     case CMD_CODELINE:
       
   560                         {
       
   561                             if (!quoting) {
       
   562                                 if (priv->text.lastAtom()->type() == Atom::Code
       
   563                                         && priv->text.lastAtom()->string().endsWith("\n\n"))
       
   564                                     priv->text.lastAtom()->chopString();
       
   565                                 appendToCode("\n");
       
   566                             }
       
   567                             else {
       
   568                                 append(Atom::CodeQuoteCommand, cmdStr);
       
   569                                 append(Atom::CodeQuoteArgument, " ");
       
   570                             }
       
   571                         }
       
   572                         break;
       
   573                     case CMD_DOTS:
       
   574                         {
       
   575                             if (!quoting) {
       
   576                                 if (priv->text.lastAtom()->type() == Atom::Code
       
   577                                         && priv->text.lastAtom()->string().endsWith("\n\n"))
       
   578                                     priv->text.lastAtom()->chopString();
       
   579 
       
   580                                 QString arg = getOptionalArgument();
       
   581                                 int indent = 4;
       
   582                                 if (!arg.isEmpty())
       
   583                                     indent = arg.toInt();
       
   584                                 for (int i = 0; i < indent; ++i)
       
   585                                     appendToCode(" ");
       
   586                                 appendToCode("...\n");
       
   587                             }
       
   588                             else {
       
   589                                 append(Atom::CodeQuoteCommand, cmdStr);
       
   590                                 QString arg = getOptionalArgument();
       
   591                                 if (arg.isEmpty())
       
   592                                     arg = "4";
       
   593                                 append(Atom::CodeQuoteArgument, arg);
       
   594                             }
       
   595                         }
       
   596                         break;
       
   597                     case CMD_ELSE:
       
   598                         if (preprocessorSkipping.size() > 0) {
       
   599                             if (preprocessorSkipping.top()) {
       
   600                                 --numPreprocessorSkipping;
       
   601                             }
       
   602                             else {
       
   603                                 ++numPreprocessorSkipping;
       
   604                             }
       
   605                             preprocessorSkipping.top() = !preprocessorSkipping.top();
       
   606                             (void)getRestOfLine(); // ### should ensure that it's empty
       
   607                             if (numPreprocessorSkipping)
       
   608                                 skipToNextPreprocessorCommand();
       
   609                         }
       
   610                         else {
       
   611                             location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ELSE)));
       
   612                         }
       
   613                         break;
       
   614                     case CMD_ENDABSTRACT:
       
   615                         if (closeCommand(cmd)) {
       
   616                             leavePara();
       
   617                             append(Atom::AbstractRight);
       
   618                         }
       
   619                         break;
       
   620                     case CMD_ENDCHAPTER:
       
   621                         endSection(0, cmd);
       
   622                         break;
       
   623                     case CMD_ENDCODE:
       
   624                         closeCommand(cmd);
       
   625                         break;
       
   626 #ifdef QDOC_QML
       
   627                     case CMD_ENDQML:
       
   628                         closeCommand(cmd);
       
   629                         break;
       
   630                     case CMD_ENDQMLTEXT:
       
   631                         append(Atom::EndQmlText);
       
   632                         break;
       
   633 #endif                        
       
   634                     case CMD_ENDFOOTNOTE:
       
   635                         if (closeCommand(cmd)) {
       
   636                             leavePara();
       
   637                             append(Atom::FootnoteRight);
       
   638                             paraState = InsideMultiLinePara; // ###
       
   639                         }
       
   640                         break;
       
   641                     case CMD_ENDIF:
       
   642                         if (preprocessorSkipping.count() > 0) {
       
   643                             if (preprocessorSkipping.pop())
       
   644                                 --numPreprocessorSkipping;
       
   645                             (void)getRestOfLine(); // ### should ensure that it's empty
       
   646                             if (numPreprocessorSkipping)
       
   647                                 skipToNextPreprocessorCommand();
       
   648                         }
       
   649                         else {
       
   650                             location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDIF)));
       
   651                         }
       
   652                         break;
       
   653                     case CMD_ENDLEGALESE:
       
   654                         if (closeCommand(cmd)) {
       
   655                             leavePara();
       
   656                             append(Atom::LegaleseRight);
       
   657                         }
       
   658                         break;
       
   659                     case CMD_ENDLINK:
       
   660                         if (closeCommand(cmd)) {
       
   661                             if (priv->text.lastAtom()->type() == Atom::String
       
   662                                     && priv->text.lastAtom()->string().endsWith(" "))
       
   663                                 priv->text.lastAtom()->chopString();
       
   664                             append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
   665                         }
       
   666                         break;
       
   667                     case CMD_ENDLIST:
       
   668                         if (closeCommand(cmd)) {
       
   669                             leavePara();
       
   670                             if (openedLists.top().isStarted()) {
       
   671                                 append(Atom::ListItemRight,
       
   672                                         openedLists.top().styleString());
       
   673                                 append(Atom::ListRight,
       
   674                                         openedLists.top().styleString());
       
   675                             }
       
   676                             openedLists.pop();
       
   677                         }
       
   678                         break;
       
   679                     case CMD_ENDOMIT:
       
   680                         closeCommand(cmd);
       
   681                         break;
       
   682                     case CMD_ENDPART:
       
   683                         endSection(-1, cmd);
       
   684                         break;
       
   685                     case CMD_ENDQUOTATION:
       
   686                         if (closeCommand(cmd)) {
       
   687                             leavePara();
       
   688                             append(Atom::QuotationRight);
       
   689                         }
       
   690                         break;
       
   691                     case CMD_ENDRAW:
       
   692                         location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDRAW)));
       
   693                         break;
       
   694                     case CMD_ENDSECTION1:
       
   695                         endSection(1, cmd);
       
   696                         break;
       
   697                     case CMD_ENDSECTION2:
       
   698                         endSection(2, cmd);
       
   699                         break;
       
   700                     case CMD_ENDSECTION3:
       
   701                         endSection(3, cmd);
       
   702                         break;
       
   703                     case CMD_ENDSECTION4:
       
   704                         endSection(4, cmd);
       
   705                         break;
       
   706                     case CMD_ENDSIDEBAR:
       
   707                         if (closeCommand(cmd)) {
       
   708                             leavePara();
       
   709                             append(Atom::SidebarRight);
       
   710                         }
       
   711                         break;
       
   712                     case CMD_ENDTABLE:
       
   713                         if (closeCommand(cmd)) {
       
   714                             leaveTableRow();
       
   715                             append(Atom::TableRight);
       
   716                         }
       
   717                         break;
       
   718                     case CMD_EXPIRE:
       
   719                         checkExpiry(getArgument());
       
   720                         break;
       
   721                     case CMD_FOOTNOTE:
       
   722                         if (openCommand(cmd)) {
       
   723                             enterPara();
       
   724                             append(Atom::FootnoteLeft);
       
   725                             paraState = OutsidePara; // ###
       
   726                         }
       
   727                         break;
       
   728                     case CMD_ANNOTATEDLIST:
       
   729                         append(Atom::AnnotatedList, getArgument());
       
   730                         break;
       
   731                     case CMD_SINCELIST:
       
   732                         append(Atom::SinceList, getArgument());
       
   733                         break;
       
   734                     case CMD_GENERATELIST:
       
   735                         append(Atom::GeneratedList, getArgument());
       
   736                         break;
       
   737                     case CMD_GRANULARITY:
       
   738                         priv->constructExtra();
       
   739                         priv->extra->granularity = getSectioningUnit();
       
   740                         break;
       
   741                     case CMD_HEADER:
       
   742                         if (openedCommands.top() == CMD_TABLE) {
       
   743                             leaveTableRow();
       
   744                             append(Atom::TableHeaderLeft);
       
   745                             inTableHeader = true;
       
   746                         }
       
   747                         else {
       
   748                             if (openedCommands.contains(CMD_TABLE)) {
       
   749                                 location().warning(tr("Cannot use '\\%1' within '\\%2'")
       
   750                                                    .arg(cmdName(CMD_HEADER))
       
   751                                                    .arg(cmdName(openedCommands.top())));
       
   752                             }
       
   753                             else {
       
   754                                 location().warning(tr("Cannot use '\\%1' outside of '\\%2'")
       
   755                                                    .arg(cmdName(CMD_HEADER))
       
   756                                                    .arg(cmdName(CMD_TABLE)));
       
   757                             }
       
   758                         }
       
   759                         break;
       
   760                     case CMD_I:
       
   761                         startFormat(ATOM_FORMATTING_ITALIC, cmd);
       
   762                         break;
       
   763                     case CMD_IF:
       
   764                         preprocessorSkipping.push(!Tokenizer::isTrue(getRestOfLine()));
       
   765                         if (preprocessorSkipping.top())
       
   766                             ++numPreprocessorSkipping;
       
   767                         if (numPreprocessorSkipping)
       
   768                             skipToNextPreprocessorCommand();
       
   769                         break;
       
   770                     case CMD_IMAGE:
       
   771                         leaveValueList();
       
   772                         append(Atom::Image, getArgument());
       
   773                         append(Atom::ImageText, getRestOfLine());
       
   774                         break;
       
   775                     case CMD_INCLUDE:
       
   776                         include(getArgument());
       
   777                         break;
       
   778                     case CMD_INLINEIMAGE:
       
   779                         enterPara();
       
   780                         append(Atom::InlineImage, getArgument());
       
   781                         append(Atom::ImageText, getRestOfLine());
       
   782                         append(Atom::String, " ");
       
   783                         break;
       
   784                     case CMD_INDEX:
       
   785                         if (paraState == OutsidePara) {
       
   786                             enterPara();
       
   787                             indexStartedPara = true;
       
   788                         }
       
   789                         else {
       
   790                             const Atom *last = priv->text.lastAtom();
       
   791                             if (indexStartedPara &&
       
   792                                  (last->type() != Atom::FormattingRight ||
       
   793                                   last->string() != ATOM_FORMATTING_INDEX))
       
   794                                 indexStartedPara = false;
       
   795                         }
       
   796                         startFormat(ATOM_FORMATTING_INDEX, cmd);
       
   797                         break;
       
   798                     case CMD_KEYWORD:
       
   799                         insertTarget(getRestOfLine(),true);
       
   800                         break;
       
   801                     case CMD_L:
       
   802                         enterPara();
       
   803                         if (isLeftBraceAhead()) {
       
   804                             x = getArgument();
       
   805                             append(Atom::Link, x);
       
   806                             if (isLeftBraceAhead()) {
       
   807                                 currentLinkAtom = priv->text.lastAtom();
       
   808                                 startFormat(ATOM_FORMATTING_LINK, cmd);
       
   809                             }
       
   810                             else {
       
   811                                 append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
       
   812                                 append(Atom::String, cleanLink(x));
       
   813                                 append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
   814                             }
       
   815                         }
       
   816                         else {
       
   817                             x = getArgument();
       
   818                             append(Atom::Link, x);
       
   819                             append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
       
   820                             append(Atom::String, cleanLink(x));
       
   821                             append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
   822                         }
       
   823                         break;
       
   824                     case CMD_LEGALESE:
       
   825                         leavePara();
       
   826                         if (openCommand(cmd))
       
   827                             append(Atom::LegaleseLeft);
       
   828                         docPrivate->hasLegalese = true;
       
   829                         break;
       
   830                     case CMD_LINK:
       
   831                         if (openCommand(cmd)) {
       
   832                             enterPara();
       
   833                             x = getArgument();
       
   834                             append(Atom::Link, x);
       
   835                             append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
       
   836                             skipSpacesOrOneEndl();
       
   837                         }
       
   838                         break;
       
   839                     case CMD_LIST:
       
   840                         if (openCommand(cmd)) {
       
   841                             leavePara();
       
   842                             openedLists.push(OpenedList(location(),
       
   843                                                          getOptionalArgument()));
       
   844                         }
       
   845                         break;
       
   846                     case CMD_META:
       
   847                         priv->constructExtra();
       
   848                         x = getArgument();
       
   849                         priv->extra->metaMap.insert(x, getRestOfLine());
       
   850                         break;
       
   851                     case CMD_NEWCODE:
       
   852                         location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_NEWCODE)));
       
   853                         break;
       
   854                     case CMD_O:
       
   855                         leavePara();
       
   856                         if (openedCommands.top() == CMD_LIST) {
       
   857                             if (openedLists.top().isStarted()) {
       
   858                                 append(Atom::ListItemRight,
       
   859                                         openedLists.top().styleString());
       
   860                             }
       
   861                             else {
       
   862                                 append(Atom::ListLeft,
       
   863                                         openedLists.top().styleString());
       
   864                             }
       
   865                             openedLists.top().next();
       
   866                             append(Atom::ListItemNumber,
       
   867                                     openedLists.top().numberString());
       
   868                             append(Atom::ListItemLeft,
       
   869                                     openedLists.top().styleString());
       
   870                             enterPara();
       
   871                         }
       
   872                         else if (openedCommands.top() == CMD_TABLE) {
       
   873                             x = "1,1";
       
   874                             if (isLeftBraceAhead())
       
   875                                 x = getArgument();
       
   876 
       
   877                             if (!inTableHeader && !inTableRow) {
       
   878                                 location().warning(tr("Missing '\\%1' or '\\%1' before '\\%3'")
       
   879                                                    .arg(cmdName(CMD_HEADER))
       
   880                                                    .arg(cmdName(CMD_ROW))
       
   881                                                    .arg(cmdName(CMD_O)));
       
   882                                 append(Atom::TableRowLeft);
       
   883                                 inTableRow = true;
       
   884                             }
       
   885                             else if (inTableItem) {
       
   886                                 append(Atom::TableItemRight);
       
   887                                 inTableItem = false;
       
   888                             }
       
   889 
       
   890                             append(Atom::TableItemLeft, x);
       
   891                             inTableItem = true;
       
   892                         }
       
   893                         else {
       
   894                             location().warning(tr("Command '\\%1' outside of '\\%2' and '\\%3'")
       
   895                                                .arg(cmdName(cmd))
       
   896                                                .arg(cmdName(CMD_LIST))
       
   897                                                .arg(cmdName(CMD_TABLE)));
       
   898                         }
       
   899                         break;
       
   900                     case CMD_OLDCODE:
       
   901                         leavePara();
       
   902                         append(Atom::CodeOld, getCode(CMD_OLDCODE, marker));
       
   903                         append(Atom::CodeNew, getCode(CMD_NEWCODE, marker));
       
   904                         break;
       
   905                     case CMD_OMIT:
       
   906                         getUntilEnd(cmd);
       
   907                         break;
       
   908                     case CMD_OMITVALUE:
       
   909                         x = getArgument();
       
   910                         if (!priv->enumItemList.contains(x))
       
   911                             priv->enumItemList.append(x);
       
   912                         if (!priv->omitEnumItemList.contains(x))
       
   913                             priv->omitEnumItemList.append(x);
       
   914                         break;
       
   915                     case CMD_PART:
       
   916                         startSection(Doc::Part, cmd);
       
   917                         break;
       
   918                     case CMD_PRINTLINE:
       
   919                         leavePara();
       
   920                         if (!quoting)
       
   921                             appendToCode(quoter.quoteLine(location(), cmdStr,
       
   922                                                            getRestOfLine()));
       
   923                         else {
       
   924                             append(Atom::CodeQuoteCommand, cmdStr);
       
   925                             append(Atom::CodeQuoteArgument, getRestOfLine());
       
   926                         }
       
   927                         break;
       
   928                     case CMD_PRINTTO:
       
   929                         leavePara();
       
   930                         if (!quoting)
       
   931                             appendToCode(quoter.quoteTo(location(), cmdStr,
       
   932                                           getRestOfLine()));
       
   933                         else {
       
   934                             append(Atom::CodeQuoteCommand, cmdStr);
       
   935                             append(Atom::CodeQuoteArgument, getRestOfLine());
       
   936                         }
       
   937                         break;
       
   938                     case CMD_PRINTUNTIL:
       
   939                         leavePara();
       
   940                         if (!quoting)
       
   941                             appendToCode(quoter.quoteUntil(location(), cmdStr,
       
   942                                           getRestOfLine()));
       
   943                         else {
       
   944                             append(Atom::CodeQuoteCommand, cmdStr);
       
   945                             append(Atom::CodeQuoteArgument, getRestOfLine());
       
   946                         }
       
   947                         break;
       
   948                     case CMD_QUOTATION:
       
   949                         if (openCommand(cmd)) {
       
   950                             leavePara();
       
   951                             append(Atom::QuotationLeft);
       
   952                         }
       
   953                         break;
       
   954                     case CMD_QUOTEFILE:
       
   955                     {
       
   956                         leavePara();
       
   957                         QString fileName = getArgument();
       
   958                         Doc::quoteFromFile(location(), quoter, fileName);
       
   959                         if (!quoting) {
       
   960                             append(Atom::Code,
       
   961                                     quoter.quoteTo(location(), cmdStr, ""));
       
   962                             quoter.reset();
       
   963                         }
       
   964                         else {
       
   965                             append(Atom::CodeQuoteCommand, cmdStr);
       
   966                             append(Atom::CodeQuoteArgument, fileName);
       
   967                         }
       
   968                         break;
       
   969                     }
       
   970                     case CMD_QUOTEFROMFILE:
       
   971                         leavePara();
       
   972                         if (!quoting)
       
   973                             quoteFromFile();
       
   974                         else {
       
   975                             append(Atom::CodeQuoteCommand, cmdStr);
       
   976                             append(Atom::CodeQuoteArgument, getArgument());
       
   977                         }
       
   978                         break;
       
   979                     case CMD_QUOTEFUNCTION:
       
   980                         leavePara();
       
   981                         marker = quoteFromFile();
       
   982                         x = getRestOfLine();
       
   983                         if (!quoting) {
       
   984                             quoter.quoteTo(location(), cmdStr,
       
   985                                             slashed(marker->functionBeginRegExp(x)));
       
   986                             append(Atom::Code,
       
   987                                     quoter.quoteUntil(location(), cmdStr,
       
   988                                             slashed(marker->functionEndRegExp(x))));
       
   989                             quoter.reset();
       
   990                         }
       
   991                         else {
       
   992                             append(Atom::CodeQuoteCommand, cmdStr);
       
   993                             append(Atom::CodeQuoteArgument, slashed(marker->functionEndRegExp(x)));
       
   994                         }
       
   995                         break;
       
   996                     case CMD_RAW:
       
   997                         leavePara();
       
   998                         x = getRestOfLine();
       
   999                         if (x.isEmpty())
       
  1000                             location().warning(tr("Missing format name after '\\%1")
       
  1001                                                .arg(cmdName(CMD_RAW)));
       
  1002                         append(Atom::FormatIf, x);
       
  1003                         append(Atom::RawString, untabifyEtc(getUntilEnd(cmd)));
       
  1004                         append(Atom::FormatElse);
       
  1005                         append(Atom::FormatEndif);
       
  1006                         break;
       
  1007                     case CMD_ROW:
       
  1008                         if (openedCommands.top() == CMD_TABLE) {
       
  1009                             leaveTableRow();
       
  1010                             append(Atom::TableRowLeft);
       
  1011                             inTableRow = true;
       
  1012                         }
       
  1013                         else {
       
  1014                             if (openedCommands.contains(CMD_TABLE)) {
       
  1015                                 location().warning(tr("Cannot use '\\%1' within '\\%2'")
       
  1016                                                    .arg(cmdName(CMD_ROW))
       
  1017                                                    .arg(cmdName(openedCommands.top())));
       
  1018                             }
       
  1019                             else {
       
  1020                                 location().warning(tr("Cannot use '\\%1' outside of '\\%2'")
       
  1021                                                    .arg(cmdName(CMD_ROW))
       
  1022                                                    .arg(cmdName(CMD_TABLE)));
       
  1023                             }
       
  1024                         }
       
  1025                         break;
       
  1026                     case CMD_SA:
       
  1027                         parseAlso();
       
  1028                         break;
       
  1029                     case CMD_SECTION1:
       
  1030                         startSection(Doc::Section1, cmd);
       
  1031                         break;
       
  1032                     case CMD_SECTION2:
       
  1033                         startSection(Doc::Section2, cmd);
       
  1034                         break;
       
  1035                     case CMD_SECTION3:
       
  1036                         startSection(Doc::Section3, cmd);
       
  1037                         break;
       
  1038                     case CMD_SECTION4:
       
  1039                         startSection(Doc::Section4, cmd);
       
  1040                         break;
       
  1041                     case CMD_SIDEBAR:
       
  1042                         if (openCommand(cmd)) {
       
  1043                             leavePara();
       
  1044                             append(Atom::SidebarLeft);
       
  1045                         }
       
  1046                         break;
       
  1047                     case CMD_SKIPLINE:
       
  1048                         leavePara();
       
  1049                         if (!quoting)
       
  1050                             quoter.quoteLine(location(),
       
  1051                                              cmdStr,
       
  1052                                              getRestOfLine());
       
  1053                         else {
       
  1054                             append(Atom::CodeQuoteCommand, cmdStr);
       
  1055                             append(Atom::CodeQuoteArgument, getRestOfLine());
       
  1056                         }
       
  1057                         break;
       
  1058                     case CMD_SKIPTO:
       
  1059                         leavePara();
       
  1060                         if (!quoting)
       
  1061                             quoter.quoteTo(location(),
       
  1062                                            cmdStr,
       
  1063                                            getRestOfLine());
       
  1064                         else {
       
  1065                             append(Atom::CodeQuoteCommand, cmdStr);
       
  1066                             append(Atom::CodeQuoteArgument, getRestOfLine());
       
  1067                         }
       
  1068                         break;
       
  1069                     case CMD_SKIPUNTIL:
       
  1070                         leavePara();
       
  1071                         if (!quoting)
       
  1072                             quoter.quoteUntil(location(),
       
  1073                                               cmdStr,
       
  1074                                               getRestOfLine());
       
  1075                         else {
       
  1076                             append(Atom::CodeQuoteCommand, cmdStr);
       
  1077                             append(Atom::CodeQuoteArgument, getRestOfLine());
       
  1078                         }
       
  1079                         break;
       
  1080                     case CMD_SNIPPET:
       
  1081                         leavePara();
       
  1082                         {
       
  1083                             QString snippet = getArgument();
       
  1084                             QString identifier = getRestOfLine();
       
  1085                             if (quoting) {
       
  1086                                 append(Atom::SnippetCommand, cmdStr);
       
  1087                                 append(Atom::SnippetLocation, snippet);
       
  1088                                 append(Atom::SnippetIdentifier, identifier);
       
  1089                             }
       
  1090                             else {
       
  1091                                 Doc::quoteFromFile(location(),quoter,snippet);
       
  1092                                 appendToCode(quoter.quoteSnippet(location(),
       
  1093                                                                  identifier));
       
  1094                             }
       
  1095                         }
       
  1096                         break;
       
  1097                     case CMD_SUB:
       
  1098                         startFormat(ATOM_FORMATTING_SUBSCRIPT, cmd);
       
  1099                         break;
       
  1100                     case CMD_SUP:
       
  1101                         startFormat(ATOM_FORMATTING_SUPERSCRIPT, cmd);
       
  1102                         break;
       
  1103                     case CMD_TABLE:
       
  1104                         x = getRestOfLine();
       
  1105                         if (openCommand(cmd)) {
       
  1106                             leavePara();
       
  1107                             append(Atom::TableLeft, x);
       
  1108                             inTableHeader = false;
       
  1109                             inTableRow = false;
       
  1110                             inTableItem = false;
       
  1111                         }
       
  1112                         break;
       
  1113                     case CMD_TABLEOFCONTENTS:
       
  1114                         x = "1";
       
  1115                         if (isLeftBraceAhead())
       
  1116                             x = getArgument();
       
  1117                         x += ",";
       
  1118                         x += QString::number((int)getSectioningUnit());
       
  1119                         append(Atom::TableOfContents, x);
       
  1120                         break;
       
  1121                     case CMD_TARGET:
       
  1122                         insertTarget(getRestOfLine(),false);
       
  1123                         break;
       
  1124                     case CMD_TT:
       
  1125                         startFormat(ATOM_FORMATTING_TELETYPE, cmd);
       
  1126                         break;
       
  1127                     case CMD_UNDERLINE:
       
  1128                         startFormat(ATOM_FORMATTING_UNDERLINE, cmd);
       
  1129                         break;
       
  1130                     case CMD_UNICODE:
       
  1131                         enterPara();
       
  1132                         x = getArgument();
       
  1133                         {
       
  1134                             bool ok;
       
  1135                             uint unicodeChar = x.toUInt(&ok, 0);
       
  1136                             if (!ok ||
       
  1137                                 (unicodeChar == 0x0000) ||
       
  1138                                 (unicodeChar > 0xFFFE)) {
       
  1139                                 location().warning(tr("Invalid Unicode character '%1' specified "
       
  1140                                                       "with '%2'")
       
  1141                                                    .arg(x, cmdName(CMD_UNICODE)));
       
  1142                             }
       
  1143                             else {
       
  1144                                 append(Atom::String, QChar(unicodeChar));
       
  1145                             }
       
  1146                         }
       
  1147                         break;
       
  1148                     case CMD_VALUE:
       
  1149                         leaveValue();
       
  1150                         if (openedLists.top().style() == OpenedList::Value) {
       
  1151                             x = getArgument();
       
  1152                             if (!priv->enumItemList.contains(x))
       
  1153                                 priv->enumItemList.append(x);
       
  1154 
       
  1155                             openedLists.top().next();
       
  1156                             append(Atom::ListTagLeft, ATOM_LIST_VALUE);
       
  1157                             append(Atom::String, x);
       
  1158                             append(Atom::ListTagRight, ATOM_LIST_VALUE);
       
  1159                             append(Atom::ListItemLeft, ATOM_LIST_VALUE);
       
  1160 
       
  1161                             skipSpacesOrOneEndl();
       
  1162                             if (isBlankLine())
       
  1163                                 append(Atom::Nop);
       
  1164                         }
       
  1165                         else {
       
  1166                             // ### problems
       
  1167                         }
       
  1168                         break;
       
  1169                     case CMD_WARNING:
       
  1170                         startNewPara();
       
  1171                         append(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
       
  1172                         append(Atom::String, "Warning:");
       
  1173                         append(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
       
  1174                         append(Atom::String, " ");
       
  1175                         break;
       
  1176                     case CMD_OVERLOAD:
       
  1177                         priv->metacommandsUsed.insert(cmdStr);
       
  1178                         x.clear();
       
  1179                         if (!isBlankLine())
       
  1180                             x = getRestOfLine();
       
  1181                         if (!x.isEmpty()) {
       
  1182                             append(Atom::ParaLeft);
       
  1183                             append(Atom::String, "This function overloads ");
       
  1184                             append(Atom::AutoLink,x);
       
  1185                             append(Atom::String, ".");
       
  1186                             append(Atom::ParaRight);
       
  1187                         }
       
  1188                         else {
       
  1189                             append(Atom::ParaLeft);
       
  1190                             append(Atom::String,"This is an overloaded function.");
       
  1191                             append(Atom::ParaRight);
       
  1192                             x = getMetaCommandArgument(cmdStr);
       
  1193                         }
       
  1194                         priv->metaCommandMap[cmdStr].append(x);
       
  1195                         break;
       
  1196                     case NOT_A_CMD:
       
  1197                         if (metaCommandSet.contains(cmdStr)) {
       
  1198                             priv->metacommandsUsed.insert(cmdStr);
       
  1199                             QString xxx = getMetaCommandArgument(cmdStr);
       
  1200                             priv->metaCommandMap[cmdStr].append(xxx);
       
  1201                         }
       
  1202                         else if (macroHash()->contains(cmdStr)) {
       
  1203                             const Macro &macro = macroHash()->value(cmdStr);
       
  1204                             int numPendingFi = 0;
       
  1205                             QStringMap::ConstIterator d;
       
  1206                             d = macro.otherDefs.begin();
       
  1207                             while (d != macro.otherDefs.end()) {
       
  1208                                 append(Atom::FormatIf, d.key());
       
  1209                                 expandMacro(cmdStr, *d, macro.numParams);
       
  1210                                 ++d;
       
  1211 
       
  1212                                 if (d == macro.otherDefs.end()) {
       
  1213                                     append(Atom::FormatEndif);
       
  1214                                 }
       
  1215                                 else {
       
  1216                                     append(Atom::FormatElse);
       
  1217                                     numPendingFi++;
       
  1218                                 }
       
  1219                             }
       
  1220                             while (numPendingFi-- > 0)
       
  1221                                 append(Atom::FormatEndif);
       
  1222 
       
  1223                             if (!macro.defaultDef.isEmpty()) {
       
  1224                                 if (!macro.otherDefs.isEmpty()) {
       
  1225                                     macro.defaultDefLocation.warning(
       
  1226                                       tr("Macro cannot have both "
       
  1227                                          "format-specific and qdoc- "
       
  1228                                          "syntax definitions"));
       
  1229                                 }
       
  1230                                 else {
       
  1231                                     location().push(macro.defaultDefLocation.filePath());
       
  1232                                     in.insert(pos, macro.defaultDef);
       
  1233                                     len = in.length();
       
  1234                                     openedInputs.push(pos + macro.defaultDef.length());
       
  1235                                 }
       
  1236                             }
       
  1237                         }
       
  1238                         else {
       
  1239                             location().warning(
       
  1240                               tr("Unknown command '\\%1'").arg(cmdStr),
       
  1241                                  detailsUnknownCommand(metaCommandSet,cmdStr));
       
  1242                             enterPara();
       
  1243                             append(Atom::UnknownCommand, cmdStr);
       
  1244                         }
       
  1245                     }
       
  1246                 }
       
  1247             }
       
  1248             break;
       
  1249         case '{':
       
  1250             enterPara();
       
  1251             appendChar('{');
       
  1252             braceDepth++;
       
  1253             pos++;
       
  1254             break;
       
  1255         case '}':
       
  1256             {
       
  1257                 braceDepth--;
       
  1258                 pos++;
       
  1259 
       
  1260                 QMap<int, QString>::Iterator f =
       
  1261                     pendingFormats.find(braceDepth);
       
  1262                 if (f == pendingFormats.end()) {
       
  1263                     enterPara();
       
  1264                     appendChar('}');
       
  1265                 }
       
  1266                 else {
       
  1267                     append(Atom::FormattingRight, *f);
       
  1268                     if (*f == ATOM_FORMATTING_INDEX) {
       
  1269                         if (indexStartedPara)
       
  1270                             skipAllSpaces();
       
  1271                     }
       
  1272                     else if (*f == ATOM_FORMATTING_LINK) {
       
  1273                         // hack for C++ to support links like
       
  1274                         // \l{QString::}{count()}
       
  1275                         if (currentLinkAtom &&
       
  1276                             currentLinkAtom->string().endsWith("::")) {
       
  1277                             QString suffix = Text::subText(currentLinkAtom,
       
  1278                                           priv->text.lastAtom()).toString();
       
  1279                             currentLinkAtom->appendString(suffix);
       
  1280                         }
       
  1281                         currentLinkAtom = 0;
       
  1282                     }
       
  1283                     pendingFormats.erase(f);
       
  1284                 }
       
  1285             }
       
  1286             break;
       
  1287         default:
       
  1288             {
       
  1289                 bool newWord;
       
  1290                 switch (priv->text.lastAtom()->type()) {
       
  1291                 case Atom::ParaLeft:
       
  1292                     newWord = true;
       
  1293                     break;
       
  1294                 default:
       
  1295                     newWord = false;
       
  1296                 }
       
  1297 
       
  1298                 if (paraState == OutsidePara) {
       
  1299                     if (ch.isSpace()) {
       
  1300                         ++pos;
       
  1301                         newWord = false;
       
  1302                     }
       
  1303                     else {
       
  1304                         enterPara();
       
  1305                         newWord = true;
       
  1306                     }
       
  1307                 }
       
  1308                 else {
       
  1309                     if (ch.isSpace()) {
       
  1310                         ++pos;
       
  1311                         if ((ch == '\n') &&
       
  1312                             (paraState == InsideSingleLinePara ||
       
  1313                              isBlankLine())) {
       
  1314                             leavePara();
       
  1315                             newWord = false;
       
  1316                         }
       
  1317                         else {
       
  1318                             appendChar(' ');
       
  1319                             newWord = true;
       
  1320                         }
       
  1321                     }
       
  1322                     else {
       
  1323                         newWord = true;
       
  1324                     }
       
  1325                 }
       
  1326 
       
  1327                 if (newWord) {
       
  1328                     int startPos = pos;
       
  1329                     int numInternalUppercase = 0;
       
  1330                     int numLowercase = 0;
       
  1331                     int numStrangeSymbols = 0;
       
  1332 
       
  1333                     while (pos < len) {
       
  1334                         unsigned char latin1Ch = in.at(pos).toLatin1();
       
  1335                         if (islower(latin1Ch)) {
       
  1336                             ++numLowercase;
       
  1337                             ++pos;
       
  1338                         }
       
  1339                         else if (isupper(latin1Ch)) {
       
  1340                             if (pos > startPos)
       
  1341                                 ++numInternalUppercase;
       
  1342                             ++pos;
       
  1343                         }
       
  1344                         else if (isdigit(latin1Ch)) {
       
  1345                             if (pos > startPos) {
       
  1346                                 ++pos;
       
  1347                             }
       
  1348                             else {
       
  1349                                 break;
       
  1350                             }
       
  1351                         }
       
  1352                         else if (latin1Ch == '_' || latin1Ch == '@') {
       
  1353                             ++numStrangeSymbols;
       
  1354                             ++pos;
       
  1355                         }
       
  1356                         else if (latin1Ch == ':' && pos < len - 1
       
  1357                                    && in.at(pos + 1) == QLatin1Char(':')) {
       
  1358                             ++numStrangeSymbols;
       
  1359                             pos += 2;
       
  1360                         }
       
  1361                         else if (latin1Ch == '(') {
       
  1362                             if (pos > startPos) {
       
  1363                                 if (pos < len - 1 &&
       
  1364                                     in.at(pos + 1) == QLatin1Char(')')) {
       
  1365                                     ++numStrangeSymbols;
       
  1366                                     pos += 2;
       
  1367                                     break;
       
  1368                                 }
       
  1369                                 else {
       
  1370                                     // ### handle functions with signatures
       
  1371                                     // and function calls
       
  1372                                     break;
       
  1373                                 }
       
  1374                             }
       
  1375                             else {
       
  1376                                 break;
       
  1377                             }
       
  1378                         }
       
  1379                         else {
       
  1380                             break;
       
  1381                         }
       
  1382                     }
       
  1383 
       
  1384                     if (pos == startPos) {
       
  1385                         if (!ch.isSpace()) {
       
  1386                             appendChar(ch);
       
  1387                             ++pos;
       
  1388                         }
       
  1389                     }
       
  1390                     else {
       
  1391                         QString word = in.mid(startPos, pos - startPos);
       
  1392                         // is word a C++ symbol or an English word?
       
  1393                         if ((numInternalUppercase >= 1 && numLowercase >= 2)
       
  1394                                 || numStrangeSymbols >= 1) {
       
  1395                             append(Atom::AutoLink, word);
       
  1396                         }
       
  1397                         else {
       
  1398                             appendWord(word);
       
  1399                         }
       
  1400                     }
       
  1401                 }
       
  1402             }
       
  1403         }
       
  1404     }
       
  1405     leaveValueList();
       
  1406 
       
  1407     // for compatibility
       
  1408     if (openedCommands.top() == CMD_LEGALESE) {
       
  1409         append(Atom::LegaleseRight);
       
  1410         openedCommands.pop();
       
  1411     }
       
  1412 
       
  1413     if (openedCommands.top() != CMD_OMIT) {
       
  1414         location().warning(tr("Missing '\\%1'").arg(endCmdName(openedCommands.top())));
       
  1415     }
       
  1416     else if (preprocessorSkipping.count() > 0) {
       
  1417         location().warning(tr("Missing '\\%1'").arg(cmdName(CMD_ENDIF)));
       
  1418     }
       
  1419 
       
  1420     while (currentSectioningUnit > Doc::Chapter) {
       
  1421         int delta = currentSectioningUnit - priv->extra->sectioningUnit;
       
  1422         append(Atom::SectionRight, QString::number(delta));
       
  1423         currentSectioningUnit = Doc::SectioningUnit(int(currentSectioningUnit) - 1);
       
  1424     }
       
  1425 
       
  1426     if (priv->extra && priv->extra->granularity < priv->extra->sectioningUnit)
       
  1427         priv->extra->granularity = priv->extra->sectioningUnit;
       
  1428     priv->text.stripFirstAtom();
       
  1429 }
       
  1430 
       
  1431 Location &DocParser::location()
       
  1432 {
       
  1433     while (!openedInputs.isEmpty() && openedInputs.top() <= pos) {
       
  1434         cachedLoc.pop();
       
  1435         cachedPos = openedInputs.pop();
       
  1436     }
       
  1437     while (cachedPos < pos)
       
  1438         cachedLoc.advance(in.at(cachedPos++));
       
  1439     return cachedLoc;
       
  1440 }
       
  1441 
       
  1442 QString DocParser::detailsUnknownCommand(const QSet<QString> &metaCommandSet,
       
  1443                                          const QString &str)
       
  1444 {
       
  1445     QSet<QString> commandSet = metaCommandSet;
       
  1446     int i = 0;
       
  1447     while (cmds[i].english != 0) {
       
  1448         commandSet.insert(*cmds[i].alias);
       
  1449         i++;
       
  1450     }
       
  1451 
       
  1452     if (aliasMap()->contains(str))
       
  1453         return tr("The command '\\%1' was renamed '\\%2' by the configuration"
       
  1454                    " file. Use the new name.")
       
  1455                .arg(str).arg((*aliasMap())[str]);
       
  1456 
       
  1457     QString best = nearestName(str, commandSet);
       
  1458     if (best.isEmpty())
       
  1459         return QString();
       
  1460     return tr("Maybe you meant '\\%1'?").arg(best);
       
  1461 }
       
  1462 
       
  1463 void DocParser::checkExpiry(const QString& date)
       
  1464 {
       
  1465     QRegExp ymd("(\\d{4})(?:-(\\d{2})(?:-(\\d{2})))");
       
  1466 
       
  1467     if (ymd.exactMatch(date)) {
       
  1468 	int y = ymd.cap(1).toInt();
       
  1469 	int m = ymd.cap(2).toInt();
       
  1470 	int d = ymd.cap(3).toInt();
       
  1471 
       
  1472 	if (m == 0)
       
  1473 	    m = 1;
       
  1474 	if (d == 0)
       
  1475 	    d = 1;
       
  1476 	QDate expiryDate(y, m, d);
       
  1477 	if (expiryDate.isValid()) {
       
  1478 	    int days = expiryDate.daysTo(QDate::currentDate());
       
  1479 	    if (days == 0) {
       
  1480 		location().warning(tr("Documentation expires today"));
       
  1481 	    }
       
  1482             else if (days == 1) {
       
  1483 		location().warning(tr("Documentation expired yesterday"));
       
  1484 	    }
       
  1485             else if (days >= 2) {
       
  1486 		location().warning(tr("Documentation expired %1 days ago")
       
  1487 				    .arg(days));
       
  1488 	    }
       
  1489 	}
       
  1490         else {
       
  1491 	    location().warning(tr("Date '%1' invalid").arg(date));
       
  1492 	}
       
  1493     }
       
  1494     else {
       
  1495 	location().warning(tr("Date '%1' not in YYYY-MM-DD format")
       
  1496 			    .arg(date));
       
  1497     }
       
  1498 }
       
  1499 
       
  1500 void DocParser::insertBaseName(const QString &baseName)
       
  1501 {
       
  1502     priv->constructExtra();
       
  1503     if (currentSectioningUnit == priv->extra->sectioningUnit) {
       
  1504 	priv->extra->baseName = baseName;
       
  1505     }
       
  1506     else {
       
  1507 	Atom *atom = priv->text.firstAtom();
       
  1508 	Atom *sectionLeft = 0;
       
  1509 
       
  1510 	int delta = currentSectioningUnit - priv->extra->sectioningUnit;
       
  1511 
       
  1512 	while (atom != 0) {
       
  1513 	    if (atom->type() == Atom::SectionLeft &&
       
  1514 		 atom->string().toInt() == delta)
       
  1515 		sectionLeft = atom;
       
  1516 	    atom = atom->next();
       
  1517 	}
       
  1518 	if (sectionLeft != 0)
       
  1519 	    (void) new Atom(sectionLeft, Atom::BaseName, baseName);
       
  1520     }
       
  1521 }
       
  1522 
       
  1523 void DocParser::insertTarget(const QString &target, bool keyword)
       
  1524 {
       
  1525     if (targetMap.contains(target)) {
       
  1526 	location().warning(tr("Duplicate target name '%1'").arg(target));
       
  1527 	targetMap[target].warning(tr("(The previous occurrence is here)"));
       
  1528     }
       
  1529     else {
       
  1530 	targetMap.insert(target, location());
       
  1531 	append(Atom::Target, target);
       
  1532         priv->constructExtra();
       
  1533         if (keyword)
       
  1534             priv->extra->keywords.append(priv->text.lastAtom());
       
  1535         else
       
  1536             priv->extra->targets.append(priv->text.lastAtom());
       
  1537     }
       
  1538 }
       
  1539 
       
  1540 void DocParser::include(const QString& fileName)
       
  1541 {
       
  1542     if (location().depth() > 16)
       
  1543 	location().fatal(tr("Too many nested '\\%1's")
       
  1544 			  .arg(cmdName(CMD_INCLUDE)));
       
  1545 
       
  1546     QString userFriendlyFilePath;
       
  1547     // ### use current directory?
       
  1548     QString filePath = Config::findFile(location(),
       
  1549                                         sourceFiles,
       
  1550                                         sourceDirs,
       
  1551                                         fileName,
       
  1552                                         userFriendlyFilePath);
       
  1553     if (filePath.isEmpty()) {
       
  1554 	location().warning(tr("Cannot find leaf file '%1'").arg(fileName));
       
  1555     }
       
  1556     else {
       
  1557 	QFile inFile(filePath);
       
  1558 	if (!inFile.open(QFile::ReadOnly)) {
       
  1559 	    location().warning(tr("Cannot open leaf file '%1'")
       
  1560 				.arg(userFriendlyFilePath));
       
  1561 	}
       
  1562         else {
       
  1563 	    location().push(userFriendlyFilePath);
       
  1564 
       
  1565 	    QTextStream inStream(&inFile);
       
  1566 	    QString includedStuff = inStream.readAll();
       
  1567 	    inFile.close();
       
  1568 
       
  1569 	    in.insert(pos, includedStuff);
       
  1570 	    len = in.length();
       
  1571 	    openedInputs.push(pos + includedStuff.length());
       
  1572 	}
       
  1573     }
       
  1574 }
       
  1575 
       
  1576 void DocParser::startFormat(const QString& format, int cmd)
       
  1577 {
       
  1578     enterPara();
       
  1579 
       
  1580     QMap<int, QString>::ConstIterator f = pendingFormats.begin();
       
  1581     while (f != pendingFormats.end()) {
       
  1582 	if (*f == format) {
       
  1583 	    location().warning(tr("Cannot nest '\\%1' commands")
       
  1584 				.arg(cmdName(cmd)));
       
  1585 	    return;
       
  1586 	}
       
  1587 	++f;
       
  1588     }
       
  1589 
       
  1590     append(Atom::FormattingLeft, format);
       
  1591 
       
  1592     if (isLeftBraceAhead()) {
       
  1593 	skipSpacesOrOneEndl();
       
  1594 	pendingFormats.insert(braceDepth, format);
       
  1595 	++braceDepth;
       
  1596 	++pos;
       
  1597     }
       
  1598     else {
       
  1599 	append(Atom::String, getArgument());
       
  1600 	append(Atom::FormattingRight, format);
       
  1601 	if (format == ATOM_FORMATTING_INDEX && indexStartedPara) {
       
  1602 	    skipAllSpaces();
       
  1603 	    indexStartedPara = false;
       
  1604 	}
       
  1605     }
       
  1606 }
       
  1607 
       
  1608 bool DocParser::openCommand(int cmd)
       
  1609 {
       
  1610     int outer = openedCommands.top();
       
  1611     bool ok = true;
       
  1612 
       
  1613     if (cmd != CMD_LINK) {
       
  1614         if (outer == CMD_LIST) {
       
  1615 	    ok = (cmd == CMD_FOOTNOTE || cmd == CMD_LIST);
       
  1616         }
       
  1617         else if (outer == CMD_ABSTRACT) {
       
  1618 	    ok = (cmd == CMD_LIST ||
       
  1619                   cmd == CMD_QUOTATION ||
       
  1620                   cmd == CMD_TABLE);
       
  1621         }
       
  1622         else if (outer == CMD_SIDEBAR) {
       
  1623 	    ok = (cmd == CMD_LIST ||
       
  1624                   cmd == CMD_QUOTATION ||
       
  1625                   cmd == CMD_SIDEBAR);
       
  1626         }
       
  1627         else if (outer == CMD_QUOTATION) {
       
  1628 	    ok = (cmd == CMD_LIST);
       
  1629         }
       
  1630         else if (outer == CMD_TABLE) {
       
  1631 	    ok = (cmd == CMD_LIST ||
       
  1632                   cmd == CMD_FOOTNOTE ||
       
  1633                   cmd == CMD_QUOTATION);
       
  1634         }
       
  1635         else if (outer == CMD_FOOTNOTE || outer == CMD_LINK) {
       
  1636 	    ok = false;
       
  1637         }
       
  1638     }
       
  1639 
       
  1640     if (ok) {
       
  1641 	openedCommands.push(cmd);
       
  1642     }
       
  1643     else {
       
  1644 	location().warning(tr("Cannot use '\\%1' within '\\%2'")
       
  1645 			   .arg(cmdName(cmd)).arg(cmdName(outer)));
       
  1646     }
       
  1647     return ok;
       
  1648 }
       
  1649 
       
  1650 bool DocParser::closeCommand(int endCmd)
       
  1651 {
       
  1652     if (endCmdFor(openedCommands.top()) == endCmd && openedCommands.size() > 1) {
       
  1653 	openedCommands.pop();
       
  1654 	return true;
       
  1655     }
       
  1656     else {
       
  1657 	bool contains = false;
       
  1658 	QStack<int> opened2 = openedCommands;
       
  1659 	while (opened2.size() > 1) {
       
  1660 	    if (endCmdFor(opened2.top()) == endCmd) {
       
  1661 		contains = true;
       
  1662 		break;
       
  1663 	    }
       
  1664 	    opened2.pop();
       
  1665 	}
       
  1666 
       
  1667 	if (contains) {
       
  1668 	    while (endCmdFor(openedCommands.top()) != endCmd && openedCommands.size() > 1) {
       
  1669 		location().warning(tr("Missing '\\%1' before '\\%2'")
       
  1670 				    .arg(endCmdName(openedCommands.top()))
       
  1671 				    .arg(cmdName(endCmd)));
       
  1672 		openedCommands.pop();
       
  1673 	    }
       
  1674 	}
       
  1675         else {
       
  1676 	    location().warning(tr("Unexpected '\\%1'")
       
  1677 				.arg(cmdName(endCmd)));
       
  1678 	}
       
  1679 	return false;
       
  1680     }
       
  1681 }
       
  1682 
       
  1683 void DocParser::startSection(Doc::SectioningUnit unit, int cmd)
       
  1684 {
       
  1685     leavePara();
       
  1686 
       
  1687     if (currentSectioningUnit == Doc::Book) {
       
  1688 #if 0
       
  1689         // mws didn't think this was necessary.
       
  1690 	if (unit > Doc::Section1)
       
  1691 	    location().warning(tr("Unexpected '\\%1' without '\\%2'")
       
  1692 				.arg(cmdName(cmd))
       
  1693 				.arg(cmdName(CMD_SECTION1)));
       
  1694 #endif        
       
  1695 	currentSectioningUnit = (Doc::SectioningUnit) (unit - 1);
       
  1696 	priv->constructExtra();
       
  1697 	priv->extra->sectioningUnit = currentSectioningUnit;
       
  1698     }
       
  1699 
       
  1700     if (unit <= priv->extra->sectioningUnit) {
       
  1701 	location().warning(tr("Unexpected '\\%1' in this documentation")
       
  1702 			    .arg(cmdName(cmd)));
       
  1703     }
       
  1704     else if (unit - currentSectioningUnit > 1) {
       
  1705 	location().warning(tr("Unexpected '\\%1' at this point")
       
  1706 			    .arg(cmdName(cmd)));
       
  1707     }
       
  1708     else {
       
  1709 	if (currentSectioningUnit >= unit)
       
  1710 	    endSection(unit, cmd);
       
  1711 
       
  1712 	int delta = unit - priv->extra->sectioningUnit;
       
  1713 	append(Atom::SectionLeft, QString::number(delta));
       
  1714         priv->constructExtra();
       
  1715         priv->extra->tableOfContents.append(priv->text.lastAtom());
       
  1716         priv->extra->tableOfContentsLevels.append(unit);
       
  1717 	enterPara(Atom::SectionHeadingLeft,
       
  1718                   Atom::SectionHeadingRight,
       
  1719                   QString::number(delta));
       
  1720 	currentSectioningUnit = unit;
       
  1721     }
       
  1722 }
       
  1723 
       
  1724 void DocParser::endSection(int unit, int endCmd)
       
  1725 {
       
  1726     leavePara();
       
  1727 
       
  1728     if (unit < priv->extra->sectioningUnit) {
       
  1729 	location().warning(tr("Unexpected '\\%1' in this documentation")
       
  1730 			    .arg(cmdName(endCmd)));
       
  1731     }
       
  1732     else if (unit > currentSectioningUnit) {
       
  1733 	location().warning(tr("Unexpected '\\%1' at this point")
       
  1734 			    .arg(cmdName(endCmd)));
       
  1735     }
       
  1736     else {
       
  1737 	while (currentSectioningUnit >= unit) {
       
  1738 	    int delta = currentSectioningUnit - priv->extra->sectioningUnit;
       
  1739 	    append(Atom::SectionRight, QString::number(delta));
       
  1740 	    currentSectioningUnit =
       
  1741 		    (Doc::SectioningUnit) (currentSectioningUnit - 1);
       
  1742 	}
       
  1743     }
       
  1744 }
       
  1745 
       
  1746 void DocParser::parseAlso()
       
  1747 {
       
  1748     leavePara();
       
  1749     skipSpacesOnLine();
       
  1750     while (pos < len && in[pos] != '\n') {
       
  1751         QString target;
       
  1752         QString str;
       
  1753 
       
  1754         if (in[pos] == '{') {
       
  1755             target = getArgument();
       
  1756             skipSpacesOnLine();
       
  1757             if (in[pos] == '{') {
       
  1758                 str = getArgument();
       
  1759 
       
  1760                 // hack for C++ to support links like \l{QString::}{count()}
       
  1761                 if (target.endsWith("::"))
       
  1762                     target += str;
       
  1763             }
       
  1764             else {
       
  1765                 str = target;
       
  1766             }
       
  1767 #ifdef QDOC2_COMPAT
       
  1768         }
       
  1769         else if (in[pos] == '\\' && in.mid(pos, 5) == "\\link") {
       
  1770             pos += 6;
       
  1771             target = getArgument();
       
  1772             int endPos = in.indexOf("\\endlink", pos);
       
  1773             if (endPos != -1) {
       
  1774                 str = in.mid(pos, endPos - pos).trimmed();
       
  1775                 pos = endPos + 8;
       
  1776             }
       
  1777 #endif
       
  1778         }
       
  1779         else {
       
  1780             target = getArgument();
       
  1781             str = cleanLink(target);
       
  1782         }
       
  1783 
       
  1784         Text also;
       
  1785         also << Atom(Atom::Link, target)
       
  1786              << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
       
  1787              << str
       
  1788              << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
       
  1789         priv->addAlso(also);
       
  1790 
       
  1791         skipSpacesOnLine();
       
  1792         if (pos < len && in[pos] == ',') {
       
  1793             pos++;
       
  1794             skipSpacesOrOneEndl();
       
  1795         }
       
  1796         else if (in[pos] != '\n') {
       
  1797             location().warning(tr("Missing comma in '\\%1'").arg(cmdName(CMD_SA)));
       
  1798         }
       
  1799     }
       
  1800 }
       
  1801 
       
  1802 void DocParser::append(Atom::Type type, const QString &string)
       
  1803 {
       
  1804     Atom::Type lastType = priv->text.lastAtom()->type();
       
  1805 #ifdef QDOC_QML
       
  1806     if (((lastType == Atom::Code) || (lastType == Atom::Code)) &&
       
  1807 #else        
       
  1808     if ((lastType == Atom::Code) &&
       
  1809 #endif
       
  1810         priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n")))
       
  1811         priv->text.lastAtom()->chopString();
       
  1812     priv->text << Atom(type, string);
       
  1813 }
       
  1814 
       
  1815 void DocParser::appendChar(QChar ch)
       
  1816 {
       
  1817     if (priv->text.lastAtom()->type() != Atom::String)
       
  1818         append(Atom::String);
       
  1819     Atom *atom = priv->text.lastAtom();
       
  1820     if (ch == QLatin1Char(' ')) {
       
  1821         if (!atom->string().endsWith(QLatin1Char(' ')))
       
  1822             atom->appendChar(QLatin1Char(' '));
       
  1823     }
       
  1824     else 
       
  1825         atom->appendChar(ch);
       
  1826 }
       
  1827 
       
  1828 void DocParser::appendWord(const QString &word)
       
  1829 {
       
  1830     if (priv->text.lastAtom()->type() != Atom::String) {
       
  1831 	append(Atom::String, word);
       
  1832     }
       
  1833     else
       
  1834 	priv->text.lastAtom()->appendString(word);
       
  1835 }
       
  1836 
       
  1837 void DocParser::appendToCode(const QString& markedCode)
       
  1838 {
       
  1839     Atom::Type lastType = priv->text.lastAtom()->type();
       
  1840 #ifdef QDOC_QML
       
  1841     if (lastType != Atom::Qml)
       
  1842 	append(Atom::Qml);
       
  1843 #else
       
  1844     if (lastType != Atom::Code)
       
  1845 	append(Atom::Code);
       
  1846 #endif    
       
  1847     priv->text.lastAtom()->appendString(markedCode);
       
  1848 }
       
  1849 
       
  1850 void DocParser::startNewPara()
       
  1851 {
       
  1852     leavePara();
       
  1853     enterPara();
       
  1854 }
       
  1855 
       
  1856 void DocParser::enterPara(Atom::Type leftType,
       
  1857                           Atom::Type rightType,
       
  1858                           const QString& string)
       
  1859 {
       
  1860     if (paraState == OutsidePara) {
       
  1861         if (priv->text.lastAtom()->type() != Atom::ListItemLeft)
       
  1862             leaveValueList();
       
  1863 	append(leftType, string);
       
  1864 	indexStartedPara = false;
       
  1865 	pendingParaLeftType = leftType;
       
  1866 	pendingParaRightType = rightType;
       
  1867 	pendingParaString = string;
       
  1868 	if (
       
  1869 #if 0
       
  1870 	     leftType == Atom::BriefLeft ||
       
  1871 #endif
       
  1872 	     leftType == Atom::SectionHeadingLeft) {
       
  1873 	    paraState = InsideSingleLinePara;
       
  1874 	}
       
  1875         else {
       
  1876 	    paraState = InsideMultiLinePara;
       
  1877 	}
       
  1878 	skipSpacesOrOneEndl();
       
  1879     }
       
  1880 }
       
  1881 
       
  1882 void DocParser::leavePara()
       
  1883 {
       
  1884     if (paraState != OutsidePara) {
       
  1885 	if (!pendingFormats.isEmpty()) {
       
  1886 	    location().warning(tr("Missing '}'"));
       
  1887 	    pendingFormats.clear();
       
  1888 	}
       
  1889 
       
  1890 	if (priv->text.lastAtom()->type() == pendingParaLeftType) {
       
  1891 	    priv->text.stripLastAtom();
       
  1892 	}
       
  1893         else {
       
  1894 	    if (priv->text.lastAtom()->type() == Atom::String &&
       
  1895 		 priv->text.lastAtom()->string().endsWith(" ")) {
       
  1896 		priv->text.lastAtom()->chopString();
       
  1897 	    }
       
  1898 	    append(pendingParaRightType, pendingParaString);
       
  1899 	}
       
  1900 	paraState = OutsidePara;
       
  1901 	indexStartedPara = false;
       
  1902 	pendingParaRightType = Atom::Nop;
       
  1903 	pendingParaString = "";
       
  1904     }
       
  1905 }
       
  1906 
       
  1907 void DocParser::leaveValue()
       
  1908 {
       
  1909     leavePara();
       
  1910     if (openedLists.isEmpty()) {
       
  1911 	openedLists.push(OpenedList(OpenedList::Value));
       
  1912 	append(Atom::ListLeft, ATOM_LIST_VALUE);
       
  1913     }
       
  1914     else {
       
  1915         if (priv->text.lastAtom()->type() == Atom::Nop)
       
  1916             priv->text.stripLastAtom();
       
  1917 	append(Atom::ListItemRight, ATOM_LIST_VALUE);
       
  1918     }
       
  1919 }
       
  1920 
       
  1921 void DocParser::leaveValueList()
       
  1922 {
       
  1923     leavePara();
       
  1924     if (!openedLists.isEmpty() &&
       
  1925         (openedLists.top().style() == OpenedList::Value)) {
       
  1926         if (priv->text.lastAtom()->type() == Atom::Nop)
       
  1927             priv->text.stripLastAtom();
       
  1928 	append(Atom::ListItemRight, ATOM_LIST_VALUE);
       
  1929 	append(Atom::ListRight, ATOM_LIST_VALUE);
       
  1930 	openedLists.pop();
       
  1931     }
       
  1932 }
       
  1933 
       
  1934 void DocParser::leaveTableRow()
       
  1935 {
       
  1936     if (inTableItem) {
       
  1937         leavePara();
       
  1938         append(Atom::TableItemRight);
       
  1939         inTableItem = false;
       
  1940     }
       
  1941     if (inTableHeader) {
       
  1942         append(Atom::TableHeaderRight);
       
  1943         inTableHeader = false;
       
  1944     }
       
  1945     if (inTableRow) {
       
  1946         append(Atom::TableRowRight);
       
  1947         inTableRow = false;
       
  1948     }
       
  1949 }
       
  1950 
       
  1951 CodeMarker *DocParser::quoteFromFile()
       
  1952 {
       
  1953     return Doc::quoteFromFile(location(), quoter, getArgument());
       
  1954 }
       
  1955 
       
  1956 void DocParser::expandMacro(const QString &name,
       
  1957                             const QString &def,
       
  1958                             int numParams)
       
  1959 {
       
  1960     if (numParams == 0) {
       
  1961 	append(Atom::RawString, def);
       
  1962     }
       
  1963     else {
       
  1964 	QStringList args;
       
  1965 	QString rawString;
       
  1966 
       
  1967 	for (int i = 0; i < numParams; i++) {
       
  1968 	    if (numParams == 1 || isLeftBraceAhead()) {
       
  1969 		args << getArgument(true);
       
  1970 	    }
       
  1971             else {
       
  1972 		location().warning(tr("Macro '\\%1' invoked with too few"
       
  1973 				       " arguments (expected %2, got %3)")
       
  1974 				    .arg(name).arg(numParams).arg(i));
       
  1975 		break;
       
  1976 	    }
       
  1977 	}
       
  1978 
       
  1979 	int j = 0;
       
  1980 	while (j < def.size()) {
       
  1981 	    int paramNo;
       
  1982 	    if ((def[j] == '\\') && (j < def.size() - 1) &&
       
  1983                 ((paramNo = def[j + 1].digitValue()) >= 1) &&
       
  1984                 (paramNo <= numParams)) {
       
  1985 		if (!rawString.isEmpty()) {
       
  1986 		    append(Atom::RawString, rawString);
       
  1987 		    rawString = "";
       
  1988 		}
       
  1989 		append(Atom::String, args[paramNo - 1]);
       
  1990 		j += 2;
       
  1991 	    }
       
  1992             else {
       
  1993 		rawString += def[j++];
       
  1994 	    }
       
  1995 	}
       
  1996 	if (!rawString.isEmpty())
       
  1997 	    append(Atom::RawString, rawString);
       
  1998     }
       
  1999 }
       
  2000 
       
  2001 Doc::SectioningUnit DocParser::getSectioningUnit()
       
  2002 {
       
  2003     QString name = getOptionalArgument();
       
  2004 
       
  2005     if (name == "part") {
       
  2006 	return Doc::Part;
       
  2007     }
       
  2008     else if (name == "chapter") {
       
  2009 	return Doc::Chapter;
       
  2010     }
       
  2011     else if (name == "section1") {
       
  2012 	return Doc::Section1;
       
  2013     }
       
  2014     else if (name == "section2") {
       
  2015 	return Doc::Section2;
       
  2016     }
       
  2017     else if (name == "section3") {
       
  2018 	return Doc::Section3;
       
  2019     }
       
  2020     else if (name == "section4") {
       
  2021 	return Doc::Section4;
       
  2022     }
       
  2023     else if (name.isEmpty()) {
       
  2024 	return Doc::Section4;
       
  2025     }
       
  2026     else {
       
  2027 	location().warning(tr("Invalid sectioning unit '%1'").arg(name));
       
  2028 	return Doc::Book;
       
  2029     }
       
  2030 }
       
  2031 
       
  2032 QString DocParser::getArgument(bool verbatim)
       
  2033 {
       
  2034     QString arg;
       
  2035     int delimDepth = 0;
       
  2036 
       
  2037     skipSpacesOrOneEndl();
       
  2038 
       
  2039     int startPos = pos;
       
  2040 
       
  2041     /*
       
  2042       Typically, an argument ends at the next white-space. However,
       
  2043       braces can be used to group words:
       
  2044 
       
  2045 	  {a few words}
       
  2046 
       
  2047       Also, opening and closing parentheses have to match. Thus,
       
  2048 
       
  2049 	  printf("%d\n", x)
       
  2050 
       
  2051       is an argument too, although it contains spaces. Finally,
       
  2052       trailing punctuation is not included in an argument, nor is 's.
       
  2053     */
       
  2054     if (pos < (int) in.length() && in[pos] == '{') {
       
  2055 	pos++;
       
  2056 	while (pos < (int) in.length() && delimDepth >= 0) {
       
  2057 	    switch (in[pos].unicode()) {
       
  2058 	    case '{':
       
  2059 		delimDepth++;
       
  2060 		arg += "{";
       
  2061 		pos++;
       
  2062 		break;
       
  2063 	    case '}':
       
  2064 		delimDepth--;
       
  2065 		if (delimDepth >= 0)
       
  2066 		    arg += "}";
       
  2067 		pos++;
       
  2068 		break;
       
  2069 	    case '\\':
       
  2070 		if (verbatim) {
       
  2071 		    arg += in[pos];
       
  2072 		    pos++;
       
  2073 		}
       
  2074                 else {
       
  2075 		    pos++;
       
  2076 		    if (pos < (int) in.length()) {
       
  2077 			if (in[pos].isLetterOrNumber())
       
  2078 			    break;
       
  2079 			arg += in[pos];
       
  2080 			if (in[pos].isSpace()) {
       
  2081 			    skipAllSpaces();
       
  2082 			}
       
  2083                         else {
       
  2084 			    pos++;
       
  2085 			}
       
  2086 		    }
       
  2087 		}
       
  2088 		break;
       
  2089 	    default:
       
  2090 		arg += in[pos];
       
  2091 		pos++;
       
  2092 	    }
       
  2093 	}
       
  2094 	if (delimDepth > 0)
       
  2095 	    location().warning(tr("Missing '}'"));
       
  2096     }
       
  2097     else {
       
  2098 	while (pos < in.length() &&
       
  2099                ((delimDepth > 0) ||
       
  2100                 ((delimDepth == 0) &&
       
  2101                  !in[pos].isSpace()))) {
       
  2102 	    switch (in[pos].unicode()) {
       
  2103 	    case '(':
       
  2104 	    case '[':
       
  2105 	    case '{':
       
  2106 		delimDepth++;
       
  2107 		arg += in[pos];
       
  2108 		pos++;
       
  2109 		break;
       
  2110 	    case ')':
       
  2111 	    case ']':
       
  2112 	    case '}':
       
  2113 		delimDepth--;
       
  2114 		if (pos == startPos || delimDepth >= 0) {
       
  2115 		    arg += in[pos];
       
  2116 		    pos++;
       
  2117 		}
       
  2118 		break;
       
  2119 	    case '\\':
       
  2120 		if (verbatim) {
       
  2121 		    arg += in[pos];
       
  2122 		    pos++;
       
  2123 		}
       
  2124                 else {
       
  2125 		    pos++;
       
  2126 		    if (pos < (int) in.length()) {
       
  2127 			if (in[pos].isLetterOrNumber())
       
  2128 			    break;
       
  2129 			arg += in[pos];
       
  2130 			if (in[pos].isSpace()) {
       
  2131 			    skipAllSpaces();
       
  2132 			}
       
  2133                         else {
       
  2134 			    pos++;
       
  2135 			}
       
  2136 		    }
       
  2137 		}
       
  2138 		break;
       
  2139 	    default:
       
  2140 		arg += in[pos];
       
  2141 		pos++;
       
  2142 	    }
       
  2143 	}
       
  2144 	if ((arg.length() > 1) &&
       
  2145             (QString(".,:;!?").indexOf(in[pos - 1]) != -1) &&
       
  2146             !arg.endsWith("...")) {
       
  2147 	    arg.truncate(arg.length() - 1);
       
  2148 	    pos--;
       
  2149 	}
       
  2150 	if (arg.length() > 2 && in.mid(pos - 2, 2) == "'s") {
       
  2151 	    arg.truncate(arg.length() - 2);
       
  2152 	    pos -= 2;
       
  2153 	}
       
  2154     }
       
  2155     return arg.simplified();
       
  2156 }
       
  2157 
       
  2158 QString DocParser::getOptionalArgument()
       
  2159 {
       
  2160     skipSpacesOrOneEndl();
       
  2161     if (pos + 1 < (int) in.length() && in[pos] == '\\' &&
       
  2162 	 in[pos + 1].isLetterOrNumber()) {
       
  2163 	return "";
       
  2164     }
       
  2165     else {
       
  2166 	return getArgument();
       
  2167     }
       
  2168 }
       
  2169 
       
  2170 QString DocParser::getRestOfLine()
       
  2171 {
       
  2172     QString t;
       
  2173 
       
  2174     skipSpacesOnLine();
       
  2175 
       
  2176     bool trailingSlash = false;
       
  2177 
       
  2178     do {
       
  2179         int begin = pos;
       
  2180 
       
  2181         while (pos < in.size() && in[pos] != '\n') {
       
  2182             if (in[pos] == '\\' && !trailingSlash) {
       
  2183                 trailingSlash = true;
       
  2184                 ++pos;
       
  2185                 while ((pos < in.size()) &&
       
  2186                        in[pos].isSpace() &&
       
  2187                        (in[pos] != '\n'))
       
  2188                     ++pos;
       
  2189             }
       
  2190             else {
       
  2191                 trailingSlash = false;
       
  2192                 ++pos;
       
  2193             }
       
  2194         }
       
  2195 
       
  2196         if (!t.isEmpty())
       
  2197             t += " ";
       
  2198         t += in.mid(begin, pos - begin).simplified();
       
  2199 
       
  2200         if (trailingSlash) {
       
  2201             t.chop(1);
       
  2202             t = t.simplified();
       
  2203         }
       
  2204         if (pos < in.size())
       
  2205             ++pos;
       
  2206     } while (pos < in.size() && trailingSlash);
       
  2207 
       
  2208     return t;
       
  2209 }
       
  2210 
       
  2211 /*!
       
  2212   The metacommand argument is normally the remaining text to
       
  2213   the right of the metacommand itself. The extra blanks are
       
  2214   stripped and the argument string is returned.
       
  2215  */
       
  2216 QString DocParser::getMetaCommandArgument(const QString &cmdStr)
       
  2217 {
       
  2218     skipSpacesOnLine();
       
  2219 
       
  2220     int begin = pos;
       
  2221     int parenDepth = 0;
       
  2222 
       
  2223     while (pos < in.size() && (in[pos] != '\n' || parenDepth > 0)) {
       
  2224         if (in.at(pos) == '(')
       
  2225             ++parenDepth;
       
  2226         else if (in.at(pos) == ')')
       
  2227             --parenDepth;
       
  2228 
       
  2229 	++pos;
       
  2230     }
       
  2231     if (pos == in.size() && parenDepth > 0) {
       
  2232         pos = begin;
       
  2233         location().warning(tr("Unbalanced parentheses in '%1'").arg(cmdStr));
       
  2234     }
       
  2235 
       
  2236     QString t = in.mid(begin, pos - begin).simplified();
       
  2237     skipSpacesOnLine();
       
  2238     return t;
       
  2239 }
       
  2240 
       
  2241 QString DocParser::getUntilEnd(int cmd)
       
  2242 {
       
  2243     int endCmd = endCmdFor(cmd);
       
  2244     QRegExp rx("\\\\" + cmdName(endCmd) + "\\b");
       
  2245     QString t;
       
  2246     int end = rx.indexIn(in, pos);
       
  2247 
       
  2248     if (end == -1) {
       
  2249 	location().warning(tr("Missing '\\%1'").arg(cmdName(endCmd)));
       
  2250 	pos = in.length();
       
  2251     }
       
  2252     else {
       
  2253 	t = in.mid(pos, end - pos);
       
  2254 	pos = end + rx.matchedLength();
       
  2255     }
       
  2256     return t;
       
  2257 }
       
  2258 
       
  2259 QString DocParser::getCode(int cmd, CodeMarker *marker)
       
  2260 {
       
  2261     QString code = untabifyEtc(getUntilEnd(cmd));
       
  2262     int indent = indentLevel(code);
       
  2263     if (indent < minIndent)
       
  2264         minIndent = indent;
       
  2265     code = unindent(minIndent, code);
       
  2266     marker = CodeMarker::markerForCode(code);
       
  2267     return marker->markedUpCode(code, 0, "");
       
  2268 }
       
  2269 
       
  2270 /*!
       
  2271   Was used only for generating doxygen output.
       
  2272  */
       
  2273 QString DocParser::getUnmarkedCode(int cmd)
       
  2274 {
       
  2275     QString code = getUntilEnd(cmd);
       
  2276 #if 0    
       
  2277     int indent = indentLevel(code);
       
  2278     if (indent < minIndent)
       
  2279         minIndent = indent;
       
  2280     code = unindent(minIndent, code);
       
  2281 #endif    
       
  2282     return code;
       
  2283 }
       
  2284 
       
  2285 bool DocParser::isBlankLine()
       
  2286 {
       
  2287     int i = pos;
       
  2288 
       
  2289     while (i < len && in[i].isSpace()) {
       
  2290 	if (in[i] == '\n')
       
  2291 	    return true;
       
  2292 	i++;
       
  2293     }
       
  2294     return false;
       
  2295 }
       
  2296 
       
  2297 bool DocParser::isLeftBraceAhead()
       
  2298 {
       
  2299     int numEndl = 0;
       
  2300     int i = pos;
       
  2301 
       
  2302     while (i < len && in[i].isSpace() && numEndl < 2) {
       
  2303 	// ### bug with '\\'
       
  2304 	if (in[i] == '\n')
       
  2305 	    numEndl++;
       
  2306     	i++;
       
  2307     }
       
  2308     return numEndl < 2 && i < len && in[i] == '{';
       
  2309 }
       
  2310 
       
  2311 void DocParser::skipSpacesOnLine()
       
  2312 {
       
  2313     while ((pos < in.length()) &&
       
  2314            in[pos].isSpace() &&
       
  2315            (in[pos].unicode() != '\n'))
       
  2316 	++pos;
       
  2317 }
       
  2318 
       
  2319 void DocParser::skipSpacesOrOneEndl()
       
  2320 {
       
  2321     int firstEndl = -1;
       
  2322     while (pos < (int) in.length() && in[pos].isSpace()) {
       
  2323 	QChar ch = in[pos];
       
  2324 	if (ch == '\n') {
       
  2325 	    if (firstEndl == -1) {
       
  2326 		firstEndl = pos;
       
  2327 	    }
       
  2328             else {
       
  2329 		pos = firstEndl;
       
  2330 		break;
       
  2331 	    }
       
  2332 	}
       
  2333 	pos++;
       
  2334     }
       
  2335 }
       
  2336 
       
  2337 void DocParser::skipAllSpaces()
       
  2338 {
       
  2339     while (pos < len && in[pos].isSpace())
       
  2340 	pos++;
       
  2341 }
       
  2342 
       
  2343 void DocParser::skipToNextPreprocessorCommand()
       
  2344 {
       
  2345     QRegExp rx("\\\\(?:" + cmdName(CMD_IF) + "|" +
       
  2346                            cmdName(CMD_ELSE) + "|" +
       
  2347                            cmdName(CMD_ENDIF) + ")\\b");
       
  2348     int end = rx.indexIn(in, pos + 1); // ### + 1 necessary?
       
  2349 
       
  2350     if (end == -1)
       
  2351 	pos = in.length();
       
  2352     else
       
  2353 	pos = end;
       
  2354 }
       
  2355 
       
  2356 int DocParser::endCmdFor(int cmd)
       
  2357 {
       
  2358     switch (cmd) {
       
  2359     case CMD_ABSTRACT:
       
  2360 	return CMD_ENDABSTRACT;
       
  2361     case CMD_BADCODE:
       
  2362 	return CMD_ENDCODE;
       
  2363     case CMD_CHAPTER:
       
  2364 	return CMD_ENDCHAPTER;
       
  2365     case CMD_CODE:
       
  2366 	return CMD_ENDCODE;
       
  2367 #ifdef QDOC_QML        
       
  2368     case CMD_QML:
       
  2369 	return CMD_ENDQML;
       
  2370     case CMD_QMLTEXT:
       
  2371 	return CMD_ENDQMLTEXT;
       
  2372 #endif        
       
  2373     case CMD_FOOTNOTE:
       
  2374 	return CMD_ENDFOOTNOTE;
       
  2375     case CMD_LEGALESE:
       
  2376         return CMD_ENDLEGALESE;
       
  2377     case CMD_LINK:
       
  2378         return CMD_ENDLINK;
       
  2379     case CMD_LIST:
       
  2380 	return CMD_ENDLIST;
       
  2381     case CMD_NEWCODE:
       
  2382         return CMD_ENDCODE;
       
  2383     case CMD_OLDCODE:
       
  2384         return CMD_NEWCODE;
       
  2385     case CMD_OMIT:
       
  2386 	return CMD_ENDOMIT;
       
  2387     case CMD_PART:
       
  2388 	return CMD_ENDPART;
       
  2389     case CMD_QUOTATION:
       
  2390 	return CMD_ENDQUOTATION;
       
  2391     case CMD_RAW:
       
  2392         return CMD_ENDRAW;
       
  2393     case CMD_SECTION1:
       
  2394 	return CMD_ENDSECTION1;
       
  2395     case CMD_SECTION2:
       
  2396 	return CMD_ENDSECTION2;
       
  2397     case CMD_SECTION3:
       
  2398 	return CMD_ENDSECTION3;
       
  2399     case CMD_SECTION4:
       
  2400 	return CMD_ENDSECTION4;
       
  2401     case CMD_SIDEBAR:
       
  2402 	return CMD_ENDSIDEBAR;
       
  2403     case CMD_TABLE:
       
  2404 	return CMD_ENDTABLE;
       
  2405     default:
       
  2406 	return cmd;
       
  2407     }
       
  2408 }
       
  2409 
       
  2410 QString DocParser::cmdName(int cmd)
       
  2411 {
       
  2412     return *cmds[cmd].alias;
       
  2413 }
       
  2414 
       
  2415 QString DocParser::endCmdName(int cmd)
       
  2416 {
       
  2417     return cmdName(endCmdFor(cmd));
       
  2418 }
       
  2419 
       
  2420 QString DocParser::untabifyEtc(const QString& str)
       
  2421 {
       
  2422     QString result;
       
  2423     result.reserve(str.length());
       
  2424     int column = 0;
       
  2425 
       
  2426     for (int i = 0; i < str.length(); i++) {
       
  2427         const QChar c = str.at(i);
       
  2428         if (c == QLatin1Char('\r'))
       
  2429             continue;
       
  2430         if (c == QLatin1Char('\t')) {
       
  2431             result += "        " + (column % tabSize);
       
  2432             column = ((column / tabSize) + 1) * tabSize;
       
  2433             continue;
       
  2434         }
       
  2435         if (c == QLatin1Char('\n')) {
       
  2436             while (result.endsWith(QLatin1Char(' ')))
       
  2437                 result.chop(1);
       
  2438             result += c;
       
  2439             column = 0;
       
  2440             continue;
       
  2441         }
       
  2442         result += c;
       
  2443         column++;
       
  2444     }
       
  2445 
       
  2446     while (result.endsWith("\n\n"))
       
  2447         result.truncate(result.length() - 1);
       
  2448     while (result.startsWith("\n"))
       
  2449         result = result.mid(1);
       
  2450    
       
  2451     return result;
       
  2452 }
       
  2453 
       
  2454 int DocParser::indentLevel(const QString& str)
       
  2455 {
       
  2456     int minIndent = INT_MAX;
       
  2457     int column = 0;
       
  2458 
       
  2459     for (int i = 0; i < (int) str.length(); i++) {
       
  2460 	if (str[i] == '\n') {
       
  2461 	    column = 0;
       
  2462 	}
       
  2463         else {
       
  2464 	    if (str[i] != ' ' && column < minIndent)
       
  2465 		minIndent = column;
       
  2466 	    column++;
       
  2467 	}
       
  2468     }
       
  2469     return minIndent;
       
  2470 }
       
  2471 
       
  2472 QString DocParser::unindent(int level, const QString& str)
       
  2473 {
       
  2474     if (level == 0)
       
  2475 	return str;
       
  2476 
       
  2477     QString t;
       
  2478     int column = 0;
       
  2479 
       
  2480     for (int i = 0; i < (int) str.length(); i++) {
       
  2481         if (str[i] == QLatin1Char('\n')) {
       
  2482 	    t += '\n';
       
  2483 	    column = 0;
       
  2484 	}
       
  2485         else {
       
  2486 	    if (column >= level)
       
  2487 		t += str[i];
       
  2488 	    column++;
       
  2489 	}
       
  2490     }
       
  2491     return t;
       
  2492 }
       
  2493 
       
  2494 QString DocParser::slashed(const QString& str)
       
  2495 {
       
  2496     QString result = str;
       
  2497     result.replace("/", "\\/");
       
  2498     return "/" + result + "/";
       
  2499 }
       
  2500 
       
  2501 #define COMMAND_BRIEF                   Doc::alias("brief")
       
  2502 
       
  2503 #ifdef QDOC_QML
       
  2504 #define COMMAND_QMLBRIEF                Doc::alias("qmlbrief")
       
  2505 #endif
       
  2506 
       
  2507 
       
  2508 Doc::Doc(const Location& start_loc,
       
  2509          const Location& end_loc,
       
  2510          const QString& source,
       
  2511          const QSet<QString>& metaCommandSet)
       
  2512 {
       
  2513     priv = new DocPrivate(start_loc,end_loc,source);
       
  2514     DocParser parser;
       
  2515     parser.parse(source,priv,metaCommandSet);
       
  2516 }
       
  2517 
       
  2518 Doc::Doc(const Doc& doc)
       
  2519     : priv(0)
       
  2520 {
       
  2521     operator=(doc);
       
  2522 }
       
  2523 
       
  2524 Doc::~Doc()
       
  2525 {
       
  2526     if (priv && priv->deref())
       
  2527 	delete priv;
       
  2528 }
       
  2529 
       
  2530 Doc &Doc::operator=(const Doc& doc)
       
  2531 {
       
  2532     if (doc.priv)
       
  2533 	doc.priv->ref();
       
  2534     if (priv && priv->deref())
       
  2535 	delete priv;
       
  2536     priv = doc.priv;
       
  2537     return *this;
       
  2538 }
       
  2539 
       
  2540 void Doc::renameParameters(const QStringList &oldNames,
       
  2541                            const QStringList &newNames)
       
  2542 {
       
  2543     if (priv && oldNames != newNames) {
       
  2544         detach();
       
  2545 
       
  2546         priv->params = newNames.toSet();
       
  2547 
       
  2548         Atom *atom = priv->text.firstAtom();
       
  2549         while (atom) {
       
  2550             if (atom->type() == Atom::FormattingLeft
       
  2551                     && atom->string() == ATOM_FORMATTING_PARAMETER) {
       
  2552                 atom = atom->next();
       
  2553                 if (!atom)
       
  2554                     return;
       
  2555                 int index = oldNames.indexOf(atom->string());
       
  2556                 if (index != -1 && index < newNames.count())
       
  2557                     atom->setString(newNames.at(index));
       
  2558             }
       
  2559             atom = atom->next();
       
  2560         }
       
  2561     }
       
  2562 }
       
  2563 
       
  2564 void Doc::simplifyEnumDoc()
       
  2565 {
       
  2566     if (priv) {
       
  2567         if (priv->isEnumDocSimplifiable()) {
       
  2568             detach();
       
  2569 
       
  2570             Text newText;
       
  2571 
       
  2572             Atom *atom = priv->text.firstAtom();
       
  2573             while (atom) {
       
  2574                 if ((atom->type() == Atom::ListLeft) &&
       
  2575                     (atom->string() == ATOM_LIST_VALUE)) {
       
  2576                     while (atom && ((atom->type() != Atom::ListRight) ||
       
  2577                                     (atom->string() != ATOM_LIST_VALUE)))
       
  2578                         atom = atom->next();
       
  2579                     if (atom)
       
  2580                         atom = atom->next();
       
  2581                 }
       
  2582                 else {
       
  2583                     newText << *atom;
       
  2584                     atom = atom->next();
       
  2585                 }
       
  2586             }
       
  2587             priv->text = newText;
       
  2588         }
       
  2589     }
       
  2590 }
       
  2591 
       
  2592 void Doc::setBody(const Text &text)
       
  2593 {
       
  2594     detach();
       
  2595     priv->text = text;
       
  2596 }
       
  2597 
       
  2598 /*!
       
  2599   Returns the starting location of a qdoc comment.
       
  2600  */
       
  2601 const Location &Doc::location() const
       
  2602 {
       
  2603     static const Location dummy;
       
  2604     return priv == 0 ? dummy : priv->start_loc;
       
  2605 }
       
  2606 
       
  2607 const QString &Doc::source() const
       
  2608 {
       
  2609     static QString null;
       
  2610     return priv == 0 ? null : priv->src;
       
  2611 }
       
  2612 
       
  2613 bool Doc::isEmpty() const
       
  2614 {
       
  2615     return priv == 0 || priv->src.isEmpty();
       
  2616 }
       
  2617 
       
  2618 const Text& Doc::body() const
       
  2619 {
       
  2620     static const Text dummy;
       
  2621     return priv == 0 ? dummy : priv->text;
       
  2622 }
       
  2623 
       
  2624 Text Doc::briefText() const
       
  2625 {
       
  2626     return body().subText(Atom::BriefLeft, Atom::BriefRight);
       
  2627 }
       
  2628 
       
  2629 Text Doc::trimmedBriefText(const QString &className) const
       
  2630 {
       
  2631     QString classNameOnly = className;
       
  2632     if (className.contains("::"))
       
  2633         classNameOnly = className.split("::").last();
       
  2634 
       
  2635     Text originalText = briefText();
       
  2636     Text resultText;
       
  2637     const Atom *atom = originalText.firstAtom();
       
  2638     if (atom) {
       
  2639         QString briefStr;
       
  2640         QString whats;
       
  2641         bool standardWording = true;
       
  2642 
       
  2643         /*
       
  2644           This code is really ugly. The entire \brief business
       
  2645           should be rethought.
       
  2646         */
       
  2647         while (atom) {
       
  2648             if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) {
       
  2649                 briefStr += atom->string();
       
  2650             }
       
  2651             atom = atom->next();
       
  2652         }
       
  2653 
       
  2654         QStringList w = briefStr.split(" ");
       
  2655         if (!w.isEmpty() && w.first() == "Returns") {
       
  2656         }
       
  2657         else {
       
  2658             if (!w.isEmpty() && w.first() == "The")
       
  2659                 w.removeFirst();
       
  2660             else {
       
  2661                 location().warning(
       
  2662                   tr("Nonstandard wording in '\\%1' text for '%2' (expected 'The')")
       
  2663                   .arg(COMMAND_BRIEF).arg(className));
       
  2664                 standardWording = false;
       
  2665             }
       
  2666 
       
  2667             if (!w.isEmpty() && (w.first() == className || w.first() == classNameOnly))
       
  2668                 w.removeFirst();
       
  2669             else {
       
  2670                 location().warning(
       
  2671                   tr("Nonstandard wording in '\\%1' text for '%2' (expected '%3')")
       
  2672                   .arg(COMMAND_BRIEF).arg(className).arg(className));
       
  2673                 standardWording = false;
       
  2674             }
       
  2675 
       
  2676             if (!w.isEmpty() && ((w.first() == "class") ||
       
  2677                                  (w.first() == "function") ||
       
  2678                                  (w.first() == "macro") ||
       
  2679                                  (w.first() == "widget") ||
       
  2680                                  (w.first() == "namespace") ||
       
  2681                                  (w.first() == "header")))
       
  2682                 w.removeFirst();
       
  2683             else {
       
  2684                 location().warning(
       
  2685                   tr("Nonstandard wording in '\\%1' text for '%2' ("
       
  2686                      "expected 'class', 'function', 'macro', 'widget', "
       
  2687                      "'namespace' or 'header')")
       
  2688                   .arg(COMMAND_BRIEF).arg(className));
       
  2689                 standardWording = false;
       
  2690             }
       
  2691 
       
  2692             if (!w.isEmpty() && (w.first() == "is" || w.first() == "provides"))
       
  2693                 w.removeFirst();
       
  2694 
       
  2695             if (!w.isEmpty() && (w.first() == "a" || w.first() == "an"))
       
  2696                 w.removeFirst();
       
  2697         }
       
  2698 
       
  2699         whats = w.join(" ");
       
  2700         
       
  2701         if (whats.endsWith("."))
       
  2702 	    whats.truncate(whats.length() - 1);
       
  2703 
       
  2704         if (whats.isEmpty()) {
       
  2705 	    location().warning(
       
  2706                 tr("Nonstandard wording in '\\%1' text for '%2' (expected more text)")
       
  2707                 .arg(COMMAND_BRIEF).arg(className));
       
  2708 	    standardWording = false;
       
  2709         }
       
  2710         else
       
  2711 	    whats[0] = whats[0].toUpper();
       
  2712 
       
  2713 	// ### move this once \brief is abolished for properties
       
  2714         if (standardWording)
       
  2715             resultText << whats;
       
  2716     }
       
  2717     return resultText;
       
  2718 }
       
  2719 
       
  2720 Text Doc::legaleseText() const
       
  2721 {
       
  2722     if (priv == 0 || !priv->hasLegalese)
       
  2723 	return Text();
       
  2724     else
       
  2725 	return body().subText(Atom::LegaleseLeft, Atom::LegaleseRight);
       
  2726 }
       
  2727 
       
  2728 const QString& Doc::baseName() const
       
  2729 {
       
  2730     static QString null;
       
  2731     if (priv == 0 || priv->extra == 0) {
       
  2732 	return null;
       
  2733     }
       
  2734     else {
       
  2735 	return priv->extra->baseName;
       
  2736     }
       
  2737 }
       
  2738 
       
  2739 Doc::SectioningUnit Doc::granularity() const
       
  2740 {
       
  2741     if (priv == 0 || priv->extra == 0) {
       
  2742 	return DocPrivateExtra().granularity;
       
  2743     }
       
  2744     else {
       
  2745 	return priv->extra->granularity;
       
  2746     }
       
  2747 }
       
  2748 
       
  2749 #if notyet // ###
       
  2750 Doc::SectioningUnit Doc::sectioningUnit() const
       
  2751 {
       
  2752     if (priv == 0 || priv->extra == 0) {
       
  2753 	return DocPrivateExtra().sectioningUnit;
       
  2754     }
       
  2755     else {
       
  2756 	return priv->extra->sectioningUnit;
       
  2757     }
       
  2758 }
       
  2759 #endif
       
  2760 
       
  2761 const QSet<QString> &Doc::parameterNames() const
       
  2762 {
       
  2763     return priv == 0 ? *null_Set_QString() : priv->params;
       
  2764 }
       
  2765 
       
  2766 const QStringList &Doc::enumItemNames() const
       
  2767 {
       
  2768     return priv == 0 ? *null_QStringList() : priv->enumItemList;
       
  2769 }
       
  2770 
       
  2771 const QStringList &Doc::omitEnumItemNames() const
       
  2772 {
       
  2773     return priv == 0 ? *null_QStringList() : priv->omitEnumItemList;
       
  2774 }
       
  2775 
       
  2776 const QSet<QString> &Doc::metaCommandsUsed() const
       
  2777 {
       
  2778     return priv == 0 ? *null_Set_QString() : priv->metacommandsUsed;
       
  2779 }
       
  2780 
       
  2781 QStringList Doc::metaCommandArgs(const QString& metacommand) const
       
  2782 {
       
  2783     return priv == 0 ? QStringList() : priv->metaCommandMap.value(metacommand);
       
  2784 }
       
  2785 
       
  2786 const QList<Text> &Doc::alsoList() const
       
  2787 {
       
  2788     return priv == 0 ? *null_QList_Text() : priv->alsoList;
       
  2789 }
       
  2790 
       
  2791 bool Doc::hasTableOfContents() const
       
  2792 {
       
  2793     return priv && priv->extra && !priv->extra->tableOfContents.isEmpty();
       
  2794 }
       
  2795 
       
  2796 bool Doc::hasKeywords() const
       
  2797 {
       
  2798     return priv && priv->extra && !priv->extra->keywords.isEmpty();
       
  2799 }
       
  2800 
       
  2801 bool Doc::hasTargets() const
       
  2802 {
       
  2803     return priv && priv->extra && !priv->extra->targets.isEmpty();
       
  2804 }
       
  2805 
       
  2806 const QList<Atom *> &Doc::tableOfContents() const
       
  2807 {
       
  2808     priv->constructExtra();
       
  2809     return priv->extra->tableOfContents;
       
  2810 }
       
  2811 
       
  2812 const QList<int> &Doc::tableOfContentsLevels() const
       
  2813 {
       
  2814     priv->constructExtra();
       
  2815     return priv->extra->tableOfContentsLevels;
       
  2816 }
       
  2817 
       
  2818 const QList<Atom *> &Doc::keywords() const
       
  2819 {
       
  2820     priv->constructExtra();
       
  2821     return priv->extra->keywords;
       
  2822 }
       
  2823 
       
  2824 const QList<Atom *> &Doc::targets() const
       
  2825 {
       
  2826     priv->constructExtra();
       
  2827     return priv->extra->targets;
       
  2828 }
       
  2829 
       
  2830 const QStringMap &Doc::metaTagMap() const
       
  2831 {
       
  2832     return priv && priv->extra ? priv->extra->metaMap : *null_QStringMap();
       
  2833 }
       
  2834 
       
  2835 void Doc::initialize(const Config& config)
       
  2836 {
       
  2837     DocParser::tabSize = config.getInt(CONFIG_TABSIZE);
       
  2838     DocParser::exampleFiles = config.getStringList(CONFIG_EXAMPLES);
       
  2839     DocParser::exampleDirs = config.getStringList(CONFIG_EXAMPLEDIRS);
       
  2840     DocParser::sourceFiles = config.getStringList(CONFIG_SOURCES);
       
  2841     DocParser::sourceDirs = config.getStringList(CONFIG_SOURCEDIRS);
       
  2842     DocParser::quoting = config.getBool(CONFIG_QUOTINGINFORMATION);
       
  2843 
       
  2844     QStringMap reverseAliasMap;
       
  2845 
       
  2846     QSet<QString> commands = config.subVars(CONFIG_ALIAS);
       
  2847     QSet<QString>::ConstIterator c = commands.begin();
       
  2848     while (c != commands.end()) {
       
  2849 	QString alias = config.getString(CONFIG_ALIAS + Config::dot + *c);
       
  2850 	if (reverseAliasMap.contains(alias)) {
       
  2851 	    config.lastLocation().warning(tr("Command name '\\%1' cannot stand"
       
  2852 					      " for both '\\%2' and '\\%3'")
       
  2853 					   .arg(alias)
       
  2854 					   .arg(reverseAliasMap[alias])
       
  2855 					   .arg(*c));
       
  2856 	}
       
  2857         else {
       
  2858 	    reverseAliasMap.insert(alias, *c);
       
  2859 	}
       
  2860 	aliasMap()->insert(*c, alias);
       
  2861 	++c;
       
  2862     }
       
  2863 
       
  2864     int i = 0;
       
  2865     while (cmds[i].english) {
       
  2866 	cmds[i].alias = new QString(alias(cmds[i].english));
       
  2867 	cmdHash()->insert(*cmds[i].alias, cmds[i].no);
       
  2868 
       
  2869 	if (cmds[i].no != i)
       
  2870 	    Location::internalError(tr("command %1 missing").arg(i));
       
  2871 	i++;
       
  2872     }
       
  2873 
       
  2874     QSet<QString> macroNames = config.subVars(CONFIG_MACRO);
       
  2875     QSet<QString>::ConstIterator n = macroNames.begin();
       
  2876     while (n != macroNames.end()) {
       
  2877 	QString macroDotName = CONFIG_MACRO + Config::dot + *n;
       
  2878 	Macro macro;
       
  2879 	macro.numParams = -1;
       
  2880 	macro.defaultDef = config.getString(macroDotName);
       
  2881 	if (!macro.defaultDef.isEmpty()) {
       
  2882 	    macro.defaultDefLocation = config.lastLocation();
       
  2883 	    macro.numParams = Config::numParams(macro.defaultDef);
       
  2884 	}
       
  2885 	bool silent = false;
       
  2886 
       
  2887 	QSet<QString> formats = config.subVars(macroDotName);
       
  2888 	QSet<QString>::ConstIterator f = formats.begin();
       
  2889 	while (f != formats.end()) {
       
  2890 	    QString def = config.getString(macroDotName + Config::dot + *f);
       
  2891 	    if (!def.isEmpty()) {
       
  2892 		macro.otherDefs.insert(*f, def);
       
  2893 		int m = Config::numParams(macro.defaultDef);
       
  2894 		if (macro.numParams == -1) {
       
  2895 		    macro.numParams = m;
       
  2896 		}
       
  2897                 else if (macro.numParams != m) {
       
  2898 		    if (!silent) {
       
  2899 			QString other = tr("default");
       
  2900 			if (macro.defaultDef.isEmpty())
       
  2901 			    other = macro.otherDefs.begin().key();
       
  2902 			config.lastLocation().warning(tr("Macro '\\%1' takes"
       
  2903 							  " inconsistent number"
       
  2904 							  " of arguments (%2"
       
  2905 							  " %3, %4 %5)")
       
  2906 						       .arg(*n)
       
  2907 						       .arg(*f)
       
  2908 						       .arg(m)
       
  2909 						       .arg(other)
       
  2910 						       .arg(macro.numParams));
       
  2911 			silent = true;
       
  2912 		    }
       
  2913 		    if (macro.numParams < m)
       
  2914 			macro.numParams = m;
       
  2915 		}
       
  2916 	    }
       
  2917 	    ++f;
       
  2918 	}
       
  2919 
       
  2920 	if (macro.numParams != -1)
       
  2921 	    macroHash()->insert(*n, macro);
       
  2922 	++n;
       
  2923     }
       
  2924 }
       
  2925 
       
  2926 void Doc::terminate()
       
  2927 {
       
  2928     DocParser::exampleFiles.clear();
       
  2929     DocParser::exampleDirs.clear();
       
  2930     DocParser::sourceFiles.clear();
       
  2931     DocParser::sourceDirs.clear();
       
  2932     aliasMap()->clear();
       
  2933     cmdHash()->clear();
       
  2934     macroHash()->clear();
       
  2935 
       
  2936     int i = 0;
       
  2937     while (cmds[i].english) {
       
  2938         delete cmds[i].alias;
       
  2939         cmds[i].alias = 0;
       
  2940         ++i;
       
  2941     }
       
  2942 }
       
  2943 
       
  2944 QString Doc::alias(const QString &english)
       
  2945 {
       
  2946     return aliasMap()->value(english, english);
       
  2947 }
       
  2948 
       
  2949 /*!
       
  2950   Trims the deadwood out of \a str. i.e., this function
       
  2951   cleans up \a str.
       
  2952  */
       
  2953 void Doc::trimCStyleComment(Location& location, QString& str)
       
  2954 {
       
  2955     QString cleaned;
       
  2956     Location m = location;
       
  2957     bool metAsterColumn = true;
       
  2958     int asterColumn = location.columnNo() + 1;
       
  2959     int i;
       
  2960 
       
  2961     for (i = 0; i < (int) str.length(); i++) {
       
  2962 	if (m.columnNo() == asterColumn) {
       
  2963 	    if (str[i] != '*')
       
  2964 		break;
       
  2965 	    cleaned += ' ';
       
  2966 	    metAsterColumn = true;
       
  2967 	}
       
  2968         else {
       
  2969 	    if (str[i] == '\n') {
       
  2970 		if (!metAsterColumn)
       
  2971 		    break;
       
  2972 		metAsterColumn = false;
       
  2973 	    }
       
  2974 	    cleaned += str[i];
       
  2975 	}
       
  2976 	m.advance(str[i]);
       
  2977     }
       
  2978     if (cleaned.length() == str.length())
       
  2979 	str = cleaned;
       
  2980 
       
  2981     for (int i = 0; i < 3; i++)
       
  2982 	location.advance(str[i]);
       
  2983     str = str.mid(3, str.length() - 5);
       
  2984 }
       
  2985 
       
  2986 CodeMarker *Doc::quoteFromFile(const Location &location,
       
  2987                                Quoter &quoter,
       
  2988                                const QString &fileName)
       
  2989 {
       
  2990     quoter.reset();
       
  2991 
       
  2992     QString code;
       
  2993 
       
  2994     QString userFriendlyFilePath;
       
  2995     QString filePath = Config::findFile(location,
       
  2996                                         DocParser::exampleFiles,
       
  2997                                         DocParser::exampleDirs,
       
  2998 					fileName, userFriendlyFilePath);
       
  2999     if (filePath.isEmpty()) {
       
  3000 	location.warning(tr("Cannot find example file '%1'").arg(fileName));
       
  3001     }
       
  3002     else {
       
  3003 	QFile inFile(filePath);
       
  3004 	if (!inFile.open(QFile::ReadOnly)) {
       
  3005 	    location.warning(tr("Cannot open example file '%1'").arg(userFriendlyFilePath));
       
  3006 	}
       
  3007         else {
       
  3008 	    QTextStream inStream(&inFile);
       
  3009 	    code = DocParser::untabifyEtc(inStream.readAll());
       
  3010 	}
       
  3011     }
       
  3012 
       
  3013     QString dirPath = QFileInfo(filePath).path();
       
  3014     CodeMarker *marker = CodeMarker::markerForFileName(fileName);
       
  3015     quoter.quoteFromFile(userFriendlyFilePath,
       
  3016                          code,
       
  3017                          marker->markedUpCode(code, 0, dirPath));
       
  3018     return marker;
       
  3019 }
       
  3020 
       
  3021 QString Doc::canonicalTitle(const QString &title)
       
  3022 {
       
  3023     // The code below is equivalent to the following chunk, but _much_
       
  3024     // faster (accounts for ~10% of total running time)
       
  3025     //
       
  3026     //  QRegExp attributeExpr("[^A-Za-z0-9]+");
       
  3027     //  QString result = title.toLower();
       
  3028     //  result.replace(attributeExpr, " ");
       
  3029     //  result = result.simplified();
       
  3030     //  result.replace(QLatin1Char(' '), QLatin1Char('-'));
       
  3031 
       
  3032     QString result;
       
  3033     result.reserve(title.size());
       
  3034 
       
  3035     bool slurping = false;
       
  3036     bool begun = false;
       
  3037     int lastAlnum = 0;
       
  3038     for (int i = 0; i != title.size(); ++i) {
       
  3039         uint c = title.at(i).unicode();
       
  3040         if (c >= 'A' && c <= 'Z')
       
  3041             c -= 'A' - 'a';
       
  3042         bool alnum = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
       
  3043         if (alnum) {
       
  3044             result += QLatin1Char(c);
       
  3045             begun = true;
       
  3046             slurping = false;
       
  3047             lastAlnum = result.size();
       
  3048         }
       
  3049         else if (!slurping) {
       
  3050             if (begun)
       
  3051                 result += QLatin1Char('-');
       
  3052             slurping = true;
       
  3053         }
       
  3054         else {
       
  3055             // !alnum && slurping -> nothin
       
  3056         }
       
  3057     }
       
  3058     result.truncate(lastAlnum);
       
  3059     return result;
       
  3060 }
       
  3061 
       
  3062 void Doc::detach()
       
  3063 {
       
  3064     if (!priv) {
       
  3065         priv = new DocPrivate;
       
  3066         return;
       
  3067     }
       
  3068     if (priv->count == 1)
       
  3069         return;
       
  3070 
       
  3071     --priv->count;
       
  3072 
       
  3073     DocPrivate *newPriv = new DocPrivate(*priv);
       
  3074     newPriv->count = 1;
       
  3075     if (priv->extra)
       
  3076         newPriv->extra = new DocPrivateExtra(*priv->extra);
       
  3077 
       
  3078     priv = newPriv;
       
  3079 }
       
  3080 
       
  3081 QT_END_NAMESPACE