summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/tagcache.c355
-rw-r--r--apps/tagcache.h4
-rw-r--r--apps/tagnavi.config36
-rw-r--r--apps/tagtree.c233
-rw-r--r--apps/tagtree.h1
5 files changed, 398 insertions, 231 deletions
diff --git a/apps/tagcache.c b/apps/tagcache.c
index ca2421171b..38537b5ceb 100644
--- a/apps/tagcache.c
+++ b/apps/tagcache.c
@@ -538,6 +538,96 @@ static bool write_index(int masterfd, int idxid, struct index_entry *idx)
538 return true; 538 return true;
539} 539}
540 540
541static bool open_files(struct tagcache_search *tcs, int tag)
542{
543 if (tcs->idxfd[tag] < 0)
544 {
545 char fn[MAX_PATH];
546
547 snprintf(fn, sizeof fn, TAGCACHE_FILE_INDEX, tag);
548 tcs->idxfd[tag] = open(fn, O_RDONLY);
549 }
550
551 if (tcs->idxfd[tag] < 0)
552 {
553 logf("File not open!");
554 return false;
555 }
556
557 return true;
558}
559
560static bool retrieve(struct tagcache_search *tcs, struct index_entry *idx,
561 int tag, char *buf, long size)
562{
563 struct tagfile_entry tfe;
564 long seek;
565
566 *buf = '\0';
567
568 if (tagcache_is_numeric_tag(tag))
569 return false;
570
571 seek = idx->tag_seek[tag];
572 if (seek < 0)
573 {
574 logf("Retrieve failed");
575 return false;
576 }
577
578#ifdef HAVE_TC_RAMCACHE
579 if (tcs->ramsearch)
580 {
581 struct tagfile_entry *ep;
582
583# ifdef HAVE_DIRCACHE
584 if (tag == tag_filename && idx->flag & FLAG_DIRCACHE)
585 {
586 dircache_copy_path((struct dircache_entry *)seek,
587 buf, size);
588 return true;
589 }
590 else
591# endif
592 if (tag != tag_filename)
593 {
594 ep = (struct tagfile_entry *)&hdr->tags[tag][seek];
595 strncpy(buf, ep->tag_data, size-1);
596
597 return true;
598 }
599 }
600#endif
601
602 if (!open_files(tcs, tag))
603 return false;
604
605 lseek(tcs->idxfd[tag], seek, SEEK_SET);
606 if (read(tcs->idxfd[tag], &tfe, sizeof(struct tagfile_entry)) !=
607 sizeof(struct tagfile_entry))
608 {
609 logf("read error #5");
610 return false;
611 }
612
613 if (tfe.tag_length >= size)
614 {
615 logf("too small buffer");
616 return false;
617 }
618
619 if (read(tcs->idxfd[tag], buf, tfe.tag_length) !=
620 tfe.tag_length)
621 {
622 logf("read error #6");
623 return false;
624 }
625
626 buf[tfe.tag_length] = '\0';
627
628 return true;
629}
630
541static long check_virtual_tags(int tag, const struct index_entry *idx) 631static long check_virtual_tags(int tag, const struct index_entry *idx)
542{ 632{
543 long data = 0; 633 long data = 0;
@@ -671,6 +761,96 @@ static bool check_against_clause(long numeric, const char *str,
671 return false; 761 return false;
672} 762}
673 763
764bool check_clauses(struct tagcache_search *tcs,
765 struct index_entry *idx,
766 struct tagcache_search_clause **clause, int count)
767{
768 int i;
769
770#ifdef HAVE_TC_RAMCACHE
771 if (tcs->ramsearch)
772 {
773 /* Go through all conditional clauses. */
774 for (i = 0; i < count; i++)
775 {
776 struct tagfile_entry *tfe;
777 int seek;
778 char buf[256];
779 char *str = NULL;
780
781 seek = check_virtual_tags(clause[i]->tag, idx);
782
783 if (!tagcache_is_numeric_tag(clause[i]->tag))
784 {
785 if (clause[i]->tag == tag_filename)
786 {
787 retrieve(tcs, idx, tag_filename, buf, sizeof buf);
788 str = buf;
789 }
790 else
791 {
792 tfe = (struct tagfile_entry *)&hdr->tags[clause[i]->tag][seek];
793 str = tfe->tag_data;
794 }
795 }
796
797 if (!check_against_clause(seek, str, clause[i]))
798 return false;
799 }
800 }
801 else
802#endif
803 {
804 /* Check for conditions. */
805 for (i = 0; i < count; i++)
806 {
807 struct tagfile_entry tfe;
808 int seek;
809 char str[256];
810
811 seek = check_virtual_tags(clause[i]->tag, idx);
812
813 memset(str, 0, sizeof str);
814 if (!tagcache_is_numeric_tag(clause[i]->tag))
815 {
816 int fd = tcs->idxfd[clause[i]->tag];
817 lseek(fd, seek, SEEK_SET);
818 read(fd, &tfe, sizeof(struct tagfile_entry));
819 if (tfe.tag_length >= (int)sizeof(str))
820 {
821 logf("Too long tag read!");
822 break ;
823 }
824
825 read(fd, str, tfe.tag_length);
826
827 /* Check if entry has been deleted. */
828 if (str[0] == '\0')
829 break;
830 }
831
832 if (!check_against_clause(seek, str, clause[i]))
833 return false;
834 }
835 }
836
837 return true;
838}
839
840bool tagcache_check_clauses(struct tagcache_search *tcs,
841 struct tagcache_search_clause **clause, int count)
842{
843 struct index_entry idx;
844
845 if (count == 0)
846 return true;
847
848 if (!get_index(tcs->masterfd, tcs->idx_id, &idx, true))
849 return false;
850
851 return check_clauses(tcs, &idx, clause, count);
852}
853
674static bool add_uniqbuf(struct tagcache_search *tcs, long id) 854static bool add_uniqbuf(struct tagcache_search *tcs, long id)
675{ 855{
676 int i; 856 int i;
@@ -713,18 +893,18 @@ static bool build_lookup_list(struct tagcache_search *tcs)
713 893
714 for (i = tcs->seek_pos; i < hdr->h.tch.entry_count; i++) 894 for (i = tcs->seek_pos; i < hdr->h.tch.entry_count; i++)
715 { 895 {
896 struct index_entry *idx = &hdr->indices[i];
716 if (tcs->seek_list_count == SEEK_LIST_SIZE) 897 if (tcs->seek_list_count == SEEK_LIST_SIZE)
717 break ; 898 break ;
718 899
719 /* Skip deleted files. */ 900 /* Skip deleted files. */
720 if (hdr->indices[i].flag & FLAG_DELETED) 901 if (idx->flag & FLAG_DELETED)
721 continue; 902 continue;
722 903
723 /* Go through all filters.. */ 904 /* Go through all filters.. */
724 for (j = 0; j < tcs->filter_count; j++) 905 for (j = 0; j < tcs->filter_count; j++)
725 { 906 {
726 if (hdr->indices[i].tag_seek[tcs->filter_tag[j]] != 907 if (idx->tag_seek[tcs->filter_tag[j]] != tcs->filter_seek[j])
727 tcs->filter_seek[j])
728 { 908 {
729 break ; 909 break ;
730 } 910 }
@@ -733,51 +913,17 @@ static bool build_lookup_list(struct tagcache_search *tcs)
733 if (j < tcs->filter_count) 913 if (j < tcs->filter_count)
734 continue ; 914 continue ;
735 915
736 /* Go through all conditional clauses. */ 916 /* Check for conditions. */
737 for (j = 0; j < tcs->clause_count; j++) 917 if (!check_clauses(tcs, idx, tcs->clause, tcs->clause_count))
738 { 918 continue;
739 struct index_entry *idx = &hdr->indices[i];
740 int seek;
741 char buf[256];
742 char *str = NULL;
743 struct tagfile_entry *entry;
744
745 seek = check_virtual_tags(tcs->clause[j]->tag, idx);
746
747 if (!tagcache_is_numeric_tag(tcs->clause[j]->tag))
748 {
749 if (tcs->clause[j]->tag == tag_filename)
750 {
751 int oldtype = tcs->type;
752 tcs->type = tag_filename;
753 tagcache_retrieve(tcs, i, buf, sizeof buf);
754 tcs->type = oldtype;
755 str = buf;
756 }
757 else
758 {
759 entry = (struct tagfile_entry *)&hdr->tags[tcs->clause[j]->tag][seek];
760 str = entry->tag_data;
761 }
762 }
763
764
765 if (!check_against_clause(seek, str, tcs->clause[j]))
766 break ;
767 }
768
769 if (j < tcs->clause_count)
770 continue ;
771 919
772 /* Add to the seek list if not already in uniq buffer. */ 920 /* Add to the seek list if not already in uniq buffer. */
773 if (!add_uniqbuf(tcs, hdr->indices[i].tag_seek[tcs->type])) 921 if (!add_uniqbuf(tcs, idx->tag_seek[tcs->type]))
774 continue; 922 continue;
775 923
776 /* Lets add it. */ 924 /* Lets add it. */
777 tcs->seek_list[tcs->seek_list_count] = 925 tcs->seek_list[tcs->seek_list_count] = idx->tag_seek[tcs->type];
778 hdr->indices[i].tag_seek[tcs->type]; 926 tcs->seek_flags[tcs->seek_list_count] = idx->flag;
779 tcs->seek_flags[tcs->seek_list_count] =
780 hdr->indices[i].flag;
781 tcs->seek_list_count++; 927 tcs->seek_list_count++;
782 } 928 }
783 929
@@ -813,39 +959,8 @@ static bool build_lookup_list(struct tagcache_search *tcs)
813 continue ; 959 continue ;
814 960
815 /* Check for conditions. */ 961 /* Check for conditions. */
816 for (i = 0; i < tcs->clause_count; i++) 962 if (!check_clauses(tcs, &entry, tcs->clause, tcs->clause_count))
817 { 963 continue;
818 struct tagfile_entry tfe;
819 int seek;
820 char str[256];
821
822 seek = check_virtual_tags(tcs->clause[i]->tag, &entry);
823
824 memset(str, 0, sizeof str);
825 if (!tagcache_is_numeric_tag(tcs->clause[i]->tag))
826 {
827 int fd = tcs->idxfd[tcs->clause[i]->tag];
828 lseek(fd, seek, SEEK_SET);
829 read(fd, &tfe, sizeof(struct tagfile_entry));
830 if (tfe.tag_length >= (int)sizeof(str))
831 {
832 logf("Too long tag read!");
833 break ;
834 }
835
836 read(fd, str, tfe.tag_length);
837
838 /* Check if entry has been deleted. */
839 if (str[0] == '\0')
840 break;
841 }
842
843 if (!check_against_clause(seek, str, tcs->clause[i]))
844 break ;
845 }
846
847 if (i < tcs->clause_count)
848 continue ;
849 964
850 /* Add to the seek list if not already in uniq buffer. */ 965 /* Add to the seek list if not already in uniq buffer. */
851 if (!add_uniqbuf(tcs, entry.tag_seek[tcs->type])) 966 if (!add_uniqbuf(tcs, entry.tag_seek[tcs->type]))
@@ -1025,25 +1140,6 @@ bool tagcache_search_add_clause(struct tagcache_search *tcs,
1025 return true; 1140 return true;
1026} 1141}
1027 1142
1028static bool open_files(struct tagcache_search *tcs)
1029{
1030 if (tcs->idxfd[tcs->type] < 0)
1031 {
1032 char fn[MAX_PATH];
1033
1034 snprintf(fn, sizeof fn, TAGCACHE_FILE_INDEX, tcs->type);
1035 tcs->idxfd[tcs->type] = open(fn, O_RDONLY);
1036 }
1037
1038 if (tcs->idxfd[tcs->type] < 0)
1039 {
1040 logf("File not open!");
1041 return false;
1042 }
1043
1044 return true;
1045}
1046
1047#define TAG_FILENAME_RAM(tcs) ((tcs->type == tag_filename) \ 1143#define TAG_FILENAME_RAM(tcs) ((tcs->type == tag_filename) \
1048 ? (flag & FLAG_DIRCACHE) : 1) 1144 ? (flag & FLAG_DIRCACHE) : 1)
1049 1145
@@ -1085,7 +1181,7 @@ static bool get_next(struct tagcache_search *tcs)
1085 if ((!tcs->ramsearch || !TAG_FILENAME_RAM(tcs)) 1181 if ((!tcs->ramsearch || !TAG_FILENAME_RAM(tcs))
1086 && !tagcache_is_numeric_tag(tcs->type)) 1182 && !tagcache_is_numeric_tag(tcs->type))
1087 { 1183 {
1088 if (!open_files(tcs)) 1184 if (!open_files(tcs, tcs->type))
1089 return false; 1185 return false;
1090 1186
1091 lseek(tcs->idxfd[tcs->type], tcs->seek_list[tcs->seek_list_count], SEEK_SET); 1187 lseek(tcs->idxfd[tcs->type], tcs->seek_list[tcs->seek_list_count], SEEK_SET);
@@ -1144,7 +1240,7 @@ static bool get_next(struct tagcache_search *tcs)
1144 else 1240 else
1145#endif 1241#endif
1146 { 1242 {
1147 if (!open_files(tcs)) 1243 if (!open_files(tcs, tcs->type))
1148 return false; 1244 return false;
1149 1245
1150 tcs->result_seek = lseek(tcs->idxfd[tcs->type], 0, SEEK_CUR); 1246 tcs->result_seek = lseek(tcs->idxfd[tcs->type], 0, SEEK_CUR);
@@ -1193,72 +1289,13 @@ bool tagcache_get_next(struct tagcache_search *tcs)
1193bool tagcache_retrieve(struct tagcache_search *tcs, int idxid, 1289bool tagcache_retrieve(struct tagcache_search *tcs, int idxid,
1194 char *buf, long size) 1290 char *buf, long size)
1195{ 1291{
1196 struct tagfile_entry tfe;
1197 struct index_entry idx; 1292 struct index_entry idx;
1198 long seek;
1199 1293
1200 *buf = '\0'; 1294 *buf = '\0';
1201 if (!get_index(tcs->masterfd, idxid, &idx, true)) 1295 if (!get_index(tcs->masterfd, idxid, &idx, true))
1202 return false; 1296 return false;
1203 1297
1204 seek = idx.tag_seek[tcs->type]; 1298 return retrieve(tcs, &idx, tcs->type, buf, size);
1205 if (seek < 0)
1206 {
1207 logf("Retrieve failed");
1208 return false;
1209 }
1210
1211#ifdef HAVE_TC_RAMCACHE
1212 if (tcs->ramsearch)
1213 {
1214 struct tagfile_entry *ep;
1215
1216# ifdef HAVE_DIRCACHE
1217 if (tcs->type == tag_filename && hdr->indices[idxid].flag & FLAG_DIRCACHE)
1218 {
1219 dircache_copy_path((struct dircache_entry *)seek,
1220 buf, size);
1221 return true;
1222 }
1223 else
1224# endif
1225 if (tcs->type != tag_filename)
1226 {
1227 ep = (struct tagfile_entry *)&hdr->tags[tcs->type][seek];
1228 strncpy(buf, ep->tag_data, size-1);
1229
1230 return true;
1231 }
1232 }
1233#endif
1234
1235 if (!open_files(tcs))
1236 return false;
1237
1238 lseek(tcs->idxfd[tcs->type], seek, SEEK_SET);
1239 if (read(tcs->idxfd[tcs->type], &tfe, sizeof(struct tagfile_entry)) !=
1240 sizeof(struct tagfile_entry))
1241 {
1242 logf("read error #5");
1243 return false;
1244 }
1245
1246 if (tfe.tag_length >= size)
1247 {
1248 logf("too small buffer");
1249 return false;
1250 }
1251
1252 if (read(tcs->idxfd[tcs->type], buf, tfe.tag_length) !=
1253 tfe.tag_length)
1254 {
1255 logf("read error #6");
1256 return false;
1257 }
1258
1259 buf[tfe.tag_length] = '\0';
1260
1261 return true;
1262} 1299}
1263 1300
1264#if 0 1301#if 0
@@ -2961,11 +2998,13 @@ static bool delete_entry(long idx_id)
2961 2998
2962 /* Skip the header block */ 2999 /* Skip the header block */
2963 lseek(fd, myidx.tag_seek[tag] + sizeof(struct tagfile_entry), SEEK_SET); 3000 lseek(fd, myidx.tag_seek[tag] + sizeof(struct tagfile_entry), SEEK_SET);
2964 3001
3002 /* Debug, print 10 first characters of the tag
2965 read(fd, buf, 10); 3003 read(fd, buf, 10);
2966 buf[10]='\0'; 3004 buf[10]='\0';
2967 logf("TAG:%s", buf); 3005 logf("TAG:%s", buf);
2968 lseek(fd, -10, SEEK_CUR); 3006 lseek(fd, -10, SEEK_CUR);
3007 */
2969 3008
2970 /* Write first data byte in tag as \0 */ 3009 /* Write first data byte in tag as \0 */
2971 write(fd, "", 1); 3010 write(fd, "", 1);
diff --git a/apps/tagcache.h b/apps/tagcache.h
index f2694cea1b..37423e63c8 100644
--- a/apps/tagcache.h
+++ b/apps/tagcache.h
@@ -98,7 +98,7 @@ struct tagcache_search_clause
98 bool numeric; 98 bool numeric;
99 bool input; 99 bool input;
100 long numeric_data; 100 long numeric_data;
101 char str[128]; 101 char *str;
102}; 102};
103 103
104struct tagcache_search { 104struct tagcache_search {
@@ -139,6 +139,8 @@ bool tagcache_is_numeric_tag(int type);
139bool tagcache_is_unique_tag(int type); 139bool tagcache_is_unique_tag(int type);
140bool tagcache_is_sorted_tag(int type); 140bool tagcache_is_sorted_tag(int type);
141bool tagcache_find_index(struct tagcache_search *tcs, const char *filename); 141bool tagcache_find_index(struct tagcache_search *tcs, const char *filename);
142bool tagcache_check_clauses(struct tagcache_search *tcs,
143 struct tagcache_search_clause **clause, int count);
142bool tagcache_search(struct tagcache_search *tcs, int tag); 144bool tagcache_search(struct tagcache_search *tcs, int tag);
143void tagcache_search_set_uniqbuf(struct tagcache_search *tcs, 145void tagcache_search_set_uniqbuf(struct tagcache_search *tcs,
144 void *buffer, long length); 146 void *buffer, long length);
diff --git a/apps/tagnavi.config b/apps/tagnavi.config
index fdf66828af..191b4bdda5 100644
--- a/apps/tagnavi.config
+++ b/apps/tagnavi.config
@@ -5,6 +5,15 @@
5# Instead, you can modify "/.rockbox/tagnavi_custom.config" which will never 5# Instead, you can modify "/.rockbox/tagnavi_custom.config" which will never
6# get overwritten automatically. 6# get overwritten automatically.
7 7
8# Basic format declarations
9%format "fmt_title" "%02d. %s" tracknum title ? tracknum > "0"
10%format "fmt_title" "%s" title
11%format "fmt_mostplayed" "(%3d) %s" playcount title %sort = "inverse" %limit = "100"
12%format "fmt_lastplayed" "%06d%s" lastplayed title %sort = "inverse" %limit = "99" %strip = "6"
13%format "fmt_best_tracks" "%02d. %s (%3d)" tracknum title autoscore
14%format "fmt_played" "(%3d/%d) %s" autoscore playcount title
15%format "fmt_score" "(%3d) %s" autoscore title
16
8# Include our custom menu 17# Include our custom menu
9%include "/.rockbox/tagnavi_custom.config" 18%include "/.rockbox/tagnavi_custom.config"
10 19
@@ -14,11 +23,11 @@
14 23
15# Define the search sub menu 24# Define the search sub menu
16%menu_start "search" "Search by..." 25%menu_start "search" "Search by..."
17"Artist" -> artist ? artist ~ "" -> album -> title = "%02d. %s" tracknum title 26"Artist" -> artist ? artist ~ "" -> album -> title = "fmt_title"
18"Album" -> album ? album ~ "" -> title = "%02d. %s" tracknum title 27"Album" -> album ? album ~ "" -> title = "fmt_title"
19"Title" -> title ? title ~ "" 28"Title" -> title ? title ~ ""
20"Filename" -> filename ? filename ~ "" 29"Filename" -> filename ? filename ~ ""
21"Score" -> title = "(%3d) %s" autoscore title ? autoscore > "" 30"Score" -> title = "fmt_score" ? autoscore > ""
22 31
23# ^ An empy line ends the menu 32# ^ An empy line ends the menu
24 33
@@ -28,19 +37,20 @@
28 37
29# Define the title of the main menu 38# Define the title of the main menu
30%menu_start "main" "Browse by..." 39%menu_start "main" "Browse by..."
31"Artist" -> artist -> album -> title = "%02d. %s" tracknum title 40"Artist" -> artist -> album -> title = "fmt_title"
32"Album" -> album -> title = "%02d. %s" tracknum title 41"Album" -> album -> title = "fmt_title"
33"Genre" -> genre -> artist -> album -> title = "%02d. %s" tracknum title 42"Genre" -> genre -> artist -> album -> title = "fmt_title"
34"Composer" -> composer -> album -> title = "%02d. %s" tracknum title 43"Composer" -> composer -> album -> title = "fmt_title"
35"Track" -> title 44"Track" -> title
36"Year" -> year ? year > "1000" & year < "2008" -> artist -> album -> title = "%02d. %s" tracknum title 45"Year" -> year ? year > "1000" & year < "2008" -> artist -> album -> title = "fmt_title"
37"Search..." ==> "search" 46"Search..." ==> "search"
38"Most played tracks" -> title = "(%3d) %s" playcount title %sort = "inverse" %limit = "100" ? playcount > "0" 47"Most played tracks" -> title = "fmt_mostplayed" ? playcount > "0"
39"Last played tracks" -> title = "%06d%s" lastplayed title %sort = "inverse" %limit = "99" %strip = "6" ? playcount > "0" 48"Last played tracks" -> title = "fmt_lastplayed" ? playcount > "0"
40"Never played tracks" -> artist ? playcount == "0" -> album -> title = "%02d. %s" tracknum title 49"Never played tracks" -> artist ? playcount == "0" -> album -> title = "fmt_title"
41"Best tracks" -> artist ? playcount > "1" & autoscore > "85" -> album -> title = "%02d. %s (%3d)" tracknum title autoscore 50"Best tracks" -> artist ? playcount > "1" & autoscore > "85" -> album -> title = "fmt_best_tracks"
42"List played tracks" -> title = "(%3d/%d) %s" autoscore playcount title ? playcount > "0" 51"List played tracks" -> title = "fmt_played" ? playcount > "0"
43"Custom view..." ==> "custom" 52"Custom view..." ==> "custom"
44 53
45# And finally set main menu as our root menu 54# And finally set main menu as our root menu
46%root_menu "main" 55%root_menu "main"
56
diff --git a/apps/tagtree.c b/apps/tagtree.c
index d8d8597d68..a360098aee 100644
--- a/apps/tagtree.c
+++ b/apps/tagtree.c
@@ -56,6 +56,7 @@ enum variables {
56 var_menu_start, 56 var_menu_start,
57 var_include, 57 var_include,
58 var_rootmenu, 58 var_rootmenu,
59 var_format,
59 menu_next, 60 menu_next,
60 menu_load, 61 menu_load,
61}; 62};
@@ -82,8 +83,11 @@ static bool sort_inverse;
82 * sort_inverse = true 83 * sort_inverse = true
83 */ 84 */
84struct display_format { 85struct display_format {
85 bool valid; 86 char name[32];
86 char formatstr[64]; 87 struct tagcache_search_clause *clause[TAGCACHE_MAX_CLAUSES];
88 int clause_count;
89 char *formatstr;
90 int group_id;
87 int tags[MAX_TAGS]; 91 int tags[MAX_TAGS];
88 int tag_count; 92 int tag_count;
89 93
@@ -92,12 +96,15 @@ struct display_format {
92 bool sort_inverse; 96 bool sort_inverse;
93}; 97};
94 98
99static struct display_format *formats[TAGMENU_MAX_FMTS];
100static int format_count;
101
95struct search_instruction { 102struct search_instruction {
96 char name[64]; 103 char name[64];
97 int tagorder[MAX_TAGS]; 104 int tagorder[MAX_TAGS];
98 int tagorder_count; 105 int tagorder_count;
99 struct tagcache_search_clause clause[MAX_TAGS][TAGCACHE_MAX_CLAUSES]; 106 struct tagcache_search_clause *clause[MAX_TAGS][TAGCACHE_MAX_CLAUSES];
100 struct display_format format[MAX_TAGS]; 107 int format_id[MAX_TAGS];
101 int clause_count[MAX_TAGS]; 108 int clause_count[MAX_TAGS];
102 int result_seek[MAX_TAGS]; 109 int result_seek[MAX_TAGS];
103}; 110};
@@ -130,6 +137,7 @@ static int root_menu;
130static int current_offset; 137static int current_offset;
131static int current_entry_count; 138static int current_entry_count;
132 139
140static int format_count;
133static struct tree_context *tc; 141static struct tree_context *tc;
134 142
135static int get_token_str(char *buf, int size) 143static int get_token_str(char *buf, int size)
@@ -200,6 +208,7 @@ static int get_tag(int *tag)
200 MATCH(tag, buf, "%menu_start", var_menu_start); 208 MATCH(tag, buf, "%menu_start", var_menu_start);
201 MATCH(tag, buf, "%include", var_include); 209 MATCH(tag, buf, "%include", var_include);
202 MATCH(tag, buf, "%root_menu", var_rootmenu); 210 MATCH(tag, buf, "%root_menu", var_rootmenu);
211 MATCH(tag, buf, "%format", var_format);
203 MATCH(tag, buf, "->", menu_next); 212 MATCH(tag, buf, "->", menu_next);
204 MATCH(tag, buf, "==>", menu_load); 213 MATCH(tag, buf, "==>", menu_load);
205 214
@@ -249,45 +258,36 @@ static int get_clause(int *condition)
249 return 0; 258 return 0;
250} 259}
251 260
252static bool add_clause(struct search_instruction *inst, 261static bool read_clause(struct tagcache_search_clause *clause)
253 int tag, int type, const char *str)
254{ 262{
255 int len = strlen(str); 263 char buf[256];
256 struct tagcache_search_clause *clause;
257 264
258 if (inst->clause_count[inst->tagorder_count] >= TAGCACHE_MAX_CLAUSES) 265 if (get_tag(&clause->tag) <= 0)
259 {
260 logf("Too many clauses");
261 return false; 266 return false;
262 }
263 267
264 clause = &inst->clause[inst->tagorder_count] 268 if (get_clause(&clause->type) <= 0)
265 [inst->clause_count[inst->tagorder_count]];
266 if (len >= (int)sizeof(clause->str) - 1)
267 {
268 logf("Too long str in condition");
269 return false; 269 return false;
270 }
271 270
272 clause->tag = tag; 271 if (get_token_str(buf, sizeof buf) < 0)
273 clause->type = type; 272 return false;
274 if (len == 0) 273
274 clause->str = buffer_alloc(strlen(buf)+1);
275 strcpy(clause->str, buf);
276
277 logf("got clause: %d/%d [%s]", clause->tag, clause->type, clause->str);
278
279 if (*(clause->str) == '\0')
275 clause->input = true; 280 clause->input = true;
276 else 281 else
277 clause->input = false; 282 clause->input = false;
278 283
279 if (tagcache_is_numeric_tag(tag)) 284 if (tagcache_is_numeric_tag(clause->tag))
280 { 285 {
281 clause->numeric = true; 286 clause->numeric = true;
282 clause->numeric_data = atoi(str); 287 clause->numeric_data = atoi(clause->str);
283 } 288 }
284 else 289 else
285 {
286 clause->numeric = false; 290 clause->numeric = false;
287 strcpy(clause->str, str);
288 }
289
290 inst->clause_count[inst->tagorder_count]++;
291 291
292 return true; 292 return true;
293} 293}
@@ -313,12 +313,38 @@ static int get_format_str(struct display_format *fmt)
313{ 313{
314 int ret; 314 int ret;
315 char buf[128]; 315 char buf[128];
316 int i;
316 317
317 memset(fmt, 0, sizeof(struct display_format)); 318 memset(fmt, 0, sizeof(struct display_format));
318 319
319 if (get_token_str(fmt->formatstr, sizeof fmt->formatstr) < 0) 320 if (get_token_str(fmt->name, sizeof fmt->name) < 0)
321 return -10;
322
323 /* Determine the group id */
324 fmt->group_id = 0;
325 for (i = 0; i < format_count; i++)
326 {
327 if (!strcasecmp(formats[i]->name, fmt->name))
328 {
329 fmt->group_id = formats[i]->group_id;
330 break;
331 }
332
333 if (formats[i]->group_id > fmt->group_id)
334 fmt->group_id = formats[i]->group_id;
335 }
336
337 if (i == format_count)
338 fmt->group_id++;
339
340 logf("format: (%d) %s", fmt->group_id, fmt->name);
341
342 if (get_token_str(buf, sizeof buf) < 0)
320 return -10; 343 return -10;
321 344
345 fmt->formatstr = buffer_alloc(strlen(buf) + 1);
346 strcpy(fmt->formatstr, buf);
347
322 while (fmt->tag_count < MAX_TAGS) 348 while (fmt->tag_count < MAX_TAGS)
323 { 349 {
324 ret = get_tag(&fmt->tags[fmt->tag_count]); 350 ret = get_tag(&fmt->tags[fmt->tag_count]);
@@ -353,27 +379,79 @@ static int get_format_str(struct display_format *fmt)
353 } 379 }
354 } 380 }
355 381
356 fmt->valid = true; 382 return 1;
383}
384
385static int add_format(const char *buf)
386{
387 strp = buf;
388
389 if (formats[format_count] == NULL)
390 formats[format_count] = buffer_alloc(sizeof(struct display_format));
391
392 memset(formats[format_count], 0, sizeof(struct display_format));
393 if (get_format_str(formats[format_count]) < 0)
394 {
395 logf("get_format_str() parser failed!");
396 return -4;
397 }
398
399 while (*strp != '\0' && *strp != '?')
400 strp++;
401
402 if (*strp == '?')
403 {
404 int clause_count = 0;
405 strp++;
406
407 while (1)
408 {
409 formats[format_count]->clause[clause_count] =
410 buffer_alloc(sizeof(struct tagcache_search_clause));
411
412 if (!read_clause(formats[format_count]->clause[clause_count]))
413 break;
414
415 clause_count++;
416 }
417
418 formats[format_count]->clause_count = clause_count;
419 }
420
421 format_count++;
357 422
358 return 1; 423 return 1;
359} 424}
360 425
361static int get_condition(struct search_instruction *inst) 426static int get_condition(struct search_instruction *inst)
362{ 427{
363 int tag; 428 int clause_count;
364 int condition;
365 char buf[128]; 429 char buf[128];
366 430
367 switch (*strp) 431 switch (*strp)
368 { 432 {
369 case '=': 433 case '=':
370 if (get_format_str(&inst->format[inst->tagorder_count]) < 0) 434 {
435 int i;
436
437 if (get_token_str(buf, sizeof buf) < 0)
438 return -1;
439
440 for (i = 0; i < format_count; i++)
371 { 441 {
372 logf("get_format_str() parser failed!"); 442 if (!strcasecmp(formats[i]->name, buf))
373 return -4; 443 break;
374 } 444 }
445
446 if (i == format_count)
447 {
448 logf("format not found: %s", buf);
449 return -2;
450 }
451
452 inst->format_id[inst->tagorder_count] = formats[i]->group_id;
375 return 1; 453 return 1;
376 454 }
377 case '?': 455 case '?':
378 case ' ': 456 case ' ':
379 case '&': 457 case '&':
@@ -384,17 +462,20 @@ static int get_condition(struct search_instruction *inst)
384 return 0; 462 return 0;
385 } 463 }
386 464
387 if (get_tag(&tag) <= 0) 465 clause_count = inst->clause_count[inst->tagorder_count];
388 return -1; 466 if (clause_count >= TAGCACHE_MAX_CLAUSES)
467 {
468 logf("Too many clauses");
469 return false;
470 }
389 471
390 if (get_clause(&condition) <= 0) 472 inst->clause[inst->tagorder_count][clause_count] =
391 return -2; 473 buffer_alloc(sizeof(struct tagcache_search_clause));
392 474
393 if (get_token_str(buf, sizeof buf) < 0) 475 if (!read_clause(inst->clause[inst->tagorder_count][clause_count]))
394 return -3; 476 return -1;
395 477
396 logf("got clause: %d/%d [%s]", tag, condition, buf); 478 inst->clause_count[inst->tagorder_count]++;
397 add_clause(inst, tag, condition, buf);
398 479
399 return 1; 480 return 1;
400} 481}
@@ -465,7 +546,9 @@ static bool parse_search(struct menu_entry *entry, const char *str)
465 546
466 logf("tag: %d", inst->tagorder[inst->tagorder_count]); 547 logf("tag: %d", inst->tagorder[inst->tagorder_count]);
467 548
468 while (get_condition(inst) > 0) ; 549 while ( (ret = get_condition(inst)) > 0 ) ;
550 if (ret < 0)
551 return false;
469 552
470 inst->tagorder_count++; 553 inst->tagorder_count++;
471 554
@@ -658,6 +741,13 @@ static bool parse_menu(const char *filename)
658 741
659 switch (variable) 742 switch (variable)
660 { 743 {
744 case var_format:
745 if (add_format(strp) < 0)
746 {
747 logf("Format add fail: %s", data);
748 }
749 break;
750
661 case var_include: 751 case var_include:
662 if (get_token_str(data, sizeof(data)) < 0) 752 if (get_token_str(data, sizeof(data)) < 0)
663 { 753 {
@@ -731,9 +821,9 @@ static bool parse_menu(const char *filename)
731 menu->items[menu->itemcount] = buffer_alloc(sizeof(struct menu_entry)); 821 menu->items[menu->itemcount] = buffer_alloc(sizeof(struct menu_entry));
732 memset(menu->items[menu->itemcount], 0, sizeof(struct menu_entry)); 822 memset(menu->items[menu->itemcount], 0, sizeof(struct menu_entry));
733 menu->items[menu->itemcount]->si = buffer_alloc(sizeof(struct search_instruction)); 823 menu->items[menu->itemcount]->si = buffer_alloc(sizeof(struct search_instruction));
734 memset(menu->items[menu->itemcount]->si, 0, sizeof(struct search_instruction));
735 } 824 }
736 825
826 memset(menu->items[menu->itemcount]->si, 0, sizeof(struct search_instruction));
737 if (!parse_search(menu->items[menu->itemcount], buf)) 827 if (!parse_search(menu->items[menu->itemcount], buf))
738 continue; 828 continue;
739 829
@@ -746,6 +836,7 @@ static bool parse_menu(const char *filename)
746 836
747void tagtree_init(void) 837void tagtree_init(void)
748{ 838{
839 format_count = 0;
749 menu_count = 0; 840 menu_count = 0;
750 menu = NULL; 841 menu = NULL;
751 root_menu = 0; 842 root_menu = 0;
@@ -852,14 +943,21 @@ int retrieve_entries(struct tree_context *c, struct tagcache_search *tcs,
852 int j; 943 int j;
853 944
854 for (j = 0; j < csi->clause_count[i]; j++) 945 for (j = 0; j < csi->clause_count[i]; j++)
855 tagcache_search_add_clause(tcs, &csi->clause[i][j]); 946 tagcache_search_add_clause(tcs, csi->clause[i][j]);
856 } 947 }
857 948
858 current_offset = offset; 949 current_offset = offset;
859 current_entry_count = 0; 950 current_entry_count = 0;
860 c->dirfull = false; 951 c->dirfull = false;
861 fmt = &csi->format[level]; 952
862 if (fmt->valid) 953 fmt = NULL;
954 for (i = 0; i < format_count; i++)
955 {
956 if (formats[i]->group_id == csi->format_id[level])
957 fmt = formats[i];
958 }
959
960 if (fmt)
863 { 961 {
864 sort_inverse = fmt->sort_inverse; 962 sort_inverse = fmt->sort_inverse;
865 sort_limit = fmt->limit; 963 sort_limit = fmt->limit;
@@ -900,12 +998,27 @@ int retrieve_entries(struct tree_context *c, struct tagcache_search *tcs,
900 else 998 else
901 dptr->extraseek = tcs->result_seek; 999 dptr->extraseek = tcs->result_seek;
902 1000
903 if (!tcs->ramresult || fmt->valid) 1001 fmt = NULL;
1002 /* Check the format */
1003 for (i = 0; i < format_count; i++)
1004 {
1005 if (formats[i]->group_id != csi->format_id[level])
1006 continue;
1007
1008 if (tagcache_check_clauses(tcs, formats[i]->clause,
1009 formats[i]->clause_count))
1010 {
1011 fmt = formats[i];
1012 break;
1013 }
1014 }
1015
1016 if (!tcs->ramresult || fmt)
904 { 1017 {
905 char buf[MAX_PATH]; 1018 char buf[MAX_PATH];
906 int buf_pos = 0; 1019 int buf_pos = 0;
907 1020
908 if (fmt->valid) 1021 if (fmt)
909 { 1022 {
910 char fmtbuf[8]; 1023 char fmtbuf[8];
911 bool read_format = false; 1024 bool read_format = false;
@@ -968,7 +1081,7 @@ int retrieve_entries(struct tree_context *c, struct tagcache_search *tcs,
968 } 1081 }
969 1082
970 dptr->name = &c->name_buffer[namebufused]; 1083 dptr->name = &c->name_buffer[namebufused];
971 if (fmt->valid) 1084 if (fmt)
972 namebufused += buf_pos; 1085 namebufused += buf_pos;
973 else 1086 else
974 namebufused += tcs->result_len; 1087 namebufused += tcs->result_len;
@@ -980,7 +1093,7 @@ int retrieve_entries(struct tree_context *c, struct tagcache_search *tcs,
980 sort = false; 1093 sort = false;
981 break ; 1094 break ;
982 } 1095 }
983 if (fmt->valid) 1096 if (fmt)
984 strcpy(dptr->name, buf); 1097 strcpy(dptr->name, buf);
985 else 1098 else
986 strcpy(dptr->name, tcs->result); 1099 strcpy(dptr->name, tcs->result);
@@ -1192,7 +1305,7 @@ int tagtree_enter(struct tree_context* c)
1192 { 1305 {
1193 for (j = 0; j < csi->clause_count[i]; j++) 1306 for (j = 0; j < csi->clause_count[i]; j++)
1194 { 1307 {
1195 if (!csi->clause[i][j].input) 1308 if (!csi->clause[i][j]->input)
1196 continue; 1309 continue;
1197 1310
1198 rc = kbd_input(searchstring, sizeof(searchstring)); 1311 rc = kbd_input(searchstring, sizeof(searchstring));
@@ -1202,11 +1315,11 @@ int tagtree_enter(struct tree_context* c)
1202 return 0; 1315 return 0;
1203 } 1316 }
1204 1317
1205 if (csi->clause[i][j].numeric) 1318 if (csi->clause[i][j]->numeric)
1206 csi->clause[i][j].numeric_data = atoi(searchstring); 1319 csi->clause[i][j]->numeric_data = atoi(searchstring);
1207 else 1320 else
1208 strncpy(csi->clause[i][j].str, searchstring, 1321 strncpy(csi->clause[i][j]->str, searchstring,
1209 sizeof(csi->clause[i][j].str)-1); 1322 sizeof(csi->clause[i][j]->str)-1);
1210 } 1323 }
1211 } 1324 }
1212 } 1325 }
@@ -1297,6 +1410,7 @@ bool insert_all_playlist(struct tree_context *c, int position, bool queue)
1297 int i; 1410 int i;
1298 char buf[MAX_PATH]; 1411 char buf[MAX_PATH];
1299 int from, to, direction; 1412 int from, to, direction;
1413 int files_left = c->filesindir;
1300 1414
1301 cpu_boost_id(true, CPUBOOSTID_TAGTREE); 1415 cpu_boost_id(true, CPUBOOSTID_TAGTREE);
1302 if (!tagcache_search(&tcs, tag_filename)) 1416 if (!tagcache_search(&tcs, tag_filename))
@@ -1321,7 +1435,8 @@ bool insert_all_playlist(struct tree_context *c, int position, bool queue)
1321 1435
1322 for (i = from; i != to; i += direction) 1436 for (i = from; i != to; i += direction)
1323 { 1437 {
1324 if (!show_search_progress(false, i)) 1438 /* Count back to zero */
1439 if (!show_search_progress(false, files_left--))
1325 break; 1440 break;
1326 1441
1327 if (!tagcache_retrieve(&tcs, tagtree_get_entry(c, i)->extraseek, 1442 if (!tagcache_retrieve(&tcs, tagtree_get_entry(c, i)->extraseek,
diff --git a/apps/tagtree.h b/apps/tagtree.h
index 9f570917f7..2c85bf90e1 100644
--- a/apps/tagtree.h
+++ b/apps/tagtree.h
@@ -25,6 +25,7 @@
25#define TAGNAVI_VERSION "#! rockbox/tagbrowser/2.0" 25#define TAGNAVI_VERSION "#! rockbox/tagbrowser/2.0"
26#define TAGMENU_MAX_ITEMS 64 26#define TAGMENU_MAX_ITEMS 64
27#define TAGMENU_MAX_MENUS 16 27#define TAGMENU_MAX_MENUS 16
28#define TAGMENU_MAX_FMTS 32
28 29
29enum table { root = 1, navibrowse, allsubentries, playtrack }; 30enum table { root = 1, navibrowse, allsubentries, playtrack };
30 31