#include #include #include #include #include #include "tagz.h" #ifdef TAGZ_UNICODE #define _TX(X) L##X #define t_strdup _wcsdup #define t_strlen wcslen #define t_strnicmp _wcsnicmp #define t_strtoul wcstoul #define t_stricmp _wcsicmp #define t_strstr wcsstr #define sprintf swprintf #else #define _TX(X) X #define t_strdup _strdup #define t_strlen strlen #define t_strnicmp _strnicmp #define t_strtoul strtoul #define t_stricmp _stricmp #define t_strstr strstr #endif int t_atoi(T_CHAR * c) { if (c[0]=='-') return -(int)t_strtoul(c+1,0,10); else return (int)t_strtoul(c,0,10); } #define TABSIZE(x) (sizeof(x)/sizeof(x[0])) /* char * strndup(char * p1,UINT n) { char * r=(char*)malloc(n+1); if (r) { memcpy(r,p1,n); r[n]=0; } return n; } */ class String { private: T_CHAR * data; UINT size, used; public: String() {data=0;size=0;used=0;} void AddChar(T_CHAR c) { if (!data) { data=(T_CHAR*)malloc((size=512)*sizeof(T_CHAR)); used=0; } else if (size==used) { UINT old_size = size; T_CHAR * old_data = data; size<<=1; data=(T_CHAR*)realloc((char*)data,size*sizeof(T_CHAR)); if (!data) { size = old_size; data = old_data; return; } } if (data) data[used++]=c; } void AddInt(int i) { T_CHAR foo[16]; sprintf(foo,_TX("%i"),i); AddString(foo); } void AddString(const T_CHAR * z) { while(*z) {AddChar(*z);z++;} } void AddString(String & s) { AddString(s.Peek()); } ~String() { if (data) free(data); } T_CHAR * GetBuf() { if (!data) return t_strdup(_TX("")); T_CHAR * r=(T_CHAR*)realloc(data,(used+1)*sizeof(T_CHAR)); r[used]=0; data=0; return r; } T_CHAR operator[](UINT i) { if (!data || i>=used) return 0; else return data[i]; } UINT Len() {return data ? used : 0;} void Reset() { if (data) {free(data);data=0;} } const T_CHAR * Peek() { AddChar(0); used--; return data; } T_CHAR * _strdup() { return ::t_strdup(Peek()); } }; static int separator(T_CHAR x) { if (!x || x==' ') return 1; if (x=='\'' || x=='_') return 0; #ifdef TAGZ_UNICODE return !iswalnum(x); #else return !isalnum(x); #endif } static int sepcmp(T_CHAR* src,T_CHAR* val) { UINT l=t_strlen(val); return !t_strnicmp(src,val,l) && separator(src[l]); } static char roman_num[]= { 'I','V','X','L','C','D','M' }; static int is_roman(T_CHAR * ptr)//could be more smart i think { if (ptr[0]==']' && ptr[1]=='[' && separator(ptr[2])) return 1; while(!separator(*ptr)) { UINT n; bool found=0; for(n=0;n='9') return 0; ptr++; } return 1; } class VarList { private: typedef struct tagVAR { tagVAR * next; T_CHAR * name; T_CHAR * value; } VAR; VAR * vars; public: VarList() {vars=0;} ~VarList() { VAR * p=vars; while(p) { VAR *n=p->next; free(p->name); free(p->value); delete p; p=n; } } T_CHAR * Get(T_CHAR * name) { VAR * p=vars; while(p) { if (!t_stricmp(name,p->name)) return p->value; p=p->next; } return 0; } void Put(T_CHAR * name,T_CHAR* value) { VAR * p=vars; while(p) { if (!t_stricmp(name,p->name)) { free(p->value); p->value=t_strdup(value); return; } p=p->next; } p=new VAR; p->next=vars; vars=p; p->value=t_strdup(value); p->name=t_strdup(name); } }; typedef void (*TEXTFUNC)(UINT n_src,T_CHAR **src,UINT*,String &out,VarList & vars); #define MAKEFUNC(X) static void X(UINT n_src,T_CHAR ** src,UINT *found_src,String &out,VarList &vars) /* void Blah(UINT n_src,T_CHAR ** src,UINT*,String &out) { out.AddString("blah"); } void Nop(UINT n_src,T_CHAR ** src,UINT *,String &out) { UINT n; for(n=0;n=1) { T_CHAR * p=vars.Get(src[0]); if (p) out.AddString(p); } } MAKEFUNC(Put) { if (n_src>=2) { vars.Put(src[0],src[1]); out.AddString(src[1]); } } MAKEFUNC(PutQ) { if (n_src>=2) { vars.Put(src[0],src[1]); } } MAKEFUNC(If) { if (n_src!=3) out.AddString(_TX("[INVALID $IF SYNTAX]")); else { out.AddString(src[found_src[0] ? 1 : 2]); } } MAKEFUNC(Iflonger) { if (n_src!=4) out.AddString(_TX("[INVALID $IFLONGER SYNTAX]")); else { out.AddString(src[(int)t_strlen(src[0])>t_atoi(src[1]) ? 2 : 3]); } } MAKEFUNC(Ifgreater) { if (n_src!=4) out.AddString(_TX("[INVALID $IFGREATER SYNTAX]")); else { out.AddString(src[t_atoi(src[0])>t_atoi(src[1]) ? 2 : 3]); } } MAKEFUNC(Upper) { if (n_src>=1) { T_CHAR * s=src[0]; while(*s) { out.AddChar(toupper(*(s++))); } } } MAKEFUNC(Lower) { if (n_src>=1) { T_CHAR * s=src[0]; while(*s) { out.AddChar(tolower(*(s++))); } } } MAKEFUNC(Pad) { if (n_src>=2) { T_CHAR *fill=_TX(" "); if (n_src>=3 && src[2][0]) fill=src[2]; int num=t_atoi(src[1]); T_CHAR *p=src[0]; while(*p) {out.AddChar(*(p++));num--;} UINT fl=t_strlen(fill); while(num>0) {out.AddChar(fill[(--num)%fl]);} } } MAKEFUNC(Cut) { if (n_src>=2) { UINT num=t_atoi(src[1]); T_CHAR *p=src[0]; while(*p && num) {out.AddChar(*(p++));num--;} } } MAKEFUNC(PadCut) { if (n_src>=2) { T_CHAR *fill=_TX(" "); if (n_src>=3 && src[2][0]) fill=src[3]; int num=t_atoi(src[1]); T_CHAR *p=src[0]; while(*p && num>0) {out.AddChar(*(p++));num--;} UINT fl=t_strlen(fill); while(num>0) {out.AddChar(fill[(--num)%fl]);} } } MAKEFUNC(Abbr) {//abbr(string,len) if (n_src==0 || (n_src>=2 && (int)t_strlen(src[0])m) {m=l;ptr=src[n];} } if (ptr) out.AddString(ptr); } MAKEFUNC(Shortest) { T_CHAR * ptr=0; UINT n,m=(UINT)(-1); for(n=0;n=3) { n2=t_atoi(src[2]); } else n2=n1; if (n1<1) n1=1; if (n2>=n1) { n1--; n2--; while(n1<=n2 && src[0][n1]) { out.AddChar(src[0][n1++]); } } } MAKEFUNC(Len) { if (n_src>=1) out.AddInt(t_strlen(src[0])); } MAKEFUNC(FileName) { if (n_src<1) return; T_CHAR * p=src[0]; T_CHAR * p1=0; while(*p) { if (*p=='\\' || *p=='/') p1=p; p++; } if (p1) p1++; else p1=p; while(*p1 && *p1!='.') out.AddChar(*(p1++)); } MAKEFUNC(Add) { UINT n; int s=0; for(n=0;n=1) { UINT n; int s=t_atoi(src[0]); for(n=1;n=1) { UINT n; int s=t_atoi(src[0]); for(n=1;n=1) { UINT n; int s=t_atoi(src[0]); for(n=1;nm) m=t; } out.AddInt(m); } MAKEFUNC(Min) { if (!n_src) return; int m=t_atoi(src[0]); UINT n; for(n=1;ns2 || (*p!=',' && *p!=')')) {Error(_TX("internal error"));return;} T_CHAR bk=*p; *p=0; temp[nt]=_FMT(p1,&temp_f[nt]); nt++; *p=bk;; p1=p+1; p++; } *s1=0; UINT n; TEXTFUNC fn=0; for(n=0;nvars; found=0; org_spec=0; f=base->f; ff=base->ff; fp=base->fp; spec=_spec; } public: FMT(T_CHAR * p_spec,TAGFUNC _f,TAGFREEFUNC _ff,void * _fp,VarList * _vars) { vars=_vars; found=0; org_spec=spec=t_strdup(p_spec); f=_f; ff=_ff; fp=_fp; } operator T_CHAR*() { run(); return str.GetBuf(); } ~FMT() { if (org_spec) free(org_spec); } }; extern "C" { UINT tagz_format(T_CHAR * spec,TAGFUNC f,TAGFREEFUNC ff,void *fp,T_CHAR* out,UINT max) { T_CHAR * zz=tagz_format_r(spec,f,ff,fp); UINT r=0; while(r, eg. \"%artist%\"\n" "* $abbr(x) - inserts abbreviation of x, eg. \"$abbr(%album%)\" - will convert album name of \"Final Fantasy VI\" to \"FFVI\"\n" "* $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" "* $lower(x), $upper(x) - converts x to in lower/uppercase, eg. \"$upper(%title%)\"\n" "* $num(x,y) - displays x number and pads with zeros up to y characters (useful for track numbers), eg. $num(%tracknumber%,2)\n" "* $caps(x) - converts first letter in every word of x to uppercase, and all other letters to lowercase, eg. \"blah BLAH\" -> \"Blah Blah\"\n" "* $caps2(x) - similar to $caps, but leaves uppercase letters as they are, eg. \"blah BLAH\" -> \"Blah BLAH\"\n" "* $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" "* $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" "* $pad(x,y) - pads x with spaces up to y characters\n" "* $cut(x,y) - truncates x to y characters\n" "* $padcut(x,y) - pads x to y characters and truncates to y if longer\n" "* [ .... ] - 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" "* \' (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" "\n" "eg. \"[%artist% - ][$abbr(%album%,10)[ %tracknumber%] / ]%title%[ %streamtitle%]\"\n"; }