summary refs log tree commit diff
path: root/misc/benc.c
diff options
context:
space:
mode:
authorRichard Nyberg <rnyberg@murmeldjur.se>2005-06-24 09:51:38 +0000
committerRichard Nyberg <rnyberg@murmeldjur.se>2005-06-24 09:51:38 +0000
commitdd0d462afae75ff243f8cd1528963f9ad489706d (patch)
tree2ef874a1fe5212245814d16f4c9b389524aed9d1 /misc/benc.c
downloadbtpd-dd0d462afae75ff243f8cd1528963f9ad489706d.tar.gz
btpd-dd0d462afae75ff243f8cd1528963f9ad489706d.zip
Import btpd-0.1.
git-svn-id: file:///home/rnyberg/svngit/btpd/releases/0.1@1 76a1f634-46fa-0310-9943-bd1476092a85
Diffstat (limited to 'misc/benc.c')
-rw-r--r--misc/benc.c345
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);
+}