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

Last change on this file since 277 was 277, checked in by rbri, 12 years ago

PDF plugin: Poppler library updated to version 0.12.3

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