Coverage Report

Created: 2026-10-17 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/xpdf-4.06/xpdf/Lexer.cc
Line
Count
Source
1
//========================================================================
2
//
3
// Lexer.cc
4
//
5
// Copyright 1996-2003 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
#include <aconf.h>
10
11
#include <stdlib.h>
12
#include <stddef.h>
13
#include <string.h>
14
#include <ctype.h>
15
#include "gmempp.h"
16
#include "Lexer.h"
17
#include "Error.h"
18
19
//------------------------------------------------------------------------
20
21
// A '1' in this array means the character is white space.  A '1' or
22
// '2' means the character ends a name or command.
23
static char specialChars[256] = {
24
  1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0,   // 0x
25
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 1x
26
  1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2,   // 2x
27
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0,   // 3x
28
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 4x
29
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0,   // 5x
30
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 6x
31
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0,   // 7x
32
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 8x
33
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // 9x
34
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // ax
35
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // bx
36
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // cx
37
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // dx
38
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   // ex
39
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0    // fx
40
};
41
42
//------------------------------------------------------------------------
43
// Lexer
44
//------------------------------------------------------------------------
45
46
611k
Lexer::Lexer(XRef *xref, Stream *str) {
47
611k
  Object obj;
48
49
611k
  curStr.initStream(str);
50
611k
  streams = new Array(xref);
51
611k
  streams->add(curStr.copy(&obj));
52
611k
  strPtr = 0;
53
611k
  freeArray = gTrue;
54
611k
  curStr.streamReset();
55
611k
}
56
57
48.3k
Lexer::Lexer(XRef *xref, Object *obj) {
58
48.3k
  Object obj2;
59
60
48.3k
  if (obj->isStream()) {
61
47.8k
    streams = new Array(xref);
62
47.8k
    freeArray = gTrue;
63
47.8k
    streams->add(obj->copy(&obj2));
64
47.8k
  } else {
65
523
    streams = obj->getArray();
66
523
    freeArray = gFalse;
67
523
  }
68
48.3k
  strPtr = 0;
69
48.3k
  if (streams->getLength() > 0) {
70
48.3k
    streams->get(strPtr, &curStr);
71
48.3k
    curStr.streamReset();
72
48.3k
  }
73
48.3k
}
74
75
659k
Lexer::~Lexer() {
76
659k
  if (!curStr.isNone()) {
77
328k
    curStr.streamClose();
78
328k
    curStr.free();
79
328k
  }
80
659k
  if (freeArray) {
81
658k
    delete streams;
82
658k
  }
83
659k
}
84
85
2.82G
int Lexer::getChar() {
86
2.82G
  int c;
87
88
2.82G
  c = EOF;
89
2.82G
  while (!curStr.isNone() && (c = curStr.streamGetChar()) == EOF) {
90
331k
    curStr.streamClose();
91
331k
    curStr.free();
92
331k
    ++strPtr;
93
331k
    if (strPtr < streams->getLength()) {
94
1.40k
      streams->get(strPtr, &curStr);
95
1.40k
      curStr.streamReset();
96
1.40k
    }
97
331k
  }
98
2.82G
  return c;
99
2.82G
}
100
101
482M
int Lexer::lookChar() {
102
482M
  if (curStr.isNone()) {
103
2.66k
    return EOF;
104
2.66k
  }
105
482M
  return curStr.streamLookChar();
106
482M
}
107
108
233M
Object *Lexer::getObj(Object *obj) {
109
233M
  char *p;
110
233M
  int c, c2;
111
233M
  GBool comment, neg, doubleMinus, done, invalid;
112
233M
  int numParen, nErrors;
113
233M
  int xi;
114
233M
  double xf, scale;
115
233M
  GString *s;
116
233M
  int n, m;
117
118
  // skip whitespace and comments
119
233M
  comment = gFalse;
120
1.69G
  while (1) {
121
1.69G
    if ((c = getChar()) == EOF) {
122
129M
      return obj->initEOF();
123
129M
    }
124
1.56G
    if (comment) {
125
24.0M
      if (c == '\r' || c == '\n')
126
299k
  comment = gFalse;
127
1.54G
    } else if (c == '%') {
128
301k
      comment = gTrue;
129
1.54G
    } else if (specialChars[c] != 1) {
130
103M
      break;
131
103M
    }
132
1.56G
  }
133
134
  // start reading token
135
103M
  switch (c) {
136
137
  // number
138
16.0M
  case '0': case '1': case '2': case '3': case '4':
139
21.8M
  case '5': case '6': case '7': case '8': case '9':
140
23.7M
  case '+': case '-': case '.':
141
    // Adobe's number lexer has some "interesting" behavior:
142
    // "--123" is interpreted as 0
143
    // "--123.4" is interpreted as -123.4 [I've seen this in the wild]
144
    // "50-100" is interpreted as 50 [I've seen this in the wild]
145
    // "50--100" is interpreted as 50
146
    // "50-100.0" is an error -- but older versions of Acrobat may
147
    //   have interpreted it as 50100.0 (?)
148
    // "50--100.0" is an error -- but older versions of Acrobat may
149
    //   have interpreted it as 50100.0 (?)
150
    // "50.0-100" is interpreted as 50.0 (or maybe 50.0100?)
151
    // "50.0--100" is interpreted as 50.0 (or maybe 50.0100?)
152
    // "-50-100" is interpreted as -50
153
    // "-" is interpreted as 0
154
    // "-." is interpreted as 0.0
155
23.7M
    neg = gFalse;
156
23.7M
    doubleMinus = gFalse;
157
23.7M
    xf = xi = 0;
158
23.7M
    if (c == '+') {
159
      // just ignore it
160
23.6M
    } else if (c == '-') {
161
956k
      neg = gTrue;
162
956k
      if (lookChar() == '-') {
163
14.4k
  doubleMinus = gTrue;
164
22.8k
  do {
165
22.8k
    getChar();
166
22.8k
  } while (lookChar() == '-');
167
14.4k
      }
168
22.7M
    } else if (c == '.') {
169
846k
      goto doReal;
170
21.8M
    } else {
171
21.8M
      xf = xi = c - '0';
172
21.8M
    }
173
63.8M
    while (1) {
174
63.8M
      c = lookChar();
175
63.8M
      if (isdigit(c)) {
176
40.9M
  getChar();
177
40.9M
  xi = xi * 10 + (c - '0');
178
40.9M
  if (xf < 1e20) {
179
33.1M
    xf = xf * 10 + (c - '0');
180
33.1M
  }
181
40.9M
      } else if (c == '.') {
182
2.83M
  getChar();
183
2.83M
  goto doReal;
184
20.0M
      } else {
185
20.0M
  break;
186
20.0M
      }
187
63.8M
    }
188
20.2M
    while ((c = lookChar()) == '-' || isdigit(c)) {
189
152k
      getChar();
190
152k
    }
191
20.0M
    if (neg) {
192
716k
      xi = -xi;
193
716k
    }
194
20.0M
    if (doubleMinus) {
195
12.6k
      xi = 0;
196
12.6k
    }
197
20.0M
    obj->initInt(xi);
198
20.0M
    break;
199
3.68M
  doReal:
200
3.68M
    scale = 0.1;
201
12.1M
    while (1) {
202
12.1M
      c = lookChar();
203
12.1M
      if (c == '-') {
204
38.2k
  error(errSyntaxWarning, getPos(), "Badly formatted number");
205
38.2k
  getChar();
206
38.2k
  continue;
207
38.2k
      }
208
12.1M
      if (!isdigit(c)) {
209
3.68M
  break;
210
3.68M
      }
211
8.42M
      getChar();
212
8.42M
      xf = xf + scale * (c - '0');
213
8.42M
      scale *= 0.1;
214
8.42M
    }
215
3.68M
    while ((c = lookChar()) == '-' || isdigit(c)) {
216
0
      getChar();
217
0
    }
218
3.68M
    if (neg) {
219
240k
      xf = -xf;
220
240k
    }
221
3.68M
    obj->initReal(xf);
222
3.68M
    break;
223
224
  // string
225
655k
  case '(':
226
655k
    p = tokBuf;
227
655k
    n = 0;
228
655k
    numParen = 1;
229
655k
    done = gFalse;
230
655k
    s = NULL;
231
655M
    do {
232
655M
      c2 = EOF;
233
655M
      switch (c = getChar()) {
234
235
14.8k
      case EOF:
236
14.8k
  error(errSyntaxError, getPos(), "Unterminated string");
237
14.8k
  done = gTrue;
238
14.8k
  break;
239
240
3.06M
      case '(':
241
3.06M
  ++numParen;
242
3.06M
  c2 = c;
243
3.06M
  break;
244
245
2.30M
      case ')':
246
2.30M
  if (--numParen == 0) {
247
639k
    done = gTrue;
248
1.66M
  } else {
249
1.66M
    c2 = c;
250
1.66M
  }
251
2.30M
  break;
252
253
9.25M
      case '\r':
254
  // The PDF spec says that any literal end-of-line sequence
255
  // (LF, CR, CR+LF) is translated to a single LF char.
256
9.25M
  c = lookChar();
257
9.25M
  if (c == '\n') {
258
179k
    getChar();
259
179k
  }
260
9.25M
  c2 = '\n';
261
9.25M
  break;
262
263
1.16M
      case '\\':
264
1.16M
  switch (c = getChar()) {
265
6.07k
  case 'n':
266
6.07k
    c2 = '\n';
267
6.07k
    break;
268
61.9k
  case 'r':
269
61.9k
    c2 = '\r';
270
61.9k
    break;
271
26.4k
  case 't':
272
26.4k
    c2 = '\t';
273
26.4k
    break;
274
8.05k
  case 'b':
275
8.05k
    c2 = '\b';
276
8.05k
    break;
277
4.69k
  case 'f':
278
4.69k
    c2 = '\f';
279
4.69k
    break;
280
30.1k
  case '\\':
281
67.6k
  case '(':
282
101k
  case ')':
283
101k
    c2 = c;
284
101k
    break;
285
80.9k
  case '0': case '1': case '2': case '3':
286
125k
  case '4': case '5': case '6': case '7':
287
125k
    c2 = c - '0';
288
125k
    c = lookChar();
289
125k
    if (c >= '0' && c <= '7') {
290
59.0k
      getChar();
291
59.0k
      c2 = (c2 << 3) + (c - '0');
292
59.0k
      c = lookChar();
293
59.0k
      if (c >= '0' && c <= '7') {
294
39.4k
        getChar();
295
39.4k
        c2 = (c2 << 3) + (c - '0');
296
39.4k
      }
297
59.0k
    }
298
125k
    break;
299
7.82k
  case '\r':
300
7.82k
    c = lookChar();
301
7.82k
    if (c == '\n') {
302
1.19k
      getChar();
303
1.19k
    }
304
7.82k
    break;
305
10.2k
  case '\n':
306
10.2k
    break;
307
964
  case EOF:
308
964
    error(errSyntaxError, getPos(), "Unterminated string");
309
964
    done = gTrue;
310
964
    break;
311
815k
  default:
312
815k
    c2 = c;
313
815k
    break;
314
1.16M
  }
315
1.16M
  break;
316
317
639M
      default:
318
639M
  c2 = c;
319
639M
  break;
320
655M
      }
321
322
655M
      if (c2 != EOF) {
323
654M
  if (n == tokBufSize) {
324
4.99M
    if (!s)
325
102k
      s = new GString(tokBuf, tokBufSize);
326
4.88M
    else
327
4.88M
      s->append(tokBuf, tokBufSize);
328
4.99M
    p = tokBuf;
329
4.99M
    n = 0;
330
4.99M
  }
331
654M
  *p++ = (char)c2;
332
654M
  ++n;
333
654M
      }
334
655M
    } while (!done);
335
655k
    if (!s)
336
552k
      s = new GString(tokBuf, n);
337
102k
    else
338
102k
      s->append(tokBuf, n);
339
655k
    obj->initString(s);
340
655k
    break;
341
342
  // name
343
10.0M
  case '/':
344
10.0M
    p = tokBuf;
345
10.0M
    n = 0;
346
10.0M
    s = NULL;
347
10.0M
    invalid = gFalse;
348
50.7M
    while ((c = lookChar()) != EOF && !specialChars[c]) {
349
40.7M
      getChar();
350
40.7M
      if (c == '#') {
351
854k
  c2 = lookChar();
352
854k
  if (c2 >= '0' && c2 <= '9') {
353
4.76k
    c = c2 - '0';
354
850k
  } else if (c2 >= 'A' && c2 <= 'F') {
355
49.6k
    c = c2 - 'A' + 10;
356
800k
  } else if (c2 >= 'a' && c2 <= 'f') {
357
100k
    c = c2 - 'a' + 10;
358
700k
  } else {
359
700k
    error(errSyntaxError, getPos(), "Invalid hex escape in name");
360
700k
    goto notEscChar;
361
700k
  }
362
154k
  getChar();
363
154k
  c2 = lookChar();
364
154k
  if (c2 >= '0' && c2 <= '9') {
365
28.8k
    c = (c << 4) + (c2 - '0');
366
125k
  } else if (c2 >= 'A' && c2 <= 'F') {
367
46.7k
    c = (c << 4) + (c2 - 'A' + 10);
368
79.0k
  } else if (c2 >= 'a' && c2 <= 'f') {
369
4.10k
    c = (c << 4) + (c2 - 'a' + 10);
370
74.9k
  } else {
371
74.9k
    error(errSyntaxError, getPos(), "Invalid hex escape in name");
372
74.9k
    goto notEscChar;
373
74.9k
  }
374
79.7k
  getChar();
375
79.7k
  if (c == 0) {
376
1.03k
    invalid = gTrue;
377
1.03k
  }
378
79.7k
      }
379
40.7M
     notEscChar:
380
      // the PDF spec claims that names are limited to 127 chars, but
381
      // Distiller 8 will produce longer names, and Acrobat 8 will
382
      // accept longer names
383
40.7M
      ++n;
384
40.7M
      if (n < tokBufSize) {
385
39.5M
  *p++ = (char)c;
386
39.5M
      } else if (n == tokBufSize) {
387
8.28k
  *p = (char)c;
388
8.28k
  s = new GString(tokBuf, n);
389
1.23M
      } else {
390
1.23M
  s->append((char)c);
391
1.23M
      }
392
40.7M
    }
393
10.0M
    if (invalid) {
394
1.03k
      error(errSyntaxError, getPos(), "Null character in name");
395
1.03k
      obj->initError();
396
1.03k
      if (s) {
397
315
  delete s;
398
315
      }
399
10.0M
    } else if (n < tokBufSize) {
400
10.0M
      *p = '\0';
401
10.0M
      obj->initName(tokBuf);
402
10.0M
    } else {
403
7.97k
      obj->initName(s->getCString());
404
7.97k
      delete s;
405
7.97k
    }
406
10.0M
    break;
407
408
  // array punctuation
409
10.2M
  case '[':
410
11.0M
  case ']':
411
11.0M
    tokBuf[0] = (char)c;
412
11.0M
    tokBuf[1] = '\0';
413
11.0M
    obj->initCmd(tokBuf);
414
11.0M
    break;
415
416
  // hex string or dict punctuation
417
19.2M
  case '<':
418
19.2M
    c = lookChar();
419
420
    // dict punctuation
421
19.2M
    if (c == '<') {
422
3.71M
      getChar();
423
3.71M
      tokBuf[0] = tokBuf[1] = '<';
424
3.71M
      tokBuf[2] = '\0';
425
3.71M
      obj->initCmd(tokBuf);
426
427
    // hex string
428
15.5M
    } else {
429
15.5M
      p = tokBuf;
430
15.5M
      m = n = 0;
431
15.5M
      c2 = 0;
432
15.5M
      s = NULL;
433
15.5M
      nErrors = 0;
434
97.9M
      while (nErrors < 100) {
435
97.7M
  c = getChar();
436
97.7M
  if (c == '>') {
437
15.3M
    break;
438
82.4M
  } else if (c == EOF) {
439
6.71k
    error(errSyntaxError, getPos(), "Unterminated hex string");
440
6.71k
    break;
441
82.3M
  } else if (specialChars[c] != 1) {
442
72.7M
    c2 = c2 << 4;
443
72.7M
    if (c >= '0' && c <= '9') {
444
12.1M
      c2 += c - '0';
445
60.5M
    } else if (c >= 'A' && c <= 'F') {
446
1.16M
      c2 += c - 'A' + 10;
447
59.4M
    } else if (c >= 'a' && c <= 'f') {
448
21.1M
      c2 += c - 'a' + 10;
449
38.2M
    } else {
450
38.2M
      error(errSyntaxError, getPos(),
451
38.2M
      "Illegal character <{0:02x}> in hex string", c);
452
38.2M
      ++nErrors;
453
38.2M
    }
454
72.7M
    if (++m == 2) {
455
29.0M
      if (n == tokBufSize) {
456
13.0k
        if (!s)
457
9.64k
    s = new GString(tokBuf, tokBufSize);
458
3.36k
        else
459
3.36k
    s->append(tokBuf, tokBufSize);
460
13.0k
        p = tokBuf;
461
13.0k
        n = 0;
462
13.0k
      }
463
29.0M
      *p++ = (char)c2;
464
29.0M
      ++n;
465
29.0M
      c2 = 0;
466
29.0M
      m = 0;
467
29.0M
    }
468
72.7M
  }
469
97.7M
      }
470
15.5M
      if (!s)
471
15.5M
  s = new GString(tokBuf, n);
472
9.64k
      else
473
9.64k
  s->append(tokBuf, n);
474
15.5M
      if (m == 1)
475
14.6M
  s->append((char)(c2 << 4));
476
15.5M
      obj->initString(s);
477
15.5M
    }
478
19.2M
    break;
479
480
  // dict punctuation
481
4.97M
  case '>':
482
4.97M
    c = lookChar();
483
4.97M
    if (c == '>') {
484
2.39M
      getChar();
485
2.39M
      tokBuf[0] = tokBuf[1] = '>';
486
2.39M
      tokBuf[2] = '\0';
487
2.39M
      obj->initCmd(tokBuf);
488
2.57M
    } else {
489
2.57M
      error(errSyntaxError, getPos(), "Illegal character '>'");
490
2.57M
      obj->initError();
491
2.57M
    }
492
4.97M
    break;
493
494
  // error
495
1.54M
  case ')':
496
2.21M
  case '{':
497
2.40M
  case '}':
498
2.40M
    error(errSyntaxError, getPos(), "Illegal character '{0:c}'", c);
499
2.40M
    obj->initError();
500
2.40M
    break;
501
502
  // command
503
31.2M
  default:
504
31.2M
    p = tokBuf;
505
31.2M
    *p++ = (char)c;
506
31.2M
    n = 1;
507
296M
    while ((c = lookChar()) != EOF && !specialChars[c]) {
508
266M
      getChar();
509
266M
      if (++n == tokBufSize) {
510
1.13M
  error(errSyntaxError, getPos(), "Command token too long");
511
1.13M
  break;
512
1.13M
      }
513
265M
      *p++ = (char)c;
514
265M
    }
515
31.2M
    *p = '\0';
516
31.2M
    if (tokBuf[0] == 't' && !strcmp(tokBuf, "true")) {
517
146k
      obj->initBool(gTrue);
518
31.1M
    } else if (tokBuf[0] == 'f' && !strcmp(tokBuf, "false")) {
519
13.8k
      obj->initBool(gFalse);
520
31.1M
    } else if (tokBuf[0] == 'n' && !strcmp(tokBuf, "null")) {
521
15.2k
      obj->initNull();
522
31.1M
    } else {
523
31.1M
      obj->initCmd(tokBuf);
524
31.1M
    }
525
31.2M
    break;
526
103M
  }
527
528
103M
  return obj;
529
103M
}
530
531
210k
void Lexer::skipToNextLine() {
532
210k
  int c;
533
534
555k
  while (1) {
535
555k
    c = getChar();
536
555k
    if (c == EOF || c == '\n') {
537
97.5k
      return;
538
97.5k
    }
539
457k
    if (c == '\r') {
540
113k
      if ((c = lookChar()) == '\n') {
541
100k
  getChar();
542
100k
      }
543
113k
      return;
544
113k
    }
545
457k
  }
546
210k
}
547
548
57.9k
void Lexer::skipToEOF() {
549
3.18M
  while (getChar() != EOF) ;
550
57.9k
}
551
552
245M
GBool Lexer::isSpace(int c) {
553
245M
  return c >= 0 && c <= 0xff && specialChars[c] == 1;
554
245M
}