~ubuntu-branches/ubuntu/precise/ipe/precise

« back to all changes in this revision

Viewing changes to src/xpdflib/xref.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Steve M. Robbins
  • Date: 2004-06-08 00:44:02 UTC
  • Revision ID: james.westby@ubuntu.com-20040608004402-72yu51xlh7vt6p9m
Tags: upstream-6.0pre16
ImportĀ upstreamĀ versionĀ 6.0pre16

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//========================================================================
 
2
//
 
3
// XRef.cc
 
4
//
 
5
// Copyright 1996-2002 Glyph & Cog, LLC
 
6
//
 
7
//========================================================================
 
8
 
 
9
#include "aconf.h"
 
10
 
 
11
#ifdef USE_GCC_PRAGMAS
 
12
#pragma implementation
 
13
#endif
 
14
 
 
15
#include <stdlib.h>
 
16
#include <stddef.h>
 
17
#include <string.h>
 
18
#include <ctype.h>
 
19
#include "gmem.h"
 
20
#include "object.h"
 
21
#include "stream.h"
 
22
#include "lexer.h"
 
23
#include "parser.h"
 
24
#include "dict.h"
 
25
#ifndef NO_DECRYPTION
 
26
#include "decrypt.h"
 
27
#endif
 
28
#include "error.h"
 
29
#include "errorcodes.h"
 
30
#include "xref.h"
 
31
 
 
32
//------------------------------------------------------------------------
 
33
 
 
34
#define xrefSearchSize 1024     // read this many bytes at end of file
 
35
                                //   to look for 'startxref'
 
36
 
 
37
#ifndef NO_DECRYPTION
 
38
//------------------------------------------------------------------------
 
39
// Permission bits
 
40
//------------------------------------------------------------------------
 
41
 
 
42
#define permPrint    (1<<2)
 
43
#define permChange   (1<<3)
 
44
#define permCopy     (1<<4)
 
45
#define permNotes    (1<<5)
 
46
#define defPermFlags 0xfffc
 
47
#endif
 
48
 
 
49
//------------------------------------------------------------------------
 
50
// XRef
 
51
//------------------------------------------------------------------------
 
52
 
 
53
XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
 
54
  Guint pos;
 
55
  int i;
 
56
 
 
57
  ok = gTrue;
 
58
  errCode = errNone;
 
59
  size = 0;
 
60
  entries = NULL;
 
61
  streamEnds = NULL;
 
62
  streamEndsLen = 0;
 
63
 
 
64
  // read the trailer
 
65
  str = strA;
 
66
  start = str->getStart();
 
67
  pos = readTrailer();
 
68
 
 
69
  // if there was a problem with the trailer,
 
70
  // try to reconstruct the xref table
 
71
  if (pos == 0) {
 
72
    if (!(ok = constructXRef())) {
 
73
      errCode = errDamaged;
 
74
      return;
 
75
    }
 
76
 
 
77
  // trailer is ok - read the xref table
 
78
  } else {
 
79
    entries = (XRefEntry *)gmalloc(size * sizeof(XRefEntry));
 
80
    for (i = 0; i < size; ++i) {
 
81
      entries[i].offset = 0xffffffff;
 
82
      entries[i].used = gFalse;
 
83
    }
 
84
    while (readXRef(&pos)) ;
 
85
 
 
86
    // if there was a problem with the xref table,
 
87
    // try to reconstruct it
 
88
    if (!ok) {
 
89
      gfree(entries);
 
90
      size = 0;
 
91
      entries = NULL;
 
92
      if (!(ok = constructXRef())) {
 
93
        errCode = errDamaged;
 
94
        return;
 
95
      }
 
96
    }
 
97
  }
 
98
 
 
99
  // now set the trailer dictionary's xref pointer so we can fetch
 
100
  // indirect objects from it
 
101
  trailerDict.getDict()->setXRef(this);
 
102
 
 
103
  // check for encryption
 
104
#ifndef NO_DECRYPTION
 
105
  encrypted = gFalse;
 
106
#endif
 
107
  if (checkEncrypted(ownerPassword, userPassword)) {
 
108
    ok = gFalse;
 
109
    errCode = errEncrypted;
 
110
    return;
 
111
  }
 
112
}
 
113
 
 
114
XRef::~XRef() {
 
115
  gfree(entries);
 
116
  trailerDict.free();
 
117
  if (streamEnds) {
 
118
    gfree(streamEnds);
 
119
  }
 
120
}
 
121
 
 
122
// Read startxref position, xref table size, and root.  Returns
 
123
// first xref position.
 
124
Guint XRef::readTrailer() {
 
125
  Parser *parser;
 
126
  Object obj;
 
127
  char buf[xrefSearchSize+1];
 
128
  int n;
 
129
  Guint pos, pos1;
 
130
  char *p;
 
131
  int c;
 
132
  int i;
 
133
 
 
134
  // read last xrefSearchSize bytes
 
135
  str->setPos(xrefSearchSize, -1);
 
136
  for (n = 0; n < xrefSearchSize; ++n) {
 
137
    if ((c = str->getChar()) == EOF)
 
138
      break;
 
139
    buf[n] = c;
 
140
  }
 
141
  buf[n] = '\0';
 
142
 
 
143
  // find startxref
 
144
  for (i = n - 9; i >= 0; --i) {
 
145
    if (!strncmp(&buf[i], "startxref", 9))
 
146
      break;
 
147
  }
 
148
  if (i < 0)
 
149
    return 0;
 
150
  for (p = &buf[i+9]; isspace(*p); ++p) ;
 
151
  pos = lastXRefPos = strToUnsigned(p);
 
152
 
 
153
  // find trailer dict by looking after first xref table
 
154
  // (NB: we can't just use the trailer dict at the end of the file --
 
155
  // this won't work for linearized files.)
 
156
  str->setPos(start + pos);
 
157
  for (i = 0; i < 4; ++i)
 
158
    buf[i] = str->getChar();
 
159
  if (strncmp(buf, "xref", 4))
 
160
    return 0;
 
161
  pos1 = pos + 4;
 
162
  while (1) {
 
163
    str->setPos(start + pos1);
 
164
    for (i = 0; i < 35; ++i) {
 
165
      if ((c = str->getChar()) == EOF)
 
166
        return 0;
 
167
      buf[i] = c;
 
168
    }
 
169
    if (!strncmp(buf, "trailer", 7))
 
170
      break;
 
171
    p = buf;
 
172
    while (isspace(*p)) ++p;
 
173
    while ('0' <= *p && *p <= '9') ++p;
 
174
    while (isspace(*p)) ++p;
 
175
    n = atoi(p);
 
176
    while ('0' <= *p && *p <= '9') ++p;
 
177
    while (isspace(*p)) ++p;
 
178
    if (p == buf)
 
179
      return 0;
 
180
    pos1 += (p - buf) + n * 20;
 
181
  }
 
182
  pos1 += 7;
 
183
 
 
184
  // read trailer dict
 
185
  obj.initNull();
 
186
  parser = new Parser(NULL,
 
187
             new Lexer(NULL,
 
188
               str->makeSubStream(start + pos1, gFalse, 0, &obj)));
 
189
  parser->getObj(&trailerDict);
 
190
  if (trailerDict.isDict()) {
 
191
    trailerDict.dictLookupNF("Size", &obj);
 
192
    if (obj.isInt())
 
193
      size = obj.getInt();
 
194
    else
 
195
      pos = 0;
 
196
    obj.free();
 
197
    trailerDict.dictLookupNF("Root", &obj);
 
198
    if (obj.isRef()) {
 
199
      rootNum = obj.getRefNum();
 
200
      rootGen = obj.getRefGen();
 
201
    } else {
 
202
      pos = 0;
 
203
    }
 
204
    obj.free();
 
205
  } else {
 
206
    pos = 0;
 
207
  }
 
208
  delete parser;
 
209
 
 
210
  // return first xref position
 
211
  return pos;
 
212
}
 
213
 
 
214
// Read an xref table and the prev pointer from the trailer.
 
215
GBool XRef::readXRef(Guint *pos) {
 
216
  Parser *parser;
 
217
  Object obj, obj2;
 
218
  char s[20];
 
219
  GBool more;
 
220
  int first, newSize, n, i, j;
 
221
  int c;
 
222
 
 
223
  // seek to xref in stream
 
224
  str->setPos(start + *pos);
 
225
 
 
226
  // make sure it's an xref table
 
227
  while ((c = str->getChar()) != EOF && isspace(c)) ;
 
228
  s[0] = (char)c;
 
229
  s[1] = (char)str->getChar();
 
230
  s[2] = (char)str->getChar();
 
231
  s[3] = (char)str->getChar();
 
232
  if (!(s[0] == 'x' && s[1] == 'r' && s[2] == 'e' && s[3] == 'f')) {
 
233
    goto err2;
 
234
  }
 
235
 
 
236
  // read xref
 
237
  while (1) {
 
238
    while ((c = str->lookChar()) != EOF && isspace(c)) {
 
239
      str->getChar();
 
240
    }
 
241
    if (c == 't') {
 
242
      break;
 
243
    }
 
244
    for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i) {
 
245
      s[i] = (char)c;
 
246
    }
 
247
    if (i == 0) {
 
248
      goto err2;
 
249
    }
 
250
    s[i] = '\0';
 
251
    first = atoi(s);
 
252
    while ((c = str->lookChar()) != EOF && isspace(c)) {
 
253
      str->getChar();
 
254
    }
 
255
    for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i) {
 
256
      s[i] = (char)c;
 
257
    }
 
258
    if (i == 0) {
 
259
      goto err2;
 
260
    }
 
261
    s[i] = '\0';
 
262
    n = atoi(s);
 
263
    while ((c = str->lookChar()) != EOF && isspace(c)) {
 
264
      str->getChar();
 
265
    }
 
266
    // check for buggy PDF files with an incorrect (too small) xref
 
267
    // table size
 
268
    if (first + n > size) {
 
269
      newSize = size + 256;
 
270
      entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
 
271
      for (i = size; i < newSize; ++i) {
 
272
        entries[i].offset = 0xffffffff;
 
273
        entries[i].used = gFalse;
 
274
      }
 
275
      size = newSize;
 
276
    }
 
277
    for (i = first; i < first + n; ++i) {
 
278
      for (j = 0; j < 20; ++j) {
 
279
        if ((c = str->getChar()) == EOF) {
 
280
          goto err2;
 
281
        }
 
282
        s[j] = (char)c;
 
283
      }
 
284
      if (entries[i].offset == 0xffffffff) {
 
285
        s[10] = '\0';
 
286
        entries[i].offset = strToUnsigned(s);
 
287
        s[16] = '\0';
 
288
        entries[i].gen = atoi(&s[11]);
 
289
        if (s[17] == 'n') {
 
290
          entries[i].used = gTrue;
 
291
        } else if (s[17] == 'f') {
 
292
          entries[i].used = gFalse;
 
293
        } else {
 
294
          goto err2;
 
295
        }
 
296
        // PDF files of patents from the IBM Intellectual Property
 
297
        // Network have a bug: the xref table claims to start at 1
 
298
        // instead of 0.
 
299
        if (i == 1 && first == 1 &&
 
300
            entries[1].offset == 0 && entries[1].gen == 65535 &&
 
301
            !entries[1].used) {
 
302
          i = first = 0;
 
303
          entries[0] = entries[1];
 
304
          entries[1].offset = 0xffffffff;
 
305
        }
 
306
      }
 
307
    }
 
308
  }
 
309
 
 
310
  // read prev pointer from trailer dictionary
 
311
  obj.initNull();
 
312
  parser = new Parser(NULL,
 
313
             new Lexer(NULL,
 
314
               str->makeSubStream(str->getPos(), gFalse, 0, &obj)));
 
315
  parser->getObj(&obj);
 
316
  if (!obj.isCmd("trailer")) {
 
317
    goto err1;
 
318
  }
 
319
  obj.free();
 
320
  parser->getObj(&obj);
 
321
  if (!obj.isDict()) {
 
322
    goto err1;
 
323
  }
 
324
  obj.getDict()->lookupNF("Prev", &obj2);
 
325
  if (obj2.isInt()) {
 
326
    *pos = (Guint)obj2.getInt();
 
327
    more = gTrue;
 
328
  } else {
 
329
    more = gFalse;
 
330
  }
 
331
  obj.free();
 
332
  obj2.free();
 
333
 
 
334
  delete parser;
 
335
  return more;
 
336
 
 
337
 err1:
 
338
  obj.free();
 
339
 err2:
 
340
  ok = gFalse;
 
341
  return gFalse;
 
342
}
 
343
 
 
344
// Attempt to construct an xref table for a damaged file.
 
345
GBool XRef::constructXRef() {
 
346
  Parser *parser;
 
347
  Object obj;
 
348
  char buf[256];
 
349
  Guint pos;
 
350
  int num, gen;
 
351
  int newSize;
 
352
  int streamEndsSize;
 
353
  char *p;
 
354
  int i;
 
355
  GBool gotRoot;
 
356
 
 
357
  error(0, "PDF file is damaged - attempting to reconstruct xref table...");
 
358
  gotRoot = gFalse;
 
359
  streamEndsLen = streamEndsSize = 0;
 
360
 
 
361
  str->reset();
 
362
  while (1) {
 
363
    pos = str->getPos();
 
364
    if (!str->getLine(buf, 256)) {
 
365
      break;
 
366
    }
 
367
    p = buf;
 
368
 
 
369
    // got trailer dictionary
 
370
    if (!strncmp(p, "trailer", 7)) {
 
371
      obj.initNull();
 
372
      parser = new Parser(NULL,
 
373
                 new Lexer(NULL,
 
374
                   str->makeSubStream(start + pos + 7, gFalse, 0, &obj)));
 
375
      if (!trailerDict.isNone())
 
376
        trailerDict.free();
 
377
      parser->getObj(&trailerDict);
 
378
      if (trailerDict.isDict()) {
 
379
        trailerDict.dictLookupNF("Root", &obj);
 
380
        if (obj.isRef()) {
 
381
          rootNum = obj.getRefNum();
 
382
          rootGen = obj.getRefGen();
 
383
          gotRoot = gTrue;
 
384
        }
 
385
        obj.free();
 
386
      } else {
 
387
        pos = 0;
 
388
      }
 
389
      delete parser;
 
390
 
 
391
    // look for object
 
392
    } else if (isdigit(*p)) {
 
393
      num = atoi(p);
 
394
      do {
 
395
        ++p;
 
396
      } while (*p && isdigit(*p));
 
397
      if (isspace(*p)) {
 
398
        do {
 
399
          ++p;
 
400
        } while (*p && isspace(*p));
 
401
        if (isdigit(*p)) {
 
402
          gen = atoi(p);
 
403
          do {
 
404
            ++p;
 
405
          } while (*p && isdigit(*p));
 
406
          if (isspace(*p)) {
 
407
            do {
 
408
              ++p;
 
409
            } while (*p && isspace(*p));
 
410
            if (!strncmp(p, "obj", 3)) {
 
411
              if (num >= size) {
 
412
                newSize = (num + 1 + 255) & ~255;
 
413
                entries = (XRefEntry *)
 
414
                            grealloc(entries, newSize * sizeof(XRefEntry));
 
415
                for (i = size; i < newSize; ++i) {
 
416
                  entries[i].offset = 0xffffffff;
 
417
                  entries[i].used = gFalse;
 
418
                }
 
419
                size = newSize;
 
420
              }
 
421
              if (!entries[num].used || gen >= entries[num].gen) {
 
422
                entries[num].offset = pos - start;
 
423
                entries[num].gen = gen;
 
424
                entries[num].used = gTrue;
 
425
              }
 
426
            }
 
427
          }
 
428
        }
 
429
      }
 
430
 
 
431
    } else if (!strncmp(p, "endstream", 9)) {
 
432
      if (streamEndsLen == streamEndsSize) {
 
433
        streamEndsSize += 64;
 
434
        streamEnds = (Guint *)grealloc(streamEnds,
 
435
                                       streamEndsSize * sizeof(int));
 
436
      }
 
437
      streamEnds[streamEndsLen++] = pos;
 
438
    }
 
439
  }
 
440
 
 
441
  if (gotRoot)
 
442
    return gTrue;
 
443
 
 
444
  error(-1, "Couldn't find trailer dictionary");
 
445
  return gFalse;
 
446
}
 
447
 
 
448
#ifndef NO_DECRYPTION
 
449
GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
 
450
  Object encrypt, filterObj, versionObj, revisionObj, lengthObj;
 
451
  Object ownerKey, userKey, permissions, fileID, fileID1;
 
452
  GBool encrypted1;
 
453
  GBool ret;
 
454
 
 
455
  ret = gFalse;
 
456
 
 
457
  permFlags = defPermFlags;
 
458
  ownerPasswordOk = gFalse;
 
459
  trailerDict.dictLookup("Encrypt", &encrypt);
 
460
  if ((encrypted1 = encrypt.isDict())) {
 
461
    ret = gTrue;
 
462
    encrypt.dictLookup("Filter", &filterObj);
 
463
    if (filterObj.isName("Standard")) {
 
464
      encrypt.dictLookup("V", &versionObj);
 
465
      encrypt.dictLookup("R", &revisionObj);
 
466
      encrypt.dictLookup("Length", &lengthObj);
 
467
      encrypt.dictLookup("O", &ownerKey);
 
468
      encrypt.dictLookup("U", &userKey);
 
469
      encrypt.dictLookup("P", &permissions);
 
470
      trailerDict.dictLookup("ID", &fileID);
 
471
      if (versionObj.isInt() &&
 
472
          revisionObj.isInt() &&
 
473
          ownerKey.isString() && ownerKey.getString()->getLength() == 32 &&
 
474
          userKey.isString() && userKey.getString()->getLength() == 32 &&
 
475
          permissions.isInt() &&
 
476
          fileID.isArray()) {
 
477
        encVersion = versionObj.getInt();
 
478
        encRevision = revisionObj.getInt();
 
479
        if (lengthObj.isInt()) {
 
480
          keyLength = lengthObj.getInt() / 8;
 
481
        } else {
 
482
          keyLength = 5;
 
483
        }
 
484
        permFlags = permissions.getInt();
 
485
        if (encVersion >= 1 && encVersion <= 2 &&
 
486
            encRevision >= 2 && encRevision <= 3) {
 
487
          fileID.arrayGet(0, &fileID1);
 
488
          if (fileID1.isString()) {
 
489
            if (Decrypt::makeFileKey(encVersion, encRevision, keyLength,
 
490
                                     ownerKey.getString(), userKey.getString(),
 
491
                                     permFlags, fileID1.getString(),
 
492
                                     ownerPassword, userPassword, fileKey,
 
493
                                     &ownerPasswordOk)) {
 
494
              if (ownerPassword && !ownerPasswordOk) {
 
495
                error(-1, "Incorrect owner password");
 
496
              }
 
497
              ret = gFalse;
 
498
            } else {
 
499
              error(-1, "Incorrect password");
 
500
            }
 
501
          } else {
 
502
            error(-1, "Weird encryption info");
 
503
          }
 
504
          fileID1.free();
 
505
        } else {
 
506
          error(-1, "Unsupported version/revision (%d/%d) of Standard security handler",
 
507
                encVersion, encRevision);
 
508
        }
 
509
      } else {
 
510
        error(-1, "Weird encryption info");
 
511
      }
 
512
      fileID.free();
 
513
      permissions.free();
 
514
      userKey.free();
 
515
      ownerKey.free();
 
516
      lengthObj.free();
 
517
      revisionObj.free();
 
518
      versionObj.free();
 
519
    } else {
 
520
      error(-1, "Unknown security handler '%s'",
 
521
            filterObj.isName() ? filterObj.getName() : "???");
 
522
    }
 
523
    filterObj.free();
 
524
  }
 
525
  encrypt.free();
 
526
 
 
527
  // this flag has to be set *after* we read the O/U/P strings
 
528
  encrypted = encrypted1;
 
529
 
 
530
  return ret;
 
531
}
 
532
#else
 
533
GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
 
534
  Object obj;
 
535
  GBool encrypted;
 
536
 
 
537
  trailerDict.dictLookup("Encrypt", &obj);
 
538
  if ((encrypted = !obj.isNull())) {
 
539
    error(-1, "PDF file is encrypted and this version of the Xpdf tools");
 
540
    error(-1, "was built without decryption support.");
 
541
  }
 
542
  obj.free();
 
543
  return encrypted;
 
544
}
 
545
#endif
 
546
 
 
547
GBool XRef::okToPrint(GBool ignoreOwnerPW) {
 
548
#ifndef NO_DECRYPTION
 
549
  if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permPrint)) {
 
550
    return gFalse;
 
551
  }
 
552
#endif
 
553
  return gTrue;
 
554
}
 
555
 
 
556
GBool XRef::okToChange(GBool ignoreOwnerPW) {
 
557
#ifndef NO_DECRYPTION
 
558
  if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permChange)) {
 
559
    return gFalse;
 
560
  }
 
561
#endif
 
562
  return gTrue;
 
563
}
 
564
 
 
565
GBool XRef::okToCopy(GBool ignoreOwnerPW) {
 
566
#ifndef NO_DECRYPTION
 
567
  if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permCopy)) {
 
568
    return gFalse;
 
569
  }
 
570
#endif
 
571
  return gTrue;
 
572
}
 
573
 
 
574
GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
 
575
#ifndef NO_DECRYPTION
 
576
  if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permNotes)) {
 
577
    return gFalse;
 
578
  }
 
579
#endif
 
580
  return gTrue;
 
581
}
 
582
 
 
583
Object *XRef::fetch(int num, int gen, Object *obj) {
 
584
  XRefEntry *e;
 
585
  Parser *parser;
 
586
  Object obj1, obj2, obj3;
 
587
 
 
588
  // check for bogus ref - this can happen in corrupted PDF files
 
589
  if (num < 0 || num >= size) {
 
590
    obj->initNull();
 
591
    return obj;
 
592
  }
 
593
 
 
594
  e = &entries[num];
 
595
  if (e->gen == gen && e->offset != 0xffffffff) {
 
596
    obj1.initNull();
 
597
    parser = new Parser(this,
 
598
               new Lexer(this,
 
599
                 str->makeSubStream(start + e->offset, gFalse, 0, &obj1)));
 
600
    parser->getObj(&obj1);
 
601
    parser->getObj(&obj2);
 
602
    parser->getObj(&obj3);
 
603
    if (obj1.isInt() && obj1.getInt() == num &&
 
604
        obj2.isInt() && obj2.getInt() == gen &&
 
605
        obj3.isCmd("obj")) {
 
606
#ifndef NO_DECRYPTION
 
607
      parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, keyLength,
 
608
                     num, gen);
 
609
#else
 
610
      parser->getObj(obj);
 
611
#endif
 
612
    } else {
 
613
      obj->initNull();
 
614
    }
 
615
    obj1.free();
 
616
    obj2.free();
 
617
    obj3.free();
 
618
    delete parser;
 
619
  } else {
 
620
    obj->initNull();
 
621
  }
 
622
  return obj;
 
623
}
 
624
 
 
625
Object *XRef::getDocInfo(Object *obj) {
 
626
  return trailerDict.dictLookup("Info", obj);
 
627
}
 
628
 
 
629
// Added for the pdftex project.
 
630
Object *XRef::getDocInfoNF(Object *obj) {
 
631
  return trailerDict.dictLookupNF("Info", obj);
 
632
}
 
633
 
 
634
GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
 
635
  int a, b, m;
 
636
 
 
637
  if (streamEndsLen == 0 ||
 
638
      streamStart > streamEnds[streamEndsLen - 1]) {
 
639
    return gFalse;
 
640
  }
 
641
 
 
642
  a = -1;
 
643
  b = streamEndsLen - 1;
 
644
  // invariant: streamEnds[a] < streamStart <= streamEnds[b]
 
645
  while (b - a > 1) {
 
646
    m = (a + b) / 2;
 
647
    if (streamStart <= streamEnds[m]) {
 
648
      b = m;
 
649
    } else {
 
650
      a = m;
 
651
    }
 
652
  }
 
653
  *streamEnd = streamEnds[b];
 
654
  return gTrue;
 
655
}
 
656
 
 
657
Guint XRef::strToUnsigned(char *s) {
 
658
  Guint x;
 
659
  char *p;
 
660
  int i;
 
661
 
 
662
  x = 0;
 
663
  for (p = s, i = 0; *p && isdigit(*p) && i < 10; ++p, ++i) {
 
664
    x = 10 * x + (*p - '0');
 
665
  }
 
666
  return x;
 
667
}