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