#include "database.hpp" #include "basesubs.hpp" #include "arrindex.hpp" #include "dbsubst.hpp" #if 0 NumSubstitution::NumSubstitution(int num) { AddMacro(new ScriptMacroConst("n", ScriptNumber(num))); } #endif void add_index_macros(ScriptMacroprocessor &smp, int idx) { #if 0 if(idx == -1) { smp.AddMacro(new ScriptMacroConst("idx", "")); smp.AddMacro(new ScriptMacroConst("_idx", "")); smp.AddMacro(new ScriptMacroConst("idx0", "")); return; } #endif ScriptNumber idxs(idx); ScriptVariable empty(""); smp.AddMacro(new ScriptMacroConst("idx", idx ? idxs : empty)); smp.AddMacro(new ScriptMacroConst("_idx", idx ? ScriptVariable("_")+idxs : empty)); smp.AddMacro(new ScriptMacroConst("idx0", idxs)); } #if 0 IdSubstitution::IdSubstitution(const char *id, int idx) { AddMacro(new ScriptMacroConst("id", id)); if(idx != -1) { ScriptNumber idxs(idx); ScriptVariable empty(""); AddMacro(new ScriptMacroConst("idx", idx ? idxs : empty)); AddMacro(new ScriptMacroConst("_idx", idx ? ScriptVariable("_")+idxs : empty)); AddMacro(new ScriptMacroConst("idx0", idxs)); } } #endif class VarListData : public ScriptMacroprocessorMacro { const ListData *the_data; public: VarListData() : ScriptMacroprocessorMacro("ls", true), the_data(0) {} void SetData(const ListData *d) { the_data = d; } ScriptVariable Expand(const ScriptVector ¶ms) const; }; ScriptVariable VarListData::Expand(const ScriptVector ¶ms) const { if(!the_data || params.Length() != 1) return ScriptVariableInv(); ScriptVariable s = params[0]; s.Trim(); if(s == "id") return the_data->id; if(s == "srctype") return the_data->srctype == listsrc_ini ? "ini" : "set"; if(s == "name" || s == "srcname") return the_data->srcname; if(s == "tag") return the_data->tag; if(s == "first") return the_data->items[0]; if(s == "last") return the_data->items[the_data->items.Length()-1]; return ScriptVariable("[ls:") + s + "?!]"; } class VarListItemData : public ScriptMacroprocessorMacro { const Database *the_master; const ListItemData *the_data; public: VarListItemData(const Database *m) : ScriptMacroprocessorMacro("li"), the_master(m), the_data(0) {} void SetData(const ListItemData *d); ScriptVariable Expand(const ScriptVector ¶ms) const; private: ScriptVariable DoExpand(const ScriptVector ¶ms) const; ScriptVariableInv list_id; int items_per_page; ScriptVariableInv set_id, tag; void SyncListNavInfo(const ScriptVariable &lsname); ScriptVariableInv cached_item_id; int index; ScriptVariableInv prev_id, next_id; void SyncItemNavInfo(); int DoGetWorkIndex(ScriptVariable lsname); ScriptVariable FindListArrayNum(const ScriptVariable &lsname) const; ScriptVariable GetListIndex(const ScriptVariable &lsname) const; bool DoGetPrevNext(ScriptVariable lsname); ScriptVariable GetPrevNext(const ScriptVariable &lsname, int pn) const; ScriptVariable FindAuxParam(ScriptVariable fnm) const; }; void VarListItemData::SetData(const ListItemData *d) { the_data = d; } void VarListItemData::SyncListNavInfo(const ScriptVariable &lsname) { if(list_id.IsValid() && lsname == list_id) return; the_master->GetListNavigationInfo(lsname, items_per_page, set_id, tag); } void VarListItemData::SyncItemNavInfo() { if(!the_data) { index = -1; prev_id.Invalidate(); next_id.Invalidate(); return; } if(cached_item_id.IsValid() && cached_item_id == the_data->item_id) return; bool ok = the_master->GetSetListIndexInfo(set_id, tag, the_data->item_id, index, prev_id, next_id); if(!ok) { index = -1; prev_id.Invalidate(); next_id.Invalidate(); } cached_item_id = the_data->item_id; } int VarListItemData::DoGetWorkIndex(ScriptVariable lsname) { if(lsname.IsValid()) lsname.Trim(); SyncListNavInfo(lsname); if(lsname.IsInvalid() || lsname == "" || tag.IsInvalid() || tag == "") { // perhaps ini-sourced list return the_data->index; } else { SyncItemNavInfo(); return index; } } ScriptVariable VarListItemData::FindListArrayNum(const ScriptVariable &lsname) const { int work_idx = const_cast(this)->DoGetWorkIndex(lsname); if(items_per_page <= 0 || work_idx < 0) return ""; int r = work_idx == 0 ? 1 : ((work_idx-1) / items_per_page + 1); return ScriptNumber(r); } ScriptVariable VarListItemData::GetListIndex(const ScriptVariable &lsname) const { int work_idx = const_cast(this)->DoGetWorkIndex(lsname); if(work_idx < 0) return ""; return ScriptNumber(work_idx); } bool VarListItemData::DoGetPrevNext(ScriptVariable lsname) { if(lsname.IsValid()) lsname.Trim(); SyncListNavInfo(lsname); if(lsname.IsInvalid() || lsname == "" || tag.IsInvalid() || tag == "") return false; SyncItemNavInfo(); return true; } ScriptVariable VarListItemData::GetPrevNext(const ScriptVariable &lsname, int pn) const { bool from_set = const_cast(this)->DoGetPrevNext(lsname); if(from_set) return pn == 0 ? prev_id : next_id; return pn == 0 ? the_data->prev_id : the_data->next_id; } ScriptVariable VarListItemData::Expand(const ScriptVector ¶ms) const { ScriptVariable res = DoExpand(params); if(res.IsInvalid()) res = ""; return res; } #if 0 static ScriptVariable number_or_empty_for_negative(int x) { return x < 0 ? ScriptVariable("") : ScriptNumber(x); } static ScriptVariable find_listarraynum(const ListItemData *itd, const Database *db, const ScriptVariable &lsname) { int itemsperpage; ScriptVariable set_id, tag; db->GetListNavigationInfo(lsname, itemsperpage, set_id, tag); if(itemsperpage <= 0) return ""; if(tag.IsInvalid() || tag == "") { // perhaps ini-sourced list if(itd->index < 0) return ""; int r = (itd->index-1) / itemsperpage + 1; return ScriptNumber(r); } int index = db->GetSetListIndex(set_id, tag, itd->item_id); if(index < 0) return ""; int r = (index-1) / itemsperpage + 1; return ScriptNumber(r); } static ScriptVariable find_listidx(const ListItemData *itd, const Database *db, const ScriptVariable &lsname) { if(lsname.IsInvalid() || lsname == "") return number_or_empty_for_negative(itd->index); int ignored; ScriptVariable set_id, tag; db->GetListNavigationInfo(lsname, ignored, set_id, tag); if(tag.IsInvalid() || tag == "") return number_or_empty_for_negative(itd->index); int index = db->GetSetListIndex(set_id, tag, itd->item_id); return number_or_empty_for_negative(index); } #endif static bool string_true(const ScriptVariable &s) { return s.IsValid() && s.Length() > 0; } ScriptVariable VarListItemData::FindAuxParam(ScriptVariable fnm) const { fnm.Trim(); int i; for(i = 0; i < the_data->aux_params.Length()-1; i += 2) if(fnm == the_data->aux_params[i]) return the_data->aux_params[i+1]; return ScriptVariableInv(); } ScriptVariable VarListItemData::DoExpand(const ScriptVector ¶ms) const { #if 0 if(!the_data) return "==NO_LISTITEMDATA=="; if(params.Length() < 1) return "==INVALID_LI_CALL=="; #endif if(!the_data || params.Length() < 1) return ScriptVariableInv(); ScriptVariable s = params[0]; s.Trim(); if(s == "id") return the_data->item_id; if(s == "prev") return GetPrevNext(params[1], 0); if(s == "next") return GetPrevNext(params[1], 1); if(s == "date") return the_data->date; if(s == "title") return the_data->title; if(s == "descr") return the_data->descr; if(s == "text") return the_data->text; if(s == "tags") return the_data->tags.Join(", "); if(s == "unixtime") return the_data->unixtime > 0 ? ScriptNumber(the_data->unixtime) : ScriptVariable(""); if(s == "iflong") return string_true(the_data->text) ? params[1] : params[2]; if(s == "ifprev") return string_true(GetPrevNext(params[1], 0)) ? params[2] : params[3]; if(s == "ifnext") return string_true(GetPrevNext(params[1], 1)) ? params[2] : params[3]; if(s == "iffile") { params[1].Trim(); int i; for(i = 0; i < the_data->files.Length(); i++) if(params[1] == the_data->files[i]) return params[2]; return params[3]; } if(s == "ifmore") return the_data->descr.Length() < the_data->text.Length() ? params[1] : params[2]; if(s == "ifcomenabled") return the_data->comments == "enabled" ? params[1] : params[2]; if(s == "iflistarraynum") return FindListArrayNum(params[1]) != "" ? params[2] : params[3]; if(s == "listarraynum") return FindListArrayNum(params[1]); if(s == "iflistidx") return GetListIndex(params[1]) != "" ? params[2] : params[3]; if(s == "listidx") return GetListIndex(params[1]); if(s == "hf") return FindAuxParam(params[1]); ScriptVariable apr = FindAuxParam(s); /* XXX LEGACY! remove? */ if(apr.IsValid()) return apr; return ScriptVariable("[li:") + s + "?!]"; } #if 0 // this function should disappear one day, being replaced by // data string generation done from within The Database // tunable, e.g. for particular names of months, days etc. static ScriptVariable make_date(long long date) { time_t tt = date; struct tm *t = gmtime(&tt); return ScriptVariable(32, "%04d/%02d/%02d %02d:%02d", t->tm_year+1900, t->tm_mon+1, t->tm_mday+1, t->tm_hour, t->tm_min); } #endif class VarCommentData : public ScriptMacroprocessorMacro { const CommentData *the_data; public: VarCommentData() : ScriptMacroprocessorMacro("cmt"), the_data(0) {} void SetData(const CommentData *d) { the_data = d; } ScriptVariable Expand(const ScriptVector ¶ms) const; }; ScriptVariable VarCommentData::Expand(const ScriptVector ¶ms) const { if(!the_data || params.Length() < 1) return ScriptVariableInv(); ScriptVariable s = params[0]; s.Trim(); if(s == "id") return ScriptNumber(the_data->id); if(s == "parent") return ScriptNumber(the_data->parent); if(s == "pgofparent") return the_data->page_of_parent_uri; if(s == "user") return the_data->user; if(s == "unixtime") return the_data->unixtime > 0 ? ScriptNumber(the_data->unixtime) : ScriptVariable(""); if(s == "date") return the_data->date; if(s == "from") return the_data->from; if(s == "title") return the_data->title; if(s == "text") return the_data->text; if(s == "threadpg") return the_data->thread_page_uri; if(s == "replies") return the_data->replies == -1 ? ScriptVariable("") : ScriptNumber(the_data->replies); if(s == "ifroot") return the_data->parent == 0 ? params[1] : params[2]; if(s == "ifparent" || s == "ifhasparent") return the_data->parent == 0 ? params[2] : params[1]; if(s == "ifflag") return the_data->HasFlag(params[1]) ? params[2] : params[3]; if(s == "aux") { if(!the_data->the_tree_aux_params || params[1].IsInvalid()) return ""; int i; for(i = 0; i < the_data->the_tree_aux_params->Length()-1; i+=2) if((*the_data->the_tree_aux_params)[i] == params[1]) return (*the_data->the_tree_aux_params)[i+1]; return ""; } if(s == "hf") { ScriptVariable nm = params[1]; nm.Trim(); int len = the_data->aux_params.Length(); int i; for(i = 1; i < len; i+=2) if(nm == the_data->aux_params[i-1]) return the_data->aux_params[i]; return ""; } return ScriptVariable("%[cmt:") + s + ":?!]"; } class VarHtmlSection : public ScriptMacroprocessorMacro { const DatabaseSubstitution *the_master; const Database *the_database; public: VarHtmlSection(const DatabaseSubstitution *m, const Database *db) : ScriptMacroprocessorMacro("html"), the_master(m), the_database(db) {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; ScriptVariable VarHtmlSection::Expand(const ScriptVector ¶ms) const { int len = params.Length(); if(len < 1) return ScriptVariableInv(); ScriptVariable name = params[0]; name.Trim(); ScriptVariable sn = the_database->GetHtmlSnippet(name); return the_master->Process(sn, params, 1, len-1); } class VarOptions : public ScriptMacroprocessorMacro { const Database *the_database; public: VarOptions(const Database *db) : ScriptMacroprocessorMacro("opt"), the_database(db) {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; ScriptVariable VarOptions::Expand(const ScriptVector ¶ms) const { int len = params.Length(); if(len < 2) return ScriptVariableInv(); ScriptVariable sect = params[0]; ScriptVariable name = params[1]; sect.Trim(); name.Trim(); return the_database->GetOption(sect, name); } ////////////////////////////////////////////////////////////////////////// class ArrayIndex : public ScriptMacroprocessorMacro { const DatabaseSubstitution *the_macroproc; const Database *the_database; IndexBarStyle style; ScriptVariable style_name; public: ArrayIndex(const DatabaseSubstitution *sub, const Database *db) : ScriptMacroprocessorMacro("indexbar"), the_macroproc(sub), the_database(db), style_name(ScriptVariableInv()) {} ScriptVariable Expand(const ScriptVector ¶ms) const; private: bool GetStyle(const ScriptVariable &nm); }; ScriptVariable ArrayIndex::Expand(const ScriptVector ¶ms) const { // params[0] == style_name params[1] == anchor (optional) int len = params.Length(); if(len < 1) return ScriptVariableInv(); ScriptVariable stname = params[0]; stname.Trim(); ScriptVariableInv anchor; if(len >= 2 && params[1].IsValid()) { anchor = params[1]; anchor.Trim(); } bool ok = const_cast(this)->GetStyle(stname); if(!ok) return ScriptVariableInv(); ArrayData *ad = the_database->CurrentArrayData(); if(!ad) return ""; return build_array_index(style, *ad, *the_macroproc, anchor); } bool ArrayIndex::GetStyle(const ScriptVariable &nm) { if(style_name.IsValid() && style_name == nm) return true; style_name = nm; bool ok = the_database->GetIndexBarStyle(style_name, style); if(!ok) style_name = ScriptVariableInv(); return ok; } ////////////////////////////////////////////////////////////////////////// class ListInfo : public ScriptMacroprocessorMacro { const Database *the_database; ScriptVariable loaded_id; ListData *lsd; public: ListInfo(const Database *db) : ScriptMacroprocessorMacro("listinfo"), the_database(db), lsd(0) {} ~ListInfo() { if(lsd) delete lsd; } ScriptVariable Expand(const ScriptVector ¶ms) const; private: void GetData(const ScriptVariable &nls); }; ScriptVariable ListInfo::Expand(const ScriptVector ¶ms) const { // params[0] == function params[1] == list id if(params.Length() < 2) return ScriptVariableInv(); ScriptVariable s = params[0]; s.Trim(); ScriptVariable lsn = params[1]; lsn.Trim(); if(loaded_id.IsInvalid() || loaded_id != lsn) const_cast(this)->GetData(lsn); if(!lsd) // no such list? return ScriptVariableInv(); if(s == "first") return lsd->items[0]; if(s == "last") return lsd->items[lsd->items.Length()-1]; return ScriptVariable("[listinfo:") + s + "?!]"; } void ListInfo::GetData(const ScriptVariable &nls) { if(lsd) { delete lsd; lsd = 0; } lsd = new ListData; bool ok = the_database->GetListData(nls, *lsd); if(!ok) { delete lsd; lsd = 0; } } ////////////////////////////////////////////////////////////////////////// class Menu : public ScriptMacroprocessorMacro { const Database *the_database; public: Menu(const Database *db) : ScriptMacroprocessorMacro("menu"), the_database(db) {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; ScriptVariable Menu::Expand(const ScriptVector ¶ms) const { int len = params.Length(); if(len < 1) return ScriptVariableInv(); ScriptVariable p0 = params[0]; ScriptVariable p1 = params[1]; return the_database->BuildMenu(p0.Trim(), p1.Trim()); } class Blocks : public ScriptMacroprocessorMacro { const Database *the_database; public: Blocks(const Database *db) : ScriptMacroprocessorMacro("blocks"), the_database(db) {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; ScriptVariable Blocks::Expand(const ScriptVector ¶ms) const { int len = params.Length(); if(len < 1) return ScriptVariableInv(); ScriptVariable p0 = params[0]; ScriptVariable p1 = params[1]; return the_database->BuildBlocks(p0.Trim(), p1.Trim()); } class Embedlist : public ScriptMacroprocessorMacro { const Database *the_database; public: Embedlist(const Database *db) : ScriptMacroprocessorMacro("embedlist"), the_database(db) {} ScriptVariable Expand(const ScriptVector ¶ms) const; }; ScriptVariable Embedlist::Expand(const ScriptVector ¶ms) const { int len = params.Length(); if(len < 1) return ScriptVariableInv(); ScriptVariable id = params[0]; id.Trim(); if(!the_database->IsListEmbedded(id)) return ScriptVariable("LIST ") + id + " CAN'T BE EMBEDDED"; ListData listdata; if(!the_database->GetListData(id, listdata)) return ScriptVariable("CONFIGURATION ERROR FOR LIST ") + id; Database::MacroRealm save; the_database->SaveMacroRealm(save); the_database->SetMacroData(&listdata, 0); ScriptVariable result; result += the_database->BuildListHead(listdata); int count = listdata.items.Length(); bool rev = listdata.reverse; int i; for(i = rev ? count-1 : 0; rev?(i >= 0):(i < count); rev?i--:i++) { ListItemData itemdata; bool ok; if(listdata.srctype == listsrc_ini) { ok = the_database->GetListItemData(listdata, i, itemdata); } else { ok = the_database->GetSetItemDataById(listdata.srcname, listdata.items[i], &itemdata); } if(!ok) { result += ScriptVariable("NO DATA FOR ") + listdata.id + "/" + listdata.items[i] + " [ " + itemdata.title + " ]"; continue; } if(itemdata.HasFlag("hidden")) continue; #if 0 // XXXXXXXXXXXXXXXXXXXXXX printf("*** XXX %s %d %s %s\n", id.c_str(), i, listdata.items[i].c_str(), itemdata.title.c_str()); #endif the_database->SetMacroData(&listdata, &itemdata); result += the_database->BuildListItem(listdata, itemdata); #if 0 printf("%d\n", result.Length()); #endif the_database->SetMacroData(&listdata, 0); } result += the_database->BuildListTail(listdata); the_database->RestoreMacroRealm(save); return result; } class BaseUri : public ScriptMacroprocessorMacro { const Database *the_database; public: BaseUri(const Database *db) : ScriptMacroprocessorMacro("base_uri"), the_database(db) {} ScriptVariable Expand(const ScriptVector &ignored) const { return the_database->GetBaseUri(); } }; class BaseUrl : public ScriptMacroprocessorMacro { const Database *the_database; public: BaseUrl(const Database *db) : ScriptMacroprocessorMacro("base_url"), the_database(db) {} ScriptVariable Expand(const ScriptVector &ignored) const { return the_database->GetBaseUrl(); } }; DatabaseSubstitution::DatabaseSubstitution(const Database *master) : BaseSubstitutions(""), vld(new VarListData), vlid(new VarListItemData(master)), vcmd(new VarCommentData), the_database(master) { // please note that ownersip over the three 'var' objects is // transferred here to the base class object, and IT will delete them AddMacro(vlid); // [li: ] AddMacro(vld); // [ls: ] AddMacro(vcmd); // [cmt: ] AddMacro(new VarHtmlSection(this, master)); // [html: ] AddMacro(new VarOptions(master)); // [opt: ] AddMacro(new Menu(master)); // [menu: ] AddMacro(new Blocks(master)); // [blocks: ] AddMacro(new ArrayIndex(this, master)); // [indexbar: ] AddMacro(new ListInfo(master)); // [listinfo: ] AddMacro(new Embedlist(master)); // [embedlist: ] AddMacro(new BaseUri(master)); // [base_uri] AddMacro(new BaseUrl(master)); // [base_url] } DatabaseSubstitution::~DatabaseSubstitution() { #if 0 delete vhs; delete vlid; delete vld; #endif // no deletion here! they will be deleted by the base class destructor, // because these objects belong to it! } void DatabaseSubstitution::SetData(const ListData *ld, const ListItemData *lid) { vld->SetData(ld); vlid->SetData(lid); } void DatabaseSubstitution::SetCmtData(const CommentData *cmd) { vcmd->SetData(cmd); }