1
0
mirror of https://github.com/OlafvdSpek/ctemplate.git synced 2025-10-05 19:16:54 +08:00

ctemplate 0.3

This commit is contained in:
csilvers 2007-03-21 23:20:57 +00:00
parent 187110dc1a
commit 14d041d16d
20 changed files with 10779 additions and 3587 deletions

View File

@ -15,3 +15,11 @@ Wed Jun 14 14:56:04 2006 Google Inc. <opensource@google.com>
* SetTemplateGlobalValue(): new variable type with new scoping (ehamon)
* Export a nothreads version of the ctemplate library (csilvers)
* Got rid of scandir call, which should improve portability (csilvers)
Mon Aug 21 17:44:32 2006 Google Inc. <opensource@google.com>
* ctemplate: version 0.3 release
* New contrib/ directory entry: vi syntax highlighting (patlac)
* New contrib/ directory entry: emacs syntax highlighting (tonyg)
* Allow escape-modifiers to affect includes, not just vars (csilvers)
* Add JSON escape-functor (majewski)

View File

@ -152,7 +152,8 @@ deb: dist-gzip packages/deb.sh packages/deb/*
libtool: $(LIBTOOL_DEPS)
$(SHELL) ./config.status --recheck
EXTRA_DIST = packages/rpm.sh packages/rpm/rpm.spec packages/deb.sh packages/deb \
$(SCRIPTS) libtool
$(SCRIPTS) libtool \
contrib
## If you create hash_map.h, hash_set.h, and/or hash_fun.h via the
## ACC_CXX_MAKE_*_H configure.ac macros, add those files here.

View File

@ -209,7 +209,8 @@ template_nothreads_regtest_LDADD = libctemplate_nothreads.la
noinst_PROGRAMS = $(TESTS)
EXTRA_DIST = packages/rpm.sh packages/rpm/rpm.spec packages/deb.sh packages/deb \
$(SCRIPTS) libtool
$(SCRIPTS) libtool \
contrib
DISTCLEANFILES = src/google/ctemplate/hash_map.h src/google/ctemplate/hash_set.h

1784
trunk/aclocal.m4 vendored

File diff suppressed because it is too large Load Diff

5533
trunk/configure vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,11 @@
## Process this file with autoconf to produce configure.
## In general, the safest way to proceed is to run the following:
## % aclocal -I `pwd`/../autoconf && autoheader && autoconf && automake
## % aclocal -I . -I `pwd`/../autoconf && autoheader && autoconf && automake
# make sure we're interpreted by some minimal autoconf
AC_PREREQ(2.57)
AC_INIT(ctemplate, 0.2, opensource@google.com)
AC_INIT(ctemplate, 0.3, opensource@google.com)
# The argument here is just something that should be in the current directory
# (for sanity checking)
AC_CONFIG_SRCDIR(README)

View File

@ -0,0 +1,13 @@
All files under this contrib directory are UNSUPPORTED; use at your
own risk. They were provided by users of ctemplate and were not
tested by the authors of ctemplate. These files are not owned by
Google. Please contact the authors of the contributions for help
about these, not the ctemplate authors. Thanks!, and enjoy.
highlighting.vim by Patrick Lacasse <patlac@borabora.crchul.ulaval.ca>
How to set up syntax highlighting (colorization) for ctemplate .tpl
files, using vim. A shar file; see top-of-file for how to extract.
tpl-mode.el by Tony Gentilcore
Elisp file for syntax highlighting (colorization) for ctemplate
.tpl files, using emacs. See top-of-file for how to use.

View File

@ -0,0 +1,212 @@
#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.2.1).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 2006-08-08 13:38 PDT by <patlac@borabora.crchul.ulaval.ca>.
# Commandline: shar -T .vim
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 1477 -rw-r--r-- .vim/syntax/tpl.vim
# 56 -rw-r--r-- .vim/ftdetect/tpl.vim
#
echo >/dev/null <<_NOTES_EOF
From: Patrick Lacasse <patlac@borabora.crchul.ulaval.ca>
Subject: vim color for google-ctemplate howto
To: google-ctemplate@googlegroups.com
Date: Fri, 4 Aug 2006 11:38:39 -0400
Hi group,
I'm now using google-ctemplate. My text editor is vim. Here is a little gift
for other people like me.
Howto have google-ctemplate colored by vim :
In your home directory, run 'sh $0'.
Now restart vim.
This will autodetects file with tpl extension and colors them. You can change
the color by changing the HiLink lines ( try changing String by
Identifier ) .
I'm not sure exactly about what are the legal marker names. Feel free to
change the regexes.
I only tryed this with vim 6.4 , I just cut and past the things about version
checking.
For more information about syntax higlithning in vim, try
:help syntax
Amusez-vous bien,
Patrick Lacasse
patlac@borabora.crchul.ulaval.ca
_NOTES_EOF
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=FAILED
locale_dir=FAILED
first_param="$1"
for dir in $PATH
do
if test "$gettext_dir" = FAILED && test -f $dir/gettext \
&& ($dir/gettext --version >/dev/null 2>&1)
then
set `$dir/gettext --version 2>&1`
if test "$3" = GNU
then
gettext_dir=$dir
fi
fi
if test "$locale_dir" = FAILED && test -f $dir/shar \
&& ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
then
locale_dir=`$dir/shar --print-text-domain-dir`
fi
done
IFS="$save_IFS"
if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED
then
echo=echo
else
TEXTDOMAINDIR=$locale_dir
export TEXTDOMAINDIR
TEXTDOMAIN=sharutils
export TEXTDOMAIN
echo="$gettext_dir/gettext -s"
fi
if touch -am -t 200112312359.59 $$.touch >/dev/null 2>&1 && test ! -f 200112312359.59 -a -f $$.touch; then
shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'
elif touch -am 123123592001.59 $$.touch >/dev/null 2>&1 && test ! -f 123123592001.59 -a ! -f 123123592001.5 -a -f $$.touch; then
shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'
elif touch -am 1231235901 $$.touch >/dev/null 2>&1 && test ! -f 1231235901 -a -f $$.touch; then
shar_touch='touch -am $3$4$5$6$2 "$8"'
else
shar_touch=:
echo
$echo 'WARNING: not restoring timestamps. Consider getting and'
$echo "installing GNU \`touch', distributed in GNU File Utilities..."
echo
fi
rm -f 200112312359.59 123123592001.59 123123592001.5 1231235901 $$.touch
#
if mkdir _sh09814; then
$echo 'x -' 'creating lock directory'
else
$echo 'failed to create lock directory'
exit 1
fi
# ============= .vim/syntax/tpl.vim ==============
if test ! -d '.vim'; then
$echo 'x -' 'creating directory' '.vim'
mkdir '.vim'
fi
if test ! -d '.vim/syntax'; then
$echo 'x -' 'creating directory' '.vim/syntax'
mkdir '.vim/syntax'
fi
if test -f '.vim/syntax/tpl.vim' && test "$first_param" != -c; then
$echo 'x -' SKIPPING '.vim/syntax/tpl.vim' '(file already exists)'
else
$echo 'x -' extracting '.vim/syntax/tpl.vim' '(text)'
sed 's/^X//' << 'SHAR_EOF' > '.vim/syntax/tpl.vim' &&
" Vim syntax file
" Language: google-ctemplate
" Maintainer: Patrick Lacasse <patlac@borabora.crchul.ulaval.ca>
" Last Change: 2006 Août 03
"
" For information about google-ctemplate see
" http://goog-ctemplate.sourceforge.net/
"
" This vim syntax file works on vim 5.6, 5.7, 5.8 and 6.x.
" It implements Bram Moolenaar's April 25, 2001 recommendations to make
" the syntax file maximally portable across different versions of vim.
X
" For version 5.x: Clear all syntax items
" For version 6.x: Quit when a syntax file was already loaded
if version < 600
X syntax clear
elseif exists("b:current_syntax")
X finish
endif
X
syntax match tplMarkerError "{{}\?\([^}]\+}\?\)*}}"
syntax match tplSectionMarker "{{[#/][A-Za-z_]\+}}"
syntax match tplInclude "{{>[A-Za-z_]\+}}"
syntax match tplComment "{{![A-Za-z_]\+}}"
syntax match tplVariableMarker "{{[_A-Za-z]\+}}"
X
" Define the default highlighting.
" For version 5.7 and earlier: only when not done already
" For version 5.8 and later: only when an item doesn't have highlighting yet
if version >= 508 || !exists("did_tpl_syn_inits")
X if version < 508
X let did_tpl_syn_inits = 1
X command -nargs=+ HiLink hi link <args>
X else
X command -nargs=+ HiLink hi def link <args>
X endif
X
X HiLink tplSectionMarker Repeat
X HiLink tplInclude Include
X HiLink tplComment Comment
X HiLink tplVariableMarker String
X HiLink tplMarkerError Error
X
X delcommand HiLink
endif
X
let b:current_syntax = "tpl"
SHAR_EOF
(set 20 06 08 08 13 34 11 '.vim/syntax/tpl.vim'; eval "$shar_touch") &&
chmod 0644 '.vim/syntax/tpl.vim' ||
$echo 'restore of' '.vim/syntax/tpl.vim' 'failed'
if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
&& ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
md5sum -c << SHAR_EOF >/dev/null 2>&1 \
|| $echo '.vim/syntax/tpl.vim:' 'MD5 check failed'
536faef79eff0597e642c5db04c1f79d .vim/syntax/tpl.vim
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < '.vim/syntax/tpl.vim'`"
test 1477 -eq "$shar_count" ||
$echo '.vim/syntax/tpl.vim:' 'original size' '1477,' 'current size' "$shar_count!"
fi
fi
# ============= .vim/ftdetect/tpl.vim ==============
if test ! -d '.vim/ftdetect'; then
$echo 'x -' 'creating directory' '.vim/ftdetect'
mkdir '.vim/ftdetect'
fi
if test -f '.vim/ftdetect/tpl.vim' && test "$first_param" != -c; then
$echo 'x -' SKIPPING '.vim/ftdetect/tpl.vim' '(file already exists)'
else
$echo 'x -' extracting '.vim/ftdetect/tpl.vim' '(text)'
sed 's/^X//' << 'SHAR_EOF' > '.vim/ftdetect/tpl.vim' &&
au BufRead,BufNewFile *.tpl set filetype=tpl
SHAR_EOF
(set 20 06 08 08 13 34 20 '.vim/ftdetect/tpl.vim'; eval "$shar_touch") &&
chmod 0644 '.vim/ftdetect/tpl.vim' ||
$echo 'restore of' '.vim/ftdetect/tpl.vim' 'failed'
if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
&& ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
md5sum -c << SHAR_EOF >/dev/null 2>&1 \
|| $echo '.vim/ftdetect/tpl.vim:' 'MD5 check failed'
774fd4a092b77400ef6e74a7256ff8ef .vim/ftdetect/tpl.vim
SHAR_EOF
else
shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < '.vim/ftdetect/tpl.vim'`"
test 56 -eq "$shar_count" ||
$echo '.vim/ftdetect/tpl.vim:' 'original size' '56,' 'current size' "$shar_count!"
fi
fi
rm -fr _sh09814
exit 0

263
trunk/contrib/tpl-mode.el Normal file
View File

@ -0,0 +1,263 @@
;;; tpl-mode.el -- a major mode for editing Google CTemplate files.
;;; By Tony Gentilcore, July 2006
;;;
;;; TO USE:
;;; 1) Copy this file somewhere you in emacs load-path. To see what
;;; your load-path is, run inside emacs: C-h v load-path<RET>
;;; 2) Add the following two lines to your .emacs file:
;;; (setq auto-mode-alist (cons '("\\.tpl$" . tpl-mode) auto-mode-alist))
;;; (autoload 'tpl-mode "tpl-mode" "Major mode for editing CTemplate files." t)
;;; 3) Optionally (but recommended), add this third line as well:
;;; (add-hook 'tpl-mode-hook '(lambda () (font-lock-mode 1)))
;;; ---
;;;
;;; While the CTemplate language can be used for any types of text,
;;; this mode is intended for using CTemplate to write HTML.
;;;
;;; The indentation still has minor bugs due to the fact that
;;; templates do not require valid HTML.
;;;
;;; It would be nice to be able to highlight attributes of HTML tags,
;;; however this is difficult due to the presence of CTemplate symbols
;;; embedded within attributes.
(eval-when-compile
(require 'font-lock))
(defgroup tpl-mode nil
"Major mode for editing Google CTemplate files"
:group 'languages)
(defvar tpl-mode-version "1.0"
"Version of `tpl-mode.el'.")
(defvar tpl-mode-abbrev-table nil
"Abbrev table for use in tpl-mode buffers.")
(define-abbrev-table 'tpl-mode-abbrev-table ())
(defcustom tpl-mode-hook nil
"*Hook that runs upon entering tpl-mode."
:type 'hook
)
(defvar tpl-mode-map nil
"Keymap for tpl-mode major mode")
(if tpl-mode-map
nil
(setq tpl-mode-map (make-sparse-keymap))
)
(define-key tpl-mode-map "\t" 'tpl-indent-command)
(define-key tpl-mode-map "\C-m" 'newline-and-indent)
(defvar tpl-mode-syntax-table nil
"Syntax table in use in tpl-mode buffers.")
;; Syntax table.
(if tpl-mode-syntax-table
nil
(setq tpl-mode-syntax-table (make-syntax-table text-mode-syntax-table))
(modify-syntax-entry ?< "(> " tpl-mode-syntax-table)
(modify-syntax-entry ?> ")< " tpl-mode-syntax-table)
(modify-syntax-entry ?\" ". " tpl-mode-syntax-table)
(modify-syntax-entry ?\\ ". " tpl-mode-syntax-table)
(modify-syntax-entry ?' "w " tpl-mode-syntax-table)
)
(defvar tpl-basic-offset 2
"The basic indentation offset.")
;; Constant regular expressions to identify template elements.
(defconst tpl-mode-tpl-token "[a-zA-Z][a-zA-Z0-9_:]*?")
(defconst tpl-mode-section (concat "\\({{[#/]"
tpl-mode-tpl-token
"}}\\)"))
(defconst tpl-mode-open-section (concat "\\({{#"
tpl-mode-tpl-token
"}}\\)"))
(defconst tpl-mode-close-section (concat "{{/\\("
tpl-mode-tpl-token
"\\)}}"))
;; TODO(tonyg) Figure out a way to support multiline comments.
(defconst tpl-mode-comment "\\({{!.*?}}\\)")
(defconst tpl-mode-include (concat "\\({{>"
tpl-mode-tpl-token
"}}\\)"))
(defconst tpl-mode-variable (concat "\\({{"
tpl-mode-tpl-token
"}}\\)"))
(defconst tpl-mode-builtins
(concat
"\\({{\\<"
(regexp-opt
'("BI_NEWLINE" "BI_SPACE")
t)
"\\>}}\\)"))
(defconst tpl-mode-close-section-at-start (concat "^[ \t]*?"
tpl-mode-close-section))
;; Constant regular expressions to identify html tags.
;; Taken from HTML 4.01 / XHTML 1.0 Reference found at:
;; http://www.w3schools.com/tags/default.asp.
(defconst tpl-mode-html-constant "\\(&#?[a-z0-9]\\{2,5\\};\\)")
(defconst tpl-mode-pair-tag
(concat
"\\<"
(regexp-opt
'("a" "abbr" "acronym" "address" "applet" "area" "b" "bdo"
"big" "blockquote" "body" "button" "caption" "center" "cite"
"code" "col" "colgroup" "dd" "del" "dfn" "dif" "div" "dl"
"dt" "em" "fieldset" "font" "form" "frame" "frameset" "h1"
"h2" "h3" "h4" "h5" "h6" "head" "html" "i" "iframe" "ins"
"kbd" "label" "legend" "li" "link" "map" "menu" "noframes"
"noscript" "object" "ol" "optgroup" "option" "p" "pre" "q"
"s" "samp" "script" "select" "small" "span" "strike"
"strong" "style" "sub" "sup" "table" "tbody" "td" "textarea"
"tfoot" "th" "thead" "title" "tr" "tt" "u" "ul" "var")
t)
"\\>"))
(defconst tpl-mode-standalone-tag
(concat
"\\<"
(regexp-opt
'("base" "br" "hr" "img" "input" "meta" "param")
t)
"\\>"))
(defconst tpl-mode-open-tag (concat "<\\("
tpl-mode-pair-tag
"\\)"))
(defconst tpl-mode-close-tag (concat "</\\("
tpl-mode-pair-tag
"\\)>"))
(defconst tpl-mode-close-tag-at-start (concat "^[ \t]*?"
tpl-mode-close-tag))
(defconst tpl-mode-blank-line "^[ \t]*?$")
(defconst tpl-mode-dangling-open (concat "\\("
tpl-mode-open-section
"\\)\\|\\("
tpl-mode-open-tag
"\\)[^/]*$"))
(defun tpl-indent-command ()
"Command for indenting text. Just calls tpl-indent."
(interactive)
(tpl-indent))
;; Function to control indenting.
(defun tpl-indent ()
"Indent current line"
;; Set the point to beginning of line.
(beginning-of-line)
;; If we are at the beginning of the file, indent to 0.
(if (bobp)
(indent-line-to 0)
(let ((tag-stack 1) (close-tag "") (cur-indent 0) (old-pnt (point-marker))
(close-at-start) (open-token) (dangling-open))
(progn
;; Determine if this is a template line or an html line.
(if (looking-at "^[ \t]*?{{")
(setq close-at-start tpl-mode-close-section-at-start
open-token "{{#")
(setq close-at-start tpl-mode-close-tag-at-start
open-token "<")
)
;; If there is a closing tag at the start of the line, search back
;; for its opener and indent to that level.
(if (looking-at close-at-start)
(progn
(save-excursion
(setq close-tag (match-string 1))
;; Keep searching for a match for the close tag until
;; the tag-stack is 0.
(while (and (not (bobp))
(> tag-stack 0)
(re-search-backward (concat open-token
"\\(/?\\)"
close-tag) nil t))
(if (string-equal (match-string 1) "/")
;; We found another close tag, so increment tag-stack.
(setq tag-stack (+ tag-stack 1))
;; We found an open tag, so decrement tag-stack.
(setq tag-stack (- tag-stack 1))
)
(setq cur-indent (current-indentation))
)
)
(if (> tag-stack 0)
(save-excursion
(forward-line -1)
(setq cur-indent (current-indentation))
)
)
)
;; This was not a closing tag, so we check if the previous line
;; was an opening tag.
(save-excursion
;; Keep moving back until we find a line that is not blank
(while (progn
(forward-line -1)
(and (not (bobp)) (looking-at tpl-mode-blank-line))
)
)
(setq cur-indent (current-indentation))
(if (re-search-forward tpl-mode-dangling-open old-pnt t)
(setq cur-indent (+ cur-indent tpl-basic-offset))
)
)
)
;; Finally, we execute the actual indentation.
(if (> cur-indent 0)
(indent-line-to cur-indent)
(indent-line-to 0)
)
)
)
)
)
;; controls highlighting
(defconst tpl-mode-font-lock-keywords
(list
(list tpl-mode-section
'(1 font-lock-keyword-face))
(list tpl-mode-comment
'(1 font-lock-comment-face))
(list tpl-mode-include
'(1 font-lock-builtin-face))
(list tpl-mode-builtins
'(1 font-lock-variable-name-face))
(list tpl-mode-variable
'(1 font-lock-reference-face))
(list (concat "</?\\(" tpl-mode-pair-tag "\\)")
'(1 font-lock-function-name-face))
(list (concat "<\\(" tpl-mode-standalone-tag "\\)")
'(1 font-lock-function-name-face))
(list tpl-mode-html-constant
'(1 font-lock-variable-name-face))
))
(put 'tpl-mode 'font-lock-defaults '(tpl-font-lock-keywords nil t))
(defun tpl-mode ()
"Major mode for editing Google CTemplate file."
(interactive)
(kill-all-local-variables)
(use-local-map tpl-mode-map)
(setq major-mode 'tpl-mode)
(setq mode-name "tpl-mode")
(setq local-abbrev-table tpl-mode-abbrev-table)
(setq indent-tabs-mode nil)
(set-syntax-table tpl-mode-syntax-table)
; show trailing whitespace, but only when the user can fix it
(setq show-trailing-whitespace (not buffer-read-only))
(make-local-variable 'indent-line-function)
(setq indent-line-function 'tpl-indent)
(setq font-lock-defaults '(tpl-mode-font-lock-keywords))
(run-hooks 'tpl-mode-hook)
)
(provide 'tpl-mode)

View File

@ -234,7 +234,10 @@ HREF="#modifiers">modifiers</A>. In that case, the template-system
starts by finding the appropriate value for that variable in the
dictionary, just like normal. Then it applies each modifier to the
variable, left to right. Finally, it emits the modified value to the
output.</p>
output. Template-includes can have modifiers in a similar way. In
such cases, after the sub-template is expanded, but before its content
is injected into the current template, it has the modifiers
applied.</p>
<p>If no dictionary key is found for a given template marker, then the
template marker is ignored: if a variable, it expands to the empty
@ -252,7 +255,7 @@ syntax error for any template marker to violate this rule.</p>
whatsoever, including (single) curly braces and NUL characters.</p>
<h3><A NAME="modifiers">Variable Modifiers</A></h3>
<h3><A NAME="modifiers">Modifiers</A></h3>
<p>Recall that variables look like this: <code>{{VARNAME}}</code>. We
actually allow a more generic form: the variable name may be followed
@ -270,6 +273,11 @@ this:</p>
dictionary to be <code>Jim &amp; Bob</code>, what will actually be
emitted in the template is <code>Jim &amp;amp; Bob</code>.</p>
<p>Modifiers work for variable names and also for template-includes:
<code>{{>SUB_TEMPLATE:html_escape}}</code> means that when you expand
<code>SUB_TEMPLATE</code>, html-escape the expanded text before
inserting it into the current template.</p>
<p>You can chain modifiers together. This template first html-escapes
<code>NAME</code>, and then javascript-escapes that result:</p>
<pre>

6397
trunk/libtool.m4 vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -54,7 +54,7 @@ rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root)
%doc AUTHORS COPYING ChangeLog INSTALL NEWS README doc/designstyle.css doc/index.html doc/howto.html doc/tips.html doc/example.html
%doc AUTHORS COPYING ChangeLog INSTALL NEWS README doc/designstyle.css doc/index.html doc/howto.html doc/tips.html doc/example.html contrib/README.contrib contrib/highlighting.vim contrib/tpl-mode.el
%{prefix}/lib/libctemplate.so.0
%{prefix}/lib/libctemplate.so.0.0.0

View File

@ -210,6 +210,10 @@ class TemplateDictionary {
struct JavascriptEscape { std::string operator()(const std::string&) const; };
static JavascriptEscape javascript_escape;
// Escapes " \ / <FF> <CR> <LF> <BS> <TAB> to \" \\ \/ \f \r \n \b \t
struct JsonEscape { std::string operator()(const std::string&) const; };
static JsonEscape json_escape;
// --- DEBUGGING TOOLS

View File

@ -337,6 +337,14 @@ struct Token {
}
};
// This applies the modifiers to a string, modifying the string in place
static void ModifyString(const ModifierAndNonces& modifiers, string* s) {
for (ModifierAndNonces::const_iterator it = modifiers.begin();
it != modifiers.end(); ++it) {
*s = (*it->first)(*s, it->second);
}
}
}; // anonymous namespace
// ----------------------------------------------------------------------
@ -538,10 +546,7 @@ void VariableTemplateNode::Expand(ExpandEmitter *output_buffer,
output_buffer->Emit(value); // so just emit it
} else {
string modified_value(value);
for (ModifierAndNonces::const_iterator it = token_.modifier_plus_values.begin();
it != token_.modifier_plus_values.end(); ++it) {
modified_value = (*it->first)(modified_value, it->second);
}
ModifyString(token_.modifier_plus_values, &modified_value);
output_buffer->Emit(modified_value);
}
@ -644,10 +649,22 @@ void TemplateTemplateNode::Expand(ExpandEmitter *output_buffer,
// sub-dictionary NULL means 'just use the current dictionary instead'.
// We force children to annotate the output if we have to.
included_template->Expand(output_buffer,
*dv_iter ? *dv_iter : dictionary,
ShouldAnnotateOutput(dictionary, force_annotate));
// If the include-template has modifiers, we need to expand to a string,
// modify the string, and append to output_buffer. Otherwise (common
// case), we can just expand into the output-buffer directly.
if (token_.modifier_plus_values.empty()) { // no need to modify sub-template
included_template->Expand(output_buffer,
*dv_iter ? *dv_iter : dictionary,
ShouldAnnotateOutput(dictionary, force_annotate));
} else {
string sub_template;
StringEmitter subtemplate_buffer(&sub_template);
included_template->Expand(&subtemplate_buffer,
*dv_iter ? *dv_iter : dictionary,
ShouldAnnotateOutput(dictionary, force_annotate));
ModifyString(token_.modifier_plus_values, &sub_template);
output_buffer->Emit(sub_template);
}
if (ShouldAnnotateOutput(dictionary, force_annotate)) {
output_buffer->Emit(CloseAnnotation("INC"));
}
@ -1123,11 +1140,13 @@ Token SectionTemplateNode::GetNextToken(Template *my_template) {
g_modifiers[mod_index].modifier, value_string));
}
// For now, we only allow variable nodes to have modifiers
// TODO(csilvers): figure out what they mean for sections/includes
if (!modifiers.empty() && ttype != TOKENTYPE_VARIABLE) {
// For now, we only allow variable and include nodes to have modifiers.
// TODO(csilvers): figure out if it's useful to have to for sections
if (!modifiers.empty() &&
ttype != TOKENTYPE_VARIABLE && ttype != TOKENTYPE_TEMPLATE) {
FAIL(string(token_start, token_end - token_start)
<< "malformed: only variables are allowed to have modifiers");
<< "malformed: only variables and template-includes "
<< "are allowed to have modifiers");
}
// Whew! We passed the guantlet. Get ready for the next token

View File

@ -90,6 +90,7 @@ static StaticMutexInit g_static_mutex_initializer; // constructs early
/*static*/ TemplateDictionary::HtmlEscape TemplateDictionary::html_escape;
/*static*/ TemplateDictionary::XmlEscape TemplateDictionary::xml_escape;
/*static*/ TemplateDictionary::JavascriptEscape TemplateDictionary::javascript_escape;
/*static*/ TemplateDictionary::JsonEscape TemplateDictionary::json_escape;
// ----------------------------------------------------------------------
@ -804,4 +805,25 @@ string TemplateDictionary::JavascriptEscape::operator()(const string& in) const
return out;
}
// Escapes " / \ <BS> <FF> <CR> <LF> <TAB> to \" \/ \\ \b \f \r \n \t
string TemplateDictionary::JsonEscape::operator()(const string& in) const {
string out;
// we'll reserve some space in out to account for minimal escaping: say 1.5%
out.reserve(in.size() + in.size()/64 + 2);
for (int i = 0; i < in.length(); ++i) {
switch (in[i]) {
case '"': out += "\\\""; break;
case '\\': out += "\\\\"; break;
case '/': out += "\\/"; break;
case '\b': out += "\\b"; break;
case '\f': out += "\\f"; break;
case '\n': out += "\\n"; break;
case '\r': out += "\\r"; break;
case '\t': out += "\\t"; break;
default: out += in[i];
}
}
return out;
}
_END_GOOGLE_NAMESPACE_

View File

@ -207,6 +207,13 @@ class TemplateDictionaryUnittest {
dict.SetEscapedValue("hardest JS",
("f = 'foo';\r\n\tprint \"\\&foo = \b\", \"foo\""),
TemplateDictionary::javascript_escape);
dict.SetEscapedValue("easy JSON", "joo",
TemplateDictionary::json_escape);
dict.SetEscapedValue("harder JSON", "f = \"joo\"; e = 'joo';",
TemplateDictionary::json_escape);
dict.SetEscapedValue("hardest JSON",
("f = 'foo';\r\n\t\fprint \"\\&foo = /\b\", \"foo\""),
TemplateDictionary::json_escape);
FooEscaper foo_escaper;
dict.SetEscapedValue("easy foo", "hello there!",
FooEscaper());
@ -233,6 +240,10 @@ class TemplateDictionaryUnittest {
ASSERT_STREQ(dict.GetSectionValue("harder JS"), "f = \\'joo\\';");
ASSERT_STREQ(dict.GetSectionValue("hardest JS"),
"f = \\'foo\\';\\r\\n\tprint \\\"\\\\&foo = \\b\\\", \\\"foo\\\"");
ASSERT_STREQ(dict.GetSectionValue("easy JSON"), "joo");
ASSERT_STREQ(dict.GetSectionValue("harder JSON"), "f = \\\"joo\\\"; e = 'joo';");
ASSERT_STREQ(dict.GetSectionValue("hardest JSON"),
"f = 'foo';\\r\\n\\t\\fprint \\\"\\\\&foo = \\/\\b\\\", \\\"foo\\\"");
ASSERT_STREQ(dict.GetSectionValue("easy foo"), "foo");
ASSERT_STREQ(dict.GetSectionValue("harder foo"), "foo");
ASSERT_STREQ(dict.GetSectionValue("easy double"), "doo");

View File

@ -294,6 +294,12 @@ static TemplateDictionary* MakeDict1() {
TemplateDictionary* footer_dict = dict->AddIncludeDictionary("FOOTER");
footer_dict->SetFilename("template_unittest_test_footer.in");
// --- These are used by template_unittest_test_modifiers.in
// UPDATE and UPDATE_SECTION we inherit from test_html.in
TemplateDictionary* inc_simple = dict->AddIncludeDictionary("SIMPLE");
inc_simple->SetFilename("template_unittest_test_simple.in");
return dict;
}

View File

@ -248,11 +248,9 @@ class TemplateUnittest {
tpl = StringToTemplate("hi {{VAR:html_escape=yes}} lo", STRIP_WHITESPACE);
ASSERT(tpl == NULL);
// Check we don't allow modifiers on sections or include-templates
// Check we don't allow modifiers on sections
tpl = StringToTemplate("hi {{#VAR:h}} lo {{/VAR}}", STRIP_WHITESPACE);
ASSERT(tpl == NULL);
tpl = StringToTemplate("hi {{>VAR:html_escape}} lo", STRIP_WHITESPACE);
ASSERT(tpl == NULL);
}
static void TestSection() {
@ -307,6 +305,25 @@ class TemplateUnittest {
AssertExpandIs(tpl2, &dict, "hi include file\ninclude file\ninc2\n bar");
}
static void TestIncludeWithModifiers() {
string incname = StringToTemplateFile("include & print file\n");
string incname2 = StringToTemplateFile("inc2\n");
// Note this also tests that html-escape, but not javascript-escape,
// escapes \n to <space>
Template* tpl1 = StringToTemplate("hi {{>INC:h}} bar\n", DO_NOT_STRIP);
Template* tpl2 = StringToTemplate("hi {{>INC:javascript_escape}} bar\n",
DO_NOT_STRIP);
TemplateDictionary dict("dict");
AssertExpandIs(tpl1, &dict, "hi bar\n");
dict.AddIncludeDictionary("INC")->SetFilename(incname);
AssertExpandIs(tpl1, &dict, "hi include &amp; print file bar\n");
dict.AddIncludeDictionary("INC")->SetFilename(incname2);
AssertExpandIs(tpl1, &dict, "hi include &amp; print file inc2 bar\n");
AssertExpandIs(tpl2, &dict, "hi include & print file\\ninc2\\n bar\n");
// Don't test modifier syntax here; that's in TestVariableWithModifiers()
}
// Tests that vars inherit/override their parents properly
static void TestInheritence() {
Template* tpl = StringToTemplate("{{FOO}}{{#SEC}}{{FOO}}{{#SEC}}{{FOO}}{{/SEC}}{{/SEC}}",
@ -353,6 +370,8 @@ class TemplateUnittest {
}
// Tests annotation, in particular inheriting annotation among children
// This should be called first, so the filenames don't change as we add
// more tests.
static void TestAnnotation() {
string incname = StringToTemplateFile("include {{#ISEC}}file{{/ISEC}}\n");
string incname2 = StringToTemplateFile("include #2\n");
@ -369,11 +388,11 @@ class TemplateUnittest {
dict.SetAnnotateOutput("");
char expected[10240]; // 10k should be big enough!
snprintf(expected, sizeof(expected),
"{{#FILE=%s/template.034}}{{#SEC=__MAIN__}}boo!\n"
"{{#INC=INC}}{{#FILE=%s/template.032}}"
"{{#FILE=%s/template.003}}{{#SEC=__MAIN__}}boo!\n"
"{{#INC=INC}}{{#FILE=%s/template.001}}"
"{{#SEC=__MAIN__}}include {{#SEC=ISEC}}file{{/SEC}}\n"
"{{/SEC}}{{/FILE}}{{/INC}}"
"{{#INC=INC}}{{#FILE=%s/template.033}}"
"{{#INC=INC}}{{#FILE=%s/template.002}}"
"{{#SEC=__MAIN__}}include #2\n{{/SEC}}{{/FILE}}{{/INC}}"
"\nhi {{#SEC=SEC}}lo{{/SEC}} bar{{/SEC}}{{/FILE}}",
FLAGS_test_tmpdir.c_str(), FLAGS_test_tmpdir.c_str(),
@ -382,11 +401,11 @@ class TemplateUnittest {
dict.SetAnnotateOutput("/template.");
AssertExpandIs(tpl, &dict,
"{{#FILE=/template.034}}{{#SEC=__MAIN__}}boo!\n"
"{{#INC=INC}}{{#FILE=/template.032}}"
"{{#FILE=/template.003}}{{#SEC=__MAIN__}}boo!\n"
"{{#INC=INC}}{{#FILE=/template.001}}"
"{{#SEC=__MAIN__}}include {{#SEC=ISEC}}file{{/SEC}}\n"
"{{/SEC}}{{/FILE}}{{/INC}}"
"{{#INC=INC}}{{#FILE=/template.033}}"
"{{#INC=INC}}{{#FILE=/template.002}}"
"{{#SEC=__MAIN__}}include #2\n{{/SEC}}{{/FILE}}{{/INC}}"
"\nhi {{#SEC=SEC}}lo{{/SEC}} bar{{/SEC}}{{/FILE}}");
@ -671,13 +690,16 @@ class TemplateUnittest {
int main(int argc, char** argv) {
CleanTestDir(FLAGS_test_tmpdir);
// This goes first so that future tests don't mess up the filenames
TemplateUnittest::TestAnnotation();
TemplateUnittest::TestVariable();
TemplateUnittest::TestVariableWithModifiers();
TemplateUnittest::TestSection();
TemplateUnittest::TestInclude();
TemplateUnittest::TestIncludeWithModifiers();
TemplateUnittest::TestInheritence();
TemplateUnittest::TestExpand();
TemplateUnittest::TestAnnotation();
TemplateUnittest::TestGetTemplate();
TemplateUnittest::TestStrip();

View File

@ -8,5 +8,7 @@
{{! There should be no problem with this comment having a : in it. }}
<IMG src=foo.jpg align={{ALIGNMENT}}>
<IMG src="mouseover() {img=\'foo.jpg\' align={{ALIGNMENT:j}}}">
{{>SIMPLE:html_escape}}
</body>
</html>{{BI_NEWLINE}}

View File

@ -1 +1 @@
<html><body>monday &amp; tuesdaymonday &amp;amp; tuesdaymonday &amp; tuesday<IMG src=foo.jpg align="right"><IMG src="mouseover() {img=\'foo.jpg\' align=\"right\"}"></body></html>
<html><body>monday &amp; tuesdaymonday &amp;amp; tuesdaymonday &amp; tuesday<IMG src=foo.jpg align="right"><IMG src="mouseover() {img=\'foo.jpg\' align=\"right\"}">&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;/body&gt;&lt;/html&gt; </body></html>