blob: e56c805fa384347c306a4821dd5fdfb62d92bb95 [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
Michael Sevakisffa86262013-07-12 12:03:20 -040022/**
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +000023 * Basic structure on this file was copied from dbtree.c and modified to
24 * support the tag cache interface.
25 */
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +000026
Jeffrey Goode9d842682009-11-03 16:25:03 +000027/*#define LOGF_ENABLE*/
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +000028
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +000029#include <stdio.h>
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +000030#include <stdlib.h>
Thomas Martitz50a6ca32010-05-06 21:04:40 +000031#include "string-extra.h"
Jonathan Gordon36a2e302007-04-18 13:03:01 +000032#include "config.h"
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +000033#include "system.h"
34#include "kernel.h"
35#include "splash.h"
36#include "icons.h"
37#include "tree.h"
Linus Nielsen Feltzing224c0a12006-08-15 12:27:07 +000038#include "action.h"
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +000039#include "settings.h"
40#include "tagcache.h"
41#include "tagtree.h"
42#include "lang.h"
43#include "logf.h"
44#include "playlist.h"
45#include "keyboard.h"
46#include "gui/list.h"
Thomas Martitzd0b72e22011-08-30 14:01:33 +000047#include "core_alloc.h"
Miika Pekkarinen29b91462006-08-09 07:28:48 +000048#include "yesno.h"
Miika Pekkarinena1ac7432006-10-21 20:37:33 +000049#include "misc.h"
Jonathan Gordon36a2e302007-04-18 13:03:01 +000050#include "filetypes.h"
Jonathan Gordon1f415e22007-10-29 12:15:47 +000051#include "audio.h"
Jonathan Gordon71898e52008-10-16 10:38:03 +000052#include "appevents.h"
Frank Gevaerts2f8a0082008-11-01 16:14:28 +000053#include "storage.h"
Thomas Martitz6eaab4d2010-09-01 21:29:34 +000054#include "dir.h"
Michael Sevakis65109732011-02-23 14:31:13 +000055#include "playback.h"
Thomas Martitz84c7d612014-01-15 23:18:35 +010056#include "strnatcmp.h"
Thomas Martitzbaa070c2011-08-30 14:01:45 +000057#include "panic.h"
Miika Pekkarinen4a63c092006-04-04 19:28:13 +000058
Michael Giacomelli66e8fc02011-01-02 02:49:13 +000059#define str_or_empty(x) (x ? x : "(NULL)")
60
Miika Pekkarinen4a63c092006-04-04 19:28:13 +000061#define FILE_SEARCH_INSTRUCTIONS ROCKBOX_DIR "/tagnavi.config"
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +000062
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +000063static int tagtree_play_folder(struct tree_context* c);
Miika Pekkarinen4a63c092006-04-04 19:28:13 +000064
Thomas Martitzbaa070c2011-08-30 14:01:45 +000065/* this needs to be same size as struct entry (tree.h) and name needs to be
66 * the first; so that they're compatible enough to walk arrays of both
67 * derefencing the name member*/
68struct tagentry {
69 char* name;
70 int newtable;
71 int extraseek;
72};
73
74static struct tagentry* tagtree_get_entry(struct tree_context *c, int id);
75
Robert Kuklac4366572007-10-30 10:02:57 +000076#define SEARCHSTR_SIZE 256
77
Thomas Martitzfc502e02009-05-30 16:10:34 +000078enum table {
79 ROOT = 1,
80 NAVIBROWSE,
81 ALLSUBENTRIES,
82 PLAYTRACK,
83};
84
Jonathan Gordon75eff7a2007-10-29 14:10:24 +000085static const struct id3_to_search_mapping {
86 char *string;
87 size_t id3_offset;
88} id3_to_search_mapping[] = {
Robert Kuklac4366572007-10-30 10:02:57 +000089 { "", 0 }, /* offset n/a */
90 { "#directory#", 0 }, /* offset n/a */
Jonathan Gordon75eff7a2007-10-29 14:10:24 +000091 { "#title#", offsetof(struct mp3entry, title) },
92 { "#artist#", offsetof(struct mp3entry, artist) },
93 { "#album#", offsetof(struct mp3entry, album) },
94 { "#genre#", offsetof(struct mp3entry, genre_string) },
95 { "#composer#", offsetof(struct mp3entry, composer) },
96 { "#albumartist#", offsetof(struct mp3entry, albumartist) },
97};
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +000098enum variables {
99 var_sorttype = 100,
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000100 var_limit,
Miika Pekkarinen8d145f62006-09-26 18:59:16 +0000101 var_strip,
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000102 var_menu_start,
103 var_include,
104 var_rootmenu,
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000105 var_format,
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000106 menu_next,
107 menu_load,
William Wilgus501404d2019-07-29 20:49:41 -0500108 menu_reload,
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +0000109};
110
Miika Pekkarinene3080642006-08-25 13:22:46 +0000111/* Capacity 10 000 entries (for example 10k different artists) */
112#define UNIQBUF_SIZE (64*1024)
Thomas Martitzbaa070c2011-08-30 14:01:45 +0000113static long uniqbuf[UNIQBUF_SIZE / sizeof(long)];
Miika Pekkarinene3080642006-08-25 13:22:46 +0000114
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000115#define MAX_TAGS 5
Miika Pekkarinen6ecb06f2007-06-22 12:48:06 +0000116#define MAX_MENU_ID_SIZE 32
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000117
William Wilgus501404d2019-07-29 20:49:41 -0500118#define RELOAD_TAGTREE (-1024)
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +0000119static bool sort_inverse;
120
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000121/*
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +0000122 * "%3d. %s" autoscore title %sort = "inverse" %limit = "100"
Michael Sevakisffa86262013-07-12 12:03:20 -0400123 *
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000124 * valid = true
125 * formatstr = "%-3d. %s"
126 * tags[0] = tag_autoscore
127 * tags[1] = tag_title
128 * tag_count = 2
Michael Sevakisffa86262013-07-12 12:03:20 -0400129 *
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +0000130 * limit = 100
131 * sort_inverse = true
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000132 */
133struct display_format {
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000134 char name[32];
135 struct tagcache_search_clause *clause[TAGCACHE_MAX_CLAUSES];
136 int clause_count;
137 char *formatstr;
138 int group_id;
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000139 int tags[MAX_TAGS];
140 int tag_count;
Michael Sevakisffa86262013-07-12 12:03:20 -0400141
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +0000142 int limit;
Miika Pekkarinen8d145f62006-09-26 18:59:16 +0000143 int strip;
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +0000144 bool sort_inverse;
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000145};
146
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000147static struct display_format *formats[TAGMENU_MAX_FMTS];
148static int format_count;
149
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000150struct menu_entry {
151 char name[64];
152 int type;
Thomas Martitz19d08c22011-06-20 19:32:52 +0000153 struct search_instruction {
154 char name[64];
155 int tagorder[MAX_TAGS];
156 int tagorder_count;
157 struct tagcache_search_clause *clause[MAX_TAGS][TAGCACHE_MAX_CLAUSES];
158 int format_id[MAX_TAGS];
159 int clause_count[MAX_TAGS];
160 int result_seek[MAX_TAGS];
161 } si;
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000162 int link;
163};
164
Thomas Martitz38d51752009-05-30 16:35:28 +0000165struct menu_root {
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000166 char title[64];
Miika Pekkarinen6ecb06f2007-06-22 12:48:06 +0000167 char id[MAX_MENU_ID_SIZE];
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000168 int itemcount;
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000169 struct menu_entry *items[TAGMENU_MAX_ITEMS];
170};
171
Nils Wallméniusf42a3052011-05-31 19:44:21 +0000172struct match
173{
174 const char* str;
175 int symbol;
176};
177
Miika Pekkarinen6e8ab3d2006-09-25 19:52:38 +0000178/* Statusbar text of the current view. */
179static char current_title[MAX_TAGS][128];
180
Thomas Martitzbaa070c2011-08-30 14:01:45 +0000181static struct menu_root * menus[TAGMENU_MAX_MENUS];
182static struct menu_root * menu;
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000183static struct search_instruction *csi;
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000184static const char *strp;
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000185static int menu_count;
Thomas Martitz38d51752009-05-30 16:35:28 +0000186static int rootmenu;
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000187
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +0000188static int current_offset;
189static int current_entry_count;
190
Miika Pekkarinen4e6c79b2006-07-25 07:41:00 +0000191static struct tree_context *tc;
192
Thomas Martitz316f9a02011-06-20 19:32:56 +0000193/* a few memory alloc helper */
Thomas Martitzbaa070c2011-08-30 14:01:45 +0000194static int tagtree_handle, lock_count;
Thomas Martitzd0b72e22011-08-30 14:01:33 +0000195static size_t tagtree_bufsize, tagtree_buf_used;
Thomas Martitzbaa070c2011-08-30 14:01:45 +0000196
197#define UPDATE(x, y) { x = (typeof(x))((char*)(x) + (y)); }
198static int move_callback(int handle, void* current, void* new)
199{
200 (void)handle; (void)current; (void)new;
201 ptrdiff_t diff = new - current;
202
203 if (lock_count > 0)
204 return BUFLIB_CB_CANNOT_MOVE;
205
Michael Sevakis6436c6e2017-02-10 20:10:14 -0500206 if (menu)
207 UPDATE(menu, diff);
208
209 if (csi)
210 UPDATE(csi, diff);
211
Thomas Martitzbaa070c2011-08-30 14:01:45 +0000212 /* loop over menus */
213 for(int i = 0; i < menu_count; i++)
214 {
215 struct menu_root* menu = menus[i];
216 /* then over the menu_entries of a menu */
217 for(int j = 0; j < menu->itemcount; j++)
218 {
219 struct menu_entry* mentry = menu->items[j];
220 /* then over the search_instructions of each menu_entry */
221 for(int k = 0; k < mentry->si.tagorder_count; k++)
222 {
223 for(int l = 0; l < mentry->si.clause_count[k]; l++)
224 {
225 UPDATE(mentry->si.clause[k][l]->str, diff);
226 UPDATE(mentry->si.clause[k][l], diff);
227 }
228 }
229 UPDATE(menu->items[j], diff);
230 }
231 UPDATE(menus[i], diff);
232 }
233
234 /* now the same game for formats */
235 for(int i = 0; i < format_count; i++)
236 {
237 for(int j = 0; j < formats[i]->clause_count; j++)
238 {
239 UPDATE(formats[i]->clause[j]->str, diff);
240 UPDATE(formats[i]->clause[j], diff);
241 }
242
243 if (formats[i]->formatstr)
244 UPDATE(formats[i]->formatstr, diff);
245
246 UPDATE(formats[i], diff);
247 }
248 return BUFLIB_CB_OK;
249}
250#undef UPDATE
251
252static inline void tagtree_lock(void)
253{
254 lock_count++;
255}
256
257static inline void tagtree_unlock(void)
258{
259 lock_count--;
260}
261
262static struct buflib_callbacks ops = {
263 .move_callback = move_callback,
264 .shrink_callback = NULL,
265};
266
Thomas Martitz316f9a02011-06-20 19:32:56 +0000267static void* tagtree_alloc(size_t size)
268{
Thomas Martitzd0b72e22011-08-30 14:01:33 +0000269 size = ALIGN_UP(size, sizeof(void*));
William Wilgus501404d2019-07-29 20:49:41 -0500270 if (size > (tagtree_bufsize - tagtree_buf_used))
271 return NULL;
272
273 char* buf = core_get_data(tagtree_handle) + tagtree_buf_used;
274
Thomas Martitzd0b72e22011-08-30 14:01:33 +0000275 tagtree_buf_used += size;
276 return buf;
Thomas Martitz316f9a02011-06-20 19:32:56 +0000277}
278
279static void* tagtree_alloc0(size_t size)
280{
281 void* ret = tagtree_alloc(size);
William Wilgus501404d2019-07-29 20:49:41 -0500282 if (ret)
283 memset(ret, 0, size);
Thomas Martitz316f9a02011-06-20 19:32:56 +0000284 return ret;
285}
286
287static char* tagtree_strdup(const char* buf)
288{
289 size_t len = strlen(buf) + 1;
290 char* dest = tagtree_alloc(len);
William Wilgus501404d2019-07-29 20:49:41 -0500291 if (dest)
292 strcpy(dest, buf);
Thomas Martitz316f9a02011-06-20 19:32:56 +0000293 return dest;
294}
295
Thomas Martitzbaa070c2011-08-30 14:01:45 +0000296/* save to call without locking */
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000297static int get_token_str(char *buf, int size)
298{
299 /* Find the start. */
300 while (*strp != '"' && *strp != '\0')
301 strp++;
302
303 if (*strp == '\0' || *(++strp) == '\0')
304 return -1;
Michael Sevakisffa86262013-07-12 12:03:20 -0400305
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000306 /* Read the data. */
307 while (*strp != '"' && *strp != '\0' && --size > 0)
308 *(buf++) = *(strp++);
Michael Sevakisffa86262013-07-12 12:03:20 -0400309
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000310 *buf = '\0';
311 if (*strp != '"')
312 return -2;
Michael Sevakisffa86262013-07-12 12:03:20 -0400313
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000314 strp++;
Michael Sevakisffa86262013-07-12 12:03:20 -0400315
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000316 return 0;
317}
318
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000319static int get_tag(int *tag)
320{
Nils Wallméniusf42a3052011-05-31 19:44:21 +0000321 static const struct match get_tag_match[] =
Thomas Martitz1b7ff722011-07-18 18:57:50 +0000322 {
323 {"album", tag_album},
324 {"artist", tag_artist},
325 {"bitrate", tag_bitrate},
326 {"composer", tag_composer},
327 {"comment", tag_comment},
328 {"albumartist", tag_albumartist},
329 {"ensemble", tag_albumartist},
330 {"grouping", tag_grouping},
331 {"genre", tag_genre},
332 {"length", tag_length},
333 {"Lm", tag_virt_length_min},
334 {"Ls", tag_virt_length_sec},
335 {"Pm", tag_virt_playtime_min},
336 {"Ps", tag_virt_playtime_sec},
337 {"title", tag_title},
338 {"filename", tag_filename},
Michael Hohmutheab7e742011-07-31 16:26:35 +0000339 {"basename", tag_virt_basename},
Thomas Martitz1b7ff722011-07-18 18:57:50 +0000340 {"tracknum", tag_tracknumber},
341 {"discnum", tag_discnumber},
342 {"year", tag_year},
343 {"playcount", tag_playcount},
344 {"rating", tag_rating},
345 {"lastplayed", tag_lastplayed},
Michael Sevakis31b71222013-07-14 07:59:39 -0400346 {"lastelapsed", tag_lastelapsed},
Thomas Martitz1b7ff722011-07-18 18:57:50 +0000347 {"lastoffset", tag_lastoffset},
348 {"commitid", tag_commitid},
349 {"entryage", tag_virt_entryage},
350 {"autoscore", tag_virt_autoscore},
351 {"%sort", var_sorttype},
352 {"%limit", var_limit},
353 {"%strip", var_strip},
354 {"%menu_start", var_menu_start},
355 {"%include", var_include},
356 {"%root_menu", var_rootmenu},
357 {"%format", var_format},
358 {"->", menu_next},
William Wilgus501404d2019-07-29 20:49:41 -0500359 {"==>", menu_load},
360 {"%reload", menu_reload}
Nils Wallméniusf42a3052011-05-31 19:44:21 +0000361 };
Miika Pekkarinen3b7111e2006-10-10 10:29:42 +0000362 char buf[128];
Nils Wallméniusf42a3052011-05-31 19:44:21 +0000363 unsigned int i;
Michael Sevakisffa86262013-07-12 12:03:20 -0400364
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000365 /* Find the start. */
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000366 while ((*strp == ' ' || *strp == '>') && *strp != '\0')
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000367 strp++;
Michael Sevakisffa86262013-07-12 12:03:20 -0400368
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000369 if (*strp == '\0' || *strp == '?')
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000370 return 0;
Nils Wallméniusf42a3052011-05-31 19:44:21 +0000371
372 for (i = 0; i < sizeof(buf)-1; i++)
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000373 {
374 if (*strp == '\0' || *strp == ' ')
375 break ;
376 buf[i] = *strp;
377 strp++;
378 }
379 buf[i] = '\0';
Nils Wallméniusf42a3052011-05-31 19:44:21 +0000380
Thomas Martitz1b7ff722011-07-18 18:57:50 +0000381 for (i = 0; i < ARRAYLEN(get_tag_match); i++)
Nils Wallméniusf42a3052011-05-31 19:44:21 +0000382 {
Thomas Martitz1b7ff722011-07-18 18:57:50 +0000383 if (!strcasecmp(buf, get_tag_match[i].str))
384 {
385 *tag = get_tag_match[i].symbol;
386 return 1;
387 }
Nils Wallméniusf42a3052011-05-31 19:44:21 +0000388 }
Thomas Martitz1b7ff722011-07-18 18:57:50 +0000389
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000390 logf("NO MATCH: %s\n", buf);
391 if (buf[0] == '?')
392 return 0;
Nils Wallméniusf42a3052011-05-31 19:44:21 +0000393
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000394 return -1;
395}
396
397static int get_clause(int *condition)
398{
Nils Wallméniusf42a3052011-05-31 19:44:21 +0000399 static const struct match get_clause_match[] =
Thomas Martitz1b7ff722011-07-18 18:57:50 +0000400 {
401 {"=", clause_is},
402 {"==", clause_is},
403 {"!=", clause_is_not},
404 {">", clause_gt},
405 {">=", clause_gteq},
406 {"<", clause_lt},
407 {"<=", clause_lteq},
408 {"~", clause_contains},
409 {"!~", clause_not_contains},
410 {"^", clause_begins_with},
411 {"!^", clause_not_begins_with},
412 {"$", clause_ends_with},
413 {"!$", clause_not_ends_with},
414 {"@", clause_oneof}
Nils Wallméniusf42a3052011-05-31 19:44:21 +0000415 };
416
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000417 char buf[4];
Nils Wallméniusf42a3052011-05-31 19:44:21 +0000418 unsigned int i;
419
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000420 /* Find the start. */
421 while (*strp == ' ' && *strp != '\0')
422 strp++;
Nils Wallméniusf42a3052011-05-31 19:44:21 +0000423
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000424 if (*strp == '\0')
425 return 0;
Nils Wallméniusf42a3052011-05-31 19:44:21 +0000426
427 for (i = 0; i < sizeof(buf)-1; i++)
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000428 {
429 if (*strp == '\0' || *strp == ' ')
430 break ;
431 buf[i] = *strp;
432 strp++;
433 }
434 buf[i] = '\0';
Nils Wallméniusf42a3052011-05-31 19:44:21 +0000435
Thomas Martitz1b7ff722011-07-18 18:57:50 +0000436 for (i = 0; i < ARRAYLEN(get_clause_match); i++)
Nils Wallméniusf42a3052011-05-31 19:44:21 +0000437 {
Thomas Martitz1b7ff722011-07-18 18:57:50 +0000438 if (!strcasecmp(buf, get_clause_match[i].str))
439 {
440 *condition = get_clause_match[i].symbol;
441 return 1;
442 }
Nils Wallméniusf42a3052011-05-31 19:44:21 +0000443 }
Thomas Martitz1b7ff722011-07-18 18:57:50 +0000444
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000445 return 0;
446}
447
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000448static bool read_clause(struct tagcache_search_clause *clause)
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000449{
Robert Kuklac4366572007-10-30 10:02:57 +0000450 char buf[SEARCHSTR_SIZE];
451 unsigned int i;
Michael Sevakisffa86262013-07-12 12:03:20 -0400452
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000453 if (get_tag(&clause->tag) <= 0)
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000454 return false;
Michael Sevakisffa86262013-07-12 12:03:20 -0400455
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000456 if (get_clause(&clause->type) <= 0)
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000457 return false;
Michael Sevakisffa86262013-07-12 12:03:20 -0400458
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000459 if (get_token_str(buf, sizeof buf) < 0)
460 return false;
Michael Sevakisffa86262013-07-12 12:03:20 -0400461
Robert Kuklac4366572007-10-30 10:02:57 +0000462 for (i=0; i<ARRAYLEN(id3_to_search_mapping); i++)
463 {
464 if (!strcasecmp(buf, id3_to_search_mapping[i].string))
465 break;
466 }
467
468 if (i<ARRAYLEN(id3_to_search_mapping)) /* runtime search operand found */
469 {
470 clause->source = source_runtime+i;
Thomas Martitz316f9a02011-06-20 19:32:56 +0000471 clause->str = tagtree_alloc(SEARCHSTR_SIZE);
Michael Sevakisffa86262013-07-12 12:03:20 -0400472 }
473 else
Jonathan Gordon75eff7a2007-10-29 14:10:24 +0000474 {
Robert Kuklac4366572007-10-30 10:02:57 +0000475 clause->source = source_constant;
Thomas Martitz316f9a02011-06-20 19:32:56 +0000476 clause->str = tagtree_strdup(buf);
Michael Sevakisffa86262013-07-12 12:03:20 -0400477 }
478
William Wilgus501404d2019-07-29 20:49:41 -0500479 if (!clause->str)
480 {
481 logf("tagtree failed to allocate %s", "clause string");
482 return false;
483 }
484 else if (TAGCACHE_IS_NUMERIC(clause->tag))
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000485 {
486 clause->numeric = true;
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000487 clause->numeric_data = atoi(clause->str);
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000488 }
489 else
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000490 clause->numeric = false;
Robert Kuklac4366572007-10-30 10:02:57 +0000491
Michael Sevakisffa86262013-07-12 12:03:20 -0400492 logf("got clause: %d/%d [%s]", clause->tag, clause->type, clause->str);
493
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000494 return true;
495}
496
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +0000497static bool read_variable(char *buf, int size)
498{
499 int condition;
Michael Sevakisffa86262013-07-12 12:03:20 -0400500
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +0000501 if (!get_clause(&condition))
502 return false;
Michael Sevakisffa86262013-07-12 12:03:20 -0400503
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +0000504 if (condition != clause_is)
505 return false;
Michael Sevakisffa86262013-07-12 12:03:20 -0400506
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +0000507 if (get_token_str(buf, size) < 0)
508 return false;
Michael Sevakisffa86262013-07-12 12:03:20 -0400509
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +0000510 return true;
511}
512
513/* "%3d. %s" autoscore title %sort = "inverse" %limit = "100" */
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000514static int get_format_str(struct display_format *fmt)
515{
516 int ret;
Miika Pekkarinenbba9de22006-10-11 04:32:33 +0000517 char buf[128];
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000518 int i;
Michael Sevakisffa86262013-07-12 12:03:20 -0400519
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000520 memset(fmt, 0, sizeof(struct display_format));
Michael Sevakisffa86262013-07-12 12:03:20 -0400521
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000522 if (get_token_str(fmt->name, sizeof fmt->name) < 0)
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000523 return -10;
Michael Sevakisffa86262013-07-12 12:03:20 -0400524
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000525 /* Determine the group id */
526 fmt->group_id = 0;
527 for (i = 0; i < format_count; i++)
528 {
529 if (!strcasecmp(formats[i]->name, fmt->name))
530 {
531 fmt->group_id = formats[i]->group_id;
532 break;
533 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400534
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000535 if (formats[i]->group_id > fmt->group_id)
536 fmt->group_id = formats[i]->group_id;
537 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400538
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000539 if (i == format_count)
540 fmt->group_id++;
Michael Sevakisffa86262013-07-12 12:03:20 -0400541
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000542 logf("format: (%d) %s", fmt->group_id, fmt->name);
Michael Sevakisffa86262013-07-12 12:03:20 -0400543
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000544 if (get_token_str(buf, sizeof buf) < 0)
545 return -10;
Michael Sevakisffa86262013-07-12 12:03:20 -0400546
Thomas Martitz316f9a02011-06-20 19:32:56 +0000547 fmt->formatstr = tagtree_strdup(buf);
Michael Sevakisffa86262013-07-12 12:03:20 -0400548
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000549 while (fmt->tag_count < MAX_TAGS)
550 {
551 ret = get_tag(&fmt->tags[fmt->tag_count]);
552 if (ret < 0)
553 return -11;
Michael Sevakisffa86262013-07-12 12:03:20 -0400554
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000555 if (ret == 0)
556 break;
Michael Sevakisffa86262013-07-12 12:03:20 -0400557
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +0000558 switch (fmt->tags[fmt->tag_count]) {
559 case var_sorttype:
560 if (!read_variable(buf, sizeof buf))
561 return -12;
562 if (!strcasecmp("inverse", buf))
563 fmt->sort_inverse = true;
564 break;
Michael Sevakisffa86262013-07-12 12:03:20 -0400565
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +0000566 case var_limit:
567 if (!read_variable(buf, sizeof buf))
568 return -13;
569 fmt->limit = atoi(buf);
570 break;
Michael Sevakisffa86262013-07-12 12:03:20 -0400571
Miika Pekkarinen8d145f62006-09-26 18:59:16 +0000572 case var_strip:
573 if (!read_variable(buf, sizeof buf))
574 return -14;
575 fmt->strip = atoi(buf);
576 break;
Michael Sevakisffa86262013-07-12 12:03:20 -0400577
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +0000578 default:
579 fmt->tag_count++;
580 }
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000581 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400582
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000583 return 1;
584}
585
586static int add_format(const char *buf)
587{
Michael Hohmuth851cac82011-06-14 20:54:26 +0000588 if (format_count >= TAGMENU_MAX_FMTS)
589 {
590 logf("too many formats");
591 return -1;
592 }
593
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000594 strp = buf;
Michael Sevakisffa86262013-07-12 12:03:20 -0400595
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000596 if (formats[format_count] == NULL)
Thomas Martitz316f9a02011-06-20 19:32:56 +0000597 formats[format_count] = tagtree_alloc0(sizeof(struct display_format));
William Wilgus501404d2019-07-29 20:49:41 -0500598 if (!formats[format_count])
599 {
600 logf("tagtree failed to allocate %s", "format string");
601 return -2;
602 }
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000603 if (get_format_str(formats[format_count]) < 0)
604 {
605 logf("get_format_str() parser failed!");
William Wilgus501404d2019-07-29 20:49:41 -0500606 if (formats[format_count])
607 memset(formats[format_count], 0, sizeof(struct display_format));
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000608 return -4;
609 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400610
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000611 while (*strp != '\0' && *strp != '?')
612 strp++;
613
614 if (*strp == '?')
615 {
616 int clause_count = 0;
617 strp++;
Thomas Martitzbaa070c2011-08-30 14:01:45 +0000618
619 tagtree_lock();
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000620 while (1)
621 {
William Wilgus501404d2019-07-29 20:49:41 -0500622 struct tagcache_search_clause *new_clause;
Michael Sevakisffa86262013-07-12 12:03:20 -0400623
Miika Pekkarinen649fc772006-10-25 15:14:15 +0000624 if (clause_count >= TAGCACHE_MAX_CLAUSES)
625 {
626 logf("too many clauses");
627 break;
628 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400629
William Wilgus501404d2019-07-29 20:49:41 -0500630 new_clause = tagtree_alloc(sizeof(struct tagcache_search_clause));
631 if (!new_clause)
632 {
633 logf("tagtree failed to allocate %s", "search clause");
634 return -3;
635 }
636 formats[format_count]->clause[clause_count] = new_clause;
637 if (!read_clause(new_clause))
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000638 break;
Michael Sevakisffa86262013-07-12 12:03:20 -0400639
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000640 clause_count++;
641 }
Thomas Martitzbaa070c2011-08-30 14:01:45 +0000642 tagtree_unlock();
Michael Sevakisffa86262013-07-12 12:03:20 -0400643
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000644 formats[format_count]->clause_count = clause_count;
645 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400646
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000647 format_count++;
Michael Sevakisffa86262013-07-12 12:03:20 -0400648
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000649 return 1;
650}
651
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000652static int get_condition(struct search_instruction *inst)
653{
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +0000654 struct tagcache_search_clause *new_clause;
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000655 int clause_count;
Miika Pekkarinenbba9de22006-10-11 04:32:33 +0000656 char buf[128];
Michael Sevakisffa86262013-07-12 12:03:20 -0400657
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000658 switch (*strp)
659 {
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000660 case '=':
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000661 {
662 int i;
Michael Sevakisffa86262013-07-12 12:03:20 -0400663
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000664 if (get_token_str(buf, sizeof buf) < 0)
665 return -1;
Michael Sevakisffa86262013-07-12 12:03:20 -0400666
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000667 for (i = 0; i < format_count; i++)
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000668 {
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000669 if (!strcasecmp(formats[i]->name, buf))
670 break;
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000671 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400672
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000673 if (i == format_count)
674 {
675 logf("format not found: %s", buf);
676 return -2;
677 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400678
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000679 inst->format_id[inst->tagorder_count] = formats[i]->group_id;
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +0000680 return 1;
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000681 }
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000682 case '?':
683 case ' ':
684 case '&':
685 strp++;
686 return 1;
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000687 case '-':
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000688 case '\0':
689 return 0;
690 }
691
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000692 clause_count = inst->clause_count[inst->tagorder_count];
693 if (clause_count >= TAGCACHE_MAX_CLAUSES)
694 {
695 logf("Too many clauses");
William Wilgus501404d2019-07-29 20:49:41 -0500696 return -2;
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000697 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400698
Thomas Martitz316f9a02011-06-20 19:32:56 +0000699 new_clause = tagtree_alloc(sizeof(struct tagcache_search_clause));
William Wilgus501404d2019-07-29 20:49:41 -0500700 if (!new_clause)
701 {
702 logf("tagtree failed to allocate %s", "search clause");
703 return -3;
704 }
705
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +0000706 inst->clause[inst->tagorder_count][clause_count] = new_clause;
Michael Sevakisffa86262013-07-12 12:03:20 -0400707
Michael Hohmuth6a24a7a2011-05-10 10:25:41 +0000708 if (*strp == '|')
709 {
710 strp++;
711 new_clause->type = clause_logical_or;
712 }
Thomas Martitzbaa070c2011-08-30 14:01:45 +0000713 else
714 {
715 tagtree_lock();
716 bool ret = read_clause(new_clause);
717 tagtree_unlock();
718 if (!ret)
719 return -1;
720 }
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000721 inst->clause_count[inst->tagorder_count]++;
Michael Sevakisffa86262013-07-12 12:03:20 -0400722
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000723 return 1;
724}
725
726/* example search:
727 * "Best" artist ? year >= "2000" & title !^ "crap" & genre = "good genre" \
728 * : album ? year >= "2000" : songs
729 * ^ begins with
730 * * contains
731 * $ ends with
732 */
733
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000734static bool parse_search(struct menu_entry *entry, const char *str)
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000735{
736 int ret;
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000737 int type;
Thomas Martitz19d08c22011-06-20 19:32:52 +0000738 struct search_instruction *inst = &entry->si;
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000739 char buf[MAX_PATH];
740 int i;
Michael Sevakisffa86262013-07-12 12:03:20 -0400741
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000742 strp = str;
Michael Sevakisffa86262013-07-12 12:03:20 -0400743
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000744 /* Parse entry name */
745 if (get_token_str(entry->name, sizeof entry->name) < 0)
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000746 {
747 logf("No name found.");
748 return false;
749 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400750
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000751 /* Parse entry type */
752 if (get_tag(&entry->type) <= 0)
753 return false;
Michael Sevakisffa86262013-07-12 12:03:20 -0400754
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000755 if (entry->type == menu_load)
756 {
757 if (get_token_str(buf, sizeof buf) < 0)
758 return false;
Michael Sevakisffa86262013-07-12 12:03:20 -0400759
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000760 /* Find the matching root menu or "create" it */
761 for (i = 0; i < menu_count; i++)
762 {
Miika Pekkarinen3ae1c102006-09-26 06:40:14 +0000763 if (!strcasecmp(menus[i]->id, buf))
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000764 {
765 entry->link = i;
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000766 return true;
767 }
768 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400769
Miika Pekkarinen6ecb06f2007-06-22 12:48:06 +0000770 if (menu_count >= TAGMENU_MAX_MENUS)
771 {
772 logf("max menucount reached");
773 return false;
774 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400775
Miika Pekkarinen6ecb06f2007-06-22 12:48:06 +0000776 /* Allocate a new menu unless link is found. */
Thomas Martitz316f9a02011-06-20 19:32:56 +0000777 menus[menu_count] = tagtree_alloc0(sizeof(struct menu_root));
William Wilgus501404d2019-07-29 20:49:41 -0500778 if (!menus[menu_count])
779 {
780 logf("tagtree failed to allocate %s", "menu");
781 return false;
782 }
Thomas Martitzbaa070c2011-08-30 14:01:45 +0000783 strlcpy(menus[menu_count]->id, buf, MAX_MENU_ID_SIZE);
Miika Pekkarinen6ecb06f2007-06-22 12:48:06 +0000784 entry->link = menu_count;
785 ++menu_count;
Michael Sevakisffa86262013-07-12 12:03:20 -0400786
Miika Pekkarinen6ecb06f2007-06-22 12:48:06 +0000787 return true;
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000788 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400789
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000790 if (entry->type != menu_next)
791 return false;
Michael Sevakisffa86262013-07-12 12:03:20 -0400792
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000793 while (inst->tagorder_count < MAX_TAGS)
794 {
795 ret = get_tag(&inst->tagorder[inst->tagorder_count]);
Michael Sevakisffa86262013-07-12 12:03:20 -0400796 if (ret < 0)
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000797 {
798 logf("Parse error #1");
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000799 logf("%s", strp);
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000800 return false;
801 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400802
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000803 if (ret == 0)
804 break ;
Michael Sevakisffa86262013-07-12 12:03:20 -0400805
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000806 logf("tag: %d", inst->tagorder[inst->tagorder_count]);
Thomas Martitzbaa070c2011-08-30 14:01:45 +0000807
808 tagtree_lock();
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000809 while ( (ret = get_condition(inst)) > 0 ) ;
Thomas Martitzbaa070c2011-08-30 14:01:45 +0000810 tagtree_unlock();
811
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +0000812 if (ret < 0)
813 return false;
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000814
815 inst->tagorder_count++;
Michael Sevakisffa86262013-07-12 12:03:20 -0400816
Miika Pekkarinenba34c372006-09-19 11:54:33 +0000817 if (get_tag(&type) <= 0 || type != menu_next)
818 break;
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000819 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400820
Miika Pekkarinen4a63c092006-04-04 19:28:13 +0000821 return true;
822}
823
Miika Pekkarinen44b76bc2006-03-30 12:07:32 +0000824static int compare(const void *p1, const void *p2)
825{
826 struct tagentry *e1 = (struct tagentry *)p1;
827 struct tagentry *e2 = (struct tagentry *)p2;
Thomas Martitzbaa070c2011-08-30 14:01:45 +0000828
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +0000829 if (sort_inverse)
830 return strncasecmp(e2->name, e1->name, MAX_PATH);
Michael Sevakisffa86262013-07-12 12:03:20 -0400831
Miika Pekkarinen44b76bc2006-03-30 12:07:32 +0000832 return strncasecmp(e1->name, e2->name, MAX_PATH);
833}
834
Thomas Martitz84c7d612014-01-15 23:18:35 +0100835static int nat_compare(const void *p1, const void *p2)
836{
837 struct tagentry *e1 = (struct tagentry *)p1;
838 struct tagentry *e2 = (struct tagentry *)p2;
839
840 if (sort_inverse)
841 return strnatcasecmp(e2->name, e1->name);
842
843 return strnatcasecmp(e1->name, e2->name);
844}
845
Thomas Martitz470989b2014-03-14 23:15:16 +0100846static void tagtree_buffer_event(unsigned short id, void *ev_data)
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000847{
Thomas Martitz470989b2014-03-14 23:15:16 +0100848 (void)id;
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +0000849 struct tagcache_search tcs;
Thomas Martitz470989b2014-03-14 23:15:16 +0100850 struct mp3entry *id3 = ((struct track_event *)ev_data)->id3;
Michael Sevakis023f6b62013-07-12 12:06:38 -0400851
852 bool runtimedb = global_settings.runtimedb;
853 bool autoresume = global_settings.autoresume_enable;
Michael Sevakisffa86262013-07-12 12:03:20 -0400854
Miika Pekkarinen890989d2006-08-26 16:06:54 +0000855 /* Do not gather data unless proper setting has been enabled. */
Michael Sevakis023f6b62013-07-12 12:06:38 -0400856 if (!runtimedb && !autoresume)
Miika Pekkarinen890989d2006-08-26 16:06:54 +0000857 return;
858
Miika Pekkarinenf98a0a92007-11-23 17:58:03 +0000859 logf("be:%s", id3->path);
Michael Sevakisffa86262013-07-12 12:03:20 -0400860
Dominik Riebelingd2cc5ce2011-01-17 22:28:36 +0000861 while (! tagcache_is_fully_initialized())
Jonathan Gordon7270e5e2010-11-16 13:17:37 +0000862 yield();
863
Miika Pekkarinen890989d2006-08-26 16:06:54 +0000864 if (!tagcache_find_index(&tcs, id3->path))
865 {
866 logf("tc stat: not found: %s", id3->path);
867 return;
868 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400869
Michael Sevakis023f6b62013-07-12 12:06:38 -0400870 if (runtimedb)
Michael Giacomelli66e8fc02011-01-02 02:49:13 +0000871 {
872 id3->playcount = tagcache_get_numeric(&tcs, tag_playcount);
873 if (!id3->rating)
874 id3->rating = tagcache_get_numeric(&tcs, tag_rating);
875 id3->lastplayed = tagcache_get_numeric(&tcs, tag_lastplayed);
876 id3->score = tagcache_get_numeric(&tcs, tag_virt_autoscore) / 10;
877 id3->playtime = tagcache_get_numeric(&tcs, tag_playtime);
Michael Sevakisffa86262013-07-12 12:03:20 -0400878
Michael Giacomelli66e8fc02011-01-02 02:49:13 +0000879 logf("-> %ld/%ld", id3->playcount, id3->playtime);
880 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400881
882 #if CONFIG_CODEC == SWCODEC
Michael Sevakis023f6b62013-07-12 12:06:38 -0400883 if (autoresume)
Michael Giacomelli66e8fc02011-01-02 02:49:13 +0000884 {
Michael Sevakis31b71222013-07-14 07:59:39 -0400885 /* Load current file resume info if not already defined (by
Michael Giacomelli66e8fc02011-01-02 02:49:13 +0000886 another resume mechanism) */
Michael Sevakis31b71222013-07-14 07:59:39 -0400887 if (id3->elapsed == 0)
888 {
889 id3->elapsed = tagcache_get_numeric(&tcs, tag_lastelapsed);
890
891 logf("tagtree_buffer_event: Set elapsed for %s to %lX\n",
892 str_or_empty(id3->title), id3->elapsed);
893 }
894
Michael Giacomelli66e8fc02011-01-02 02:49:13 +0000895 if (id3->offset == 0)
896 {
897 id3->offset = tagcache_get_numeric(&tcs, tag_lastoffset);
898
Michael Sevakisffa86262013-07-12 12:03:20 -0400899 logf("tagtree_buffer_event: Set offset for %s to %lX\n",
Michael Giacomelli66e8fc02011-01-02 02:49:13 +0000900 str_or_empty(id3->title), id3->offset);
901 }
902 }
Michael Giacomellicacc64a2011-01-02 03:48:40 +0000903 #endif
Michael Sevakisffa86262013-07-12 12:03:20 -0400904
Miika Pekkarinen9d756e22007-07-21 17:35:19 +0000905 /* Store our tagcache index pointer. */
Robert Kuklaad5610d2008-02-12 18:51:10 +0000906 id3->tagcache_idx = tcs.idx_id+1;
Michael Sevakisffa86262013-07-12 12:03:20 -0400907
Miika Pekkarinen890989d2006-08-26 16:06:54 +0000908 tagcache_search_finish(&tcs);
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000909}
910
Thomas Martitz470989b2014-03-14 23:15:16 +0100911static void tagtree_track_finish_event(unsigned short id, void *ev_data)
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000912{
Thomas Martitz470989b2014-03-14 23:15:16 +0100913 (void)id;
914 struct track_event *te = (struct track_event *)ev_data;
Michael Sevakis023f6b62013-07-12 12:06:38 -0400915 struct mp3entry *id3 = te->id3;
Michael Sevakisffa86262013-07-12 12:03:20 -0400916
Michael Sevakis023f6b62013-07-12 12:06:38 -0400917 long tagcache_idx = id3->tagcache_idx;
Robert Kuklaad5610d2008-02-12 18:51:10 +0000918 if (!tagcache_idx)
Miika Pekkarinen9d756e22007-07-21 17:35:19 +0000919 {
920 logf("No tagcache index pointer found");
921 return;
922 }
Robert Kuklaad5610d2008-02-12 18:51:10 +0000923 tagcache_idx--;
Michael Sevakisffa86262013-07-12 12:03:20 -0400924
Michael Hohmuth48441422011-02-08 20:31:27 +0000925#if CONFIG_CODEC == SWCODEC /* HWCODEC doesn't have automatic_skip */
Michael Sevakis023f6b62013-07-12 12:06:38 -0400926 bool auto_skip = te->flags & TEF_AUTO_SKIP;
Michael Hohmuth48441422011-02-08 20:31:27 +0000927#endif
Michael Sevakis023f6b62013-07-12 12:06:38 -0400928 bool runtimedb = global_settings.runtimedb;
929 bool autoresume = global_settings.autoresume_enable;
930
931 /* Don't process unplayed tracks, or tracks interrupted within the
932 first 15 seconds but always process autoresume point */
933 if (runtimedb && (id3->elapsed == 0
934#if CONFIG_CODEC == SWCODEC
935 || (id3->elapsed < 15 * 1000 && !auto_skip)
936#endif
937 ))
Miika Pekkarinend8ac6072006-08-02 17:39:34 +0000938 {
Michael Sevakis023f6b62013-07-12 12:06:38 -0400939 logf("not db logging unplayed or skipped track");
940 runtimedb = false;
941 }
942
943#if CONFIG_CODEC == SWCODEC
944 /* 3s because that is the threshold the WPS uses to rewind instead
945 of skip backwards */
946 if (autoresume && (id3->elapsed == 0
947 || (id3->elapsed < 3 * 1000 && !auto_skip)))
948 {
949 logf("not logging autoresume");
950 autoresume = false;
951 }
952#endif
953
954 /* Do not gather data unless proper setting has been enabled and at least
955 one is still slated to be recorded */
956 if (!(runtimedb || autoresume))
957 {
958 logf("runtimedb gathering and autoresume not enabled/ignored");
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +0000959 return;
Miika Pekkarinend8ac6072006-08-02 17:39:34 +0000960 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400961
Michael Sevakis023f6b62013-07-12 12:06:38 -0400962 long lastplayed = tagcache_increase_serial();
Miika Pekkarinencb8c7952006-07-20 12:19:31 +0000963 if (lastplayed < 0)
Miika Pekkarinend8ac6072006-08-02 17:39:34 +0000964 {
Magnus Holmgren01a010f2007-03-18 09:50:53 +0000965 logf("incorrect tc serial:%ld", lastplayed);
Miika Pekkarinencb8c7952006-07-20 12:19:31 +0000966 return;
Miika Pekkarinend8ac6072006-08-02 17:39:34 +0000967 }
Michael Sevakisffa86262013-07-12 12:03:20 -0400968
Michael Sevakis023f6b62013-07-12 12:06:38 -0400969 if (runtimedb)
Michael Giacomelli66e8fc02011-01-02 02:49:13 +0000970 {
971 long playcount;
972 long playtime;
Robert Kuklaa334bd22007-11-23 23:36:42 +0000973
Michael Giacomelli66e8fc02011-01-02 02:49:13 +0000974 playcount = id3->playcount + 1;
Michael Sevakisffa86262013-07-12 12:03:20 -0400975
Michael Giacomelli66e8fc02011-01-02 02:49:13 +0000976 /* Ignore the last 15s (crossfade etc.) */
977 playtime = id3->playtime + MIN(id3->length, id3->elapsed + 15 * 1000);
Michael Sevakisffa86262013-07-12 12:03:20 -0400978
Michael Giacomelli66e8fc02011-01-02 02:49:13 +0000979 logf("ube:%s", id3->path);
980 logf("-> %ld/%ld", playcount, playtime);
981 logf("-> %ld/%ld/%ld", id3->elapsed, id3->length,
982 MIN(id3->length, id3->elapsed + 15 * 1000));
983
984 /* Queue the updates to the tagcache system. */
985 tagcache_update_numeric(tagcache_idx, tag_playcount, playcount);
986 tagcache_update_numeric(tagcache_idx, tag_playtime, playtime);
987 tagcache_update_numeric(tagcache_idx, tag_lastplayed, lastplayed);
988 }
989
Michael Sevakisffa86262013-07-12 12:03:20 -0400990#if CONFIG_CODEC == SWCODEC
Michael Sevakis023f6b62013-07-12 12:06:38 -0400991 if (autoresume)
Michael Giacomelli66e8fc02011-01-02 02:49:13 +0000992 {
Michael Sevakis31b71222013-07-14 07:59:39 -0400993 unsigned long elapsed = auto_skip ? 0 : id3->elapsed;
Michael Sevakis023f6b62013-07-12 12:06:38 -0400994 unsigned long offset = auto_skip ? 0 : id3->offset;
Michael Sevakis31b71222013-07-14 07:59:39 -0400995 tagcache_update_numeric(tagcache_idx, tag_lastelapsed, elapsed);
Michael Giacomelli66e8fc02011-01-02 02:49:13 +0000996 tagcache_update_numeric(tagcache_idx, tag_lastoffset, offset);
997
Michael Sevakis31b71222013-07-14 07:59:39 -0400998 logf("tagtree_track_finish_event: Save resume for %s: %lX %lX",
999 str_or_empty(id3->title), elapsed, offset);
Michael Giacomelli66e8fc02011-01-02 02:49:13 +00001000 }
Michael Giacomellicacc64a2011-01-02 03:48:40 +00001001#endif
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +00001002}
1003
William Wilgusdd40c462018-10-15 23:04:04 -04001004int tagtree_export(void)
Miika Pekkarinen00422012006-07-16 15:04:46 +00001005{
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001006 struct tagcache_search tcs;
Michael Sevakisffa86262013-07-12 12:03:20 -04001007
Nils Wallménius01729e72008-08-15 08:27:39 +00001008 splash(0, str(LANG_CREATING));
Miika Pekkarinen00422012006-07-16 15:04:46 +00001009 if (!tagcache_create_changelog(&tcs))
1010 {
Nils Wallménius01729e72008-08-15 08:27:39 +00001011 splash(HZ*2, ID2P(LANG_FAILED));
Miika Pekkarinen00422012006-07-16 15:04:46 +00001012 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001013
William Wilgusdd40c462018-10-15 23:04:04 -04001014 return 0;
Miika Pekkarinen00422012006-07-16 15:04:46 +00001015}
1016
William Wilgusdd40c462018-10-15 23:04:04 -04001017int tagtree_import(void)
Miika Pekkarinencb8c7952006-07-20 12:19:31 +00001018{
Nils Wallménius01729e72008-08-15 08:27:39 +00001019 splash(0, ID2P(LANG_WAIT));
Miika Pekkarinencb8c7952006-07-20 12:19:31 +00001020 if (!tagcache_import_changelog())
1021 {
Nils Wallménius01729e72008-08-15 08:27:39 +00001022 splash(HZ*2, ID2P(LANG_FAILED));
Miika Pekkarinencb8c7952006-07-20 12:19:31 +00001023 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001024
William Wilgusdd40c462018-10-15 23:04:04 -04001025 return 0;
Miika Pekkarinencb8c7952006-07-20 12:19:31 +00001026}
1027
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001028static bool parse_menu(const char *filename);
1029
Miika Pekkarinend0084ff2011-06-23 20:22:00 +00001030static int parse_line(int n, char *buf, void *parameters)
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001031{
1032 char data[256];
1033 int variable;
1034 static bool read_menu;
1035 int i;
Miika Pekkarinend0084ff2011-06-23 20:22:00 +00001036 char *p;
Michael Sevakisffa86262013-07-12 12:03:20 -04001037
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001038 (void)parameters;
Michael Sevakisffa86262013-07-12 12:03:20 -04001039
Miika Pekkarinend0084ff2011-06-23 20:22:00 +00001040 /* Strip possible <CR> at end of line. */
1041 p = strchr(buf, '\r');
1042 if (p != NULL)
1043 *p = '\0';
Michael Sevakisffa86262013-07-12 12:03:20 -04001044
Miika Pekkarinen649fc772006-10-25 15:14:15 +00001045 logf("parse:%d/%s", n, buf);
Michael Sevakisffa86262013-07-12 12:03:20 -04001046
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001047 /* First line, do initialisation. */
1048 if (n == 0)
1049 {
1050 if (strcasecmp(TAGNAVI_VERSION, buf))
1051 {
1052 logf("Version mismatch");
1053 return -1;
1054 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001055
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001056 read_menu = false;
1057 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001058
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001059 if (buf[0] == '#')
1060 return 0;
Michael Sevakisffa86262013-07-12 12:03:20 -04001061
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001062 if (buf[0] == '\0')
1063 {
1064 if (read_menu)
1065 {
1066 /* End the menu */
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001067 read_menu = false;
1068 }
1069 return 0;
1070 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001071
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001072 if (!read_menu)
1073 {
1074 strp = buf;
1075 if (get_tag(&variable) <= 0)
1076 return 0;
Michael Sevakisffa86262013-07-12 12:03:20 -04001077
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001078 switch (variable)
1079 {
1080 case var_format:
1081 if (add_format(strp) < 0)
1082 {
1083 logf("Format add fail: %s", data);
1084 }
1085 break;
Michael Sevakisffa86262013-07-12 12:03:20 -04001086
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001087 case var_include:
1088 if (get_token_str(data, sizeof(data)) < 0)
1089 {
Magnus Holmgren01a010f2007-03-18 09:50:53 +00001090 logf("%%include empty");
Miika Pekkarinen649fc772006-10-25 15:14:15 +00001091 return 0;
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001092 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001093
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001094 if (!parse_menu(data))
1095 {
1096 logf("Load menu fail: %s", data);
1097 }
1098 break;
Michael Sevakisffa86262013-07-12 12:03:20 -04001099
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001100 case var_menu_start:
1101 if (menu_count >= TAGMENU_MAX_MENUS)
1102 {
1103 logf("max menucount reached");
Miika Pekkarinen649fc772006-10-25 15:14:15 +00001104 return 0;
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001105 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001106
Miika Pekkarinen6ecb06f2007-06-22 12:48:06 +00001107 if (get_token_str(data, sizeof data) < 0)
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001108 {
Magnus Holmgren01a010f2007-03-18 09:50:53 +00001109 logf("%%menu_start id empty");
Miika Pekkarinen649fc772006-10-25 15:14:15 +00001110 return 0;
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001111 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001112
Miika Pekkarinen6ecb06f2007-06-22 12:48:06 +00001113 menu = NULL;
1114 for (i = 0; i < menu_count; i++)
1115 {
1116 if (!strcasecmp(menus[i]->id, data))
1117 {
1118 menu = menus[i];
1119 }
1120 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001121
1122 if (menu == NULL)
Miika Pekkarinen6ecb06f2007-06-22 12:48:06 +00001123 {
Thomas Martitz316f9a02011-06-20 19:32:56 +00001124 menus[menu_count] = tagtree_alloc0(sizeof(struct menu_root));
William Wilgus501404d2019-07-29 20:49:41 -05001125 if (!menus[menu_count])
1126 {
1127 logf("tagtree failed to allocate %s", "menu");
1128 return -2;
1129 }
Miika Pekkarinen6ecb06f2007-06-22 12:48:06 +00001130 menu = menus[menu_count];
1131 ++menu_count;
Nils Wallménius3d4701a2009-07-14 13:57:45 +00001132 strlcpy(menu->id, data, MAX_MENU_ID_SIZE);
Miika Pekkarinen6ecb06f2007-06-22 12:48:06 +00001133 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001134
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001135 if (get_token_str(menu->title, sizeof(menu->title)) < 0)
1136 {
Magnus Holmgren01a010f2007-03-18 09:50:53 +00001137 logf("%%menu_start title empty");
Miika Pekkarinen649fc772006-10-25 15:14:15 +00001138 return 0;
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001139 }
Miika Pekkarinen649fc772006-10-25 15:14:15 +00001140 logf("menu: %s", menu->title);
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001141 read_menu = true;
1142 break;
Michael Sevakisffa86262013-07-12 12:03:20 -04001143
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001144 case var_rootmenu:
1145 /* Only set root menu once. */
Thomas Martitz38d51752009-05-30 16:35:28 +00001146 if (rootmenu >= 0)
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001147 break;
Michael Sevakisffa86262013-07-12 12:03:20 -04001148
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001149 if (get_token_str(data, sizeof(data)) < 0)
1150 {
Thomas Martitz38d51752009-05-30 16:35:28 +00001151 logf("%%rootmenu empty");
Miika Pekkarinen649fc772006-10-25 15:14:15 +00001152 return 0;
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001153 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001154
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001155 for (i = 0; i < menu_count; i++)
1156 {
1157 if (!strcasecmp(menus[i]->id, data))
1158 {
Thomas Martitz38d51752009-05-30 16:35:28 +00001159 rootmenu = i;
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001160 }
1161 }
1162 break;
1163 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001164
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001165 return 0;
1166 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001167
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001168 if (menu->itemcount >= TAGMENU_MAX_ITEMS)
1169 {
1170 logf("max itemcount reached");
1171 return 0;
1172 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001173
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001174 /* Allocate */
1175 if (menu->items[menu->itemcount] == NULL)
Thomas Martitz316f9a02011-06-20 19:32:56 +00001176 menu->items[menu->itemcount] = tagtree_alloc0(sizeof(struct menu_entry));
William Wilgus501404d2019-07-29 20:49:41 -05001177 if (!menu->items[menu->itemcount])
1178 {
1179 logf("tagtree failed to allocate %s", "menu items");
1180 return -2;
1181 }
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001182 tagtree_lock();
1183 if (parse_search(menu->items[menu->itemcount], buf))
1184 menu->itemcount++;
1185 tagtree_unlock();
1186
Miika Pekkarinena1ac7432006-10-21 20:37:33 +00001187 return 0;
1188}
1189
Miika Pekkarinenba34c372006-09-19 11:54:33 +00001190static bool parse_menu(const char *filename)
Miika Pekkarinen4a63c092006-04-04 19:28:13 +00001191{
1192 int fd;
Miika Pekkarinen649fc772006-10-25 15:14:15 +00001193 char buf[1024];
William Wilgus501404d2019-07-29 20:49:41 -05001194 int rc;
Miika Pekkarinenba34c372006-09-19 11:54:33 +00001195
1196 if (menu_count >= TAGMENU_MAX_MENUS)
Miika Pekkarinen1dc35772006-09-21 17:16:35 +00001197 {
1198 logf("max menucount reached");
Miika Pekkarinenba34c372006-09-19 11:54:33 +00001199 return false;
Miika Pekkarinen1dc35772006-09-21 17:16:35 +00001200 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001201
Miika Pekkarinenba34c372006-09-19 11:54:33 +00001202 fd = open(filename, O_RDONLY);
Miika Pekkarinen4a63c092006-04-04 19:28:13 +00001203 if (fd < 0)
1204 {
1205 logf("Search instruction file not found.");
Miika Pekkarinenba34c372006-09-19 11:54:33 +00001206 return false;
Miika Pekkarinen4a63c092006-04-04 19:28:13 +00001207 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001208
Linus Nielsen Feltzingeed923e2006-08-23 08:41:18 +00001209 /* Now read file for real, parsing into si */
William Wilgus501404d2019-07-29 20:49:41 -05001210 rc = fast_readline(fd, buf, sizeof buf, NULL, parse_line);
Miika Pekkarinen4a63c092006-04-04 19:28:13 +00001211 close(fd);
Michael Sevakisffa86262013-07-12 12:03:20 -04001212
William Wilgus501404d2019-07-29 20:49:41 -05001213 return (rc >= 0);
1214}
1215
1216static void tagtree_unload(struct tree_context *c)
1217{
1218 int i;
1219 tagtree_lock();
1220
1221 remove_event(PLAYBACK_EVENT_TRACK_BUFFER, tagtree_buffer_event);
1222 remove_event(PLAYBACK_EVENT_TRACK_FINISH, tagtree_track_finish_event);
1223
1224 if (c)
1225 {
1226 tree_lock_cache(c);
1227 struct tagentry *dptr = core_get_data(c->cache.entries_handle);
1228 menu = menus[c->currextra];
1229 if (!menu)
1230 {
1231 logf("tagtree menu doesn't exist");
1232 return;
1233 }
1234
1235 for (i = 0; i < menu->itemcount; i++)
1236 {
1237 dptr->name = NULL;
1238 dptr->newtable = 0;
1239 dptr->extraseek = 0;
1240 dptr++;
1241 }
1242 }
1243
1244 for (i = 0; i < menu_count; i++)
1245 menus[i] = NULL;
1246 menu_count = 0;
1247
1248 for (i = 0; i < format_count; i++)
1249 formats[i] = NULL;
1250 format_count = 0;
1251
1252 core_free(tagtree_handle);
1253 tagtree_handle = 0;
1254 tagtree_buf_used = 0;
1255 tagtree_bufsize = 0;
1256
1257 if (c)
1258 tree_unlock_cache(c);
1259 tagtree_unlock();
1260 if (lock_count > 0)
1261 tagtree_unlock();/* second unlock to enable re-init */
Miika Pekkarinenba34c372006-09-19 11:54:33 +00001262}
1263
1264void tagtree_init(void)
1265{
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001266 format_count = 0;
Miika Pekkarinenba34c372006-09-19 11:54:33 +00001267 menu_count = 0;
Miika Pekkarinen3ae1c102006-09-26 06:40:14 +00001268 menu = NULL;
Thomas Martitz38d51752009-05-30 16:35:28 +00001269 rootmenu = -1;
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001270 tagtree_handle = core_alloc_maximum("tagtree", &tagtree_bufsize, &ops);
William Wilgus501404d2019-07-29 20:49:41 -05001271 if (!parse_menu(FILE_SEARCH_INSTRUCTIONS))
1272 {
1273 tagtree_unload(NULL);
1274 return;
1275 }
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001276
1277 /* safety check since tree.c needs to cast tagentry to entry */
1278 if (sizeof(struct tagentry) != sizeof(struct entry))
1279 panicf("tagentry(%zu) and entry mismatch(%zu)",
1280 sizeof(struct tagentry), sizeof(struct entry));
1281 if (lock_count > 0)
1282 panicf("tagtree locked after parsing");
Michael Sevakisffa86262013-07-12 12:03:20 -04001283
Miika Pekkarinene6de6e52007-04-12 18:33:40 +00001284 /* If no root menu is set, assume it's the first single menu
1285 * we have. That shouldn't normally happen. */
Thomas Martitz38d51752009-05-30 16:35:28 +00001286 if (rootmenu < 0)
1287 rootmenu = 0;
Miika Pekkarinen19c6e662008-03-16 13:55:16 +00001288
Thomas Martitz470989b2014-03-14 23:15:16 +01001289 add_event(PLAYBACK_EVENT_TRACK_BUFFER, tagtree_buffer_event);
1290 add_event(PLAYBACK_EVENT_TRACK_FINISH, tagtree_track_finish_event);
Thomas Martitzd0b72e22011-08-30 14:01:33 +00001291
1292 core_shrink(tagtree_handle, core_get_data(tagtree_handle), tagtree_buf_used);
Miika Pekkarinen4a63c092006-04-04 19:28:13 +00001293}
1294
Jens Arnold2597a132006-12-25 14:01:47 +00001295static bool show_search_progress(bool init, int count)
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001296{
1297 static int last_tick = 0;
Michael Sevakisffa86262013-07-12 12:03:20 -04001298
Barry Wardellca576992008-02-27 17:42:51 +00001299 /* Don't show splashes for 1/2 second after starting search */
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001300 if (init)
1301 {
Barry Wardellca576992008-02-27 17:42:51 +00001302 last_tick = current_tick + HZ/2;
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001303 return true;
1304 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001305
Barry Wardellca576992008-02-27 17:42:51 +00001306 /* Update progress every 1/10 of a second */
Thomas Martitz16983af2009-08-20 19:31:09 +00001307 if (TIME_AFTER(current_tick, last_tick + HZ/10))
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001308 {
Nils Wallménius01729e72008-08-15 08:27:39 +00001309 splashf(0, str(LANG_PLAYLIST_SEARCH_MSG), count, str(LANG_OFF_ABORT));
Linus Nielsen Feltzing224c0a12006-08-15 12:27:07 +00001310 if (action_userabort(TIMEOUT_NOBLOCK))
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001311 return false;
1312 last_tick = current_tick;
Miika Pekkarinen54ec1f52006-08-12 11:00:39 +00001313 yield();
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001314 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001315
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001316 return true;
1317}
1318
Jens Arnold2597a132006-12-25 14:01:47 +00001319static int format_str(struct tagcache_search *tcs, struct display_format *fmt,
1320 char *buf, int buf_size)
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001321{
Michael Hohmuthaccea182011-07-31 16:26:31 +00001322 char fmtbuf[20];
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001323 bool read_format = false;
Michael Hohmuthaccea182011-07-31 16:26:31 +00001324 unsigned fmtbuf_pos = 0;
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001325 int parpos = 0;
1326 int buf_pos = 0;
1327 int i;
Michael Sevakisffa86262013-07-12 12:03:20 -04001328
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001329 memset(buf, 0, buf_size);
1330 for (i = 0; fmt->formatstr[i] != '\0'; i++)
1331 {
1332 if (fmt->formatstr[i] == '%')
1333 {
1334 read_format = true;
1335 fmtbuf_pos = 0;
1336 if (parpos >= fmt->tag_count)
1337 {
1338 logf("too many format tags");
1339 return -1;
1340 }
1341 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001342
Michael Hohmuth8207a4a2011-08-04 10:23:18 +00001343 char formatchar = fmt->formatstr[i];
1344
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001345 if (read_format)
1346 {
Michael Hohmuthaccea182011-07-31 16:26:31 +00001347 fmtbuf[fmtbuf_pos++] = formatchar;
1348 if (fmtbuf_pos >= sizeof fmtbuf)
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001349 {
1350 logf("format parse error");
1351 return -2;
1352 }
Michael Hohmuthaccea182011-07-31 16:26:31 +00001353
1354 if (formatchar == 's' || formatchar == 'd')
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001355 {
Michael Hohmuthaccea182011-07-31 16:26:31 +00001356 unsigned space_left = buf_size - buf_pos;
Michael Hohmuth8207a4a2011-08-04 10:23:18 +00001357 char tmpbuf[MAX_PATH];
Michael Hohmuthaccea182011-07-31 16:26:31 +00001358 char *result;
1359
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001360 fmtbuf[fmtbuf_pos] = '\0';
1361 read_format = false;
Michael Hohmuthaccea182011-07-31 16:26:31 +00001362
1363 switch (formatchar)
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001364 {
Michael Hohmuthaccea182011-07-31 16:26:31 +00001365 case 's':
1366 if (fmt->tags[parpos] == tcs->type)
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001367 {
Michael Hohmuthaccea182011-07-31 16:26:31 +00001368 result = tcs->result;
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001369 }
Michael Hohmuthaccea182011-07-31 16:26:31 +00001370 else
1371 {
1372 /* Need to fetch the tag data. */
1373 int tag = fmt->tags[parpos];
1374
1375 if (!tagcache_retrieve(tcs, tcs->idx_id,
1376 (tag == tag_virt_basename ?
1377 tag_filename : tag),
Michael Hohmuth8207a4a2011-08-04 10:23:18 +00001378 tmpbuf, sizeof tmpbuf))
Michael Hohmuthaccea182011-07-31 16:26:31 +00001379 {
1380 logf("retrieve failed");
1381 return -3;
1382 }
1383
1384 if (tag == tag_virt_basename
1385 && (result = strrchr(tmpbuf, '/')) != NULL)
1386 {
1387 result++;
1388 }
1389 else
1390 result = tmpbuf;
1391 }
Michael Hohmuth8207a4a2011-08-04 10:23:18 +00001392 buf_pos +=
1393 snprintf(&buf[buf_pos], space_left, fmtbuf, result);
Michael Hohmuthaccea182011-07-31 16:26:31 +00001394 break;
1395
1396 case 'd':
Michael Hohmuth8207a4a2011-08-04 10:23:18 +00001397 buf_pos +=
1398 snprintf(&buf[buf_pos], space_left, fmtbuf,
1399 tagcache_get_numeric(tcs, fmt->tags[parpos]));
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001400 }
Michael Hohmuthaccea182011-07-31 16:26:31 +00001401
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001402 parpos++;
1403 }
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001404 }
Michael Hohmuthaccea182011-07-31 16:26:31 +00001405 else
Michael Hohmuth8207a4a2011-08-04 10:23:18 +00001406 buf[buf_pos++] = formatchar;
Michael Sevakisffa86262013-07-12 12:03:20 -04001407
Michael Hohmuth8207a4a2011-08-04 10:23:18 +00001408 if (buf_pos >= buf_size - 1) /* need at least one more byte for \0 */
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001409 {
1410 logf("buffer overflow");
1411 return -4;
1412 }
1413 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001414
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001415 buf[buf_pos++] = '\0';
Michael Sevakisffa86262013-07-12 12:03:20 -04001416
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001417 return 0;
1418}
1419
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001420static struct tagentry* get_entries(struct tree_context *tc)
1421{
1422 return core_get_data(tc->cache.entries_handle);
1423}
1424
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001425static int retrieve_entries(struct tree_context *c, int offset, bool init)
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001426{
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001427 struct tagcache_search tcs;
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +00001428 struct display_format *fmt;
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001429 int i;
1430 int namebufused = 0;
1431 int total_count = 0;
Miika Pekkarinenf9bfd732006-04-19 18:56:59 +00001432 int special_entry_count = 0;
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001433 int level = c->currextra;
Miika Pekkarinen991665b2006-04-19 11:03:37 +00001434 int tag;
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001435 bool sort = false;
Miika Pekkarinen8d145f62006-09-26 18:59:16 +00001436 int sort_limit;
1437 int strip;
Thomas Martitz16983af2009-08-20 19:31:09 +00001438
1439 /* Show search progress straight away if the disk needs to spin up,
1440 otherwise show it after the normal 1/2 second delay */
1441 show_search_progress(
Frank Gevaerts46573012008-10-07 19:37:33 +00001442#ifdef HAVE_DISK_STORAGE
Thomas Martitz16983af2009-08-20 19:31:09 +00001443 storage_disk_is_active()
Barry Wardellca576992008-02-27 17:42:51 +00001444#else
Thomas Martitz16983af2009-08-20 19:31:09 +00001445 true
Barry Wardell3e4d79e2008-02-26 19:25:07 +00001446#endif
Thomas Martitz16983af2009-08-20 19:31:09 +00001447 , 0);
1448
Thomas Martitzfc502e02009-05-30 16:10:34 +00001449 if (c->currtable == ALLSUBENTRIES)
Miika Pekkarinen991665b2006-04-19 11:03:37 +00001450 {
1451 tag = tag_title;
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001452 level--;
Miika Pekkarinen991665b2006-04-19 11:03:37 +00001453 }
1454 else
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001455 tag = csi->tagorder[level];
Miika Pekkarinen991665b2006-04-19 11:03:37 +00001456
William Wilgus501404d2019-07-29 20:49:41 -05001457 if (tag == menu_reload)
1458 return RELOAD_TAGTREE;
1459
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001460 if (!tagcache_search(&tcs, tag))
Miika Pekkarinen3b313462006-04-16 17:32:54 +00001461 return -1;
Michael Sevakisffa86262013-07-12 12:03:20 -04001462
Miika Pekkarinene3080642006-08-25 13:22:46 +00001463 /* Prevent duplicate entries in the search list. */
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001464 tagcache_search_set_uniqbuf(&tcs, uniqbuf, UNIQBUF_SIZE);
Michael Sevakisffa86262013-07-12 12:03:20 -04001465
Andrew Mahone1248a0d2009-06-03 08:19:32 +00001466 if (level || csi->clause_count[0] || TAGCACHE_IS_NUMERIC(tag))
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001467 sort = true;
Michael Sevakisffa86262013-07-12 12:03:20 -04001468
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001469 for (i = 0; i < level; i++)
1470 {
Andrew Mahone1248a0d2009-06-03 08:19:32 +00001471 if (TAGCACHE_IS_NUMERIC(csi->tagorder[i]))
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001472 {
1473 static struct tagcache_search_clause cc;
Michael Sevakisffa86262013-07-12 12:03:20 -04001474
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001475 memset(&cc, 0, sizeof(struct tagcache_search_clause));
1476 cc.tag = csi->tagorder[i];
1477 cc.type = clause_is;
1478 cc.numeric = true;
1479 cc.numeric_data = csi->result_seek[i];
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001480 tagcache_search_add_clause(&tcs, &cc);
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001481 }
1482 else
1483 {
Michael Sevakisffa86262013-07-12 12:03:20 -04001484 tagcache_search_add_filter(&tcs, csi->tagorder[i],
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001485 csi->result_seek[i]);
1486 }
1487 }
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001488
1489 /* because tagcache saves the clauses, we need to lock the buffer
1490 * for the entire duration of the search */
1491 tagtree_lock();
Miika Pekkarinen93bbd442006-08-25 21:13:49 +00001492 for (i = 0; i <= level; i++)
Miika Pekkarinene3080642006-08-25 13:22:46 +00001493 {
1494 int j;
Michael Sevakisffa86262013-07-12 12:03:20 -04001495
Miika Pekkarinene3080642006-08-25 13:22:46 +00001496 for (j = 0; j < csi->clause_count[i]; j++)
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001497 tagcache_search_add_clause(&tcs, csi->clause[i][j]);
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001498 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001499
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001500 current_offset = offset;
1501 current_entry_count = 0;
1502 c->dirfull = false;
Michael Sevakisffa86262013-07-12 12:03:20 -04001503
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001504 fmt = NULL;
1505 for (i = 0; i < format_count; i++)
1506 {
1507 if (formats[i]->group_id == csi->format_id[level])
1508 fmt = formats[i];
1509 }
1510
1511 if (fmt)
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +00001512 {
1513 sort_inverse = fmt->sort_inverse;
1514 sort_limit = fmt->limit;
Miika Pekkarinen8d145f62006-09-26 18:59:16 +00001515 strip = fmt->strip;
Robert Kuklaf9f2d6d2008-07-01 20:55:19 +00001516 sort = true;
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +00001517 }
1518 else
1519 {
1520 sort_inverse = false;
1521 sort_limit = 0;
Miika Pekkarinen8d145f62006-09-26 18:59:16 +00001522 strip = 0;
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +00001523 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001524
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001525 /* lock buflib out due to possible yields */
1526 tree_lock_cache(c);
1527 struct tagentry *dptr = core_get_data(c->cache.entries_handle);
1528
Miika Pekkarinen00422012006-07-16 15:04:46 +00001529 if (tag != tag_title && tag != tag_filename)
Miika Pekkarinen991665b2006-04-19 11:03:37 +00001530 {
1531 if (offset == 0)
1532 {
Thomas Martitzfc502e02009-05-30 16:10:34 +00001533 dptr->newtable = ALLSUBENTRIES;
Miika Pekkarinen991665b2006-04-19 11:03:37 +00001534 dptr->name = str(LANG_TAGNAVI_ALL_TRACKS);
1535 dptr++;
1536 current_entry_count++;
Michael Hohmuthe7c24492011-08-04 10:21:52 +00001537 special_entry_count++;
Miika Pekkarinen991665b2006-04-19 11:03:37 +00001538 }
Steve Bavinde741cd2007-12-21 13:31:50 +00001539 if (offset <= 1)
1540 {
Thomas Martitzfc502e02009-05-30 16:10:34 +00001541 dptr->newtable = NAVIBROWSE;
Steve Bavinde741cd2007-12-21 13:31:50 +00001542 dptr->name = str(LANG_TAGNAVI_RANDOM);
1543 dptr->extraseek = -1;
1544 dptr++;
1545 current_entry_count++;
Michael Hohmuthe7c24492011-08-04 10:21:52 +00001546 special_entry_count++;
Steve Bavinde741cd2007-12-21 13:31:50 +00001547 }
Michael Hohmuthe7c24492011-08-04 10:21:52 +00001548
1549 total_count += 2;
Miika Pekkarinen991665b2006-04-19 11:03:37 +00001550 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001551
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001552 while (tagcache_get_next(&tcs))
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001553 {
1554 if (total_count++ < offset)
1555 continue;
Michael Sevakisffa86262013-07-12 12:03:20 -04001556
Thomas Martitzfc502e02009-05-30 16:10:34 +00001557 dptr->newtable = NAVIBROWSE;
Miika Pekkarinen00422012006-07-16 15:04:46 +00001558 if (tag == tag_title || tag == tag_filename)
Miika Pekkarinen42946152006-08-30 18:18:37 +00001559 {
Thomas Martitzfc502e02009-05-30 16:10:34 +00001560 dptr->newtable = PLAYTRACK;
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001561 dptr->extraseek = tcs.idx_id;
Miika Pekkarinen42946152006-08-30 18:18:37 +00001562 }
1563 else
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001564 dptr->extraseek = tcs.result_seek;
Michael Sevakisffa86262013-07-12 12:03:20 -04001565
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001566 fmt = NULL;
1567 /* Check the format */
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001568 tagtree_lock();
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001569 for (i = 0; i < format_count; i++)
1570 {
1571 if (formats[i]->group_id != csi->format_id[level])
1572 continue;
Michael Sevakisffa86262013-07-12 12:03:20 -04001573
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001574 if (tagcache_check_clauses(&tcs, formats[i]->clause,
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001575 formats[i]->clause_count))
1576 {
1577 fmt = formats[i];
1578 break;
1579 }
1580 }
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001581 tagtree_unlock();
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001582
Michael Hohmuth05a19842011-06-01 08:00:37 +00001583 if (strcmp(tcs.result, UNTAGGED) == 0)
1584 {
1585 tcs.result = str(LANG_TAGNAVI_UNTAGGED);
1586 tcs.result_len = strlen(tcs.result);
1587 tcs.ramresult = true;
1588 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001589
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001590 if (!tcs.ramresult || fmt)
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001591 {
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001592 dptr->name = core_get_data(c->cache.name_buffer_handle)+namebufused;
Michael Sevakisffa86262013-07-12 12:03:20 -04001593
Miika Pekkarinenb89b5ba2006-10-15 11:01:18 +00001594 if (fmt)
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +00001595 {
Michael Hohmuth8207a4a2011-08-04 10:23:18 +00001596 int ret = format_str(&tcs, fmt, dptr->name,
1597 c->cache.name_buffer_size - namebufused);
1598 if (ret == -4) /* buffer full */
1599 {
1600 logf("chunk mode #2: %d", current_entry_count);
1601 c->dirfull = true;
1602 sort = false;
1603 break ;
1604 }
1605 else if (ret < 0)
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +00001606 {
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001607 logf("format_str() failed");
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001608 tagcache_search_finish(&tcs);
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001609 tree_unlock_cache(c);
1610 tagtree_unlock();
Miika Pekkarinen02df5a82006-10-24 16:20:48 +00001611 return 0;
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +00001612 }
Michael Hohmuth8207a4a2011-08-04 10:23:18 +00001613 else
1614 namebufused += strlen(dptr->name)+1;
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +00001615 }
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001616 else
Miika Pekkarinen45dfe2a2006-07-15 17:36:25 +00001617 {
Michael Hohmuth8207a4a2011-08-04 10:23:18 +00001618 namebufused += tcs.result_len;
1619 if (namebufused < c->cache.name_buffer_size)
1620 strcpy(dptr->name, tcs.result);
1621 else
1622 {
1623 logf("chunk mode #2a: %d", current_entry_count);
1624 c->dirfull = true;
1625 sort = false;
1626 break ;
1627 }
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001628 }
1629 }
1630 else
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001631 dptr->name = tcs.result;
Michael Sevakisffa86262013-07-12 12:03:20 -04001632
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001633 dptr++;
1634 current_entry_count++;
1635
Thomas Martitz98096972011-08-03 09:49:25 +00001636 if (current_entry_count >= c->cache.max_entries)
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001637 {
1638 logf("chunk mode #3: %d", current_entry_count);
1639 c->dirfull = true;
1640 sort = false;
1641 break ;
1642 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001643
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001644 if (init && !tcs.ramsearch)
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001645 {
Barry Wardellca576992008-02-27 17:42:51 +00001646 if (!show_search_progress(false, total_count))
Thomas Martitz16983af2009-08-20 19:31:09 +00001647 { /* user aborted */
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001648 tagcache_search_finish(&tcs);
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001649 tree_unlock_cache(c);
1650 tagtree_unlock();
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001651 return current_entry_count;
1652 }
1653 }
1654 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001655
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001656 if (sort)
Thomas Martitz98096972011-08-03 09:49:25 +00001657 {
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001658 struct tagentry *entries = get_entries(c);
1659 qsort(&entries[special_entry_count],
Miika Pekkarinenf9bfd732006-04-19 18:56:59 +00001660 current_entry_count - special_entry_count,
Thomas Martitz84c7d612014-01-15 23:18:35 +01001661 sizeof(struct tagentry),
1662 global_settings.interpret_numbers ? nat_compare : compare);
Thomas Martitz98096972011-08-03 09:49:25 +00001663 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001664
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001665 if (!init)
1666 {
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001667 tagcache_search_finish(&tcs);
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001668 tree_unlock_cache(c);
1669 tagtree_unlock();
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001670 return current_entry_count;
1671 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001672
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001673 while (tagcache_get_next(&tcs))
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001674 {
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001675 if (!tcs.ramsearch)
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001676 {
1677 if (!show_search_progress(false, total_count))
1678 break;
1679 }
1680 total_count++;
1681 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001682
Miika Pekkarinen8e2bdca2009-06-20 16:17:54 +00001683 tagcache_search_finish(&tcs);
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001684 tree_unlock_cache(c);
1685 tagtree_unlock();
1686
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +00001687 if (!sort && (sort_inverse || sort_limit))
1688 {
Nils Wallménius01729e72008-08-15 08:27:39 +00001689 splashf(HZ*4, ID2P(LANG_SHOWDIR_BUFFER_FULL), total_count);
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +00001690 logf("Too small dir buffer");
1691 return 0;
1692 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001693
Miika Pekkarinen4f1a2522006-08-26 22:00:22 +00001694 if (sort_limit)
1695 total_count = MIN(total_count, sort_limit);
Michael Sevakisffa86262013-07-12 12:03:20 -04001696
Miika Pekkarinen8d145f62006-09-26 18:59:16 +00001697 if (strip)
1698 {
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001699 dptr = get_entries(c);
Michael Hohmuthe7c24492011-08-04 10:21:52 +00001700 for (i = special_entry_count; i < current_entry_count; i++, dptr++)
Miika Pekkarinen8d145f62006-09-26 18:59:16 +00001701 {
1702 int len = strlen(dptr->name);
Michael Sevakisffa86262013-07-12 12:03:20 -04001703
Miika Pekkarinen8d145f62006-09-26 18:59:16 +00001704 if (len < strip)
1705 continue;
Michael Sevakisffa86262013-07-12 12:03:20 -04001706
Miika Pekkarinen8d145f62006-09-26 18:59:16 +00001707 dptr->name = &dptr->name[strip];
1708 }
1709 }
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001710
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001711 return total_count;
Michael Sevakisffa86262013-07-12 12:03:20 -04001712
Miika Pekkarinen75b6abb2006-04-15 13:57:15 +00001713}
1714
Miika Pekkarinen3b313462006-04-16 17:32:54 +00001715static int load_root(struct tree_context *c)
1716{
Thomas Martitzbaa070c2011-08-30 14:01:45 +00001717 struct tagentry *dptr = core_get_data(c->cache.entries_handle);
Miika Pekkarinen3b313462006-04-16 17:32:54 +00001718 int i;
Michael Sevakisffa86262013-07-12 12:03:20 -04001719
Miika Pekkarinen4e6c79b2006-07-25 07:41:00 +00001720 tc = c;
Thomas Martitzfc502e02009-05-30 16:10:34 +00001721 c->currtable = ROOT;
Miika Pekkarinenba34c372006-09-19 11:54:33 +00001722 if (c->dirlevel == 0)
Thomas Martitz38d51752009-05-30 16:35:28 +00001723 c->currextra = rootmenu;
Michael Sevakisffa86262013-07-12 12:03:20 -04001724
Miika Pekkarinen3ae1c102006-09-26 06:40:14 +00001725 menu = menus[c->currextra];
1726 if (menu == NULL)
1727 return 0;
Michael Sevakisffa86262013-07-12 12:03:20 -04001728
William Wilgus3e277052018-12-22 12:45:02 -06001729 if (menu->itemcount > c->cache.max_entries)
1730 panicf("%s tree_cache too small", __func__);
1731
Miika Pekkarinenba34c372006-09-19 11:54:33 +00001732 for (i = 0; i < menu->itemcount; i++)
Miika Pekkarinen3b313462006-04-16 17:32:54 +00001733 {
Miika Pekkarinenba34c372006-09-19 11:54:33 +00001734 dptr->name = menu->items[i]->name;
1735 switch (menu->items[i]->type)
1736 {
1737 case menu_next:
Thomas Martitzfc502e02009-05-30 16:10:34 +00001738 dptr->newtable = NAVIBROWSE;
Miika Pekkarinenba34c372006-09-19 11:54:33 +00001739 dptr->extraseek = i;
1740 break;
Michael Sevakisffa86262013-07-12 12:03:20 -04001741
Miika Pekkarinenba34c372006-09-19 11:54:33 +00001742 case menu_load:
Thomas Martitzfc502e02009-05-30 16:10:34 +00001743 dptr->newtable = ROOT;
Miika Pekkarinenba34c372006-09-19 11:54:33 +00001744 dptr->extraseek = menu->items[i]->link;
1745 break;
1746 }
1747
Miika Pekkarinen3b313462006-04-16 17:32:54 +00001748 dptr++;
1749 }
Michael Sevakisffa86262013-07-12 12:03:20 -04001750
Miika Pekkarinen3b313462006-04-16 17:32:54 +00001751 current_offset = 0;
1752 current_entry_count = i;
Michael Sevakisffa86262013-07-12 12:03:20 -04001753
Miika Pekkarinen3b313462006-04-16 17:32:54 +00001754 return i;
1755}
1756
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001757int tagtree_load(struct tree_context* c)
1758{
Miika Pekkarinen3b313462006-04-16 17:32:54 +00001759 int count;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001760 int table = c->currtable;
Michael Sevakisffa86262013-07-12 12:03:20 -04001761
Steve Bavine9947742006-10-20 06:49:31 +00001762 c->dirsindir = 0;
Miika Pekkarinen7c4e0c82006-03-26 11:33:42 +00001763
1764 if (!table)
1765 {
1766 c->dirfull = false;
Thomas Martitzfc502e02009-05-30 16:10:34 +0000