LCOV - code coverage report
Current view: top level - protobuf-c-text - generate.c (source / functions) Hit Total Coverage
Test: PB-C-TEXT Code Coverage Lines: 212 223 95.1 %
Date: 2014-09-08 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /** \file
       2             :  * Routines to generate text format protobufs.
       3             :  *
       4             :  * This file contains the internal support functions as well as the
       5             :  * exported functions which are used to generate text format protobufs
       6             :  * from C protobuf data types.
       7             :  *
       8             :  * \author Kevin Lyda <kevin@ie.suberic.net>
       9             :  * \date   March 2014
      10             :  */
      11             : 
      12             : #include <sys/types.h>
      13             : #include <unistd.h>
      14             : #include <stdarg.h>
      15             : #include <stdio.h>
      16             : #include <string.h>
      17             : #include <stdlib.h>
      18             : #include <protobuf-c/protobuf-c.h>
      19             : #include "protobuf-c-text.h"
      20             : #include "protobuf-c-util.h"
      21             : #include "config.h"
      22             : 
      23             : /** A dynamic string struct.
      24             :  *
      25             :  * Used to track additions to a growing string and memory allocation
      26             :  * errors that occur in processing
      27             :  */
      28             : typedef struct _ReturnString {
      29             :   int malloc_err;  /**< Set to 1 when there's been a malloc error. */
      30             :   int allocated;   /**< Size of allocated string. */
      31             :   int pos;         /**< Current end of the string. */
      32             :   char *s;         /**< The string. */
      33             : } ReturnString;
      34             : 
      35             : /** Append a string to the ReturnString.
      36             :  *
      37             :  * Append the string built from \c format and its args to the \c rs
      38             :  * string. Note that \c malloc_err is checked and if it's true,
      39             :  * this function won't do anything.
      40             :  *
      41             :  * \param[in,out] rs The string to append to.
      42             :  * \param[in] guess A guess at the number of chars being added.
      43             :  * \param[in] allocator allocator functions.
      44             :  * \param[in] format Printf-style format string.
      45             :  * \param[in] ... Variable number of args for \c format.
      46             :  */
      47             : static void rs_append(ReturnString *rs, int guess,
      48             :     ProtobufCAllocator *allocator,
      49             :     const char *format, ...)
      50             :   __attribute__((format(printf, 4, 5)));
      51             : static void
      52        1120 : rs_append(ReturnString *rs, int guess,
      53             :     ProtobufCAllocator *allocator,
      54             :     const char *format, ...)
      55             : {
      56             :   va_list args;
      57             :   int added;
      58             : 
      59        1120 :   if (rs->malloc_err) {
      60         117 :     return;
      61             :   }
      62             : 
      63        1003 :   if (rs->allocated - rs->pos < guess * 2) {
      64             :     char *tmp;
      65             : 
      66         325 :     tmp = PBC_ALLOC(rs->allocated + guess * 2);
      67         325 :     if (!tmp) {
      68          18 :       PBC_FREE(rs->s);
      69          18 :       rs->s = NULL;
      70          18 :       rs->malloc_err = 1;
      71          18 :       return;
      72             :     }
      73         307 :     memcpy(tmp, rs->s, rs->allocated);
      74         307 :     PBC_FREE(rs->s);
      75         307 :     rs->s = tmp;
      76         307 :     rs->allocated += guess * 2;
      77             :   }
      78         985 :   va_start(args, format);
      79         985 :   added = vsnprintf(rs->s + rs->pos, rs->allocated - rs->pos, format, args);
      80         985 :   va_end(args);
      81         985 :   rs->pos += added;
      82         985 :   return;
      83             : }
      84             : 
      85             : /** @} */  /* End of utility group. */
      86             : 
      87             : 
      88             : /** \defgroup generate Functions to generate text format proto bufs
      89             :  * \ingroup internal
      90             :  * @{
      91             :  */
      92             : 
      93             : /** Escape string.
      94             :  *
      95             :  * Add escape characters to strings for problematic characters.
      96             :  *
      97             :  * \param[in] src The unescaped string to process.
      98             :  * \param[in] len Length of \c src. Note that \c src might have ASCII
      99             :  *                \c NULs so strlen() isn't good enough here.
     100             :  * \param[in] allocator allocator functions.
     101             :  * \return The fully escaped string, or \c NULL if there has been an
     102             :  *         allocation error.
     103             :  */
     104             : static char *
     105         223 : esc_str(char *src, int len, ProtobufCAllocator *allocator)
     106             : {
     107         223 :   int i, escapes = 0, dst_len = 0;
     108             :   unsigned char *dst;
     109             : 
     110        4566 :   for (i = 0; i < len; i++) {
     111        4343 :     if (!isprint(src[i])) {
     112         239 :       escapes++;
     113             :     }
     114             :   }
     115         223 :   dst = PBC_ALLOC((escapes * 4) + ((len - escapes) * 2) + 1);
     116         223 :   if (!dst) {
     117          13 :     return NULL;
     118             :   }
     119             : 
     120        3721 :   for (i = 0; i < len; i++) {
     121        3511 :     switch (src[i]) {
     122             :       /* Special cases. */
     123             :       case '\'':
     124           1 :         dst[dst_len++] = '\\';
     125           1 :         dst[dst_len++] = '\'';
     126           1 :         break;
     127             :       case '\"':
     128           1 :         dst[dst_len++] = '\\';
     129           1 :         dst[dst_len++] = '\"';
     130           1 :         break;
     131             :       case '\\':
     132           5 :         dst[dst_len++] = '\\';
     133           5 :         dst[dst_len++] = '\\';
     134           5 :         break;
     135             :       case '\n':
     136          31 :         dst[dst_len++] = '\\';
     137          31 :         dst[dst_len++] = 'n';
     138          31 :         break;
     139             :       case '\r':
     140          21 :         dst[dst_len++] = '\\';
     141          21 :         dst[dst_len++] = 'r';
     142          21 :         break;
     143             :       case '\t':
     144           5 :         dst[dst_len++] = '\\';
     145           5 :         dst[dst_len++] = 't';
     146           5 :         break;
     147             : 
     148             :       /* Escape with octal if !isprint. */
     149             :       default:
     150        3447 :         if (!isprint(src[i])) {
     151         134 :           dst_len += sprintf(dst + dst_len, "\\%03o", src[i]);
     152             :         } else {
     153        3313 :           dst[dst_len++] = src[i];
     154             :         }
     155        3447 :         break;
     156             :     }
     157             :   }
     158         210 :   dst[dst_len] = '\0';
     159             : 
     160         210 :   return dst;
     161             : }
     162             : 
     163             : /** Internal function to back API function.
     164             :  *
     165             :  * Has a few extra params to better enable recursion.  This function gets
     166             :  * called for each nested message as the \c ProtobufCMessage struct is
     167             :  * traversed.
     168             :  *
     169             :  * \param[in,out] rs The string being built up for the text format protobuf.
     170             :  * \param[in] level Indent level - increments in 2's.
     171             :  * \param[in] m The \c ProtobufCMessage being serialised.
     172             :  * \param[in] d The descriptor for the \c ProtobufCMessage.
     173             :  * \param[in] allocator allocator functions.
     174             :  */
     175             : static void
     176         198 : protobuf_c_text_to_string_internal(ReturnString *rs,
     177             :     int level,
     178             :     ProtobufCMessage *m,
     179             :     const ProtobufCMessageDescriptor *d,
     180             :     ProtobufCAllocator *allocator)
     181             : {
     182             :   int i;
     183             :   size_t j, quantifier_offset;
     184             :   double float_var;
     185             :   const ProtobufCFieldDescriptor *f;
     186             :   ProtobufCEnumDescriptor *enumd;
     187             :   const ProtobufCEnumValue *enumv;
     188             : 
     189         198 :   f = d->fields;
     190        1156 :   for (i = 0; i < d->n_fields; i++) {
     191        1032 :     if (rs->malloc_err) {
     192             :       /* If there's been a malloc error, go die. */
     193          61 :       return;
     194             :     }
     195             : 
     196             :     /* Decide if something needs to be done for this field. */
     197         971 :     switch (f[i].label) {
     198             :       case PROTOBUF_C_LABEL_OPTIONAL:
     199         680 :         if (f[i].type == PROTOBUF_C_TYPE_STRING) {
     200          90 :           if (!STRUCT_MEMBER(char *, m, f[i].offset)
     201         124 :               || (STRUCT_MEMBER(char *, m, f[i].offset)
     202          62 :                 == (char *)f[i].default_value)) {
     203          28 :             continue;
     204             :           }
     205         590 :         } else if (f[i].type == PROTOBUF_C_TYPE_MESSAGE) {
     206           2 :           if (!STRUCT_MEMBER(char *, m, f[i].offset)) {
     207           1 :             continue;
     208             :           }
     209             :         } else {
     210         588 :           if (!STRUCT_MEMBER(protobuf_c_boolean, m, f[i].quantifier_offset)) {
     211          65 :             continue;
     212             :           }
     213             :         }
     214         586 :         break;
     215             :       case PROTOBUF_C_LABEL_REPEATED:
     216         100 :         if (!STRUCT_MEMBER(size_t, m, f[i].quantifier_offset)) {
     217           7 :           continue;
     218             :         }
     219          93 :         break;
     220             :     }
     221             : 
     222         870 :     quantifier_offset = STRUCT_MEMBER(size_t, m, f[i].quantifier_offset);
     223             :     /* Field exists and has data, dump it. */
     224         870 :     switch (f[i].type) {
     225             :       case PROTOBUF_C_TYPE_INT32:
     226             :       case PROTOBUF_C_TYPE_UINT32:
     227             :       case PROTOBUF_C_TYPE_FIXED32:
     228         130 :         if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
     229           6 :           for (j = 0; j < quantifier_offset; j++) {
     230           8 :             rs_append(rs, level + strlen(f[i].name) + 20,
     231             :                 allocator,
     232             :                 "%*s%s: %u\n",
     233           4 :                 level, "", f[i].name,
     234           4 :                 STRUCT_MEMBER(uint32_t *, m, f[i].offset)[j]);
     235             :           }
     236             :         } else {
     237         256 :           rs_append(rs, level + strlen(f[i].name) + 20,
     238             :               allocator,
     239             :               "%*s%s: %u\n",
     240         128 :               level, "", f[i].name,
     241         128 :               STRUCT_MEMBER(uint32_t, m, f[i].offset));
     242             :         }
     243         130 :         break;
     244             :       case PROTOBUF_C_TYPE_SINT32:
     245             :       case PROTOBUF_C_TYPE_SFIXED32:
     246          73 :         if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
     247           6 :           for (j = 0; j < quantifier_offset; j++) {
     248           8 :             rs_append(rs, level + strlen(f[i].name) + 20,
     249             :                 allocator,
     250             :                 "%*s%s: %d\n",
     251           4 :                 level, "", f[i].name,
     252           4 :                 STRUCT_MEMBER(int32_t *, m, f[i].offset)[j]);
     253             :           }
     254             :         } else {
     255         142 :           rs_append(rs, level + strlen(f[i].name) + 20,
     256             :               allocator,
     257             :               "%*s%s: %d\n",
     258          71 :               level, "", f[i].name,
     259          71 :               STRUCT_MEMBER(int32_t, m, f[i].offset));
     260             :         }
     261          73 :         break;
     262             :       case PROTOBUF_C_TYPE_INT64:
     263             :       case PROTOBUF_C_TYPE_UINT64:
     264             :       case PROTOBUF_C_TYPE_FIXED64:
     265         113 :         if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
     266           9 :           for (j = 0; j < quantifier_offset; j++) {
     267          12 :             rs_append(rs, level + strlen(f[i].name) + 20,
     268             :                 allocator,
     269             :                 "%*s%s: %lu\n",
     270           6 :                 level, "", f[i].name,
     271           6 :                 STRUCT_MEMBER(uint64_t *, m, f[i].offset)[j]);
     272             :           }
     273             :         } else {
     274         220 :           rs_append(rs, level + strlen(f[i].name) + 20,
     275             :               allocator,
     276             :               "%*s%s: %lu\n",
     277         110 :               level, "", f[i].name,
     278         110 :               STRUCT_MEMBER(uint64_t, m, f[i].offset));
     279             :         }
     280         113 :         break;
     281             :       case PROTOBUF_C_TYPE_SINT64:
     282             :       case PROTOBUF_C_TYPE_SFIXED64:
     283          78 :         if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
     284           6 :           for (j = 0; j < quantifier_offset; j++) {
     285           8 :             rs_append(rs, level + strlen(f[i].name) + 20,
     286             :                 allocator,
     287             :                 "%*s%s: %ld\n",
     288           4 :                 level, "", f[i].name,
     289           4 :                 STRUCT_MEMBER(int64_t *, m, f[i].offset)[j]);
     290             :           }
     291             :         } else {
     292         152 :           rs_append(rs, level + strlen(f[i].name) + 20,
     293             :               allocator,
     294             :               "%*s%s: %ld\n",
     295          76 :               level, "", f[i].name,
     296          76 :               STRUCT_MEMBER(int64_t, m, f[i].offset));
     297             :         }
     298          78 :         break;
     299             :       case PROTOBUF_C_TYPE_FLOAT:
     300          40 :         if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
     301           3 :           for (j = 0; j < quantifier_offset; j++) {
     302           2 :             float_var = STRUCT_MEMBER(float *, m, f[i].offset)[j];
     303           2 :             rs_append(rs, level + strlen(f[i].name) + 20,
     304             :                 allocator,
     305             :                 "%*s%s: %g\n",
     306           2 :                 level, "", f[i].name,
     307             :                 float_var);
     308             :           }
     309             :         } else {
     310          39 :           float_var = STRUCT_MEMBER(float, m, f[i].offset);
     311          39 :           rs_append(rs, level + strlen(f[i].name) + 20,
     312             :               allocator,
     313             :               "%*s%s: %g\n",
     314          39 :               level, "", f[i].name,
     315             :               float_var);
     316             :         }
     317          40 :         break;
     318             :       case PROTOBUF_C_TYPE_DOUBLE:
     319          41 :         if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
     320           3 :           for (j = 0; j < quantifier_offset; j++) {
     321           4 :             rs_append(rs, level + strlen(f[i].name) + 20,
     322             :                 allocator,
     323             :                 "%*s%s: %g\n",
     324           2 :                 level, "", f[i].name,
     325           2 :                 STRUCT_MEMBER(double *, m, f[i].offset)[j]);
     326             :           }
     327             :         } else {
     328          80 :           rs_append(rs, level + strlen(f[i].name) + 20,
     329             :               allocator,
     330             :               "%*s%s: %g\n",
     331          40 :               level, "", f[i].name,
     332          40 :               STRUCT_MEMBER(double, m, f[i].offset));
     333             :         }
     334          41 :         break;
     335             :       case PROTOBUF_C_TYPE_BOOL:
     336          35 :         if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
     337           3 :           for (j = 0; j < quantifier_offset; j++) {
     338           4 :             rs_append(rs, level + strlen(f[i].name) + 20,
     339             :                 allocator,
     340             :                 "%*s%s: %s\n",
     341           2 :                 level, "", f[i].name,
     342           2 :                 STRUCT_MEMBER(protobuf_c_boolean *, m, f[i].offset)[j]?
     343             :                 "true": "false");
     344             :           }
     345             :         } else {
     346          68 :           rs_append(rs, level + strlen(f[i].name) + 20,
     347             :               allocator,
     348             :               "%*s%s: %s\n",
     349          34 :               level, "", f[i].name,
     350          34 :               STRUCT_MEMBER(protobuf_c_boolean, m, f[i].offset)?
     351             :               "true": "false");
     352             :         }
     353          35 :         break;
     354             :       case PROTOBUF_C_TYPE_ENUM:
     355          59 :         enumd = (ProtobufCEnumDescriptor *)f[i].descriptor;
     356          59 :         if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
     357           3 :           for (j = 0; j < quantifier_offset; j++) {
     358           2 :             enumv = protobuf_c_enum_descriptor_get_value(
     359           2 :                 enumd, STRUCT_MEMBER(int *, m, f[i].offset)[j]);
     360           4 :             rs_append(rs, level + strlen(f[i].name) + 20,
     361             :                 allocator,
     362             :                 "%*s%s: %s\n",
     363           2 :                 level, "", f[i].name,
     364             :                 enumv? enumv->name: "unknown");
     365             :           }
     366             :         } else {
     367          58 :           enumv = protobuf_c_enum_descriptor_get_value(
     368          58 :               enumd, STRUCT_MEMBER(int, m, f[i].offset));
     369         116 :           rs_append(rs, level + strlen(f[i].name) + 20,
     370             :               allocator,
     371             :               "%*s%s: %s\n",
     372          58 :               level, "", f[i].name,
     373             :               enumv? enumv->name: "unknown");
     374             :         }
     375          59 :         break;
     376             :       case PROTOBUF_C_TYPE_STRING:
     377         184 :         if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
     378           3 :           for (j = 0; j < quantifier_offset; j++) {
     379             :             unsigned char *escaped;
     380             : 
     381           4 :             escaped = esc_str(
     382           2 :                 STRUCT_MEMBER(unsigned char **, m, f[i].offset)[j],
     383           2 :                 strlen(STRUCT_MEMBER(unsigned char **, m, f[i].offset)[j]),
     384             :                 allocator);
     385           2 :             if (!escaped) {
     386           0 :               PBC_FREE(rs->s);
     387           0 :               rs->s = NULL;
     388           0 :               rs->malloc_err = 1;
     389           0 :               return;
     390             :             }
     391           2 :             rs_append(rs, level + strlen(f[i].name) + strlen(escaped) + 10,
     392             :                 allocator,
     393           2 :                 "%*s%s: \"%s\"\n", level, "", f[i].name, escaped);
     394           2 :             PBC_FREE(escaped);
     395             :           }
     396             :         } else {
     397             :           unsigned char *escaped;
     398             : 
     399         183 :           escaped = esc_str(STRUCT_MEMBER(unsigned char *, m, f[i].offset),
     400         183 :               strlen(STRUCT_MEMBER(unsigned char *, m, f[i].offset)),
     401             :               allocator);
     402         183 :           if (!escaped) {
     403          10 :             PBC_FREE(rs->s);
     404          10 :             rs->s = NULL;
     405          10 :             rs->malloc_err = 1;
     406          10 :             return;
     407             :           }
     408         173 :           rs_append(rs, level + strlen(f[i].name) + strlen(escaped) + 10,
     409             :               allocator,
     410         173 :               "%*s%s: \"%s\"\n", level, "", f[i].name, escaped);
     411         173 :           PBC_FREE(escaped);
     412             :         }
     413         174 :         break;
     414             :       case PROTOBUF_C_TYPE_BYTES:
     415          37 :         if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
     416           3 :           for (j = 0; j < quantifier_offset; j++) {
     417             :             unsigned char *escaped;
     418             : 
     419           4 :             escaped = esc_str(
     420           2 :                 STRUCT_MEMBER(ProtobufCBinaryData *, m, f[i].offset)[j].data,
     421           2 :                 STRUCT_MEMBER(ProtobufCBinaryData *, m, f[i].offset)[j].len,
     422             :                 allocator);
     423           2 :             if (!escaped) {
     424           0 :               PBC_FREE(rs->s);
     425           0 :               rs->s = NULL;
     426           0 :               rs->malloc_err = 1;
     427           0 :               return;
     428             :             }
     429           2 :             rs_append(rs, level + strlen(f[i].name) + strlen(escaped) + 10,
     430             :                 allocator,
     431           2 :                 "%*s%s: \"%s\"\n", level, "", f[i].name, escaped);
     432           2 :             PBC_FREE(escaped);
     433             :           }
     434             :         } else {
     435             :           unsigned char *escaped;
     436             : 
     437          72 :           escaped = esc_str(
     438          36 :               STRUCT_MEMBER(ProtobufCBinaryData, m, f[i].offset).data,
     439          36 :               STRUCT_MEMBER(ProtobufCBinaryData, m, f[i].offset).len,
     440             :               allocator);
     441          36 :           if (!escaped) {
     442           3 :             PBC_FREE(rs->s);
     443           3 :             rs->s = NULL;
     444           3 :             rs->malloc_err = 1;
     445           3 :             return;
     446             :           }
     447          33 :           rs_append(rs, level + strlen(f[i].name) + strlen(escaped) + 10,
     448             :               allocator,
     449          33 :               "%*s%s: \"%s\"\n", level, "", f[i].name, escaped);
     450          33 :           PBC_FREE(escaped);
     451             :         }
     452          34 :         break;
     453             : 
     454             :       case PROTOBUF_C_TYPE_MESSAGE:
     455          80 :         if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
     456         318 :           for (j = 0;
     457         240 :               j < STRUCT_MEMBER(size_t, m, f[i].quantifier_offset);
     458         162 :               j++) {
     459         162 :             rs_append(rs, level + strlen(f[i].name) + 10,
     460             :                 allocator,
     461         162 :                 "%*s%s {\n", level, "", f[i].name);
     462         162 :             protobuf_c_text_to_string_internal(rs, level + 2,
     463         162 :                 STRUCT_MEMBER(ProtobufCMessage **, m, f[i].offset)[j],
     464         162 :                 (ProtobufCMessageDescriptor *)f[i].descriptor,
     465             :                 allocator);
     466         162 :             rs_append(rs, level + 10,
     467             :                 allocator,
     468             :                 "%*s}\n", level, "");
     469             :           }
     470             :         } else {
     471           2 :           rs_append(rs, level + strlen(f[i].name) + 10,
     472             :               allocator,
     473           2 :               "%*s%s {\n", level, "", f[i].name);
     474           2 :           protobuf_c_text_to_string_internal(rs, level + 2,
     475           2 :               STRUCT_MEMBER(ProtobufCMessage *, m, f[i].offset),
     476           2 :               (ProtobufCMessageDescriptor *)f[i].descriptor,
     477             :               allocator);
     478           2 :           rs_append(rs, level + 10,
     479             :               allocator,
     480             :               "%*s}\n", level, "");
     481             :         }
     482          80 :         break;
     483             :       default:
     484           0 :         PBC_FREE(rs->s);
     485           0 :         rs->s = NULL;
     486           0 :         return;
     487             :         break;
     488             :     }
     489             : 
     490             :   }
     491             : }
     492             : 
     493             : /** @} */  /* End of generate group. */
     494             : 
     495             : /* See .h file for API docs. */
     496             : 
     497             : char *
     498          34 : protobuf_c_text_to_string(ProtobufCMessage *m,
     499             :     ProtobufCAllocator *allocator)
     500             : {
     501          34 :   ReturnString rs = { 0, 0, 0, NULL };
     502             : 
     503          34 :   protobuf_c_text_to_string_internal(&rs, 0, m, m->descriptor, allocator);
     504             : 
     505          34 :   return rs.s;
     506             : }

Generated by: LCOV version 1.10