+ ret = para_printf(b,
+ "%s" /* filename stuff */
+ "%s%s%s%s" /* score */
+ "%s\n" /* attributes */
+ "%s: %s\n" /* hash */
+ "%s" /* image id, image name */
+ "%s" /* lyrics */
+ "%s: %dkbit/s\n" /* bitrate */
+ "%s: %s\n" /* format */
+ "%s: %dHz\n" /* frequency */
+ "%s: %d\n" /* channels */
+ "%s: %s\n" /* duration */
+ "%s: %lu\n" /* seconds total */
+ "%s: %s\n" /* last played time */
+ "%s: %d\n" /* num_played */
+ "%s: %u\n" /* ampplification */
+ "%s" /* tag info */
+ "%s: %lu\n" /* chunk time */
+ "%s: %lu\n", /* num chunks */
+ filename_lines,
+ have_score? status_item_list[SI_SCORE] : "",
+ have_score? ": " : "",
+ score_buf,
+ have_score? "\n" : "",
+ att_lines,
+ status_item_list[SI_HASH], asc_hash,
+ image_lines,
+ lyrics_lines,
+ status_item_list[SI_BITRATE], afhi->bitrate,
+ status_item_list[SI_FORMAT], audio_format_name(afsi->audio_format_id),
+ status_item_list[SI_FREQUENCY], afhi->frequency,
+ status_item_list[SI_CHANNELS], afhi->channels,
+ status_item_list[SI_DURATION], duration_buf,
+ status_item_list[SI_SECONDS_TOTAL], afhi->seconds_total,
+ status_item_list[SI_LAST_PLAYED], last_played_time,
+ status_item_list[SI_NUM_PLAYED], afsi->num_played,
+ status_item_list[SI_AMPLIFICATION], afsi->amp,
+ afhi->info_string,
+ status_item_list[SI_CHUNK_TIME], tv2ms(&afhi->chunk_tv),
+ status_item_list[SI_NUM_CHUNKS], afhi->chunks_total
+ );
+ if (ret < 0)
+ goto out;
+ if (opts->mode == LS_MODE_MBOX) {
+ struct osl_object lyrics_def;
+ lyr_get_def_by_id(afsi->lyrics_id, &lyrics_def);
+ if (lyrics_def.data) {
+ ret = para_printf(b, "Lyrics:\n~~~~~~~\n%s",
+ (char *)lyrics_def.data);
+ osl_close_disk_object(&lyrics_def);
+ }
+ }
+ free(att_lines);
+ free(lyrics_lines);
+ free(image_lines);
+ free(filename_lines);
+out:
+ free(afhi->info_string);
+ return ret;
+}
+
+/**
+ * Write a list of audio-file related status items with empty values.
+ *
+ * \param buf Result pointer.
+ *
+ * This is used by vss when currently no audio file is open.
+ */
+void make_empty_status_items(char *buf)
+{
+ sprintf(buf,
+ "%s: \n" /* path */
+ "%s: \n" /* dirname */
+ "%s: \n" /* basename */
+ "%s: \n" /* score */
+ "%s: \n" /* attributes bitmap */
+ "%s: \n" /* attributes txt */
+ "%s: \n" /* hash */
+ "%s: \n" /* image id */
+ "%s: \n" /* image name */
+ "%s: \n" /* lyrics id */
+ "%s: \n" /* lyrics name */
+ "%s: \n" /* bitrate */
+ "%s: \n" /* format */
+ "%s: \n" /* frequency */
+ "%s: \n" /* channels */
+ "%s: \n" /* duration */
+ "%s: \n" /* seconds total */
+ "%s: \n" /* num played */
+ "%s: \n" /* last played */
+ "%s: \n" /* audio file info */
+ "%s: \n" /* taginfo1 */
+ "%s: \n" /* taginfo2 */
+ "%s: \n" /* amplification */
+ ,
+ status_item_list[SI_PATH],
+ status_item_list[SI_DIRECTORY],
+ status_item_list[SI_BASENAME],
+ status_item_list[SI_SCORE],
+ status_item_list[SI_ATTRIBUTES_BITMAP],
+ status_item_list[SI_ATTRIBUTES_TXT],
+ status_item_list[SI_HASH],
+ status_item_list[SI_IMAGE_ID],
+ status_item_list[SI_IMAGE_NAME],
+ status_item_list[SI_LYRICS_ID],
+ status_item_list[SI_LYRICS_NAME],
+ status_item_list[SI_BITRATE],
+ status_item_list[SI_FORMAT],
+ status_item_list[SI_FREQUENCY],
+ status_item_list[SI_CHANNELS],
+ status_item_list[SI_DURATION],
+ status_item_list[SI_SECONDS_TOTAL],
+ status_item_list[SI_NUM_PLAYED],
+ status_item_list[SI_LAST_PLAYED],
+ status_item_list[SI_AUDIO_FILE_INFO],
+ status_item_list[SI_TAGINFO1],
+ status_item_list[SI_TAGINFO2],
+ status_item_list[SI_AMPLIFICATION]
+ );
+}
+
+static int make_status_items(struct audio_file_data *afd,
+ struct afs_info *afsi, char *path, long score,
+ HASH_TYPE *hash)
+{
+ struct ls_data d = {
+ .afhi = afd->afhi, /* struct copy */
+ .afsi = *afsi,
+ .path = path,
+ .score = score,
+ .hash = hash
+ };
+ struct ls_options opts = {
+ .flags = LS_FLAG_FULL_PATH | LS_FLAG_ADMISSIBLE_ONLY,
+ .mode = LS_MODE_VERBOSE,
+ };
+ struct para_buffer pb = {.max_size = VERBOSE_LS_OUTPUT_SIZE - 1};
+ time_t current_time;
+ int ret;
+
+ time(¤t_time);
+ ret = print_list_item(&d, &opts, &pb, current_time); /* frees info string */
+ afd->afhi.info_string = NULL;
+ if (ret < 0)
+ goto out;
+ strncpy(afd->verbose_ls_output, pb.buf, VERBOSE_LS_OUTPUT_SIZE);
+ afd->verbose_ls_output[VERBOSE_LS_OUTPUT_SIZE - 1] = '\0';
+out:
+ free(pb.buf);
+ return ret;
+}
+
+/**
+ * Mmap the given audio file and update statistics.
+ *
+ * \param aft_row Determines the audio file to be opened and updated.
+ * \param score The score of the audio file.
+ * \param afd Result pointer.
+ *
+ * On success, the numplayed field of the audio file selector info is increased
+ * and the lastplayed time is set to the current time. Finally, the score of
+ * the audio file is updated.
+ *
+ * \return Positive shmid on success, negative on errors.
+ */
+int open_and_update_audio_file(struct osl_row *aft_row, long score,
+ struct audio_file_data *afd)
+{
+ HASH_TYPE *aft_hash, file_hash[HASH_SIZE];
+ struct osl_object afsi_obj;
+ struct afs_info old_afsi, 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 *path;
+
+ if (ret < 0)
+ return ret;
+ ret = get_audio_file_path_of_row(aft_row, &path);
+ if (ret < 0)
+ return ret;
+ ret = get_afsi_object_of_row(aft_row, &afsi_obj);
+ if (ret < 0)
+ return ret;
+ ret = load_afsi(&old_afsi, &afsi_obj);
+ if (ret < 0)
+ return ret;
+ ret = get_afhi_of_row(aft_row, &afd->afhi);
+ if (ret < 0)
+ return ret;
+ afd->afhi.chunk_table = NULL;
+ ret = osl_open_disk_object(audio_file_table, aft_row,
+ AFTCOL_CHUNKS, &chunk_table_obj);
+ if (ret < 0)
+ goto err;
+ ret = mmap_full_file(path, O_RDONLY, &map.data,
+ &map.size, &afd->fd);
+ if (ret < 0)
+ goto err;
+ 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 = old_afsi;
+ new_afsi.num_played++;
+ new_afsi.last_played = time(NULL);
+ save_afsi(&new_afsi, &afsi_obj); /* in-place update */
+
+ load_chunk_table(&afd->afhi, chunk_table_obj.data);
+ ret = make_status_items(afd, &old_afsi, path, score, file_hash);
+ if (ret < 0)
+ goto err;
+ aced.aft_row = aft_row;
+ aced.old_afsi = &old_afsi;
+ afs_event(AFSI_CHANGE, NULL, &aced);
+ ret = save_afd(afd);
+err:
+ free(afd->afhi.chunk_table);
+ free(afd->afhi.info_string);
+ osl_close_disk_object(&chunk_table_obj);
+ return ret;