Implement pattern matching for com_touch().
authorAndre Noll <maan@systemlinux.org>
Thu, 27 Sep 2007 15:47:46 +0000 (17:47 +0200)
committerAndre Noll <maan@systemlinux.org>
Thu, 27 Sep 2007 15:47:46 +0000 (17:47 +0200)
Also, add -p and -v options and fix converting the last played
time which is a 64 bit intger, so strtol() will fail do it on 32
bit machines after 2039 ;)

To fix this, introduce para_atoi64() which calls strtoll() which
is 64 bit even on 32 bit machines and define para_atoi32() which
calls para_atoi64() and checks the boundaries.

afs.c
afs.cmd
afs.h
aft.c
error.h
mood.c
string.c
string.h

diff --git a/afs.c b/afs.c
index bef3542..f40e022 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -337,40 +337,6 @@ int string_compare(const struct osl_object *obj1, const struct osl_object *obj2)
        return strncmp(str1, str2, PARA_MIN(obj1->size, obj2->size));
 }
 
-/**
- * A wrapper for strtol(3).
- *
- * \param str The string to be converted to a long integer.
- * \param result The converted value is stored here.
- *
- * \return Positive on success, -E_ATOL on errors.
- *
- * \sa strtol(3), atoi(3).
- */
-int para_atol(const char *str, long *result)
-{
-       char *endptr;
-       long val;
-       int ret, base = 10;
-
-       errno = 0; /* To distinguish success/failure after call */
-       val = strtol(str, &endptr, base);
-       ret = -E_ATOL;
-       if (errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
-               goto out; /* overflow */
-       if (errno != 0 && val == 0)
-               goto out; /* other error */
-       if (endptr == str)
-               goto out; /* No digits were found */
-       if (*endptr != '\0')
-               goto out; /* Further characters after number */
-       *result = val;
-       ret = 1;
-out:
-       return ret;
-}
-
-
 /*
  * write input from fd to dynamically allocated buffer,
  * but maximal max_size byte.
diff --git a/afs.cmd b/afs.cmd
index 200836e..f27fa4a 100644 (file)
--- a/afs.cmd
+++ b/afs.cmd
@@ -163,7 +163,7 @@ H:  a slash (see fnmatch(3)).
 N: touch
 P: AFS_READ | AFS_WRITE
 D: Manupulate the afs data for all audio files matching a pattern.
-U: touch [-n numplayed] [-l lastplayed] [-y lyrics_id] [-i image_id] pattern
+U: touch [-n numplayed] [-l lastplayed] [-y lyrics_id] [-i image_id] [-v] [-p] pattern
 H: If no option is given, lastplayed is set to the current time and numplayed is
 H: increased by one. Otherwise, only the given options are taken into account.
 H:
@@ -183,6 +183,13 @@ H: -y      Set the lyrics id. Specify the lyrics data file associated with
 H:     this audio file.
 H:
 H: -i  Set the image id. Same as -y, but sets the image.
+H:
+H: -v  Verbose mode. Explain what is being done.
+H:
+H: -p  Pathname match. Match a slash in the path only with a slash
+H:     in pattern and not by an asterisk (*) or a question mark
+H:     (?) metacharacter, nor by a bracket expression ([]) containing
+H:     a slash (see fnmatch(3)).
 ---
 T: add
 N: add@member@
diff --git a/afs.h b/afs.h
index f4f0222..9628d5c 100644 (file)
--- a/afs.h
+++ b/afs.h
@@ -72,7 +72,6 @@ int send_option_arg_callback_request(struct osl_object *options,
 int stdin_command(int fd, struct osl_object *arg_obj, callback_function *f,
                unsigned max_len, struct osl_object *result);
 int string_compare(const struct osl_object *obj1, const struct osl_object *obj2);
-int para_atol(const char *str, long *result);
 int open_next_audio_file(struct audio_file_data *afd);
 int close_audio_file(struct audio_file_data *afd);
 int for_each_matching_row(struct pattern_match_data *pmd);
diff --git a/aft.c b/aft.c
index 362ce1c..bcceb4b 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -1642,60 +1642,99 @@ int com_add(int fd, int argc, char * const * const argv)
 
 }
 
+enum touch_flags {
+       TOUCH_FLAG_FNM_PATHNAME = 1,
+       TOUCH_FLAG_VERBOSE = 2
+};
+
 struct com_touch_options {
-       long num_played;
-       long last_played;
-       long lyrics_id;
-       long image_id;
+       int32_t num_played;
+       int64_t last_played;
+       int32_t lyrics_id;
+       int32_t image_id;
+       unsigned flags;
 };
 
-static int com_touch_callback(const struct osl_object *query,
-               __a_unused struct osl_object *result)
+struct touch_action_data {
+       struct com_touch_options *cto;
+       struct para_buffer pb;
+};
+
+static int touch_audio_file(__a_unused struct osl_table *table,
+               struct osl_row *row, const char *name, void *data)
 {
-       struct com_touch_options *cto = query->data;
-       char *p = (char *)query->data + sizeof(*cto);
-       size_t len;
-       int ret, no_options = cto->num_played < 0 && cto->last_played < 0 &&
-               cto->lyrics_id < 0 && cto->image_id < 0;
-
-       for (;p < (char *)query->data + query->size; p += len + 1) {
-               struct afs_info old_afsi, new_afsi;
-               struct osl_object obj;
-               struct osl_row *row;
+       struct touch_action_data *tad = data;
+       struct osl_object obj;
+       struct afs_info old_afsi, new_afsi;
+       int ret, no_options = tad->cto->num_played < 0 && tad->cto->last_played < 0 &&
+               tad->cto->lyrics_id < 0 && tad->cto->image_id < 0;
 
-               len = strlen(p);
-               ret = aft_get_row_of_path(p, &row);
-               if (ret < 0)
-                       return ret;
-               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;
-               if (no_options) {
-                       new_afsi.num_played++;
-                       new_afsi.last_played = time(NULL);
-               } else {
-                       if (cto->lyrics_id >= 0)
-                               new_afsi.lyrics_id = cto->lyrics_id;
-                       if (cto->image_id >= 0)
-                               new_afsi.image_id = cto->image_id;
-                       if (cto->num_played >= 0)
-                               new_afsi.num_played = cto->num_played;
-                       if (cto->last_played >= 0)
-                               new_afsi.last_played = cto->last_played;
-               }
-               save_afsi(&new_afsi, &obj); /* in-place update */
-               ret = mood_update_audio_file(row, &old_afsi);
-               if (ret < 0)
-                       return ret;
+       ret = get_afsi_object_of_row(row, &obj);
+       if (ret < 0) {
+               para_printf(&tad->pb, "%s: %s\n", name, PARA_STRERROR(-ret));
+               return 1;
+       }
+       ret = load_afsi(&old_afsi, &obj);
+       if (ret < 0) {
+               para_printf(&tad->pb, "%s: %s\n", name, PARA_STRERROR(-ret));
+               return 1;
+       }
+       new_afsi = old_afsi;
+       if (no_options) {
+               new_afsi.num_played++;
+               new_afsi.last_played = time(NULL);
+               if (tad->cto->flags & TOUCH_FLAG_VERBOSE)
+                       para_printf(&tad->pb, "%s: num_played = %u, "
+                               "last_played = now()\n", name,
+                               new_afsi.num_played);
+       } else {
+               if (tad->cto->flags & TOUCH_FLAG_VERBOSE)
+                       para_printf(&tad->pb, "touching %s\n", name);
+               if (tad->cto->lyrics_id >= 0)
+                       new_afsi.lyrics_id = tad->cto->lyrics_id;
+               if (tad->cto->image_id >= 0)
+                       new_afsi.image_id = tad->cto->image_id;
+               if (tad->cto->num_played >= 0)
+                       new_afsi.num_played = tad->cto->num_played;
+               if (tad->cto->last_played >= 0)
+                       new_afsi.last_played = tad->cto->last_played;
        }
+       save_afsi(&new_afsi, &obj); /* in-place update */
+       ret = mood_update_audio_file(row, &old_afsi);
+       if (ret < 0)
+               para_printf(&tad->pb, "%s: %s\n", name, PARA_STRERROR(-ret));
        return 1;
 }
 
-int com_touch(__a_unused int fd, int argc, char * const * const argv)
+static int com_touch_callback(const struct osl_object *query,
+               struct osl_object *result)
+{
+       struct touch_action_data tad = {.cto = query->data};
+       int ret;
+       struct pattern_match_data pmd = {
+               .table = audio_file_table,
+               .loop_col_num = AFTCOL_HASH,
+               .match_col_num = AFTCOL_PATH,
+               .patterns = {.data = (char *)query->data + sizeof(*tad.cto),
+                       .size = query->size - sizeof(*tad.cto)},
+               .data = &tad,
+               .action = touch_audio_file
+       };
+       if (tad.cto->flags & TOUCH_FLAG_FNM_PATHNAME)
+               pmd.fnmatch_flags |= FNM_PATHNAME;
+       ret = for_each_matching_row(&pmd);
+       if (ret < 0)
+               para_printf(&tad.pb, "%s\n", PARA_STRERROR(-ret));
+       if (tad.pb.buf) {
+               result->data = tad.pb.buf;
+               result->size = tad.pb.size;
+               return 1;
+       }
+       return ret < 0? ret : 0;
+}
+
+
+int com_touch(int fd, int argc, char * const * const argv)
 {
        struct com_touch_options cto = {
                .num_played = -1,
@@ -1703,7 +1742,8 @@ int com_touch(__a_unused int fd, int argc, char * const * const argv)
                .lyrics_id = -1,
                .image_id = -1
        };
-       struct osl_object options = {.data = &cto, .size = sizeof(cto)};
+       struct osl_object query = {.data = &cto, .size = sizeof(cto)},
+               result;
        int i, ret;
 
 
@@ -1716,36 +1756,48 @@ int com_touch(__a_unused int fd, int argc, char * const * const argv)
                        break;
                }
                if (!strncmp(arg, "-n", 2)) {
-                       ret = para_atol(arg + 2, &cto.num_played);
+                       ret = para_atoi32(arg + 2, &cto.num_played);
                        if (ret < 0)
-                               goto err;
+                               return ret;
                        continue;
                }
                if (!strncmp(arg, "-l", 2)) {
-                       ret = para_atol(arg + 2, &cto.last_played);
+                       ret = para_atoi64(arg + 2, &cto.last_played);
                        if (ret < 0)
-                               goto err;
+                               return ret;
                        continue;
                }
                if (!strncmp(arg, "-y", 2)) {
-                       ret = para_atol(arg + 2, &cto.lyrics_id);
+                       ret = para_atoi32(arg + 2, &cto.lyrics_id);
                        if (ret < 0)
-                               goto err;
+                               return ret;
                        continue;
                }
                if (!strncmp(arg, "-i", 2)) {
-                       ret = para_atol(arg + 2, &cto.image_id);
+                       ret = para_atoi32(arg + 2, &cto.image_id);
                        if (ret < 0)
-                               goto err;
+                               return ret;
+                       continue;
+               }
+               if (!strcmp(arg, "-p")) {
+                       cto.flags |= TOUCH_FLAG_FNM_PATHNAME;
+                       continue;
+               }
+               if (!strcmp(arg, "-v")) {
+                       cto.flags |= TOUCH_FLAG_VERBOSE;
                        continue;
                }
+               break; /* non-option starting with dash */
        }
-       ret = -E_AFT_SYNTAX;
        if (i >= argc)
-               goto err;
-       return send_option_arg_callback_request(&options, argc - i,
-               argv + i, com_touch_callback, NULL);
-err:
+               return -E_AFT_SYNTAX;
+       ret = send_option_arg_callback_request(&query, argc - i,
+               argv + i, com_touch_callback, &result);
+       if (ret > 0) {
+               send_buffer(fd, (char *)result.data);
+               free(result.data);
+       } else
+               send_va_buffer(fd, "%s\n", PARA_STRERROR(-ret));
        return ret;
 }
 
@@ -1841,6 +1893,7 @@ int com_afs_rm(int fd, int argc,  char * const * const argv)
                        flags |= RM_FLAG_VERBOSE;
                        continue;
                }
+               break;
        }
        if (i >= argc)
                return -E_AFT_SYNTAX;
diff --git a/error.h b/error.h
index ca97dba..b7b66c6 100644 (file)
--- a/error.h
+++ b/error.h
@@ -153,7 +153,6 @@ extern const char **para_errlist[];
        PARA_ERROR(BAD_TABLE_NAME, "invalid table"), \
        PARA_ERROR(INPUT_TOO_LARGE, "input too large for stdin command"), \
        PARA_ERROR(READ, "read error"), \
-       PARA_ERROR(ATOL, "failed to convert to long"), \
        PARA_ERROR(AFS_SYNTAX, "afs syntax error"), \
 
 
@@ -349,6 +348,10 @@ extern const char **para_errlist[];
 #define STRING_ERRORS \
        PARA_ERROR(MKSTEMP, "mkstemp error: unable to create tmp file"), \
        PARA_ERROR(FCHMOD, "fchmod error: can not set mode"), \
+       PARA_ERROR(ATOI_OVERFLOW, "value too large"), \
+       PARA_ERROR(STRTOLL, "unknown strtoll error"), \
+       PARA_ERROR(ATOI_NO_DIGITS, "no digits found in string"), \
+       PARA_ERROR(ATOI_JUNK_AT_END, "further characters after number"), \
 
 
 #define EXEC_ERRORS \
diff --git a/mood.c b/mood.c
index 83c7fa6..54867e5 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -101,7 +101,7 @@ struct mood_item {
        /** The data structure computed by the mood parser. */
        void *parser_data;
        /** The given score value, or zero if none was given. */
-       long score_arg;
+       int32_t score_arg;
        /** Non-zero if random scoring was requested. */
        int random_score;
        /** Whether the "not" keyword was given in the mood line. */
@@ -418,7 +418,7 @@ static int parse_mood_line(char *mood_line, void *data)
                        goto out;
                if (strcmp(*w, "random")) {
                        mi->random_score = 0;
-                       ret = para_atol(*w, &mi->score_arg);
+                       ret = para_atoi32(*w, &mi->score_arg);
                        if (ret < 0)
                                goto out;
                } else {
index 60050ad..78d54f6 100644 (file)
--- a/string.c
+++ b/string.c
@@ -548,3 +548,64 @@ __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...)
        }
        return ret;
 }
+
+#ifndef LLONG_MAX
+#define LLONG_MAX (1 << (sizeof(long) - 1))
+#endif
+#ifndef LLONG_MIN
+#define LLONG_MIN (-LLONG_MAX - 1LL)
+#endif
+
+/**
+ * Convert a string to a 64-bit signed integer value.
+ *
+ * \param str The string to be converted.
+ * \param value Result pointer.
+ *
+ * \return Positive on success, negative on errors.
+ *
+ * \sa para_atoi32(), strtol(3), atoi(3).
+ */
+int para_atoi64(const char *str, int64_t *value)
+{
+       char *endptr;
+       long long tmp;
+
+       errno = 0; /* To distinguish success/failure after call */
+       tmp = strtoll(str, &endptr, 10);
+       if (errno == ERANGE && (tmp == LLONG_MAX || tmp == LLONG_MIN))
+               return -E_ATOI_OVERFLOW;
+       if (errno != 0 && tmp == 0) /* other error */
+               return -E_STRTOLL;
+       if (endptr == str)
+               return -E_ATOI_NO_DIGITS;
+       if (*endptr != '\0') /* Further characters after number */
+               return -E_ATOI_JUNK_AT_END;
+       *value = tmp;
+       return 1;
+}
+
+/**
+ * Convert a string to a 32-bit signed integer value.
+ *
+ * \param str The string to be converted.
+ * \param value Result pointer.
+ *
+ * \return Positive on success, negative on errors.
+ *
+ * \sa para_atoi64().
+*/
+int para_atoi32(const char *str, int32_t *value)
+{
+       int64_t tmp;
+       int ret;
+       const int32_t max = 2147483647;
+
+       ret = para_atoi64(str, &tmp);
+       if (ret < 0)
+               return ret;
+       if (tmp > max || tmp < -max - 1)
+               return -E_ATOI_OVERFLOW;
+       *value = tmp;
+       return 1;
+}
index a2dfa42..16f4610 100644 (file)
--- a/string.h
+++ b/string.h
@@ -34,4 +34,5 @@ int for_each_line(char *buf, size_t size, line_handler_t *line_handler,
        void *private_data);
 int for_each_line_ro(char *buf, size_t size, line_handler_t line_handler,
        void *private_data);
-
+int para_atoi64(const char *str, int64_t *result);
+int para_atoi32(const char *str, int32_t *value);