6 const char **names
; // short names
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
;
27 taghh_t
*tl_hash
= NULL
, *hx
, *thx
;
28 int wasspec
[ARRAYLEN(specdirs
)];
31 void add_from_tv (const tagvalue_t
*tv
) {
33 files
= calloc(1, sizeof(file_info_t
*)*tv
->idcount
);
34 for (int f
= 0; f
< tv
->idcount
; ++f
) files
[f
] = tv
->ids
[f
];
38 void remove_not_in_tv (const tagvalue_t
*tv
) {
39 for (int c
= 0; c
< fcount
; ++c
) {
41 for (int f
= 0; f
< tv
->idcount
; ++f
) if (files
[c
] == tv
->ids
[f
]) { found
= 1; break; }
43 // remove this file from list
44 for (int f
= c
+1; f
< fcount
; ++f
) files
[f
-1] = files
[f
];
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
;
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
;
83 dlogf("path: [%s]\n", path
);
84 // select and then refine
87 while (*path
== '/') ++path
;
90 char *e
= strchr(path
, '/');
92 strcpy(condstr
, path
);
95 memcpy(condstr
, path
, e
-path
);
100 dlogf("(fcount=%d) condstr: [%s]; inorig: %d\n", fcount
, condstr
, inorig
);
101 if (condstr
[0] == ':') {
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
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
116 dlogf("special: [%s] (fc=%d)\n", condstr
, fcount
);
117 if (strcmp(condstr
, ":files") == 0) {
118 // we want only 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
;
128 if (strcmp(condstr
, ":otags") == 0) {
129 if (inorig
>= 0) { dlogf(" error: 'otags' in tmode\n"); fcount
= 0; break; } // error
131 dlogf("otags; path=[%s]\n", path
);
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;;
141 if (inorig
== 1) { dlogf(" 't' tag disabled, inorig=%d\n", inorig
); fcount
= 0; break; } // no normal in orig dirs
143 if (inorig
< 0) for (size_t f
= 0; f
< ARRAYLEN(specdirs
); ++f
) if (specdirs
[f
][1] == 'o') wasspec
[f
] = 1;;
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));
158 if (inorig
< 0 || inorig
== 0) {
159 HASH_FIND_STR(tagv_hash_t
, condstr
, tv
);
163 dlogf(" for tag '%s': %d items\n", condstr
, tv
->idcount
);
165 // first time: add all files with this tag
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
);
178 dlogf(" for tag '%s': %d items\n", condstr
, tv
->idcount
);
180 // first time: add all files with this tag
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
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
);
196 // first time: add all files with this tag
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;
205 for (int c
= f
+1; c
< fcount
; ++c
) files
[c
-1] = files
[c
];
210 tagh
= NULL
; // processed
215 //di->fcount += 2; // "." and ".."
216 ++di
->fcount
; // :files
219 di
->fcount
+= ARRAYLEN(specdirs
);
221 // path ends with tag value
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
);
232 hx
= calloc(1, sizeof(*hx
));
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
;
243 // path ends with tag name: collect all from field
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
);
249 hx
= calloc(1, sizeof(*hx
));
252 HASH_ADD_KEYPTR(hh
, tl_hash
, hx
->s
, strlen(hx
->s
), hx
);
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
);
264 hx
= calloc(1, sizeof(*hx
));
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
);
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
);
284 di
->names
[pos
++] = ":files";
285 for (size_t f
= 0; f
< ARRAYLEN(specdirs
); ++f
) {
287 if (inorig
< 0 || (inorig
== 0 && specdirs
[f
][1] != 'o') || (inorig
== 1 && specdirs
[f
][1] == 'o')) {
288 di
->names
[pos
++] = specdirs
[f
];
293 HASH_ITER(hh
, tl_hash
, hx
, thx
) {
294 di
->names
[pos
++] = hx
->tv
->s
;
295 HASH_DEL(tl_hash
, hx
);
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
);
308 for (int f
= 0; f
< fcount
; ++f
) di
->names
[pos
++] = files
[f
]->shortname
;
310 if (files
!= NULL
) free(files
);