summaryrefslogtreecommitdiffstats
path: root/src/template.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/template.cpp')
-rw-r--r--src/template.cpp317
1 files changed, 282 insertions, 35 deletions
diff --git a/src/template.cpp b/src/template.cpp
index 527148c..10b3e43 100644
--- a/src/template.cpp
+++ b/src/template.cpp
@@ -34,6 +34,7 @@
#include "message.h"
#include "util.h"
#include "resourcemgr.h"
+#include "portable.h"
#define ENABLE_TRACING 0
@@ -552,6 +553,10 @@ class TemplateContextImpl : public TemplateContext
if (m_activeEscapeIntf) m_activeEscapeIntf->enableTabbing(b);
}
bool tabbingEnabled() const { return m_tabbingEnabled; }
+ bool needsRecoding() const { return !m_encoding.isEmpty(); }
+ QCString encoding() const { return m_encoding; }
+ void setEncoding(const QCString &file,int line,const QCString &enc);
+ QCString recode(const QCString &s);
void warn(const char *fileName,int line,const char *fmt,...) const;
// index related functions
@@ -573,6 +578,8 @@ class TemplateContextImpl : public TemplateContext
bool m_tabbingEnabled;
TemplateAutoRef<TemplateStruct> m_indices;
QDict< QStack<TemplateVariant> > m_indexStacks;
+ QCString m_encoding;
+ void *m_fromUtf8;
};
//-----------------------------------------------------------------------------
@@ -1285,6 +1292,64 @@ class FilterDivisibleBy
}
};
+//--------------------------------------------------------------------
+
+/** @brief The implementation of the "isRelativeURL" filter */
+class FilterIsRelativeURL
+{
+ public:
+ static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
+ {
+ if (v.isValid() && v.type()==TemplateVariant::String)
+ {
+ QCString s = v.toString();
+ if (!s.isEmpty() && s.at(0)=='!') return TRUE;
+ }
+ return FALSE;
+ }
+};
+
+//--------------------------------------------------------------------
+
+/** @brief The implementation of the "isRelativeURL" filter */
+class FilterIsAbsoluteURL
+{
+ public:
+ static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
+ {
+ if (v.isValid() && v.type()==TemplateVariant::String)
+ {
+ QCString s = v.toString();
+ if (!s.isEmpty() && s.at(0)=='^') return TRUE;
+ }
+ return FALSE;
+ }
+};
+
+//--------------------------------------------------------------------
+
+/** @brief The implementation of the "decodeURL" filter
+ * The leading character is removed from the value in case it is a ^ or !.
+ * - ^ is used to encode a absolute URL
+ * - ! is used to encode a relative URL
+ */
+class FilterDecodeURL
+{
+ public:
+ static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &)
+ {
+ if (v.isValid() && v.type()==TemplateVariant::String)
+ {
+ QCString s = v.toString();
+ if (!s.isEmpty() && (s.at(0)=='^' || s.at(0)=='!'))
+ {
+ return s.mid(1);
+ }
+ }
+ return v;
+ }
+};
+
//--------------------------------------------------------------------
@@ -1336,25 +1401,28 @@ class TemplateFilterFactory
};
// register a handlers for each filter we support
-static TemplateFilterFactory::AutoRegister<FilterAdd> fAdd("add");
-static TemplateFilterFactory::AutoRegister<FilterGet> fGet("get");
-static TemplateFilterFactory::AutoRegister<FilterRaw> fRaw("raw");
-static TemplateFilterFactory::AutoRegister<FilterList> fList("list");
-static TemplateFilterFactory::AutoRegister<FilterAppend> fAppend("append");
-static TemplateFilterFactory::AutoRegister<FilterLength> fLength("length");
-static TemplateFilterFactory::AutoRegister<FilterNoWrap> fNoWrap("nowrap");
-static TemplateFilterFactory::AutoRegister<FilterFlatten> fFlatten("flatten");
-static TemplateFilterFactory::AutoRegister<FilterDefault> fDefault("default");
-static TemplateFilterFactory::AutoRegister<FilterPrepend> fPrepend("prepend");
-static TemplateFilterFactory::AutoRegister<FilterGroupBy> fGroupBy("groupBy");
-static TemplateFilterFactory::AutoRegister<FilterRelative> fRelative("relative");
-static TemplateFilterFactory::AutoRegister<FilterListSort> fListSort("listsort");
-static TemplateFilterFactory::AutoRegister<FilterTexLabel> fTexLabel("texLabel");
-static TemplateFilterFactory::AutoRegister<FilterTexIndex> fTexIndex("texIndex");
-static TemplateFilterFactory::AutoRegister<FilterPaginate> fPaginate("paginate");
-static TemplateFilterFactory::AutoRegister<FilterStripPath> fStripPath("stripPath");
-static TemplateFilterFactory::AutoRegister<FilterAlphaIndex> fAlphaIndex("alphaIndex");
-static TemplateFilterFactory::AutoRegister<FilterDivisibleBy> fDivisibleBy("divisibleby");
+static TemplateFilterFactory::AutoRegister<FilterAdd> fAdd("add");
+static TemplateFilterFactory::AutoRegister<FilterGet> fGet("get");
+static TemplateFilterFactory::AutoRegister<FilterRaw> fRaw("raw");
+static TemplateFilterFactory::AutoRegister<FilterList> fList("list");
+static TemplateFilterFactory::AutoRegister<FilterAppend> fAppend("append");
+static TemplateFilterFactory::AutoRegister<FilterLength> fLength("length");
+static TemplateFilterFactory::AutoRegister<FilterNoWrap> fNoWrap("nowrap");
+static TemplateFilterFactory::AutoRegister<FilterFlatten> fFlatten("flatten");
+static TemplateFilterFactory::AutoRegister<FilterDefault> fDefault("default");
+static TemplateFilterFactory::AutoRegister<FilterPrepend> fPrepend("prepend");
+static TemplateFilterFactory::AutoRegister<FilterGroupBy> fGroupBy("groupBy");
+static TemplateFilterFactory::AutoRegister<FilterRelative> fRelative("relative");
+static TemplateFilterFactory::AutoRegister<FilterListSort> fListSort("listsort");
+static TemplateFilterFactory::AutoRegister<FilterTexLabel> fTexLabel("texLabel");
+static TemplateFilterFactory::AutoRegister<FilterTexIndex> fTexIndex("texIndex");
+static TemplateFilterFactory::AutoRegister<FilterPaginate> fPaginate("paginate");
+static TemplateFilterFactory::AutoRegister<FilterStripPath> fStripPath("stripPath");
+static TemplateFilterFactory::AutoRegister<FilterDecodeURL> fDecodeURL("decodeURL");
+static TemplateFilterFactory::AutoRegister<FilterAlphaIndex> fAlphaIndex("alphaIndex");
+static TemplateFilterFactory::AutoRegister<FilterDivisibleBy> fDivisibleBy("divisibleby");
+static TemplateFilterFactory::AutoRegister<FilterIsRelativeURL> fIsRelativeURL("isRelativeURL");
+static TemplateFilterFactory::AutoRegister<FilterIsAbsoluteURL> fIsAbsoluteURL("isAbsoluteURL");
//--------------------------------------------------------------------
@@ -2303,6 +2371,7 @@ TemplateContextImpl::TemplateContextImpl(const TemplateEngine *e)
m_indexStacks.setAutoDelete(TRUE);
m_contextStack.setAutoDelete(TRUE);
m_escapeIntfDict.setAutoDelete(TRUE);
+ m_fromUtf8 = (void*)(-1);
push();
set("index",m_indices.get());
}
@@ -2312,6 +2381,49 @@ TemplateContextImpl::~TemplateContextImpl()
pop();
}
+void TemplateContextImpl::setEncoding(const QCString &templateName,int line,const QCString &enc)
+{
+ if (enc==m_encoding) return; // nothing changed
+ if (m_fromUtf8!=(void *)(-1))
+ {
+ portable_iconv_close(m_fromUtf8);
+ m_fromUtf8 = (void*)(-1);
+ }
+ m_encoding=enc;
+ if (!enc.isEmpty())
+ {
+ m_fromUtf8 = portable_iconv_open(enc,"UTF-8");
+ if (m_fromUtf8==(void*)(-1))
+ {
+ warn(templateName,line,"unsupported character conversion: '%s'->'UTF-8'\n", enc.data());
+ }
+ }
+ //printf("TemplateContextImpl::setEncoding(%s)\n",enc.data());
+}
+
+QCString TemplateContextImpl::recode(const QCString &s)
+{
+ //printf("TemplateContextImpl::recode(%s)\n",s.data());
+ int iSize = s.length();
+ int oSize = iSize*4+1;
+ QCString output(oSize);
+ size_t iLeft = iSize;
+ size_t oLeft = oSize;
+ char *iPtr = s.rawData();
+ char *oPtr = output.rawData();
+ if (!portable_iconv(m_fromUtf8,&iPtr,&iLeft,&oPtr,&oLeft))
+ {
+ oSize -= (int)oLeft;
+ output.resize(oSize+1);
+ output.at(oSize)='\0';
+ return output;
+ }
+ else
+ {
+ return s;
+ }
+}
+
void TemplateContextImpl::set(const char *name,const TemplateVariant &v)
{
TemplateVariant *pv = m_contextStack.getFirst()->find(name);
@@ -2439,7 +2551,7 @@ void TemplateContextImpl::warn(const char *fileName,int line,const char *fmt,...
void TemplateContextImpl::openSubIndex(const QCString &indexName)
{
- printf("TemplateContextImpl::openSubIndex(%s)\n",indexName.data());
+ //printf("TemplateContextImpl::openSubIndex(%s)\n",indexName.data());
QStack<TemplateVariant> *stack = m_indexStacks.find(indexName);
if (!stack || stack->isEmpty() || stack->top()->type()==TemplateVariant::List) // error: no stack yet or no entry
{
@@ -2460,7 +2572,7 @@ void TemplateContextImpl::openSubIndex(const QCString &indexName)
void TemplateContextImpl::closeSubIndex(const QCString &indexName)
{
- printf("TemplateContextImpl::closeSubIndex(%s)\n",indexName.data());
+ //printf("TemplateContextImpl::closeSubIndex(%s)\n",indexName.data());
QStack<TemplateVariant> *stack = m_indexStacks.find(indexName);
if (!stack || stack->count()<3)
{
@@ -2586,16 +2698,30 @@ class TemplateNodeText : public TemplateNode
void render(FTextStream &ts, TemplateContext *c)
{
- //printf("TemplateNodeText::render(%s)\n",m_data.data());
TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
if (ci==0) return; // should not happen
+ //printf("TemplateNodeText::render(%s) needsRecoding=%d ci=%p\n",m_data.data(),ci->needsRecoding(),ci);
if (ci->spacelessEnabled())
{
- ts << ci->spacelessIntf()->remove(m_data);
+ if (ci->needsRecoding())
+ {
+ ts << ci->recode(ci->spacelessIntf()->remove(m_data));
+ }
+ else
+ {
+ ts << ci->spacelessIntf()->remove(m_data);
+ }
}
else
{
- ts << m_data;
+ if (ci->needsRecoding())
+ {
+ ts << ci->recode(m_data);
+ }
+ else
+ {
+ ts << m_data;
+ }
}
}
private:
@@ -2638,11 +2764,25 @@ class TemplateNodeVariable : public TemplateNode
}
if (ci->escapeIntf() && !v.raw())
{
- ts << ci->escapeIntf()->escape(v.toString());
+ if (ci->needsRecoding())
+ {
+ ts << ci->recode(ci->escapeIntf()->escape(v.toString()));
+ }
+ else
+ {
+ ts << ci->escapeIntf()->escape(v.toString());
+ }
}
else
{
- ts << v.toString();
+ if (ci->needsRecoding())
+ {
+ ts << ci->recode(v.toString());
+ }
+ else
+ {
+ ts << v.toString();
+ }
}
}
}
@@ -2791,7 +2931,7 @@ class TemplateNodeIf : public TemplateNodeCreator<TemplateNodeIf>
if (guardValue.toBool()) // render nodes for the first guard that evaluated to 'true'
{
nodes->trueNodes.render(ts,c);
- processed=TRUE;
+ processed=TRUE;
}
}
else
@@ -3961,11 +4101,25 @@ class TemplateNodeCycle : public TemplateNodeCreator<TemplateNodeCycle>
}
if (ci->escapeIntf() && !v.raw())
{
- ts << ci->escapeIntf()->escape(v.toString());
+ if (ci->needsRecoding())
+ {
+ ts << ci->recode(ci->escapeIntf()->escape(v.toString()));
+ }
+ else
+ {
+ ts << ci->escapeIntf()->escape(v.toString());
+ }
}
else
{
- ts << v.toString();
+ if (ci->needsRecoding())
+ {
+ ts << ci->recode(v.toString());
+ }
+ else
+ {
+ ts << v.toString();
+ }
}
}
if (++m_index==m_args.count()) // wrap around
@@ -4109,7 +4263,14 @@ class TemplateNodeMarkers : public TemplateNodeCreator<TemplateNodeMarkers>
int index=0,newIndex,matchLen;
while ((newIndex=marker.match(str,index,&matchLen))!=-1)
{
- ts << str.mid(index,newIndex-index); // write text before marker
+ if (ci->needsRecoding())
+ {
+ ts << ci->recode(str.mid(index,newIndex-index)); // write text before marker
+ }
+ else
+ {
+ ts << str.mid(index,newIndex-index); // write text before marker
+ }
bool ok;
uint entryIndex = str.mid(newIndex+1,matchLen-1).toUInt(&ok); // get marker id
TemplateVariant var;
@@ -4137,7 +4298,14 @@ class TemplateNodeMarkers : public TemplateNodeCreator<TemplateNodeMarkers>
}
index=newIndex+matchLen; // set index just after marker
}
- ts << str.right(str.length()-index); // write text after last marker
+ if (ci->needsRecoding())
+ {
+ ts << ci->recode(str.right(str.length()-index)); // write text after last marker
+ }
+ else
+ {
+ ts << str.right(str.length()-index); // write text after last marker
+ }
c->pop();
delete it;
}
@@ -4244,8 +4412,7 @@ class TemplateNodeResource : public TemplateNodeCreator<TemplateNodeResource>
QCString targetFile = m_asExpr->resolve(c).toString();
mkpath(ci,targetFile);
if (targetFile.isEmpty())
- {
- ci->warn(m_templateName,m_line,"invalid parameter at right side of 'as' for resource command\n");
+ { ci->warn(m_templateName,m_line,"invalid parameter at right side of 'as' for resource command\n");
}
else
{
@@ -4266,6 +4433,59 @@ class TemplateNodeResource : public TemplateNodeCreator<TemplateNodeResource>
//----------------------------------------------------------
+/** @brief Class representing the 'encoding' tag in a template */
+class TemplateNodeEncoding : public TemplateNodeCreator<TemplateNodeEncoding>
+{
+ public:
+ TemplateNodeEncoding(TemplateParser *parser,TemplateNode *parent,int line,const QCString &data)
+ : TemplateNodeCreator<TemplateNodeEncoding>(parser,parent,line)
+ {
+ TRACE(("{TemplateNodeEncoding(%s)\n",data.data()));
+ ExpressionParser ep(parser,line);
+ if (data.isEmpty())
+ {
+ parser->warn(m_templateName,line,"encoding tag is missing encoding argument");
+ m_encExpr = 0;
+ }
+ else
+ {
+ m_encExpr = ep.parse(data);
+ }
+ QStrList stopAt;
+ stopAt.append("endencoding");
+ parser->parse(this,line,stopAt,m_nodes);
+ parser->removeNextToken(); // skip over endencoding
+ TRACE(("}TemplateNodeEncoding(%s)\n",data.data()));
+ }
+ ~TemplateNodeEncoding()
+ {
+ delete m_encExpr;
+ }
+ void render(FTextStream &ts, TemplateContext *c)
+ {
+ TemplateContextImpl *ci = dynamic_cast<TemplateContextImpl*>(c);
+ if (ci==0) return; // should not happen
+ ci->setLocation(m_templateName,m_line);
+ QCString encStr;
+ if (m_encExpr)
+ {
+ encStr = m_encExpr->resolve(c).toString();
+ }
+ QCString oldEncStr = ci->encoding();
+ if (!encStr.isEmpty())
+ {
+ ci->setEncoding(m_templateName,m_line,encStr);
+ }
+ m_nodes.render(ts,c);
+ ci->setEncoding(m_templateName,m_line,oldEncStr);
+ }
+ private:
+ ExprAst *m_encExpr;
+ TemplateNodeList m_nodes;
+};
+
+//----------------------------------------------------------
+
/** @brief Factory class for creating tag AST nodes found in a template */
class TemplateNodeFactory
{
@@ -4328,6 +4548,7 @@ static TemplateNodeFactory::AutoRegister<TemplateNodeInclude> autoRefInclu
static TemplateNodeFactory::AutoRegister<TemplateNodeMarkers> autoRefMarkers("markers");
static TemplateNodeFactory::AutoRegister<TemplateNodeTabbing> autoRefTabbing("tabbing");
static TemplateNodeFactory::AutoRegister<TemplateNodeResource> autoRefResource("resource");
+static TemplateNodeFactory::AutoRegister<TemplateNodeEncoding> autoRefEncoding("encoding");
static TemplateNodeFactory::AutoRegister<TemplateNodeSpaceless> autoRefSpaceless("spaceless");
static TemplateNodeFactory::AutoRegister<TemplateNodeIndexEntry> autoRefIndexEntry("indexentry");
static TemplateNodeFactory::AutoRegister<TemplateNodeOpenSubIndex> autoRefOpenSubIndex("opensubindex");
@@ -4715,7 +4936,8 @@ void TemplateParser::parse(
command=="endrecursetree" || command=="endspaceless" ||
command=="endmarkers" || command=="endmsg" ||
command=="endrepeat" || command=="elif" ||
- command=="endrange" || command=="endtabbing")
+ command=="endrange" || command=="endtabbing" ||
+ command=="endencoding")
{
warn(m_templateName,tok->line,"Found tag '%s' without matching start tag",command.data());
}
@@ -4866,8 +5088,23 @@ class TemplateEngine::Private
//printf("loadByName(%s,%d) {\n",fileName.data(),line);
m_includeStack.append(new IncludeEntry(IncludeEntry::Template,fileName,QCString(),line));
Template *templ = m_templateCache.find(fileName);
- if (templ==0)
+ if (templ==0) // first time template is referenced
{
+ QCString filePath = m_templateDirName+"/"+fileName;
+ QFile f(filePath);
+ if (f.open(IO_ReadOnly))
+ {
+ QFileInfo fi(filePath);
+ int size=fi.size();
+ QCString data(size+1);
+ if (f.readBlock(data.rawData(),size)==size)
+ {
+ templ = new TemplateImpl(m_engine,filePath,data,m_extension);
+ m_templateCache.insert(fileName,templ);
+ return templ;
+ }
+ }
+ // fallback to default built-in template
const QCString data = ResourceMgr::instance().getAsString(fileName);
if (!data.isEmpty())
{
@@ -4940,12 +5177,18 @@ class TemplateEngine::Private
return m_extension;
}
+ void setTemplateDir(const char *dirName)
+ {
+ m_templateDirName = dirName;
+ }
+
private:
QDict<Template> m_templateCache;
//mutable int m_indent;
TemplateEngine *m_engine;
QList<IncludeEntry> m_includeStack;
QCString m_extension;
+ QCString m_templateDirName;
};
TemplateEngine::TemplateEngine()
@@ -5003,5 +5246,9 @@ QCString TemplateEngine::outputExtension() const
return p->outputExtension();
}
+void TemplateEngine::setTemplateDir(const char *dirName)
+{
+ p->setTemplateDir(dirName);
+}