com_setatt(): Allow to specify a file name pattern.
authorAndre Noll <maan@systemlinux.org>
Mon, 22 Jul 2013 20:10:40 +0000 (22:10 +0200)
committerAndre Noll <maan@systemlinux.org>
Sat, 10 Aug 2013 19:44:33 +0000 (21:44 +0200)
The help text of the setatt command claims that file name patterns
may be specified as arguments. The text also contains an example which
involves a wildcard pattern. However, this never worked as advertised
since the command actually accepts only full paths.

This commit teaches the command to process the given arguments as
patterns by employing the same pattern matching loop that is also
used by other commands.

In addition we now also send a "no matches" message to the client
in case there was no match. Previously the command stayed silent and
terminated successfully in this case.

aft.c

diff --git a/aft.c b/aft.c
index 412d19a..1d2d6d8 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -2502,13 +2502,60 @@ int com_cpsi(struct command_context *cc)
        return ret;
 }
 
-static void com_setatt_callback(__a_unused int fd, const struct osl_object *query)
+struct change_atts_data {
+       uint64_t add_mask, del_mask;
+       struct para_buffer pb;
+};
+
+static int change_atts(__a_unused struct osl_table *table,
+               struct osl_row *row, __a_unused const char *name, void *data)
+{
+       int ret;
+       struct osl_object obj;
+       struct afs_info old_afsi, new_afsi;
+       struct afsi_change_event_data aced = {
+               .aft_row = row,
+               .old_afsi = &old_afsi
+       };
+       struct change_atts_data *cad = data;
+
+       ret = get_afsi_object_of_row(row, &obj);
+       if (ret < 0)
+               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 */
+       afs_event(AFSI_CHANGE, &cad->pb, &aced);
+       return 1;
+}
+
+static void com_setatt_callback(int fd, const struct osl_object *query)
 {
        char *p;
-       uint64_t add_mask = 0, del_mask = 0;
        int ret;
        size_t len;
-       struct osl_object obj;
+       struct change_atts_data cad = {
+               .pb = {
+                       .max_size = shm_get_shmmax(),
+                       .max_size_handler = afs_max_size_handler,
+                       .private_data = &(struct afs_max_size_handler_data) {
+                               .fd = fd,
+                               .band = SBD_OUTPUT
+                       }
+               }
+       };
+       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
+       };
 
        for (p = query->data; p < (char *)query->data + query->size; p += len + 1) {
                char c;
@@ -2516,7 +2563,7 @@ static void com_setatt_callback(__a_unused int fd, const struct osl_object *quer
 
                len = strlen(p);
                ret = -E_ATTR_SYNTAX;
-               if (!*p)
+               if (len == 0)
                        goto out;
                c = p[len - 1];
                if (c != '+' && c != '-')
@@ -2526,38 +2573,27 @@ static void com_setatt_callback(__a_unused int fd, const struct osl_object *quer
                if (ret < 0)
                        goto out;
                if (c == '+')
-                       add_mask |= (1UL << bitnum);
+                       cad.add_mask |= (1UL << bitnum);
                else
-                       del_mask |= (1UL << bitnum);
+                       cad.del_mask |= (1UL << bitnum);
        }
        ret = -E_ATTR_SYNTAX;
-       if (!add_mask && !del_mask)
+       if (!cad.add_mask && !cad.del_mask)
                goto out;
-       PARA_DEBUG_LOG("masks: %llx:%llx\n",(long long unsigned)add_mask,
-               (long long unsigned)del_mask);
-       for (; p < (char *)query->data + query->size; p += len + 1) { /* TODO: fnmatch */
-               struct afs_info old_afsi, new_afsi;
-               struct afsi_change_event_data aced = {.old_afsi = &old_afsi};
-
-               len = strlen(p);
-               ret = aft_get_row_of_path(p, &aced.aft_row);
-               if (ret < 0)
-                       goto out;
-               ret = get_afsi_object_of_row(aced.aft_row, &obj);
-               if (ret < 0)
-                       goto out;
-               ret = load_afsi(&old_afsi, &obj);
-               if (ret < 0)
-                       goto out;
-               new_afsi = old_afsi;
-               new_afsi.attributes |= add_mask;
-               new_afsi.attributes &= ~del_mask;
-               save_afsi(&new_afsi, &obj); /* in-place update */
-               afs_event(AFSI_CHANGE, NULL, &aced);
-       }
+       pmd.patterns.data = p;
+       assert(p < (char *)query->data + query->size);
+       pmd.patterns.size = (char *)query->data + query->size - p;
+       ret = for_each_matching_row(&pmd);
+       if (ret < 0)
+               goto out;
+       if (pmd.num_matches == 0)
+               para_printf(&cad.pb, "no matches\n");
 out:
        if (ret < 0)
-               PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
+               para_printf(&cad.pb, "%s\n", para_strerror(-ret));
+       if (cad.pb.offset)
+               pass_buffer_as_shm(fd, SBD_OUTPUT, cad.pb.buf, cad.pb.offset);
+       free(cad.pb.buf);
 }
 
 int com_setatt(struct command_context *cc)
@@ -2565,7 +2601,7 @@ int com_setatt(struct command_context *cc)
        if (cc->argc < 3)
                return -E_ATTR_SYNTAX;
        return send_standard_callback_request(cc->argc - 1, cc->argv + 1,
-               com_setatt_callback, NULL, NULL);
+               com_setatt_callback, afs_cb_result_handler, cc);
 }
 
 static void afs_stat_callback(int fd, const struct osl_object *query)