#include #include #include #include "_version.h" #include "imgsize.h" #include "urlenc.hpp" #include "basesubs.hpp" /////////////////////////////////////////////////////////// // class headers // class IfForm : public ScriptMacroprocessorMacro { public: IfForm() : ScriptMacroprocessorMacro("if") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class OrForm : public ScriptMacroprocessorMacro { public: OrForm() : ScriptMacroprocessorMacro("or") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class IfEqForm : public ScriptMacroprocessorMacro { public: IfEqForm() : ScriptMacroprocessorMacro("ifeq") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class IfBelongs : public ScriptMacroprocessorMacro { public: IfBelongs() : ScriptMacroprocessorMacro("ifbelongs") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class IfAAB : public ScriptMacroprocessorMacro { public: IfAAB() : ScriptMacroprocessorMacro("ifaab") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class SwitchForm : public ScriptMacroprocessorMacro { public: SwitchForm() : ScriptMacroprocessorMacro("switch") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class ForEach : public ScriptMacroprocessorMacro { ScriptMacroprocessor *the_master; public: ForEach(ScriptMacroprocessor *m) : ScriptMacroprocessorMacro("foreach"), the_master(m) {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class ImgFileDimensions : public ScriptMacroprocessorMacro { ScriptVariable basepath; public: ImgFileDimensions(const ScriptVariable &bp) : ScriptMacroprocessorMacro("imgdim"), basepath(bp) {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class IfFile : public ScriptMacroprocessorMacro { ScriptVariable basepath; public: IfFile(const ScriptVariable &bp) : ScriptMacroprocessorMacro("iffile"), basepath(bp) {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class FileSize : public ScriptMacroprocessorMacro { ScriptVariable basepath; public: FileSize(const ScriptVariable &bp) : ScriptMacroprocessorMacro("filesize"), basepath(bp) {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class ReadFile : public ScriptMacroprocessorMacro { ScriptVariable basepath; public: ReadFile(const ScriptVariable &bp) : ScriptMacroprocessorMacro("readfile"), basepath(bp) {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class ListDir : public ScriptMacroprocessorMacro { ScriptVariable basepath; public: ListDir(const ScriptVariable &bp) : ScriptMacroprocessorMacro("dir"), basepath(bp) {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class AttributeQuote : public ScriptMacroprocessorMacro { public: AttributeQuote() : ScriptMacroprocessorMacro("q") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class RemoveLF : public ScriptMacroprocessorMacro { public: RemoveLF() : ScriptMacroprocessorMacro("rmlf") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class CollapseWS : public ScriptMacroprocessorMacro { public: CollapseWS() : ScriptMacroprocessorMacro("collapsews") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class Trim : public ScriptMacroprocessorMacro { public: Trim() : ScriptMacroprocessorMacro("trim") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class SubstitutionURLEnc : public ScriptMacroprocessorMacro { public: SubstitutionURLEnc() : ScriptMacroprocessorMacro("urlenc") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class SubstitutionLtGt : public ScriptMacroprocessorMacro { public: SubstitutionLtGt() : ScriptMacroprocessorMacro("ltgt") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class Lhead : public ScriptMacroprocessorMacro { public: Lhead() : ScriptMacroprocessorMacro("lhead") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class Ltail : public ScriptMacroprocessorMacro { public: Ltail() : ScriptMacroprocessorMacro("ltail") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class Lindex : public ScriptMacroprocessorMacro { public: Lindex() : ScriptMacroprocessorMacro("lindex") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class Lsort : public ScriptMacroprocessorMacro { public: Lsort() : ScriptMacroprocessorMacro("lsort") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class MacroRFC2822Date : public ScriptMacroprocessorMacro { public: MacroRFC2822Date() : ScriptMacroprocessorMacro("rfcdate") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class NowTime : public ScriptMacroprocessorMacro { public: NowTime() : ScriptMacroprocessorMacro("now") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; class ThalassaVersion : public ScriptMacroprocessorMacro { public: ThalassaVersion() : ScriptMacroprocessorMacro("thalassa_version") {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; /////////////////////////////////////////////////////////////// // implementations ScriptVariable IfForm::Expand(const ScriptVector ¶ms) const { // %[if:cond:then:else] ScriptVariable p0 = params[0]; bool b = p0.IsValid() && p0.Trim() != ""; ScriptVariable res = b ? params[1] : params[2]; if(res.IsInvalid()) res = ""; return res; } ScriptVariable OrForm::Expand(const ScriptVector ¶ms) const { // %[or:alt1:alt2:...] int pc = params.Length(); int i; for(i = 0; i < pc; i++) { ScriptVariable par = params[i]; if(par.IsValid() && par.Trim().Length() > 0) return params[i]; } return ""; } ScriptVariable IfEqForm::Expand(const ScriptVector ¶ms) const { // %[ifeq:a:b:then:else] ScriptVariable p0 = params[0]; ScriptVariable p1 = params[1]; p0.Trim(); p1.Trim(); ScriptVariable res = (p0 == p1) ? params[2] : params[3]; if(res.IsInvalid()) res = ""; return res; } ScriptVariable IfBelongs::Expand(const ScriptVector ¶ms) const { // %[ifbelongs:x:a b x c d:then:else] if(params.Length() < 3) return ScriptVariableInv(); ScriptVariable p0 = params[0]; p0.Trim(); ScriptWordVector v(params[1]); bool found = false; int i; for(i = 0; i < v.Length(); i++) if(v[i] == p0) { found = true; break; } ScriptVariable res = found ? params[2] : params[3]; if(res.IsInvalid()) res = ""; return res; } ScriptVariable IfAAB::Expand(const ScriptVector ¶ms) const { // %[ifaab:a:b] if(params.Length() < 2) return ScriptVariableInv(); ScriptVariable a = params[0]; a.Trim(); if(a != "") { ScriptVariable b = params[1]; b.Trim(); a += b; } return a; } ScriptVariable IfCond::Expand(const ScriptVector ¶ms) const { // %[ifcond:then:else] ScriptVariable res = cond ? params[0] : params[1]; if(res.IsInvalid()) res = ""; return res; } ScriptVariable ForEach::Expand(const ScriptVector ¶ms) const { // %[foreach:list:func:param1:..:paramN] int paramlen = params.Length(); if(paramlen < 2) return ScriptVariableInv(); int i; ScriptVector args; for(i = 2; i < paramlen; i++) { ScriptVariable p = params[i]; p.Trim(); args.AddItem(p); } ScriptVariable p1 = params[1]; p1.Trim(); int the_idx = args.Length(); ScriptVariable res; ScriptWordVector lst(params[0]); int len = lst.Length(); for(i = 0; i < len; i++) { args[the_idx] = lst[i]; ScriptVariable m = the_master->Apply(p1, args); if(m.IsValid()) res += m; } return res; } ScriptVariable SwitchForm::Expand(const ScriptVector ¶ms) const { // %[switch:expr:key1:val1:key2:val2:...] int paramlen = params.Length(); if(paramlen < 2) return ScriptVariableInv(); ScriptVariable expr = params[0]; expr.Trim(); int i; for(i = 1; i < paramlen-1; i += 2) { ScriptVariable v = params[i]; v.Trim(); if(v == expr) return params[i+1]; } return ""; } ScriptVariable IfFile::Expand(const ScriptVector ¶ms) const { if(params.Length() < 2) return ScriptVariableInv(); ScriptVariable p0 = params[0]; p0.Trim(); ScriptVariable fname; if(p0[0] == '/') { fname = p0; } else { if(basepath.Length() > 0) fname = basepath + "/" + p0; else fname = p0; } // %[iffile:filename:then:else] FileStat fs(fname.c_str()); ScriptVariable res = fs.Exists() ? params[1] : params[2]; if(res.IsInvalid()) res = ""; return res; } ScriptVariable FileSize::Expand(const ScriptVector ¶ms) const { if(params.Length() < 1) return ScriptVariableInv(); ScriptVariable p0 = params[0]; p0.Trim(); ScriptVariable fname; if(p0[0] == '/') { fname = p0; } else { if(basepath.Length() > 0) fname = basepath + "/" + p0; else fname = p0; } FileStat fs(fname.c_str()); if(!fs.Exists() || !fs.IsRegularFile()) return ""; return ScriptNumber(fs.GetSize()); } ScriptVariable ReadFile::Expand(const ScriptVector ¶ms) const { if(params.Length() < 1) return ScriptVariableInv(); ScriptVariable p0 = params[0]; p0.Trim(); if(p0 == "") return ""; ScriptVariable fname; if(p0[0] == '/') { fname = p0; } else { if(basepath.Length() > 0) fname = basepath + "/" + p0; else fname = p0; } ReadText rt(fname.c_str()); if(!rt.IsOpen()) return ""; ScriptVariable res; rt.ReadUntilEof(res); return res; } static bool any_whitespace(const char *s) { const char *p; for(p = s; *p; p++) if(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') return true; return false; } ScriptVariable ListDir::Expand(const ScriptVector ¶ms) const { if(params.Length() < 1) return ScriptVariableInv(); ScriptVariable p0 = params[0]; p0.Trim(); if(p0 == "") return ""; ScriptVariable dname; if(p0[0] == '/') { dname = p0; } else { if(basepath.Length() > 0) dname = basepath + "/" + p0; else dname = p0; } bool show_hidden = false, show_underscored = false; char must_start = 0; if(params.Length() >= 2) { ScriptVariable p1 = params[1]; if(p1.Strchr('H').IsValid()) { must_start = '.'; } else if(p1.Strchr('U').IsValid()) { must_start = '_'; } else { if(p1.Strchr('h').IsValid()) show_hidden = true; if(p1.Strchr('u').IsValid()) show_underscored = true; } } ScriptVariable res(""); ReadDir rd(dname.c_str()); const char *nm; while((nm = rd.Next())) { if(any_whitespace(nm)) continue; if(must_start && must_start != *nm) continue; if(*nm == '.' && !show_hidden) continue; if(*nm == '_' && !show_underscored) continue; if(res != "") res += " "; res += nm; } return res; } ScriptVariable ImgFileDimensions::Expand(const ScriptVector ¶ms) const { if(params.Length() < 1) return ScriptVariableInv(); ScriptVariable p0 = params[0]; p0.Trim(); ScriptVariable fname; if(p0[0] == '/') { fname = p0; } else { if(basepath.Length() > 0) fname = basepath + "/" + p0; else fname = p0; } int w, h, res; res = extract_image_dimensions(fname.c_str(), &w, &h); if(res) return ScriptVariable(0, "width=\"%d\" height=\"%d\"", w, h); else return ""; } ScriptVariable AttributeQuote::Expand(const ScriptVector ¶ms) const { bool has_sq = false, has_dq = false; const char *p; for(p = params[0].c_str(); *p; p++) { switch(*p) { case '\'': has_sq = true; if(has_dq) goto ready; case '\"': has_dq = true; if(has_sq) goto ready; } } ready: if(!has_dq) return ScriptVariable("\"") + params[0] + "\""; // from now on, we know for sure ``"'' is there, so we need to trick it if(!has_sq) return ScriptVariable("\'") + params[0] + "\'"; // the worst possible case -- they both are there; we leave the // surrounding dq's and replace every dq inside with ``"'' ScriptVariable res("\""); for(p = params[0].c_str(); *p; p++) if(*p == '\"') res += """; else res += *p; res += '\"'; return res; } ScriptVariable RemoveLF::Expand(const ScriptVector ¶ms) const { ScriptVariable res(""); const char *p; for(p = params[0].c_str(); *p; p++) if(*p != '\r' && *p != '\n') res += *p; return res; } ScriptVariable CollapseWS::Expand(const ScriptVector ¶ms) const { ScriptVariable res(""); const char *p; bool spc = true; for(p = params[0].c_str(); *p; p++) { if(*p == ' ' || *p == '\n' || *p == '\t' || *p == '\r') { if(!spc) res += ' '; spc = true; } else { res += *p; spc = false; } } return res; } ScriptVariable Trim::Expand(const ScriptVector ¶ms) const { ScriptVariable s = params[0]; s.Trim(); return s; } ScriptVariable SubstitutionURLEnc::Expand(const ScriptVector ¶ms) const { if(params.Length() != 1 || params[0].IsInvalid()) return ScriptVariableInv(); return url_encode(params[0]); } ScriptVariable SubstitutionLtGt::Expand(const ScriptVector ¶ms) const { if(params.Length() != 1) return ScriptVariableInv(); ScriptVariable res(""); const char *src = params[0].c_str(); for(; *src; src++) { switch(*src) { case '<': res += "<"; break; case '>': res += ">"; break; case '&': res += "&"; break; // symbols <<">> and <<'>> are considered special by // JS function htmlspecialchars(). // we don't understand the reason so we don't convert them here // we might add them here as well (" -> " ' -> ') // if we experience any problems with them one day default: res += *src; } } return res; } ScriptVariable Lhead::Expand(const ScriptVector ¶ms) const { // [] if(params.Length() < 1) return ScriptVariableInv(); ScriptVariable delim(""); if(params.Length() >= 2) { delim = params[1]; delim.Trim(); } ScriptVariable p0 = params[0]; ScriptVariable::Substring w; if(delim == "") { p0.Whole().FetchWord(w); } else { w = p0.Strstr(delim); if(w.IsValid()) w = w.Before(); else w = p0.Whole(); } return w.Get(); } ScriptVariable Ltail::Expand(const ScriptVector ¶ms) const { if(params.Length() < 1) return ScriptVariableInv(); ScriptVariable delim(""); if(params.Length() >= 2) { delim = params[1]; delim.Trim(); } ScriptVariable p0 = params[0]; ScriptVariable::Substring w; if(delim == "") { p0.Whole().FetchWord(w); w = w.After(); } else { w = p0.Strstr(delim); if(w.IsValid()) w = w.After(); else w = p0.Whole(); } return w.Get(); } static void extract_delimiters(const ScriptVector ¶ms, int delim_idx, ScriptVariable &delimarg, ScriptVariable &trimarg) { delimarg = " \t\r\n"; trimarg.Invalidate(); if(params.Length() < delim_idx) // delimiters not specified return; const char *p = params[delim_idx].c_str(); if(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') { // special cases ScriptVariable dp = params[delim_idx]; dp.Trim(); if(dp == "n") { delimarg = "\n"; trimarg = "\r"; return; } if(dp == "N") { delimarg = "\n"; trimarg = " \t\r"; return; } // this behaviour is "unspecified" in the documentation // we leave the string untokenized, hopefully it's the most unexpected delimarg = ""; trimarg = ""; return; } ScriptVariable delimchars(""); bool ws = false; for(; *p; p++) { if(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') ws = true; else delimchars += *p; } if(delimchars.Length() > 0) { // argument was not ``empty'' delimarg = delimchars; trimarg = ws ? " \t\r\n" : ""; } } ScriptVariable Lindex::Expand(const ScriptVector ¶ms) const { // [] if(params.Length() < 2) return ScriptVariableInv(); ScriptVariable delimarg, trimarg; extract_delimiters(params, 2, delimarg, trimarg); ScriptVector vec(params[0], delimarg.c_str(), trimarg.IsValid() ? trimarg.c_str() : 0); ScriptVariable res(""); char esc = 0; ScriptVariable p1 = params[1]; p1.Trim(); const char *src = p1.c_str(); if(*src < '0' || *src > '9') { esc = *src; src++; } bool escaped = false; for(; *src; src++) { if(esc) { if(esc == *src) { escaped = true; continue; } if(escaped) { if(*src >= '0' && *src <= '9') { int idx = *src - '0'; res += vec[idx]; escaped = false; continue; } // current char will be added later escaped = false; } } else { if(*src >= '0' && *src <= '9') { int idx = *src - '0'; res += vec[idx]; continue; } } res += *src; } return res; } ScriptVariable Lsort::Expand(const ScriptVector ¶ms) const { // [ [] ] if(params.Length() < 1) return ScriptVariableInv(); ScriptVariable delimarg, trimarg; extract_delimiters(params, 1, delimarg, trimarg); ScriptVector vec(params[0], delimarg.c_str(), trimarg.IsValid() ? trimarg.c_str() : 0); scriptpp_sort(vec); ScriptVariable glue(" "); if(params.Length() >= 3) glue = params[2]; return vec.Join(glue); } ScriptVariable MacroRFC2822Date::Expand(const ScriptVector ¶ms) const { static const char * const monthname[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; if(params.Length() != 1 || params[0].IsInvalid()) return ScriptVariableInv(); ScriptVariable v = params[0]; v.Trim(); long long ln; if(!v.GetLongLong(ln, 10)) return ScriptVariableInv(); time_t tt = ln; struct tm *t = gmtime(&tt); int mx = t->tm_mon; if(mx < 0 || mx >= 12) // must never happen unless gmtime is broken return ScriptVariableInv(); ScriptVariable res(64, "%d %s %04d %02d:%02d:%02d +0000", t->tm_mday, monthname[mx], t->tm_year + 1900, t->tm_hour, t->tm_min, t->tm_sec); return res; } ScriptVariable NowTime::Expand(const ScriptVector ¶ms) const { return ScriptNumber(time(0)); } ScriptVariable ThalassaVersion::Expand(const ScriptVector ¶ms) const { ScriptVariable s(""); if(params.Length() > 0) { s = params[0]; s.Trim(); } if(s == "") return THALASSA_VERSION; if(s == "full") return "Thalassa CMS v. " THALASSA_VERSION " (built " __DATE__ ")"; if(s == "id") return ScriptNumber(THALASSA_VERSIONID); if(s == "built") return __DATE__; return ""; } BaseSubstitutions::BaseSubstitutions(const char *bp) { AddMacro(new IfForm); // [if: ] AddMacro(new OrForm); // [or: ] AddMacro(new IfEqForm); // [ifeq: ] AddMacro(new IfBelongs); // [ifbelongs: ] AddMacro(new IfAAB); // [ifaab: ] AddMacro(new ForEach(this)); // [foreach: ] AddMacro(new SwitchForm); // [switch: ] AddMacro(new AttributeQuote); // [q: ] AddMacro(new RemoveLF); // [rmlf: ] AddMacro(new CollapseWS); // [collapsews: ] AddMacro(new Trim); // [trim: ] AddMacro(new SubstitutionLtGt); // [ltgt: ] AddMacro(new SubstitutionURLEnc); // [urlenc: ] AddMacro(new Lhead); // [lhead: ] AddMacro(new Ltail); // [ltail: ] AddMacro(new Lindex); // [lindex: ] AddMacro(new Lsort); // [lsort: ] AddMacro(new IfFile(bp)); // [iffile: ] AddMacro(new FileSize(bp)); // [filesize: ] AddMacro(new ReadFile(bp)); // [readfile: ] AddMacro(new ListDir(bp)); // [dir: ] AddMacro(new ImgFileDimensions(bp)); // [imgdim: ] AddMacro(new MacroRFC2822Date); // [rfcdate: ] AddMacro(new NowTime); // [now] AddMacro(new ThalassaVersion); // [thalassa_version: ] } BaseSubstitutions::~BaseSubstitutions() { }