/* $Id$ */ /* * Copyright (c) 2005 Dimitri Sokolyuk * * 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 #include #include #include #include #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] = '\0'; *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 */