Coverage Report

Created: 2025-11-11 07:17

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