removed some useless output
[k8muffin.git] / src / search.c
blob910dbbc805c4a4aaa3e076662a58f20d052f922f
1 #include "common.h"
4 typedef struct {
5 //file_info_t **files;
6 const char **names; // short names
7 int fcount;
8 } dirinfo_t;
11 typedef struct {
12 tagvalue_t *tv;
13 char *s; // tag name
14 UT_hash_handle hh;
15 } taghh_t;
18 // /tag/tag/tag/
19 // /:artist/name/:album/name/
20 static dirinfo_t *list_dir (const char *path) {
21 file_info_t **files = NULL; // NULL: all files in list
22 int fcount = 0, pos = 0;
23 int onlyfiles = 0, inorig = -1;
24 char *condstr = alloca(strlen(path)+1);
25 tagvalue_t **tagh = NULL, **ftlist;
26 int fieldofs = 0;
27 taghh_t *tl_hash = NULL, *hx, *thx;
28 int wasspec[ARRAYLEN(specdirs)];
29 dirinfo_t *di;
31 void add_from_tv (const tagvalue_t *tv) {
32 // just add all files
33 files = calloc(1, sizeof(file_info_t *)*tv->idcount);
34 for (int f = 0; f < tv->idcount; ++f) files[f] = tv->ids[f];
35 fcount = tv->idcount;
38 void remove_not_in_tv (const tagvalue_t *tv) {
39 for (int c = 0; c < fcount; ++c) {
40 int found = 0;
41 for (int f = 0; f < tv->idcount; ++f) if (files[c] == tv->ids[f]) { found = 1; break; }
42 if (!found) {
43 // remove this file from list
44 for (int f = c+1; f < fcount; ++f) files[f-1] = files[f];
45 --fcount;
46 --c; // process this item again
51 memset(wasspec, 0, sizeof(wasspec));
52 di = calloc(1, sizeof(*di));
54 if (strcmp(path, "/") == 0) {
55 // wow! list ALL TAGS here!
56 di = calloc(1, sizeof(*di));
57 di->fcount = HASH_COUNT(tagv_hash_t)+ARRAYLEN(specdirs)+2;
58 di->names = calloc(di->fcount, sizeof(di->names[0]));
60 di->names[pos++] = ":files";
61 di->names[pos++] = ":otags";
63 for (int f = 0; f < ARRAYLEN(specdirs); ++f) di->names[pos++] = specdirs[f];
64 for (const tagvalue_t *tv = tagv_hash_t; tv != NULL; tv = tv->hh.next) di->names[pos++] = tv->s;
65 di->fcount = pos;
66 return di;
69 if (strcmp(path, "/:otags") == 0) {
70 // wow! list ALL TAGS here!
71 di = calloc(1, sizeof(*di));
72 di->fcount = HASH_COUNT(tagv_hash_o)+ARRAYLEN(specdirs)+1;
73 di->names = calloc(di->fcount, sizeof(di->names[0]));
75 di->names[pos++] = ":files";
77 for (int f = 0; f < ARRAYLEN(specdirs); ++f) if (specdirs[f][1] == 'o') di->names[pos++] = specdirs[f];
78 for (const tagvalue_t *tv = tagv_hash_o; tv != NULL; tv = tv->hh.next) di->names[pos++] = tv->s;
79 di->fcount = pos;
80 return di;
83 dlogf("path: [%s]\n", path);
84 // select and then refine
85 while (*path) {
86 tagvalue_t *tv;
87 while (*path == '/') ++path;
88 if (!path[0]) break;
90 char *e = strchr(path, '/');
91 if (e == NULL) {
92 strcpy(condstr, path);
93 path += strlen(path);
94 } else {
95 memcpy(condstr, path, e-path);
96 condstr[e-path] = 0;
97 path = e+1;
100 dlogf("(fcount=%d) condstr: [%s]; inorig: %d\n", fcount, condstr, inorig);
101 if (condstr[0] == ':') {
102 // specific tag
103 if (tagh != NULL) { dlogf("error: 2 specials\n"); fcount = 0; break; } // error
104 for (size_t f = 0; f < ARRAYLEN(specdirs); ++f) {
105 if (strcmp(specdirs[f], condstr) == 0) {
106 if (wasspec[f]) { dlogf(" error: dupspecial '%s'\n", condstr+1); fcount = 0; break; } // error
107 wasspec[f] = 1;
110 if (condstr[1] == 'o' && strcmp(specdirs[f]+2, condstr+2) == 0) {
111 if (wasspec[f]) { dlogf(" error: dupspecial '%s'\n", condstr+2); fcount = 0; break; } // error
112 wasspec[f] = 1;
116 dlogf("special: [%s] (fc=%d)\n", condstr, fcount);
117 if (strcmp(condstr, ":files") == 0) {
118 // we want only files
119 onlyfiles = 1;
120 if (files == NULL) {
121 // wow, all files!
122 files = calloc(1, sizeof(file_info_t *)*HASH_COUNT(file_name_hash));
123 for (file_info_t *fi = file_name_hash; fi != NULL; fi = fi->hh.next, ++fcount) files[fcount] = fi;
125 break;
128 if (strcmp(condstr, ":otags") == 0) {
129 if (inorig >= 0) { dlogf(" error: 'otags' in tmode\n"); fcount = 0; break; } // error
130 inorig = 1;
131 dlogf("otags; path=[%s]\n", path);
132 continue;
135 if (condstr[1] == 'o') {
136 if (inorig == 0) { dlogf(" 'o' tag disabled, inorig=%d\n", inorig); fcount = 0; break; } // no ':o' in normal dirs
137 // disable 'non-o' tags
138 if (inorig < 0) for (size_t f = 0; f < ARRAYLEN(specdirs); ++f) if (specdirs[f][1] != 'o') wasspec[f] = 1;;
139 inorig = 1;
140 } else {
141 if (inorig == 1) { dlogf(" 't' tag disabled, inorig=%d\n", inorig); fcount = 0; break; } // no normal in orig dirs
142 // disable 'o' tags
143 if (inorig < 0) for (size_t f = 0; f < ARRAYLEN(specdirs); ++f) if (specdirs[f][1] == 'o') wasspec[f] = 1;;
144 inorig = 0;
147 tagh = special_hash(condstr+1);
148 if (tagh == NULL) { fcount = 0; break; } // unknown tag name, nothing can be found
149 fieldofs = special_ofs(condstr+1);
150 dlogf(" tag '%s' (fofs=%d; fc=%d)\n", condstr+1, fieldofs, fcount);
151 //dlogf(" tag '%s' (fofs: %d), hcnt=%d\n", condstr+1, fieldofs, HASH_COUNT(*tagh));
152 continue;
155 if (tagh == NULL) {
156 int wasit = 0;
157 // global tag
158 if (inorig < 0 || inorig == 0) {
159 HASH_FIND_STR(tagv_hash_t, condstr, tv);
160 if (tv != NULL) {
161 wasit = 1;
162 inorig = 0;
163 dlogf(" for tag '%s': %d items\n", condstr, tv->idcount);
164 if (files == NULL) {
165 // first time: add all files with this tag
166 add_from_tv(tv);
167 } else {
168 // remove all files that have no such tag
169 remove_not_in_tv(tv);
173 if (!wasit && (inorig < 0 || inorig == 1)) {
174 HASH_FIND_STR(tagv_hash_o, condstr, tv);
175 if (tv != NULL) {
176 wasit = 1;
177 inorig = 1;
178 dlogf(" for tag '%s': %d items\n", condstr, tv->idcount);
179 if (files == NULL) {
180 // first time: add all files with this tag
181 add_from_tv(tv);
182 } else {
183 // remove all files that have no such tag
184 remove_not_in_tv(tv);
188 if (!wasit) { fcount = 0; break; } // no such tag, nothing can be found
189 continue;
190 } else {
191 // special tag
192 HASH_FIND_STR(*tagh, condstr, tv);
193 if (tv == NULL) { dlogf(" for tag '%s': no items\n", condstr); fcount = 0; break; } // no such tag, nothing can be found
194 dlogf(" for tag '%s': %d items\n", condstr, tv->idcount);
195 if (files == NULL) {
196 // first time: add all files with this tag
197 add_from_tv(tv);
198 } else {
199 // remove all files that have no such tag in given field
200 for (int f = 0; f < fcount; ++f) {
201 ftlist = (tagvalue_t **)((uint8_t *)(files[f])+fieldofs);
202 if (ftlist[0] != NULL && strcmp(ftlist[0]->s, tv->s) == 0) continue;
203 if (ftlist[1] != NULL && strcmp(ftlist[1]->s, tv->s) == 0) continue;
204 // drop this file
205 for (int c = f+1; c < fcount; ++c) files[c-1] = files[c];
206 --fcount;
207 --f;
210 tagh = NULL; // processed
211 continue;
214 // search complete
215 //di->fcount += 2; // "." and ".."
216 ++di->fcount; // :files
217 if (!onlyfiles) {
218 // specials
219 di->fcount += ARRAYLEN(specdirs);
220 if (tagh == NULL) {
221 // path ends with tag value
222 // build tag list
223 dlogf("inorig=%d\n", inorig);
224 for (int f = 0; f < fcount; ++f) {
225 for (size_t sn = 0; sn < ARRAYLEN(specdirs); ++sn) {
226 if ((inorig == 0 && specdirs[sn][1] == 'o') || (inorig == 1 && specdirs[sn][1] != 'o')) continue;
227 ftlist = (tagvalue_t **)((uint8_t *)(files[f])+special_ofs(specdirs[sn]+1));
228 if (ftlist[0] != NULL) {
229 //dlogf("[%s] [%s]\n", files[f]->shortname, ftlist[0]->s);
230 HASH_FIND_STR(tl_hash, ftlist[0]->s, hx);
231 if (hx == NULL) {
232 hx = calloc(1, sizeof(*hx));
233 hx->tv = ftlist[0];
234 hx->s = ftlist[0]->s;
235 HASH_ADD_KEYPTR(hh, tl_hash, hx->s, strlen(hx->s), hx);
240 di->fcount += HASH_COUNT(tl_hash);
241 di->fcount += fcount;
242 } else {
243 // path ends with tag name: collect all from field
244 if (files == NULL) {
245 // /:artist -- collect all 'artist' tags
246 for (tagvalue_t *tv = *tagh; tv != NULL; tv = tv->hh.next) {
247 HASH_FIND_STR(tl_hash, tv->s, hx);
248 if (hx == NULL) {
249 hx = calloc(1, sizeof(*hx));
250 hx->tv = tv;
251 hx->s = tv->s;
252 HASH_ADD_KEYPTR(hh, tl_hash, hx->s, strlen(hx->s), hx);
255 } else {
256 // build tag list
257 dlogf("inorig=%d; tag '%s'; fcount=%d\n", inorig, condstr, fcount);
258 for (int f = 0; f < fcount; ++f) {
259 ftlist = (tagvalue_t **)((uint8_t *)(files[f])+fieldofs);
260 if (ftlist[0] != NULL) {
261 //dlogf("[%s] [%s]\n", files[f]->shortname, ftlist[0]->s);
262 HASH_FIND_STR(tl_hash, ftlist[0]->s, hx);
263 if (hx == NULL) {
264 hx = calloc(1, sizeof(*hx));
265 hx->tv = ftlist[0];
266 hx->s = ftlist[0]->s;
267 HASH_ADD_KEYPTR(hh, tl_hash, hx->s, strlen(hx->s), hx);
272 di->fcount += HASH_COUNT(tl_hash);
273 fcount = 0;
275 } else {
276 // only files
277 di->fcount = fcount;
280 di->names = calloc(di->fcount+1, sizeof(di->names[0]));
281 dlogf("allocated %d names (%p); pos=%d\n", di->fcount+1, di->names, pos);
282 if (!onlyfiles) {
283 // specials
284 di->names[pos++] = ":files";
285 for (size_t f = 0; f < ARRAYLEN(specdirs); ++f) {
286 if (!wasspec[f]) {
287 if (inorig < 0 || (inorig == 0 && specdirs[f][1] != 'o') || (inorig == 1 && specdirs[f][1] == 'o')) {
288 di->names[pos++] = specdirs[f];
292 // tags
293 HASH_ITER(hh, tl_hash, hx, thx) {
294 di->names[pos++] = hx->tv->s;
295 HASH_DEL(tl_hash, hx);
296 free(hx);
299 // files
300 if (fcount > 0) {
301 // sort files, so mplayer and others will see sorted dir
302 qsort(files, fcount, sizeof(files[0]), lambda(int, (const void *i0, const void *i1) {
303 const file_info_t **f0 = (const file_info_t **)i0;
304 const file_info_t **f1 = (const file_info_t **)i1;
305 //dlogf("[%s] [%s]\n", f0[0]->shortname, f1[0]->shortname);
306 return strcmp(f0[0]->shortname, f1[0]->shortname);
307 }));
308 for (int f = 0; f < fcount; ++f) di->names[pos++] = files[f]->shortname;
310 if (files != NULL) free(files);
311 di->fcount = pos;
312 return di;