/* silcbuffmt.c Author: Pekka Riikonen Copyright (C) 1997 - 2006 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ /* $Id$ */ #include "silc.h" /************************** Types and definitions ***************************/ /* Check that buffer has enough room to format data in it, if not allocate more. */ #define FORMAT_HAS_SPACE(s, b, req) \ do { \ if (silc_unlikely(!silc_buffer_senlarge(s, b, req))) \ goto fail; \ flen += req; \ } while(0) /* Check that there is data to be unformatted */ #define UNFORMAT_HAS_SPACE(b, req) \ do { \ if (silc_unlikely(req > silc_buffer_len(b))) \ goto fail; \ if (silc_unlikely((req + 1) <= 0)) \ goto fail; \ } while(0) /******************************* Formatting *********************************/ int silc_buffer_format(SilcBuffer dst, ...) { va_list ap; int ret; va_start(ap, dst); ret = silc_buffer_sformat_vp(NULL, dst, ap); va_end(ap); return ret; } int silc_buffer_format_vp(SilcBuffer dst, va_list ap) { return silc_buffer_sformat_vp(NULL, dst, ap); } int silc_buffer_sformat(SilcStack stack, SilcBuffer dst, ...) { va_list ap; int ret; va_start(ap, dst); ret = silc_buffer_sformat_vp(stack, dst, ap); va_end(ap); return ret; } int silc_buffer_sformat_vp(SilcStack stack, SilcBuffer dst, va_list ap) { SilcBufferParamType fmt; int flen = 0; SilcBool advance = FALSE; /* Parse the arguments by formatting type. */ while (1) { fmt = va_arg(ap, SilcBufferParamType); switch(fmt) { case SILC_PARAM_FUNC: { SilcBufferFormatFunc func = NULL; SilcBufferSFormatFunc funcs = NULL; void *val; void *context; int tmp_len; if (!stack) func = va_arg(ap, SilcBufferFormatFunc); else funcs = va_arg(ap, SilcBufferSFormatFunc); val = va_arg(ap, void *); context = va_arg(ap, void *); if (!stack) tmp_len = func(dst, val, context); else tmp_len = funcs(stack, dst, val, context); if (tmp_len < 0) goto fail; if (tmp_len) { silc_buffer_pull(dst, tmp_len); flen += tmp_len; } } break; case SILC_PARAM_UI8_STRING: case SILC_PARAM_UI16_STRING: case SILC_PARAM_UI32_STRING: case SILC_PARAM_UI8_STRING_ALLOC: case SILC_PARAM_UI16_STRING_ALLOC: case SILC_PARAM_UI32_STRING_ALLOC: { unsigned char *x = va_arg(ap, unsigned char *); SilcUInt32 tmp_len = x ? strlen(x) : 0; if (x && tmp_len) { FORMAT_HAS_SPACE(stack, dst, tmp_len); silc_buffer_put(dst, x, tmp_len); silc_buffer_pull(dst, tmp_len); } break; } case SILC_PARAM_UI8_NSTRING: case SILC_PARAM_UI16_NSTRING: case SILC_PARAM_UI32_NSTRING: case SILC_PARAM_UI_XNSTRING: case SILC_PARAM_DATA: case SILC_PARAM_UI8_NSTRING_ALLOC: case SILC_PARAM_UI16_NSTRING_ALLOC: case SILC_PARAM_UI32_NSTRING_ALLOC: case SILC_PARAM_UI_XNSTRING_ALLOC: case SILC_PARAM_DATA_ALLOC: { unsigned char *x = va_arg(ap, unsigned char *); SilcUInt32 tmp_len = va_arg(ap, SilcUInt32); if (x && tmp_len) { FORMAT_HAS_SPACE(stack, dst, tmp_len); silc_buffer_put(dst, x, tmp_len); silc_buffer_pull(dst, tmp_len); } break; } case SILC_PARAM_UI8_CHAR: { unsigned char x = (unsigned char)va_arg(ap, int); FORMAT_HAS_SPACE(stack, dst, 1); silc_buffer_put(dst, &x, 1); silc_buffer_pull(dst, 1); break; } case SILC_PARAM_UI16_SHORT: { unsigned char xf[2]; SilcUInt16 x = (SilcUInt16)va_arg(ap, int); FORMAT_HAS_SPACE(stack, dst, 2); SILC_PUT16_MSB(x, xf); silc_buffer_put(dst, xf, 2); silc_buffer_pull(dst, 2); break; } case SILC_PARAM_UI32_INT: { unsigned char xf[4]; SilcUInt32 x = va_arg(ap, SilcUInt32); FORMAT_HAS_SPACE(stack, dst, 4); SILC_PUT32_MSB(x, xf); silc_buffer_put(dst, xf, 4); silc_buffer_pull(dst, 4); break; } case SILC_PARAM_UI64_INT: { unsigned char xf[8]; SilcUInt64 x = va_arg(ap, SilcUInt64); FORMAT_HAS_SPACE(stack, dst, sizeof(SilcUInt64)); SILC_PUT64_MSB(x, xf); silc_buffer_put(dst, xf, sizeof(SilcUInt64)); silc_buffer_pull(dst, sizeof(SilcUInt64)); break; } case SILC_PARAM_SI8_CHAR: { char x = (char)va_arg(ap, int); FORMAT_HAS_SPACE(stack, dst, 1); silc_buffer_put(dst, &x, 1); silc_buffer_pull(dst, 1); break; } case SILC_PARAM_SI16_SHORT: { unsigned char xf[2]; SilcInt16 x = (SilcInt16)va_arg(ap, int); FORMAT_HAS_SPACE(stack, dst, 2); SILC_PUT16_MSB(x, xf); silc_buffer_put(dst, xf, 2); silc_buffer_pull(dst, 2); break; } case SILC_PARAM_SI32_INT: { unsigned char xf[4]; SilcInt32 x = va_arg(ap, SilcInt32); FORMAT_HAS_SPACE(stack, dst, 4); SILC_PUT32_MSB(x, xf); silc_buffer_put(dst, xf, 4); silc_buffer_pull(dst, 4); break; } case SILC_PARAM_SI64_INT: { unsigned char xf[8]; SilcInt64 x = va_arg(ap, SilcInt64); FORMAT_HAS_SPACE(stack, dst, sizeof(SilcInt64)); SILC_PUT64_MSB(x, xf); silc_buffer_put(dst, xf, sizeof(SilcInt64)); silc_buffer_pull(dst, sizeof(SilcInt64)); break; } case SILC_PARAM_BUFFER: case SILC_PARAM_BUFFER_ALLOC: { SilcBuffer x = va_arg(ap, SilcBuffer); unsigned char xf[4]; if (x && silc_buffer_len(x)) { FORMAT_HAS_SPACE(stack, dst, silc_buffer_len(x) + 4); SILC_PUT32_MSB(silc_buffer_len(x), xf); silc_buffer_put(dst, xf, 4); silc_buffer_pull(dst, 4); silc_buffer_put(dst, silc_buffer_data(x), silc_buffer_len(x)); silc_buffer_pull(dst, silc_buffer_len(x)); } } break; case SILC_PARAM_OFFSET: { int offst = va_arg(ap, int); if (!offst) break; if (offst > 1) { if (offst > silc_buffer_len(dst)) goto fail; silc_buffer_pull(dst, offst); flen += offst; } else { silc_buffer_push(dst, -(offst)); flen += -(offst); } break; } case SILC_PARAM_ADVANCE: advance = TRUE; break; case SILC_PARAM_END: goto ok; break; default: SILC_LOG_DEBUG(("Bad buffer formatting type `%d'. Could not " "format the data.", fmt)); goto fail; break; } } fail: SILC_LOG_DEBUG(("Error occured while formatting data")); if (!advance) silc_buffer_push(dst, flen); return -1; ok: /* Push the buffer back to where it belongs. */ if (!advance) silc_buffer_push(dst, flen); return flen; } /****************************** Unformatting ********************************/ int silc_buffer_unformat(SilcBuffer src, ...) { va_list ap; int ret; va_start(ap, src); ret = silc_buffer_sunformat_vp(NULL, src, ap); va_end(ap); return ret; } int silc_buffer_unformat_vp(SilcBuffer src, va_list ap) { return silc_buffer_sunformat_vp(NULL, src, ap); } int silc_buffer_sunformat(SilcStack stack, SilcBuffer src, ...) { va_list ap; int ret; va_start(ap, src); ret = silc_buffer_sunformat_vp(stack, src, ap); va_end(ap); return ret; } int silc_buffer_sunformat_vp(SilcStack stack, SilcBuffer src, va_list ap) { SilcBufferParamType fmt; unsigned char *start_ptr = src->data; int len = 0; SilcBool advance = FALSE; /* Parse the arguments by formatting type. */ while(1) { fmt = va_arg(ap, SilcBufferParamType); switch(fmt) { case SILC_PARAM_FUNC: { SilcBufferUnformatFunc func = NULL; SilcBufferSUnformatFunc funcs = NULL; void **val; void *context; int tmp_len; if (!stack) func = va_arg(ap, SilcBufferUnformatFunc); else funcs = va_arg(ap, SilcBufferSUnformatFunc); val = va_arg(ap, void **); context = va_arg(ap, void *); if (!stack) tmp_len = func(src, val, context); else tmp_len = funcs(stack, src, val, context); if (tmp_len < 0) goto fail; if (tmp_len) { UNFORMAT_HAS_SPACE(src, tmp_len); silc_buffer_pull(src, tmp_len); } } case SILC_PARAM_UI_XNSTRING: case SILC_PARAM_DATA: { unsigned char **x = va_arg(ap, unsigned char **); SilcUInt32 len2 = va_arg(ap, SilcUInt32); UNFORMAT_HAS_SPACE(src, len2); if (silc_likely(len2 && x)) *x = src->data; silc_buffer_pull(src, len2); break; } case SILC_PARAM_UI_XNSTRING_ALLOC: case SILC_PARAM_DATA_ALLOC: { unsigned char **x = va_arg(ap, unsigned char **); SilcUInt32 len2 = va_arg(ap, SilcUInt32); UNFORMAT_HAS_SPACE(src, len2); if (silc_likely(len2 && x)) { *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char)); memcpy(*x, src->data, len2); } silc_buffer_pull(src, len2); break; } case SILC_PARAM_UI8_CHAR: { unsigned char *x = va_arg(ap, unsigned char *); UNFORMAT_HAS_SPACE(src, 1); if (silc_likely(x)) *x = src->data[0]; silc_buffer_pull(src, 1); break; } case SILC_PARAM_UI16_SHORT: { SilcUInt16 *x = va_arg(ap, SilcUInt16 *); UNFORMAT_HAS_SPACE(src, 2); if (silc_likely(x)) SILC_GET16_MSB(*x, src->data); silc_buffer_pull(src, 2); break; } case SILC_PARAM_UI32_INT: { SilcUInt32 *x = va_arg(ap, SilcUInt32 *); UNFORMAT_HAS_SPACE(src, 4); if (silc_likely(x)) SILC_GET32_MSB(*x, src->data); silc_buffer_pull(src, 4); break; } case SILC_PARAM_UI64_INT: { SilcUInt64 *x = va_arg(ap, SilcUInt64 *); UNFORMAT_HAS_SPACE(src, sizeof(SilcUInt64)); if (silc_likely(x)) SILC_GET64_MSB(*x, src->data); silc_buffer_pull(src, sizeof(SilcUInt64)); break; } case SILC_PARAM_SI8_CHAR: { char *x = va_arg(ap, char *); UNFORMAT_HAS_SPACE(src, 1); if (silc_likely(x)) *x = src->data[0]; silc_buffer_pull(src, 1); break; } case SILC_PARAM_SI16_SHORT: { SilcInt16 *x = va_arg(ap, SilcInt16 *); UNFORMAT_HAS_SPACE(src, 2); if (silc_likely(x)) SILC_GET16_MSB(*x, src->data); silc_buffer_pull(src, 2); break; } case SILC_PARAM_SI32_INT: { SilcInt32 *x = va_arg(ap, SilcInt32 *); UNFORMAT_HAS_SPACE(src, 4); if (silc_likely(x)) SILC_GET32_MSB(*x, src->data); silc_buffer_pull(src, 4); break; } case SILC_PARAM_SI64_INT: { SilcInt64 *x = va_arg(ap, SilcInt64 *); UNFORMAT_HAS_SPACE(src, sizeof(SilcInt64)); if (silc_likely(x)) SILC_GET64_MSB(*x, src->data); silc_buffer_pull(src, sizeof(SilcInt64)); break; } case SILC_PARAM_UI8_STRING: { SilcUInt8 len2; unsigned char **x = va_arg(ap, unsigned char **); UNFORMAT_HAS_SPACE(src, 1); len2 = (SilcUInt8)src->data[0]; silc_buffer_pull(src, 1); UNFORMAT_HAS_SPACE(src, len2); if (silc_likely(x)) *x = src->data; silc_buffer_pull(src, len2); break; } case SILC_PARAM_UI16_STRING: { SilcUInt16 len2; unsigned char **x = va_arg(ap, unsigned char **); UNFORMAT_HAS_SPACE(src, 2); SILC_GET16_MSB(len2, src->data); silc_buffer_pull(src, 2); UNFORMAT_HAS_SPACE(src, len2); if (silc_likely(x)) *x = src->data; silc_buffer_pull(src, len2); break; } case SILC_PARAM_UI8_STRING_ALLOC: { SilcUInt8 len2; unsigned char **x = va_arg(ap, unsigned char **); UNFORMAT_HAS_SPACE(src, 1); len2 = (SilcUInt8)src->data[0]; silc_buffer_pull(src, 1); UNFORMAT_HAS_SPACE(src, len2); if (silc_likely(x && len2)) { *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char)); memcpy(*x, src->data, len2); } silc_buffer_pull(src, len2); break; } case SILC_PARAM_UI16_STRING_ALLOC: { SilcUInt16 len2; unsigned char **x = va_arg(ap, unsigned char **); UNFORMAT_HAS_SPACE(src, 2); SILC_GET16_MSB(len2, src->data); silc_buffer_pull(src, 2); UNFORMAT_HAS_SPACE(src, len2); if (silc_likely(x && len2)) { *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char)); memcpy(*x, src->data, len2); } silc_buffer_pull(src, len2); break; } case SILC_PARAM_UI32_STRING: { SilcUInt32 len2; unsigned char **x = va_arg(ap, unsigned char **); UNFORMAT_HAS_SPACE(src, 4); SILC_GET32_MSB(len2, src->data); silc_buffer_pull(src, 4); UNFORMAT_HAS_SPACE(src, len2); if (silc_likely(x)) *x = src->data; silc_buffer_pull(src, len2); break; } case SILC_PARAM_UI32_STRING_ALLOC: { SilcUInt32 len2; unsigned char **x = va_arg(ap, unsigned char **); UNFORMAT_HAS_SPACE(src, 4); SILC_GET32_MSB(len2, src->data); silc_buffer_pull(src, 4); UNFORMAT_HAS_SPACE(src, len2); if (silc_likely(x && len2)) { *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char)); memcpy(*x, src->data, len2); } silc_buffer_pull(src, len2); break; } case SILC_PARAM_UI8_NSTRING: { SilcUInt8 len2; unsigned char **x = va_arg(ap, unsigned char **); SilcUInt8 *len3 = va_arg(ap, SilcUInt8 *); UNFORMAT_HAS_SPACE(src, 1); len2 = (SilcUInt8)src->data[0]; silc_buffer_pull(src, 1); UNFORMAT_HAS_SPACE(src, len2); if (len3) *len3 = len2; if (x) *x = src->data; silc_buffer_pull(src, len2); break; } case SILC_PARAM_UI16_NSTRING: { SilcUInt16 len2; unsigned char **x = va_arg(ap, unsigned char **); SilcUInt16 *len3 = va_arg(ap, SilcUInt16 *); UNFORMAT_HAS_SPACE(src, 2); SILC_GET16_MSB(len2, src->data); silc_buffer_pull(src, 2); UNFORMAT_HAS_SPACE(src, len2); if (len3) *len3 = len2; if (x) *x = src->data; silc_buffer_pull(src, len2); break; } case SILC_PARAM_UI8_NSTRING_ALLOC: { SilcUInt8 len2; unsigned char **x = va_arg(ap, unsigned char **); SilcUInt8 *len3 = va_arg(ap, SilcUInt8 *); UNFORMAT_HAS_SPACE(src, 1); len2 = (SilcUInt8)src->data[0]; silc_buffer_pull(src, 1); UNFORMAT_HAS_SPACE(src, len2); if (len3) *len3 = len2; if (x && len2) { *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char)); memcpy(*x, src->data, len2); } silc_buffer_pull(src, len2); break; } case SILC_PARAM_UI16_NSTRING_ALLOC: { SilcUInt16 len2; unsigned char **x = va_arg(ap, unsigned char **); SilcUInt16 *len3 = va_arg(ap, SilcUInt16 *); UNFORMAT_HAS_SPACE(src, 2); SILC_GET16_MSB(len2, src->data); silc_buffer_pull(src, 2); UNFORMAT_HAS_SPACE(src, len2); if (len3) *len3 = len2; if (x && len2) { *x = silc_scalloc(stack, len2 + 1, sizeof(unsigned char)); memcpy(*x, src->data, len2); } silc_buffer_pull(src, len2); break; } case SILC_PARAM_UI32_NSTRING: { SilcUInt32 len2; unsigned char **x = va_arg(ap, unsigned char **); SilcUInt32 *len3 = va_arg(ap, SilcUInt32 *); UNFORMAT_HAS_SPACE(src, 4); SILC_GET32_MSB(len2, src->data); silc_buffer_pull(src, 4); UNFORMAT_HAS_SPACE(src, len2); if (len3) *len3 = len2; if (x) *x = src->data; silc_buffer_pull(src, len2); break; } case SILC_PARAM_BUFFER: { SilcBuffer x = va_arg(ap, SilcBuffer); SilcUInt32 len2; UNFORMAT_HAS_SPACE(src, 4); SILC_GET32_MSB(len2, src->data); silc_buffer_pull(src, 4); UNFORMAT_HAS_SPACE(src, len2); silc_buffer_set(x, src->data, len2); silc_buffer_pull(src, len2); } break; case SILC_PARAM_BUFFER_ALLOC: { SilcBuffer x = va_arg(ap, SilcBuffer); SilcUInt32 len2; UNFORMAT_HAS_SPACE(src, 4); SILC_GET32_MSB(len2, src->data); silc_buffer_pull(src, 4); UNFORMAT_HAS_SPACE(src, len2); silc_buffer_sformat(stack, x, SILC_STR_DATA(src->data, len2), SILC_STR_END); silc_buffer_pull(src, len2); } break; case SILC_PARAM_OFFSET: { int offst = va_arg(ap, int); if (!offst) break; if (offst > 1) { UNFORMAT_HAS_SPACE(src, offst); silc_buffer_pull(src, offst); } else { silc_buffer_push(src, -(offst)); } break; } case SILC_PARAM_ADVANCE: advance = TRUE; break; case SILC_PARAM_END: goto ok; break; default: SILC_LOG_DEBUG(("Bad buffer formatting type `%d'. Could not " "format the data.", fmt)); goto fail; break; } } fail: SILC_LOG_DEBUG(("Error occured while unformatting buffer, type %d", fmt)); len = src->data - start_ptr; silc_buffer_push(src, len); return -1; ok: /* Push the buffer back to the start. */ if (!advance) { len = src->data - start_ptr; silc_buffer_push(src, len); } return len; } /**************************** Utility functions *****************************/ /* Formats strings into a buffer */ int silc_buffer_strformat(SilcBuffer dst, ...) { int len = silc_buffer_truelen(dst); int hlen = silc_buffer_headlen(dst); va_list va; va_start(va, dst); /* Parse the arguments by formatting type. */ while(1) { char *string = va_arg(va, char *); unsigned char *d; SilcInt32 slen; if (!string) continue; if (string == (char *)SILC_PARAM_END) goto ok; slen = strlen(string); d = silc_realloc(dst->head, sizeof(*dst->head) * (slen + len + 1)); if (silc_unlikely(!d)) return -1; dst->head = d; memcpy(dst->head + len, string, slen); len += slen; dst->head[len] = '\0'; } SILC_LOG_DEBUG(("Error occured while formatting buffer")); va_end(va); return -1; ok: dst->end = dst->head + len; dst->data = dst->head + hlen; dst->tail = dst->end; va_end(va); return len; } /* Formats strings into a buffer. Allocates memory from SilcStack. */ int silc_buffer_sstrformat(SilcStack stack, SilcBuffer dst, ...) { int len = silc_buffer_truelen(dst); int hlen = silc_buffer_headlen(dst); va_list va; va_start(va, dst); /* Parse the arguments by formatting type. */ while(1) { char *string = va_arg(va, char *); unsigned char *d; SilcInt32 slen; if (!string) continue; if (string == (char *)SILC_PARAM_END) goto ok; slen = strlen(string); d = silc_srealloc_ua(stack, len + 1, dst->head, sizeof(*dst->head) * (slen + len + 1)); if (silc_unlikely(!d)) return -1; dst->head = d; memcpy(dst->head + len, string, slen); len += slen; dst->head[len] = '\0'; } SILC_LOG_DEBUG(("Error occured while formatting buffer")); va_end(va); return -1; ok: dst->end = dst->head + len; dst->data = dst->head + hlen; dst->tail = dst->end; va_end(va); return len; }