tagz.cpp
上传用户:wstnjxml
上传日期:2014-04-03
资源大小:7248k
文件大小:16k
源码类别:

Windows CE

开发平台:

C/C++

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