#include "btcli.h" #include "utils.h" void usage_list(void) { printf( "List torrents.\n" "\n" "Usage: list [-a] [-i] [-f ]\n" " list torrent ...\n" "\n" "Arguments:\n" "torrent ...\n" "\tThe torrents to list. Running 'btcli list' without any arguments\n" "\tor options is equivalent to running 'btcli list -ai'.\n" "\n" "Options:\n" "-a\n" "\tList active torrents.\n" "\n" "-i\n" "\tList inactive torrents.\n" "\n" ); exit(1); } struct item { unsigned num, peers; char *name, *dir, *label; char hash[SHAHEXSIZE]; char st; long long cgot, csize, totup, downloaded, uploaded, rate_up, rate_down; uint32_t torrent_pieces, pieces_have, pieces_seen; BTPDQ_ENTRY(item) entry; }; struct items { int count; char **argv; int ntps; struct ipc_torrent *tps; BTPDQ_HEAD(item_tq, item) hd; }; void itm_insert(struct items *itms, struct item *itm) { struct item *p; BTPDQ_FOREACH(p, &itms->hd, entry) if (strcmp(itm->name, p->name) < 0) break; if (p != NULL) BTPDQ_INSERT_BEFORE(p, itm, entry); else BTPDQ_INSERT_TAIL(&itms->hd, itm, entry); } static void list_cb(int obji, enum ipc_err objerr, struct ipc_get_res *res, void *arg) { struct items *itms = arg; struct item *itm = calloc(1, sizeof(*itm)); if (objerr != IPC_OK) diemsg("list failed for '%s' (%s).\n", itms->argv[obji], ipc_strerror(objerr)); itms->count++; itm->num = (unsigned)res[IPC_TVAL_NUM].v.num; itm->peers = (unsigned)res[IPC_TVAL_PCOUNT].v.num; itm->st = tstate_char(res[IPC_TVAL_STATE].v.num); if (res[IPC_TVAL_NAME].type == IPC_TYPE_ERR) asprintf(&itm->name, "%s", ipc_strerror(res[IPC_TVAL_NAME].v.num)); else asprintf(&itm->name, "%.*s", (int)res[IPC_TVAL_NAME].v.str.l, res[IPC_TVAL_NAME].v.str.p); if (res[IPC_TVAL_DIR].type == IPC_TYPE_ERR) asprintf(&itm->dir, "%s", ipc_strerror(res[IPC_TVAL_DIR].v.num)); else asprintf(&itm->dir, "%.*s", (int)res[IPC_TVAL_DIR].v.str.l, res[IPC_TVAL_DIR].v.str.p); if (res[IPC_TVAL_LABEL].type == IPC_TYPE_ERR) asprintf(&itm->label, "%s", ipc_strerror(res[IPC_TVAL_LABEL].v.num)); else asprintf(&itm->label, "%.*s", (int)res[IPC_TVAL_LABEL].v.str.l, res[IPC_TVAL_LABEL].v.str.p); bin2hex(res[IPC_TVAL_IHASH].v.str.p, itm->hash, 20); itm->cgot = res[IPC_TVAL_CGOT].v.num; itm->csize = res[IPC_TVAL_CSIZE].v.num; itm->totup = res[IPC_TVAL_TOTUP].v.num; itm->downloaded = res[IPC_TVAL_SESSDWN].v.num; itm->uploaded = res[IPC_TVAL_SESSUP].v.num; itm->rate_up = res[IPC_TVAL_RATEUP].v.num; itm->rate_down = res[IPC_TVAL_RATEDWN].v.num; itm->torrent_pieces = (uint32_t)res[IPC_TVAL_PCCOUNT].v.num; itm->pieces_seen = (uint32_t)res[IPC_TVAL_PCSEEN].v.num; itm->pieces_have = (uint32_t)res[IPC_TVAL_PCGOT].v.num; itm_insert(itms, itm); } void print_items(struct items* itms, char *format) { struct item *p; char *it; BTPDQ_FOREACH(p, &itms->hd, entry) { if(format) { for (it = format; *it; ++it) { switch (*it) { case '%': ++it; switch (*it) { case '%': putchar('%'); break; case '#': printf("%u", p->num); break; case '^': printf("%lld", p->rate_up); break; case 'A': printf("%u", p->pieces_seen); break; case 'D': printf("%lld", p->downloaded); break; case 'H': printf("%u", p->pieces_have); break; case 'P': printf("%u", p->peers); break; case 'S': printf("%lld", p->csize); break; case 'U': printf("%lld", p->uploaded); break; case 'T': printf("%u", p->torrent_pieces); break; case 'd': printf("%s", p->dir); break; case 'g': printf("%lld", p->cgot); break; case 'h': printf("%s", p->hash); break; case 'l': printf("%s", p->label); break; case 'n': printf("%s", p->name); break; case 'p': print_percent(p->cgot, p->csize); break; case 'r': print_ratio(p->totup, p->csize); break; case 's': print_size(p->csize); break; case 't': printf("%c", p->st); break; case 'u': printf("%lld", p->totup); break; case 'v': printf("%lld", p->rate_down); break; case '\0': continue; } break; case '\\': ++it; switch (*it) { case 'n': putchar('\n'); break; case 't': putchar('\t'); break; case '\0': continue; } break; default: putchar(*it); break; } } } else { printf("%-40.40s %4u %c. ", p->name, p->num, p->st); print_percent(p->cgot, p->csize); print_size(p->csize); print_ratio(p->totup, p->csize); printf("\n"); } } } static struct option list_opts [] = { { "format", required_argument, NULL, 'f' }, { "help", no_argument, NULL, 'H' }, {NULL, 0, NULL, 0} }; void cmd_list(int argc, char **argv) { int ch, inactive = 0, active = 0; char *format = NULL; enum ipc_err code; enum ipc_twc twc; enum ipc_tval keys[] = { IPC_TVAL_NUM, IPC_TVAL_STATE, IPC_TVAL_NAME, IPC_TVAL_TOTUP, IPC_TVAL_CSIZE, IPC_TVAL_CGOT, IPC_TVAL_PCOUNT, IPC_TVAL_PCCOUNT, IPC_TVAL_PCSEEN, IPC_TVAL_PCGOT, IPC_TVAL_SESSUP, IPC_TVAL_SESSDWN, IPC_TVAL_RATEUP, IPC_TVAL_RATEDWN, IPC_TVAL_IHASH, IPC_TVAL_DIR, IPC_TVAL_LABEL }; size_t nkeys = ARRAY_COUNT(keys); struct items itms; while ((ch = getopt_long(argc, argv, "aif:", list_opts, NULL)) != -1) { switch (ch) { case 'a': active = 1; break; case 'f': format = optarg; break; case 'i': inactive = 1; break; default: usage_list(); } } argc -= optind; argv += optind; if (argc > 0) { if (inactive || active) usage_list(); itms.tps = malloc(argc * sizeof(*itms.tps)); for (itms.ntps = 0; itms.ntps < argc; itms.ntps++) { if (!torrent_spec(argv[itms.ntps], &itms.tps[itms.ntps])) exit(1); } } else { itms.ntps = 0; itms.tps = NULL; } if (inactive == active) twc = IPC_TWC_ALL; else if (inactive) twc = IPC_TWC_INACTIVE; else twc = IPC_TWC_ACTIVE; btpd_connect(); itms.count = 0; itms.argv = argv; BTPDQ_INIT(&itms.hd); if (itms.tps == NULL) code = btpd_tget_wc(ipc, twc, keys, nkeys, list_cb, &itms); else code = btpd_tget(ipc, itms.tps, itms.ntps, keys, nkeys, list_cb, &itms); if (code != IPC_OK) diemsg("command failed (%s).\n", ipc_strerror(code)); if (format == NULL) printf("%-40.40s NUM ST HAVE SIZE RATIO\n", "NAME"); print_items(&itms, format); }