Improve lastplayed time format for ls and stat output.
[paraslash.git] / aft.c
diff --git a/aft.c b/aft.c
index 47931656a019b2eb3d548af7332fd5e17a138111..8df2bc6091e55945843e7a93b0eec7f4f5cbf2b3 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -241,19 +241,12 @@ enum audio_file_table_columns {
        NUM_AFT_COLUMNS
 };
 
-/**
- * Compare two osl objects pointing to hash values.
- *
- * \param obj1 Pointer to the first hash object.
- * \param obj2 Pointer to the second hash object.
- *
- * \return The values required for an osl compare function.
- *
- * \sa osl_compare_func, uint32_compare().
- */
-static int aft_hash_compare(const struct osl_object *obj1, const struct osl_object *obj2)
+/* compare function for the hash column */
+static int aft_hash_compare(const struct osl_object *obj1,
+               const struct osl_object *obj2)
 {
-       return hash_compare((unsigned char *)obj1->data, (unsigned char *)obj2->data);
+       return hash_compare((unsigned char *)obj1->data,
+               (unsigned char *)obj2->data);
 }
 
 static struct osl_column_description aft_cols[] = {
@@ -684,6 +677,11 @@ static int get_local_time(uint64_t *seconds, char *buf, size_t size,
        time_t current_time, enum ls_listing_mode lm)
 {
        struct tm t;
+       /*
+        * Omit year but show time if the given value is closer to the current
+        * time than this many seconds.
+        */
+       const time_t m = 6 * 30 * 24 * 3600; /* six months */
 
        if (!localtime_r((time_t *)seconds, &t))
                return -E_LOCALTIME;
@@ -692,12 +690,12 @@ static int get_local_time(uint64_t *seconds, char *buf, size_t size,
                        return -E_STRFTIME;
                return 1;
        }
-       if (*seconds + 6 * 30 * 24 * 3600 > current_time) {
+       if (*seconds > current_time - m && *seconds < current_time + m) {
                if (!strftime(buf, size, "%b %e %k:%M", &t))
                        return -E_STRFTIME;
                return 1;
        }
-       if (!strftime(buf, size, "%b %e  %Y", &t))
+       if (!strftime(buf, size, "%b %e %Y", &t))
                return -E_STRFTIME;
        return 1;
 }
@@ -1365,18 +1363,26 @@ int com_ls(struct command_context *cc)
                        i++;
                        break;
                }
+               /*
+                * Compatibility: Prior to 0.5.5 it was necessary to specify
+                * the listing mode without the '=' character as in -lv, for
+                * example. Now the variant with '=' is preferred and
+                * documented but we still accept the old way to specify the
+                * listing mode.
+                *
+                * Support for the legacy syntax can be dropped at 0.6.0
+                * or later.
+                */
                if (!strncmp(arg, "-l", 2)) {
-                       if (!*(arg + 2)) {
-                               mode = LS_MODE_LONG;
-                               continue;
-                       }
-                       if (*(arg + 3))
-                               return -E_AFT_SYNTAX;
-                       switch(*(arg + 2)) {
+                       arg += 2;
+                       if (*arg == '=')
+                               arg++;
+                       switch (*arg) {
                        case 's':
                                mode = LS_MODE_SHORT;
                                continue;
                        case 'l':
+                       case '\0':
                                mode = LS_MODE_LONG;
                                continue;
                        case 'v':
@@ -1411,10 +1417,12 @@ int com_ls(struct command_context *cc)
                        flags |= LS_FLAG_UNIXDATE;
                        continue;
                }
+               /* The compatibility remark above applies also to -s. */
                if (!strncmp(arg, "-s", 2)) {
-                       if (!*(arg + 2) || *(arg + 3))
-                               return -E_AFT_SYNTAX;
-                       switch(*(arg + 2)) {
+                       arg += 2;
+                       if (*arg == '=')
+                               arg++;
+                       switch (*arg) {
                        case 'p':
                                sort = LS_SORT_BY_PATH;
                                continue;
@@ -1477,27 +1485,36 @@ int audio_file_loop(void *private_data, osl_rbtree_loop_func *func)
                func));
 }
 
-static struct osl_row *find_hash_sister(unsigned char *hash)
+static int find_hash_sister(unsigned char *hash, struct osl_row **result)
 {
-       const struct osl_object obj = {.data = hash, .size = HASH_SIZE};
-       struct osl_row *row;
+       int ret = aft_get_row_of_hash(hash, result);
+
+       if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
+               return 0;
+       return ret;
+}
+
+static int find_path_brother(const char *path, struct osl_row **result)
+{
+       int ret = aft_get_row_of_path(path, result);
 
-       osl_get_row(audio_file_table, AFTCOL_HASH, &obj, &row);
-       return row;
+       if (ret == -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
+               return 0;
+       return ret;
 }
 
 /** The format of the data stored by save_audio_file_data(). */
 enum com_add_buffer_offsets {
-       /** afhi (if present) starts here. */
+       /* afhi (if present) starts at this offset. */
        CAB_AFHI_OFFSET_POS = 0,
        /** Start of the chunk table (if present). */
-       CAB_CHUNKS_OFFSET_POS = 2,
-       /** Audio format id. */
-       CAB_AUDIO_FORMAT_OFFSET = 4,
+       CAB_CHUNKS_OFFSET_POS = 4,
        /** Flags given to the add command. */
-       CAB_FLAGS_OFFSET = 5,
+       CAB_FLAGS_OFFSET = 8,
+       /** Audio format id. */
+       CAB_AUDIO_FORMAT_ID_OFFSET = 12,
        /** The hash of the audio file being added. */
-       CAB_HASH_OFFSET = 9,
+       CAB_HASH_OFFSET = 13,
        /** Start of the path of the audio file. */
        CAB_PATH_OFFSET = (CAB_HASH_OFFSET + HASH_SIZE),
 };
@@ -1518,27 +1535,23 @@ static void save_add_callback_buffer(unsigned char *hash, const char *path,
        size_t size = CAB_PATH_OFFSET + path_len + afhi_size
                + sizeof_chunk_table(afhi);
        char *buf = para_malloc(size);
-       uint16_t pos;
-
-       write_u8(buf + CAB_AUDIO_FORMAT_OFFSET, audio_format_num);
-       write_u32(buf + CAB_FLAGS_OFFSET, flags);
-
-       memcpy(buf + CAB_HASH_OFFSET, hash, HASH_SIZE);
-       strcpy(buf + CAB_PATH_OFFSET, path);
+       uint32_t pos;
 
        pos = CAB_PATH_OFFSET + path_len;
-       PARA_DEBUG_LOG("size: %zu, afhi starts at %d\n", size, pos);
-       PARA_DEBUG_LOG("last afhi byte: %p, pos %zu\n", buf + pos + afhi_size - 1,
-               pos + afhi_size - 1);
-       write_u16(buf + CAB_AFHI_OFFSET_POS, pos);
+       write_u32(buf + CAB_AFHI_OFFSET_POS, pos);
        save_afhi(afhi, buf + pos);
-
        pos += afhi_size;
-       PARA_DEBUG_LOG("size: %zu, chunks start at %d\n", size, pos);
-       write_u16(buf + CAB_CHUNKS_OFFSET_POS, pos);
+
+       write_u32(buf + CAB_CHUNKS_OFFSET_POS, pos);
        if (afhi)
                save_chunk_table(afhi, buf + pos);
-       PARA_DEBUG_LOG("last byte in buf: %p\n", buf + size - 1);
+
+       write_u32(buf + CAB_FLAGS_OFFSET, flags);
+       write_u8(buf + CAB_AUDIO_FORMAT_ID_OFFSET, audio_format_num);
+
+       memcpy(buf + CAB_HASH_OFFSET, hash, HASH_SIZE);
+       strcpy(buf + CAB_PATH_OFFSET, path);
+
        obj->data = buf;
        obj->size = size;
 }
@@ -1630,9 +1643,11 @@ static int com_add_callback(struct afs_callback_arg *aca)
        objs[AFTCOL_PATH].size = strlen(path) + 1;
 
        PARA_INFO_LOG("request to add %s\n", path);
-       hs = find_hash_sister(hash);
-       ret = aft_get_row_of_path(path, &pb);
-       if (ret < 0 && ret != -OSL_ERRNO_TO_PARA_ERROR(E_OSL_RB_KEY_NOT_FOUND))
+       ret = find_hash_sister(hash, &hs);
+       if (ret < 0)
+               goto out;
+       ret = find_path_brother(path, &pb);
+       if (ret < 0)
                goto out;
        if (hs && pb && hs == pb && !(flags & ADD_FLAG_FORCE)) {
                if (flags & ADD_FLAG_VERBOSE)
@@ -1673,8 +1688,8 @@ static int com_add_callback(struct afs_callback_arg *aca)
                        goto out;
        }
        /* no hs or force mode, child must have sent afhi */
-       afhi_offset = read_u16(buf + CAB_AFHI_OFFSET_POS);
-       chunks_offset = read_u16(buf + CAB_CHUNKS_OFFSET_POS);
+       afhi_offset = read_u32(buf + CAB_AFHI_OFFSET_POS);
+       chunks_offset = read_u32(buf + CAB_CHUNKS_OFFSET_POS);
 
        objs[AFTCOL_AFHI].data = buf + afhi_offset;
        objs[AFTCOL_AFHI].size = chunks_offset - afhi_offset;
@@ -1721,7 +1736,7 @@ static int com_add_callback(struct afs_callback_arg *aca)
        if (flags & ADD_FLAG_VERBOSE)
                para_printf(&aca->pbout, "new file\n");
        default_afsi.last_played = time(NULL) - 365 * 24 * 60 * 60;
-       default_afsi.audio_format_id = read_u8(buf + CAB_AUDIO_FORMAT_OFFSET);
+       default_afsi.audio_format_id = read_u8(buf + CAB_AUDIO_FORMAT_ID_OFFSET);
 
        objs[AFTCOL_AFSI].data = &afsi_buf;
        objs[AFTCOL_AFSI].size = AFSI_SIZE;
@@ -1746,8 +1761,8 @@ static int path_brother_callback(struct afs_callback_arg *aca)
 {
        char *path = aca->query.data;
        struct osl_row *path_brother;
-       int ret = aft_get_row_of_path(path, &path_brother);
-       if (ret < 0)
+       int ret = find_path_brother(path, &path_brother);
+       if (ret <= 0)
                return ret;
        return pass_buffer_as_shm(aca->fd, SBD_OUTPUT, (char *)&path_brother,
                sizeof(path_brother));
@@ -1757,10 +1772,10 @@ static int hash_sister_callback(struct afs_callback_arg *aca)
 {
        unsigned char *hash = aca->query.data;
        struct osl_row *hash_sister;
+       int ret = find_hash_sister(hash, &hash_sister);
 
-       hash_sister = find_hash_sister(hash);
-       if (!hash_sister)
-               return 0;
+       if (ret <= 0)
+               return ret;
        return pass_buffer_as_shm(aca->fd, SBD_OUTPUT, (char *)&hash_sister,
                sizeof(hash_sister));
 }
@@ -2139,9 +2154,10 @@ static int com_rm_callback(struct afs_callback_arg *aca)
        ret = for_each_matching_row(&pmd);
        if (ret < 0)
                goto out;
-       if (pmd.num_matches == 0)
-               ret = -E_NO_MATCH;
-       else if (flags & RM_FLAG_VERBOSE)
+       if (pmd.num_matches == 0) {
+               if (!(flags & RM_FLAG_FORCE))
+                       ret = -E_NO_MATCH;
+       } else if (flags & RM_FLAG_VERBOSE)
                para_printf(&aca->pbout, "removed %u file(s)\n",
                        pmd.num_matches);
 out:
@@ -2403,7 +2419,8 @@ static int com_setatt_callback(struct afs_callback_arg *aca)
        if (!cad.add_mask && !cad.del_mask)
                goto out;
        pmd.patterns.data = p;
-       assert(p < (char *)aca->query.data + aca->query.size);
+       if (p >= (char *)aca->query.data + aca->query.size)
+               goto out;
        pmd.patterns.size = (char *)aca->query.data + aca->query.size - p;
        ret = for_each_matching_row(&pmd);
        if (ret < 0)