}
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;
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) {
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);
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);
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;
}
}
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)
{
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);
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);
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);
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,
[/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]
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
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