server: Convert blob commands to lopsub.
authorAndre Noll <maan@tuebingen.mpg.de>
Sun, 20 Mar 2016 18:41:25 +0000 (18:41 +0000)
committerAndre Noll <maan@tuebingen.mpg.de>
Sun, 26 Mar 2017 09:02:28 +0000 (11:02 +0200)
This converts all blob commands (ls, cat rm, mv) in one go to lopsub,
making the patch rather large.

The addblob commands are special in that we must pass not only the
blob name given at the command line to the callback but also the
blob data which was read from stdin. The command handler does not
serialize the parse result like most other commands do, but constructs
a query object containing name and data for the callback. Therefore
the command handler does not call send_lls_callback_request() but
send_callback_request(). We continue to follow this scheme.

blob.c contains a few macros which expand to a set of functions,
one for each type of bloc (mood, playlist, image, lyrics). These
macros have to be adjusted to take another argument because we need
the prefixes (e.g., lyr for lyrics) in both upper and lower case and
there is no preprocessor toupper().

afs.cmd
blob.c
m4/lls/server_cmd.suite.m4

diff --git a/afs.cmd b/afs.cmd
index 6047dbd..1c7583d 100644 (file)
--- a/afs.cmd
+++ b/afs.cmd
@@ -51,56 +51,3 @@ H:   y: by lyrics id
 H:   b: by bit rate
 H:   d: by duration
 H:   a: by audio format
----
-T: add
-N: add@member@
-O: int com_add@member@(struct command_context *cc);
-P: AFS_READ | AFS_WRITE
-D: Add stdin as a blob to the @member@ table.
-U: add@member@ @member@_name
-H: Read from stdin and ask the audio file selector to create a blob in the
-H: corresponding osl table. If the named blob already exists, it gets replaced
-H: with the new data.
----
-T: cat
-N: cat@member@
-O: int com_cat@member@(struct command_context *cc);
-P: AFS_READ
-D: Dump the contents of a blob of type @member@ to stdout.
-U: cat@member@ @member@_name
-H: Retrieve the named blob and write it to stdout.
----
-T: ls
-N: ls@member@
-O: int com_ls@member@(struct command_context *cc);
-P: AFS_READ
-D: List blobs of type @member@ which match a pattern.
-U: ls@member@ [-i] [-l] [-r] [pattern]
-H: Print the list of all blobs which match the given pattern. If no
-H: pattern is given, the full list is printed.
-H:
-H: Options:
-H:
-H: -i  Sort by identifier. The default is to sort alphabetically by name.
-H:
-H: -l  Print identifier and name. The default is to print only the name.
-H:
-H: -r  Reverse sort order.
----
-T: rm
-N: rm@member@
-O: int com_rm@member@(struct command_context *cc);
-P: AFS_READ | AFS_WRITE
-D: Remove blob(s) of type @member@ from the @member@ table.
-U: rm@member@ pattern...
-H: Remove all blobs whose name matches any of the given patterns.
----
-T: mv
-N: mv@member@
-O: int com_mv@member@(struct command_context *cc);
-P: AFS_READ | AFS_WRITE
-D: Rename a blob of type @member@.
-U: mv@member@ source_@member@_name dest_@member@_name
-H: Rename the blob identified by the source blob name to the destination blob
-H: name. The command fails if the source does not exist, or if the destination
-H: already exists.
diff --git a/blob.c b/blob.c
index 339897c..8abecf6 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -11,6 +11,7 @@
 #include <osl.h>
 #include <lopsub.h>
 
+#include "server_cmd.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "crypt.h"
@@ -91,26 +92,16 @@ INIT_BLOB_TABLE(moods);
 INIT_BLOB_TABLE(playlists);
 /** \endcond blob_table */
 
-/** Flags that may be passed to the \p ls functions of each blob  type. */
-enum blob_ls_flags {
-       /** List both id and name. */
-       BLOB_LS_FLAG_LONG = 1,
-       /** Reverse sort order. */
-       BLOB_LS_FLAG_REVERSE = 2,
-       /** Sort by id instead of name. */
-       BLOB_LS_FLAG_SORT_BY_ID = 4,
-};
-
 static int print_blob(struct osl_table *table, struct osl_row *row,
                const char *name, void *data)
 {
        struct afs_callback_arg *aca = data;
-       uint32_t flags = *(uint32_t *)aca->query.data;
+       bool l_given = SERVER_CMD_OPT_GIVEN(LSMOOD, LONG, aca->lpr);
        struct osl_object obj;
        uint32_t id;
        int ret;
 
-       if (!(flags & BLOB_LS_FLAG_LONG)) {
+       if (!l_given) {
                para_printf(&aca->pbout, "%s\n", name);
                return 0;
        }
@@ -124,68 +115,44 @@ static int print_blob(struct osl_table *table, struct osl_row *row,
        return 1;
 }
 
-static int com_lsblob_callback(struct osl_table *table,
-               struct afs_callback_arg *aca)
+static int com_lsblob_callback(const struct lls_command * const cmd,
+               struct osl_table *table, struct afs_callback_arg *aca)
 {
-       uint32_t flags = *(uint32_t *)aca->query.data;
+       bool i_given, r_given;
        struct pattern_match_data pmd = {
                .table = table,
-               .patterns = {.data = (char *)aca->query.data + sizeof(uint32_t),
-                       .size = aca->query.size - sizeof(uint32_t)},
                .pm_flags = PM_NO_PATTERN_MATCHES_EVERYTHING | PM_SKIP_EMPTY_NAME,
                .match_col_num = BLOBCOL_NAME,
                .data = aca,
                .action = print_blob,
        };
        int ret;
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       pmd.lpr = aca->lpr;
+       assert(ret >= 0);
+       i_given = SERVER_CMD_OPT_GIVEN(LSMOOD, ID_SORT, aca->lpr);
+       r_given = SERVER_CMD_OPT_GIVEN(LSMOOD, REVERSE, aca->lpr);
 
-       if (flags & BLOB_LS_FLAG_REVERSE)
+       if (r_given)
                pmd.pm_flags |= PM_REVERSE_LOOP;
-       if (!(flags & BLOB_LS_FLAG_SORT_BY_ID))
-               pmd.loop_col_num = BLOBCOL_NAME;
-       else
+       if (i_given)
                pmd.loop_col_num = BLOBCOL_ID;
+       else
+               pmd.loop_col_num = BLOBCOL_NAME;
        ret = for_each_matching_row(&pmd);
        if (ret < 0)
                goto out;
-       if (pmd.num_matches == 0 && pmd.patterns.size > 0)
+       if (pmd.num_matches == 0 && lls_num_inputs(aca->lpr) > 0)
                ret = -E_NO_MATCH;
 out:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-static int com_lsblob(afs_callback *f, struct command_context *cc)
+static int com_lsblob(afs_callback *f, const struct lls_command * const cmd,
+               struct command_context *cc, struct lls_parse_result *lpr)
 {
-       uint32_t flags = 0;
-       struct osl_object options = {.data = &flags, .size = sizeof(flags)};
-       int i;
-
-       for (i = 1; i < cc->argc; i++) {
-               const char *arg = cc->argv[i];
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strcmp(arg, "-l")) {
-                       flags |= BLOB_LS_FLAG_LONG;
-                       continue;
-               }
-               if (!strcmp(arg, "-i")) {
-                       flags |= BLOB_LS_FLAG_SORT_BY_ID;
-                       continue;
-               }
-               if (!strcmp(arg, "-r")) {
-                       flags |= BLOB_LS_FLAG_REVERSE;
-                       continue;
-               }
-               break;
-       }
-//     if (argc > i)
-//             return -E_BLOB_SYNTAX;
-       return send_option_arg_callback_request(&options, cc->argc - i,
-               cc->argv + i, f, afs_cb_result_handler, cc);
+       return send_lls_callback_request(f, cmd, lpr, cc);
 }
 
 static int cat_blob(struct osl_table *table, struct osl_row *row,
@@ -203,33 +170,42 @@ static int cat_blob(struct osl_table *table, struct osl_row *row,
        return (ret < 0)? ret : ret2;
 }
 
-static int com_catblob_callback(struct osl_table *table,
-               struct afs_callback_arg *aca)
+static int com_catblob_callback(const struct lls_command * const cmd,
+               struct osl_table *table, struct afs_callback_arg *aca)
 {
        int ret;
        struct pattern_match_data pmd = {
                .table = table,
-               .patterns = aca->query,
                .loop_col_num = BLOBCOL_NAME,
                .match_col_num = BLOBCOL_NAME,
                .pm_flags = PM_SKIP_EMPTY_NAME,
                .data = &aca->fd,
                .action = cat_blob
        };
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       assert(ret >= 0);
+       pmd.lpr = aca->lpr;
        ret = for_each_matching_row(&pmd);
        if (ret < 0)
-               return ret;
+               goto out;
        if (pmd.num_matches == 0)
                ret = -E_NO_MATCH;
+out:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-static int com_catblob(afs_callback *f, struct command_context *cc)
+static int com_catblob(afs_callback *f, const struct lls_command * const cmd,
+               struct command_context *cc, struct lls_parse_result *lpr)
 {
-       if (cc->argc < 2)
-               return -E_BLOB_SYNTAX;
-       return send_standard_callback_request(cc->argc - 1, cc->argv + 1, f,
-               afs_cb_result_handler, cc);
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx));
+
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       return send_lls_callback_request(f, cmd, lpr, cc);
 }
 
 static int remove_blob(struct osl_table *table, struct osl_row *row,
@@ -244,19 +220,21 @@ static int remove_blob(struct osl_table *table, struct osl_row *row,
        return 1;
 }
 
-static int com_rmblob_callback(struct osl_table *table,
-               struct afs_callback_arg *aca)
+static int com_rmblob_callback(const struct lls_command * const cmd,
+               struct osl_table *table, struct afs_callback_arg *aca)
 {
        int ret;
        struct pattern_match_data pmd = {
                .table = table,
-               .patterns = aca->query,
                .loop_col_num = BLOBCOL_NAME,
                .match_col_num = BLOBCOL_NAME,
                .pm_flags = PM_SKIP_EMPTY_NAME,
                .data = aca,
                .action = remove_blob
        };
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       assert(ret >= 0);
+       pmd.lpr = aca->lpr;
        ret = for_each_matching_row(&pmd);
        if (ret < 0)
                goto out;
@@ -268,19 +246,25 @@ static int com_rmblob_callback(struct osl_table *table,
                ret = afs_event(BLOB_REMOVE, NULL, table);
        }
 out:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-static int com_rmblob(afs_callback *f, struct command_context *cc)
+static int com_rmblob(afs_callback *f, const struct lls_command * const cmd,
+               struct command_context *cc, struct lls_parse_result *lpr)
 {
-       if (cc->argc < 2)
-               return -E_MOOD_SYNTAX;
-       return send_option_arg_callback_request(NULL, cc->argc - 1, cc->argv + 1, f,
-               afs_cb_result_handler, cc);
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx));
+
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       return send_lls_callback_request(f, cmd, lpr, cc);
 }
 
-static int com_addblob_callback(struct osl_table *table,
-               struct afs_callback_arg *aca)
+static int com_addblob_callback(__a_unused const struct lls_command * const cmd,
+               struct osl_table *table, struct afs_callback_arg *aca)
 {
        struct osl_object objs[NUM_BLOB_COLUMNS];
        char *name = aca->query.data;
@@ -400,11 +384,12 @@ again:
  * the name of the blob to create. The combined buffer is made available to the
  * afs process via the callback method.
  */
-static int stdin_command(struct command_context *cc, struct osl_object *arg_obj,
-               afs_callback *f)
+static int stdin_command(struct command_context *cc,
+               struct lls_parse_result *lpr, afs_callback *f)
 {
        struct osl_object query, stdin_obj;
        int ret;
+       size_t len = strlen(lls_input(0, lpr));
 
        ret = send_sb(&cc->scc, NULL, 0, SBD_AWAITING_DATA, false);
        if (ret < 0)
@@ -412,11 +397,11 @@ static int stdin_command(struct command_context *cc, struct osl_object *arg_obj,
        ret = fd2buf(&cc->scc, &stdin_obj);
        if (ret < 0)
                return ret;
-       query.size = arg_obj->size + stdin_obj.size;
+       query.size = len + 1 + stdin_obj.size;
        query.data = para_malloc(query.size);
-       memcpy(query.data, arg_obj->data, arg_obj->size);
+       memcpy(query.data, lls_input(0, lpr), len + 1);
        if (stdin_obj.size > 0)
-               memcpy((char *)query.data + arg_obj->size, stdin_obj.data,
+               memcpy((char *)query.data + len + 1, stdin_obj.data,
                        stdin_obj.size);
        free(stdin_obj.data);
        ret = send_callback_request(f, &query, afs_cb_result_handler, cc);
@@ -424,33 +409,42 @@ static int stdin_command(struct command_context *cc, struct osl_object *arg_obj,
        return ret;
 }
 
-static int com_addblob(afs_callback *f, struct command_context *cc)
+static int com_addblob(afs_callback *f, __a_unused const struct lls_command * const cmd,
+               struct command_context *cc, struct lls_parse_result *lpr)
 {
-       struct osl_object arg_obj;
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 1, 1, &errctx));
 
-       if (cc->argc != 2)
-               return -E_BLOB_SYNTAX;
-       if (!*cc->argv[1]) /* empty name is reserved for the dummy row */
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       if (!lls_input(0, lpr)[0]) /* empty name is reserved for the dummy row */
                return -E_BLOB_SYNTAX;
-       arg_obj.size = strlen(cc->argv[1]) + 1;
-       arg_obj.data = (char *)cc->argv[1];
-       return stdin_command(cc, &arg_obj, f);
+       return stdin_command(cc, lpr, f);
 }
 
-static int com_mvblob_callback(struct osl_table *table,
-               struct afs_callback_arg *aca)
+static int com_mvblob_callback(const struct lls_command * const cmd,
+               struct osl_table *table, struct afs_callback_arg *aca)
 {
-       char *src = (char *)aca->query.data;
-       struct osl_object obj = {.data = src, .size = strlen(src) + 1};
-       char *dest = src + obj.size;
+       const char *src, *dest;
+       struct osl_object obj;
        struct osl_row *row;
-       int ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row));
+       int ret;
+
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       assert(ret >= 0);
+       src = lls_input(0, aca->lpr);
+       dest = lls_input(1, aca->lpr);
+       obj.data = (char *)src;
+       obj.size = strlen(src) + 1;
+       ret = osl(osl_get_row(table, BLOBCOL_NAME, &obj, &row));
 
        if (ret < 0) {
                para_printf(&aca->pbout, "cannot find source blob %s\n", src);
                goto out;
        }
-       obj.data = dest;
+       obj.data = (char *)dest;
        obj.size = strlen(dest) + 1;
        ret = osl(osl_update_object(table, row, BLOBCOL_NAME, &obj));
        if (ret < 0) {
@@ -460,26 +454,35 @@ static int com_mvblob_callback(struct osl_table *table,
        }
        ret = afs_event(BLOB_RENAME, NULL, table);
 out:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-static int com_mvblob(afs_callback *f, struct command_context *cc)
+static int com_mvblob(afs_callback *f, const struct lls_command * const cmd,
+               struct command_context *cc, struct lls_parse_result *lpr)
 {
-       if (cc->argc != 3)
-               return -E_MOOD_SYNTAX;
-       return send_option_arg_callback_request(NULL, cc->argc - 1,
-               cc->argv + 1, f, afs_cb_result_handler, cc);
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 2, 2, &errctx));
+
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       return send_lls_callback_request(f, cmd, lpr, cc);
 }
 
-#define DEFINE_BLOB_COMMAND(cmd_name, table_name, cmd_prefix) \
-       static int com_ ## cmd_name ## cmd_prefix ## _callback(struct afs_callback_arg *aca) \
+#define DEFINE_BLOB_COMMAND(cmd_name, c_cmd_name, table_name, short_name, c_short_name) \
+       static int com_ ## cmd_name ## short_name ## _callback(struct afs_callback_arg *aca) \
        { \
-               return com_ ## cmd_name ## blob_callback(table_name ## _table, aca); \
+               const struct lls_command *cmd = SERVER_CMD_CMD_PTR(c_cmd_name ## c_short_name); \
+               return com_ ## cmd_name ## blob_callback(cmd, table_name ## _table, aca); \
        } \
-       int com_ ## cmd_name ## cmd_prefix(struct command_context *cc) \
+       static int com_ ## cmd_name ## short_name(struct command_context *cc, struct lls_parse_result *lpr) \
        { \
-               return com_ ## cmd_name ## blob(com_ ## cmd_name ## cmd_prefix ## _callback, cc); \
-       }
+               const struct lls_command *cmd = SERVER_CMD_CMD_PTR(c_cmd_name ## c_short_name); \
+               return com_ ## cmd_name ## blob(com_ ## cmd_name ## short_name ## _callback, cmd, cc, lpr); \
+       } \
+       EXPORT_SERVER_CMD_HANDLER(cmd_name ## short_name);
 
 static int blob_get_name_by_id(struct osl_table *table, uint32_t id,
                char **name)
@@ -630,25 +633,25 @@ static int blob_open(struct osl_table **table,
 
 
 /** Define all functions for this blob type. */
-#define DEFINE_BLOB_FUNCTIONS(table_name, cmd_prefix) \
+#define DEFINE_BLOB_FUNCTIONS(table_name, short_name, c_short_name) \
        DEFINE_BLOB_OPEN(table_name) \
        DEFINE_BLOB_CLOSE(table_name) \
        DEFINE_BLOB_CREATE(table_name) \
        DEFINE_BLOB_INIT(table_name) \
-       DEFINE_BLOB_COMMAND(ls, table_name, cmd_prefix) \
-       DEFINE_BLOB_COMMAND(cat, table_name, cmd_prefix) \
-       DEFINE_BLOB_COMMAND(add, table_name, cmd_prefix) \
-       DEFINE_BLOB_COMMAND(rm, table_name, cmd_prefix) \
-       DEFINE_BLOB_COMMAND(mv, table_name, cmd_prefix) \
-       DEFINE_GET_NAME_BY_ID(table_name, cmd_prefix); \
-       DEFINE_GET_DEF_BY_ID(table_name, cmd_prefix); \
-       DEFINE_GET_DEF_BY_NAME(table_name, cmd_prefix); \
-       DEFINE_GET_NAME_AND_DEF_BY_ROW(table_name, cmd_prefix); \
+       DEFINE_BLOB_COMMAND(ls, LS, table_name, short_name, c_short_name) \
+       DEFINE_BLOB_COMMAND(cat, CAT, table_name, short_name, c_short_name) \
+       DEFINE_BLOB_COMMAND(add, ADD, table_name, short_name, c_short_name) \
+       DEFINE_BLOB_COMMAND(rm, RM, table_name, short_name, c_short_name) \
+       DEFINE_BLOB_COMMAND(mv, MV, table_name, short_name, c_short_name) \
+       DEFINE_GET_NAME_BY_ID(table_name, short_name); \
+       DEFINE_GET_DEF_BY_ID(table_name, short_name); \
+       DEFINE_GET_DEF_BY_NAME(table_name, short_name); \
+       DEFINE_GET_NAME_AND_DEF_BY_ROW(table_name, short_name); \
 
 /* doxygen isn't smart enough to recognize these */
 /** \cond blob_function */
-DEFINE_BLOB_FUNCTIONS(lyrics, lyr);
-DEFINE_BLOB_FUNCTIONS(images, img);
-DEFINE_BLOB_FUNCTIONS(moods, mood);
-DEFINE_BLOB_FUNCTIONS(playlists, pl);
+DEFINE_BLOB_FUNCTIONS(lyrics, lyr, LYR);
+DEFINE_BLOB_FUNCTIONS(images, img, IMG);
+DEFINE_BLOB_FUNCTIONS(moods, mood, MOOD);
+DEFINE_BLOB_FUNCTIONS(playlists, pl, PL);
 /** \endcond blob_function */
index 83041dd..29df123 100644 (file)
@@ -507,3 +507,65 @@ aux_info_prefix = Permissions:
                short_opt = v
                summary = print detailed (multi-line) version text
 
+m4_define(`BLOB_COMMANDS', `
+[subcommand rm`$2']
+       purpose = remove `$1' blob(s)
+       non-opts-name = pattern...
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Remove all `$1' blobs which match any of the given patterns.
+       [/description]
+
+[subcommand mv`$2']
+       purpose = rename `$1' blob(s)
+       non-opts-name = source dest
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Rename `$1' source to dest. The command fails if the source `$1'
+               does not exist or if the destination `$1' already exists.
+       [/description]
+
+[subcommand add`$2']
+       purpose = add a blob to the `$1' table
+       non-opts-name = `$1'_name
+       aux_info = AFS_READ | AFS_WRITE
+       [description]
+               Read from stdin and ask the audio file selector to create a blob in
+               the `$1' table. If the named blob already exists, it gets replaced
+               with the new data.
+       [/description]
+
+[subcommand cat`$2']
+       purpose = dump a `$1' blob to stdout
+       non-opts-name = `$1'_name
+       aux_info = AFS_READ
+
+[subcommand ls`$2']
+       purpose = list blobs of type `$1' which match a pattern
+       non-opts-name = [pattern...]
+       aux_info = AFS_READ
+       [description]
+               Print the list of all blobs which match the given pattern. If no
+               pattern is given, the full list is printed.
+       [/description]
+       [option id-sort]
+               short_opt = i
+               summary = sort by identifier
+               [help]
+                       The default is to sort alphabetically by name.
+               [/help]
+       [option long]
+               short_opt = l
+               summary = long listing
+               [help]
+                       Print identifier and name. The default is to print only the name.
+               [/help]
+       [option reverse]
+               short_opt = r
+               summary = reverse sort order
+')
+
+BLOB_COMMANDS(`moods', `mood')
+BLOB_COMMANDS(`playlist', `pl')
+BLOB_COMMANDS(`image', `img')
+BLOB_COMMANDS(`lyrics', `lyr')