diff options
author | George Hazan <george.hazan@gmail.com> | 2024-12-15 12:53:15 +0300 |
---|---|---|
committer | George Hazan <george.hazan@gmail.com> | 2024-12-15 12:53:15 +0300 |
commit | c787b1dd7c74b639f7e7e373cecf06d131b2fe3b (patch) | |
tree | 249a298ab8088ed20744736651407832f98c11d9 /protocols/Steam/src/protobuf-c/protobuf-c-text.c | |
parent | 4d6ff1f6d3a818998364004e032d640675918ce7 (diff) |
Steam: protobuf-c printer added for debugging purposes
Diffstat (limited to 'protocols/Steam/src/protobuf-c/protobuf-c-text.c')
-rw-r--r-- | protocols/Steam/src/protobuf-c/protobuf-c-text.c | 501 |
1 files changed, 501 insertions, 0 deletions
diff --git a/protocols/Steam/src/protobuf-c/protobuf-c-text.c b/protocols/Steam/src/protobuf-c/protobuf-c-text.c new file mode 100644 index 0000000000..6cbc9b46e7 --- /dev/null +++ b/protocols/Steam/src/protobuf-c/protobuf-c-text.c @@ -0,0 +1,501 @@ +/** \file + * Routines to generate text format protobufs. + * + * This file contains the internal support functions as well as the + * exported functions which are used to generate text format protobufs + * from C protobuf data types. + * + * \author Kevin Lyda <kevin@ie.suberic.net> + * \date March 2014 + */ + +#include <sys/types.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "protobuf-c.h" +#include "protobuf-c-text.h" +#include "protobuf-c-util.h" + +/** A dynamic string struct. + * + * Used to track additions to a growing string and memory allocation + * errors that occur in processing + */ +typedef struct _ReturnString { + int malloc_err; /**< Set to 1 when there's been a malloc error. */ + int allocated; /**< Size of allocated string. */ + int pos; /**< Current end of the string. */ + char *s; /**< The string. */ +} ReturnString; + +/** Append a string to the ReturnString. + * + * Append the string built from \c format and its args to the \c rs + * string. Note that \c malloc_err is checked and if it's true, + * this function won't do anything. + * + * \param[in,out] rs The string to append to. + * \param[in] guess A guess at the number of chars being added. + * \param[in] allocator allocator functions. + * \param[in] format Printf-style format string. + * \param[in] ... Variable number of args for \c format. + */ + +static void +rs_append(ReturnString *rs, int guess, + ProtobufCAllocator *allocator, + const char *format, ...) +{ + va_list args; + int added; + + if (rs->malloc_err) { + return; + } + + if (rs->allocated - rs->pos < guess * 2) { + char *tmp; + + tmp = PBC_ALLOC(rs->allocated + guess * 2); + if (!tmp) { + PBC_FREE(rs->s); + rs->s = NULL; + rs->malloc_err = 1; + return; + } + memcpy(tmp, rs->s, rs->allocated); + PBC_FREE(rs->s); + rs->s = tmp; + rs->allocated += guess * 2; + } + va_start(args, format); + added = vsnprintf(rs->s + rs->pos, rs->allocated - rs->pos, format, args); + va_end(args); + rs->pos += added; + return; +} + +/** @} */ /* End of utility group. */ + + +/** \defgroup generate Functions to generate text format proto bufs + * \ingroup internal + * @{ + */ + +/** Escape string. + * + * Add escape characters to strings for problematic characters. + * + * \param[in] src The unescaped string to process. + * \param[in] len Length of \c src. Note that \c src might have ASCII + * \c NULs so strlen() isn't good enough here. + * \param[in] allocator allocator functions. + * \return The fully escaped string, or \c NULL if there has been an + * allocation error. + */ +static char * +esc_str(char *src, int len, ProtobufCAllocator *allocator) +{ + int i, escapes = 0, dst_len = 0; + unsigned char *dst; + + for (i = 0; i < len; i++) { + if (!isprint(src[i])) { + escapes++; + } + } + dst = PBC_ALLOC((escapes * 4) + ((len - escapes) * 2) + 1); + if (!dst) { + return NULL; + } + + for (i = 0; i < len; i++) { + switch (src[i]) { + /* Special cases. */ + case '\'': + dst[dst_len++] = '\\'; + dst[dst_len++] = '\''; + break; + case '\"': + dst[dst_len++] = '\\'; + dst[dst_len++] = '\"'; + break; + case '\\': + dst[dst_len++] = '\\'; + dst[dst_len++] = '\\'; + break; + case '\n': + dst[dst_len++] = '\\'; + dst[dst_len++] = 'n'; + break; + case '\r': + dst[dst_len++] = '\\'; + dst[dst_len++] = 'r'; + break; + case '\t': + dst[dst_len++] = '\\'; + dst[dst_len++] = 't'; + break; + + /* Escape with octal if !isprint. */ + default: + if (!isprint(src[i])) { + dst_len += sprintf(dst + dst_len, "\\%03o", src[i]); + } else { + dst[dst_len++] = src[i]; + } + break; + } + } + dst[dst_len] = '\0'; + + return dst; +} + +/** Internal function to back API function. + * + * Has a few extra params to better enable recursion. This function gets + * called for each nested message as the \c ProtobufCMessage struct is + * traversed. + * + * \param[in,out] rs The string being built up for the text format protobuf. + * \param[in] level Indent level - increments in 2's. + * \param[in] m The \c ProtobufCMessage being serialised. + * \param[in] d The descriptor for the \c ProtobufCMessage. + * \param[in] allocator allocator functions. + */ +static void +protobuf_c_text_to_string_internal(ReturnString *rs, + int level, + ProtobufCMessage *m, + const ProtobufCMessageDescriptor *d, + ProtobufCAllocator *allocator) +{ + int i; + size_t j, quantifier_offset; + double float_var; + const ProtobufCFieldDescriptor *f; + ProtobufCEnumDescriptor *enumd; + const ProtobufCEnumValue *enumv; + + f = d->fields; + for (i = 0; i < d->n_fields; i++) { + if (rs->malloc_err) { + /* If there's been a malloc error, go die. */ + return; + } + + /* Decide if something needs to be done for this field. */ + switch (f[i].label) { + case PROTOBUF_C_LABEL_OPTIONAL: + if (f[i].type == PROTOBUF_C_TYPE_STRING) { + if (!STRUCT_MEMBER(char *, m, f[i].offset) + || (STRUCT_MEMBER(char *, m, f[i].offset) + == (char *)f[i].default_value)) { + continue; + } + } else if (f[i].type == PROTOBUF_C_TYPE_MESSAGE) { + if (!STRUCT_MEMBER(char *, m, f[i].offset)) { + continue; + } + } else { + if (!STRUCT_MEMBER(protobuf_c_boolean, m, f[i].quantifier_offset)) { + continue; + } + } + break; + case PROTOBUF_C_LABEL_REPEATED: + if (!STRUCT_MEMBER(size_t, m, f[i].quantifier_offset)) { + continue; + } + break; + } + + quantifier_offset = STRUCT_MEMBER(size_t, m, f[i].quantifier_offset); + /* Field exists and has data, dump it. */ + switch (f[i].type) { + case PROTOBUF_C_TYPE_INT32: + case PROTOBUF_C_TYPE_UINT32: + case PROTOBUF_C_TYPE_FIXED32: + if (f[i].label == PROTOBUF_C_LABEL_REPEATED) { + for (j = 0; j < quantifier_offset; j++) { + rs_append(rs, level + strlen(f[i].name) + 20, + allocator, + "%*s%s: %u\n", + level, "", f[i].name, + STRUCT_MEMBER(uint32_t *, m, f[i].offset)[j]); + } + } else { + rs_append(rs, level + strlen(f[i].name) + 20, + allocator, + "%*s%s: %u\n", + level, "", f[i].name, + STRUCT_MEMBER(uint32_t, m, f[i].offset)); + } + break; + case PROTOBUF_C_TYPE_SINT32: + case PROTOBUF_C_TYPE_SFIXED32: + if (f[i].label == PROTOBUF_C_LABEL_REPEATED) { + for (j = 0; j < quantifier_offset; j++) { + rs_append(rs, level + strlen(f[i].name) + 20, + allocator, + "%*s%s: %d\n", + level, "", f[i].name, + STRUCT_MEMBER(int32_t *, m, f[i].offset)[j]); + } + } else { + rs_append(rs, level + strlen(f[i].name) + 20, + allocator, + "%*s%s: %d\n", + level, "", f[i].name, + STRUCT_MEMBER(int32_t, m, f[i].offset)); + } + break; + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_UINT64: + case PROTOBUF_C_TYPE_FIXED64: + if (f[i].label == PROTOBUF_C_LABEL_REPEATED) { + for (j = 0; j < quantifier_offset; j++) { + rs_append(rs, level + strlen(f[i].name) + 20, + allocator, + "%*s%s: %lu\n", + level, "", f[i].name, + STRUCT_MEMBER(uint64_t *, m, f[i].offset)[j]); + } + } else { + rs_append(rs, level + strlen(f[i].name) + 20, + allocator, + "%*s%s: %lu\n", + level, "", f[i].name, + STRUCT_MEMBER(uint64_t, m, f[i].offset)); + } + break; + case PROTOBUF_C_TYPE_SINT64: + case PROTOBUF_C_TYPE_SFIXED64: + if (f[i].label == PROTOBUF_C_LABEL_REPEATED) { + for (j = 0; j < quantifier_offset; j++) { + rs_append(rs, level + strlen(f[i].name) + 20, + allocator, + "%*s%s: %ld\n", + level, "", f[i].name, + STRUCT_MEMBER(int64_t *, m, f[i].offset)[j]); + } + } else { + rs_append(rs, level + strlen(f[i].name) + 20, + allocator, + "%*s%s: %ld\n", + level, "", f[i].name, + STRUCT_MEMBER(int64_t, m, f[i].offset)); + } + break; + case PROTOBUF_C_TYPE_FLOAT: + if (f[i].label == PROTOBUF_C_LABEL_REPEATED) { + for (j = 0; j < quantifier_offset; j++) { + float_var = STRUCT_MEMBER(float *, m, f[i].offset)[j]; + rs_append(rs, level + strlen(f[i].name) + 20, + allocator, + "%*s%s: %g\n", + level, "", f[i].name, + float_var); + } + } else { + float_var = STRUCT_MEMBER(float, m, f[i].offset); + rs_append(rs, level + strlen(f[i].name) + 20, + allocator, + "%*s%s: %g\n", + level, "", f[i].name, + float_var); + } + break; + case PROTOBUF_C_TYPE_DOUBLE: + if (f[i].label == PROTOBUF_C_LABEL_REPEATED) { + for (j = 0; j < quantifier_offset; j++) { + rs_append(rs, level + strlen(f[i].name) + 20, + allocator, + "%*s%s: %g\n", + level, "", f[i].name, + STRUCT_MEMBER(double *, m, f[i].offset)[j]); + } + } else { + rs_append(rs, level + strlen(f[i].name) + 20, + allocator, + "%*s%s: %g\n", + level, "", f[i].name, + STRUCT_MEMBER(double, m, f[i].offset)); + } + break; + case PROTOBUF_C_TYPE_BOOL: + if (f[i].label == PROTOBUF_C_LABEL_REPEATED) { + for (j = 0; j < quantifier_offset; j++) { + rs_append(rs, level + strlen(f[i].name) + 20, + allocator, + "%*s%s: %s\n", + level, "", f[i].name, + STRUCT_MEMBER(protobuf_c_boolean *, m, f[i].offset)[j]? + "true": "false"); + } + } else { + rs_append(rs, level + strlen(f[i].name) + 20, + allocator, + "%*s%s: %s\n", + level, "", f[i].name, + STRUCT_MEMBER(protobuf_c_boolean, m, f[i].offset)? + "true": "false"); + } + break; + case PROTOBUF_C_TYPE_ENUM: + enumd = (ProtobufCEnumDescriptor *)f[i].descriptor; + if (f[i].label == PROTOBUF_C_LABEL_REPEATED) { + for (j = 0; j < quantifier_offset; j++) { + enumv = protobuf_c_enum_descriptor_get_value( + enumd, STRUCT_MEMBER(int *, m, f[i].offset)[j]); + rs_append(rs, level + strlen(f[i].name) + 20, + allocator, + "%*s%s: %s\n", + level, "", f[i].name, + enumv? enumv->name: "unknown"); + } + } else { + enumv = protobuf_c_enum_descriptor_get_value( + enumd, STRUCT_MEMBER(int, m, f[i].offset)); + rs_append(rs, level + strlen(f[i].name) + 20, + allocator, + "%*s%s: %s\n", + level, "", f[i].name, + enumv? enumv->name: "unknown"); + } + break; + case PROTOBUF_C_TYPE_STRING: + if (f[i].label == PROTOBUF_C_LABEL_REPEATED) { + for (j = 0; j < quantifier_offset; j++) { + unsigned char *escaped; + + escaped = esc_str( + STRUCT_MEMBER(unsigned char **, m, f[i].offset)[j], + strlen(STRUCT_MEMBER(unsigned char **, m, f[i].offset)[j]), + allocator); + if (!escaped) { + PBC_FREE(rs->s); + rs->s = NULL; + rs->malloc_err = 1; + return; + } + rs_append(rs, level + strlen(f[i].name) + strlen(escaped) + 10, + allocator, + "%*s%s: \"%s\"\n", level, "", f[i].name, escaped); + PBC_FREE(escaped); + } + } else { + unsigned char *escaped; + + escaped = esc_str(STRUCT_MEMBER(unsigned char *, m, f[i].offset), + strlen(STRUCT_MEMBER(unsigned char *, m, f[i].offset)), + allocator); + if (!escaped) { + PBC_FREE(rs->s); + rs->s = NULL; + rs->malloc_err = 1; + return; + } + rs_append(rs, level + strlen(f[i].name) + strlen(escaped) + 10, + allocator, + "%*s%s: \"%s\"\n", level, "", f[i].name, escaped); + PBC_FREE(escaped); + } + break; + case PROTOBUF_C_TYPE_BYTES: + if (f[i].label == PROTOBUF_C_LABEL_REPEATED) { + for (j = 0; j < quantifier_offset; j++) { + unsigned char *escaped; + + escaped = esc_str( + STRUCT_MEMBER(ProtobufCBinaryData *, m, f[i].offset)[j].data, + STRUCT_MEMBER(ProtobufCBinaryData *, m, f[i].offset)[j].len, + allocator); + if (!escaped) { + PBC_FREE(rs->s); + rs->s = NULL; + rs->malloc_err = 1; + return; + } + rs_append(rs, level + strlen(f[i].name) + strlen(escaped) + 10, + allocator, + "%*s%s: \"%s\"\n", level, "", f[i].name, escaped); + PBC_FREE(escaped); + } + } else { + unsigned char *escaped; + + escaped = esc_str( + STRUCT_MEMBER(ProtobufCBinaryData, m, f[i].offset).data, + STRUCT_MEMBER(ProtobufCBinaryData, m, f[i].offset).len, + allocator); + if (!escaped) { + PBC_FREE(rs->s); + rs->s = NULL; + rs->malloc_err = 1; + return; + } + rs_append(rs, level + strlen(f[i].name) + strlen(escaped) + 10, + allocator, + "%*s%s: \"%s\"\n", level, "", f[i].name, escaped); + PBC_FREE(escaped); + } + break; + + case PROTOBUF_C_TYPE_MESSAGE: + if (f[i].label == PROTOBUF_C_LABEL_REPEATED) { + for (j = 0; + j < STRUCT_MEMBER(size_t, m, f[i].quantifier_offset); + j++) { + rs_append(rs, level + strlen(f[i].name) + 10, + allocator, + "%*s%s {\n", level, "", f[i].name); + protobuf_c_text_to_string_internal(rs, level + 2, + STRUCT_MEMBER(ProtobufCMessage **, m, f[i].offset)[j], + (ProtobufCMessageDescriptor *)f[i].descriptor, + allocator); + rs_append(rs, level + 10, + allocator, + "%*s}\n", level, ""); + } + } else { + rs_append(rs, level + strlen(f[i].name) + 10, + allocator, + "%*s%s {\n", level, "", f[i].name); + protobuf_c_text_to_string_internal(rs, level + 2, + STRUCT_MEMBER(ProtobufCMessage *, m, f[i].offset), + (ProtobufCMessageDescriptor *)f[i].descriptor, + allocator); + rs_append(rs, level + 10, + allocator, + "%*s}\n", level, ""); + } + break; + default: + PBC_FREE(rs->s); + rs->s = NULL; + return; + break; + } + + } +} + +/** @} */ /* End of generate group. */ + +/* See .h file for API docs. */ + +char * +protobuf_c_text_to_string(ProtobufCMessage *m, + ProtobufCAllocator *allocator) +{ + ReturnString rs = { 0, 0, 0, NULL }; + + protobuf_c_text_to_string_internal(&rs, 0, m, m->descriptor, allocator); + + return rs.s; +} |