X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=aft.c;h=6d6cd8d0218c860a3e89032cd91a019b67403953;hp=c63ab47106866dd5caa9e99062377c742b999a68;hb=b3ee0fa979a74e9d7a1497ec5a3ac8fa7cc7472b;hpb=53d503ce75eb1d9439cfb75053d3cff4cbca6ff9 diff --git a/aft.c b/aft.c index c63ab471..6d6cd8d0 100644 --- a/aft.c +++ b/aft.c @@ -6,6 +6,7 @@ /** \file aft.c Audio file table functions. */ +#include /* readdir() */ #include "para.h" #include "error.h" #include "string.h" @@ -15,6 +16,8 @@ #include "afs.h" #include "net.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); } /** @@ -343,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; } @@ -367,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_DEBUG_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); @@ -379,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; @@ -397,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; } @@ -505,7 +524,10 @@ int get_afsi_of_path(const char *path, struct afs_info *afsi) * \param row Pointer to a row in the audio file table. * \param path Result pointer. * - * \return Positive on success, negative on errors. + * The result is a pointer to mmapped data. The caller must not attempt + * to free it. + * + * \return Standard. */ int get_audio_file_path_of_row(const struct osl_row *row, char **path) { @@ -576,28 +598,52 @@ 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 size = sizeof(*afd) + + 4 * (afd->afhi.chunks_total + 1); + + PARA_DEBUG_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); + 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->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. * @@ -616,12 +662,17 @@ 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; + char *tmp, *path; if (ret < 0) return ret; - ret = get_audio_file_path_of_row(aft_row, &afd->path); + ret = get_audio_file_path_of_row(aft_row, &path); if (ret < 0) return ret; + strncpy(afd->path, path, sizeof(afd->path) - 1); + afd->path[sizeof(afd->path) - 1] = '\0'; ret = get_afsi_object_of_row(aft_row, &afsi_obj); if (ret < 0) return ret; @@ -631,27 +682,45 @@ 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(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; + ret = get_attribute_text(&afd->afsi.attributes, " ", &tmp); + if (ret < 0) + goto err; + tmp[sizeof(afd->attributes_string) - 1] = '\0'; + strcpy(afd->attributes_string, tmp); /* OK */ + free(tmp); + + 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; } @@ -711,16 +780,17 @@ static void get_duration_buf(int seconds, char *buf, short unsigned max_width) seconds % 60); } -static char *make_attribute_line(const char *att_bitmap, struct afs_info *afsi) +static char *make_attribute_lines(const char *att_bitmap, struct afs_info *afsi) { - char *att_text, *att_line; + char *att_text, *att_lines; get_attribute_text(&afsi->attributes, " ", &att_text); if (!att_text) return para_strdup(att_bitmap); - att_line = make_message("%s (%s)", att_bitmap, att_text); + att_lines = make_message("%s\nattributes_txt: %s", + att_bitmap, att_text); free(att_text); - return att_line; + return att_lines; } static char *make_lyrics_line(struct afs_info *afsi) @@ -755,7 +825,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, struct ls_widths *w = &opts->widths; int have_score = opts->flags & LS_FLAG_ADMISSIBLE_ONLY; char asc_hash[2 * HASH_SIZE + 1]; - char *att_line, *lyrics_line, *image_line; + char *att_lines, *lyrics_line, *image_line; if (opts->mode == LS_MODE_SHORT) { para_printf(b, "%s\n", d->path); @@ -774,7 +844,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 */ @@ -805,11 +874,11 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, return 1; } hash_to_asc(d->hash, asc_hash); - att_line = make_attribute_line(att_buf, afsi); + att_lines = make_attribute_lines(att_buf, afsi); lyrics_line = make_lyrics_line(afsi); image_line = make_image_line(afsi); + /* TODO: Merge this with status items */ if (opts->mode == LS_MODE_VERBOSE) { - para_printf(b, "%s: %s\n" /* path */ "%s%s%s" /* score */ @@ -829,7 +898,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, "path" : "file", d->path, have_score? "score: " : "", score_buf, have_score? "\n" : "", - att_line, + att_lines, asc_hash, image_line, lyrics_line, @@ -850,7 +919,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, "Received: from\nTo: bar\nFrom: a\n" "Subject: %s\n\n" /* path */ "%s%s%s" /* score */ - "attributes: %s\n" + "%s" "hash: %s\n" "image_id: %s\n" "lyrics_id: %s\n" @@ -866,7 +935,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, d->path, have_score? "score: " : "", score_buf, have_score? "\n" : "", - att_line, + att_lines, asc_hash, image_line, lyrics_line, @@ -883,7 +952,7 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts, if (lyrics_def.data) osl_close_disk_object(lyrics_def.data); } - free(att_line); + free(att_lines); free(lyrics_line); free(image_line); return 1; @@ -1096,13 +1165,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; @@ -1114,10 +1181,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; @@ -1137,7 +1202,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); @@ -1152,7 +1216,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; @@ -1388,7 +1452,7 @@ static int com_add_callback(const struct osl_object *query, { 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]; @@ -1429,13 +1493,18 @@ static int com_add_callback(const struct osl_object *query, pb = NULL; } /* file rename, update hs' path */ - ret = osl_get_object(audio_file_table, hs, AFTCOL_PATH, &obj); - if (flags & ADD_FLAG_VERBOSE) + 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) goto out; + afs_event(AUDIO_FILE_RENAME, &msg, hs); if (!(flags & ADD_FLAG_FORCE)) goto out; } @@ -1466,7 +1535,7 @@ static int com_add_callback(const struct osl_object *query, 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_printf(&msg, "updating afhi and chunk table\n"); @@ -1476,6 +1545,9 @@ static int com_add_callback(const struct osl_object *query, goto out; ret = osl_update_object(audio_file_table, row, AFTCOL_CHUNKS, &objs[AFTCOL_CHUNKS]); + if (ret < 0) + goto out; + afs_event(AFHI_CHANGE, &msg, row); goto out; } /* new entry, use default afsi */ @@ -1495,8 +1567,8 @@ out: return 0; result->data = msg.buf; result->size = msg.size; + afs_event(AUDIO_FILE_ADD, &msg, aft_row); return 1; - // mood_update_audio_file(aft_row, NULL); } struct private_add_data { @@ -1540,7 +1612,7 @@ static int add_one_audio_file(const char *path, const void *private_data) 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; @@ -1564,7 +1636,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); @@ -1607,7 +1679,7 @@ static int add_one_audio_file(const char *path, const void *private_data) 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, &result); - if (result.data && result.size) { + 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) @@ -1728,6 +1800,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) { @@ -1760,9 +1833,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; } @@ -1855,7 +1928,7 @@ int com_touch(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; } @@ -1880,9 +1953,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)); @@ -1925,7 +1996,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)}, @@ -1961,7 +2032,7 @@ 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; } @@ -1999,12 +2070,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) @@ -2019,6 +2092,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; } @@ -2222,10 +2298,45 @@ static int aft_create(const char *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; }