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