source: trunk/poppler/mypoppler/poppler/Lexer.cc @ 257

Last change on this file since 257 was 257, checked in by Eugene Romanenko, 13 years ago

PDF plugin: Poppler library updated to version 0.10.0

File size: 11.3 KB
Line 
1//========================================================================
2//
3// Lexer.cc
4//
5// Copyright 1996-2003 Glyph & Cog, LLC
6//
7//========================================================================
8
9//========================================================================
10//
11// Modified under the Poppler project - http://poppler.freedesktop.org
12//
13// All changes made under the Poppler project to this file are licensed
14// under GPL version 2 or later
15//
16// Copyright (C) 2006-2008 Albert Astals Cid <aacid@kde.org>
17// Copyright (C) 2006 Krzysztof Kowalczyk <kkowalczyk@gmail.com>
18//
19// To see a description of the changes please see the Changelog file that
20// came with your tarball or type make ChangeLog if you are building from git
21//
22//========================================================================
23
24#include <config.h>
25
26#ifdef USE_GCC_PRAGMAS
27#pragma implementation
28#endif
29
30#include <stdlib.h>
31#include <stddef.h>
32#include <string.h>
33#include <ctype.h>
34#include "Lexer.h"
35#include "Error.h"
36#include "XRef.h"
37
38//------------------------------------------------------------------------
39
40// A '1' in this array means the character is white space.  A '1' or
41// '2' means the character ends a name or command.
42static const char specialChars[256] = {
43  1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,   // 0x
44  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 1x
45  1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2,   // 2x
46  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0,   // 3x
47  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 4x
48  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0,   // 5x
49  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 6x
50  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0,   // 7x
51  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 8x
52  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 9x
53  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // ax
54  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // bx
55  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // cx
56  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // dx
57  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // ex
58  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0    // fx
59};
60
61//------------------------------------------------------------------------
62// Lexer
63//------------------------------------------------------------------------
64
65Lexer::Lexer(XRef *xrefA, Stream *str) {
66  Object obj;
67
68  lookCharLastValueCached = LOOK_VALUE_NOT_CACHED;
69  xref = xrefA;
70
71  curStr.initStream(str);
72  streams = new Array(xref);
73  streams->add(curStr.copy(&obj));
74  strPtr = 0;
75  freeArray = gTrue;
76  curStr.streamReset();
77}
78
79Lexer::Lexer(XRef *xrefA, Object *obj) {
80  Object obj2;
81
82  lookCharLastValueCached = LOOK_VALUE_NOT_CACHED;
83  xref = xrefA;
84
85  if (obj->isStream()) {
86    streams = new Array(xref);
87    freeArray = gTrue;
88    streams->add(obj->copy(&obj2));
89  } else {
90    streams = obj->getArray();
91    freeArray = gFalse;
92  }
93  strPtr = 0;
94  if (streams->getLength() > 0) {
95    streams->get(strPtr, &curStr);
96    curStr.streamReset();
97  }
98}
99
100Lexer::~Lexer() {
101  if (!curStr.isNone()) {
102    curStr.streamClose();
103    curStr.free();
104  }
105  if (freeArray) {
106    delete streams;
107  }
108}
109
110int Lexer::getChar(GBool comesFromLook) {
111  int c;
112
113  if (LOOK_VALUE_NOT_CACHED != lookCharLastValueCached) {
114    c = lookCharLastValueCached;
115    lookCharLastValueCached = LOOK_VALUE_NOT_CACHED;
116    return c;
117  }
118
119  c = EOF;
120  while (!curStr.isNone() && (c = curStr.streamGetChar()) == EOF) {
121    if (comesFromLook == gTrue) {
122      return EOF;
123    } else {
124      curStr.streamClose();
125      curStr.free();
126      ++strPtr;
127      if (strPtr < streams->getLength()) {
128        streams->get(strPtr, &curStr);
129        curStr.streamReset();
130      }
131    }
132  }
133  return c;
134}
135
136int Lexer::lookChar() {
137 
138  if (LOOK_VALUE_NOT_CACHED != lookCharLastValueCached) {
139    return lookCharLastValueCached;
140  }
141  lookCharLastValueCached = getChar(gTrue);
142  if (lookCharLastValueCached == EOF) {
143    lookCharLastValueCached = LOOK_VALUE_NOT_CACHED;
144    return EOF;
145  } else {
146    return lookCharLastValueCached;
147  }
148}
149
150Object *Lexer::getObj(Object *obj, int objNum) {
151  char *p;
152  int c, c2;
153  GBool comment, neg, done;
154  int numParen;
155  int xi;
156  double xf, scale;
157  GooString *s;
158  int n, m;
159
160  // skip whitespace and comments
161  comment = gFalse;
162  while (1) {
163    if ((c = getChar()) == EOF) {
164      return obj->initEOF();
165    }
166    if (comment) {
167      if (c == '\r' || c == '\n')
168        comment = gFalse;
169    } else if (c == '%') {
170      comment = gTrue;
171    } else if (specialChars[c] != 1) {
172      break;
173    }
174  }
175
176  // start reading token
177  switch (c) {
178
179  // number
180  case '0': case '1': case '2': case '3': case '4':
181  case '5': case '6': case '7': case '8': case '9':
182  case '-': case '.':
183    neg = gFalse;
184    xi = 0;
185    if (c == '-') {
186      neg = gTrue;
187    } else if (c == '.') {
188      goto doReal;
189    } else {
190      xi = c - '0';
191    }
192    while (1) {
193      c = lookChar();
194      if (isdigit(c)) {
195        getChar();
196        xi = xi * 10 + (c - '0');
197      } else if (c == '.') {
198        getChar();
199        goto doReal;
200      } else {
201        break;
202      }
203    }
204    if (neg)
205      xi = -xi;
206    obj->initInt(xi);
207    break;
208  doReal:
209    xf = xi;
210    scale = 0.1;
211    while (1) {
212      c = lookChar();
213      if (c == '-') {
214        // ignore minus signs in the middle of numbers to match
215        // Adobe's behavior
216        error(getPos(), "Badly formatted number");
217        getChar();
218        continue;
219      }
220      if (!isdigit(c)) {
221        break;
222      }
223      getChar();
224      xf = xf + scale * (c - '0');
225      scale *= 0.1;
226    }
227    if (neg)
228      xf = -xf;
229    obj->initReal(xf);
230    break;
231
232  // string
233  case '(':
234    p = tokBuf;
235    n = 0;
236    numParen = 1;
237    done = gFalse;
238    s = NULL;
239    do {
240      c2 = EOF;
241      switch (c = getChar()) {
242
243      case EOF:
244#if 0
245      // This breaks some PDF files, e.g., ones from Photoshop.
246      case '\r':
247      case '\n':
248#endif
249        error(getPos(), "Unterminated string");
250        done = gTrue;
251        break;
252
253      case '(':
254        ++numParen;
255        c2 = c;
256        break;
257
258      case ')':
259        if (--numParen == 0) {
260          done = gTrue;
261        } else {
262          c2 = c;
263        }
264        break;
265
266      case '\\':
267        switch (c = getChar()) {
268        case 'n':
269          c2 = '\n';
270          break;
271        case 'r':
272          c2 = '\r';
273          break;
274        case 't':
275          c2 = '\t';
276          break;
277        case 'b':
278          c2 = '\b';
279          break;
280        case 'f':
281          c2 = '\f';
282          break;
283        case '\\':
284        case '(':
285        case ')':
286          c2 = c;
287          break;
288        case '0': case '1': case '2': case '3':
289        case '4': case '5': case '6': case '7':
290          c2 = c - '0';
291          c = lookChar();
292          if (c >= '0' && c <= '7') {
293            getChar();
294            c2 = (c2 << 3) + (c - '0');
295            c = lookChar();
296            if (c >= '0' && c <= '7') {
297              getChar();
298              c2 = (c2 << 3) + (c - '0');
299            }
300          }
301          break;
302        case '\r':
303          c = lookChar();
304          if (c == '\n') {
305            getChar();
306          }
307          break;
308        case '\n':
309          break;
310        case EOF:
311          error(getPos(), "Unterminated string");
312          done = gTrue;
313          break;
314        default:
315          c2 = c;
316          break;
317        }
318        break;
319
320      default:
321        c2 = c;
322        break;
323      }
324
325      if (c2 != EOF) {
326        if (n == tokBufSize) {
327          if (!s)
328            s = new GooString(tokBuf, tokBufSize);
329          else
330            s->append(tokBuf, tokBufSize);
331          p = tokBuf;
332          n = 0;
333         
334          // we are growing see if the document is not malformed and we are growing too much
335          if (objNum > 0 && xref != NULL)
336          {
337            int newObjNum = xref->getNumEntry(curStr.streamGetPos());
338            if (newObjNum != objNum)
339            {
340              error(getPos(), "Unterminated string");
341              done = gTrue;
342              delete s;
343              n = -2;
344            }
345          }
346        }
347        *p++ = (char)c2;
348        ++n;
349      }
350    } while (!done);
351    if (n >= 0) {
352      if (!s)
353        s = new GooString(tokBuf, n);
354      else
355        s->append(tokBuf, n);
356      obj->initString(s);
357    } else {
358      obj->initEOF();
359    }
360    break;
361
362  // name
363  case '/':
364    p = tokBuf;
365    n = 0;
366    s = NULL;
367    while ((c = lookChar()) != EOF && !specialChars[c]) {
368      getChar();
369      if (c == '#') {
370        c2 = lookChar();
371        if (c2 >= '0' && c2 <= '9') {
372          c = c2 - '0';
373        } else if (c2 >= 'A' && c2 <= 'F') {
374          c = c2 - 'A' + 10;
375        } else if (c2 >= 'a' && c2 <= 'f') {
376          c = c2 - 'a' + 10;
377        } else {
378          goto notEscChar;
379        }
380        getChar();
381        c <<= 4;
382        c2 = getChar();
383        if (c2 >= '0' && c2 <= '9') {
384          c += c2 - '0';
385        } else if (c2 >= 'A' && c2 <= 'F') {
386          c += c2 - 'A' + 10;
387        } else if (c2 >= 'a' && c2 <= 'f') {
388          c += c2 - 'a' + 10;
389        } else {
390          error(getPos(), "Illegal digit in hex char in name");
391        }
392      }
393     notEscChar:
394      if (n == tokBufSize) {
395        if (!s)
396        {
397          error(getPos(), "Warning: name token is longer than what the specification says it can be");
398          s = new GooString(tokBuf, tokBufSize);
399        }
400        else
401        {
402          // the spec says 127 is the maximum, we are already at 256 so bail out
403          error(getPos(), "Name token too long");
404          break;
405        }
406        p = tokBuf;
407        n = 0;
408      }
409      *p++ = c;
410      ++n;
411    }
412    *p = '\0';
413    if (s) {
414      s->append(tokBuf, n);
415      obj->initName(s->getCString());
416      delete s;
417    } else obj->initName(tokBuf);
418    break;
419
420  // array punctuation
421  case '[':
422  case ']':
423    tokBuf[0] = c;
424    tokBuf[1] = '\0';
425    obj->initCmd(tokBuf);
426    break;
427
428  // hex string or dict punctuation
429  case '<':
430    c = lookChar();
431
432    // dict punctuation
433    if (c == '<') {
434      getChar();
435      tokBuf[0] = tokBuf[1] = '<';
436      tokBuf[2] = '\0';
437      obj->initCmd(tokBuf);
438
439    // hex string
440    } else {
441      p = tokBuf;
442      m = n = 0;
443      c2 = 0;
444      s = NULL;
445      while (1) {
446        c = getChar();
447        if (c == '>') {
448          break;
449        } else if (c == EOF) {
450          error(getPos(), "Unterminated hex string");
451          break;
452        } else if (specialChars[c] != 1) {
453          c2 = c2 << 4;
454          if (c >= '0' && c <= '9')
455            c2 += c - '0';
456          else if (c >= 'A' && c <= 'F')
457            c2 += c - 'A' + 10;
458          else if (c >= 'a' && c <= 'f')
459            c2 += c - 'a' + 10;
460          else
461            error(getPos(), "Illegal character <%02x> in hex string", c);
462          if (++m == 2) {
463            if (n == tokBufSize) {
464              if (!s)
465                s = new GooString(tokBuf, tokBufSize);
466              else
467                s->append(tokBuf, tokBufSize);
468              p = tokBuf;
469              n = 0;
470            }
471            *p++ = (char)c2;
472            ++n;
473            c2 = 0;
474            m = 0;
475          }
476        }
477      }
478      if (!s)
479        s = new GooString(tokBuf, n);
480      else
481        s->append(tokBuf, n);
482      if (m == 1)
483        s->append((char)(c2 << 4));
484      obj->initString(s);
485    }
486    break;
487
488  // dict punctuation
489  case '>':
490    c = lookChar();
491    if (c == '>') {
492      getChar();
493      tokBuf[0] = tokBuf[1] = '>';
494      tokBuf[2] = '\0';
495      obj->initCmd(tokBuf);
496    } else {
497      error(getPos(), "Illegal character '>'");
498      obj->initError();
499    }
500    break;
501
502  // error
503  case ')':
504  case '{':
505  case '}':
506    error(getPos(), "Illegal character '%c'", c);
507    obj->initError();
508    break;
509
510  // command
511  default:
512    p = tokBuf;
513    *p++ = c;
514    n = 1;
515    while ((c = lookChar()) != EOF && !specialChars[c]) {
516      getChar();
517      if (++n == tokBufSize) {
518        error(getPos(), "Command token too long");
519        break;
520      }
521      *p++ = c;
522    }
523    *p = '\0';
524    if (tokBuf[0] == 't' && !strcmp(tokBuf, "true")) {
525      obj->initBool(gTrue);
526    } else if (tokBuf[0] == 'f' && !strcmp(tokBuf, "false")) {
527      obj->initBool(gFalse);
528    } else if (tokBuf[0] == 'n' && !strcmp(tokBuf, "null")) {
529      obj->initNull();
530    } else {
531      obj->initCmd(tokBuf);
532    }
533    break;
534  }
535
536  return obj;
537}
538
539void Lexer::skipToNextLine() {
540  int c;
541
542  while (1) {
543    c = getChar();
544    if (c == EOF || c == '\n') {
545      return;
546    }
547    if (c == '\r') {
548      if ((c = lookChar()) == '\n') {
549        getChar();
550      }
551      return;
552    }
553  }
554}
555
556GBool Lexer::isSpace(int c) {
557  return c >= 0 && c <= 0xff && specialChars[c] == 1;
558}
Note: See TracBrowser for help on using the repository browser.