protobuf-c-text
Library to generate & parse text format protobufs in C.
 All Data Structures Files Functions Variables Enumerations Enumerator Macros Groups
generate.c
Go to the documentation of this file.
1 
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 
28 typedef struct _ReturnString {
29  int malloc_err;
30  int allocated;
31  int pos;
32  char *s;
33 } ReturnString;
34 
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 rs_append(ReturnString *rs, int guess,
53  ProtobufCAllocator *allocator,
54  const char *format, ...)
55 {
56  va_list args;
57  int added;
58 
59  if (rs->malloc_err) {
60  return;
61  }
62 
63  if (rs->allocated - rs->pos < guess * 2) {
64  char *tmp;
65 
66  tmp = PBC_ALLOC(rs->allocated + guess * 2);
67  if (!tmp) {
68  PBC_FREE(rs->s);
69  rs->s = NULL;
70  rs->malloc_err = 1;
71  return;
72  }
73  memcpy(tmp, rs->s, rs->allocated);
74  PBC_FREE(rs->s);
75  rs->s = tmp;
76  rs->allocated += guess * 2;
77  }
78  va_start(args, format);
79  added = vsnprintf(rs->s + rs->pos, rs->allocated - rs->pos, format, args);
80  va_end(args);
81  rs->pos += added;
82  return;
83 }
84  /* End of utility group. */
86 
87 
104 static char *
105 esc_str(char *src, int len, ProtobufCAllocator *allocator)
106 {
107  int i, escapes = 0, dst_len = 0;
108  unsigned char *dst;
109 
110  for (i = 0; i < len; i++) {
111  if (!isprint(src[i])) {
112  escapes++;
113  }
114  }
115  dst = PBC_ALLOC((escapes * 4) + ((len - escapes) * 2) + 1);
116  if (!dst) {
117  return NULL;
118  }
119 
120  for (i = 0; i < len; i++) {
121  switch (src[i]) {
122  /* Special cases. */
123  case '\'':
124  dst[dst_len++] = '\\';
125  dst[dst_len++] = '\'';
126  break;
127  case '\"':
128  dst[dst_len++] = '\\';
129  dst[dst_len++] = '\"';
130  break;
131  case '\\':
132  dst[dst_len++] = '\\';
133  dst[dst_len++] = '\\';
134  break;
135  case '\n':
136  dst[dst_len++] = '\\';
137  dst[dst_len++] = 'n';
138  break;
139  case '\r':
140  dst[dst_len++] = '\\';
141  dst[dst_len++] = 'r';
142  break;
143  case '\t':
144  dst[dst_len++] = '\\';
145  dst[dst_len++] = 't';
146  break;
147 
148  /* Escape with octal if !isprint. */
149  default:
150  if (!isprint(src[i])) {
151  dst_len += sprintf(dst + dst_len, "\\%03o", src[i]);
152  } else {
153  dst[dst_len++] = src[i];
154  }
155  break;
156  }
157  }
158  dst[dst_len] = '\0';
159 
160  return dst;
161 }
162 
175 static void
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  f = d->fields;
190  for (i = 0; i < d->n_fields; i++) {
191  if (rs->malloc_err) {
192  /* If there's been a malloc error, go die. */
193  return;
194  }
195 
196  /* Decide if something needs to be done for this field. */
197  switch (f[i].label) {
198  case PROTOBUF_C_LABEL_OPTIONAL:
199  if (f[i].type == PROTOBUF_C_TYPE_STRING) {
200  if (!STRUCT_MEMBER(char *, m, f[i].offset)
201  || (STRUCT_MEMBER(char *, m, f[i].offset)
202  == (char *)f[i].default_value)) {
203  continue;
204  }
205  } else if (f[i].type == PROTOBUF_C_TYPE_MESSAGE) {
206  if (!STRUCT_MEMBER(char *, m, f[i].offset)) {
207  continue;
208  }
209  } else {
210  if (!STRUCT_MEMBER(protobuf_c_boolean, m, f[i].quantifier_offset)) {
211  continue;
212  }
213  }
214  break;
215  case PROTOBUF_C_LABEL_REPEATED:
216  if (!STRUCT_MEMBER(size_t, m, f[i].quantifier_offset)) {
217  continue;
218  }
219  break;
220  }
221 
222  quantifier_offset = STRUCT_MEMBER(size_t, m, f[i].quantifier_offset);
223  /* Field exists and has data, dump it. */
224  switch (f[i].type) {
225  case PROTOBUF_C_TYPE_INT32:
226  case PROTOBUF_C_TYPE_UINT32:
227  case PROTOBUF_C_TYPE_FIXED32:
228  if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
229  for (j = 0; j < quantifier_offset; j++) {
230  rs_append(rs, level + strlen(f[i].name) + 20,
231  allocator,
232  "%*s%s: %u\n",
233  level, "", f[i].name,
234  STRUCT_MEMBER(uint32_t *, m, f[i].offset)[j]);
235  }
236  } else {
237  rs_append(rs, level + strlen(f[i].name) + 20,
238  allocator,
239  "%*s%s: %u\n",
240  level, "", f[i].name,
241  STRUCT_MEMBER(uint32_t, m, f[i].offset));
242  }
243  break;
244  case PROTOBUF_C_TYPE_SINT32:
245  case PROTOBUF_C_TYPE_SFIXED32:
246  if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
247  for (j = 0; j < quantifier_offset; j++) {
248  rs_append(rs, level + strlen(f[i].name) + 20,
249  allocator,
250  "%*s%s: %d\n",
251  level, "", f[i].name,
252  STRUCT_MEMBER(int32_t *, m, f[i].offset)[j]);
253  }
254  } else {
255  rs_append(rs, level + strlen(f[i].name) + 20,
256  allocator,
257  "%*s%s: %d\n",
258  level, "", f[i].name,
259  STRUCT_MEMBER(int32_t, m, f[i].offset));
260  }
261  break;
262  case PROTOBUF_C_TYPE_INT64:
263  case PROTOBUF_C_TYPE_UINT64:
264  case PROTOBUF_C_TYPE_FIXED64:
265  if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
266  for (j = 0; j < quantifier_offset; j++) {
267  rs_append(rs, level + strlen(f[i].name) + 20,
268  allocator,
269  "%*s%s: %lu\n",
270  level, "", f[i].name,
271  STRUCT_MEMBER(uint64_t *, m, f[i].offset)[j]);
272  }
273  } else {
274  rs_append(rs, level + strlen(f[i].name) + 20,
275  allocator,
276  "%*s%s: %lu\n",
277  level, "", f[i].name,
278  STRUCT_MEMBER(uint64_t, m, f[i].offset));
279  }
280  break;
281  case PROTOBUF_C_TYPE_SINT64:
282  case PROTOBUF_C_TYPE_SFIXED64:
283  if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
284  for (j = 0; j < quantifier_offset; j++) {
285  rs_append(rs, level + strlen(f[i].name) + 20,
286  allocator,
287  "%*s%s: %ld\n",
288  level, "", f[i].name,
289  STRUCT_MEMBER(int64_t *, m, f[i].offset)[j]);
290  }
291  } else {
292  rs_append(rs, level + strlen(f[i].name) + 20,
293  allocator,
294  "%*s%s: %ld\n",
295  level, "", f[i].name,
296  STRUCT_MEMBER(int64_t, m, f[i].offset));
297  }
298  break;
299  case PROTOBUF_C_TYPE_FLOAT:
300  if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
301  for (j = 0; j < quantifier_offset; j++) {
302  float_var = STRUCT_MEMBER(float *, m, f[i].offset)[j];
303  rs_append(rs, level + strlen(f[i].name) + 20,
304  allocator,
305  "%*s%s: %g\n",
306  level, "", f[i].name,
307  float_var);
308  }
309  } else {
310  float_var = STRUCT_MEMBER(float, m, f[i].offset);
311  rs_append(rs, level + strlen(f[i].name) + 20,
312  allocator,
313  "%*s%s: %g\n",
314  level, "", f[i].name,
315  float_var);
316  }
317  break;
318  case PROTOBUF_C_TYPE_DOUBLE:
319  if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
320  for (j = 0; j < quantifier_offset; j++) {
321  rs_append(rs, level + strlen(f[i].name) + 20,
322  allocator,
323  "%*s%s: %g\n",
324  level, "", f[i].name,
325  STRUCT_MEMBER(double *, m, f[i].offset)[j]);
326  }
327  } else {
328  rs_append(rs, level + strlen(f[i].name) + 20,
329  allocator,
330  "%*s%s: %g\n",
331  level, "", f[i].name,
332  STRUCT_MEMBER(double, m, f[i].offset));
333  }
334  break;
335  case PROTOBUF_C_TYPE_BOOL:
336  if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
337  for (j = 0; j < quantifier_offset; j++) {
338  rs_append(rs, level + strlen(f[i].name) + 20,
339  allocator,
340  "%*s%s: %s\n",
341  level, "", f[i].name,
342  STRUCT_MEMBER(protobuf_c_boolean *, m, f[i].offset)[j]?
343  "true": "false");
344  }
345  } else {
346  rs_append(rs, level + strlen(f[i].name) + 20,
347  allocator,
348  "%*s%s: %s\n",
349  level, "", f[i].name,
350  STRUCT_MEMBER(protobuf_c_boolean, m, f[i].offset)?
351  "true": "false");
352  }
353  break;
354  case PROTOBUF_C_TYPE_ENUM:
355  enumd = (ProtobufCEnumDescriptor *)f[i].descriptor;
356  if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
357  for (j = 0; j < quantifier_offset; j++) {
358  enumv = protobuf_c_enum_descriptor_get_value(
359  enumd, STRUCT_MEMBER(int *, m, f[i].offset)[j]);
360  rs_append(rs, level + strlen(f[i].name) + 20,
361  allocator,
362  "%*s%s: %s\n",
363  level, "", f[i].name,
364  enumv? enumv->name: "unknown");
365  }
366  } else {
367  enumv = protobuf_c_enum_descriptor_get_value(
368  enumd, STRUCT_MEMBER(int, m, f[i].offset));
369  rs_append(rs, level + strlen(f[i].name) + 20,
370  allocator,
371  "%*s%s: %s\n",
372  level, "", f[i].name,
373  enumv? enumv->name: "unknown");
374  }
375  break;
376  case PROTOBUF_C_TYPE_STRING:
377  if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
378  for (j = 0; j < quantifier_offset; j++) {
379  unsigned char *escaped;
380 
381  escaped = esc_str(
382  STRUCT_MEMBER(unsigned char **, m, f[i].offset)[j],
383  strlen(STRUCT_MEMBER(unsigned char **, m, f[i].offset)[j]),
384  allocator);
385  if (!escaped) {
386  PBC_FREE(rs->s);
387  rs->s = NULL;
388  rs->malloc_err = 1;
389  return;
390  }
391  rs_append(rs, level + strlen(f[i].name) + strlen(escaped) + 10,
392  allocator,
393  "%*s%s: \"%s\"\n", level, "", f[i].name, escaped);
394  PBC_FREE(escaped);
395  }
396  } else {
397  unsigned char *escaped;
398 
399  escaped = esc_str(STRUCT_MEMBER(unsigned char *, m, f[i].offset),
400  strlen(STRUCT_MEMBER(unsigned char *, m, f[i].offset)),
401  allocator);
402  if (!escaped) {
403  PBC_FREE(rs->s);
404  rs->s = NULL;
405  rs->malloc_err = 1;
406  return;
407  }
408  rs_append(rs, level + strlen(f[i].name) + strlen(escaped) + 10,
409  allocator,
410  "%*s%s: \"%s\"\n", level, "", f[i].name, escaped);
411  PBC_FREE(escaped);
412  }
413  break;
414  case PROTOBUF_C_TYPE_BYTES:
415  if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
416  for (j = 0; j < quantifier_offset; j++) {
417  unsigned char *escaped;
418 
419  escaped = esc_str(
420  STRUCT_MEMBER(ProtobufCBinaryData *, m, f[i].offset)[j].data,
421  STRUCT_MEMBER(ProtobufCBinaryData *, m, f[i].offset)[j].len,
422  allocator);
423  if (!escaped) {
424  PBC_FREE(rs->s);
425  rs->s = NULL;
426  rs->malloc_err = 1;
427  return;
428  }
429  rs_append(rs, level + strlen(f[i].name) + strlen(escaped) + 10,
430  allocator,
431  "%*s%s: \"%s\"\n", level, "", f[i].name, escaped);
432  PBC_FREE(escaped);
433  }
434  } else {
435  unsigned char *escaped;
436 
437  escaped = esc_str(
438  STRUCT_MEMBER(ProtobufCBinaryData, m, f[i].offset).data,
439  STRUCT_MEMBER(ProtobufCBinaryData, m, f[i].offset).len,
440  allocator);
441  if (!escaped) {
442  PBC_FREE(rs->s);
443  rs->s = NULL;
444  rs->malloc_err = 1;
445  return;
446  }
447  rs_append(rs, level + strlen(f[i].name) + strlen(escaped) + 10,
448  allocator,
449  "%*s%s: \"%s\"\n", level, "", f[i].name, escaped);
450  PBC_FREE(escaped);
451  }
452  break;
453 
454  case PROTOBUF_C_TYPE_MESSAGE:
455  if (f[i].label == PROTOBUF_C_LABEL_REPEATED) {
456  for (j = 0;
457  j < STRUCT_MEMBER(size_t, m, f[i].quantifier_offset);
458  j++) {
459  rs_append(rs, level + strlen(f[i].name) + 10,
460  allocator,
461  "%*s%s {\n", level, "", f[i].name);
463  STRUCT_MEMBER(ProtobufCMessage **, m, f[i].offset)[j],
464  (ProtobufCMessageDescriptor *)f[i].descriptor,
465  allocator);
466  rs_append(rs, level + 10,
467  allocator,
468  "%*s}\n", level, "");
469  }
470  } else {
471  rs_append(rs, level + strlen(f[i].name) + 10,
472  allocator,
473  "%*s%s {\n", level, "", f[i].name);
475  STRUCT_MEMBER(ProtobufCMessage *, m, f[i].offset),
476  (ProtobufCMessageDescriptor *)f[i].descriptor,
477  allocator);
478  rs_append(rs, level + 10,
479  allocator,
480  "%*s}\n", level, "");
481  }
482  break;
483  default:
484  PBC_FREE(rs->s);
485  rs->s = NULL;
486  return;
487  break;
488  }
489 
490  }
491 }
492  /* End of generate group. */
494 
495 /* See .h file for API docs. */
496 
497 char *
498 protobuf_c_text_to_string(ProtobufCMessage *m,
499  ProtobufCAllocator *allocator)
500 {
501  ReturnString rs = { 0, 0, 0, NULL };
502 
503  protobuf_c_text_to_string_internal(&rs, 0, m, m->descriptor, allocator);
504 
505  return rs.s;
506 }
int pos
Current end of the string.
Definition: generate.c:31
#define PBC_FREE(ptr)
Free possibly using the pbc allocator.
char * protobuf_c_text_to_string(ProtobufCMessage *m, ProtobufCAllocator *allocator)
Convert a ProtobufCMessage to a string.
Definition: generate.c:498
#define STRUCT_MEMBER(member_type, struct_p, struct_offset)
Return a field from a message based on offset and type.
char * s
The string.
Definition: generate.c:32
static char * esc_str(char *src, int len, ProtobufCAllocator *allocator)
Escape string.
Definition: generate.c:105
A dynamic string struct.
Definition: generate.c:28
static void protobuf_c_text_to_string_internal(ReturnString *rs, int level, ProtobufCMessage *m, const ProtobufCMessageDescriptor *d, ProtobufCAllocator *allocator)
Internal function to back API function.
Definition: generate.c:176
static void rs_append(ReturnString *rs, int guess, ProtobufCAllocator *allocator, const char *format,...) __attribute__((format(printf
Append a string to the ReturnString.
Definition: generate.c:52
Internal utility header file.
int allocated
Size of allocated string.
Definition: generate.c:30
int malloc_err
Set to 1 when there's been a malloc error.
Definition: generate.c:29
#define PBC_ALLOC(size)
Allocate possibly using the pbc allocator.