blob: 0c85fc5957997417c3f7dd7623501e106ec350ac [file] [log] [blame]
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Miika Pekkarinen
11 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +000016 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
Miika Pekkarinen7136fd92006-07-25 10:44:19 +000022/*
23 * TagCache API
24 *
25 * ----------x---------x------------------x-----
26 * | | | External
27 * +---------------x-------+ | TagCache | Libraries
28 * | Modification routines | | Core |
29 * +-x---------x-----------+ | |
30 * | (R/W) | | | |
31 * | +------x-------------x-+ +-------------x-----+ |
32 * | | x==x Filters & clauses | |
33 * | | Search routines | +-------------------+ |
34 * | | x============================x DirCache
35 * | +-x--------------------+ | (optional)
36 * | | (R) |
37 * | | +-------------------------------+ +---------+ |
38 * | | | DB Commit (sort,unique,index) | | | |
39 * | | +-x--------------------------x--+ | Control | |
40 * | | | (R/W) | (R) | Thread | |
41 * | | | +----------------------+ | | | |
42 * | | | | TagCache DB Builder | | +---------+ |
43 * | | | +-x-------------x------+ | |
44 * | | | | (R) | (W) | |
45 * | | | | +--x--------x---------+ |
46 * | | | | | Temporary Commit DB | |
47 * | | | | +---------------------+ |
Miika Pekkarinend8ac6072006-08-02 17:39:34 +000048 * +-x----x-------x--+ |
Miika Pekkarinen7136fd92006-07-25 10:44:19 +000049 * | TagCache RAM DB x==\(W) +-----------------+ |
Miika Pekkarinend8ac6072006-08-02 17:39:34 +000050 * +-----------------+ \===x | |
Miika Pekkarinen7136fd92006-07-25 10:44:19 +000051 * | | | | (R) | Ram DB Loader x============x DirCache
52 * +-x----x---x---x---+ /==x | | (optional)
53 * | Tagcache Disk DB x==/ +-----------------+ |
54 * +------------------+ |
55 *
56 */
57
Jeffrey Goode9d842682009-11-03 16:25:03 +000058/*#define LOGF_ENABLE*/
Miika Pekkarinend8bb6702008-08-29 21:14:58 +000059
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +000060#include <stdio.h>
Dave Chapman1feb8bd2007-05-07 13:32:56 +000061#include <stdlib.h>
62#include <ctype.h>
Thomas Martitz87c8be42010-12-23 19:02:18 +000063#ifdef APPLICATION
64#include <unistd.h> /* readlink() */
Thomas Martitz4aec8ef2011-01-29 01:26:43 +000065#include <limits.h> /* PATH_MAX */
Thomas Martitz87c8be42010-12-23 19:02:18 +000066#endif
Miika Pekkarinena85044b2006-09-16 16:18:11 +000067#include "config.h"
Miika Pekkarinen9eec03f2008-02-17 18:35:27 +000068#include "ata_idle_notify.h"
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +000069#include "thread.h"
70#include "kernel.h"
71#include "system.h"
72#include "logf.h"
Thomas Martitz50a6ca32010-05-06 21:04:40 +000073#include "string-extra.h"
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +000074#include "usb.h"
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +000075#include "metadata.h"
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +000076#include "tagcache.h"
Miika Pekkarinena93e5912006-03-26 12:49:19 +000077#include "buffer.h"
Miika Pekkarinenfa893c62006-04-18 18:56:56 +000078#include "crc32.h"
Miika Pekkarinena1ac7432006-10-21 20:37:33 +000079#include "misc.h"
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +000080#include "settings.h"
Kevin Ferrare011a3252007-07-20 17:06:55 +000081#include "dir.h"
Thomas Martitz2c241602010-12-06 22:26:31 +000082#include "filefuncs.h"
Miika Pekkarinen9b9539c2007-02-13 21:51:18 +000083#include "structec.h"
Thomas Martitz87c8be42010-12-23 19:02:18 +000084#include "debug.h"
Bertrik Sikkene15f8a22008-05-03 08:35:14 +000085
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +000086#ifndef __PCTOOL__
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +000087#include "lang.h"
88#include "eeprom_settings.h"
89#endif
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +000090
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +000091#ifdef __PCTOOL__
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +000092#define yield() do { } while(0)
93#define sim_sleep(timeout) do { } while(0)
Steve Bavin6d7f68c2007-03-10 14:34:56 +000094#define do_timed_yield() do { } while(0)
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +000095#endif
96
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +000097#ifndef __PCTOOL__
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +000098/* Tag Cache thread. */
Michael Sevakisb15aa472011-02-14 11:27:45 +000099static struct event_queue tagcache_queue SHAREDBSS_ATTR;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000100static long tagcache_stack[(DEFAULT_STACK_SIZE + 0x4000)/sizeof(long)];
101static const char tagcache_thread_name[] = "tagcache";
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +0000102#endif
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000103
104/* Previous path when scanning directory tree recursively. */
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +0000105static char curpath[TAG_MAXLEN+32];
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000106
107/* Used when removing duplicates. */
108static char *tempbuf; /* Allocated when needed. */
109static long tempbufidx; /* Current location in buffer. */
110static long tempbuf_size; /* Buffer size (TEMPBUF_SIZE). */
111static long tempbuf_left; /* Buffer space left. */
112static long tempbuf_pos;
113
Andrew Mahone52a8e382009-06-03 09:38:06 +0000114#define SORTED_TAGS_COUNT 8
Andrew Mahonede7c5712009-06-06 00:00:58 +0000115#define TAGCACHE_IS_UNIQUE(tag) (BIT_N(tag) & TAGCACHE_UNIQUE_TAGS)
116#define TAGCACHE_IS_SORTED(tag) (BIT_N(tag) & TAGCACHE_SORTED_TAGS)
117#define TAGCACHE_IS_NUMERIC_OR_NONUNIQUE(tag) \
118 (BIT_N(tag) & (TAGCACHE_NUMERIC_TAGS | ~TAGCACHE_UNIQUE_TAGS))
Miika Pekkarinenea07cd52006-03-27 07:44:32 +0000119/* Tags we want to get sorted (loaded to the tempbuf). */
Andrew Mahone1248a0d2009-06-03 08:19:32 +0000120#define TAGCACHE_SORTED_TAGS ((1LU << tag_artist) | (1LU << tag_album) | \
121 (1LU << tag_genre) | (1LU << tag_composer) | (1LU << tag_comment) | \
122 (1LU << tag_albumartist) | (1LU << tag_grouping) | (1LU << tag_title))
Miika Pekkarinen58fe4de2006-04-10 10:26:24 +0000123
124/* Uniqued tags (we can use these tags with filters and conditional clauses). */
Andrew Mahone1248a0d2009-06-03 08:19:32 +0000125#define TAGCACHE_UNIQUE_TAGS ((1LU << tag_artist) | (1LU << tag_album) | \
126 (1LU << tag_genre) | (1LU << tag_composer) | (1LU << tag_comment) | \
127 (1LU << tag_albumartist) | (1LU << tag_grouping))
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000128
Miika Pekkarinen4e976642007-02-10 12:09:28 +0000129/* String presentation of the tags defined in tagcache.h. Must be in correct order! */
Miika Pekkarinencb8c7952006-07-20 12:19:31 +0000130static const char *tags_str[] = { "artist", "album", "genre", "title",
Miika Pekkarinenf6039462007-12-16 21:10:26 +0000131 "filename", "composer", "comment", "albumartist", "grouping", "year",
132 "discnumber", "tracknumber", "bitrate", "length", "playcount", "rating",
Michael Hohmuth1a23e0c2011-02-27 20:25:11 +0000133 "playtime", "lastplayed", "commitid", "mtime", "lastoffset" };
Miika Pekkarinencb8c7952006-07-20 12:19:31 +0000134
Miika Pekkarinen41427102006-04-23 18:47:26 +0000135/* Status information of the tagcache. */
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +0000136static struct tagcache_stat tc_stat;
Miika Pekkarinen1b18dd02006-04-12 10:31:24 +0000137
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000138/* Queue commands. */
Miika Pekkarinenfa893c62006-04-18 18:56:56 +0000139enum tagcache_queue {
140 Q_STOP_SCAN = 0,
141 Q_START_SCAN,
Miika Pekkarinen86f07c72006-07-23 15:35:53 +0000142 Q_IMPORT_CHANGELOG,
Miika Pekkarinen9cd5c3e2006-07-10 16:22:03 +0000143 Q_UPDATE,
144 Q_REBUILD,
Miika Pekkarinen9d756e22007-07-21 17:35:19 +0000145
146 /* Internal tagcache command queue. */
147 CMD_UPDATE_MASTER_HEADER,
148 CMD_UPDATE_NUMERIC,
Miika Pekkarinenfa893c62006-04-18 18:56:56 +0000149};
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000150
Miika Pekkarinen9d756e22007-07-21 17:35:19 +0000151struct tagcache_command_entry {
Jonathan Gordon7c0a8e12008-04-24 07:54:32 +0000152 int32_t command;
153 int32_t idx_id;
154 int32_t tag;
155 int32_t data;
Miika Pekkarinen9d756e22007-07-21 17:35:19 +0000156};
157
Björn Stenbergee46a3d2008-12-15 23:42:19 +0000158#ifndef __PCTOOL__
Miika Pekkarinen9d756e22007-07-21 17:35:19 +0000159static struct tagcache_command_entry command_queue[TAGCACHE_COMMAND_QUEUE_LENGTH];
160static volatile int command_queue_widx = 0;
161static volatile int command_queue_ridx = 0;
Michael Sevakisb15aa472011-02-14 11:27:45 +0000162static struct mutex command_queue_mutex SHAREDBSS_ATTR;
Björn Stenbergee46a3d2008-12-15 23:42:19 +0000163#endif
Miika Pekkarinen3b313462006-04-16 17:32:54 +0000164
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000165/* Tag database structures. */
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000166
167/* Variable-length tag entry in tag files. */
168struct tagfile_entry {
Thomas Martitz2b6e4ad2009-03-06 15:21:58 +0000169 int32_t tag_length; /* Length of the data in bytes including '\0' */
170 int32_t idx_id; /* Corresponding entry location in index file of not unique tags */
Miika Pekkarinencb8c7952006-07-20 12:19:31 +0000171 char tag_data[0]; /* Begin of the tag data */
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000172};
173
174/* Fixed-size tag entry in master db index. */
175struct index_entry {
Jonathan Gordon7c0a8e12008-04-24 07:54:32 +0000176 int32_t tag_seek[TAG_COUNT]; /* Location of tag data or numeric tag data */
177 int32_t flag; /* Status flags */
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000178};
179
180/* Header is the same in every file. */
181struct tagcache_header {
Jonathan Gordon7c0a8e12008-04-24 07:54:32 +0000182 int32_t magic; /* Header version number */
183 int32_t datasize; /* Data size in bytes */
184 int32_t entry_count; /* Number of entries in this file */
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000185};
186
Miika Pekkarinencb8c7952006-07-20 12:19:31 +0000187struct master_header {
188 struct tagcache_header tch;
Jonathan Gordon7c0a8e12008-04-24 07:54:32 +0000189 int32_t serial; /* Increasing counting number */
190 int32_t commitid; /* Number of commits so far */
191 int32_t dirty;
Miika Pekkarinencb8c7952006-07-20 12:19:31 +0000192};
193
Miika Pekkarinen9b9539c2007-02-13 21:51:18 +0000194/* For the endianess correction */
Thomas Martitz2b6e4ad2009-03-06 15:21:58 +0000195static const char *tagfile_entry_ec = "ll";
Miika Pekkarinend8bb6702008-08-29 21:14:58 +0000196/**
197 Note: This should be (1 + TAG_COUNT) amount of l's.
198 */
Michael Giacomelli66e8fc02011-01-02 02:49:13 +0000199static const char *index_entry_ec = "llllllllllllllllllllll";
Miika Pekkarinend8bb6702008-08-29 21:14:58 +0000200
Miika Pekkarinen9b9539c2007-02-13 21:51:18 +0000201static const char *tagcache_header_ec = "lll";
Miika Pekkarinenf5184f32007-02-25 20:41:51 +0000202static const char *master_header_ec = "llllll";
Miika Pekkarinen9b9539c2007-02-13 21:51:18 +0000203
Miika Pekkarinenf5184f32007-02-25 20:41:51 +0000204static struct master_header current_tcmh;
Miika Pekkarinencb8c7952006-07-20 12:19:31 +0000205
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000206#ifdef HAVE_TC_RAMCACHE
207/* Header is created when loading database to ram. */
208struct ramcache_header {
Miika Pekkarinencb8c7952006-07-20 12:19:31 +0000209 struct index_entry *indices; /* Master index file content */
210 char *tags[TAG_COUNT]; /* Tag file content (not including filename tag) */
211 int entry_count[TAG_COUNT]; /* Number of entries in the indices. */
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000212};
213
Peter D'Hoyec4a59a22006-08-15 22:54:06 +0000214# ifdef HAVE_EEPROM_SETTINGS
Miika Pekkarinen954b7322006-08-05 20:19:10 +0000215struct statefile_header {
Miika Pekkarinen8d9b9d22011-06-19 14:36:26 +0000216 struct master_header mh; /* Header from the master index */
Miika Pekkarinen954b7322006-08-05 20:19:10 +0000217 struct ramcache_header *hdr;
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +0000218 struct tagcache_stat tc_stat;
Miika Pekkarinen954b7322006-08-05 20:19:10 +0000219};
220# endif
221
Miika Pekkarinencb8c7952006-07-20 12:19:31 +0000222/* Pointer to allocated ramcache_header */
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000223static struct ramcache_header *hdr;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000224#endif
225
226/**
227 * Full tag entries stored in a temporary file waiting
228 * for commit to the cache. */
229struct temp_file_entry {
230 long tag_offset[TAG_COUNT];
231 short tag_length[TAG_COUNT];
Miika Pekkarinen812cbad2006-10-04 09:05:01 +0000232 long flag;
233
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000234 long data_length;
235};
236
Miika Pekkarinen41427102006-04-23 18:47:26 +0000237struct tempbuf_id_list {
238 long id;
239 struct tempbuf_id_list *next;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000240};
241
242struct tempbuf_searchidx {
Miika Pekkarinen44b76bc2006-03-30 12:07:32 +0000243 long idx_id;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000244 char *str;
245 int seek;
Miika Pekkarinen41427102006-04-23 18:47:26 +0000246 struct tempbuf_id_list idlist;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000247};
248
Miika Pekkarinen36e30812006-09-26 11:23:18 +0000249/* Lookup buffer for fixing messed up index while after sorting. */
Miika Pekkarinena47bd732006-07-22 07:09:57 +0000250static long commit_entry_count;
Miika Pekkarinen36e30812006-09-26 11:23:18 +0000251static long lookup_buffer_depth;
Jens Arnold2597a132006-12-25 14:01:47 +0000252static struct tempbuf_searchidx **lookup;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000253
254/* Used when building the temporary file. */
255static int cachefd = -1, filenametag_fd;
256static int total_entry_count = 0;
257static int data_size = 0;
258static int processed_dir_count;
259
Miika Pekkarinend8ac6072006-08-02 17:39:34 +0000260/* Thread safe locking */
261static volatile int write_lock;
262static volatile int read_lock;
263
Miika Pekkarinenf6039462007-12-16 21:10:26 +0000264static bool delete_entry(long idx_id);
265
Miika Pekkarinencb8c7952006-07-20 12:19:31 +0000266const char* tagcache_tag_to_str(int tag)
267{
268 return tags_str[tag];
269}
270
Amaury Pouly1bff4b32010-03-16 12:15:56 +0000271/* Helper functions for the two most read/write data structure: tagfile_entry and index_entry */
Jens Arnold41ef1d52010-03-17 08:23:50 +0000272static ssize_t ecread_tagfile_entry(int fd, struct tagfile_entry *buf)
Amaury Pouly1bff4b32010-03-16 12:15:56 +0000273{
274 return ecread(fd, buf, 1, tagfile_entry_ec, tc_stat.econ);
275}
276
Jens Arnold41ef1d52010-03-17 08:23:50 +0000277static ssize_t ecread_index_entry(int fd, struct index_entry *buf)
Amaury Pouly1bff4b32010-03-16 12:15:56 +0000278{
279 return ecread(fd, buf, 1, index_entry_ec, tc_stat.econ);
280}
281
Jens Arnold41ef1d52010-03-17 08:23:50 +0000282static ssize_t ecwrite_index_entry(int fd, struct index_entry *buf)
Amaury Pouly1bff4b32010-03-16 12:15:56 +0000283{
284 return ecwrite(fd, buf, 1, index_entry_ec, tc_stat.econ);
285}
286
Miika Pekkarinen784112a2008-01-13 19:34:49 +0000287#ifdef HAVE_DIRCACHE
Miika Pekkarinen39c597b2008-01-13 19:13:37 +0000288/**
289 * Returns true if specified flag is still present, i.e., dircache
290 * has not been reloaded.
291 */
292static bool is_dircache_intact(void)
293{
294 return dircache_get_appflag(DIRCACHE_APPFLAG_TAGCACHE);
295}
Miika Pekkarinen784112a2008-01-13 19:34:49 +0000296#endif
Miika Pekkarinen39c597b2008-01-13 19:13:37 +0000297
Miika Pekkarinen29fa15f2006-07-23 11:15:28 +0000298static int open_tag_fd(struct tagcache_header *hdr, int tag, bool write)
299{
300 int fd;
Thomas Martitz2c241602010-12-06 22:26:31 +0000301 char buf[MAX_PATH];
Miika Pekkarinen91614252006-07-23 14:28:12 +0000302 int rc;
Miika Pekkarinen29fa15f2006-07-23 11:15:28 +0000303
Andrew Mahone1248a0d2009-06-03 08:19:32 +0000304 if (TAGCACHE_IS_NUMERIC(tag) || tag < 0 || tag >= TAG_COUNT)
Miika Pekkarinen29fa15f2006-07-23 11:15:28 +0000305 return -1;
306
307 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, tag);
308
Thomas Martitz2c241602010-12-06 22:26:31 +0000309 fd = open(buf, write ? O_RDWR : O_RDONLY);
Miika Pekkarinen29fa15f2006-07-23 11:15:28 +0000310 if (fd < 0)
311 {
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +0000312 logf("tag file open failed: tag=%d write=%d file=%s", tag, write, buf);
313 tc_stat.ready = false;
Miika Pekkarinen29fa15f2006-07-23 11:15:28 +0000314 return fd;
315 }
316
317 /* Check the header. */
Miika Pekkarinen9b9539c2007-02-13 21:51:18 +0000318 rc = ecread(fd, hdr, 1, tagcache_header_ec, tc_stat.econ);
Miika Pekkarinen91614252006-07-23 14:28:12 +0000319 if (hdr->magic != TAGCACHE_MAGIC || rc != sizeof(struct tagcache_header))
Miika Pekkarinen29fa15f2006-07-23 11:15:28 +0000320 {
321 logf("header error");
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +0000322 tc_stat.ready = false;
Miika Pekkarinen29fa15f2006-07-23 11:15:28 +0000323 close(fd);
324 return -2;
325 }
Miika Pekkarinen36e30812006-09-26 11:23:18 +0000326
Miika Pekkarinen29fa15f2006-07-23 11:15:28 +0000327 return fd;
328}
329
Miika Pekkarinenf6039462007-12-16 21:10:26 +0000330static int open_master_fd(struct master_header *hdr, bool write)
331{
332 int fd;
333 int rc;
334
Thomas Martitz2c241602010-12-06 22:26:31 +0000335 fd = open(TAGCACHE_FILE_MASTER, write ? O_RDWR : O_RDONLY);
Miika Pekkarinenf6039462007-12-16 21:10:26 +0000336 if (fd < 0)
337 {
338 logf("master file open failed for R/W");
339 tc_stat.ready = false;
340 return fd;
341 }
342
343 tc_stat.econ = false;
344
345 /* Check the header. */
346 rc = read(fd, hdr, sizeof(struct master_header));
347 if (hdr->tch.magic == TAGCACHE_MAGIC && rc == sizeof(struct master_header))
348 {
349 /* Success. */
350 return fd;
351 }
352
353 /* Trying to read again, this time with endianess correction enabled. */
354 lseek(fd, 0, SEEK_SET);
355
356 rc = ecread(fd, hdr, 1, master_header_ec, true);
357 if (hdr->tch.magic != TAGCACHE_MAGIC || rc != sizeof(struct master_header))
358 {
359 logf("header error");
360 tc_stat.ready = false;
361 close(fd);
362 return -2;
363 }
364
365 tc_stat.econ = true;
366
367 return fd;
368}
369
Steve Bavin6d7f68c2007-03-10 14:34:56 +0000370#ifndef __PCTOOL__
371static bool do_timed_yield(void)
372{
373 /* Sorting can lock up for quite a while, so yield occasionally */
374 static long wakeup_tick = 0;
Bertrik Sikkend24d8852009-10-18 15:50:30 +0000375 if (TIME_AFTER(current_tick, wakeup_tick))
Steve Bavin6d7f68c2007-03-10 14:34:56 +0000376 {
377 wakeup_tick = current_tick + (HZ/4);
378 yield();
379 return true;
380 }
381 return false;
382}
383#endif
384
Miika Pekkarinen9cd5c3e2006-07-10 16:22:03 +0000385#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000386static long find_entry_ram(const char *filename,
Amaury Poulyf82c0212010-02-16 22:30:55 +0000387 const struct dircache_entry *dc)
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000388{
389 static long last_pos = 0;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000390 int i;
391
Alexander Levin871af162010-12-08 22:08:08 +0000392 /* Check if tagcache is loaded into ram. */
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +0000393 if (!tc_stat.ramcache)
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000394 return -1;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000395
396 if (dc == NULL)
397 dc = dircache_get_entry_ptr(filename);
398
399 if (dc == NULL)
400 {
401 logf("tagcache: file not found.");
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000402 return -1;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000403 }
404
405 try_again:
406
407 if (last_pos > 0)
408 i = last_pos;
409 else
410 i = 0;
411
Miika Pekkarinen8d9b9d22011-06-19 14:36:26 +0000412 for (; i < current_tcmh.tch.entry_count; i++)
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000413 {
414 if (hdr->indices[i].tag_seek[tag_filename] == (long)dc)
415 {
416 last_pos = MAX(0, i - 3);
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000417 return i;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000418 }
419
Steve Bavin6d7f68c2007-03-10 14:34:56 +0000420 do_timed_yield();
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000421 }
422
423 if (last_pos > 0)
424 {
425 last_pos = 0;
426 goto try_again;
427 }
428
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000429 return -1;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000430}
431#endif
432
Thomas Jarosch0060fed2011-02-16 01:01:49 +0000433static long find_entry_disk(const char *filename_raw, bool localfd)
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000434{
Miika Pekkarinen29fa15f2006-07-23 11:15:28 +0000435 struct tagcache_header tch;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000436 static long last_pos = -1;
437 long pos_history[POS_HISTORY_COUNT];
438 long pos_history_idx = 0;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000439 bool found = false;
440 struct tagfile_entry tfe;
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000441 int fd;
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +0000442 char buf[TAG_MAXLEN+32];
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000443 int i;
444 int pos = -1;
445
Thomas Jarosch1dbea9d2011-02-16 17:58:57 +0000446 const char *filename = filename_raw;
Thomas Jarosch0060fed2011-02-16 01:01:49 +0000447#ifdef APPLICATION
Thomas Jarosch29e4a2d2011-02-16 18:40:44 +0000448 char pathbuf[PATH_MAX]; /* Note: Don't use MAX_PATH here, it's too small */
Thomas Jarosch0060fed2011-02-16 01:01:49 +0000449 if (realpath(filename, pathbuf) == pathbuf)
450 filename = pathbuf;
451#endif
452
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +0000453 if (!tc_stat.ready)
Miika Pekkarinen29fa15f2006-07-23 11:15:28 +0000454 return -2;
455
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000456 fd = filenametag_fd;
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +0000457 if (fd < 0 || localfd)
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000458 {
459 last_pos = -1;
Miika Pekkarinen29fa15f2006-07-23 11:15:28 +0000460 if ( (fd = open_tag_fd(&tch, tag_filename, false)) < 0)
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000461 return -1;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000462 }
463
464 check_again:
465
466 if (last_pos > 0)
467 lseek(fd, last_pos, SEEK_SET);
Miika Pekkarinen9c0b54a2006-07-24 12:10:50 +0000468 else
469 lseek(fd, sizeof(struct tagcache_header), SEEK_SET);
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000470
471 while (true)
472 {
473 pos = lseek(fd, 0, SEEK_CUR);
474 for (i = pos_history_idx-1; i >= 0; i--)
475 pos_history[i+1] = pos_history[i];
476 pos_history[0] = pos;
477
Amaury Pouly1bff4b32010-03-16 12:15:56 +0000478 if (ecread_tagfile_entry(fd, &tfe)
Miika Pekkarinen9b9539c2007-02-13 21:51:18 +0000479 != sizeof(struct tagfile_entry))
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000480 {
481 break ;
482 }
483
484 if (tfe.tag_length >= (long)sizeof(buf))
485 {
Miika Pekkarinen9cd5c3e2006-07-10 16:22:03 +0000486 logf("too long tag #1");
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000487 close(fd);
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +0000488 if (!localfd)
489 filenametag_fd = -1;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000490 last_pos = -1;
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000491 return -2;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000492 }
493
494 if (read(fd, buf, tfe.tag_length) != tfe.tag_length)
495 {
496 logf("read error #2");
497 close(fd);
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +0000498 if (!localfd)
499 filenametag_fd = -1;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000500 last_pos = -1;
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000501 return -3;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000502 }
503
504 if (!strcasecmp(filename, buf))
505 {
506 last_pos = pos_history[pos_history_idx];
507 found = true;
508 break ;
509 }
510
511 if (pos_history_idx < POS_HISTORY_COUNT - 1)
512 pos_history_idx++;
513 }
514
515 /* Not found? */
516 if (!found)
517 {
518 if (last_pos > 0)
519 {
520 last_pos = -1;
521 logf("seek again");
522 goto check_again;
523 }
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000524
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +0000525 if (fd != filenametag_fd || localfd)
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000526 close(fd);
527 return -4;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000528 }
529
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +0000530 if (fd != filenametag_fd || localfd)
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000531 close(fd);
532
533 return tfe.idx_id;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +0000534}
535
Miika Pekkarinencb8c7952006-07-20 12:19:31 +0000536static int find_index(const char *filename)
Miika Pekkarinen44b76bc2006-03-30 12:07:32 +0000537{
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000538 long idx_id = -1;
Miika Pekkarinen44b76bc2006-03-30 12:07:32 +0000539
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000540#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
Miika Pekkarinen39c597b2008-01-13 19:13:37 +0000541 if (tc_stat.ramcache && is_dircache_intact())
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000542 idx_id = find_entry_ram(filename, NULL);
543#endif
544
545 if (idx_id < 0)
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +0000546 idx_id = find_entry_disk(filename, true);
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000547
Miika Pekkarinencb8c7952006-07-20 12:19:31 +0000548 return idx_id;
549}
550
551bool tagcache_find_index(struct tagcache_search *tcs, const char *filename)
552{
553 int idx_id;
554
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +0000555 if (!tc_stat.ready)
Miika Pekkarinen29fa15f2006-07-23 11:15:28 +0000556 return false;
557
Miika Pekkarinencb8c7952006-07-20 12:19:31 +0000558 idx_id = find_index(filename);
559 if (idx_id < 0)
560 return false;
561
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000562 if (!tagcache_search(tcs, tag_filename))
563 return false;
564
565 tcs->entry_count = 0;
566 tcs->idx_id = idx_id;
567
568 return true;
569}
570
Miika Pekkarinendafa0d42006-07-24 15:35:46 +0000571static bool get_index(int masterfd, int idxid,
572 struct index_entry *idx, bool use_ram)
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000573{
Miika Pekkarinenf6039462007-12-16 21:10:26 +0000574 bool localfd = false;
575
Miika Pekkarinen42946152006-08-30 18:18:37 +0000576 if (idxid < 0)
577 {
578 logf("Incorrect idxid: %d", idxid);
579 return false;
580 }
581
Miika Pekkarinen44b76bc2006-03-30 12:07:32 +0000582#ifdef HAVE_TC_RAMCACHE
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +0000583 if (tc_stat.ramcache && use_ram)
Miika Pekkarinen44b76bc2006-03-30 12:07:32 +0000584 {
Miika Pekkarinenf9bfd732006-04-19 18:56:59 +0000585 if (hdr->indices[idxid].flag & FLAG_DELETED)
586 return false;
587
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +0000588# ifdef HAVE_DIRCACHE
589 if (!(hdr->indices[idxid].flag & FLAG_DIRCACHE)
590 || is_dircache_intact())
591#endif
592 {
593 memcpy(idx, &hdr->indices[idxid], sizeof(struct index_entry));
594 return true;
595 }
Miika Pekkarinen44b76bc2006-03-30 12:07:32 +0000596 }
Jens Arnolda5f42cb2006-08-02 06:57:13 +0000597#else
598 (void)use_ram;
Miika Pekkarinen44b76bc2006-03-30 12:07:32 +0000599#endif
600
Miika Pekkarinenf6039462007-12-16 21:10:26 +0000601 if (masterfd < 0)
602 {
603 struct master_header tcmh;
604
605 localfd = true;
606 masterfd = open_master_fd(&tcmh, false);
607 if (masterfd < 0)
608 return false;
609 }
610
Miika Pekkarinen86f07c72006-07-23 15:35:53 +0000611 lseek(masterfd, idxid * sizeof(struct index_entry)
Miika Pekkarinencb8c7952006-07-20 12:19:31 +0000612 + sizeof(struct master_header), SEEK_SET);
Amaury Pouly1bff4b32010-03-16 12:15:56 +0000613 if (ecread_index_entry(masterfd, idx)
Miika Pekkarinen9b9539c2007-02-13 21:51:18 +0000614 != sizeof(struct index_entry))
Miika Pekkarinen44b76bc2006-03-30 12:07:32 +0000615 {
616 logf("read error #3");
Miika Pekkarinenf6039462007-12-16 21:10:26 +0000617 if (localfd)
618 close(masterfd);
619
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000620 return false;
Miika Pekkarinen44b76bc2006-03-30 12:07:32 +0000621 }
Miika Pekkarinen44b76bc2006-03-30 12:07:32 +0000622
Miika Pekkarinenf6039462007-12-16 21:10:26 +0000623 if (localfd)
624 close(masterfd);
625
Miika Pekkarinen86f07c72006-07-23 15:35:53 +0000626 if (idx->flag & FLAG_DELETED)
627 return false;
628
629 return true;
630}
631
Björn Stenbergee46a3d2008-12-15 23:42:19 +0000632#ifndef __PCTOOL__
633
Miika Pekkarinen86f07c72006-07-23 15:35:53 +0000634static bool write_index(int masterfd, int idxid, struct index_entry *idx)
635{
Miika Pekkarinendafa0d42006-07-24 15:35:46 +0000636 /* We need to exclude all memory only flags & tags when writing to disk. */
637 if (idx->flag & FLAG_DIRCACHE)
638 {
639 logf("memory only flags!");
640 return false;
641 }
642
Miika Pekkarinen86f07c72006-07-23 15:35:53 +0000643#ifdef HAVE_TC_RAMCACHE
Miika Pekkarinen4cee8d92007-05-27 11:40:55 +0000644 /* Only update numeric data. Writing the whole index to RAM by memcpy
645 * destroys dircache pointers!
646 */
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +0000647 if (tc_stat.ramcache)
Miika Pekkarinendafa0d42006-07-24 15:35:46 +0000648 {
Miika Pekkarinen4cee8d92007-05-27 11:40:55 +0000649 int tag;
650 struct index_entry *idx_ram = &hdr->indices[idxid];
651
652 for (tag = 0; tag < TAG_COUNT; tag++)
653 {
Andrew Mahone1248a0d2009-06-03 08:19:32 +0000654 if (TAGCACHE_IS_NUMERIC(tag))
Miika Pekkarinen4cee8d92007-05-27 11:40:55 +0000655 {
656 idx_ram->tag_seek[tag] = idx->tag_seek[tag];
657 }
658 }
659
Miika Pekkarinend8bb6702008-08-29 21:14:58 +0000660 /* Don't touch the dircache flag or attributes. */
661 idx_ram->flag = (idx->flag & 0x0000ffff)
662 | (idx_ram->flag & (0xffff0000 | FLAG_DIRCACHE));
Miika Pekkarinendafa0d42006-07-24 15:35:46 +0000663 }
Miika Pekkarinen86f07c72006-07-23 15:35:53 +0000664#endif
665
666 lseek(masterfd, idxid * sizeof(struct index_entry)
667 + sizeof(struct master_header), SEEK_SET);
Amaury Pouly1bff4b32010-03-16 12:15:56 +0000668 if (ecwrite_index_entry(masterfd, idx) != sizeof(struct index_entry))
Miika Pekkarinen86f07c72006-07-23 15:35:53 +0000669 {
670 logf("write error #3");
Miika Pekkarinend8ac6072006-08-02 17:39:34 +0000671 logf("idxid: %d", idxid);
Miika Pekkarinen86f07c72006-07-23 15:35:53 +0000672 return false;
673 }
674
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000675 return true;
676}
677
Björn Stenbergee46a3d2008-12-15 23:42:19 +0000678#endif /* !__PCTOOL__ */
679
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000680static bool open_files(struct tagcache_search *tcs, int tag)
681{
682 if (tcs->idxfd[tag] < 0)
683 {
Thomas Martitz2c241602010-12-06 22:26:31 +0000684 char fn[MAX_PATH];
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000685
686 snprintf(fn, sizeof fn, TAGCACHE_FILE_INDEX, tag);
Thomas Martitz2c241602010-12-06 22:26:31 +0000687 tcs->idxfd[tag] = open(fn, O_RDONLY);
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000688 }
689
690 if (tcs->idxfd[tag] < 0)
691 {
692 logf("File not open!");
693 return false;
694 }
695
696 return true;
697}
698
699static bool retrieve(struct tagcache_search *tcs, struct index_entry *idx,
700 int tag, char *buf, long size)
701{
702 struct tagfile_entry tfe;
703 long seek;
704
705 *buf = '\0';
706
Andrew Mahone1248a0d2009-06-03 08:19:32 +0000707 if (TAGCACHE_IS_NUMERIC(tag))
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000708 return false;
709
710 seek = idx->tag_seek[tag];
711 if (seek < 0)
712 {
713 logf("Retrieve failed");
714 return false;
715 }
716
717#ifdef HAVE_TC_RAMCACHE
718 if (tcs->ramsearch)
719 {
720 struct tagfile_entry *ep;
721
722# ifdef HAVE_DIRCACHE
Miika Pekkarinen39c597b2008-01-13 19:13:37 +0000723 if (tag == tag_filename && (idx->flag & FLAG_DIRCACHE)
724 && is_dircache_intact())
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000725 {
Amaury Poulyf82c0212010-02-16 22:30:55 +0000726 dircache_copy_path((struct dircache_entry *)seek,
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000727 buf, size);
728 return true;
729 }
730 else
731# endif
732 if (tag != tag_filename)
733 {
734 ep = (struct tagfile_entry *)&hdr->tags[tag][seek];
Nils Wallménius3d4701a2009-07-14 13:57:45 +0000735 strlcpy(buf, ep->tag_data, size);
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000736
737 return true;
738 }
739 }
740#endif
741
742 if (!open_files(tcs, tag))
743 return false;
744
745 lseek(tcs->idxfd[tag], seek, SEEK_SET);
Amaury Pouly1bff4b32010-03-16 12:15:56 +0000746 if (ecread_tagfile_entry(tcs->idxfd[tag], &tfe)
Miika Pekkarinen9b9539c2007-02-13 21:51:18 +0000747 != sizeof(struct tagfile_entry))
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000748 {
749 logf("read error #5");
750 return false;
751 }
752
753 if (tfe.tag_length >= size)
754 {
755 logf("too small buffer");
756 return false;
757 }
758
759 if (read(tcs->idxfd[tag], buf, tfe.tag_length) !=
760 tfe.tag_length)
761 {
762 logf("read error #6");
763 return false;
764 }
765
766 buf[tfe.tag_length] = '\0';
767
768 return true;
769}
770
Jonathan Gordon893d2f32010-11-23 00:15:13 +0000771#define COMMAND_QUEUE_IS_EMPTY (command_queue_ridx == command_queue_widx)
772
Michael Hohmuth564a8ed2011-06-06 22:48:57 +0000773static long find_tag(int tag, int idx_id, const struct index_entry *idx)
Jonathan Gordon893d2f32010-11-23 00:15:13 +0000774{
Jonathan Gordonefba7d82010-11-23 00:27:54 +0000775#ifndef __PCTOOL__
Michael Hohmuth564a8ed2011-06-06 22:48:57 +0000776 if (! COMMAND_QUEUE_IS_EMPTY && TAGCACHE_IS_NUMERIC(tag))
Jonathan Gordon893d2f32010-11-23 00:15:13 +0000777 {
778 /* Attempt to find tag data through store-to-load forwarding in
779 command queue */
780 long result = -1;
781
782 mutex_lock(&command_queue_mutex);
783
784 int ridx = command_queue_widx;
785
786 while (ridx != command_queue_ridx)
787 {
788 if (--ridx < 0)
789 ridx = TAGCACHE_COMMAND_QUEUE_LENGTH - 1;
790
791 if (command_queue[ridx].command == CMD_UPDATE_NUMERIC
792 && command_queue[ridx].idx_id == idx_id
793 && command_queue[ridx].tag == tag)
794 {
795 result = command_queue[ridx].data;
796 break;
797 }
798 }
799
800 mutex_unlock(&command_queue_mutex);
801
802 if (result >= 0)
803 {
Michael Hohmuth564a8ed2011-06-06 22:48:57 +0000804 logf("find_tag: "
Jonathan Gordon893d2f32010-11-23 00:15:13 +0000805 "Recovered tag %d value %lX from write queue",
806 tag, result);
807 return result;
808 }
809 }
Jonathan Gordon1e0006b2010-11-23 00:25:48 +0000810#endif
Jonathan Gordon893d2f32010-11-23 00:15:13 +0000811
812 return idx->tag_seek[tag];
813}
814
815
816static long check_virtual_tags(int tag, int idx_id,
817 const struct index_entry *idx)
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000818{
819 long data = 0;
820
821 switch (tag)
822 {
Miika Pekkarinen9d9937a2007-04-12 20:14:05 +0000823 case tag_virt_length_sec:
Michael Hohmuth564a8ed2011-06-06 22:48:57 +0000824 data = (find_tag(tag_length, idx_id, idx)/1000) % 60;
Miika Pekkarinen9d9937a2007-04-12 20:14:05 +0000825 break;
826
827 case tag_virt_length_min:
Michael Hohmuth564a8ed2011-06-06 22:48:57 +0000828 data = (find_tag(tag_length, idx_id, idx)/1000) / 60;
Miika Pekkarinen9d9937a2007-04-12 20:14:05 +0000829 break;
830
Miika Pekkarinen5e47daa2007-04-12 20:21:56 +0000831 case tag_virt_playtime_sec:
Michael Hohmuth564a8ed2011-06-06 22:48:57 +0000832 data = (find_tag(tag_playtime, idx_id, idx)/1000) % 60;
Miika Pekkarinen5e47daa2007-04-12 20:21:56 +0000833 break;
834
835 case tag_virt_playtime_min:
Michael Hohmuth564a8ed2011-06-06 22:48:57 +0000836 data = (find_tag(tag_playtime, idx_id, idx)/1000) / 60;
Miika Pekkarinen5e47daa2007-04-12 20:21:56 +0000837 break;
838
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000839 case tag_virt_autoscore:
Michael Hohmuth564a8ed2011-06-06 22:48:57 +0000840 if (find_tag(tag_length, idx_id, idx) == 0
841 || find_tag(tag_playcount, idx_id, idx) == 0)
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000842 {
843 data = 0;
844 }
845 else
846 {
Amaury Pouly75fc9ee2010-02-02 20:49:35 +0000847 /* A straight calculus gives:
848 autoscore = 100 * playtime / length / playcout (1)
849 Now, consider the euclidian division of playtime by length:
850 playtime = alpha * length + beta
851 With:
852 0 <= beta < length
853 Now, (1) becomes:
854 autoscore = 100 * (alpha / playcout + beta / length / playcount)
855 Both terms should be small enough to avoid any overflow
856 */
Michael Hohmuth564a8ed2011-06-06 22:48:57 +0000857 data = 100 * (find_tag(tag_playtime, idx_id, idx)
858 / find_tag(tag_length, idx_id, idx))
859 + (100 * (find_tag(tag_playtime, idx_id, idx)
860 % find_tag(tag_length, idx_id, idx)))
861 / find_tag(tag_length, idx_id, idx);
862 data /= find_tag(tag_playcount, idx_id, idx);
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000863 }
864 break;
865
Miika Pekkarinenf5184f32007-02-25 20:41:51 +0000866 /* How many commits before the file has been added to the DB. */
867 case tag_virt_entryage:
Jonathan Gordon893d2f32010-11-23 00:15:13 +0000868 data = current_tcmh.commitid
Michael Hohmuth564a8ed2011-06-06 22:48:57 +0000869 - find_tag(tag_commitid, idx_id, idx) - 1;
Miika Pekkarinenf5184f32007-02-25 20:41:51 +0000870 break;
871
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000872 default:
Michael Hohmuth564a8ed2011-06-06 22:48:57 +0000873 data = find_tag(tag, idx_id, idx);
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000874 }
875
876 return data;
Miika Pekkarinen44b76bc2006-03-30 12:07:32 +0000877}
878
Miika Pekkarinen58fe4de2006-04-10 10:26:24 +0000879long tagcache_get_numeric(const struct tagcache_search *tcs, int tag)
880{
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000881 struct index_entry idx;
Miika Pekkarinen58fe4de2006-04-10 10:26:24 +0000882
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +0000883 if (!tc_stat.ready)
Miika Pekkarinen29fa15f2006-07-23 11:15:28 +0000884 return false;
885
Andrew Mahone1248a0d2009-06-03 08:19:32 +0000886 if (!TAGCACHE_IS_NUMERIC(tag))
Miika Pekkarinen58fe4de2006-04-10 10:26:24 +0000887 return -1;
888
Miika Pekkarinendafa0d42006-07-24 15:35:46 +0000889 if (!get_index(tcs->masterfd, tcs->idx_id, &idx, true))
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000890 return -2;
891
Jonathan Gordon893d2f32010-11-23 00:15:13 +0000892 return check_virtual_tags(tag, tcs->idx_id, &idx);
Miika Pekkarinen58fe4de2006-04-10 10:26:24 +0000893}
894
Miika Pekkarinene5059a72006-08-13 14:53:19 +0000895inline static bool str_ends_with(const char *str1, const char *str2)
896{
897 int str_len = strlen(str1);
898 int clause_len = strlen(str2);
899
900 if (clause_len > str_len)
901 return false;
902
903 return !strcasecmp(&str1[str_len - clause_len], str2);
904}
905
Miika Pekkarinenbe2eb022006-10-09 10:23:35 +0000906inline static bool str_oneof(const char *str, const char *list)
907{
908 const char *sep;
909 int l, len = strlen(str);
910
911 while (*list)
912 {
913 sep = strchr(list, '|');
914 l = sep ? (long)sep - (long)list : (int)strlen(list);
915 if ((l==len) && !strncasecmp(str, list, len))
916 return true;
917 list += sep ? l + 1 : l;
918 }
919
920 return false;
921}
922
Miika Pekkarinen4c6fd0f2006-04-03 18:57:34 +0000923static bool check_against_clause(long numeric, const char *str,
924 const struct tagcache_search_clause *clause)
925{
Miika Pekkarinen3eb9e702006-10-05 09:49:18 +0000926 if (clause->numeric)
Miika Pekkarinen4c6fd0f2006-04-03 18:57:34 +0000927 {
Miika Pekkarinen3eb9e702006-10-05 09:49:18 +0000928 switch (clause->type)
929 {
930 case clause_is:
Miika Pekkarinen4c6fd0f2006-04-03 18:57:34 +0000931 return numeric == clause->numeric_data;
Miika Pekkarinen3eb9e702006-10-05 09:49:18 +0000932 case clause_is_not:
933 return numeric != clause->numeric_data;
934 case clause_gt:
935 return numeric > clause->numeric_data;
936 case clause_gteq:
937 return numeric >= clause->numeric_data;
938 case clause_lt:
939 return numeric < clause->numeric_data;
940 case clause_lteq:
941 return numeric <= clause->numeric_data;
942 default:
943 logf("Incorrect numeric tag: %d", clause->type);
944 }
945 }
946 else
947 {
948 switch (clause->type)
949 {
950 case clause_is:
Miika Pekkarinen4c6fd0f2006-04-03 18:57:34 +0000951 return !strcasecmp(clause->str, str);
Miika Pekkarinen3eb9e702006-10-05 09:49:18 +0000952 case clause_is_not:
953 return strcasecmp(clause->str, str);
954 case clause_gt:
955 return 0>strcasecmp(clause->str, str);
956 case clause_gteq:
957 return 0>=strcasecmp(clause->str, str);
958 case clause_lt:
959 return 0<strcasecmp(clause->str, str);
960 case clause_lteq:
961 return 0<=strcasecmp(clause->str, str);
962 case clause_contains:
963 return (strcasestr(str, clause->str) != NULL);
964 case clause_not_contains:
965 return (strcasestr(str, clause->str) == NULL);
966 case clause_begins_with:
967 return (strcasestr(str, clause->str) == str);
968 case clause_not_begins_with:
Jonathan Gordon8ca99d32007-02-27 11:09:09 +0000969 return (strcasestr(str, clause->str) != str);
Miika Pekkarinen3eb9e702006-10-05 09:49:18 +0000970 case clause_ends_with:
971 return str_ends_with(str, clause->str);
972 case clause_not_ends_with:
973 return !str_ends_with(str, clause->str);
Miika Pekkarinenbe2eb022006-10-09 10:23:35 +0000974 case clause_oneof:
975 return str_oneof(str, clause->str);
Miika Pekkarinen3eb9e702006-10-05 09:49:18 +0000976
977 default:
978 logf("Incorrect tag: %d", clause->type);
979 }
Miika Pekkarinen4c6fd0f2006-04-03 18:57:34 +0000980 }
Miika Pekkarinene5059a72006-08-13 14:53:19 +0000981
Miika Pekkarinen4c6fd0f2006-04-03 18:57:34 +0000982 return false;
983}
984
Jens Arnold2597a132006-12-25 14:01:47 +0000985static bool check_clauses(struct tagcache_search *tcs,
986 struct index_entry *idx,
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +0000987 struct tagcache_search_clause **clauses, int count)
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000988{
989 int i;
990
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +0000991 /* Go through all conditional clauses. */
992 for (i = 0; i < count; i++)
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000993 {
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +0000994 int seek;
995 char buf[256];
Michael Hohmuth6a0021c2011-06-06 22:49:07 +0000996 char *str = buf;
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +0000997 struct tagcache_search_clause *clause = clauses[i];
998
999 if (clause->type == clause_logical_or)
1000 break; /* all conditions before logical-or satisfied --
1001 stop processing clauses */
1002
Michael Hohmuth6a0021c2011-06-06 22:49:07 +00001003 seek = check_virtual_tags(clause->tag, tcs->idx_id, idx);
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +00001004
Michael Hohmuth6a0021c2011-06-06 22:49:07 +00001005#ifdef HAVE_TC_RAMCACHE
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +00001006 if (tcs->ramsearch)
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001007 {
1008 struct tagfile_entry *tfe;
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001009
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +00001010 if (!TAGCACHE_IS_NUMERIC(clause->tag))
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001011 {
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +00001012 if (clause->tag == tag_filename)
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001013 {
1014 retrieve(tcs, idx, tag_filename, buf, sizeof buf);
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001015 }
1016 else
1017 {
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +00001018 tfe = (struct tagfile_entry *)&hdr->tags[clause->tag][seek];
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001019 str = tfe->tag_data;
1020 }
1021 }
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001022 }
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +00001023 else
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001024#endif
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001025 {
1026 struct tagfile_entry tfe;
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001027
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +00001028 if (!TAGCACHE_IS_NUMERIC(clause->tag))
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001029 {
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +00001030 int fd = tcs->idxfd[clause->tag];
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001031 lseek(fd, seek, SEEK_SET);
Amaury Pouly1bff4b32010-03-16 12:15:56 +00001032 ecread_tagfile_entry(fd, &tfe);
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +00001033 if (tfe.tag_length >= (int)sizeof(buf))
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001034 {
1035 logf("Too long tag read!");
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +00001036 return false;
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001037 }
1038
1039 read(fd, str, tfe.tag_length);
Michael Hohmuth6a0021c2011-06-06 22:49:07 +00001040 str[tfe.tag_length] = '\0';
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001041
1042 /* Check if entry has been deleted. */
1043 if (str[0] == '\0')
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +00001044 return false;
1045 }
1046 }
1047
1048 if (!check_against_clause(seek, str, clause))
1049 {
1050 /* Clause failed -- try finding a logical-or clause */
1051 while (++i < count)
1052 {
1053 if (clauses[i]->type == clause_logical_or)
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001054 break;
1055 }
1056
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +00001057 if (i < count) /* Found logical-or? */
1058 continue; /* Check clauses after logical-or */
1059
1060 return false;
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001061 }
1062 }
1063
1064 return true;
1065}
1066
1067bool tagcache_check_clauses(struct tagcache_search *tcs,
1068 struct tagcache_search_clause **clause, int count)
1069{
1070 struct index_entry idx;
1071
1072 if (count == 0)
1073 return true;
1074
1075 if (!get_index(tcs->masterfd, tcs->idx_id, &idx, true))
1076 return false;
1077
1078 return check_clauses(tcs, &idx, clause, count);
1079}
1080
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +00001081static bool add_uniqbuf(struct tagcache_search *tcs, unsigned long id)
Miika Pekkarinene3080642006-08-25 13:22:46 +00001082{
1083 int i;
1084
1085 /* If uniq buffer is not defined we must return true for search to work. */
Andrew Mahone1248a0d2009-06-03 08:19:32 +00001086 if (tcs->unique_list == NULL || (!TAGCACHE_IS_UNIQUE(tcs->type)
Michael Hohmuth509d6a92011-06-06 22:48:53 +00001087 && !TAGCACHE_IS_NUMERIC(tcs->type)))
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001088 {
Miika Pekkarinene3080642006-08-25 13:22:46 +00001089 return true;
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001090 }
Miika Pekkarinene3080642006-08-25 13:22:46 +00001091
1092 for (i = 0; i < tcs->unique_list_count; i++)
1093 {
1094 /* Return false if entry is found. */
1095 if (tcs->unique_list[i] == id)
1096 return false;
1097 }
1098
1099 if (tcs->unique_list_count < tcs->unique_list_capacity)
1100 {
1101 tcs->unique_list[i] = id;
1102 tcs->unique_list_count++;
1103 }
1104
1105 return true;
1106}
1107
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001108static bool build_lookup_list(struct tagcache_search *tcs)
1109{
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001110 struct index_entry entry;
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001111 int i, j;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001112
1113 tcs->seek_list_count = 0;
1114
1115#ifdef HAVE_TC_RAMCACHE
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001116 if (tcs->ramsearch
1117# ifdef HAVE_DIRCACHE
1118 && (tcs->type != tag_filename || is_dircache_intact())
1119# endif
1120 )
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001121 {
Miika Pekkarinen8d9b9d22011-06-19 14:36:26 +00001122 for (i = tcs->seek_pos; i < current_tcmh.tch.entry_count; i++)
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001123 {
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001124 struct tagcache_seeklist_entry *seeklist;
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001125 struct index_entry *idx = &hdr->indices[i];
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001126 if (tcs->seek_list_count == SEEK_LIST_SIZE)
1127 break ;
Miika Pekkarinenf9bfd732006-04-19 18:56:59 +00001128
1129 /* Skip deleted files. */
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001130 if (idx->flag & FLAG_DELETED)
Miika Pekkarinenf9bfd732006-04-19 18:56:59 +00001131 continue;
1132
Miika Pekkarinen93ed0a72006-04-09 20:00:49 +00001133 /* Go through all filters.. */
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001134 for (j = 0; j < tcs->filter_count; j++)
1135 {
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001136 if (idx->tag_seek[tcs->filter_tag[j]] != tcs->filter_seek[j])
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001137 {
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001138 break ;
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001139 }
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001140 }
1141
1142 if (j < tcs->filter_count)
1143 continue ;
Miika Pekkarinen93ed0a72006-04-09 20:00:49 +00001144
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001145 /* Check for conditions. */
1146 if (!check_clauses(tcs, idx, tcs->clause, tcs->clause_count))
1147 continue;
Miika Pekkarinen4c6fd0f2006-04-03 18:57:34 +00001148
Miika Pekkarinene3080642006-08-25 13:22:46 +00001149 /* Add to the seek list if not already in uniq buffer. */
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001150 if (!add_uniqbuf(tcs, idx->tag_seek[tcs->type]))
Miika Pekkarinene3080642006-08-25 13:22:46 +00001151 continue;
1152
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001153 /* Lets add it. */
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001154 seeklist = &tcs->seeklist[tcs->seek_list_count];
1155 seeklist->seek = idx->tag_seek[tcs->type];
1156 seeklist->flag = idx->flag;
1157 seeklist->idx_id = i;
Miika Pekkarinene3080642006-08-25 13:22:46 +00001158 tcs->seek_list_count++;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001159 }
1160
1161 tcs->seek_pos = i;
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001162
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001163 return tcs->seek_list_count > 0;
1164 }
1165#endif
1166
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001167 if (tcs->masterfd < 0)
1168 {
1169 struct master_header tcmh;
1170 tcs->masterfd = open_master_fd(&tcmh, false);
1171 }
1172
Miika Pekkarinen93ed0a72006-04-09 20:00:49 +00001173 lseek(tcs->masterfd, tcs->seek_pos * sizeof(struct index_entry) +
Miika Pekkarinencb8c7952006-07-20 12:19:31 +00001174 sizeof(struct master_header), SEEK_SET);
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001175
Amaury Pouly1bff4b32010-03-16 12:15:56 +00001176 while (ecread_index_entry(tcs->masterfd, &entry)
Miika Pekkarinen9b9539c2007-02-13 21:51:18 +00001177 == sizeof(struct index_entry))
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001178 {
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001179 struct tagcache_seeklist_entry *seeklist;
1180
Miika Pekkarinen408dfd62007-03-11 08:52:33 +00001181 if (tcs->seek_list_count == SEEK_LIST_SIZE)
1182 break ;
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001183
1184 i = tcs->seek_pos;
Miika Pekkarinen408dfd62007-03-11 08:52:33 +00001185 tcs->seek_pos++;
1186
Miika Pekkarinen9cd5c3e2006-07-10 16:22:03 +00001187 /* Check if entry has been deleted. */
1188 if (entry.flag & FLAG_DELETED)
1189 continue;
1190
Miika Pekkarinen93ed0a72006-04-09 20:00:49 +00001191 /* Go through all filters.. */
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001192 for (j = 0; j < tcs->filter_count; j++)
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001193 {
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001194 if (entry.tag_seek[tcs->filter_tag[j]] != tcs->filter_seek[j])
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001195 break ;
1196 }
1197
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001198 if (j < tcs->filter_count)
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001199 continue ;
1200
Miika Pekkarinen4c6fd0f2006-04-03 18:57:34 +00001201 /* Check for conditions. */
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001202 if (!check_clauses(tcs, &entry, tcs->clause, tcs->clause_count))
1203 continue;
Miika Pekkarinen4c6fd0f2006-04-03 18:57:34 +00001204
Miika Pekkarinene3080642006-08-25 13:22:46 +00001205 /* Add to the seek list if not already in uniq buffer. */
1206 if (!add_uniqbuf(tcs, entry.tag_seek[tcs->type]))
1207 continue;
1208
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001209 /* Lets add it. */
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001210 seeklist = &tcs->seeklist[tcs->seek_list_count];
1211 seeklist->seek = entry.tag_seek[tcs->type];
1212 seeklist->flag = entry.flag;
1213 seeklist->idx_id = i;
Miika Pekkarinene3080642006-08-25 13:22:46 +00001214 tcs->seek_list_count++;
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001215
1216 yield();
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001217 }
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001218
1219 return tcs->seek_list_count > 0;
1220}
1221
Miika Pekkarinen58fe4de2006-04-10 10:26:24 +00001222
Miika Pekkarinencb8c7952006-07-20 12:19:31 +00001223static void remove_files(void)
1224{
1225 int i;
1226 char buf[MAX_PATH];
1227
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +00001228 tc_stat.ready = false;
1229 tc_stat.ramcache = false;
Miika Pekkarinen9b9539c2007-02-13 21:51:18 +00001230 tc_stat.econ = false;
Thomas Martitz2c241602010-12-06 22:26:31 +00001231 remove(TAGCACHE_FILE_MASTER);
Miika Pekkarinencb8c7952006-07-20 12:19:31 +00001232 for (i = 0; i < TAG_COUNT; i++)
1233 {
Andrew Mahone1248a0d2009-06-03 08:19:32 +00001234 if (TAGCACHE_IS_NUMERIC(i))
Miika Pekkarinencb8c7952006-07-20 12:19:31 +00001235 continue;
Thomas Martitz2c241602010-12-06 22:26:31 +00001236
Miika Pekkarinencb8c7952006-07-20 12:19:31 +00001237 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, i);
Thomas Martitz2c241602010-12-06 22:26:31 +00001238 remove(buf);
Miika Pekkarinencb8c7952006-07-20 12:19:31 +00001239 }
1240}
1241
Miika Pekkarinen29fa15f2006-07-23 11:15:28 +00001242
Miika Pekkarinenf5184f32007-02-25 20:41:51 +00001243static bool check_all_headers(void)
1244{
1245 struct master_header myhdr;
1246 struct tagcache_header tch;
1247 int tag;
1248 int fd;
1249
1250 if ( (fd = open_master_fd(&myhdr, false)) < 0)
1251 return false;
1252
1253 close(fd);
1254 if (myhdr.dirty)
1255 {
1256 logf("tagcache is dirty!");
1257 return false;
1258 }
1259
1260 memcpy(&current_tcmh, &myhdr, sizeof(struct master_header));
1261
1262 for (tag = 0; tag < TAG_COUNT; tag++)
1263 {
Andrew Mahone1248a0d2009-06-03 08:19:32 +00001264 if (TAGCACHE_IS_NUMERIC(tag))
Miika Pekkarinenf5184f32007-02-25 20:41:51 +00001265 continue;
1266
1267 if ( (fd = open_tag_fd(&tch, tag, false)) < 0)
1268 return false;
1269
1270 close(fd);
1271 }
1272
1273 return true;
1274}
1275
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001276bool tagcache_is_busy(void)
1277{
1278 return read_lock || write_lock;
1279}
1280
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001281bool tagcache_search(struct tagcache_search *tcs, int tag)
1282{
Miika Pekkarinencb8c7952006-07-20 12:19:31 +00001283 struct tagcache_header tag_hdr;
1284 struct master_header master_hdr;
Miika Pekkarinen53e921c2006-04-08 08:03:51 +00001285 int i;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001286
Miika Pekkarinend8ac6072006-08-02 17:39:34 +00001287 while (read_lock)
1288 sleep(1);
1289
Miika Pekkarinen4c6fd0f2006-04-03 18:57:34 +00001290 memset(tcs, 0, sizeof(struct tagcache_search));
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +00001291 if (tc_stat.commit_step > 0 || !tc_stat.ready)
Miika Pekkarinen3b313462006-04-16 17:32:54 +00001292 return false;
1293
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001294 tcs->position = sizeof(struct tagcache_header);
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001295 tcs->type = tag;
1296 tcs->seek_pos = 0;
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001297 tcs->list_position = 0;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001298 tcs->seek_list_count = 0;
1299 tcs->filter_count = 0;
Miika Pekkarinen4c6fd0f2006-04-03 18:57:34 +00001300 tcs->masterfd = -1;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001301
Miika Pekkarinen53e921c2006-04-08 08:03:51 +00001302 for (i = 0; i < TAG_COUNT; i++)
1303 tcs->idxfd[i] = -1;
1304
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001305#ifndef HAVE_TC_RAMCACHE
1306 tcs->ramsearch = false;
1307#else
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +00001308 tcs->ramsearch = tc_stat.ramcache;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001309 if (tcs->ramsearch)
1310 {
1311 tcs->entry_count = hdr->entry_count[tcs->type];
1312 }
1313 else
1314#endif
1315 {
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001316 /* Always open as R/W so we can pass tcs to functions that modify data also
1317 * without failing. */
1318 tcs->masterfd = open_master_fd(&master_hdr, true);
1319 if (tcs->masterfd < 0)
1320 return false;
1321
Andrew Mahone1248a0d2009-06-03 08:19:32 +00001322 if (!TAGCACHE_IS_NUMERIC(tcs->type))
Miika Pekkarinend8ac6072006-08-02 17:39:34 +00001323 {
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001324 tcs->idxfd[tcs->type] = open_tag_fd(&tag_hdr, tcs->type, false);
1325 if (tcs->idxfd[tcs->type] < 0)
1326 return false;
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001327
1328 tcs->entry_count = tag_hdr.entry_count;
Miika Pekkarinend8ac6072006-08-02 17:39:34 +00001329 }
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001330 else
1331 {
1332 tcs->entry_count = master_hdr.tch.entry_count;
1333 }
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001334 }
1335
Miika Pekkarinend8ac6072006-08-02 17:39:34 +00001336 tcs->valid = true;
1337 tcs->initialized = true;
1338 write_lock++;
1339
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001340 return true;
1341}
1342
Miika Pekkarinene3080642006-08-25 13:22:46 +00001343void tagcache_search_set_uniqbuf(struct tagcache_search *tcs,
1344 void *buffer, long length)
1345{
1346 tcs->unique_list = (unsigned long *)buffer;
1347 tcs->unique_list_capacity = length / sizeof(*tcs->unique_list);
1348 tcs->unique_list_count = 0;
1349}
1350
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001351bool tagcache_search_add_filter(struct tagcache_search *tcs,
1352 int tag, int seek)
1353{
1354 if (tcs->filter_count == TAGCACHE_MAX_FILTERS)
1355 return false;
1356
Andrew Mahonede7c5712009-06-06 00:00:58 +00001357 if (TAGCACHE_IS_NUMERIC_OR_NONUNIQUE(tag))
Miika Pekkarinen58fe4de2006-04-10 10:26:24 +00001358 return false;
1359
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001360 tcs->filter_tag[tcs->filter_count] = tag;
1361 tcs->filter_seek[tcs->filter_count] = seek;
1362 tcs->filter_count++;
1363
1364 return true;
1365}
1366
Miika Pekkarinen4c6fd0f2006-04-03 18:57:34 +00001367bool tagcache_search_add_clause(struct tagcache_search *tcs,
1368 struct tagcache_search_clause *clause)
1369{
Miika Pekkarinene3080642006-08-25 13:22:46 +00001370 int i;
1371
Miika Pekkarinen4c6fd0f2006-04-03 18:57:34 +00001372 if (tcs->clause_count >= TAGCACHE_MAX_CLAUSES)
1373 {
1374 logf("Too many clauses");
1375 return false;
1376 }
Miika Pekkarinen53e921c2006-04-08 08:03:51 +00001377
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +00001378 if (clause->type != clause_logical_or)
Miika Pekkarinene3080642006-08-25 13:22:46 +00001379 {
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +00001380 /* Check if there is already a similar filter in present (filters are
1381 * much faster than clauses).
1382 */
1383 for (i = 0; i < tcs->filter_count; i++)
1384 {
1385 if (tcs->filter_tag[i] == clause->tag)
1386 return true;
1387 }
1388
1389 if (!TAGCACHE_IS_NUMERIC(clause->tag) && tcs->idxfd[clause->tag] < 0)
1390 {
1391 char buf[MAX_PATH];
1392
1393 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, clause->tag);
1394 tcs->idxfd[clause->tag] = open(buf, O_RDONLY);
1395 }
Miika Pekkarinen53e921c2006-04-08 08:03:51 +00001396 }
Miika Pekkarinen4c6fd0f2006-04-03 18:57:34 +00001397
1398 tcs->clause[tcs->clause_count] = clause;
1399 tcs->clause_count++;
1400
1401 return true;
1402}
1403
Miika Pekkarinen9cd5c3e2006-07-10 16:22:03 +00001404static bool get_next(struct tagcache_search *tcs)
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001405{
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +00001406 static char buf[TAG_MAXLEN+32];
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001407 struct tagfile_entry entry;
Andree Buschmannf19d6a52011-05-03 20:28:56 +00001408#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
Miika Pekkarinen42946152006-08-30 18:18:37 +00001409 long flag = 0;
Andree Buschmannf19d6a52011-05-03 20:28:56 +00001410#endif
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001411
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +00001412 if (!tcs->valid || !tc_stat.ready)
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001413 return false;
1414
Andrew Mahone1248a0d2009-06-03 08:19:32 +00001415 if (tcs->idxfd[tcs->type] < 0 && !TAGCACHE_IS_NUMERIC(tcs->type)
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001416#ifdef HAVE_TC_RAMCACHE
1417 && !tcs->ramsearch
1418#endif
1419 )
1420 return false;
1421
1422 /* Relative fetch. */
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001423 if (tcs->filter_count > 0 || tcs->clause_count > 0
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001424 || TAGCACHE_IS_NUMERIC(tcs->type)
1425#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
1426 /* We need to retrieve flag status for dircache. */
1427 || (tcs->ramsearch && tcs->type == tag_filename)
1428#endif
1429 )
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001430 {
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001431 struct tagcache_seeklist_entry *seeklist;
1432
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001433 /* Check for end of list. */
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001434 if (tcs->list_position == tcs->seek_list_count)
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001435 {
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001436 tcs->list_position = 0;
1437
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001438 /* Try to fetch more. */
1439 if (!build_lookup_list(tcs))
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001440 {
1441 tcs->valid = false;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001442 return false;
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001443 }
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001444 }
1445
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001446 seeklist = &tcs->seeklist[tcs->list_position];
Andree Buschmannf19d6a52011-05-03 20:28:56 +00001447#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001448 flag = seeklist->flag;
Andree Buschmannf19d6a52011-05-03 20:28:56 +00001449#endif
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001450 tcs->position = seeklist->seek;
1451 tcs->idx_id = seeklist->idx_id;
1452 tcs->list_position++;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001453 }
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001454 else
1455 {
1456 if (tcs->entry_count == 0)
1457 {
1458 tcs->valid = false;
1459 return false;
1460 }
1461
1462 tcs->entry_count--;
1463 }
1464
1465 tcs->result_seek = tcs->position;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001466
Andrew Mahone1248a0d2009-06-03 08:19:32 +00001467 if (TAGCACHE_IS_NUMERIC(tcs->type))
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001468 {
Thomas Martitz50a6ca32010-05-06 21:04:40 +00001469 snprintf(buf, sizeof(buf), "%ld", tcs->position);
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001470 tcs->result = buf;
1471 tcs->result_len = strlen(buf) + 1;
1472 return true;
1473 }
1474
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001475 /* Direct fetch. */
1476#ifdef HAVE_TC_RAMCACHE
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001477 if (tcs->ramsearch)
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001478 {
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001479
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001480#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
1481 if (tcs->type == tag_filename && (flag & FLAG_DIRCACHE)
1482 && is_dircache_intact())
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001483 {
Amaury Poulyf82c0212010-02-16 22:30:55 +00001484 dircache_copy_path((struct dircache_entry *)tcs->position,
Miika Pekkarinen9cd5c3e2006-07-10 16:22:03 +00001485 buf, sizeof buf);
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001486 tcs->result = buf;
1487 tcs->result_len = strlen(buf) + 1;
Miika Pekkarinen42946152006-08-30 18:18:37 +00001488 tcs->ramresult = false;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001489
1490 return true;
1491 }
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001492 else
1493#endif
1494 if (tcs->type != tag_filename)
1495 {
1496 struct tagfile_entry *ep;
1497
1498 ep = (struct tagfile_entry *)&hdr->tags[tcs->type][tcs->position];
1499 tcs->result = ep->tag_data;
1500 tcs->result_len = strlen(tcs->result) + 1;
1501 tcs->idx_id = ep->idx_id;
1502 tcs->ramresult = true;
1503
1504 /* Increase position for the next run. This may get overwritten. */
1505 tcs->position += sizeof(struct tagfile_entry) + ep->tag_length;
1506
1507 return true;
1508 }
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001509 }
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001510#endif
Miika Pekkarinen5c241312009-06-19 14:30:59 +00001511
1512 if (!open_files(tcs, tcs->type))
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001513 {
1514 tcs->valid = false;
Miika Pekkarinen5c241312009-06-19 14:30:59 +00001515 return false;
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001516 }
Miika Pekkarinen5c241312009-06-19 14:30:59 +00001517
1518 /* Seek stream to the correct position and continue to direct fetch. */
1519 lseek(tcs->idxfd[tcs->type], tcs->position, SEEK_SET);
Miika Pekkarinen5c241312009-06-19 14:30:59 +00001520
Amaury Pouly1bff4b32010-03-16 12:15:56 +00001521 if (ecread_tagfile_entry(tcs->idxfd[tcs->type], &entry) != sizeof(struct tagfile_entry))
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001522 {
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001523 logf("read error #5");
Miika Pekkarinen5c241312009-06-19 14:30:59 +00001524 tcs->valid = false;
1525 return false;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001526 }
1527
1528 if (entry.tag_length > (long)sizeof(buf))
1529 {
1530 tcs->valid = false;
Miika Pekkarinen9cd5c3e2006-07-10 16:22:03 +00001531 logf("too long tag #2");
Amaury Pouly7430bb02010-03-16 12:15:44 +00001532 logf("P:%lX/%lX", tcs->position, entry.tag_length);
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001533 return false;
1534 }
1535
Miika Pekkarinen9cd5c3e2006-07-10 16:22:03 +00001536 if (read(tcs->idxfd[tcs->type], buf, entry.tag_length) != entry.tag_length)
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001537 {
1538 tcs->valid = false;
Miika Pekkarinen36e30812006-09-26 11:23:18 +00001539 logf("read error #4");
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001540 return false;
1541 }
1542
Miika Pekkarinen5c241312009-06-19 14:30:59 +00001543 /**
1544 Update the position for the next read (this may be overridden
1545 if filters or clauses are being used).
1546 */
1547 tcs->position += sizeof(struct tagfile_entry) + entry.tag_length;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001548 tcs->result = buf;
Miika Pekkarinen9cd5c3e2006-07-10 16:22:03 +00001549 tcs->result_len = strlen(tcs->result) + 1;
Miika Pekkarinen44b76bc2006-03-30 12:07:32 +00001550 tcs->idx_id = entry.idx_id;
Miika Pekkarinen42946152006-08-30 18:18:37 +00001551 tcs->ramresult = false;
Miika Pekkarinen58fe4de2006-04-10 10:26:24 +00001552
1553 return true;
1554}
1555
Miika Pekkarinen9cd5c3e2006-07-10 16:22:03 +00001556bool tagcache_get_next(struct tagcache_search *tcs)
1557{
1558 while (get_next(tcs))
1559 {
1560 if (tcs->result_len > 1)
1561 return true;
1562 }
1563
1564 return false;
1565}
1566
Miika Pekkarinen58fe4de2006-04-10 10:26:24 +00001567bool tagcache_retrieve(struct tagcache_search *tcs, int idxid,
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001568 int tag, char *buf, long size)
Miika Pekkarinen58fe4de2006-04-10 10:26:24 +00001569{
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +00001570 struct index_entry idx;
Miika Pekkarinen58fe4de2006-04-10 10:26:24 +00001571
Miika Pekkarinen6ee82e72006-09-23 10:29:14 +00001572 *buf = '\0';
Miika Pekkarinendafa0d42006-07-24 15:35:46 +00001573 if (!get_index(tcs->masterfd, idxid, &idx, true))
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +00001574 return false;
1575
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001576 return retrieve(tcs, &idx, tag, buf, size);
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001577}
1578
Miika Pekkarinenf5184f32007-02-25 20:41:51 +00001579static bool update_master_header(void)
1580{
1581 struct master_header myhdr;
1582 int fd;
1583
1584 if (!tc_stat.ready)
1585 return false;
1586
1587 if ( (fd = open_master_fd(&myhdr, true)) < 0)
1588 return false;
1589
1590 myhdr.serial = current_tcmh.serial;
1591 myhdr.commitid = current_tcmh.commitid;
Miika Pekkarinena1614502009-04-15 06:46:25 +00001592 myhdr.dirty = current_tcmh.dirty;
Miika Pekkarinenf5184f32007-02-25 20:41:51 +00001593
1594 /* Write it back */
1595 lseek(fd, 0, SEEK_SET);
1596 ecwrite(fd, &myhdr, 1, master_header_ec, tc_stat.econ);
1597 close(fd);
1598
Miika Pekkarinenf5184f32007-02-25 20:41:51 +00001599 return true;
1600}
1601
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001602#if 0
Miika Pekkarinenfa893c62006-04-18 18:56:56 +00001603
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001604void tagcache_modify(struct tagcache_search *tcs, int type, const char *text)
1605{
1606 struct tagentry *entry;
1607
1608 if (tcs->type != tag_title)
1609 return ;
1610
1611 /* We will need reserve buffer for this. */
1612 if (tcs->ramcache)
1613 {
1614 struct tagfile_entry *ep;
1615
1616 ep = (struct tagfile_entry *)&hdr->tags[tcs->type][tcs->result_seek];
1617 tcs->seek_list[tcs->seek_list_count];
1618 }
1619
Miika Pekkarinen9cd5c3e2006-07-10 16:22:03 +00001620 entry = find_entry_ram();
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001621
1622}
1623#endif
1624
1625void tagcache_search_finish(struct tagcache_search *tcs)
1626{
Miika Pekkarinen53e921c2006-04-08 08:03:51 +00001627 int i;
1628
Miika Pekkarinend8ac6072006-08-02 17:39:34 +00001629 if (!tcs->initialized)
1630 return;
1631
Miika Pekkarinen4c6fd0f2006-04-03 18:57:34 +00001632 if (tcs->masterfd >= 0)
1633 {
1634 close(tcs->masterfd);
1635 tcs->masterfd = -1;
1636 }
Miika Pekkarinen53e921c2006-04-08 08:03:51 +00001637
1638 for (i = 0; i < TAG_COUNT; i++)
1639 {
1640 if (tcs->idxfd[i] >= 0)
1641 {
1642 close(tcs->idxfd[i]);
1643 tcs->idxfd[i] = -1;
1644 }
1645 }
Miika Pekkarinen4c6fd0f2006-04-03 18:57:34 +00001646
1647 tcs->ramsearch = false;
1648 tcs->valid = false;
Miika Pekkarinend8ac6072006-08-02 17:39:34 +00001649 tcs->initialized = 0;
1650 if (write_lock > 0)
1651 write_lock--;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001652}
1653
Miika Pekkarinen9cd5c3e2006-07-10 16:22:03 +00001654#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
Miika Pekkarinen70c07912006-04-03 06:00:56 +00001655static struct tagfile_entry *get_tag(const struct index_entry *entry, int tag)
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001656{
1657 return (struct tagfile_entry *)&hdr->tags[tag][entry->tag_seek[tag]];
1658}
1659
Jonathan Gordon893d2f32010-11-23 00:15:13 +00001660static long get_tag_numeric(const struct index_entry *entry, int tag, int idx_id)
Miika Pekkarinen70c07912006-04-03 06:00:56 +00001661{
Jonathan Gordon893d2f32010-11-23 00:15:13 +00001662 return check_virtual_tags(tag, idx_id, entry);
Miika Pekkarinen70c07912006-04-03 06:00:56 +00001663}
Miika Pekkarinen9cd5c3e2006-07-10 16:22:03 +00001664
Magnus Holmgren74e572c2007-03-04 19:54:49 +00001665static char* get_tag_string(const struct index_entry *entry, int tag)
1666{
1667 char* s = get_tag(entry, tag)->tag_data;
1668 return strcmp(s, UNTAGGED) ? s : NULL;
1669}
1670
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001671bool tagcache_fill_tags(struct mp3entry *id3, const char *filename)
1672{
1673 struct index_entry *entry;
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +00001674 int idx_id;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001675
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001676 if (!tc_stat.ready || !tc_stat.ramcache)
Miika Pekkarinen29fa15f2006-07-23 11:15:28 +00001677 return false;
1678
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001679 /* Find the corresponding entry in tagcache. */
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +00001680 idx_id = find_entry_ram(filename, NULL);
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001681 if (idx_id < 0)
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001682 return false;
1683
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +00001684 entry = &hdr->indices[idx_id];
1685
Jonathan Gordon49f27092010-05-06 09:22:55 +00001686 memset(id3, 0, sizeof(struct mp3entry));
1687
Magnus Holmgren74e572c2007-03-04 19:54:49 +00001688 id3->title = get_tag_string(entry, tag_title);
1689 id3->artist = get_tag_string(entry, tag_artist);
1690 id3->album = get_tag_string(entry, tag_album);
1691 id3->genre_string = get_tag_string(entry, tag_genre);
1692 id3->composer = get_tag_string(entry, tag_composer);
1693 id3->comment = get_tag_string(entry, tag_comment);
1694 id3->albumartist = get_tag_string(entry, tag_albumartist);
Dan Evertoneecfe9f2007-08-08 10:19:56 +00001695 id3->grouping = get_tag_string(entry, tag_grouping);
Robert Kukla28530bc2007-03-04 17:30:12 +00001696
Jonathan Gordon893d2f32010-11-23 00:15:13 +00001697 id3->length = get_tag_numeric(entry, tag_length, idx_id);
1698 id3->playcount = get_tag_numeric(entry, tag_playcount, idx_id);
1699 id3->rating = get_tag_numeric(entry, tag_rating, idx_id);
1700 id3->lastplayed = get_tag_numeric(entry, tag_lastplayed, idx_id);
1701 id3->score = get_tag_numeric(entry, tag_virt_autoscore, idx_id) / 10;
1702 id3->year = get_tag_numeric(entry, tag_year, idx_id);
Robert Kukla28530bc2007-03-04 17:30:12 +00001703
Jonathan Gordon893d2f32010-11-23 00:15:13 +00001704 id3->discnum = get_tag_numeric(entry, tag_discnumber, idx_id);
1705 id3->tracknum = get_tag_numeric(entry, tag_tracknumber, idx_id);
1706 id3->bitrate = get_tag_numeric(entry, tag_bitrate, idx_id);
Miika Pekkarinen70c07912006-04-03 06:00:56 +00001707 if (id3->bitrate == 0)
1708 id3->bitrate = 1;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001709
Michael Giacomellicacc64a2011-01-02 03:48:40 +00001710#if CONFIG_CODEC == SWCODEC
Michael Giacomelli66e8fc02011-01-02 02:49:13 +00001711 if (global_settings.autoresume_enable)
1712 {
1713 id3->offset = get_tag_numeric(entry, tag_lastoffset, idx_id);
1714 logf("tagcache_fill_tags: Set offset for %s to %lX\n",
1715 id3->title, id3->offset);
1716 }
Michael Giacomellicacc64a2011-01-02 03:48:40 +00001717#endif
Michael Giacomelli66e8fc02011-01-02 02:49:13 +00001718
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001719 return true;
1720}
1721#endif
1722
1723static inline void write_item(const char *item)
1724{
1725 int len = strlen(item) + 1;
Peter D'Hoyeb4f80fb2008-01-25 21:21:41 +00001726
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001727 data_size += len;
1728 write(cachefd, item, len);
1729}
1730
Miika Pekkarinen812cbad2006-10-04 09:05:01 +00001731static int check_if_empty(char **tag)
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001732{
Miika Pekkarinen812cbad2006-10-04 09:05:01 +00001733 int length;
Peter D'Hoyeb4f80fb2008-01-25 21:21:41 +00001734
1735 if (*tag == NULL || **tag == '\0')
Miika Pekkarinen812cbad2006-10-04 09:05:01 +00001736 {
Robert Kukla28530bc2007-03-04 17:30:12 +00001737 *tag = UNTAGGED;
Linus Nielsen Feltzing41621862007-03-04 19:30:03 +00001738 return sizeof(UNTAGGED); /* Tag length */
Miika Pekkarinen812cbad2006-10-04 09:05:01 +00001739 }
Peter D'Hoyeb4f80fb2008-01-25 21:21:41 +00001740
Miika Pekkarinen812cbad2006-10-04 09:05:01 +00001741 length = strlen(*tag);
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +00001742 if (length > TAG_MAXLEN)
Miika Pekkarinen812cbad2006-10-04 09:05:01 +00001743 {
1744 logf("over length tag: %s", *tag);
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +00001745 length = TAG_MAXLEN;
Peter D'Hoyeb4f80fb2008-01-25 21:21:41 +00001746 (*tag)[length] = '\0';
Miika Pekkarinen812cbad2006-10-04 09:05:01 +00001747 }
Peter D'Hoyeb4f80fb2008-01-25 21:21:41 +00001748
Miika Pekkarinen812cbad2006-10-04 09:05:01 +00001749 return length + 1;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001750}
1751
Miika Pekkarinen812cbad2006-10-04 09:05:01 +00001752#define ADD_TAG(entry,tag,data) \
1753 /* Adding tag */ \
1754 entry.tag_offset[tag] = offset; \
1755 entry.tag_length[tag] = check_if_empty(data); \
1756 offset += entry.tag_length[tag]
Magnus Holmgrena538b892009-06-30 21:32:38 +00001757/* GCC 3.4.6 for Coldfire can choose to inline this function. Not a good
1758 * idea, as it uses lots of stack and is called from a recursive function
1759 * (check_dir).
1760 */
1761static void __attribute__ ((noinline)) add_tagcache(char *path,
1762 unsigned long mtime
Miika Pekkarinen9cd5c3e2006-07-10 16:22:03 +00001763#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
Amaury Poulyf82c0212010-02-16 22:30:55 +00001764 ,const struct dircache_entry *dc
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001765#endif
Magnus Holmgrena538b892009-06-30 21:32:38 +00001766 )
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001767{
Nicolas Pennequin67f50822007-10-01 20:06:21 +00001768 struct mp3entry id3;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001769 struct temp_file_entry entry;
1770 bool ret;
1771 int fd;
Miika Pekkarinenf6039462007-12-16 21:10:26 +00001772 int idx_id = -1;
Miika Pekkarinen44b76bc2006-03-30 12:07:32 +00001773 char tracknumfix[3];
Miika Pekkarinen812cbad2006-10-04 09:05:01 +00001774 int offset = 0;
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +00001775 int path_length = strlen(path);
Dan Everton00dd1492007-06-25 11:33:21 +00001776 bool has_albumartist;
Dan Evertoneecfe9f2007-08-08 10:19:56 +00001777 bool has_grouping;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001778
Jonas Häggqvist54929e82009-07-04 19:28:47 +00001779#ifdef SIMULATOR
1780 /* Crude logging for the sim - to aid in debugging */
1781 int logfd = open(ROCKBOX_DIR "/database.log",
Thomas Martitz0a1d7c22010-05-06 17:35:13 +00001782 O_WRONLY | O_APPEND | O_CREAT, 0666);
Jonas Häggqvist54929e82009-07-04 19:28:47 +00001783 if (logfd >= 0) {
1784 write(logfd, path, strlen(path));
1785 write(logfd, "\n", 1);
1786 close(logfd);
1787 }
1788#endif
1789
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001790 if (cachefd < 0)
1791 return ;
1792
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +00001793 /* Check for overlength file path. */
1794 if (path_length > TAG_MAXLEN)
1795 {
1796 /* Path can't be shortened. */
Magnus Holmgren01a010f2007-03-18 09:50:53 +00001797 logf("Too long path: %s", path);
Miika Pekkarinen0dd7ea22006-11-10 08:03:33 +00001798 return ;
1799 }
1800
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001801 /* Check if the file is supported. */
1802 if (probe_file_format(path) == AFMT_UNKNOWN)
1803 return ;
1804
1805 /* Check if the file is already cached. */
Miika Pekkarinen9cd5c3e2006-07-10 16:22:03 +00001806#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
Miika Pekkarinen39c597b2008-01-13 19:13:37 +00001807 if (tc_stat.ramcache && is_dircache_intact())
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001808 {
Miika Pekkarinenf6039462007-12-16 21:10:26 +00001809 idx_id = find_entry_ram(path, dc);
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001810 }
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001811#endif
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001812
1813 /* Be sure the entry doesn't exist. */
1814 if (filenametag_fd >= 0 && idx_id < 0)
1815 idx_id = find_entry_disk(path, false);
Miika Pekkarinenf6039462007-12-16 21:10:26 +00001816
1817 /* Check if file has been modified. */
1818 if (idx_id >= 0)
1819 {
1820 struct index_entry idx;
1821
Miika Pekkarinend8bb6702008-08-29 21:14:58 +00001822 /* TODO: Mark that the index exists (for fast reverse scan) */
1823 //found_idx[idx_id/8] |= idx_id%8;
1824
Miika Pekkarinenf6039462007-12-16 21:10:26 +00001825 if (!get_index(-1, idx_id, &idx, true))
1826 {
1827 logf("failed to retrieve index entry");
1828 return ;
1829 }
1830
1831 if ((unsigned long)idx.tag_seek[tag_mtime] == mtime)
1832 {
1833 /* No changes to file. */
1834 return ;
1835 }
1836
1837 /* Metadata might have been changed. Delete the entry. */
1838 logf("Re-adding: %s", path);
1839 if (!delete_entry(idx_id))
1840 {
1841 logf("delete_entry failed: %d", idx_id);
1842 return ;
Miika Pekkarinen29fa15f2006-07-23 11:15:28 +00001843 }
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001844 }
1845
1846 fd = open(path, O_RDONLY);
1847 if (fd < 0)
1848 {
1849 logf("open fail: %s", path);
1850 return ;
1851 }
1852
Nicolas Pennequin67f50822007-10-01 20:06:21 +00001853 memset(&id3, 0, sizeof(struct mp3entry));
Miika Pekkarinen44b76bc2006-03-30 12:07:32 +00001854 memset(&entry, 0, sizeof(struct temp_file_entry));
1855 memset(&tracknumfix, 0, sizeof(tracknumfix));
Nicolas Pennequin67f50822007-10-01 20:06:21 +00001856 ret = get_metadata(&id3, fd, path);
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001857 close(fd);
1858
1859 if (!ret)
Miika Pekkarinen3f3aeb02006-03-27 10:46:09 +00001860 return ;
1861
Miika Pekkarinen6ee82e72006-09-23 10:29:14 +00001862 logf("-> %s", path);
Miika Pekkarinen9c0b54a2006-07-24 12:10:50 +00001863
Michael Hohmuthf9f96322011-06-06 23:03:31 +00001864 if (id3.tracknum <= 0) /* Track number missing? */