merge down from merged-API-layer branch: cvs -q up -dP -j API_LAYER_MERGING_BASELINE...
[flac.git] / src / plugin_winamp2 / tagz.cpp
1 #include <string.h>\r
2 #include <stdio.h>\r
3 #include <ctype.h>\r
4 #include <stdlib.h>\r
5 #include "tagz.h"\r
6 \r
7 #ifdef TAGZ_UNICODE\r
8 \r
9 #define _TX(X)      L##X\r
10 #define t_strdup    wcsdup\r
11 #define t_strlen    wcslen\r
12 #define t_strnicmp  wcsnicmp\r
13 #define t_atoi(x)   wcstol(x,0,10)\r
14 #define t_stricmp   wcsicmp\r
15 #define t_strstr    wcsstr\r
16 #define sprintf     swprintf\r
17 \r
18 #else\r
19 \r
20 #define _TX(X)      X\r
21 #define t_strdup    strdup\r
22 #define t_strlen    strlen\r
23 #define t_strnicmp  strnicmp\r
24 #define t_atoi      atoi\r
25 #define t_stricmp   stricmp\r
26 #define t_strstr    strstr\r
27 \r
28 #endif\r
29 \r
30 #define TABSIZE(x) (sizeof(x)/sizeof(x[0]))\r
31 \r
32 \r
33 class T_String\r
34 {\r
35 private:\r
36         T_CHAR * data;\r
37         UINT size,used;\r
38 public:\r
39         T_String() {data=0;size=0;used=0;}\r
40         void AddChar(T_CHAR c)\r
41         {\r
42                 if (!data)\r
43                 {\r
44                         data=(T_CHAR*)malloc((size=512)*sizeof(T_CHAR));\r
45                         used=0;\r
46                 }\r
47                 else if (size==used)\r
48                 {\r
49                         size<<=1;\r
50                         data=(T_CHAR*)realloc((char*)data,size*sizeof(T_CHAR));\r
51                 }\r
52                 if (data) data[used++]=c;               \r
53         }\r
54         void AddInt(int i)\r
55         {\r
56                 T_CHAR foo[16];\r
57                 sprintf(foo,_TX("%i"),i);\r
58                 AddString(foo);\r
59         }\r
60         void AddString(const T_CHAR * z)\r
61         {\r
62                 while(*z) {AddChar(*z);z++;}\r
63         }\r
64         void AddString(T_String & s)\r
65         {\r
66                 AddString(s.Peek());\r
67         }\r
68         ~T_String()\r
69         {\r
70                 if (data) free(data);\r
71         }\r
72         T_CHAR * GetBuf()\r
73         {\r
74                 if (!data) return ::t_strdup(_TX(""));\r
75                 T_CHAR * r=(T_CHAR*)realloc(data,(used+1)*sizeof(T_CHAR));\r
76                 r[used]=0;\r
77                 data=0;\r
78                 return r;\r
79         }\r
80         T_CHAR operator[](UINT i)\r
81         {\r
82                 if (!data || i>=used) return 0;\r
83                 else return data[i];\r
84         }\r
85         UINT Len() {return data ? used : 0;}\r
86         void Reset()\r
87         {\r
88                 if (data) {free(data);data=0;}          \r
89         }\r
90         const T_CHAR * Peek()\r
91         {\r
92                 AddChar(0);\r
93                 used--;\r
94                 return data;\r
95         }\r
96         T_CHAR * strdup()\r
97         {\r
98                 return ::t_strdup(Peek());\r
99         }\r
100 };\r
101 \r
102 \r
103 \r
104 \r
105 static int separator(T_CHAR x)\r
106 {\r
107         if (!x || x==' ') return 1;\r
108         if (x=='\'' || x=='_') return 0;\r
109 #ifdef TAGZ_UNICODE\r
110         return !iswalnum(x);\r
111 #else\r
112         return !isalnum(x);\r
113 #endif\r
114 }\r
115 \r
116 static int sepcmp(T_CHAR* src,T_CHAR* val)\r
117 {\r
118         UINT l=t_strlen(val);\r
119         return !t_strnicmp(src,val,l) && separator(src[l]);\r
120 }\r
121 \r
122 static char roman_num[]=\r
123 {\r
124         'I','V','X','L','C','D','M'\r
125 };\r
126 \r
127 \r
128 static int is_roman(T_CHAR * ptr)/* could be more smart i think */\r
129 {\r
130         if (ptr[0]==']' && ptr[1]=='[' && separator(ptr[2])) return 1;\r
131         while(!separator(*ptr))\r
132         {\r
133                 UINT n;\r
134                 bool found=0;\r
135                 for(n=0;n<TABSIZE(roman_num);n++)\r
136                 {\r
137                         if (*ptr==roman_num[n]) {found=1;break;}\r
138                 }\r
139                 if (!found) return 0;\r
140                 ptr++;\r
141         }\r
142         return 1;\r
143 }\r
144 \r
145 static int need_full(T_CHAR* ptr)\r
146 {\r
147         if (is_roman(ptr)) return 1;\r
148         if (sepcmp(ptr,_TX("RPG"))) return 1;\r
149         while(!separator(*ptr))\r
150         {\r
151                 if (*ptr<'0' || *ptr>'9') return 0;\r
152                 ptr++;\r
153         }\r
154         return 1;\r
155 }\r
156 \r
157 typedef bool (*TEXTFUNC)(UINT n_src,T_CHAR **src,UINT*,T_String &out);\r
158 \r
159 #define MAKEFUNC(X) static bool X(UINT n_src,T_CHAR ** src,UINT *found_src,T_String &out)\r
160 \r
161 \r
162 MAKEFUNC(If)\r
163 {\r
164         if (n_src!=3) return false;\r
165 \r
166         out.AddString(src[found_src[0] ? 1 : 2]);\r
167         return true;\r
168 }\r
169 \r
170 MAKEFUNC(If2)\r
171 {\r
172         if (n_src!=2) return false;\r
173 \r
174         out.AddString(src[found_src[0] ? 0 : 1]);\r
175         return true;\r
176 }\r
177 \r
178 \r
179 MAKEFUNC(Iflonger)\r
180 {\r
181         if (n_src!=4) return false;\r
182 \r
183         out.AddString(src[(int)t_strlen(src[0])>t_atoi(src[1]) ? 2 : 3]);\r
184         return true;\r
185 }\r
186 \r
187 MAKEFUNC(Ifgreater)\r
188 {\r
189         if (n_src!=4) return false;\r
190 \r
191         out.AddString(src[t_atoi(src[0])>t_atoi(src[1]) ? 2 : 3]);\r
192         return true;\r
193 }\r
194 \r
195 MAKEFUNC(Upper)\r
196 {\r
197         if (n_src!=1) return false;\r
198 \r
199         T_CHAR * s=src[0];\r
200 \r
201         while(*s)\r
202                 out.AddChar(toupper(*(s++)));\r
203 \r
204         return true;\r
205 }\r
206 \r
207 MAKEFUNC(Lower)\r
208 {\r
209         if (n_src!=1) return false;\r
210 \r
211         T_CHAR * s=src[0];\r
212 \r
213         while(*s)\r
214                 out.AddChar(tolower(*(s++)));\r
215 \r
216         return true;\r
217 }\r
218 \r
219 MAKEFUNC(Pad)\r
220 {\r
221         if (n_src<2 || n_src>3) return false;\r
222 \r
223         T_CHAR *fill=_TX(" ");\r
224         if (n_src==3 && src[2][0])\r
225                 fill = src[2];\r
226 \r
227         int num = t_atoi(src[1]);\r
228         T_CHAR *p = src[0];\r
229 \r
230         while (*p) { out.AddChar(*(p++)); num--; }\r
231 \r
232         UINT fl = t_strlen(fill);\r
233         while (num>0)\r
234                 out.AddChar(fill[(--num)%fl]);\r
235 \r
236         return true;\r
237 }\r
238 \r
239 MAKEFUNC(Cut)\r
240 {\r
241         if (n_src!=2) return false;\r
242 \r
243         UINT num = t_atoi(src[1]);\r
244         T_CHAR *p = src[0];\r
245 \r
246         while (*p && num>0) {out.AddChar(*(p++));num--;}\r
247 \r
248         return true;\r
249 }\r
250 \r
251 MAKEFUNC(PadCut)\r
252 {\r
253         if (n_src<2 || n_src>3) return false;\r
254 \r
255         T_CHAR *fill = _TX(" ");\r
256         if (n_src==3 && src[2][0])\r
257                 fill = src[2];\r
258 \r
259         int num = t_atoi(src[1]);\r
260         T_CHAR *p = src[0];\r
261 \r
262         while(*p && num>0) {out.AddChar(*(p++));num--;}\r
263 \r
264         UINT fl=t_strlen(fill);\r
265         while (num>0)\r
266                 out.AddChar(fill[(--num)%fl]);\r
267 \r
268         return true;\r
269 }\r
270 \r
271 /* abbr(string) */\r
272 /* abbr(string,len) */\r
273 MAKEFUNC(Abbr)\r
274 {\r
275         if (n_src==0 || n_src>2) return false;\r
276 \r
277 \r
278         if (n_src==2 && (int)t_strlen(src[0])<t_atoi(src[1]))\r
279         {\r
280                 out.AddString(src[0]);\r
281                 return true;\r
282         }\r
283 \r
284         T_CHAR * meta=src[0];\r
285         bool w=0, r=0;\r
286 \r
287         while(*meta)\r
288         {\r
289                 bool an=!separator(*meta) || *meta==']' || *meta=='[';\r
290 \r
291                 if (w && !an)\r
292                         w=0;\r
293                 else if (!w && an)\r
294                 {\r
295                         w=1;\r
296                         r=need_full(meta)?1:0;\r
297                         out.AddChar(*meta);\r
298                 }\r
299                 else if (w && r)\r
300                         out.AddChar(*meta);\r
301                 meta++;\r
302         }\r
303 \r
304         return true;\r
305 }\r
306 \r
307 \r
308 \r
309 MAKEFUNC(Caps)\r
310 {\r
311         if (n_src!=1) return false;\r
312 \r
313         T_CHAR* sp=src[0];\r
314         int sep = 1;\r
315 \r
316         while(*sp)\r
317         {\r
318                 T_CHAR c=*(sp++);\r
319                 int s = separator(c);\r
320                 if (!s && sep)\r
321                         c=toupper(c);\r
322                 else if (!sep) c=tolower(c);\r
323                 sep=s;\r
324                 out.AddChar(c);\r
325         }\r
326 \r
327         return true;\r
328 }\r
329 \r
330 MAKEFUNC(Caps2)\r
331 {\r
332         if (n_src!=1) return false;\r
333 \r
334         T_CHAR* sp=src[0];\r
335         int sep=1;\r
336 \r
337         while(*sp)\r
338         {\r
339                 T_CHAR c=*(sp++);\r
340                 int s = separator(c);\r
341                 if (!s && sep)\r
342                         c=toupper(c);\r
343                 sep=s;\r
344                 out.AddChar(c);\r
345         }\r
346 \r
347         return true;\r
348 }\r
349 \r
350 MAKEFUNC(Longest)\r
351 {\r
352         T_CHAR *ptr=0;\r
353         UINT n, m=0;\r
354 \r
355         for(n=0;n<n_src;n++)\r
356         {\r
357                 UINT l=t_strlen(src[n]);\r
358                 if (l>m) {m=l;ptr=src[n];}\r
359         }\r
360 \r
361         if (ptr) out.AddString(ptr);\r
362         return true;\r
363 }\r
364 \r
365 MAKEFUNC(Shortest)\r
366 {\r
367         T_CHAR * ptr=0;\r
368         UINT n,m=(UINT)(-1);\r
369 \r
370         for(n=0;n<n_src;n++)\r
371         {\r
372                 UINT l=t_strlen(src[n]);\r
373                 if (l<m) {m=l;ptr=src[n];}\r
374         }\r
375 \r
376         if (ptr) out.AddString(ptr);\r
377         return true;\r
378 }\r
379 \r
380 MAKEFUNC(Num)\r
381 {\r
382         if (n_src!=2) return false;\r
383 \r
384         T_CHAR tmp[16];\r
385         T_CHAR tmp1[16];\r
386         sprintf(tmp1,_TX("%%0%uu"),t_atoi(src[1]));\r
387         sprintf(tmp,tmp1,t_atoi(src[0]));\r
388         out.AddString(tmp);\r
389 \r
390         return true;\r
391 }\r
392 \r
393 MAKEFUNC(Hex)\r
394 {\r
395         if (n_src!=2) return false;\r
396 \r
397         T_CHAR tmp[16];\r
398         T_CHAR tmp1[16];\r
399         sprintf(tmp1,_TX("%%0%ux"),t_atoi(src[1]));\r
400         sprintf(tmp,tmp1,t_atoi(src[0]));\r
401         out.AddString(tmp);\r
402 \r
403         return true;\r
404 }\r
405 \r
406 MAKEFUNC(StrChr)\r
407 {\r
408         if (n_src!=2) return false;\r
409 \r
410         T_CHAR * p=src[0];\r
411         T_CHAR s=src[1][0];\r
412 \r
413         while (*p && *p!=s) p++;\r
414         if (*p==s)\r
415                 out.AddInt(1+p-src[0]);\r
416         else out.AddChar('0');\r
417 \r
418         return true;\r
419 }\r
420 \r
421 MAKEFUNC(StrRChr)\r
422 {\r
423         if (n_src!=2) return false;\r
424 \r
425         T_CHAR * p=src[0],*p1=0;\r
426         T_CHAR s=src[1][0];\r
427 \r
428         while(*p)\r
429         {\r
430                 if (*p==s) p1=p;\r
431                 p++;\r
432         }\r
433 \r
434         if (p1)\r
435                 out.AddInt(1+p1-src[0]);\r
436         else out.AddChar('0');\r
437 \r
438         return true;\r
439 }\r
440 \r
441 MAKEFUNC(StrStr)\r
442 {\r
443         if (n_src!=2) return false;\r
444 \r
445         T_CHAR * p = t_strstr(src[0],src[1]);\r
446 \r
447         if (p)\r
448                 out.AddInt(1+p-src[0]);\r
449         else out.AddChar('0');\r
450 \r
451         return true;\r
452 }\r
453 \r
454 /* substr(string, index) */\r
455 /* substr(string, index, length) */\r
456 MAKEFUNC(SubStr)\r
457 {\r
458         if (n_src<2 || n_src>3) return false;\r
459 \r
460         int n1 = t_atoi(src[1]), n2;\r
461 \r
462         if (n_src == 3)\r
463                 n2 = t_atoi(src[2]);\r
464         else n2 = n1;\r
465 \r
466         if (n1 < 1) n1=1;\r
467         if (n2 >= n1)\r
468         {\r
469                 n1--;\r
470                 n2--;\r
471                 while(n1<=n2 && src[0][n1])\r
472                         out.AddChar(src[0][n1++]);\r
473         }\r
474 \r
475         return true;\r
476 }\r
477 \r
478 MAKEFUNC(Len)\r
479 {\r
480         if (n_src!=1) return false;\r
481 \r
482         out.AddInt(t_strlen(src[0]));\r
483         return true;\r
484 }\r
485 \r
486 MAKEFUNC(Add)\r
487 {\r
488         UINT n;\r
489         int s=0;\r
490 \r
491         for (n=0;n<n_src;n++)\r
492                 s+=t_atoi(src[n]);\r
493 \r
494         out.AddInt(s);\r
495 \r
496         return true;\r
497 }\r
498 \r
499 MAKEFUNC(Sub)\r
500 {\r
501         if (n_src==0) return false;\r
502 \r
503         UINT n;\r
504         int s=t_atoi(src[0]);\r
505 \r
506         for (n=1;n<n_src;n++)\r
507                 s-=t_atoi(src[n]);\r
508 \r
509         out.AddInt(s);\r
510 \r
511         return true;\r
512 }\r
513 \r
514 MAKEFUNC(Mul)\r
515 {\r
516         UINT n;\r
517         int s=1;\r
518 \r
519         for(n=0;n<n_src;n++)\r
520                 s*=t_atoi(src[n]);\r
521 \r
522         out.AddInt(s);\r
523 \r
524         return true;\r
525 }\r
526                                 \r
527 MAKEFUNC(Div)\r
528 {\r
529         if (n_src==0) return false;\r
530 \r
531         UINT n;\r
532         int s=t_atoi(src[0]);\r
533 \r
534         for(n=1;n<n_src;n++)\r
535         {\r
536                 int t=t_atoi(src[n]);\r
537                 if (t) s/=t;\r
538                 else t=0;\r
539         }\r
540 \r
541         out.AddInt(s);\r
542 \r
543         return true;\r
544 }\r
545 \r
546 MAKEFUNC(Mod)\r
547 {\r
548         if (n_src==0) return false;\r
549 \r
550         UINT n;\r
551         int s=t_atoi(src[0]);\r
552 \r
553         for(n=1;n<n_src;n++)\r
554         {\r
555                 int t=t_atoi(src[n]);\r
556                 if (t) s%=t;\r
557                 else t=0;\r
558         }\r
559 \r
560         out.AddInt(s);\r
561 \r
562         return true;\r
563 }\r
564 \r
565 MAKEFUNC(Max)\r
566 {\r
567         if (!n_src) return false;\r
568 \r
569         int m = t_atoi(src[0]);\r
570         UINT n;\r
571 \r
572         for (n=1; n<n_src; n++)\r
573         {\r
574                 int t = t_atoi(src[n]);\r
575                 if (t > m) m = t;\r
576         }\r
577         out.AddInt(m);\r
578 \r
579         return true;\r
580 }\r
581 \r
582 MAKEFUNC(Min)\r
583 {\r
584         if (!n_src) return false;\r
585 \r
586         int m=t_atoi(src[0]);\r
587         UINT n;\r
588 \r
589         for(n=1;n<n_src;n++)\r
590         {\r
591                 int t=t_atoi(src[n]);\r
592                 if (t<m) m=t;\r
593         }\r
594         out.AddInt(m);\r
595 \r
596         return true;\r
597 }\r
598 \r
599 /* replace(string, what_to_replace, replacement) */\r
600 MAKEFUNC(Replace)\r
601 {\r
602         if (n_src!=3) return false;\r
603         T_CHAR *p = src[0];\r
604 \r
605         while (*p)\r
606         {\r
607                 UINT n=0;\r
608 \r
609                 while (src[1][n] && p[n]==src[1][n]) n++;\r
610 \r
611                 if (!src[1][n])\r
612                 {\r
613                         out.AddString(src[2]);\r
614                         p += n;\r
615                 }\r
616                 else out.AddChar(*p++);\r
617         }\r
618 \r
619         return true;\r
620 }\r
621 \r
622 struct\r
623 {\r
624         TEXTFUNC func;\r
625         const T_CHAR * name;\r
626 }\r
627 FUNCS[] =\r
628 {\r
629         If,_TX("if"),\r
630         If2,_TX("if2"),\r
631         Upper,_TX("upper"),\r
632         Lower,_TX("lower"),\r
633         Pad,_TX("pad"),\r
634         Cut,_TX("cut"),\r
635         PadCut,_TX("padcut"),\r
636         Abbr,_TX("abbr"),\r
637         Caps,_TX("caps"),\r
638         Caps2,_TX("caps2"),\r
639         Longest,_TX("longest"),\r
640         Shortest,_TX("shortest"),\r
641         Iflonger,_TX("iflonger"),\r
642         Ifgreater,_TX("ifgreater"),\r
643         Num,_TX("num"),Num,_TX("dec"),\r
644         Hex,_TX("hex"),\r
645         StrChr,_TX("strchr"),\r
646         StrChr,_TX("strlchr"),\r
647         StrRChr,_TX("strrchr"),\r
648         StrStr,_TX("strstr"),\r
649         SubStr,_TX("substr"),\r
650         Len,_TX("len"),\r
651         Add,_TX("add"),\r
652         Sub,_TX("sub"),\r
653         Mul,_TX("mul"),\r
654         Div,_TX("div"),\r
655         Mod,_TX("mod"),\r
656         Min,_TX("min"),\r
657         Max,_TX("max"),\r
658         Replace,_TX("replace"),\r
659 };\r
660 \r
661 \r
662 class FMT\r
663 {\r
664 private:\r
665         T_String str;\r
666         T_CHAR * spec;\r
667         TAGFUNC f;\r
668         TAGFREEFUNC ff;\r
669         void * fp;\r
670         T_CHAR * org_spec;\r
671         int found;\r
672 \r
673         void Error(T_CHAR *e=0)\r
674         {\r
675                 str.Reset();\r
676                 str.AddString(e ? e : _TX("[SYNTAX ERROR IN FORMATTING STRING]"));\r
677                 found++;  /* force displaying */\r
678         }\r
679 \r
680         T_CHAR * _FMT(T_CHAR * s,UINT *f=0)\r
681         {\r
682                 FMT fmt(this,s);\r
683                 T_CHAR * c=(T_CHAR*)fmt;\r
684                 if (f) *f=fmt.found;\r
685                 found+=fmt.found;\r
686                 return c;\r
687         }\r
688 \r
689         static bool skipshit(T_CHAR** _p,T_CHAR *bl)\r
690         {\r
691                 T_CHAR * p=*_p;\r
692                 int bc1=0,bc2=0;\r
693                 while(*p)\r
694                 {\r
695                         if (!bc1 && !bc2 && bl)\r
696                         {\r
697                                 T_CHAR *z=bl;\r
698                                 while(*z)\r
699                                 {\r
700                                         if (*z==*p) break;\r
701                                         z++;\r
702                                 }\r
703                                 if (*z) break;                          \r
704                         }\r
705                         if (*p=='\'')\r
706                         {\r
707                                 p++;\r
708                                 while(*p && *p!='\'') p++;\r
709                                 if (!*p) return 0;\r
710                         }\r
711                         else if (*p=='(') bc1++;\r
712                         else if (*p==')')\r
713                         {\r
714                                 if (--bc1<0) return 0;\r
715                         }\r
716                         else if (*p=='[') bc2++;\r
717                         else if (*p==']')\r
718                         {\r
719                                 if (--bc2<0) return 0;\r
720                         }\r
721                         p++;\r
722                 }\r
723                 *_p=p;\r
724                 return *p && !bc1 && !bc2;\r
725         }\r
726 \r
727         void run()\r
728         {\r
729                 if (!spec) {Error();return;}\r
730                 while(*spec)\r
731                 {\r
732                         if (*spec=='%')\r
733                         {\r
734                                 spec++;\r
735                                 if (*spec=='%') {str.AddChar('%');spec++;continue;}\r
736                                 T_CHAR* s1=spec+1;\r
737                                 while(*s1 && *s1!='%') s1++;\r
738                                 if (!*s1) {Error();break;}\r
739                                 *s1=0;\r
740                                 T_CHAR * tag=f(spec,fp);\r
741                                 *s1='%';\r
742                                 /*if (!tag) tag=tag_unknown; */\r
743                                 if (tag && tag[0])\r
744                                 {\r
745                                         found++;\r
746                                         str.AddString(tag);\r
747                                 }\r
748                                 else\r
749                                 {\r
750                                         str.AddString(_TX("?"));\r
751                                 }\r
752                                 if (tag && ff) ff(tag,fp);\r
753                                 spec=s1+1;\r
754                         }\r
755                         else if (*spec=='$')\r
756                         {\r
757                                 spec++;\r
758                                 if (*spec=='$') {str.AddChar('$');spec++;continue;}\r
759                                 T_CHAR * s1=spec+1;\r
760                                 while(*s1 && *s1!='(') s1++;\r
761                                 if (!*s1) {Error();break;}\r
762                                 T_CHAR * s2=s1+1;\r
763                                 if (!skipshit(&s2,_TX(")"))) {Error();break;}\r
764                                 if (!*s2) {Error();break;};\r
765                                 T_CHAR * p=s1+1;\r
766                                 T_CHAR* temp[64];\r
767                                 UINT temp_f[64];\r
768                                 UINT nt=0;\r
769                                 T_CHAR * p1=s1+1;\r
770                                 while(p<=s2 && nt<64)\r
771                                 {\r
772                                         if (!skipshit(&p,_TX(",)"))) {Error();return;}\r
773                                         if (p>s2 || (*p!=',' && *p!=')')) {Error(_TX("internal error"));return;}\r
774                                         T_CHAR bk=*p;\r
775                                         *p=0;\r
776                                         temp[nt]=_FMT(p1,&temp_f[nt]);\r
777                                         nt++;\r
778                                         *p=bk;;\r
779                                         p1=p+1;\r
780                                         p++;\r
781                                 }\r
782                                 *s1=0;\r
783                                 UINT n;\r
784 \r
785                                 for (n=0; n<TABSIZE(FUNCS); n++)\r
786                                         if (!t_stricmp(spec, FUNCS[n].name))\r
787                                                 break;\r
788 \r
789                                 *s1='(';\r
790 \r
791                                 if (n != TABSIZE(FUNCS))\r
792                                 {\r
793                                         if (!FUNCS[n].func(nt, temp, temp_f, str))\r
794                                         {\r
795                                                 Error(_TX("[INVALID $"));\r
796                                                 str.AddString(FUNCS[n].name);\r
797                                                 str.AddString(_TX(" SYNTAX]"));\r
798                                                 return;\r
799                                         }\r
800                                 }\r
801                                 else\r
802                                 {\r
803                                         Error(_TX("[UNKNOWN FUNCTION]"));\r
804                                         return;\r
805                                 }\r
806 \r
807                                 for(n=0;n<nt;n++) free(temp[n]);\r
808                                 spec=s2+1;\r
809                         }\r
810                         else if (*spec=='\'')\r
811                         {\r
812                                 spec++;\r
813                                 if (*spec=='\'') {str.AddChar('\'');spec++;continue;}\r
814                                 T_CHAR * s1=spec+1;\r
815                                 while(*s1 && *s1!='\'') s1++;\r
816                                 if (!*s1) {Error();break;}\r
817                                 *s1=0;\r
818                                 str.AddString(spec);\r
819                                 *s1='\'';\r
820                                 spec=s1+1;\r
821                         }\r
822                         else if (*spec=='[')\r
823                         {\r
824                                 spec++;\r
825                                 T_CHAR * s1=spec;\r
826                                 UINT bc=0;\r
827                                 if (!skipshit(&s1,_TX("]"))) {Error();break;}\r
828                                 T_CHAR bk=*s1;\r
829                                 *s1=0;\r
830                                 FMT fmt(this,spec);\r
831                                 fmt.run();\r
832                                 if (fmt.found)\r
833                                 {\r
834                                         str.AddString(fmt.str);\r
835                                         found+=fmt.found;\r
836                                 }\r
837                                 *s1=bk;\r
838                                 spec=s1+1;\r
839                         }\r
840                         else if (*spec == ']') {Error();break;}\r
841                         else\r
842                         {\r
843                                 str.AddChar(*spec);\r
844                                 spec++;\r
845                         }\r
846                 }\r
847         }\r
848 \r
849         FMT(FMT* base,T_CHAR * _spec)\r
850         {\r
851                 found=0;\r
852                 org_spec=0;\r
853                 f=base->f;\r
854                 ff=base->ff;\r
855                 fp=base->fp;\r
856                 spec=_spec;\r
857         }\r
858 public:\r
859         FMT(const T_CHAR * p_spec,TAGFUNC _f,TAGFREEFUNC _ff,void * _fp)\r
860         {\r
861                 found=0;\r
862                 org_spec=spec=t_strdup(p_spec);\r
863                 f=_f;\r
864                 ff=_ff;\r
865                 fp=_fp;\r
866         }\r
867         operator T_CHAR*()\r
868         {\r
869                 run();\r
870                 return str.GetBuf();\r
871         }\r
872         ~FMT()\r
873         {\r
874                 if (org_spec) free(org_spec);\r
875         }\r
876 };\r
877 \r
878 extern "C"\r
879 {\r
880 \r
881 UINT tagz_format(const T_CHAR * spec,TAGFUNC f,TAGFREEFUNC ff,void *fp,T_CHAR* out,UINT max)\r
882 {\r
883         T_CHAR * zz=tagz_format_r(spec,f,ff,fp);\r
884         UINT r=0;\r
885         while(r<max-1 && zz[r])\r
886         {\r
887                 out[r]=zz[r];\r
888                 r++;\r
889         }\r
890         out[r]=0;\r
891         free(zz);\r
892         return r;\r
893 }       \r
894 \r
895 T_CHAR * tagz_format_r(const T_CHAR* spec,TAGFUNC f,TAGFREEFUNC ff,void * fp)\r
896 {\r
897         return FMT(spec,f,ff,fp);\r
898 }\r
899 \r
900 const char tagz_manual[]="Syntax reference: \n"\r
901         "\n"\r
902         "* %tagname% - inserts field named <tagname>, eg. \"%artist%\"\n"\r
903         "* $abbr(x) - inserts abbreviation of x, eg. \"$abbr(%album%)\" - will convert album name of \"Final Fantasy VI\" to \"FFVI\"\n"\r
904         "* $abbr(x,y) - inserts abbreviation of x if x is longer than y characters; otherwise inserts full value of x, eg. \"$abbr(%album%,10)\"\n"\r
905         "* $lower(x), $upper(x) - converts x to in lower/uppercase, eg. \"$upper(%title%)\"\n"\r
906         "* $num(x,y) - displays x number and pads with zeros up to y characters (useful for track numbers), eg. $num(%tracknumber%,2)\n"\r
907         "* $caps(x) - converts first letter in every word of x to uppercase, and all other letters to lowercase, eg. \"blah BLAH\" -> \"Blah Blah\"\n"\r
908         "* $caps2(x) - similar to $caps, but leaves uppercase letters as they are, eg. \"blah BLAH\" -> \"Blah BLAH\"\n"\r
909         "* $if(A,B,C) - if A contains at least one valid tag, displays B, otherwise displays C; eg. \"$if(%artist%,%artist%,unknown artist)\" will display artist name if present; otherwise will display \"unknown artist\"; note that \"$if(A,A,)\" is equivalent to \"[A]\" (see below)\n"\r
910         "* $if2(A,B) - equals to $if(A,A,B)\n"\r
911         "* $longest(A,B,C,....) - compares lengths of output strings produced by A,B,C... and displays the longest one, eg. \"$longest(%title%,%comment%)\" will display either title if it's longer than comment; otherwise it will display comment\n"\r
912         "* $pad(x,y) - pads x with spaces up to y characters\n"\r
913         "* $cut(x,y) - truncates x to y characters\n"\r
914         "* $padcut(x,y) - pads x to y characters and truncates to y if longer\n"\r
915         "* [ .... ] - displays contents of brackets only if at least one of fields referenced inside has been found, eg. \"%artist% - [%album% / ]%title%\" will hide [] block if album field is not present\n"\r
916         "* \' (single quotation mark) - outputs raw text without parsing, eg, \'blah$blah%blah[][]\' will output the contained string and ignore all reserved characters (%,$,[,]) in it; you can use this feature to insert square brackets for an example.\n"\r
917         "\n"\r
918         "eg. \"[%artist% - ][$abbr(%album%,10)[ %tracknumber%] / ]%title%[ %streamtitle%]\"\n";\r
919 \r
920 \r
921 }\r