+ ret = print_list_item(&status_item_ls_data, &opts, &pb, current_time);
+ if (ret < 0)
+ return ret;
+ make_inode_status_items(&pb);
+ free(status_items);
+ status_items = pb.buf;
+ memset(&pb, 0, sizeof(pb));
+ pb.max_size = shm_get_shmmax() - 1;
+ pb.flags = PBF_SIZE_PREFIX;
+ ret = print_list_item(&status_item_ls_data, &opts, &pb, current_time);
+ if (ret < 0) {
+ free(status_items);
+ status_items = NULL;
+ return ret;
+ }
+ make_inode_status_items(&pb);
+ free(parser_friendly_status_items);
+ parser_friendly_status_items = pb.buf;
+ return 1;
+}
+
+/**
+ * Open the audio file with highest score and set up an afd structure.
+ *
+ * \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 audio_file_data *afd)
+{
+ unsigned char file_hash[HASH_SIZE];
+ struct osl_object afsi_obj;
+ struct afs_info new_afsi;
+ int ret;
+ struct afsi_change_event_data aced;
+ struct osl_object map, chunk_table_obj;
+ struct ls_data *d = &status_item_ls_data;
+again:
+ ret = score_get_best(¤t_aft_row, &d->score);
+ if (ret < 0)
+ return ret;
+ ret = get_hash_of_row(current_aft_row, &d->hash);
+ if (ret < 0)
+ return ret;
+ ret = get_audio_file_path_of_row(current_aft_row, &d->path);
+ if (ret < 0)
+ return ret;
+ PARA_NOTICE_LOG("%s\n", d->path);
+ ret = get_afsi_object_of_row(current_aft_row, &afsi_obj);
+ if (ret < 0)
+ return ret;
+ ret = load_afsi(&d->afsi, &afsi_obj);
+ if (ret < 0)
+ return ret;
+ ret = get_afhi_of_row(current_aft_row, &afd->afhi);
+ if (ret < 0)
+ return ret;
+ d->afhi = afd->afhi;
+ d->afhi.chunk_table = afd->afhi.chunk_table = NULL;
+ ret = osl(osl_open_disk_object(audio_file_table, current_aft_row,
+ AFTCOL_CHUNKS, &chunk_table_obj));
+ if (ret < 0)
+ return ret;
+ ret = mmap_full_file(d->path, O_RDONLY, &map.data, &map.size, &afd->fd);
+ if (ret < 0)
+ goto out;
+ hash_function(map.data, map.size, file_hash);
+ ret = hash_compare(file_hash, d->hash);
+ para_munmap(map.data, map.size);
+ if (ret) {
+ ret = -E_HASH_MISMATCH;
+ goto out;
+ }
+ new_afsi = d->afsi;
+ new_afsi.num_played++;
+ new_afsi.last_played = time(NULL);
+ save_afsi(&new_afsi, &afsi_obj); /* in-place update */
+
+ afd->audio_format_id = d->afsi.audio_format_id;
+ load_chunk_table(&afd->afhi, chunk_table_obj.data);
+ aced.aft_row = current_aft_row;
+ aced.old_afsi = &d->afsi;
+ /*
+ * No need to update the status items as the AFSI_CHANGE event will
+ * recreate them.
+ */
+ afs_event(AFSI_CHANGE, NULL, &aced);
+ ret = save_afd(afd);
+out:
+ free(afd->afhi.chunk_table);
+ osl_close_disk_object(&chunk_table_obj);
+ if (ret < 0) {
+ PARA_ERROR_LOG("%s: %s\n", d->path, para_strerror(-ret));
+ ret = score_delete(current_aft_row);
+ if (ret >= 0)
+ goto again;
+ }
+ return ret;