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