summaryrefslogtreecommitdiffstats
path: root/src/bencoding.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bencoding.c')
-rw-r--r--src/bencoding.c357
1 files changed, 258 insertions, 99 deletions
diff --git a/src/bencoding.c b/src/bencoding.c
index 801c5b6..2063bdc 100644
--- a/src/bencoding.c
+++ b/src/bencoding.c
@@ -60,9 +60,9 @@ int bcompare (struct bencoding * a, struct bencoding * b) {
return -1;
if (a && !b)
return 1;
- if (a->type & (num | string | list | dict) < b->type (num | string | list | dict))
+ if ((a->type & (num | string | list | dict)) < (b->type & (num | string | list | dict)))
return -1;
- if (a->type & (num | string | list | dict) > b->type (num | string | list | dict))
+ if ((a->type & (num | string | list | dict)) > (b->type & (num | string | list | dict)))
return 1;
int ret = bcompare(a->key, b->key);
if (ret)
@@ -83,9 +83,9 @@ int bcompare (struct bencoding * a, struct bencoding * b) {
return -1;
if (a->child && !b->child)
return 1;
- a = a->child;
- b = b->child;
- if (a->value & (list | dict)) {
+ if (a->type & (list | dict)) {
+ a = a->child;
+ b = b->child;
while (1) {
if (!a && !b)
return 0;
@@ -96,68 +96,49 @@ int bcompare (struct bencoding * a, struct bencoding * b) {
b = b->next;
}
}
+ return 0;
}
/**
- * insert into bencoding dict or list. if key already exists, it's prepended to the already existing key, unless opts has replace set.
- *
- * the memory pointed to by elem is considered ownership and responsibility of the dict now, so it shouldn't be freed by the caller. it can still be modified, however.
- *
- * if elem or benc is NULL, function does nothing.
+ * returns a bencoding element that represents a string. the resulting element is allocated on heap and must be bencoding_free()d.
*
- * default (without replace), new element will be inserted into the dict, but before the old element, with the aim that finders, such as bpath(), would return the new element instead of the old.
- * replace option frees the old element and inserts this one instead if the key already exists in the dict. this is not the default, because it frees objects that may be used elsewhere.
+ * the string ownership is transfered, so for static strings, do strdup() before pass
*
- * @param benc [in] the structure to which elem will be inserted into
- * @param elem [in] the element that will be inserted into the structure
+ * @param str [in] the string to be converted to a bencoding element
*/
-void binsert (struct bencoding * benc, struct bencoding * elem) {
- if (!benc || !elem)
+struct bencoding * bstr (char * str) {
+ if (!str)
return NULL;
- elem->parent = benc;
- if (!benc->child) {
- elem->next = NULL;
- elem->prev = NULL;
- elem->parent = benc;
- benc->child = elem;
- return;
- }
- benc = benc->child;
- while (benc->next && bcompare(elem->key, benc->next->key) < 0)
- benc = benc->next;
- struct bencoding * oldnext = benc->next;
- benc->next = elem;
- elem->prev = benc;
- elem->next = oldnext;
- if (oldnext)
- oldnext->prev = elem;
+ struct bencoding * b = calloc(1, sizeof *b);
+ if (!b)
+ return NULL;
+ b->type = string;
+ b->valuelen = strlen(str);
+ b->value = str;
+ return b;
}
/**
- * returns a bencoding element that represents a string
+ * returns a bencoding element that represents a string. the string ownership is not transfered and the element is allocated on the stack, so no cleanup is required. ideal for bval(), DO NOT USE ON binsert() and similar.
*
- * the string ownership is not transfered, the string is strdup()ed
+ * implemented as a macro that edits the current function's stack.
*
* @param str [in] the string to be converted to a bencoding element
*/
-struct bencoding * bstr (const char * str) {
- struct bencoding * b = calloc(1, sizeof *b);
- if (!b)
- return NULL;
+#define bstrs(x) bstrs_set(alloca(sizeof(struct bencoding)), x)
+
+struct bencoding * bstrs_set (struct bencoding * b, char * s) {
+ memset(b, '\0', sizeof *b);
+ b->value = s;
+ b->valuelen = strlen(s);
b->type = string;
- b->valuelen = strlen(str);
- b->value = strdup(str);
- if (!b->value) {
- free(b);
- return NULL;
- }
return b;
}
/**
- * returns a bencoding element that represents a number
+ * returns a bencoding element that represents a number. the resulting element is allocated on heap and must be bencoding_free()d.
*
* @param num [in] the number to be converted to a bencoding number
*/
@@ -167,14 +148,33 @@ struct bencoding * bnum (long int num) {
if (!b)
return NULL;
b->type = num;
- char buf[512];
+ /* char buf[512];
sprintf(buf, "%ld", num);
b->value = strdup(buf);
- if (!b->valueint) {
+ if (!b->intvalue) {
free(b);
return NULL;
- }
- b->valueint = num;
+ } */ // we could do this, but I don't think it's necessary.
+ b->valuelen = 0;
+ b->intvalue = num;
+ return b;
+}
+
+/**
+ * returns a bencoding element that represents a number. the element is allocated on the stack, so no cleanup is required. ideal for bval(), DO NOT USE ON binsert() and similar.
+ *
+ * implemented as a macro that edits the current function's stack.
+ *
+ * @param num [in] the number to be converted to a bencoding number
+ */
+
+#define bnums(x) bnums_set(alloca(sizeof(struct bencoding)), x)
+
+struct bencoding * bnums_set (struct bencoding * b, long int num) {
+ memset(b, '\0', sizeof *b);
+ b->type = num;
+ b->valuelen = 0;
+ b->intvalue = num;
return b;
}
@@ -266,40 +266,32 @@ char * b2json_charrepr (char * dest, unsigned char a) {
*/
int b2json_length (struct bencoding * b) {
+ int add = 0;
if (!b)
return 4;
+ if (b->key)
+ add += 1 + b2json_length(b->key);
if (b->type & string) {
int size = 2;
for (size_t i = 0; i < b->valuelen; i++)
size += b2json_charsize(b->value[i]);
- return size;
+ return size+add;
}
if (b->type & num) {
char buf[512];
sprintf(buf, "%ld", b->intvalue);
- return strlen(buf);
+ return strlen(buf)+add;
}
- if (b->type & list) {
+ if (b->type & (list | dict)) {
if (!b->child)
- return 2;
+ return 2+add;
struct bencoding * t = b->child;
int size = 2 + b2json_length(t);
while (t->next) {
t = t->next;
size += b2json_length(t) + 1;
}
- return size;
- }
- if (b->type & dict) {
- if (!b->child)
- return 2;
- struct bencoding * t = b->child;
- int size = 3 + b2json_length(t) + b2json_length(t->key);
- while (t->next) {
- t = t->next;
- size += 1 + b2json_length(t) + 1 + b2json_length(t->key);
- }
- return size;
+ return size+add;
}
return 5;
}
@@ -321,6 +313,10 @@ char * b2json (char * dest, struct bencoding * b) {
strncpy(dest, "null", 4);
return dest+4;
}
+ if (b->key) {
+ dest = b2json(dest, b->key);
+ *dest++ = ':';
+ }
if (b->type & string) {
*dest++ = '"';
for (size_t i = 0; i < b->valuelen; i++)
@@ -334,40 +330,20 @@ char * b2json (char * dest, struct bencoding * b) {
strncpy(dest, buf, strlen(buf));
return dest+strlen(buf);
}
- if (b->type & list) {
+ if (b->type & (list | dict)) {
if (!b->child) {
- strncpy(dest, "[]", 2);
+ strncpy(dest, b->type & list ? "[]" : "{}", 2);
return dest+2;
}
+ *dest++ = b->type & list ? '[' : '{';
struct bencoding * t = b->child;
- *dest++ = '[';
dest = b2json(dest, t);
while (t->next) {
t = t->next;
*dest++ = ',';
dest = b2json(dest, t);
}
- *dest++ = ']';
- return dest;
- }
- if (b->type & dict) {
- if (!b->child) {
- strncpy(dest, "{}", 2);
- return dest+2;
- }
- *dest++ = '{';
- struct bencoding * t = b->child;
- dest = b2json(dest, t->key);
- *dest++ = ':';
- dest = b2json(dest, t);
- while (t->next) {
- t = t->next;
- *dest++ = ',';
- dest = b2json(dest, t->key);
- *dest++ = ':';
- dest = b2json(dest, t);
- }
- *dest++ = '}';
+ *dest++ = b->type & list ? ']' : '}';
return dest;
}
strncpy(dest, "false", 4);
@@ -376,6 +352,83 @@ char * b2json (char * dest, struct bencoding * b) {
}
/**
+ * print bstructure to a FILE *. used for debugging. in JSON
+ *
+ * @param benc [in] the structure to be printed
+ * @param benc [in] the FILE * to which to print to
+ */
+
+void bprint (FILE * out, struct bencoding * benc) {
+ char o[1+b2json_length(benc)];
+ b2json(o, benc);
+ o[b2json_length(benc)] = '\0';
+ fprintf(out, "%s\n", o);
+}
+
+/**
+ * insert into bencoding dict or list. if key already exists, it's prepended to the already existing key, unless opts has replace set.
+ *
+ * the memory pointed to by elem is considered ownership and responsibility of the dict now, so it shouldn't be freed by the caller. it can still be modified, however.
+ *
+ * if elem or benc is NULL, function does nothing.
+ *
+ * default (without replace), new element will be inserted into the dict, but before the old element, with the aim that finders, such as bpath(), would return the new element instead of the old.
+ * replace option frees the old element and inserts this one instead if the key already exists in the dict. this is not the default, because it frees objects that may be used elsewhere.
+ *
+ * for lists, binsert adds the new element to the start of the list
+ *
+ * @param benc [in] the structure to which elem will be inserted into
+ * @param elem [in] the element that will be inserted into the structure
+ */
+
+void binsert (struct bencoding * benc, struct bencoding * elem) {
+ if (!benc || !elem)
+ return;
+ elem->parent = benc;
+ struct bencoding ** place = &benc->child;
+ while (1) {
+ if (!*place) {
+ *place = elem;
+ elem->next = NULL;
+ elem->prev = NULL;
+ return;
+ }
+ if (bcompare((*place)->key, elem->key) >= 0) {
+ elem->prev = (*place)->prev;
+ elem->next = *place;
+ (*place)->prev = elem;
+ if (((benc->type & replace) || (elem->type & replace)) && !bcompare((*place)->key, elem->key)) {
+ elem->next = (*place)->next;
+ if ((*place)->next)
+ (*place)->next->prev = elem;
+ free_bencoding(*place);
+ }
+ *place = elem;
+ return;
+ }
+ place = &(*place)->next;
+ }
+}
+
+/**
+ * detaches an element from a bencoding list or dict, does not free it.
+ *
+ * @param elem [in] the element to be detached but not freed
+ */
+
+void bdetach (struct bencoding * elem) {
+ if (!elem)
+ return;
+ if (elem->prev)
+ elem->prev->next = elem->next;
+ if (elem->next)
+ elem->next->prev = elem->prev;
+ elem->next = NULL;
+ elem->prev = NULL;
+ // elem->parent = NULL; // we could also do that, no issue with me 20221123
+}
+
+/**
* bdecodes a bencoded structure from a string into a bencoding structure that must be free_bencodinged by the caller.
*
* nonstandard things: this parser allows for dict keys to be of any type, valuekey
@@ -551,26 +604,132 @@ struct bencoding * bpath (struct bencoding * benc, const char * key) {
/**
* find a value in a list. returns NULL if not found.
*
+ * to easily check if a number or string is present in a list or dict as a value:
+ *
+ * if (bval(benc, bnums(123)) || bval(benc, bstrs("some string")));
+ *
* @param benc [in] the bencoding list or dict to look in
- * @param str [in] the value
+ * @param val [in] the value
*/
-struct bencoding * bval (struct bencoding * benc, const char * val) {
+struct bencoding * bval (struct bencoding * benc, struct bencoding * val) {
if (!benc)
return NULL;
if (!benc->child)
return NULL;
benc = benc->child;
while (benc) {
- if (benc->type & num) {
- char buf[412];
- sprintf(buf, "%ld", benc->intvalue);
- if (!strcmp(buf, val))
- return benc;
- }
- if (benc->type & string && strlen(val) == benc->valuelen && !strncmp(benc->value, val, benc->valuelen))
+ if (!bcompare(benc, val))
return benc;
benc = benc->next;
}
return NULL;
}
+
+/**
+ * returns the size required to store a bencoded object, without the terminating NULL byte.
+ *
+ * @param b [in] the bencoding object
+ */
+
+int bencode_length (struct bencoding * b) {
+ if (!b)
+ return 0;
+ char buf[512];
+ if (b->type & num) {
+ sprintf(buf, "%ld", b->intvalue);
+ return strlen(buf)+bencode_length(b->key)+2;
+ }
+ if (b->type & string) {
+ sprintf(buf, "%ld", b->valuelen);
+ return strlen(buf)+1+b->valuelen+bencode_length(b->key);
+ }
+ if (b->type & (list | dict)) {
+ struct bencoding * t = b->child;
+ int size = 2;
+ while (t) {
+ size += bencode_length(t);
+ t = t->next;
+ }
+ return size+bencode_length(b->key);
+ }
+ return 0;
+}
+
+/**
+ * encode a bencoding object with bencoding and store it into memory pointed to by dest with at least bencode_length(b) space.
+ *
+ * does not write a terminating NULL byte.
+ *
+ * @param dest [in] the destination to which to write to
+ * @param b [in] the bencoding object
+ * @return the pointer to the byte after the last written byte
+ */
+
+char * bencode (char * dest, struct bencoding * b) {
+ if (!b)
+ return dest;
+ char buf[512];
+ if (b->key)
+ dest = bencode(dest, b->key);
+ if (b->type & num) {
+ sprintf(buf, "i%ld", b->intvalue);
+ strncpy(dest, buf, strlen(buf));
+ dest += strlen(buf);
+ *dest++ = 'e';
+ }
+ if (b->type & string) {
+ sprintf(buf, "%ld:", b->valuelen);
+ strncpy(dest, buf, strlen(buf));
+ dest += strlen(buf);
+ memcpy(dest, b->value, b->valuelen);
+ dest += b->valuelen;
+ }
+ if (b->type & (list | dict)) {
+ if (b->type & list)
+ *dest++ = 'l';
+ else
+ *dest++ = 'd';
+ struct bencoding * t = b->child;
+ while (t) {
+ dest = bencode(dest, t);
+ t = t->next;
+ }
+ *dest++ = 'e';
+ }
+ return dest;
+}
+
+/**
+ * clones a bencoding object including all of it's children
+ *
+ * @param b [in] the source object
+ * @return the new object, allocated on heap
+ */
+
+struct bencoding * bclone (struct bencoding * b) {
+ if (!b)
+ return NULL;
+ struct bencoding * c = calloc(1, sizeof(*c));
+ if (b->key) {
+ c->key = bclone(b->key);
+ c->key->parent = c;
+ }
+ if (b->child) {
+ c->child = bclone(b->child);
+ c->child->parent = c;
+ }
+ if (b->next) {
+ c->next = bclone(b->child);
+ c->next->parent = c;
+ }
+ c->valuelen = b->valuelen;
+ if (b->value) {
+ c->value = malloc(c->valuelen+1);
+ memcpy(c->value, b->value, c->valuelen+1);
+ }
+ c->intvalue = b->intvalue;
+ c->type = b->type;
+ c->index = b->index;
+ return c;
+}