Skip to content

Instantly share code, notes, and snippets.

@mischief
Created June 23, 2025 07:55
Show Gist options
  • Select an option

  • Save mischief/d486d090eeabacb3511ff03c09895f28 to your computer and use it in GitHub Desktop.

Select an option

Save mischief/d486d090eeabacb3511ff03c09895f28 to your computer and use it in GitHub Desktop.
#include <sys/param.h> /* MIN */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stddef.h>
#include <errno.h>
#include <assert.h>
#include <be.h>
enum {
/* 9,999,999 */
BE_MAX_STR_LEN = 7,
/* roughly int64 */
BE_MAX_INT_LEN = 20,
};
void
be_value_free(struct be_value *v)
{
struct be_value *ov, *head;
for(; v;){
switch(v->type){
case BE_STRING:
free(v->string.iov_base);
break;
case BE_INTEGER:
break;
case BE_DICTIONARY:
case BE_LIST:
head = STAILQ_FIRST(&v->listhead);
if(head){
STAILQ_REMOVE_HEAD(&v->listhead, next);
v = head;
continue;
}
break;
default:
break;
}
free(v->key);
ov = v->up;
free(v);
v = ov;
}
}
static int
be_alloc(int type, struct be_value *up, struct be_value **out)
{
struct be_value *v;
v = calloc(1, sizeof(*v));
if(!v)
return -ENOMEM;
v->type = type;
v->up = up;
STAILQ_INIT(&v->listhead);
*out = v;
return 0;
}
enum {
IN_DICT_FALSE,
IN_DICT_KEY,
IN_DICT_VALUE,
};
static int
be_value_parse_string(const char *buf, size_t sz, struct be_value **out)
{
const char *p = buf, *cp;
size_t spn;
ptrdiff_t pdif;
int rv;
unsigned long insz;
struct be_value *v;
*out = NULL;
cp = memchr(p, ':', MIN(BE_MAX_STR_LEN, sz));
if(!cp)
return -EINVAL;
spn = strspn(p, "0123456789");
pdif = cp - p;
if(spn != (size_t)pdif)
return -EINVAL;
rv = be_alloc(BE_STRING, NULL, &v);
if(rv < 0)
return rv;
*out = v;
insz = strtoul(p, NULL, 10);
if(insz > sz)
return -ERANGE;
v->string.iov_base = malloc(insz);
if(!v->string.iov_base)
return -ENOMEM;
v->string.iov_len = insz;
memcpy(v->string.iov_base, cp+1, insz);
return (cp+1-p) + insz;
}
static int
be_value_parse_number(const char *buf, size_t sz, struct be_value **out)
{
const char *p = buf, *cp;
size_t spn;
ptrdiff_t pdif;
int rv;
struct be_value *v;
cp = memchr(p, 'e', MIN(BE_MAX_INT_LEN, sz));
if(!cp)
return -EINVAL;
spn = strspn(p+1, "-0123456789");
pdif = cp - (p + 1);
if(spn != (size_t)pdif)
return -EINVAL;
rv = be_alloc(BE_INTEGER, NULL, &v);
if(rv < 0)
return rv;
v->integer = strtol(p+1, NULL, 10);
*out = v;
return cp+1-p;
}
static int be_value_parse_inner(int depth, const char *buf, size_t sz, struct be_value **out);
static void
be_insert(struct be_value *v, struct be_value *val)
{
if(v->type == BE_LIST || v->type == BE_DICTIONARY)
STAILQ_INSERT_TAIL(&v->listhead, val, next);
val->up = v;
}
static int
be_value_parse_list(int depth, const char *buf, size_t sz, struct be_value **out)
{
const char *p = buf + 1;
struct be_value *v, *inner;
int rv, chr;
chr = 1;
rv = be_alloc(BE_LIST, NULL, &v);
if(rv < 0)
return rv;
while(*p && *p != 'e'){
rv = be_value_parse_inner(depth, p, sz - chr, &inner);
if(rv < 0)
return rv;
be_insert(v, inner);
p += rv;
chr += rv;
}
*out = v;
return chr;
}
static int
be_value_parse_dictionary(int depth, const char *buf, size_t sz, struct be_value **out)
{
const char *p = buf + 1;
struct be_value *v, *inner, *key;
int rv, chr;
chr = 1;
rv = be_alloc(BE_DICTIONARY, NULL, &v);
if(rv < 0)
return rv;
while(*p && *p != 'e'){
switch(p[0]){
case '0' ... '9':
rv = be_value_parse_string(p, sz, &key);
if(rv < 0)
return rv;
p += rv;
chr += rv;
break;
default:
return -EINVAL;
}
rv = be_value_parse_inner(depth, p, sz - chr, &inner);
if(rv < 0)
return rv;
/* kidnap key */
inner->key = key->string.iov_base;
key->string.iov_base = NULL;
be_value_free(key);
be_insert(v, inner);
p += rv;
chr += rv;
}
*out = v;
return chr;
}
static int
be_value_parse_inner(int depth, const char *buf, size_t sz, struct be_value **out)
{
if(depth > 128)
return -EINVAL;
if(sz < 2)
return -ENOMSG;
depth++;
switch(buf[0]){
case '0' ... '9':
return be_value_parse_string(buf, sz, out);
case 'i':
return be_value_parse_number(buf, sz, out);
case 'l':
return be_value_parse_list(depth, buf, sz, out);
case 'd':
return be_value_parse_dictionary(depth, buf, sz, out);
}
return -EINVAL;
}
int
be_value_parse(const char *buf, size_t sz, struct be_value **out)
{
return be_value_parse_inner(0, buf, sz, out);
}
void
be_walk(struct be_value *be, int (*cb)(struct be_value *be, void *arg), void *arg)
{
struct be_value *inner;
if(!be)
return;
cb(be, arg);
switch(be->type){
case BE_LIST:
[[fallthrough]];
case BE_DICTIONARY:
STAILQ_FOREACH(inner, &be->listhead, next){
be_walk(inner, cb, arg);
}
break;
}
}
struct be_value*
be_find(struct be_value *be, const char *key)
{
struct be_value *inner;
if(!be || !(be->type == BE_DICTIONARY))
return NULL;
STAILQ_FOREACH(inner, &be->listhead, next){
if(strcmp(key, inner->key) == 0)
return inner;
}
return NULL;
}
int be_message_new(struct be_message *m, const char *buf)
{
m->buf = buf;
m->sz = strlen(buf);
m->cur = 0;
return 0;
}
int
be_peek_type(struct be_message *m, int *type)
{
const char *cp;
size_t spn;
ptrdiff_t pdif;
if(m->sz - m->cur == 0)
return 0;
if(m->sz - m->cur < 2)
return -ENOMSG;
switch(m->buf[m->cur]){
case '0' ... '9':
/* string, with a limit on size */
cp = memchr(&m->buf[m->cur], ':', BE_MAX_STR_LEN);
if(!cp)
return -EINVAL;
spn = strspn(&m->buf[m->cur], "0123456789");
pdif = cp - &m->buf[m->cur];
if(spn != (size_t)pdif)
return -EINVAL;
*type = BE_STRING;
return 1;
case 'i':
cp = memchr(&m->buf[m->cur], 'e', BE_MAX_INT_LEN);
if(!cp)
return -EINVAL;
spn = strspn(&m->buf[m->cur], "0123456789");
pdif = cp - &m->buf[m->cur];
if(spn != (size_t)pdif)
return -EINVAL;
*type = BE_INTEGER;
return 1;
case 'l': return BE_LIST;
case 'd': return BE_DICTIONARY;
default:
fprintf(stderr, "default\n");
return -ENOMSG;
}
return 0;
}
#include <sys/uio.h>
#include <sys/queue.h>
#include <inttypes.h>
enum {
BE_STRING = 0,
BE_INTEGER = 1,
BE_LIST = 2,
BE_DICTIONARY = 3,
};
struct be_value;
STAILQ_HEAD(be_value_head, be_value);
struct be_value
{
int type;
struct be_value *up;
char *key;
STAILQ_ENTRY(be_value) next;
union {
long long integer;
struct iovec string;
struct be_value_head listhead;
};
};
void be_value_free(struct be_value *v);
int be_value_parse(const char *buf, size_t sz, struct be_value **out);
void be_walk(struct be_value *be, int (*cb)(struct be_value *be, void *arg), void *arg);
struct be_value *be_find(struct be_value *be, const char *key);
int be_value_new(const char *buf, size_t sz, struct be_value **out);
struct be_message
{
const char *buf;
size_t sz;
size_t cur;
};
int be_message_new(struct be_message *m, const char *buf);
int be_peek_type(struct be_message *m, int *type);
#include <sys/queue.h>
enum {
INFOHASH_SHA1 = 1,
INFOHASH_SHA1HEXLEN = 40,
};
struct infohash;
STAILQ_HEAD(infohash_head, infohash);
struct infohash
{
STAILQ_ENTRY(infohash) next;
int type;
char *hash;
};
#include <fcntl.h>
#include <err.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <systemd/sd-event.h>
#include <lsd.h>
static int
lsd_handler(sd_event_source *s, int fd, uint32_t revents, void *v)
{
int rv;
struct lsd *lsd = v;
__attribute__((cleanup(lsd_frame_freep))) struct lsd_frame *frame = NULL;
__attribute__((cleanup(lsd_frame_info_freep))) struct lsd_frame_info *info = NULL;
struct infohash *ih;
(void)s;
(void)fd;
(void)revents;
rv = lsd_recv(lsd, &frame);
if(rv < 0){
warnx("lsd_recv: %s", strerror(-rv));
sd_event_exit(sd_event_source_get_event(s), rv);
return rv;
}
rv = lsd_frame_parse(frame, &info);
if(rv < 0){
warnx("lsd_frame_parse: %s", strerror(-rv));
sd_event_exit(sd_event_source_get_event(s), rv);
return rv;
}
printf("received LSD frame from %s\n", info->remote);
printf("bt port %hu\n", info->port);
printf("cookie: %s\n", info->cookie);
printf("hashes:\n");
STAILQ_FOREACH(ih, &info->infohashes, next){
printf("%s\n", ih->hash);
}
return 0;
}
int
main(int argc, char *argv[])
{
int rv;
__attribute__((cleanup(lsd_freep))) struct lsd *lsdfd = NULL;
__attribute__((cleanup(peer_freep))) struct peer *p = NULL;
__attribute__((cleanup(sd_event_unrefp))) sd_event *ev = NULL;
(void)argc;
(void)argv;
if((rv = lsd_init(&lsdfd, O_NONBLOCK)) < 0)
errx(1, "lsd_init: %s", strerror(-rv));
if((rv = sd_event_default(&ev)) < 0)
errx(1, "sd_event_default: %s", strerror(-rv));
if((rv = sd_event_add_io(ev, NULL, lsdfd->fd, EPOLLIN, lsd_handler, lsdfd)) < 0)
errx(1, "sd_event_add_io: %s", strerror(-rv));
rv = sd_event_loop(ev);
if(rv < 0)
errx(1, "sd_event_loop: %s", strerror(-rv));
return 0;
}
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <be.h>
#ifndef __AFL_FUZZ_TESTCASE_LEN
ssize_t fuzz_len;
#define __AFL_FUZZ_TESTCASE_LEN fuzz_len
unsigned char fuzz_buf[1024000];
#define __AFL_FUZZ_TESTCASE_BUF fuzz_buf
#define __AFL_FUZZ_INIT() void sync(void);
#define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0)
#define __AFL_INIT() sync()
#endif
__AFL_FUZZ_INIT();
int
main(int argc, char *argv[])
{
struct be_value *be;
int rv;
(void) argc;
(void) argv;
#ifdef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif
// must be after __AFL_INIT
// and before __AFL_LOOP!
unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;
while (__AFL_LOOP(10000)) {
// don't use the macro directly in a call!
int len = __AFL_FUZZ_TESTCASE_LEN;
// check for a required/useful minimum input length
if (len < 2) continue;
/* Setup function call, e.g. struct target *tmp = libtarget_init() */
/* Call function to be fuzzed, e.g.: */
be = NULL;
rv = be_value_parse(buf, len, &be);
(void)rv;
/* Reset state. e.g. libtarget_free(tmp) */
if(be)
be_value_free(be);
}
return 0;
}
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <limits.h>
#include <errno.h>
#include <lsd.h>
void
lsd_freep(struct lsd **v)
{
if(*v){
close((*v)->fd);
free(*v);
}
}
void
lsd_frame_freep(struct lsd_frame **v)
{
if(*v)
free(*v);
}
void
lsd_frame_info_freep(struct lsd_frame_info **v)
{
struct infohash *ih, *next;
if(!*v)
return;
ih = STAILQ_FIRST(&(*v)->infohashes);
while(ih){
next = STAILQ_NEXT(ih, next);
free(ih->hash);
free(ih);
ih = next;
}
free((*v)->cookie);
free(*v);
}
int
lsd_init(struct lsd **out, int setfl)
{
struct lsd *lsd;
int fd, serr, y = 1;
struct sockaddr_in sa;
struct ip_mreq mreq;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if(fd < 0)
return -errno;
if(fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
goto err;
if(setfl)
if(fcntl(fd, F_SETFL, setfl) < 0)
goto err;
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(y)) < 0)
goto err;
//memset(&sa, 0, sizeof(sa));
sa = (struct sockaddr_in){
.sin_family = AF_INET,
.sin_addr = (struct in_addr){ .s_addr = htonl(INADDR_ANY) },
.sin_port = htons(6771),
};
if(bind(fd, (struct sockaddr*)&sa, sizeof(sa)) < 0)
goto err;
mreq = (struct ip_mreq){
.imr_multiaddr = (struct in_addr){ .s_addr = inet_addr("239.192.152.143") },
.imr_interface = (struct in_addr){ .s_addr = htonl(INADDR_ANY) },
};
if(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &mreq, sizeof(mreq)) < 0)
goto err;
lsd = calloc(1, sizeof(*lsd));
if(!lsd)
goto err;
lsd->fd = fd;
*out = lsd;
return 0;
err:
serr = errno;
close(fd);
return -serr;
}
int
lsd_recv(struct lsd *lsd, struct lsd_frame **out)
{
int rv;
socklen_t salen;
struct lsd_frame *frame;
assert(lsd);
assert(out);
frame = calloc(1, sizeof(*frame));
if(!frame)
return -ENOMEM;
salen = sizeof(frame->sa);
rv = recvfrom(lsd->fd, frame->frame, sizeof(frame->frame)-1, 0, (struct sockaddr*)&frame->sa, &salen);
if(rv < 0){
free(frame);
return -errno;
}
frame->len = rv;
frame->frame[rv] = '\0';
assert(strlcpy(frame->remote, "Unknown", sizeof(frame->remote)) < sizeof(frame->remote));
switch(frame->sa.ss_family){
case AF_INET:
inet_ntop(frame->sa.ss_family, &((struct sockaddr_in*)&frame->sa)->sin_addr, frame->remote, salen);
break;
case AF_INET6:
inet_ntop(frame->sa.ss_family, &((struct sockaddr_in6*)&frame->sa)->sin6_addr, frame->remote, salen);
break;
}
*out = frame;
return 0;
}
int
lsd_frame_parse(struct lsd_frame *frame, struct lsd_frame_info **out)
{
unsigned long port;
const char *p, *ep;
struct lsd_frame_info *info;
struct infohash *ih;
p = frame->frame;
ep = strstr(p, "\r\n");
if(!ep)
return -ENOMSG;
if(strncmp(p, "BT-SEARCH", 9) != 0)
return -ENOMSG;
p = ep+2;
ep = strstr(p, "\r\n");
if(!ep)
return -ENOMSG;
if(strncmp(p, "Host: ", 6) != 0)
return -ENOMSG;
p = ep+2;
ep = strstr(p, "\r\n");
if(!ep)
return -ENOMSG;
if(strncmp(p, "Port: ", 6) != 0)
return -ENOMSG;
p += 6;
port = strtoul(p, NULL, 10);
if(port > USHRT_MAX)
return -ERANGE;
p = ep+2;
info = calloc(1, sizeof(*info));
info->sa = frame->sa;
assert(strlcpy(info->remote, frame->remote, sizeof(info->remote)) <= sizeof(info->remote));
info->port = port;
STAILQ_INIT(&info->infohashes);
while(p && *p){
ep = strstr(p, "\r\n");
if(!ep)
break;
if(strncmp(p, "Infohash: ", 10) == 0){
/* malformed? TODO: decode hex */
if(ep - (p+10) != INFOHASH_SHA1HEXLEN)
goto next;
ih = calloc(1, sizeof(*ih));
if(!ih)
goto err;
ih->type = INFOHASH_SHA1;
ih->hash = strndup(p+10, INFOHASH_SHA1HEXLEN);
STAILQ_INSERT_TAIL(&info->infohashes, ih, next);
} else if(strncmp(p, "cookie: ", 8) == 0 && !info->cookie){
info->cookie = strndup(p+8, ep - (p+8));
}
next:
p = ep+2;
}
*out = info;
return 0;
err:
lsd_frame_info_freep(&info);
return -ENOMEM;
}
#include <net/ethernet.h>
#include <arpa/inet.h>
#include <bt.h>
struct lsd
{
int fd;
};
struct lsd_frame
{
struct sockaddr_storage sa;
char remote[INET6_ADDRSTRLEN];
char frame[ETH_DATA_LEN];
int len;
};
struct lsd_frame_info
{
struct sockaddr_storage sa;
char remote[INET6_ADDRSTRLEN];
uint16_t port;
char *cookie;
struct infohash_head infohashes;
};
void lsd_freep(struct lsd **v);
void lsd_frame_freep(struct lsd_frame **v);
void lsd_frame_info_freep(struct lsd_frame_info **v);
int lsd_init(struct lsd **out, int setfl);
int lsd_recv(struct lsd *lsd, struct lsd_frame **out);
int lsd_frame_parse(struct lsd_frame *frame, struct lsd_frame_info **out);
#include <fcntl.h>
#include <err.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <systemd/sd-event.h>
#include <lsd.h>
static int
lsd_handler(sd_event_source *s, int fd, uint32_t revents, void *v)
{
int rv;
struct lsd *lsd = v;
__attribute__((cleanup(lsd_frame_freep))) struct lsd_frame *frame = NULL;
__attribute__((cleanup(lsd_frame_info_freep))) struct lsd_frame_info *info = NULL;
struct infohash *ih;
(void)s;
(void)fd;
(void)revents;
rv = lsd_recv(lsd, &frame);
if(rv < 0){
warnx("lsd_recv: %s", strerror(-rv));
sd_event_exit(sd_event_source_get_event(s), rv);
return rv;
}
rv = lsd_frame_parse(frame, &info);
if(rv < 0){
warnx("lsd_frame_parse: %s", strerror(-rv));
sd_event_exit(sd_event_source_get_event(s), rv);
return rv;
}
printf("received LSD frame from %s\n", info->remote);
printf("bt port %hu\n", info->port);
printf("cookie: %s\n", info->cookie);
printf("hashes:\n");
STAILQ_FOREACH(ih, &info->infohashes, next){
printf("%s\n", ih->hash);
}
return 0;
}
int
main(int argc, char *argv[])
{
int rv;
__attribute__((cleanup(lsd_freep))) struct lsd *lsdfd = NULL;
__attribute__((cleanup(sd_event_unrefp))) sd_event *ev = NULL;
(void)argc;
(void)argv;
if((rv = lsd_init(&lsdfd, O_NONBLOCK)) < 0)
errx(1, "lsd_init: %s", strerror(-rv));
if((rv = sd_event_default(&ev)) < 0)
errx(1, "sd_event_default: %s", strerror(-rv));
if((rv = sd_event_add_io(ev, NULL, lsdfd->fd, EPOLLIN, lsd_handler, lsdfd)) < 0)
errx(1, "sd_event_add_io: %s", strerror(-rv));
rv = sd_event_loop(ev);
if(rv < 0)
errx(1, "sd_event_loop: %s", strerror(-rv));
return 0;
}
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <peer.h>
int
peer_new(struct peer **peer, uint16_t port)
{
struct peer *p;
int rv, fd = -1, y = 1;
struct sockaddr_in sa;
p = calloc(1, sizeof(*p));
if(!p)
return -ENOMEM;
*p = (struct peer){
.port = port,
};
fd = socket(AF_INET, SOCK_DGRAM, 0);
if(fd < 0){
rv = -errno;
goto err;
}
p->fd = fd;
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(y)) < 0){
rv = -errno;
goto err;
}
sa = (struct sockaddr_in){
.sin_family = AF_INET,
.sin_addr = (struct in_addr){ .s_addr = htonl(INADDR_ANY) },
.sin_port = htons(port),
};
if(bind(fd, (struct sockaddr*)&sa, sizeof(sa)) < 0){
rv = -errno;
goto err;
}
*peer = p;
return 0;
err:
if(fd > 0)
close(fd);
free(p);
return rv;
}
void
peer_freep(struct peer **p)
{
if(!*p)
return;
close((*p)->fd);
free(*p);
}
#include <sys/queue.h>
#include <stdint.h>
struct remote;
STAILQ_HEAD(remote_head, remote);
struct remote {
STAILQ_ENTRY(remote) next;
struct sockaddr_storage sa;
socklen_t sasz;
};
struct peer {
uint16_t port;
int fd;
};
int peer_new(struct peer **p, uint16_t port);
void peer_freep(struct peer **p);
int peer_send(unsigned char *buf, size_t sz, struct sockaddr_storage *sa, socklen_t sasz);
#include <sys/param.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <be.h>
static int
be_count_depth(struct be_value *be)
{
int depth;
depth = 0;
if(!be)
return 0;
for(be = be->up; be; be = be->up)
depth++;
return depth;
}
static int
be_pp(struct be_value *be, void *arg)
{
int depth;
const char indent[] = " ";
const int nindent = sizeof(indent);
(void) arg;
depth = be_count_depth(be);
printf("%.*s", MIN(depth, nindent), indent);
if(be->key)
printf("%lu:%s = ", strlen(be->key), be->key);
switch (be->type) {
case BE_INTEGER:
printf("i%llde\n", be->integer);
break;
case BE_STRING:
printf("%lu:%s\n", be->string.iov_len, be->string.iov_base);
break;
case BE_LIST:
printf("l\n");
break;
case BE_DICTIONARY:
printf("d\n");
break;
default:
assert(0);
}
return 0;
}
static void
be_pp_errno(struct be_value *be, void *arg)
{
int *err = arg;
*err = be->integer;
}
static void
assert_errno(int v, int err)
{
assert(v == err);
}
int
main(int argc, char *argv[])
{
int type;
struct be_message m;
int rv;
struct be_value *be;
(void)argc;
(void)argv;
const char *buf = "4:eggs";
size_t len = strlen(buf);
/*
rv = be_value_parse(buf, len, &be);
assert(rv > 0);
assert(be->type == BE_STRING);
assert(strcmp(be->string, "eggs") == 0);
be_value_free(be);
buf = "i42e";
len = strlen(buf);
rv = be_value_parse(buf, len, &be);
assert(rv > 0);
assert(be->type == BE_INTEGER);
assert(be->integer == 42);
be_value_free(be);
buf = "l4:spam4:eggse";
len = strlen(buf);
rv = be_value_parse(buf, len, &be);
assert(rv > 0);
assert(be->type == BE_LIST);
//assert(be->integer == 42);
be_value_free(be);
*/
buf = "d3:cow3:moo4:spam4:eggs1:tld3:fooi42eei69eee";
len = strlen(buf);
rv = be_value_parse(buf, len, &be);
assert(rv > 0);
assert(be->type == BE_DICTIONARY);
struct be_value *cow;
cow = be_find(be, "cow");
assert(cow);
assert(cow->type == BE_STRING);
assert(strcmp(cow->string.iov_base, "moo") == 0);
be_walk(be, be_pp, NULL);
be_value_free(be);
return 0;
assert(be_message_new(&m, "4:eggs") == 0);
assert(be_peek_type(&m, &type) == 1);
assert(type == BE_STRING);
assert(be_message_new(&m, "9999999:eggs") == 0);
assert_errno(be_peek_type(&m, &type), -EINVAL);
assert(type == BE_STRING);
assert(be_message_new(&m, "i40e") == 0);
assert(be_peek_type(&m, &type) == 1);
assert(type == BE_INTEGER);
return 0;
}
#include <sys/param.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <assert.h>
#include <be.h>
static int
be_count_depth(struct be_value *be)
{
int depth;
depth = 0;
if(!be)
return 0;
for(be = be->up; be; be = be->up)
depth++;
return depth;
}
static int
be_pp(struct be_value *be, void *arg)
{
int depth;
const char indent[] = " ";
const int nindent = sizeof(indent);
(void) arg;
depth = be_count_depth(be);
printf("%.*s", MIN(depth, nindent), indent);
if(be->key)
printf("%lu:%s = ", strlen(be->key), be->key);
switch (be->type) {
case BE_INTEGER:
printf("i%llde\n", be->integer);
break;
case BE_STRING:
printf("%lu:%s\n", be->string.iov_len, be->string.iov_base);
break;
case BE_LIST:
printf("l\n");
break;
case BE_DICTIONARY:
printf("d\n");
break;
default:
assert(0);
}
return 0;
}
int
main(int argc, char *argv[])
{
int rv;
struct be_value *be;
(void)argc;
(void)argv;
const char *buf;
size_t len;
FILE *fp;
buf = calloc(1, 1024*1024);
if(!buf)
err(1, "calloc");
fp = fopen(argv[1], "rb");
if(!fp)
err(1, "fopen");
len = fread(buf, 1, 1024*1024, fp);
if(len <= 0)
err(1, "fread");
rv = be_value_parse(buf, len, &be);
if(rv < 0)
errx(1, "be_value_parse: %s", strerror(-rv));
be_walk(be, be_pp, NULL);
be_value_free(be);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment