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

Mon May 14 17:27:10 2007 Google Inc. <opensource@google.com>

* ctemplate: version 0.5 release
	* Add new MakeCopy() method to copy template dictionaries (csilvers)
	* Add JSON-escaping (mikepurvis)
	* Internal change that should ease thread-handling a bit (csilvers)
	* Fix url_query_escape to avoid stack overflow (csilvers)
This commit is contained in:
csilvers 2007-05-15 06:31:08 +00:00
parent 73dd30e487
commit 422d6f7443
27 changed files with 4185 additions and 9392 deletions

View File

@ -33,3 +33,11 @@ Mon Jan 15 14:10:42 2007 Google Inc. <opensource@google.com>
* Add support for "pre" escaping, which preserves whitespace (dboswell)
* Typo fixes in documentation (csilvers)
* Expand() returns false if a template file failed to load (jmittleman)
Mon May 14 17:27:10 2007 Google Inc. <opensource@google.com>
* ctemplate: version 0.5 release
* Add new MakeCopy() method to copy template dictionaries (csilvers)
* Add JSON-escaping (mikepurvis)
* Internal change that should ease thread-handling a bit (csilvers)
* Fix url_query_escape to avoid stack overflow (csilvers)

View File

@ -62,13 +62,16 @@ noinst_SCRIPTS = src/tests/make_tpl_varnames_h_unittest.sh
## vvvv RULES TO MAKE THE LIBRARIES, BINARIES, AND UNITTESTS
# These are the symbols (classes, mostly) we want to export from our library
CTEMPLATE_SYMBOLS = '[^A-Za-z](Template|TemplateDictionary|TemplateNamelist|TemplateFromString|TemplateString|TemplateState|Strip)[^A-Za-z]'
lib_LTLIBRARIES += libctemplate.la
libctemplate_la_SOURCES = $(googleinclude_HEADERS) src/config.h \
src/base/arena.h src/base/arena.cc \
src/base/arena.h src/base/arena.cc src/base/mutex.h src/base/mutex.cc \
src/template.cc src/template_dictionary.cc src/template_namelist.cc \
src/template_from_string.cc
libctemplate_la_CXXFLAGS = $(PTHREAD_CFLAGS) -DNDEBUG
libctemplate_la_LDFLAGS = $(PTHREAD_CFLAGS)
libctemplate_la_LDFLAGS = $(PTHREAD_CFLAGS) -export-symbols-regex $(CTEMPLATE_SYMBOLS)
libctemplate_la_LIBADD = $(PTHREAD_LIBS)
# automake will make different .o files for this library, which is good,
@ -76,6 +79,7 @@ libctemplate_la_LIBADD = $(PTHREAD_LIBS)
lib_LTLIBRARIES += libctemplate_nothreads.la
libctemplate_nothreads_la_SOURCES = $(libctemplate_la_SOURCES)
libctemplate_nothreads_la_CXXFLAGS = -DNDEBUG -DNO_THREADS
libctemplate_nothreads_la_LDFLAGS = -export-symbols-regex $(CTEMPLATE_SYMBOLS)
# Helper apps
bin_PROGRAMS += make_tpl_varnames_h

File diff suppressed because it is too large Load Diff

8000
trunk/aclocal.m4 vendored

File diff suppressed because it is too large Load Diff

947
trunk/config.guess vendored

File diff suppressed because it is too large Load Diff

189
trunk/config.sub vendored
View File

@ -1,9 +1,9 @@
#! /bin/sh
# Configuration validation subroutine script.
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
# 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
timestamp='2003-06-18'
timestamp='2005-07-08'
# This file is (in principle) common to ALL GNU software.
# The presence of a machine in this file suggests that SOME GNU software
@ -21,14 +21,15 @@ timestamp='2003-06-18'
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# Please send patches to <config-patches@gnu.org>. Submit a context
# diff and a properly formatted ChangeLog entry.
#
@ -70,7 +71,7 @@ Report bugs and patches to <config-patches@gnu.org>."
version="\
GNU config.sub ($timestamp)
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
@ -83,11 +84,11 @@ Try \`$me --help' for more information."
while test $# -gt 0 ; do
case $1 in
--time-stamp | --time* | -t )
echo "$timestamp" ; exit 0 ;;
echo "$timestamp" ; exit ;;
--version | -v )
echo "$version" ; exit 0 ;;
echo "$version" ; exit ;;
--help | --h* | -h )
echo "$usage"; exit 0 ;;
echo "$usage"; exit ;;
-- ) # Stop option processing
shift; break ;;
- ) # Use stdin as input.
@ -99,7 +100,7 @@ while test $# -gt 0 ; do
*local*)
# First pass through any local machine types.
echo $1
exit 0;;
exit ;;
* )
break ;;
@ -118,7 +119,8 @@ esac
# Here we must recognize all the valid KERNEL-OS combinations.
maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
case $maybe_os in
nto-qnx* | linux-gnu* | freebsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \
kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
os=-$maybe_os
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
;;
@ -144,7 +146,7 @@ case $os in
-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
-apple | -axis)
-apple | -axis | -knuth | -cray)
os=
basic_machine=$1
;;
@ -228,14 +230,16 @@ case $basic_machine in
| a29k \
| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
| am33_2.0 \
| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
| bfin \
| c4x | clipper \
| d10v | d30v | dlx | dsp16xx \
| fr30 | frv \
| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
| i370 | i860 | i960 | ia64 \
| ip2k \
| m32r | m68000 | m68k | m88k | mcore \
| ip2k | iq2000 \
| m32r | m32rle | m68000 | m68k | m88k | maxq | mcore \
| mips | mipsbe | mipseb | mipsel | mipsle \
| mips16 \
| mips64 | mips64el \
@ -244,31 +248,37 @@ case $basic_machine in
| mips64vr4100 | mips64vr4100el \
| mips64vr4300 | mips64vr4300el \
| mips64vr5000 | mips64vr5000el \
| mips64vr5900 | mips64vr5900el \
| mipsisa32 | mipsisa32el \
| mipsisa32r2 | mipsisa32r2el \
| mipsisa64 | mipsisa64el \
| mipsisa64r2 | mipsisa64r2el \
| mipsisa64sb1 | mipsisa64sb1el \
| mipsisa64sr71k | mipsisa64sr71kel \
| mipstx39 | mipstx39el \
| mn10200 | mn10300 \
| ms1 \
| msp430 \
| ns16k | ns32k \
| openrisc | or32 \
| or32 \
| pdp10 | pdp11 | pj | pjl \
| powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
| pyramid \
| s390 | s390x \
| sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
| sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
| sh64 | sh64le \
| sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv9 | sparcv9b \
| sparc | sparc64 | sparc64b | sparc86x | sparclet | sparclite \
| sparcv8 | sparcv9 | sparcv9b \
| strongarm \
| tahoe | thumb | tic4x | tic80 | tron \
| v850 | v850e \
| we32k \
| x86 | xscale | xstormy16 | xtensa \
| x86 | xscale | xscalee[bl] | xstormy16 | xtensa \
| z8k)
basic_machine=$basic_machine-unknown
;;
m32c)
basic_machine=$basic_machine-unknown
;;
m6811 | m68hc11 | m6812 | m68hc12)
# Motorola 68HC11/12.
basic_machine=$basic_machine-unknown
@ -296,19 +306,19 @@ case $basic_machine in
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
| arm-* | armbe-* | armle-* | armeb-* | armv*-* \
| avr-* \
| bs2000-* \
| bfin-* | bs2000-* \
| c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
| clipper-* | cydra-* \
| clipper-* | craynv-* | cydra-* \
| d10v-* | d30v-* | dlx-* \
| elxsi-* \
| f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
| h8300-* | h8500-* \
| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
| i*86-* | i860-* | i960-* | ia64-* \
| ip2k-* \
| m32r-* \
| ip2k-* | iq2000-* \
| m32r-* | m32rle-* \
| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
| m88110-* | m88k-* | mcore-* \
| m88110-* | m88k-* | maxq-* | mcore-* \
| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
| mips16-* \
| mips64-* | mips64el-* \
@ -317,34 +327,40 @@ case $basic_machine in
| mips64vr4100-* | mips64vr4100el-* \
| mips64vr4300-* | mips64vr4300el-* \
| mips64vr5000-* | mips64vr5000el-* \
| mips64vr5900-* | mips64vr5900el-* \
| mipsisa32-* | mipsisa32el-* \
| mipsisa32r2-* | mipsisa32r2el-* \
| mipsisa64-* | mipsisa64el-* \
| mipsisa64r2-* | mipsisa64r2el-* \
| mipsisa64sb1-* | mipsisa64sb1el-* \
| mipsisa64sr71k-* | mipsisa64sr71kel-* \
| mipstx39-* | mipstx39el-* \
| mmix-* \
| ms1-* \
| msp430-* \
| none-* | np1-* | nv1-* | ns16k-* | ns32k-* \
| none-* | np1-* | ns16k-* | ns32k-* \
| orion-* \
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
| pyramid-* \
| romp-* | rs6000-* \
| s390-* | s390x-* \
| sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \
| sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | shbe-* \
| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
| sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \
| sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
| sparc-* | sparc64-* | sparc64b-* | sparc86x-* | sparclet-* \
| sparclite-* \
| sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
| tahoe-* | thumb-* \
| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
| tron-* \
| v850-* | v850e-* | vax-* \
| we32k-* \
| x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \
| xtensa-* \
| x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \
| xstormy16-* | xtensa-* \
| ymp-* \
| z8k-*)
;;
m32c-*)
;;
# Recognize the various machine names and aliases which stand
# for a CPU type and a company and sometimes even an OS.
386bsd)
@ -361,6 +377,9 @@ case $basic_machine in
basic_machine=a29k-amd
os=-udi
;;
abacus)
basic_machine=abacus-unknown
;;
adobe68k)
basic_machine=m68010-adobe
os=-scout
@ -378,6 +397,9 @@ case $basic_machine in
amd64)
basic_machine=x86_64-pc
;;
amd64-*)
basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
amdahl)
basic_machine=580-amdahl
os=-sysv
@ -437,12 +459,27 @@ case $basic_machine in
basic_machine=j90-cray
os=-unicos
;;
craynv)
basic_machine=craynv-cray
os=-unicosmp
;;
cr16c)
basic_machine=cr16c-unknown
os=-elf
;;
crds | unos)
basic_machine=m68k-crds
;;
crisv32 | crisv32-* | etraxfs*)
basic_machine=crisv32-axis
;;
cris | cris-* | etrax*)
basic_machine=cris-axis
;;
crx)
basic_machine=crx-unknown
os=-elf
;;
da30 | da30-*)
basic_machine=m68k-da30
;;
@ -465,6 +502,10 @@ case $basic_machine in
basic_machine=m88k-motorola
os=-sysv3
;;
djgpp)
basic_machine=i586-pc
os=-msdosdjgpp
;;
dpx20 | dpx20-*)
basic_machine=rs6000-bull
os=-bosx
@ -643,10 +684,6 @@ case $basic_machine in
mips3*)
basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
;;
mmix*)
basic_machine=mmix-knuth
os=-mmixware
;;
monitor)
basic_machine=m68k-rom68k
os=-coff
@ -727,10 +764,6 @@ case $basic_machine in
np1)
basic_machine=np1-gould
;;
nv1)
basic_machine=nv1-cray
os=-unicosmp
;;
nsr-tandem)
basic_machine=nsr-tandem
;;
@ -738,9 +771,12 @@ case $basic_machine in
basic_machine=hppa1.1-oki
os=-proelf
;;
or32 | or32-*)
openrisc | openrisc-*)
basic_machine=or32-unknown
os=-coff
;;
os400)
basic_machine=powerpc-ibm
os=-os400
;;
OSE68000 | ose68000)
basic_machine=m68000-ericsson
@ -833,6 +869,12 @@ case $basic_machine in
rtpc | rtpc-*)
basic_machine=romp-ibm
;;
s390 | s390-*)
basic_machine=s390-ibm
;;
s390x | s390x-*)
basic_machine=s390x-ibm
;;
sa29200)
basic_machine=a29k-amd
os=-udi
@ -956,6 +998,10 @@ case $basic_machine in
tower | tower-32)
basic_machine=m68k-ncr
;;
tpf)
basic_machine=s390x-ibm
os=-tpf
;;
udi29k)
basic_machine=a29k-amd
os=-udi
@ -999,6 +1045,10 @@ case $basic_machine in
basic_machine=hppa1.1-winbond
os=-proelf
;;
xbox)
basic_machine=i686-pc
os=-mingw32
;;
xps | xps100)
basic_machine=xps100-honeywell
;;
@ -1029,6 +1079,9 @@ case $basic_machine in
romp)
basic_machine=romp-ibm
;;
mmix)
basic_machine=mmix-knuth
;;
rs6000)
basic_machine=rs6000-ibm
;;
@ -1045,13 +1098,10 @@ case $basic_machine in
we32k)
basic_machine=we32k-att
;;
sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele)
sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
basic_machine=sh-unknown
;;
sh64)
basic_machine=sh64-unknown
;;
sparc | sparcv9 | sparcv9b)
sparc | sparcv8 | sparcv9 | sparcv9b)
basic_machine=sparc-sun
;;
cydra)
@ -1124,19 +1174,21 @@ case $os in
| -aos* \
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
| -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
| -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
| -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
| -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \
| -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
| -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
| -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
| -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
| -chorusos* | -chorusrdb* \
| -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
| -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
| -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
| -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
| -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
| -powermax* | -dnix* | -nx6 | -nx7 | -sei*)
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
| -skyos* | -haiku*)
# Remember, each alternative MUST END IN *, to match a version number.
;;
-qnx*)
@ -1154,12 +1206,15 @@ case $os in
os=`echo $os | sed -e 's|nto|nto-qnx|'`
;;
-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
| -windows* | -osx | -abug | -netware* | -os9* | -beos* \
| -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
| -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
;;
-mac*)
os=`echo $os | sed -e 's|mac|macos|'`
;;
-linux-dietlibc)
os=-linux-dietlibc
;;
-linux*)
os=`echo $os | sed -e 's|linux|linux-gnu|'`
;;
@ -1172,6 +1227,9 @@ case $os in
-opened*)
os=-openedition
;;
-os400*)
os=-os400
;;
-wince*)
os=-wince
;;
@ -1193,6 +1251,9 @@ case $os in
-atheos*)
os=-atheos
;;
-syllable*)
os=-syllable
;;
-386bsd)
os=-bsd
;;
@ -1215,6 +1276,9 @@ case $os in
-sinix*)
os=-sysv4
;;
-tpf*)
os=-tpf
;;
-triton*)
os=-sysv3
;;
@ -1251,6 +1315,9 @@ case $os in
-kaos*)
os=-kaos
;;
-zvmoe)
os=-zvmoe
;;
-none)
;;
*)
@ -1282,9 +1349,9 @@ case $basic_machine in
arm*-semi)
os=-aout
;;
c4x-* | tic4x-*)
os=-coff
;;
c4x-* | tic4x-*)
os=-coff
;;
# This must come before the *-dec entry.
pdp10-*)
os=-tops20
@ -1328,9 +1395,15 @@ case $basic_machine in
*-be)
os=-beos
;;
*-haiku)
os=-haiku
;;
*-ibm)
os=-aix
;;
*-knuth)
os=-mmixware
;;
*-wec)
os=-proelf
;;
@ -1463,9 +1536,15 @@ case $basic_machine in
-mvs* | -opened*)
vendor=ibm
;;
-os400*)
vendor=ibm
;;
-ptx*)
vendor=sequent
;;
-tpf*)
vendor=ibm
;;
-vxsim* | -vxworks* | -windiss*)
vendor=wrs
;;
@ -1490,7 +1569,7 @@ case $basic_machine in
esac
echo $basic_machine$os
exit 0
exit
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)

2284
trunk/configure vendored

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
# make sure we're interpreted by some minimal autoconf
AC_PREREQ(2.57)
AC_INIT(ctemplate, 0.4, opensource@google.com)
AC_INIT(ctemplate, 0.5, 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

@ -1,7 +1,3 @@
/* This file is duplicated at http://www.corp.google.com/css/designstyle.css
* If you change this file, copy it to ~web/html/css/designstyle.css
*/
body {
background-color: #ffffff;
color: black;

View File

@ -319,8 +319,19 @@ are the modifiers that are supported:</p>
<td>javascript-escapes the variable before output
(eg <code>"</code> -> <code>\"</code>)</td>
</tr>
<tr><td><code>:json_escape</code></td><td><code>:o</code></td>
<td>json-escapes a variable before output as a string in json;
similar to javascript escaping, but ignores characters such
as <code>=</code> and <code>&</code>.</td>
</tr>
</table>
<p><strong>NOTE:</strong> At the moment, there are no filters for handling XML
attributes and text nodes. For HTML snippets, use the html filter; in other
situations, it may be appropriate to use CDATA blocks.</p>
<h3> <A NAME="inheritence">Details on Dictionary Lookup</A> </h3>
@ -742,6 +753,24 @@ using a normal, file-based template, and then switch to
template-from-string later if you so desire.</p>
<h3> Copying a Template Dictionary </h3>
<p>You can use the <code>MakeCopy()</code> method on a template
dictionary to make a "deep" copy of the template. This can be useful
for situations like the following: you want to fill a template several
times, each time with 90% of the values the same, but the last 10%
different. Computing the values is slow. Here's how you can use
<code>MakeCopy()</code> to do it:</p>
<ol>
<li> fill dict with 90%
<li> <code>newdict1 = dict->MakeCopy();</code>
<li> fill newdict1 with last 10%
<li> <code>newdict2 = dict->MakeCopy();</code>
<li> fill newdict2 with last 10%
<li> etc.
</ol>
<h2>Security Considerations</h2>
<p>Like all web applications, programs that use the Google Template System

View File

@ -1,3 +1,27 @@
ctemplate (0.5) unstable; urgency=low
* New upstream release.
-- Google Inc. <opensource@google.com> Mon, 14 May 2007 17:27:10 -0700
ctemplate (0.4) unstable; urgency=low
* New upstream release.
-- Google Inc. <opensource@google.com> Mon, 15 Jan 2007 14:10:42 -0800
ctemplate (0.3) unstable; urgency=low
* New upstream release.
-- Google Inc. <opensource@google.com> Mon, 21 Aug 2006 17:44:32 -0700
ctemplate (0.2) unstable; urgency=low
* New upstream release.
-- Google Inc. <opensource@google.com> Wed, 14 Jun 2006 14:56:04 -0700
ctemplate (0.1-1) unstable; urgency=low
* Initial release.

View File

@ -10,18 +10,18 @@ Section: libdevel
Architecture: any
Depends: libctemplate0 (= ${Source-Version})
Description: This package contains a library implementing a simple but
powerful template language for C++. It emphasizes separating logic
from presentation: it is impossible to embed application logic in this
template language. The devel package contains static and debug
libraries and header files for developing applications that use the
ctemplate package.
powerful template language for C++. It emphasizes separating logic
from presentation: it is impossible to embed application logic in this
template language. The devel package contains static and debug
libraries and header files for developing applications that use the
ctemplate package.
Package: libctemplate0
Section: libs
Architecture: any
Description: This package contains a library implementing a simple but
powerful template language for C++. It emphasizes separating logic
from presentation: it is impossible to embed application logic in this
template language. This limits the power of the template language
without limiting the power of the template *system*. Indeed, Google's
"main" web search uses this system exclusively for formatting output.
powerful template language for C++. It emphasizes separating logic
from presentation: it is impossible to embed application logic in this
template language. This limits the power of the template language
without limiting the power of the template *system*. Indeed, Google's
"main" web search uses this system exclusively for formatting output.

View File

@ -9,3 +9,7 @@ doc/index.html
doc/howto.html
doc/tips.html
doc/example.html
doc/xss_resources.html
contrib/README.contrib
contrib/highlighting.vim
contrib/tpl-mode.el

View File

@ -65,7 +65,6 @@ rm -rf $RPM_BUILD_ROOT
%defattr(-,root,root)
%{prefix}/include/google
%{prefix}/lib/debug
%{prefix}/lib/libctemplate.a
%{prefix}/lib/libctemplate.la
%{prefix}/lib/libctemplate.so

79
trunk/src/base/mutex.cc Normal file
View File

@ -0,0 +1,79 @@
/* Copyright (c) 2007, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ---
* Author: Craig Silverstein
*
* A simple mutex wrapper. Right now, it's implemented in terms of
* pthreads, but is meant to be easy to extend to other threads impls.
*/
#include "config.h"
#include "mutex.h"
#if defined(NO_THREADS)
Mutex::Mutex() {}
Mutex::~Mutex() {}
void Mutex::Lock() {}
void Mutex::Unlock() {}
void Mutex::ReaderLock() {}
void Mutex::ReaderUnlock() {}
#elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK)
#include <stdlib.h> // for abort()
#include <pthread.h>
#define SAFE_PTHREAD(fncall) do { if ((fncall) != 0) abort(); } while (0)
Mutex::Mutex() { SAFE_PTHREAD(pthread_rwlock_init(&mutex_, NULL)); }
Mutex::~Mutex() { SAFE_PTHREAD(pthread_rwlock_destroy(&mutex_)); }
void Mutex::Lock() { SAFE_PTHREAD(pthread_rwlock_wrlock(&mutex_)); }
void Mutex::Unlock() { SAFE_PTHREAD(pthread_rwlock_unlock(&mutex_)); }
void Mutex::ReaderLock() { SAFE_PTHREAD(pthread_rwlock_rdlock(&mutex_)); }
void Mutex::ReaderUnlock() { SAFE_PTHREAD(pthread_rwlock_unlock(&mutex_)); }
#elif defined(HAVE_PTHREAD)
#include <stdlib.h> // for abort()
#include <pthread.h>
#define SAFE_PTHREAD(fncall) do { if ((fncall) != 0) abort(); } while (0)
Mutex::Mutex() { SAFE_PTHREAD(pthread_mutex_init(&mutex_, NULL)); }
Mutex::~Mutex() { SAFE_PTHREAD(pthread_mutex_destroy(&mutex_)); }
void Mutex::Lock() { SAFE_PTHREAD(pthread_mutex_lock(&mutex_)); }
void Mutex::Unlock() { SAFE_PTHREAD(pthread_mutex_unlock(&mutex_)); }
void Mutex::ReaderLock() { Lock(); } // we don't have read-write locks
void Mutex::ReaderUnlock() { Unlock(); }
#else
#error Need to implement mutex.h/cc for your architecture, or #define NO_THREADS
#endif

134
trunk/src/base/mutex.h Normal file
View File

@ -0,0 +1,134 @@
/* Copyright (c) 2007, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ---
* Author: Craig Silverstein.
*
* A simple mutex wrapper, supporting locks and read-write locks.
*
* To use: you should define the following macros in your configure.ac:
* ACX_PTHREAD
* AC_RWLOCK
* The latter is defined in ../autoconf.
*
* This class is meant to be internal-only, so it's defined in the
* global namespace. If you want to expose it, you'll want to move
* it to the Google namespace.
*/
#include "config.h" // to figure out pthreads support
#if defined(NO_THREADS)
typedef int MutexType; // some dummy type; it won't be used
#elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK)
// Needed for pthread_rwlock_*. If it causes problems, you could take
// it out, but then you'd have to unset HAVE_RWLOCK (at least on linux).
# define _XOPEN_SOURCE 500 // needed to get the rwlock calls
# include <pthread.h>
typedef pthread_rwlock_t MutexType;
#elif defined(HAVE_PTHREAD)
# include <pthread.h>
typedef pthread_mutex_t MutexType;
#else
# error Need to implement mutex.h/cc for your architecture, or #define NO_THREADS
#endif
class Mutex {
public:
// Create a Mutex that is not held by anybody. This constructor is
// typically used for Mutexes allocated on the heap or the stack.
// See below for a recommendation for constructing global Mutex
// objects.
Mutex();
// Destructor
~Mutex();
void Lock(); // Block if necessary until free, then acquire exclusively
void Unlock(); // Release. Caller must hold it exclusively (via Lock())
// Note that on systems that don't support read-write locks, these may
// be implemented as synonyms to Lock() and Unlock(). So you can use
// these for efficiency, but don't use them anyplace where being able
// to do shared reads is necessary to avoid deadlock.
void ReaderLock(); // Block until free or shared, then acquire a share
void ReaderUnlock(); // Release a read share of this Mutex
void WriterLock() { Lock(); } // Block until free, then acquire exclusively
void WriterUnlock() { Unlock(); } // Release the exclusive lock of this Mutex
private:
MutexType mutex_;
// Catch the error of writing Mutex when intending MutexLock.
Mutex(Mutex *ignored) {}
// Disallow "evil" constructors
Mutex(const Mutex&);
void operator=(const Mutex&);
};
// MutexLock(mu) acquires mu when constructed and releases it when destroyed.
class MutexLock {
public:
explicit MutexLock(Mutex *mu) : mu_(mu) { mu_->Lock(); }
~MutexLock() { mu_->Unlock(); }
private:
Mutex * const mu_;
// Disallow "evil" constructors
MutexLock(const MutexLock&);
void operator=(const MutexLock&);
};
// ReaderMutexLock and WriterMutexLock do the same, for rwlocks
class ReaderMutexLock {
public:
explicit ReaderMutexLock(Mutex *mu) : mu_(mu) { mu_->ReaderLock(); }
~ReaderMutexLock() { mu_->ReaderUnlock(); }
private:
Mutex * const mu_;
// Disallow "evil" constructors
ReaderMutexLock(const ReaderMutexLock&);
void operator=(const ReaderMutexLock&);
};
class WriterMutexLock {
public:
explicit WriterMutexLock(Mutex *mu) : mu_(mu) { mu_->WriterLock(); }
~WriterMutexLock() { mu_->WriterUnlock(); }
private:
Mutex * const mu_;
// Disallow "evil" constructors
WriterMutexLock(const WriterMutexLock&);
void operator=(const WriterMutexLock&);
};
// Catch bug where variable name is omitted, e.g. MutexLock (&mu);
#define MutexLock(x) COMPILE_ASSERT(0, mutex_lock_decl_missing_var_name)
#define ReaderMutexLock(x) COMPILE_ASSERT(0, rmutex_lock_decl_missing_var_name)
#define WriterMutexLock(x) COMPILE_ASSERT(0, wmutex_lock_decl_missing_var_name)

View File

@ -13,12 +13,6 @@
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* define if the compiler has hash_map */
#undef HAVE_EXT_HASH_MAP
/* define if the compiler has hash_set */
#undef HAVE_EXT_HASH_SET
/* Define to 1 if you have the `getopt_long' function. */
#undef HAVE_GETOPT_LONG
@ -102,9 +96,15 @@
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* printf format code for printing a size_t */
/* printf format code for printing a size_t and ssize_t */
#undef PRIdS
/* printf format code for printing a size_t and ssize_t */
#undef PRIuS
/* printf format code for printing a size_t and ssize_t */
#undef PRIxS
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
#undef PTHREAD_CREATE_JOINABLE

View File

@ -245,7 +245,7 @@ class Template {
// The mutex object used during ReloadIfChanged to prevent the same
// object from reloading the template file in parallel by different
// threads.
mutable class RWLock* mutex_; // RWLock defined in template.cc
mutable class Mutex* mutex_;
// The root directory for all templates. Defaults to "./" until
// SetTemplateRootDirectory changes it

View File

@ -56,6 +56,8 @@
@ac_google_start_namespace@
class UnsafeArena;
// Most methods below take a TemplateString rather than a C++ string.
// This is for efficiency: it can avoid extra string copies.
// For any argument that takes a TemplateString, you can pass in any of:
@ -88,10 +90,18 @@ class TemplateDictionary {
// arena is used to store all names and values. It can be NULL (the
// default), in which case we create own own arena.
explicit TemplateDictionary(const std::string& name,
class UnsafeArena* arena=NULL);
UnsafeArena* arena=NULL);
~TemplateDictionary();
const std::string& name() const { return name_; }
// Returns a recursive copy of this dictionary. This dictionary
// *must* be a "top-level" dictionary (that is, not created via
// AddSectionDictionary() or AddIncludeDictionary()). Caller owns
// the resulting dict, and must delete it. If arena is NULL, we
// create our own. Returns NULL if the copy fails (probably because
// the "top-level" rule was violated).
TemplateDictionary* MakeCopy(const std::string& name_of_copy,
UnsafeArena* arena=NULL);
// --- Routines for VARIABLES
// These are the five main routines used to set the value of a variable.
@ -285,10 +295,18 @@ class TemplateDictionary {
// Helps set up the static stuff
static GlobalDict* SetupGlobalDictUnlocked();
// This is true iff this template was created via the public constructor,
// and not the private one used for subsidiary dicts (section/include)
bool is_rootlevel_template() const { return template_global_dict_owner_; }
// Utility functions for copying a string into the arena.
const char *Memdup(const char* s, int slen);
const char *Memdup(const TemplateString& s) {return Memdup(s.ptr_, s.length_);}
// Used for recursive MakeCopy calls.
TemplateDictionary* InternalMakeCopy(const std::string& name_of_copy,
UnsafeArena* arena);
// Used to do the formatting for the SetFormatted*() functions
static int StringAppendV(char* space, char** out,
const char* format, va_list ap);

View File

@ -56,6 +56,7 @@
#ifndef TEMPLATE_FROM_STRING_H
#define TEMPLATE_FROM_STRING_H
#include <string>
#include <google/template.h>
@ac_google_start_namespace@

View File

@ -33,10 +33,7 @@
#include "config.h"
// Needed for pthread_rwlock_*. If it causes problems, you could take
// it out, but then you'd have to unset HAVE_RWLOCK (at least on linux).
#define _XOPEN_SOURCE 500 // needed to get the rwlock calls
#include "base/mutex.h" // This must go first so we get _XOPEN_SOURCE
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
@ -45,9 +42,6 @@
#include <sys/stat.h>
#include <unistd.h> // for stat() and open()
#include <string.h>
#if defined(HAVE_PTHREAD) && !defined(NO_THREADS)
# include <pthread.h>
#endif
#include <iostream> // for logging
#include <iomanip> // for indenting in Dump()
#include <string>
@ -70,58 +64,14 @@ using HASH_NAMESPACE::hash;
const int kIndent = 2; // num spaces to indent each level
#define SAFE_PTHREAD(fncall) do { if ((fncall) != 0) abort(); } while (0)
#if defined(HAVE_PTHREAD) && !defined(NO_THREADS)
# ifdef HAVE_RWLOCK
// Easiest to use a wrapper class for the read-write mutex
class RWLock {
public:
RWLock() { SAFE_PTHREAD(pthread_rwlock_init(&lock_, NULL)); }
~RWLock() { SAFE_PTHREAD(pthread_rwlock_destroy(&lock_)); }
void LockRO() { SAFE_PTHREAD(pthread_rwlock_rdlock(&lock_)); }
void LockRW() { SAFE_PTHREAD(pthread_rwlock_wrlock(&lock_)); }
void Unlock() { SAFE_PTHREAD(pthread_rwlock_unlock(&lock_)); }
private:
pthread_rwlock_t lock_;
};
# else
// Not as efficient, but the best we can do if !HAVE_RWLOCK
class RWLock {
public:
RWLock() { SAFE_PTHREAD(pthread_mutex_init(&lock_, NULL)); }
~RWLock() { SAFE_PTHREAD(pthread_mutex_destroy(&lock_)); }
void LockRO() { SAFE_PTHREAD(pthread_mutex_lock(&lock_)); }
void LockRW() { SAFE_PTHREAD(pthread_mutex_lock(&lock_)); }
void Unlock() { SAFE_PTHREAD(pthread_mutex_unlock(&lock_)); }
private:
pthread_mutex_t lock_;
};
# endif
// Mutexes protecting the globals below. First protects g_use_current_dict
// and template_root_directory_, second protects g_template_cache.
static pthread_mutex_t g_static_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t g_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
// This protects vars_seen in WriteOneHeaderEntry, below.
static pthread_mutex_t g_header_mutex = PTHREAD_MUTEX_INITIALIZER;
# define LOCK(m) SAFE_PTHREAD(pthread_mutex_lock(m))
# define UNLOCK(m) SAFE_PTHREAD(pthread_mutex_unlock(m))
#else
class RWLock { // since mutex_ has this type
public:
RWLock() {}
void LockRO() {}
void LockRW() {}
void Unlock() {}
};
# define LOCK(m)
# define UNLOCK(m)
#endif
namespace {
// Mutexes protecting the globals below. First protects g_use_current_dict
// and template_root_directory_, second protects g_template_cache.
// Third protects vars_seen in WriteOneHeaderEntry, below.
static Mutex g_static_mutex;
static Mutex g_cache_mutex;
static Mutex g_header_mutex;
const char * const kDefaultTemplateDirectory = "./";
const char * const kMainSectionName = "__MAIN__";
static vector<TemplateDictionary*>* g_use_current_dict; // vector == {NULL}
@ -187,23 +137,31 @@ static string HtmlModifier(const string& input, const string&) {
return TemplateDictionary::html_escape(input);
}
static string PreModifier(const string& input, const string&) {
return TemplateDictionary::pre_escape(input);
}
static string JavascriptModifier(const string& input, const string&) {
return TemplateDictionary::javascript_escape(input);
}
static string JsonModifier(const string& input, const string&) {
return TemplateDictionary::json_escape(input);
}
static string PreModifier(const string& input, const string&) {
return TemplateDictionary::pre_escape(input);
}
static string UrlQueryEscapeModifier(const string& input, const string&) {
return TemplateDictionary::url_query_escape(input);
}
// Using "o" for JSON is not really very intuitive; template authors should
// either use json_escape in full, or provide a comment explaining the
// non-intuitive shortening.
static const Modifier g_modifiers[] = {
{ "html_escape", 'h', MODVAL_FORBIDDEN, &HtmlModifier },
{ "pre_escape", 'p', MODVAL_FORBIDDEN, &PreModifier },
{ "javascript_escape", 'j', MODVAL_FORBIDDEN, &JavascriptModifier },
{ "url_query_escape", 'u', MODVAL_FORBIDDEN, &UrlQueryEscapeModifier },
{ "json_escape", 'o', MODVAL_FORBIDDEN, &JsonModifier },
{ "pre_escape", 'p', MODVAL_FORBIDDEN, &PreModifier },
{ "url_query_escape", 'u', MODVAL_FORBIDDEN, &UrlQueryEscapeModifier }
};
@ -245,7 +203,7 @@ class HeaderEntryStringHash { // not all STL implementations define this...
static void WriteOneHeaderEntry(string *outstring,
const string& variable,
const string& full_pathname) {
LOCK(&g_header_mutex);
MutexLock ml(&g_header_mutex);
// we use hash_map instead of hash_set just to keep the stl size down
static hash_map<string, bool, HeaderEntryStringHash> vars_seen;
@ -303,7 +261,6 @@ static void WriteOneHeaderEntry(string *outstring,
}
vars_seen[variable] = true;
}
UNLOCK(&g_header_mutex);
}
// ----------------------------------------------------------------------
@ -869,7 +826,7 @@ void SectionTemplateNode::Dump(int level) const {
// --- AddSubnode and its sub-routines
void SectionTemplateNode::AddTextNode(const char* text, int textlen) {
if (text != "") { // ignore null text sections
if (textlen > 0) { // ignore null text sections
node_list_.push_back(new TextTemplateNode(text, textlen));
}
}
@ -1216,7 +1173,7 @@ Template::Template(const string& filename, Strip strip)
: filename_(filename), filename_mtime_(0), strip_(strip),
state_(TS_EMPTY),
template_text_(NULL), template_text_len_(0), tree_(NULL),
parse_state_(), mutex_(new RWLock) {
parse_state_(), mutex_(new Mutex) {
// Make sure g_use_current_dict, etc. are initted before any possbility
// of calling Expand() or other Template classes that access globals.
AssureGlobalsInitialized();
@ -1247,21 +1204,20 @@ Template::~Template() {
// NOTE: This function must be called by any static function that
// accesses any of the variables set here.
void Template::AssureGlobalsInitialized() {
LOCK(&g_static_mutex); // protects all the vars defined here
MutexLock ml(&g_static_mutex); // protects all the vars defined here
if (template_root_directory_ == NULL) { // only need to run this once!
template_root_directory_ = new string(kDefaultTemplateDirectory);
// this_dict is a dictionary with a single NULL entry in it
g_use_current_dict = new vector<TemplateDictionary*>;
g_use_current_dict->push_back(NULL);
}
UNLOCK(&g_static_mutex);
}
Template *Template::GetTemplate(const string& filename, Strip strip) {
// No need to have the cache-mutex acquired for this step
string abspath(PathJoin(template_root_directory(), filename));
LOCK(&g_cache_mutex);
MutexLock ml(&g_cache_mutex);
if (g_template_cache == NULL)
g_template_cache = new TemplateCache;
@ -1277,7 +1233,6 @@ Template *Template::GetTemplate(const string& filename, Strip strip) {
tpl = new Template(abspath, strip);
(*g_template_cache)[pair<string, Strip>(abspath, strip)] = tpl;
}
UNLOCK(&g_cache_mutex); // we're done messing with g_template_cache
// if the statis is not TS_READY, then it is TS_ERROR at this
// point. If it is TS_ERROR, we leave the state as is, but return
@ -1374,7 +1329,7 @@ bool Template::SetTemplateRootDirectory(const string& directory) {
AssureGlobalsInitialized();
// This is needed since we access/modify template_root_directory_
LOCK(&g_static_mutex);
MutexLock ml(&g_static_mutex);
// make sure it ends with '/'
if (directory.length() == 0 || directory[directory.length()-1] != '/') {
*template_root_directory_ = directory + '/';
@ -1383,7 +1338,6 @@ bool Template::SetTemplateRootDirectory(const string& directory) {
}
VLOG(2) << "Setting Template directory to " << *template_root_directory_
<< endl;
UNLOCK(&g_static_mutex);
return true;
}
@ -1391,10 +1345,8 @@ bool Template::SetTemplateRootDirectory(const string& directory) {
string Template::template_root_directory() {
// Make sure template_root_directory_ has been initialized
AssureGlobalsInitialized();
LOCK(&g_static_mutex); // protects the static var template_root_directory_
string retval = *template_root_directory_;
UNLOCK(&g_static_mutex);
return retval;
MutexLock ml(&g_static_mutex); // protects the static var t_r_d_
return *template_root_directory_;
}
void Template::set_state(TemplateState new_state) {
@ -1428,21 +1380,19 @@ bool Template::ReloadIfChanged() {
// from different threads, they don't stomp on tree_ and state_.
// This is still not perfect, since set_filename() could stomp
// on filename_ while we're reading it, but it's good enough.
mutex_->LockRW();
WriterMutexLock ml(mutex_);
struct stat statbuf;
if (stat(filename_.c_str(), &statbuf) != 0) {
LOG(WARNING) << "Unable to stat file " << filename_ << endl;
// We keep the old tree if there is one, otherwise we're in error
set_state(tree_ ? TS_READY : TS_ERROR);
mutex_->Unlock();
return false;
}
if (statbuf.st_mtime == filename_mtime_ && filename_mtime_ > 0
&& tree_) { // force a reload if we don't already have a tree_
VLOG(1) << "Not reloading file " << filename_ << ": no new mod-time" << endl;
set_state(TS_READY);
mutex_->Unlock();
return false; // file's timestamp hasn't changed, so no need to reload
}
@ -1451,7 +1401,6 @@ bool Template::ReloadIfChanged() {
LOG(ERROR) << "Can't find file " << filename_ << "; skipping" << endl;
// We keep the old tree if there is one, otherwise we're in error
set_state(tree_ ? TS_READY : TS_ERROR);
mutex_->Unlock();
return false;
}
char* file_buffer = new char[statbuf.st_size];
@ -1462,7 +1411,6 @@ bool Template::ReloadIfChanged() {
delete[] file_buffer;
// We could just keep the old tree, but probably safer to say 'error'
set_state(TS_ERROR);
mutex_->Unlock();
return false;
}
fclose(fp);
@ -1480,19 +1428,16 @@ bool Template::ReloadIfChanged() {
// of input_buffer in every case, and will eventually delete it.
if ( BuildTree(input_buffer, input_buffer + buflen) ) {
assert(state() == TS_READY);
mutex_->Unlock();
return true;
} else {
assert(state() != TS_READY);
mutex_->Unlock();
return false;
}
}
void Template::ReloadAllIfChanged() {
LOCK(&g_cache_mutex); // this protects the static g_template_cache
MutexLock ml(&g_cache_mutex); // this protects the static g_template_cache
if (g_template_cache == NULL) {
UNLOCK(&g_cache_mutex);
return;
}
for (TemplateCache::const_iterator iter = g_template_cache->begin();
@ -1500,7 +1445,6 @@ void Template::ReloadAllIfChanged() {
++iter) {
(*iter).second->set_state(TS_RELOAD);
}
UNLOCK(&g_cache_mutex);
}
// ----------------------------------------------------------------------
@ -1509,9 +1453,8 @@ void Template::ReloadAllIfChanged() {
// ----------------------------------------------------------------------
void Template::ClearCache() {
LOCK(&g_cache_mutex); // this protects the static g_template_cache
MutexLock ml(&g_cache_mutex); // this protects the static g_template_cache
if (g_template_cache == NULL) {
UNLOCK(&g_cache_mutex);
return;
}
for (TemplateCache::const_iterator iter = g_template_cache->begin();
@ -1521,7 +1464,6 @@ void Template::ClearCache() {
}
delete g_template_cache;
g_template_cache = NULL;
UNLOCK(&g_cache_mutex);
}
// ----------------------------------------------------------------------
@ -1668,11 +1610,10 @@ bool Template::Expand(ExpandEmitter *expand_emitter,
// tree_, and we want to make sure it doesn't do that (in another
// thread) while we're expanding. We also protect state_, etc.
// Note we only need a read-lock here, so many expands can go on at once.
mutex_->LockRO();
ReaderMutexLock ml(mutex_);
if (state() != TS_READY) {
// We'd like to reload if state_ == TS_RELOAD, but we're a const method
mutex_->Unlock();
return false;
}
@ -1701,8 +1642,6 @@ bool Template::Expand(ExpandEmitter *expand_emitter,
expand_emitter->Emit(TemplateNode::CloseAnnotation("FILE"));
}
mutex_->Unlock();
return error_free;
}

View File

@ -33,18 +33,11 @@
// Based on the 'old' TemplateDictionary by Frank Jernigan.
#include "config.h"
// Needed for pthread_rwlock_*. If it causes problems, you could take
// it out, but then you'd have to unset HAVE_RWLOCK (at least on linux).
#define _XOPEN_SOURCE 500 // needed to get the rwlock calls
#include "base/mutex.h" // This must go first so we get _XOPEN_SOURCE
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h> // for varargs with StringAppendV
#if defined(HAVE_PTHREAD) && !defined(NO_THREADS)
# include <pthread.h>
#endif
#include <string>
#include <algorithm> // for sort
#include <vector>
@ -59,31 +52,7 @@ using std::string;
using std::pair;
using HASH_NAMESPACE::hash_map;
#define SAFE_PTHREAD(fncall) do { if ((fncall) != 0) abort(); } while (0)
#if defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK) && !defined(NO_THREADS)
# define STATIC_ROLOCK SAFE_PTHREAD(pthread_rwlock_rdlock(&g_static_mutex))
# define STATIC_RWLOCK SAFE_PTHREAD(pthread_rwlock_wrlock(&g_static_mutex))
# define STATIC_UNLOCK SAFE_PTHREAD(pthread_rwlock_unlock(&g_static_mutex))
// PTHREAD_RWLOCK_INITIALIZER isn't defined on OS X, at least as of 6/1/06.
// We use a static class instance to force initialization at program-start time.
static pthread_rwlock_t g_static_mutex;
namespace { // keep this class name from polluting the global namespace
struct StaticMutexInit {
StaticMutexInit() { SAFE_PTHREAD(pthread_rwlock_init(&g_static_mutex, NULL)); }
};
static StaticMutexInit g_static_mutex_initializer; // constructs early
}
#elif defined(HAVE_PTHREAD) && !defined(NO_THREADS)
# define STATIC_ROLOCK SAFE_PTHREAD(pthread_mutex_lock(&g_static_mutex))
# define STATIC_RWLOCK SAFE_PTHREAD(pthread_mutex_lock(&g_static_mutex))
# define STATIC_UNLOCK SAFE_PTHREAD(pthread_mutex_unlock(&g_static_mutex))
static pthread_mutex_t g_static_mutex = PTHREAD_MUTEX_INITIALIZER;
#else
# define STATIC_ROLOCK
# define STATIC_RWLOCK
# define STATIC_UNLOCK
#endif
static Mutex g_static_mutex;
/*static*/ TemplateDictionary::GlobalDict* TemplateDictionary::global_dict_
= NULL;
@ -133,10 +102,9 @@ TemplateDictionary::TemplateDictionary(const string& name, UnsafeArena* arena)
parent_dict_(NULL),
filename_(NULL),
template_path_start_for_annotations_(NULL) {
STATIC_RWLOCK;
MutexLock ml(&g_static_mutex);
if (global_dict_ == NULL)
global_dict_ = SetupGlobalDictUnlocked();
STATIC_UNLOCK;
}
TemplateDictionary::TemplateDictionary(const string& name, UnsafeArena* arena,
@ -154,10 +122,9 @@ TemplateDictionary::TemplateDictionary(const string& name, UnsafeArena* arena,
parent_dict_(parent_dict),
filename_(NULL),
template_path_start_for_annotations_(NULL) {
STATIC_RWLOCK;
MutexLock ml(&g_static_mutex);
if (global_dict_ == NULL)
global_dict_ = SetupGlobalDictUnlocked();
STATIC_UNLOCK;
}
TemplateDictionary::~TemplateDictionary() {
@ -190,10 +157,84 @@ TemplateDictionary::~TemplateDictionary() {
if (template_global_dict_owner_) {
delete template_global_dict_;
}
if (should_delete_arena_)
if (should_delete_arena_) {
delete arena_;
}
}
// ----------------------------------------------------------------------
// TemplateDictionary::MakeCopy()
// Makes a recursive copy: so we copy any include dictionaries and
// section dictionaries we see as well. InternalMakeCopy() is
// needed just so we can ensure that if we're doing a copy of a
// subtree, it's due to a recursive call.
// ----------------------------------------------------------------------
TemplateDictionary* TemplateDictionary::InternalMakeCopy(
const string& name_of_copy, UnsafeArena* arena) {
TemplateDictionary* newdict;
if (is_rootlevel_template()) { // rootlevel uses public constructor
newdict = new TemplateDictionary(name_of_copy, arena);
} else { // recursve calls use private contructor
// Note: we always use our own arena, even when we have a parent
newdict = new TemplateDictionary(name_of_copy, arena,
parent_dict_, template_global_dict_);
}
// Copy the variable dictionary
for (VariableDict::const_iterator it = variable_dict_->begin();
it != variable_dict_->end(); ++it) {
newdict->SetValue(it->first, it->second);
}
// ...and the template-global-dict, if we're the owner of it
if (template_global_dict_owner_) {
for (VariableDict::const_iterator it = template_global_dict_->begin();
it != template_global_dict_->end(); ++it) {
newdict->SetTemplateGlobalValue(it->first, it->second);
}
}
// Copy the section dictionary
for (SectionDict::iterator it = section_dict_->begin();
it != section_dict_->end(); ++it) {
DictVector* dicts = new DictVector;
(*newdict->section_dict_)[newdict->Memdup(it->first)] = dicts;
for (DictVector::iterator it2 = it->second->begin();
it2 != it->second->end(); ++it2) {
TemplateDictionary* subdict = *it2;
dicts->push_back(subdict->InternalMakeCopy(subdict->name(),
newdict->arena_));
}
}
// Copy the includes-dictionary
for (IncludeDict::iterator it = include_dict_->begin();
it != include_dict_->end(); ++it) {
DictVector* dicts = new DictVector;
(*newdict->include_dict_)[newdict->Memdup(it->first)] = dicts;
for (DictVector::iterator it2 = it->second->begin();
it2 != it->second->end(); ++it2) {
TemplateDictionary* subdict = *it2;
dicts->push_back(subdict->InternalMakeCopy(subdict->name(),
newdict->arena_));
}
}
// Finally, copy everything else not set properly by the constructor
newdict->filename_ = newdict->Memdup(filename_);
newdict->template_path_start_for_annotations_ =
newdict->Memdup(template_path_start_for_annotations_);
return newdict;
}
TemplateDictionary* TemplateDictionary::MakeCopy(const string& name_of_copy,
UnsafeArena* arena) {
if (!is_rootlevel_template()) { // we're not at the root, which is illegal
return NULL;
}
return InternalMakeCopy(name_of_copy, arena);
}
// ----------------------------------------------------------------------
// TemplateDictionary::StringAppendV()
// Does an snprintf to a string. Idea is to grow string as needed.
@ -325,11 +366,10 @@ void TemplateDictionary::SetTemplateGlobalValue(const TemplateString variable,
memcpy(value_copy, value.ptr_, value.length_);
value_copy[value.length_] = '\0';
STATIC_RWLOCK;
MutexLock ml(&g_static_mutex);
if (global_dict_ == NULL)
global_dict_ = SetupGlobalDictUnlocked();
(*global_dict_)[variable_copy] = value_copy;
STATIC_UNLOCK;
}
// ----------------------------------------------------------------------
@ -485,9 +525,8 @@ void TemplateDictionary::DumpToString(string* out, int indent) const {
vector<pair<const char*, const char*> > sorted_global_dict;
{
STATIC_ROLOCK;
ReaderMutexLock ml(&g_static_mutex);
SortByStringKeyInto(*global_dict_, &sorted_global_dict);
STATIC_UNLOCK;
}
for (vector<pair<const char*, const char*> >::const_iterator it
= sorted_global_dict.begin();
@ -682,12 +721,11 @@ const char *TemplateDictionary::GetSectionValue(const string& variable) const {
// No match in dict tree or template-global dict. Last chance: global dict.
{
STATIC_ROLOCK;
ReaderMutexLock ml(&g_static_mutex);
GlobalDict::const_iterator it = global_dict_->find(variable.c_str());
const char* retval = ""; // what we'll return if global lookup fails
if (it != global_dict_->end())
retval = it->second;
STATIC_UNLOCK;
return retval;
}
}
@ -842,26 +880,22 @@ string TemplateDictionary::UrlQueryEscape::operator()(const string& in) const {
0x00000000L, 0x00000000L, 0x00000000L, 0x00000000L
};
int max_string_length = in.size() * 3 + 1;
char out[max_string_length];
string out;
out.reserve(in.size() * 3 + 1);
int i;
int j;
for (i = 0, j = 0; i < in.size(); i++) {
for (int i = 0; i < in.size(); i++) {
unsigned char c = in[i];
if (c == ' ') {
out[j++] = '+';
out += '+';
} else if ((_safe_characters[(c)>>5] & (1 << ((c) & 31)))) {
out[j++] = c;
out += c;
} else {
out[j++] = '%';
out[j++] = ((c>>4) < 10 ? ((c>>4) + '0') : (((c>>4) - 10) + 'A'));
out[j++] = ((c&0xf) < 10 ? ((c&0xf) + '0') : (((c&0xf) - 10) + 'A'));
out += '%';
out += ((c>>4) < 10 ? ((c>>4) + '0') : (((c>>4) - 10) + 'A'));
out += ((c&0xf) < 10 ? ((c&0xf) + '0') : (((c&0xf) - 10) + 'A'));
}
}
out[j++] = '\0';
return string(out);
return out;
}
// Escapes " / \ <BS> <FF> <CR> <LF> <TAB> to \" \/ \\ \b \f \r \n \t

View File

@ -32,10 +32,8 @@
//
#include "config.h"
#include "base/mutex.h" // This must go first so we get _XOPEN_SOURCE
#include <assert.h>
#if defined(HAVE_PTHREAD) && !defined(NO_THREADS)
#include <pthread.h>
#endif
#include <string>
#include <google/ctemplate/hash_map.h>
#include <google/template_from_string.h>
@ -43,21 +41,12 @@
_START_GOOGLE_NAMESPACE_
#if defined(HAVE_PTHREAD) && !defined(NO_THREADS)
# define LOCK(m) pthread_mutex_lock(m)
# define UNLOCK(m) pthread_mutex_unlock(m)
// This is used to protect g_template_from_string_cache, below
static pthread_mutex_t g_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
#else
# define LOCK(m)
# define UNLOCK(m)
#endif
static Mutex g_cache_mutex;
using std::string;
using std::pair;
using HASH_NAMESPACE::hash_map;
// TemplateFromString Constructor
// Calls its parent with an empty string for filename so the parent's
// constructor will not try to "load" the template from a file. Instead,
@ -111,7 +100,7 @@ TemplateFromString *TemplateFromString::GetTemplate(const string& template_name,
Strip strip) {
// Only perform this method when you have the lock so multiple threads
// don't conflict over inserting and retrieving into the cache
LOCK(&g_cache_mutex);
MutexLock ml(&g_cache_mutex);
if (g_template_from_string_cache == NULL) {
g_template_from_string_cache = new TemplateFromStringCache;
}
@ -126,7 +115,6 @@ TemplateFromString *TemplateFromString::GetTemplate(const string& template_name,
(*g_template_from_string_cache)[pair<string, Strip>(template_name, strip)] =
tpl;
}
UNLOCK(&g_cache_mutex); // done messing with the cache
// state_ can be TS_RELOAD if ReloadAllIfChanged() touched this file.
// That's fine; we'll just ignore the reload directive for this guy.

View File

@ -39,7 +39,7 @@
#include <string>
#include <vector> // that's MissingListType, SyntaxListType
#include <iostream> // for cerr
#include <algorithm> // for find()
#include <algorithm> // for binary_search
#include <google/ctemplate/hash_set.h> // that's NameListType
#include <google/template_namelist.h>
#include <google/template.h> // for Strip, GetTemplate(), etc.
@ -96,6 +96,7 @@ const TemplateNamelist::NameListType& TemplateNamelist::GetList() {
// and adds to the list any that are missing
// On subsequent calls, if refresh is false it merely returns the
// list created in the prior call that refreshed the list.
// Returns a sorted list of missing templates.
const TemplateNamelist::MissingListType& TemplateNamelist::GetMissingList(
bool refresh) {
if (!missing_list_) {
@ -158,11 +159,9 @@ const TemplateNamelist::SyntaxListType& TemplateNamelist::GetBadSyntaxList(
++iter) {
Template *tpl = Template::GetTemplate((*iter), strip);
if (!tpl) {
MissingListType::const_iterator pos =
find(missing_list.begin(), missing_list.end(), (*iter));
// If it's not in the missing list, then we're here because it caused
// an error during parsing
if (pos == missing_list.end()) {
if (!binary_search(missing_list.begin(), missing_list.end(), *iter)) {
// If it's not in the missing list, then we're here because
// it caused an error during parsing
bad_syntax_list_->push_back(*iter);
std::cerr << "ERROR loading template: " << (*iter) << std::endl;
}

View File

@ -771,6 +771,57 @@ class TemplateDictionaryUnittest {
"}\n");
ASSERT_STREQ(dump.c_str(), expected);
}
static void TestMakeCopy(bool use_local_arena) {
UnsafeArena local_arena(1024);
UnsafeArena* arena = NULL;
if (use_local_arena)
arena = &local_arena;
// First, let's make a non-trivial template dictionary (We use
// 'new' because later we'll test deleting this dict but keeping
// around the copy.)
TemplateDictionary* dict = new TemplateDictionary("testdict", arena);
dict->SetValue("TOPLEVEL", "foo");
dict->SetTemplateGlobalValue("TEMPLATELEVEL", "foo3");
TemplateDictionary* subdict_1a = dict->AddIncludeDictionary("include1");
subdict_1a->SetFilename("incfile1a");
subdict_1a->SetValue("SUBLEVEL", "subfoo");
TemplateDictionary* subdict_1b = dict->AddIncludeDictionary("include1");
// Let's try not calling SetFilename on this one.
subdict_1b->SetValue("SUBLEVEL", "subbar");
TemplateDictionary* subdict_2a = dict->AddSectionDictionary("section1");
TemplateDictionary* subdict_2b = dict->AddSectionDictionary("section1");
subdict_2a->SetValue("SUBLEVEL", "subfoo");
subdict_2b->SetValue("SUBLEVEL", "subbar");
TemplateDictionary* subdict_3 = dict->AddSectionDictionary("section2");
subdict_3->SetValue("TOPLEVEL", "bar"); // overriding top dict
TemplateDictionary* subdict_3_1 = subdict_3->AddSectionDictionary("sub");
subdict_3_1->SetIntValue("GLOBAL", 21); // overrides value in setUp()
string orig;
dict->DumpToString(&orig);
// Make a copy
TemplateDictionary* dict_copy = dict->MakeCopy("testdict", NULL);
// Make sure it doesn't work to copy a sub-dictionary
ASSERT(subdict_1a->MakeCopy("copy of subdict") == NULL);
ASSERT(subdict_2a->MakeCopy("copy of subdict") == NULL);
// Delete the original dict, to make sure the copy really is independent
delete dict;
dict = NULL;
string copy;
dict_copy->DumpToString(&copy);
delete dict_copy;
ASSERT_STREQ(orig.c_str(), copy.c_str());
}
};
_END_GOOGLE_NAMESPACE_
@ -792,6 +843,9 @@ int main(int argc, char** argv) {
TemplateDictionaryUnittest::TestSetTemplateGlobalValue();
TemplateDictionaryUnittest::TestAddIncludeDictionary();
TemplateDictionaryUnittest::TestMakeCopy(true); // use our own arena
TemplateDictionaryUnittest::TestMakeCopy(false); // use fake arena
printf("DONE.\n");
return 0;
}

View File

@ -47,17 +47,17 @@
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#if HAVE_DIRENT_H
#include <dirent.h> // for opendir() etc
#ifdef HAVE_DIRENT_H
# include <dirent.h> // for opendir() etc
#else
# define dirent direct
# if HAVE_SYS_NDIR_H
# ifdef HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
# ifdef HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# if HAVE_NDIR_H
# ifdef HAVE_NDIR_H
# include <ndir.h>
# endif
#endif

View File

@ -40,17 +40,17 @@
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h> // for mkdir
#if HAVE_DIRENT_H
#ifdef HAVE_DIRENT_H
# include <dirent.h> // for readdir
#else
# define dirent direct
# if HAVE_SYS_NDIR_H
# ifdef HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
# ifdef HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# if HAVE_NDIR_H
# ifdef HAVE_NDIR_H
# include <ndir.h>
# endif
#endif