]> git.ipfire.org Git - thirdparty/cups.git/blob - pdftops/XRef.cxx
Merge changes from CUPS 1.4svn-r7199.
[thirdparty/cups.git] / pdftops / XRef.cxx
1 //========================================================================
2 //
3 // XRef.cc
4 //
5 // Copyright 1996-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <config.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 #include "Error.h"
26 #include "ErrorCodes.h"
27 #include "XRef.h"
28
29 //------------------------------------------------------------------------
30
31 #define xrefSearchSize 1024 // read this many bytes at end of file
32 // to look for 'startxref'
33
34 //------------------------------------------------------------------------
35 // Permission bits
36 //------------------------------------------------------------------------
37
38 #define permPrint (1<<2)
39 #define permChange (1<<3)
40 #define permCopy (1<<4)
41 #define permNotes (1<<5)
42 #define defPermFlags 0xfffc
43
44 //------------------------------------------------------------------------
45 // ObjectStream
46 //------------------------------------------------------------------------
47
48 class ObjectStream {
49 public:
50
51 // Create an object stream, using object number <objStrNum>,
52 // generation 0.
53 ObjectStream(XRef *xref, int objStrNumA);
54
55 ~ObjectStream();
56
57 // Return the object number of this object stream.
58 int getObjStrNum() { return objStrNum; }
59
60 // Get the <objIdx>th object from this stream, which should be
61 // object number <objNum>, generation 0.
62 Object *getObject(int objIdx, int objNum, Object *obj);
63
64 private:
65
66 int objStrNum; // object number of the object stream
67 int nObjects; // number of objects in the stream
68 Object *objs; // the objects (length = nObjects)
69 int *objNums; // the object numbers (length = nObjects)
70 };
71
72 ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
73 Stream *str;
74 Parser *parser;
75 int *offsets;
76 Object objStr, obj1, obj2;
77 int first, i;
78
79 objStrNum = objStrNumA;
80 nObjects = 0;
81 objs = NULL;
82 objNums = NULL;
83
84 if (!xref->fetch(objStrNum, 0, &objStr)->isStream()) {
85 goto err1;
86 }
87
88 if (!objStr.streamGetDict()->lookup("N", &obj1)->isInt()) {
89 obj1.free();
90 goto err1;
91 }
92 nObjects = obj1.getInt();
93 obj1.free();
94 if (nObjects <= 0) {
95 goto err1;
96 }
97
98 if (!objStr.streamGetDict()->lookup("First", &obj1)->isInt()) {
99 obj1.free();
100 goto err1;
101 }
102 first = obj1.getInt();
103 obj1.free();
104 if (first < 0) {
105 goto err1;
106 }
107
108 objs = new Object[nObjects];
109 objNums = (int *)gmallocn(nObjects, sizeof(int));
110 offsets = (int *)gmallocn(nObjects, sizeof(int));
111
112 // parse the header: object numbers and offsets
113 objStr.streamReset();
114 obj1.initNull();
115 str = new EmbedStream(objStr.getStream(), &obj1, gTrue, first);
116 parser = new Parser(xref, new Lexer(xref, str));
117 for (i = 0; i < nObjects; ++i) {
118 parser->getObj(&obj1);
119 parser->getObj(&obj2);
120 if (!obj1.isInt() || !obj2.isInt()) {
121 obj1.free();
122 obj2.free();
123 delete parser;
124 gfree(offsets);
125 goto err1;
126 }
127 objNums[i] = obj1.getInt();
128 offsets[i] = obj2.getInt();
129 obj1.free();
130 obj2.free();
131 if (objNums[i] < 0 || offsets[i] < 0 ||
132 (i > 0 && offsets[i] < offsets[i-1])) {
133 delete parser;
134 gfree(offsets);
135 goto err1;
136 }
137 }
138 while (str->getChar() != EOF) ;
139 delete parser;
140
141 // skip to the first object - this shouldn't be necessary because
142 // the First key is supposed to be equal to offsets[0], but just in
143 // case...
144 for (i = first; i < offsets[0]; ++i) {
145 objStr.getStream()->getChar();
146 }
147
148 // parse the objects
149 for (i = 0; i < nObjects; ++i) {
150 obj1.initNull();
151 if (i == nObjects - 1) {
152 str = new EmbedStream(objStr.getStream(), &obj1, gFalse, 0);
153 } else {
154 str = new EmbedStream(objStr.getStream(), &obj1, gTrue,
155 offsets[i+1] - offsets[i]);
156 }
157 parser = new Parser(xref, new Lexer(xref, str));
158 parser->getObj(&objs[i]);
159 while (str->getChar() != EOF) ;
160 delete parser;
161 }
162
163 gfree(offsets);
164
165 err1:
166 objStr.free();
167 return;
168 }
169
170 ObjectStream::~ObjectStream() {
171 int i;
172
173 if (objs) {
174 for (i = 0; i < nObjects; ++i) {
175 objs[i].free();
176 }
177 delete[] objs;
178 }
179 gfree(objNums);
180 }
181
182 Object *ObjectStream::getObject(int objIdx, int objNum, Object *obj) {
183 if (objIdx < 0 || objIdx >= nObjects || objNum != objNums[objIdx]) {
184 return obj->initNull();
185 }
186 return objs[objIdx].copy(obj);
187 }
188
189 //------------------------------------------------------------------------
190 // XRef
191 //------------------------------------------------------------------------
192
193 XRef::XRef(BaseStream *strA) {
194 Guint pos;
195 Object obj;
196
197 ok = gTrue;
198 errCode = errNone;
199 size = 0;
200 entries = NULL;
201 streamEnds = NULL;
202 streamEndsLen = 0;
203 objStr = NULL;
204
205 encrypted = gFalse;
206 permFlags = defPermFlags;
207 ownerPasswordOk = gFalse;
208
209 // read the trailer
210 str = strA;
211 start = str->getStart();
212 pos = getStartXref();
213
214 // if there was a problem with the 'startxref' position, try to
215 // reconstruct the xref table
216 if (pos == 0) {
217 if (!(ok = constructXRef())) {
218 errCode = errDamaged;
219 return;
220 }
221
222 // read the xref table
223 } else {
224 while (readXRef(&pos)) ;
225
226 // if there was a problem with the xref table,
227 // try to reconstruct it
228 if (!ok) {
229 if (!(ok = constructXRef())) {
230 errCode = errDamaged;
231 return;
232 }
233 }
234 }
235
236 // get the root dictionary (catalog) object
237 trailerDict.dictLookupNF("Root", &obj);
238 if (obj.isRef()) {
239 rootNum = obj.getRefNum();
240 rootGen = obj.getRefGen();
241 obj.free();
242 } else {
243 obj.free();
244 if (!(ok = constructXRef())) {
245 errCode = errDamaged;
246 return;
247 }
248 }
249
250 // now set the trailer dictionary's xref pointer so we can fetch
251 // indirect objects from it
252 trailerDict.getDict()->setXRef(this);
253 }
254
255 XRef::~XRef() {
256 gfree(entries);
257 trailerDict.free();
258 if (streamEnds) {
259 gfree(streamEnds);
260 }
261 if (objStr) {
262 delete objStr;
263 }
264 }
265
266 // Read the 'startxref' position.
267 Guint XRef::getStartXref() {
268 char buf[xrefSearchSize+1];
269 char *p;
270 int c, n, i;
271
272 // read last xrefSearchSize bytes
273 str->setPos(xrefSearchSize, -1);
274 for (n = 0; n < xrefSearchSize; ++n) {
275 if ((c = str->getChar()) == EOF) {
276 break;
277 }
278 buf[n] = c;
279 }
280 buf[n] = '\0';
281
282 // find startxref
283 for (i = n - 9; i >= 0; --i) {
284 if (!strncmp(&buf[i], "startxref", 9)) {
285 break;
286 }
287 }
288 if (i < 0) {
289 return 0;
290 }
291 for (p = &buf[i+9]; isspace(*p); ++p) ;
292 lastXRefPos = strToUnsigned(p);
293
294 return lastXRefPos;
295 }
296
297 // Read one xref table section. Also reads the associated trailer
298 // dictionary, and returns the prev pointer (if any).
299 GBool XRef::readXRef(Guint *pos) {
300 Parser *parser;
301 Object obj;
302 GBool more;
303
304 // start up a parser, parse one token
305 obj.initNull();
306 parser = new Parser(NULL,
307 new Lexer(NULL,
308 str->makeSubStream(start + *pos, gFalse, 0, &obj)));
309 parser->getObj(&obj);
310
311 // parse an old-style xref table
312 if (obj.isCmd("xref")) {
313 obj.free();
314 more = readXRefTable(parser, pos);
315
316 // parse an xref stream
317 } else if (obj.isInt()) {
318 obj.free();
319 if (!parser->getObj(&obj)->isInt()) {
320 goto err1;
321 }
322 obj.free();
323 if (!parser->getObj(&obj)->isCmd("obj")) {
324 goto err1;
325 }
326 obj.free();
327 if (!parser->getObj(&obj)->isStream()) {
328 goto err1;
329 }
330 more = readXRefStream(obj.getStream(), pos);
331 obj.free();
332
333 } else {
334 goto err1;
335 }
336
337 delete parser;
338 return more;
339
340 err1:
341 obj.free();
342 delete parser;
343 ok = gFalse;
344 return gFalse;
345 }
346
347 GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
348 XRefEntry entry;
349 GBool more;
350 Object obj, obj2;
351 Guint pos2;
352 int first, n, newSize, i;
353
354 while (1) {
355 parser->getObj(&obj);
356 if (obj.isCmd("trailer")) {
357 obj.free();
358 break;
359 }
360 if (!obj.isInt()) {
361 goto err1;
362 }
363 first = obj.getInt();
364 obj.free();
365 if (!parser->getObj(&obj)->isInt()) {
366 goto err1;
367 }
368 n = obj.getInt();
369 obj.free();
370 if (first < 0 || n < 0 || first + n < 0) {
371 goto err1;
372 }
373 if (first + n > size) {
374 for (newSize = size ? 2 * size : 1024;
375 first + n > newSize && newSize > 0;
376 newSize <<= 1) ;
377 if (newSize < 0) {
378 goto err1;
379 }
380 entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
381 for (i = size; i < newSize; ++i) {
382 entries[i].offset = 0xffffffff;
383 entries[i].type = xrefEntryFree;
384 }
385 size = newSize;
386 }
387 for (i = first; i < first + n; ++i) {
388 if (!parser->getObj(&obj)->isInt()) {
389 goto err1;
390 }
391 entry.offset = (Guint)obj.getInt();
392 obj.free();
393 if (!parser->getObj(&obj)->isInt()) {
394 goto err1;
395 }
396 entry.gen = obj.getInt();
397 obj.free();
398 parser->getObj(&obj);
399 if (obj.isCmd("n")) {
400 entry.type = xrefEntryUncompressed;
401 } else if (obj.isCmd("f")) {
402 entry.type = xrefEntryFree;
403 } else {
404 goto err1;
405 }
406 obj.free();
407 if (entries[i].offset == 0xffffffff) {
408 entries[i] = entry;
409 // PDF files of patents from the IBM Intellectual Property
410 // Network have a bug: the xref table claims to start at 1
411 // instead of 0.
412 if (i == 1 && first == 1 &&
413 entries[1].offset == 0 && entries[1].gen == 65535 &&
414 entries[1].type == xrefEntryFree) {
415 i = first = 0;
416 entries[0] = entries[1];
417 entries[1].offset = 0xffffffff;
418 }
419 }
420 }
421 }
422
423 // read the trailer dictionary
424 if (!parser->getObj(&obj)->isDict()) {
425 goto err1;
426 }
427
428 // get the 'Prev' pointer
429 obj.getDict()->lookupNF("Prev", &obj2);
430 if (obj2.isInt()) {
431 *pos = (Guint)obj2.getInt();
432 more = gTrue;
433 } else if (obj2.isRef()) {
434 // certain buggy PDF generators generate "/Prev NNN 0 R" instead
435 // of "/Prev NNN"
436 *pos = (Guint)obj2.getRefNum();
437 more = gTrue;
438 } else {
439 more = gFalse;
440 }
441 obj2.free();
442
443 // save the first trailer dictionary
444 if (trailerDict.isNone()) {
445 obj.copy(&trailerDict);
446 }
447
448 // check for an 'XRefStm' key
449 if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) {
450 pos2 = (Guint)obj2.getInt();
451 readXRef(&pos2);
452 if (!ok) {
453 obj2.free();
454 goto err1;
455 }
456 }
457 obj2.free();
458
459 obj.free();
460 return more;
461
462 err1:
463 obj.free();
464 ok = gFalse;
465 return gFalse;
466 }
467
468 GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
469 Dict *dict;
470 int w[3];
471 GBool more;
472 Object obj, obj2, idx;
473 int newSize, first, n, i;
474
475 dict = xrefStr->getDict();
476
477 if (!dict->lookupNF("Size", &obj)->isInt()) {
478 goto err1;
479 }
480 newSize = obj.getInt();
481 obj.free();
482 if (newSize < 0) {
483 goto err1;
484 }
485 if (newSize > size) {
486 entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
487 for (i = size; i < newSize; ++i) {
488 entries[i].offset = 0xffffffff;
489 entries[i].type = xrefEntryFree;
490 }
491 size = newSize;
492 }
493
494 if (!dict->lookupNF("W", &obj)->isArray() ||
495 obj.arrayGetLength() < 3) {
496 goto err1;
497 }
498 for (i = 0; i < 3; ++i) {
499 if (!obj.arrayGet(i, &obj2)->isInt()) {
500 obj2.free();
501 goto err1;
502 }
503 w[i] = obj2.getInt();
504 obj2.free();
505 if (w[i] < 0 || w[i] > 4) {
506 goto err1;
507 }
508 }
509 obj.free();
510
511 xrefStr->reset();
512 dict->lookupNF("Index", &idx);
513 if (idx.isArray()) {
514 for (i = 0; i+1 < idx.arrayGetLength(); i += 2) {
515 if (!idx.arrayGet(i, &obj)->isInt()) {
516 idx.free();
517 goto err1;
518 }
519 first = obj.getInt();
520 obj.free();
521 if (!idx.arrayGet(i+1, &obj)->isInt()) {
522 idx.free();
523 goto err1;
524 }
525 n = obj.getInt();
526 obj.free();
527 if (first < 0 || n < 0 ||
528 !readXRefStreamSection(xrefStr, w, first, n)) {
529 idx.free();
530 goto err0;
531 }
532 }
533 } else {
534 if (!readXRefStreamSection(xrefStr, w, 0, newSize)) {
535 idx.free();
536 goto err0;
537 }
538 }
539 idx.free();
540
541 dict->lookupNF("Prev", &obj);
542 if (obj.isInt()) {
543 *pos = (Guint)obj.getInt();
544 more = gTrue;
545 } else {
546 more = gFalse;
547 }
548 obj.free();
549 if (trailerDict.isNone()) {
550 trailerDict.initDict(dict);
551 }
552
553 return more;
554
555 err1:
556 obj.free();
557 err0:
558 ok = gFalse;
559 return gFalse;
560 }
561
562 GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
563 Guint offset;
564 int type, gen, c, newSize, i, j;
565
566 if (first + n < 0) {
567 return gFalse;
568 }
569 if (first + n > size) {
570 for (newSize = size ? 2 * size : 1024;
571 first + n > newSize && newSize > 0;
572 newSize <<= 1) ;
573 if (newSize < 0) {
574 return gFalse;
575 }
576 entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
577 for (i = size; i < newSize; ++i) {
578 entries[i].offset = 0xffffffff;
579 entries[i].type = xrefEntryFree;
580 }
581 size = newSize;
582 }
583 for (i = first; i < first + n; ++i) {
584 if (w[0] == 0) {
585 type = 1;
586 } else {
587 for (type = 0, j = 0; j < w[0]; ++j) {
588 if ((c = xrefStr->getChar()) == EOF) {
589 return gFalse;
590 }
591 type = (type << 8) + c;
592 }
593 }
594 for (offset = 0, j = 0; j < w[1]; ++j) {
595 if ((c = xrefStr->getChar()) == EOF) {
596 return gFalse;
597 }
598 offset = (offset << 8) + c;
599 }
600 for (gen = 0, j = 0; j < w[2]; ++j) {
601 if ((c = xrefStr->getChar()) == EOF) {
602 return gFalse;
603 }
604 gen = (gen << 8) + c;
605 }
606 if (entries[i].offset == 0xffffffff) {
607 switch (type) {
608 case 0:
609 entries[i].offset = offset;
610 entries[i].gen = gen;
611 entries[i].type = xrefEntryFree;
612 break;
613 case 1:
614 entries[i].offset = offset;
615 entries[i].gen = gen;
616 entries[i].type = xrefEntryUncompressed;
617 break;
618 case 2:
619 entries[i].offset = offset;
620 entries[i].gen = gen;
621 entries[i].type = xrefEntryCompressed;
622 break;
623 default:
624 return gFalse;
625 }
626 }
627 }
628
629 return gTrue;
630 }
631
632 // Attempt to construct an xref table for a damaged file.
633 GBool XRef::constructXRef() {
634 Parser *parser;
635 Object newTrailerDict, obj;
636 char buf[256];
637 Guint pos;
638 int num, gen;
639 int newSize;
640 int streamEndsSize;
641 char *p;
642 int i;
643 GBool gotRoot;
644
645 gfree(entries);
646 size = 0;
647 entries = NULL;
648
649 error(0, "PDF file is damaged - attempting to reconstruct xref table...");
650 gotRoot = gFalse;
651 streamEndsLen = streamEndsSize = 0;
652
653 str->reset();
654 while (1) {
655 pos = str->getPos();
656 if (!str->getLine(buf, 256)) {
657 break;
658 }
659 p = buf;
660
661 // got trailer dictionary
662 if (!strncmp(p, "trailer", 7)) {
663 obj.initNull();
664 parser = new Parser(NULL,
665 new Lexer(NULL,
666 str->makeSubStream(pos + 7, gFalse, 0, &obj)));
667 parser->getObj(&newTrailerDict);
668 if (newTrailerDict.isDict()) {
669 newTrailerDict.dictLookupNF("Root", &obj);
670 if (obj.isRef()) {
671 rootNum = obj.getRefNum();
672 rootGen = obj.getRefGen();
673 if (!trailerDict.isNone()) {
674 trailerDict.free();
675 }
676 newTrailerDict.copy(&trailerDict);
677 gotRoot = gTrue;
678 }
679 obj.free();
680 }
681 newTrailerDict.free();
682 delete parser;
683
684 // look for object
685 } else if (isdigit(*p)) {
686 num = atoi(p);
687 if (num > 0) {
688 do {
689 ++p;
690 } while (*p && isdigit(*p));
691 if (isspace(*p)) {
692 do {
693 ++p;
694 } while (*p && isspace(*p));
695 if (isdigit(*p)) {
696 gen = atoi(p);
697 do {
698 ++p;
699 } while (*p && isdigit(*p));
700 if (isspace(*p)) {
701 do {
702 ++p;
703 } while (*p && isspace(*p));
704 if (!strncmp(p, "obj", 3)) {
705 if (num >= size) {
706 newSize = (num + 1 + 255) & ~255;
707 if (newSize < 0) {
708 error(-1, "Bad object number");
709 return gFalse;
710 }
711 entries = (XRefEntry *)
712 greallocn(entries, newSize, sizeof(XRefEntry));
713 for (i = size; i < newSize; ++i) {
714 entries[i].offset = 0xffffffff;
715 entries[i].type = xrefEntryFree;
716 }
717 size = newSize;
718 }
719 if (entries[num].type == xrefEntryFree ||
720 gen >= entries[num].gen) {
721 entries[num].offset = pos - start;
722 entries[num].gen = gen;
723 entries[num].type = xrefEntryUncompressed;
724 }
725 }
726 }
727 }
728 }
729 }
730
731 } else if (!strncmp(p, "endstream", 9)) {
732 if (streamEndsLen == streamEndsSize) {
733 streamEndsSize += 64;
734 streamEnds = (Guint *)greallocn(streamEnds,
735 streamEndsSize, sizeof(int));
736 }
737 streamEnds[streamEndsLen++] = pos;
738 }
739 }
740
741 if (gotRoot)
742 return gTrue;
743
744 error(-1, "Couldn't find trailer dictionary");
745 return gFalse;
746 }
747
748 void XRef::setEncryption(int permFlagsA, GBool ownerPasswordOkA,
749 Guchar *fileKeyA, int keyLengthA, int encVersionA) {
750 int i;
751
752 encrypted = gTrue;
753 permFlags = permFlagsA;
754 ownerPasswordOk = ownerPasswordOkA;
755 if (keyLengthA <= 16) {
756 keyLength = keyLengthA;
757 } else {
758 keyLength = 16;
759 }
760 for (i = 0; i < keyLength; ++i) {
761 fileKey[i] = fileKeyA[i];
762 }
763 encVersion = encVersionA;
764 }
765
766 GBool XRef::okToPrint(GBool ignoreOwnerPW) {
767 return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint);
768 }
769
770 GBool XRef::okToChange(GBool ignoreOwnerPW) {
771 return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permChange);
772 }
773
774 GBool XRef::okToCopy(GBool ignoreOwnerPW) {
775 return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permCopy);
776 }
777
778 GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
779 return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permNotes);
780 }
781
782 Object *XRef::fetch(int num, int gen, Object *obj) {
783 XRefEntry *e;
784 Parser *parser;
785 Object obj1, obj2, obj3;
786
787 // check for bogus ref - this can happen in corrupted PDF files
788 if (num < 0 || num >= size) {
789 goto err;
790 }
791
792 e = &entries[num];
793 switch (e->type) {
794
795 case xrefEntryUncompressed:
796 if (e->gen != gen) {
797 goto err;
798 }
799 obj1.initNull();
800 parser = new Parser(this,
801 new Lexer(this,
802 str->makeSubStream(start + e->offset, gFalse, 0, &obj1)));
803 parser->getObj(&obj1);
804 parser->getObj(&obj2);
805 parser->getObj(&obj3);
806 if (!obj1.isInt() || obj1.getInt() != num ||
807 !obj2.isInt() || obj2.getInt() != gen ||
808 !obj3.isCmd("obj")) {
809 obj1.free();
810 obj2.free();
811 obj3.free();
812 delete parser;
813 goto err;
814 }
815 parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, keyLength,
816 num, gen);
817 obj1.free();
818 obj2.free();
819 obj3.free();
820 delete parser;
821 break;
822
823 case xrefEntryCompressed:
824 if (gen != 0) {
825 goto err;
826 }
827 if (!objStr || objStr->getObjStrNum() != (int)e->offset) {
828 if (objStr) {
829 delete objStr;
830 }
831 objStr = new ObjectStream(this, e->offset);
832 }
833 objStr->getObject(e->gen, num, obj);
834 break;
835
836 default:
837 goto err;
838 }
839
840 return obj;
841
842 err:
843 return obj->initNull();
844 }
845
846 Object *XRef::getDocInfo(Object *obj) {
847 return trailerDict.dictLookup("Info", obj);
848 }
849
850 // Added for the pdftex project.
851 Object *XRef::getDocInfoNF(Object *obj) {
852 return trailerDict.dictLookupNF("Info", obj);
853 }
854
855 GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
856 int a, b, m;
857
858 if (streamEndsLen == 0 ||
859 streamStart > streamEnds[streamEndsLen - 1]) {
860 return gFalse;
861 }
862
863 a = -1;
864 b = streamEndsLen - 1;
865 // invariant: streamEnds[a] < streamStart <= streamEnds[b]
866 while (b - a > 1) {
867 m = (a + b) / 2;
868 if (streamStart <= streamEnds[m]) {
869 b = m;
870 } else {
871 a = m;
872 }
873 }
874 *streamEnd = streamEnds[b];
875 return gTrue;
876 }
877
878 Guint XRef::strToUnsigned(char *s) {
879 Guint x;
880 char *p;
881 int i;
882
883 x = 0;
884 for (p = s, i = 0; *p && isdigit(*p) && i < 10; ++p, ++i) {
885 x = 10 * x + (*p - '0');
886 }
887 return x;
888 }