]>
jfr.im git - irc/rizon/znc.git/blob - Template.cpp
2 * Copyright (C) 2004-2011 See the AUTHORS file for details.
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published
6 * by the Free Software Foundation.
10 #include "FileUtils.h"
15 using std::stringstream
;
17 void CTemplateOptions::Parse(const CString
& sLine
) {
18 CString sName
= sLine
.Token(0, false, "=").Trim_n().AsUpper();
19 CString sValue
= sLine
.Token(1, true, "=").Trim_n();
22 m_eEscapeTo
= CString::ToEscape(sValue
);
23 } else if (sName
== "ESCFROM") {
24 m_eEscapeFrom
= CString::ToEscape(sValue
);
28 CTemplate
* CTemplateLoopContext::GetRow(unsigned int uIndex
) {
29 unsigned int uSize
= m_pvRows
->size();
33 return (*m_pvRows
)[uSize
- uIndex
-1];
35 return (*m_pvRows
)[uIndex
];
42 CString
CTemplateLoopContext::GetValue(const CString
& sName
, bool bFromIf
) {
43 CTemplate
* pTemplate
= GetCurRow();
46 DEBUG("Loop [" + GetName() + "] has no row index [" + CString(GetRowIndex()) + "]");
50 if (sName
.Equals("__ID__")) {
51 return CString(GetRowIndex() +1);
52 } else if (sName
.Equals("__COUNT__")) {
53 return CString(GetRowCount());
54 } else if (sName
.Equals("__ODD__")) {
55 return ((GetRowIndex() %2
) ? "" : "1");
56 } else if (sName
.Equals("__EVEN__")) {
57 return ((GetRowIndex() %2
) ? "1" : "");
58 } else if (sName
.Equals("__FIRST__")) {
59 return ((GetRowIndex() == 0) ? "1" : "");
60 } else if (sName
.Equals("__LAST__")) {
61 return ((GetRowIndex() == m_pvRows
->size() -1) ? "1" : "");
62 } else if (sName
.Equals("__OUTER__")) {
63 return ((GetRowIndex() == 0 || GetRowIndex() == m_pvRows
->size() -1) ? "1" : "");
64 } else if (sName
.Equals("__INNER__")) {
65 return ((GetRowIndex() == 0 || GetRowIndex() == m_pvRows
->size() -1) ? "" : "1");
68 return pTemplate
->GetValue(sName
, bFromIf
);
71 CTemplate::~CTemplate() {
72 for (map
<CString
, vector
<CTemplate
*> >::iterator it
= m_mvLoops
.begin(); it
!= m_mvLoops
.end(); ++it
) {
73 vector
<CTemplate
*>& vLoop
= it
->second
;
74 for (unsigned int a
= 0; a
< vLoop
.size(); a
++) {
79 for (unsigned int a
= 0; a
< m_vLoopContexts
.size(); a
++) {
80 delete m_vLoopContexts
[a
];
84 void CTemplate::Init() {
85 /* We have no CConfig in znc land
86 CString sPath(CConfig::GetValue("WebFilesPath"));
97 CString
CTemplate::ExpandFile(const CString
& sFilename
, bool bFromInc
) {
98 /*if (sFilename.Left(1) == "/" || sFilename.Left(2) == "./") {
102 CString
sFile(ResolveLiteral(sFilename
).TrimLeft_n("/"));
104 for (list
<pair
<CString
, bool> >::iterator it
= m_lsbPaths
.begin(); it
!= m_lsbPaths
.end(); ++it
) {
105 CString
& sRoot
= it
->first
;
106 CString
sFilePath(CDir::ChangeDir(sRoot
, sFile
));
108 // Make sure path ends with a slash because "/foo/pub*" matches "/foo/public_keep_out/" but "/foo/pub/*" doesn't
109 if (!sRoot
.empty() && sRoot
.Right(1) != "/") {
113 if (it
->second
&& !bFromInc
) {
114 DEBUG("\t\tSkipping path (not from INC) [" + sFilePath
+ "]");
118 if (CFile::Exists(sFilePath
)) {
119 if (sRoot
.empty() || sFilePath
.Left(sRoot
.length()) == sRoot
) {
120 DEBUG(" Found [" + sFilePath
+ "]");
123 DEBUG("\t\tOutside of root [" + sFilePath
+ "] !~ [" + sRoot
+ "]");
128 switch (m_lsbPaths
.size()) {
130 DEBUG("Unable to find [" + sFile
+ "] using the current directory");
133 DEBUG("Unable to find [" + sFile
+ "] in the defined path [" + m_lsbPaths
.begin()->first
+ "]");
136 DEBUG("Unable to find [" + sFile
+ "] in any of the " + CString(m_lsbPaths
.size()) + " defined paths");
142 void CTemplate::SetPath(const CString
& sPaths
) {
144 sPaths
.Split(":", vsDirs
, false);
146 for (size_t a
= 0; a
< vsDirs
.size(); a
++) {
147 AppendPath(vsDirs
[a
], false);
151 CString
CTemplate::MakePath(const CString
& sPath
) const {
152 CString
sRet(CDir::ChangeDir("./", sPath
+ "/"));
154 if (!sRet
.empty() && sRet
.Right(1) != "/") {
161 void CTemplate::PrependPath(const CString
& sPath
, bool bIncludesOnly
) {
162 DEBUG("CTemplate::PrependPath(" + sPath
+ ") == [" + MakePath(sPath
) + "]");
163 m_lsbPaths
.push_front(make_pair(MakePath(sPath
), bIncludesOnly
));
166 void CTemplate::AppendPath(const CString
& sPath
, bool bIncludesOnly
) {
167 DEBUG("CTemplate::AppendPath(" + sPath
+ ") == [" + MakePath(sPath
) + "]");
168 m_lsbPaths
.push_back(make_pair(MakePath(sPath
), bIncludesOnly
));
171 void CTemplate::RemovePath(const CString
& sPath
) {
172 DEBUG("CTemplate::RemovePath(" + sPath
+ ") == [" + CDir::ChangeDir("./", sPath
+ "/") + "]");
174 for (list
<pair
<CString
, bool> >::iterator it
= m_lsbPaths
.begin(); it
!= m_lsbPaths
.end(); ++it
) {
175 if (it
->first
== sPath
) {
176 m_lsbPaths
.remove(*it
);
177 RemovePath(sPath
); // @todo probably shouldn't use recursion, being lazy
183 void CTemplate::ClearPaths() {
187 bool CTemplate::SetFile(const CString
& sFileName
) {
188 m_sFileName
= ExpandFile(sFileName
, false);
189 PrependPath(sFileName
+ "/..");
191 if (sFileName
.empty()) {
192 DEBUG("CTemplate::SetFile() - Filename is empty");
196 if (m_sFileName
.empty()) {
197 DEBUG("CTemplate::SetFile() - [" + sFileName
+ "] does not exist");
201 DEBUG("Set template file to [" + m_sFileName
+ "]");
209 CLoopSorter(const CString
& sType
) : m_sType(sType
) {}
210 bool operator()(CTemplate
* pTemplate1
, CTemplate
* pTemplate2
) {
211 return (pTemplate1
->GetValue(m_sType
, false) < pTemplate2
->GetValue(m_sType
, false));
215 CTemplate
& CTemplate::AddRow(const CString
& sName
) {
216 CTemplate
* pTmpl
= new CTemplate(m_spOptions
, this);
217 m_mvLoops
[sName
].push_back(pTmpl
);
222 CTemplate
* CTemplate::GetRow(const CString
& sName
, unsigned int uIndex
) {
223 vector
<CTemplate
*>* pvLoop
= GetLoop(sName
);
226 if (pvLoop
->size() > uIndex
) {
227 return (*pvLoop
)[uIndex
];
234 vector
<CTemplate
*>* CTemplate::GetLoop(const CString
& sName
) {
235 CTemplateLoopContext
* pContext
= GetCurLoopContext();
238 CTemplate
* pTemplate
= pContext
->GetCurRow();
241 return pTemplate
->GetLoop(sName
);
245 map
<CString
, vector
<CTemplate
*> >::iterator it
= m_mvLoops
.find(sName
);
247 if (it
!= m_mvLoops
.end()) {
248 return &(it
->second
);
254 bool CTemplate::PrintString(CString
& sRet
) {
256 stringstream sStream
;
257 bool bRet
= Print(sStream
);
259 sRet
= sStream
.str();
264 bool CTemplate::Print(ostream
& oOut
) {
265 return Print(m_sFileName
, oOut
);
268 bool CTemplate::Print(const CString
& sFileName
, ostream
& oOut
) {
269 if (sFileName
.empty()) {
270 DEBUG("Empty filename in CTemplate::Print()");
274 CFile
File(sFileName
);
277 DEBUG("Unable to open file [" + sFileName
+ "] in CTemplate::Print()");
282 CString sSetBlockVar
;
283 bool bValidLastIf
= false;
284 bool bInSetBlock
= false;
285 unsigned long uFilePos
= 0;
286 unsigned long uCurPos
= 0;
287 unsigned int uLineNum
= 0;
288 unsigned int uNestedIfs
= 0;
289 unsigned int uSkip
= 0;
290 bool bLoopCont
= false;
291 bool bLoopBreak
= false;
294 while (File
.ReadLine(sLine
)) {
296 bool bFoundATag
= false;
297 bool bTmplLoopHasData
= false;
299 CString::size_type iPos
= 0;
301 unsigned int uLineSize
= sLine
.size();
305 iPos
= sLine
.find("<?");
307 if (iPos
== CString::npos
) {
315 sOutput
+= sLine
.substr(0, iPos
);
318 sLine
= sLine
.substr(iPos
+2);
320 CString::size_type iPos2
= sLine
.find("?>");
322 // Make sure our tmpl tag is ended properly
323 if (iPos2
== CString::npos
) {
324 DEBUG("Template tag not ended properly in file [" + sFileName
+ "] [<?" + sLine
+ "]");
330 CString sMid
= CString(sLine
.substr(0, iPos2
)).Trim_n();
332 // Make sure we don't have a nested tag
333 if (sMid
.find("<?") == CString::npos
) {
334 sLine
= sLine
.substr(iPos2
+2);
335 CString sAction
= sMid
.Token(0);
336 CString sArgs
= sMid
.Token(1, true);
337 bool bNotFound
= false;
339 // If we're breaking or continuing from within a loop, skip all tags that aren't ENDLOOP
340 if ((bLoopCont
|| bLoopBreak
) && !sAction
.Equals("ENDLOOP")) {
345 if (sAction
.Equals("INC")) {
346 if (!Print(ExpandFile(sArgs
, true), oOut
)) {
347 DEBUG("Unable to print INC'd file [" + sArgs
+ "]");
350 } else if (sAction
.Equals("SETOPTION")) {
351 m_spOptions
->Parse(sArgs
);
352 } else if (sAction
.Equals("ADDROW")) {
353 CString sLoopName
= sArgs
.Token(0);
356 if (sArgs
.Token(1, true, " ").OptionSplit(msRow
)) {
357 CTemplate
& NewRow
= AddRow(sLoopName
);
359 for (MCString::iterator it
= msRow
.begin(); it
!= msRow
.end(); ++it
) {
360 NewRow
[it
->first
] = it
->second
;
363 } else if (sAction
.Equals("SET")) {
364 CString sName
= sArgs
.Token(0);
365 CString sValue
= sArgs
.Token(1, true);
367 (*this)[sName
] = sValue
;
368 } else if (sAction
.Equals("JOIN")) {
370 //sArgs.Split(" ", vsArgs, false, "\"", "\"");
371 sArgs
.QuoteSplit(vsArgs
);
373 if (vsArgs
.size() > 1) {
374 CString sDelim
= vsArgs
[0];
375 bool bFoundOne
= false;
376 CString::EEscape eEscape
= CString::EASCII
;
378 for (unsigned int a
= 1; a
< vsArgs
.size(); a
++) {
379 const CString
& sArg
= vsArgs
[a
];
381 if (sArg
.Equals("ESC=", false, 4)) {
382 eEscape
= CString::ToEscape(sArg
.LeftChomp_n(4));
384 CString sValue
= GetValue(sArg
);
386 if (!sValue
.empty()) {
391 sOutput
+= sValue
.Escape_n(eEscape
);
397 } else if (sAction
.Equals("SETBLOCK")) {
398 sSetBlockVar
= sArgs
;
400 } else if (sAction
.Equals("EXPAND")) {
401 sOutput
+= ExpandFile(sArgs
, true);
402 } else if (sAction
.Equals("VAR")) {
403 sOutput
+= GetValue(sArgs
);
404 } else if (sAction
.Equals("LT")) {
406 } else if (sAction
.Equals("GT")) {
408 } else if (sAction
.Equals("CONTINUE")) {
409 CTemplateLoopContext
* pContext
= GetCurLoopContext();
417 DEBUG("[" + sFileName
+ ":" + CString(uCurPos
- iPos2
-4) + "] <? CONTINUE ?> must be used inside of a loop!");
419 } else if (sAction
.Equals("BREAK")) {
421 CTemplateLoopContext
* pContext
= GetCurLoopContext();
429 DEBUG("[" + sFileName
+ ":" + CString(uCurPos
- iPos2
-4) + "] <? BREAK ?> must be used inside of a loop!");
431 } else if (sAction
.Equals("EXIT")) {
433 } else if (sAction
.Equals("DEBUG")) {
434 DEBUG("CTemplate DEBUG [" + sFileName
+ "@" + CString(uCurPos
- iPos2
-4) + "b] -> [" + sArgs
+ "]");
435 } else if (sAction
.Equals("LOOP")) {
436 CTemplateLoopContext
* pContext
= GetCurLoopContext();
438 if (!pContext
|| pContext
->GetFilePosition() != uCurPos
) {
439 // we are at a brand new loop (be it new or a first pass at an inner loop)
441 CString sLoopName
= sArgs
.Token(0);
442 bool bReverse
= (sArgs
.Token(1).Equals("REVERSE"));
443 bool bSort
= (sArgs
.Token(1).Left(4).Equals("SORT"));
444 vector
<CTemplate
*>* pvLoop
= GetLoop(sLoopName
);
446 if (bSort
&& pvLoop
!= NULL
&& pvLoop
->size() > 1) {
449 if(sArgs
.Token(1).TrimPrefix_n("SORT").Left(4).Equals("ASC=")) {
450 sKey
= sArgs
.Token(1).TrimPrefix_n("SORTASC=");
451 } else if(sArgs
.Token(1).TrimPrefix_n("SORT").Left(5).Equals("DESC=")) {
452 sKey
= sArgs
.Token(1).TrimPrefix_n("SORTDESC=");
457 std::sort(pvLoop
->begin(), pvLoop
->end(), CLoopSorter(sKey
));
462 // If we found data for this loop, add it to our context vector
463 //unsigned long uBeforeLoopTag = uCurPos - iPos2 - 4;
464 unsigned long uAfterLoopTag
= uCurPos
;
466 for (CString::size_type t
= 0; t
< sLine
.size(); t
++) {
468 if (c
== '\r' || c
== '\n') {
475 m_vLoopContexts
.push_back(new CTemplateLoopContext(uAfterLoopTag
, sLoopName
, bReverse
, pvLoop
));
476 } else { // If we don't have data, just skip this loop and everything inside
480 } else if (sAction
.Equals("IF")) {
481 if (ValidIf(sArgs
)) {
486 bValidLastIf
= false;
488 } else if (sAction
.Equals("REM")) {
493 } else if (sAction
.Equals("REM")) {
495 } else if (sAction
.Equals("IF")) {
497 } else if (sAction
.Equals("LOOP")) {
501 if (sAction
.Equals("ENDIF")) {
507 } else if (sAction
.Equals("ENDREM")) {
511 } else if (sAction
.Equals("ENDSETBLOCK")) {
514 } else if (sAction
.Equals("ENDLOOP")) {
515 if (bLoopCont
&& uSkip
== 1) {
520 if (bLoopBreak
&& uSkip
== 1) {
527 // We are at the end of the loop so we need to inc the index
528 CTemplateLoopContext
* pContext
= GetCurLoopContext();
531 pContext
->IncRowIndex();
533 // If we didn't go out of bounds we need to seek back to the top of our loop
534 if (!bLoopBreak
&& pContext
->GetCurRow()) {
535 uCurPos
= pContext
->GetFilePosition();
542 if (!sOutput
.Trim_n().empty()) {
543 pContext
->SetHasData();
548 if (sOutput
.Trim_n().empty()) {
552 bTmplLoopHasData
= pContext
->HasData();
558 } else if (sAction
.Equals("ELSE")) {
559 if (!bValidLastIf
&& uSkip
== 1) {
560 CString sArg
= sArgs
.Token(0);
562 if (sArg
.empty() || (sArg
.Equals("IF") && ValidIf(sArgs
.Token(1, true)))) {
569 } else if (bNotFound
) {
570 // Unknown tag that isn't being skipped...
571 vector
<CSmartPtr
<CTemplateTagHandler
> >& vspTagHandlers
= GetTagHandlers();
573 if (!vspTagHandlers
.empty()) { // @todo this should go up to the top to grab handlers
574 CTemplate
* pTmpl
= GetCurTemplate();
575 CString sCustomOutput
;
577 for (unsigned int j
= 0; j
< vspTagHandlers
.size(); j
++) {
578 CSmartPtr
<CTemplateTagHandler
> spTagHandler
= vspTagHandlers
[j
];
580 if (spTagHandler
->HandleTag(*pTmpl
, sAction
, sArgs
, sCustomOutput
)) {
581 sOutput
+= sCustomOutput
;
588 DEBUG("Unknown/Unhandled tag [" + sAction
+ "]");
596 DEBUG("Malformed tag on line " + CString(uLineNum
) + " of [" << File
.GetLongName() + "]");
597 DEBUG("--------------- [" + sLine
+ "]");
601 uFilePos
+= uLineSize
;
608 if (!bFoundATag
|| bTmplLoopHasData
|| sOutput
.find_first_not_of(" \t\r\n") != CString::npos
) {
610 CString sName
= sSetBlockVar
.Token(0);
611 //CString sValue = sSetBlockVar.Token(1, true);
612 (*this)[sName
] += sOutput
;
628 void CTemplate::DelCurLoopContext() {
629 if (m_vLoopContexts
.empty()) {
633 delete m_vLoopContexts
.back();
634 m_vLoopContexts
.pop_back();
637 CTemplateLoopContext
* CTemplate::GetCurLoopContext() {
638 if (!m_vLoopContexts
.empty()) {
639 return m_vLoopContexts
.back();
645 bool CTemplate::ValidIf(const CString
& sArgs
) {
646 CString sArgStr
= sArgs
;
647 //sArgStr.Replace(" ", "", "\"", "\"", true);
648 sArgStr
.Replace(" &&", "&&", "\"", "\"", false);
649 sArgStr
.Replace("&& ", "&&", "\"", "\"", false);
650 sArgStr
.Replace(" ||", "||", "\"", "\"", false);
651 sArgStr
.Replace("|| ", "||", "\"", "\"", false);
653 CString::size_type uOrPos
= sArgStr
.find("||");
654 CString::size_type uAndPos
= sArgStr
.find("&&");
656 while (uOrPos
!= CString::npos
|| uAndPos
!= CString::npos
|| !sArgStr
.empty()) {
659 if (uAndPos
< uOrPos
) {
663 CString sExpr
= sArgStr
.Token(0, false, ((bAnd
) ? "&&" : "||"));
664 sArgStr
= sArgStr
.Token(1, true, ((bAnd
) ? "&&" : "||"));
666 if (ValidExpr(sExpr
)) {
676 uOrPos
= sArgStr
.find("||");
677 uAndPos
= sArgStr
.find("&&");
683 bool CTemplate::ValidExpr(const CString
& sExpression
) {
684 bool bNegate
= false;
685 CString
sExpr(sExpression
);
689 if (sExpr
.Left(1) == "!") {
694 if (sExpr
.find("!=") != CString::npos
) {
695 sName
= sExpr
.Token(0, false, "!=").Trim_n();
696 sValue
= sExpr
.Token(1, true, "!=", false, "\"", "\"", true).Trim_n();
698 } else if (sExpr
.find("==") != CString::npos
) {
699 sName
= sExpr
.Token(0, false, "==").Trim_n();
700 sValue
= sExpr
.Token(1, true, "==", false, "\"", "\"", true).Trim_n();
701 } else if (sExpr
.find(">=") != CString::npos
) {
702 sName
= sExpr
.Token(0, false, ">=").Trim_n();
703 sValue
= sExpr
.Token(1, true, ">=", false, "\"", "\"", true).Trim_n();
704 return (GetValue(sName
, true).ToLong() >= sValue
.ToLong());
705 } else if (sExpr
.find("<=") != CString::npos
) {
706 sName
= sExpr
.Token(0, false, "<=").Trim_n();
707 sValue
= sExpr
.Token(1, true, "<=", false, "\"", "\"", true).Trim_n();
708 return (GetValue(sName
, true).ToLong() <= sValue
.ToLong());
709 } else if (sExpr
.find(">") != CString::npos
) {
710 sName
= sExpr
.Token(0, false, ">").Trim_n();
711 sValue
= sExpr
.Token(1, true, ">", false, "\"", "\"", true).Trim_n();
712 return (GetValue(sName
, true).ToLong() > sValue
.ToLong());
713 } else if (sExpr
.find("<") != CString::npos
) {
714 sName
= sExpr
.Token(0, false, "<").Trim_n();
715 sValue
= sExpr
.Token(1, true, "<", false, "\"", "\"", true).Trim_n();
716 return (GetValue(sName
, true).ToLong() < sValue
.ToLong());
718 sName
= sExpr
.Trim_n();
721 if (sValue
.empty()) {
722 return (bNegate
!= IsTrue(sName
));
725 sValue
= ResolveLiteral(sValue
);
727 return (bNegate
!= GetValue(sName
, true).Equals(sValue
));
730 bool CTemplate::IsTrue(const CString
& sName
) {
731 if (HasLoop(sName
)) {
735 return GetValue(sName
, true).ToBool();
738 bool CTemplate::HasLoop(const CString
& sName
) {
739 return (GetLoop(sName
) != NULL
);
742 CTemplate
* CTemplate::GetParent(bool bRoot
) {
747 return (m_pParent
) ? m_pParent
->GetParent(bRoot
) : this;
750 CTemplate
* CTemplate::GetCurTemplate() {
751 CTemplateLoopContext
* pContext
= GetCurLoopContext();
757 return pContext
->GetCurRow();
760 CString
CTemplate::ResolveLiteral(const CString
& sString
) {
761 if (sString
.Left(2) == "**") {
762 // Allow string to start with a literal * by using two in a row
763 return sString
.substr(1);
764 } else if (sString
.Left(1) == "*") {
765 // If it starts with only one * then treat it as a var and do a lookup
766 return GetValue(sString
.substr(1));
772 CString
CTemplate::GetValue(const CString
& sArgs
, bool bFromIf
) {
773 CTemplateLoopContext
* pContext
= GetCurLoopContext();
774 CString sName
= sArgs
.Token(0);
775 CString sRest
= sArgs
.Token(1, true);
778 while (sRest
.Replace(" =", "=", "\"", "\"")) {}
779 while (sRest
.Replace("= ", "=", "\"", "\"")) {}
783 //sRest.Split(" ", vArgs, false, "\"", "\"");
784 sRest
.QuoteSplit(vArgs
);
786 for (unsigned int a
= 0; a
< vArgs
.size(); a
++) {
787 const CString
& sArg
= vArgs
[a
];
789 msArgs
[sArg
.Token(0, false, "=").AsUpper()] = sArg
.Token(1, true, "=");
792 /* We have no CConfig in znc land
793 if (msArgs.find("CONFIG") != msArgs.end()) {
794 sRet = CConfig::GetValue(sName);
795 } else*/ if (msArgs
.find("ROWS") != msArgs
.end()) {
796 vector
<CTemplate
*>* pLoop
= GetLoop(sName
);
797 sRet
= CString((pLoop
) ? pLoop
->size() : 0);
798 } else if (msArgs
.find("TOP") == msArgs
.end() && pContext
) {
799 sRet
= pContext
->GetValue(sArgs
, bFromIf
);
805 if (sName
.Left(1) == "*") {
807 MCString::iterator it
= find(sName
);
808 sName
= (it
!= end()) ? it
->second
: "";
811 MCString::iterator it
= find(sName
);
812 sRet
= (it
!= end()) ? it
->second
: "";
815 vector
<CSmartPtr
<CTemplateTagHandler
> >& vspTagHandlers
= GetTagHandlers();
817 if (!vspTagHandlers
.empty()) { // @todo this should go up to the top to grab handlers
818 CTemplate
* pTmpl
= GetCurTemplate();
821 for (unsigned int j
= 0; j
< vspTagHandlers
.size(); j
++) {
822 CSmartPtr
<CTemplateTagHandler
> spTagHandler
= vspTagHandlers
[j
];
823 CString sCustomOutput
;
825 if (!bFromIf
&& spTagHandler
->HandleVar(*pTmpl
, sArgs
.Token(0), sArgs
.Token(1, true), sCustomOutput
)) {
826 sRet
= sCustomOutput
;
828 } else if (bFromIf
&& spTagHandler
->HandleIf(*pTmpl
, sArgs
.Token(0), sArgs
.Token(1, true), sCustomOutput
)) {
829 sRet
= sCustomOutput
;
835 for (unsigned int j
= 0; j
< vspTagHandlers
.size(); j
++) {
836 CSmartPtr
<CTemplateTagHandler
> spTagHandler
= vspTagHandlers
[j
];
838 if (spTagHandler
->HandleValue(*pTmpl
, sRet
, msArgs
)) {
846 sRet
= ResolveLiteral(msArgs
["DEFAULT"]);
849 MCString::iterator it
= msArgs
.find("ESC");
851 if (it
!= msArgs
.end()) {
853 it
->second
.Split(",", vsEscs
, false);
855 for (unsigned int a
= 0; a
< vsEscs
.size(); a
++) {
856 sRet
.Escape(CString::ToEscape(vsEscs
[a
]));
859 sRet
.Escape(m_spOptions
->GetEscapeFrom(), m_spOptions
->GetEscapeTo());