summaryrefslogtreecommitdiff
path: root/bencode.c
diff options
context:
space:
mode:
Diffstat (limited to 'bencode.c')
-rw-r--r--bencode.c285
1 files changed, 285 insertions, 0 deletions
diff --git a/bencode.c b/bencode.c
new file mode 100644
index 0000000..d03523b
--- /dev/null
+++ b/bencode.c
@@ -0,0 +1,285 @@
+/* $Id$ */
+/*
+ * Copyright (c) 2005 Dimitri Sokolyuk <demon@vhost.dyndns.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bencode.h"
+#include "tools.h"
+
+#ifndef lint
+static const char rcsid[] = "@(#)$Id$";
+#endif /* not lint */
+
+static struct btnode *dispatch(char **);
+static struct btnode *getdict(char **);
+static struct btnode *getlist(char **);
+static struct btnode *getint(char **);
+static struct btnode *getstr(char **);
+static off_t getnum(char **);
+#if DEBUG
+static char *indent(int); /* used only for debug purposes */
+#endif /* DEBUG */
+
+struct btnode *
+btbencode(char *str)
+{
+ return dispatch(&str);
+}
+
+void
+btfreenode(struct btnode *np)
+{
+ if (np == NULL)
+ return;
+
+ if (np->next != NULL)
+ btfreenode(np->next);
+
+ switch (np->type) {
+ case NODE_DICT:
+ btfreenode(np->dict.key);
+ btfreenode(np->dict.val);
+ break;
+ case NODE_LIST:
+ btfreenode(np->list);
+ break;
+ case NODE_INT:
+ /* nothing */
+ break;
+ case NODE_STR:
+ free(np->str);
+ break;
+ }
+ free(np);
+}
+
+struct btnode *
+btfinddict(struct btnode *np, char *key)
+{
+ if (np == NULL || key == NULL)
+ return NULL;
+
+ if (np->type != NODE_DICT)
+ return NULL;
+
+ if (np->dict.key == NULL)
+ return NULL;
+
+ /* the key is always string */
+ if (np->dict.key->type != NODE_STR)
+ return NULL;
+
+ if (np->dict.key->str == NULL)
+ return NULL;
+
+ /* there is only one key entry per dict possible */
+ if (strncmp(np->dict.key->str, key,
+ strlen(np->dict.key->str)) == 0)
+ return np->dict.val;
+
+ return btfinddict(np->next, key);
+}
+
+static struct btnode *
+dispatch(char **c)
+{
+ switch (*(*c)++) {
+ case 'd':
+ return getdict(c);
+ case 'l':
+ return getlist(c);
+ case 'i':
+ return getint(c);
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ --*c;
+ return getstr(c);
+ case 'e':
+ default:
+ return NULL;
+ }
+ /* NOTREACHED */
+}
+
+static struct btnode *
+getdict(char **c)
+{
+ struct btnode *np;
+ char *off;
+
+ if ((np = calloc(1, sizeof(struct btnode))) == NULL)
+ return NULL;
+
+ np->type = NODE_DICT;
+
+ if ((np->dict.key = dispatch(c)) == NULL) {
+ free(np); /* empty dictonary */
+ return NULL;
+ }
+
+ off = *c;
+ np->dict.val = dispatch(c);
+ if (np->dict.val != NULL) {
+ np->dict.val->aux.off = off;
+ np->dict.val->aux.len = *c - off;
+ }
+
+ np->next = getdict(c);
+
+ return np;
+}
+
+static struct btnode *
+getlist(char **c)
+{
+ struct btnode *np;
+
+ if ((np = calloc(1, sizeof(struct btnode))) == NULL)
+ return NULL;
+
+ np->type = NODE_LIST;
+
+ if ((np->list = dispatch(c)) == NULL) {
+ free(np); /* empty list */
+ return NULL;
+ }
+
+ np->next = getlist(c);
+
+ return np;
+}
+
+static struct btnode *
+getint(char **c)
+{
+ struct btnode *np;
+
+ if ((np = calloc(1, sizeof(struct btnode))) == NULL)
+ return NULL;
+
+ np->type = NODE_INT;
+ np->num = getnum(c);
+
+ return np;
+}
+
+static off_t
+getnum(char **c)
+{
+ off_t n = 0;
+ int mflag = 0;
+
+ if (**c == '-') {
+ mflag = 1;
+ ++*c;
+ }
+
+ while (**c >= '0' && **c <= '9') {
+ n *= 10;
+ n += *(*c)++ - '0';
+ }
+ ++*c; /* skip the last char (usually `e' or `:') */
+
+ return mflag ? -n : n;
+}
+
+static struct btnode *
+getstr(char **c)
+{
+ off_t len;
+ struct btnode *np;
+
+ if ((len = getnum(c)) <= 0)
+ return NULL;
+
+ if ((np = calloc(1, sizeof(struct btnode))) == NULL)
+ return NULL;
+
+ np->type = NODE_STR;
+ np->len = len;
+
+ if ((np->str = calloc(len + 1, sizeof(char))) == NULL) {
+ free(np);
+ return NULL;
+ }
+
+ memcpy(np->str, *c, len);
+ np->str[len] = NULL;
+ *c += len;
+
+ return np;
+}
+
+#if DEBUG
+static char *
+indent(int n)
+{
+ int i = 0;
+ static char buf[80] = { NULL };
+
+ while (i < n && i < 79)
+ buf[i++] = ' ';
+ buf[i] = NULL;
+
+ return buf;
+}
+
+void
+btdumpnode(struct btnode *np)
+{
+ const char *type[] = { "Dictonary", "List", "Integer", "String" };
+ static int i = 0;
+
+ if (np == NULL)
+ return;
+ printf("%s%s\n", indent(i), type[np->type]);
+ switch (np->type) {
+ case NODE_DICT:
+ i += 8;
+ btdumpnode(np->dict.key);
+ btdumpnode(np->dict.val);
+ i -= 8;
+ break;
+ case NODE_LIST:
+ i += 8;
+ btdumpnode(np->list);
+ i -= 8;
+ break;
+ case NODE_INT:
+ printf("%s%lld\n", indent(i + 4), np->num);
+ break;
+ case NODE_STR:
+ printf("%s(%d) %s\n", indent(i + 4), np->len, np->str);
+ break;
+ }
+
+ if (np->next != NULL)
+ btdumpnode(np->next);
+}
+#endif /* DEBUG */