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

Last change on this file since 461 was 461, checked in by Silvan Scherrer, 11 years ago

poppler update to 0.14.2

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