server: Convert com_mvatt() to lopsub.
[paraslash.git] / aft.c
diff --git a/aft.c b/aft.c
index f14440e6fb903548e25ccfdf611f4c2f2881aa49..f1864081cc2e362d1fc1c4dc31711489b1997c7e 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -11,7 +11,9 @@
 #include <fnmatch.h>
 #include <sys/shm.h>
 #include <osl.h>
+#include <lopsub.h>
 
+#include "server_cmd.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "crypt.h"
@@ -738,11 +740,11 @@ static void get_duration_buf(int seconds, char *buf, struct ls_options *opts)
        if (!hours) { /* m:ss or mm:ss */
                max_width = opts->mode == LS_MODE_LONG?
                        opts->widths.duration_width : 4;
-               sprintf(buf, "%*u:%02u", max_width - 3, mins, seconds % 60);
+               sprintf(buf, "%*u:%02d", max_width - 3, mins, seconds % 60);
        } else { /* more than one hour => h:mm:ss, hh:mm:ss, hhh:mm:ss, ... */
                max_width = opts->mode == LS_MODE_LONG?
                        opts->widths.duration_width : 7;
-               sprintf(buf, "%*u:%02u:%02u", max_width - 6, hours, mins,
+               sprintf(buf, "%*u:%02u:%02d", max_width - 6, hours, mins,
                        seconds % 60);
        }
 }
@@ -876,14 +878,14 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
                para_printf(b,
                        "%s "   /* attributes */
                        "%*u "  /* amp */
-                       "%*d "  /* image_id  */
-                       "%*d "  /* lyrics_id */
-                       "%*d "  /* bitrate */
+                       "%*u "  /* image_id  */
+                       "%*u "  /* lyrics_id */
+                       "%*u "  /* bitrate */
                        "%*s "  /* audio format */
-                       "%*d "  /* frequency */
-                       "%d "   /* channels */
+                       "%*u "  /* frequency */
+                       "%u "   /* channels */
                        "%s "   /* duration */
-                       "%*d "  /* num_played */
+                       "%*u "  /* num_played */
                        "%s "   /* last_played */
                        "%s\n", /* path */
                        att_buf,
@@ -930,7 +932,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
        WRITE_STATUS_ITEM(b, SI_SECONDS_TOTAL, "%" PRIu32 "\n",
                afhi->seconds_total);
        WRITE_STATUS_ITEM(b, SI_LAST_PLAYED, "%s\n", last_played_time);
-       WRITE_STATUS_ITEM(b, SI_NUM_PLAYED, "%d\n", afsi->num_played);
+       WRITE_STATUS_ITEM(b, SI_NUM_PLAYED, "%u\n", afsi->num_played);
        WRITE_STATUS_ITEM(b, SI_AMPLIFICATION, "%u\n", afsi->amp);
        WRITE_STATUS_ITEM(b, SI_CHUNK_TIME, "%lu\n", tv2ms(&afhi->chunk_tv));
        WRITE_STATUS_ITEM(b, SI_NUM_CHUNKS, "%" PRIu32 "\n",
@@ -1410,10 +1412,14 @@ int com_ls(struct command_context *cc)
                                return -E_AFT_SYNTAX;
                        }
                }
-               if (!strcmp(arg, "-p")) {
+               if (!strcmp(arg, "-p") || !strcmp(arg, "-F")) {
                        flags |= LS_FLAG_FULL_PATH;
                        continue;
                }
+               if (!strcmp(arg, "-b")) {
+                       flags &= ~LS_FLAG_FULL_PATH;
+                       continue;
+               }
                if (!strcmp(arg, "-a")) {
                        flags |= LS_FLAG_ADMISSIBLE_ONLY;
                        continue;
@@ -1518,8 +1524,8 @@ enum com_add_buffer_offsets {
        CAB_AFHI_OFFSET_POS = 0,
        /** Start of the chunk table (if present). */
        CAB_CHUNKS_OFFSET_POS = 4,
-       /** Flags given to the add command. */
-       CAB_FLAGS_OFFSET = 8,
+       /** Start of the (serialized) lopsub parse result. */
+       CAB_LPR_OFFSET = 8,
        /** Audio format id. */
        CAB_AUDIO_FORMAT_ID_OFFSET = 12,
        /** The hash of the audio file being added. */
@@ -1536,31 +1542,32 @@ enum com_add_buffer_offsets {
  * handler info won't be stored in the buffer.
  */
 static void save_add_callback_buffer(unsigned char *hash, const char *path,
-               struct afh_info *afhi, uint32_t flags,
+               struct afh_info *afhi, const char *slpr, size_t slpr_size,
                uint8_t audio_format_num, struct osl_object *obj)
 {
        size_t path_len = strlen(path) + 1;
        size_t afhi_size = sizeof_afhi_buf(afhi);
        size_t size = CAB_PATH_OFFSET + path_len + afhi_size
-               + sizeof_chunk_table(afhi);
+               + sizeof_chunk_table(afhi) + slpr_size;
        char *buf = para_malloc(size);
        uint32_t pos;
 
+       assert(size <= ~(uint32_t)0);
+       write_u8(buf + CAB_AUDIO_FORMAT_ID_OFFSET, audio_format_num);
+       memcpy(buf + CAB_HASH_OFFSET, hash, HASH_SIZE);
+       strcpy(buf + CAB_PATH_OFFSET, path);
        pos = CAB_PATH_OFFSET + path_len;
        write_u32(buf + CAB_AFHI_OFFSET_POS, pos);
        save_afhi(afhi, buf + pos);
        pos += afhi_size;
-
        write_u32(buf + CAB_CHUNKS_OFFSET_POS, pos);
-       if (afhi)
+       if (afhi) {
                save_chunk_table(afhi, buf + pos);
-
-       write_u32(buf + CAB_FLAGS_OFFSET, flags);
-       write_u8(buf + CAB_AUDIO_FORMAT_ID_OFFSET, audio_format_num);
-
-       memcpy(buf + CAB_HASH_OFFSET, hash, HASH_SIZE);
-       strcpy(buf + CAB_PATH_OFFSET, path);
-
+               pos += sizeof_chunk_table(afhi);
+       }
+       write_u32(buf + CAB_LPR_OFFSET, pos);
+       memcpy(buf + pos, slpr, slpr_size);
+       assert(pos + slpr_size == size);
        obj->data = buf;
        obj->size = size;
 }
@@ -1616,18 +1623,6 @@ Notes:
 
 */
 
-/** Flags passed to the add command. */
-enum com_add_flags {
-       /** Skip paths that exist already. */
-       ADD_FLAG_LAZY = 1,
-       /** Force adding. */
-       ADD_FLAG_FORCE = 2,
-       /** Print what is being done. */
-       ADD_FLAG_VERBOSE = 4,
-       /** Try to add files with unknown suffixes. */
-       ADD_FLAG_ALL = 8,
-};
-
 static int com_add_callback(struct afs_callback_arg *aca)
 {
        char *buf = aca->query.data, *path;
@@ -1638,9 +1633,16 @@ static int com_add_callback(struct afs_callback_arg *aca)
        char asc[2 * HASH_SIZE + 1];
        int ret;
        char afsi_buf[AFSI_SIZE];
-       uint32_t flags = read_u32(buf + CAB_FLAGS_OFFSET);
+       char *slpr = buf + read_u32(buf + CAB_LPR_OFFSET);
        struct afs_info default_afsi = {.last_played = 0};
        uint16_t afhi_offset, chunks_offset;
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADD);
+       const struct lls_opt_result *r_f, *r_v;
+
+       ret = lls_deserialize_parse_result(slpr, cmd, &aca->lpr);
+       assert(ret >= 0);
+       r_f = SERVER_CMD_OPT_RESULT(ADD, FORCE, aca->lpr);
+       r_v = SERVER_CMD_OPT_RESULT(ADD, VERBOSE, aca->lpr);
 
        hash = (unsigned char *)buf + CAB_HASH_OFFSET;
        hash_to_asc(hash, asc);
@@ -1658,8 +1660,8 @@ static int com_add_callback(struct afs_callback_arg *aca)
        ret = find_path_brother(path, &pb);
        if (ret < 0)
                goto out;
-       if (hs && pb && hs == pb && !(flags & ADD_FLAG_FORCE)) {
-               if (flags & ADD_FLAG_VERBOSE)
+       if (hs && pb && hs == pb && !lls_opt_given(r_f)) {
+               if (lls_opt_given(r_v))
                        para_printf(&aca->pbout, "ignoring duplicate\n");
                ret = 1;
                goto out;
@@ -1667,7 +1669,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
        if (hs && hs != pb) {
                struct osl_object obj;
                if (pb) { /* hs trumps pb, remove pb */
-                       if (flags & ADD_FLAG_VERBOSE)
+                       if (lls_opt_given(r_v))
                                para_printf(&aca->pbout, "removing %s\n", path);
                        ret = afs_event(AUDIO_FILE_REMOVE, &aca->pbout, pb);
                        if (ret < 0)
@@ -1678,7 +1680,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
                        pb = NULL;
                }
                /* file rename, update hs' path */
-               if (flags & ADD_FLAG_VERBOSE) {
+               if (lls_opt_given(r_v)) {
                        ret = osl(osl_get_object(audio_file_table, hs,
                                AFTCOL_PATH, &obj));
                        if (ret < 0)
@@ -1693,7 +1695,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
                ret = afs_event(AUDIO_FILE_RENAME, &aca->pbout, hs);
                if (ret < 0)
                        goto out;
-               if (!(flags & ADD_FLAG_FORCE))
+               if (!lls_opt_given(r_f))
                        goto out;
        }
        /* no hs or force mode, child must have sent afhi */
@@ -1714,18 +1716,18 @@ static int com_add_callback(struct afs_callback_arg *aca)
                if (ret < 0)
                        goto out;
                hash_to_asc(old_hash, old_asc);
-               if (flags & ADD_FLAG_VERBOSE)
+               if (lls_opt_given(r_v))
                        para_printf(&aca->pbout, "file change: %s -> %s\n",
                                old_asc, asc);
-               ret = osl_update_object(audio_file_table, pb, AFTCOL_HASH,
-                       &objs[AFTCOL_HASH]);
+               ret = osl(osl_update_object(audio_file_table, pb, AFTCOL_HASH,
+                       &objs[AFTCOL_HASH]));
                if (ret < 0)
                        goto out;
        }
        if (hs || pb) { /* (hs != NULL and pb != NULL) implies hs == pb */
                struct osl_row *row = pb? pb : hs;
                /* update afhi and chunk_table */
-               if (flags & ADD_FLAG_VERBOSE)
+               if (lls_opt_given(r_v))
                        para_printf(&aca->pbout,
                                "updating afhi and chunk table\n");
                ret = osl(osl_update_object(audio_file_table, row, AFTCOL_AFHI,
@@ -1737,12 +1739,10 @@ static int com_add_callback(struct afs_callback_arg *aca)
                if (ret < 0)
                        goto out;
                ret = afs_event(AFHI_CHANGE, &aca->pbout, row);
-               if (ret < 0)
-                       goto out;
                goto out;
        }
        /* new entry, use default afsi */
-       if (flags & ADD_FLAG_VERBOSE)
+       if (lls_opt_given(r_v))
                para_printf(&aca->pbout, "new file\n");
        default_afsi.last_played = time(NULL) - 365 * 24 * 60 * 60;
        default_afsi.audio_format_id = read_u8(buf + CAB_AUDIO_FORMAT_ID_OFFSET);
@@ -1757,15 +1757,20 @@ static int com_add_callback(struct afs_callback_arg *aca)
 out:
        if (ret < 0)
                para_printf(&aca->pbout, "could not add %s\n", path);
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-/** Used by com_add(). */
+/* Used by com_add(). */
 struct private_add_data {
-       /** The pointer passed to the original command handler. */
+       /* The pointer passed to the original command handler. */
        struct command_context *cc;
-       /** The given add flags. */
-       uint32_t flags;
+       /* Contains the flags given at the command line. */
+       struct lls_parse_result *lpr;
+       /* Serialized lopsub parse result. */
+       char *slpr;
+       /* Number of bytes. */
+       size_t slpr_size;
 };
 
 static int path_brother_callback(struct afs_callback_arg *aca)
@@ -1810,9 +1815,13 @@ static int add_one_audio_file(const char *path, void *private_data)
        struct osl_row *pb = NULL, *hs = NULL; /* path brother/hash sister */
        struct osl_object map, obj = {.data = NULL}, query;
        unsigned char hash[HASH_SIZE];
+       bool a_given = SERVER_CMD_OPT_GIVEN(ADD, ALL, pad->lpr);
+       bool f_given = SERVER_CMD_OPT_GIVEN(ADD, FORCE, pad->lpr);
+       bool l_given = SERVER_CMD_OPT_GIVEN(ADD, LAZY, pad->lpr);
+       bool v_given = SERVER_CMD_OPT_GIVEN(ADD, VERBOSE, pad->lpr);
 
        ret = guess_audio_format(path);
-       if (ret < 0 && !(pad->flags & ADD_FLAG_ALL)) {
+       if (ret < 0 && !a_given) {
                ret = 0;
                goto out_free;
        }
@@ -1823,8 +1832,8 @@ static int add_one_audio_file(const char *path, void *private_data)
        if (ret < 0 && ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
                goto out_free;
        ret = 1;
-       if (pb && (pad->flags & ADD_FLAG_LAZY)) { /* lazy is really cheap */
-               if (pad->flags & ADD_FLAG_VERBOSE)
+       if (pb && l_given) { /* lazy is really cheap */
+               if (v_given)
                        send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT,
                                "lazy-ignore: %s\n", path);
                goto out_free;
@@ -1844,8 +1853,8 @@ static int add_one_audio_file(const char *path, void *private_data)
                goto out_unmap;
        /* Return success if we already know this file. */
        ret = 1;
-       if (pb && hs && hs == pb && !(pad->flags & ADD_FLAG_FORCE)) {
-               if (pad->flags & ADD_FLAG_VERBOSE)
+       if (pb && hs && hs == pb && !f_given) {
+               if (v_given)
                        send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT,
                                "%s exists, not forcing update\n", path);
                goto out_unmap;
@@ -1854,7 +1863,7 @@ static int add_one_audio_file(const char *path, void *private_data)
         * We won't recalculate the audio format info and the chunk table if
         * there is a hash sister and FORCE was not given.
         */
-       if (!hs || (pad->flags & ADD_FLAG_FORCE)) {
+       if (!hs || f_given) {
                ret = compute_afhi(path, map.data, map.size, fd, &afhi);
                if (ret < 0)
                        goto out_unmap;
@@ -1863,13 +1872,14 @@ static int add_one_audio_file(const char *path, void *private_data)
        }
        munmap(map.data, map.size);
        close(fd);
-       if (pad->flags & ADD_FLAG_VERBOSE) {
+       if (v_given) {
                send_ret = send_sb_va(&pad->cc->scc, SBD_OUTPUT,
                        "adding %s\n", path);
                if (send_ret < 0)
                        goto out_free;
        }
-       save_add_callback_buffer(hash, path, afhi_ptr, pad->flags, format_num, &obj);
+       save_add_callback_buffer(hash, path, afhi_ptr, pad->slpr,
+               pad->slpr_size, format_num, &obj);
        /* Ask afs to consider this entry for adding. */
        ret = send_callback_request(com_add_callback, &obj,
                afs_cb_result_handler, pad->cc);
@@ -1888,46 +1898,30 @@ out_free:
        return send_ret;
 }
 
-int com_add(struct command_context *cc)
+static int com_add(struct command_context *cc, struct lls_parse_result *lpr)
 {
        int i, ret;
-       struct private_add_data pad = {.cc = cc, .flags = 0};
+       struct private_add_data pad = {.cc = cc, .lpr = lpr};
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(ADD);
+       unsigned num_inputs;
+       char *errctx;
 
-       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, "-a")) {
-                       pad.flags |= ADD_FLAG_ALL;
-                       continue;
-               }
-               if (!strcmp(arg, "-l")) {
-                       pad.flags |= ADD_FLAG_LAZY;
-                       continue;
-               }
-               if (!strcmp(arg, "-f")) {
-                       pad.flags |= ADD_FLAG_FORCE;
-                       continue;
-               }
-               if (!strcmp(arg, "-v")) {
-                       pad.flags |= ADD_FLAG_VERBOSE;
-                       continue;
-               }
+       ret = lls(lls_check_arg_count(lpr, 1, INT_MAX, &errctx));
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
        }
-       if (cc->argc <= i)
-               return -E_AFT_SYNTAX;
-       for (; i < cc->argc; i++) {
+       ret = lls_serialize_parse_result(lpr, cmd, &pad.slpr, &pad.slpr_size);
+       assert(ret >= 0);
+       num_inputs = lls_num_inputs(lpr);
+       for (i = 0; i < num_inputs; i++) {
                char *path;
-               ret = verify_path(cc->argv[i], &path);
+               ret = verify_path(lls_input(i, lpr), &path);
                if (ret < 0) {
                        ret = send_sb_va(&cc->scc, SBD_ERROR_LOG, "%s: %s\n",
-                               cc->argv[i], para_strerror(-ret));
+                               lls_input(i, lpr), para_strerror(-ret));
                        if (ret < 0)
-                               return ret;
+                               goto out;
                        continue;
                }
                if (ret == 1) /* directory */
@@ -1943,8 +1937,12 @@ int com_add(struct command_context *cc)
                }
                free(path);
        }
-       return 1;
+       ret = 1;
+out:
+       free(pad.slpr);
+       return ret;
 }
+EXPORT_SERVER_CMD_HANDLER(add);
 
 /**
  * Flags used by the touch command.
@@ -2040,6 +2038,22 @@ static int com_touch_callback(struct afs_callback_arg *aca)
                .data = aca,
                .action = touch_audio_file
        };
+       if (cto->image_id >= 0) {
+               ret = img_get_name_by_id(cto->image_id, NULL);
+               if (ret < 0) {
+                       para_printf(&aca->pbout, "invalid image ID: %d\n",
+                               cto->image_id);
+                       return ret;
+               }
+       }
+       if (cto->lyrics_id >= 0) {
+               ret = lyr_get_name_by_id(cto->lyrics_id, NULL);
+               if (ret < 0) {
+                       para_printf(&aca->pbout, "invalid lyrics ID: %d\n",
+                               cto->lyrics_id);
+                       return ret;
+               }
+       }
        if (cto->flags & TOUCH_FLAG_FNM_PATHNAME)
                pmd.fnmatch_flags |= FNM_PATHNAME;
        ret = for_each_matching_row(&pmd);
@@ -2387,9 +2401,8 @@ static int change_atts(__a_unused struct osl_table *table,
 
 static int com_setatt_callback(struct afs_callback_arg *aca)
 {
-       char *p;
-       int ret;
-       size_t len;
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SETATT);
+       int i, ret;
        struct change_atts_data cad = {.aca = aca};
        struct pattern_match_data pmd = {
                .table = audio_file_table,
@@ -2399,27 +2412,36 @@ static int com_setatt_callback(struct afs_callback_arg *aca)
                .data = &cad,
                .action = change_atts
        };
+       unsigned num_inputs;
 
-       for (
-               p = aca->query.data;
-               p < (char *)aca->query.data + aca->query.size;
-               p += len + 1
-       ) {
-               char c;
+       ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr);
+       assert(ret >= 0);
+       pmd.lpr = aca->lpr;
+
+       num_inputs = lls_num_inputs(aca->lpr);
+       for (i = 0; i < num_inputs; i++) {
                unsigned char bitnum;
                uint64_t one = 1;
+               const char *arg = lls_input(i, aca->lpr);
+               char c, *p;
+               size_t len = strlen(arg);
 
-               len = strlen(p);
                ret = -E_ATTR_SYNTAX;
                if (len == 0)
                        goto out;
-               c = p[len - 1];
-               if (c != '+' && c != '-')
-                       break;
+               c = arg[len - 1];
+               if (c != '+' && c != '-') {
+                       if (cad.add_mask == 0 && cad.del_mask == 0)
+                               goto out; /* no attribute modifier given */
+                       goto set_atts;
+               }
+               p = para_malloc(len);
+               memcpy(p, arg, len - 1);
                p[len - 1] = '\0';
                ret = get_attribute_bitnum_by_name(p, &bitnum);
+               free(p);
                if (ret < 0) {
-                       para_printf(&aca->pbout, "attribute not found: %s\n", p);
+                       para_printf(&aca->pbout, "invalid argument: %s\n", arg);
                        goto out;
                }
                if (c == '+')
@@ -2427,29 +2449,32 @@ static int com_setatt_callback(struct afs_callback_arg *aca)
                else
                        cad.del_mask |= (one << bitnum);
        }
+       /* no pattern given */
        ret = -E_ATTR_SYNTAX;
-       if (!cad.add_mask && !cad.del_mask)
-               goto out;
-       pmd.patterns.data = p;
-       if (p >= (char *)aca->query.data + aca->query.size)
-               goto out;
-       pmd.patterns.size = (char *)aca->query.data + aca->query.size - p;
+       goto out;
+set_atts:
+       pmd.input_skip = i;
        ret = for_each_matching_row(&pmd);
-       if (ret < 0)
-               goto out;
-       if (pmd.num_matches == 0)
+       if (ret >= 0 && pmd.num_matches == 0)
                ret = -E_NO_MATCH;
 out:
+       lls_free_parse_result(aca->lpr, cmd);
        return ret;
 }
 
-int com_setatt(struct command_context *cc)
+static int com_setatt(struct command_context *cc, struct lls_parse_result *lpr)
 {
-       if (cc->argc < 3)
-               return -E_ATTR_SYNTAX;
-       return send_standard_callback_request(cc->argc - 1, cc->argv + 1,
-               com_setatt_callback, afs_cb_result_handler, cc);
+       const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SETATT);
+       char *errctx;
+       int ret = lls(lls_check_arg_count(lpr, 2, INT_MAX, &errctx));
+
+       if (ret < 0) {
+               send_errctx(cc, errctx);
+               return ret;
+       }
+       return send_lls_callback_request(com_setatt_callback, cmd, lpr, cc);
 }
+EXPORT_SERVER_CMD_HANDLER(setatt);
 
 static int afs_stat_callback(struct afs_callback_arg *aca)
 {
@@ -2621,7 +2646,7 @@ static int aft_open(const char *dir)
        if (ret >= 0) {
                unsigned num;
                osl_get_num_rows(audio_file_table, &num);
-               PARA_INFO_LOG("audio file table contains %d files\n", num);
+               PARA_INFO_LOG("audio file table contains %u files\n", num);
                return ret;
        }
        PARA_NOTICE_LOG("failed to open audio file table\n");