/* This file is part of vmod-binlog Copyright (C) 2013-2018 Sergey Poznyakoff Vmod-binlog 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; either version 3, or (at your option) any later version. Vmod-binlog 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. You should have received a copy of the GNU General Public License along with vmod-binlog. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include "pack.h" /* Data format specification is a simplified form of Perl pack() template. It consists of a series of template letters optionally followed by numeric repeat count (which may be enclosed in square brackets). The valid template letters are: Z A null-terminated (ASCIZ) string of at most N-1 characters, will be null padded. This letter must be followed by repeat count. c A signed char (8-bit) value. s A signed short (16-bit) value. S An unsigned short value. l A signed long (32-bit) value. L An unsigned long value. q A signed quad (64-bit) value. Q An unsigned quad value. i A signed integer value. I A unsigned integer value. n An unsigned short (16-bit) in "network" (big-endian) order. N An unsigned long (32-bit) in "network" (big-endian) order. v An unsigned short (16-bit) in "VAX" (little-endian) order. V An unsigned long (32-bit) in "VAX" (little-endian) order. f A single-precision float in native format. d A double-precision float in native format. x A null byte (a.k.a ASCII NUL, "\000", chr(0)) X Back up a byte. @ Null-fill or truncate to absolute position, counted from the current position. . Null-fill or truncate to absolute position specified by the repeat count. */ #define F_REP 0x01 struct packspec { int ch; int flags; void (*packer)(struct packenv *, int); void (*unpacker)(struct packenv *, int); }; struct packinst { struct packinst *next; struct packspec *spec; int rep; int cur; }; static char * packenv_get(struct packenv *env) { if (env->argi == env->argc) return NULL; return env->argv[env->argi++]; } static void printunum(FILE *fp, uintmax_t num) { char numbuf[512]; char *p = numbuf + sizeof(numbuf); *--p = 0; do *--p = num % 10 + '0'; while (num /= 10); fputs(p, fp); } static void printsnum(FILE *fp, intmax_t num) { if (num < 0) { fputc('-', fp); num = - num; } printunum(fp, num); } static int getunum(char *s, uintmax_t maxval, uintmax_t *retval) { uintmax_t x = 0; for (; *s && isspace(*s); s++); for (; *s; s++) { if (!isdigit(*s)) { errno = EINVAL; return -1; } x = x*10 + *s - '0'; } if (x > maxval) { errno = ERANGE; return -1; } *retval = x; return 0; } static int getsnum(char *s, intmax_t minval, intmax_t maxval, intmax_t *retval) { uintmax_t x = 0; int neg = 0; for (; *s && isspace(*s); s++); if (*s == '-') { neg = 1; ++s; } for (; *s; s++) { if (!isdigit(*s)) { errno = EINVAL; return -1; } x = x*10 + *s - '0'; } if (neg) { if (x > -minval) { errno = ERANGE; return -1; } *retval = -x; } else if (x > maxval) { errno = ERANGE; return -1; } else *retval = x; return 0; } #define GETSARG(env, type, minv, maxv) do { \ if ((env)->buf_base) { \ char *arg = packenv_get(env); \ if (arg) { \ intmax_t x; \ if (getsnum(arg, minv, maxv, &x) == 0) { \ type v = (type) x; \ memcpy(env->buf_base + env->buf_pos, \ &v, sizeof(v)); \ } else \ packerror("error converting %s to %s: %s", \ arg, #type, strerror(errno)); \ } else \ packerror("out of arguments"); \ } \ } while(0) #define GETUARG(env, type, maxv) do { \ if ((env)->buf_base) { \ char *arg = packenv_get(env); \ if (arg) { \ uintmax_t x; \ if (getunum(arg, maxv, &x) == 0) { \ type v = (type) x; \ memcpy(env->buf_base + env->buf_pos, \ &v, sizeof(v)); \ } else \ packerror("error converting %s to %s: %s", \ arg, #type, strerror(errno)); \ } else \ packerror("out of arguments"); \ } \ } while(0) static void Z_packer(struct packenv *env, int rep) { if (env->buf_base) { char *arg = packenv_get(env); memset(env->buf_base + env->buf_pos, 0, rep); if (arg) { size_t len = strlen(arg); if (len > rep - 1) len = rep - 1; memcpy(env->buf_base + env->buf_pos, arg, len); } else packerror("out of arguments"); } env->buf_pos += rep; } static void Z_unpacker(struct packenv *env, int rep) { fprintf(env->fp, "%-*.*s", rep-1, rep-1, env->buf_base + env->buf_pos); env->buf_pos += rep; } static void c_packer(struct packenv *env, int rep) { if (env->buf_base) { char *arg = packenv_get(env); if (arg) env->buf_base[env->buf_pos] = *arg; else packerror("out of arguments"); } env->buf_pos++; } static void c_unpacker(struct packenv *env, int rep) { fprintf(env->fp, "%c", env->buf_base[env->buf_pos++]); } static void s_packer(struct packenv *env, int rep) { GETSARG(env, int16_t, INT16_MIN, INT16_MAX); env->buf_pos += sizeof(int16_t); } static void s_unpacker(struct packenv *env, int rep) { printsnum(env->fp, *(int16_t *) (env->buf_base + env->buf_pos)); env->buf_pos += sizeof(int16_t); } static void S_packer(struct packenv *env, int rep) { GETUARG(env, uint16_t, UINT16_MAX); env->buf_pos += sizeof(uint16_t); } static void S_unpacker(struct packenv *env, int rep) { printunum(env->fp, *(uint16_t *)(env->buf_base + env->buf_pos)); env->buf_pos += sizeof(uint16_t); } static void l_packer(struct packenv *env, int rep) { GETSARG(env, int32_t, INT32_MIN, INT32_MAX); env->buf_pos += sizeof(int32_t); } static void l_unpacker(struct packenv *env, int rep) { printsnum(env->fp, *(int32_t *)(env->buf_base + env->buf_pos)); env->buf_pos += sizeof(int32_t); } static void L_packer(struct packenv *env, int rep) { GETUARG(env, uint32_t, UINT32_MAX); env->buf_pos += sizeof(uint32_t); } static void L_unpacker(struct packenv *env, int rep) { printunum(env->fp, *(uint32_t *)(env->buf_base + env->buf_pos)); env->buf_pos += sizeof(uint32_t); } static void q_packer(struct packenv *env, int rep) { GETSARG(env, int64_t, INT64_MIN, INT64_MAX); env->buf_pos += sizeof(int64_t); } static void q_unpacker(struct packenv *env, int rep) { printsnum(env->fp, *(int64_t *)(env->buf_base + env->buf_pos)); env->buf_pos += sizeof(int64_t); } static void Q_packer(struct packenv *env, int rep) { GETUARG(env, uint64_t, UINT64_MAX); env->buf_pos += sizeof(uint64_t); } static void Q_unpacker(struct packenv *env, int rep) { printunum(env->fp, *(uint64_t *)(env->buf_base + env->buf_pos)); env->buf_pos += sizeof(uint64_t); } static void i_packer(struct packenv *env, int rep) { GETSARG(env, int, INT_MIN, INT_MAX); env->buf_pos += sizeof(int); } static void i_unpacker(struct packenv *env, int rep) { printsnum(env->fp, *(int *)(env->buf_base + env->buf_pos)); env->buf_pos += sizeof(int); } static void I_packer(struct packenv *env, int rep) { GETUARG(env, unsigned, UINT_MAX); env->buf_pos += sizeof(unsigned); } static void I_unpacker(struct packenv *env, int rep) { printunum(env->fp, *(unsigned *)(env->buf_base + env->buf_pos)); env->buf_pos += sizeof(unsigned); } /* n - An unsigned short (16-bit) in "network" (big-endian) order. */ static void n_packer(struct packenv *env, int rep) { if (env->buf_base) { char *arg = packenv_get(env); if (arg) { uintmax_t x; if (getunum(arg, UINT16_MAX, &x) == 0) { uint16_t v = htons((uint16_t) x); memcpy(env->buf_base + env->buf_pos, &v, sizeof(v)); } else packerror("error converting %s to %s: %s", arg, "uint16_t (network order)", strerror(errno)); } else packerror("out of arguments"); } env->buf_pos += sizeof(uint16_t); } static void n_unpacker(struct packenv *env, int rep) { uint16_t v = ntohs(*(uint16_t *)(env->buf_base + env->buf_pos)); printunum(env->fp, v); env->buf_pos += sizeof(uint16_t); } /* N - An unsigned long (32-bit) in "network" (big-endian) order. */ static void N_packer(struct packenv *env, int rep) { if (env->buf_base) { char *arg = packenv_get(env); if (arg) { uintmax_t x; if (getunum(arg, UINT32_MAX, &x) == 0) { uint32_t v = htonl((uint32_t) x); memcpy(env->buf_base + env->buf_pos, &v, sizeof(v)); } else packerror("error converting %s to %s: %s", arg, "uint32_t (network order)", strerror(errno)); } else packerror("out of arguments"); } env->buf_pos += sizeof(uint32_t); } static void N_unpacker(struct packenv *env, int rep) { uint32_t v = ntohl(*(uint32_t *)(env->buf_base + env->buf_pos)); printunum(env->fp, v); env->buf_pos += sizeof(uint32_t); } /* v - An unsigned short (16-bit) in "VAX" (little-endian) order. */ static void v_packer(struct packenv *env, int rep) { if (env->buf_base) { char *arg = packenv_get(env); if (arg) { uintmax_t x; if (getunum(arg, UINT16_MAX, &x) == 0) { uint16_t v = ntohs((uint16_t) x); memcpy(env->buf_base + env->buf_pos, &v, sizeof(v)); } else packerror("error converting %s to %s: %s", arg, "uint16_t (host order)", strerror(errno)); } else packerror("out of arguments"); } env->buf_pos += sizeof(uint16_t); } static void v_unpacker(struct packenv *env, int rep) { uint16_t v = htons(*(uint16_t *)(env->buf_base + env->buf_pos)); printunum(env->fp, v); env->buf_pos += sizeof(uint16_t); } /* V - An unsigned long (32-bit) in "VAX" (little-endian) order. */ static void V_packer(struct packenv *env, int rep) { if (env->buf_base) { char *arg = packenv_get(env); if (arg) { uintmax_t x; if (getunum(arg, UINT32_MAX, &x) == 0) { uint32_t v = ntohl((uint32_t) x); memcpy(env->buf_base + env->buf_pos, &v, sizeof(v)); } else packerror("error converting %s to %s: %s", arg, "uint32_t (host order)", strerror(errno)); } else packerror("out of arguments"); } env->buf_pos += sizeof(uint32_t); } static void V_unpacker(struct packenv *env, int rep) { uint32_t v = htonl(*(uint32_t *)(env->buf_base + env->buf_pos)); printunum(env->fp, v); env->buf_pos += sizeof(uint32_t); } /* f - A single-precision float in native format. */ static void f_packer(struct packenv *env, int rep) { if (env->buf_base) { char *arg = packenv_get(env); if (arg) { float v; char *p; errno = 0; v = strtof(arg, &p); if (*p) packerror("error converting %s to %s (near %s)", arg, "float", p); else if (errno) packerror("error converting %s to %s: %s", arg, "float", strerror(errno)); else memcpy(env->buf_base + env->buf_pos, &v, sizeof(v)); } } env->buf_pos += sizeof(float); } static void f_unpacker(struct packenv *env, int rep) { float v = *(float *)(env->buf_base + env->buf_pos); fprintf(env->fp, "%f", v); /* FIXME: Format? */ env->buf_pos += sizeof(uint32_t); } /* d - A double-precision float in native format. */ static void d_packer(struct packenv *env, int rep) { if (env->buf_base) { char *arg = packenv_get(env); if (arg) { double v; char *p; errno = 0; v = strtod(arg, &p); if (*p) packerror("error converting %s to %s (near %s)", arg, "double", p); else if (errno) packerror("error converting %s to %s: %s", arg, "double", strerror(errno)); else memcpy(env->buf_base + env->buf_pos, &v, sizeof(v)); } } env->buf_pos += sizeof(double); } static void d_unpacker(struct packenv *env, int rep) { double v = *(double *)(env->buf_base + env->buf_pos); fprintf(env->fp, "%g", v); /* FIXME: Format? */ env->buf_pos += sizeof(double); } static void x_packer(struct packenv *env, int rep) { if (env->buf_base) env->buf_base[env->buf_pos] = 0; env->buf_pos++; } static void x_unpacker(struct packenv *env, int rep) { fputc(0, env->fp); } static void X_packer(struct packenv *env, int rep) { if (env->buf_pos) --env->buf_pos; } static void at_packer(struct packenv *env, int rep) { if (env->buf_base) memset(env->buf_base + env->buf_pos, 0, rep); env->buf_pos += rep; } static void at_unpacker(struct packenv *env, int rep) { env->buf_pos += rep; } static void dot_packer(struct packenv *env, int rep) { if (env->buf_base) { if (rep < env->buf_pos) memset(env->buf_base + rep, 0, env->buf_pos - rep); else memset(env->buf_base + env->buf_pos, 0, rep - env->buf_pos); } env->buf_pos = rep; } static void dot_unpacker(struct packenv *env, int rep) { env->buf_pos = rep; } static struct packspec packspec[] = { { 'Z', F_REP, Z_packer, Z_unpacker }, { 'c', 0, c_packer, c_unpacker }, { 's', 0, s_packer, s_unpacker }, { 'S', 0, S_packer, S_unpacker }, { 'l', 0, l_packer, l_unpacker }, { 'L', 0, L_packer, L_unpacker }, { 'q', 0, q_packer, q_unpacker }, { 'Q', 0, Q_packer, Q_unpacker }, { 'i', 0, i_packer, i_unpacker }, { 'I', 0, I_packer, I_unpacker }, { 'n', 0, n_packer, n_unpacker }, { 'N', 0, N_packer, N_unpacker }, { 'v', 0, v_packer, v_unpacker }, { 'V', 0, V_packer, V_unpacker }, { 'f', 0, f_packer, f_unpacker }, { 'd', 0, d_packer, d_unpacker }, { 'x', 0, x_packer, x_unpacker }, { 'X', 0, X_packer, X_packer }, { '@', F_REP, at_packer, at_unpacker }, { '.', F_REP, dot_packer, dot_unpacker }, { 0 } }; static int getrep(const char *s, char const **endp, int *rep) { int n; char *p; if (*s == '[') { if (s[1]) { if (isdigit(s[1])) { n = strtol(s + 1, &p, 10); if (n <= 0) { *endp = s; return -1; } else if (*p != ']') { *endp = p; return -1; } *rep = n; *endp = p + 1; } else { *endp = s; return -1; } } else { *endp = s; return -1; } } else if (isdigit(*s)) { n = strtol(s, &p, 10); if (n <= 0) { *endp = s; return -1; } *rep = n; *endp = p; } else { *rep = 1; *endp = s; } return 0; } struct packinst * packcomp(const char *s, char **endp) { struct packinst *head = NULL, *tail = NULL, *pi; struct packspec *ps; int rep; int ec = 0; errno = 0; while (*s) { if (isspace(*s)) { ++s; continue; } for (ps = packspec; ps->ch; ps++) if (ps->ch == *s) break; if (!ps->ch) { ec = EINVAL; break; } if (getrep(s + 1, &s, &rep)) { ec = EINVAL; break; } pi = malloc(sizeof(*pi)); if (!pi) { ec = ENOMEM; break; } pi->next = NULL; pi->spec = ps; pi->rep = rep; if (tail) tail->next = pi; else head = pi; tail = pi; } if (ec) { packfree(head); head = NULL; errno = ec; } if (endp) *endp = (char*) s; return head; } struct packinst * packdup(struct packinst *src) { struct packinst *head = NULL, *tail = NULL, *p; for (; src; src = src->next) { p = malloc(sizeof(*p)); if (!p) { packfree(head); errno = ENOMEM; return NULL; } p->next = NULL; p->spec = src->spec; p->rep = src->rep; p->cur = src->cur; if (tail) tail->next = p; else head = p; tail = p; } return head; } void packfree(struct packinst *pi) { while (pi) { struct packinst *next = pi->next; free(pi); pi = next; } } void packin(struct packinst *pi, struct packenv *env) { int i; for (; pi; pi = pi->next) { if (pi->spec->flags & F_REP) pi->spec->packer(env, pi->rep); else for (i = 0; i < pi->rep; i++) pi->spec->packer(env, 1); } } struct packinst * packinnext(struct packinst *pi, struct packenv *env) { if (!pi) return NULL; if (pi->spec->flags & F_REP) pi->spec->packer(env, pi->rep); else { pi->spec->packer(env, 1); if (++pi->cur < pi->rep) return pi; } return packinit(pi->next); } void packout(struct packinst *pi, struct packenv *env) { int i; for (; pi; pi = pi->next) { if (pi->spec->flags & F_REP) pi->spec->unpacker(env, pi->rep); else for (i = 0; i < pi->rep; i++) { if (i > 0) fputc(' ', env->fp); pi->spec->unpacker(env, 1); } if (pi->next) fputc(' ', env->fp); } } size_t packsize(struct packinst *pi) { struct packenv env; memset(&env, 0, sizeof env); packin(pi, &env); return env.buf_pos; } struct packinst * packinit(struct packinst *inst) { if (inst) inst->cur = 0; return inst; } struct packenv * packenv_create(size_t size) { struct packenv *env = calloc(1, sizeof(*env)); if (env) { env->buf_base = calloc(1, size); if (!env->buf_base) { free(env); return NULL; } env->buf_size = size; } return env; } void packenv_free(struct packenv *env) { if (!env) return; free(env->buf_base); free(env); } void packenv_init(struct packenv *env) { memset(env->buf_base, 0, env->buf_size); env->buf_pos = 0; }