X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=aft.c;h=b9db5a819fd13ebf7f780a0afdfe20d63cbc14f5;hp=ce84ef0a47c822256ab52283c6259e0e787c9e37;hb=cd38753609763dbc55e72a7df446837792628d6d;hpb=b7bfffa86a33626ebb8e8a5fcba51568492c3808 diff --git a/aft.c b/aft.c index ce84ef0a..b9db5a81 100644 --- a/aft.c +++ b/aft.c @@ -8,12 +8,12 @@ #include "para.h" #include "error.h" +#include "string.h" #include #include #include "afh.h" #include "afs.h" #include "net.h" -#include "string.h" #include "vss.h" static struct osl_table *audio_file_table; @@ -138,8 +138,10 @@ enum afsi_offsets { AFSI_LYRICS_ID_OFFSET = 24, /** Storage position of the .audio_format_id field. */ AFSI_AUDIO_FORMAT_ID_OFFSET = 28, + /** 3 bytes reserved space for future usage. */ + AFSI_AUDIO_FORMAT_UNUSED_OFFSET = 29, /** On-disk storage space needed. */ - AFSI_SIZE = 29 + AFSI_SIZE = 32 }; /** @@ -161,6 +163,7 @@ void save_afsi(struct afs_info *afsi, struct osl_object *obj) write_u32(buf + AFSI_LYRICS_ID_OFFSET, afsi->lyrics_id); write_u8(buf + AFSI_AUDIO_FORMAT_ID_OFFSET, afsi->audio_format_id); + memset(buf + AFSI_AUDIO_FORMAT_UNUSED_OFFSET, 0, 3); } /** @@ -298,12 +301,19 @@ bad_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 }; @@ -609,6 +619,7 @@ int open_and_update_audio_file(struct osl_row *aft_row, struct audio_file_data * struct osl_object afsi_obj; struct afs_info new_afsi; int ret = get_hash_of_row(aft_row, &aft_hash); + struct afsi_change_event_data aced; if (ret < 0) return ret; @@ -638,10 +649,11 @@ int open_and_update_audio_file(struct osl_row *aft_row, struct audio_file_data * new_afsi.num_played++; new_afsi.last_played = time(NULL); save_afsi(&new_afsi, &afsi_obj); /* in-place update */ - if (afd->current_play_mode == PLAY_MODE_PLAYLIST) - ret = playlist_update_audio_file(aft_row); - else - ret = mood_update_audio_file(aft_row, &afd->afsi); + + aced.aft_row = aft_row; + aced.old_afsi = &afd->afsi; + afs_event(AFSI_CHANGE, NULL, &aced); + return ret; err: free(afd->afhi.chunk_table); @@ -670,6 +682,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; \ @@ -766,7 +779,6 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, sprintf(score_buf, "%li ", d->score); } - PARA_NOTICE_LOG("id: %s, %d\n", d->path, afsi->audio_format_id); if (opts->mode == LS_MODE_LONG) { para_printf(b, "%s" /* score */ @@ -1088,13 +1100,11 @@ static int com_ls_callback(const struct osl_object *query, time_t current_time; - PARA_NOTICE_LOG("%d patterns\n", opts->num_patterns); if (opts->num_patterns) { opts->patterns = para_malloc(opts->num_patterns * sizeof(char *)); for (i = 0, p = pattern_start; i < opts->num_patterns; i++) { opts->patterns[i] = p; p += strlen(p) + 1; - PARA_NOTICE_LOG("pattern %d: %s\n", i, opts->patterns[i]); } } else opts->patterns = NULL; @@ -1106,10 +1116,8 @@ static int com_ls_callback(const struct osl_object *query, if (ret < 0) goto out; ret = opts->num_patterns? -E_NO_MATCH : 0; - if (!opts->num_matching_paths) { - PARA_NOTICE_LOG("no match, ret: %d\n", ret); + if (!opts->num_matching_paths) goto out; - } ret = sort_matching_paths(opts); if (ret < 0) goto out; @@ -1129,7 +1137,6 @@ static int com_ls_callback(const struct osl_object *query, ret = 1; out: ls_output->data = b.buf; - PARA_NOTICE_LOG("ls_outoute.data: %p\n", ls_output->data); ls_output->size = b.size; free(opts->data); free(opts->data_ptr); @@ -1363,20 +1370,24 @@ 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; - const struct osl_row *hs; + struct osl_row *hs; struct osl_object objs[NUM_AFT_COLUMNS]; HASH_TYPE *hash; char asc[2 * HASH_SIZE + 1]; @@ -1384,6 +1395,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);; @@ -1401,29 +1413,35 @@ 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); + if (flags & ADD_FLAG_VERBOSE) { + ret = osl_get_object(audio_file_table, hs, + AFTCOL_PATH, &obj); + if (ret < 0) + goto out; + 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; + afs_event(AUDIO_FILE_RENAME, &msg, hs); 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); @@ -1431,8 +1449,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 */ @@ -1440,35 +1459,35 @@ 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; + 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; + afs_event(AFHI_CHANGE, &msg, row); + 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); @@ -1476,9 +1495,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; + afs_event(AUDIO_FILE_ADD, &msg, aft_row); + return 1; } struct private_add_data { @@ -1517,7 +1542,7 @@ static int hash_sister_callback(const struct osl_object *query, 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; @@ -1528,13 +1553,8 @@ static int add_one_audio_file(const char *path, const void *private_data) afhi.header_offset = 0; afhi.header_len = 0; 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 = (char *)path; query.size = strlen(path) + 1; ret = send_callback_request(path_brother_callback, &query, &result); @@ -1571,7 +1591,7 @@ static int add_one_audio_file(const char *path, 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; } /* @@ -1593,7 +1613,13 @@ static int add_one_audio_file(const char *path, 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: @@ -1676,8 +1702,15 @@ int com_add(int fd, int argc, char * const * const argv) } +/** + * 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 }; @@ -1702,6 +1735,7 @@ static int touch_audio_file(__a_unused struct osl_table *table, 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; + struct afsi_change_event_data aced; ret = get_afsi_object_of_row(row, &obj); if (ret < 0) { @@ -1734,9 +1768,9 @@ static int touch_audio_file(__a_unused struct osl_table *table, 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)); + aced.aft_row = row; + aced.old_afsi = &old_afsi; + afs_event(AFSI_CHANGE, &tad->pb, &aced); return 1; } @@ -1854,9 +1888,7 @@ static int remove_audio_file(__a_unused struct osl_table *table, if (crd->flags & RM_FLAG_VERBOSE) para_printf(&crd->pb, "removing %s\n", name); - ret = mood_delete_audio_file(row); - if (ret < 0) - para_printf(&crd->pb, "%s: %s\n", name, PARA_STRERROR(-ret)); + afs_event(AUDIO_FILE_REMOVE, &crd->pb, row); ret = osl_del_row(audio_file_table, row); if (ret < 0) para_printf(&crd->pb, "%s: %s\n", name, PARA_STRERROR(-ret)); @@ -1935,17 +1967,28 @@ int com_afs_rm(int fd, int argc, char * const * const argv) if (ret > 0) { send_buffer(fd, (char *)result.data); free(result.data); - } else + } else if (ret < 0) send_va_buffer(fd, "%s\n", PARA_STRERROR(-ret)); 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, }; @@ -1962,12 +2005,14 @@ static int copy_selector_info(__a_unused struct osl_table *table, struct cpsi_action_data *cad = data; struct osl_object target_afsi_obj; int ret; - struct afs_info target_afsi; + struct afs_info old_afsi, target_afsi; + struct afsi_change_event_data aced; ret = get_afsi_object_of_row(row, &target_afsi_obj); if (ret < 0) return ret; load_afsi(&target_afsi, &target_afsi_obj); + old_afsi = target_afsi; if (cad->flags & CPSI_FLAG_COPY_LYRICS_ID) target_afsi.lyrics_id = cad->source_afsi.lyrics_id; if (cad->flags & CPSI_FLAG_COPY_IMAGE_ID) @@ -1982,6 +2027,9 @@ static int copy_selector_info(__a_unused struct osl_table *table, cad->num_copied++; if (cad->flags & CPSI_FLAG_VERBOSE) para_printf(&cad->pb, "copied afsi to %s\n", name); + aced.aft_row = row; + aced.old_afsi = &old_afsi; + afs_event(AFSI_CHANGE, &cad->pb, &aced); return 1; } @@ -2116,6 +2164,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}; @@ -2128,8 +2186,6 @@ int aft_check_callback(__a_unused const struct osl_object *query, struct osl_obj } - - /** * Close the audio file table. * @@ -2137,29 +2193,27 @@ int aft_check_callback(__a_unused const struct osl_object *query, struct osl_obj * * \sa osl_close_table(). */ -void aft_shutdown(enum osl_close_flags flags) +static void aft_close(void) { - osl_close_table(audio_file_table, flags); + osl_close_table(audio_file_table, OSL_MARK_CLEAN); audio_file_table = NULL; } /** * Open the audio file table. * - * \param ti Gets initialized by this function. - * \param db The database directory. + * \param dir The database directory. * - * \return Positive on success, negative on errors. + * \return Standard. * * \sa osl_open_table(). */ -int aft_init(struct table_info *ti, const char *db) +static int aft_open(const char *dir) { int ret; - audio_file_table_desc.dir = db; - ti->desc = &audio_file_table_desc; - ret = osl_open_table(ti->desc, &audio_file_table); + audio_file_table_desc.dir = dir; + ret = osl_open_table(&audio_file_table_desc, &audio_file_table); if (ret >= 0) { unsigned num; osl_get_num_rows(audio_file_table, &num); @@ -2168,5 +2222,56 @@ 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; +} + +static int aft_create(const char *dir) +{ + audio_file_table_desc.dir = dir; + return osl_create_table(&audio_file_table_desc); +} + +static int clear_attribute(struct osl_row *row, void *data) +{ + struct rmatt_event_data *red = data; + struct afs_info afsi; + struct osl_object obj; + int ret = get_afsi_object_of_row(row, &obj); + uint64_t mask = ~(1ULL << red->bitnum); + + if (ret < 0) + return ret; + ret = load_afsi(&afsi, &obj); + if (ret < 0) + return ret; + afsi.attributes &= mask; + save_afsi(&afsi, &obj); + return 1; +} + +static int aft_event_handler(enum afs_events event, struct para_buffer *pb, + void *data) +{ + switch(event) { + case ATTRIBUTE_REMOVE: { + const struct rmatt_event_data *red = data; + para_printf(pb, "clearing attribute %s (bit %u) from all " + "entries in the audio file table\n", red->name, + red->bitnum); + return audio_file_loop(data, clear_attribute); + } + default: + return 1; + } +} + +void aft_init(struct afs_table *t) +{ + t->name = audio_file_table_desc.name; + t->open = aft_open; + t->close = aft_close; + t->create = aft_create; + t->event_handler = aft_event_handler; }