X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=aft.c;h=99b972c9d19a6dfb729bdd0ed56b09f73762ac59;hp=ce84ef0a47c822256ab52283c6259e0e787c9e37;hb=a551870e3ac31ff77e1757f226d3e307e4ef83fc;hpb=b7bfffa86a33626ebb8e8a5fcba51568492c3808 diff --git a/aft.c b/aft.c index ce84ef0a..99b972c9 100644 --- a/aft.c +++ b/aft.c @@ -6,15 +6,18 @@ /** \file aft.c Audio file table functions. */ +#include /* readdir() */ #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" +#include "fd.h" +#include "ipc.h" static struct osl_table *audio_file_table; @@ -138,8 +141,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 +166,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 +304,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 }; @@ -336,11 +349,13 @@ static void load_afhi(const char *buf, struct audio_format_info *afhi) strcpy(afhi->info_string, buf + AFHI_INFO_STRING_OFFSET); } +//#define SIZEOF_CHUNK_TABLE(afhi) (((afhi)->chunks_total + 1) * sizeof(uint32_t)) + static unsigned sizeof_chunk_info_buf(struct audio_format_info *afhi) { if (!afhi) return 0; - return 4 * afhi->chunks_total + 20; + return 4 * (afhi->chunks_total + 1) + 20; } @@ -360,11 +375,25 @@ enum chunk_info_offsets{ CHUNK_TABLE_OFFSET = 20, }; -/* TODO: audio format handlers could just produce this */ -static void save_chunk_info(struct audio_format_info *afhi, char *buf) +static void save_chunk_table(struct audio_format_info *afhi, char *buf) +{ + int i; + + PARA_NOTICE_LOG("%lu chunks\n", afhi->chunks_total); + for (i = 0; i <= afhi->chunks_total; i++) + write_u32(buf + 4 * i, afhi->chunk_table[i]); +} + +static void load_chunk_table(struct audio_format_info *afhi, char *buf) { int i; + for (i = 0; i <= afhi->chunks_total; i++) + afhi->chunk_table[i] = read_u32(buf + 4 * i); +} +/* TODO: audio format handlers could just produce this */ +static void save_chunk_info(struct audio_format_info *afhi, char *buf) +{ if (!afhi) return; write_u32(buf + CHUNKS_TOTAL_OFFSET, afhi->chunks_total); @@ -372,14 +401,12 @@ static void save_chunk_info(struct audio_format_info *afhi, char *buf) write_u32(buf + HEADER_OFFSET_OFFSET, afhi->header_offset); write_u32(buf + CHUNK_TV_TV_SEC_OFFSET, afhi->chunk_tv.tv_sec); write_u32(buf + CHUNK_TV_TV_USEC, afhi->chunk_tv.tv_usec); - for (i = 0; i < afhi->chunks_total; i++) - write_u32(buf + CHUNK_TABLE_OFFSET + 4 * i, afhi->chunk_table[i]); + save_chunk_table(afhi, buf + CHUNK_TABLE_OFFSET); } static int load_chunk_info(struct osl_object *obj, struct audio_format_info *afhi) { char *buf = obj->data; - int i; if (obj->size < CHUNK_TABLE_OFFSET) return -E_BAD_DATA_SIZE; @@ -390,11 +417,10 @@ static int load_chunk_info(struct osl_object *obj, struct audio_format_info *afh afhi->chunk_tv.tv_sec = read_u32(buf + CHUNK_TV_TV_SEC_OFFSET); afhi->chunk_tv.tv_usec = read_u32(buf + CHUNK_TV_TV_USEC); - if (afhi->chunks_total * 4 + CHUNK_TABLE_OFFSET > obj->size) + if ((afhi->chunks_total + 1) * 4 + CHUNK_TABLE_OFFSET > obj->size) return -E_BAD_DATA_SIZE; - afhi->chunk_table = para_malloc(afhi->chunks_total * sizeof(size_t)); - for (i = 0; i < afhi->chunks_total; i++) - afhi->chunk_table[i] = read_u32(buf + CHUNK_TABLE_OFFSET + 4 * i); + afhi->chunk_table = para_malloc((afhi->chunks_total + 1) * 4); + load_chunk_table(afhi, buf + CHUNK_TABLE_OFFSET); return 1; } @@ -569,28 +595,57 @@ int get_afhi_of_row(const struct osl_row *row, struct audio_format_info *afhi) return 1; } -/** - * Get the chunk table of an audio file, given a row of the audio file table. - * - * \param row Pointer to a row of the audio file table. - * \param afhi Result pointer. - * - * \return The return value of the underlying call to osl_open_disk_object(). - * - * \sa get_afhi_of_row(). - */ -static int get_chunk_table_of_row(const struct osl_row *row, struct audio_format_info *afhi) +/* returns shmid on success */ +static int save_afd(struct audio_file_data *afd) { - struct osl_object obj; - int ret = osl_open_disk_object(audio_file_table, row, AFTCOL_CHUNKS, - &obj); + size_t path_size = strlen(afd->path) + 1; + size_t size = sizeof(*afd) + path_size + + 4 * (afd->afhi.chunks_total + 1); + + PARA_NOTICE_LOG("size: %zu\n", size); + int shmid, ret = shm_new(size); + void *shm_afd; + char *buf; + if (ret < 0) return ret; - ret = load_chunk_info(&obj, afhi); - osl_close_disk_object(&obj); + shmid = ret; + ret = shm_attach(shmid, ATTACH_RW, &shm_afd); + if (ret < 0) + goto err; + *(struct audio_file_data *)shm_afd = *afd; + buf = shm_afd; + buf += sizeof(*afd); + strcpy(buf, afd->path); + buf += path_size; + save_chunk_table(&afd->afhi, buf); + shm_detach(shm_afd); + return shmid; +err: + shm_destroy(shmid); return ret; } +int load_afd(int shmid, struct audio_file_data *afd) +{ + void *shm_afd; + char *buf; + int ret; + + ret = shm_attach(shmid, ATTACH_RO, &shm_afd); + if (ret < 0) + return ret; + *afd = *(struct audio_file_data *)shm_afd; + buf = shm_afd; + buf += sizeof(*afd); + afd->path = para_strdup(buf); + buf += strlen(buf) + 1; + afd->afhi.chunk_table = para_malloc((afd->afhi.chunks_total + 1) * 4); + load_chunk_table(&afd->afhi, buf); + shm_detach(shm_afd); + return 1; +} + /** * Mmap the given audio file and update statistics. * @@ -609,6 +664,8 @@ 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; + struct osl_object map, chunk_table_obj; if (ret < 0) return ret; @@ -624,27 +681,39 @@ int open_and_update_audio_file(struct osl_row *aft_row, struct audio_file_data * ret = get_afhi_of_row(aft_row, &afd->afhi); if (ret < 0) return ret; - ret = get_chunk_table_of_row(aft_row, &afd->afhi); + ret = osl_open_disk_object(audio_file_table, aft_row, + AFTCOL_CHUNKS, &chunk_table_obj); if (ret < 0) return ret; - ret = mmap_full_file(afd->path, O_RDONLY, &afd->map); + ret = mmap_full_file(afd->path, O_RDONLY, &map.data, + &map.size, &afd->fd); if (ret < 0) goto err; - hash_function(afd->map.data, afd->map.size, file_hash); - ret = -E_HASH_MISMATCH; - if (hash_compare(file_hash, aft_hash)) + hash_function(map.data, map.size, file_hash); + ret = hash_compare(file_hash, aft_hash); + para_munmap(map.data, map.size); + if (ret) { + ret = -E_HASH_MISMATCH; goto err; + } new_afsi = afd->afsi; 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); - return ret; -err: + + ret = load_chunk_info(&chunk_table_obj, &afd->afhi); + if (ret < 0) + goto err; + + aced.aft_row = aft_row; + aced.old_afsi = &afd->afsi; + afs_event(AFSI_CHANGE, NULL, &aced); + ret = save_afd(afd); + if (ret < 0) + goto err; free(afd->afhi.chunk_table); +err: + osl_close_disk_object(&chunk_table_obj); return ret; } @@ -670,6 +739,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 +836,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 +1157,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 +1173,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 +1194,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); @@ -1144,7 +1208,7 @@ out: * full list: list everything, including afsi, afhi, atts as clear text * * */ -int com_afs_ls(int fd, int argc, char * const * const argv) +int com_ls(int fd, int argc, char * const * const argv) { int i, ret; unsigned flags = 0; @@ -1363,20 +1427,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 +1452,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 +1470,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 +1506,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 +1516,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 +1552,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,24 +1599,19 @@ 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; struct osl_row *pb = NULL, *hs = NULL; /* path brother/hash sister */ - struct osl_object map, obj = {.data = NULL}, query, result; + struct osl_object map, obj = {.data = NULL}, query, result = {.data = NULL}; HASH_TYPE hash[HASH_SIZE]; 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); @@ -1551,7 +1628,7 @@ static int add_one_audio_file(const char *path, const void *private_data) goto out_free; } /* We still want to add this file. Compute its hash. */ - ret = mmap_full_file(path, O_RDONLY, &map); + ret = mmap_full_file(path, O_RDONLY, &map.data, &map.size, NULL); if (ret < 0) goto out_free; hash_function(map.data, map.size, hash); @@ -1571,7 +1648,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 +1670,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 (ret >= 0 && 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 +1759,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 +1792,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 +1825,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 +1945,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)); @@ -1899,7 +1988,7 @@ static int com_rm_callback(const struct osl_object *query, } /* TODO options: -r (recursive) */ -int com_afs_rm(int fd, int argc, char * const * const argv) +int com_rm(int fd, int argc, char * const * const argv) { uint32_t flags = 0; struct osl_object query = {.data = &flags, .size = sizeof(flags)}, @@ -1935,17 +2024,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 +2062,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 +2084,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 +2221,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 +2243,6 @@ int aft_check_callback(__a_unused const struct osl_object *query, struct osl_obj } - - /** * Close the audio file table. * @@ -2137,29 +2250,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 +2279,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; }