Branch data Line data Source code
1 : : /* -*- mode: C -*-
2 : : *
3 : : * File: pdf-token-reader.c
4 : : * Date: Mon Dec 29 00:45:09 2008
5 : : *
6 : : * GNU PDF Library - Stream tokeniser
7 : : *
8 : : */
9 : :
10 : : /* Copyright (C) 2008-2011 Free Software Foundation, Inc. */
11 : :
12 : : /* This program is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU General Public License as published by
14 : : * the Free Software Foundation, either version 3 of the License, or
15 : : * (at your option) any later version.
16 : : *
17 : : * This program is distributed in the hope that it will be useful,
18 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 : : * GNU General Public License for more details.
21 : : *
22 : : * You should have received a copy of the GNU General Public License
23 : : * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 : : */
25 : :
26 : : #include <config.h>
27 : :
28 : : #include <stdlib.h>
29 : : #include <string.h>
30 : :
31 : : #include <pdf-tokeniser.h>
32 : : #include <pdf-token-reader.h>
33 : :
34 : : enum pdf_token_reader_state_e
35 : : {
36 : : /* PDF_TOKR_ is used as an internal prefix for the token reader. */
37 : : PDF_TOKR_STATE_NONE = 0,
38 : : PDF_TOKR_STATE_COMMENT,
39 : : PDF_TOKR_STATE_KEYWORD,
40 : : PDF_TOKR_STATE_NAME,
41 : : PDF_TOKR_STATE_STRING,
42 : : PDF_TOKR_STATE_HEXSTRING,
43 : : PDF_TOKR_STATE_DICTEND,
44 : : PDF_TOKR_STATE_PENDING,
45 : : PDF_TOKR_STATE_EOF
46 : : };
47 : :
48 : : /* Token reader states (from pdf_token_reader_state_e):
49 : : * NONE - Initial state; not reading a token.
50 : : * COMMENT - Reading a comment. buffer collects the comment bytes.
51 : : * Substates:
52 : : * 0 - normal state
53 : : * 1 - don't produce a token
54 : : * KEYWORD - Reading some regular characters into buffer; this could result
55 : : * in a symbol like "null", or a number.
56 : : * NAME - Reading a name (which starts with '/').
57 : : * Substates:
58 : : * 0 - normal state
59 : : * 1 - just read a '#' (escape prefix)
60 : : * 2 - read the first hex digit after '#'; the value is in charparam
61 : : * buffer collects the name, excluding the initial '/'.
62 : : * STRING - Reading a literal string (enclosed in '(', ')').
63 : : * Substates:
64 : : * 0 - normal state
65 : : * 1 - ignore the next byte if its value is 10 (ASCII LF;
66 : : * this is used to treat CRLF as a single line ending)
67 : : * 2 - just saw a backslash (escape prefix)
68 : : * 3 - read 1 octal digit; the value is in charparam
69 : : * 4 - read 2 octal digits; the value is in charparam
70 : : * intparam is the bracket nesting level; ')' at level 0 ends the string.
71 : : * buffer collects the string.
72 : : * HEXSTRING - Reading a hex string.
73 : : * Substates:
74 : : * 0 - initial state: we just saw the opening '<', and if the next byte is
75 : : * also '<' this is the start of a dictionary rather than a string
76 : : * 1 - normal state (the next hex digit will be the first in a pair)
77 : : * 2 - read the first hex digit; its value is in charparam
78 : : * 3 - end state; saw the closing '>'
79 : : * buffer collects the string.
80 : : * DICTEND - Just got a '>'; expecting another.
81 : : * Substates:
82 : : * 0 - starting state
83 : : * 1 - saw the second '>'
84 : : * PENDING - Need to emit a token (determined by charparam) ASAP.
85 : : * EOF - Can't continue tokenising (reached EOF, or beginning of stream)
86 : : */
87 : :
88 : : /* Internal state */
89 : : struct pdf_token_reader_s {
90 : : pdf_stm_t *stream; /* stream to read bytes from */
91 : :
92 : : pdf_size_t state_pos;
93 : : pdf_size_t beg_pos; /* Beginning position of the last read token in
94 : : the input stream */
95 : :
96 : : /* variables used by the state machine (described above) */
97 : : int state;
98 : : int substate;
99 : : pdf_char_t charparam;
100 : : int intparam;
101 : : pdf_buffer_t *buffer;
102 : : /***/
103 : : pdf_size_t buffer_size_min;
104 : : };
105 : :
106 : : /* Returns 255 on invalid hex values */
107 : : #define HEXVAL(ch) \
108 : : ((ch >= '0' && ch <= '9') ? ch - '0' : \
109 : : ((ch >= 'A' && ch <= 'F') ? ch - 'A' + 10 : \
110 : : ((ch >= 'a' && ch <= 'f') ? ch - 'a' + 10 : \
111 : : 255)))
112 : :
113 : : static pdf_bool_t
114 : : reset_buffer (pdf_token_reader_t *reader,
115 : : pdf_error_t **error)
116 : : {
117 : 2332 : reader->buffer->wp = 0;
118 : :
119 : : /* Try to shrink the buffer */
120 [ - + # # ]: 2332 : return ((reader->buffer->size > reader->buffer_size_min &&
[ - + ][ # # ]
[ + + ][ + - ]
121 : : !pdf_buffer_resize (reader->buffer,
122 : : reader->buffer_size_min,
123 : : error)) ? PDF_FALSE : PDF_TRUE);
124 : : }
125 : :
126 : : pdf_token_reader_t *
127 : 703 : pdf_token_reader_new (pdf_stm_t *stm,
128 : : pdf_error_t **error)
129 : : {
130 : 703 : pdf_token_reader_t *tokr = NULL;
131 : :
132 [ - + ]: 703 : PDF_ASSERT_POINTER_RETURN_VAL (stm, NULL);
133 : :
134 : : /* Allow only read streams */
135 [ - + ]: 703 : if (pdf_stm_get_mode (stm) != PDF_STM_READ)
136 : : {
137 : 0 : pdf_set_error (error,
138 : : PDF_EDOMAIN_BASE_TOKENISER,
139 : : PDF_EBADDATA,
140 : : "cannot create token reader: "
141 : : "read stream needed");
142 : 0 : return NULL;
143 : : }
144 : :
145 : 703 : tokr = pdf_alloc (sizeof (struct pdf_token_reader_s));
146 [ - + ]: 703 : if (!tokr)
147 : : {
148 : 0 : pdf_set_error (error,
149 : : PDF_EDOMAIN_BASE_TOKENISER,
150 : : PDF_ENOMEM,
151 : : "cannot create token reader: "
152 : : "couldn't allocate '%lu' bytes",
153 : : (unsigned long)sizeof (struct pdf_token_reader_s));
154 : 0 : return NULL;
155 : : }
156 : :
157 : 703 : tokr->beg_pos = 0;
158 : 703 : tokr->state_pos = 0;
159 : :
160 : : /* buffer_size_min is the default buffer size, which is also the maximum
161 : : * size for keywords, names, numbers, etc.; strings and comments will
162 : : * enlarge the buffer to whatever size is needed. */
163 : 703 : tokr->buffer_size_min = 32768;
164 : 703 : tokr->buffer = pdf_buffer_new (tokr->buffer_size_min, error);
165 [ - + ]: 703 : if (!tokr->buffer)
166 : : {
167 : 0 : pdf_token_reader_destroy (tokr);
168 : 0 : return NULL;
169 : : }
170 : :
171 : 703 : tokr->stream = stm;
172 [ - + ]: 703 : if (!pdf_token_reader_reset (tokr, error))
173 : : {
174 : 0 : pdf_token_reader_destroy (tokr);
175 : 0 : return NULL;
176 : : }
177 : :
178 : 703 : return tokr;
179 : : }
180 : :
181 : : static void
182 : : enter_state (pdf_token_reader_t *reader,
183 : : enum pdf_token_reader_state_e state)
184 : : {
185 : 2489 : reader->state = state;
186 : 2489 : reader->state_pos = pdf_stm_tell (reader->stream);
187 : : }
188 : :
189 : : pdf_bool_t
190 : 703 : pdf_token_reader_reset (pdf_token_reader_t *reader,
191 : : pdf_error_t **error)
192 : : {
193 [ - + ]: 703 : PDF_ASSERT_POINTER_RETURN_VAL (reader, PDF_FALSE);
194 : :
195 : : enter_state (reader, PDF_TOKR_STATE_NONE);
196 : 703 : reader->substate = 0;
197 : 703 : return reset_buffer (reader, error);
198 : : }
199 : :
200 : : void
201 : 522 : pdf_token_reader_destroy (pdf_token_reader_t *reader)
202 : : {
203 [ + - ]: 522 : if (!reader)
204 : : return;
205 : :
206 [ + - ]: 522 : if (reader->buffer)
207 : 522 : pdf_buffer_destroy (reader->buffer);
208 : 522 : pdf_dealloc (reader);
209 : : }
210 : :
211 : : static pdf_i32_t
212 : : parse_integer (pdf_buffer_t *buffer,
213 : : pdf_i32_t *int_value,
214 : : pdf_i32_t *int_state)
215 : : {
216 : : /* Parse an ASCII integer with the given radix, at the beginning of
217 : : * the buffer (possibly leaving unread bytes at the end).
218 : : *
219 : : * Return value is 0 on failure, or a bitmask otherwise:
220 : : * 1 = valid integer
221 : : * 2 = signed
222 : : * 4 = overflowed (no value stored in *int_value)
223 : : */
224 : :
225 : 825 : pdf_i32_t sign = 0;
226 : 825 : pdf_i32_t tmpint = 0;
227 : 825 : pdf_bool_t overflowed = PDF_FALSE;
228 : : pdf_i32_t ret;
229 : :
230 : : /* Integer states (int_state):
231 : : * 0 = at start (looking for sign or digits)
232 : : * 1 = saw sign
233 : : * 2 = saw digits
234 : : */
235 : :
236 : 825 : *int_state = 0;
237 [ + + ]: 1062 : for (; buffer->rp < buffer->wp; ++buffer->rp)
238 : : {
239 : : pdf_i32_t chval;
240 : : pdf_char_t ch;
241 : :
242 : 888 : ch = buffer->data[buffer->rp];
243 : :
244 [ - + ]: 888 : if (ch == '+' || ch == '-')
245 : : {
246 [ # # ]: 0 : if (*int_state != 0)
247 : : break;
248 : :
249 : 0 : *int_state = 1;
250 [ # # ]: 0 : sign = (ch == '-') ? 1 : -1;
251 : 0 : continue;
252 : : }
253 : :
254 : 888 : chval = ch - '0'; /* assume this is a digit */
255 [ + + ]: 888 : if (chval < 0 || chval > 9)
256 : : break; /* not a valid number */
257 : :
258 : 237 : *int_state = 2;
259 [ + + ]: 237 : if (overflowed)
260 : 14 : continue;
261 : :
262 : : /* convert the digits to an integer, if possible */
263 [ - + ]: 223 : if (sign < 0)
264 : : {
265 : 0 : chval = -chval;
266 [ # # ][ # # ]: 0 : if (tmpint < (INT_MIN / 10) ||
267 : 0 : (tmpint == (INT_MIN / 10) &&
268 : 0 : chval < (INT_MIN % 10)))
269 : : {
270 : 0 : overflowed = PDF_TRUE; /* would overflow */
271 : 0 : continue;
272 : : }
273 : : }
274 : : else
275 : : {
276 [ + + ][ - + ]: 223 : if (tmpint > (INT_MAX / 10) ||
277 : 444 : (tmpint == (INT_MAX / 10) &&
278 : 222 : chval > (INT_MAX % 10)))
279 : : {
280 : 1 : overflowed = PDF_TRUE; /* would overflow */
281 : 1 : continue;
282 : : }
283 : : }
284 : :
285 : 222 : tmpint += chval + (tmpint * 9);
286 : : }
287 : :
288 [ + + ]: 825 : if (*int_state != 2)
289 : 639 : return 0; /* never saw any digits */
290 : :
291 : 186 : ret = 1;
292 [ - + ]: 186 : if (sign)
293 : 0 : ret += 2;
294 : :
295 [ + + ]: 186 : if (overflowed)
296 : 1 : ret += 4;
297 : : else
298 : 185 : *int_value = tmpint;
299 : :
300 : 186 : return ret;
301 : : }
302 : :
303 : : static pdf_bool_t
304 : : validate_real (pdf_buffer_t *buffer,
305 : : pdf_i32_t int_state)
306 : : {
307 : : /* Determines whether the given number is a valid PS/PDF real number;
308 : : * assumes the initial sign was already read (if present), and any data
309 : : * before buffer->rp is a valid integer.
310 : : *
311 : : * Return value:
312 : : * 0 = not a real number
313 : : * 1 = valid PDF/PS real
314 : : */
315 : :
316 : 651 : int seen_point = 0;
317 : :
318 : : /* Integer states (int_state):
319 : : * 0 = at start
320 : : * 1 = saw sign
321 : : * 2 = saw digits
322 : : */
323 : :
324 [ + + ]: 680 : for (; buffer->rp < buffer->wp; ++buffer->rp)
325 : : {
326 : : pdf_char_t ch;
327 : :
328 : 668 : ch = buffer->data[buffer->rp];
329 [ + + ]: 668 : if (ch == '.') /* '.' */
330 : : {
331 [ + - ]: 12 : if (!seen_point)
332 : 12 : seen_point = 1;
333 : : else
334 : 0 : return PDF_FALSE;
335 : : }
336 [ - + ]: 656 : else if (ch == '+' || ch == '-')
337 : : {
338 [ # # ]: 0 : if (int_state == 0)
339 : 0 : int_state = 1;
340 : : else
341 : 0 : return PDF_FALSE;
342 : : }
343 [ + + ]: 656 : else if (ch >= '0' && ch <= '9')
344 : 17 : int_state = 2;
345 : : else
346 : 639 : return PDF_FALSE;
347 : : }
348 : :
349 : : /* only valid if we saw a digit */
350 : 12 : return (int_state == 2 ? PDF_TRUE : PDF_FALSE);
351 : : }
352 : :
353 : : /* Given a buffer containing a validated PDF real (in ASCII), convert it to a
354 : : * double by translating it to the execution character set, replacing '.' with
355 : : * the locale's decimal point, and calling strtod. */
356 : : static pdf_bool_t
357 : 13 : parse_real (pdf_buffer_t *buffer,
358 : : const pdf_char_t *locale_dec_pt,
359 : : double *value,
360 : : pdf_error_t **error)
361 : : {
362 : : pdf_size_t tmplen;
363 : : pdf_size_t wpos;
364 : : pdf_size_t ptlen;
365 : : pdf_char_t *tmp;
366 : : pdf_char_t *endptr;
367 : :
368 : 13 : ptlen = strlen (locale_dec_pt);
369 : : /* we may remove 1 byte ('.') and replace it with ptlen bytes */
370 : 13 : tmplen = buffer->wp - 1 + ptlen;
371 : :
372 : 13 : tmp = pdf_alloc (tmplen + 1);
373 [ - + ]: 13 : if (!tmp)
374 : : {
375 : 0 : pdf_set_error (error,
376 : : PDF_EDOMAIN_BASE_TOKENISER,
377 : : PDF_ENOMEM,
378 : : "cannot parse real: "
379 : : "couldn't allocate '%lu' bytes",
380 : : (unsigned long)(tmplen + 1));
381 : 0 : return PDF_FALSE;
382 : : }
383 : :
384 : 13 : wpos = 0;
385 [ + + ]: 79 : for (buffer->rp = 0; buffer->rp < buffer->wp; ++buffer->rp)
386 : : {
387 : : pdf_char_t ch;
388 : :
389 : 66 : ch = buffer->data[buffer->rp];
390 [ - + ]: 66 : if (wpos >= tmplen)
391 : : {
392 : 0 : pdf_set_error (error,
393 : : PDF_EDOMAIN_BASE_TOKENISER,
394 : : PDF_ERROR,
395 : : "cannot parse real: "
396 : : "out of bounds, pos(%lf) >= len(%lf)",
397 : : wpos, tmplen);
398 : 0 : pdf_dealloc (tmp);
399 : 0 : return PDF_FALSE;
400 : : }
401 : :
402 [ + + ]: 66 : if (ch == '.')
403 : : {
404 [ - + ]: 12 : if (wpos + ptlen > tmplen)
405 : : {
406 : 0 : pdf_set_error (error,
407 : : PDF_EDOMAIN_BASE_TOKENISER,
408 : : PDF_ERROR,
409 : : "cannot parse real: "
410 : : "out of bounds, pos(%lf) + ptlen(%lf) > len(%lf)",
411 : : wpos, ptlen, tmplen);
412 : 0 : pdf_dealloc (tmp);
413 : 0 : return PDF_FALSE;
414 : : }
415 : :
416 : 12 : memcpy (tmp + wpos, locale_dec_pt, ptlen);
417 : 12 : wpos += ptlen;
418 : : }
419 [ + - ][ + - ]: 54 : else if (ch == '+' ||
420 : 54 : ch == '-' ||
421 : 54 : (ch >= '0' && ch <= '9'))
422 : : {
423 : 54 : tmp[wpos++] = ch;
424 : : }
425 : : else
426 : : {
427 : 0 : pdf_set_error (error,
428 : : PDF_EDOMAIN_BASE_TOKENISER,
429 : : PDF_ERROR,
430 : : "cannot parse real: "
431 : : "unexpected value: %c (%d)",
432 : : ch, ch);
433 : 0 : pdf_dealloc (tmp);
434 : 0 : return PDF_FALSE;
435 : : }
436 : : }
437 : :
438 : : /* null-terminate the new string, and call strtod to get its value
439 : : * (strtof would also work if it's available) */
440 : 13 : tmp[wpos] = '\0';
441 : 13 : *value = strtod (tmp, &endptr);
442 [ + - ]: 13 : if (endptr == tmp + wpos)
443 : : {
444 : 13 : pdf_dealloc (tmp);
445 : 13 : return PDF_TRUE;
446 : : }
447 : :
448 : 0 : pdf_dealloc (tmp);
449 : 13 : return PDF_FALSE;
450 : : }
451 : :
452 : : /*
453 : : * Return value:
454 : : * 0 = not a number
455 : : * 1 = integer (stored in *int_value)
456 : : * 2 = real
457 : : */
458 : : static pdf_i32_t
459 : : recognise_number (pdf_buffer_t *buffer,
460 : : pdf_i32_t *int_value)
461 : : {
462 : : pdf_i32_t rv;
463 : 825 : pdf_i32_t tmpint = 0;
464 : 825 : pdf_i32_t int_state = 0;
465 : :
466 : : /* try to parse as an integer */
467 : :
468 : 825 : buffer->rp = 0;
469 : 825 : rv = parse_integer (buffer, &tmpint, &int_state);
470 : :
471 [ + + ]: 825 : if (buffer->rp < buffer->wp) /* didn't look at the whole buffer */
472 [ + + ]: 651 : return (validate_real (buffer, int_state) ? 2 : 0);
473 : :
474 [ - + ]: 174 : if (!rv)
475 : 0 : return 0;
476 [ + + ]: 174 : else if (rv & 4)
477 : 1 : return 2; /* integer overflowed, but could be read as a real */
478 : :
479 : 173 : *int_value = tmpint;
480 : 173 : return 1;
481 : : }
482 : :
483 : : static pdf_bool_t
484 : 1808 : flush_token (pdf_token_reader_t *reader,
485 : : pdf_u32_t flags,
486 : : pdf_bool_t *eof,
487 : : pdf_token_t **token,
488 : : pdf_error_t **error)
489 : : {
490 : : pdf_token_t *new_token;
491 : 1808 : pdf_char_t *data = (pdf_char_t *)reader->buffer->data;
492 : 1808 : int datasize = reader->buffer->wp;
493 : :
494 [ + + + + + : 1808 : switch (reader->state)
+ + + + - ]
495 : : {
496 : : case PDF_TOKR_STATE_NONE:
497 : : /* no state to exit */
498 : 3 : return PDF_TRUE;
499 : :
500 : : case PDF_TOKR_STATE_EOF:
501 : : /* can't continue parsing after EOF */
502 : 176 : *eof = PDF_TRUE;
503 : 176 : return PDF_TRUE;
504 : :
505 : : case PDF_TOKR_STATE_COMMENT:
506 : : {
507 [ - + ][ # # ]: 3 : if (reader->substate == 1 ||
508 : 0 : !(flags & PDF_TOKEN_RET_COMMENTS))
509 : : {
510 : : /* don't return a token */
511 : 3 : return reset_buffer (reader, error);
512 : : }
513 : :
514 : 0 : new_token = pdf_token_comment_new (data, datasize, error);
515 : : }
516 : 0 : break;
517 : :
518 : : case PDF_TOKR_STATE_KEYWORD:
519 : : {
520 : : int value;
521 : : int ntyp;
522 : :
523 : 1650 : ntyp = recognise_number (reader->buffer, &value);
524 [ + + ]: 825 : if (ntyp == 1)
525 : : {
526 : 173 : new_token = pdf_token_integer_new (value, error);
527 : : }
528 [ + + ]: 652 : else if (ntyp == 2)
529 : : {
530 : : double realvalue;
531 : :
532 [ - + ]: 13 : if (!parse_real (reader->buffer,
533 : : pdf_tokeniser_get_decimal_point (),
534 : : &realvalue,
535 : : error))
536 : 0 : return PDF_FALSE;
537 : :
538 : 13 : new_token = pdf_token_real_new ((float)realvalue, error);
539 : : }
540 : : else
541 : : {
542 : 639 : new_token = pdf_token_keyword_new (data, datasize, error);
543 : : }
544 : : }
545 : : break;
546 : :
547 : : case PDF_TOKR_STATE_NAME:
548 : : {
549 [ - + ]: 358 : if (reader->substate != 0) /* reading an escape sequence */
550 : : {
551 : 0 : pdf_set_error (error,
552 : : PDF_EDOMAIN_BASE_TOKENISER,
553 : : PDF_EBADFILE,
554 : : "cannot flush token: invalid state");
555 : 0 : return PDF_FALSE;
556 : : }
557 : :
558 : 358 : new_token = pdf_token_name_new (data, datasize, error);
559 : : }
560 : 358 : break;
561 : :
562 : : case PDF_TOKR_STATE_STRING:
563 : : {
564 [ - + ]: 7 : if (reader->intparam >= 0) /* didn't see the closing ')' */
565 : : {
566 : 0 : pdf_set_error (error,
567 : : PDF_EDOMAIN_BASE_TOKENISER,
568 : : PDF_EBADFILE,
569 : : "cannot flush token: "
570 : : "(string) no closing ')'");
571 : 0 : return PDF_FALSE;
572 : : }
573 : :
574 : 7 : new_token = pdf_token_string_new (data, datasize, error);
575 : : }
576 : 7 : break;
577 : :
578 : : case PDF_TOKR_STATE_HEXSTRING:
579 : : {
580 [ - + ]: 3 : if (reader->substate != 3) /* didn't see the closing '>' */
581 : : {
582 : 0 : pdf_set_error (error,
583 : : PDF_EDOMAIN_BASE_TOKENISER,
584 : : PDF_EBADFILE,
585 : : "cannot flush token: "
586 : : "(hexstring) no closing '>'");
587 : 0 : return PDF_FALSE;
588 : : }
589 : :
590 : 3 : new_token = pdf_token_string_new (data, datasize, error);
591 : : }
592 : 3 : break;
593 : :
594 : : case PDF_TOKR_STATE_DICTEND:
595 : : {
596 [ - + ]: 1 : if (reader->substate != 1) /* didn't see a second '>' */
597 : : {
598 : 0 : pdf_set_error (error,
599 : : PDF_EDOMAIN_BASE_TOKENISER,
600 : : PDF_EBADFILE,
601 : : "cannot flush token: "
602 : : "(dictend) no second '>'");
603 : 0 : return PDF_FALSE;
604 : : }
605 : :
606 : 1 : new_token = pdf_token_valueless_new (PDF_TOKEN_DICT_END, error);
607 : : }
608 : 1 : break;
609 : :
610 : : case PDF_TOKR_STATE_PENDING:
611 : : {
612 [ + + + + + : 432 : switch (reader->charparam)
- ]
613 : : {
614 : : case '<':
615 : 1 : new_token = pdf_token_valueless_new (PDF_TOKEN_DICT_START, error);
616 : 1 : break;
617 : : case '[':
618 : 1 : new_token = pdf_token_valueless_new (PDF_TOKEN_ARRAY_START, error);
619 : 1 : break;
620 : : case ']':
621 : 1 : new_token = pdf_token_valueless_new (PDF_TOKEN_ARRAY_END, error);
622 : 1 : break;
623 : : case '{':
624 : 215 : new_token = pdf_token_valueless_new (PDF_TOKEN_PROC_START, error);
625 : 215 : break;
626 : : case '}':
627 : 214 : new_token = pdf_token_valueless_new (PDF_TOKEN_PROC_END, error);
628 : 214 : break;
629 : : default:
630 : 0 : pdf_set_error (error,
631 : : PDF_EDOMAIN_BASE_TOKENISER,
632 : : PDF_ERROR,
633 : : "cannot flush token: "
634 : : "invalid char found '%c' (%d)",
635 : 0 : reader->charparam,
636 : 0 : reader->charparam);
637 : 0 : return PDF_FALSE;
638 : : }
639 : : }
640 : : break;
641 : :
642 : : default:
643 : 0 : pdf_set_error (error,
644 : : PDF_EDOMAIN_BASE_TOKENISER,
645 : : PDF_ERROR,
646 : : "cannot flush token: "
647 : : "invalid state (%d)",
648 : : reader->state);
649 : 0 : return PDF_FALSE;
650 : : }
651 : :
652 : : /* If no token generated, return already set error */
653 [ - + ]: 1626 : if (!new_token)
654 : : {
655 : 0 : pdf_prefix_error (error, "cannot flush token: ");
656 : 0 : return PDF_FALSE;
657 : : }
658 : :
659 : : /* Set output new token */
660 : 1626 : *token = new_token;
661 : :
662 : : /* Set the beginning position of this state */
663 : 1626 : reader->beg_pos = reader->state_pos;
664 : :
665 : 1808 : return reset_buffer (reader, error);
666 : : }
667 : :
668 : : static pdf_bool_t
669 : 1808 : exit_state (pdf_token_reader_t *reader,
670 : : pdf_u32_t flags,
671 : : pdf_bool_t *eof,
672 : : pdf_token_t **token,
673 : : pdf_error_t **error)
674 : : {
675 [ - + ]: 1808 : if (!flush_token (reader, flags, eof, token, error))
676 : 0 : return PDF_FALSE;
677 : :
678 : 1808 : reader->state = PDF_TOKR_STATE_NONE;
679 : 1808 : reader->substate = 0;
680 : 1808 : return PDF_TRUE;
681 : : }
682 : :
683 : : #define CAN_STORE_CHAR(reader) \
684 : : (reader->buffer->wp < reader->buffer->size ? \
685 : : PDF_TRUE : PDF_FALSE)
686 : :
687 : : static pdf_bool_t
688 : : enlarge_buffer (pdf_token_reader_t *reader,
689 : : pdf_error_t **error)
690 : : {
691 : : pdf_size_t size;
692 : : pdf_size_t new_size;
693 : :
694 : 1 : size = reader->buffer->size;
695 : 1 : new_size = size * 2;
696 : :
697 [ - + ]: 1 : if (new_size < size)
698 : 0 : PDF_ASSERT_TRACE_NOT_REACHED ();
699 : :
700 : 1 : return pdf_buffer_resize (reader->buffer, new_size, error);
701 : : }
702 : :
703 : : static pdf_bool_t
704 : : store_char (pdf_token_reader_t *reader,
705 : : pdf_char_t ch,
706 : : pdf_error_t **error)
707 : : {
708 [ - + ][ - + ]: 2834 : if (!CAN_STORE_CHAR (reader))
[ - + ]
709 : : {
710 : 0 : pdf_set_error (error,
711 : : PDF_EDOMAIN_BASE_TOKENISER,
712 : : PDF_ENOMEM,
713 : : "cannot store char: "
714 : : "not enough empty space");
715 : 0 : return PDF_FALSE;
716 : : }
717 : :
718 : 2834 : reader->buffer->data[reader->buffer->wp++] = ch;
719 : 2834 : return PDF_TRUE;
720 : : }
721 : :
722 : : static pdf_bool_t
723 : 42109 : store_char_grow (pdf_token_reader_t *reader,
724 : : pdf_char_t ch,
725 : : pdf_error_t **error)
726 : : {
727 [ + + - + ]: 42110 : if (!CAN_STORE_CHAR (reader) &&
728 : : !enlarge_buffer (reader, error))
729 : 0 : return PDF_FALSE;
730 : :
731 : 42109 : reader->buffer->data[reader->buffer->wp++] = ch;
732 : 42109 : return PDF_TRUE;
733 : : }
734 : :
735 : : static pdf_bool_t
736 : 42129 : handle_string_char (pdf_token_reader_t *reader,
737 : : pdf_u32_t flags,
738 : : pdf_char_t ch,
739 : : pdf_bool_t *eof,
740 : : pdf_token_t **token,
741 : : pdf_error_t **error)
742 : : {
743 : : while (PDF_TRUE)
744 : : {
745 [ + + + + : 42129 : switch (reader->substate)
- ]
746 : : {
747 : : case 1: /* ignore LF */
748 : : {
749 : 6 : reader->substate = 0;
750 [ + + ]: 6 : if (ch == '\n')
751 : 3 : return PDF_TRUE;
752 : : } /* fall through */
753 : :
754 : : case 0: /* no special state */
755 : : {
756 : : pdf_bool_t was_cr;
757 : :
758 [ + + ]: 42104 : if (ch == '\\')
759 : : {
760 : : /* start an escape sequence */
761 : 18 : reader->substate = 2;
762 : 18 : return PDF_TRUE;
763 : : }
764 : :
765 [ + + ][ + + ]: 42086 : if (ch == ')' &&
766 : 9 : reader->intparam <= 0) /* ')'; end of string */
767 : : {
768 : 7 : reader->intparam = -1;
769 : 7 : return exit_state (reader, flags, eof, token, error);
770 : : }
771 : :
772 : 42079 : was_cr = (ch == '\r');
773 [ + + ]: 42079 : if (was_cr)
774 : 3 : ch = '\n'; /* treat as LF */
775 : :
776 [ - + ]: 42079 : if (!store_char_grow (reader, ch, error))
777 : 0 : return PDF_FALSE;
778 : :
779 [ + + ]: 42079 : if (was_cr)
780 : : {
781 : : /* ignore the next char if it's LF */
782 : 3 : reader->substate = 1;
783 : : }
784 [ + + ]: 42076 : else if (ch == '(')
785 : 2 : ++reader->intparam;
786 [ + + ]: 42074 : else if (ch == ')')
787 : 2 : --reader->intparam;
788 : :
789 : 42079 : return PDF_TRUE;
790 : : }
791 : :
792 : : case 2: /* just saw a '\\' (starting an escape sequence) */
793 : : {
794 : 18 : reader->substate = 0;
795 : :
796 [ + + ]: 18 : if (ch == 'b')
797 : 1 : ch = 8; /* BS: backspace */
798 [ + + ]: 17 : else if (ch == 'f')
799 : 1 : ch = 12; /* FF: formfeed */
800 [ + + ]: 16 : else if (ch == 'n')
801 : 1 : ch = '\n'; /* NL: newline */
802 [ + + ]: 15 : else if (ch == 'r')
803 : 1 : ch = '\r'; /* CR: carriage return */
804 [ + + ]: 14 : else if (ch == 't')
805 : 1 : ch = '\t'; /* HT: horizontal tab */
806 [ + + ]: 13 : else if (ch == '\n') /* NL */
807 : 1 : return PDF_TRUE; /* ignore the line break */
808 [ + + ]: 12 : else if (ch == '\r') /* CR */
809 : : {
810 : : /* ignore the line break; also ignore the next byte if it's LF */
811 : 3 : reader->substate = 1;
812 : 3 : return PDF_TRUE;
813 : : }
814 [ + + ]: 9 : else if (ch >= '0' && ch <= '7')
815 : : {
816 : : /* starting an octal escape; we'll read three digits even if the
817 : : * first is '4'--'7' (and calculate the final char modulo 256),
818 : : * since the PDF/PS specs say to ignore high-order overflow */
819 : 2 : reader->substate = 3;
820 : 2 : reader->charparam = (ch - '0');
821 : 2 : return PDF_TRUE;
822 : : }
823 : :
824 : : /* for any other character, including '(', ')', and '\\',
825 : : * store the same character (dropping the leading backslash) */
826 : 12 : return store_char_grow (reader, ch, error);
827 : : }
828 : :
829 : : case 3: /* saw 1 digit of an octal escape */
830 : : {
831 : : } /* fall through */
832 : :
833 : : case 4: /* saw 2 digits of an octal escape */
834 : : {
835 [ + + ]: 4 : if (ch < '0' || ch > '7')
836 : : {
837 [ - + ]: 1 : if (!store_char_grow (reader, reader->charparam, error))
838 : 0 : return PDF_FALSE;
839 : :
840 : : /* ch isn't part of the escape sequence, so retry */
841 : 1 : reader->substate = 0;
842 : 1 : continue;
843 : : }
844 : :
845 : : /* ch is a digit from '0'--'7' */
846 : 3 : reader->charparam = ((reader->charparam & 0x1f) << 3) | (ch - '0');
847 [ + + ]: 3 : if (reader->substate == 4) /* this was the final digit */
848 : : {
849 [ - + ]: 1 : if (!store_char_grow (reader, reader->charparam, error))
850 : 0 : return PDF_FALSE;
851 : :
852 : 1 : reader->substate = 0;
853 : 1 : return PDF_TRUE;
854 : : }
855 : :
856 : 2 : reader->substate = 4;
857 : 2 : return PDF_TRUE;
858 : : }
859 : :
860 : : default:
861 : : {
862 : 0 : pdf_set_error (error,
863 : : PDF_EDOMAIN_BASE_TOKENISER,
864 : : PDF_ERROR,
865 : : "cannot handle string char: "
866 : : "invalid substate (%d)",
867 : : reader->substate);
868 : 42128 : return PDF_FALSE;
869 : : }
870 : : }
871 : 1 : }
872 : : }
873 : :
874 : : static pdf_bool_t
875 : 46 : handle_hexstring_char (pdf_token_reader_t *reader,
876 : : pdf_u32_t flags,
877 : : pdf_char_t ch,
878 : : pdf_bool_t *eof,
879 : : pdf_token_t **token,
880 : : pdf_error_t **error)
881 : : {
882 [ + + ]: 46 : if (reader->substate == 0)
883 : : {
884 : : /* this is the first character after the initial '<' */
885 [ + + ]: 4 : if (ch == '<') /* '>': start of hex string */
886 : : {
887 : : /* this was actually the start of a dictionary */
888 : 1 : reader->state = PDF_TOKR_STATE_PENDING;
889 : 1 : reader->charparam = ch;
890 : 1 : return exit_state (reader, flags, eof, token, error);
891 : : }
892 : :
893 : 3 : reader->substate = 1;
894 : : }
895 : :
896 : : /* whitespaces are skipped */
897 [ + + ][ + + ]: 45 : if (pdf_is_wspace_char (ch))
[ + + ]
898 : 12 : return PDF_TRUE;
899 : :
900 [ + + ]: 33 : if (ch == '>') /* '>': end of hex string */
901 : : {
902 [ + + ]: 3 : if (reader->substate == 2)
903 : : {
904 : : /* the last digit is missing; assume it's '0' */
905 [ - + ]: 2 : if (!store_char_grow (reader,
906 : 2 : reader->charparam << 4,
907 : : error))
908 : 0 : return PDF_FALSE;
909 : : }
910 : :
911 : 3 : reader->substate = 3; /* saw end of string */
912 : 3 : return exit_state (reader, flags, eof, token, error);
913 : : }
914 : :
915 [ + + ][ + + ]: 30 : if ((ch = HEXVAL (ch)) == 255)
[ + - ]
916 : : {
917 : : pdf_set_error (error,
918 : : PDF_EDOMAIN_BASE_TOKENISER,
919 : : PDF_EBADFILE,
920 : : "cannot handle hexstring char: "
921 : : "not an hex character '%c' (%d)",
922 : : ch, ch);
923 : : return PDF_FALSE;
924 : : }
925 : :
926 [ + + ]: 30 : if (reader->substate == 1) /* first character in a pair */
927 : : {
928 : 16 : reader->substate = 2;
929 : 16 : reader->charparam = ch;
930 : 16 : return PDF_TRUE;
931 : : }
932 : :
933 [ - + ]: 14 : if (!store_char_grow (reader,
934 : 14 : (reader->charparam << 4) | ch,
935 : : error))
936 : 0 : return PDF_FALSE;
937 : :
938 : 14 : reader->substate = 1;
939 : 46 : return PDF_TRUE;
940 : : }
941 : :
942 : : /* Tries to handle the given character and possibly produce a token.
943 : : * Sets (*token) if a token is produced, and leaves it unmodified otherwise.
944 : : *
945 : : * Returns PDF_OK if the character was accepted. Otherwise, an error code
946 : : * is returned, and the call can be repeated later with the same ch value.
947 : : * A token may be produced even if the character isn't accepted.
948 : : */
949 : : static pdf_bool_t
950 : 49258 : handle_char (pdf_token_reader_t *reader,
951 : : pdf_u32_t flags,
952 : : pdf_char_t ch,
953 : : pdf_bool_t *again,
954 : : pdf_bool_t *eof,
955 : : pdf_token_t **token,
956 : : pdf_error_t **error)
957 : : {
958 : : /* first, handle the states that shouldn't be exited when whitespace
959 : : * or a delimiter is seen */
960 : :
961 [ + + + + + : 49258 : switch (reader->state)
+ ]
962 : : {
963 : : case PDF_TOKR_STATE_EOF:
964 : : {
965 : 1 : *eof = PDF_TRUE;
966 : 1 : return PDF_TRUE;
967 : : }
968 : :
969 : : case PDF_TOKR_STATE_STRING:
970 : 42128 : return handle_string_char (reader, flags, ch, eof, token, error);
971 : :
972 : : case PDF_TOKR_STATE_HEXSTRING:
973 : 46 : return handle_hexstring_char (reader, flags, ch, eof, token, error);
974 : :
975 : : case PDF_TOKR_STATE_DICTEND:
976 : : {
977 [ - + ]: 1 : if (ch != '>')
978 : : {
979 : 0 : pdf_set_error (error,
980 : : PDF_EDOMAIN_BASE_TOKENISER,
981 : : PDF_EBADFILE,
982 : : "cannot handle char: "
983 : : "no dict end");
984 : 0 : return PDF_FALSE;
985 : : }
986 : :
987 : 1 : reader->substate = 1; /* saw the closing '>' */
988 : 1 : return exit_state (reader, flags, eof, token, error);
989 : : }
990 : :
991 : : case PDF_TOKR_STATE_COMMENT:
992 : : {
993 [ + + ]: 15 : if (pdf_is_eol_char (ch))
994 : : {
995 [ - + ]: 3 : if (!exit_state (reader, flags, eof, token, error))
996 : 0 : return PDF_FALSE;
997 : :
998 : : /* don't accept this character, but process it next time */
999 : 3 : *again = PDF_TRUE;
1000 : 3 : return PDF_TRUE;
1001 : : }
1002 : :
1003 [ + - ]: 12 : if (!(flags & PDF_TOKEN_RET_COMMENTS))
1004 : 12 : reader->substate = 1;
1005 : :
1006 [ + - ]: 12 : if (reader->substate == 1)
1007 : 12 : return PDF_TRUE; /* we don't care about this comment */
1008 : :
1009 : 0 : return store_char_grow (reader, ch, error);
1010 : : }
1011 : :
1012 : : default:
1013 : : /* Nothing to do */
1014 : : break;
1015 : : }
1016 : :
1017 : : /* now handle delimiters and whitespace */
1018 : :
1019 [ + + ][ + + ]: 7067 : if (pdf_is_wspace_char (ch))
[ + + ]
1020 : : {
1021 [ + + ]: 2571 : if (reader->state)
1022 : : {
1023 [ - + ]: 1403 : if (!exit_state (reader, flags, eof, token, error))
1024 : 0 : return PDF_FALSE;
1025 : :
1026 : : /* avoid reading this byte so PDF_TOKEN_END_AT_STREAM
1027 : : * will work properly if it's '\r' */
1028 : 1403 : *again = PDF_TRUE;
1029 : 1403 : return PDF_TRUE;
1030 : : }
1031 : :
1032 [ + + ]: 1168 : if ((flags & PDF_TOKEN_END_AT_STREAM) && ch == '\n') /* LF */
1033 : : {
1034 : : /* found the beginning of a stream */
1035 : : enter_state (reader, PDF_TOKR_STATE_EOF);
1036 : : }
1037 : :
1038 : 1168 : return PDF_TRUE;
1039 : : }
1040 : :
1041 [ - + ]: 4496 : if ((flags & PDF_TOKEN_END_AT_STREAM) && ch != '%')
1042 : : {
1043 : : /* only allow whitespace/comments after the "stream" keyword */
1044 : 0 : pdf_set_error (error,
1045 : : PDF_EDOMAIN_BASE_TOKENISER,
1046 : : PDF_EBADFILE,
1047 : : "cannot handle char: "
1048 : : "only whitespace/comments allowed after 'stream' keyword");
1049 : 0 : return PDF_FALSE;
1050 : : }
1051 : :
1052 [ + + ][ + + ]: 4496 : if (pdf_is_delim_char (ch))
[ + + ][ + + ]
[ + + ]
1053 : : {
1054 : : /* set state 0 (UNINIT), substate 0, bufpos 0 */
1055 [ + + ]: 991 : if (reader->state)
1056 : : {
1057 [ - + ]: 31 : if (!exit_state (reader, flags, eof, token, error))
1058 : 0 : return PDF_FALSE;
1059 : :
1060 : 31 : *again = PDF_TRUE;
1061 : 31 : return PDF_TRUE;
1062 : : }
1063 : :
1064 [ + + - + + : 960 : switch (ch)
+ + - ]
1065 : : {
1066 : : case '%':
1067 : : {
1068 : : enter_state (reader, PDF_TOKR_STATE_COMMENT);
1069 : 3 : return PDF_TRUE;
1070 : : }
1071 : :
1072 : : case '(':
1073 : : {
1074 : : enter_state (reader, PDF_TOKR_STATE_STRING);
1075 : 7 : reader->intparam = 0;
1076 : 7 : return PDF_TRUE;
1077 : : }
1078 : :
1079 : : case ')':
1080 : : {
1081 : : /* this shouldn't occur outside the STRING and COMMENT states */
1082 : 0 : pdf_set_error (error,
1083 : : PDF_EDOMAIN_BASE_TOKENISER,
1084 : : PDF_EBADFILE,
1085 : : "cannot handle char: "
1086 : : "wrong ')' out of string and comment states");
1087 : 0 : return PDF_FALSE;
1088 : : }
1089 : :
1090 : : case '/':
1091 : : {
1092 : : enter_state (reader, PDF_TOKR_STATE_NAME);
1093 : 514 : return PDF_TRUE;
1094 : : }
1095 : :
1096 : : case '<':
1097 : : {
1098 : : enter_state (reader, PDF_TOKR_STATE_HEXSTRING);
1099 : 4 : return PDF_TRUE;
1100 : : }
1101 : :
1102 : : case '>':
1103 : : {
1104 : : enter_state (reader, PDF_TOKR_STATE_DICTEND);
1105 : 1 : return PDF_TRUE;
1106 : : }
1107 : : case '[':
1108 : : case ']':
1109 : : case '{':
1110 : : case '}':
1111 : : {
1112 : : /* exit_state may have emitted a token, so we can't emit another
1113 : : * one now; we'll do it when exiting the PENDING state */
1114 : : enter_state (reader, PDF_TOKR_STATE_PENDING);
1115 : 431 : reader->charparam = ch;
1116 : 431 : return PDF_TRUE;
1117 : : }
1118 : : default:
1119 : : {
1120 : : /* not reached (all delimiter chars should be handled) */
1121 : 0 : PDF_ASSERT_TRACE_NOT_REACHED ();
1122 : 0 : return PDF_FALSE;
1123 : : }
1124 : : }
1125 : : }
1126 : :
1127 : : /* ch is a regular character */
1128 : :
1129 [ + + + + : 3505 : switch (reader->state)
- ]
1130 : : {
1131 : : case PDF_TOKR_STATE_PENDING:
1132 : : {
1133 [ - + ]: 1 : if (!exit_state (reader, flags, eof, token, error))
1134 : 0 : return PDF_FALSE;
1135 : :
1136 : 1 : *again = PDF_TRUE;
1137 : 1 : return PDF_TRUE;
1138 : : }
1139 : :
1140 : : case PDF_TOKR_STATE_NONE:
1141 : : enter_state (reader, PDF_TOKR_STATE_KEYWORD);
1142 : : /* fall through */
1143 : :
1144 : : case PDF_TOKR_STATE_KEYWORD:
1145 : : /* Note: numbers are treated as keywords until flush_token is called. */
1146 : 2472 : return store_char (reader, ch, error);
1147 : :
1148 : : case PDF_TOKR_STATE_NAME:
1149 : : {
1150 [ + + ]: 1032 : if (reader->substate == 0)
1151 : : {
1152 [ + + ]: 518 : if ((ch < 0x21) || (ch > 0x7e))
1153 : : {
1154 : : /* Invalid character in a name. */
1155 : 156 : pdf_set_error (error,
1156 : : PDF_EDOMAIN_BASE_TOKENISER,
1157 : : PDF_EBADFILE,
1158 : : "cannot handle char: "
1159 : : "invalid character in a name (%d)",
1160 : : ch);
1161 : 156 : return PDF_FALSE;
1162 : : }
1163 : :
1164 [ + + ][ - + ]: 362 : if (ch != '#' ||
1165 : 257 : (flags & PDF_TOKEN_NO_NAME_ESCAPES))
1166 : 105 : return store_char (reader, ch, error);
1167 : :
1168 : 257 : reader->substate = 1;
1169 : 257 : return PDF_TRUE;
1170 : : }
1171 : :
1172 [ + + ][ - + ]: 514 : if ((ch = HEXVAL (ch)) == 255)
[ + - ]
1173 : : {
1174 : : pdf_set_error (error,
1175 : : PDF_EDOMAIN_BASE_TOKENISER,
1176 : : PDF_EBADFILE,
1177 : : "cannot handle char: "
1178 : : "not an hex character '%c' (%d)",
1179 : : ch, ch);
1180 : : return PDF_FALSE;
1181 : : }
1182 : :
1183 [ + + ]: 514 : if (reader->substate == 1) /* the first hex digit of an escape */
1184 : : {
1185 : 257 : reader->substate = 2;
1186 : 257 : reader->charparam = ch;
1187 : 257 : return PDF_TRUE;
1188 : : }
1189 : :
1190 : 257 : ch = (reader->charparam << 4) | ch;
1191 [ - + ]: 257 : if (ch == 0) /* the PDF spec forbids "#00" */
1192 : : {
1193 : 0 : pdf_set_error (error,
1194 : : PDF_EDOMAIN_BASE_TOKENISER,
1195 : : PDF_EBADFILE,
1196 : : "cannot handle char: "
1197 : : "'#00' is forbidden");
1198 : 0 : return PDF_FALSE;
1199 : : }
1200 : :
1201 [ - + ]: 257 : if (!store_char (reader, ch, error))
1202 : 0 : return PDF_FALSE;
1203 : :
1204 : 257 : reader->substate = 0;
1205 : 257 : return PDF_TRUE;
1206 : : }
1207 : :
1208 : : default:
1209 : : {
1210 : 0 : pdf_set_error (error,
1211 : : PDF_EDOMAIN_BASE_TOKENISER,
1212 : : PDF_ERROR,
1213 : : "cannot handle char: "
1214 : : "invalid state (%d)",
1215 : : reader->state);
1216 : 49258 : return PDF_FALSE;
1217 : : }
1218 : : }
1219 : : }
1220 : :
1221 : : pdf_token_t *
1222 : 1962 : pdf_token_reader_read (pdf_token_reader_t *reader,
1223 : : pdf_u32_t flags,
1224 : : pdf_error_t **error)
1225 : : {
1226 : 1962 : pdf_token_t *new_token = NULL;
1227 : : pdf_bool_t eof;
1228 : : pdf_uchar_t ch;
1229 : :
1230 [ - + ]: 1962 : PDF_ASSERT_POINTER_RETURN_VAL (reader, NULL);
1231 [ - + ]: 1962 : PDF_ASSERT_POINTER_RETURN_VAL (reader->stream, NULL);
1232 : :
1233 [ + + ]: 49616 : while (pdf_stm_peek_char (reader->stream, &ch, error))
1234 : : {
1235 : 49258 : pdf_bool_t again = PDF_FALSE;
1236 : :
1237 : 49258 : eof = PDF_FALSE;
1238 [ + + ]: 49258 : if (!handle_char (reader,
1239 : : flags,
1240 : 49258 : (pdf_char_t)ch,
1241 : : &again,
1242 : : &eof,
1243 : : &new_token,
1244 : : error))
1245 : 156 : return NULL;
1246 : :
1247 : : /* On EOF, return NULL without error */
1248 [ + + ]: 49102 : if (eof)
1249 : 1 : return NULL;
1250 : :
1251 [ + + ]: 49101 : if (!again)
1252 : : {
1253 : : /* The character we peeked at was accepted, so get rid of it. */
1254 [ - + ]: 47663 : if (!pdf_stm_read_char (reader->stream, &ch, error))
1255 : : {
1256 [ # # ]: 0 : if (new_token)
1257 : 0 : pdf_token_destroy (new_token);
1258 : 0 : return PDF_FALSE;
1259 : : }
1260 : : }
1261 : :
1262 [ + + ]: 49101 : if (new_token)
1263 : : {
1264 : : /* Don't return an error code if we got a valid token.
1265 : : * We'll probably see the same error on the next call since we
1266 : : * didn't call read_char. */
1267 : 1447 : return new_token;
1268 : : }
1269 : :
1270 : : /* Keep on loop. Note that if again == TRUE,
1271 : : * we peek again the same char */
1272 : : }
1273 : :
1274 : 358 : eof = PDF_FALSE;
1275 [ - + ]: 358 : if (!exit_state (reader, flags, &eof, &new_token, error))
1276 : 0 : return NULL;
1277 : :
1278 : : /* On EOF, return NULL without error */
1279 [ + + ]: 358 : if (eof)
1280 : 176 : return NULL;
1281 : :
1282 : 182 : reader->state = PDF_TOKR_STATE_EOF;
1283 : :
1284 [ + + ]: 182 : if (new_token)
1285 : 179 : return new_token;
1286 : :
1287 : : /* Mark EOF */
1288 : 1962 : return NULL;
1289 : : }
1290 : :
1291 : : pdf_size_t
1292 : 1422 : pdf_token_reader_begin_pos (pdf_token_reader_t *reader)
1293 : : {
1294 [ - + ]: 1422 : PDF_ASSERT_POINTER_RETURN_VAL (reader, (pdf_size_t)0);
1295 : :
1296 : 1422 : return reader->beg_pos;
1297 : : }
1298 : :
1299 : : /* End of pdf-token-reader.c */
|