From: Andre Noll Date: Sun, 16 Mar 2025 23:15:40 +0000 (+0100) Subject: server: Deprecate setatt in favor of touch. X-Git-Url: https://git.tuebingen.mpg.de/?a=commitdiff_plain;h=93f4172802017468c00055ec0e154a0359b3d43d;p=paraslash.git server: Deprecate setatt in favor of touch. This adds --set-attribute and --unset-attribute to the touch subcommand, re-implementing the features of the setatt command with a saner syntax. We augment the change_atts_data structure using pre-computed values for verbose and dry-run mode and pass a pointer to this structure rather than the general callback arg pointer. The existing setatt subcommand neither sets nor consults the two new booleans. The touch completer of para_client is updated to complete the two new options, executing the lsatt subcommand to get the attribute names. The manual and the test suite also need minor adjustments. --- diff --git a/aft.c b/aft.c index 132f5119..36b5a490 100644 --- a/aft.c +++ b/aft.c @@ -2036,12 +2036,18 @@ out: } EXPORT_SERVER_CMD_HANDLER(add); +struct change_atts_data { + uint64_t add_mask, del_mask; + struct afs_callback_arg *aca; + bool verbose, dry_run; +}; + static int touch_audio_file(__a_unused struct osl_table *table, struct osl_row *row, const char *name, void *data) { - struct afs_callback_arg *aca = data; - bool v_given = SERVER_CMD_OPT_GIVEN(TOUCH, VERBOSE, aca->lpr); - const struct lls_opt_result *r_n, *r_l, *r_i, *r_y, *r_a; + struct change_atts_data *cad = data; + struct afs_callback_arg *aca = cad->aca; + const struct lls_opt_result *r_n, *r_l, *r_i, *r_y, *r_a, *r_s, *r_u; int ret; struct osl_object obj; struct afs_info old_afsi, new_afsi; @@ -2053,8 +2059,11 @@ static int touch_audio_file(__a_unused struct osl_table *table, r_i = SERVER_CMD_OPT_RESULT(TOUCH, IMAGE_ID, aca->lpr); r_y = SERVER_CMD_OPT_RESULT(TOUCH, LYRICS_ID, aca->lpr); r_a = SERVER_CMD_OPT_RESULT(TOUCH, AMP, aca->lpr); + r_s = SERVER_CMD_OPT_RESULT(TOUCH, SET_ATTRIBUTE, aca->lpr); + r_u = SERVER_CMD_OPT_RESULT(TOUCH, UNSET_ATTRIBUTE, aca->lpr); no_options = !lls_opt_given(r_n) && !lls_opt_given(r_l) && !lls_opt_given(r_i) - && !lls_opt_given(r_y) && !lls_opt_given(r_a); + && !lls_opt_given(r_y) && !lls_opt_given(r_a) + && !lls_opt_given(r_s) && !lls_opt_given(r_u); ret = get_afsi_object_of_row(row, &obj); if (ret < 0) { @@ -2070,7 +2079,7 @@ static int touch_audio_file(__a_unused struct osl_table *table, if (no_options) { new_afsi.num_played++; new_afsi.last_played = time(NULL); - if (v_given) + if (cad->verbose) para_printf(&aca->pbout, "%s: num_played = %u, " "last_played = now()\n", name, new_afsi.num_played); @@ -2085,33 +2094,70 @@ static int touch_audio_file(__a_unused struct osl_table *table, new_afsi.lyrics_id = lls_uint32_val(0, r_y); if (lls_opt_given(r_a)) new_afsi.amp = lls_uint32_val(0, r_a); - if (v_given) + new_afsi.attributes |= cad->add_mask; + new_afsi.attributes &= ~cad->del_mask; + if (cad->verbose) para_printf(&aca->pbout, "touching %s\n", name); } - save_afsi(&new_afsi, &obj); /* in-place update */ + if (!cad->dry_run) + save_afsi(&new_afsi, &obj); /* in-place update */ aced.aft_row = row; aced.old_afsi = &old_afsi; return afs_event(AFSI_CHANGE, &aca->pbout, &aced); } +/* + * Embed a change_atts_data structure into a pattern_match_data structure + * for the for_each_matching_row() iterator, with touch_audio_file() as + * the iterator callback for matching files. + */ static int com_touch_callback(struct afs_callback_arg *aca) { const struct lls_command *cmd = SERVER_CMD_CMD_PTR(TOUCH); bool p_given; - const struct lls_opt_result *r_i, *r_y; - int ret; + const struct lls_opt_result *r_s, *r_u, *r_i, *r_y; + const uint64_t one = 1; + int ret, s_given, u_given; + struct change_atts_data cad = {.aca = aca}; struct pattern_match_data pmd = { .table = audio_file_table, .loop_col_num = AFTCOL_HASH, .match_col_num = AFTCOL_PATH, - .data = aca, + .data = &cad, .action = touch_audio_file }; ret = lls_deserialize_parse_result(aca->query.data, cmd, &aca->lpr); assert(ret >= 0); pmd.lpr = aca->lpr; + cad.dry_run = SERVER_CMD_OPT_GIVEN(TOUCH, DRY_RUN, aca->lpr); + cad.verbose = cad.dry_run || SERVER_CMD_OPT_GIVEN(TOUCH, + VERBOSE, aca->lpr); + r_s = SERVER_CMD_OPT_RESULT(TOUCH, SET_ATTRIBUTE, aca->lpr); + s_given = SERVER_CMD_OPT_GIVEN(TOUCH, SET_ATTRIBUTE, aca->lpr); + for (int i = 0; i < s_given; i++) { + unsigned char bitnum; + const char *arg = lls_string_val(i, r_s); + ret = get_attribute_bitnum_by_name(arg, &bitnum); + if (ret < 0) { + afs_error(aca, "invalid attribute: %s\n", arg); + goto out; + } + cad.add_mask |= (one << bitnum); + } + r_u = SERVER_CMD_OPT_RESULT(TOUCH, UNSET_ATTRIBUTE, aca->lpr); + u_given = SERVER_CMD_OPT_GIVEN(TOUCH, UNSET_ATTRIBUTE, aca->lpr); + for (int i = 0; i < u_given; i++) { + unsigned char bitnum; + const char *arg = lls_string_val(i, r_u); + ret = get_attribute_bitnum_by_name(arg, &bitnum); + if (ret < 0) { + afs_error(aca, "invalid attribute: %s\n", arg); + goto out; + } + cad.del_mask |= (one << bitnum); + } r_i = SERVER_CMD_OPT_RESULT(TOUCH, IMAGE_ID, aca->lpr); if (lls_opt_given(r_i)) { uint32_t id = lls_uint32_val(0, r_i); @@ -2136,6 +2182,7 @@ static int com_touch_callback(struct afs_callback_arg *aca) ret = for_each_matching_row(&pmd); if (ret >= 0 && pmd.num_matches == 0) ret = -E_NO_MATCH; +out: lls_free_parse_result(aca->lpr, cmd); return ret; } @@ -2333,11 +2380,6 @@ static int com_cpsi(struct command_context *cc, struct lls_parse_result *lpr) } EXPORT_SERVER_CMD_HANDLER(cpsi); -struct change_atts_data { - uint64_t add_mask, del_mask; - struct afs_callback_arg *aca; -}; - static int change_atts(__a_unused struct osl_table *table, struct osl_row *row, __a_unused const char *name, void *data) { @@ -2426,6 +2468,7 @@ out: return ret; } +/* This function and its callback can be removed after 0.8.0 */ static int com_setatt(struct command_context *cc, struct lls_parse_result *lpr) { const struct lls_command *cmd = SERVER_CMD_CMD_PTR(SETATT); @@ -2436,6 +2479,10 @@ static int com_setatt(struct command_context *cc, struct lls_parse_result *lpr) send_errctx(cc, errctx); return ret; } + send_sb_va(&cc->scc, SBD_WARNING_LOG, + "Warning: 'setatt' is obsolete, " + "use 'touch --set-attribute' instead.\n" + ); return send_lls_callback_request(com_setatt_callback, cmd, lpr, cc); } EXPORT_SERVER_CMD_HANDLER(setatt); diff --git a/client.c b/client.c index 4ae60f23..444af57a 100644 --- a/client.c +++ b/client.c @@ -159,24 +159,39 @@ out: return ret; } -static int extract_matches_from_command(const char *word, char *cmd, - char ***matches) +static int extract_matches_from_command_pfx(const char *word, char *cmd, + const char *pfx, char ***matches) { - char *buf, **sl; - int ret; + char *buf, **argv; + int argc, ret; ret = execute_client_command(cmd, &buf); if (ret < 0) return ret; - ret = create_argv(buf, "\n", &sl); + ret = create_argv(buf, "\n", &argv); free(buf); if (ret < 0) return ret; - ret = i9e_extract_completions(word, sl, matches); - free_argv(sl); + argc = ret; + if (pfx) { + char **nargv = arr_alloc(argc + 1, sizeof(char *)); + for (unsigned n = 0; n < argc; n++) + nargv[n] = make_message("%s%s", pfx, argv[n]); + nargv[argc] = NULL; + free_argv(argv); + argv = nargv; + } + ret = i9e_extract_completions(word, argv, matches); + free_argv(argv); return ret; } +static int extract_matches_from_command(const char *word, char *cmd, + char ***matches) +{ + return extract_matches_from_command_pfx(word, cmd, NULL, matches); +} + static int complete_attributes(const char *word, char ***matches) { return extract_matches_from_command(word, "lsatt", matches); @@ -477,12 +492,29 @@ static void touch_completer(struct i9e_completion_info *ci, struct i9e_completion_result *cr) { char * const opts[] = {LSG_SERVER_CMD_TOUCH_OPTS, NULL}; + int num = i9e_cword_is_option_arg(opts, ci); + const char *opt; - if (i9e_cword_is_option_arg(opts, ci) >= 0) + if (num < 0) { /* no option arg, is it an option? */ + if (ci->word[0] == '-') + return i9e_complete_option(opts, ci, cr); + cr->filename_completion_desired = true; return; - if (ci->word[0] == '-') - return i9e_complete_option(opts, ci, cr); - cr->filename_completion_desired = true; + } + opt = opts[num]; + /* only attribute option args can be completed */ + if (strcmp(opt, "-s=") && strcmp(opt, "--set-attribute=") && + strcmp(opt, "-u=") && strcmp(opt, "--unset-attribute=")) + return; + + if (strncmp(opt, ci->word, strlen(opt))) { + /* complete on unprefixed attributes (-s att syntax) */ + extract_matches_from_command(ci->word, "lsatt", &cr->matches); + return; + } + /* complete on prefixed attributes (-s=att syntax) */ + extract_matches_from_command_pfx(ci->word, "lsatt", opt, &cr->matches); + return; } static void cpsi_completer(struct i9e_completion_info *ci, diff --git a/m4/lls/server_cmd.suite.m4 b/m4/lls/server_cmd.suite.m4 index 6056541c..9a447ad0 100644 --- a/m4/lls/server_cmd.suite.m4 +++ b/m4/lls/server_cmd.suite.m4 @@ -478,17 +478,13 @@ m4_include(`com_ll.m4') [/description] [subcommand setatt] - purpose = set or unset attributes + purpose = obsolete way to set or unset attributes synopsis = attribute{+|-}... pattern... aux_info = AFS_READ | AFS_WRITE [description] - Set ('+') or unset ('-') the given attributes for all audio files - matching the given pattern. Example: - - setatt rock+ punk+ pop- '*foo.mp3' - - sets the 'rock' and the 'punk' attribute and unsets the 'pop' attribute - of all files ending with 'foo.mp3'. + This subcommand is obsolete and should no longer be used. It has been + superseded by the --set-atribute and --unset-attribute options of + the touch subcommand. [/description] [subcommand si] @@ -544,9 +540,10 @@ m4_include(`com_ll.m4') This command modifies the afs info structure of all rows of the audio file table whose path matches at least one of the given patters. - If at least one option is given which takes a number as its argument, - only those fields of the afs info structure are updated which - correspond to the given options while all other fields stay unmodified. + If one or more options which affect the afs information are given, only + the corresponding fields of the afs info structure are updated. All other + fields stay unmodified. For example, "touch --numplayed=42" only modifies + the numplayed field. If no such option is given, the lastplayed field is set to the current time and the value of the numplayed field is increased by one while @@ -637,9 +634,33 @@ m4_include(`com_ll.m4') The amp filter of para_audiod amplifies the volume according to this value. [/help] + [option set-attribute] + short_opt = s + summary = turn on a bit in the attribute bitmask of each matching file + arg_type = string + arg_info = required_arg + typestr = attr + default_val = none + flag multiple + [help] + May be given more than once to set multiple attributes in one invocation. + [/help] + [option unset-attribute] + short_opt = u + summary = turn off a bit in the attribute bitmask of each matching file + arg_type = string + arg_info = required_arg + typestr = attribute + default_val = none + flag multiple + [help] + Like --set-attribute, this may be given multiple times. + [/help] [option verbose] short_opt = v summary = explain what is being done + [option dry-run] + summary = do not store the modified afs information [option pathname-match] short_opt = p summary = modify matching behaviour diff --git a/t/t0004-server.sh b/t/t0004-server.sh index f7a407dc..dff7f23a 100755 --- a/t/t0004-server.sh +++ b/t/t0004-server.sh @@ -74,9 +74,9 @@ cmdline[$i]="lsatt" good[$i]='^1$' let i++ -commands[$i]='setatt' +commands[$i]='touch' required_objects[$i]='ogg_afh' -cmdline[$i]="setatt 33+ ${oggs[@]}" +cmdline[$i]="touch --set-attribute=33 ${oggs[@]}" bad[$i]='.' let i++ diff --git a/web/manual.md b/web/manual.md index 4c7d3eb4..f81c341a 100644 --- a/web/manual.md +++ b/web/manual.md @@ -865,16 +865,16 @@ and lists all available attributes. You can set the "test" attribute for an audio file by executing - para_client setatt test+ /path/to/the/audio/file + para_client -- touch --set-attribute=test /path/to/the/audio/file Similarly, the "test" bit can be removed from an audio file with - para_client setatt test- /path/to/the/audio/file + para_client -- touch --unset-attribute=test /path/to/the/audio/file Instead of a path you may use a shell wildcard pattern. The attribute is applied to all audio files matching this pattern: - para_client setatt test+ '/test/directory/*' + para_client -- touch --set-attribute=test '/test/directory/*' The command @@ -883,7 +883,7 @@ The command gives you a verbose listing of your audio files also showing which attributes are set. -In case you wonder why the double-dash in the above command is needed: +In case you wonder why the double-dash in the above commands is needed: It tells para_client to not interpret the options after the dashes. If you find this annoying, just say @@ -898,7 +898,7 @@ The "test" attribute can be dropped from the database with Read the output of para help ls - para help setatt + para help touch for more information and a complete list of command line options to these commands.