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