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