diff options
Diffstat (limited to 'misc/benc.c')
| -rw-r--r-- | misc/benc.c | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/misc/benc.c b/misc/benc.c new file mode 100644 index 0000000..632b0fd --- /dev/null +++ b/misc/benc.c @@ -0,0 +1,345 @@ +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <inttypes.h> +#include <stdlib.h> +#include <string.h> + +#include "benc.h" + +#define benc_safeset(out, val) if ((out) != NULL) *(out) = (val) + +static const char *benc_validate_aux(const char *p, const char *end); + +int +benc_validate(const char *p, size_t len) +{ + const char *end = p + len - 1; + + if (len <= 0) + return EINVAL; + + return benc_validate_aux(p, end) == end ? 0 : EINVAL; +} + +static const char * +benc_validate_aux(const char *p, const char *end) +{ + size_t d = 0; + switch (*p) { + case 'd': + d = 1; + case 'l': + for (p++; p <= end && *p != 'e'; p++) { + if (d != 0) { + if (d % 2 == 1 && !isdigit(*p)) + return NULL; + else + d++; + } + if ((p = benc_validate_aux(p, end)) == NULL) + return NULL; + } + if (p > end || (d != 0 && d % 2 != 1)) + return NULL; + break; + case 'i': + p++; + if (p > end) + return NULL; + if (*p == '-') + p++; + if (p > end || !isdigit(*p)) + return NULL; + p++; + while (p <= end && isdigit(*p)) + p++; + if (p > end || *p != 'e') + return NULL; + break; + default: + if (isdigit(*p)) { + size_t len = 0; + while (p <= end && isdigit(*p)) { + len *= 10; + len += *p - '0'; + p++; + } + if (p <= end && *p == ':' && p + len <= end) + p += len; + else + return NULL; + } + else + return NULL; + break; + } + return p; +} + +size_t +benc_length(const char *p) +{ + size_t blen; + const char *next; + + switch (*p) { + case 'd': + case 'l': + blen = 2; // [l|d]...e + next = benc_first(p); + while (*next != 'e') { + size_t len = benc_length(next); + blen += len; + next += len; + } + return blen; + case 'i': + for (next = p + 1; *next != 'e'; next++) + ; + return next - p + 1; + default: + assert(benc_str(p, &next, &blen, NULL) == 0); + return next - p + blen; + } +} + +size_t +benc_nelems(const char *p) +{ + size_t nelems = 0; + for (p = benc_first(p); p != NULL; p = benc_next(p)) + nelems++; + return nelems; +} + +const char * +benc_first(const char *p) +{ + assert(benc_islst(p)); + return *(p + 1) == 'e' ? NULL : p + 1; +} + +const char * +benc_next(const char *p) +{ + size_t blen = benc_length(p); + return *(p + blen) == 'e' ? NULL : p + blen; +} + +int +benc_str(const char *p, const char **out, size_t *len, const char**next) +{ + size_t blen = 0; + assert(isdigit(*p)); + blen = *p - '0'; + p++; + while (isdigit(*p)) { + blen *= 10; + blen += *p - '0'; + p++; + } + assert(*p == ':'); + benc_safeset(len, blen); + benc_safeset(out, p + 1); + benc_safeset(next, *(p + blen + 1) == 'e' ? NULL : p + blen + 1); + return 0; +} + +int +benc_strz(const char *p, char **out, size_t *len, const char **next) +{ + int err; + size_t blen; + const char *bstr; + + if ((err = benc_str(p, &bstr, &blen, next)) == 0) { + if ((*out = malloc(blen + 1)) != NULL) { + memcpy(*out, bstr, blen); + (*out)[blen] = '\0'; + benc_safeset(len, blen); + } else + err = ENOMEM; + } + return err; +} + +int +benc_stra(const char *p, char **out, size_t *len, const char **next) +{ + int err; + size_t blen; + const char *bstr; + + if ((err = benc_str(p, &bstr, &blen, next)) == 0) { + if ((*out = malloc(blen)) != NULL) { + memcpy(*out, bstr, blen); + benc_safeset(len, blen); + } else + err = ENOMEM; + } + return err; +} + +int +benc_int64(const char *p, int64_t *out, const char **next) +{ + int sign = 1; + int64_t res = 0; + + assert(*p == 'i'); + p++; + if (*p == '-') { + sign = -1; + p++; + } + assert(isdigit(*p)); + res += sign * (*p - '0'); + p++; + while (isdigit(*p)) { + res *= sign * 10; + res += sign * (*p - '0'); + p++; + } + assert(*p == 'e'); + benc_safeset(out, res); + benc_safeset(next, *(p + 1) == 'e' ? NULL : p + 1); + + return 0; +} + +int +benc_uint32(const char *p, uint32_t *out, const char **next) +{ + int err; + int64_t res; + if ((err = benc_int64(p, &res, next)) == 0) { + if (res >= 0 && res <= 0xffffffffUL) + *out = (uint32_t)res; + else + err = EINVAL; + } + return err; +} + +int +benc_dget_any(const char *p, const char *key, const char **val) +{ + int res; + size_t len, blen; + const char *bstr; + + assert(benc_isdct(p)); + + len = strlen(key); + + p = benc_first(p); + while (p != NULL) { + if ((res = benc_str(p, &bstr, &blen, &p)) != 0) + return res; + + res = strncmp(bstr, key, blen); + if (res == 0 && len == blen) { + *val = p; + return 0; + } else if (res <= 0) { + p = benc_next(p); + } else + return ENOENT; + } + return ENOENT; +} + +int +benc_dget_lst(const char *p, const char *key, const char **val) +{ + int err; + if ((err = benc_dget_any(p, key, val)) == 0) + if (!benc_islst(*val)) + err = EINVAL; + return err; +} + +int +benc_dget_dct(const char *p, const char *key, const char **val) +{ + int err; + if ((err = benc_dget_any(p, key, val)) == 0) + if (!benc_isdct(*val)) + err = EINVAL; + return err; +} + +int +benc_dget_str(const char *p, const char *key, const char **val, size_t *len) +{ + int err; + const char *sp; + if ((err = benc_dget_any(p, key, &sp)) == 0) + err = benc_isstr(sp) ? benc_str(sp, val, len, NULL) : EINVAL; + return err; +} + +int +benc_dget_stra(const char *p, const char *key, char **val, size_t *len) +{ + int err; + const char *sp; + if ((err = benc_dget_any(p, key, &sp)) == 0) + err = benc_isstr(sp) ? benc_stra(sp, val, len, NULL) : EINVAL; + return err; +} + +int +benc_dget_strz(const char *p, const char *key, char **val, size_t *len) +{ + int err; + const char *sp; + if ((err = benc_dget_any(p, key, &sp)) == 0) + err = benc_isstr(sp) ? benc_strz(sp, val, len, NULL) : EINVAL; + return err; +} + +int +benc_dget_int64(const char *p, const char *key, int64_t *val) +{ + int err; + const char *ip; + if ((err = benc_dget_any(p, key, &ip)) == 0) + err = benc_isint(ip) ? benc_int64(ip, val, NULL) : EINVAL; + return err; +} + +int +benc_dget_uint32(const char *p, const char *key, uint32_t *val) +{ + int err; + const char *ip; + if ((err = benc_dget_any(p, key, &ip)) == 0) + err = benc_isint(ip) ? benc_uint32(ip, val, NULL) : EINVAL; + return err; +} + +int +benc_islst(const char *p) +{ + return *p == 'l' || *p == 'd'; +} + +int +benc_isdct(const char *p) +{ + return *p == 'd'; +} + +int +benc_isint(const char *p) +{ + return *p == 'i'; +} + +int +benc_isstr(const char *p) +{ + return isdigit(*p); +} |