+ return ret;
+ ret = load_afsi(&old_afsi, &obj);
+ if (ret < 0)
+ return ret;
+ new_afsi = old_afsi;
+ new_afsi.attributes |= cad->add_mask;
+ new_afsi.attributes &= ~cad->del_mask;
+ save_afsi(&new_afsi, &obj); /* in-place update */
+ return afs_event(AFSI_CHANGE, &cad->aca->pbout, &aced);
+}
+
+static int com_setatt_callback(struct afs_callback_arg *aca)
+{
+ 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,
+ .loop_col_num = AFTCOL_HASH,
+ .match_col_num = AFTCOL_PATH,
+ .pm_flags = PM_SKIP_EMPTY_NAME,
+ .data = &cad,
+ .action = change_atts
+ };
+ unsigned num_inputs;
+
+ 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);
+
+ ret = -E_ATTR_SYNTAX;
+ if (len == 0)
+ goto out;
+ 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, "invalid argument: %s\n", arg);
+ goto out;
+ }
+ if (c == '+')
+ cad.add_mask |= (one << bitnum);
+ else
+ cad.del_mask |= (one << bitnum);
+ }
+ /* no pattern given */
+ ret = -E_ATTR_SYNTAX;
+ goto out;
+set_atts:
+ pmd.input_skip = 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);