/* This file is part of vmod-binlog Copyright (C) 2013 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. C An unsigned char (octet) 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)) return -1; x = x*10 + *s - '0'; } if (x > maxval) 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)) return -1; x = x*10 + *s - '0'; } if (neg) { if (x > -minval) return -1; *retval = -x; } else if (x > maxval) 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)); \ } \ } \ } \ } 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)); \ } \ } \ } \ } 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); } } env->buf_pos += rep; } static void Z_unpacker(struct packenv *env, int rep) { fprintf(env->fp, "%-*.*s", rep, rep, 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; } 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)); } } } 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)); } } } 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)); } } } 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)); } } } 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 == 0 && errno == 0) 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 == 0 && errno == 0) 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; while (s) { for (ps = packspec; ps->ch; ps++) if (ps->ch == *s) break; if (!ps->ch) break; if (getrep(s + 1, &s, &rep)) break; pi = malloc(sizeof(*pi)); if (!pi) return NULL; pi->next = NULL; pi->spec = ps; pi->rep = rep; if (tail) tail->next = pi; else head = pi; tail = pi; } if (endp) *endp = (char*) s; 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) { int i; 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; } #ifdef STANDALONE #include int main(int argc, char **argv) { void (*fn)(struct packinst *pi, struct packenv *env) = packin; struct packinst *pi; struct packenv *env; char *end; int c; while ((c = getopt(argc, argv, "d")) != EOF) { switch (c) { case 'd': fn = packout; break; default: exit(1); } } argc -= optind; argv += optind; if (argc == 0) abort(); pi = packcomp(argv[0], &end); if (!pi) { fprintf(stderr, "out of memory\n"); abort(); } if (*end) { fprintf(stderr, "compile error near %s\n", end); exit(1); } env = packenv_create(packsize(pi)); if (!env) { fprintf(stderr, "out of memory\n"); abort(); } env->fp = stdout; env->argv = argv + 1; env->argc = argc - 1; if (fn == packout) fread(env->buf_base, env->buf_size, 1, stdin); fn(pi, env); if (fn == packin) { fwrite(env->buf_base, env->buf_size, 1, stdout); } packenv_free(env); packfree(pi); } #endif