/*- * Copyright (c) 2015--2018 Taylor R. Campbell * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include "pb_dump.h" #define PB_ABI_VERSION 0x00000000 static int pb_dump_by_hdr(FILE *, unsigned, const struct pb_msg_hdr *); static int pb_dump_field(FILE *, unsigned, const struct pb_field *, const unsigned char *); static int pb_dump_field_value(FILE *, unsigned, const struct pb_field *, const unsigned char *); int pb_dump(FILE *out, struct pb_msg msg) { const struct pb_msg_hdr *const msg_hdr = (const struct pb_msg_hdr *)msg.pbm_ptr; if (msg_hdr->pbmh_msgdesc != msg.pbm_msgdesc) /* XXX pb_bug */ return EINVAL; if (!pb_abi_compatible(msg.pbm_msgdesc->pbmd_abi_version, PB_ABI_VERSION)) /* XXX pb_bug */ return EINVAL; return pb_dump_by_hdr(out, 0, msg_hdr); } static int pb_dump_by_hdr(FILE *out, unsigned indent, const struct pb_msg_hdr *msg_hdr) { const unsigned char *const addr = (const void *)msg_hdr; const struct pb_msgdesc *const msgdesc = msg_hdr->pbmh_msgdesc; size_t i; int error; for (i = 0; i < msgdesc->pbmd_nfields; i++) { error = pb_dump_field(out, indent, &msgdesc->pbmd_fields[i], addr); if (error) return error; } return 0; } static int pb_dump_field(FILE *out, unsigned indent, const struct pb_field *field, const unsigned char *addr) { int error; switch (field->pbf_quant) { case PBQ_REQUIRED: return pb_dump_field_value(out, indent, field, (addr + field->pbf_qu.required.offset)); case PBQ_OPTIONAL: if (*(const bool *)(addr + field->pbf_qu.optional.present_offset)) return pb_dump_field_value(out, indent, field, (addr + field->pbf_qu.optional.value_offset)); return 0; case PBQ_REPEATED: { const struct pb_repeated *const repeated = (const struct pb_repeated *)(addr + field->pbf_qu.repeated.hdr_offset); const unsigned char *const ptr = /* XXX Sketchy pointer cast. */ *(const void *const *)(addr + field->pbf_qu.repeated.ptr_offset); const size_t elemsize = pb_type_size(&field->pbf_type); size_t i; /* XXX Handle packed repeated fields. */ for (i = 0; i < pb_repeated_count(repeated); i++) { error = pb_dump_field_value(out, indent, field, (ptr + (i * elemsize))); if (error) return error; } return 0; } default: pb_bug("field %s: invalid quantifier: %d", field->pbf_name, (int)field->pbf_quant); } } static int pb_dump_field_value(FILE *out, unsigned indent, const struct pb_field *field, const unsigned char *v) { int error; if (fprintf(out, "%*s%s", indent, "", field->pbf_name) < 0) return errno; switch (field->pbf_type.pbt_type) { case PB_TYPE_BOOL: if (fprintf(out, ": %s\n", *(const bool *)v? "true" : "false") < 0) return errno; return 0; case PB_TYPE_UINT32: case PB_TYPE_FIXED32: if (fprintf(out, ": %"PRIu32"\n", *(const uint32_t *)v) < 0) return errno; return 0; case PB_TYPE_UINT64: case PB_TYPE_FIXED64: if (fprintf(out, ": %"PRIu64"\n", *(const uint64_t *)v) < 0) return errno; return 0; case PB_TYPE_INT32: case PB_TYPE_SINT32: case PB_TYPE_SFIXED32: if (fprintf(out, ": %"PRId32"\n", *(const int32_t *)v) < 0) return errno; return 0; case PB_TYPE_INT64: case PB_TYPE_SINT64: case PB_TYPE_SFIXED64: if (fprintf(out, ": %"PRId64"\n", *(const int64_t *)v) < 0) return errno; return 0; case PB_TYPE_ENUM: { int32_t number = *(const int32_t *)v; const struct pb_enumeration *const enumeration = field->pbf_type.pbt_u.enumerated.enumeration; const struct pb_enumerand *const enumerands = enumeration->pben_enumerands; size_t i, start = 0, end = enumeration->pben_nenumerands; while (start < end) { i = (start + ((end - start) / 2)); if (number < enumerands[i].pbed_number) end = i; else if (number > enumerands[i].pbed_number) start = (i + 1); else break; } if (start < end) { if (fprintf(out, ": %s\n", enumerands[i].pbed_name) < 0) return errno; } else { if (fprintf(out, ": %"PRId32"\n", number) < 0) return errno; } return 0; } case PB_TYPE_FLOAT: if (fprintf(out, ": %a\n", (double)*(const float *)v) < 0) return errno; return 0; case PB_TYPE_DOUBLE: if (fprintf(out, ": %a\n", *(const float *)v) < 0) return errno; return 0; case PB_TYPE_BYTES: { const struct pb_bytes *const bytes = (const struct pb_bytes *)v; const uint8_t *p; size_t i, n; if (fprintf(out, ": 0x'") < 0) return errno; p = pb_bytes_ptr(bytes, &n); for (i = 0; i < n; i++) { if (fprintf(out, "%02hhx", p[i]) < 0) return errno; } if (fprintf(out, "'\n") < 0) return errno; return 0; } case PB_TYPE_STRING: { const struct pb_string *const string = (const struct pb_string *)v; const char *const p = pb_string_ptr(string); const size_t len = pb_string_len(string); size_t i; if (fprintf(out, ": \"") < 0) return errno; for (i = 0; i < len; i++) { const uint8_t c = (uint8_t)p[i]; char esc = '\0'; switch (c) { case '\n': esc = 'n'; break; case '\r': esc = 'r'; break; case '\t': esc = 't'; break; case '\"': esc = '"'; break; case '\'': esc = '\''; break; case '\\': esc = '\\'; break; default: break; } if (esc != '\0') { if (fprintf(out, "\\%c", esc) < 0) return errno; } else if (c < 32 || 127 < c) { if (fprintf(out, "\\%03o", c) < 0) return errno; } else { if (fprintf(out, "%c", c) < 0) return errno; } } if (fprintf(out, "\"\n") < 0) return errno; return 0; } case PB_TYPE_MSG: { const struct pb_msg_hdr *const msg_hdr = (const struct pb_msg_hdr *)v; if (fprintf(out, " {\n") < 0) return errno; error = pb_dump_by_hdr(out, indent + 2, msg_hdr); if (error) return error; if (fprintf(out, "%*s}\n", indent, "") < 0) return errno; return 0; } default: pb_bug("field %s: invalid type: %d", field->pbf_name, field->pbf_type.pbt_type); } }