]> git.lyx.org Git - lyx.git/blob - src/lyxlex_pimpl.C
cosmetic fix
[lyx.git] / src / lyxlex_pimpl.C
1 #include <config.h>
2
3 #include "lyxlex_pimpl.h"
4 #include "debug.h"
5
6 #include "support/lyxalgo.h"
7 #include "support/filetools.h"
8 #include "support/lstrings.h"
9
10 #include <algorithm>
11
12 using namespace lyx::support;
13
14 using std::sort;
15 using std::ostream;
16 using std::ios;
17 using std::istream;
18 using std::endl;
19 using std::lower_bound;
20 using std::vector;
21 using std::getline;
22
23 // namespace {
24 struct compare_tags {
25         // used by lower_bound, sort and sorted
26         inline
27         int operator()(keyword_item const & a, keyword_item const & b) const {
28                 // we use the ascii version, because in turkish, 'i'
29                 // is not the lowercase version of 'I', and thus
30                 // turkish locale breaks parsing of tags.
31                 return compare_ascii_no_case(a.tag, b.tag) < 0;
32         }
33 };
34 // } // end of anon namespace
35
36
37 LyXLex::Pimpl::Pimpl(keyword_item * tab, int num)
38         : is(&fb__), table(tab), no_items(num),
39           status(0), lineno(0), commentChar('#')
40 {
41         verifyTable();
42 }
43
44
45 string const LyXLex::Pimpl::getString() const
46 {
47         return string(buff.begin(), buff.end());
48 }
49
50
51 void LyXLex::Pimpl::printError(string const & message) const
52 {
53         string const tmpmsg = subst(message, "$$Token", getString());
54         lyxerr << "LyX: " << tmpmsg << " [around line " << lineno
55                << " of file " << MakeDisplayPath(name) << ']' << endl;
56 }
57
58
59 void LyXLex::Pimpl::printTable(ostream & os)
60 {
61         os << "\nNumber of tags: " << no_items << '\n';
62         for (int i= 0; i < no_items; ++i)
63                 os << "table[" << i
64                    << "]:  tag: `" << table[i].tag
65                    << "'  code:" << table[i].code << '\n';
66         os.flush();
67 }
68
69
70 void LyXLex::Pimpl::verifyTable()
71 {
72         // Check if the table is sorted and if not, sort it.
73         if (table
74             && !lyx::sorted(table, table + no_items, compare_tags())) {
75                 lyxerr << "The table passed to LyXLex is not sorted!\n"
76                        << "Tell the developers to fix it!" << endl;
77                 // We sort it anyway to avoid problems.
78                 lyxerr << "\nUnsorted:\n";
79                 printTable(lyxerr);
80
81                 sort(table, table + no_items, compare_tags());
82                 lyxerr << "\nSorted:\n";
83                 printTable(lyxerr);
84         }
85 }
86
87
88 void LyXLex::Pimpl::pushTable(keyword_item * tab, int num)
89 {
90         pushed_table tmppu(table, no_items);
91         pushed.push(tmppu);
92
93         table = tab;
94         no_items = num;
95
96         verifyTable();
97 }
98
99
100 void LyXLex::Pimpl::popTable()
101 {
102         if (pushed.empty()) {
103                 lyxerr << "LyXLex error: nothing to pop!" << endl;
104                 return;
105         }
106
107         pushed_table tmp = pushed.top();
108         pushed.pop();
109         table = tmp.table_elem;
110         no_items = tmp.table_siz;
111 }
112
113
114 bool LyXLex::Pimpl::setFile(string const & filename)
115 {
116         // The check only outputs a debug message, because it triggers
117         // a bug in compaq cxx 6.2, where is_open() returns 'true' for a
118         // fresh new filebuf.  (JMarc)
119         if (fb__.is_open() || is.tellg() > 0)
120                 lyxerr[Debug::LYXLEX] << "Error in LyXLex::setFile: "
121                         "file or stream already set." << endl;
122         fb__.open(filename.c_str(), ios::in);
123         is.rdbuf(&fb__);
124         name = filename;
125         lineno = 0;
126         return fb__.is_open() && is.good();
127 }
128
129
130 void LyXLex::Pimpl::setStream(istream & i)
131 {
132         if (fb__.is_open() || is.tellg() > 0)
133                 lyxerr[Debug::LYXLEX]  << "Error in LyXLex::setStream: "
134                         "file or stream already set." << endl;
135         is.rdbuf(i.rdbuf());
136         lineno = 0;
137 }
138
139
140 void LyXLex::Pimpl::setCommentChar(char c)
141 {
142         commentChar = c;
143 }
144
145
146 bool LyXLex::Pimpl::next(bool esc /* = false */)
147 {
148         if (!pushTok.empty()) {
149                 // There can have been a whole line pushed so
150                 // we extract the first word and leaves the rest
151                 // in pushTok. (Lgb)
152                 if (pushTok.find(' ') != string::npos && pushTok[0] == '\\') {
153                         string tmp;
154                         pushTok = split(pushTok, tmp, ' ');
155                         buff.assign(tmp.begin(), tmp.end());
156                         return true;
157                 } else {
158                         buff.assign(pushTok.begin(), pushTok.end());
159                         pushTok.erase();
160                         return true;
161                 }
162         }
163         if (!esc) {
164                 unsigned char c = 0; // getc() returns an int
165                 char cc = 0;
166                 status = 0;
167                 while (is && !status) {
168                         is.get(cc);
169                         c = cc;
170                         if (c == commentChar) {
171                                 // Read rest of line (fast :-)
172 #if 1
173                                 // That is not fast... (Lgb)
174                                 string dummy;
175                                 getline(is, dummy);
176
177                                 lyxerr[Debug::LYXLEX] << "Comment read: `" << c
178                                                       << dummy << '\'' << endl;
179 #else
180                                 // unfortunately ignore is buggy (Lgb)
181                                 is.ignore(100, '\n');
182 #endif
183                                 ++lineno;
184                                 continue;
185                         }
186
187                         if (c == '\"') {
188                                 buff.clear();
189
190                                 do {
191                                         is.get(cc);
192                                         c = cc;
193                                         if (c != '\r')
194                                                 buff.push_back(c);
195                                 } while (c != '\"' && c != '\n' && is);
196
197                                 if (c != '\"') {
198                                         printError("Missing quote");
199                                         if (c == '\n')
200                                                 ++lineno;
201                                 }
202
203                                 buff.pop_back();
204                                 status = LEX_DATA;
205                                 break;
206                         }
207
208                         if (c == ',')
209                                 continue;              /* Skip ','s */
210
211                                 // using relational operators with chars other
212                                 // than == and != is not safe. And if it is done
213                                 // the type _have_ to be unsigned. It usually a
214                                 // lot better to use the functions from cctype
215                         if (c > ' ' && is)  {
216                                 buff.clear();
217
218                                 do {
219                                         buff.push_back(c);
220                                         is.get(cc);
221                                         c = cc;
222                                 } while (c > ' ' && c != ',' && is);
223
224                                 status = LEX_TOKEN;
225                         }
226
227                         if (c == '\r' && is) {
228                                 // The Windows support has lead to the
229                                 // possibility of "\r\n" at the end of
230                                 // a line.  This will stop LyX choking
231                                 // when it expected to find a '\n'
232                                 is.get(cc);
233                                 c = cc;
234                         }
235
236                         if (c == '\n')
237                                 ++lineno;
238
239                 }
240                 if (status) return true;
241
242                 status = is.eof() ? LEX_FEOF: LEX_UNDEF;
243                 buff.clear();
244                 return false;
245         } else {
246                 unsigned char c = 0; // getc() returns an int
247                 char cc = 0;
248
249                 status = 0;
250                 while (is && !status) {
251                         is.get(cc);
252                         c = cc;
253
254                         // skip ','s
255                         if (c == ',') continue;
256
257                         if (c == '\\') {
258                                 // escape
259                                 buff.clear();
260
261                                 do {
262                                         if (c == '\\') {
263                                                 // escape the next char
264                                                 is.get(cc);
265                                                 c = cc;
266                                         }
267                                         buff.push_back(c);
268                                         is.get(cc);
269                                         c = cc;
270                                 } while (c > ' ' && c != ',' && is);
271
272                                 status = LEX_TOKEN;
273                                 continue;
274                         }
275
276                         if (c == commentChar) {
277                                 // Read rest of line (fast :-)
278 #if 1
279                                 // That is still not fast... (Lgb)
280                                 string dummy;
281                                 getline(is, dummy);
282
283                                 lyxerr[Debug::LYXLEX] << "Comment read: `" << c
284                                                       << dummy << '\'' << endl;
285 #else
286                                 // but ignore is also still buggy (Lgb)
287                                 // This is fast (Lgb)
288                                 is.ignore(100, '\n');
289 #endif
290                                 ++lineno;
291                                 continue;
292                         }
293
294                         // string
295                         if (c == '\"') {
296                                 buff.clear();
297
298                                 bool escaped = false;
299                                 do {
300                                         escaped = false;
301                                         is.get(cc);
302                                         c = cc;
303                                         if (c == '\r') continue;
304                                         if (c == '\\') {
305                                                 // escape the next char
306                                                 is.get(cc);
307                                                 c = cc;
308                                                 if (c == '\"' || c == '\\')
309                                                         escaped = true;
310                                                 else
311                                                         buff.push_back('\\');
312                                         }
313                                         buff.push_back(c);
314
315                                         if (!escaped && c == '\"') break;
316                                 } while (c != '\n' && is);
317
318                                 if (c != '\"') {
319                                         printError("Missing quote");
320                                         if (c == '\n')
321                                                 ++lineno;
322                                 }
323
324                                 buff.pop_back();
325                                 status = LEX_DATA;
326                                 break;
327                         }
328
329                         if (c > ' ' && is) {
330                                 buff.clear();
331
332                                 do {
333                                         if (c == '\\') {
334                                                 // escape the next char
335                                                 is.get(cc);
336                                                 c = cc;
337                                                 //escaped = true;
338                                         }
339                                         buff.push_back(c);
340                                         is.get(cc);
341                                         c = cc;
342                                 } while (c > ' ' && c != ',' && is);
343
344                                 status = LEX_TOKEN;
345                         }
346                         // new line
347                         if (c == '\n')
348                                 ++lineno;
349                 }
350
351                 if (status) return true;
352
353                 status = is.eof() ? LEX_FEOF : LEX_UNDEF;
354                 buff.clear();
355                 return false;
356         }
357 }
358
359
360 int LyXLex::Pimpl::search_kw(char const * const tag) const
361 {
362         keyword_item search_tag = { tag, 0 };
363         keyword_item * res =
364                 lower_bound(table, table + no_items,
365                             search_tag, compare_tags());
366         // use the compare_ascii_no_case instead of compare_no_case,
367         // because in turkish, 'i' is not the lowercase version of 'I',
368         // and thus turkish locale breaks parsing of tags.
369         if (res != table + no_items
370             && !compare_ascii_no_case(res->tag, tag))
371                 return res->code;
372         return LEX_UNDEF;
373 }
374
375
376 int LyXLex::Pimpl::lex()
377 {
378         //NOTE: possible bug.
379         if (next() && status == LEX_TOKEN) {
380                 return search_kw(getString().c_str());
381         } else
382                 return status;
383 }
384
385
386 bool LyXLex::Pimpl::eatLine()
387 {
388         buff.clear();
389
390         unsigned char c = '\0';
391         char cc = 0;
392         while (is && c != '\n') {
393                 is.get(cc);
394                 c = cc;
395                 //lyxerr[Debug::LYXLEX] << "LyXLex::EatLine read char: `"
396                 //                    << c << '\'' << endl;
397                 if (c != '\r')
398                         buff.push_back(c);
399         }
400
401         if (c == '\n') {
402                 ++lineno;
403                 buff.pop_back();
404                 status = LEX_DATA;
405                 return true;
406         } else {
407                 return false;
408         }
409 }
410
411
412 bool LyXLex::Pimpl::nextToken()
413 {
414         if (!pushTok.empty()) {
415                 // There can have been a whole line pushed so
416                 // we extract the first word and leaves the rest
417                 // in pushTok. (Lgb)
418                 if (pushTok.find(' ') != string::npos && pushTok[0] == '\\') {
419                         string tmp;
420                         pushTok = split(pushTok, tmp, ' ');
421                         buff.assign(tmp.begin(), tmp.end());
422                         return true;
423                 } else {
424                         buff.assign(pushTok.begin(), pushTok.end());
425                         pushTok.erase();
426                         return true;
427                 }
428         }
429
430         status = 0;
431         while (is && !status) {
432                 unsigned char c = 0;
433                 char cc = 0;
434                 is.get(cc);
435                 c = cc;
436                 if (c >= ' ' && is) {
437                         buff.clear();
438
439                         if (c == '\\') { // first char == '\\'
440                                 do {
441                                         buff.push_back(c);
442                                         is.get(cc);
443                                         c = cc;
444                                 } while (c > ' ' && c != '\\' && is);
445                         } else {
446                                 do {
447                                         buff.push_back(c);
448                                         is.get(cc);
449                                         c = cc;
450                                 } while (c >= ' ' && c != '\\' && is);
451                         }
452
453                         if (c == '\\') is.putback(c); // put it back
454                         status = LEX_TOKEN;
455                 }
456
457                 if (c == '\n')
458                         ++lineno;
459
460         }
461         if (status)  return true;
462
463         status = is.eof() ? LEX_FEOF: LEX_UNDEF;
464         buff.clear();
465         return false;
466 }
467
468
469 void LyXLex::Pimpl::pushToken(string const & pt)
470 {
471         pushTok = pt;
472 }