X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=aft.c;h=5112affacdcd29ebcaf51b82eb9d6980035d5f88;hp=8c7dd21a0f86ac25e39b1bfdae33ccc5f6032b71;hb=dcf9594fa041cdfae819186ed7e145d079110318;hpb=e60387b0557fbeb97895bc3d6d4423be12436c52 diff --git a/aft.c b/aft.c index 8c7dd21a..5112affa 100644 --- a/aft.c +++ b/aft.c @@ -16,8 +16,6 @@ #include "string.h" #include "vss.h" -#define AFS_AUDIO_FILE_DIR "/home/mp3" /* FIXME: Use cwd instead */ - static struct osl_table *audio_file_table; /** The different sorting methods of the ls command. */ @@ -243,60 +241,7 @@ static struct osl_table_description audio_file_table_desc = { .column_descriptions = aft_cols }; -static char *prefix_path(const char *prefix, int len, const char *path) -{ - int speclen; - char *n; - - for (;;) { - char c; - if (*path != '.') - break; - c = path[1]; - /* "." */ - if (!c) { - path++; - break; - } - /* "./" */ - if (c == '/') { - path += 2; - continue; - } - if (c != '.') - break; - c = path[2]; - if (!c) - path += 2; - else if (c == '/') - path += 3; - else - break; - /* ".." and "../" */ - /* Remove last component of the prefix */ - do { - if (!len) - return NULL; - len--; - } while (len && prefix[len-1] != '/'); - continue; - } - if (!len) - return para_strdup(path); - speclen = strlen(path); - n = para_malloc(speclen + len + 1); - memcpy(n, prefix, len); - memcpy(n + len, path, speclen+1); - return n; -} - -/* - * We fundamentally don't like some paths: we don't want - * dot or dot-dot anywhere. - * - * Also, we don't want double slashes or slashes at the - * end that can make pathnames ambiguous. - */ +/* We don't want * dot or dot-dot anywhere. */ static int verify_dotfile(const char *rest) { /* @@ -304,57 +249,68 @@ static int verify_dotfile(const char *rest) * now test the rest. */ switch (*rest) { - /* "." is not allowed */ - case '\0': case '/': - return 1; - - case '.': + case '\0': case '/': /* /foo/. and /foo/./bar are not ok */ + return -1; + case '.': /* path start with /foo/.. */ if (rest[1] == '\0' || rest[1] == '/') - return -1; + return -1; /* /foo/.. or /foo/../bar are not ok */ + /* /foo/..bar is ok */ } return 1; } +/* + * We fundamentally don't like some paths: We don't want double slashes or + * slashes at the end that can make pathnames ambiguous. + */ static int verify_path(const char *orig_path, char **resolved_path) { char c; - const char prefix[] = AFS_AUDIO_FILE_DIR "/"; - const char *path = orig_path; - const size_t prefix_len = strlen(prefix); + size_t len; + char *path; + if (*orig_path != '/') /* we only accept absolute paths */ + return -E_BAD_PATH; + len = strlen(orig_path); + *resolved_path = para_strdup(orig_path); + path = *resolved_path; + while (len > 1 && path[--len] == '/') + path[len] = '\0'; /* remove slash at the end */ c = *path++; - if (!c) - goto bad_path; while (c) { if (c == '/') { c = *path++; switch (c) { - default: - continue; case '/': /* double slash */ goto bad_path; case '.': if (verify_dotfile(path) < 0) goto bad_path; + default: + continue; } } c = *path++; } - if (*orig_path != '/') - *resolved_path = prefix_path(prefix, prefix_len, orig_path); - else - *resolved_path = para_strdup(orig_path); return 1; bad_path: + free(*resolved_path); return -E_BAD_PATH; } +/** The on-disk layout of a afhi struct. */ enum afhi_offsets { + /** Where the number of seconds is stored. */ AFHI_SECONDS_TOTAL_OFFSET = 0, + /** Position of the bitrate. */ AFHI_BITRATE_OFFSET = 4, + /** Position of the frequency. */ AFHI_FREQUENCY_OFFSET = 8, + /** Number of channels is stored here. */ AFHI_CHANNELS_OFFSET = 12, + /** The tag info position. */ AFHI_INFO_STRING_OFFSET = 13, + /** Minimal on-disk size of a valid afhi struct. */ MIN_AFHI_SIZE = 14 }; @@ -721,6 +677,7 @@ static int get_local_time(uint64_t *seconds, char *buf, size_t size, return 1; } +/** Compute the number of (decimal) digits of a number. */ #define GET_NUM_DIGITS(x, num) { \ typeof((x)) _tmp = PARA_ABS(x); \ *num = 1; \ @@ -1414,16 +1371,20 @@ afhi <=> force or no HS */ - -#define ADD_FLAG_LAZY 1 -#define ADD_FLAG_FORCE 2 -#define ADD_FLAG_VERBOSE 4 -#define ADD_FLAG_ALL 8 - -/* TODO: change log messages so that they get written to the result buffer */ +/** Flags passed to the add command. */ +enum com_add_flags { + /** Skip paths that exist already. */ + ADD_FLAG_LAZY = 1, + /** Force adding. */ + ADD_FLAG_FORCE = 2, + /** Print what is being done. */ + ADD_FLAG_VERBOSE = 4, + /** Try to add files with unknown suffixes. */ + ADD_FLAG_ALL = 8, +}; static int com_add_callback(const struct osl_object *query, - __a_unused struct osl_object *result) + struct osl_object *result) { char *buf = query->data, *path; struct osl_row *pb, *aft_row; @@ -1435,6 +1396,7 @@ static int com_add_callback(const struct osl_object *query, char afsi_buf[AFSI_SIZE]; uint32_t flags = read_u32(buf + AFTROW_FLAGS_OFFSET); struct afs_info default_afsi = {.last_played = 0}; + struct para_buffer msg = {.buf = NULL}; hash = (HASH_TYPE *)buf + AFTROW_HASH_OFFSET; hash_to_asc(hash, asc);; @@ -1452,29 +1414,30 @@ static int com_add_callback(const struct osl_object *query, return ret; if (hs && pb && hs == pb && !(flags & ADD_FLAG_FORCE)) { if (flags & ADD_FLAG_VERBOSE) - PARA_NOTICE_LOG("ignoring duplicate %p\n", path); - return 1; + para_printf(&msg, "ignoring duplicate\n"); + ret = 1; + goto out; } if (hs && hs != pb) { struct osl_object obj; if (pb) { /* hs trumps pb, remove pb */ if (flags & ADD_FLAG_VERBOSE) - PARA_NOTICE_LOG("removing path brother\n"); + para_printf(&msg, "removing path brother\n"); ret = osl_del_row(audio_file_table, pb); if (ret < 0) - return ret; + goto out; pb = NULL; } /* file rename, update hs' path */ ret = osl_get_object(audio_file_table, hs, AFTCOL_PATH, &obj); if (flags & ADD_FLAG_VERBOSE) - PARA_NOTICE_LOG("rename %s -> %s\n", (char *)obj.data, path); + para_printf(&msg, "renamed from %s\n", (char *)obj.data); ret = osl_update_object(audio_file_table, hs, AFTCOL_PATH, &objs[AFTCOL_PATH]); if (ret < 0) - return ret; + goto out; if (!(flags & ADD_FLAG_FORCE)) - return ret; + goto out; } /* no hs or force mode, child must have sent afhi */ uint16_t afhi_offset = read_u16(buf + AFTROW_AFHI_OFFSET_POS); @@ -1482,8 +1445,9 @@ static int com_add_callback(const struct osl_object *query, objs[AFTCOL_AFHI].data = buf + afhi_offset; objs[AFTCOL_AFHI].size = chunks_offset - afhi_offset; + ret = -E_NO_AFHI; if (!objs[AFTCOL_AFHI].size) /* "impossible" */ - return -E_NO_AFHI; + goto out; objs[AFTCOL_CHUNKS].data = buf + chunks_offset; objs[AFTCOL_CHUNKS].size = query->size - chunks_offset; if (pb && !hs) { /* update pb's hash */ @@ -1491,35 +1455,32 @@ static int com_add_callback(const struct osl_object *query, HASH_TYPE *old_hash; ret = get_hash_of_row(pb, &old_hash); if (ret < 0) - return ret; + goto out; hash_to_asc(old_hash, old_asc); if (flags & ADD_FLAG_VERBOSE) - PARA_NOTICE_LOG("file change: %s %s -> %s\n", path, + para_printf(&msg, "file change: %s -> %s\n", old_asc, asc); ret = osl_update_object(audio_file_table, pb, AFTCOL_HASH, &objs[AFTCOL_HASH]); if (ret < 0) - return ret; + goto out; } if (hs || pb) { /* (hs != NULL and pb != NULL) implies hs == pb */ const struct osl_row *row = pb? pb : hs; /* update afhi and chunk_table */ if (flags & ADD_FLAG_VERBOSE) - PARA_DEBUG_LOG("updating audio format handler info (%zd bytes)\n", - objs[AFTCOL_AFHI].size); + para_printf(&msg, "updating afhi and chunk table\n"); ret = osl_update_object(audio_file_table, row, AFTCOL_AFHI, &objs[AFTCOL_AFHI]); if (ret < 0) - return ret; - if (flags & ADD_FLAG_VERBOSE) - PARA_DEBUG_LOG("updating chunk table\n"); + goto out; ret = osl_update_object(audio_file_table, row, AFTCOL_CHUNKS, &objs[AFTCOL_CHUNKS]); - if (ret < 0) - return ret; - return mood_update_audio_file(row, NULL); + goto out; } /* new entry, use default afsi */ + if (flags & ADD_FLAG_VERBOSE) + para_printf(&msg, "new file\n"); default_afsi.last_played = time(NULL) - 365 * 24 * 60 * 60; default_afsi.audio_format_id = read_u8(buf + AFTROW_AUDIO_FORMAT_OFFSET); @@ -1527,9 +1488,15 @@ static int com_add_callback(const struct osl_object *query, objs[AFTCOL_AFSI].size = AFSI_SIZE; save_afsi(&default_afsi, &objs[AFTCOL_AFSI]); ret = osl_add_and_get_row(audio_file_table, objs, &aft_row); +out: if (ret < 0) - return ret; - return mood_update_audio_file(aft_row, NULL); + para_printf(&msg, "%s\n", PARA_STRERROR(-ret)); + if (!msg.buf) + return 0; + result->data = msg.buf; + result->size = msg.size; + return 1; + // mood_update_audio_file(aft_row, NULL); } struct private_add_data { @@ -1566,31 +1533,22 @@ static int hash_sister_callback(const struct osl_object *query, return 1; } -static int add_one_audio_file(const char *arg, const void *private_data) +static int add_one_audio_file(const char *path, const void *private_data) { - int ret; + int ret, ret2; uint8_t format_num = -1; const struct private_add_data *pad = private_data; struct audio_format_info afhi, *afhi_ptr = NULL; struct osl_row *pb = NULL, *hs = NULL; /* path brother/hash sister */ struct osl_object map, obj = {.data = NULL}, query, result; - char *path = NULL; HASH_TYPE hash[HASH_SIZE]; afhi.header_offset = 0; afhi.header_len = 0; - ret = verify_path(arg, &path); - if (ret < 0) - goto out_free; ret = guess_audio_format(path); - if (ret < 0 && !(pad->flags & ADD_FLAG_ALL)) { - if (pad->flags & ADD_FLAG_VERBOSE) - ret = send_va_buffer(pad->fd, "%s: %s\n", - PARA_STRERROR(-ret), path); - ret = 1; + if (ret < 0 && !(pad->flags & ADD_FLAG_ALL)) goto out_free; - } - query.data = path; + query.data = (char *)path; query.size = strlen(path) + 1; ret = send_callback_request(path_brother_callback, &query, &result); if (ret < 0 && ret != -E_RB_KEY_NOT_FOUND) @@ -1626,7 +1584,7 @@ static int add_one_audio_file(const char *arg, const void *private_data) if (pb && hs && hs == pb && (!(pad->flags & ADD_FLAG_FORCE))) { if (pad->flags & ADD_FLAG_VERBOSE) ret = send_va_buffer(pad->fd, - "not forcing update: %s\n", path); + "%s exists, not forcing update\n", path); goto out_unmap; } /* @@ -1648,17 +1606,22 @@ static int add_one_audio_file(const char *arg, const void *private_data) munmap(map.data, map.size); save_audio_file_info(hash, path, afhi_ptr, pad->flags, format_num, &obj); /* Ask afs to consider this entry for adding. */ - ret = send_callback_request(com_add_callback, &obj, NULL); + ret = send_callback_request(com_add_callback, &obj, &result); + if (result.data && result.size) { + ret2 = send_va_buffer(pad->fd, "%s", (char *)result.data); + free(result.data); + if (ret >= 0 && ret2 < 0) + ret = ret2; + } goto out_free; out_unmap: munmap(map.data, map.size); out_free: if (ret < 0 && ret != -E_SEND) - send_va_buffer(pad->fd, "failed to add %s (%s)\n", path? - path : arg, PARA_STRERROR(-ret)); + send_va_buffer(pad->fd, "failed to add %s (%s)\n", path, + PARA_STRERROR(-ret)); free(obj.data); - free(path); if (afhi_ptr) free(afhi_ptr->chunk_table); /* it's not an error if not all files could be added */ @@ -1699,35 +1662,48 @@ int com_add(int fd, int argc, char * const * const argv) if (argc <= i) return -E_AFT_SYNTAX; for (; i < argc; i++) { - char *path = para_strdup(argv[i]); - size_t len = strlen(path); - while (len > 1 && path[--len] == '/') - path[len] = '\0'; + char *path; + ret = verify_path(argv[i], &path); + if (ret < 0) { + ret = send_va_buffer(fd, "%s: %s\n", argv[i], PARA_STRERROR(-ret)); + if (ret < 0) + return ret; + continue; + } ret = stat(path, &statbuf); - if (ret < 0) - PARA_NOTICE_LOG("failed to stat %s (%s)", path, + if (ret < 0) { + ret = send_va_buffer(fd, "failed to stat %s (%s)\n", path, strerror(errno)); - else { - if (S_ISDIR(statbuf.st_mode)) - ret = for_each_file_in_dir(path, add_one_audio_file, - &pad); - else - ret = add_one_audio_file(path, &pad); - if (ret < 0) { - send_va_buffer(fd, "%s: %s\n", path, PARA_STRERROR(-ret)); - free(path); + free(path); + if (ret < 0) return ret; - } + continue; + } + if (S_ISDIR(statbuf.st_mode)) + ret = for_each_file_in_dir(path, add_one_audio_file, + &pad); + else + ret = add_one_audio_file(path, &pad); + if (ret < 0) { + send_va_buffer(fd, "%s: %s\n", path, PARA_STRERROR(-ret)); + free(path); + return ret; } free(path); } - ret = 1; - return ret; + return 1; } +/** + * Flags used by the touch command. + * + * \sa com_touch(). + */ enum touch_flags { + /** Whether the \p FNM_PATHNAME flag should be passed to fnmatch(). */ TOUCH_FLAG_FNM_PATHNAME = 1, + /** Activates verbose mode. */ TOUCH_FLAG_VERBOSE = 2 }; @@ -1990,12 +1966,23 @@ int com_afs_rm(int fd, int argc, char * const * const argv) return ret; } +/** + * Flags used by the cpsi command. + * + * \sa com_cpsi(). + */ enum cpsi_flags { + /** Whether the lyrics id should be copied. */ CPSI_FLAG_COPY_LYRICS_ID = 1, + /** Whether the image id should be copied. */ CPSI_FLAG_COPY_IMAGE_ID = 2, + /** Whether the lastplayed time should be copied. */ CPSI_FLAG_COPY_LASTPLAYED = 4, + /** Whether the numplayed count should be copied. */ CPSI_FLAG_COPY_NUMPLAYED = 8, + /** Whether the attributes should be copied. */ CPSI_FLAG_COPY_ATTRIBUTES = 16, + /** Activates verbose mode. */ CPSI_FLAG_VERBOSE = 32, }; @@ -2166,6 +2153,16 @@ static int check_audio_file(struct osl_row *row, void *data) return 1; } +/** + * Check the audio file table for inconsistencies. + * + * \param query Unused. + * \param result Contains message string upon return. + * + * This function always succeeds. + * + * \sa com_check(). + */ int aft_check_callback(__a_unused const struct osl_object *query, struct osl_object *result) { struct para_buffer pb = {.buf = NULL}; @@ -2178,8 +2175,6 @@ int aft_check_callback(__a_unused const struct osl_object *query, struct osl_obj } - - /** * Close the audio file table. * @@ -2218,5 +2213,7 @@ int aft_init(struct table_info *ti, const char *db) } PARA_INFO_LOG("failed to open audio file table\n"); audio_file_table = NULL; - return ret == -E_NOENT? 1 : ret; + if (ret >= 0 || is_errno(-ret, ENOENT)) + return 1; + return ret; }