diff options
author | Michiel Van Der Kolk <not.valid@email.address> | 2005-04-28 12:33:38 +0000 |
---|---|---|
committer | Michiel Van Der Kolk <not.valid@email.address> | 2005-04-28 12:33:38 +0000 |
commit | 9369d4867d3bf033e0e3bbcff05cd7f0a9bb83e8 (patch) | |
tree | 0276c6299a3b26705b028f399ceadf3ac7867b2f | |
parent | a7f7781dca4db172a507e7e6f73bee03fc7deb2f (diff) | |
download | rockbox-9369d4867d3bf033e0e3bbcff05cd7f0a9bb83e8.tar.gz rockbox-9369d4867d3bf033e0e3bbcff05cd7f0a9bb83e8.zip |
Search engine core for database v2, has an hardcoded "songs for year >= 1980 and year < 1990" at the moment.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6367 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | apps/plugin.c | 6 | ||||
-rw-r--r-- | apps/plugin.h | 7 | ||||
-rw-r--r-- | apps/plugins/Makefile | 2 | ||||
-rw-r--r-- | apps/plugins/searchengine/Makefile | 105 | ||||
-rw-r--r-- | apps/plugins/searchengine/dbinterface.c | 97 | ||||
-rw-r--r-- | apps/plugins/searchengine/dbinterface.h | 32 | ||||
-rw-r--r-- | apps/plugins/searchengine/parser.c | 240 | ||||
-rw-r--r-- | apps/plugins/searchengine/parser.h | 12 | ||||
-rw-r--r-- | apps/plugins/searchengine/searchengine.c | 101 | ||||
-rw-r--r-- | apps/plugins/searchengine/searchengine.h | 37 | ||||
-rw-r--r-- | apps/plugins/searchengine/token.c | 61 | ||||
-rw-r--r-- | apps/plugins/searchengine/token.h | 40 |
12 files changed, 738 insertions, 2 deletions
diff --git a/apps/plugin.c b/apps/plugin.c index 76c9ddb8bf..384ab22ede 100644 --- a/apps/plugin.c +++ b/apps/plugin.c | |||
@@ -45,6 +45,7 @@ | |||
45 | #include "powermgmt.h" | 45 | #include "powermgmt.h" |
46 | #include "system.h" | 46 | #include "system.h" |
47 | #include "sound.h" | 47 | #include "sound.h" |
48 | #include "database.h" | ||
48 | #if (CONFIG_HWCODEC == MASNONE) | 49 | #if (CONFIG_HWCODEC == MASNONE) |
49 | #include "pcm_playback.h" | 50 | #include "pcm_playback.h" |
50 | #endif | 51 | #endif |
@@ -319,6 +320,11 @@ static const struct plugin_api rockbox_api = { | |||
319 | #ifdef HAVE_LCD_BITMAP | 320 | #ifdef HAVE_LCD_BITMAP |
320 | read_bmp_file, | 321 | read_bmp_file, |
321 | #endif | 322 | #endif |
323 | &tagdbheader, | ||
324 | &tagdb_fd, | ||
325 | &tagdb_initialized, | ||
326 | tagdb_init, | ||
327 | strcasestr, | ||
322 | }; | 328 | }; |
323 | 329 | ||
324 | int plugin_load(const char* plugin, void* parameter) | 330 | int plugin_load(const char* plugin, void* parameter) |
diff --git a/apps/plugin.h b/apps/plugin.h index 0bb57b27f1..2604cae4f9 100644 --- a/apps/plugin.h +++ b/apps/plugin.h | |||
@@ -78,7 +78,7 @@ | |||
78 | #endif | 78 | #endif |
79 | 79 | ||
80 | /* increase this every time the api struct changes */ | 80 | /* increase this every time the api struct changes */ |
81 | #define PLUGIN_API_VERSION 38 | 81 | #define PLUGIN_API_VERSION 39 |
82 | 82 | ||
83 | /* update this to latest version if a change to the api struct breaks | 83 | /* update this to latest version if a change to the api struct breaks |
84 | backwards compatibility (and please take the opportunity to sort in any | 84 | backwards compatibility (and please take the opportunity to sort in any |
@@ -375,6 +375,11 @@ struct plugin_api { | |||
375 | int (*read_bmp_file)(char* filename, int *get_width, int *get_height, | 375 | int (*read_bmp_file)(char* filename, int *get_width, int *get_height, |
376 | char *bitmap, int maxsize); | 376 | char *bitmap, int maxsize); |
377 | #endif | 377 | #endif |
378 | struct tagdb_header *tagdbheader; | ||
379 | int *tagdb_fd; | ||
380 | int *tagdb_initialized; | ||
381 | int (*tagdb_init) (void); | ||
382 | char *(*strcasestr) (const char* phaystack, const char* pneedle); | ||
378 | }; | 383 | }; |
379 | 384 | ||
380 | /* defined by the plugin loader (plugin.c) */ | 385 | /* defined by the plugin loader (plugin.c) */ |
diff --git a/apps/plugins/Makefile b/apps/plugins/Makefile index 9c3ff4adc2..83617278d5 100644 --- a/apps/plugins/Makefile +++ b/apps/plugins/Makefile | |||
@@ -37,7 +37,7 @@ DIRS = . | |||
37 | 37 | ||
38 | #for any recorder and iRiver model | 38 | #for any recorder and iRiver model |
39 | ifneq (,$(strip $(foreach tgt,RECORDER IRIVER,$(findstring $(tgt),$(TARGET))))) | 39 | ifneq (,$(strip $(foreach tgt,RECORDER IRIVER,$(findstring $(tgt),$(TARGET))))) |
40 | SUBDIRS += rockboy | 40 | SUBDIRS += rockboy searchengine |
41 | endif | 41 | endif |
42 | 42 | ||
43 | .PHONY: $(SUBDIRS) | 43 | .PHONY: $(SUBDIRS) |
diff --git a/apps/plugins/searchengine/Makefile b/apps/plugins/searchengine/Makefile new file mode 100644 index 0000000000..d7253e2e10 --- /dev/null +++ b/apps/plugins/searchengine/Makefile | |||
@@ -0,0 +1,105 @@ | |||
1 | # __________ __ ___. | ||
2 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
3 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
4 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
5 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
6 | # \/ \/ \/ \/ \/ | ||
7 | # $Id$ | ||
8 | # | ||
9 | |||
10 | INCLUDES = -I$(APPSDIR) -I.. -I. -I$(FIRMDIR)/include -I$(FIRMDIR)/export \ | ||
11 | -I$(FIRMDIR)/common -I$(FIRMDIR)/drivers | ||
12 | CFLAGS = $(GCCOPTS) -O3 $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) \ | ||
13 | -DMEM=${MEMORYSIZE} -DPLUGIN | ||
14 | |||
15 | ifdef APPEXTRA | ||
16 | INCLUDES += -I$(APPSDIR)/$(APPEXTRA) | ||
17 | endif | ||
18 | |||
19 | LINKFILE := $(OBJDIR)/link.lds | ||
20 | DEPFILE = $(OBJDIR)/dep-searchengine | ||
21 | SRC = searchengine.c parser.c token.c dbinterface.c | ||
22 | |||
23 | SOURCES = $(SRC) | ||
24 | OBJS := $(SRC:%.c=$(OBJDIR)/%.o) | ||
25 | DIRS = . | ||
26 | |||
27 | |||
28 | ifndef SIMVER | ||
29 | ifneq (,$(findstring RECORDER,$(TARGET))) ## Archos recorder targets | ||
30 | OUTPUT = $(OUTDIR)/searchengine.rock | ||
31 | else ## iRiver target | ||
32 | LDS := ../plugin.lds | ||
33 | OUTPUT = $(OUTDIR)/searchengine.rock | ||
34 | endif | ||
35 | else ## simulators | ||
36 | OUTPUT = $(OUTDIR)/searchengine.rock | ||
37 | endif | ||
38 | |||
39 | all: $(OUTPUT) | ||
40 | |||
41 | ifndef SIMVER | ||
42 | $(OBJDIR)/searchengine.elf: $(OBJS) $(LINKFILE) $(OUTDIR)/libplugin.a | ||
43 | @echo "LD $@" | ||
44 | @$(CC) $(GCCOPTS) -O -nostdlib -o $@ $(OBJS) -L$(OUTDIR) -lplugin -lgcc \ | ||
45 | -T$(LINKFILE) -Wl,-Map,$(OBJDIR)/searchengine.map | ||
46 | |||
47 | $(OUTPUT): $(OBJDIR)/searchengine.elf | ||
48 | @echo "OBJCOPY $<" | ||
49 | @$(OC) -O binary $< $@ | ||
50 | else | ||
51 | |||
52 | ifeq ($(SIMVER), x11) | ||
53 | ################################################### | ||
54 | # This is the X11 simulator version | ||
55 | |||
56 | $(OUTPUT): $(OBJS) $(OUTDIR)/libplugin.a | ||
57 | @echo "LD $@" | ||
58 | @$(CC) $(CFLAGS) -shared $(OBJS) -L$(OUTDIR) -lplugin -o $@ | ||
59 | ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) | ||
60 | # 'x' must be kept or you'll have "Win32 error 5" | ||
61 | # $ fgrep 5 /usr/include/w32api/winerror.h | head -1 | ||
62 | # #define ERROR_ACCESS_DENIED 5L | ||
63 | else | ||
64 | @chmod -x $@ | ||
65 | endif | ||
66 | |||
67 | else # end of x11-simulator | ||
68 | ################################################### | ||
69 | # This is the win32 simulator version | ||
70 | DLLTOOLFLAGS = --export-all | ||
71 | DLLWRAPFLAGS = -s --entry _DllMain@12 --target=i386-mingw32 -mno-cygwin | ||
72 | |||
73 | $(OUTPUT): $(OBJS) $(OUTDIR)/libplugin.a | ||
74 | @echo "DLL $@" | ||
75 | @$(DLLTOOL) $(DLLTOOLFLAGS) -z $(OBJDIR)/$*.def $(OBJS) | ||
76 | @$(DLLWRAP) $(DLLWRAPFLAGS) --def $(OBJDIR)/$*.def $(OBJS) \ | ||
77 | $(OUTDIR)/libplugin.a -o $@ | ||
78 | ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) | ||
79 | # 'x' must be kept or you'll have "Win32 error 5" | ||
80 | # $ fgrep 5 /usr/include/w32api/winerror.h | head -1 | ||
81 | # #define ERROR_ACCESS_DENIED 5L | ||
82 | else | ||
83 | @chmod -x $@ | ||
84 | endif | ||
85 | endif # end of win32-simulator | ||
86 | |||
87 | endif # end of simulator section | ||
88 | |||
89 | |||
90 | include $(TOOLSDIR)/make.inc | ||
91 | |||
92 | # MEM should be passed on to this makefile with the chosen memory size given | ||
93 | # in number of MB | ||
94 | $(LINKFILE): $(LDS) | ||
95 | @echo "build $@" | ||
96 | @cat $< | $(CC) -DMEMORYSIZE=$(MEMORYSIZE) $(INCLUDES) $(TARGET) $(DEFINES) \ | ||
97 | -E -P - >$@ | ||
98 | |||
99 | clean: | ||
100 | @echo "cleaning searchengine" | ||
101 | @rm -rf $(OBJDIR)/searchengine | ||
102 | @rm -f $(OBJDIR)/searchengine.* $(DEPFILE) | ||
103 | |||
104 | -include $(DEPFILE) | ||
105 | |||
diff --git a/apps/plugins/searchengine/dbinterface.c b/apps/plugins/searchengine/dbinterface.c new file mode 100644 index 0000000000..bf2a6cfa5c --- /dev/null +++ b/apps/plugins/searchengine/dbinterface.c | |||
@@ -0,0 +1,97 @@ | |||
1 | #include "searchengine.h" | ||
2 | #include "dbinterface.h" | ||
3 | |||
4 | #undef SONGENTRY_SIZE | ||
5 | #undef FILEENTRY_SIZE | ||
6 | #undef ALBUMENTRY_SIZE | ||
7 | #undef ARTISTENTRY_SIZE | ||
8 | #undef FILERECORD2OFFSET | ||
9 | |||
10 | #define SONGENTRY_SIZE (rb->tagdbheader->songlen+12+rb->tagdbheader->genrelen+4) | ||
11 | #define FILEENTRY_SIZE (rb->tagdbheader->filelen+12) | ||
12 | #define ALBUMENTRY_SIZE (rb->tagdbheader->albumlen+4+rb->tagdbheader->songarraylen*4) | ||
13 | #define ARTISTENTRY_SIZE (rb->tagdbheader->artistlen+rb->tagdbheader->albumarraylen*4) | ||
14 | |||
15 | #define FILERECORD2OFFSET(_x_) (rb->tagdbheader->filestart + _x_ * FILEENTRY_SIZE) | ||
16 | |||
17 | struct entry *currententry; | ||
18 | |||
19 | static struct entry *entryarray; | ||
20 | |||
21 | int database_init() { | ||
22 | char *p; | ||
23 | unsigned int i; | ||
24 | // allocate room for all entries | ||
25 | entryarray=(struct entry *)my_malloc(sizeof(struct entry)*rb->tagdbheader->filecount); | ||
26 | p=(char *)entryarray; | ||
27 | // zero all entries. | ||
28 | for(i=0;i<sizeof(struct entry)*rb->tagdbheader->filecount;i++) | ||
29 | *(p++)=0; | ||
30 | if(*rb->tagdb_initialized!=1) { | ||
31 | if(!rb->tagdb_init()) { | ||
32 | // failed loading db | ||
33 | return -1; | ||
34 | } | ||
35 | } | ||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | void loadentry(int filerecord) { | ||
40 | if(entryarray[filerecord].loadedfiledata==0) { | ||
41 | rb->lseek(*rb->tagdb_fd,FILERECORD2OFFSET(filerecord),SEEK_SET); | ||
42 | entryarray[filerecord].filename=(char *)my_malloc(rb->tagdbheader->filelen); | ||
43 | rb->read(*rb->tagdb_fd,entryarray[filerecord].filename,rb->tagdbheader->filelen); | ||
44 | rb->read(*rb->tagdb_fd,&entryarray[filerecord].hash,4); | ||
45 | rb->read(*rb->tagdb_fd,&entryarray[filerecord].songentry,4); | ||
46 | rb->read(*rb->tagdb_fd,&entryarray[filerecord].rundbentry,4); | ||
47 | entryarray[filerecord].loadedfiledata=1; | ||
48 | } | ||
49 | currententry=&entryarray[filerecord]; | ||
50 | } | ||
51 | |||
52 | void loadsongdata() { | ||
53 | if(currententry->loadedsongdata || | ||
54 | !currententry->loadedfiledata) | ||
55 | return; | ||
56 | currententry->title=(char *)my_malloc(rb->tagdbheader->songlen); | ||
57 | currententry->genre=(char *)my_malloc(rb->tagdbheader->genrelen); | ||
58 | rb->lseek(*rb->tagdb_fd,currententry->songentry,SEEK_SET); | ||
59 | rb->read(*rb->tagdb_fd,currententry->title,rb->tagdbheader->songlen); | ||
60 | rb->read(*rb->tagdb_fd,¤tentry->artistoffset,4); | ||
61 | rb->read(*rb->tagdb_fd,¤tentry->albumoffset,4); | ||
62 | rb->lseek(*rb->tagdb_fd,4,SEEK_CUR); | ||
63 | rb->read(*rb->tagdb_fd,currententry->genre,rb->tagdbheader->genrelen); | ||
64 | rb->read(*rb->tagdb_fd,¤tentry->bitrate,2); | ||
65 | rb->read(*rb->tagdb_fd,¤tentry->year,2); | ||
66 | currententry->loadedsongdata=1; | ||
67 | } | ||
68 | |||
69 | void loadrundbdata() { | ||
70 | // we don't do this yet. | ||
71 | currententry->loadedrundbdata=1; | ||
72 | } | ||
73 | |||
74 | void loadartistname() { | ||
75 | /* memory optimization possible, only malloc for an album name once, then | ||
76 | * write that pointer to the entrys using it. | ||
77 | */ | ||
78 | currententry->artistname=(char *)my_malloc(rb->tagdbheader->artistlen); | ||
79 | rb->lseek(*rb->tagdb_fd,currententry->artistoffset,SEEK_SET); | ||
80 | rb->read(*rb->tagdb_fd,currententry->artistname,rb->tagdbheader->artistlen); | ||
81 | currententry->loadedartistname=1; | ||
82 | } | ||
83 | |||
84 | void loadalbumname() { | ||
85 | /* see the note at loadartistname */ | ||
86 | currententry->albumname=(char *)my_malloc(rb->tagdbheader->albumlen); | ||
87 | rb->lseek(*rb->tagdb_fd,currententry->albumoffset,SEEK_SET); | ||
88 | rb->read(*rb->tagdb_fd,currententry->albumname,rb->tagdbheader->albumlen); | ||
89 | currententry->loadedalbumname=1; | ||
90 | } | ||
91 | |||
92 | char *getfilename(int entry) { | ||
93 | if(entryarray[entry].loadedfiledata==0) | ||
94 | return "error O.o;;;"; | ||
95 | else | ||
96 | return entryarray[entry].filename; | ||
97 | } | ||
diff --git a/apps/plugins/searchengine/dbinterface.h b/apps/plugins/searchengine/dbinterface.h new file mode 100644 index 0000000000..32363b11c6 --- /dev/null +++ b/apps/plugins/searchengine/dbinterface.h | |||
@@ -0,0 +1,32 @@ | |||
1 | struct entry { | ||
2 | int loadedfiledata, | ||
3 | loadedsongdata, | ||
4 | loadedrundbdata, | ||
5 | loadedalbumname, | ||
6 | loadedartistname; | ||
7 | char *filename; | ||
8 | int hash; | ||
9 | int songentry; | ||
10 | int rundbentry; | ||
11 | short year; | ||
12 | short bitrate; | ||
13 | int rating; | ||
14 | int playcount; | ||
15 | char *title; | ||
16 | char *genre; | ||
17 | int artistoffset; | ||
18 | int albumoffset; | ||
19 | char *artistname; | ||
20 | char *albumname; | ||
21 | }; | ||
22 | |||
23 | extern struct entry *currententry; | ||
24 | extern struct entry *entryarray; | ||
25 | |||
26 | int database_init(void); | ||
27 | void loadentry(int filerecord); | ||
28 | void loadsongdata(void); | ||
29 | void loadrundbdata(void); | ||
30 | void loadartistname(void); | ||
31 | void loadalbumname(void); | ||
32 | char *getfilename(int entry); | ||
diff --git a/apps/plugins/searchengine/parser.c b/apps/plugins/searchengine/parser.c new file mode 100644 index 0000000000..fe10452c17 --- /dev/null +++ b/apps/plugins/searchengine/parser.c | |||
@@ -0,0 +1,240 @@ | |||
1 | #include "searchengine.h" | ||
2 | #include "token.h" | ||
3 | #include "dbinterface.h" | ||
4 | #include "parser.h" | ||
5 | |||
6 | struct token *tokenbuffer,*currentToken; | ||
7 | int currentindex; | ||
8 | int syntaxerror; | ||
9 | char errormsg[250]; | ||
10 | |||
11 | unsigned char *parse(struct token *tokenbuf) { | ||
12 | unsigned char *ret=0; | ||
13 | currentindex=0; | ||
14 | syntaxerror=0; | ||
15 | tokenbuffer=tokenbuf; | ||
16 | database_init(); | ||
17 | currentToken=&tokenbuffer[currentindex]; | ||
18 | PUTS("parse"); | ||
19 | ret=parseMExpr(); | ||
20 | if(syntaxerror) { | ||
21 | PUTS("Syntaxerror"); | ||
22 | rb->splash(HZ*3,true,errormsg); | ||
23 | } | ||
24 | parser_accept(TOKEN_EOF); | ||
25 | return ret; | ||
26 | } | ||
27 | |||
28 | void parser_acceptIt(void) { | ||
29 | if(syntaxerror) return; | ||
30 | currentToken=&tokenbuffer[++currentindex]; | ||
31 | } | ||
32 | |||
33 | int parser_accept(unsigned char kind) { | ||
34 | if(currentToken->kind!=kind) { | ||
35 | syntaxerror=1; | ||
36 | rb->snprintf(errormsg,250,"'%d' found where '%d' expected\n",currentToken->kind,kind); | ||
37 | return 0; | ||
38 | } | ||
39 | else { | ||
40 | parser_acceptIt(); | ||
41 | return 1; | ||
42 | } | ||
43 | } | ||
44 | |||
45 | unsigned char *parseCompareNum() { | ||
46 | struct token *number1,*number2; | ||
47 | unsigned char *ret; | ||
48 | int i,n1=-1,n2=-1; | ||
49 | int op; | ||
50 | if(syntaxerror) return 0; | ||
51 | PUTS("parseCompareNum"); | ||
52 | if(currentToken->kind==TOKEN_NUM || | ||
53 | currentToken->kind==TOKEN_NUMIDENTIFIER) { | ||
54 | number1=currentToken; | ||
55 | parser_acceptIt(); | ||
56 | } | ||
57 | else { | ||
58 | syntaxerror=1; | ||
59 | rb->snprintf(errormsg,250,"'%d' found where NUM/NUMID expected\n",currentToken->kind); | ||
60 | return 0; | ||
61 | } | ||
62 | if(currentToken->kind>=TOKEN_GT && currentToken->kind <= TOKEN_NE) { | ||
63 | op=currentToken->kind; | ||
64 | parser_acceptIt(); | ||
65 | } | ||
66 | else { | ||
67 | syntaxerror=1; | ||
68 | rb->snprintf(errormsg,250,"'%d' found where NUMOP expected\n",currentToken->kind); | ||
69 | return 0; | ||
70 | } | ||
71 | if(currentToken->kind==TOKEN_NUM || | ||
72 | currentToken->kind==TOKEN_NUMIDENTIFIER) { | ||
73 | number2=currentToken; | ||
74 | parser_acceptIt(); | ||
75 | } | ||
76 | else { | ||
77 | syntaxerror=1; | ||
78 | rb->snprintf(errormsg,250,"'%d' found where NUM/NUMID expected\n",currentToken->kind); | ||
79 | return 0; | ||
80 | } | ||
81 | ret=my_malloc(sizeof(unsigned char)*rb->tagdbheader->filecount); | ||
82 | if(number1->kind==TOKEN_NUM) | ||
83 | n1=getvalue(number1); | ||
84 | if(number2->kind==TOKEN_NUM) | ||
85 | n2=getvalue(number2); | ||
86 | for(i=0;i<rb->tagdbheader->filecount;i++) { | ||
87 | loadentry(i); | ||
88 | if(number1->kind==TOKEN_NUMIDENTIFIER) | ||
89 | n1=getvalue(number1); | ||
90 | if(number2->kind==TOKEN_NUMIDENTIFIER) | ||
91 | n2=getvalue(number2); | ||
92 | switch(op) { | ||
93 | case TOKEN_GT: | ||
94 | ret[i]=n1 > n2; | ||
95 | break; | ||
96 | case TOKEN_GTE: | ||
97 | ret[i]=n1 >= n2; | ||
98 | break; | ||
99 | case TOKEN_LT: | ||
100 | ret[i]=n1 < n2; | ||
101 | break; | ||
102 | case TOKEN_LTE: | ||
103 | ret[i]=n1 <= n2; | ||
104 | break; | ||
105 | case TOKEN_EQ: | ||
106 | ret[i]=n1 == n2; | ||
107 | break; | ||
108 | case TOKEN_NE: | ||
109 | ret[i]=n1 != n2; | ||
110 | break; | ||
111 | } | ||
112 | } | ||
113 | return ret; | ||
114 | } | ||
115 | |||
116 | unsigned char *parseCompareString() { | ||
117 | struct token *string1,*string2; | ||
118 | unsigned char *ret; | ||
119 | char *s1=NULL,*s2=NULL; | ||
120 | int i,contains; | ||
121 | if(syntaxerror) return 0; | ||
122 | PUTS("parseCompareString"); | ||
123 | if(currentToken->kind==TOKEN_STRING || | ||
124 | currentToken->kind==TOKEN_STRINGIDENTIFIER) { | ||
125 | string1=currentToken; | ||
126 | parser_acceptIt(); | ||
127 | } | ||
128 | else { | ||
129 | syntaxerror=1; | ||
130 | rb->snprintf(errormsg,250,"'%d' found where STRING/STRINGID expected\n",currentToken->kind); | ||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | contains=currentToken->kind==TOKEN_CONTAINS; | ||
135 | if(currentToken->kind==TOKEN_CONTAINS || | ||
136 | currentToken->kind==TOKEN_EQUALS) | ||
137 | parser_acceptIt(); | ||
138 | else { | ||
139 | syntaxerror=1; | ||
140 | rb->snprintf(errormsg,250,"'%d' found where CONTAINS/EQUALS expected\n",currentToken->kind); | ||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | if(currentToken->kind==TOKEN_STRING || | ||
145 | currentToken->kind==TOKEN_STRINGIDENTIFIER) { | ||
146 | string2=currentToken; | ||
147 | parser_acceptIt(); | ||
148 | } | ||
149 | else { | ||
150 | syntaxerror=1; | ||
151 | rb->snprintf(errormsg,250,"'%d' found where STRING/STRINGID expected\n",currentToken->kind); | ||
152 | return 0; | ||
153 | } | ||
154 | ret=my_malloc(sizeof(unsigned char)*rb->tagdbheader->filecount); | ||
155 | if(string1->kind==TOKEN_STRING) | ||
156 | s1=getstring(string1); | ||
157 | if(string2->kind==TOKEN_STRING) | ||
158 | s2=getstring(string2); | ||
159 | for(i=0;i<rb->tagdbheader->filecount;i++) { | ||
160 | loadentry(i); | ||
161 | if(string1->kind==TOKEN_STRINGIDENTIFIER) | ||
162 | s1=getstring(string1); | ||
163 | if(string2->kind==TOKEN_STRINGIDENTIFIER) | ||
164 | s2=getstring(string2); | ||
165 | if(contains) | ||
166 | ret[i]=rb->strcasestr(s1,s2)!=0; | ||
167 | else | ||
168 | ret[i]=rb->strcasecmp(s1,s2)==0; | ||
169 | } | ||
170 | return ret; | ||
171 | } | ||
172 | |||
173 | unsigned char *parseExpr() { | ||
174 | unsigned char *ret; | ||
175 | int i; | ||
176 | if(syntaxerror) return 0; | ||
177 | PUTS("parseExpr"); | ||
178 | switch(currentToken->kind) { | ||
179 | case TOKEN_NOT: | ||
180 | parser_accept(TOKEN_NOT); | ||
181 | PUTS("parseNot"); | ||
182 | ret = parseExpr(); | ||
183 | if(ret==NULL) return 0; | ||
184 | for(i=0;i<rb->tagdbheader->filecount;i++) | ||
185 | ret[i]=!ret[i]; | ||
186 | break; | ||
187 | case TOKEN_LPAREN: | ||
188 | parser_accept(TOKEN_LPAREN); | ||
189 | ret = parseMExpr(); | ||
190 | if(ret==NULL) return 0; | ||
191 | parser_accept(TOKEN_RPAREN); | ||
192 | break; | ||
193 | case TOKEN_NUM: | ||
194 | case TOKEN_NUMIDENTIFIER: | ||
195 | ret = parseCompareNum(); | ||
196 | if(ret==NULL) return 0; | ||
197 | break; | ||
198 | case TOKEN_STRING: | ||
199 | case TOKEN_STRINGIDENTIFIER: | ||
200 | ret = parseCompareString(); | ||
201 | if(ret==NULL) return 0; | ||
202 | break; | ||
203 | default: | ||
204 | // error, unexpected symbol | ||
205 | syntaxerror=1; | ||
206 | rb->snprintf(errormsg,250,"unexpected '%d' found at parseExpr\n",currentToken->kind); | ||
207 | ret=0; | ||
208 | break; | ||
209 | } | ||
210 | return ret; | ||
211 | } | ||
212 | |||
213 | unsigned char *parseMExpr() { | ||
214 | unsigned char *ret,*ret2; | ||
215 | int i; | ||
216 | if(syntaxerror) return 0; | ||
217 | PUTS("parseMExpr"); | ||
218 | ret=parseExpr(); | ||
219 | while(currentToken->kind==TOKEN_AND||currentToken->kind==TOKEN_OR) { | ||
220 | switch(currentToken->kind) { | ||
221 | case TOKEN_AND: | ||
222 | parser_accept(TOKEN_AND); | ||
223 | PUTS("parseAnd"); | ||
224 | ret2 = parseExpr(); | ||
225 | if(ret2==NULL) return 0; | ||
226 | for(i=0;i<rb->tagdbheader->filecount;i++) | ||
227 | ret[i]=ret[i] && ret2[i]; | ||
228 | break; | ||
229 | case TOKEN_OR: | ||
230 | parser_accept(TOKEN_OR); | ||
231 | PUTS("parseOr"); | ||
232 | ret2 = parseExpr(); | ||
233 | if(ret2==NULL) return 0; | ||
234 | for(i=0;i<rb->tagdbheader->filecount;i++) | ||
235 | ret[i]=ret[i] || ret2[i]; | ||
236 | break; | ||
237 | } | ||
238 | } | ||
239 | return ret; | ||
240 | } | ||
diff --git a/apps/plugins/searchengine/parser.h b/apps/plugins/searchengine/parser.h new file mode 100644 index 0000000000..e63f15f39b --- /dev/null +++ b/apps/plugins/searchengine/parser.h | |||
@@ -0,0 +1,12 @@ | |||
1 | extern struct token *tokenbuffer,*currentToken; | ||
2 | |||
3 | extern int syntaxerror; | ||
4 | extern char errormsg[250]; | ||
5 | |||
6 | unsigned char *parse(struct token *tokenbuf); | ||
7 | void parser_acceptIt(void); | ||
8 | int parser_accept(unsigned char kind); | ||
9 | unsigned char *parseCompareNum(void); | ||
10 | unsigned char *parseCompareString(void); | ||
11 | unsigned char *parseExpr(void); | ||
12 | unsigned char *parseMExpr(void); | ||
diff --git a/apps/plugins/searchengine/searchengine.c b/apps/plugins/searchengine/searchengine.c new file mode 100644 index 0000000000..4b14836ddc --- /dev/null +++ b/apps/plugins/searchengine/searchengine.c | |||
@@ -0,0 +1,101 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id | ||
9 | * | ||
10 | * Copyright (C) 2005 by Michiel van der Kolk | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #include "searchengine.h" | ||
20 | #include "parser.h" | ||
21 | #include "token.h" | ||
22 | #include "dbinterface.h" | ||
23 | |||
24 | void *audio_bufferbase; | ||
25 | void *audio_bufferpointer; | ||
26 | unsigned int audio_buffer_free; | ||
27 | struct plugin_api* rb; | ||
28 | int w, h, y; | ||
29 | |||
30 | void *my_malloc(size_t size) | ||
31 | { | ||
32 | void *alloc; | ||
33 | |||
34 | if (!audio_bufferbase) | ||
35 | { | ||
36 | audio_bufferbase = audio_bufferpointer | ||
37 | = rb->plugin_get_audio_buffer(&audio_buffer_free); | ||
38 | } | ||
39 | if (size + 4 > audio_buffer_free) | ||
40 | return 0; | ||
41 | alloc = audio_bufferpointer; | ||
42 | audio_bufferpointer += size + 4; | ||
43 | audio_buffer_free -= size + 4; | ||
44 | return alloc; | ||
45 | } | ||
46 | |||
47 | void setmallocpos(void *pointer) | ||
48 | { | ||
49 | audio_bufferpointer = pointer; | ||
50 | audio_buffer_free = audio_bufferpointer - audio_bufferbase; | ||
51 | } | ||
52 | |||
53 | struct token tokenstream[10]; | ||
54 | |||
55 | /* this is the plugin entry point */ | ||
56 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | ||
57 | { | ||
58 | unsigned char *result,buf[500]; | ||
59 | /* this macro should be called as the first thing you do in the plugin. | ||
60 | it test that the api version and model the plugin was compiled for | ||
61 | matches the machine it is running on */ | ||
62 | TEST_PLUGIN_API(api); | ||
63 | |||
64 | (void)parameter; | ||
65 | |||
66 | /* if you are using a global api pointer, don't forget to copy it! | ||
67 | otherwise you will get lovely "I04: IllInstr" errors... :-) */ | ||
68 | rb = api; | ||
69 | |||
70 | audio_bufferbase=audio_bufferpointer=0; | ||
71 | audio_buffer_free=0; | ||
72 | |||
73 | /* now go ahead and have fun! */ | ||
74 | rb->splash(HZ*2, true, "SearchEngine v0.1"); | ||
75 | tokenstream[0].kind=TOKEN_NUMIDENTIFIER; | ||
76 | tokenstream[0].intvalue=INTVALUE_YEAR; | ||
77 | tokenstream[1].kind=TOKEN_GTE; | ||
78 | tokenstream[2].kind=TOKEN_NUM; | ||
79 | tokenstream[2].intvalue=1980; | ||
80 | tokenstream[3].kind=TOKEN_AND; | ||
81 | tokenstream[4].kind=TOKEN_NUMIDENTIFIER; | ||
82 | tokenstream[4].intvalue=INTVALUE_YEAR; | ||
83 | tokenstream[5].kind=TOKEN_LT; | ||
84 | tokenstream[6].kind=TOKEN_NUM; | ||
85 | tokenstream[6].intvalue=1990; | ||
86 | tokenstream[7].kind=TOKEN_EOF; | ||
87 | result=parse(tokenstream); | ||
88 | rb->snprintf(buf,250,"Retval: 0x%x",result); | ||
89 | PUTS(buf); | ||
90 | if(result!=0) { | ||
91 | int fd=rb->open("/search.m3u", O_WRONLY|O_CREAT|O_TRUNC); | ||
92 | int i; | ||
93 | for(i=0;i<rb->tagdbheader->filecount;i++) | ||
94 | if(result[i]) | ||
95 | rb->fdprintf(fd,"%s\n",getfilename(i)); | ||
96 | /* rb->write(fd,result,rb->tagdbheader->filecount);*/ | ||
97 | rb->close(fd); | ||
98 | } | ||
99 | rb->sleep(HZ*10); | ||
100 | return PLUGIN_OK; | ||
101 | } | ||
diff --git a/apps/plugins/searchengine/searchengine.h b/apps/plugins/searchengine/searchengine.h new file mode 100644 index 0000000000..3f7e4c609e --- /dev/null +++ b/apps/plugins/searchengine/searchengine.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id | ||
9 | * | ||
10 | * Copyright (C) 2005 by Michiel van der Kolk | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #ifndef SEARCHENGINE_H | ||
20 | #define SEARCHENGINE_H | ||
21 | #include <plugin.h> | ||
22 | #include <database.h> | ||
23 | |||
24 | extern int w, h, y; | ||
25 | #define PUTS(str) do { \ | ||
26 | rb->lcd_putsxy(1, y, str); \ | ||
27 | rb->lcd_getstringsize(str, &w, &h); \ | ||
28 | y += h + 1; \ | ||
29 | } while (0); \ | ||
30 | rb->lcd_update() | ||
31 | |||
32 | extern struct plugin_api* rb; | ||
33 | |||
34 | void *my_malloc(size_t size); | ||
35 | void setmallocpos(void *pointer); | ||
36 | |||
37 | #endif | ||
diff --git a/apps/plugins/searchengine/token.c b/apps/plugins/searchengine/token.c new file mode 100644 index 0000000000..d2da94ab52 --- /dev/null +++ b/apps/plugins/searchengine/token.c | |||
@@ -0,0 +1,61 @@ | |||
1 | #include "token.h" | ||
2 | #include "dbinterface.h" | ||
3 | |||
4 | #define REQUIRESONGDATA() if(!currententry->loadedsongdata) loadsongdata(); | ||
5 | #define REQUIRERUNDBDATA() if(!currententry->loadedrundbdata) loadrundbdata(); | ||
6 | #define REQUIREALBUMNAME() if(!currententry->loadedalbumname) { REQUIRESONGDATA(); loadalbumname(); } | ||
7 | #define REQUIREARTISTNAME() if(!currententry->loadedartistname) { REQUIRESONGDATA(); loadartistname(); } | ||
8 | |||
9 | char *getstring(struct token *token) { | ||
10 | switch(token->kind) { | ||
11 | case TOKEN_STRING: | ||
12 | return token->spelling; | ||
13 | case TOKEN_STRINGIDENTIFIER: | ||
14 | switch(token->intvalue) { | ||
15 | case INTVALUE_TITLE: | ||
16 | REQUIRESONGDATA(); | ||
17 | return currententry->title; | ||
18 | case INTVALUE_ARTIST: | ||
19 | REQUIREARTISTNAME(); | ||
20 | return currententry->artistname; | ||
21 | case INTVALUE_ALBUM: | ||
22 | REQUIREALBUMNAME(); | ||
23 | return currententry->albumname; | ||
24 | case INTVALUE_GENRE: | ||
25 | REQUIRESONGDATA(); | ||
26 | return currententry->genre; | ||
27 | case INTVALUE_FILENAME: | ||
28 | return currententry->filename; | ||
29 | default: | ||
30 | return 0; | ||
31 | } | ||
32 | break; | ||
33 | default: | ||
34 | // report error | ||
35 | return 0; | ||
36 | } | ||
37 | } | ||
38 | |||
39 | int getvalue(struct token *token) { | ||
40 | switch(token->kind) { | ||
41 | case TOKEN_NUM: | ||
42 | return token->intvalue; | ||
43 | case TOKEN_NUMIDENTIFIER: | ||
44 | switch(token->intvalue) { | ||
45 | case INTVALUE_YEAR: | ||
46 | REQUIRESONGDATA(); | ||
47 | return currententry->year; | ||
48 | case INTVALUE_RATING: | ||
49 | REQUIRERUNDBDATA(); | ||
50 | return currententry->rating; | ||
51 | case INTVALUE_PLAYCOUNT: | ||
52 | REQUIRERUNDBDATA(); | ||
53 | return currententry->playcount; | ||
54 | default: | ||
55 | // report error. | ||
56 | return 0; | ||
57 | } | ||
58 | default: | ||
59 | return 0; | ||
60 | } | ||
61 | } | ||
diff --git a/apps/plugins/searchengine/token.h b/apps/plugins/searchengine/token.h new file mode 100644 index 0000000000..b858f4f217 --- /dev/null +++ b/apps/plugins/searchengine/token.h | |||
@@ -0,0 +1,40 @@ | |||
1 | #define TOKEN_INVALID -1 | ||
2 | #define TOKEN_EOF 0 // EOF | ||
3 | #define TOKEN_NOT 1 // "not" | ||
4 | #define TOKEN_AND 2 // "and" | ||
5 | #define TOKEN_OR 3 // "or" | ||
6 | #define TOKEN_GT 4 // '>' | ||
7 | #define TOKEN_GTE 5 // '>=' | ||
8 | #define TOKEN_LT 6 // '<' | ||
9 | #define TOKEN_LTE 7 // '<=' | ||
10 | #define TOKEN_EQ 8 // '==' | ||
11 | #define TOKEN_NE 9 // '!=' | ||
12 | #define TOKEN_CONTAINS 10 // "contains" | ||
13 | #define TOKEN_EQUALS 11 // "equals" | ||
14 | #define TOKEN_LPAREN 12 // '(' | ||
15 | #define TOKEN_RPAREN 13 // ')' | ||
16 | #define TOKEN_NUM 14 // (0..9)+ | ||
17 | #define TOKEN_NUMIDENTIFIER 15 // year, trackid, bpm, etc. | ||
18 | #define TOKEN_STRING 16 // (?)+ | ||
19 | #define TOKEN_STRINGIDENTIFIER 17 // album, artist, title, genre ... | ||
20 | |||
21 | #define INTVALUE_YEAR 1 | ||
22 | #define INTVALUE_RATING 2 | ||
23 | #define INTVALUE_PLAYCOUNT 3 | ||
24 | #define INTVALUE_TITLE 4 | ||
25 | #define INTVALUE_ARTIST 5 | ||
26 | #define INTVALUE_ALBUM 6 | ||
27 | #define INTVALUE_GENRE 7 | ||
28 | #define INTVALUE_FILENAME 8 | ||
29 | |||
30 | static char *spelling[] = { "not", "and", "or",">",">=","<", "<=","==","!=", | ||
31 | "contains","(",")" }; | ||
32 | |||
33 | struct token { | ||
34 | unsigned char kind; | ||
35 | char spelling[256]; | ||
36 | int intvalue; | ||
37 | }; | ||
38 | |||
39 | char *getstring(struct token *token); | ||
40 | int getvalue(struct token *token); | ||