LCOV - code coverage report
Current view: top level - protobuf-c-text - parse.re (source / functions) Hit Total Coverage
Test: PB-C-TEXT Code Coverage Lines: 460 468 98.3 %
Date: 2014-09-08 Functions: 18 18 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* c-basic-offset: 2; tab-width: 8; indent-tabs-mode: nil; mode: c
       2             :  * vi: set shiftwidth=2 tabstop=8 expandtab filetype=c:
       3             :  * :indentSize=2:tabSize=8:noTabs=true:mode=c:
       4             :  */
       5             : 
       6             : /** \file
       7             :  * Routines to parse text format protobufs.
       8             :  *
       9             :  * This file contains the internal support functions as well as the
      10             :  * exported functions which are used to parse text format protobufs
      11             :  * into C protobuf data types.
      12             :  *
      13             :  * Note that this file must first be pre-processed with \c re2c.  The
      14             :  * build system does this for you, but the manual step is as follows:
      15             :  *
      16             :  * \code
      17             :  * re2c -s -o protobuf-c-text/parse.c protobuf-c-text/parse.re
      18             :  * \endcode
      19             :  *
      20             :  * \author Kevin Lyda <kevin@ie.suberic.net>
      21             :  * \date   March 2014
      22             :  */
      23             : 
      24             : #include <errno.h>
      25             : #include <stdarg.h>
      26             : #include <stdbool.h>
      27             : #include <stdint.h>
      28             : #include <stdio.h>
      29             : #include <stdlib.h>
      30             : #include <string.h>
      31             : #include <unistd.h>
      32             : #include <protobuf-c/protobuf-c.h>
      33             : #include "protobuf-c-text.h"
      34             : #include "protobuf-c-util.h"
      35             : #include "config.h"
      36             : 
      37             : /** \defgroup utility Utility functions
      38             :  * \ingroup internal
      39             :  * @{
      40             :  */
      41             : 
      42             : /** A realloc implementation using ProtobufCAllocator functions.
      43             :  *
      44             :  * Similar to \c realloc, but using ProtobufCAllocator functions to do the
      45             :  * memory manipulations.
      46             :  *
      47             :  * \param[in,out] ptr Memory to realloc.
      48             :  * \param[in] old_size The size of ptr before realloc.
      49             :  * \param[in] size The desired size of ptr after realloc.
      50             :  * \param[in] allocator The functions to use to achieve this.
      51             :  * \return \c NULL on realloc failure - note \c ptr isn't freed in this
      52             :  *         case.  The new, \c size sized pointer (and \c ptr is freed in
      53             :  *         this case).
      54             :  */
      55             : static void *
      56        2319 : local_realloc(void *ptr,
      57             :     size_t old_size,
      58             :     size_t size,
      59             :     ProtobufCAllocator *allocator)
      60             : {
      61             :   void *tmp;
      62             : 
      63        2319 :   tmp = PBC_ALLOC(size);
      64        2319 :   if (!tmp) {
      65          36 :     return NULL;
      66             :   }
      67        2283 :   if (old_size < size) {
      68             :     /* Extending. */
      69        2283 :     memcpy(tmp, ptr, old_size);
      70             :   } else {
      71             :     /* Truncating. */
      72           0 :     memcpy(tmp, ptr, size);
      73             :   }
      74             : 
      75        2283 :   PBC_FREE(ptr);
      76        2283 :   return tmp;
      77             : }
      78             : 
      79             : /** @} */  /* End of utility group. */
      80             : 
      81             : /** \defgroup lexer Routines related to lexing text format protobufs
      82             :  * \ingroup internal
      83             :  * @{
      84             :  */
      85             : 
      86             : /** Token types.
      87             :  *
      88             :  * Types of tokens found by scan(). These will be in the \c id
      89             :  * field of \c Token .
      90             :  */
      91             : typedef enum {
      92             :   TOK_EOF,        /**< End of file. */
      93             :   TOK_BAREWORD,   /**< A bare, unquoted single word. */
      94             :   TOK_OBRACE,     /**< An opening brace. */
      95             :   TOK_CBRACE,     /**< A closing brace. */
      96             :   TOK_COLON,      /**< A colon. */
      97             :   TOK_QUOTED,     /**< A quoted string. */
      98             :   TOK_NUMBER,     /**< A number. */
      99             :   TOK_BOOLEAN,    /**< The unquoted form of "true" and "false". */
     100             :   TOK_MALLOC_ERR  /**< A memory allocation error occurred. */
     101             : } TokenId;
     102             : 
     103             : /** A token and its value.
     104             :  *
     105             :  * A \c Token found by scan().  It contains the \c TokenId and the
     106             :  * value of the token found (if a value is relevant).
     107             :  */
     108             : typedef struct _Token {
     109             :   TokenId id;        /**< The kind of token. */
     110             :   union {
     111             :     char *number;    /**< \b TOK_NUMBER: string with the number. */
     112             :     char *bareword;  /**< \b TOK_BAREWORD: string with bareword in it. */
     113             :     ProtobufCBinaryData *qs; /**< \b TOK_QUOTED: Unescaped quoted string
     114             :                                with the quotes removed. */
     115             :     bool boolean;    /**< \b TOK_BOOLEAN: \c true or \c false . */
     116             :   };
     117             : } Token;
     118             : 
     119             : /** Converts a Token to a string based on its type.
     120             :  *
     121             :  * Provides a string summary of the type of token; used for error messages.
     122             :  *
     123             :  * \param[in] t The token.
     124             :  * \return A string representation of the token. This is a char * on the stack.
     125             :  *         Do not modify or free it.
     126             :  */
     127             : static const char *
     128           9 : token2txt(Token *t)
     129             : {
     130           9 :   switch (t->id) {
     131             :     case TOK_EOF:
     132           1 :       return "[EOF]"; break;
     133             :     case TOK_BAREWORD:
     134           1 :       return t->bareword; break;
     135             :     case TOK_OBRACE:
     136           1 :       return "{"; break;
     137             :     case TOK_CBRACE:
     138           1 :       return "}"; break;
     139             :     case TOK_COLON:
     140           1 :       return ":"; break;
     141             :     case TOK_QUOTED:
     142           1 :       return "[string]"; break;
     143             :     case TOK_NUMBER:
     144           1 :       return t->number; break;
     145             :     case TOK_BOOLEAN:
     146           2 :       return t->boolean? "true": "false"; break;
     147             :     default:
     148           0 :       return "[UNKNOWN]"; break;
     149             :   }
     150             : }
     151             : 
     152             : /** Frees memory allocated in a \c Token instance.
     153             :  *
     154             :  * Frees memory allocated in a \c Token instance, but not
     155             :  * the \c Token itself.
     156             :  *
     157             :  * \param[in] t The token.
     158             :  * \param[in] allocator The memory allocator functions.
     159             :  */
     160             : static void
     161       21925 : token_free(Token *t, ProtobufCAllocator *allocator)
     162             : {
     163       21925 :   switch (t->id) {
     164             :     case TOK_BAREWORD:
     165        7619 :       PBC_FREE(t->bareword);
     166        7619 :       break;
     167             :     case TOK_QUOTED:
     168        1364 :       PBC_FREE(t->qs->data);
     169        1364 :       break;
     170             :     case TOK_NUMBER:
     171        4714 :       PBC_FREE(t->number);
     172        4714 :       break;
     173             :     default:
     174        8228 :       break;
     175             :   }
     176       21925 : }
     177             : 
     178             : /** Maintains state for successive calls to scan() .
     179             :  *
     180             :  * This structure is used by the scanner to maintain state.
     181             :  */
     182             : typedef struct _Scanner {
     183             :   unsigned char *cursor; /**< Where we are in the \c buffer. */
     184             :   unsigned char *marker; /**< Used for backtracking. */
     185             :   unsigned char *buffer; /**< The buffer holding the data being parsed. */
     186             :   unsigned char *limit;  /**< Where the buffer ends. */
     187             :   unsigned char *token;  /**< Pointer to the start of the current token. */
     188             :   FILE *f;  /**< For file scanners, this is the input source.  Data read
     189             :               from it is put in \c buffer. */
     190             :   int line; /**< Current line number being parsed. Used for error
     191             :               reporting. */
     192             : } Scanner;
     193             : 
     194             : /** Initialise a \c Scanner from a \c FILE
     195             :  *
     196             :  * The resulting \c Scanner will load input from a \c FILE.
     197             :  *
     198             :  * \param[in,out] scanner The state struct for the scanner.
     199             :  * \param[in] f \c FILE to read input from.
     200             :  */
     201             : static void
     202         315 : scanner_init_file(Scanner *scanner, FILE *f)
     203             : {
     204         315 :   memset(scanner, 0, sizeof(Scanner));
     205         315 :   scanner->f = f;
     206         315 :   scanner->line = 1;
     207         315 : }
     208             : 
     209             : /** Initialise a \c Scanner from a string.
     210             :  *
     211             :  * The resulting \c Scanner will load input from a string.
     212             :  *
     213             :  * \param[in,out] scanner The state struct for the scanner.
     214             :  * \param[in] buf String to get input from.
     215             :  */
     216             : static void
     217           3 : scanner_init_string(Scanner *scanner, char *buf)
     218             : {
     219           3 :   memset(scanner, 0, sizeof(Scanner));
     220           3 :   scanner->buffer = buf;
     221           3 :   scanner->marker = buf;
     222           3 :   scanner->cursor = buf;
     223           3 :   scanner->limit = &buf[strlen(buf)];
     224           3 :   scanner->line = 1;
     225           3 : }
     226             : 
     227             : /** Free data internal to the \c Scanner instance.
     228             :  *
     229             :  * \param[in,out] scanner The state struct for the scanner.
     230             :  * \param[in] allocator Allocator functions.
     231             :  */
     232             : static void
     233         310 : scanner_free(Scanner *scanner, ProtobufCAllocator *allocator)
     234             : {
     235         310 :   if (scanner->f && scanner->buffer)
     236         305 :     PBC_FREE(scanner->buffer);
     237         310 :   scanner->buffer = NULL;
     238         310 : }
     239             : 
     240             : /** Unescape string.
     241             :  *
     242             :  * Remove escape sequences from a string and replace them with the
     243             :  * actual characters.
     244             :  *
     245             :  * \param[in] src String to unescape.
     246             :  * \param[in] len Length of string to unescape.
     247             :  * \param[in] allocator Allocator functions.
     248             :  * \return A ProtobufCBinaryData pointer with the unescaped data.
     249             :  *         Note this must be freed with the ProtobufCAllocator
     250             :  *         allocator you called this with.
     251             :  */
     252             : static ProtobufCBinaryData *
     253        1403 : unesc_str(unsigned char *src, int len, ProtobufCAllocator *allocator)
     254             : {
     255             :   ProtobufCBinaryData *dst_pbbd;
     256             :   unsigned char *dst;
     257        1403 :   int i = 0, dst_len = 0;
     258             :   unsigned char oct[4];
     259             : 
     260        1403 :   dst_pbbd = PBC_ALLOC(sizeof(ProtobufCBinaryData));
     261        1403 :   dst = PBC_ALLOC(len + 1);
     262        1403 :   if (!dst_pbbd || !dst) {
     263             :     goto unesc_str_error;
     264             :   }
     265        1365 :   oct[3] = '\0';
     266             : 
     267       16991 :   while (i < len) {
     268       14262 :     if (src[i] != '\\') {
     269       12216 :       dst[dst_len++] = src[i++];
     270             :     } else {
     271        2046 :       i++;
     272        2046 :       if (i == len) {
     273             :         /* Fell off the end of the string after \. */
     274           0 :         goto unesc_str_error;
     275             :       }
     276        2046 :       switch (src[i]) {
     277             :         case '0':
     278        1141 :           if (i + 2 < len
     279        1141 :               && (src[i+1] >= '0' && src[i+1] <= '7')
     280        1141 :               && (src[i+2] >= '0' && src[i+2] <= '7')) {
     281        1141 :             memcpy(oct, src + i, 3);
     282        1141 :             dst[dst_len++] = (unsigned char)strtoul(oct, NULL, 8);
     283        1141 :             i += 2;  /* Gets incremented again down below. */
     284             :           } else {
     285             :             /* Decoding a \0 failed or was cut off.. */
     286             :             goto unesc_str_error;
     287             :           }
     288        1141 :           break;
     289             :         case '\'':
     290         141 :           dst[dst_len++] = '\'';
     291         141 :           break;
     292             :         case '\"':
     293         141 :           dst[dst_len++] = '\"';
     294         141 :           break;
     295             :         case '\\':
     296         149 :           dst[dst_len++] = '\\';
     297         149 :           break;
     298             :         case 'n':
     299         239 :           dst[dst_len++] = '\n';
     300         239 :           break;
     301             :         case 'r':
     302          85 :           dst[dst_len++] = '\r';
     303          85 :           break;
     304             :         case 't':
     305         149 :           dst[dst_len++] = '\t';
     306         149 :           break;
     307             :         default:
     308           1 :           goto unesc_str_error;
     309             :           break;
     310             :       }
     311        2045 :       i++;
     312             :     }
     313             :   }
     314             : 
     315        1364 :   dst_pbbd->data = dst;
     316        1364 :   dst_pbbd->len = dst_len;
     317        1364 :   return dst_pbbd;
     318             : 
     319             : unesc_str_error:
     320          39 :   PBC_FREE(dst);
     321          39 :   PBC_FREE(dst_pbbd);
     322          39 :   return NULL;
     323             : }
     324             : 
     325             : /** Amount of data to read from a file each time. */
     326             : #define CHUNK 4096
     327             : 
     328             : /** Function to request more data from input source in \c Scanner.
     329             :  *
     330             :  * In the case of a string being the input source for \c Scanner,
     331             :  * nothing happens. For a \c FILE backed \c Scanner, a \c CHUNK's
     332             :  * worth of data is read from the \c FILE.
     333             :  *
     334             :  * \param[in,out] scanner The state struct for the scanner.
     335             :  * \param[in] allocator Allocator functions.
     336             :  * \return Returns the success of the function:
     337             :  *         - -1: Memory allocation failure.
     338             :  *         - 0: No more input added.
     339             :  *         - >0: Input added.
     340             :  */
     341             : static int
     342         359 : fill(Scanner *scanner, ProtobufCAllocator *allocator)
     343             : {
     344             :   char *buf;
     345             :   int len, oldlen, nmemb;
     346             : 
     347         359 :   if (scanner->token > scanner->limit) {
     348             :     /* this shouldn't happen */
     349           0 :     return 0;
     350             :   }
     351         359 :   if (scanner->f && !feof(scanner->f)) {
     352         307 :     oldlen = scanner->limit - scanner->token;
     353         307 :     len = CHUNK + oldlen;
     354         307 :     buf = PBC_ALLOC(len);
     355         307 :     if (!buf) {
     356           2 :       return -1;
     357             :     }
     358         305 :     memcpy(buf, scanner->token, oldlen);
     359         305 :     nmemb = fread(buf + oldlen, 1, CHUNK, scanner->f);
     360         305 :     if (nmemb != CHUNK) {
     361             :       /* Short read.  eof.  Append nul. */
     362         305 :       len = oldlen + nmemb;
     363         305 :       buf[len] = '\0';
     364             :     }
     365             :     /* Reset the world to use buf. */
     366         305 :     scanner->cursor = &buf[scanner->cursor - scanner->token];
     367         305 :     scanner->limit = buf + len;
     368         305 :     scanner->token = buf;
     369         305 :     PBC_FREE(scanner->buffer);
     370         305 :     scanner->buffer = buf;
     371         305 :     scanner->marker = buf;
     372             :   }
     373             : 
     374         357 :   return scanner->limit >= scanner->cursor? 1: 0;
     375             : }
     376             : 
     377             : /** Return the token. */
     378             : #define RETURN(tt) { t.id = tt; return t; }
     379             : /** Retrieves more input if available. */
     380             : #define YYFILL(n) { fill_result = fill(scanner, allocator); \
     381             :                     if (fill_result <= 0) \
     382             :                       RETURN((fill_result == -1? TOK_MALLOC_ERR: TOK_EOF)); }
     383             : 
     384             : /** Generated lexer.
     385             :  *
     386             :  * The guts of the parser generated by \c re2c.
     387             :  *
     388             :  * \param[in,out] scanner The state struct for the scanner.
     389             :  * \param[in] allocator Allocator functions.
     390             :  * \return Returns the next \c Token it finds.
     391             :  */
     392             : static Token
     393       45444 : scan(Scanner *scanner, ProtobufCAllocator *allocator)
     394             : {
     395             :   Token t;
     396             :   int fill_result;
     397             : 
     398             : token_start:
     399       45444 :   scanner->token = scanner->cursor;
     400             : 
     401             :   /* I don't think multiline strings are allowed.  If I'm wrong,
     402             :    * the QS re should be ["] (EQ|[^"]|NL)* ["]; */
     403             : 
     404             :   /*!re2c
     405             :   re2c:define:YYCTYPE   = "unsigned char";
     406             :   re2c:define:YYCURSOR  = scanner->cursor;
     407             :   re2c:define:YYLIMIT   = scanner->limit;
     408             :   re2c:define:YYMARKER  = scanner->marker;
     409             : 
     410             :   I = [-]? [0-9]+;
     411             :   F = [-]? [0-9]* "." [0-9]+;
     412             :   BW = [a-zA-Z0-9_]+;
     413             :   EQ = [\\] ["];
     414             :   NL = "\n";
     415             :   QS = ["] (EQ|[^"])* ["];
     416             :   WS = [ \t];
     417             : 
     418             :   I | F       {
     419        4773 :                 t.number = PBC_ALLOC((scanner->cursor - scanner->token) + 1);
     420        4773 :                 if (!t.number) {
     421          59 :                   RETURN(TOK_MALLOC_ERR);
     422             :                 }
     423        4714 :                 memcpy(t.number, scanner->token,
     424        4714 :                        scanner->cursor - scanner->token);
     425        4714 :                 t.number[scanner->cursor - scanner->token] = '\0';
     426        4714 :                 RETURN(TOK_NUMBER);
     427             :               }
     428          65 :   "true"      { t.boolean=true; RETURN(TOK_BOOLEAN); }
     429         236 :   "false"     { t.boolean=false; RETURN(TOK_BOOLEAN); }
     430             :   BW          {
     431        7724 :                 t.bareword = PBC_ALLOC((scanner->cursor - scanner->token) + 1);
     432        7724 :                 if (!t.bareword) {
     433         105 :                   RETURN(TOK_MALLOC_ERR);
     434             :                 }
     435        7619 :                 memcpy(t.bareword, scanner->token,
     436        7619 :                        scanner->cursor - scanner->token);
     437        7619 :                 t.bareword[scanner->cursor - scanner->token] = '\0';
     438        7619 :                 RETURN(TOK_BAREWORD);
     439             :               }
     440             :   QS          {
     441        1403 :                 t.qs = unesc_str(scanner->token + 1,
     442        1403 :                                  scanner->cursor - scanner->token - 2,
     443             :                                  allocator);
     444        1403 :                 if (!t.qs) {
     445          39 :                   RETURN(TOK_MALLOC_ERR);
     446             :                 }
     447        1364 :                 RETURN(TOK_QUOTED);
     448             :               }
     449         570 :   "{"         { RETURN(TOK_OBRACE); }
     450         376 :   "}"         { RETURN(TOK_CBRACE); }
     451        6766 :   ":"         { RETURN(TOK_COLON); }
     452       16018 :   WS          { goto token_start; }
     453        7501 :   NL          { scanner->line++; goto token_start; }
     454           9 :   "\000"      { RETURN(TOK_EOF); }
     455             :   */
     456             : }
     457             : 
     458             : /** @} */  /* End of lexer group. */
     459             : 
     460             : /** \defgroup state Routines that define a simple finite state machine
     461             :  * \ingroup internal
     462             :  * @{
     463             :  */
     464             : 
     465             : /** StateId enumeration.
     466             :  *
     467             :  * A list of states for the FSM.
     468             :  */
     469             : typedef enum {
     470             :   STATE_OPEN,       /**< Ready to start a new statement or close
     471             :                       a nested message. */
     472             :   STATE_ASSIGNMENT, /**< Ready to assign a scalar or a nested message. */
     473             :   STATE_VALUE,      /**< Assign the scalar. */
     474             :   STATE_DONE        /**< Nothing more to read or there's been an error. */
     475             : } StateId;
     476             : 
     477             : /** Max size of an error message. */
     478             : #define STATE_ERROR_STR_MAX 160
     479             : 
     480             : /** Maintain state for the FSM.
     481             :  *
     482             :  * Tracks the current state of the FSM.
     483             :  */
     484             : typedef struct {
     485             :   Scanner *scanner;         /**< Tracks state for the scanner. */
     486             :   const ProtobufCFieldDescriptor *field;  /**< After finding a
     487             :                           \b TOK_BAREWORD in a \b STATE_OPEN \c field
     488             :                           is set to the field in the message that
     489             :                           matches that bareword. */
     490             :   int current_msg;          /**< Index on the message stack of the
     491             :                               current message. */
     492             :   int max_msg;              /**< Size of the message stack. */
     493             :   ProtobufCMessage **msgs;  /**< The message stack.  As nested messages
     494             :                               are found, they're put here. */
     495             :   ProtobufCAllocator *allocator;  /**< allocator functions. */
     496             :   int error;                /**< Notes an error has occurred. */
     497             :   char *error_str;          /**< Text of error. */
     498             : } State;
     499             : 
     500             : /** Initialise a \c State struct.
     501             :  * \param[in,out] state A state struct pointer - the is the state
     502             :  *                      for the FSM.
     503             :  * \param[in,out] scanner The state struct for the scanner.
     504             :  * \param[in] descriptor Message descriptor.
     505             :  * \param[in] allocator Allocator functions.
     506             :  * \return Success (1) or failure (0). Failure is due to out of
     507             :  *         memory errors.
     508             :  */
     509             : static int
     510         318 : state_init(State *state,
     511             :     Scanner *scanner,
     512             :     const ProtobufCMessageDescriptor *descriptor,
     513             :     ProtobufCAllocator *allocator)
     514             : {
     515             :   ProtobufCMessage *msg;
     516             : 
     517         318 :   memset(state, 0, sizeof(State));
     518         318 :   state->allocator = allocator;
     519         318 :   state->scanner = scanner;
     520         318 :   state->error_str = ST_ALLOC(STATE_ERROR_STR_MAX);
     521         318 :   state->msgs = ST_ALLOC(10 * sizeof(ProtobufCMessage *));
     522         318 :   state->max_msg = 10;
     523         318 :   msg = ST_ALLOC(descriptor->sizeof_message);
     524         318 :   if (!state->msgs || !msg || !state->error_str) {
     525           8 :     ST_FREE(state->error_str);
     526           8 :     ST_FREE(state->msgs);
     527           8 :     ST_FREE(msg);
     528           8 :     return 0;
     529             :   }
     530         310 :   descriptor->message_init(msg);
     531         310 :   state->msgs[0] = msg;
     532             : 
     533         310 :   return 1;
     534             : }
     535             : 
     536             : /** Free internal data in a \c State struct.
     537             :  *
     538             :  * Frees allocated data within the \c State instance. Note that the
     539             :  * \c State instance itself is not freed and that the \c State instance
     540             :  * contains a pointer to the \c ProtobufCAllocator allocator that
     541             :  * was passed in state_init().
     542             :  *
     543             :  * Note also that the \c error_str element is only freed if there hasn't
     544             :  * been an error.  If there has been an error, the responsibility falls
     545             :  * on the caller to free \c error_str .
     546             :  *
     547             :  * \param[in,out] state A state struct pointer.
     548             :  */
     549             : static void
     550         310 : state_free(State *state)
     551             : {
     552         310 :   if (!state->error) {
     553           7 :     ST_FREE(state->error_str);
     554             :   }
     555         310 :   ST_FREE(state->msgs);
     556         310 : }
     557             : 
     558             : /*
     559             :  * Helper function to handle errors.
     560             :  */
     561             : /** Handle an error in the FSM.
     562             :  *
     563             :  * At any point in the FSM if an error is encounters, call this function
     564             :  * with an explination of the error - and return the resulting value.
     565             :  *
     566             :  * \param[in,out] state A state struct pointer.
     567             :  * \param[in] t The \c Token to process.
     568             :  * \param[in] error_fmt printf style format string for the error message.
     569             :  * \param[in] ... Arguments for \c error_fmt .
     570             :  * \return This will always return \c STATE_DONE .
     571             :  */
     572             : static StateId state_error(State *state, Token *t, char *error_fmt, ...)
     573             :   __attribute__((format(printf, 3, 4)));
     574             : static StateId
     575         303 : state_error(State *state, Token *t, char *error_fmt, ...)
     576             : {
     577             :   va_list args;
     578             :   int error_idx;
     579             : 
     580             :   /* 10 solid lines of errors is more than enough. */
     581         303 :   state->error = 1;
     582         303 :   error_idx = snprintf(state->error_str, STATE_ERROR_STR_MAX,
     583         303 :       "Error found on line %d.\n", state->scanner->line);
     584             : 
     585         303 :   if (error_idx < STATE_ERROR_STR_MAX) {
     586         303 :     va_start(args, error_fmt);
     587         303 :     vsnprintf(state->error_str + error_idx, STATE_ERROR_STR_MAX - error_idx,
     588             :         error_fmt, args);
     589         303 :     va_end(args);
     590             :   }
     591             : 
     592         303 :   return STATE_DONE;
     593             : }
     594             : 
     595             : /** Expect an element name (bareword) or a closing brace.
     596             :  *
     597             :  * Initial state, and state after each assignment completes (or a message
     598             :  * assignment starts. If a bareword is found, go into \c STATE_ASSIGNMENT
     599             :  * and if a closing brace is found, go into \c STATE_DONE.
     600             :  *
     601             :  * If something else is found or if there are no more messages on the stack
     602             :  * (in other words, we're already at the base message), call and return
     603             :  * with state_error().
     604             :  *
     605             :  * \param[in,out] state A state struct pointer.
     606             :  * \param[in] t The \c Token to process.
     607             :  * \return A StateID value.
     608             :  */
     609             : static StateId
     610        7725 : state_open(State *state, Token *t)
     611             : {
     612        7725 :   switch (t->id) {
     613             :     case TOK_BAREWORD:
     614        7338 :       state->field = protobuf_c_message_descriptor_get_field_by_name(
     615        7338 :           state->msgs[state->current_msg]->descriptor, t->bareword);
     616        7338 :       if (state->field) {
     617        7336 :         if (state->field->label != PROTOBUF_C_LABEL_REQUIRED
     618        4761 :             && state->field->label != PROTOBUF_C_LABEL_OPTIONAL
     619        2356 :             && state->field->label != PROTOBUF_C_LABEL_REPEATED) {
     620           0 :           return state_error(state, t,
     621             :               "Internal error: unknown label type %d for '%s'.",
     622           0 :               state->field->label, state->field->name);
     623             :         }
     624        7336 :         return STATE_ASSIGNMENT;
     625             :       } else {
     626           2 :         return state_error(state, t, "Can't find field '%s' in message '%s'.",
     627             :                            t->bareword,
     628           2 :                            state->msgs[state->current_msg]->descriptor->name);
     629             :       }
     630             :       break;
     631             :     case TOK_CBRACE:
     632         375 :       if (state->current_msg > 0) {
     633         374 :         state->current_msg--;
     634             :       } else {
     635           1 :         return state_error(state, t, "Extra closing brace found.");
     636             :       }
     637         374 :       return STATE_OPEN;
     638             :       break;
     639             :     case TOK_EOF:
     640           9 :       if (state->current_msg > 0) {
     641           2 :         return state_error(state, t, "Missing '%d' closing braces.",
     642             :             state->current_msg);
     643             :       }
     644           7 :       return STATE_DONE;
     645             :       break;
     646             :     default:
     647           3 :       return state_error(state, t,
     648             :                          "Expected element name or '}'; found '%s' instead.",
     649             :                          token2txt(t));
     650             :       break;
     651             :   }
     652             : }
     653             : 
     654             : /** Expect a colon or opening brace.
     655             :  *
     656             :  * The state where we expect an assignment.
     657             :  *
     658             :  * \param[in,out] state A state struct pointer.
     659             :  * \param[in] t The \c Token to process.
     660             :  * \return A StateID value.
     661             :  */
     662             : static StateId
     663        7336 : state_assignment(State *state, Token *t)
     664             : {
     665             :   ProtobufCMessage *msg;
     666             : 
     667        7336 :   msg = state->msgs[state->current_msg];
     668        7336 :   switch (t->id) {
     669             :     case TOK_COLON:
     670        6765 :       if (state->field->type == PROTOBUF_C_TYPE_MESSAGE) {
     671           1 :         return state_error(state, t,
     672             :             "Expected a '{', got a ':' - '%s' is a message type field.",
     673           1 :             state->field->name);
     674             :       }
     675        6764 :       return STATE_VALUE;
     676             :       break;
     677             :     case TOK_OBRACE:
     678         569 :       if (state->field->type == PROTOBUF_C_TYPE_MESSAGE) {
     679             :         ProtobufCMessage **tmp;
     680             :         size_t n_members;
     681             : 
     682             :         /* Don't assign over an existing message. */
     683         568 :         if (state->field->label == PROTOBUF_C_LABEL_OPTIONAL
     684         546 :             || state->field->label == PROTOBUF_C_LABEL_REQUIRED) {
     685             :           /* Do optional member accounting. */
     686          40 :           if (STRUCT_MEMBER(protobuf_c_boolean, msg,
     687             :                 state->field->offset)) {
     688           1 :             return state_error(state, t,
     689             :                 "The '%s' message has already been assigned.",
     690           1 :                 state->field->name);
     691             :           }
     692             :         }
     693             : 
     694             :         /* Allocate space for the repeated message list. */
     695         567 :         if (state->field->label == PROTOBUF_C_LABEL_REPEATED) {
     696         528 :           STRUCT_MEMBER(size_t, msg, state->field->quantifier_offset) += 1;
     697         528 :           n_members = STRUCT_MEMBER(size_t, msg,
     698             :                                     state->field->quantifier_offset);
     699        1056 :           tmp = local_realloc(
     700         528 :               STRUCT_MEMBER(ProtobufCMessage *, msg, state->field->offset),
     701             :               (n_members - 1) * sizeof(ProtobufCMessage *),
     702             :               n_members * sizeof(ProtobufCMessage *),
     703             :               state->allocator);
     704         528 :           if (!tmp) {
     705           6 :             return state_error(state, t, "Malloc failure.");
     706             :           }
     707         522 :           STRUCT_MEMBER(ProtobufCMessage **, msg, state->field->offset)
     708         522 :             = tmp;
     709         522 :           tmp[n_members - 1] = NULL;
     710             :         }
     711             : 
     712             :         /* Create and push a new message on the message stack. */
     713         561 :         state->current_msg++;
     714         561 :         if (state->current_msg == state->max_msg) {
     715             :           ProtobufCMessage **tmp_msgs;
     716             : 
     717           1 :           state->max_msg += 10;
     718           4 :           tmp_msgs = local_realloc(
     719           2 :               state->msgs, (state->current_msg) * sizeof(ProtobufCMessage *),
     720           1 :               (state->max_msg) * sizeof(ProtobufCMessage *),
     721             :               state->allocator);
     722           1 :           if (!tmp_msgs) {
     723           0 :             return state_error(state, t, "Malloc failure.");
     724             :           }
     725           1 :           state->msgs = tmp_msgs;
     726             :         }
     727         561 :         state->msgs[state->current_msg]
     728         561 :           = ST_ALLOC(((ProtobufCMessageDescriptor *)
     729             :                state->field->descriptor)->sizeof_message);
     730         561 :         if (!state->msgs[state->current_msg]) {
     731           8 :           return state_error(state, t, "Malloc failure.");
     732             :         }
     733         553 :         ((ProtobufCMessageDescriptor *)state->field->descriptor)
     734         553 :           ->message_init(state->msgs[state->current_msg]);
     735             : 
     736             :         /* Assign the message just created. */
     737         553 :         if (state->field->label == PROTOBUF_C_LABEL_REPEATED) {
     738         516 :           tmp[n_members - 1] = state->msgs[state->current_msg];
     739         516 :           return STATE_OPEN;
     740             :         } else {
     741          37 :           STRUCT_MEMBER(ProtobufCMessage *, msg, state->field->offset)
     742          37 :             = state->msgs[state->current_msg];
     743          37 :           return STATE_OPEN;
     744             :         }
     745             : 
     746             :       } else {
     747           1 :         return state_error(state, t,
     748           1 :             "'%s' is not a message field.", state->field->name);
     749             :       }
     750             :       break;
     751             :     default:
     752           2 :       return state_error(state, t,
     753             :                          "Expected ':' or '{'; found '%s' instead.",
     754             :                          token2txt(t));
     755             :       break;
     756             :   }
     757             : }
     758             : 
     759             : /** Expect a quoted string, enum (bareword) or boolean.
     760             :  *
     761             :  * Assign the value in \c Token to the field we identified in the
     762             :  * state_open() call.  This function is huge in order to handle the
     763             :  * variety of data types and the struct offset math required to manipulate
     764             :  * them.
     765             :  *
     766             :  * \param[in,out] state A state struct pointer.
     767             :  * \param[in] t The \c Token to process.
     768             :  * \return A StateID value.
     769             :  */
     770             : static StateId
     771        6659 : state_value(State *state, Token *t)
     772             : {
     773             :   ProtobufCMessage *msg;
     774             :   size_t n_members;
     775             :   char *end;
     776             :   int64_t val;
     777             : 
     778        6659 :   msg = state->msgs[state->current_msg];
     779        6659 :   if (state->field->type != PROTOBUF_C_TYPE_STRING) {
     780        5566 :     if (state->field->label == PROTOBUF_C_LABEL_OPTIONAL) {
     781             :       /* Do optional member accounting. */
     782        2094 :       if (STRUCT_MEMBER(protobuf_c_boolean, msg,
     783             :             state->field->quantifier_offset)) {
     784        6660 :         return state_error(state, t,
     785           1 :             "'%s' has already been assigned.", state->field->name);
     786             :       }
     787        2093 :       STRUCT_MEMBER(protobuf_c_boolean, msg,
     788        2093 :             state->field->quantifier_offset) = 1;
     789             :     }
     790             :   }
     791        6658 :   switch (t->id) {
     792             :     case TOK_BAREWORD:
     793         279 :       if (state->field->type == PROTOBUF_C_TYPE_ENUM) {
     794             :         ProtobufCEnumDescriptor *enumd;
     795             :         const ProtobufCEnumValue *enumv;
     796             : 
     797         278 :         enumd = (ProtobufCEnumDescriptor *)state->field->descriptor;
     798         278 :         enumv = protobuf_c_enum_descriptor_get_value_by_name(enumd,
     799         278 :             t->bareword);
     800         278 :         if (enumv) {
     801         275 :           if (state->field->label == PROTOBUF_C_LABEL_REPEATED) {
     802             :             int *tmp;
     803             : 
     804          19 :             STRUCT_MEMBER(size_t, msg, state->field->quantifier_offset) += 1;
     805          19 :             n_members = STRUCT_MEMBER(size_t, msg,
     806             :                 state->field->quantifier_offset);
     807          38 :             tmp = local_realloc(
     808          19 :                 STRUCT_MEMBER(int *, msg, state->field->offset),
     809             :                 (n_members - 1) * sizeof(int),
     810             :                 n_members * sizeof(int), state->allocator);
     811          19 :             if (!tmp) {
     812           2 :               return state_error(state, t, "Malloc failure.");
     813             :             }
     814          17 :             STRUCT_MEMBER(int *, msg, state->field->offset) = tmp;
     815          17 :             tmp[n_members - 1] = enumv->value;
     816          17 :             return STATE_OPEN;
     817             :           } else {
     818         256 :             STRUCT_MEMBER(int, msg, state->field->offset) = enumv->value;
     819         256 :             return STATE_OPEN;
     820             :           }
     821             :         } else {
     822           3 :           return state_error(state, t,
     823             :               "Invalid enum '%s' for field '%s'.",
     824           3 :               t->bareword, state->field->name);
     825             :         }
     826             :       }
     827           1 :       return state_error(state, t,
     828           1 :           "'%s' is not an enum field.", state->field->name);
     829             :       break;
     830             : 
     831             :     case TOK_BOOLEAN:
     832         299 :       if (state->field->type == PROTOBUF_C_TYPE_BOOL) {
     833         298 :         if (state->field->label == PROTOBUF_C_LABEL_REPEATED) {
     834             :           protobuf_c_boolean *tmp;
     835             : 
     836          62 :           STRUCT_MEMBER(size_t, msg, state->field->quantifier_offset) += 1;
     837          62 :           n_members = STRUCT_MEMBER(size_t, msg,
     838             :                                     state->field->quantifier_offset);
     839         124 :           tmp = local_realloc(
     840          62 :               STRUCT_MEMBER(protobuf_c_boolean *, msg, state->field->offset),
     841             :               (n_members - 1) * sizeof(protobuf_c_boolean),
     842             :               n_members * sizeof(protobuf_c_boolean), state->allocator);
     843          62 :           if (!tmp) {
     844           2 :             return state_error(state, t, "Malloc failure.");
     845             :           }
     846          60 :           STRUCT_MEMBER(protobuf_c_boolean *, msg, state->field->offset) = tmp;
     847          60 :           tmp[n_members - 1] = t->boolean;
     848          60 :           return STATE_OPEN;
     849             :         } else {
     850         236 :           STRUCT_MEMBER(protobuf_c_boolean, msg, state->field->offset)
     851         236 :             = t->boolean;
     852         236 :           return STATE_OPEN;
     853             :         }
     854             : 
     855             :       }
     856           1 :       return state_error(state, t,
     857           1 :           "'%s' is not a boolean field.", state->field->name);
     858             :       break;
     859             : 
     860             :     case TOK_QUOTED:
     861        1363 :       if (state->field->type == PROTOBUF_C_TYPE_BYTES) {
     862             :         ProtobufCBinaryData *pbbd;
     863             : 
     864         275 :         if (state->field->label == PROTOBUF_C_LABEL_REPEATED) {
     865          47 :           STRUCT_MEMBER(size_t, msg, state->field->quantifier_offset) += 1;
     866          47 :           n_members = STRUCT_MEMBER(size_t, msg,
     867             :                                     state->field->quantifier_offset);
     868          94 :           pbbd = local_realloc(
     869          47 :               STRUCT_MEMBER(ProtobufCBinaryData *, msg, state->field->offset),
     870             :               (n_members - 1) * sizeof(ProtobufCBinaryData),
     871             :               n_members * sizeof(ProtobufCBinaryData), state->allocator);
     872          47 :           if (!pbbd) {
     873           2 :             return state_error(state, t, "Malloc failure.");
     874             :           }
     875          45 :           STRUCT_MEMBER(ProtobufCBinaryData *, msg, state->field->offset)
     876          45 :             = pbbd;
     877          45 :           pbbd[n_members - 1].data = ST_ALLOC(t->qs->len);
     878          45 :           if (!pbbd[n_members - 1].data) {
     879           2 :             return state_error(state, t, "Malloc failure.");
     880             :           }
     881          43 :           memcpy(pbbd[n_members - 1].data, t->qs->data, t->qs->len);
     882          43 :           pbbd[n_members - 1].len = t->qs->len;
     883          43 :           return STATE_OPEN;
     884             :         } else {
     885         228 :           pbbd = STRUCT_MEMBER_PTR(ProtobufCBinaryData, msg,
     886             :               state->field->offset);
     887         228 :           pbbd->data = ST_ALLOC(t->qs->len);
     888         228 :           if (!pbbd->data) {
     889           4 :             return state_error(state, t, "Malloc failure.");
     890             :           }
     891         224 :           memcpy(pbbd->data, t->qs->data, t->qs->len);
     892         224 :           pbbd->len = t->qs->len;
     893         224 :           return STATE_OPEN;
     894             :         }
     895             : 
     896        1088 :       } else if (state->field->type == PROTOBUF_C_TYPE_STRING) {
     897        1087 :         if (state->field->label == PROTOBUF_C_LABEL_OPTIONAL) {
     898             :           /* Do optional member accounting. */
     899         247 :           if (STRUCT_MEMBER(unsigned char *, msg, state->field->offset)
     900           2 :               && (STRUCT_MEMBER(unsigned char *, msg, state->field->offset)
     901           1 :                 != state->field->default_value)) {
     902           1 :             return state_error(state, t,
     903           1 :                 "'%s' has already been assigned.", state->field->name);
     904             :           }
     905             :         }
     906        1086 :         if (state->field->label == PROTOBUF_C_LABEL_REPEATED) {
     907             :           unsigned char **s;
     908             : 
     909         213 :           STRUCT_MEMBER(size_t, msg, state->field->quantifier_offset) += 1;
     910         213 :           n_members = STRUCT_MEMBER(size_t, msg,
     911             :                                     state->field->quantifier_offset);
     912         426 :           s = local_realloc(
     913         213 :               STRUCT_MEMBER(unsigned char **, msg, state->field->offset),
     914             :               (n_members - 1) * sizeof(unsigned char *),
     915             :               n_members * sizeof(unsigned char *), state->allocator);
     916         213 :           if (!s) {
     917           2 :             return state_error(state, t, "Malloc failure.");
     918             :           }
     919         211 :           STRUCT_MEMBER(unsigned char **, msg, state->field->offset) = s;
     920         211 :           s[n_members - 1] = ST_ALLOC(t->qs->len + 1);
     921         211 :           if (!s[n_members - 1]) {
     922           2 :             return state_error(state, t, "Malloc failure.");
     923             :           }
     924         209 :           memcpy(s[n_members - 1], t->qs->data, t->qs->len);
     925         209 :           s[n_members - 1][t->qs->len] = '\0';
     926         209 :           return STATE_OPEN;
     927             :         } else {
     928             :           unsigned char *s;
     929             : 
     930         873 :           s = ST_ALLOC(t->qs->len + 1);
     931         873 :           if (!s) {
     932          11 :             return state_error(state, t, "Malloc failure.");
     933             :           }
     934         862 :           memcpy(s, t->qs->data, t->qs->len);
     935         862 :           s[t->qs->len] = '\0';
     936         862 :           STRUCT_MEMBER(unsigned char *, msg, state->field->offset) = s;
     937         862 :           return STATE_OPEN;
     938             :         }
     939             : 
     940             :       }
     941           1 :       return state_error(state, t,
     942           1 :           "'%s' is not a string or byte field.", state->field->name);
     943             :       break;
     944             : 
     945             :     case TOK_NUMBER:
     946        4713 :       switch (state->field->type) {
     947             :         case PROTOBUF_C_TYPE_INT32:
     948             :         case PROTOBUF_C_TYPE_UINT32:
     949             :         case PROTOBUF_C_TYPE_FIXED32:
     950        1057 :           val = strtoul(t->number, &end, 10);
     951        1057 :           if (*end != '\0' || val > (uint64_t)UINT32_MAX) {
     952           4 :             return state_error(state, t,
     953             :                 "Unable to convert '%s' for field '%s'.",
     954           4 :                 t->number, state->field->name);
     955             :           }
     956        1053 :           if (state->field->label == PROTOBUF_C_LABEL_REPEATED) {
     957             :             uint32_t *vals;
     958             : 
     959         262 :             STRUCT_MEMBER(size_t, msg, state->field->quantifier_offset) += 1;
     960         262 :             n_members = STRUCT_MEMBER(size_t, msg,
     961             :                                       state->field->quantifier_offset);
     962         524 :             vals = local_realloc(
     963         262 :                 STRUCT_MEMBER(uint32_t *, msg, state->field->offset),
     964             :                 (n_members - 1) * sizeof(uint32_t),
     965             :                 n_members * sizeof(uint32_t), state->allocator);
     966         262 :             if (!vals) {
     967           4 :               return state_error(state, t, "Malloc failure.");
     968             :             }
     969         258 :             STRUCT_MEMBER(uint32_t *, msg, state->field->offset) = vals;
     970         258 :             vals[n_members - 1] = (uint32_t)val;
     971         258 :             return STATE_OPEN;
     972             :           } else {
     973         791 :             STRUCT_MEMBER(uint32_t, msg, state->field->offset) = (uint32_t)val;
     974         791 :             return STATE_OPEN;
     975             :           }
     976             :           break;
     977             : 
     978             :         case PROTOBUF_C_TYPE_SINT32:
     979             :         case PROTOBUF_C_TYPE_SFIXED32:
     980         732 :           val = strtol(t->number, &end, 10);
     981         732 :           if (*end != '\0' || val < INT32_MIN || val > INT32_MAX) {
     982           1 :             return state_error(state, t,
     983             :                 "Unable to convert '%s' for field '%s'.",
     984           1 :                 t->number, state->field->name);
     985             :           }
     986         731 :           if (state->field->label == PROTOBUF_C_LABEL_REPEATED) {
     987             :             int32_t *vals;
     988             : 
     989         214 :             STRUCT_MEMBER(size_t, msg, state->field->quantifier_offset) += 1;
     990         214 :             n_members = STRUCT_MEMBER(size_t, msg,
     991             :                                       state->field->quantifier_offset);
     992         428 :             vals = local_realloc(
     993         214 :                 STRUCT_MEMBER(int32_t *, msg, state->field->offset),
     994             :                 (n_members - 1) * sizeof(int32_t),
     995             :                 n_members * sizeof(int32_t), state->allocator);
     996         214 :             if (!vals) {
     997           4 :               return state_error(state, t, "Malloc failure.");
     998             :             }
     999         210 :             STRUCT_MEMBER(int32_t *, msg, state->field->offset) = vals;
    1000         210 :             vals[n_members - 1] = (uint32_t)val;
    1001         210 :             return STATE_OPEN;
    1002             :           } else {
    1003         517 :             STRUCT_MEMBER(int32_t, msg, state->field->offset) = val;
    1004         517 :             return STATE_OPEN;
    1005             :           }
    1006             :           break;
    1007             : 
    1008             :         case PROTOBUF_C_TYPE_INT64:
    1009             :         case PROTOBUF_C_TYPE_UINT64:
    1010             :         case PROTOBUF_C_TYPE_FIXED64:
    1011        1231 :           val = strtoull(t->number, &end, 10);
    1012        1231 :           if (*end != '\0') {
    1013           1 :             return state_error(state, t,
    1014             :                 "Unable to convert '%s' for field '%s'.",
    1015           1 :                 t->number, state->field->name);
    1016             :           }
    1017        1230 :           if (state->field->label == PROTOBUF_C_LABEL_REPEATED) {
    1018             :             uint64_t *vals;
    1019             : 
    1020         407 :             STRUCT_MEMBER(size_t, msg, state->field->quantifier_offset) += 1;
    1021         407 :             n_members = STRUCT_MEMBER(size_t, msg,
    1022             :                                       state->field->quantifier_offset);
    1023         814 :             vals = local_realloc(
    1024         407 :                 STRUCT_MEMBER(uint64_t *, msg, state->field->offset),
    1025             :                 (n_members - 1) * sizeof(uint64_t),
    1026             :                 n_members * sizeof(uint64_t), state->allocator);
    1027         407 :             if (!vals) {
    1028           6 :               return state_error(state, t, "Malloc failure.");
    1029             :             }
    1030         401 :             STRUCT_MEMBER(uint64_t *, msg, state->field->offset) = vals;
    1031         401 :             vals[n_members - 1] = val;
    1032         401 :             return STATE_OPEN;
    1033             :           } else {
    1034         823 :             STRUCT_MEMBER(uint64_t, msg, state->field->offset) = val;
    1035         823 :             return STATE_OPEN;
    1036             :           }
    1037             :           break;
    1038             : 
    1039             :         case PROTOBUF_C_TYPE_SINT64:
    1040             :         case PROTOBUF_C_TYPE_SFIXED64:
    1041         707 :           val = strtoll(t->number, &end, 10);
    1042         707 :           if (*end != '\0') {
    1043           1 :             return state_error(state, t,
    1044             :                 "Unable to convert '%s' for field '%s'.",
    1045           1 :                 t->number, state->field->name);
    1046             :           }
    1047         706 :           if (state->field->label == PROTOBUF_C_LABEL_REPEATED) {
    1048             :             int64_t *vals;
    1049             : 
    1050         190 :             STRUCT_MEMBER(size_t, msg, state->field->quantifier_offset) += 1;
    1051         190 :             n_members = STRUCT_MEMBER(size_t, msg,
    1052             :                                       state->field->quantifier_offset);
    1053         380 :             vals = local_realloc(
    1054         190 :                 STRUCT_MEMBER(int64_t *, msg, state->field->offset),
    1055             :                 (n_members - 1) * sizeof(int64_t),
    1056             :                 n_members * sizeof(int64_t), state->allocator);
    1057         190 :             if (!vals) {
    1058           4 :               return state_error(state, t, "Malloc failure.");
    1059             :             }
    1060         186 :             STRUCT_MEMBER(int64_t *, msg, state->field->offset) = vals;
    1061         186 :             vals[n_members - 1] = val;
    1062         186 :             return STATE_OPEN;
    1063             :           } else {
    1064         516 :             STRUCT_MEMBER(int64_t, msg, state->field->offset) = val;
    1065         516 :             return STATE_OPEN;
    1066             :           }
    1067             :           break;
    1068             : 
    1069             :         case PROTOBUF_C_TYPE_FLOAT:
    1070             :           {
    1071             :             float val, *vals;
    1072             : 
    1073         482 :             errno = 0;
    1074         482 :             val = strtof(t->number, &end);
    1075         482 :             if (*end != '\0' || errno == ERANGE) {
    1076           1 :               return state_error(state, t,
    1077             :                   "Unable to convert '%s' for field '%s'.",
    1078           1 :                   t->number, state->field->name);
    1079             :             }
    1080         481 :             if (state->field->label == PROTOBUF_C_LABEL_REPEATED) {
    1081         181 :               STRUCT_MEMBER(size_t, msg, state->field->quantifier_offset) += 1;
    1082         181 :               n_members = STRUCT_MEMBER(size_t, msg,
    1083             :                                         state->field->quantifier_offset);
    1084         362 :               vals = local_realloc(
    1085         181 :                   STRUCT_MEMBER(float *, msg, state->field->offset),
    1086             :                   (n_members - 1) * sizeof(float),
    1087             :                   n_members * sizeof(float), state->allocator);
    1088         181 :               if (!vals) {
    1089           2 :                 return state_error(state, t, "Malloc failure.");
    1090             :               }
    1091         179 :               STRUCT_MEMBER(float *, msg, state->field->offset) = vals;
    1092         179 :               vals[n_members - 1] = val;
    1093         179 :               return STATE_OPEN;
    1094             :             } else {
    1095         300 :               STRUCT_MEMBER(float, msg, state->field->offset) = val;
    1096         300 :               return STATE_OPEN;
    1097             :             }
    1098             :           }
    1099             :           break;
    1100             : 
    1101             :         case PROTOBUF_C_TYPE_DOUBLE:
    1102             :           {
    1103             :             double val,*vals;
    1104             : 
    1105         503 :             errno = 0;
    1106         503 :             val = strtod(t->number, &end);
    1107         503 :             if (*end != '\0' || errno == ERANGE) {
    1108           1 :               return state_error(state, t,
    1109             :                   "Unable to convert '%s' for field '%s'.",
    1110           1 :                   t->number, state->field->name);
    1111             :             }
    1112         502 :             if (state->field->label == PROTOBUF_C_LABEL_REPEATED) {
    1113         195 :               STRUCT_MEMBER(size_t, msg, state->field->quantifier_offset) += 1;
    1114         195 :               n_members = STRUCT_MEMBER(size_t, msg,
    1115             :                                         state->field->quantifier_offset);
    1116         390 :               vals = local_realloc(
    1117         195 :                   STRUCT_MEMBER(double *, msg, state->field->offset),
    1118             :                   (n_members - 1) * sizeof(double),
    1119             :                   n_members * sizeof(double), state->allocator);
    1120         195 :               if (!vals) {
    1121           2 :                 return state_error(state, t, "Malloc failure.");
    1122             :               }
    1123         193 :               STRUCT_MEMBER(double *, msg, state->field->offset) = vals;
    1124         193 :               vals[n_members - 1] = val;
    1125         193 :               return STATE_OPEN;
    1126             :             } else {
    1127         307 :               STRUCT_MEMBER(double, msg, state->field->offset) = val;
    1128         307 :               return STATE_OPEN;
    1129             :             }
    1130             :           }
    1131             :           break;
    1132             : 
    1133             :         default:
    1134           1 :           return state_error(state, t,
    1135           1 :               "'%s' is not a numeric field.", state->field->name);
    1136             :           break;
    1137             :       }
    1138             :       break;
    1139             : 
    1140             :     default:
    1141           4 :       return state_error(state, t,
    1142             :                          "Expected value; found '%s' instead.",
    1143             :                          token2txt(t));
    1144             :       break;
    1145             :   }
    1146             : }
    1147             : 
    1148             : /** Table of states and actions.
    1149             :  *
    1150             :  * This is a table of each state and the action to take when in it.
    1151             :  */
    1152             : static StateId(* states[])(State *, Token *) = {
    1153             :   [STATE_OPEN] = state_open,
    1154             :   [STATE_ASSIGNMENT] = state_assignment,
    1155             :   [STATE_VALUE] = state_value
    1156             : };
    1157             : 
    1158             : /** @} */  /* End of state group. */
    1159             : 
    1160             : /** \defgroup base-parse Base parsing function
    1161             :  * \ingroup internal
    1162             :  * @{
    1163             :  */
    1164             : 
    1165             : /** Base function for the API functions.
    1166             :  *
    1167             :  * The API functions take a string or a \c FILE.  This function takes an
    1168             :  * appropriately initialised \c Scanner instead.  After that it works
    1169             :  * the same as the protobuf_c_text_from* family of functions.
    1170             :  *
    1171             :  * \param[in] descriptor a \c ProtobufCMessageDescriptor of a message you
    1172             :  *                       want to deserialise.
    1173             :  * \param[in] scanner A \c Scanner which will be used by the FSM to parse
    1174             :  *                    the text format protobuf.
    1175             :  * \param[in,out] result A \c ProtobufCTextError instance to record any
    1176             :  *                       errors.  It is not an option to pass \c NULL for
    1177             :  *                       this and it must be checked for errors.
    1178             :  * \param[in] allocator Allocator functions.
    1179             :  * \return \c NULL on error. A \c ProtobufCMessage representation of the
    1180             :  *         text format protobuf on success.
    1181             :  */
    1182             : static ProtobufCMessage *
    1183         318 : protobuf_c_text_parse(const ProtobufCMessageDescriptor *descriptor,
    1184             :     Scanner *scanner,
    1185             :     ProtobufCTextError *result,
    1186             :     ProtobufCAllocator *allocator)
    1187             : {
    1188             :   Token token;
    1189             :   State state;
    1190             :   StateId state_id;
    1191         318 :   ProtobufCMessage *msg = NULL;
    1192             : 
    1193         318 :   result->error_txt = NULL;
    1194         318 :   result->complete = -1;  /* -1 means the check wasn't performed. */
    1195             : 
    1196         318 :   state_id = STATE_OPEN;
    1197         318 :   if (!state_init(&state, scanner, descriptor, allocator)) {
    1198           8 :     return NULL;
    1199             :   }
    1200             : 
    1201       22340 :   while (state_id != STATE_DONE) {
    1202       21925 :     token = scan(scanner, allocator);
    1203       21925 :     if (token.id == TOK_MALLOC_ERR) {
    1204         205 :       token_free(&token, allocator);
    1205         205 :       state_error(&state, &token, "String unescape or malloc failure.");
    1206         205 :       break;
    1207             :     }
    1208       21720 :     state_id = states[state_id](&state, &token);
    1209       21720 :     token_free(&token, allocator);
    1210             :   }
    1211             : 
    1212         310 :   scanner_free(scanner, allocator);
    1213         310 :   if (state.error) {
    1214         303 :     result->error_txt = state.error_str;
    1215         303 :     if (msg) {
    1216           0 :       protobuf_c_message_free_unpacked(state.msgs[0], allocator);
    1217             :     }
    1218             :   } else {
    1219           7 :     msg = state.msgs[0];
    1220             : #ifdef HAVE_PROTOBUF_C_MESSAGE_CHECK
    1221           7 :     result->complete = protobuf_c_message_check(msg);
    1222             : #endif
    1223             :   }
    1224         310 :   state_free(&state);
    1225         310 :   return msg;
    1226             : }
    1227             : 
    1228             : /** @} */  /* End of base-parse group. */
    1229             : 
    1230             : /* See .h file for API docs. */
    1231             : 
    1232             : ProtobufCMessage *
    1233         315 : protobuf_c_text_from_file(const ProtobufCMessageDescriptor *descriptor,
    1234             :     FILE *msg_file,
    1235             :     ProtobufCTextError *result,
    1236             :     ProtobufCAllocator *allocator)
    1237             : {
    1238             :   Scanner scanner;
    1239             : 
    1240         315 :   scanner_init_file(&scanner, msg_file);
    1241         315 :   return protobuf_c_text_parse(descriptor, &scanner, result, allocator);
    1242             : }
    1243             : 
    1244             : ProtobufCMessage *
    1245           3 : protobuf_c_text_from_string(const ProtobufCMessageDescriptor *descriptor,
    1246             :     char *msg,
    1247             :     ProtobufCTextError *result,
    1248             :     ProtobufCAllocator *allocator)
    1249             : {
    1250             :   Scanner scanner;
    1251             : 
    1252           3 :   scanner_init_string(&scanner, msg);
    1253           3 :   return protobuf_c_text_parse(descriptor, &scanner, result, allocator);
    1254             : }

Generated by: LCOV version 1.10