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