It sorts by name instead of number (should probably add options for sorting). It now also takes torrents to list as optional arguments. Added ratio to btcli stat display. Changed the help text for both commands. Some code shuffle and other tweaks.master
@@ -24,6 +24,35 @@ handle_ipc_res(enum ipc_err code, const char *cmd, const char *target) | |||||
return code; | return code; | ||||
} | } | ||||
void | |||||
print_percent(long long part, long long whole) | |||||
{ | |||||
printf("%5.1f%% ", floor(1000.0 * part / whole) / 10); | |||||
} | |||||
void | |||||
print_rate(long long rate) | |||||
{ | |||||
if (rate >= 999.995 * (1 << 10)) | |||||
printf("%6.2fMB/s ", (double)rate / (1 << 20)); | |||||
else | |||||
printf("%6.2fkB/s ", (double)rate / (1 << 10)); | |||||
} | |||||
void | |||||
print_size(long long size) | |||||
{ | |||||
if (size >= 999.995 * (1 << 20)) | |||||
printf("%6.2fG ", (double)size / (1 << 30)); | |||||
else | |||||
printf("%6.2fM ", (double)size / (1 << 20)); | |||||
} | |||||
void | |||||
print_ratio(long long part, long long whole) | |||||
{ | |||||
printf("%7.2f ", (double)part / whole); | |||||
} | |||||
char | char | ||||
tstate_char(enum ipc_tstate ts) | tstate_char(enum ipc_tstate ts) | ||||
{ | { | ||||
@@ -6,6 +6,7 @@ | |||||
#include <getopt.h> | #include <getopt.h> | ||||
#include <inttypes.h> | #include <inttypes.h> | ||||
#include <limits.h> | #include <limits.h> | ||||
#include <math.h> | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
@@ -26,6 +27,11 @@ enum ipc_err handle_ipc_res(enum ipc_err err, const char *cmd, | |||||
char tstate_char(enum ipc_tstate ts); | char tstate_char(enum ipc_tstate ts); | ||||
int torrent_spec(char *arg, struct ipc_torrent *tp); | int torrent_spec(char *arg, struct ipc_torrent *tp); | ||||
void print_rate(long long rate); | |||||
void print_size(long long size); | |||||
void print_ratio(long long part, long long whole); | |||||
void print_percent(long long part, long long whole); | |||||
void usage_add(void); | void usage_add(void); | ||||
void cmd_add(int argc, char **argv); | void cmd_add(int argc, char **argv); | ||||
void usage_del(void); | void usage_del(void); | ||||
@@ -7,6 +7,19 @@ usage_list(void) | |||||
"List torrents.\n" | "List torrents.\n" | ||||
"\n" | "\n" | ||||
"Usage: list [-a] [-i]\n" | "Usage: list [-a] [-i]\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" | "\n" | ||||
); | ); | ||||
exit(1); | exit(1); | ||||
@@ -16,11 +29,15 @@ struct item { | |||||
unsigned num; | unsigned num; | ||||
char *name; | char *name; | ||||
char st; | char st; | ||||
long long cgot, csize, totup; | |||||
BTPDQ_ENTRY(item) entry; | BTPDQ_ENTRY(item) entry; | ||||
}; | }; | ||||
struct items { | struct items { | ||||
int count; | int count; | ||||
char **argv; | |||||
int ntps; | |||||
struct ipc_torrent *tps; | |||||
BTPDQ_HEAD(item_tq, item) hd; | BTPDQ_HEAD(item_tq, item) hd; | ||||
}; | }; | ||||
@@ -29,7 +46,7 @@ itm_insert(struct items *itms, struct item *itm) | |||||
{ | { | ||||
struct item *p; | struct item *p; | ||||
BTPDQ_FOREACH(p, &itms->hd, entry) | BTPDQ_FOREACH(p, &itms->hd, entry) | ||||
if (itm->num < p->num) | |||||
if (strcmp(itm->name, p->name) < 0) | |||||
break; | break; | ||||
if (p != NULL) | if (p != NULL) | ||||
BTPDQ_INSERT_BEFORE(p, itm, entry); | BTPDQ_INSERT_BEFORE(p, itm, entry); | ||||
@@ -42,6 +59,9 @@ list_cb(int obji, enum ipc_err objerr, struct ipc_get_res *res, void *arg) | |||||
{ | { | ||||
struct items *itms = arg; | struct items *itms = arg; | ||||
struct item *itm = calloc(1, sizeof(*itm)); | struct item *itm = calloc(1, sizeof(*itm)); | ||||
if (objerr != IPC_OK) | |||||
errx(1, "list failed for '%s' (%s)", itms->argv[obji], | |||||
ipc_strerror(objerr)); | |||||
itms->count++; | itms->count++; | ||||
itm->num = (unsigned)res[IPC_TVAL_NUM].v.num; | itm->num = (unsigned)res[IPC_TVAL_NUM].v.num; | ||||
itm->st = tstate_char(res[IPC_TVAL_STATE].v.num); | itm->st = tstate_char(res[IPC_TVAL_STATE].v.num); | ||||
@@ -50,21 +70,22 @@ list_cb(int obji, enum ipc_err objerr, struct ipc_get_res *res, void *arg) | |||||
else | else | ||||
asprintf(&itm->name, "%.*s", (int)res[IPC_TVAL_NAME].v.str.l, | asprintf(&itm->name, "%.*s", (int)res[IPC_TVAL_NAME].v.str.l, | ||||
res[IPC_TVAL_NAME].v.str.p); | res[IPC_TVAL_NAME].v.str.p); | ||||
itm->totup = res[IPC_TVAL_TOTUP].v.num; | |||||
itm->cgot = res[IPC_TVAL_CGOT].v.num; | |||||
itm->csize = res[IPC_TVAL_CSIZE].v.num; | |||||
itm_insert(itms, itm); | itm_insert(itms, itm); | ||||
} | } | ||||
void | void | ||||
print_items(struct items* itms) | print_items(struct items* itms) | ||||
{ | { | ||||
int n; | |||||
struct item *p; | struct item *p; | ||||
BTPDQ_FOREACH(p, &itms->hd, entry) { | BTPDQ_FOREACH(p, &itms->hd, entry) { | ||||
n = printf("%u: ", p->num); | |||||
while (n < 7) { | |||||
putchar(' '); | |||||
n++; | |||||
} | |||||
printf("%c. %s\n", p->st, p->name); | |||||
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"); | |||||
} | } | ||||
} | } | ||||
@@ -79,7 +100,9 @@ cmd_list(int argc, char **argv) | |||||
int ch, inactive = 0, active = 0; | int ch, inactive = 0, active = 0; | ||||
enum ipc_err code; | enum ipc_err code; | ||||
enum ipc_twc twc; | enum ipc_twc twc; | ||||
enum ipc_tval keys[] = { IPC_TVAL_NUM, IPC_TVAL_STATE, IPC_TVAL_NAME }; | |||||
enum ipc_tval keys[] = { IPC_TVAL_NUM, IPC_TVAL_STATE, IPC_TVAL_NAME, | |||||
IPC_TVAL_TOTUP, IPC_TVAL_CSIZE, IPC_TVAL_CGOT }; | |||||
size_t nkeys = sizeof(keys) / sizeof(keys[0]); | |||||
struct items itms; | struct items itms; | ||||
while ((ch = getopt_long(argc, argv, "ai", list_opts, NULL)) != -1) { | while ((ch = getopt_long(argc, argv, "ai", list_opts, NULL)) != -1) { | ||||
switch (ch) { | switch (ch) { | ||||
@@ -93,7 +116,22 @@ cmd_list(int argc, char **argv) | |||||
usage_list(); | 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) | if (inactive == active) | ||||
twc = IPC_TWC_ALL; | twc = IPC_TWC_ALL; | ||||
else if (inactive) | else if (inactive) | ||||
@@ -102,11 +140,15 @@ cmd_list(int argc, char **argv) | |||||
twc = IPC_TWC_ACTIVE; | twc = IPC_TWC_ACTIVE; | ||||
btpd_connect(); | btpd_connect(); | ||||
printf("NUM ST NAME\n"); | |||||
itms.count = 0; | itms.count = 0; | ||||
itms.argv = argv; | |||||
BTPDQ_INIT(&itms.hd); | BTPDQ_INIT(&itms.hd); | ||||
if ((code = btpd_tget_wc(ipc, twc, keys, 3, list_cb, &itms)) != IPC_OK) | |||||
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) | |||||
errx(1, "%s", ipc_strerror(code)); | errx(1, "%s", ipc_strerror(code)); | ||||
printf("%-40.40s NUM ST HAVE SIZE RATIO\n", "NAME"); | |||||
print_items(&itms); | print_items(&itms); | ||||
printf("Listed %d torrent%s.\n", itms.count, itms.count == 1 ? "" : "s"); | |||||
} | } |
@@ -1,5 +1,3 @@ | |||||
#include <math.h> | |||||
#include "btcli.h" | #include "btcli.h" | ||||
void | void | ||||
@@ -8,11 +6,11 @@ usage_stat(void) | |||||
printf( | printf( | ||||
"Display stats for active torrents.\n" | "Display stats for active torrents.\n" | ||||
"\n" | "\n" | ||||
"Usage: stat [-i] [-w seconds] [file ...]\n" | |||||
"Usage: stat [-i] [-w seconds] [torrent ...]\n" | |||||
"\n" | "\n" | ||||
"Arguments:\n" | "Arguments:\n" | ||||
"file ...\n" | |||||
"\tOnly display stats for the given torrent(s).\n" | |||||
"torrent ...\n" | |||||
"\tOnly display stats for the given torrents.\n" | |||||
"\n" | "\n" | ||||
"Options:\n" | "Options:\n" | ||||
"-i\n" | "-i\n" | ||||
@@ -32,7 +30,7 @@ struct btstat { | |||||
enum ipc_tstate state; | enum ipc_tstate state; | ||||
unsigned peers, tr_errors; | unsigned peers, tr_errors; | ||||
long long content_got, content_size, downloaded, uploaded, rate_up, | long long content_got, content_size, downloaded, uploaded, rate_up, | ||||
rate_down; | |||||
rate_down, tot_up; | |||||
uint32_t pieces_seen, torrent_pieces; | uint32_t pieces_seen, torrent_pieces; | ||||
}; | }; | ||||
@@ -51,6 +49,7 @@ static enum ipc_tval stkeys[] = { | |||||
IPC_TVAL_PCSEEN, | IPC_TVAL_PCSEEN, | ||||
IPC_TVAL_SESSUP, | IPC_TVAL_SESSUP, | ||||
IPC_TVAL_SESSDWN, | IPC_TVAL_SESSDWN, | ||||
IPC_TVAL_TOTUP, | |||||
IPC_TVAL_RATEUP, | IPC_TVAL_RATEUP, | ||||
IPC_TVAL_RATEDWN, | IPC_TVAL_RATEDWN, | ||||
IPC_TVAL_CGOT, | IPC_TVAL_CGOT, | ||||
@@ -59,30 +58,6 @@ static enum ipc_tval stkeys[] = { | |||||
static size_t nstkeys = sizeof(stkeys) / sizeof(stkeys[0]); | static size_t nstkeys = sizeof(stkeys) / sizeof(stkeys[0]); | ||||
static void | |||||
print_percent(long long part, long long whole) | |||||
{ | |||||
printf("%5.1f%% ", floor(1000.0 * part / whole) / 10); | |||||
} | |||||
static void | |||||
print_rate(long long rate) | |||||
{ | |||||
if (rate >= 999.995 * (1 << 10)) | |||||
printf("%6.2fMB/s ", (double)rate / (1 << 20)); | |||||
else | |||||
printf("%6.2fkB/s ", (double)rate / (1 << 10)); | |||||
} | |||||
static void | |||||
print_size(long long size) | |||||
{ | |||||
if (size >= 999.995 * (1 << 20)) | |||||
printf("%6.2fG ", (double)size / (1 << 30)); | |||||
else | |||||
printf("%6.2fM ", (double)size / (1 << 20)); | |||||
} | |||||
static void | static void | ||||
print_stat(struct btstat *st) | print_stat(struct btstat *st) | ||||
{ | { | ||||
@@ -91,7 +66,8 @@ print_stat(struct btstat *st) | |||||
print_rate(st->rate_down); | print_rate(st->rate_down); | ||||
print_size(st->uploaded); | print_size(st->uploaded); | ||||
print_rate(st->rate_up); | print_rate(st->rate_up); | ||||
printf("%5u ", st->peers); | |||||
print_ratio(st->tot_up, st->content_size); | |||||
printf("%4u ", st->peers); | |||||
print_percent(st->pieces_seen, st->torrent_pieces); | print_percent(st->pieces_seen, st->torrent_pieces); | ||||
if (st->tr_errors > 0) | if (st->tr_errors > 0) | ||||
printf("E%u", st->tr_errors); | printf("E%u", st->tr_errors); | ||||
@@ -117,18 +93,14 @@ stat_cb(int obji, enum ipc_err objerr, struct ipc_get_res *res, void *arg) | |||||
tot->rate_down += (st.rate_down = res[IPC_TVAL_RATEDWN].v.num); | tot->rate_down += (st.rate_down = res[IPC_TVAL_RATEDWN].v.num); | ||||
tot->rate_up += (st.rate_up = res[IPC_TVAL_RATEUP].v.num); | tot->rate_up += (st.rate_up = res[IPC_TVAL_RATEUP].v.num); | ||||
tot->peers += (st.peers = res[IPC_TVAL_PCOUNT].v.num); | tot->peers += (st.peers = res[IPC_TVAL_PCOUNT].v.num); | ||||
tot->tot_up += (st.tot_up = res[IPC_TVAL_TOTUP].v.num); | |||||
if ((st.tr_errors = res[IPC_TVAL_TRERR].v.num) > 0) | if ((st.tr_errors = res[IPC_TVAL_TRERR].v.num) > 0) | ||||
tot->tr_errors++; | tot->tr_errors++; | ||||
if (cba->individual) { | if (cba->individual) { | ||||
if (cba->names) | if (cba->names) | ||||
printf("%.*s\n", (int)res[IPC_TVAL_NAME].v.str.l, | printf("%.*s\n", (int)res[IPC_TVAL_NAME].v.str.l, | ||||
res[IPC_TVAL_NAME].v.str.p); | res[IPC_TVAL_NAME].v.str.p); | ||||
int n = printf("%u:", st.num); | |||||
while (n < 7) { | |||||
putchar(' '); | |||||
n++; | |||||
} | |||||
printf("%c. ", tstate_char(st.state)); | |||||
printf("%4u %c. ", st.num, tstate_char(st.state)); | |||||
print_stat(&st); | print_stat(&st); | ||||
} | } | ||||
} | } | ||||
@@ -149,10 +121,11 @@ again: | |||||
if (header == 0) { | if (header == 0) { | ||||
if (individual) { | if (individual) { | ||||
header = 1; | header = 1; | ||||
printf("NUM ST "); | |||||
printf(" NUM ST "); | |||||
} else | } else | ||||
header = 20; | header = 20; | ||||
printf(" HAVE DLOAD RTDWN ULOAD RTUP PEERS AVAIL\n"); | |||||
printf(" HAVE DLOAD RTDWN ULOAD RTUP RATIO CONN" | |||||
" AVAIL\n"); | |||||
} | } | ||||
bzero(&cba.tot, sizeof(cba.tot)); | bzero(&cba.tot, sizeof(cba.tot)); | ||||
@@ -165,9 +138,9 @@ again: | |||||
if (err != IPC_OK) | if (err != IPC_OK) | ||||
errx(1, ipc_strerror(err)); | errx(1, ipc_strerror(err)); | ||||
if (names) | if (names) | ||||
printf("-----\n"); | |||||
printf("-------\n"); | |||||
if (individual) | if (individual) | ||||
printf("Total: "); | |||||
printf(" "); | |||||
print_stat(&cba.tot); | print_stat(&cba.tot); | ||||
if (seconds > 0) { | if (seconds > 0) { | ||||
sleep(seconds); | sleep(seconds); | ||||