From: Richard Kettlewell Date: Sat, 6 Aug 2011 17:18:25 +0000 (+0100) Subject: protogen: support multiple return values. X-Git-Tag: branchpoint-5.1~22^2~10 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/commitdiff_plain/c12575c6cea802f894df6ca8b04c1b3656496592 protogen: support multiple return values. This allow volume and rtp-address to be handled. Also fix up some new-tracks stuff missed in the previous change. --- diff --git a/cgi/disorder-cgi.h b/cgi/disorder-cgi.h index 4587e78..43ad241 100644 --- a/cgi/disorder-cgi.h +++ b/cgi/disorder-cgi.h @@ -92,8 +92,8 @@ extern struct queue_entry *dcgi_queue; extern struct queue_entry *dcgi_playing; extern struct queue_entry *dcgi_recent; -extern int dcgi_volume_left; -extern int dcgi_volume_right; +extern long dcgi_volume_left; +extern long dcgi_volume_right; extern char **dcgi_new; extern int dcgi_nnew; diff --git a/cgi/lookup.c b/cgi/lookup.c index fd94900..2b6d594 100644 --- a/cgi/lookup.c +++ b/cgi/lookup.c @@ -33,8 +33,8 @@ struct queue_entry *dcgi_queue; struct queue_entry *dcgi_playing; struct queue_entry *dcgi_recent; -int dcgi_volume_left; -int dcgi_volume_right; +long dcgi_volume_left; +long dcgi_volume_right; char **dcgi_new; int dcgi_nnew; @@ -68,7 +68,7 @@ void dcgi_lookup(unsigned want) { queuemap_add(dcgi_playing); } if(need & DCGI_NEW) - disorder_new_tracks(dcgi_client, &dcgi_new, &dcgi_nnew, 0); + disorder_new_tracks(dcgi_client, 0, &dcgi_new, &dcgi_nnew); if(need & DCGI_RECENT) { /* we need to reverse the order of the list */ disorder_recent(dcgi_client, &r); diff --git a/cgi/macros-disorder.c b/cgi/macros-disorder.c index dc96c73..1ef1ee1 100644 --- a/cgi/macros-disorder.c +++ b/cgi/macros-disorder.c @@ -458,7 +458,7 @@ static int exp_volume(int attribute((unused)) nargs, struct sink *output, void attribute((unused)) *u) { dcgi_lookup(DCGI_VOLUME); - return sink_printf(output, "%d", + return sink_printf(output, "%ld", !strcmp(args[0], "left") ? dcgi_volume_left : dcgi_volume_right) < 0 ? -1 : 0; } diff --git a/lib/client-stubs.c b/lib/client-stubs.c index 92dd3e2..310505a 100644 --- a/lib/client-stubs.c +++ b/lib/client-stubs.c @@ -22,132 +22,228 @@ */ int disorder_adopt(disorder_client *c, const char *id) { - return disorder_simple(c, 0, "adopt", id, (char *)0); + return disorder_simple(c, NULL, "adopt", id, (char *)NULL); } int disorder_adduser(disorder_client *c, const char *user, const char *password, const char *rights) { - return disorder_simple(c, 0, "adduser", user, password, rights, (char *)0); + return disorder_simple(c, NULL, "adduser", user, password, rights, (char *)NULL); } int disorder_allfiles(disorder_client *c, const char *dir, const char *re, char ***filesp, int *nfilesp) { - return disorder_simple_list(c, filesp, nfilesp, "allfiles", dir, re, (char *)0); + int rc = disorder_simple(c, NULL, "allfiles", dir, re, (char *)NULL); + if(rc) + return rc; + if(readlist(c, filesp, nfilesp)) + return -1; + return 0; } int disorder_confirm(disorder_client *c, const char *confirmation) { - char *u; - int rc; - if((rc = disorder_simple(c, &u, "confirm", confirmation, (char *)0))) + char **v, *r; + int nv; + int rc = disorder_simple(c, &r, "confirm", confirmation, (char *)NULL); + if(rc) return rc; - c->user = u; + v = split(r, &nv, SPLIT_QUOTES, 0, 0); + if(nv != 1) { + disorder_error(0, "malformed reply to %s", "confirm"); + return -1; + } + c->user = v[0]; return 0; } int disorder_cookie(disorder_client *c, const char *cookie) { - char *u; - int rc; - if((rc = disorder_simple(c, &u, "cookie", cookie, (char *)0))) + char **v, *r; + int nv; + int rc = disorder_simple(c, &r, "cookie", cookie, (char *)NULL); + if(rc) return rc; - c->user = u; + v = split(r, &nv, SPLIT_QUOTES, 0, 0); + if(nv != 1) { + disorder_error(0, "malformed reply to %s", "cookie"); + return -1; + } + c->user = v[0]; return 0; } int disorder_deluser(disorder_client *c, const char *user) { - return disorder_simple(c, 0, "deluser", user, (char *)0); + return disorder_simple(c, NULL, "deluser", user, (char *)NULL); } int disorder_dirs(disorder_client *c, const char *dir, const char *re, char ***filesp, int *nfilesp) { - return disorder_simple_list(c, filesp, nfilesp, "dirs", dir, re, (char *)0); + int rc = disorder_simple(c, NULL, "dirs", dir, re, (char *)NULL); + if(rc) + return rc; + if(readlist(c, filesp, nfilesp)) + return -1; + return 0; } int disorder_disable(disorder_client *c) { - return disorder_simple(c, 0, "disable", (char *)0); + return disorder_simple(c, NULL, "disable", (char *)NULL); } int disorder_edituser(disorder_client *c, const char *username, const char *property, const char *value) { - return disorder_simple(c, 0, "edituser", username, property, value, (char *)0); + return disorder_simple(c, NULL, "edituser", username, property, value, (char *)NULL); } int disorder_enable(disorder_client *c) { - return disorder_simple(c, 0, "enable", (char *)0); + return disorder_simple(c, NULL, "enable", (char *)NULL); } int disorder_enabled(disorder_client *c, int *enabledp) { - char *v; - int rc; - if((rc = disorder_simple(c, &v, "enabled", (char *)0))) + char **v, *r; + int nv; + int rc = disorder_simple(c, &r, "enabled", (char *)NULL); + if(rc) return rc; - return boolean("enabled", v, enabledp); + v = split(r, &nv, SPLIT_QUOTES, 0, 0); + if(nv != 1) { + disorder_error(0, "malformed reply to %s", "enabled"); + return -1; + } + if(boolean("enabled", v[0], enabledp)) + return -1; + return 0; } int disorder_exists(disorder_client *c, const char *track, int *existsp) { - char *v; - int rc; - if((rc = disorder_simple(c, &v, "exists", track, (char *)0))) + char **v, *r; + int nv; + int rc = disorder_simple(c, &r, "exists", track, (char *)NULL); + if(rc) return rc; - return boolean("exists", v, existsp); + v = split(r, &nv, SPLIT_QUOTES, 0, 0); + if(nv != 1) { + disorder_error(0, "malformed reply to %s", "exists"); + return -1; + } + if(boolean("exists", v[0], existsp)) + return -1; + return 0; } int disorder_files(disorder_client *c, const char *dir, const char *re, char ***filesp, int *nfilesp) { - return disorder_simple_list(c, filesp, nfilesp, "files", dir, re, (char *)0); + int rc = disorder_simple(c, NULL, "files", dir, re, (char *)NULL); + if(rc) + return rc; + if(readlist(c, filesp, nfilesp)) + return -1; + return 0; } int disorder_get(disorder_client *c, const char *track, const char *pref, char **valuep) { - return dequote(disorder_simple(c, valuep, "get", track, pref, (char *)0), valuep); + char **v, *r; + int nv; + int rc = disorder_simple(c, &r, "get", track, pref, (char *)NULL); + if(rc) + return rc; + v = split(r, &nv, SPLIT_QUOTES, 0, 0); + if(nv != 1) { + disorder_error(0, "malformed reply to %s", "get"); + return -1; + } + *valuep = v[0]; + return 0; } int disorder_get_global(disorder_client *c, const char *pref, char **valuep) { - return dequote(disorder_simple(c, valuep, "get-global", pref, (char *)0), valuep); + char **v, *r; + int nv; + int rc = disorder_simple(c, &r, "get-global", pref, (char *)NULL); + if(rc) + return rc; + v = split(r, &nv, SPLIT_QUOTES, 0, 0); + if(nv != 1) { + disorder_error(0, "malformed reply to %s", "get-global"); + return -1; + } + *valuep = v[0]; + return 0; } int disorder_length(disorder_client *c, const char *track, long *lengthp) { - char *v; - int rc; - - if((rc = disorder_simple(c, &v, "length", track, (char *)0))) + char **v, *r; + int nv; + int rc = disorder_simple(c, &r, "length", track, (char *)NULL); + if(rc) return rc; - *lengthp = atol(v); - xfree(v); + v = split(r, &nv, SPLIT_QUOTES, 0, 0); + if(nv != 1) { + disorder_error(0, "malformed reply to %s", "length"); + return -1; + } + *lengthp = atol(v[0]); return 0; } int disorder_make_cookie(disorder_client *c, char **cookiep) { - return dequote(disorder_simple(c, cookiep, "make-cookie", (char *)0), cookiep); + char **v, *r; + int nv; + int rc = disorder_simple(c, &r, "make-cookie", (char *)NULL); + if(rc) + return rc; + v = split(r, &nv, SPLIT_QUOTES, 0, 0); + if(nv != 1) { + disorder_error(0, "malformed reply to %s", "make-cookie"); + return -1; + } + *cookiep = v[0]; + return 0; } int disorder_move(disorder_client *c, const char *track, long delta) { char buf_delta[16]; byte_snprintf(buf_delta, sizeof buf_delta, "%ld", delta); - return disorder_simple(c, 0, "move", track, buf_delta, (char *)0); + return disorder_simple(c, NULL, "move", track, buf_delta, (char *)NULL); } int disorder_moveafter(disorder_client *c, const char *target, char **ids, int nids) { - return disorder_simple(c, 0, "moveafter", target, disorder_list, ids, nids, (char *)0); + return disorder_simple(c, NULL, "moveafter", target, disorder_list, ids, nids, (char *)NULL); } int disorder_new_tracks(disorder_client *c, long max, char ***tracksp, int *ntracksp) { char buf_max[16]; byte_snprintf(buf_max, sizeof buf_max, "%ld", max); - return disorder_simple_list(c, tracksp, ntracksp, "new", buf_max, (char *)0); + int rc = disorder_simple(c, NULL, "new", buf_max, (char *)NULL); + if(rc) + return rc; + if(readlist(c, tracksp, ntracksp)) + return -1; + return 0; } int disorder_nop(disorder_client *c) { - return disorder_simple(c, 0, "nop", (char *)0); + return disorder_simple(c, NULL, "nop", (char *)NULL); } int disorder_part(disorder_client *c, const char *track, const char *context, const char *part, char **partp) { - return dequote(disorder_simple(c, partp, "part", track, context, part, (char *)0), partp); + char **v, *r; + int nv; + int rc = disorder_simple(c, &r, "part", track, context, part, (char *)NULL); + if(rc) + return rc; + v = split(r, &nv, SPLIT_QUOTES, 0, 0); + if(nv != 1) { + disorder_error(0, "malformed reply to %s", "part"); + return -1; + } + *partp = v[0]; + return 0; } int disorder_pause(disorder_client *c) { - return disorder_simple(c, 0, "pause", (char *)0); + return disorder_simple(c, NULL, "pause", (char *)NULL); } int disorder_play(disorder_client *c, const char *track, char **idp) { - return dequote(disorder_simple(c, idp, "play", track, (char *)0), idp); + return disorder_simple(c, idp, "play", track, (char *)NULL); } int disorder_playafter(disorder_client *c, const char *target, char **tracks, int ntracks) { - return disorder_simple(c, 0, "playafter", target, disorder_list, tracks, ntracks, (char *)0); + return disorder_simple(c, NULL, "playafter", target, disorder_list, tracks, ntracks, (char *)NULL); } int disorder_playing(disorder_client *c, struct queue_entry **playingp) { @@ -155,154 +251,291 @@ int disorder_playing(disorder_client *c, struct queue_entry **playingp) { } int disorder_playlist_delete(disorder_client *c, const char *playlist) { - return disorder_simple(c, 0, "playlist-delete", playlist, (char *)0); + return disorder_simple(c, NULL, "playlist-delete", playlist, (char *)NULL); } int disorder_playlist_get(disorder_client *c, const char *playlist, char ***tracksp, int *ntracksp) { - return disorder_simple_list(c, tracksp, ntracksp, "playlist-get", playlist, (char *)0); + int rc = disorder_simple(c, NULL, "playlist-get", playlist, (char *)NULL); + if(rc) + return rc; + if(readlist(c, tracksp, ntracksp)) + return -1; + return 0; } int disorder_playlist_get_share(disorder_client *c, const char *playlist, char **sharep) { - return dequote(disorder_simple(c, sharep, "playlist-get-share", playlist, (char *)0), sharep); + return disorder_simple(c, sharep, "playlist-get-share", playlist, (char *)NULL); } int disorder_playlist_lock(disorder_client *c, const char *playlist) { - return disorder_simple(c, 0, "playlist-lock", playlist, (char *)0); + return disorder_simple(c, NULL, "playlist-lock", playlist, (char *)NULL); } int disorder_playlist_set(disorder_client *c, const char *playlist, char **tracks, int ntracks) { - return disorder_simple(c, 0, "playlist-set", playlist, disorder_body, tracks, ntracks, (char *)0); + return disorder_simple(c, NULL, "playlist-set", playlist, disorder_body, tracks, ntracks, (char *)NULL); } int disorder_playlist_set_share(disorder_client *c, const char *playlist, const char *share) { - return disorder_simple(c, 0, "playlist-set-share", playlist, share, (char *)0); + return disorder_simple(c, NULL, "playlist-set-share", playlist, share, (char *)NULL); } int disorder_playlist_unlock(disorder_client *c) { - return disorder_simple(c, 0, "playlist-unlock", (char *)0); + return disorder_simple(c, NULL, "playlist-unlock", (char *)NULL); } int disorder_playlists(disorder_client *c, char ***playlistsp, int *nplaylistsp) { - return disorder_simple_list(c, playlistsp, nplaylistsp, "playlists", (char *)0); + int rc = disorder_simple(c, NULL, "playlists", (char *)NULL); + if(rc) + return rc; + if(readlist(c, playlistsp, nplaylistsp)) + return -1; + return 0; } int disorder_prefs(disorder_client *c, const char *track, struct kvp **prefsp) { - return pairlist(c, prefsp, "prefs", track, (char *)0); + return pairlist(c, prefsp, "prefs", track, (char *)NULL); } int disorder_queue(disorder_client *c, struct queue_entry **queuep) { - return somequeue(c, "queue", queuep); + int rc = disorder_simple(c, NULL, "queue", (char *)NULL); + if(rc) + return rc; + if(readqueue(c, queuep)) + return -1; + return 0; } int disorder_random_disable(disorder_client *c) { - return disorder_simple(c, 0, "random-disable", (char *)0); + return disorder_simple(c, NULL, "random-disable", (char *)NULL); } int disorder_random_enable(disorder_client *c) { - return disorder_simple(c, 0, "random-enable", (char *)0); + return disorder_simple(c, NULL, "random-enable", (char *)NULL); } int disorder_random_enabled(disorder_client *c, int *enabledp) { - char *v; - int rc; - if((rc = disorder_simple(c, &v, "random-enabled", (char *)0))) + char **v, *r; + int nv; + int rc = disorder_simple(c, &r, "random-enabled", (char *)NULL); + if(rc) return rc; - return boolean("random-enabled", v, enabledp); + v = split(r, &nv, SPLIT_QUOTES, 0, 0); + if(nv != 1) { + disorder_error(0, "malformed reply to %s", "random-enabled"); + return -1; + } + if(boolean("random-enabled", v[0], enabledp)) + return -1; + return 0; } int disorder_recent(disorder_client *c, struct queue_entry **recentp) { - return somequeue(c, "recent", recentp); + int rc = disorder_simple(c, NULL, "recent", (char *)NULL); + if(rc) + return rc; + if(readqueue(c, recentp)) + return -1; + return 0; } int disorder_reconfigure(disorder_client *c) { - return disorder_simple(c, 0, "reconfigure", (char *)0); + return disorder_simple(c, NULL, "reconfigure", (char *)NULL); } int disorder_register(disorder_client *c, const char *username, const char *password, const char *email, char **confirmationp) { - return dequote(disorder_simple(c, confirmationp, "register", username, password, email, (char *)0), confirmationp); + char **v, *r; + int nv; + int rc = disorder_simple(c, &r, "register", username, password, email, (char *)NULL); + if(rc) + return rc; + v = split(r, &nv, SPLIT_QUOTES, 0, 0); + if(nv != 1) { + disorder_error(0, "malformed reply to %s", "register"); + return -1; + } + *confirmationp = v[0]; + return 0; } int disorder_reminder(disorder_client *c, const char *username) { - return disorder_simple(c, 0, "reminder", username, (char *)0); + return disorder_simple(c, NULL, "reminder", username, (char *)NULL); } int disorder_remove(disorder_client *c, const char *id) { - return disorder_simple(c, 0, "remove", id, (char *)0); + return disorder_simple(c, NULL, "remove", id, (char *)NULL); } int disorder_rescan(disorder_client *c) { - return disorder_simple(c, 0, "rescan", (char *)0); + return disorder_simple(c, NULL, "rescan", (char *)NULL); } int disorder_resolve(disorder_client *c, const char *track, char **resolvedp) { - return dequote(disorder_simple(c, resolvedp, "resolve", track, (char *)0), resolvedp); + char **v, *r; + int nv; + int rc = disorder_simple(c, &r, "resolve", track, (char *)NULL); + if(rc) + return rc; + v = split(r, &nv, SPLIT_QUOTES, 0, 0); + if(nv != 1) { + disorder_error(0, "malformed reply to %s", "resolve"); + return -1; + } + *resolvedp = v[0]; + return 0; } int disorder_resume(disorder_client *c) { - return disorder_simple(c, 0, "resume", (char *)0); + return disorder_simple(c, NULL, "resume", (char *)NULL); } int disorder_revoke(disorder_client *c) { - return disorder_simple(c, 0, "revoke", (char *)0); + return disorder_simple(c, NULL, "revoke", (char *)NULL); +} + +int disorder_rtp_address(disorder_client *c, char **addressp, char **portp) { + char **v, *r; + int nv; + int rc = disorder_simple(c, &r, "rtp-address", (char *)NULL); + if(rc) + return rc; + v = split(r, &nv, SPLIT_QUOTES, 0, 0); + if(nv != 2) { + disorder_error(0, "malformed reply to %s", "rtp-address"); + return -1; + } + *addressp = v[0]; + *portp = v[1]; + return 0; } int disorder_scratch(disorder_client *c, const char *id) { - return disorder_simple(c, 0, "scratch", id, (char *)0); + return disorder_simple(c, NULL, "scratch", id, (char *)NULL); } int disorder_schedule_del(disorder_client *c, const char *event) { - return disorder_simple(c, 0, "schedule-del", event, (char *)0); + return disorder_simple(c, NULL, "schedule-del", event, (char *)NULL); } int disorder_schedule_get(disorder_client *c, const char *id, struct kvp **actiondatap) { - return pairlist(c, actiondatap, "schedule-get", id, (char *)0); + return pairlist(c, actiondatap, "schedule-get", id, (char *)NULL); } int disorder_schedule_list(disorder_client *c, char ***idsp, int *nidsp) { - return disorder_simple_list(c, idsp, nidsp, "schedule-list", (char *)0); + int rc = disorder_simple(c, NULL, "schedule-list", (char *)NULL); + if(rc) + return rc; + if(readlist(c, idsp, nidsp)) + return -1; + return 0; } int disorder_search(disorder_client *c, const char *terms, char ***tracksp, int *ntracksp) { - return disorder_simple_list(c, tracksp, ntracksp, "search", terms, (char *)0); + int rc = disorder_simple(c, NULL, "search", terms, (char *)NULL); + if(rc) + return rc; + if(readlist(c, tracksp, ntracksp)) + return -1; + return 0; } int disorder_set(disorder_client *c, const char *track, const char *pref, const char *value) { - return disorder_simple(c, 0, "set", track, pref, value, (char *)0); + return disorder_simple(c, NULL, "set", track, pref, value, (char *)NULL); } int disorder_set_global(disorder_client *c, const char *pref, const char *value) { - return disorder_simple(c, 0, "set-global", pref, value, (char *)0); + return disorder_simple(c, NULL, "set-global", pref, value, (char *)NULL); } int disorder_shutdown(disorder_client *c) { - return disorder_simple(c, 0, "shutdown", (char *)0); + return disorder_simple(c, NULL, "shutdown", (char *)NULL); } int disorder_stats(disorder_client *c, char ***statsp, int *nstatsp) { - return disorder_simple_list(c, statsp, nstatsp, "stats", (char *)0); + int rc = disorder_simple(c, NULL, "stats", (char *)NULL); + if(rc) + return rc; + if(readlist(c, statsp, nstatsp)) + return -1; + return 0; } int disorder_tags(disorder_client *c, char ***tagsp, int *ntagsp) { - return disorder_simple_list(c, tagsp, ntagsp, "tags", (char *)0); + int rc = disorder_simple(c, NULL, "tags", (char *)NULL); + if(rc) + return rc; + if(readlist(c, tagsp, ntagsp)) + return -1; + return 0; } int disorder_unset(disorder_client *c, const char *track, const char *pref) { - return disorder_simple(c, 0, "unset", track, pref, (char *)0); + return disorder_simple(c, NULL, "unset", track, pref, (char *)NULL); } int disorder_unset_global(disorder_client *c, const char *pref) { - return disorder_simple(c, 0, "unset-global", pref, (char *)0); + return disorder_simple(c, NULL, "unset-global", pref, (char *)NULL); } int disorder_userinfo(disorder_client *c, const char *username, const char *property, char **valuep) { - return dequote(disorder_simple(c, valuep, "userinfo", username, property, (char *)0), valuep); + char **v, *r; + int nv; + int rc = disorder_simple(c, &r, "userinfo", username, property, (char *)NULL); + if(rc) + return rc; + v = split(r, &nv, SPLIT_QUOTES, 0, 0); + if(nv != 1) { + disorder_error(0, "malformed reply to %s", "userinfo"); + return -1; + } + *valuep = v[0]; + return 0; } int disorder_users(disorder_client *c, char ***usersp, int *nusersp) { - return disorder_simple_list(c, usersp, nusersp, "users", (char *)0); + int rc = disorder_simple(c, NULL, "users", (char *)NULL); + if(rc) + return rc; + if(readlist(c, usersp, nusersp)) + return -1; + return 0; } int disorder_version(disorder_client *c, char **versionp) { - return dequote(disorder_simple(c, versionp, "version", (char *)0), versionp); + char **v, *r; + int nv; + int rc = disorder_simple(c, &r, "version", (char *)NULL); + if(rc) + return rc; + v = split(r, &nv, SPLIT_QUOTES, 0, 0); + if(nv != 1) { + disorder_error(0, "malformed reply to %s", "version"); + return -1; + } + *versionp = v[0]; + return 0; +} + +int disorder_set_volume(disorder_client *c, long left, long right) { + char buf_left[16]; + byte_snprintf(buf_left, sizeof buf_left, "%ld", left); + char buf_right[16]; + byte_snprintf(buf_right, sizeof buf_right, "%ld", right); + return disorder_simple(c, NULL, "volume", buf_left, buf_right, (char *)NULL); +} + +int disorder_get_volume(disorder_client *c, long *leftp, long *rightp) { + char **v, *r; + int nv; + int rc = disorder_simple(c, &r, "volume", (char *)NULL); + if(rc) + return rc; + v = split(r, &nv, SPLIT_QUOTES, 0, 0); + if(nv != 2) { + disorder_error(0, "malformed reply to %s", "volume"); + return -1; + } + *leftp = atol(v[0]); + *rightp = atol(v[1]); + return 0; } diff --git a/lib/client-stubs.h b/lib/client-stubs.h index fd85670..153b082 100644 --- a/lib/client-stubs.h +++ b/lib/client-stubs.h @@ -533,6 +533,16 @@ int disorder_resume(disorder_client *c); */ int disorder_revoke(disorder_client *c); +/** @brief Get the server's RTP address information + * + * + * + * @param c Client + * @param addressp Where to store hostname or address + * @return 0 on success, non-0 on error + */ +int disorder_rtp_address(disorder_client *c, char **addressp, char **portp); + /** @brief Terminate the playing track. * * Requires one of the 'scratch mine', 'scratch random' or 'scratch any' rights depending on how the track came to be added to the queue. @@ -621,7 +631,7 @@ int disorder_shutdown(disorder_client *c); /** @brief Get server statistics * - * The details of what the server reports are not really defined. The returned strings are intended to be printed out one to a line.. + * The details of what the server reports are not really defined. The returned strings are intended to be printed out one to a line. * * @param c Client * @param statsp List of server information strings. @@ -695,4 +705,25 @@ int disorder_users(disorder_client *c, char ***usersp, int *nusersp); */ int disorder_version(disorder_client *c, char **versionp); +/** @brief Set the volume + * + * + * + * @param c Client + * @param left Left channel volume + * @param right Right channel volume + * @return 0 on success, non-0 on error + */ +int disorder_set_volume(disorder_client *c, long left, long right); + +/** @brief Get the volume + * + * + * + * @param c Client + * @param leftp Left channel volume + * @return 0 on success, non-0 on error + */ +int disorder_get_volume(disorder_client *c, long *leftp, long *rightp); + #endif diff --git a/lib/client.c b/lib/client.c index 5884e48..521a4c7 100644 --- a/lib/client.c +++ b/lib/client.c @@ -184,7 +184,6 @@ static const char disorder_list[1]; * * Usually you would call this via one of the following interfaces: * - disorder_simple() - * - disorder_simple_list() */ static int disorder_simple_v(disorder_client *c, char **rp, @@ -557,14 +556,11 @@ static int onequeue(disorder_client *c, const char *cmd, } /** @brief Fetch the queue, recent list, etc */ -static int 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; @@ -583,7 +579,7 @@ static int 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; @@ -624,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, 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) @@ -719,41 +688,6 @@ static int boolean(const char *cmd, const char *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 @@ -775,30 +709,6 @@ int disorder_log(disorder_client *c, struct sink *s) { return 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 Add a scheduled event * @param c Client * @param when When to trigger the event diff --git a/lib/client.h b/lib/client.h index e41ea00..9bc6fec 100644 --- a/lib/client.h +++ b/lib/client.h @@ -51,10 +51,7 @@ int disorder_playing(disorder_client *c, struct queue_entry **qp); char *disorder_user(disorder_client *c); int disorder_prefs(disorder_client *c, const char *track, struct kvp **kp); -int disorder_set_volume(disorder_client *c, int left, int right); -int disorder_get_volume(disorder_client *c, int *left, int *right); int disorder_log(disorder_client *c, struct sink *s); -int disorder_rtp_address(disorder_client *c, char **addressp, char **portp); const char *disorder_last(disorder_client *c); int disorder_schedule_get(disorder_client *c, const char *id, struct kvp **actiondatap); diff --git a/scripts/protocol b/scripts/protocol index 0704942..610656b 100755 --- a/scripts/protocol +++ b/scripts/protocol @@ -27,6 +27,7 @@ use strict; # Types: # # string A (Unicode) string. +# string-raw A string that is not subject to de-quoting (return only) # integer An integer. Decimal on the wire. # boolean True or false. "yes" or "no" on the wire. # list In commands: a list of strings in the command. @@ -89,7 +90,7 @@ sub c_out_decl { return () unless defined $arg; my $type = $arg->[0]; my $name = $arg->[1]; - if($type eq 'string') { + if($type eq 'string' or $type eq 'string-raw') { return ("char **${name}p"); } elsif($type eq 'integer') { return ("long *${name}p"); @@ -131,32 +132,35 @@ sub c_param_docs { # # Return the doc string for a C output parameter. sub c_return_docs { - my $return = shift; - return () unless defined $return; - my $type = $return->[0]; - my $name = $return->[1]; - my $descr = $return->[2]; - if($type eq 'string' - or $type eq 'integer' - or $type eq 'boolean') { - return (" * \@param ${name}p $descr\n"); - } elsif($type eq 'list' or $type eq 'body') { - return (" * \@param ${name}p $descr\n", - " * \@param n${name}p Number of elements in ${name}p\n"); - } elsif($type eq 'pair-list') { - return (" * \@param ${name}p $descr\n"); - } elsif($type eq 'queue' or $type eq 'queue-one') { - return (" * \@param ${name}p $descr\n"); - } elsif($type eq 'user') { - return (); - } else { - die "$0: c_return_docs: unknown type '$type'\n"; + my $returns = shift; + return () unless defined $returns; + for my $return (@$returns) { + my $type = $return->[0]; + my $name = $return->[1]; + my $descr = $return->[2]; + if($type eq 'string' + or $type eq 'string-raw' + or $type eq 'integer' + or $type eq 'boolean') { + return (" * \@param ${name}p $descr\n"); + } elsif($type eq 'list' or $type eq 'body') { + return (" * \@param ${name}p $descr\n", + " * \@param n${name}p Number of elements in ${name}p\n"); + } elsif($type eq 'pair-list') { + return (" * \@param ${name}p $descr\n"); + } elsif($type eq 'queue' or $type eq 'queue-one') { + return (" * \@param ${name}p $descr\n"); + } elsif($type eq 'user') { + return (); + } else { + die "$0: c_return_docs: unknown type '$type'\n"; + } } } # simple(CMD, SUMMARY, DETAIL, # [[TYPE,NAME,DESCR], [TYPE,NAME,DESCR], ...], -# [RETURN-TYPE, RETURN-NAME, RETURN_DESCR]) +# [[RETURN-TYPE, RETURN-NAME, RETURN_DESCR]]) # # CMD is normally just the name of the command, but can # be [COMMAND,FUNCTION] if the function name should differ @@ -166,7 +170,7 @@ sub simple { my $summary = shift; my $detail = shift; my $args = shift; - my $return = shift; + my $returns = shift; my $cmdc; if(ref $cmd eq 'ARRAY') { @@ -185,19 +189,19 @@ sub simple { " *\n", " * \@param c Client\n", c_param_docs($args), - c_return_docs($return), + c_return_docs($returns), " * \@return 0 on success, non-0 on error\n", " */\n", "int disorder_$cmdc(", join(", ", "disorder_client *c", map(c_in_decl($_), @$args), - c_out_decl($return)), + map(c_out_decl($_), @$returns)), ");\n\n"); print STDERR "C "; push(@c, "int disorder_$cmdc(", join(", ", "disorder_client *c", map(c_in_decl($_), @$args), - c_out_decl($return)), + map(c_out_decl($_), @$returns)), ") {\n"); my @cargs = (); for my $arg (@$args) { @@ -213,67 +217,99 @@ sub simple { die "$0: unsupported arg type '$arg->[0]' for '$cmd'\n"; } } - if(!defined $return) { + if(!defined $returns or scalar @$returns == 0) { + # Simple case push(@c, " return disorder_simple(", - join(", ", "c", 0, "\"$cmd\"", @cargs, "(char *)0"), + join(", ", "c", "NULL", "\"$cmd\"", @cargs, "(char *)NULL"), ");\n"); - } elsif($return->[0] eq 'string') { - push(@c, " return dequote(disorder_simple(", - join(", ", "c", "$return->[1]p", "\"$cmd\"", - @cargs, - "(char *)0"), - "), $return->[1]p);\n"); - } elsif($return->[0] eq 'boolean') { - push(@c, " char *v;\n", - " int rc;\n", - " if((rc = disorder_simple(", - join(", ", "c", "&v", "\"$cmd\"", - @cargs, - "(char *)0"), - ")))\n", - " return rc;\n", - " return boolean(\"$cmd\", v, $return->[1]p);\n"); - } elsif($return->[0] eq 'integer') { - push(@c, " char *v;\n", - " int rc;\n", - "\n", - " if((rc = disorder_simple(", - join(", ", "c", "&v", "\"$cmd\"", - @cargs, - "(char *)0"), - ")))\n", - " return rc;\n", - " *$return->[1]p = atol(v);\n", - " xfree(v);\n", - " return 0;\n"); - } elsif($return->[0] eq 'user') { - push(@c, " char *u;\n", - " int rc;\n", - " if((rc = disorder_simple(", - join(", ", "c", "&u", "\"$cmd\"", - @cargs, "(char *)0"), - ")))\n", - " return rc;\n", - " c->user = u;\n", - " return 0;\n"); - } elsif($return->[0] eq 'body') { - push(@c, " return disorder_simple_list(", - join(", ", "c", "$return->[1]p", "n$return->[1]p", "\"$cmd\"", - @cargs, - "(char *)0"), - ");\n"); - } elsif($return->[0] eq 'queue') { - push(@c, " return somequeue(c, \"$cmd\", $return->[1]p);\n"); - } elsif($return->[0] eq 'queue-one') { + } elsif(scalar @$returns == 1 + and $returns->[0]->[0] eq 'queue-one') { + # Special case + my $return = $$returns[0]; push(@c, " return onequeue(c, \"$cmd\", $return->[1]p);\n"); - } elsif($return->[0] eq 'pair-list') { + } elsif(scalar @$returns == 1 + and $returns->[0]->[0] eq 'string-raw') { + # Special case + my $return = $$returns[0]; + push(@c, " return disorder_simple(", + join(", ", "c", "$return->[1]p", "\"$cmd\"", @cargs, "(char *)NULL"), + ");\n"); + } elsif(scalar @$returns == 1 + and $returns->[0]->[0] eq 'pair-list') { + # Special case + my $return = $$returns[0]; push(@c, " return pairlist(", join(", ", "c", "$return->[1]p", "\"$cmd\"", @cargs, - "(char *)0"), + "(char *)NULL"), ");\n"); } else { - die "$0: C API: unknown type '$return->[0]' for '$cmd'\n"; + my $split = 0; + for(my $n = 0; $n < scalar @$returns; ++$n) { + my $return = $returns->[$n]; + my $type = $return->[0]; + my $name = $return->[1]; + if($type eq 'string' + or $type eq 'boolean' + or $type eq 'integer' + or $type eq 'user') { + $split = 1; + } + } + if($split) { + push(@c, " char **v, *r;\n", + " int nv;\n"); + } + push(@c, + " int rc = disorder_simple(", + join(", ", + "c", + $split ? "&r" : "NULL", + "\"$cmd\"", + @cargs, + "(char *)NULL"), + ");\n", + " if(rc)\n", + " return rc;\n"); + if($split) { + push(@c, + " v = split(r, &nv, SPLIT_QUOTES, 0, 0);\n", + " if(nv != ", scalar @$returns, ") {\n", + " disorder_error(0, \"malformed reply to %s\", \"$cmd\");\n", + " return -1;\n", + " }\n"); + } + for(my $n = 0; $n < scalar @$returns; ++$n) { + my $return = $returns->[$n]; + my $type = $return->[0]; + my $name = $return->[1]; + if($type eq 'string') { + push(@c, + " *${name}p = v[$n];\n"); + } elsif($type eq 'boolean') { + push(@c, + " if(boolean(\"$cmd\", v[$n], ${name}p))\n", + " return -1;\n"); + } elsif($type eq 'integer') { + push(@c, + " *${name}p = atol(v[$n]);\n"); + } elsif($type eq 'user') { + push(@c, + " c->user = v[$n];\n"); + } elsif($type eq 'body') { + push(@c, + " if(readlist(c, ${name}p, n${name}p))\n", + " return -1;\n"); + } elsif($type eq 'queue') { + push(@c, + " if(readqueue(c, ${name}p))\n", + " return -1;\n"); + } else { + die "$0: C API: unknown return type '$type' for '$name'\n"; + } + } + push(@c, " return 0;\n"); + # TODO xfree unconsumed split output } push(@c, "}\n\n"); @@ -344,19 +380,19 @@ simple("allfiles", "See 'files' and 'dirs' for more specific lists.", [["string", "dir", "Directory to list (optional)"], ["string", "re", "Regexp that results must match (optional)"]], - ["body", "files", "List of matching files and directories"]); + [["body", "files", "List of matching files and directories"]]); simple("confirm", "Confirm registration", "The confirmation string must have been created with 'register'. The username is returned so the caller knows who they are.", [["string", "confirmation", "Confirmation string"]], - ["user"]); + [["user"]]); simple("cookie", "Log in with a cookie", "The cookie must have been created with 'make-cookie'. The username is returned so the caller knows who they are.", [["string", "cookie", "Cookie string"]], - ["user"]); + [["user"]]); simple("deluser", "Delete user", @@ -368,7 +404,7 @@ simple("dirs", "", [["string", "dir", "Directory to list (optional)"], ["string", "re", "Regexp that results must match (optional)"]], - ["body", "files", "List of matching directories"]); + [["body", "files", "List of matching directories"]]); simple("disable", "Disable play", @@ -391,39 +427,39 @@ simple("enabled", "Detect whether play is enabled", "", [], - ["boolean", "enabled", "1 if play is enabled and 0 otherwise"]); + [["boolean", "enabled", "1 if play is enabled and 0 otherwise"]]); simple("exists", "Test whether a track exists", "", [["string", "track", "Track name"]], - ["boolean", "exists", "1 if the track exists and 0 otherwise"]); + [["boolean", "exists", "1 if the track exists and 0 otherwise"]]); simple("files", "List files in a directory", "", [["string", "dir", "Directory to list (optional)"], ["string", "re", "Regexp that results must match (optional)"]], - ["body", "files", "List of matching files"]); + [["body", "files", "List of matching files"]]); simple("get", "Get a track preference", "If the track does not exist that is an error. If the track exists but the preference does not then a null value is returned.", [["string", "track", "Track name"], ["string", "pref", "Preference name"]], - ["string", "value", "Preference value"]); + [["string", "value", "Preference value"]]); simple("get-global", "Get a global preference", "If the preference does exist not then a null value is returned.", [["string", "pref", "Global preference name"]], - ["string", "value", "Preference value"]); + [["string", "value", "Preference value"]]); simple("length", "Get a track's length", "If the track does not exist an error is returned.", [["string", "track", "Track name"]], - ["integer", "length", "Track length in seconds"]); + [["integer", "length", "Track length in seconds"]]); # TODO log @@ -431,7 +467,7 @@ simple("make-cookie", "Create a login cookie for this user", "The cookie may be redeemed via the 'cookie' command", [], - ["string", "cookie", "Newly created cookie"]); + [["string", "cookie", "Newly created cookie"]]); simple("move", "Move a track", @@ -449,7 +485,7 @@ simple(["new", "new_tracks"], "List recently added tracks", "", [["integer", "max", "Maximum tracks to fetch, or 0 for all available"]], - ["body", "tracks", "Recently added tracks"]); + [["body", "tracks", "Recently added tracks"]]); simple("nop", "Do nothing", @@ -462,7 +498,7 @@ simple("part", [["string", "track", "Track name"], ["string", "context", "Context (\"sort\" or \"display\")"], ["string", "part", "Name part (\"artist\", \"album\" or \"title\")"]], - ["string", "part", "Value of name part"]); + [["string", "part", "Value of name part"]]); simple("pause", "Pause the currently playing track", @@ -473,7 +509,7 @@ simple("play", "Play a track", "Requires the 'play' right.", [["string", "track", "Track to play"]], - ["string", "id", "Queue ID of new track"]); + [["string-raw", "id", "Queue ID of new track"]]); simple("playafter", "Play multiple tracks", @@ -485,7 +521,7 @@ simple("playing", "Retrieve the playing track", "", [], - ["queue-one", "playing", "Details of the playing track"]); + [["queue-one", "playing", "Details of the playing track"]]); simple("playlist-delete", "Delete a playlist", @@ -496,13 +532,13 @@ simple("playlist-get", "List the contents of a playlist", "Requires the 'read' right and oermission to read the playlist.", [["string", "playlist", "Playlist name"]], - ["body", "tracks", "List of tracks in playlist"]); + [["body", "tracks", "List of tracks in playlist"]]); simple("playlist-get-share", "Get a playlist's sharing status", "Requires the 'read' right and permission to read the playlist.", [["string", "playlist", "Playlist to read"]], - ["string", "share", "Sharing status (\"public\", \"private\" or \"shared\")"]); + [["string-raw", "share", "Sharing status (\"public\", \"private\" or \"shared\")"]]); simple("playlist-lock", "Lock a playlist", @@ -530,19 +566,19 @@ simple("playlists", "List playlists", "Requires the 'read' right. Only playlists that you have permission to read are returned.", [], - ["body", "playlists", "Playlist names"]); + [["body", "playlists", "Playlist names"]]); simple("prefs", "Get all the preferences for a track", "", [["string", "track", "Track name"]], - ["pair-list", "prefs", "Track preferences"]); + [["pair-list", "prefs", "Track preferences"]]); simple("queue", "List the queue", "", [], - ["queue", "queue", "Current queue contents"]); + [["queue", "queue", "Current queue contents"]]); simple("random-disable", "Disable random play", @@ -558,13 +594,13 @@ simple("random-enabled", "Detect whether random play is enabled", "Random play counts as enabled even if play is disabled.", [], - ["boolean", "enabled", "1 if random play is enabled and 0 otherwise"]); + [["boolean", "enabled", "1 if random play is enabled and 0 otherwise"]]); simple("recent", "List recently played tracks", "", [], - ["queue", "recent", "Recently played tracks"]); + [["queue", "recent", "Recently played tracks"]]); simple("reconfigure", "Re-read configuraiton file.", @@ -577,7 +613,7 @@ simple("register", [["string", "username", "Requested new username"], ["string", "password", "Requested initial password"], ["string", "email", "New user's email address"]], - ["string", "confirmation", "Confirmation string"]); + [["string", "confirmation", "Confirmation string"]]); simple("reminder", "Send a password reminder.", @@ -598,7 +634,7 @@ simple("resolve", "Resolve a track name", "Converts aliases to non-alias track names", [["string", "track", "Track name (might be an alias)"]], - ["string", "resolved", "Resolve track name (definitely not an alias)"]); + [["string", "resolved", "Resolve track name (definitely not an alias)"]]); simple("resume", "Resume the currently playing track", @@ -610,7 +646,12 @@ simple("revoke", "It will not subsequently be possible to log in with the cookie.", []); -# TODO rtp-address +simple("rtp-address", + "Get the server's RTP address information", + "", + [], + [["string", "address", "Where to store hostname or address"], + ["string", "port", "Where to store service name or port number"]]); simple("scratch", "Terminate the playing track.", @@ -628,19 +669,19 @@ simple("schedule-get", "Get the details of scheduled event", "", [["string", "id", "Event ID"]], - ["pair-list", "actiondata", "Details of event"]); + [["pair-list", "actiondata", "Details of event"]]); simple("schedule-list", "List scheduled events", "This just lists IDs. Use 'schedule-get' to retrieve more detail", [], - ["body", "ids", "List of event IDs"]); + [["body", "ids", "List of event IDs"]]); simple("search", "Search for tracks", "Terms are either keywords or tags formatted as 'tag:TAG-NAME'.", [["string", "terms", "List of search terms"]], - ["body", "tracks", "List of matching tracks"]); + [["body", "tracks", "List of matching tracks"]]); simple("set", "Set a track preference", @@ -662,15 +703,15 @@ simple("shutdown", simple("stats", "Get server statistics", - "The details of what the server reports are not really defined. The returned strings are intended to be printed out one to a line..", + "The details of what the server reports are not really defined. The returned strings are intended to be printed out one to a line.", [], - ["body", "stats", "List of server information strings."]); + [["body", "stats", "List of server information strings."]]); simple("tags", "Get a list of known tags", "Only tags which apply to at least one track are returned.", [], - ["body", "tags", "List of tags"]); + [["body", "tags", "List of tags"]]); simple("unset", "Unset a track preference", @@ -690,21 +731,32 @@ simple("userinfo", "If the user does not exist an error is returned, if the user exists but the property does not then a null value is returned.", [["string", "username", "User to read"], ["string", "property", "Property to read"]], - ["string", "value", "Value of property"]); + [["string", "value", "Value of property"]]); simple("users", "Get a list of users", "", [], - ["body", "users", "List of users"]); + [["body", "users", "List of users"]]); simple("version", "Get the server version", "", [], - ["string", "version", "Server version string"]); + [["string", "version", "Server version string"]]); + +simple(["volume", "set_volume"], + "Set the volume", + "", + [["integer", "left", "Left channel volume"], + ["integer", "right", "Right channel volume"]]); -# TODO volume +simple(["volume", "get_volume"], + "Get the volume", + "", + [], + [["integer", "left", "Left channel volume"], + ["integer", "right", "Right channel volume"]]); # End matter ------------------------------------------------------------------