Coverage Report

Created: 2026-07-16 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/xpdf-4.06/xpdf/XRef.cc
Line
Count
Source
1
//========================================================================
2
//
3
// XRef.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 <limits.h>
16
#include "gmem.h"
17
#include "gmempp.h"
18
#include "gfile.h"
19
#include "Object.h"
20
#include "Stream.h"
21
#include "Lexer.h"
22
#include "Parser.h"
23
#include "Dict.h"
24
#include "Error.h"
25
#include "ErrorCodes.h"
26
#include "XRef.h"
27
28
//------------------------------------------------------------------------
29
30
55.4k
#define xrefSearchSize 1024  // read this many bytes at end of file
31
        //   to look for 'startxref'
32
33
//------------------------------------------------------------------------
34
// Permission bits
35
//------------------------------------------------------------------------
36
37
17.7k
#define permPrint    (1<<2)
38
17.7k
#define permChange   (1<<3)
39
17.7k
#define permCopy     (1<<4)
40
17.7k
#define permNotes    (1<<5)
41
55.3k
#define defPermFlags 0xfffc
42
43
//------------------------------------------------------------------------
44
// XRefPosSet
45
//------------------------------------------------------------------------
46
47
class XRefPosSet {
48
public:
49
50
  XRefPosSet();
51
  ~XRefPosSet();
52
  void add(GFileOffset pos);
53
  GBool check(GFileOffset pos);
54
1.32k
  int getLength() { return len; }
55
1.38k
  GFileOffset get(int idx) { return tab[idx]; }
56
57
private:
58
59
  int find(GFileOffset pos);
60
61
  GFileOffset *tab;
62
  int size;
63
  int len;
64
};
65
66
1.32k
XRefPosSet::XRefPosSet() {
67
1.32k
  size = 16;
68
1.32k
  len = 0;
69
1.32k
  tab = (GFileOffset *)gmallocn(size, sizeof(GFileOffset));
70
1.32k
}
71
72
1.32k
XRefPosSet::~XRefPosSet() {
73
1.32k
  gfree(tab);
74
1.32k
}
75
76
1.39k
void XRefPosSet::add(GFileOffset pos) {
77
1.39k
  int i;
78
79
1.39k
  i = find(pos);
80
1.39k
  if (i < len && tab[i] == pos) {
81
0
    return;
82
0
  }
83
1.39k
  if (len == size) {
84
0
    if (size > INT_MAX / 2) {
85
0
      gMemError("Integer overflow in XRefPosSet::add()");
86
0
    }
87
0
    size *= 2;
88
0
    tab = (GFileOffset *)greallocn(tab, size, sizeof(GFileOffset));
89
0
  }
90
1.39k
  if (i < len) {
91
14
    memmove(&tab[i + 1], &tab[i], (len - i) * sizeof(GFileOffset));
92
14
  }
93
1.39k
  tab[i] = pos;
94
1.39k
  ++len;
95
1.39k
}
96
97
1.40k
GBool XRefPosSet::check(GFileOffset pos) {
98
1.40k
  int i;
99
100
1.40k
  i = find(pos);
101
1.40k
  return i < len && tab[i] == pos;
102
1.40k
}
103
104
2.79k
int XRefPosSet::find(GFileOffset pos) {
105
2.79k
  int a, b, m;
106
107
2.79k
  a = - 1;
108
2.79k
  b = len;
109
  // invariant: tab[a] < pos < tab[b]
110
2.96k
  while (b - a > 1) {
111
179
    m = (a + b) / 2;
112
179
    if (tab[m] < pos) {
113
124
      a = m;
114
124
    } else if (tab[m] > pos) {
115
46
      b = m;
116
46
    } else {
117
9
      return m;
118
9
    }
119
179
  }
120
2.78k
  return b;
121
2.79k
}
122
123
//------------------------------------------------------------------------
124
// ObjectStream
125
//------------------------------------------------------------------------
126
127
class ObjectStream {
128
public:
129
130
  // Create an object stream, using object number <objStrNum>,
131
  // generation 0.
132
  ObjectStream(XRef *xref, int objStrNumA, int recursion);
133
134
5.99k
  GBool isOk() { return ok; }
135
136
  ~ObjectStream();
137
138
  // Return the object number of this object stream.
139
29.4k
  int getObjStrNum() { return objStrNum; }
140
141
  // Get the <objIdx>th object from this stream, which should be
142
  // object number <objNum>, generation 0.
143
  Object *getObject(int objIdx, int objNum, Object *obj);
144
145
private:
146
147
  int objStrNum;    // object number of the object stream
148
  int nObjects;     // number of objects in the stream
149
  Object *objs;     // the objects (length = nObjects)
150
  int *objNums;     // the object numbers (length = nObjects)
151
  GBool ok;
152
};
153
154
5.99k
ObjectStream::ObjectStream(XRef *xref, int objStrNumA, int recursion) {
155
5.99k
  Stream *str;
156
5.99k
  Lexer *lexer;
157
5.99k
  Parser *parser;
158
5.99k
  int *offsets;
159
5.99k
  Object objStr, obj1, obj2;
160
5.99k
  int first, i;
161
162
5.99k
  objStrNum = objStrNumA;
163
5.99k
  nObjects = 0;
164
5.99k
  objs = NULL;
165
5.99k
  objNums = NULL;
166
5.99k
  ok = gFalse;
167
168
5.99k
  if (!xref->fetch(objStrNum, 0, &objStr, recursion)->isStream()) {
169
128
    goto err1;
170
128
  }
171
172
5.86k
  if (!objStr.streamGetDict()->lookup("N", &obj1, recursion)->isInt()) {
173
19
    obj1.free();
174
19
    goto err1;
175
19
  }
176
5.84k
  nObjects = obj1.getInt();
177
5.84k
  obj1.free();
178
5.84k
  if (nObjects <= 0) {
179
18
    goto err1;
180
18
  }
181
182
5.82k
  if (!objStr.streamGetDict()->lookup("First", &obj1, recursion)->isInt()) {
183
192
    obj1.free();
184
192
    goto err1;
185
192
  }
186
5.63k
  first = obj1.getInt();
187
5.63k
  obj1.free();
188
5.63k
  if (first < 0) {
189
39
    goto err1;
190
39
  }
191
192
  // this is an arbitrary limit to avoid integer overflow problems
193
  // in the 'new Object[nObjects]' call (Acrobat apparently limits
194
  // object streams to 100-200 objects)
195
5.59k
  if (nObjects > 1000000) {
196
0
    error(errSyntaxError, -1, "Too many objects in an object stream");
197
0
    goto err1;
198
0
  }
199
5.59k
  objs = new Object[nObjects];
200
5.59k
  objNums = (int *)gmallocn(nObjects, sizeof(int));
201
5.59k
  offsets = (int *)gmallocn(nObjects, sizeof(int));
202
203
  // parse the header: object numbers and offsets
204
5.59k
  objStr.streamReset();
205
5.59k
  obj1.initNull();
206
5.59k
  str = new EmbedStream(objStr.getStream(), &obj1, gTrue, first);
207
5.59k
  lexer = new Lexer(xref, str);
208
5.59k
  parser = new Parser(xref, lexer, gFalse);
209
53.4k
  for (i = 0; i < nObjects; ++i) {
210
48.7k
    parser->getObj(&obj1, gTrue);
211
48.7k
    parser->getObj(&obj2, gTrue);
212
48.7k
    if (!obj1.isInt() || !obj2.isInt()) {
213
452
      obj1.free();
214
452
      obj2.free();
215
452
      delete parser;
216
452
      gfree(offsets);
217
452
      goto err2;
218
452
    }
219
48.2k
    objNums[i] = obj1.getInt();
220
48.2k
    offsets[i] = obj2.getInt();
221
48.2k
    obj1.free();
222
48.2k
    obj2.free();
223
48.2k
    if (objNums[i] < 0 || offsets[i] < 0 ||
224
48.0k
  (i > 0 && offsets[i] < offsets[i-1])) {
225
428
      delete parser;
226
428
      gfree(offsets);
227
428
      goto err2;
228
428
    }
229
48.2k
  }
230
4.71k
  lexer->skipToEOF();
231
4.71k
  delete parser;
232
233
  // skip to the first object - this generally shouldn't be needed,
234
  // because offsets[0] is normally 0, but just in case...
235
4.71k
  if (offsets[0] > 0) {
236
58
    objStr.getStream()->discardChars(offsets[0]);
237
58
  }
238
239
  // parse the objects
240
41.9k
  for (i = 0; i < nObjects; ++i) {
241
37.2k
    obj1.initNull();
242
37.2k
    if (i == nObjects - 1) {
243
4.71k
      str = new EmbedStream(objStr.getStream(), &obj1, gFalse, 0);
244
32.5k
    } else {
245
32.5k
      str = new EmbedStream(objStr.getStream(), &obj1, gTrue,
246
32.5k
          offsets[i+1] - offsets[i]);
247
32.5k
    }
248
37.2k
    lexer = new Lexer(xref, str);
249
37.2k
    parser = new Parser(xref, lexer, gFalse);
250
37.2k
    parser->getObj(&objs[i]);
251
37.2k
    lexer->skipToEOF();
252
37.2k
    delete parser;
253
37.2k
  }
254
255
4.71k
  gfree(offsets);
256
4.71k
  ok = gTrue;
257
258
5.59k
 err2:
259
5.59k
  objStr.streamClose();
260
5.99k
 err1:
261
5.99k
  objStr.free();
262
5.99k
}
263
264
5.99k
ObjectStream::~ObjectStream() {
265
5.99k
  int i;
266
267
5.99k
  if (objs) {
268
207k
    for (i = 0; i < nObjects; ++i) {
269
202k
      objs[i].free();
270
202k
    }
271
5.59k
    delete[] objs;
272
5.59k
  }
273
5.99k
  gfree(objNums);
274
5.99k
}
275
276
21.5k
Object *ObjectStream::getObject(int objIdx, int objNum, Object *obj) {
277
21.5k
  if (objIdx < 0 || objIdx >= nObjects || objNum != objNums[objIdx]) {
278
22
    obj->initNull();
279
21.4k
  } else {
280
21.4k
    objs[objIdx].copy(obj);
281
21.4k
  }
282
21.5k
  return obj;
283
21.5k
}
284
285
//------------------------------------------------------------------------
286
// XRef
287
//------------------------------------------------------------------------
288
289
55.3k
XRef::XRef(BaseStream *strA, GBool repair) {
290
55.3k
  GFileOffset pos;
291
55.3k
  Object obj;
292
55.3k
  XRefPosSet *posSet;
293
55.3k
  int i;
294
295
55.3k
  ok = gTrue;
296
55.3k
  errCode = errNone;
297
55.3k
  repaired = gFalse;
298
55.3k
  size = 0;
299
55.3k
  last = -1;
300
55.3k
  entries = NULL;
301
55.3k
  lastStartxrefPos = 0;
302
55.3k
  xrefTablePos = NULL;
303
55.3k
  xrefTablePosLen = 0;
304
55.3k
  streamEnds = NULL;
305
55.3k
  streamEndsLen = 0;
306
7.14M
  for (i = 0; i < objStrCacheSize; ++i) {
307
7.08M
    objStrs[i] = NULL;
308
7.08M
    objStrLastUse[i] = 0;
309
7.08M
  }
310
55.3k
  objStrCacheLength = 0;
311
55.3k
  objStrTime = 0;
312
313
55.3k
  encrypted = gFalse;
314
55.3k
  permFlags = defPermFlags;
315
55.3k
  ownerPasswordOk = gFalse;
316
317
941k
  for (i = 0; i < xrefCacheSize; ++i) {
318
886k
    cache[i].num = -1;
319
886k
  }
320
321
55.3k
#if MULTITHREADED
322
55.3k
  gInitMutex(&objStrsMutex);
323
55.3k
  gInitMutex(&cacheMutex);
324
55.3k
#endif
325
326
55.3k
  str = strA;
327
55.3k
  start = str->getStart();
328
329
  // if the 'repair' flag is set, try to reconstruct the xref table
330
55.3k
  if (repair) {
331
27.6k
    if (!(ok = constructXRef())) {
332
9.40k
      errCode = errDamaged;
333
9.40k
      return;
334
9.40k
    }
335
18.2k
    repaired = gTrue;
336
337
  // if the 'repair' flag is not set, read the xref table
338
27.7k
  } else {
339
340
    // read the trailer
341
27.7k
    pos = getStartXref();
342
27.7k
    if (pos == 0) {
343
26.3k
      errCode = errDamaged;
344
26.3k
      ok = gFalse;
345
26.3k
      return;
346
26.3k
    }
347
348
    // read the xref table
349
1.32k
    posSet = new XRefPosSet();
350
1.39k
    while (readXRef(&pos, posSet, gFalse)) ;
351
1.32k
    xrefTablePosLen = posSet->getLength();
352
1.32k
    xrefTablePos = (GFileOffset *)gmallocn(xrefTablePosLen,
353
1.32k
             sizeof(GFileOffset));
354
2.71k
    for (i = 0; i < xrefTablePosLen; ++i)  {
355
1.38k
      xrefTablePos[i] = posSet->get(i);
356
1.38k
    }
357
1.32k
    delete posSet;
358
1.32k
    if (!ok) {
359
1.23k
      errCode = errDamaged;
360
1.23k
      return;
361
1.23k
    }
362
1.32k
  }
363
364
  // get the root dictionary (catalog) object
365
18.3k
  trailerDict.dictLookupNF("Root", &obj);
366
18.3k
  if (obj.isRef()) {
367
18.2k
    rootNum = obj.getRefNum();
368
18.2k
    rootGen = obj.getRefGen();
369
18.2k
    obj.free();
370
18.2k
  } else {
371
159
    obj.free();
372
159
    if (!(ok = constructXRef())) {
373
51
      errCode = errDamaged;
374
51
      return;
375
51
    }
376
159
  }
377
378
  // now set the trailer dictionary's xref pointer so we can fetch
379
  // indirect objects from it
380
18.3k
  trailerDict.getDict()->setXRef(this);
381
18.3k
}
382
383
55.2k
XRef::~XRef() {
384
55.2k
  int i;
385
386
939k
  for (i = 0; i < xrefCacheSize; ++i) {
387
884k
    if (cache[i].num >= 0) {
388
165k
      cache[i].obj.free();
389
165k
    }
390
884k
  }
391
55.2k
  gfree(entries);
392
55.2k
  trailerDict.free();
393
55.2k
  if (xrefTablePos) {
394
1.32k
    gfree(xrefTablePos);
395
1.32k
  }
396
55.2k
  if (streamEnds) {
397
15.5k
    gfree(streamEnds);
398
15.5k
  }
399
7.13M
  for (i = 0; i < objStrCacheSize; ++i) {
400
7.07M
    if (objStrs[i]) {
401
4.71k
      delete objStrs[i];
402
4.71k
    }
403
7.07M
  }
404
55.2k
#if MULTITHREADED
405
55.2k
  gDestroyMutex(&objStrsMutex);
406
55.2k
  gDestroyMutex(&cacheMutex);
407
55.2k
#endif
408
55.2k
}
409
410
// Read the 'startxref' position.
411
27.7k
GFileOffset XRef::getStartXref() {
412
27.7k
  char buf[xrefSearchSize+1];
413
27.7k
  char *p;
414
27.7k
  int n, i;
415
416
  // read last xrefSearchSize bytes
417
27.7k
  str->setPos(xrefSearchSize, -1);
418
27.7k
  n = str->getBlock(buf, xrefSearchSize);
419
27.7k
  buf[n] = '\0';
420
421
  // find startxref
422
21.6M
  for (i = n - 9; i >= 0; --i) {
423
21.6M
    if (!strncmp(&buf[i], "startxref", 9)) {
424
1.69k
      break;
425
1.69k
    }
426
21.6M
  }
427
27.7k
  if (i < 0) {
428
26.0k
    return 0;
429
26.0k
  }
430
4.22k
  for (p = &buf[i+9]; isspace(*p & 0xff); ++p) ;
431
1.69k
  lastXRefPos = strToFileOffset(p);
432
1.69k
  lastStartxrefPos = str->getPos() - n + i;
433
434
1.69k
  return lastXRefPos;
435
27.7k
}
436
437
// Read one xref table section.  Also reads the associated trailer
438
// dictionary, and returns the prev pointer (if any).  The [hybrid]
439
// flag is true when following the XRefStm link in a hybrid-reference
440
// file.
441
1.40k
GBool XRef::readXRef(GFileOffset *pos, XRefPosSet *posSet, GBool hybrid) {
442
1.40k
  Parser *parser;
443
1.40k
  Object obj;
444
1.40k
  GBool more;
445
1.40k
  char buf[100];
446
1.40k
  int n, i;
447
448
  // check for a loop in the xref tables
449
1.40k
  if (posSet->check(*pos)) {
450
9
    error(errSyntaxWarning, -1, "Infinite loop in xref table");
451
9
    return gFalse;
452
9
  }
453
1.39k
  posSet->add(*pos);
454
455
  // the xref data should either be "xref ..." (for an xref table) or
456
  // "nn gg obj << ... >> stream ..." (for an xref stream); possibly
457
  // preceded by whitespace
458
1.39k
  str->setPos(start + *pos);
459
1.39k
  n = str->getBlock(buf, 100);
460
3.67k
  for (i = 0; i < n && Lexer::isSpace(buf[i]); ++i) ;
461
462
  // parse an old-style xref table
463
1.39k
  if (!hybrid &&
464
1.38k
      i + 4 < n &&
465
1.22k
      buf[i] == 'x' && buf[i+1] == 'r' && buf[i+2] == 'e' && buf[i+3] == 'f' &&
466
190
      Lexer::isSpace(buf[i+4])) {
467
189
    more = readXRefTable(pos, i + 5, posSet);
468
469
  // parse an xref stream
470
1.20k
  } else {
471
1.20k
    obj.initNull();
472
1.20k
    parser = new Parser(NULL,
473
1.20k
         new Lexer(NULL,
474
1.20k
     str->makeSubStream(start + *pos, gFalse, 0, &obj)),
475
1.20k
         gTrue);
476
1.20k
    if (!parser->getObj(&obj, gTrue)->isInt()) {
477
780
      goto err;
478
780
    }
479
422
    obj.free();
480
422
    if (!parser->getObj(&obj, gTrue)->isInt()) {
481
173
      goto err;
482
173
    }
483
249
    obj.free();
484
249
    if (!parser->getObj(&obj, gTrue)->isCmd("obj")) {
485
19
      goto err;
486
19
    }
487
230
    obj.free();
488
230
    if (!parser->getObj(&obj)->isStream()) {
489
31
      goto err;
490
31
    }
491
199
    more = readXRefStream(obj.getStream(), pos);
492
199
    obj.free();
493
199
    delete parser;
494
199
  }
495
496
388
  return more;
497
498
1.00k
 err:
499
1.00k
  obj.free();
500
1.00k
  delete parser;
501
1.00k
  if (hybrid) {
502
0
    error(errSyntaxError, -1, "Invalid XRefStm link in trailer");
503
1.00k
  } else {
504
1.00k
    ok = gFalse;
505
1.00k
  }
506
1.00k
  return gFalse;
507
1.39k
}
508
509
189
GBool XRef::readXRefTable(GFileOffset *pos, int offset, XRefPosSet *posSet) {
510
189
  XRefEntry entry;
511
189
  Parser *parser;
512
189
  Object obj, obj2;
513
189
  char buf[6];
514
189
  GFileOffset off, pos2;
515
189
  GBool more;
516
189
  int first, n, digit, newSize, gen, i, c;
517
518
189
  str->setPos(start + *pos + offset);
519
520
421
  while (1) {
521
2.62k
    do {
522
2.62k
      c = str->getChar();
523
2.62k
    } while (Lexer::isSpace(c));
524
419
    if (c == 't') {
525
57
      if (str->getBlock(buf, 6) != 6 || memcmp(buf, "railer", 6)) {
526
11
  goto err1;
527
11
      }
528
46
      break;
529
57
    }
530
362
    if (c < '0' || c > '9') {
531
6
      goto err1;
532
6
    }
533
356
    first = 0;
534
921
    do {
535
921
      digit = c - '0';
536
921
      if (first > (INT_MAX - digit) / 10) {
537
5
  goto err1;
538
5
      }
539
916
      first = (first * 10) + digit;
540
916
      c = str->getChar();
541
916
    } while (c >= '0' && c <= '9');
542
351
    if (!Lexer::isSpace(c)) {
543
13
      goto err1;
544
13
    }
545
573
    do {
546
573
      c = str->getChar();
547
573
    } while (Lexer::isSpace(c));
548
338
    n = 0;
549
617
    do {
550
617
      digit = c - '0';
551
617
      if (n > (INT_MAX - digit) / 10) {
552
6
  goto err1;
553
6
      }
554
611
      n = (n * 10) + digit;
555
611
      c = str->getChar();
556
611
    } while (c >= '0' && c <= '9');
557
332
    if (!Lexer::isSpace(c)) {
558
22
      goto err1;
559
22
    }
560
310
    if (first > INT_MAX - n) {
561
7
      goto err1;
562
7
    }
563
303
    if (first + n > size) {
564
149
      newSize = size ? size : 512;
565
324
      do {
566
324
  if (newSize > INT_MAX / 2) {
567
2
    goto err1;
568
2
  }
569
322
  newSize <<= 1;
570
322
      } while (first + n > newSize);
571
147
      entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
572
19.0M
      for (i = size; i < newSize; ++i) {
573
19.0M
  entries[i].offset = (GFileOffset)-1;
574
19.0M
  entries[i].type = xrefEntryFree;
575
19.0M
      }
576
147
      size = newSize;
577
147
    }
578
872
    for (i = first; i < first + n; ++i) {
579
1.40k
      do {
580
1.40k
  c = str->getChar();
581
1.40k
      } while (Lexer::isSpace(c));
582
640
      off = 0;
583
5.12k
      do {
584
5.12k
  off = (off * 10) + (c - '0');
585
5.12k
  c = str->getChar();
586
5.12k
      } while (c >= '0' && c <= '9');
587
640
      if (!Lexer::isSpace(c)) {
588
34
  goto err1;
589
34
      }
590
606
      entry.offset = off;
591
954
      do {
592
954
  c = str->getChar();
593
954
      } while (Lexer::isSpace(c));
594
606
      gen = 0;
595
2.64k
      do {
596
2.64k
  gen = (gen * 10) + (c - '0');
597
2.64k
  c = str->getChar();
598
2.64k
      } while (c >= '0' && c <= '9');
599
606
      if (!Lexer::isSpace(c)) {
600
19
  goto err1;
601
19
      }
602
587
      entry.gen = gen;
603
708
      do {
604
708
  c = str->getChar();
605
708
      } while (Lexer::isSpace(c));
606
587
      if (c == 'n') {
607
527
  entry.type = xrefEntryUncompressed;
608
527
      } else if (c == 'f') {
609
47
  entry.type = xrefEntryFree;
610
47
      } else {
611
13
  goto err1;
612
13
      }
613
574
      c = str->getChar();
614
574
      if (!Lexer::isSpace(c)) {
615
3
  goto err1;
616
3
      }
617
571
      if (entries[i].offset == (GFileOffset)-1) {
618
419
  entries[i] = entry;
619
  // PDF files of patents from the IBM Intellectual Property
620
  // Network have a bug: the xref table claims to start at 1
621
  // instead of 0.
622
419
  if (i == 1 && first == 1 &&
623
3
      entries[1].offset == 0 && entries[1].gen == 65535 &&
624
1
      entries[1].type == xrefEntryFree) {
625
1
    i = first = 0;
626
1
    entries[0] = entries[1];
627
1
    entries[1].offset = (GFileOffset)-1;
628
1
  }
629
419
  if (i > last) {
630
68
    last = i;
631
68
  }
632
419
      }
633
571
    }
634
301
  }
635
636
  // read the trailer dictionary
637
48
  obj.initNull();
638
48
  parser = new Parser(NULL,
639
48
       new Lexer(NULL,
640
48
         str->makeSubStream(str->getPos(), gFalse, 0, &obj)),
641
48
       gTrue);
642
48
  parser->getObj(&obj);
643
48
  delete parser;
644
48
  if (!obj.isDict()) {
645
2
    obj.free();
646
2
    goto err1;
647
2
  }
648
649
  // get the 'Prev' pointer
650
  //~ this can be a 64-bit int (?)
651
46
  obj.getDict()->lookupNF("Prev", &obj2);
652
46
  if (obj2.isInt()) {
653
17
    *pos = (GFileOffset)(Guint)obj2.getInt();
654
17
    more = gTrue;
655
29
  } else if (obj2.isRef()) {
656
    // certain buggy PDF generators generate "/Prev NNN 0 R" instead
657
    // of "/Prev NNN"
658
0
    *pos = (GFileOffset)(Guint)obj2.getRefNum();
659
0
    more = gTrue;
660
29
  } else {
661
29
    more = gFalse;
662
29
  }
663
46
  obj2.free();
664
665
  // save the first trailer dictionary
666
46
  if (trailerDict.isNone()) {
667
33
    obj.copy(&trailerDict);
668
33
  }
669
670
  // check for an 'XRefStm' key
671
  //~ this can be a 64-bit int (?)
672
46
  if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) {
673
3
    pos2 = (GFileOffset)(Guint)obj2.getInt();
674
3
    readXRef(&pos2, posSet, gTrue);
675
3
    if (!ok) {
676
0
      obj2.free();
677
0
      obj.free();
678
0
      goto err1;
679
0
    }
680
3
  }
681
46
  obj2.free();
682
683
46
  obj.free();
684
46
  return more;
685
686
143
 err1:
687
143
  ok = gFalse;
688
143
  return gFalse;
689
46
}
690
691
199
GBool XRef::readXRefStream(Stream *xrefStr, GFileOffset *pos) {
692
199
  Dict *dict;
693
199
  int w[3];
694
199
  GBool more;
695
199
  Object obj, obj2, idx;
696
199
  int newSize, first, n, i;
697
698
199
  dict = xrefStr->getDict();
699
700
199
  if (!dict->lookupNF("Size", &obj)->isInt()) {
701
9
    goto err1;
702
9
  }
703
190
  newSize = obj.getInt();
704
190
  obj.free();
705
190
  if (newSize < 0) {
706
2
    goto err1;
707
2
  }
708
188
  if (newSize > size) {
709
146
    entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
710
71.5k
    for (i = size; i < newSize; ++i) {
711
71.4k
      entries[i].offset = (GFileOffset)-1;
712
71.4k
      entries[i].type = xrefEntryFree;
713
71.4k
    }
714
146
    size = newSize;
715
146
  }
716
717
188
  if (!dict->lookupNF("W", &obj)->isArray() ||
718
185
      obj.arrayGetLength() < 3) {
719
4
    goto err1;
720
4
  }
721
730
  for (i = 0; i < 3; ++i) {
722
549
    if (!obj.arrayGet(i, &obj2)->isInt()) {
723
3
      obj2.free();
724
3
      goto err1;
725
3
    }
726
546
    w[i] = obj2.getInt();
727
546
    obj2.free();
728
546
  }
729
181
  obj.free();
730
181
  if (w[0] < 0 || w[0] > 8 ||
731
178
      w[1] < 0 || w[1] > 8 ||
732
178
      w[2] < 0 || w[2] > 8) {
733
5
    goto err0;
734
5
  }
735
736
176
  xrefStr->reset();
737
176
  dict->lookupNF("Index", &idx);
738
176
  if (idx.isArray()) {
739
175
    for (i = 0; i+1 < idx.arrayGetLength(); i += 2) {
740
123
      if (!idx.arrayGet(i, &obj)->isInt()) {
741
6
  idx.free();
742
6
  goto err1;
743
6
      }
744
117
      first = obj.getInt();
745
117
      obj.free();
746
117
      if (!idx.arrayGet(i+1, &obj)->isInt()) {
747
3
  idx.free();
748
3
  goto err1;
749
3
      }
750
114
      n = obj.getInt();
751
114
      obj.free();
752
114
      if (first < 0 || n < 0 ||
753
113
    !readXRefStreamSection(xrefStr, w, first, n)) {
754
18
  idx.free();
755
18
  goto err0;
756
18
      }
757
114
    }
758
97
  } else {
759
97
    if (!readXRefStreamSection(xrefStr, w, 0, newSize)) {
760
37
      idx.free();
761
37
      goto err0;
762
37
    }
763
97
  }
764
112
  idx.free();
765
766
  //~ this can be a 64-bit int (?)
767
112
  dict->lookupNF("Prev", &obj);
768
112
  if (obj.isInt()) {
769
59
    *pos = (GFileOffset)(Guint)obj.getInt();
770
59
    more = gTrue;
771
59
  } else {
772
53
    more = gFalse;
773
53
  }
774
112
  obj.free();
775
112
  if (trailerDict.isNone()) {
776
78
    trailerDict.initDict(dict);
777
78
  }
778
779
112
  return more;
780
781
27
 err1:
782
27
  obj.free();
783
87
 err0:
784
87
  ok = gFalse;
785
87
  return gFalse;
786
27
}
787
788
210
GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
789
210
  long long type, gen, offset;
790
210
  int c, newSize, i, j;
791
792
210
  if (first + n < 0) {
793
0
    return gFalse;
794
0
  }
795
210
  if (first + n > size) {
796
27
    for (newSize = size ? 2 * size : 1024;
797
138
   first + n > newSize && newSize > 0;
798
111
   newSize <<= 1) ;
799
27
    if (newSize < 0) {
800
0
      return gFalse;
801
0
    }
802
27
    entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
803
11.4M
    for (i = size; i < newSize; ++i) {
804
11.4M
      entries[i].offset = (GFileOffset)-1;
805
11.4M
      entries[i].type = xrefEntryFree;
806
11.4M
    }
807
27
    size = newSize;
808
27
  }
809
5.32k
  for (i = first; i < first + n; ++i) {
810
5.16k
    if (w[0] == 0) {
811
1.36k
      type = 1;
812
3.80k
    } else {
813
7.67k
      for (type = 0, j = 0; j < w[0]; ++j) {
814
3.88k
  if ((c = xrefStr->getChar()) == EOF) {
815
15
    return gFalse;
816
15
  }
817
3.86k
  type = (type << 8) + c;
818
3.86k
      }
819
3.80k
    }
820
12.9k
    for (offset = 0, j = 0; j < w[1]; ++j) {
821
7.81k
      if ((c = xrefStr->getChar()) == EOF) {
822
8
  return gFalse;
823
8
      }
824
7.81k
      offset = (offset << 8) + c;
825
7.81k
    }
826
5.14k
    if (offset < 0 || offset > GFILEOFFSET_MAX) {
827
2
      return gFalse;
828
2
    }
829
10.3k
    for (gen = 0, j = 0; j < w[2]; ++j) {
830
5.17k
      if ((c = xrefStr->getChar()) == EOF) {
831
10
  return gFalse;
832
10
      }
833
5.16k
      gen = (gen << 8) + c;
834
5.16k
    }
835
    // some PDF generators include a free entry with gen=0xffffffff
836
5.12k
    if ((gen < 0 || gen > INT_MAX) && type != 0) {
837
3
      return gFalse;
838
3
    }
839
5.12k
    if (entries[i].offset == (GFileOffset)-1) {
840
4.91k
      switch (type) {
841
232
      case 0:
842
232
  entries[i].offset = (GFileOffset)offset;
843
232
  entries[i].gen = (int)gen;
844
232
  entries[i].type = xrefEntryFree;
845
232
  break;
846
2.13k
      case 1:
847
2.13k
  entries[i].offset = (GFileOffset)offset;
848
2.13k
  entries[i].gen = (int)gen;
849
2.13k
  entries[i].type = xrefEntryUncompressed;
850
2.13k
  break;
851
2.53k
      case 2:
852
2.53k
  entries[i].offset = (GFileOffset)offset;
853
2.53k
  entries[i].gen = (int)gen;
854
2.53k
  entries[i].type = xrefEntryCompressed;
855
2.53k
  break;
856
16
      default:
857
16
  return gFalse;
858
4.91k
      }
859
4.90k
      if (i > last) {
860
3.00k
  last = i;
861
3.00k
      }
862
4.90k
    }
863
5.12k
  }
864
865
156
  return gTrue;
866
210
}
867
868
// Attempt to construct an xref table for a damaged file.
869
27.7k
GBool XRef::constructXRef() {
870
27.7k
  int *streamObjNums = NULL;
871
27.7k
  int streamObjNumsLen = 0;
872
27.7k
  int streamObjNumsSize = 0;
873
27.7k
  int lastObjNum = -1;
874
27.7k
  rootNum = -1;
875
27.7k
  int streamEndsSize = 0;
876
27.7k
  streamEndsLen = 0;
877
27.7k
  char buf[4096 + 1];
878
27.7k
  str->reset();
879
27.7k
  GFileOffset bufPos = start;
880
27.7k
  char *p = buf;
881
27.7k
  char *end = buf;
882
27.7k
  GBool startOfLine = gTrue;
883
27.7k
  GBool space = gTrue;
884
27.7k
  GBool eof = gFalse;
885
251M
  while (1) {
886
251M
    if (end - p < 256 && !eof) {
887
87.4k
      memcpy(buf, p, end - p);
888
87.4k
      bufPos += p - buf;
889
87.4k
      p = buf + (end - p);
890
87.4k
      int n = (int)(buf + 4096 - p);
891
87.4k
      int m = str->getBlock(p, n);
892
87.4k
      end = p + m;
893
87.4k
      *end = '\0';
894
87.4k
      p = buf;
895
87.4k
      eof = m < n;
896
87.4k
    }
897
251M
    if (p == end && eof) {
898
27.7k
      break;
899
27.7k
    }
900
251M
    if (startOfLine && !strncmp(p, "trailer", 7)) {
901
32.3k
      constructTrailerDict((GFileOffset)(bufPos + (p + 7 - buf)));
902
32.3k
      p += 7;
903
32.3k
      startOfLine = gFalse;
904
32.3k
      space = gFalse;
905
251M
    } else if (startOfLine && !strncmp(p, "endstream", 9)) {
906
86.8k
      if (streamEndsLen == streamEndsSize) {
907
15.7k
  streamEndsSize += 64;
908
15.7k
  streamEnds = (GFileOffset *)greallocn(streamEnds, streamEndsSize,
909
15.7k
                sizeof(GFileOffset));
910
15.7k
      }
911
86.8k
      streamEnds[streamEndsLen++] = (GFileOffset)(bufPos + (p - buf));
912
86.8k
      p += 9;
913
86.8k
      startOfLine = gFalse;
914
86.8k
      space = gFalse;
915
251M
    } else if (space && *p >= '0' && *p <= '9') {
916
3.88M
      p = constructObjectEntry(p, (GFileOffset)(bufPos + (p - buf)),
917
3.88M
             &lastObjNum);
918
3.88M
      startOfLine = gFalse;
919
3.88M
      space = gFalse;
920
247M
    } else if (p[0] == '>' && p[1] == '>') {
921
826k
      p += 2;
922
826k
      startOfLine = gFalse;
923
826k
      space = gFalse;
924
      // skip any PDF whitespace except for '\0'
925
1.35M
      while (*p == '\t' || *p == '\n' || *p == '\x0c' ||
926
1.00M
       *p == '\r' || *p == ' ') {
927
530k
  if (*p == '\n' || *p == '\r') {
928
409k
    startOfLine = gTrue;
929
409k
  }
930
530k
  space = gTrue;
931
530k
  ++p;
932
530k
      }
933
826k
      if (!strncmp(p, "stream", 6)) {
934
266k
  if (lastObjNum >= 0) {
935
264k
    if (streamObjNumsLen == streamObjNumsSize) {
936
27.2k
      streamObjNumsSize += 64;
937
27.2k
      streamObjNums = (int *)greallocn(streamObjNums, streamObjNumsSize,
938
27.2k
               sizeof(int));
939
27.2k
    }
940
264k
    streamObjNums[streamObjNumsLen++] = lastObjNum;
941
264k
  }
942
266k
  p += 6;
943
266k
  startOfLine = gFalse;
944
266k
  space = gFalse;
945
266k
      }
946
246M
    } else {
947
246M
      if (*p == '\n' || *p == '\r') {
948
6.32M
  startOfLine = gTrue;
949
6.32M
  space = gTrue;
950
240M
      } else if (Lexer::isSpace(*p & 0xff)) {
951
19.7M
  space = gTrue;
952
220M
      } else {
953
220M
  startOfLine = gFalse;
954
220M
  space = gFalse;
955
220M
      }
956
246M
      ++p;
957
246M
    }
958
251M
  }
959
960
  // read each stream object, check for xref or object stream
961
291k
  for (int i = 0; i < streamObjNumsLen; ++i) {
962
263k
    Object obj;
963
263k
    fetch(streamObjNums[i], entries[streamObjNums[i]].gen, &obj);
964
263k
    if (obj.isStream()) {
965
229k
      Dict *dict = obj.streamGetDict();
966
229k
      Object type;
967
229k
      dict->lookup("Type", &type);
968
229k
      if (type.isName("XRef")) {
969
3.46k
  saveTrailerDict(dict, gTrue);
970
225k
      } else if (type.isName("ObjStm")) {
971
134k
  constructObjectStreamEntries(&obj, streamObjNums[i]);
972
134k
      }
973
229k
      type.free();
974
229k
    }
975
263k
    obj.free();
976
263k
  }
977
978
27.7k
  gfree(streamObjNums);
979
980
  // if the file is encrypted, then any objects fetched here will be
981
  // incorrect (because decryption is not yet enabled), so clear the
982
  // cache to avoid that problem
983
469k
  for (int i = 0; i < xrefCacheSize; ++i) {
984
442k
    if (cache[i].num >= 0) {
985
90.9k
      cache[i].obj.free();
986
90.9k
      cache[i].num = -1;
987
90.9k
    }
988
442k
  }
989
990
27.7k
  if (rootNum < 0) {
991
9.45k
    error(errSyntaxError, -1, "Couldn't find trailer dictionary");
992
9.45k
    return gFalse;
993
9.45k
  }
994
18.2k
  return gTrue;
995
27.7k
}
996
997
// Attempt to construct a trailer dict at [pos] in the stream.
998
32.3k
void XRef::constructTrailerDict(GFileOffset pos) {
999
32.3k
  Object newTrailerDict, obj;
1000
32.3k
  obj.initNull();
1001
32.3k
  Parser *parser =
1002
32.3k
      new Parser(NULL,
1003
32.3k
     new Lexer(NULL,
1004
32.3k
         str->makeSubStream(pos, gFalse, 0, &obj)),
1005
32.3k
     gFalse);
1006
32.3k
  parser->getObj(&newTrailerDict);
1007
32.3k
  if (newTrailerDict.isDict()) {
1008
23.6k
    saveTrailerDict(newTrailerDict.getDict(), gFalse);
1009
23.6k
  }
1010
32.3k
  newTrailerDict.free();
1011
32.3k
  delete parser;
1012
32.3k
}
1013
1014
// If [dict] "looks like" a trailer dict (i.e., has a Root entry),
1015
// save it as the trailer dict.
1016
27.1k
void XRef::saveTrailerDict(Dict *dict, GBool isXRefStream) {
1017
27.1k
  Object obj;
1018
27.1k
  dict->lookupNF("Root", &obj);
1019
27.1k
  if (obj.isRef()) {
1020
23.3k
    int newRootNum = obj.getRefNum();
1021
    // the xref stream scanning code runs after all objects are found,
1022
    // so we can check for a valid root object number at that point
1023
23.3k
    if (!isXRefStream || newRootNum <= last) {
1024
23.2k
      rootNum = newRootNum;
1025
23.2k
      rootGen = obj.getRefGen();
1026
23.2k
      if (!trailerDict.isNone()) {
1027
5.08k
  trailerDict.free();
1028
5.08k
      }
1029
23.2k
      trailerDict.initDict(dict);
1030
23.2k
    }
1031
23.3k
  }
1032
27.1k
  obj.free();
1033
27.1k
}
1034
1035
// Look for an object header ("nnn ggg obj") at [p].  The first
1036
// character at *[p] is a digit.  [pos] is the position of *[p].
1037
3.88M
char *XRef::constructObjectEntry(char *p, GFileOffset pos, int *objNum) {
1038
  // we look for non-end-of-line space characters here, to deal with
1039
  // situations like:
1040
  //    nnn          <-- garbage digits on a line
1041
  //    nnn nnn obj  <-- actual object
1042
  // and we also ignore '\0' (because it's used to terminate the
1043
  // buffer in this damage-scanning code)
1044
3.88M
  int num = 0;
1045
11.2M
  do {
1046
11.2M
    num = (num * 10) + (*p - '0');
1047
11.2M
    ++p;
1048
11.2M
  } while (*p >= '0' && *p <= '9' && num < 100000000);
1049
3.88M
  if (*p != '\t' && *p != '\x0c' && *p != ' ') {
1050
1.34M
    return p;
1051
1.34M
  }
1052
2.62M
  do {
1053
2.62M
    ++p;
1054
2.62M
  } while (*p == '\t' || *p == '\x0c' || *p == ' ');
1055
2.53M
  if (!(*p >= '0' && *p <= '9')) {
1056
424k
    return p;
1057
424k
  }
1058
2.11M
  int gen = 0;
1059
4.13M
  do {
1060
4.13M
    gen = (gen * 10) + (*p - '0');
1061
4.13M
    ++p;
1062
4.13M
  } while (*p >= '0' && *p <= '9' && gen < 100000000);
1063
2.11M
  if (*p != '\t' && *p != '\x0c' && *p != ' ') {
1064
114k
    return p;
1065
114k
  }
1066
2.02M
  do {
1067
2.02M
    ++p;
1068
2.02M
  } while (*p == '\t' || *p == '\x0c' || *p == ' ');
1069
1.99M
  if (strncmp(p, "obj", 3)) {
1070
1.54M
    return p;
1071
1.54M
  }
1072
1073
449k
  if (constructXRefEntry(num, gen, pos - start, xrefEntryUncompressed)) {
1074
449k
    *objNum = num;
1075
449k
  }
1076
1077
449k
  return p;
1078
1.99M
}
1079
1080
// Read the header from an object stream, and add xref entries for all
1081
// of its objects.
1082
134k
void XRef::constructObjectStreamEntries(Object *objStr, int objStrObjNum) {
1083
134k
  Object obj1, obj2;
1084
1085
  // get the object count
1086
134k
  if (!objStr->streamGetDict()->lookup("N", &obj1)->isInt()) {
1087
780
    obj1.free();
1088
780
    return;
1089
780
  }
1090
133k
  int nObjects = obj1.getInt();
1091
133k
  obj1.free();
1092
133k
  if (nObjects <= 0 || nObjects > 1000000) {
1093
527
    return;
1094
527
  }
1095
1096
  // parse the header: object numbers and offsets
1097
133k
  Parser *parser = new Parser(NULL,
1098
133k
            new Lexer(NULL, objStr->getStream()->copy()),
1099
133k
            gFalse);
1100
78.2M
  for (int i = 0; i < nObjects; ++i) {
1101
78.1M
    parser->getObj(&obj1, gTrue);
1102
78.1M
    parser->getObj(&obj2, gTrue);
1103
78.1M
    if (obj1.isInt() && obj2.isInt()) {
1104
74.7k
      int num = obj1.getInt();
1105
74.7k
      if (num >= 0 && num < 1000000) {
1106
74.1k
  constructXRefEntry(num, i, objStrObjNum, xrefEntryCompressed);
1107
74.1k
      }
1108
74.7k
    }
1109
78.1M
    obj2.free();
1110
78.1M
    obj1.free();
1111
78.1M
  }
1112
133k
  delete parser;
1113
133k
}
1114
1115
GBool XRef::constructXRefEntry(int num, int gen, GFileOffset pos,
1116
523k
             XRefEntryType type) {
1117
523k
  if (num >= size) {
1118
27.7k
    int newSize = (num + 1 + 255) & ~255;
1119
27.7k
    if (newSize < 0) {
1120
0
      return gFalse;
1121
0
    }
1122
27.7k
    entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
1123
480M
    for (int i = size; i < newSize; ++i) {
1124
480M
      entries[i].offset = (GFileOffset)-1;
1125
480M
      entries[i].type = xrefEntryFree;
1126
480M
    }
1127
27.7k
    size = newSize;
1128
27.7k
  }
1129
1130
523k
  if (entries[num].type == xrefEntryFree ||
1131
502k
      gen >= entries[num].gen) {
1132
502k
    entries[num].offset = pos;
1133
502k
    entries[num].gen = gen;
1134
502k
    entries[num].type = type;
1135
502k
    if (num > last) {
1136
218k
      last = num;
1137
218k
    }
1138
502k
  }
1139
1140
523k
  return gTrue;
1141
523k
}
1142
1143
void XRef::setEncryption(int permFlagsA, GBool ownerPasswordOkA,
1144
       Guchar *fileKeyA, int keyLengthA, int encVersionA,
1145
1.15k
       CryptAlgorithm encAlgorithmA) {
1146
1.15k
  int i;
1147
1148
1.15k
  encrypted = gTrue;
1149
1.15k
  permFlags = permFlagsA;
1150
1.15k
  ownerPasswordOk = ownerPasswordOkA;
1151
1.15k
  if (keyLengthA <= 32) {
1152
1.15k
    keyLength = keyLengthA;
1153
1.15k
  } else {
1154
0
    keyLength = 32;
1155
0
  }
1156
11.7k
  for (i = 0; i < keyLength; ++i) {
1157
10.6k
    fileKey[i] = fileKeyA[i];
1158
10.6k
  }
1159
1.15k
  encVersion = encVersionA;
1160
1.15k
  encAlgorithm = encAlgorithmA;
1161
1.15k
}
1162
1163
GBool XRef::getEncryption(int *permFlagsA, GBool *ownerPasswordOkA,
1164
        int *keyLengthA, int *encVersionA,
1165
0
        CryptAlgorithm *encAlgorithmA) {
1166
0
  if (!encrypted) {
1167
0
    return gFalse;
1168
0
  }
1169
0
  *permFlagsA = permFlags;
1170
0
  *ownerPasswordOkA = ownerPasswordOk;
1171
0
  *keyLengthA = keyLength;
1172
0
  *encVersionA = encVersion;
1173
0
  *encAlgorithmA = encAlgorithm;
1174
0
  return gTrue;
1175
0
}
1176
1177
17.7k
GBool XRef::okToPrint(GBool ignoreOwnerPW) {
1178
17.7k
  return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint);
1179
17.7k
}
1180
1181
17.7k
GBool XRef::okToChange(GBool ignoreOwnerPW) {
1182
17.7k
  return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permChange);
1183
17.7k
}
1184
1185
17.7k
GBool XRef::okToCopy(GBool ignoreOwnerPW) {
1186
17.7k
  return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permCopy);
1187
17.7k
}
1188
1189
17.7k
GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
1190
17.7k
  return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permNotes);
1191
17.7k
}
1192
1193
2.14M
Object *XRef::fetch(int num, int gen, Object *obj, int recursion) {
1194
2.14M
  XRefEntry *e;
1195
2.14M
  Parser *parser;
1196
2.14M
  Object obj1, obj2, obj3;
1197
2.14M
  XRefCacheEntry tmp;
1198
2.14M
  int i, j;
1199
1200
  // check for bogus ref - this can happen in corrupted PDF files
1201
2.14M
  if (num < 0 || num >= size) {
1202
2.72k
    goto err;
1203
2.72k
  }
1204
1205
  // check the cache
1206
2.14M
#if MULTITHREADED
1207
2.14M
  gLockMutex(&cacheMutex);
1208
2.14M
#endif
1209
2.14M
  if (cache[0].num == num && cache[0].gen == gen) {
1210
711k
    cache[0].obj.copy(obj);
1211
711k
#if MULTITHREADED
1212
711k
    gUnlockMutex(&cacheMutex);
1213
711k
#endif
1214
711k
    return obj;
1215
711k
  }
1216
14.3M
  for (i = 1; i < xrefCacheSize; ++i) {
1217
13.5M
    if (cache[i].num == num && cache[i].gen == gen) {
1218
644k
      tmp = cache[i];
1219
2.40M
      for (j = i; j > 0; --j) {
1220
1.75M
  cache[j] = cache[j - 1];
1221
1.75M
      }
1222
644k
      cache[0] = tmp;
1223
644k
      cache[0].obj.copy(obj);
1224
644k
#if MULTITHREADED
1225
644k
      gUnlockMutex(&cacheMutex);
1226
644k
#endif
1227
644k
      return obj;
1228
644k
    }
1229
13.5M
  }
1230
786k
#if MULTITHREADED
1231
786k
  gUnlockMutex(&cacheMutex);
1232
786k
#endif
1233
1234
786k
  e = &entries[num];
1235
786k
  switch (e->type) {
1236
1237
305k
  case xrefEntryUncompressed:
1238
305k
    if (e->gen != gen) {
1239
5.85k
      goto err;
1240
5.85k
    }
1241
299k
    obj1.initNull();
1242
299k
    parser = new Parser(this,
1243
299k
         new Lexer(this,
1244
299k
     str->makeSubStream(start + e->offset, gFalse, 0, &obj1)),
1245
299k
         gTrue);
1246
299k
    parser->getObj(&obj1, gTrue);
1247
299k
    parser->getObj(&obj2, gTrue);
1248
299k
    parser->getObj(&obj3, gTrue);
1249
299k
    if (!obj1.isInt() || obj1.getInt() != num ||
1250
299k
  !obj2.isInt() || obj2.getInt() != gen ||
1251
299k
  !obj3.isCmd("obj")) {
1252
1.92k
      obj1.free();
1253
1.92k
      obj2.free();
1254
1.92k
      obj3.free();
1255
1.92k
      delete parser;
1256
1.92k
      goto err;
1257
1.92k
    }
1258
297k
    parser->getObj(obj, gFalse, encrypted ? fileKey : (Guchar *)NULL,
1259
297k
       encAlgorithm, keyLength, num, gen, recursion);
1260
297k
    obj1.free();
1261
297k
    obj2.free();
1262
297k
    obj3.free();
1263
297k
    delete parser;
1264
297k
    break;
1265
1266
23.0k
  case xrefEntryCompressed:
1267
#if 0 // Adobe apparently ignores the generation number on compressed objects
1268
    if (gen != 0) {
1269
      goto err;
1270
    }
1271
#endif
1272
23.0k
    if (e->offset >= (GFileOffset)size ||
1273
23.0k
  entries[e->offset].type != xrefEntryUncompressed) {
1274
298
      error(errSyntaxError, -1, "Invalid object stream");
1275
298
      goto err;
1276
298
    }
1277
22.7k
    if (!getObjectStreamObject((int)e->offset, e->gen, num, obj, recursion)) {
1278
1.27k
      goto err;
1279
1.27k
    }
1280
21.5k
    break;
1281
1282
458k
  default:
1283
458k
    goto err;
1284
786k
  }
1285
1286
  // put the new object in the cache, throwing away the oldest object
1287
  // currently in the cache
1288
318k
#if MULTITHREADED
1289
318k
  gLockMutex(&cacheMutex);
1290
318k
#endif
1291
318k
  if (cache[xrefCacheSize - 1].num >= 0) {
1292
62.7k
    cache[xrefCacheSize - 1].obj.free();
1293
62.7k
  }
1294
5.10M
  for (i = xrefCacheSize - 1; i > 0; --i) {
1295
4.78M
    cache[i] = cache[i - 1];
1296
4.78M
  }
1297
318k
  cache[0].num = num;
1298
318k
  cache[0].gen = gen;
1299
318k
  obj->copy(&cache[0].obj);
1300
318k
#if MULTITHREADED
1301
318k
  gUnlockMutex(&cacheMutex);
1302
318k
#endif
1303
1304
318k
  return obj;
1305
1306
470k
 err:
1307
470k
  return obj->initNull();
1308
786k
}
1309
1310
GBool XRef::getObjectStreamObject(int objStrNum, int objIdx,
1311
22.7k
          int objNum, Object *obj, int recursion) {
1312
22.7k
  if (recursion >= objectRecursionLimit) {
1313
0
    return gFalse;
1314
0
  }
1315
1316
  // check for a cached ObjectStream
1317
22.7k
#if MULTITHREADED
1318
22.7k
  gLockMutex(&objStrsMutex);
1319
22.7k
#endif
1320
22.7k
  ObjectStream *objStr = getObjectStreamFromCache(objStrNum);
1321
22.7k
  GBool found = gFalse;
1322
22.7k
  if (objStr) {
1323
16.7k
    objStr->getObject(objIdx, objNum, obj);
1324
16.7k
    cleanObjectStreamCache();
1325
16.7k
    found = gTrue;
1326
16.7k
  }
1327
22.7k
#if MULTITHREADED
1328
22.7k
  gUnlockMutex(&objStrsMutex);
1329
22.7k
#endif
1330
22.7k
  if (found) {
1331
16.7k
    return gTrue;
1332
16.7k
  }
1333
1334
  // load a new ObjectStream
1335
5.99k
  objStr = new ObjectStream(this, objStrNum, recursion + 1);
1336
5.99k
  if (!objStr->isOk()) {
1337
1.27k
    delete objStr;
1338
1.27k
    return gFalse;
1339
1.27k
  }
1340
4.71k
  objStr->getObject(objIdx, objNum, obj);
1341
4.71k
#if MULTITHREADED
1342
4.71k
  gLockMutex(&objStrsMutex);
1343
4.71k
#endif
1344
4.71k
  addObjectStreamToCache(objStr);
1345
4.71k
  cleanObjectStreamCache();
1346
4.71k
#if MULTITHREADED
1347
4.71k
  gUnlockMutex(&objStrsMutex);
1348
4.71k
#endif
1349
4.71k
  return gTrue;
1350
5.99k
}
1351
1352
// NB: objStrsMutex must be locked when calling this function.
1353
22.7k
ObjectStream *XRef::getObjectStreamFromCache(int objStrNum) {
1354
  // check the MRU entry in the cache
1355
22.7k
  if (objStrs[0] && objStrs[0]->getObjStrNum() == objStrNum) {
1356
14.4k
    ObjectStream *objStr = objStrs[0];
1357
14.4k
    objStrLastUse[0] = objStrTime++;
1358
14.4k
    return objStr;
1359
14.4k
  }
1360
1361
  // check the rest of the cache
1362
15.1k
  for (int i = 1; i < objStrCacheLength; ++i) {
1363
9.17k
    if (objStrs[i] && objStrs[i]->getObjStrNum() == objStrNum) {
1364
2.30k
      ObjectStream *objStr = objStrs[i];
1365
6.46k
      for (int j = i; j > 0; --j) {
1366
4.16k
  objStrs[j] = objStrs[j - 1];
1367
4.16k
  objStrLastUse[j] = objStrLastUse[j - 1];
1368
4.16k
      }
1369
2.30k
      objStrs[0] = objStr;
1370
2.30k
      objStrLastUse[0] = objStrTime++;
1371
2.30k
      return objStr;
1372
2.30k
    }
1373
9.17k
  }
1374
1375
5.99k
  return NULL;
1376
8.29k
}
1377
1378
// NB: objStrsMutex must be locked when calling this function.
1379
4.71k
void XRef::addObjectStreamToCache(ObjectStream *objStr) {
1380
  // add to the cache
1381
4.71k
  if (objStrCacheLength == objStrCacheSize) {
1382
0
    delete objStrs[objStrCacheSize - 1];
1383
0
    --objStrCacheLength;
1384
0
  }
1385
10.4k
  for (int j = objStrCacheLength; j > 0; --j) {
1386
5.72k
    objStrs[j] = objStrs[j - 1];
1387
5.72k
    objStrLastUse[j] = objStrLastUse[j - 1];
1388
5.72k
  }
1389
4.71k
  ++objStrCacheLength;
1390
4.71k
  objStrs[0] = objStr;
1391
4.71k
  objStrLastUse[0] = objStrTime++;
1392
4.71k
}
1393
1394
// If the oldest (least recently used) entry in the object stream
1395
// cache is more than objStrCacheTimeout accesses old (hasn't been
1396
// used in the last objStrCacheTimeout accesses), eject it from the
1397
// cache.
1398
// NB: objStrsMutex must be locked when calling this function.
1399
21.5k
void XRef::cleanObjectStreamCache() {
1400
  // NB: objStrTime and objStrLastUse[] are unsigned ints, so the
1401
  // mod-2^32 arithmetic makes the subtraction work out, even if the
1402
  // time wraps around.
1403
21.5k
  if (objStrCacheLength > 1 &&
1404
15.0k
      objStrTime - objStrLastUse[objStrCacheLength - 1]
1405
15.0k
        > objStrCacheTimeout) {
1406
0
    delete objStrs[objStrCacheLength - 1];
1407
0
    objStrs[objStrCacheLength - 1] = NULL;
1408
0
    --objStrCacheLength;
1409
0
  }
1410
21.5k
}
1411
1412
17.6k
Object *XRef::getDocInfo(Object *obj) {
1413
17.6k
  return trailerDict.dictLookup("Info", obj);
1414
17.6k
}
1415
1416
// Added for the pdftex project.
1417
0
Object *XRef::getDocInfoNF(Object *obj) {
1418
0
  return trailerDict.dictLookupNF("Info", obj);
1419
0
}
1420
1421
180k
GBool XRef::getStreamEnd(GFileOffset streamStart, GFileOffset *streamEnd) {
1422
180k
  int a, b, m;
1423
1424
180k
  if (streamEndsLen == 0 ||
1425
155k
      streamStart > streamEnds[streamEndsLen - 1]) {
1426
45.6k
    return gFalse;
1427
45.6k
  }
1428
1429
134k
  a = -1;
1430
134k
  b = streamEndsLen - 1;
1431
  // invariant: streamEnds[a] < streamStart <= streamEnds[b]
1432
552k
  while (b - a > 1) {
1433
417k
    m = (a + b) / 2;
1434
417k
    if (streamStart <= streamEnds[m]) {
1435
186k
      b = m;
1436
230k
    } else {
1437
230k
      a = m;
1438
230k
    }
1439
417k
  }
1440
134k
  *streamEnd = streamEnds[b];
1441
134k
  return gTrue;
1442
180k
}
1443
1444
1.69k
GFileOffset XRef::strToFileOffset(char *s) {
1445
1.69k
  GFileOffset x, d;
1446
1.69k
  char *p;
1447
1448
1.69k
  x = 0;
1449
5.11k
  for (p = s; *p && isdigit(*p & 0xff); ++p) {
1450
3.43k
    d = *p - '0';
1451
3.43k
    if (x > (GFILEOFFSET_MAX - d) / 10) {
1452
11
      break;
1453
11
    }
1454
3.42k
    x = 10 * x + d;
1455
3.42k
  }
1456
1.69k
  return x;
1457
1.69k
}