chiark / gitweb /
Bring CGI docs pretty much up to date
[disorder] / server / macros-disorder.c
index c2ac8b082e89c2dfebbba6a6d3f70fa51aa67d50..2dc809852d648c4533d4cf0172b779b1117c20c3 100644 (file)
@@ -1,3 +1,4 @@
+
 /*
  * This file is part of DisOrder.
  * Copyright (C) 2004-2008 Richard Kettlewell
 #include "disorder-cgi.h"
 
 /** @brief For error template */
-char *dcgi_error_string;
+const char *dcgi_error_string;
+
+/** @brief For login template */
+const char *dcgi_status_string;
 
 /** @brief Return @p i as a string */
 static const char *make_index(int i) {
@@ -34,7 +38,7 @@ static const char *make_index(int i) {
   return s;
 }
 
-/* @server-version
+/*! @server-version
  *
  * Expands to the server's version string, or a (safe to use) error
  * value if the server is unavailable or broken.
@@ -53,7 +57,7 @@ static int exp_server_version(int attribute((unused)) nargs,
   return sink_writes(output, cgi_sgmlquote(v)) < 0 ? -1 : 0;
 }
 
-/* @version
+/*! @version
  *
  * Expands to the local version string.
  */
@@ -65,7 +69,7 @@ static int exp_version(int attribute((unused)) nargs,
                      cgi_sgmlquote(disorder_short_version_string)) < 0 ? -1 : 0;
 }
 
-/* @url
+/*! @url
  *
  * Expands to the base URL of the web interface.
  */
@@ -77,7 +81,7 @@ static int exp_url(int attribute((unused)) nargs,
                      cgi_sgmlquote(config->url)) < 0 ? -1 : 0;
 }
 
-/* @arg{NAME}
+/*! @arg{NAME}
  *
  * Expands to the UNQUOTED form of CGI argument NAME, or the empty string if
  * there is no such argument.  Use @argq for a quick way to quote the argument.
@@ -94,7 +98,7 @@ static int exp_arg(int attribute((unused)) nargs,
     return 0;
 }
 
-/* @argq{NAME}
+/*! @argq{NAME}
  *
  * Expands to the (quoted) form of CGI argument NAME, or the empty string if
  * there is no such argument.  Use @arg for the unquoted argument.
@@ -111,7 +115,7 @@ static int exp_argq(int attribute((unused)) nargs,
     return 0;
 }
 
-/* @user
+/*! @user
  *
  * Expands to the logged-in username (which might be "guest"), or to
  * the empty string if not connected.
@@ -127,7 +131,7 @@ static int exp_user(int attribute((unused)) nargs,
   return 0;
 }
 
-/* @part{TRACK|ID}{PART}{CONTEXT}
+/*! @part{TRACK|ID}{PART}{CONTEXT}
  *
  * Expands to a track name part.
  *
@@ -155,11 +159,6 @@ static int exp_part(int nargs,
     else
       return 0;
   }
-  fprintf(stderr, "track=[%s] part=[%s] context=[%s] dcgi_client=[%p]\n",
-          track,
-          part,
-          !strcmp(context, "short") ? "display" : context,
-          dcgi_client);
   if(dcgi_client
      && !disorder_part(dcgi_client, &s,
                        track,
@@ -169,7 +168,7 @@ static int exp_part(int nargs,
   return 0;
 }
 
-/* @quote{STRING}
+/*! @quote{STRING}
  *
  * SGML-quotes STRING.  Note that most expansion results are already suitable
  * quoted, so this expansion is usually not required.
@@ -181,7 +180,7 @@ static int exp_quote(int attribute((unused)) nargs,
   return sink_writes(output, cgi_sgmlquote(args[0])) < 0 ? -1 : 0;
 }
 
-/* @who{ID}
+/*! @who{ID}
  *
  * Expands to the name of the submitter of track ID, which must be a playing
  * track, in the queue, or in the recent list.
@@ -197,7 +196,7 @@ static int exp_who(int attribute((unused)) nargs,
   return 0;
 }
 
-/* @when{ID}
+/*! @when{ID}
  *
  * Expands to the time a track started or is expected to start.  The track must
  * be a playing track, in the queue, or in the recent list.
@@ -234,7 +233,7 @@ static int exp_when(int attribute((unused)) nargs,
   return sink_writes(output, "&nbsp;") < 0 ? -1 : 0;
 }
 
-/* @length{ID|TRACK}
+/*! @length{ID|TRACK}
  *
  * Expands to the length of a track, identified by its queue ID or its name.
  * If it is the playing track (identified by ID) then the amount played so far
@@ -266,7 +265,7 @@ static int exp_length(int attribute((unused)) nargs,
   return sink_writes(output, "&nbsp;") < 0 ? -1 : 0;
 }
 
-/* @removable{ID}
+/*! @removable{ID}
  *
  * Expands to "true" if track ID is removable (or scratchable, if it is the
  * playing track) and "false" otherwise.
@@ -286,7 +285,7 @@ static int exp_removable(int attribute((unused)) nargs,
                             (dcgi_rights, disorder_user(dcgi_client), q));
 }
 
-/* @movable{ID}{DIR}
+/*! @movable{ID}{DIR}
  *
  * Expands to "true" if track ID is movable and "false" otherwise.
  *
@@ -319,11 +318,12 @@ static int exp_movable(int  nargs,
                                       q));
 }
 
-/* @playing{TEMPLATE}
+/*! @playing{TEMPLATE}
  *
- * Expands to TEMPLATE, with:
- * - @id expanded to the queue ID of the playing track
- * - @track expanded to its UNQUOTED name
+ * Expands to TEMPLATE, with the following expansions:
+ * - @id: the queue ID of the playing track
+ * - @track: the playing track's
+ UNQUOTED name
  *
  * If no track is playing expands to nothing.
  *
@@ -346,15 +346,15 @@ static int exp_playing(int nargs,
                    output, u);
 }
 
-/* @queue{TEMPLATE}
+/*! @queue{TEMPLATE}
  *
  * For each track in the queue, expands TEMPLATE with the following expansions:
- * - @id to the queue ID of the track
- * - @track to the UNQUOTED track name
- * - @index to the track number from 0
- * - @parity to "even" or "odd" alternately
- * - @first to "true" on the first track and "false" otherwise
- * - @last to "true" on the last track and "false" otherwise
+ * - @id: the queue ID of the track
+ * - @track: the UNQUOTED track name
+ * - @index: the track number from 0
+ * - @parity: "even" or "odd" alternately
+ * - @first: "true" on the first track and "false" otherwise
+ * - @last: "true" on the last track and "false" otherwise
  */
 static int exp_queue(int attribute((unused)) nargs,
                      const struct mx_node **args,
@@ -378,16 +378,16 @@ static int exp_queue(int attribute((unused)) nargs,
   return 0;
 }
 
-/* @recent{TEMPLATE}
+/*! @recent{TEMPLATE}
  *
  * For each track in the recently played list, expands TEMPLATE with the
  * following expansions:
- * - @id to the queue ID of the track
- * - @track  to the UNQUOTED track name
- * - @index to the track number from 0
- * - @parity to "even" or "odd" alternately
- * - @first to "true" on the first track and "false" otherwise
- * - @last to "true" on the last track and "false" otherwise
+ * - @id: the queue ID of the track
+ * - @track: the UNQUOTED track name
+ * - @index: the track number from 0
+ * - @parity: "even" or "odd" alternately
+ * - @first: "true" on the first track and "false" otherwise
+ * - @last: "true" on the last track and "false" otherwise
  */
 static int exp_recent(int attribute((unused)) nargs,
                       const struct mx_node **args,
@@ -411,15 +411,15 @@ static int exp_recent(int attribute((unused)) nargs,
   return 0;
 }
 
-/* @new{TEMPLATE}
+/*! @new{TEMPLATE}
  *
  * For each track in the newly added list, expands TEMPLATE wit the following
  * expansions:
- * - @track to the UNQUOTED track name
- * - @index to the track number from 0
- * - @parity to "even" or "odd" alternately
- * - @first to "true" on the first track and "false" otherwise
- * - @last to "true" on the last track and "false" otherwise
+ * - @track: the UNQUOTED track name
+ * - @index: the track number from 0
+ * - @parity: "even" or "odd" alternately
+ * - @first: "true" on the first track and "false" otherwise
+ * - @last: "true" on the last track and "false" otherwise
  *
  * Note that unlike @playing, @queue and @recent which are otherwise
  * superficially similar, there is no @id sub-expansion here.
@@ -445,7 +445,7 @@ static int exp_new(int attribute((unused)) nargs,
   return 0;
 }
 
-/* @volume{CHANNEL}
+/*! @volume{CHANNEL}
  *
  * Expands to the volume in a given channel.  CHANNEL must be "left" or
  * "right".
@@ -460,7 +460,7 @@ static int exp_volume(int attribute((unused)) nargs,
                          ? dcgi_volume_left : dcgi_volume_right) < 0 ? -1 : 0;
 }
 
-/* @isplaying
+/*! @isplaying
  *
  * Expands to "true" if there is a playing track, otherwise "false".
  */
@@ -472,7 +472,7 @@ static int exp_isplaying(int attribute((unused)) nargs,
   return mx_bool_result(output, !!dcgi_playing);
 }
 
-/* @isqueue
+/*! @isqueue
  *
  * Expands to "true" if there the queue is nonempty, otherwise "false".
  */
@@ -484,7 +484,7 @@ static int exp_isqueue(int attribute((unused)) nargs,
   return mx_bool_result(output, !!dcgi_queue);
 }
 
-/* @isrecent@
+/*! @isrecent@
  *
  * Expands to "true" if there the recently played list is nonempty, otherwise
  * "false".
@@ -497,7 +497,7 @@ static int exp_isrecent(int attribute((unused)) nargs,
   return mx_bool_result(output, !!dcgi_recent);
 }
 
-/* @isnew
+/*! @isnew
  *
  * Expands to "true" if there the newly added track list is nonempty, otherwise
  * "false".
@@ -510,7 +510,7 @@ static int exp_isnew(int attribute((unused)) nargs,
   return mx_bool_result(output, !!dcgi_nnew);
 }
 
-/* @pref{TRACK}{KEY}
+/*! @pref{TRACK}{KEY}
  *
  * Expands to a track preference.
  */
@@ -525,16 +525,16 @@ static int exp_pref(int attribute((unused)) nargs,
   return 0;
 }
 
-/* @prefs{TRACK}{TEMPLATE}
+/*! @prefs{TRACK}{TEMPLATE}
  *
  * For each track preference of track TRACK, expands TEMPLATE with the
  * following expansions:
- * - @name to the UNQUOTED preference name
- * - @index to the preference number from 0
- * - @value to the UNQUOTED preference value
- * - @parity to "even" or "odd" alternately
- * - @first to "true" on the first preference and "false" otherwise
- * - @last to "true" on the last preference and "false" otherwise
+ * - @name: the UNQUOTED preference name
+ * - @index: the preference number from 0
+ * - @value: the UNQUOTED preference value
+ * - @parity: "even" or "odd" alternately
+ * - @first: "true" on the first preference and "false" otherwise
+ * - @last: "true" on the last preference and "false" otherwise
  *
  * Use @quote to quote preference names and values where necessary; see below.
  */
@@ -564,7 +564,7 @@ static int exp_prefs(int attribute((unused)) nargs,
   return 0;
 }
 
-/* @transform{TRACK}{TYPE}{CONTEXT}
+/*! @transform{TRACK}{TYPE}{CONTEXT}
  *
  * Transforms a track name (if TYPE is "track") or directory name (if type is
  * "dir").  CONTEXT should be the context, if it is left out then "display" is
@@ -579,7 +579,7 @@ static int exp_transform(int nargs,
   return sink_writes(output, cgi_sgmlquote(t)) < 0 ? -1 : 0;
 }
 
-/* @enabled@
+/*! @enabled@
  *
  * Expands to "true" if playing is enabled, otherwise "false".
  */
@@ -594,7 +594,7 @@ static int exp_enabled(int attribute((unused)) nargs,
   return mx_bool_result(output, e);
 }
 
-/* @random-enabled
+/*! @random-enabled
  *
  * Expands to "true" if random play is enabled, otherwise "false".
  */
@@ -609,7 +609,7 @@ static int exp_random_enabled(int attribute((unused)) nargs,
   return mx_bool_result(output, e);
 }
 
-/* @trackstate{TRACK}
+/*! @trackstate{TRACK}
  *
  * Expands to "playing" if TRACK is currently playing, or "queue" if it is in
  * the queue, otherwise to nothing.
@@ -635,7 +635,7 @@ static int exp_trackstate(int attribute((unused)) nargs,
   return 0;
 }
 
-/* @thisurl
+/*! @thisurl
  *
  * Expands to an UNQUOTED URL which points back to the current page.  (NB it
  * might not be byte for byte identical - for instance, CGI arguments might be
@@ -648,7 +648,7 @@ static int exp_thisurl(int attribute((unused)) nargs,
   return sink_writes(output, cgi_thisurl(config->url)) < 0 ? -1 : 0;
 }
 
-/* @resolve{TRACK}
+/*! @resolve{TRACK}
  *
  * Expands to an UNQUOTED name for the TRACK that is not an alias, or to
  * nothing if it is not a valid track.
@@ -664,7 +664,7 @@ static int exp_resolve(int attribute((unused)) nargs,
   return 0;
 }
 
-/* @paused
+/*! @paused
  *
  * Expands to "true" if the playing track is paused, to "false" if it is
  * playing (or if there is no playing track at all).
@@ -678,7 +678,7 @@ static int exp_paused(int attribute((unused)) nargs,
                                  && dcgi_playing->state == playing_paused));
 }
 
-/* @state{ID}@
+/*! @state{ID}@
  *
  * Expands to the current state of track ID.
  */
@@ -693,7 +693,7 @@ static int exp_state(int attribute((unused)) nargs,
   return 0;
 }
 
-/* @right{RIGHT}{WITH-RIGHT}{WITHOUT-RIGHT}@
+/*! @right{RIGHT}{WITH-RIGHT}{WITHOUT-RIGHT}@
  *
  * Expands to WITH-RIGHT if the current user has right RIGHT, otherwise to
  * WITHOUT-RIGHT.  The WITHOUT-RIGHT argument may be left out.
@@ -730,7 +730,7 @@ static int exp_right(int nargs,
   return 0;
 }
 
-/* @userinfo{PROPERTY}
+/*! @userinfo{PROPERTY}
  *
  * Expands to the named property of the current user.
  */
@@ -747,7 +747,7 @@ static int exp_userinfo(int attribute((unused)) nargs,
   return 0;
 }
 
-/* @error
+/*! @error
  *
  * Expands to the latest error string.
  */
@@ -755,10 +755,23 @@ static int exp_error(int attribute((unused)) nargs,
                      char attribute((unused)) **args,
                      struct sink *output,
                      void attribute((unused)) *u) {
-  return sink_writes(output, dcgi_error_string) < 0 ? -1 : 0;
+  return sink_writes(output, dcgi_error_string ? dcgi_error_string : "")
+              < 0 ? -1 : 0;
+}
+
+/*! @status
+ *
+ * Expands to the latest status string.
+ */
+static int exp_status(int attribute((unused)) nargs,
+                      char attribute((unused)) **args,
+                      struct sink *output,
+                      void attribute((unused)) *u) {
+  return sink_writes(output, dcgi_status_string ? dcgi_status_string : "")
+              < 0 ? -1 : 0;
 }
 
-/* @image{NAME}
+/*! @image{NAME}
  *
  * Expands to the URL of the image called NAME.
  *
@@ -798,19 +811,9 @@ static int exp_image(int attribute((unused)) nargs,
   return sink_writes(output, cgi_sgmlquote(url)) < 0 ? -1 : 0;
 }
 
-/** @brief Entry in a list of tracks or directories */
-struct entry {
-  /** @brief Track name */
-  const char *track;
-  /** @brief Sort key */
-  const char *sort;
-  /** @brief Display key */
-  const char *display;
-};
-
 /** @brief Compare two @ref entry objects */
-static int compare_entry(const void *a, const void *b) {
-  const struct entry *ea = a, *eb = b;
+int dcgi_compare_entry(const void *a, const void *b) {
+  const struct dcgi_entry *ea = a, *eb = b;
 
   return compare_tracks(ea->sort, eb->sort,
                        ea->display, eb->display,
@@ -831,7 +834,7 @@ static int exp__files_dirs(int nargs,
   char **tracks, *dir, *re;
   int n, ntracks, rc;
   const struct mx_node *m;
-  struct entry *e;
+  struct dcgi_entry *e;
 
   if((rc = mx_expandstr(args[0], &dir, u, "argument #0 (DIR)")))
     return rc;
@@ -856,7 +859,7 @@ static int exp__files_dirs(int nargs,
     e[n].sort = trackname_transform(type, tracks[n], "sort");
     e[n].display = trackname_transform(type, tracks[n], "display");
   }
-  qsort(e, ntracks, sizeof (struct entry), compare_entry);
+  qsort(e, ntracks, sizeof (struct dcgi_entry), dcgi_compare_entry);
   /* Expand the subsiduary templates.  We chuck in @sort and @display because
    * it is particularly easy to do so. */
   for(n = 0; n < ntracks; ++n)
@@ -875,17 +878,17 @@ static int exp__files_dirs(int nargs,
 
 }
 
-/** @tracks{DIR}{RE}{TEMPLATE}
+/*! @tracks{DIR}{RE}{TEMPLATE}
  *
  * For each track below DIR, expands TEMPLATE with the
  * following expansions:
- * - @track to the UNQUOTED track name
- * - @index to the track number from 0
- * - @parity to "even" or "odd" alternately
- * - @first to "true" on the first track and "false" otherwise
- * - @last to "true" on the last track and "false" otherwise
- * - @sort to the sort key for this track
- * - @display to the UNQUOTED display string for this track
+ * - @track: the UNQUOTED track name
+ * - @index: the track number from 0
+ * - @parity: "even" or "odd" alternately
+ * - @first: "true" on the first track and "false" otherwise
+ * - @last: "true" on the last track and "false" otherwise
+ * - @sort: the sort key for this track
+ * - @display: the UNQUOTED display string for this track
  *
  * RE is optional and if present is the regexp to match against.
  */
@@ -896,17 +899,17 @@ static int exp_tracks(int nargs,
   return exp__files_dirs(nargs, args, output, u, "track", disorder_files);
 }
 
-/** @dirs{DIR}{RE}{TEMPLATE}
+/*! @dirs{DIR}{RE}{TEMPLATE}
  *
  * For each directory below DIR, expands TEMPLATE with the
  * following expansions:
- * - @track to the UNQUOTED directory name
- * - @index to the directory number from 0
- * - @parity to "even" or "odd" alternately
- * - @first to "true" on the first directory and "false" otherwise
- * - @last to "true" on the last directory and "false" otherwise
- * - @sort to the sort key for this directory
- * - @display to the UNQUOTED display string for this directory
+ * - @track: the UNQUOTED directory name
+ * - @index: the directory number from 0
+ * - @parity: "even" or "odd" alternately
+ * - @first: "true" on the first directory and "false" otherwise
+ * - @last: "true" on the last directory and "false" otherwise
+ * - @sort: the sort key for this directory
+ * - @display: the UNQUOTED display string for this directory
  *
  * RE is optional and if present is the regexp to match against.
  */
@@ -923,17 +926,17 @@ static int exp__search_shim(disorder_client *c, const char *terms,
   return disorder_search(c, terms, vecp, nvecp);
 }
 
-/** @search{KEYWORDS}{TEMPLATE}
+/*! @search{KEYWORDS}{TEMPLATE}
  *
  * For each track matching KEYWORDS, expands TEMPLATE with the
  * following expansions:
- * - @track to the UNQUOTED directory name
- * - @index to the directory number from 0
- * - @parity to "even" or "odd" alternately
- * - @first to "true" on the first directory and "false" otherwise
- * - @last to "true" on the last directory and "false" otherwise
- * - @sort to the sort key for this track
- * - @display to the UNQUOTED display string for this track
+ * - @track: the UNQUOTED directory name
+ * - @index: the directory number from 0
+ * - @parity: "even" or "odd" alternately
+ * - @first: "true" on the first directory and "false" otherwise
+ * - @last: "true" on the last directory and "false" otherwise
+ * - @sort: the sort key for this track
+ * - @display: the UNQUOTED display string for this track
  */
 static int exp_search(int nargs,
                       const struct mx_node **args,
@@ -942,6 +945,11 @@ static int exp_search(int nargs,
   return exp__files_dirs(nargs, args, output, u, "track", exp__search_shim);
 }
 
+/*! @label{NAME}
+ *
+ * Expands to label NAME from options.labels.  Undefined lables expand to the
+ * last dot-separated component, e.g. X.Y.Z to Z.
+ */
 static int exp_label(int attribute((unused)) nargs,
                      char **args,
                      struct sink *output,
@@ -949,6 +957,48 @@ static int exp_label(int attribute((unused)) nargs,
   return sink_writes(output, option_label(args[0])) < 0 ? -1 : 0;
 }
 
+/*! @breadcrumbs{DIR}{TEMPLATE}
+ *
+ * Expands TEMPLATE for each directory in the path up to DIR, excluding the root
+ * but including DIR itself, with the following expansions:
+ * - @dir: the directory
+ * - @last: "true" if this is the last directory, otherwise "false"
+ *
+ * DIR must be an absolute path.
+ */
+static int exp_breadcrumbs(int attribute((unused)) nargs,
+                           const struct mx_node **args,
+                           struct sink *output,
+                           void attribute((unused)) *u) {
+  int rc;
+  char *dir, *parent, *ptr;
+  
+  if((rc = mx_expandstr(args[0], &dir, u, "argument #0 (DIR)")))
+    return rc;
+  /* Reject relative paths */
+  if(dir[0] != '/') {
+    error(0, "breadcrumbs: '%s' is a relative path", dir);
+    return 0;
+  }
+  /* Skip the root */
+  ptr = dir + 1;
+  while(*ptr) {
+    /* Find the end of this directory */
+    while(*ptr && *ptr != '/')
+      ++ptr;
+    parent = xstrndup(dir, ptr - dir);
+    if((rc = mx_expand(mx_rewritel(args[1],
+                                   "dir", parent,
+                                   "last", *ptr ? "false" : "true",
+                                   (char *)0),
+                       output, u)))
+      return rc;
+    if(*ptr)
+      ++ptr;
+  }
+  return 0;
+}
+  
 /** @brief Register DisOrder-specific expansions */
 void dcgi_expansions(void) {
   mx_register("arg", 1, 1, exp_arg);
@@ -972,6 +1022,7 @@ void dcgi_expansions(void) {
   mx_register("resolve", 1, 1, exp_resolve);
   mx_register("server-version", 0, 0, exp_server_version);
   mx_register("state", 1, 1, exp_state);
+  mx_register("status", 0, 0, exp_status);
   mx_register("thisurl", 0, 0, exp_thisurl);
   mx_register("trackstate", 1, 1, exp_trackstate);
   mx_register("transform", 2, 3, exp_transform);
@@ -982,6 +1033,7 @@ void dcgi_expansions(void) {
   mx_register("volume", 1, 1, exp_volume);
   mx_register("when", 1, 1, exp_when);
   mx_register("who", 1, 1, exp_who);
+  mx_register_magic("breadcrumbs", 2, 2, exp_breadcrumbs);
   mx_register_magic("dirs", 2, 3, exp_dirs);
   mx_register_magic("new", 1, 1, exp_new);
   mx_register_magic("playing", 0, 1, exp_playing);