]> git.lyx.org Git - lyx.git/blob - src/TextClass.cpp
DocBook: properly escape PI in InsetMarginal.
[lyx.git] / src / TextClass.cpp
1 /**
2  * \file TextClass.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Jean-Marc Lasgouttes
8  * \author Angus Leeming
9  * \author John Levon
10  * \author André Pönitz
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 #include <config.h>
16
17 #include "TextClass.h"
18
19 #include "LayoutFile.h"
20 #include "CiteEnginesList.h"
21 #include "Color.h"
22 #include "Counters.h"
23 #include "Floating.h"
24 #include "FloatList.h"
25 #include "Layout.h"
26 #include "Lexer.h"
27 #include "Font.h"
28 #include "ModuleList.h"
29
30 #include "frontends/alert.h"
31
32 #include "support/lassert.h"
33 #include "support/debug.h"
34 #include "support/ExceptionMessage.h"
35 #include "support/FileName.h"
36 #include "support/filetools.h"
37 #include "support/gettext.h"
38 #include "support/lstrings.h"
39 #include "support/os.h"
40 #include "support/TempFile.h"
41
42 #include <algorithm>
43 #include <fstream>
44 #include <sstream>
45
46 #ifdef ERROR
47 #undef ERROR
48 #endif
49
50 using namespace std;
51 using namespace lyx::support;
52
53 namespace lyx {
54
55 // Keep the changes documented in the Customization manual.
56 //
57 // If you change this format, then you MUST also make sure that
58 // your changes do not invalidate the hardcoded layout file in
59 // LayoutFile.cpp. Additions will never do so, but syntax changes
60 // could. See LayoutFileList::addEmptyClass() and, especially, the
61 // definition of the layoutpost string.
62 // You should also run the development/tools/updatelayouts.py script,
63 // to update the format of all of our layout files.
64 //
65 int const LAYOUT_FORMAT = 82; // dourouc05: DocBook additions.
66
67
68 // Layout format for the current lyx file format. Controls which format is
69 // targeted by Local Layout > Convert. In master, equal to LAYOUT_FORMAT.
70 int const LYXFILE_LAYOUT_FORMAT = LAYOUT_FORMAT;
71
72
73 namespace {
74
75 bool layout2layout(FileName const & filename, FileName const & tempfile,
76                    int const format = LAYOUT_FORMAT)
77 {
78         FileName const script = libFileSearch("scripts", "layout2layout.py");
79         if (script.empty()) {
80                 LYXERR0("Could not find layout conversion "
81                         "script layout2layout.py.");
82                 return false;
83         }
84
85         ostringstream command;
86         command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
87                 << " -t " << format
88                 << ' ' << quoteName(filename.toFilesystemEncoding())
89                 << ' ' << quoteName(tempfile.toFilesystemEncoding());
90         string const command_str = command.str();
91
92         LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
93
94         cmd_ret const ret = runCommand(command_str);
95         if (ret.first != 0) {
96                 if (format == LAYOUT_FORMAT)
97                         LYXERR0("Conversion of layout with layout2layout.py has failed.");
98                 return false;
99         }
100         return true;
101 }
102
103
104 string translateReadType(TextClass::ReadType rt)
105 {
106         switch (rt) {
107         case TextClass::BASECLASS:
108                 return "textclass";
109         case TextClass::MERGE:
110                 return "input file";
111         case TextClass::MODULE:
112                 return "module file";
113         case TextClass::CITE_ENGINE:
114                 return "cite engine";
115         case TextClass::VALIDATION:
116                 return "validation";
117         }
118         // shutup warning
119         return string();
120 }
121
122 } // namespace
123
124
125 // This string should not be translated here,
126 // because it is a layout identifier.
127 docstring const TextClass::plain_layout_ = from_ascii(N_("Plain Layout"));
128
129
130 /////////////////////////////////////////////////////////////////////////
131 //
132 // TextClass
133 //
134 /////////////////////////////////////////////////////////////////////////
135
136 TextClass::TextClass()
137         : loaded_(false), tex_class_avail_(false),
138           opt_enginetype_("authoryear|numerical"), opt_fontsize_("10|11|12"),
139           opt_pagesize_("default|a4|a5|b5|letter|legal|executive"),
140           opt_pagestyle_("empty|plain|headings|fancy"), fontsize_format_("$$spt"), pagesize_("default"),
141           pagesize_format_("$$spaper"), pagestyle_("default"), tablestyle_("default"),
142           docbookroot_("article"), docbookforceabstract_(false),
143           columns_(1), sides_(OneSide), secnumdepth_(3), tocdepth_(3), outputType_(LATEX),
144           outputFormat_("latex"), has_output_format_(false), defaultfont_(sane_font), 
145           titletype_(TITLE_COMMAND_AFTER), titlename_("maketitle"),
146           min_toclevel_(0), max_toclevel_(0), maxcitenames_(2),
147           cite_full_author_list_(true), bibintoc_(false) {
148 }
149
150
151 bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
152 {
153         LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
154         if (!lay.read(lexrc, *this)) {
155                 LYXERR0("Error parsing style `" << to_utf8(lay.name()) << '\'');
156                 return false;
157         }
158         // Resolve fonts
159         lay.resfont = lay.font;
160         lay.resfont.realize(defaultfont_);
161         lay.reslabelfont = lay.labelfont;
162         lay.reslabelfont.realize(defaultfont_);
163         return true; // no errors
164 }
165
166
167 enum TextClassTags {
168         TC_OUTPUTTYPE = 1,
169         TC_OUTPUTFORMAT,
170         TC_INPUT,
171         TC_STYLE,
172         TC_MODIFYSTYLE,
173         TC_PROVIDESTYLE,
174         TC_DEFAULTSTYLE,
175         TC_INSETLAYOUT,
176         TC_NOINSETLAYOUT,
177         TC_NOSTYLE,
178         TC_COLUMNS,
179         TC_SIDES,
180         TC_PAGESTYLE,
181         TC_PAGESIZE,
182         TC_DEFAULTFONT,
183         TC_SECNUMDEPTH,
184         TC_TOCDEPTH,
185         TC_CLASSOPTIONS,
186         TC_PREAMBLE,
187         TC_HTMLPREAMBLE,
188         TC_HTMLSTYLES,
189         TC_PROVIDES,
190         TC_REQUIRES,
191         TC_PKGOPTS,
192         TC_LEFTMARGIN,
193         TC_RIGHTMARGIN,
194         TC_FLOAT,
195         TC_COUNTER,
196         TC_NOCOUNTER,
197         TC_IFCOUNTER,
198         TC_NOFLOAT,
199         TC_TITLELATEXNAME,
200         TC_TITLELATEXTYPE,
201         TC_FORMAT,
202         TC_ADDTOPREAMBLE,
203         TC_ADDTOHTMLPREAMBLE,
204         TC_ADDTOHTMLSTYLES,
205         TC_DEFAULTMODULE,
206         TC_PROVIDESMODULE,
207         TC_EXCLUDESMODULE,
208         TC_HTMLTOCSECTION,
209         TC_CITEENGINE,
210         TC_ADDTOCITEENGINE,
211         TC_CITEENGINETYPE,
212         TC_CITEFORMAT,
213         TC_CITEFRAMEWORK,
214         TC_MAXCITENAMES,
215         TC_DEFAULTBIBLIO,
216         TC_FULLAUTHORLIST,
217         TC_OUTLINERNAME,
218         TC_TABLESTYLE,
219         TC_BIBINTOC,
220         TC_DOCBOOKROOT,
221         TC_DOCBOOKFORCEABSTRACT
222 };
223
224
225 namespace {
226
227 LexerKeyword textClassTags[] = {
228         { "addtociteengine",   TC_ADDTOCITEENGINE },
229         { "addtohtmlpreamble", TC_ADDTOHTMLPREAMBLE },
230         { "addtohtmlstyles",   TC_ADDTOHTMLSTYLES },
231         { "addtopreamble",     TC_ADDTOPREAMBLE },
232         { "bibintoc",          TC_BIBINTOC },
233         { "citeengine",        TC_CITEENGINE },
234         { "citeenginetype",    TC_CITEENGINETYPE },
235         { "citeformat",        TC_CITEFORMAT },
236         { "citeframework",     TC_CITEFRAMEWORK },
237         { "classoptions",      TC_CLASSOPTIONS },
238         { "columns",           TC_COLUMNS },
239         { "counter",           TC_COUNTER },
240         { "defaultbiblio",     TC_DEFAULTBIBLIO },
241         { "defaultfont",       TC_DEFAULTFONT },
242         { "defaultmodule",     TC_DEFAULTMODULE },
243         { "defaultstyle",      TC_DEFAULTSTYLE },
244         { "docbookforceabstract", TC_DOCBOOKFORCEABSTRACT },
245         { "docbookroot",       TC_DOCBOOKROOT },
246         { "excludesmodule",    TC_EXCLUDESMODULE },
247         { "float",             TC_FLOAT },
248         { "format",            TC_FORMAT },
249         { "fullauthorlist",    TC_FULLAUTHORLIST },
250         { "htmlpreamble",      TC_HTMLPREAMBLE },
251         { "htmlstyles",        TC_HTMLSTYLES },
252         { "htmltocsection",    TC_HTMLTOCSECTION },
253         { "ifcounter",         TC_IFCOUNTER },
254         { "input",             TC_INPUT },
255         { "insetlayout",       TC_INSETLAYOUT },
256         { "leftmargin",        TC_LEFTMARGIN },
257         { "maxcitenames",      TC_MAXCITENAMES },
258         { "modifystyle",       TC_MODIFYSTYLE },
259         { "nocounter",         TC_NOCOUNTER },
260         { "nofloat",           TC_NOFLOAT },
261         { "noinsetlayout",     TC_NOINSETLAYOUT },
262         { "nostyle",           TC_NOSTYLE },
263         { "outlinername",      TC_OUTLINERNAME },
264         { "outputformat",      TC_OUTPUTFORMAT },
265         { "outputtype",        TC_OUTPUTTYPE },
266         { "packageoptions",    TC_PKGOPTS },
267         { "pagesize",          TC_PAGESIZE },
268         { "pagestyle",         TC_PAGESTYLE },
269         { "preamble",          TC_PREAMBLE },
270         { "provides",          TC_PROVIDES },
271         { "providesmodule",    TC_PROVIDESMODULE },
272         { "providestyle",      TC_PROVIDESTYLE },
273         { "requires",          TC_REQUIRES },
274         { "rightmargin",       TC_RIGHTMARGIN },
275         { "secnumdepth",       TC_SECNUMDEPTH },
276         { "sides",             TC_SIDES },
277         { "style",             TC_STYLE },
278         { "tablestyle",        TC_TABLESTYLE },
279         { "titlelatexname",    TC_TITLELATEXNAME },
280         { "titlelatextype",    TC_TITLELATEXTYPE },
281         { "tocdepth",          TC_TOCDEPTH }
282 };
283
284 } // namespace
285
286
287 bool TextClass::convertLayoutFormat(support::FileName const & filename, ReadType rt)
288 {
289         LYXERR(Debug::TCLASS, "Converting layout file to " << LAYOUT_FORMAT);
290         TempFile tmp("convertXXXXXX.layout");
291         FileName const tempfile = tmp.name();
292         bool success = layout2layout(filename, tempfile);
293         if (success)
294                 success = readWithoutConv(tempfile, rt) == OK;
295         return success;
296 }
297
298
299 std::string TextClass::convert(std::string const & str)
300 {
301         TempFile tmp1("localXXXXXX.layout");
302         FileName const fn = tmp1.name();
303         ofstream os(fn.toFilesystemEncoding().c_str());
304         os << str;
305         os.close();
306         TempFile tmp2("convert_localXXXXXX.layout");
307         FileName const tempfile = tmp2.name();
308         bool success = layout2layout(fn, tempfile, LYXFILE_LAYOUT_FORMAT);
309         if (!success)
310                 return "";
311         ifstream is(tempfile.toFilesystemEncoding().c_str());
312         string ret;
313         string tmp;
314         while (!is.eof()) {
315                 getline(is, tmp);
316                 ret += tmp + '\n';
317         }
318         is.close();
319         return ret;
320 }
321
322
323 TextClass::ReturnValues TextClass::readWithoutConv(FileName const & filename, ReadType rt)
324 {
325         if (!filename.isReadableFile()) {
326                 lyxerr << "Cannot read layout file `" << filename << "'."
327                        << endl;
328                 return ERROR;
329         }
330
331         LYXERR(Debug::TCLASS, "Reading " + translateReadType(rt) + ": " +
332                 to_utf8(makeDisplayPath(filename.absFileName())));
333
334         // Define the plain layout used in table cells, ert, etc. Note that
335         // we do this before loading any layout file, so that classes can
336         // override features of this layout if they should choose to do so.
337         if (rt == BASECLASS && !hasLayout(plain_layout_))
338                 layoutlist_.push_back(createBasicLayout(plain_layout_));
339
340         Lexer lexrc(textClassTags);
341         lexrc.setFile(filename);
342         ReturnValues retval = read(lexrc, rt);
343
344         LYXERR(Debug::TCLASS, "Finished reading " + translateReadType(rt) + ": " +
345                         to_utf8(makeDisplayPath(filename.absFileName())));
346
347         return retval;
348 }
349
350
351 bool TextClass::read(FileName const & filename, ReadType rt)
352 {
353         ReturnValues const retval = readWithoutConv(filename, rt);
354         if (retval != FORMAT_MISMATCH)
355                 return retval == OK;
356
357         bool const worx = convertLayoutFormat(filename, rt);
358         if (!worx)
359                 LYXERR0 ("Unable to convert " << filename <<
360                         " to format " << LAYOUT_FORMAT);
361         return worx;
362 }
363
364
365 TextClass::ReturnValues TextClass::validate(std::string const & str)
366 {
367         TextClass tc;
368         return tc.read(str, VALIDATION);
369 }
370
371
372 TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
373 {
374         Lexer lexrc(textClassTags);
375         istringstream is(str);
376         lexrc.setStream(is);
377         ReturnValues retval = read(lexrc, rt);
378
379         if (retval != FORMAT_MISMATCH)
380                 return retval;
381
382         // write the layout string to a temporary file
383         TempFile tmp("TextClass_read");
384         FileName const tempfile = tmp.name();
385         ofstream os(tempfile.toFilesystemEncoding().c_str());
386         if (!os) {
387                 LYXERR0("Unable to create temporary file");
388                 return ERROR;
389         }
390         os << str;
391         os.close();
392
393         // now try to convert it to LAYOUT_FORMAT
394         if (!convertLayoutFormat(tempfile, rt)) {
395                 LYXERR0("Unable to convert internal layout information to format "
396                         << LAYOUT_FORMAT);
397                 return ERROR;
398         }
399
400         return OK_OLDFORMAT;
401 }
402
403
404 // Reads a textclass structure from file.
405 TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
406 {
407         if (!lexrc.isOK())
408                 return ERROR;
409
410         // The first usable line should be
411         // Format LAYOUT_FORMAT
412         if (lexrc.lex() != TC_FORMAT || !lexrc.next()
413             || lexrc.getInteger() != LAYOUT_FORMAT)
414                 return FORMAT_MISMATCH;
415
416         // parsing
417         bool error = false;
418         while (lexrc.isOK() && !error) {
419                 int le = lexrc.lex();
420
421                 switch (le) {
422                 case Lexer::LEX_FEOF:
423                         continue;
424
425                 case Lexer::LEX_UNDEF:
426                         lexrc.printError("Unknown TextClass tag `$$Token'");
427                         error = true;
428                         continue;
429
430                 default:
431                         break;
432                 }
433
434                 // used below to track whether we are in an IfStyle or IfCounter tag.
435                 bool modifystyle  = false;
436                 bool providestyle = false;
437                 bool ifcounter    = false;
438
439                 switch (static_cast<TextClassTags>(le)) {
440
441                 case TC_FORMAT:
442                         lexrc.next();
443                         lexrc.printError("Duplicate Format directive");
444                         break;
445
446                 case TC_OUTPUTFORMAT:
447                         if (lexrc.next()) {
448                                 outputFormat_ = lexrc.getString();
449                                 has_output_format_ = true;
450                         }
451                         break;
452
453                 case TC_OUTPUTTYPE:
454                         readOutputType(lexrc);
455                         switch(outputType_) {
456                         case LATEX:
457                                 outputFormat_ = "latex";
458                                 break;
459                         case LITERATE:
460                                 outputFormat_ = "literate";
461                                 break;
462                         }
463                         break;
464
465                 case TC_INPUT: // Include file
466                         if (lexrc.next()) {
467                                 FileName tmp;
468                                 string const inc = lexrc.getString();
469                                 if (!path().empty() && (prefixIs(inc, "./") ||
470                                                         prefixIs(inc, "../")))
471                                         tmp = fileSearch(path(), inc, "layout");
472                                 else
473                                         tmp = libFileSearch("layouts", inc,
474                                                             "layout");
475
476                                 if (tmp.empty()) {
477                                         lexrc.printError("Could not find input file: " + inc);
478                                         error = true;
479                                 } else if (!read(tmp, MERGE)) {
480                                         lexrc.printError("Error reading input file: " + tmp.absFileName());
481                                         error = true;
482                                 }
483                         }
484                         break;
485
486                 case TC_DEFAULTSTYLE:
487                         if (lexrc.next()) {
488                                 docstring const name = from_utf8(subst(lexrc.getString(),
489                                                           '_', ' '));
490                                 defaultlayout_ = name;
491                         }
492                         break;
493
494                 case TC_MODIFYSTYLE:
495                         modifystyle = true;
496                 // fall through
497                 case TC_PROVIDESTYLE:
498                         // if modifystyle is true, then we got here by falling through
499                         // so we are not in an ProvideStyle block
500                         if (!modifystyle)
501                                 providestyle = true;
502                 // fall through
503                 case TC_STYLE: {
504                         if (!lexrc.next()) {
505                                 lexrc.printError("No name given for style: `$$Token'.");
506                                 error = true;
507                                 break;
508                         }
509                         docstring const name = from_utf8(subst(lexrc.getString(),
510                                                         '_', ' '));
511                         if (name.empty()) {
512                                 string s = "Could not read name for style: `$$Token' "
513                                         + lexrc.getString() + " is probably not valid UTF-8!";
514                                 lexrc.printError(s);
515                                 Layout lay;
516                                 // Since we couldn't read the name, we just scan the rest
517                                 // of the style and discard it.
518                                 error = !readStyle(lexrc, lay);
519                                 break;
520                         }
521
522                         bool const have_layout = hasLayout(name);
523
524                         // If the layout already exists, then we want to add it to
525                         // the existing layout, as long as we are not in an ProvideStyle
526                         // block.
527                         if (have_layout && !providestyle) {
528                                 Layout & lay = operator[](name);
529                                 error = !readStyle(lexrc, lay);
530                         }
531                         // If the layout does not exist, then we want to create a new
532                         // one, but not if we are in a ModifyStyle block.
533                         else if (!have_layout && !modifystyle) {
534                                 Layout layout;
535                                 layout.setName(name);
536                                 error = !readStyle(lexrc, layout);
537                                 if (!error)
538                                         layoutlist_.push_back(layout);
539
540                                 if (defaultlayout_.empty()) {
541                                         // We do not have a default layout yet, so we choose
542                                         // the first layout we encounter.
543                                         defaultlayout_ = name;
544                                 }
545                         }
546                         // There are two ways to get here:
547                         //  (i)  The layout exists but we are in an ProvideStyle block
548                         //  (ii) The layout doesn't exist, but we are in an ModifyStyle
549                         //       block.
550                         // Either way, we just scan the rest and discard it
551                         else {
552                                 Layout lay;
553                                 // signal to coverity that we do not care about the result
554                                 (void)readStyle(lexrc, lay);
555                         }
556                         break;
557                 }
558
559                 case TC_NOSTYLE:
560                         if (lexrc.next()) {
561                                 docstring const style = from_utf8(subst(lexrc.getString(),
562                                                      '_', ' '));
563                                 if (!deleteLayout(style))
564                                         lyxerr << "Cannot delete style `"
565                                                << to_utf8(style) << '\'' << endl;
566                         }
567                         break;
568
569                 case TC_NOINSETLAYOUT:
570                         if (lexrc.next()) {
571                                 docstring const style = from_utf8(subst(lexrc.getString(),
572                                                                  '_', ' '));
573                                 if (!deleteInsetLayout(style))
574                                         LYXERR0("Style `" << style << "' cannot be removed\n"
575                                                 "because it was not found!");
576                         }
577                         break;
578
579                 case TC_COLUMNS:
580                         if (lexrc.next())
581                                 columns_ = lexrc.getInteger();
582                         break;
583
584                 case TC_SIDES:
585                         if (lexrc.next()) {
586                                 switch (lexrc.getInteger()) {
587                                 case 1: sides_ = OneSide; break;
588                                 case 2: sides_ = TwoSides; break;
589                                 default:
590                                         lyxerr << "Impossible number of page"
591                                                 " sides, setting to one."
592                                                << endl;
593                                         sides_ = OneSide;
594                                         break;
595                                 }
596                         }
597                         break;
598
599                 case TC_PAGESIZE:
600                         lexrc.next();
601                         pagesize_ = rtrim(lexrc.getString());
602                         break;
603
604                 case TC_PAGESTYLE:
605                         lexrc.next();
606                         pagestyle_ = rtrim(lexrc.getString());
607                         break;
608
609                 case TC_DEFAULTFONT:
610                         defaultfont_ = lyxRead(lexrc);
611                         if (!defaultfont_.resolved()) {
612                                 lexrc.printError("Warning: defaultfont should "
613                                                  "be fully instantiated!");
614                                 defaultfont_.realize(sane_font);
615                         }
616                         break;
617
618                 case TC_SECNUMDEPTH:
619                         lexrc.next();
620                         secnumdepth_ = lexrc.getInteger();
621                         break;
622
623                 case TC_TOCDEPTH:
624                         lexrc.next();
625                         tocdepth_ = lexrc.getInteger();
626                         break;
627
628                 // First step to support options
629                 case TC_CLASSOPTIONS:
630                         readClassOptions(lexrc);
631                         break;
632
633                 case TC_PREAMBLE:
634                         preamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
635                         break;
636
637                 case TC_HTMLPREAMBLE:
638                         htmlpreamble_ = lexrc.getLongString(from_ascii("EndPreamble"));
639                         break;
640
641                 case TC_HTMLSTYLES:
642                         htmlstyles_ = lexrc.getLongString(from_ascii("EndStyles"));
643                         break;
644
645                 case TC_HTMLTOCSECTION:
646                         html_toc_section_ = from_utf8(trim(lexrc.getString()));
647                         break;
648
649                 case TC_ADDTOPREAMBLE:
650                         preamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
651                         break;
652
653                 case TC_ADDTOHTMLPREAMBLE:
654                         htmlpreamble_ += lexrc.getLongString(from_ascii("EndPreamble"));
655                         break;
656
657                 case TC_ADDTOHTMLSTYLES:
658                         htmlstyles_ += lexrc.getLongString(from_ascii("EndStyles"));
659                         break;
660
661                 case TC_PROVIDES: {
662                         lexrc.next();
663                         string const feature = lexrc.getString();
664                         lexrc.next();
665                         if (lexrc.getInteger())
666                                 provides_.insert(feature);
667                         else
668                                 provides_.erase(feature);
669                         break;
670                 }
671
672                 case TC_REQUIRES: {
673                         lexrc.eatLine();
674                         vector<string> const req
675                                 = getVectorFromString(lexrc.getString());
676                         required_.insert(req.begin(), req.end());
677                         break;
678                 }
679
680                 case TC_PKGOPTS : {
681                         lexrc.next();
682                         string const pkg = lexrc.getString();
683                         lexrc.next();
684                         string const options = lexrc.getString();
685                         package_options_[pkg] = options;
686                         break;
687                 }
688
689                 case TC_DEFAULTMODULE: {
690                         lexrc.next();
691                         string const module = lexrc.getString();
692                         if (find(default_modules_.begin(), default_modules_.end(), module) == default_modules_.end())
693                                 default_modules_.push_back(module);
694                         break;
695                 }
696
697                 case TC_PROVIDESMODULE: {
698                         lexrc.next();
699                         string const module = lexrc.getString();
700                         if (find(provided_modules_.begin(), provided_modules_.end(), module) == provided_modules_.end())
701                                 provided_modules_.push_back(module);
702                         break;
703                 }
704
705                 case TC_EXCLUDESMODULE: {
706                         lexrc.next();
707                         string const module = lexrc.getString();
708                         // modules already have their own way to exclude other modules
709                         if (rt == MODULE) {
710                                 LYXERR0("ExcludesModule tag cannot be used in a module!");
711                                 break;
712                         }
713                         if (find(excluded_modules_.begin(), excluded_modules_.end(), module) == excluded_modules_.end())
714                                 excluded_modules_.push_back(module);
715                         break;
716                 }
717
718                 case TC_LEFTMARGIN:     // left margin type
719                         if (lexrc.next())
720                                 leftmargin_ = lexrc.getDocString();
721                         break;
722
723                 case TC_RIGHTMARGIN:    // right margin type
724                         if (lexrc.next())
725                                 rightmargin_ = lexrc.getDocString();
726                         break;
727
728                 case TC_INSETLAYOUT: {
729                         if (!lexrc.next()) {
730                                 lexrc.printError("No name given for InsetLayout: `$$Token'.");
731                                 error = true;
732                                 break;
733                         }
734                         docstring const name = subst(lexrc.getDocString(), '_', ' ');
735                         bool const validating = (rt == VALIDATION);
736                         if (name.empty()) {
737                                 string s = "Could not read name for InsetLayout: `$$Token' "
738                                         + lexrc.getString() + " is probably not valid UTF-8!";
739                                 lexrc.printError(s);
740                                 InsetLayout il;
741                                 // Since we couldn't read the name, we just scan the rest
742                                 // of the style and discard it.
743                                 il.read(lexrc, *this);
744                                 // Let's try to continue rather than abort, unless we're validating
745                                 // in which case we want to report the error
746                                 if (validating)
747                                         error = true;
748                         } else if (hasInsetLayout(name)) {
749                                 InsetLayout & il = insetlayoutlist_[name];
750                                 error = !il.read(lexrc, *this, validating);
751                         } else {
752                                 InsetLayout il;
753                                 il.setName(name);
754                                 error = !il.read(lexrc, *this, validating);
755                                 if (!error)
756                                         insetlayoutlist_[name] = il;
757                         }
758                         break;
759                 }
760
761                 case TC_FLOAT:
762                         error = !readFloat(lexrc);
763                         break;
764
765                 case TC_CITEENGINE:
766                         error = !readCiteEngine(lexrc, rt);
767                         break;
768
769                 case TC_ADDTOCITEENGINE:
770                         error = !readCiteEngine(lexrc, rt, true);
771                         break;
772
773                 case TC_CITEENGINETYPE:
774                         if (lexrc.next())
775                                 opt_enginetype_ = rtrim(lexrc.getString());
776                         break;
777
778                 case TC_CITEFORMAT:
779                         error = !readCiteFormat(lexrc, rt);
780                         break;
781
782                 case TC_CITEFRAMEWORK:
783                         lexrc.next();
784                         citeframework_ = rtrim(lexrc.getString());
785                         break;
786
787                 case TC_MAXCITENAMES:
788                         lexrc.next();
789                         maxcitenames_ = size_t(lexrc.getInteger());
790                         break;
791
792                 case TC_DEFAULTBIBLIO:
793                         if (lexrc.next()) {
794                                 vector<string> const dbs =
795                                         getVectorFromString(rtrim(lexrc.getString()), "|");
796                                 for (auto const & dbase : dbs) {
797                                         if (!contains(dbase, ':')) {
798                                                 vector<string> const enginetypes =
799                                                         getVectorFromString(opt_enginetype_, "|");
800                                                 for (string const & s: enginetypes)
801                                                         cite_default_biblio_style_[s] = dbase;
802                                         } else {
803                                                 string eng;
804                                                 string const db = split(dbase, eng, ':');
805                                                 cite_default_biblio_style_[eng] = db;
806                                         }
807                                 }
808                         }
809                         break;
810
811                 case TC_BIBINTOC:
812                         if (lexrc.next())
813                                 bibintoc_ = lexrc.getBool();
814                         break;
815
816                 case TC_FULLAUTHORLIST:
817                         if (lexrc.next())
818                                 cite_full_author_list_ &= lexrc.getBool();
819                         break;
820
821                 case TC_NOCOUNTER:
822                         if (lexrc.next()) {
823                                 docstring const cnt = lexrc.getDocString();
824                                 if (!counters_.remove(cnt))
825                                         LYXERR0("Unable to remove counter: " + to_utf8(cnt));
826                         }
827                         break;
828
829                 case TC_IFCOUNTER:
830                         ifcounter = true;
831                         // fall through
832                 case TC_COUNTER:
833                         if (lexrc.next()) {
834                                 docstring const name = lexrc.getDocString();
835                                 if (name.empty()) {
836                                         string s = "Could not read name for counter: `$$Token' "
837                                                         + lexrc.getString() + " is probably not valid UTF-8!";
838                                         lexrc.printError(s.c_str());
839                                         Counter c;
840                                         // Since we couldn't read the name, we just scan the rest
841                                         // and discard it.
842                                         c.read(lexrc);
843                                 } else
844                                         error = !counters_.read(lexrc, name, !ifcounter);
845                         }
846                         else {
847                                 lexrc.printError("No name given for style: `$$Token'.");
848                                 error = true;
849                         }
850                         break;
851
852                 case TC_TITLELATEXTYPE:
853                         readTitleType(lexrc);
854                         break;
855
856                 case TC_TITLELATEXNAME:
857                         if (lexrc.next())
858                                 titlename_ = lexrc.getString();
859                         break;
860
861                 case TC_NOFLOAT:
862                         if (lexrc.next()) {
863                                 string const nofloat = lexrc.getString();
864                                 floatlist_.erase(nofloat);
865                         }
866                         break;
867
868                 case TC_OUTLINERNAME:
869                         error = !readOutlinerName(lexrc);
870                         break;
871
872         case TC_TABLESTYLE:
873                         lexrc.next();
874                         tablestyle_ = rtrim(lexrc.getString());
875                         break;
876
877                 case TC_DOCBOOKROOT:
878                         if (lexrc.next())
879                                 docbookroot_ = lexrc.getString();
880                         break;
881
882                 case TC_DOCBOOKFORCEABSTRACT:
883                         if (lexrc.next())
884                                 docbookforceabstract_ = lexrc.getBool();
885                         break;
886                 } // end of switch
887         }
888
889         // at present, we abort if we encounter an error,
890         // so there is no point continuing.
891         if (error)
892                 return ERROR;
893
894         if (rt != BASECLASS)
895                 return OK;
896
897         if (defaultlayout_.empty()) {
898                 LYXERR0("Error: Textclass '" << name_
899                                                 << "' is missing a defaultstyle.");
900                 return ERROR;
901         }
902
903         // Try to erase "stdinsets" from the provides_ set.
904         // The
905         //   Provides stdinsets 1
906         // declaration simply tells us that the standard insets have been
907         // defined. (It's found in stdinsets.inc but could also be used in
908         // user-defined files.) There isn't really any such package. So we
909         // might as well go ahead and erase it.
910         // If we do not succeed, then it was not there, which means that
911         // the textclass did not provide the definitions of the standard
912         // insets. So we need to try to load them.
913         size_type const erased = provides_.erase("stdinsets");
914         if (!erased) {
915                 FileName tmp = libFileSearch("layouts", "stdinsets.inc");
916
917                 if (tmp.empty()) {
918                         frontend::Alert::warning(_("Missing File"),
919                                 _("Could not find stdinsets.inc! This may lead to data loss!"));
920                         error = true;
921                 } else if (!read(tmp, MERGE)) {
922                         frontend::Alert::warning(_("Corrupt File"),
923                                 _("Could not read stdinsets.inc! This may lead to data loss!"));
924                         error = true;
925                 }
926         }
927
928         min_toclevel_ = Layout::NOT_IN_TOC;
929         max_toclevel_ = Layout::NOT_IN_TOC;
930         for (auto const & lay : *this) {
931                 int const toclevel = lay.toclevel;
932                 if (toclevel != Layout::NOT_IN_TOC) {
933                         if (min_toclevel_ == Layout::NOT_IN_TOC)
934                                 min_toclevel_ = toclevel;
935                         else
936                                 min_toclevel_ = min(min_toclevel_, toclevel);
937                         max_toclevel_ = max(max_toclevel_, toclevel);
938                 }
939         }
940         LYXERR(Debug::TCLASS, "Minimum TocLevel is " << min_toclevel_
941                 << ", maximum is " << max_toclevel_);
942
943         return (error ? ERROR : OK);
944 }
945
946
947 void TextClass::readTitleType(Lexer & lexrc)
948 {
949         LexerKeyword titleTypeTags[] = {
950                 { "commandafter", TITLE_COMMAND_AFTER },
951                 { "environment",  TITLE_ENVIRONMENT }
952         };
953
954         PushPopHelper pph(lexrc, titleTypeTags);
955
956         int le = lexrc.lex();
957         switch (le) {
958         case Lexer::LEX_UNDEF:
959                 lexrc.printError("Unknown output type `$$Token'");
960                 break;
961         case TITLE_COMMAND_AFTER:
962         case TITLE_ENVIRONMENT:
963                 titletype_ = static_cast<TitleLatexType>(le);
964                 break;
965         default:
966                 LYXERR0("Unhandled value " << le << " in TextClass::readTitleType.");
967                 break;
968         }
969 }
970
971
972 void TextClass::readOutputType(Lexer & lexrc)
973 {
974         LexerKeyword outputTypeTags[] = {
975                 { "latex",    LATEX },
976                 { "literate", LITERATE }
977         };
978
979         PushPopHelper pph(lexrc, outputTypeTags);
980
981         int le = lexrc.lex();
982         switch (le) {
983         case Lexer::LEX_UNDEF:
984                 lexrc.printError("Unknown output type `$$Token'");
985                 return;
986         case LATEX:
987         case LITERATE:
988                 outputType_ = static_cast<OutputType>(le);
989                 break;
990         default:
991                 LYXERR0("Unhandled value " << le);
992                 break;
993         }
994 }
995
996
997 void TextClass::readClassOptions(Lexer & lexrc)
998 {
999         enum {
1000                 CO_FONTSIZE = 1,
1001                 CO_FONTSIZE_FORMAT,
1002                 CO_PAGESIZE,
1003                 CO_PAGESIZE_FORMAT,
1004                 CO_PAGESTYLE,
1005                 CO_OTHER,
1006                 CO_END
1007         };
1008
1009         LexerKeyword classOptionsTags[] = {
1010                 {"end",       CO_END },
1011                 {"fontsize",  CO_FONTSIZE },
1012                 {"fontsizeformat", CO_FONTSIZE_FORMAT },
1013                 {"other",     CO_OTHER },
1014                 {"pagesize",  CO_PAGESIZE },
1015                 {"pagesizeformat", CO_PAGESIZE_FORMAT },
1016                 {"pagestyle", CO_PAGESTYLE }
1017         };
1018
1019         lexrc.pushTable(classOptionsTags);
1020         bool getout = false;
1021         while (!getout && lexrc.isOK()) {
1022                 int le = lexrc.lex();
1023                 switch (le) {
1024                 case Lexer::LEX_UNDEF:
1025                         lexrc.printError("Unknown ClassOption tag `$$Token'");
1026                         continue;
1027                 default:
1028                         break;
1029                 }
1030                 switch (le) {
1031                 case CO_FONTSIZE:
1032                         lexrc.next();
1033                         opt_fontsize_ = rtrim(lexrc.getString());
1034                         break;
1035                 case CO_FONTSIZE_FORMAT:
1036                         lexrc.next();
1037                         fontsize_format_ = rtrim(lexrc.getString());
1038                         break;
1039                 case CO_PAGESIZE:
1040                         lexrc.next();
1041                         opt_pagesize_ = rtrim(lexrc.getString());
1042                         break;
1043                 case CO_PAGESIZE_FORMAT:
1044                         lexrc.next();
1045                         pagesize_format_ = rtrim(lexrc.getString());
1046                         break;
1047                 case CO_PAGESTYLE:
1048                         lexrc.next();
1049                         opt_pagestyle_ = rtrim(lexrc.getString());
1050                         break;
1051                 case CO_OTHER:
1052                         lexrc.next();
1053                         if (options_.empty())
1054                                 options_ = lexrc.getString();
1055                         else
1056                                 options_ += ',' + lexrc.getString();
1057                         break;
1058                 case CO_END:
1059                         getout = true;
1060                         break;
1061                 }
1062         }
1063         lexrc.popTable();
1064 }
1065
1066
1067 vector<CitationStyle> const & TextClass::getCiteStyles(
1068         CiteEngineType const & type) const
1069 {
1070         static vector<CitationStyle> empty;
1071         map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
1072         if (it == cite_styles_.end())
1073                 return empty;
1074         return it->second;
1075 }
1076
1077
1078 bool TextClass::readCiteEngine(Lexer & lexrc, ReadType rt, bool const add)
1079 {
1080         int const type = readCiteEngineType(lexrc);
1081         bool authoryear = (type & ENGINE_TYPE_AUTHORYEAR);
1082         bool numerical = (type & ENGINE_TYPE_NUMERICAL);
1083         bool defce = (type & ENGINE_TYPE_DEFAULT);
1084
1085         if (rt == CITE_ENGINE) {
1086                 // The cite engines are not supposed to overwrite
1087                 // CiteStyle defined by the class or a module.
1088                 if (authoryear)
1089                         authoryear = getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1090                 if (numerical)
1091                         numerical = getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1092                 if (defce)
1093                         defce = getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1094         }
1095
1096         if (rt != CITE_ENGINE && !add) {
1097                 // Reset if we defined CiteStyle
1098                 // from the class or a module
1099                 if (authoryear)
1100                         cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1101                 if (numerical)
1102                         cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1103                 if (defce)
1104                         cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1105         }
1106
1107         string def;
1108         bool getout = false;
1109         while (!getout && lexrc.isOK()) {
1110                 lexrc.eatLine();
1111                 def = lexrc.getString();
1112                 def = subst(def, " ", "");
1113                 def = subst(def, "\t", "");
1114                 if (compare_ascii_no_case(def, "end") == 0) {
1115                         getout = true;
1116                         continue;
1117                 }
1118                 CitationStyle cs;
1119                 char ichar = def[0];
1120                 if (ichar == '#')
1121                         continue;
1122                 if (isUpperCase(ichar)) {
1123                         cs.forceUpperCase = true;
1124                         def[0] = lowercase(ichar);
1125                 }
1126
1127                 /** For portability reasons (between different
1128                  *  cite engines such as natbib and biblatex),
1129                  *  we distinguish between:
1130                  *  1. The LyX name as output in the LyX file
1131                  *  2. Possible aliases that might fall back to
1132                  *     the given LyX name in the current engine
1133                  *  3. The actual LaTeX command that is output
1134                  *  (2) and (3) are optional.
1135                  *  Also, the GUI string for the starred version can
1136                  *  be changed
1137                  *  The syntax is:
1138                  *  LyXName|alias,nextalias*<!stardesc!stardesctooltip>[][]=latexcmd
1139                  */
1140                 enum ScanMode {
1141                         LyXName,
1142                         Alias,
1143                         LaTeXCmd,
1144                         StarDesc
1145                 };
1146
1147                 ScanMode mode = LyXName;
1148                 ScanMode oldmode = LyXName;
1149                 string lyx_cmd;
1150                 string alias;
1151                 string latex_cmd;
1152                 string stardesc;
1153                 size_t const n = def.size();
1154                 for (size_t i = 0; i != n; ++i) {
1155                         ichar = def[i];
1156                         if (ichar == '|')
1157                                 mode = Alias;
1158                         else if (ichar == '=')
1159                                 mode = LaTeXCmd;
1160                         else if (ichar == '<') {
1161                                 oldmode = mode;
1162                                 mode = StarDesc;
1163                         } else if (ichar == '>')
1164                                 mode = oldmode;
1165                         else if (mode == LaTeXCmd)
1166                                 latex_cmd += ichar;
1167                         else if (mode == StarDesc)
1168                                 stardesc += ichar;
1169                         else if (ichar == '$')
1170                                 cs.hasQualifiedList = true;
1171                         else if (ichar == '*')
1172                                 cs.hasStarredVersion = true;
1173                         else if (ichar == '[' && cs.textAfter)
1174                                 cs.textBefore = true;
1175                         else if (ichar == '[')
1176                                 cs.textAfter = true;
1177                         else if (ichar != ']') {
1178                                 if (mode == Alias)
1179                                         alias += ichar;
1180                                 else
1181                                         lyx_cmd += ichar;
1182                         }
1183                 }
1184                 cs.name = lyx_cmd;
1185                 cs.cmd = latex_cmd.empty() ? lyx_cmd : latex_cmd;
1186                 if (!alias.empty()) {
1187                         vector<string> const aliases = getVectorFromString(alias);
1188                         for (string const & s: aliases)
1189                                 cite_command_aliases_[s] = lyx_cmd;
1190                 }
1191                 vector<string> const stardescs = getVectorFromString(stardesc, "!");
1192                 int size = int(stardesc.size());
1193                 if (size > 0)
1194                         cs.stardesc = stardescs[0];
1195                 if (size > 1)
1196                         cs.startooltip = stardescs[1];
1197                 if (add) {
1198                         if (authoryear)
1199                                 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1200                         if (numerical)
1201                                 class_cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1202                         if (defce)
1203                                 class_cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1204                 } else {
1205                         if (authoryear)
1206                                 cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(cs);
1207                         if (numerical)
1208                                 cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(cs);
1209                         if (defce)
1210                                 cite_styles_[ENGINE_TYPE_DEFAULT].push_back(cs);
1211                 }
1212         }
1213         // If we do AddToCiteEngine, do not apply yet,
1214         // except if we have already a style to add something to
1215         bool apply_ay = !add;
1216         bool apply_num = !add;
1217         bool apply_def = !add;
1218         if (add) {
1219                 if (type & ENGINE_TYPE_AUTHORYEAR)
1220                         apply_ay = !getCiteStyles(ENGINE_TYPE_AUTHORYEAR).empty();
1221                 if (type & ENGINE_TYPE_NUMERICAL)
1222                         apply_num = !getCiteStyles(ENGINE_TYPE_NUMERICAL).empty();
1223                 if (type & ENGINE_TYPE_DEFAULT)
1224                         apply_def = !getCiteStyles(ENGINE_TYPE_DEFAULT).empty();
1225         }
1226
1227         // Add the styles from AddToCiteEngine to the class' styles
1228         // (but only if they are not yet defined)
1229         for (auto const & cis : class_cite_styles_) {
1230                 // Only consider the current CiteEngineType
1231                 if (!(type & cis.first))
1232                         continue;
1233                 for (auto const & ciss : cis.second) {
1234                         bool defined = false;
1235                         // Check if the style "name" is already def'ed
1236                         for (auto const & av : getCiteStyles(cis.first))
1237                                 if (av.name == ciss.name)
1238                                         defined = true;
1239                         if (!defined) {
1240                                 if (cis.first == ENGINE_TYPE_AUTHORYEAR && apply_ay)
1241                                         cite_styles_[ENGINE_TYPE_AUTHORYEAR].push_back(ciss);
1242                                 else if (cis.first == ENGINE_TYPE_NUMERICAL && apply_num)
1243                                         cite_styles_[ENGINE_TYPE_NUMERICAL].push_back(ciss);
1244                                 else if (cis.first == ENGINE_TYPE_DEFAULT && apply_def)
1245                                         cite_styles_[ENGINE_TYPE_DEFAULT].push_back(ciss);
1246                         }
1247                 }
1248         }
1249         if (type & ENGINE_TYPE_AUTHORYEAR && apply_ay)
1250                 class_cite_styles_[ENGINE_TYPE_AUTHORYEAR].clear();
1251         if (type & ENGINE_TYPE_NUMERICAL && apply_num)
1252                 class_cite_styles_[ENGINE_TYPE_NUMERICAL].clear();
1253         if (type & ENGINE_TYPE_DEFAULT && apply_def)
1254                 class_cite_styles_[ENGINE_TYPE_DEFAULT].clear();
1255         return getout;
1256 }
1257
1258
1259 int TextClass::readCiteEngineType(Lexer & lexrc) const
1260 {
1261         static_assert(ENGINE_TYPE_DEFAULT ==
1262                                   (ENGINE_TYPE_AUTHORYEAR | ENGINE_TYPE_NUMERICAL),
1263                                   "Incorrect default engine type");
1264         if (!lexrc.next()) {
1265                 lexrc.printError("No cite engine type given for token: `$$Token'.");
1266                 return ENGINE_TYPE_DEFAULT;
1267         }
1268         string const type = rtrim(lexrc.getString());
1269         if (compare_ascii_no_case(type, "authoryear") == 0)
1270                 return ENGINE_TYPE_AUTHORYEAR;
1271         else if (compare_ascii_no_case(type, "numerical") == 0)
1272                 return ENGINE_TYPE_NUMERICAL;
1273         else if (compare_ascii_no_case(type, "default") != 0) {
1274                 string const s = "Unknown cite engine type `" + type
1275                         + "' given for token: `$$Token',";
1276                 lexrc.printError(s);
1277         }
1278         return ENGINE_TYPE_DEFAULT;
1279 }
1280
1281
1282 bool TextClass::readCiteFormat(Lexer & lexrc, ReadType rt)
1283 {
1284         int const type = readCiteEngineType(lexrc);
1285         string etype;
1286         string definition;
1287         // Cite engine definitions do not overwrite existing
1288         // definitions from the class or a module
1289         bool const overwrite = rt != CITE_ENGINE;
1290         while (lexrc.isOK()) {
1291                 lexrc.next();
1292                 etype = lexrc.getString();
1293                 if (compare_ascii_no_case(etype, "end") == 0)
1294                         break;
1295                 if (!lexrc.isOK())
1296                         return false;
1297                 lexrc.eatLine();
1298                 definition = lexrc.getString();
1299                 char initchar = etype[0];
1300                 if (initchar == '#')
1301                         continue;
1302                 if (initchar == '!' || initchar == '_' || prefixIs(etype, "B_")) {
1303                         bool defined = false;
1304                         bool aydefined = false;
1305                         bool numdefined = false;
1306                         // Check if the macro is already def'ed
1307                         for (auto const & cm : cite_macros_) {
1308                                 if (!(type & cm.first))
1309                                         continue;
1310                                 if (cm.second.find(etype) != cm.second.end()) {
1311                                         if (type == cm.first)
1312                                                 // defined as default or specific type
1313                                                 defined = true;
1314                                         if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1315                                                 // defined for author-year
1316                                                 aydefined = true;
1317                                         else if (cm.first == ENGINE_TYPE_NUMERICAL)
1318                                                 // defined for numerical
1319                                                 numdefined = true;
1320                                 }
1321                         }
1322                         if (!defined || overwrite) {
1323                                 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1324                                         cite_macros_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1325                                 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1326                                         cite_macros_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1327                                 if (type == ENGINE_TYPE_DEFAULT)
1328                                         cite_macros_[ENGINE_TYPE_DEFAULT][etype] = definition;
1329                         }
1330                 } else {
1331                         bool defined = false;
1332                         bool aydefined = false;
1333                         bool numdefined = false;
1334                         // Check if the format is already def'ed
1335                         for (auto const & cm : cite_formats_) {
1336                                 if (!(type & cm.first))
1337                                         continue;
1338                                 if (cm.second.find(etype) != cm.second.end()) {
1339                                         if (type == cm.first)
1340                                                 // defined as default or specific type
1341                                                 defined = true;
1342                                         if (cm.first == ENGINE_TYPE_AUTHORYEAR)
1343                                                 // defined for author-year
1344                                                 aydefined = true;
1345                                         else if (cm.first == ENGINE_TYPE_NUMERICAL)
1346                                                 // defined for numerical
1347                                                 numdefined = true;
1348                                 }
1349                         }
1350                         if (!defined || overwrite){
1351                                 if (type & ENGINE_TYPE_AUTHORYEAR && (type != ENGINE_TYPE_DEFAULT || !aydefined))
1352                                         cite_formats_[ENGINE_TYPE_AUTHORYEAR][etype] = definition;
1353                                 if (type & ENGINE_TYPE_NUMERICAL && (type != ENGINE_TYPE_DEFAULT || !numdefined))
1354                                         cite_formats_[ENGINE_TYPE_NUMERICAL][etype] = definition;
1355                                 if (type == ENGINE_TYPE_DEFAULT)
1356                                         cite_formats_[ENGINE_TYPE_DEFAULT][etype] = definition;
1357                         }
1358                 }
1359         }
1360         return true;
1361 }
1362
1363
1364 bool TextClass::readFloat(Lexer & lexrc)
1365 {
1366         enum {
1367                 FT_TYPE = 1,
1368                 FT_NAME,
1369                 FT_PLACEMENT,
1370                 FT_EXT,
1371                 FT_WITHIN,
1372                 FT_STYLE,
1373                 FT_LISTNAME,
1374                 FT_USESFLOAT,
1375                 FT_PREDEFINED,
1376                 FT_HTMLSTYLE,
1377                 FT_HTMLATTR,
1378                 FT_HTMLTAG,
1379                 FT_DOCBOOKATTR,
1380                 FT_DOCBOOKTAG,
1381                 FT_LISTCOMMAND,
1382                 FT_REFPREFIX,
1383                 FT_ALLOWED_PLACEMENT,
1384                 FT_ALLOWS_SIDEWAYS,
1385                 FT_ALLOWS_WIDE,
1386                 FT_REQUIRES,
1387                 FT_END
1388         };
1389
1390         LexerKeyword floatTags[] = {
1391                 { "allowedplacement", FT_ALLOWED_PLACEMENT },
1392                 { "allowssideways", FT_ALLOWS_SIDEWAYS },
1393                 { "allowswide", FT_ALLOWS_WIDE },
1394                 { "docbookattr", FT_DOCBOOKATTR },
1395                 { "docbooktag", FT_DOCBOOKTAG },
1396                 { "end", FT_END },
1397                 { "extension", FT_EXT },
1398                 { "guiname", FT_NAME },
1399                 { "htmlattr", FT_HTMLATTR },
1400                 { "htmlstyle", FT_HTMLSTYLE },
1401                 { "htmltag", FT_HTMLTAG },
1402                 { "ispredefined", FT_PREDEFINED },
1403                 { "listcommand", FT_LISTCOMMAND },
1404                 { "listname", FT_LISTNAME },
1405                 { "numberwithin", FT_WITHIN },
1406                 { "placement", FT_PLACEMENT },
1407                 { "refprefix", FT_REFPREFIX },
1408                 { "requires", FT_REQUIRES },
1409                 { "style", FT_STYLE },
1410                 { "type", FT_TYPE },
1411                 { "usesfloatpkg", FT_USESFLOAT }
1412         };
1413
1414         lexrc.pushTable(floatTags);
1415
1416         string ext;
1417         string htmlattr;
1418         docstring htmlstyle;
1419         string htmltag;
1420         string docbookattr;
1421         string docbooktag;
1422         string listname;
1423         string listcommand;
1424         string name;
1425         string placement;
1426         string allowed_placement = "!htbpH";
1427         string refprefix;
1428         string style;
1429         string type;
1430         string within;
1431         string required;
1432         bool usesfloat = true;
1433         bool ispredefined = false;
1434         bool allowswide = true;
1435         bool allowssideways = true;
1436
1437         bool getout = false;
1438         while (!getout && lexrc.isOK()) {
1439                 int le = lexrc.lex();
1440                 switch (le) {
1441                 case Lexer::LEX_UNDEF:
1442                         lexrc.printError("Unknown float tag `$$Token'");
1443                         continue;
1444                 default:
1445                         break;
1446                 }
1447                 switch (le) {
1448                 case FT_TYPE:
1449                         lexrc.next();
1450                         type = lexrc.getString();
1451                         if (floatlist_.typeExist(type)) {
1452                                 Floating const & fl = floatlist_.getType(type);
1453                                 placement = fl.placement();
1454                                 ext = fl.ext();
1455                                 within = fl.within();
1456                                 style = fl.style();
1457                                 name = fl.name();
1458                                 listname = fl.listName();
1459                                 usesfloat = fl.usesFloatPkg();
1460                                 ispredefined = fl.isPredefined();
1461                                 listcommand = fl.listCommand();
1462                                 refprefix = fl.refPrefix();
1463                         }
1464                         break;
1465                 case FT_NAME:
1466                         lexrc.next();
1467                         name = lexrc.getString();
1468                         break;
1469                 case FT_PLACEMENT:
1470                         lexrc.next();
1471                         placement = lexrc.getString();
1472                         break;
1473                 case FT_ALLOWED_PLACEMENT:
1474                         lexrc.next();
1475                         allowed_placement = lexrc.getString();
1476                         break;
1477                 case FT_EXT:
1478                         lexrc.next();
1479                         ext = lexrc.getString();
1480                         break;
1481                 case FT_WITHIN:
1482                         lexrc.next();
1483                         within = lexrc.getString();
1484                         if (within == "none")
1485                                 within.erase();
1486                         break;
1487                 case FT_STYLE:
1488                         lexrc.next();
1489                         style = lexrc.getString();
1490                         break;
1491                 case FT_LISTCOMMAND:
1492                         lexrc.next();
1493                         listcommand = lexrc.getString();
1494                         break;
1495                 case FT_REFPREFIX:
1496                         lexrc.next();
1497                         refprefix = lexrc.getString();
1498                         break;
1499                 case FT_LISTNAME:
1500                         lexrc.next();
1501                         listname = lexrc.getString();
1502                         break;
1503                 case FT_USESFLOAT:
1504                         lexrc.next();
1505                         usesfloat = lexrc.getBool();
1506                         break;
1507                 case FT_REQUIRES:
1508                         lexrc.next();
1509                         required = lexrc.getString();
1510                         break;
1511                 case FT_PREDEFINED:
1512                         lexrc.next();
1513                         ispredefined = lexrc.getBool();
1514                         break;
1515                 case FT_ALLOWS_SIDEWAYS:
1516                         lexrc.next();
1517                         allowssideways = lexrc.getBool();
1518                         break;
1519                 case FT_ALLOWS_WIDE:
1520                         lexrc.next();
1521                         allowswide = lexrc.getBool();
1522                         break;
1523                 case FT_HTMLATTR:
1524                         lexrc.next();
1525                         htmlattr = lexrc.getString();
1526                         break;
1527                 case FT_HTMLSTYLE:
1528                         lexrc.next();
1529                         htmlstyle = lexrc.getLongString(from_ascii("EndHTMLStyle"));
1530                         break;
1531                 case FT_HTMLTAG:
1532                         lexrc.next();
1533                         htmltag = lexrc.getString();
1534                         break;
1535                 case FT_DOCBOOKATTR:
1536                         lexrc.next();
1537                         docbookattr = lexrc.getString();
1538                         break;
1539                 case FT_DOCBOOKTAG:
1540                         lexrc.next();
1541                         docbooktag = lexrc.getString();
1542                         break;
1543                 case FT_END:
1544                         getout = true;
1545                         break;
1546                 }
1547         }
1548
1549         lexrc.popTable();
1550
1551         // Here we have a full float if getout == true
1552         if (getout) {
1553                 if (!usesfloat && listcommand.empty()) {
1554                         // if this float uses the same auxfile as an existing one,
1555                         // there is no need for it to provide a list command.
1556                         bool found_ext = false;
1557                         for (auto const & f : floatlist_) {
1558                                 if (f.second.ext() == ext) {
1559                                         found_ext = true;
1560                                         break;
1561                                 }
1562                         }
1563                         if (!found_ext)
1564                                 LYXERR0("The layout does not provide a list command " <<
1565                                   "for the float `" << type << "'. LyX will " <<
1566                                   "not be able to produce a float list.");
1567                 }
1568                 Floating fl(type, placement, ext, within, style, name,
1569                         listname, listcommand, refprefix, allowed_placement,
1570                         htmltag, htmlattr, htmlstyle, docbooktag, docbookattr,
1571                         required, usesfloat, ispredefined, allowswide,
1572                         allowssideways);
1573                 floatlist_.newFloat(fl);
1574                 // each float has its own counter
1575                 counters_.newCounter(from_ascii(type), from_ascii(within),
1576                                 docstring(), docstring(),
1577                                 bformat(_("%1$s (Float)"), _(name)));
1578                 // also define sub-float counters
1579                 docstring const subtype = "sub-" + from_ascii(type);
1580                 counters_.newCounter(subtype, from_ascii(type),
1581                                 "\\alph{" + subtype + "}", docstring(),
1582                                  bformat(_("Sub-%1$s (Float)"), _(name)));
1583         }
1584         return getout;
1585 }
1586
1587
1588 bool TextClass::readOutlinerName(Lexer & lexrc)
1589 {
1590         std::string type;
1591         docstring name;
1592         if (lexrc.next())
1593                 type = lexrc.getString();
1594         else {
1595                 lexrc.printError("No type given for OutlinerName: `$$Token'.");
1596                 return false;
1597         }
1598         if (lexrc.next())
1599                 name = lexrc.getDocString();
1600         else {
1601                 lexrc.printError("No name given for OutlinerName: `$$Token'.");
1602                 return false;
1603         }
1604         outliner_names_[type] = name;
1605     return true;
1606 }
1607
1608
1609 string const & TextClass::prerequisites(string const & sep) const
1610 {
1611         if (contains(prerequisites_, ',')) {
1612                 vector<string> const pres = getVectorFromString(prerequisites_);
1613                 prerequisites_ = getStringFromVector(pres, sep);
1614         }
1615         return prerequisites_;
1616 }
1617
1618
1619 bool TextClass::hasLayout(docstring const & n) const
1620 {
1621         docstring const name = n.empty() ? defaultLayoutName() : n;
1622         return getLayout(name) != nullptr;
1623 }
1624
1625
1626 bool TextClass::hasInsetLayout(docstring const & n) const
1627 {
1628         if (n.empty())
1629                 return false;
1630         InsetLayouts::const_iterator it = insetlayoutlist_.find(n);
1631         return it != insetlayoutlist_.end();
1632 }
1633
1634
1635 Layout const & TextClass::operator[](docstring const & name) const
1636 {
1637         LATTEST(!name.empty());
1638
1639         Layout const * c = getLayout(name);
1640         if (!c) {
1641                 LYXERR0("We failed to find the layout '" << name
1642                        << "' in the layout list. You MUST investigate!");
1643                 for (auto const & lay : *this)
1644                         lyxerr  << " " << to_utf8(lay.name()) << endl;
1645
1646                 // We require the name to exist
1647                 static const Layout dummy;
1648                 LASSERT(false, return dummy);
1649         }
1650
1651         return *c;
1652 }
1653
1654
1655 Layout & TextClass::operator[](docstring const & name)
1656 {
1657         LATTEST(!name.empty());
1658         // Safe to continue, given what we do below.
1659
1660         Layout * c = getLayout(name);
1661         if (!c) {
1662                 LYXERR0("We failed to find the layout '" << to_utf8(name)
1663                        << "' in the layout list. You MUST investigate!");
1664                 for (auto const & lay : *this)
1665                         LYXERR0(" " << to_utf8(lay.name()));
1666
1667                 // we require the name to exist
1668                 LATTEST(false);
1669                 // we are here only in release mode
1670                 layoutlist_.push_back(createBasicLayout(name, true));
1671                 c = getLayout(name);
1672         }
1673
1674         return *c;
1675 }
1676
1677
1678 bool TextClass::deleteLayout(docstring const & name)
1679 {
1680         if (name == defaultLayoutName() || name == plainLayoutName())
1681                 return false;
1682
1683         LayoutList::iterator it =
1684                 remove_if(layoutlist_.begin(), layoutlist_.end(),
1685                         [name](const Layout &c) { return c.name() == name; });
1686
1687         LayoutList::iterator const end = layoutlist_.end();
1688         bool const ret = (it != end);
1689         layoutlist_.erase(it, end);
1690         return ret;
1691 }
1692
1693
1694 bool TextClass::deleteInsetLayout(docstring const & name)
1695 {
1696         return insetlayoutlist_.erase(name);
1697 }
1698
1699
1700 // Load textclass info if not loaded yet
1701 bool TextClass::load(string const & path) const
1702 {
1703         if (loaded_)
1704                 return true;
1705
1706         // Read style-file, provided path is searched before the system ones
1707         // If path is a file, it is loaded directly.
1708         FileName layout_file(path);
1709         if (!path.empty() && !layout_file.isReadableFile())
1710                 layout_file = FileName(addName(path, name_ + ".layout"));
1711         if (layout_file.empty() || !layout_file.exists())
1712                 layout_file = libFileSearch("layouts", name_, "layout");
1713         loaded_ = const_cast<TextClass*>(this)->read(layout_file);
1714
1715         if (!loaded_) {
1716                 lyxerr << "Error reading `"
1717                        << to_utf8(makeDisplayPath(layout_file.absFileName()))
1718                        << "'\n(Check `" << name_
1719                        << "')\nCheck your installation and "
1720                           "try Options/Reconfigure..."
1721                        << endl;
1722         }
1723
1724         return loaded_;
1725 }
1726
1727
1728 Layout const * TextClass::getLayout(docstring const & name) const
1729 {
1730         LayoutList::const_iterator cit =
1731                 find_if(begin(), end(),
1732                         [name](const Layout &c) { return c.name() == name; });
1733         if (cit == layoutlist_.end())
1734                 return nullptr;
1735
1736         return &(*cit);
1737 }
1738
1739
1740 Layout * TextClass::getLayout(docstring const & name)
1741 {
1742         LayoutList::iterator it =
1743                 find_if(layoutlist_.begin(), layoutlist_.end(),
1744                         [name](const Layout &c) { return c.name() == name; });
1745         if (it == layoutlist_.end())
1746                 return nullptr;
1747
1748         return &(*it);
1749 }
1750
1751
1752 bool DocumentClass::addLayoutIfNeeded(docstring const & n) const
1753 {
1754         if (hasLayout(n))
1755                 return false;
1756
1757         layoutlist_.push_back(createBasicLayout(n, true));
1758         return true;
1759 }
1760
1761
1762 string DocumentClass::forcedLayouts() const
1763 {
1764         ostringstream os;
1765         bool first = true;
1766         for (auto const & lay : *this) {
1767                 if (lay.forcelocal > 0) {
1768                         if (first) {
1769                                 os << "Format " << LAYOUT_FORMAT << '\n';
1770                                 first = false;
1771                         }
1772                         lay.write(os);
1773                 }
1774         }
1775         return os.str();
1776 }
1777
1778
1779 InsetLayout const & DocumentClass::insetLayout(docstring const & name) const
1780 {
1781         // FIXME The fix for the InsetLayout part of 4812 would be here:
1782         // Add the InsetLayout to the document class if it is not found.
1783         docstring n = name;
1784         InsetLayouts::const_iterator cen = insetlayoutlist_.end();
1785         while (!n.empty()) {
1786                 InsetLayouts::const_iterator cit = insetlayoutlist_.lower_bound(n);
1787                 if (cit != cen && cit->first == n) {
1788                         if (cit->second.obsoleted_by().empty())
1789                                 return cit->second;
1790                         n = cit->second.obsoleted_by();
1791                         return insetLayout(n);
1792                 }
1793                 // If we have a generic prefix (e.g., "Note:"),
1794                 // try if this one alone is found.
1795                 size_t i = n.find(':');
1796                 if (i == string::npos)
1797                         break;
1798                 n = n.substr(0, i);
1799         }
1800         // Layout "name" not found.
1801         return plainInsetLayout();
1802 }
1803
1804
1805 InsetLayout const & DocumentClass::plainInsetLayout() {
1806         static const InsetLayout plain_insetlayout_;
1807         return plain_insetlayout_;
1808 }
1809
1810
1811 docstring const & TextClass::defaultLayoutName() const
1812 {
1813         return defaultlayout_;
1814 }
1815
1816
1817 Layout const & TextClass::defaultLayout() const
1818 {
1819         return operator[](defaultLayoutName());
1820 }
1821
1822
1823 bool TextClass::isDefaultLayout(Layout const & layout) const
1824 {
1825         return layout.name() == defaultLayoutName();
1826 }
1827
1828
1829 bool TextClass::isPlainLayout(Layout const & layout) const
1830 {
1831         return layout.name() == plainLayoutName();
1832 }
1833
1834
1835 Layout TextClass::createBasicLayout(docstring const & name, bool unknown) const
1836 {
1837         static Layout * defaultLayout = nullptr;
1838
1839         if (defaultLayout) {
1840                 defaultLayout->setUnknown(unknown);
1841                 defaultLayout->setName(name);
1842                 return *defaultLayout;
1843         }
1844
1845         static char const * s = "Margin Static\n"
1846                         "LatexType Paragraph\n"
1847                         "LatexName dummy\n"
1848                         "Align Block\n"
1849                         "AlignPossible Left, Right, Center\n"
1850                         "LabelType No_Label\n"
1851                         "End";
1852         istringstream ss(s);
1853         Lexer lex(textClassTags);
1854         lex.setStream(ss);
1855         defaultLayout = new Layout;
1856         defaultLayout->setUnknown(unknown);
1857         defaultLayout->setName(name);
1858         if (!readStyle(lex, *defaultLayout)) {
1859                 // The only way this happens is because the hardcoded layout above
1860                 // is wrong.
1861                 LATTEST(false);
1862         };
1863         return *defaultLayout;
1864 }
1865
1866
1867 DocumentClassPtr getDocumentClass(
1868                 LayoutFile const & baseClass, LayoutModuleList const & modlist,
1869                 string const & cengine, bool const clone)
1870 {
1871         DocumentClassPtr doc_class =
1872             DocumentClassPtr(new DocumentClass(baseClass));
1873         for (auto const & mod : modlist) {
1874                 LyXModule * lm = theModuleList[mod];
1875                 if (!lm) {
1876                         docstring const msg =
1877                                                 bformat(_("The module %1$s has been requested by\n"
1878                                                 "this document but has not been found in the list of\n"
1879                                                 "available modules. If you recently installed it, you\n"
1880                                                 "probably need to reconfigure LyX.\n"), from_utf8(mod));
1881                         if (!clone)
1882                                 frontend::Alert::warning(_("Module not available"), msg);
1883                         continue;
1884                 }
1885                 if (!lm->isAvailable() && !clone) {
1886                         docstring const prereqs = from_utf8(getStringFromVector(lm->prerequisites(), "\n\t"));
1887                         docstring const msg =
1888                                 bformat(_("The module %1$s requires a package that is not\n"
1889                                         "available in your LaTeX installation, or a converter that\n"
1890                                         "you have not installed. LaTeX output may not be possible.\n"
1891                                         "Missing prerequisites:\n"
1892                                                 "\t%2$s\n"
1893                                         "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1894                                 from_utf8(mod), prereqs);
1895                         frontend::Alert::warning(_("Package not available"), msg, true);
1896                 }
1897                 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1898                 if (!doc_class->read(layout_file, TextClass::MODULE)) {
1899                         docstring const msg =
1900                                                 bformat(_("Error reading module %1$s\n"), from_utf8(mod));
1901                         frontend::Alert::warning(_("Read Error"), msg);
1902                 }
1903         }
1904
1905         if (cengine.empty())
1906                 return doc_class;
1907
1908         LyXCiteEngine * ce = theCiteEnginesList[cengine];
1909         if (!ce) {
1910                 docstring const msg =
1911                                         bformat(_("The cite engine %1$s has been requested by\n"
1912                                         "this document but has not been found in the list of\n"
1913                                         "available engines. If you recently installed it, you\n"
1914                                         "probably need to reconfigure LyX.\n"), from_utf8(cengine));
1915                 if (!clone)
1916                         frontend::Alert::warning(_("Cite Engine not available"), msg);
1917         } else if (!ce->isAvailable() && !clone) {
1918                 docstring const prereqs = from_utf8(getStringFromVector(ce->prerequisites(), "\n\t"));
1919                 docstring const msg =
1920                         bformat(_("The cite engine %1$s requires a package that is not\n"
1921                                 "available in your LaTeX installation, or a converter that\n"
1922                                 "you have not installed. LaTeX output may not be possible.\n"
1923                                 "Missing prerequisites:\n"
1924                                         "\t%2$s\n"
1925                                 "See section 3.1.2.3 (Modules) of the User's Guide for more information."),
1926                         from_utf8(cengine), prereqs);
1927                 frontend::Alert::warning(_("Package not available"), msg, true);
1928         } else {
1929                 FileName layout_file = libFileSearch("citeengines", ce->getFilename());
1930                 if (!doc_class->read(layout_file, TextClass::CITE_ENGINE)) {
1931                         docstring const msg =
1932                                                 bformat(_("Error reading cite engine %1$s\n"), from_utf8(cengine));
1933                         frontend::Alert::warning(_("Read Error"), msg);
1934                 }
1935         }
1936
1937         return doc_class;
1938 }
1939
1940
1941 /////////////////////////////////////////////////////////////////////////
1942 //
1943 // DocumentClass
1944 //
1945 /////////////////////////////////////////////////////////////////////////
1946
1947 DocumentClass::DocumentClass(LayoutFile const & tc)
1948         : TextClass(tc)
1949 {}
1950
1951
1952 bool DocumentClass::hasLaTeXLayout(std::string const & lay) const
1953 {
1954         for (auto const & l : layoutlist_)
1955                 if (l.latexname() == lay)
1956                         return true;
1957         return false;
1958 }
1959
1960
1961 bool DocumentClass::provides(string const & p) const
1962 {
1963         return provides_.find(p) != provides_.end();
1964 }
1965
1966
1967 bool DocumentClass::hasTocLevels() const
1968 {
1969         return min_toclevel_ != Layout::NOT_IN_TOC;
1970 }
1971
1972
1973 Layout const & DocumentClass::getTOCLayout() const
1974 {
1975         // we're going to look for the layout with the minimum toclevel
1976         int minlevel = 1000;
1977         Layout const * lay = nullptr;
1978         for (auto const & l : *this) {
1979                 int const level = l.toclevel;
1980                 // we don't want Part or unnumbered sections
1981                 if (level == Layout::NOT_IN_TOC || level < 0
1982                         || level >= minlevel || l.counter.empty())
1983                         continue;
1984                 lay = &l;
1985                 minlevel = level;
1986         }
1987         if (lay)
1988                 return *lay;
1989         // hmm. that is very odd, so we'll do our best.
1990         return operator[](defaultLayoutName());
1991 }
1992
1993
1994 Layout const & DocumentClass::htmlTOCLayout() const
1995 {
1996         if (html_toc_section_.empty())
1997                 html_toc_section_ = getTOCLayout().name();
1998         return operator[](html_toc_section_);
1999 }
2000
2001
2002 string const DocumentClass::getCiteFormat(CiteEngineType const & type,
2003         string const & entry, bool const punct, string const & fallback) const
2004 {
2005         string default_format = "{%fullnames:author%[[%fullnames:author%, ]][[{%fullnames:editor%[[%fullnames:editor%, ed., ]]}]]}"
2006                                 "\"%title%\"{%journal%[[, {!<i>!}%journal%{!</i>!}]][[{%publisher%[[, %publisher%]]"
2007                                 "[[{%institution%[[, %institution%]]}]]}]]}{%year%[[ (%year%)]]}{%pages%[[, %pages%]]}";
2008         if (punct)
2009                 default_format += ".";
2010
2011         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_formats_.find(type);
2012         if (itype == cite_formats_.end())
2013                 return default_format;
2014         map<string, string>::const_iterator it = itype->second.find(entry);
2015         if (it == itype->second.end() && !fallback.empty())
2016                 it = itype->second.find(fallback);
2017         if (it == itype->second.end())
2018                 return default_format;
2019         if (punct)
2020                 return it->second + ".";
2021         return it->second;
2022 }
2023
2024
2025 string const & DocumentClass::getCiteMacro(CiteEngineType const & type,
2026         string const & macro) const
2027 {
2028         static string empty;
2029         map<CiteEngineType, map<string, string> >::const_iterator itype = cite_macros_.find(type);
2030         if (itype == cite_macros_.end())
2031                 return empty;
2032         map<string, string>::const_iterator it = itype->second.find(macro);
2033         if (it == itype->second.end())
2034                 return empty;
2035         return it->second;
2036 }
2037
2038
2039 vector<string> const DocumentClass::citeCommands(
2040         CiteEngineType const & type) const
2041 {
2042         vector<CitationStyle> const styles = citeStyles(type);
2043         vector<string> cmds;
2044         for (auto const & cs : styles)
2045                 cmds.push_back(cs.name);
2046
2047         return cmds;
2048 }
2049
2050
2051 vector<CitationStyle> const & DocumentClass::citeStyles(
2052         CiteEngineType const & type) const
2053 {
2054         static vector<CitationStyle> empty;
2055         map<CiteEngineType, vector<CitationStyle> >::const_iterator it = cite_styles_.find(type);
2056         if (it == cite_styles_.end())
2057                 return empty;
2058         return it->second;
2059 }
2060
2061
2062 /////////////////////////////////////////////////////////////////////////
2063 //
2064 // PageSides
2065 //
2066 /////////////////////////////////////////////////////////////////////////
2067
2068 ostream & operator<<(ostream & os, PageSides p)
2069 {
2070         switch (p) {
2071         case OneSide:
2072                 os << '1';
2073                 break;
2074         case TwoSides:
2075                 os << '2';
2076                 break;
2077         }
2078         return os;
2079 }
2080
2081
2082 } // namespace lyx