1 /*
2  *  Copyright (C) 2014-2015  Christian Heckendorf <heckendorfc@gmail.com>
3  *
4  *  This program is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <stdlib.h>
19 #include <string.h>
20 #include "build.h"
21 #include "edit_shell.h"
22 #include "util.h"
23 #include "dbact.h"
24 
25 /* substitute vars if needed
26  * escape words in quotes if needed
27  * glob
28  */
29 
free_wordchain(wordchain_t * w)30 void free_wordchain(wordchain_t *w){
31 	wordchain_t *p;
32 	if(!w)return;
33 	for(p=w->next;w;){
34 		free(w);
35 		w=p;
36 		if(p)p=p->next;
37 	}
38 }
39 
free_arglist(arglist_t * a)40 void free_arglist(arglist_t *a){
41 	arglist_t *p;
42 	if(!a)return;
43 	for(p=a->next;a;){
44 		free(a->words);
45 		free(a);
46 		a=p;
47 		if(p)p=p->next;
48 	}
49 }
50 
free_wordlist(wordlist_t * w)51 void free_wordlist(wordlist_t *w){
52 	wordlist_t *p;
53 	if(!w)return;
54 	for(p=w->next;w;){
55 		free(w->word);
56 		free(w);
57 		w=p;
58 		if(p)p=p->next;
59 	}
60 }
61 
free_commands(command_t * c)62 void free_commands(command_t *c){
63 	command_t *p;
64 	if(!c)return;
65 	for(p=c->next;c;){
66 		free_arglist(c->args);
67 		free(c);
68 		c=p;
69 		if(p)p=p->next;
70 	}
71 }
72 
make_word(wordchain_t * word,char * piece,int flags)73 wordchain_t* make_word(wordchain_t *word, char *piece, int flags){
74 	wordchain_t *ptr=word;
75 	if(piece && *piece){
76 		if(!word){
77 			INIT_MEM(word,1);
78 			ptr=word;
79 			word->word=NULL;
80 			word->flags=0;
81 			word->next=NULL;
82 		}
83 		else{
84 			wordchain_t *temp=ptr;
85 			INIT_MEM(ptr,1);
86 			ptr->next=temp;
87 		}
88 		ptr->word=strdup(piece);
89 		ptr->flags=flags;
90 	}
91 	return ptr;
92 }
93 
94 /* default:
95 	subs allowed
96    ":
97 	no subs, vars allowed
98    ':
99     no subs, no vars
100 */
wordcat(char * word,int * wordsize,wordchain_t * wc)101 void wordcat(char *word, int *wordsize, wordchain_t *wc){
102 	int i;
103 	char *wp=word;
104 
105 	while(*wp)wp++;
106 	if(wc->flags&(WORD_DEFAULT)){
107 		for(i=0;wc->word[i];i++){
108 			*(wp++)=wc->word[i];
109 		}
110 		*wp=0;
111 	}
112 	else if(wc->flags&(WORD_SQUOT|WORD_DQUOT)){
113 		for(i=0;wc->word[i];i++){
114 			switch(wc->word[i]){
115 				case '\\':
116 				case '*':
117 				case '?':
118 				case '[':
119 					*(wp++)='\\';
120 					break;
121 				case '$':
122 					if(wc->flags&WORD_SQUOT)
123 						*(wp++)='\\';
124 					break;
125 			}
126 			*(wp++)=wc->word[i];
127 		}
128 		*wp=0;
129 	}
130 	else
131 		fprintf(stderr,"Warning: invalid word flags (%s)(%d)!\n",wc->word,wc->flags);
132 }
133 
merge_wordchain(wordchain_t * word)134 char* merge_wordchain(wordchain_t *word){
135 	wordchain_t *wc_ptr;
136 	int wordsize=0;
137 	char *ret;
138 
139 	for(wc_ptr=word;wc_ptr;wc_ptr=wc_ptr->next){
140 		wordsize+=strlen(wc_ptr->word);
141 	}
142 
143 	wordsize=2*wordsize+1;
144 	INIT_MEM(ret,wordsize);
145 	*ret=0;
146 	for(wc_ptr=word;wc_ptr;wc_ptr=wc_ptr->next){
147 		wordcat(ret,&wordsize,wc_ptr);
148 	}
149 
150 	return ret;
151 }
152 
append_wordlist(wordlist_t * a,wordlist_t * b)153 wordlist_t* append_wordlist(wordlist_t *a, wordlist_t *b){
154 	if(!a){
155 		if(b){
156 			return b;
157 		}
158 		return NULL;
159 	}
160 	while(a->next){
161 		a=a->next;
162 	}
163 	a->next=b;
164 	return a;
165 }
166 
167 /* concat all words in b onto the final word in a */
concat_wordlist(wordlist_t * a,wordlist_t * b)168 wordlist_t* concat_wordlist(wordlist_t *a, wordlist_t *b){
169 	const char wordsep=' ';
170 	int count,i;
171 	char *c;
172 	wordlist_t *temp;
173 
174 	if(!a || !b)return NULL; // Should do something else...
175 
176 	INIT_MEM(c,1);
177 
178 	while(a->next)a=a->next;
179 
180 	temp=b;
181 	count=i=strlen(a->word);
182 	while(temp){
183 		i+=strlen(temp->word)+1;
184 		temp=temp->next;
185 	}
186 
187 	INIT_MEM(c,i);
188 	memcpy(c,a->word,count);
189 	temp=b;
190 	for(i=0;temp;temp=temp->next){
191 		i=strlen(temp->word);
192 		memcpy(c+count,temp->word,i);
193 		count+=i;
194 		c[count++]=wordsep;
195 	}
196 	c[count-1]=0;
197 	if(a->word)
198 		free(a->word);
199 	a->word=c;
200 
201 	return a;
202 }
203 
make_word_list(wordlist_t * wl,wordchain_t * word)204 wordlist_t* make_word_list(wordlist_t *wl, wordchain_t *word){
205 	wordlist_t *wl_ptr;
206 	if(!wl){
207 		INIT_MEM(wl,1);
208 		wl_ptr=wl;
209 		wl->next=NULL;
210 	}
211 	else{
212 		for(wl_ptr=wl;wl_ptr->next;wl_ptr=wl_ptr->next);
213 		INIT_MEM(wl_ptr->next,1);
214 		wl_ptr=wl_ptr->next;
215 		wl_ptr->next=NULL;
216 	}
217 
218 	wl_ptr->word=merge_wordchain(word);
219 	wl_ptr->flag=word->flags; // TODO: scan all words?
220 
221 	free_wordchain(word);
222 
223 	return wl;
224 }
225 
226 static char *selectors[]={
227 	"song",
228 	"album",
229 	"artist",
230 	"playlist",
231 	"tag",
232 	NULL
233 };
234 
235 static char *selectfield[]={
236 	"SongID",
237 	"AlbumID",
238 	"ArtistID",
239 	"PlaylistID",
240 	"TagID",
241 };
242 
243 static char *selecttable[]={
244 	"Song",
245 	"Album",
246 	"Artist",
247 	"Playlist",
248 	"Tag"
249 };
250 
251 static char *selectcomp[]={
252 	"Title",
253 	"Title",
254 	"Name",
255 	"Title",
256 	"Name",
257 };
258 
259 static char shouldmake[]={
260 	0,
261 	0,
262 	0,
263 	1,
264 	1,
265 };
266 
get_select_type(command_t * c)267 static int get_select_type(command_t *c){
268 	int ret;
269 
270 	for(ret=0;selectors[ret];ret++){
271 		if(strcmp(selectors[ret],c->cmd->word)==0)
272 			return ret;
273 	}
274 	//return -1;
275 	return 0; /* song default ? */
276 }
277 
get_templist(arglist_t * a,int ctype)278 static int get_templist(arglist_t *a, int ctype){
279 	if(a->words->flag==WORD_DEFAULT){ // ID
280 		int id = strtol(a->words->word,NULL,10);
281 		return insertTempSelect(&id,1);
282 	}
283 	else{ // Name
284 		char query[300];
285 		int ret,num;
286 		if(a->words->flag==WORD_DQUOT)
287 			sprintf(query,"SELECT %%d,%s FROM %s WHERE %s LIKE '%%%%%s%%%%'",selectfield[ctype],selecttable[ctype],selectcomp[ctype],a->words->word);
288 		else
289 			sprintf(query,"SELECT %%d,%s FROM %s WHERE %s='%s'",selectfield[ctype],selecttable[ctype],selectcomp[ctype],a->words->word);
290 		ret=insertTempSelectQueryCount(query,&num);
291 		if(num==0 && shouldmake[ctype]){
292 			sprintf(query,"INSERT INTO %s(%s) VALUES(\"%s\")",selecttable[ctype],selectcomp[ctype],a->words->word);
293 			harp_sqlite3_exec(conn,query,NULL,NULL,NULL);
294 			num=sqlite3_last_insert_rowid(conn);
295 			return insertTempSelect(&num,1);
296 			//sprintf(query,"SELECT %%d,%s FROM %s WHERE %s LIKE '%%%%%s%%%%'",selectfield[ctype],selecttable[ctype],selectcomp[ctype],a->words->word);
297 			//return insertTempSelectQuery(query);
298 		}
299 		return ret;
300 	}
301 }
302 
303 static char *translatequery[]={
304 	NULL,//ss
305 	"SELECT %%d,AlbumID FROM Song WHERE SongID IN (%s)",//sa (song -> album)
306 	"SELECT %%d,ArtistID FROM AlbumArtist NATURAL JOIN Song WHERE SongID IN (%s)",//sr
307 	"SELECT %%d,PlaylistID FROM PlaylistSong WHERE SongID IN (%s)",//sp
308 	"SELECT %%d,TagID FROM SongTag WHERE SongID IN (%s)",//st
309 
310 	"SELECT %%d,SongID FROM Song WHERE AlbumID IN (%s)",//as
311 	NULL,//aa
312 	"SELECT %%d,ArtistID FROM AlbumArtist WHERE AlbumID IN (%s)",//ar
313 	"SELECT %%d,PlaylistID FROM PlaylistSong NATURAL JOIN Song WHERE AlbumID IN (%s)",//ap
314 	"SELECT %%d,TagID FROM SongTag NATURAL JOIN Song WHERE AlbumID IN (%s)",//at
315 
316 	"SELECT %%d,SongID FROM Song NATURAL JOIN AlbumArtist WHERE ArtistID IN (%s)",//rs
317 	"SELECT %%d,AlbumID FROM AlbumArtist WHERE ArtistID IN (%s)",//ra
318 	NULL,//rr
319 	"SELECT %%d,PlaylistID FROM PlaylistSong NATURAL JOIN Song NATURAL JOIN AlbumArtist WHERE IN ArtistID(%s)",//rp
320 	"SELECT %%d,TagID FROM SongTag NATURAL JOIN Song NATURAL JOIN AlbumArtist WHERE ArtistID IN (%s)",//rt
321 
322 	"SELECT %%d,SongID FROM PlaylistSong WHERE PlaylistID IN (%s)",//ps
323 	"SELECT %%d,AlbumID FROM PlaylistSong NATURAL JOIN Song WHERE PlaylistID IN (%s)",//pa
324 	"SELECT %%d,ArtistID FROM PlaylistSong NATURAL JOIN Song NATURAL JOIN AlbumArtist WHERE PlaylistID IN (%s)",//pr
325 	NULL,//pp
326 	"SELECT %%d,TagID FROM SongTag NATURAL JOIN PlaylistSong WHERE PlaylistID IN (%s)",//pt
327 
328 	"SELECT %%d,SongID FROM SongTag WHERE TagID IN (%s)",//ts
329 	"SELECT %%d,AlbumID FROM SongTag NATURAL JOIN Song WHERE TagID IN (%s)",//ta
330 	"SELECT %%d,ArtistID FROM SongTag NATURAL JOIN Song NATURAL JOIN AlbumArtist WHERE TagID IN (%s)",//tr
331 	"SELECT %%d,PlaylistID FROM SongTag NATURAL JOIN PlaylistSong WHERE TagID IN (%s)",//tp
332 	NULL,//tt
333 };
334 static const int numtypes=5;
335 
translate_templist(int toid,int totype,int fromid,int fromtype,int cleanup)336 static int translate_templist(int toid, int totype, int fromid, int fromtype, int cleanup){
337 	char query[300];
338 	char subquery[300];
339 	int newid;
340 	if(toid<0 && totype==fromtype)
341 		return fromid;
342 	else{
343 		if(totype==fromtype){
344 			mergeTempSelect(toid,fromid);
345 			newid=toid;
346 		}
347 		else{
348 			sprintf(subquery,"SELECT SelectID FROM TempSelect WHERE TempID=%d",fromid);
349 			sprintf(query,translatequery[fromtype*numtypes+totype],subquery);
350 			newid=insertTempSelectQuery(query);
351 			//mergeTempSelect(toid,newid);
352 		}
353 	}
354 
355 	if(cleanup)
356 		cleanTempSelect(fromid);
357 
358 	return newid;
359 }
360 
make_com_arg(void * data,int flag)361 arglist_t* make_com_arg(void *data, int flag){
362 	arglist_t *ret;
363 
364 	INIT_MEM(ret,1);
365 	ret->flags=flag;
366 	ret->next=NULL;
367 
368 	if(flag==COM_ARG_SELECTOR){
369 		command_t *c = (command_t*)data;
370 		arglist_t *a = c->args;
371 		int atid;
372 		int cid=-1;
373 
374 		ret->tltype=c->tltype;
375 
376 		if(c->tlid>=0)
377 			cid=c->tlid;
378 		else{
379 			while(a){
380 				atid=get_templist(a,c->tltype);
381 
382 				if(cid==-1)
383 					cid=atid;
384 				else
385 					mergeTempSelect(cid,atid);
386 
387 				a=a->next;
388 			}
389 		}
390 
391 		ret->words=NULL;
392 		ret->tlid=cid;
393 	}
394 	else{
395 		wordlist_t *wl = (wordlist_t*)data;
396 		ret->words=wl;
397 		ret->tlid=-1;
398 		ret->tltype=-1;
399 	}
400 
401 	//ret->args=wl;
402 	//fprintf(stderr,"arg: %s\n",wl->word);
403 
404 	return ret;
405 }
406 
append_com_arg(arglist_t * a,arglist_t * b)407 arglist_t* append_com_arg(arglist_t *a, arglist_t *b){
408 	if(b){
409 		b->next=a;
410 		return b;
411 	}
412 	return a;
413 }
414 
com_sel_set_args(command_t * c,arglist_t * a)415 void com_sel_set_args(command_t *c, arglist_t *a){
416 	arglist_t *ta;
417 	int cid;
418 	ta=c->args=a;
419 
420 	while(ta){
421 		if(ta->tltype<0) // single native
422 			cid=ta->tlid=get_templist(ta,c->tltype);
423 		else
424 			cid=translate_templist(c->tlid,c->tltype,ta->tlid,ta->tltype,1);
425 
426 		if(c->tlid<0)
427 			c->tlid=cid;
428 		else
429 			mergeTempSelect(c->tlid,cid);
430 
431 		ta=ta->next;
432 	}
433 }
434 
com_act_set_args(command_t * c,arglist_t * a)435 void com_act_set_args(command_t *c, arglist_t *a){
436 	c->args=a;
437 }
438 
com_set_args(command_t * c,arglist_t * a,int flag)439 command_t* com_set_args(command_t *c, arglist_t *a, int flag){
440 	c->flags=flag;
441 
442 	//if(flag==COM_SEL)
443 		com_sel_set_args(c,a);
444 	//else
445 		//com_act_set_args(c,a);
446 	return c;
447 }
448 
make_command(wordlist_t * wl)449 command_t* make_command(wordlist_t *wl){
450 	command_t *ret;
451 
452 	INIT_MEM(ret,1);
453 	ret->cmd=wl;
454 	ret->args=NULL;
455 	ret->flags=0;
456 	ret->next=NULL;
457 	ret->tltype=get_select_type(ret);
458 	ret->tlid=-1;
459 
460 	return ret;
461 }
462 
append_command(command_t * a,command_t * b)463 command_t* append_command(command_t *a, command_t *b){
464 	command_t *temp=a;
465 
466 	while(a->next)a=a->next;
467 	a->next=b;
468 
469 	return temp;
470 }
471 
append_command_flags(command_t * a,const int flags)472 void append_command_flags(command_t *a, const int flags){
473 	while(a->next)a=a->next;
474 	a->flags|=flags;
475 }
476 
make_commandline(command_t * sel,command_t * act)477 commandline_t* make_commandline(command_t *sel, command_t *act){
478 	commandline_t *ret;
479 
480 	INIT_MEM(ret,1);
481 	ret->selector=sel;
482 	ret->actions=act;
483 
484 	return ret;
485 }
486