]> git.lyx.org Git - lyx.git/blob - src/lyxlex_pimpl.C
8f9b86ff11c7f48d1c1bb2b09cbdf1f4da8ca3aa
[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 const 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 (!esc) {
146                 unsigned char c = 0; // getc() returns an int
147                 char cc = 0;
148                 status = 0;
149                 while (is && !status) {
150                         is.get(cc);
151                         c = cc;
152                         if (c == '#') {
153                                 // Read rest of line (fast :-)
154                                 is.getline(buff, sizeof(buff));
155                                 lyxerr[Debug::LYXLEX] << "Comment read: `" << c
156                                                       << buff << "'" << endl;
157                                 ++lineno;
158                                 continue;
159                         }
160                         
161                         if (c == '\"') {
162                                 int i = -1;
163                                 do {
164                                         is.get(cc);
165                                         c = cc;
166                                         if (c != '\r')
167                                                 buff[++i] = c;
168                                 } while (c != '\"' && c != '\n' && is &&
169                                          i != (LEX_MAX_BUFF - 2));
170                                 
171                                 if (i == (LEX_MAX_BUFF - 2)) {
172                                         printError("Line too long");
173                                         c = '\"'; // Pretend we got a "
174                                         ++i;
175                                 }
176                                 
177                                 if (c != '\"') {
178                                         printError("Missing quote");
179                                         if (c == '\n')
180                                                 ++lineno;
181                                 }
182                                 
183                                 buff[i] = '\0';
184                                 status = LEX_DATA;
185                                 break; 
186                         }
187                         
188                         if (c == ',')
189                                 continue;              /* Skip ','s */
190                         
191                                 // using relational operators with chars other
192                                 // than == and != is not safe. And if it is done
193                                 // the type _have_ to be unsigned. It usually a
194                                 // lot better to use the functions from cctype
195                         if (c > ' ' && is)  {
196                                 int i = 0;
197                                 do {
198                                         buff[i++] = c;
199                                         is.get(cc);
200                                         c = cc;
201                                 } while (c > ' ' && c != ',' && is
202                                          && (i != LEX_MAX_BUFF - 1) );
203                                 if (i == LEX_MAX_BUFF - 1) {
204                                         printError("Line too long");
205                                 }
206                                 buff[i] = '\0';
207                                 status = LEX_TOKEN;
208                         }
209                         
210                         if (c == '\r' && is) {
211                                 // The Windows support has lead to the
212                                 // possibility of "\r\n" at the end of
213                                 // a line.  This will stop LyX choking
214                                 // when it expected to find a '\n'
215                                 is.get(cc);
216                                 c = cc;
217                         }
218                         
219                         if (c == '\n')
220                                 ++lineno;
221                         
222                 }
223                 if (status) return true;
224                 
225                 status = is.eof() ? LEX_FEOF: LEX_UNDEF;
226                 buff[0] = '\0';
227                 return false;
228         } else {
229                 unsigned char c = 0; // getc() returns an int
230                 char cc = 0;
231                 
232                 status = 0;
233                 while (is && !status) {
234                         is.get(cc);
235                         c = cc;
236                         
237                         // skip ','s
238                         if (c == ',') continue;
239                         
240                         if (c == '\\') {
241                                 // escape
242                                 int i = 0;
243                                 do {
244                                         if (c == '\\') {
245                                                 // escape the next char
246                                                 is.get(cc);
247                                                 c = cc;
248                                         }
249                                         buff[i++] = c;
250                                         is.get(cc);
251                                         c = cc;
252                                 } while (c > ' ' && c != ',' && is
253                                          && (i != LEX_MAX_BUFF - 1) );
254                                 if (i == LEX_MAX_BUFF - 1) {
255                                         printError("Line too long");
256                                 }
257                                 buff[i] = '\0';
258                                 status = LEX_TOKEN;
259                                 continue;
260                         }
261                         
262                         if (c == '#') {
263                                 // Read rest of line (fast :-)
264                                 is.getline(buff, sizeof(buff));
265                                 lyxerr[Debug::LYXLEX] << "Comment read: `" << c
266                                                       << buff << "'" << endl;
267                                 ++lineno;
268                                 continue;
269                         }
270                         
271                         // string
272                         if (c == '\"') {
273                                 int i = -1;
274                                 bool escaped = false;
275                                 do {
276                                         escaped = false;
277                                         is.get(cc);
278                                         c = cc;
279                                         if (c == '\r') continue;
280                                         if (c == '\\') {
281                                                 // escape the next char
282                                                 is.get(cc);
283                                                 c = cc;
284                                                 escaped = true;
285                                         }
286                                         buff[++i] = c;
287                                         
288                                         if (!escaped && c == '\"') break;
289                                 } while (c != '\n' && is &&
290                                          i != (LEX_MAX_BUFF - 2));
291                                 
292                                 if (i == (LEX_MAX_BUFF - 2)) {
293                                         printError("Line too long");
294                                         c = '\"'; // Pretend we got a "
295                                         ++i;
296                                 }
297                                 
298                                 if (c != '\"') {
299                                         printError("Missing quote");
300                                         if (c == '\n')
301                                                 ++lineno;
302                                 }
303                                 
304                                 buff[i] = '\0';
305                                 status = LEX_DATA;
306                                 break; 
307                         }
308                         
309                         if (c > ' ' && is) {
310                                 int i = 0;
311                                 do {
312                                         if (c == '\\') {
313                                                 // escape the next char
314                                                 is.get(cc);
315                                                 c = cc;
316                                                 //escaped = true;
317                                         }
318                                         buff[i++] = c;
319                                         is.get(cc);
320                                         c = cc;
321                                 } while (c > ' ' && c != ',' && is
322                                          && (i != LEX_MAX_BUFF-1) );
323                                 if (i == LEX_MAX_BUFF-1) {
324                                         printError("Line too long");
325                                 }
326                                 buff[i] = '\0';
327                                 status = LEX_TOKEN;
328                         }
329                         // new line
330                         if (c == '\n')
331                                 ++lineno;
332                 }
333                 
334                 if (status) return true;
335                 
336                 status = is.eof() ? LEX_FEOF : LEX_UNDEF;
337                 buff[0] = '\0';
338                 return false;
339         }
340 }
341
342         ///
343 int LyXLex::Pimpl::search_kw(char const * const tag) const
344 {
345         keyword_item * res =
346                 lower_bound(table, table + no_items,
347                             tag, compare_tags());
348         if (res != table + no_items
349             && !compare_no_case(res->tag, tag))
350                 return res->code;
351         return LEX_UNDEF;
352 }
353
354
355 int LyXLex::Pimpl::lex()
356 {
357         //NOTE: possible bug.
358         if (next() && status == LEX_TOKEN)
359                 return search_kw(buff);
360         else
361                 return status;
362 }
363
364         
365 bool LyXLex::Pimpl::EatLine()
366 {
367         int i = 0;
368         unsigned char c = '\0';
369         char cc = 0;
370         while(is && c != '\n' && i != (LEX_MAX_BUFF - 1)) {
371                 is.get(cc);
372                 c = cc;
373                 lyxerr[Debug::LYXLEX] << "LyXLex::EatLine read char: `"
374                                       << c << "'" << endl;
375                 if (c != '\r')
376                         buff[i++] = c;
377         }
378         if (i == (LEX_MAX_BUFF - 1) && c != '\n') {
379                 printError("Line too long");
380                 c = '\n'; // Pretend we had an end of line
381                 --lineno; // but don't increase line counter (netto effect)
382                 ++i; // and preserve last character read.
383         }
384         if (c == '\n') {
385                 ++lineno;
386                 buff[--i] = '\0'; // i can never be 0 here, so no danger
387                 status = LEX_DATA;
388                 return true;
389         } else {
390                 buff[i] = '\0';
391                 return false;
392         }
393 }
394
395
396 bool LyXLex::Pimpl::nextToken()
397 {
398         status = 0;
399         while (is && !status) {
400                 unsigned char c = 0;
401                 char cc = 0;
402                 is.get(cc);
403                 c = cc;
404                 if (c >= ' ' && is) {
405                         int i = 0;
406                         if (c == '\\') { // first char == '\\'
407                                 do {
408                                         buff[i++] = c;
409                                         is.get(cc);
410                                         c = cc;
411                                 } while (c > ' ' && c != '\\' && is
412                                          && i != (LEX_MAX_BUFF-1));
413                         } else {
414                                 do {
415                                         buff[i++] = c;
416                                         is.get(cc);
417                                         c = cc;
418                                 } while (c >= ' ' && c != '\\' && is
419                                          && i != (LEX_MAX_BUFF-1));
420                         }
421                         
422                         if (i == (LEX_MAX_BUFF - 1)) {
423                                 printError("Line too long");
424                         }
425                         
426                         if (c == '\\') is.putback(c); // put it back
427                         buff[i] = '\0';
428                         status = LEX_TOKEN;
429                 }
430                 
431                 if (c == '\n')
432                         ++lineno;
433                 
434         }
435         if (status)  return true;
436         
437         status = is.eof() ? LEX_FEOF: LEX_UNDEF;
438         buff[0] = '\0';
439         return false;
440 }