LCOV - code coverage report
Current view: top level - src/base - pdf-token-reader.c (source / functions) Hit Total Coverage
Test: libgnupdf.info Lines: 330 418 78.9 %
Date: 2011-12-09 Functions: 12 12 100.0 %
Branches: 235 320 73.4 %

           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 */

Generated by: LCOV version 1.8