chiark / gitweb /
Further commands.
authorRichard Kettlewell <rjk@greenend.org.uk>
Sat, 5 Jun 2010 11:21:32 +0000 (12:21 +0100)
committerRichard Kettlewell <rjk@terraraq.org.uk>
Sat, 6 Aug 2011 17:19:07 +0000 (18:19 +0100)
cgi/macros-disorder.c
clients/disorder.c
lib/client-stubs.c
lib/client-stubs.h
lib/client.c
lib/client.h
scripts/protocol

index 7c369e1..dc96c73 100644 (file)
@@ -931,7 +931,7 @@ static int exp_dirs(int nargs,
                     const struct mx_node **args,
                     struct sink *output,
                     void *u) {
-  return exp__files_dirs(nargs, args, output, u, "dir", disorder_directories);
+  return exp__files_dirs(nargs, args, output, u, "dir", disorder_dirs);
 }
 
 static int exp__search_shim(disorder_client *c, const char *terms,
index a55a6e8..48745b6 100644 (file)
@@ -244,7 +244,7 @@ static int isarg_regexp(const char *s) {
 }
 
 static void cf_dirs(char **argv) {
-  cf_somelist(argv, disorder_directories);
+  cf_somelist(argv, disorder_dirs);
 }
 
 static void cf_files(char **argv) {
index 03047ad..427805f 100644 (file)
@@ -24,6 +24,10 @@ int disorder_adduser(disorder_client *c, const char *user, const char *password,
   return disorder_simple(c, 0, "adduser", user, password, rights, (char *)0);
 }
 
+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 disorder_confirm(disorder_client *c, const char *confirmation) {
   char *u;
   int rc;
@@ -46,6 +50,10 @@ int disorder_deluser(disorder_client *c, const char *user) {
   return disorder_simple(c, 0, "deluser", user, (char *)0);
 }
 
+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 disorder_disable(disorder_client *c) {
   return disorder_simple(c, 0, "disable", (char *)0);
 }
@@ -74,6 +82,10 @@ int disorder_exists(disorder_client *c, const char *track, int *existsp) {
   return boolean("exists", v, existsp);
 }
 
+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 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);
 }
@@ -102,14 +114,18 @@ int disorder_playlist_delete(disorder_client *c, const char *playlist) {
   return disorder_simple(c, 0, "playlist-delete", playlist, (char *)0);
 }
 
-int disorder_playlist_lock(disorder_client *c, const char *playlist) {
-  return disorder_simple(c, 0, "playlist-lock", playlist, (char *)0);
+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 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);
 }
 
+int disorder_playlist_lock(disorder_client *c, const char *playlist) {
+  return disorder_simple(c, 0, "playlist-lock", playlist, (char *)0);
+}
+
 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);
 }
@@ -118,6 +134,10 @@ int disorder_playlist_unlock(disorder_client *c) {
   return disorder_simple(c, 0, "playlist-unlock", (char *)0);
 }
 
+int disorder_playlists(disorder_client *c, char ***playlistsp, int *nplaylistsp) {
+  return disorder_simple_list(c, playlistsp, nplaylistsp, "playlists", (char *)0);
+}
+
 int disorder_random_disable(disorder_client *c) {
   return disorder_simple(c, 0, "random-disable", (char *)0);
 }
@@ -174,6 +194,14 @@ int disorder_schedule_del(disorder_client *c, const char *event) {
   return disorder_simple(c, 0, "schedule-del", event, (char *)0);
 }
 
+int disorder_schedule_list(disorder_client *c, char ***idsp, int *nidsp) {
+  return disorder_simple_list(c, idsp, nidsp, "schedule-list", (char *)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 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);
 }
@@ -182,6 +210,14 @@ int disorder_set_global(disorder_client *c, const char *pref, const char *value)
   return disorder_simple(c, 0, "set-global", pref, value, (char *)0);
 }
 
+int disorder_stats(disorder_client *c, char ***statsp, int *nstatsp) {
+  return disorder_simple_list(c, statsp, nstatsp, "stats", (char *)0);
+}
+
+int disorder_tags(disorder_client *c, char ***tagsp, int *ntagsp) {
+  return disorder_simple_list(c, tagsp, ntagsp, "tags", (char *)0);
+}
+
 int disorder_unset(disorder_client *c, const char *track, const char *pref) {
   return disorder_simple(c, 0, "unset", track, pref, (char *)0);
 }
@@ -194,6 +230,10 @@ int disorder_userinfo(disorder_client *c, const char *username, const char *prop
   return dequote(disorder_simple(c, valuep, "userinfo", username, property, (char *)0), valuep);
 }
 
+int disorder_users(disorder_client *c, char ***usersp, int *nusersp) {
+  return disorder_simple_list(c, usersp, nusersp, "users", (char *)0);
+}
+
 int disorder_version(disorder_client *c, char **versionp) {
   return dequote(disorder_simple(c, versionp, "version", (char *)0), versionp);
 }
index 4e67b01..d487772 100644 (file)
@@ -38,6 +38,18 @@ int disorder_adopt(disorder_client *c, const char *id);
  */
 int disorder_adduser(disorder_client *c, const char *user, const char *password, const char *rights);
 
+/** @brief List files and directories in a directory
+ *
+ * See 'files' and 'dirs' for more specific lists.
+ *
+ * @param dir Directory to list (optional)
+ * @param re Regexp that results must match (optional)
+ * @param filesp List of matching files and directories
+ * @param nfilesp Number of elements in filesp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_allfiles(disorder_client *c, const char *dir, const char *re, char ***filesp, int *nfilesp);
+
 /** @brief Confirm registration
  *
  * The confirmation string must have been created with 'register'.  The username is returned so the caller knows who they are.
@@ -63,6 +75,18 @@ int disorder_cookie(disorder_client *c, const char *cookie);
  */
 int disorder_deluser(disorder_client *c, const char *user);
 
+/** @brief List directories in a directory
+ *
+ * 
+ *
+ * @param dir Directory to list (optional)
+ * @param re Regexp that results must match (optional)
+ * @param filesp List of matching directories
+ * @param nfilesp Number of elements in filesp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_dirs(disorder_client *c, const char *dir, const char *re, char ***filesp, int *nfilesp);
+
 /** @brief Disable play
  *
  * Play will stop at the end of the current track, if one is playing.  Requires the 'global prefs' right.
@@ -109,6 +133,18 @@ int disorder_enabled(disorder_client *c, int *enabledp);
  */
 int disorder_exists(disorder_client *c, const char *track, int *existsp);
 
+/** @brief List files in a directory
+ *
+ * 
+ *
+ * @param dir Directory to list (optional)
+ * @param re Regexp that results must match (optional)
+ * @param filesp List of matching files
+ * @param nfilesp Number of elements in filesp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_files(disorder_client *c, const char *dir, const char *re, char ***filesp, int *nfilesp);
+
 /** @brief 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.
@@ -176,14 +212,16 @@ int disorder_pause(disorder_client *c);
  */
 int disorder_playlist_delete(disorder_client *c, const char *playlist);
 
-/** @brief Lock a playlist
+/** @brief List the contents of a playlist
  *
- * Requires the 'play' right and permission to modify the playlist.  A given connection may lock at most one playlist.
+ * Requires the 'read' right and oermission to read the playlist.
  *
- * @param playlist Playlist to delete
+ * @param playlist Playlist name
+ * @param tracksp List of tracks in playlist
+ * @param ntracksp Number of elements in tracksp
  * @return 0 on success, non-0 on error
  */
-int disorder_playlist_lock(disorder_client *c, const char *playlist);
+int disorder_playlist_get(disorder_client *c, const char *playlist, char ***tracksp, int *ntracksp);
 
 /** @brief Get a playlist's sharing status
  *
@@ -195,6 +233,15 @@ int disorder_playlist_lock(disorder_client *c, const char *playlist);
  */
 int disorder_playlist_get_share(disorder_client *c, const char *playlist, char **sharep);
 
+/** @brief Lock a playlist
+ *
+ * Requires the 'play' right and permission to modify the playlist.  A given connection may lock at most one playlist.
+ *
+ * @param playlist Playlist to delete
+ * @return 0 on success, non-0 on error
+ */
+int disorder_playlist_lock(disorder_client *c, const char *playlist);
+
 /** @brief Set a playlist's sharing status
  *
  * Requires the 'play' right and permission to modify the playlist.
@@ -213,6 +260,16 @@ int disorder_playlist_set_share(disorder_client *c, const char *playlist, const
  */
 int disorder_playlist_unlock(disorder_client *c);
 
+/** @brief List playlists
+ *
+ * Requires the 'read' right.  Only playlists that you have permission to read are returned.
+ *
+ * @param playlistsp Playlist names
+ * @param nplaylistsp Number of elements in playlistsp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_playlists(disorder_client *c, char ***playlistsp, int *nplaylistsp);
+
 /** @brief Disable random play
  *
  * Requires the 'global prefs' right.
@@ -328,6 +385,27 @@ int disorder_scratch(disorder_client *c, const char *id);
  */
 int disorder_schedule_del(disorder_client *c, const char *event);
 
+/** @brief List scheduled events
+ *
+ * This just lists IDs.  Use 'schedule-get' to retrieve more detail
+ *
+ * @param idsp List of event IDs
+ * @param nidsp Number of elements in idsp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_schedule_list(disorder_client *c, char ***idsp, int *nidsp);
+
+/** @brief Search for tracks
+ *
+ * Terms are either keywords or tags formatted as 'tag:TAG-NAME'.
+ *
+ * @param terms List of search terms
+ * @param tracksp List of matching tracks
+ * @param ntracksp Number of elements in tracksp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_search(disorder_client *c, const char *terms, char ***tracksp, int *ntracksp);
+
 /** @brief Set a track preference
  *
  * Requires the 'prefs' right.
@@ -349,6 +427,26 @@ int disorder_set(disorder_client *c, const char *track, const char *pref, const
  */
 int disorder_set_global(disorder_client *c, const char *pref, const char *value);
 
+/** @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..
+ *
+ * @param statsp List of server information strings.
+ * @param nstatsp Number of elements in statsp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_stats(disorder_client *c, char ***statsp, int *nstatsp);
+
+/** @brief Get a list of known tags
+ *
+ * Only tags which apply to at least one track are returned.
+ *
+ * @param tagsp List of tags
+ * @param ntagsp Number of elements in tagsp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_tags(disorder_client *c, char ***tagsp, int *ntagsp);
+
 /** @brief Unset a track preference
  *
  * Requires the 'prefs' right.
@@ -379,6 +477,16 @@ int disorder_unset_global(disorder_client *c, const char *pref);
  */
 int disorder_userinfo(disorder_client *c, const char *username, const char *property, char **valuep);
 
+/** @brief Get a list of users
+ *
+ * 
+ *
+ * @param usersp List of users
+ * @param nusersp Number of elements in usersp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_users(disorder_client *c, char ***usersp, int *nusersp);
+
 /** @brief Get the server version
  *
  * 
index 415823a..cb65114 100644 (file)
@@ -698,45 +698,6 @@ static int disorder_simple_list(disorder_client *c,
   return readlist(c, vecp, nvecp);
 }
 
-/** @brief List directories below @p dir
- * @param c Client
- * @param dir Directory to list, or NULL for root (UTF-8)
- * @param re Regexp that results must match, or NULL (UTF-8)
- * @param vecp Where to store list (UTF-8)
- * @param nvecp Where to store number of items, or NULL
- * @return 0 on success, non-0 on error
- */
-int disorder_directories(disorder_client *c, const char *dir, const char *re,
-                        char ***vecp, int *nvecp) {
-  return disorder_simple_list(c, vecp, nvecp, "dirs", dir, re, (char *)0);
-}
-
-/** @brief List files below @p dir
- * @param c Client
- * @param dir Directory to list, or NULL for root (UTF-8)
- * @param re Regexp that results must match, or NULL (UTF-8)
- * @param vecp Where to store list (UTF-8)
- * @param nvecp Where to store number of items, or NULL
- * @return 0 on success, non-0 on error
- */
-int disorder_files(disorder_client *c, const char *dir, const char *re,
-                  char ***vecp, int *nvecp) {
-  return disorder_simple_list(c, vecp, nvecp, "files", dir, re, (char *)0);
-}
-
-/** @brief List files and directories below @p dir
- * @param c Client
- * @param dir Directory to list, or NULL for root (UTF-8)
- * @param re Regexp that results must match, or NULL (UTF-8)
- * @param vecp Where to store list (UTF-8)
- * @param nvecp Where to store number of items, or NULL
- * @return 0 on success, non-0 on error
- */
-int disorder_allfiles(disorder_client *c, const char *dir, const char *re,
-                     char ***vecp, int *nvecp) {
-  return disorder_simple_list(c, vecp, nvecp, "allfiles", dir, re, (char *)0);
-}
-
 /** @brief Return the user we logged in with
  * @param c Client
  * @return User name (owned by @p c, don't modify)
@@ -817,29 +778,6 @@ int disorder_length(disorder_client *c, const char *track,
   return 0;
 }
 
-/** @brief Search for tracks
- * @param c Client
- * @param terms Search terms (UTF-8)
- * @param vecp Where to store list (UTF-8)
- * @param nvecp Where to store number of items, or NULL
- * @return 0 on success, non-0 on error
- */
-int disorder_search(disorder_client *c, const char *terms,
-                   char ***vecp, int *nvecp) {
-  return disorder_simple_list(c, vecp, nvecp, "search", terms, (char *)0);
-}
-
-/** @brief Get server stats
- * @param c Client
- * @param vecp Where to store list (UTF-8)
- * @param nvecp Where to store number of items, or NULL
- * @return 0 on success, non-0 on error
- */
-int disorder_stats(disorder_client *c,
-                  char ***vecp, int *nvecp) {
-  return disorder_simple_list(c, vecp, nvecp, "stats", (char *)0);
-}
-
 /** @brief Set volume
  * @param c Client
  * @param left New left channel value
@@ -896,28 +834,6 @@ int disorder_log(disorder_client *c, struct sink *s) {
   return 0;
 }
 
-/** @brief List all known tags
- * @param c Client
- * @param vecp Where to store list (UTF-8)
- * @param nvecp Where to store number of items, or NULL
- * @return 0 on success, non-0 on error
- */
-int disorder_tags(disorder_client *c,
-                  char ***vecp, int *nvecp) {
-  return disorder_simple_list(c, vecp, nvecp, "tags", (char *)0);
-}
-
-/** @brief List all known users
- * @param c Client
- * @param vecp Where to store list (UTF-8)
- * @param nvecp Where to store number of items, or NULL
- * @return 0 on success, non-0 on error
- */
-int disorder_users(disorder_client *c,
-                  char ***vecp, int *nvecp) {
-  return disorder_simple_list(c, vecp, nvecp, "users", (char *)0);
-}
-
 /** @brief Get recently added tracks
  * @param c Client
  * @param vecp Where to store pointer to list (UTF-8)
@@ -958,16 +874,6 @@ int disorder_rtp_address(disorder_client *c, char **addressp, char **portp) {
   return 0;
 }
 
-/** @brief List scheduled events
- * @param c Client
- * @param idsp Where to put list of event IDs
- * @param nidsp Where to put count of event IDs, or NULL
- * @return 0 on success, non-0 on error
- */
-int disorder_schedule_list(disorder_client *c, char ***idsp, int *nidsp) {
-  return disorder_simple_list(c, idsp, nidsp, "schedule-list", (char *)0);
-}
-
 /** @brief Get details of a scheduled event
  * @param c Client
  * @param id Event ID
@@ -1037,31 +943,6 @@ int disorder_schedule_add(disorder_client *c,
   return rc;
 }
 
-/** @brief Get the contents of a playlist
- * @param c Client
- * @param playlist Playlist to get
- * @param tracksp Where to put list of tracks
- * @param ntracksp Where to put count of tracks
- * @return 0 on success, non-0 on error
- */
-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);
-}
-
-/** @brief List all readable playlists
- * @param c Client
- * @param playlistsp Where to put list of playlists
- * @param nplaylistsp Where to put count of playlists
- * @return 0 on success, non-0 on error
- */
-int disorder_playlists(disorder_client *c,
-                       char ***playlistsp, int *nplaylistsp) {
-  return disorder_simple_list(c, playlistsp, nplaylistsp,
-                              "playlists", (char *)0);
-}
-
 /** @brief Set the contents of a playlst
  * @param c Client
  * @param playlist Playlist to modify
index 34ac4b3..c4b19e4 100644 (file)
@@ -60,8 +60,6 @@ int disorder_rescan(disorder_client *c);
 int disorder_playing(disorder_client *c, struct queue_entry **qp);
 int disorder_recent(disorder_client *c, struct queue_entry **qp);
 int disorder_queue(disorder_client *c, struct queue_entry **qp);
-int disorder_directories(disorder_client *c, const char *dir, const char *re,
-                        char ***vecp, int *nvecp);
 int disorder_files(disorder_client *c, const char *dir, const char *re,
                   char ***vecp, int *nvecp);
 int disorder_allfiles(disorder_client *c, const char *dir, const char *re,
index b3ba0fc..46a1cbf 100755 (executable)
@@ -76,7 +76,7 @@ sub simple {
 
 # string(CMD, SUMMARY, DETAIL, [[NAME,DESCR], [NAME,DESCR], ...], [RETURN, DESCR])
 #
-# Response is yes/no or failure
+# Response is a string, or failure, or 555 for "none".
 sub string {
     my $cmd = shift;
     my $summary = shift;
@@ -117,9 +117,10 @@ sub string {
     # TODO
 }
 
-# string(CMD, SUMMARY, DETAIL, [[NAME,DESCR], [NAME,DESCR], ...])
+# string_login(CMD, SUMMARY, DETAIL, [[NAME,DESCR], [NAME,DESCR], ...])
 #
-# Response is yes/no or failure
+# Like string(), but the server returns a username, which we squirrel
+# away rather than returning to the caller.
 sub string_login {
     my $cmd = shift;
     my $summary = shift;
@@ -210,6 +211,50 @@ sub boolean {
     # TODO
 }
 
+# list(CMD, SUMMARY, DETAIL, [[NAME,DESCR], [NAME,DESCR], ...], [RETURN, DESCR])
+#
+# Response is a a list of strings in a dot-stuffed body
+sub list {
+    my $cmd = shift;
+    my $summary = shift;
+    my $detail = shift;
+    my $args = shift;
+    my $return = shift;
+
+    my $cmdc = $cmd;
+    $cmdc =~ s/-/_/g;
+    # Synchronous C API
+    push(@h, "/** \@brief $summary\n",
+         " *\n",
+         " * $detail\n",
+         " *\n",
+         map(" * \@param $_->[0] $_->[1]\n", @$args),
+         " * \@param $return->[0]p $return->[1]\n",
+         " * \@param n$return->[0]p Number of elements in $return->[0]p\n",
+         " * \@return 0 on success, non-0 on error\n",
+         " */\n",
+         "int disorder_$cmdc(disorder_client *c",
+         map(", const char *$_->[0]", @$args),
+         ", char ***$return->[0]p, int *n$return->[0]p);\n",
+         "\n");
+    push(@c, "int disorder_$cmdc(disorder_client *c",
+         map(", const char *$_->[0]", @$args),
+         ", char ***$return->[0]p, int *n$return->[0]p) {\n",
+         "  return disorder_simple_list(c, $return->[0]p, n$return->[0]p, \"$cmd\"",
+         map(", $_->[0]", @$args),
+         ", (char *)0);\n",
+         "}\n\n");
+
+    # Asynchronous C API
+    # TODO
+
+    # Python API
+    # TODO
+
+    # Java API
+    # TODO
+}
+
 # TODO other command classes
 
 # Front matter ----------------------------------------------------------------
@@ -255,7 +300,12 @@ simple("adduser",
         ["password", "Initial password"],
         ["rights", "Initial rights (optional)"]]);
 
-# TODO allfiles
+list("allfiles",
+     "List files and directories in a directory",
+     "See 'files' and 'dirs' for more specific lists.",
+     [["dir", "Directory to list (optional)"],
+      ["re", "Regexp that results must match (optional)"]],
+     ["files", "List of matching files and directories"]);
 
 string_login("confirm",
             "Confirm registration",
@@ -273,7 +323,12 @@ simple("deluser",
        "Requires the 'admin' right.",
        [["user", "User to delete"]]);
 
-# TODO dirs
+list("dirs",
+     "List directories in a directory",
+     "",
+     [["dir", "Directory to list (optional)"],
+      ["re", "Regexp that results must match (optional)"]],
+     ["files", "List of matching directories"]);
 
 simple("disable",
        "Disable play",
@@ -304,7 +359,12 @@ boolean("exists",
         [["track", "Track name"]],
         ["exists", "1 if the track exists and 0 otherwise"]);
 
-# TODO files
+list("files",
+     "List files in a directory",
+     "",
+     [["dir", "Directory to list (optional)"],
+      ["re", "Regexp that results must match (optional)"]],
+     ["files", "List of matching files"]);
 
 string("get",
        "Get a track preference",
@@ -362,14 +422,11 @@ simple("playlist-delete",
        "Requires the 'play' right and permission to modify the playlist.",
        [["playlist", "Playlist to delete"]]);
 
-# TODO playlist-get
-
-# TODO playlist-get-share
-
-simple("playlist-lock",
-       "Lock a playlist",
-       "Requires the 'play' right and permission to modify the playlist.  A given connection may lock at most one playlist.",
-       [["playlist", "Playlist to delete"]]);
+list("playlist-get",
+     "List the contents of a playlist",
+     "Requires the 'read' right and oermission to read the playlist.",
+     [["playlist", "Playlist name"]],
+     ["tracks", "List of tracks in playlist"]);
 
 string("playlist-get-share",
        "Get a playlist's sharing status",
@@ -377,6 +434,11 @@ string("playlist-get-share",
        [["playlist", "Playlist to read"]],
        ["share", "Sharing status (\"public\", \"private\" or \"shared\")"]);
 
+simple("playlist-lock",
+       "Lock a playlist",
+       "Requires the 'play' right and permission to modify the playlist.  A given connection may lock at most one playlist.",
+       [["playlist", "Playlist to delete"]]);
+
 simple("playlist-set-share",
        "Set a playlist's sharing status",
        "Requires the 'play' right and permission to modify the playlist.",
@@ -388,7 +450,11 @@ simple("playlist-unlock",
        "The playlist to unlock is implicit in the connection.",
        []);
 
-# TODO playlists
+list("playlists",
+     "List playlists",
+     "Requires the 'read' right.  Only playlists that you have permission to read are returned.",
+     [],
+     ["playlists", "Playlist names"]);
 
 # TODO prefs
 
@@ -472,9 +538,17 @@ simple("schedule-del",
 
 # TODO schedule-get
 
-# TODO schedule-list
+list("schedule-list",
+     "List scheduled events",
+     "This just lists IDs.  Use 'schedule-get' to retrieve more detail",
+     [],
+     ["ids", "List of event IDs"]);
 
-# TODO search
+list("search",
+     "Search for tracks",
+     "Terms are either keywords or tags formatted as 'tag:TAG-NAME'.",
+     [["terms", "List of search terms"]],
+     ["tracks", "List of matching tracks"]);
 
 simple("set",
        "Set a track preference",
@@ -491,9 +565,17 @@ simple("set-global",
 
 # TODO shutdown (also needs documenting)
 
-# TODO stats
+list("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..",
+     [],
+     ["stats", "List of server information strings."]);
 
-# TODO tags
+list("tags",
+     "Get a list of known tags",
+     "Only tags which apply to at least one track are returned.",
+     [],
+     ["tags", "List of tags"]);
 
 simple("unset",
        "Unset a track preference",
@@ -515,7 +597,11 @@ string("userinfo",
         ["property", "Property to read"]],
        ["value", "Value of property"]);
 
-# TODO users
+list("users",
+     "Get a list of users",
+     "",
+     [],
+     ["users", "List of users"]);
 
 string("version",
        "Get the server version",