X-Git-Url: https://git.distorted.org.uk/~mdw/disorder/blobdiff_plain/00861dcb1b67a282893f3d30187459c6384cbee7..c12575c6cea802f894df6ca8b04c1b3656496592:/lib/client.c diff --git a/lib/client.c b/lib/client.c index 1cb73a4..521a4c7 100644 --- a/lib/client.c +++ b/lib/client.c @@ -1,6 +1,6 @@ /* * This file is part of DisOrder. - * Copyright (C) 2004-2009 Richard Kettlewell + * Copyright (C) 2004-2010 Richard Kettlewell * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -151,12 +151,16 @@ static int check_response(disorder_client *c, char **rp) { } } +/** @brief Marker for a command body */ +static const char disorder_body[1]; + +/** @brief Marker for a list of args */ +static const char disorder_list[1]; + /** @brief Issue a command and parse a simple response * @param c Client * @param rp Where to store result, or NULL * @param cmd Command - * @param body Body or NULL - * @param nbody Length of body or -1 * @param ap Arguments (UTF-8), terminated by (char *)0 * @return 0 on success, non-0 on error * @@ -168,22 +172,28 @@ static int check_response(disorder_client *c, char **rp) { * NB that the response will NOT be converted to the local encoding * nor will quotes be stripped. See dequote(). * - * If @p body is not NULL then the body is sent immediately after the - * command. @p nbody should be the number of lines or @c -1 to count - * them if @p body is NULL-terminated. + * Put @ref disorder_body in the argument list followed by a char ** + * and int giving the body to follow the command. If the int is @c -1 + * then the list is assumed to be NULL-terminated. This may be used + * only once. + * + * Put @ref disorder_list in the argument list followed by a char ** + * and int giving a list of arguments to include. If the int is @c -1 + * then the list is assumed to be NULL-terminated. This may be used + * any number of times. * * Usually you would call this via one of the following interfaces: * - disorder_simple() - * - disorder_simple_body() - * - disorder_simple_list() */ static int disorder_simple_v(disorder_client *c, char **rp, const char *cmd, - char **body, int nbody, va_list ap) { const char *arg; struct dynstr d; + char **body = NULL; + int nbody = 0; + int has_body = 0; if(!c->fpout) { c->last = "not connected"; @@ -194,8 +204,25 @@ static int disorder_simple_v(disorder_client *c, dynstr_init(&d); dynstr_append_string(&d, cmd); while((arg = va_arg(ap, const char *))) { - dynstr_append(&d, ' '); - dynstr_append_string(&d, quoteutf8(arg)); + if(arg == disorder_body) { + body = va_arg(ap, char **); + nbody = va_arg(ap, int); + has_body = 1; + } else if(arg == disorder_list) { + char **list = va_arg(ap, char **); + int nlist = va_arg(ap, int); + if(nlist < 0) { + for(nlist = 0; list[nlist]; ++nlist) + ; + } + for(int n = 0; n < nlist; ++n) { + dynstr_append(&d, ' '); + dynstr_append_string(&d, quoteutf8(arg)); + } + } else { + dynstr_append(&d, ' '); + dynstr_append_string(&d, quoteutf8(arg)); + } } dynstr_append(&d, '\n'); dynstr_terminate(&d); @@ -203,7 +230,7 @@ static int disorder_simple_v(disorder_client *c, if(fputs(d.vec, c->fpout) < 0) goto write_error; xfree(d.vec); - if(body) { + if(has_body) { if(nbody < 0) for(nbody = 0; body[nbody]; ++nbody) ; @@ -253,30 +280,7 @@ static int disorder_simple(disorder_client *c, int ret; va_start(ap, cmd); - ret = disorder_simple_v(c, rp, cmd, 0, 0, ap); - va_end(ap); - return ret; -} - -/** @brief Issue a command with a body and parse a simple response - * @param c Client - * @param rp Where to store result, or NULL (UTF-8) - * @param body Pointer to body - * @param nbody Size of body - * @param cmd Command - * @return 0 on success, non-0 on error - * - * See disorder_simple(). - */ -static int disorder_simple_body(disorder_client *c, - char **rp, - char **body, int nbody, - const char *cmd, ...) { - va_list ap; - int ret; - - va_start(ap, cmd); - ret = disorder_simple_v(c, rp, cmd, body, nbody, ap); + ret = disorder_simple_v(c, rp, cmd, ap); va_end(ap); return ret; } @@ -522,37 +526,24 @@ int disorder_close(disorder_client *c) { return ret; } -/** @brief Move a track - * @param c Client - * @param track Track to move (UTF-8) - * @param delta Distance to move by - * @return 0 on success, non-0 on error - */ -int disorder_move(disorder_client *c, const char *track, int delta) { - char d[16]; - - byte_snprintf(d, sizeof d, "%d", delta); - return disorder_simple(c, 0, "move", track, d, (char *)0); -} - static void client_error(const char *msg, void attribute((unused)) *u) { disorder_error(0, "error parsing reply: %s", msg); } -/** @brief Get currently playing track +/** @brief Get a single queue entry * @param c Client + * @param cmd Command * @param qp Where to store track information * @return 0 on success, non-0 on error - * - * @p qp gets NULL if no track is playing. */ -int disorder_playing(disorder_client *c, struct queue_entry **qp) { +static int onequeue(disorder_client *c, const char *cmd, + struct queue_entry **qp) { char *r; struct queue_entry *q; int rc; - if((rc = disorder_simple(c, &r, "playing", (char *)0))) + if((rc = disorder_simple(c, &r, cmd, (char *)0))) return rc; if(r) { q = xmalloc(sizeof *q); @@ -565,14 +556,11 @@ int disorder_playing(disorder_client *c, struct queue_entry **qp) { } /** @brief Fetch the queue, recent list, etc */ -static int disorder_somequeue(disorder_client *c, - const char *cmd, struct queue_entry **qp) { +static int readqueue(disorder_client *c, + struct queue_entry **qp) { struct queue_entry *qh, **qt = &qh, *q; char *l; - int rc; - if((rc = disorder_simple(c, 0, cmd, (char *)0))) - return rc; while(inputline(c->ident, c->fpin, &l, '\n') >= 0) { if(!strcmp(l, ".")) { *qt = 0; @@ -591,34 +579,12 @@ static int disorder_somequeue(disorder_client *c, byte_xasprintf((char **)&c->last, "input error: %s", strerror(errno)); disorder_error(errno, "error reading %s", c->ident); } else { - c->last = "input error: unexpxected EOF"; + c->last = "input error: unexpected EOF"; disorder_error(0, "error reading %s: unexpected EOF", c->ident); } return -1; } -/** @brief Get recently played tracks - * @param c Client - * @param qp Where to store track information - * @return 0 on success, non-0 on error - * - * The last entry in the list is the most recently played track. - */ -int disorder_recent(disorder_client *c, struct queue_entry **qp) { - return disorder_somequeue(c, "recent", qp); -} - -/** @brief Get queue - * @param c Client - * @param qp Where to store track information - * @return 0 on success, non-0 on error - * - * The first entry in the list will be played next. - */ -int disorder_queue(disorder_client *c, struct queue_entry **qp) { - return disorder_somequeue(c, "queue", qp); -} - /** @brief Read a dot-stuffed list * @param c Client * @param vecp Where to store list (UTF-8) @@ -654,33 +620,6 @@ static int readlist(disorder_client *c, char ***vecp, int *nvecp) { return -1; } -/** @brief Issue a comamnd and get a list response - * @param c Client - * @param vecp Where to store list (UTF-8) - * @param nvecp Where to store number of items, or NULL - * @param cmd Command - * @return 0 on success, non-0 on error - * - * The remaining arguments are command arguments, terminated by (char - * *)0. They should be in UTF-8. - * - * 5xx responses count as errors. - * - * See disorder_simple(). - */ -static int disorder_simple_list(disorder_client *c, - char ***vecp, int *nvecp, - const char *cmd, ...) { - va_list ap; - int ret; - - va_start(ap, cmd); - ret = disorder_simple_v(c, 0, cmd, 0, 0, ap); - va_end(ap); - if(ret) return ret; - return readlist(c, vecp, nvecp); -} - /** @brief Return the user we logged in with * @param c Client * @return User name (owned by @p c, don't modify) @@ -689,29 +628,36 @@ char *disorder_user(disorder_client *c) { return c->user; } -static void pref_error_handler(const char *msg, +static void pairlist_error_handler(const char *msg, void attribute((unused)) *u) { - disorder_error(0, "error handling 'prefs' reply: %s", msg); + disorder_error(0, "error handling key-value pair reply: %s", msg); } -/** @brief Get all preferences for a trcak +/** @brief Get a list of key-value pairs * @param c Client - * @param track Track name * @param kp Where to store linked list of preferences + * @param cmd Command + * @param ... Arguments * @return 0 on success, non-0 on error */ -int disorder_prefs(disorder_client *c, const char *track, struct kvp **kp) { +static int pairlist(disorder_client *c, struct kvp **kp, const char *cmd, ...) { char **vec, **pvec; int nvec, npvec, n, rc; struct kvp *k; + va_list ap; - if((rc = disorder_simple_list(c, &vec, &nvec, "prefs", track, (char *)0))) + va_start(ap, cmd); + rc = disorder_simple_v(c, 0, cmd, ap); + va_end(ap); + if(rc) return rc; + if((rc = readlist(c, &vec, &nvec))) + return rc; for(n = 0; n < nvec; ++n) { - if(!(pvec = split(vec[n], &npvec, SPLIT_QUOTES, pref_error_handler, 0))) + if(!(pvec = split(vec[n], &npvec, SPLIT_QUOTES, pairlist_error_handler, 0))) return -1; if(npvec != 2) { - pref_error_handler("malformed response", 0); + pairlist_error_handler("malformed response", 0); return -1; } *kp = k = xmalloc(sizeof *k); @@ -742,60 +688,6 @@ static int boolean(const char *cmd, const char *value, return 0; } -/** @brief Get the length of a track - * @param c Client - * @param track Track name (UTF-8) - * @param valuep Where to store length in seconds - * @return 0 on success, non-0 on error - * - * If the length is unknown 0 is returned. - */ -int disorder_length(disorder_client *c, const char *track, - long *valuep) { - char *value; - int rc; - - if((rc = disorder_simple(c, &value, "length", track, (char *)0))) - return rc; - *valuep = atol(value); - return 0; -} - -/** @brief Set volume - * @param c Client - * @param left New left channel value - * @param right New right channel value - * @return 0 on success, non-0 on error - */ -int disorder_set_volume(disorder_client *c, int left, int right) { - char *ls, *rs; - - if(byte_asprintf(&ls, "%d", left) < 0 - || byte_asprintf(&rs, "%d", right) < 0) - return -1; - return disorder_simple(c, 0, "volume", ls, rs, (char *)0); -} - -/** @brief Get volume - * @param c Client - * @param left Where to store left channel value - * @param right Where to store right channel value - * @return 0 on success, non-0 on error - */ -int disorder_get_volume(disorder_client *c, int *left, int *right) { - char *r; - int rc; - - if((rc = disorder_simple(c, &r, "volume", (char *)0))) - return rc; - if(sscanf(r, "%d %d", left, right) != 2) { - c->last = "malformed volume response"; - disorder_error(0, "error parsing response to 'volume': '%s'", r); - return -1; - } - return 0; -} - /** @brief Log to a sink * @param c Client * @param s Sink to write log lines to @@ -817,75 +709,6 @@ int disorder_log(disorder_client *c, struct sink *s) { return 0; } -/** @brief Get recently added tracks - * @param c Client - * @param vecp Where to store pointer to list (UTF-8) - * @param nvecp Where to store count - * @param max Maximum tracks to fetch, or 0 for all available - * @return 0 on success, non-0 on error - */ -int disorder_new_tracks(disorder_client *c, - char ***vecp, int *nvecp, - int max) { - char limit[32]; - - sprintf(limit, "%d", max); - return disorder_simple_list(c, vecp, nvecp, "new", limit, (char *)0); -} - -/** @brief Get server's RTP address information - * @param c Client - * @param addressp Where to store address (UTF-8) - * @param portp Where to store port (UTF-8) - * @return 0 on success, non-0 on error - */ -int disorder_rtp_address(disorder_client *c, char **addressp, char **portp) { - char *r; - int rc, n; - char **vec; - - if((rc = disorder_simple(c, &r, "rtp-address", (char *)0))) - return rc; - vec = split(r, &n, SPLIT_QUOTES, 0, 0); - if(n != 2) { - c->last = "malformed RTP address"; - disorder_error(0, "malformed rtp-address reply"); - return -1; - } - *addressp = vec[0]; - *portp = vec[1]; - return 0; -} - -/** @brief Get details of a scheduled event - * @param c Client - * @param id Event ID - * @param actiondatap Where to put details - * @return 0 on success, non-0 on error - */ -int disorder_schedule_get(disorder_client *c, const char *id, - struct kvp **actiondatap) { - char **lines, **bits; - int rc, nbits; - - *actiondatap = 0; - if((rc = disorder_simple_list(c, &lines, NULL, - "schedule-get", id, (char *)0))) - return rc; - while(*lines) { - if(!(bits = split(*lines++, &nbits, SPLIT_QUOTES, 0, 0))) { - disorder_error(0, "invalid schedule-get reply: cannot split line"); - return -1; - } - if(nbits != 2) { - disorder_error(0, "invalid schedule-get reply: wrong number of fields"); - return -1; - } - kvp_set(actiondatap, bits[0], bits[1]); - } - return 0; -} - /** @brief Add a scheduled event * @param c Client * @param when When to trigger the event @@ -926,21 +749,6 @@ int disorder_schedule_add(disorder_client *c, return rc; } -/** @brief Set the contents of a playlst - * @param c Client - * @param playlist Playlist to modify - * @param tracks List of tracks - * @param ntracks Length of @p tracks (or -1 to count up to the first NULL) - * @return 0 on success, non-0 on error - */ -int disorder_playlist_set(disorder_client *c, - const char *playlist, - char **tracks, - int ntracks) { - return disorder_simple_body(c, 0, tracks, ntracks, - "playlist-set", playlist, (char *)0); -} - #include "client-stubs.c" /*