1 /**
2  * \file Lexer.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author John Levon
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13 
14 #include <config.h>
15 
16 #include "Lexer.h"
17 #include "Format.h"
18 
19 #include "support/convert.h"
20 #include "support/debug.h"
21 #include "support/FileName.h"
22 #include "support/filetools.h"
23 #include "support/gzstream.h"
24 #include "support/lassert.h"
25 #include "support/lstrings.h"
26 #include "support/lyxalgo.h"
27 #include "support/types.h"
28 
29 #include <functional>
30 #include <istream>
31 #include <stack>
32 #include <vector>
33 
34 using namespace std;
35 using namespace lyx::support;
36 
37 namespace lyx {
38 
39 //////////////////////////////////////////////////////////////////////
40 //
41 // Lexer::Pimpl
42 //
43 //////////////////////////////////////////////////////////////////////
44 
45 
46 ///
47 class Lexer::Pimpl {
48 public:
49 	///
50 	Pimpl(LexerKeyword * tab, int num);
51 	///
52 	string const getString() const;
53 	///
54 	docstring const getDocString() const;
55 	///
56 	void printError(string const & message) const;
57 	///
58 	void printTable(ostream & os);
59 	///
60 	void pushTable(LexerKeyword * tab, int num);
61 	///
62 	void popTable();
63 	///
64 	bool setFile(FileName const & filename);
65 	///
66 	void setStream(istream & i);
67 	///
68 	void setCommentChar(char c);
69 	///
70 	bool next(bool esc = false);
71 	///
72 	int searchKeyword(char const * const tag) const;
73 	///
74 	int lex();
75 	///
76 	bool eatLine();
77 	///
78 	bool nextToken();
79 	/// test if there is a pushed token or the stream is ok
80 	bool inputAvailable();
81 	///
82 	void pushToken(string const &);
83 	/// fb_ is only used to open files, the stream is accessed through is.
84 	filebuf fb_;
85 
86 	/// gz_ is only used to open files, the stream is accessed through is.
87 	gz::gzstreambuf gz_;
88 
89 	/// the stream that we use.
90 	istream is;
91 	///
92 	string name;
93 	///
94 	LexerKeyword * table;
95 	///
96 	int no_items;
97 	///
98 	string buff;
99 	///
100 	int status;
101 	///
102 	int lineno;
103 	///
104 	string pushTok;
105 	///
106 	char commentChar;
107 	/// used for error messages
108 	string context;
109 private:
110 	/// non-copyable
111 	Pimpl(Pimpl const &);
112 	void operator=(Pimpl const &);
113 
114 	///
115 	void verifyTable();
116 	///
117 	class PushedTable {
118 	public:
119 		///
PushedTable()120 		PushedTable()
121 			: table_elem(0), table_siz(0) {}
122 		///
PushedTable(LexerKeyword * ki,int siz)123 		PushedTable(LexerKeyword * ki, int siz)
124 			: table_elem(ki), table_siz(siz) {}
125 		///
126 		LexerKeyword * table_elem;
127 		///
128 		int table_siz;
129 	};
130 	///
131 	stack<PushedTable> pushed;
132 };
133 
134 
135 
136 namespace {
137 
138 class CompareTags
139 	: public binary_function<LexerKeyword, LexerKeyword, bool> {
140 public:
141 	// used by lower_bound, sort and sorted
operator ()(LexerKeyword const & a,LexerKeyword const & b) const142 	bool operator()(LexerKeyword const & a, LexerKeyword const & b) const
143 	{
144 		// we use the ascii version, because in turkish, 'i'
145 		// is not the lowercase version of 'I', and thus
146 		// turkish locale breaks parsing of tags.
147 		return compare_ascii_no_case(a.tag, b.tag) < 0;
148 	}
149 };
150 
151 } // namespace
152 
153 
Pimpl(LexerKeyword * tab,int num)154 Lexer::Pimpl::Pimpl(LexerKeyword * tab, int num)
155 	: is(&fb_), table(tab), no_items(num),
156 	  status(0), lineno(0), commentChar('#')
157 {
158 	verifyTable();
159 }
160 
161 
getString() const162 string const Lexer::Pimpl::getString() const
163 {
164 	return buff;
165 }
166 
167 
getDocString() const168 docstring const Lexer::Pimpl::getDocString() const
169 {
170 	return from_utf8(buff);
171 }
172 
173 
printError(string const & message) const174 void Lexer::Pimpl::printError(string const & message) const
175 {
176 	string const tmpmsg = subst(message, "$$Token", getString());
177 	lyxerr << "LyX: " << tmpmsg << " [around line " << lineno
178 		<< " of file " << to_utf8(makeDisplayPath(name))
179 		<< " current token: '" << getString() << "'"
180 		<< " context: '" << context << "']" << endl;
181 }
182 
183 
printTable(ostream & os)184 void Lexer::Pimpl::printTable(ostream & os)
185 {
186 	os << "\nNumber of tags: " << no_items << endl;
187 	for (int i= 0; i < no_items; ++i)
188 		os << "table[" << i
189 		   << "]:  tag: `" << table[i].tag
190 		   << "'  code:" << table[i].code << '\n';
191 	os.flush();
192 }
193 
194 
verifyTable()195 void Lexer::Pimpl::verifyTable()
196 {
197 	// Check if the table is sorted and if not, sort it.
198 	if (table
199 	    && !lyx::sorted(table, table + no_items, CompareTags())) {
200 		lyxerr << "The table passed to Lexer is not sorted!\n"
201 		       << "Tell the developers to fix it!" << endl;
202 		// We sort it anyway to avoid problems.
203 		lyxerr << "\nUnsorted:" << endl;
204 		printTable(lyxerr);
205 
206 		sort(table, table + no_items, CompareTags());
207 		lyxerr << "\nSorted:" << endl;
208 		printTable(lyxerr);
209 	}
210 }
211 
212 
pushTable(LexerKeyword * tab,int num)213 void Lexer::Pimpl::pushTable(LexerKeyword * tab, int num)
214 {
215 	PushedTable tmppu(table, no_items);
216 	pushed.push(tmppu);
217 
218 	table = tab;
219 	no_items = num;
220 
221 	verifyTable();
222 }
223 
224 
popTable()225 void Lexer::Pimpl::popTable()
226 {
227 	if (pushed.empty()) {
228 		lyxerr << "Lexer error: nothing to pop!" << endl;
229 		return;
230 	}
231 
232 	PushedTable tmp = pushed.top();
233 	pushed.pop();
234 	table = tmp.table_elem;
235 	no_items = tmp.table_siz;
236 }
237 
238 
setFile(FileName const & filename)239 bool Lexer::Pimpl::setFile(FileName const & filename)
240 {
241 	// Check the format of the file.
242 	if (theFormats().isZippedFile(filename)) {
243 		LYXERR(Debug::LYXLEX, "lyxlex: compressed");
244 		// The check only outputs a debug message, because it triggers
245 		// a bug in compaq cxx 6.2, where is_open() returns 'true' for
246 		// a fresh new filebuf.  (JMarc)
247 		if (gz_.is_open() || istream::off_type(is.tellg()) > -1)
248 			LYXERR(Debug::LYXLEX, "Error in LyXLex::setFile: "
249 				"file or stream already set.");
250 		gz_.open(filename.toFilesystemEncoding().c_str(), ios::in);
251 		is.rdbuf(&gz_);
252 		name = filename.absFileName();
253 		lineno = 0;
254 		if (!gz_.is_open() || !is.good())
255 			return false;
256 	} else {
257 		LYXERR(Debug::LYXLEX, "lyxlex: UNcompressed");
258 
259 		// The check only outputs a debug message, because it triggers
260 		// a bug in compaq cxx 6.2, where is_open() returns 'true' for
261 		// a fresh new filebuf.  (JMarc)
262 		if (fb_.is_open() || istream::off_type(is.tellg()) > 0) {
263 			LYXERR(Debug::LYXLEX, "Error in Lexer::setFile: "
264 				"file or stream already set.");
265 		}
266 		fb_.open(filename.toSafeFilesystemEncoding().c_str(), ios::in);
267 		is.rdbuf(&fb_);
268 		name = filename.absFileName();
269 		lineno = 0;
270 		if (!fb_.is_open() || !is.good())
271 			return false;
272 	}
273 
274 	// Skip byte order mark.
275 	if (is.peek() == 0xef) {
276 		is.get();
277 		if (is.peek() == 0xbb) {
278 			is.get();
279 			LASSERT(is.get() == 0xbf, /**/);
280 		} else
281 			is.unget();
282 	}
283 
284 	return true;
285 }
286 
287 
setStream(istream & i)288 void Lexer::Pimpl::setStream(istream & i)
289 {
290 	if (fb_.is_open() || istream::off_type(is.tellg()) > 0) {
291 		LYXERR(Debug::LYXLEX, "Error in Lexer::setStream: "
292 			"file or stream already set.");
293 	}
294 	is.rdbuf(i.rdbuf());
295 	lineno = 0;
296 }
297 
298 
setCommentChar(char c)299 void Lexer::Pimpl::setCommentChar(char c)
300 {
301 	commentChar = c;
302 }
303 
304 
next(bool esc)305 bool Lexer::Pimpl::next(bool esc /* = false */)
306 {
307 	if (!pushTok.empty()) {
308 		// There can have been a whole line pushed so
309 		// we extract the first word and leaves the rest
310 		// in pushTok. (Lgb)
311 		if (pushTok[0] == '\\' && pushTok.find(' ') != string::npos) {
312 			buff.clear();
313 			pushTok = split(pushTok, buff, ' ');
314 		} else {
315 			buff = pushTok;
316 			pushTok.clear();
317 		}
318 		status = LEX_TOKEN;
319 		return true;
320 	}
321 
322 
323 	char cc = 0;
324 	status = 0;
325 	while (is && !status) {
326 		is.get(cc);
327 		unsigned char c = cc;
328 
329 		if (c == commentChar) {
330 			// Read rest of line (fast :-)
331 #if 1
332 			// That is not fast... (Lgb)
333 			string dummy;
334 			getline(is, dummy);
335 
336 			LYXERR(Debug::LYXLEX, "Comment read: `" << c << dummy << '\'');
337 #else
338 			// unfortunately ignore is buggy (Lgb)
339 			is.ignore(100, '\n');
340 #endif
341 			++lineno;
342 			continue;
343 		}
344 
345 		if (c == '\"') {
346 			buff.clear();
347 
348 			if (esc) {
349 
350 				do {
351 					bool escaped = false;
352 					is.get(cc);
353 					c = cc;
354 					if (c == '\r') continue;
355 					if (c == '\\') {
356 						// escape the next char
357 						is.get(cc);
358 						c = cc;
359 						if (c == '\"' || c == '\\')
360 							escaped = true;
361 						else
362 							buff.push_back('\\');
363 					}
364 					buff.push_back(c);
365 
366 					if (!escaped && c == '\"')
367 						break;
368 				} while (c != '\n' && is);
369 
370 			} else {
371 
372 				do {
373 					is.get(cc);
374 					c = cc;
375 					if (c != '\r')
376 						buff.push_back(c);
377 				} while (c != '\"' && c != '\n' && is);
378 
379 			}
380 
381 			if (c != '\"') {
382 				printError("Missing quote");
383 				if (c == '\n')
384 					++lineno;
385 			}
386 
387 			buff.resize(buff.size() - 1);
388 			status = LEX_DATA;
389 			break;
390 		}
391 
392 		if (c == ',')
393 			continue;              /* Skip ','s */
394 
395 		// using relational operators with chars other
396 		// than == and != is not safe. And if it is done
397 		// the type _have_ to be unsigned. It usually a
398 		// lot better to use the functions from cctype
399 		if (c > ' ' && is)  {
400 			buff.clear();
401 
402 			do {
403 				if (esc && c == '\\') {
404 					// escape the next char
405 					is.get(cc);
406 					c = cc;
407 					//escaped = true;
408 				}
409 				buff.push_back(c);
410 				is.get(cc);
411 				c = cc;
412 			} while (c > ' ' && c != ',' && is);
413 			status = LEX_TOKEN;
414 		}
415 
416 		if (c == '\r' && is) {
417 			// The Windows support has lead to the
418 			// possibility of "\r\n" at the end of
419 			// a line.  This will stop LyX choking
420 			// when it expected to find a '\n'
421 			is.get(cc);
422 			c = cc;
423 		}
424 
425 		if (c == '\n')
426 			++lineno;
427 
428 	}
429 	if (status)
430 		return true;
431 
432 	status = is.eof() ? LEX_FEOF: LEX_UNDEF;
433 	buff.clear();
434 	return false;
435 }
436 
437 
searchKeyword(char const * const tag) const438 int Lexer::Pimpl::searchKeyword(char const * const tag) const
439 {
440 	LexerKeyword search_tag = { tag, 0 };
441 	LexerKeyword * res =
442 		lower_bound(table, table + no_items,
443 			    search_tag, CompareTags());
444 	// use the compare_ascii_no_case instead of compare_no_case,
445 	// because in turkish, 'i' is not the lowercase version of 'I',
446 	// and thus turkish locale breaks parsing of tags.
447 	if (res != table + no_items
448 	    && !compare_ascii_no_case(res->tag, tag))
449 		return res->code;
450 	return LEX_UNDEF;
451 }
452 
453 
lex()454 int Lexer::Pimpl::lex()
455 {
456 	//NOTE: possible bug.
457 	if (next() && status == LEX_TOKEN)
458 		return searchKeyword(getString().c_str());
459 	return status;
460 }
461 
462 
eatLine()463 bool Lexer::Pimpl::eatLine()
464 {
465 	buff.clear();
466 
467 	unsigned char c = '\0';
468 	char cc = 0;
469 	while (is && c != '\n') {
470 		is.get(cc);
471 		c = cc;
472 		//LYXERR(Debug::LYXLEX, "Lexer::EatLine read char: `" << c << '\'');
473 		if (c != '\r' && is)
474 			buff.push_back(c);
475 	}
476 
477 	if (c == '\n') {
478 		++lineno;
479 		buff.resize(buff.size() - 1);
480 		status = LEX_DATA;
481 		return true;
482 	} else if (buff.length() > 0) { // last line
483 		status = LEX_DATA;
484 		return true;
485 	} else {
486 		return false;
487 	}
488 }
489 
490 
nextToken()491 bool Lexer::Pimpl::nextToken()
492 {
493 	if (!pushTok.empty()) {
494 		// There can have been a whole line pushed so
495 		// we extract the first word and leaves the rest
496 		// in pushTok. (Lgb)
497 		if (pushTok[0] == '\\' && pushTok.find(' ') != string::npos) {
498 			buff.clear();
499 			pushTok = split(pushTok, buff, ' ');
500 		} else {
501 			buff = pushTok;
502 			pushTok.clear();
503 		}
504 		status = LEX_TOKEN;
505 		return true;
506 	}
507 
508 	status = 0;
509 	while (is && !status) {
510 		unsigned char c = 0;
511 		char cc = 0;
512 		is.get(cc);
513 		c = cc;
514 		if ((c >= ' ' || c == '\t') && is) {
515 			buff.clear();
516 
517 			if (c == '\\') { // first char == '\\'
518 				do {
519 					buff.push_back(c);
520 					is.get(cc);
521 					c = cc;
522 				} while (c > ' ' && c != '\\' && is);
523 			} else {
524 				do {
525 					buff.push_back(c);
526 					is.get(cc);
527 					c = cc;
528 				} while ((c >= ' ' || c == '\t') && c != '\\' && is);
529 			}
530 
531 			if (c == '\\')
532 				is.putback(c); // put it back
533 			status = LEX_TOKEN;
534 		}
535 
536 		if (c == '\n')
537 			++lineno;
538 
539 	}
540 	if (status)
541 		return true;
542 
543 	status = is.eof() ? LEX_FEOF: LEX_UNDEF;
544 	buff.clear();
545 	return false;
546 }
547 
548 
inputAvailable()549 bool Lexer::Pimpl::inputAvailable()
550 {
551 	return is.good();
552 }
553 
554 
pushToken(string const & pt)555 void Lexer::Pimpl::pushToken(string const & pt)
556 {
557 	pushTok = pt;
558 }
559 
560 
561 
562 
563 //////////////////////////////////////////////////////////////////////
564 //
565 // Lexer
566 //
567 //////////////////////////////////////////////////////////////////////
568 
Lexer()569 Lexer::Lexer()
570 	: pimpl_(new Pimpl(0, 0)), lastReadOk_(false)
571 {}
572 
573 
init(LexerKeyword * tab,int num)574 void Lexer::init(LexerKeyword * tab, int num)
575 {
576 	 pimpl_ = new Pimpl(tab, num);
577 }
578 
579 
~Lexer()580 Lexer::~Lexer()
581 {
582 	delete pimpl_;
583 }
584 
585 
isOK() const586 bool Lexer::isOK() const
587 {
588 	return pimpl_->inputAvailable();
589 }
590 
591 
setLineNumber(int l)592 void Lexer::setLineNumber(int l)
593 {
594 	pimpl_->lineno = l;
595 }
596 
597 
lineNumber() const598 int Lexer::lineNumber() const
599 {
600 	return pimpl_->lineno;
601 }
602 
603 
getStream()604 istream & Lexer::getStream()
605 {
606 	return pimpl_->is;
607 }
608 
609 
pushTable(LexerKeyword * tab,int num)610 void Lexer::pushTable(LexerKeyword * tab, int num)
611 {
612 	pimpl_->pushTable(tab, num);
613 }
614 
615 
popTable()616 void Lexer::popTable()
617 {
618 	pimpl_->popTable();
619 }
620 
621 
printTable(ostream & os)622 void Lexer::printTable(ostream & os)
623 {
624 	pimpl_->printTable(os);
625 }
626 
627 
printError(string const & message) const628 void Lexer::printError(string const & message) const
629 {
630 	pimpl_->printError(message);
631 }
632 
633 
setFile(FileName const & filename)634 bool Lexer::setFile(FileName const & filename)
635 {
636 	return pimpl_->setFile(filename);
637 }
638 
639 
setStream(istream & i)640 void Lexer::setStream(istream & i)
641 {
642 	pimpl_->setStream(i);
643 }
644 
645 
setCommentChar(char c)646 void Lexer::setCommentChar(char c)
647 {
648 	pimpl_->setCommentChar(c);
649 }
650 
651 
lex()652 int Lexer::lex()
653 {
654 	return pimpl_->lex();
655 }
656 
657 
getInteger() const658 int Lexer::getInteger() const
659 {
660 	lastReadOk_ = pimpl_->status == LEX_DATA || pimpl_->status == LEX_TOKEN;
661 	if (!lastReadOk_) {
662 		pimpl_->printError("integer token missing");
663 		return -1;
664 	}
665 
666 	if (isStrInt(pimpl_->getString()))
667 		return convert<int>(pimpl_->getString());
668 
669 	lastReadOk_ = false;
670 	pimpl_->printError("Bad integer `$$Token'");
671 	return -1;
672 }
673 
674 
getFloat() const675 double Lexer::getFloat() const
676 {
677 	// replace comma with dot in case the file was written with
678 	// the wrong locale (should be rare, but is easy enough to
679 	// avoid).
680 	lastReadOk_ = pimpl_->status == LEX_DATA || pimpl_->status == LEX_TOKEN;
681 	if (!lastReadOk_) {
682 		pimpl_->printError("float token missing");
683 		return -1;
684 	}
685 
686 	string const str = subst(pimpl_->getString(), ",", ".");
687 	if (isStrDbl(str))
688 		return convert<double>(str);
689 
690 	lastReadOk_ = false;
691 	pimpl_->printError("Bad float `$$Token'");
692 	return -1;
693 }
694 
695 
getString(bool trim) const696 string const Lexer::getString(bool trim) const
697 {
698 	lastReadOk_ = pimpl_->status == LEX_DATA || pimpl_->status == LEX_TOKEN;
699 
700 	if (lastReadOk_)
701 		return trim ? support::trim(pimpl_->getString(), "\t ") : pimpl_->getString();
702 
703 	return string();
704 }
705 
706 
getDocString(bool trim) const707 docstring const Lexer::getDocString(bool trim) const
708 {
709 	lastReadOk_ = pimpl_->status == LEX_DATA || pimpl_->status == LEX_TOKEN;
710 
711 	if (lastReadOk_)
712 		return trim ? support::trim(pimpl_->getDocString(), "\t ") : pimpl_->getDocString();
713 
714 	return docstring();
715 }
716 
717 
718 // I would prefer to give a tag number instead of an explicit token
719 // here, but it is not possible because Buffer::readDocument uses
720 // explicit tokens (JMarc)
getLongString(docstring const & endtoken)721 docstring Lexer::getLongString(docstring const & endtoken)
722 {
723 	docstring str;
724 	docstring prefix;
725 	bool firstline = true;
726 
727 	while (pimpl_->is) { //< eatLine only reads from is, not from pushTok
728 		if (!eatLine())
729 			// blank line in the file being read
730 			continue;
731 		docstring tmpstr = getDocString();
732 		docstring const token = trim(tmpstr, " \t");
733 
734 		LYXERR(Debug::PARSER, "LongString: `" << tmpstr << '\'');
735 
736 		// We do a case independent comparison, like searchKeyword does.
737 		if (compare_no_case(token, endtoken) == 0)
738 			break;
739 
740 		if (firstline) {
741 			size_t i = tmpstr.find_first_not_of(from_ascii(" \t"));
742 			if (i != string::npos)
743 				prefix = tmpstr.substr(0, i);
744 			firstline = false;
745 			LYXERR(Debug::PARSER, "Prefix = `" << prefix << "\'");
746 		}
747 
748 		// further lines in long strings may have the same
749 		// whitespace prefix as the first line. Remove it.
750 		if (!prefix.empty() && prefixIs(tmpstr, prefix))
751 			tmpstr.erase(0, prefix.length());
752 
753 		str += tmpstr + '\n';
754 	}
755 
756 	if (!pimpl_->is)
757 		printError("Long string not ended by `" + to_utf8(endtoken) + '\'');
758 
759 	return str;
760 }
761 
762 
getBool() const763 bool Lexer::getBool() const
764 {
765 	string const s = pimpl_->getString();
766 	if (s == "false" || s == "0") {
767 		lastReadOk_ = true;
768 		return false;
769 	}
770 	if (s == "true" || s == "1") {
771 		lastReadOk_ = true;
772 		return true;
773 	}
774 	pimpl_->printError("Bad boolean `$$Token'. "
775 				 "Use \"false\" or \"true\"");
776 	lastReadOk_ = false;
777 	return false;
778 }
779 
780 
eatLine()781 bool Lexer::eatLine()
782 {
783 	return pimpl_->eatLine();
784 }
785 
786 
next(bool esc)787 bool Lexer::next(bool esc)
788 {
789 	return pimpl_->next(esc);
790 }
791 
792 
nextToken()793 bool Lexer::nextToken()
794 {
795 	return pimpl_->nextToken();
796 }
797 
798 
pushToken(string const & pt)799 void Lexer::pushToken(string const & pt)
800 {
801 	pimpl_->pushToken(pt);
802 }
803 
804 
operator void const*() const805 Lexer::operator void const *() const
806 {
807 	// This behaviour is NOT the same as the streams which would
808 	// use fail() here. However, our implementation of getString() et al.
809 	// can cause the eof() and fail() bits to be set, even though we
810 	// haven't tried to read 'em.
811 	return lastReadOk_? this : 0;
812 }
813 
814 
operator !() const815 bool Lexer::operator!() const
816 {
817 	return !lastReadOk_;
818 }
819 
820 
operator >>(string & s)821 Lexer & Lexer::operator>>(string & s)
822 {
823 	if (isOK()) {
824 		next();
825 		s = getString();
826 	} else {
827 		lastReadOk_ = false;
828 	}
829 	return *this;
830 }
831 
832 
operator >>(docstring & s)833 Lexer & Lexer::operator>>(docstring & s)
834 {
835 	if (isOK()) {
836 		next();
837 		s = getDocString();
838 	} else {
839 		lastReadOk_ = false;
840 	}
841 	return *this;
842 }
843 
844 
operator >>(double & s)845 Lexer & Lexer::operator>>(double & s)
846 {
847 	if (isOK()) {
848 		next();
849 		s = getFloat();
850 	} else {
851 		lastReadOk_ = false;
852 	}
853 	return *this;
854 }
855 
856 
operator >>(int & s)857 Lexer & Lexer::operator>>(int & s)
858 {
859 	if (isOK()) {
860 		next();
861 		s = getInteger();
862 	} else {
863 		lastReadOk_ = false;
864 	}
865 	return *this;
866 }
867 
868 
operator >>(unsigned int & s)869 Lexer & Lexer::operator>>(unsigned int & s)
870 {
871 	if (isOK()) {
872 		next();
873 		s = getInteger();
874 	} else {
875 		lastReadOk_ = false;
876 	}
877 	return *this;
878 }
879 
880 
operator >>(bool & s)881 Lexer & Lexer::operator>>(bool & s)
882 {
883 	if (isOK()) {
884 		next();
885 		s = getBool();
886 	} else {
887 		lastReadOk_ = false;
888 	}
889 	return *this;
890 }
891 
892 
operator >>(char & c)893 Lexer & Lexer::operator>>(char & c)
894 {
895 	string s;
896 	operator>>(s);
897 	if (!s.empty())
898 		c = s[0];
899 	return *this;
900 }
901 
902 
903 // quotes a string, e.g. for use in preferences files or as an argument
904 // of the "log" dialog
quoteString(string const & arg)905 string Lexer::quoteString(string const & arg)
906 {
907 	string res;
908 	res += '"';
909 	res += subst(subst(arg, "\\", "\\\\"), "\"", "\\\"");
910 	res += '"';
911 	return res;
912 }
913 
914 
915 // same for docstring
quoteString(docstring const & arg)916 docstring Lexer::quoteString(docstring const & arg)
917 {
918 	docstring res;
919 	res += '"';
920 	res += subst(subst(arg, from_ascii("\\"), from_ascii("\\\\")),
921 		     from_ascii("\""), from_ascii("\\\""));
922 	res += '"';
923 	return res;
924 }
925 
926 
operator >>(char const * required)927 Lexer & Lexer::operator>>(char const * required)
928 {
929 	string token;
930 	*this >> token;
931 	if (token != required) {
932 		LYXERR0("Missing '" << required << "'-tag in " << pimpl_->context
933 			<< ". Got " << token << " instead. Line: " << lineNumber());
934 		pushToken(token);
935 	}
936 	return *this;
937 }
938 
939 
checkFor(char const * required)940 bool Lexer::checkFor(char const * required)
941 {
942 	string token;
943 	*this >> token;
944 	if (token == required)
945 		return true;
946 	pushToken(token);
947 	return false;
948 }
949 
950 
setContext(std::string const & str)951 void Lexer::setContext(std::string const & str)
952 {
953 	pimpl_->context = str;
954 }
955 
956 
957 } // namespace lyx
958