--- /dev/null
+
+seagull <seagull@mitsuki.no-ip.com>
+
+
--- /dev/null
+Copyright (c) HATTORI, Hiroki
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. 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.
+3. Neither the name of the University 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 REGENTS 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 REGENTS 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.
--- /dev/null
+# Doxyfile 1.5.2
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file that
+# follow. The default is UTF-8 which is also the encoding used for all text before
+# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into
+# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of
+# possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = CoolRain
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = 0
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian,
+# Italian, Japanese, Japanese-en (Japanese with English messages), Korean,
+# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian,
+# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = Japanese
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+ALIASES += threading="\par Threading:\n"
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to
+# include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from the
+# version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = include src
+
+# This tag can be used to specify the character encoding of the source files that
+# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default
+# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding.
+# See http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
+
+FILE_PATTERNS = *.c *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the output.
+# The symbol name can be a fully qualified name, a word, or if the wildcard * is used,
+# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code. Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = YES
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to
+# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to
+# specify the directory where the mscgen tool resides. If left empty the tool is assumed to
+# be found in the default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = YES
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a call dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a caller dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+
+CALLER_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen will always
+# show the root nodes and its direct children regardless of this setting.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, which results in a white background.
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
--- /dev/null
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory. After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc. You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script). Here is a another example:
+
+ /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
--- /dev/null
+
+SUBDIRS=src include/CoolRain tests examples
+
--- /dev/null
+
+
+coolrain-0.3.0
+=================
+
+ * Fix bugs.
+ * Add builtin tag
+ 'CR:pass', 'CR:ignore', 'CR:loop', 'CR:let' and 'CR:var' is present.
+ * New simple variable reference syntax '${varname}'.
+ * Add variable lookup calssback on stash for undefined variables.
+
+
+coolrain-0.2.0
+=================
+
+Initial release.
+
+
+coolrain-0.1.0
+=================
+
+Aplha version.
--- /dev/null
+% README - CoolRain
+% HATTORI,Hiroki
+% 2007-07-12
+
+
+CoolRainは、Cで書かれたCから使えるテンプレートエンジンです。
+
+特徴
+========
+
+ 1. テンプレートは、ウェルフォームドである必要はありません。プレーンテキストの生成もできます。
+ 2. カスタムタグは必ず開きと閉じがペアになるので、テンプレート自身をウェルフォームドに保つ事もできます。
+ 3. 全てのタグはアプリケーションが登録します。テンプレートエンジン自身はなんのタグも処理しません。
+ (組込みのタグハンドラがあるので、それを使うのは自由です)
+ 4. (たぶん)それなりに高速に動作します。
+
+ FIXME: TTとClearSilverとPHPとでベンチマークを取る事。
+
+
+入手方法
+=============
+
+[darcsリポジトリ](http://www.mitsuki.no-ip.com/~seagull/software-archives/CoolRain/)か
+[tarball置き場](http://www.mitsuki.no-ip.com/~seagull/software-archives/CoolRain/download/)から入手できます。
+
+
+必要なもの
+============
+
+glib-2.0が必要です。開発には2.12を使っていますが、configure.acを書き換えれば古いバージョンでも行けると思います。
+gthreadが入っていれば、マルチスレッド版も同時にビルドされます。
+
+
+FIXME: glibの代わりにAPRを使う版を用意する。
+
+
+コンパイル方法
+================
+
+autotoolsを使っているので、configure && make && make install で行けます。
+
+
+使いかた
+============
+
+マニュアル等は無いので、単体テストコードを参照してください。
+
+簡単にいえば、
+
+ 1. tagsetオブジェクトを作って、カスタムタグの名前とハンドラを登録する
+ 2. templateオブジェクトにtagsetをあたえて構築する。
+ 3. テンプレートをコンパイルさせる。
+ 4. writerオブジェクト(出力ストリーム)を作る
+ 5. stashオブジェクトを作る
+ 6. templateオブジェクトに、writerとstashをあたえて評価させる。
+
+の手順で使います。
+
+FIXME: これもうちょっと簡単にならんか?
+
+
+
+
+
--- /dev/null
+
+
+New subsystem
+==============
+* Template repository (Template factory)
+* I18N message catalog. (gettext / keyfile)
+
+
+
+Template parser
+=====================
+* Save and restore the compiled binary template.
+* Split Parser-state from template object.
+* glibの型システムに登録すると、バインディング作成が楽になったりしない?
+* 属性値もフラグメントとして扱えるようにする
+* 変数参照の簡便記法 ( ${変数名}で<var name='変数名' /> のエイリアスにする)
+
+
+Template evalutor
+==================
+* オーバーヘッドを少しづつでも削る
+* メモリのローカリティにも考慮する
+
+
+Writer
+======================
+* Apache module writer
+* Variable size large string writer
+
+
+
+Builtin tag and filters
+========================
+* If-Else-EndIf tag handler
+
+
+
+Misc
+======
+* Hash object wrapper
+* APR support
+* Threading with apr
+
+
+Language binding
+=====================
+* Haskell
+* Perl
+* C++ class wrapper
+
+
+
--- /dev/null
+#!/bin/sh
+
+aclocal-1.9
+autoheader
+darcs changes >ChangeLog
+automake --add-missing
+autoconf
+
--- /dev/null
+dnl
+dnl
+dnl
+
+AC_INIT(CoolRain, 0.3.0, [seagull <seagull@mitsuki.no-ip.com>])
+dnl AC_CONFIG_AUX_DIR(config)
+AC_PREREQ(2.59)
+
+AC_CANONICAL_SYSTEM
+AC_REVISION(0.2.7)
+AC_CONFIG_SRCDIR(src/template.c)
+AM_INIT_AUTOMAKE()
+
+AM_CONFIG_HEADER(include/CoolRain/config.h)
+
+AC_PROG_INSTALL
+
+AC_ISC_POSIX
+
+
+AC_PROG_CC
+AC_PROG_CC_STDC
+AC_PROG_CPP
+AC_PROG_RANLIB
+dnl AC_PROG_LIBTOOL
+
+dnl
+AC_C_INLINE
+
+AC_HEADER_STDC
+
+AC_C_CONST
+AC_TYPE_SIZE_T
+
+dnl AM_PATH_CHECK(0.9.4)
+
+CFLAGS="$CFLAGS -I \${top_srcdir}/include"
+
+
+AC_CHECK_HEADERS([CUnit/Basic.h], [COOLRAIN_HAVE_CUNIT="yes"], [COOLRAIN_HAVE_CUNIT="no"])
+AC_SUBST(COOLRAIN_HAVE_CUNIT)
+AM_CONDITIONAL(HAVE_CUNIT, [ test "$HAVE_CUNIT" == "yes" ])
+
+
+
+dnl
+AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], [To enable debug build]))
+if test "$enable_debug" == "yes"; then
+AC_DEFINE(COOLRAIN_ENABLE_DEBUG, 1, [Enable debug build])
+CFLAGS="$CFLAGS -O0 -g"
+else
+CFLAGS="$CFLAGS -O99"
+fi
+
+dnl
+AC_ARG_ENABLE(profile, AC_HELP_STRING([--enable-profile], [To enable profiling]))
+if test "$enable_profile" == "yes"; then
+AC_DEFINE(COOLRAIN_ENABLE_PROFILE, 1, [Enable profiling build])
+CLFAGS="$CFLAGS -pg"
+fi
+
+dnl
+AC_ARG_ENABLE(threading, AC_HELP_STRING([--disable-threading], [Supress to build multi-threading support]))
+if test "$enable_threading" != "no"; then
+AC_DEFINE(COOLRAIN_ENABLE_THREADING, 1, [Threading support])
+COOLRAIN_ENABLE_THREADING=1
+AC_SUBST(COOLRAIN_ENABLE_THREADING)
+fi
+
+
+HAVE_GLIB=0
+PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.12.12, [
+ PKG_CHECK_MODULES(GOBJECT, gobject-2.0 >= 2.12.12, HAVE_GLIB=1]) )
+AC_SUBST(HAVE_GLIB)
+AM_CONDITIONAL(HAVE_GLIB, [ test "$HAVE_GLIB" == "1" ])
+
+
+PKG_CHECK_MODULES(APR, apr-1 >= 1.2.7, [
+ AC_DEFINE(COOLRAIN_HAVE_APR, 1, [Have apache portable library])
+ AM_CONDITIONAL(HAVE_APR, [ true ])
+],[
+ AM_CONDITIONAL(HAVE_APR, [ false ])
+])
+
+
+PKG_CHECK_MODULES(APRUTIL, apr-util-1 >= 1.2.7,[HAVE_APRUTIL=1],[HAVE_APRUTIL=0])
+AC_SUBST(HAVE_APRUTIL)
+AM_CONDITIONAL(HAVE_APRUTIL, [ test "$HAVE_APRUTIL" == "1" ])
+
+
+dnl Threading libraries
+HAVE_GTHREAD=0
+HAVE_PTHREAD=0
+if test "${COOLRAIN_ENABLE_THREADING}" == "1"; then
+PKG_CHECK_MODULES(GTHREAD, gthread-2.0 >= 2.12.12, [
+ AC_DEFINE(COOLRAIN_HAVE_GTHREAD, 1, [Have gthread library])
+ HAVE_GTHREAD=1
+])
+
+AC_CHECK_LIB(pthread, pthread_exit, [
+ AC_DEFINE(COOLRAIN_HAVE_PTHREAD, 1, [Have pthread library])
+ HAVE_PTHREAD=1
+])
+fi
+AC_SUBST(HAVE_GTHREAD)
+AM_CONDITIONAL(HAVE_GTHREAD, [ test "$HAVE_GTHREAD" == "1" ])
+AC_SUBST(HAVE_PTHREAD)
+AM_CONDITIONAL(HAVE_PTHREAD, [ test "$HAVE_GTHREAD" == "1" ])
+
+
+
+dnl FastCGI support
+AC_CHECK_HEADERS([fcgiapp.h])
+
+
+if test "$GCC" = "yes"; then
+ CFLAGS="$CFLAGS -Wall -Wextra -ffunction-sections"
+ LDFLAGS="-Wl,--gc-sections"
+fi
+
+AC_PROG_GCC_TRADITIONAL
+
+AC_OUTPUT(Makefile
+ src/Makefile
+ include/CoolRain/Makefile
+ tests/Makefile
+ examples/Makefile
+ )
+
--- /dev/null
+
+LIBS= -L${top_builddir}/src -lcunit
+
+noinst_PROGRAMS=runcoolrain
+
+runcoolrain_SOURCES=runcoolrain.c
+runcoolrain_CFLAGS= -DCOOLRAIN_USE_GLIB=1 @GLIB_CFLAGS@ @GOBJECT_CFLAGS@
+runcoolrain_LDADD= -lcoolrain-glib-nonthread @GOBJECT_LIBS@ @GLIB_LIBS@
+runcoolrain_DEPENDENCIES=../src/libcoolrain-glib-nonthread.a
+
+
--- /dev/null
+${Greeting.Say}, ${Greeting.Ack}
+
--- /dev/null
+
+[Greeting]
+Say=Hello
+Say[ja]=こんにちわ
+
+Ack=World!
+Ack[ja]=世界
+
--- /dev/null
+BEGIN
+<CR:loop name="nth" from="0" to="10" step="3"><CR:pass>Hello, #${nth} World!</CR:pass><CR:ignore>
+Invalid output</CR:ignore>
+</CR:loop>
+END
--- /dev/null
+/**
+ *
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include <glib/gmacros.h>
+#include <glib/gkeyfile.h>
+#include <glib/gerror.h>
+
+#include <CoolRain/CoolRain.h>
+
+
+GKeyFile* keyfile = NULL;
+char const *locale = NULL;
+
+
+static void my_error_handler(struct coolrain_template *tmpl G_GNUC_UNUSED, char const *message)
+{
+ fprintf(stderr, "%s\n", message);
+}
+
+
+static int my_lookup_handler(coolrain_variant_t * restrict v, coolrain_refstring_t const * restrict key)
+{
+ char kf_group[128];
+ char kf_key[128];
+ char *kf_v;
+ char const *p;
+ char *pp;
+ GError *error = NULL;
+
+ p = key->begin;
+ pp = kf_group;
+ while (p < key->end && *p != '.' && pp < kf_group + 127) *pp++ = *p++;
+ if (p >= key->end) return -1;
+ *pp = '\0';
+
+ p++;
+ pp = kf_key;
+ while (p < key->end && pp < kf_key + 127) *pp++ = *p++;
+ *pp = '\0';
+
+ kf_v = g_key_file_get_locale_string(keyfile, kf_group, kf_key, locale, &error);
+ if (error != NULL) {
+ fprintf(stderr, "%s", error->message);
+ if (kf_v != NULL) g_free(kf_v);
+ return -1;
+ }
+
+
+ coolrain_variant_set_dynamic_string(v, kf_v);
+ g_free(kf_v);
+
+ return 0;
+}
+
+
+
+int main(int argc, char *argv[])
+{
+ int r;
+ struct coolrain_template tmpl;
+ struct coolrain_tagset tagset;
+ struct coolrain_stash stash;
+ struct coolrain_writer writer;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s <input-file>\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ if (argc >= 3) {
+ GError *error = NULL;
+ keyfile = g_key_file_new();
+ g_key_file_load_from_file(keyfile, argv[2], 0, &error);
+ if (error != NULL) {
+ fprintf(stderr, "%s", error->message);
+ g_error_free(error);
+ g_key_file_free(keyfile);
+ return EXIT_FAILURE;
+ }
+
+ }
+ if (argc >= 4) locale = argv[3];
+
+ coolrain_tagset_initialize(&tagset);
+ coolrain_tagset_register_tags(&tagset, coolrain_tagset_default_tags);
+ coolrain_tagset_register_filters(&tagset, coolrain_tagset_default_filters);
+
+ coolrain_template_initialize(&tmpl, &tagset);
+ coolrain_template_set_error_handler(&tmpl, my_error_handler, NULL);
+ if (coolrain_template_compile(&tmpl, argv[1]) != COOLRAIN_SUCCESS) {
+ coolrain_template_destroy(&tmpl);
+ coolrain_tagset_destroy(&tagset);
+ if (keyfile != NULL)
+ g_key_file_free(keyfile);
+ return EXIT_FAILURE;
+ }
+
+ coolrain_stash_initialize(&stash);
+ if (keyfile != NULL)
+ coolrain_stash_set_lookup_handler(&stash, my_lookup_handler);
+
+ coolrain_fd_writer_initialize(&writer, 1, false);
+
+ r = coolrain_template_run(&tmpl, &stash, &writer);
+
+ coolrain_writer_destroy(&writer);
+ coolrain_stash_destroy(&stash);
+ coolrain_template_destroy(&tmpl);
+ coolrain_tagset_destroy(&tagset);
+
+ if (keyfile != NULL)
+ g_key_file_free(keyfile);
+
+ return (r == COOLRAIN_SUCCESS)? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+
+
+
--- /dev/null
+<CR:let foo='hello'
+ bar='world'
+>${foo}, ${bar}!</CR:let>
--- /dev/null
+/**
+ *
+ */
+
+#if !defined(__COOLRAIN__COOLRAIN__H__)
+#define __COOLRAIN__COOLRAIN__H__
+
+#include <CoolRain/thread.h>
+#include <CoolRain/error.h>
+#include <CoolRain/refstring.h>
+#include <CoolRain/tagset.h>
+#include <CoolRain/fragment.h>
+#include <CoolRain/template.h>
+#include <CoolRain/stash.h>
+#include <CoolRain/writer.h>
+
+#endif
+
--- /dev/null
+/**
+ * \brief C++ wrapper of CoolRain templat engine.
+ *
+ */
+#if !defined(__COORAIN__COOLRAIN__HXX__)
+#define __COOLRAIN__COOLRAIN__HXX__
+
+#include <boost/noncopyable.hxx>
+#include <stdarg.h>
+#include <CoolRain/Coolrain.h>
+
+
+namespace CoolRain {
+
+ class RefString : public coolrain_refstring_t {
+ public:
+ inline RefString() { begin = NULL; end = NULL; }
+ inline RefString(char const* from, char const* to) { begin = from; end = to; }
+ inline RefString(char const* from, size_t length) { begin = from; end = from + length; }
+
+ inline void fix() { coolrain_refstring_fix(this); }
+ inline size_t length() const { return coolrain_refstring_length(this); }
+ inline bool equal_(char const* rbegin, char const* rend) { return coolrain_refstring_equal_(this, rbegin, rend); }
+ inline bool equal(RefString const& r) { return coolrain_refstring_equal(this, &r); }
+ inline unsigned int hash() const { return coolrain_refstring_hash(this); }
+ };
+
+
+
+
+ /**
+ *
+ *
+ */
+
+ /**
+ */
+ class Writer :
+ public coolrain_writer,
+ public boost::noncopyable {
+
+ protected:
+ inline Writer() { }
+
+ public:
+ inline Writer(char *buffer, size_t length) {
+ coolrain_writer_initialize(this, buffer, length);
+ }
+
+ inline ~Writer() { coolrain_writer_destroy(this); }
+
+
+ inline int chain(Writer *next_writer) { return coolrain_writer_chain(this, next_writer); }
+ inline int write(char const *buffer, size_t length) { return coolrain_writer_write(this, buffer, length); }
+ inline int flush_nonlock() { return coolrain_writer_flush_nonlock(this); }
+ inline int flush() { return coolrain_writer_flush(this); }
+
+ inline size_t used_length() { return coolrain_writer_used_length(this); }
+ inline size_t unused_length() { return coolrain_writer_unused_length(this); }
+
+ protected:
+ inline int drop(size_t length) { return coolrain_writer_drop(this, length); }
+ inline int reset() { return coolrain_writer_reset(this); }
+ inline char *reserve(size_t length) { return coolrain_writer_reserve(this, length); }
+ };
+
+ /**
+ */
+ class FDWriter : public Writer {
+ protected:
+ inline FDWriter() { }
+
+ public:
+ inline FDWriter(int fd, bool autoclose = false) {
+ coolrain_fd_writer_initialize(this, fd, autoclose);
+ }
+ };
+
+# if HAVE_FCGI_H
+ /**
+ *
+ */
+ class FCGIWriter : public Writer {
+ protected:
+ inline FCGIWriter() { }
+
+ public:
+ FCGIWriter(FCGX_Stream *stream) {
+ coolrain_FCGI_writer_initialize(&this, stream);
+ }
+ };
+#endif
+
+
+
+ /**
+ *
+ *
+ */
+
+ typedef coolrain_tag_handler_t TagHandler;
+ typedef coolrain_tag_desc TagDesc;
+
+ typedef coolrain_filter_handler_t FilterHandler;
+ typedef coolrain_filter_desc FilterDesc;
+
+ /**
+ */
+ class Tagset :
+ public coolrain_tagset,
+ public boost::noncopyable {
+ public:
+ inline Tagset() { coolrain_tagset_initialize(this); }
+ inline ~Tagset() { coolrain_tagset_destroy(this); }
+
+ inline int addTag(char const* name, TagHandler handler, unsigned int flags) {
+ return coolrain_tagset_add_tag(this, name, handler, flags);
+ }
+ inline int registerTags(TagDesc* table) { return coolrain_tagset_register_tags(this, table); }
+ inline TagDesc* lookupTag(RefString const& name) { return coolrain_tagset_lookup_tag(this, &name); }
+
+
+ inline int addFilter(char const* name, FilterHandler handler, unsigned int flags) {
+ return coolrain_stash_add_filter(this, name, handler, flags);
+ }
+ inline int registerFilters(FilterDesc* table) { coolrain_tagset_register_filters(this, table); }
+ inline FilterDesc* lookupFilter(RefString const& name) { return coolrain_tagset_lookup_filter(this, &name);
+ };
+
+
+
+ /**
+ *
+ */
+ class Variant :
+ public coolrain_variant {
+ public:
+ enum {
+ TYPE_NULL = COOLRAIN_VARIANT_TYPE_NULL,
+ TYPE_INT = COOLRAIN_VARIANT_TYPE_INT,
+ TYPE_DOUBLE = COOLRAIN_VARIANT_TYPE_DOUBLE,
+ TYPE_REFSTRING = COOLRAIN_VARIANT_TYPE_REFSTRING,
+ TYPE_STATIC_STRING = COOLRAIN_VARIANT_TYPE_STATIC_STRING,
+ TYPE_DYNAMIC_STRING = COOLRAIN_VARIANT_TYPE_DYNAMIC_STRING,
+ TYPE_POINTER = COOLRAIN_VARIANT_TYPE_POINTER,
+ };
+
+ inline Variant() { coolrain_variant_initialize(this); }
+ inline ~Variant() { coolrain_variant_destroy(this); }
+
+ inline Variant(Variant const& r) {
+ coolrain_variant_initialize(this);
+ coolrain_variant_copy(this, &r);
+ }
+
+ inline Variant& operator=(Variant const&r) {
+ coolrain_variant_copy(&r);
+ return *this;
+ }
+
+
+ inline void unset() { coolrain_variant_unset(this); }
+
+ inline int getInt() const { coolrain_variant_get_int(this); }
+ inline void setInt(int new_value) { coolrain_variant_set_int(this, new_value); }
+
+ inline double getDouble() const { return coolrain_variant_get_double(this); }
+ inline void setDouble(double new_value) { coolrain_variant_set_double(this, new_value); }
+
+ inline RefString const* getRefString() const { return dynamic_cast<RefString const *>(coolrain_variant_get_refstring(this)); }
+ inline void setRefString(RefString const& new_value) { coolrain_variant_set_refstring(this, &new_value); }
+
+ inline char const* getString() const { return coolrain_variant_get_string(this); }
+ inline void setStaticString(char const* new_value) { return coolrain_variant_set_static_string(this, new_value); }
+ inline void setDynamicString(char const* new_value) { return coolrain_variant_set_dynamic_string(this, new_value); }
+
+ inline void* getPointer() const { coolrain_variant_get_pointer(this); }
+ inline void setPointer(void *new_value, void (*destroy_handler)(void*)) { coolrain_variant_set_pointer(this, new_value, destroy_handler); }
+
+ inline int write(Writer* writer) { return coolrain_variant_write(this, writer); }
+ };
+
+
+
+ typedef coolrain_stash_value_list StashValueList;
+
+ /**
+ */
+ class Stash :
+ public coolrain_stash,
+ public boost::noncopyable {
+ public:
+ inline Stash() { coolrain_stash_initialize(this); }
+ inline ~Stash() { coolrain_stash_destroy(this); }
+
+ inline Variant const* get(RefString const& key) const { return coolrain_stash_get_value(this, &key); }
+ inline Variant const* get_(char const *key) const { return coolrain_stash_get_value_(this, key); }
+
+ inline void set(StashValueList *values, size_t count) {
+ coolrain_stash_set_values(this, values, count);
+ }
+ inline void restore(StashValueList *values, size_t count) {
+ coolrain_stash_restore(this, values, count);
+ }
+ };
+
+
+
+ /**
+ */
+ typedef coolrain_template_error_handler_t TemplateErrorHandler;
+
+ class EvalContext :
+ public coolrain_eval_context
+ {
+ public:
+ inline EvalContext() {}
+ inline EvalContext(EvalContext const& r) { *this = r; }
+
+ inline EvalContext& operator=(EvalContext const& r) {
+ coolrain_eval_context_copy(this, &r);
+ }
+ };
+
+
+
+
+ /**
+ */
+ class Template :
+ public coolrain_template,
+ public boost::noncopyable {
+ public:
+ inline Template(Tagset *tagset) { coolrain_template_initialize(this, tagset); }
+ inline ~Template() { coolrain_template_destroy(this); }
+
+ inline int clear() { return coolrain_template_clear(this); }
+
+ inline int setErrorHandler(TemplateErrorHandler handler, void *user_data) {
+ return coolrain_template_set_error_handler(this, handler, user_data);
+ }
+ inline int raiseError(char const* fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ return coolrain_template_vraise_error(this, fmt, va);
+ }
+ inline int vraiseError(char const *fmt, va_list va) {
+ return coolrain_template_vraise_error(this, fmt, va);
+ }
+
+ inline int compile(char const *path) { return coolrain_template_compile(this, path); }
+ inline int save(char const *path) { return coolrain_template_save(this, path); }
+ inline int load(char const *path) { return coolrain_template_load(this, path); }
+
+ inline int parse(
+ char const* name,
+ char const* source,
+ size_t length,
+ void (*destroy_callback)(Template*),
+ void *destroy_data)
+ {
+ return coolrain_template_parse(this, name, source, length, destroy_callback, destroy_data);
+ }
+
+ inline int eval_block(EvalContext const& cntx) { return coolrain_eval_block(this, &cntx); }
+ inline int eval_content(EvalContext const& cntx) { return coolrain_eval_content(this, &cntx); }
+ inline int run(Stash* stash, Writer* writer) { return coolrain_template_run(this, stash, writer); }
+ };
+
+}
+
+
+#endif
+
--- /dev/null
+
+CoolRain_includedir=$(includedir)/CoolRain
+CoolRain_include_HEADERS=\
+ config.h \
+ error.h \
+ thread.h \
+ writer.h \
+ fragment.h \
+ refstring.h \
+ tagset.h \
+ stash.h \
+ template.h \
+ variant.h \
+ memory.h \
+ CoolRain.h
+
--- /dev/null
+/**
+ * \brief Error code and helper module of CoolRain.
+ *
+ * Copyright (c) HATTORI, Hiroki
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ */
+#if !defined(__COOLRAIN__ERROR__H__)
+#define __COOLRAIN__ERROR__H__
+
+/** @{ \name Error codes */
+#define COOLRAIN_SUCCESS (0) /*!< No error */
+#define COOLRAIN_ERROR_NOMEM (-1) /*!< No memory */
+#define COOLRAIN_ERROR_NOFILE (-2) /*!< No file exists */
+#define COOLRAIN_ERROR_BAD_LENGTH (-3) /*!< Could'nt determine file length */
+#define COOLRAIN_ERROR_CANT_READ (-4) /*!< Read error */
+#define COOLRAIN_ERROR_PARSE_ERROR (-5) /*!< Parser erro (Syntax error on template) */
+#define COOLRAIN_ERROR_INVALID_ARG (-6) /*!< Invalid argument(s) */
+#define COOLRAIN_ERROR_BUFFER_FULL (-7) /*!< Destination buffer full */
+#define COOLRAIN_ERROR_UNBUFFERED (-8) /*!< Target buffer is unbuffered */
+#define COOLRAIN_ERROR_IO (-9) /*!< IO Error */
+#define COOLRAIN_ERROR_RUNTIME (-10) /*!< Runtime error */
+/** @} */
+
+#endif
+
--- /dev/null
+/**
+ *
+ */
+#if !defined(__COOLRAIN__FRAGMENT__H__)
+#define __COOLRAIN__FRAGMENT__H__
+
+
+#include <CoolRain/refstring.h>
+
+
+struct coolrain_tag_desc;
+
+
+/*
+ *
+ */
+struct coolrain_fragment_attribute {
+ struct coolrain_fragment_attribute *next;
+ coolrain_refstring_t name;
+ coolrain_refstring_t value;
+};
+
+
+
+
+/*
+ *
+ */
+typedef enum {
+ COOLRAIN_FRAGMENT_TYPE_CONSTANT_STRING = 0,
+ COOLRAIN_FRAGMENT_TYPE_TAG = 1,
+ COOLRAIN_FRAGMENT_TYPE_VARREF = 2,
+} coolrain_fragment_type;
+
+
+
+
+
+
+struct coolrain_fragment {
+ struct coolrain_fragment *next;
+ coolrain_fragment_type type;
+
+ union {
+
+ coolrain_refstring_t str;
+ coolrain_refstring_t varname;
+
+ struct {
+ struct coolrain_tag_desc const *tagdesc;
+ struct coolrain_fragment_attribute *attrs;
+ struct coolrain_fragment *content;
+ } tag;
+
+ } data;
+};
+
+
+struct coolrain_fragment_attribute *coolrain_fragment_attribute_alloc();
+void coolrain_fragment_attribute_free(struct coolrain_fragment_attribute *attrs);
+
+
+struct coolrain_fragment *coolrain_fragment_alloc(coolrain_fragment_type type);
+void coolrain_fragment_free(struct coolrain_fragment *fragment);
+
+coolrain_refstring_t const *coolrain_fragment_find_attribute(struct coolrain_fragment const* fragment, char const *name);
+
+#endif
+
--- /dev/null
+/**
+ * \brief Memory management module for CoolRain template engine.
+ *
+ */
+#if !defined(__COOLRAIN__MEMORY__H__)
+#define __COOLRAIN__MEMORY__H__
+
+
+# if COOLRAIN_USE_GLIB
+# include <glib/gmem.h>
+# include <glib/gstrfuncs.h>
+
+
+#define coolrain_malloc g_malloc
+#define coolrain_free g_free
+#define coolrain_strdup g_strdup
+
+
+# else
+# error "No memory library."
+#endif
+
+
+#endif
+
+
--- /dev/null
+/**
+ * \brief RefString object module for CoolRain.
+ */
+#if !defined(__COOLRAIN__REFSTRING__H__)
+#define __COOLRAIN__REFSTRING__H__
+
+#include <string.h>
+#include <stdlib.h>
+#include <glib/gtypes.h>
+#include <glib/gmacros.h>
+
+#include <CoolRain/refstring.h>
+
+
+/**
+ *
+ */
+typedef struct {
+ char const *begin; /*!< Pointer to start string. */
+ char const *end; /*!< Pointer to end of string, or null if null terminated. */
+} coolrain_refstring_t;
+
+
+
+/**
+ * Fix end pointer for null terminated string.
+ * \param str Refstring object
+ */
+inline static void coolrain_refstring_fix(coolrain_refstring_t *str)
+{
+ if (str->end == NULL)
+ str->end = str->begin + strlen(str->begin);
+}
+
+
+
+
+/**
+ * Length of refstring
+ * \param str Refstring object
+ * \return Length of string.
+ */
+inline static size_t coolrain_refstring_length(coolrain_refstring_t const *str)
+{
+ return (str->end == NULL)? strlen(str->begin) : (size_t)(str->end - str->begin);
+}
+
+/**
+ * Compare refstring.
+ * \param l Left hand string.
+ * \param rbegin Pointer to begiging of right hand string
+ * \param rend Pointer to end of right hand, or null if null terminated
+ * \return TURE if l and (rbegin, rend) is equal, otherwise FALSE.
+ */
+static inline gboolean coolrain_refstring_equal_(coolrain_refstring_t const* l, char const *rbegin, char const *rend)
+{
+ size_t rlen = (rend == NULL)? strlen(rbegin) : (size_t)(rend - rbegin);
+ return (coolrain_refstring_length(l) != rlen)? FALSE : (memcmp(l->begin, rbegin, rlen) == 0);
+}
+
+/**
+ * Compare refstring (alternate)
+ * \param l Left hand string
+ * \param r Right hand string.
+ * \return TRUE if l and r is equal, otherwise FALSE.
+ */
+static inline gboolean coolrain_refstring_equal(coolrain_refstring_t const *l, coolrain_refstring_t const *r)
+{
+ return coolrain_refstring_equal_(l, r->begin, r->end);
+}
+
+
+
+/**
+ * Calcurate hash value of refstring
+ * \param str Refstring object
+ * \return Hash value.
+ */
+static inline guint coolrain_refstring_hash(coolrain_refstring_t const *str)
+{
+ guint r = 0;
+ char const *p = str->begin;
+
+ if (str->end == NULL) { while (*p) { r <<= 2; r += *(p++); } }
+ else { while (p != str->end) { r <<= 2; r += *(p++); } }
+
+ return r;
+}
+
+
+static inline int coolrain_refstring_toint(coolrain_refstring_t const *str)
+{
+ char buf[64];
+ size_t len = MIN(63, coolrain_refstring_length(str));
+ memcpy(buf, str->begin, len);
+ buf[len] = '\0';
+
+ return strtol(buf, (char**)NULL, 10);
+}
+#endif
+
--- /dev/null
+/**
+ * \par Threading:
+ * The stash object is no thread safe.
+ */
+#if !defined(__COOLRAIN__STASH__H__)
+#define __COOLRAIN__STASH__H__
+
+#include <glib/gtypes.h>
+#include <glib/ghash.h>
+#include <glib/gmacros.h>
+#include <glib/gslist.h>
+
+#include <CoolRain/thread.h>
+#include <CoolRain/error.h>
+#include <CoolRain/variant.h>
+
+/**
+ *
+ */
+typedef int (*coolrain_stash_lookup_handler_t)(coolrain_variant_t * restrict value, coolrain_refstring_t const * restrict key);
+
+struct coolrain_stash {
+ coolrain_mutex_t mutex;
+ GHashTable *hash;
+ GSList *context_stack;
+ coolrain_stash_lookup_handler_t lookup_handler;
+ GSList *alloced_values;
+};
+
+
+
+int coolrain_stash_initialize(struct coolrain_stash *stash);
+void coolrain_stash_destroy(struct coolrain_stash *stash);
+
+
+
+coolrain_variant_t const *coolrain_stash_get_value(struct coolrain_stash * restrict stash, coolrain_refstring_t const * restrict key);
+
+static inline coolrain_variant_t const *coolrain_stash_get_value_(struct coolrain_stash * restrict stash, char const * restrict key)
+{
+ coolrain_refstring_t k;
+ k.begin = key;
+ k.end = NULL;
+ coolrain_refstring_fix(&k);
+ return coolrain_stash_get_value(stash, &k);
+}
+
+
+
+
+struct coolrain_stash_value_list {
+ coolrain_refstring_t name;
+ coolrain_variant_t value;
+ coolrain_variant_t const *old_value;
+};
+
+void coolrain_stash_set_values(struct coolrain_stash * restrict stash, struct coolrain_stash_value_list * restrict values, size_t count);
+void coolrain_stash_restore(struct coolrain_stash * restrict stash, struct coolrain_stash_value_list * restrict values, size_t count);
+
+
+static inline void coolrain_stash_set_lookup_handler(struct coolrain_stash * restrict stash, coolrain_stash_lookup_handler_t handler)
+{
+ stash->lookup_handler = handler;
+}
+
+#endif
+
--- /dev/null
+/**
+ *
+ */
+#if !defined(__COOLRAIN__TAGSET__H__)
+#define __COOLRAIN__TAGSET__H__
+
+
+#include <glib/ghash.h>
+
+#include <CoolRain/error.h>
+#include <CoolRain/thread.h>
+#include <CoolRain/refstring.h>
+#include <CoolRain/stash.h>
+#include <CoolRain/writer.h>
+
+
+struct coolrain_fragment;
+struct coolrain_template;
+struct coolrain_eval_context;
+
+
+typedef int (*coolrain_tag_handler_t)(struct coolrain_eval_context const *cntx);
+
+struct coolrain_tag_desc {
+ coolrain_refstring_t name;
+ coolrain_tag_handler_t handler;
+ unsigned int flags;
+};
+
+#define COOLRAIN_TAG_DESC_FLAGS_ALLOCED (1 << 0)
+#define COOLRAIN_TAG_DESC_FLAGS_HAS_ELSE_BLOCK (1 << 1)
+
+
+
+struct coolrain_filter_desc {
+ coolrain_refstring_t name;
+ coolrain_writer_write_handler_t handler;
+ unsigned int flags;
+};
+
+#define COOLRAIN_FILTER_DESC_FLAGS_ALLOCED (1 << 0)
+
+
+struct coolrain_tagset {
+ coolrain_mutex_t mutex;
+ GHashTable *tags;
+ GHashTable *filters;
+};
+
+
+
+
+/*
+ *
+ */
+int coolrain_tagset_initialize(struct coolrain_tagset *tagset);
+void coolrain_tagset_destroy(struct coolrain_tagset *taget);
+
+
+/* !{ @name Tag handler */
+int coolrain_tagset_add_tag(
+ struct coolrain_tagset *tagset,
+ char const* name,
+ coolrain_tag_handler_t handler,
+ unsigned int flags);
+int coolrain_tagset_register_tags(
+ struct coolrain_tagset *tagset,
+ struct coolrain_tag_desc *table);
+struct coolrain_tag_desc const *coolrain_tagset_lookup_tag(
+ struct coolrain_tagset *tagset,
+ coolrain_refstring_t const *name);
+
+extern struct coolrain_tag_desc coolrain_tagset_default_tags[];
+/* !} */
+
+/* !{ @name Filter handler */
+int coolrain_tagset_add_filter(
+ struct coolrain_tagset *tagset,
+ char const *name,
+ coolrain_filter_handler_t handler,
+ unsigned int flags);
+int coolrain_tagset_register_filters(
+ struct coolrain_tagset *tagset,
+ struct coolrain_filter_desc *table);
+struct coolrain_filter_desc const *coolrain_tagset_lookup_filter(
+ struct coolrain_tagset *tagset,
+ coolrain_refstring_t const *name);
+
+extern struct coolrain_filter_desc coolrain_tagset_default_filters[];
+/* !} */
+
+
+/* !{ @name Built-in tag handler */
+int coolrain_builtin_tag_handler_ignore(struct coolrain_eval_context const *cntx);
+#define coolrain_builtin_tag_handler_pass coolrain_template_eval_content
+int coolrain_builtin_tag_handler_let(struct coolrain_eval_context const *cntx);
+int coolrain_builtin_tag_handler_var(struct coolrain_eval_context const *cntx);
+int coolrain_builtin_tag_handler_loop(struct coolrain_eval_context const *cntx);
+/* !} */
+#endif
+
--- /dev/null
+/**
+ *
+ */
+#if !defined(__COOLRAIN__TEMPLATE__H__)
+#define __COOLRAIN__TEMPLATE__H__
+
+#include <limits.h>
+#include <stdarg.h>
+
+#include <CoolRain/refstring.h>
+#include <CoolRain/thread.h>
+#include <CoolRain/tagset.h>
+#include <CoolRain/stash.h>
+#include <CoolRain/fragment.h>
+
+typedef void (*coolrain_template_error_handler_t)(
+ struct coolrain_template *tmpl, char const *message);
+
+struct coolrain_template {
+ coolrain_mutex_t mutex;
+
+ struct coolrain_fragment *root;
+ struct coolrain_tagset *tagset;
+
+ char name[PATH_MAX];
+ coolrain_refstring_t source;
+
+ struct {
+ char const *cursor;
+ unsigned int lineno;
+ } parser_state;
+
+ void (*destroy_callback)(struct coolrain_template *tmpl);
+ void *destroy_data;
+
+ coolrain_template_error_handler_t error_handler;
+ void *error_user_data;
+};
+
+
+
+struct coolrain_eval_context {
+ struct coolrain_template *tmpl;
+ struct coolrain_stash *stash;
+ struct coolrain_fragment *fragment;
+ struct coolrain_writer *writer;
+};
+
+static inline void coolrain_eval_context_copy(struct coolrain_eval_context *dst, struct coolrain_eval_context const *src)
+{
+ dst->tmpl = src->tmpl;
+ dst->stash = src->stash;
+ dst->fragment = src->fragment;
+ dst->writer = src->writer;
+}
+
+
+
+
+int coolrain_template_initialize(struct coolrain_template *tmpl, struct coolrain_tagset *tagset);
+int coolrain_template_destroy(struct coolrain_template *tmpl);
+int coolrain_template_clear(struct coolrain_template *tmpl);
+
+int coolrain_template_set_error_handler(
+ struct coolrain_template *tmpl,
+ coolrain_template_error_handler_t handler,
+ void *user_data);
+void coolrain_template_raise_error(
+ struct coolrain_template *tmpl,
+ char const *fmt,
+ ...);
+void coolrain_template_vraise_error(
+ struct coolrain_template *tmpl,
+ char const *fmt,
+ va_list va);
+
+int coolrain_template_compile(struct coolrain_template *tmpl, char const* path);
+int coolrain_template_save(struct coolrain_template *tmpl, char const* path);
+int coolrain_template_load(struct coolrain_template *tmpl, char const* path);
+int coolrain_template_parse(
+ struct coolrain_template *tmpl,
+ char const* name,
+ char const* source,
+ size_t length,
+ void (*destroy_callback)(struct coolrain_template *),
+ void* destroy_data);
+
+int coolrain_template_eval_block(struct coolrain_eval_context const *cntx);
+int coolrain_template_eval_content(struct coolrain_eval_context const *cntx);
+
+int coolrain_template_run(
+ struct coolrain_template *tmpl,
+ struct coolrain_stash *stash,
+ struct coolrain_writer *writer);
+
+#endif
+
--- /dev/null
+/**
+ * Threading wrapper module of CoolRain
+ *
+ */
+#if !defined(__COOLRAIN__THREAD__H__)
+#define __COOLRAIN__THREAD__H__
+
+
+#include <CoolRain/config.h>
+#include <stdbool.h>
+
+
+#if COOLRAIN_USE_GTHREAD
+
+# if !COOLRAIN_HAVE_GTHREAD
+# error "No gthread library"
+# endif
+
+#include <glib/gthread.h>
+
+typedef GStaticRecMutex coolrain_mutex_t;
+
+static inline void coolrain_mutex_init(coolrain_mutex_t *mutex)
+{
+ g_static_rec_mutex_init(mutex);
+}
+
+static inline void coolrain_mutex_destroy(coolrain_mutex_t *mutex)
+{
+ g_static_rec_mutex_free(mutex);
+}
+
+static inline void coolrain_mutex_lock(coolrain_mutex_t *mutex)
+{
+ g_static_rec_mutex_lock(mutex);
+}
+
+//static inline bool coolrain_mutex_timed_lock(
+// coolrain_mutex_t * restrict mutex,
+// struct timespec const * restrict abstime)
+
+static inline bool coolrain_mutex_trylock(coolrain_mutex_t *mutex)
+{
+ return g_static_rec_mutex_trylock(mutex)? true : false;
+}
+
+static inline void coolrain_mutex_unlock(coolrain_mutex_t *mutex)
+{
+ g_static_rec_mutex_unlock(mutex);
+}
+
+
+#elif COOLRAIN_USE_PTHREAD
+
+# if !COOLRAIN_HAVE_PTHREAD
+# error "No pthread library"
+# endif
+
+#include <pthread.h>
+
+typedef pthread_mutex_t coolrain_mutex_t;
+
+static inline void coolrain_mutex_init(coolrain_mutex_t *mutex)
+{
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
+
+ pthread_mutex_init(mutex, &attr);
+
+ pthread_mutexattr_destroy(&attr);
+}
+
+static inline void coolrain_mutex_destroy(coolrain_mutex_t *mutex)
+{
+ pthread_mutex_destroy(mutex);
+}
+
+static inline void coolrain_mutex_lock(coolrain_mutex_t *mutex)
+{
+ pthread_mutex_lock(mutex);
+}
+
+
+#define COOLRAIN_MUTEX_HAS_TIMEDLOCK 1
+
+static inline bool coolrain_mutex_timedlock(
+ coolrain_mutex_t * restrict mutex,
+ truct timespec const * restrict abstime)
+{
+ return (pthread_mutex_timedlock(mutex, abstime) == 0)? true : false;
+}
+
+
+
+static inline bool coolrain_mutex_trylock(coolrain_mutex_t *mutex)
+{
+ return (pthread_mutex_trylock(mutex) == 0)? true : false;
+}
+
+static inline void coolrain_mutex_unlock(coolrain_mutex_t *mutex)
+{
+ pthread_mutex_unlock(mutex);
+}
+
+#elif COOLRAIN_USE_APR_THREAD
+
+# if !COOLRAIN_HAVE_APR
+# error "No APR library"
+# endif
+
+#include <apr_thread_mutex.h>
+
+typedef apr_thread_mutex_t *coolrain_mutex_t;
+
+static inline void coolrain_mutex_init(coolrain_mutex_t *mutex)
+{
+ apr_thread_mutex_create(mutex, APT_THREAD_MUTEX_NESTED, pool);
+}
+
+static inline void coolrain_mutex_destroy(coolrain_mutex_t *mutex)
+{
+ apr_thread_mutex_destroy(*mutex);
+ *mutex = NULL;
+}
+
+static inline void coolrain_mutex_lock(coolrain_mutex_t *mutex)
+{
+ apr_thread_mutex_lock(*mutex);
+}
+
+//static inline bool coolrain_mutex_timedlock
+
+static inline bool coolrain_mutex_trylock(coolrain_mutex_t *mutex)
+{
+ return (apr_thread_mutex_trylock(*mutex) == APR_STATUS_IS_BUSY)
+ ? false : true;
+}
+
+
+static inline void coolrain_mutex_unlock(coolrain_mutex_t *mutex)
+{
+ apr_thread_mutex_unlock(*mutex);
+}
+
+#else
+
+typedef int coolrain_mutex_t[0];
+#define coolrain_mutex_init(x) do {} while (0)
+#define coolrain_mutex_destroy(x) do {} while (0)
+#define coolrain_mutex_lock(x) do {} while (0)
+//#define coolrain_mutex_timedlock(x, y) (true)
+#define coolrain_mutex_trylock(x) (true)
+#define coolrain_mutex_unlock(x) do {} while (0)
+
+#endif
+
+
+#endif
+
--- /dev/null
+/**
+ *
+ *
+ */
+#if !defined(__COOLRAIN__VARIANT__H__)
+#define __COOLRAIN__VARIANT__H__
+
+#include <CoolRain/refstring.h>
+#include <CoolRain/memory.h>
+
+struct coolrain_writer;
+
+
+#define COOLRAIN_VARIANT_TYPE_NULL (0)
+#define COOLRAIN_VARIANT_TYPE_INT (1)
+#define COOLRAIN_VARIANT_TYPE_DOUBLE (2)
+#define COOLRAIN_VARIANT_TYPE_REFSTRING (3)
+#define COOLRAIN_VARIANT_TYPE_STATIC_STRING (4)
+#define COOLRAIN_VARIANT_TYPE_DYNAMIC_STRING (5)
+#define COOLRAIN_VARIANT_TYPE_POINTER (6)
+
+typedef struct {
+ int type;
+ union {
+ int v_int;
+ double v_double;
+ coolrain_refstring_t v_refstring;
+ char const *v_static_string;
+ char *v_dynamic_string;
+ struct {
+ void *p;
+ void (*destroy_handler)(void *data);
+ } v_pointer;
+ } data;
+} coolrain_variant_t;
+
+
+#define COOLRAIN_VARIANT_INITIALIZER { COOLRAIN_VARIANT_TYPE_NULL, { 0 } }
+
+static inline void coolrain_variant_initialize(coolrain_variant_t *v)
+{
+ v->type = COOLRAIN_VARIANT_TYPE_NULL;
+}
+
+
+void coolrain_variant_unset(coolrain_variant_t *v);
+
+
+static inline void coolrain_variant_destroy(coolrain_variant_t *v)
+{
+ coolrain_variant_unset(v);
+}
+
+
+
+
+
+
+static inline int coolrain_variant_get_int(coolrain_variant_t const *v)
+{
+ return (v->type == COOLRAIN_VARIANT_TYPE_INT)? v->data.v_int : 0;
+}
+
+
+static inline void coolrain_variant_set_int(coolrain_variant_t *v, int new_value)
+{
+ coolrain_variant_unset(v);
+ v->type = COOLRAIN_VARIANT_TYPE_INT;
+ v->data.v_int = new_value;
+}
+
+
+
+
+
+static inline double coolrain_variant_get_double(coolrain_variant_t const *v)
+{
+ return (v->type == COOLRAIN_VARIANT_TYPE_DOUBLE)? v->data.v_double : 0.0;
+}
+
+
+static inline void coolrain_variant_set_double(coolrain_variant_t *v, double new_value)
+{
+ coolrain_variant_unset(v);
+ v->type = COOLRAIN_VARIANT_TYPE_DOUBLE;
+ v->data.v_double = new_value;
+}
+
+
+
+
+
+static inline coolrain_refstring_t const *coolrain_variant_get_refstring(coolrain_variant_t const *v)
+{
+ return (v->type == COOLRAIN_VARIANT_TYPE_REFSTRING)? &v->data.v_refstring : NULL;
+}
+
+void coolrain_variant_set_refstring(coolrain_variant_t *v, coolrain_refstring_t const *new_value);
+
+
+
+
+char const *coolrain_variant_get_string(coolrain_variant_t const *v);
+static inline void coolrain_variant_set_static_string(coolrain_variant_t *v, char const *new_value)
+{
+ coolrain_variant_unset(v);
+ v->type = COOLRAIN_VARIANT_TYPE_STATIC_STRING;
+ v->data.v_static_string = new_value;
+}
+
+
+static inline void coolrain_variant_set_dynamic_string(coolrain_variant_t *v, char *new_value)
+{
+ coolrain_variant_unset(v);
+ v->type = COOLRAIN_VARIANT_TYPE_DYNAMIC_STRING;
+ v->data.v_dynamic_string = coolrain_strdup(new_value);
+}
+
+
+
+static inline void *coolrain_variant_get_pointer(coolrain_variant_t *v)
+{
+ return (v->type == COOLRAIN_VARIANT_TYPE_POINTER)? v->data.v_pointer.p : NULL;
+}
+
+
+static inline void coolrain_variant_set_pointer(coolrain_variant_t *v, void *new_value, void (*destroy_handler)(void *))
+{
+ coolrain_variant_unset(v);
+ v->type = COOLRAIN_VARIANT_TYPE_POINTER;
+ v->data.v_pointer.p = new_value;
+ v->data.v_pointer.destroy_handler = destroy_handler;
+}
+
+
+void coolrain_variant_copy(coolrain_variant_t *dst, coolrain_variant_t const *src);
+
+
+/* */
+int coolrain_variant_write(coolrain_variant_t const *v, struct coolrain_writer *writer);
+
+#endif
+
--- /dev/null
+/**
+ *
+ *
+ */
+#if !defined(__COOLRAIN__WRITER__H__)
+#define __COOLRAIN__WRITER__H__
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#include <stdbool.h>
+
+#include <CoolRain/error.h>
+#include <CoolRain/thread.h>
+
+struct coolrain_writer;
+
+typedef int (*coolrain_writer_buffer_full_handler_t)(struct coolrain_writer *writer);
+typedef int (*coolrain_writer_destroy_handler_t)(struct coolrain_writer *writer);
+typedef int (*coolrain_writer_write_handler_t)(
+ struct coolrain_writer *writer,
+ char const **pbuffer, size_t length);
+
+
+#define COOLRAIN_WRITER_FLAGS_AUTO_FREE (1 << 0)
+#define COOLRAIN_WRITER_FLAGS_AUTO_CLOSE (1 << 1)
+
+
+/**
+ *
+ */
+struct coolrain_writer {
+ coolrain_mutex_t mutex;
+
+ char *buffer; /*!< Pointer to internal buffer */
+ size_t alloc_length; /*!< Total allocation length of buffer */
+ char *cursor; /*!< Write point */
+ unsigned int flags;
+
+ union {
+ void *pointee;
+ unsigned int handle;
+ char minibuffer[sizeof(unsigned int)];
+ } write_handler_data, buffer_full_handler_data;
+
+ coolrain_writer_buffer_full_handler_t buffer_full_handler;
+ coolrain_writer_write_handler_t write_handler;
+ coolrain_writer_destroy_handler_t destroy_handler;
+};
+
+
+int coolrain_writer_initialize(struct coolrain_writer *writer, char *buffer, size_t length);
+int coolrain_writer_destroy(struct coolrain_writer *writer);
+
+int coolrain_writer_chain(struct coolrain_writer *writer, struct coolrain_writer *next_writer);
+
+
+int coolrain_writer_write(struct coolrain_writer *writer, char const *buffer, size_t length);
+
+
+static inline int coolrain_writer_flush_nonlock(struct coolrain_writer *writer)
+{
+ return (*writer->buffer_full_handler)(writer);
+}
+
+
+static inline int coolrain_writer_flush(struct coolrain_writer *writer)
+{
+ int r;
+
+ coolrain_mutex_lock(&writer->mutex);
+ r = writer->buffer_full_handler(writer);
+ coolrain_mutex_unlock(&writer->mutex);
+
+ return r;
+}
+
+
+
+/*
+ * !{ @name Direct buffer operation (for filter developpers)
+ * */
+int coolrain_writer_drop(struct coolrain_writer *writer, size_t length);
+
+
+
+/**
+ * Discard all data from internal buffer
+ * \param writer Writer object.
+ *
+ * \par Threading:
+ * This API is not thread safe.
+ */
+static inline void coolrain_writer_reset(struct coolrain_writer *writer)
+{
+ writer->cursor = writer->buffer;
+}
+
+
+
+/**
+ * Wrote size in buffer
+ * \param writer Writer object
+ * \return Length in bytes.
+ * \par Threading:
+ * This API is not thread safe.
+ */
+static inline size_t coolrain_writer_used_length(struct coolrain_writer *writer)
+{
+ return writer->cursor - writer->buffer;
+}
+
+
+
+/**
+ * Remaining buffer length.
+ * \param writer Writer object
+ * \return Length in bytes.
+ *
+ * \par Threading:
+ * This API is not thread safe.
+ */
+static inline size_t coolrain_writer_unused_length(struct coolrain_writer *writer)
+{
+ return (writer->buffer + writer->alloc_length) - writer->cursor;
+}
+
+
+static inline char *coolrain_writer_reserve(struct coolrain_writer *writer, size_t length)
+{
+ char *p;
+
+ if (coolrain_writer_unused_length(writer) < length) {
+ coolrain_writer_flush(writer);
+ if (coolrain_writer_unused_length(writer) < length)
+ return NULL;
+ }
+
+ p = writer->cursor;
+ writer->cursor += length;
+
+ return p;
+}
+/* !} */
+
+
+
+
+
+int coolrain_fd_writer_initialize(struct coolrain_writer *writer, int fd, bool autoclose);
+
+# if HAVE_FCGIAPP_H
+# include <fcgiapp.h>
+int coolrain_FCGI_writer_initialize(struct coolrain_writer *writer, FCGX_Stream *stream);
+# endif
+
+
+
+
+
+/*
+ *
+ */
+typedef coolrain_writer_write_handler_t coolrain_filter_handler_t;
+
+int coolrain_html_filter(struct coolrain_writer *writer, char const **pbuffer, size_t length);
+int coolrain_url_filter(struct coolrain_writer *writer, char const **pbufferr, size_t length);
+int coolrain_javascript_filter(struct coolrain_writer *writer, char const **pbuffer, size_t length);
+
+#endif
+
--- /dev/null
+
+lib_LIBRARIES=
+
+SOURCES= builtin_tag.c template.c fragment.c tagset.c stash.c variant.c writer_fd.c writer_fcgi.c writer.c filter.c
+
+if HAVE_GLIB
+lib_LIBRARIES+= libcoolrain-glib-nonthread.a
+libcoolrain_glib_nonthread_a_SOURCES=${SOURCES}
+libcoolrain_glib_nonthread_a_CFLAGS=-DCOOLRAIN_USE_GLIB=1 @GLIB_CFLAGS@ @GOBJECT_CFLAGS@
+
+if HAVE_GTHREAD
+lib_LIBRARIES+= libcoolrain-glib-gthread.a
+libcoolrain_glib_gthread_a_SOURCES=${SOURCES}
+libcoolrain_glib_gthread_a_CFLAGS=-DCOOLRAIN_USE_GLIB=1 -DCOOLRAIN_USE_GTHREAD=1 @GLIB_CFLAGS@ @GOBJECT_CFLAGS@ @GTHREAD_CFLAGS@
+endif
+
+endif
+
+
--- /dev/null
+/**
+ *
+ *
+ */
+#if defined(HAVE_CONFIG_H)
+# include <CoolRain/config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib/gmacros.h>
+
+#include <CoolRain/CoolRain.h>
+
+
+
+/**
+ * \brief Builtin 'ignore' tag handler
+ * \param cntx Evalutor context.
+ * \param Wlways COOLRAIN_SUCCESS
+ *
+ * The ignore block tag ignore and drop contents.
+ */
+int coolrain_builtin_tag_handler_ignore(struct coolrain_eval_context const *cntx G_GNUC_UNUSED)
+{
+ return COOLRAIN_SUCCESS;
+}
+
+
+
+
+
+/**
+ * \brief Builtin 'let' tag handler
+ * \param cntx Evalutor context.
+ * \return Error code.
+ *
+ * The let block tag takes few name=value attributes to make template variables.
+ * The variable can take by 'var' tag in contents.
+ *
+ * <let foo='hello' bar='world'><var name='foo' />, <var name='bar' /></let>
+ */
+int coolrain_builtin_tag_handler_let(struct coolrain_eval_context const *cntx)
+{
+ int r;
+ size_t count = 0;
+ size_t i;
+ struct coolrain_fragment_attribute const *attr;
+ struct coolrain_stash_value_list *vl;
+
+
+ attr = cntx->fragment->data.tag.attrs;
+ while (attr != NULL) {
+ count++;
+ attr = attr->next;
+ }
+
+ vl = coolrain_malloc(sizeof(struct coolrain_stash_value_list) * count);
+
+ attr = cntx->fragment->data.tag.attrs;
+ for (i = 0; i < count; i++, (attr = attr->next)) {
+ vl[i].name = attr->name;
+
+ coolrain_variant_initialize(&vl[i].value);
+ coolrain_variant_set_refstring(&vl[i].value, &attr->value);
+
+ vl[i].old_value = NULL;
+ }
+
+ coolrain_stash_set_values(cntx->stash, vl, count);
+
+ r = coolrain_template_eval_content(cntx);
+
+ coolrain_stash_restore(cntx->stash, vl, count);
+
+ for (i = 0; i < count; i++)
+ coolrain_variant_destroy(&vl[i].value);
+ coolrain_free(vl);
+
+ return r;
+}
+
+
+
+
+/**
+ * \brief Builtin 'var' tag handler.
+ * \param cntx Evalutor context.
+ * \return Error code.
+ *
+ * Take variable under 'let' block tag.
+ */
+int coolrain_builtin_tag_handler_var(struct coolrain_eval_context const *cntx)
+{
+ coolrain_variant_t const *var;
+ coolrain_refstring_t const *name = coolrain_fragment_find_attribute(cntx->fragment, "name");
+
+ if (name == NULL) return COOLRAIN_SUCCESS;
+
+ var = coolrain_stash_get_value(cntx->stash, name);
+ return (var == NULL)? COOLRAIN_SUCCESS : coolrain_variant_write(var, cntx->writer);
+}
+
+
+
+
+/**
+ *
+ */
+int coolrain_builtin_tag_handler_loop(struct coolrain_eval_context const *cntx)
+{
+ coolrain_refstring_t const *v;
+ struct coolrain_stash_value_list vlist[] = { { { NULL, NULL }, COOLRAIN_VARIANT_INITIALIZER, NULL } };
+ int n, step, to;
+
+ v = coolrain_fragment_find_attribute(cntx->fragment, "name");
+ if (v == NULL) {
+ vlist[0].name.begin = "i";
+ vlist[0].name.end = vlist[9].name.begin + 1;
+ } else {
+ vlist[0].name = *v;
+ }
+
+ v = coolrain_fragment_find_attribute(cntx->fragment, "from");
+ n = (v == NULL)? 0 : coolrain_refstring_toint(v);
+
+ v = coolrain_fragment_find_attribute(cntx->fragment, "to");
+ if (v == NULL) {
+ coolrain_template_raise_error(cntx->tmpl, "No 'to' attribute on loop tag\n");
+ return COOLRAIN_ERROR_RUNTIME;
+ }
+ to = coolrain_refstring_toint(v);
+
+ v = coolrain_fragment_find_attribute(cntx->fragment, "step");
+ step = (v == NULL)? 1 : abs(coolrain_refstring_toint(v));
+
+ coolrain_stash_set_values(cntx->stash, vlist, 1);
+ if (n > to) {
+ while (n >= to) {
+ coolrain_variant_set_int(&vlist[0].value, n);
+ coolrain_template_eval_content(cntx);
+ n -= step;
+ }
+ } else {
+ while (n <= to) {
+ coolrain_variant_set_int(&vlist[0].value, n);
+ coolrain_template_eval_content(cntx);
+ n += step;
+ }
+ }
+ coolrain_stash_restore(cntx->stash, vlist, 1);
+
+ return COOLRAIN_SUCCESS;
+}
+
+
--- /dev/null
+/**
+ *
+ */
+#include <CoolRain/config.h>
+
+#include <string.h>
+#include <ctype.h>
+
+#include <CoolRain/writer.h>
+
+
+
+struct html_entity {
+ char const *name;
+ char const *utf8_str;
+};
+static struct html_entity const html_entities[] = {
+ /* Basic symbols */
+ { "amp", "&" }, { "lt", "<" }, { "gt", ">" }, { "quot", "\"" }, { "apos", "'" },
+ // ISO-8859-1
+ { "nbsp", " " }, { "iexcl", "\xc2\xa1" }, { "cent", "\xc2\xa2" },
+ { "pound", "\xc2\xa0" }, { "curren", "\xc2\xa4" }, { "yen", "\xc2\xa5" },
+ { "brvbar", "\xc2\xa6"}, { "sect", "\xc2\xa7" }, { "uml", "\xc2\xa8" },
+ { "copy", "\xc2\xa9" }, { "ordf", "\xc2\xaa" }, { "laquo", "\xc2\xab" },
+ { "not", "\xc2\xac" }, { "shy", "\xc2\xad" }, { "reg", "\xc2\xae" },
+ { "macr", "\xc2\xaf" }, { "deg", "\xc2\xb0" }, { "plusmn", "\xc2\xb1" },
+ { "sup2", "\xc2\xb2"}, { "sup3", "\xc2\xb3"}, { "acute", "\xc2\xb4" },
+ { "micro", "\xc2\xb5"}, { "para", "\xc2\xb6"}, { "middot", "\xc2\xb7" },
+ { "cedil", "\xc2\xb8"}, { "sup1", "\xc2\xb9"}, { "ordm", "\xc2\xba" },
+ { "raquo", "\xc2\xbb"}, { "frac14", "\xc2\xbc" }, { "frac12", "\xc2\xbd" },
+ { "frac34", "\xc2\xbe"}, { "iquest", "\xc2\xbf" }, { "Agrave", "\xc3\x80" },
+ { "Aacute", "\xc3\x81"}, { "Acirc", "\xc3\x82" }, { "Atilde", "\xc3\x83" },
+ { "Auml", "\xc3\x84"}, { "Aring", "\xc3\x85" }, { "AElig", "\xc3\x86" },
+ { "Ccedil", "\xc3\x87"}, { "Egrave", "\xc3\x88" }, { "Eacute", "\xc3\x89" },
+ { "Ecirc", "\xc3\x8a"}, { "Euml", "\xc3\x8b" }, { "Igrave", "\xc3\x8c" },
+ { "Iacute", "\xc3\x8d"}, { "Icirc", "\xc3\x8e" }, { "Iuml", "\xc3\x8f" },
+ { "ETH", "\xc3\x90"}, { "Ntilde", "\xc3\x91" }, { "Ograve", "\xc3\x92" },
+ { "Oacute", "\xc3\x93"}, { "Ocirc", "\xc3\x94" }, { "Otilde", "\xc3\x95" },
+ { "Ouml", "\xc3\x96"}, { "times", "\xc3\x97" }, { "Oslash", "\xc3\x98" },
+ { "Ugrave", "\xc3\x99"}, { "Uacute", "\xc3\x9a" }, { "Ucirc", "\xc3\x9b" },
+ { "Uuml", "\xc3\x9c"}, { "Yacute", "\xc3\x9d" }, { "THORN", "\xc3\x9e" },
+ { "szlig", "\xc3\x9f"}, { "agrave", "\xc3\xa0" }, { "aacute", "\xc3\xa1" },
+ { "acirc", "\xc3\xa2"}, { "atilde", "\xc3\xa3" }, { "auml", "\xc3\xa4" },
+ { "aring", "\xc3\xa5"}, { "aelig", "\xc3\xa6" }, { "ccedil", "\xc3\xa7" },
+ { "egrave", "\xc3\xa8"}, { "eacute", "\xc3\xa9" }, { "ecirc", "\xc3\xaa" },
+ { "euml", "\xc3\xab"}, { "igrave", "\xc3\xac" }, { "iacute", "\xc3\xad" },
+ { "icirc", "\xc3\xae"}, { "iuml", "\xc3\xaf" }, { "eth", "\xc3\xb0" },
+ { "ntilde", "\xc3\xb1"}, { "ograve", "\xc3\xb2" }, { "oacute", "\xc3\xb3" },
+ { "ocirc", "\xc3\xb4"}, { "otilde", "\xc3\xb5" }, { "ouml", "\xc3\xb6" },
+ { "divide", "\xc3\xb7"}, { "oslash", "\xc3\xb8" }, { "ugrave", "\xc3\xb9" },
+ { "uacute", "\xc3\xba"}, { "ucirc", "\xc3\xbb" }, { "uuml", "\xc3\xbc" },
+ { "yacute", "\xc3\xbd"}, { "thorn", "\xc3\xbe" }, { "yuml", "\xc3\xbf" },
+ // ISO-10646
+ { "fnof", "\xc6\x92"}, { "Alpha", "\xce\x91" }, { "Beta", "\xce\x92" },
+ { "Gamma", "\xce\x93"}, { "Delta", "\xce\x94" }, { "Epsilon", "\xce\x95" },
+ { "Zeta", "\xce\x96"}, { "Eta", "\xce\x97" }, { "Theta", "\xce\x98" },
+ { "Iota", "\xce\x99"}, { "Kappa", "\xce\x9a" }, { "Lambda", "\xce\x9b" },
+ { "Mu", "\xce\x9c"}, { "Nu", "\xce\x9d" }, { "Xi", "\xce\x9e" },
+ { "Omicron", "\xce\x9f"}, { "Pi", "\xce\xa0" }, { "Rho", "\xce\xa1" },
+ { "Sigma", "\xce\xa3"}, { "Tau", "\xce\xa4" }, { "Upsilon", "\xce\xa5" },
+ { "Phi", "\xce\xa6"}, { "Chi", "\xce\xa7" }, { "Psi", "\xce\xa8" },
+ { "Omega", "\xce\xa9"}, { "alpha", "\xce\xb1" }, { "beta", "\xce\xb2" },
+ { "gamma", "\xce\xb3"}, { "delta", "\xce\xb4" }, { "epsilon", "\xce\xb5" },
+ { "zeta", "\xce\xb6"}, { "eta", "\xce\xb7" }, { "theta", "\xce\xb8" },
+ { "iota", "\xce\xb9"}, { "kappa", "\xce\xba" }, { "lambda", "\xce\xbb" },
+ { "mu", "\xce\xbc"}, { "nu", "\xce\xbd" }, { "xi", "\xce\xbe" },
+ { "omicron", "\xce\xbf"}, { "pi", "\xcf\x80" }, { "rho", "\xcf\x81" },
+ { "sigmaf", "\xcf\x82"}, { "sigma", "\xcf\x83" }, { "tau", "\xcf\x84" },
+ { "upsilon", "\xcf\x85"}, { "phi", "\xcf\x86" }, { "chi", "\xcf\x87" },
+ { "psi", "\xcf\x88"}, { "omega", "\xcf\x89" }, { "thetasym", "\xcf\x91" },
+ { "upsih", "\xcf\x92"}, { "piv", "\xcf\x96" }, { "bull", "\xe2\x80\xa2" },
+ { "hellip", "\xe2\x80\xa6"}, { "prime", "\xe2\x80\xb2" }, { "Prime", "\xe2\x80\xb3" },
+ { "oline", "\xe2\x80\xbe"}, { "frasl", "\xe2\x81\x84" }, { "weierp", "\xe2\x84\x98" },
+ { "image", "\xe2\x84\x91"}, { "real", "\xe2\x84\x9c" }, { "trade", "\xe2\x84\xa2" },
+ { "alefsym", "\xe2\x84\xb5"}, { "larr", "\xe2\x86\x90" }, { "uarr", "\xe2\x86\x91" },
+ { "rarr", "\xe2\x86\x92"}, { "darr", "\xe2\x86\x93" }, { "harr", "\xe2\x86\x94" },
+ { "crarr", "\xe2\x86\xb5"}, { "lArr", "\xe2\x87\x90" }, { "uArr", "\xe2\x87\x91" },
+ { "rArr", "\xe2\x87\x92"}, { "dArr", "\xe2\x87\x93" }, { "hArr", "\xe2\x87\x94" },
+ { "forall", "\xe2\x88\x80"}, { "part", "\xe2\x88\x82" }, { "exist", "\xe2\x88\x83" },
+ { "empty", "\xe2\x88\x85"}, { "nabla", "\xe2\x88\x87" }, { "isin", "\xe2\x88\x88" },
+ { "notin", "\xe2\x88\x89"}, { "ni", "\xe2\x88\x8b" }, { "prod", "\xe2\x88\x8f" },
+ { "sum", "\xe2\x88\x91"}, { "minus", "\xe2\x88\x92" }, { "lowast", "\xe2\x88\x97" },
+ { "radic", "\xe2\x88\x9a"}, { "prop", "\xe2\x88\x9d" }, { "infin", "\xe2\x88\x9e" },
+ { "ang", "\xe2\x88\xa0"}, { "and", "\xe2\x88\xa7" }, { "or", "\xe2\x88\xa8" },
+ { "cap", "\xe2\x88\xa9"}, { "cup", "\xe2\x88\xaa" }, { "int", "\xe2\x88\xab" },
+ { "there4", "\xe2\x88\xb4"}, { "sim", "\xe2\x88\xbc" }, { "cong", "\xe2\x89\x85" },
+ { "asymp", "\xe2\x89\x88"}, { "ne", "\xe2\x89\xa0" }, { "equiv", "\xe2\x89\xa1" },
+ { "le", "\xe2\x89\xa4"}, { "ge", "\xe2\x89\xa5" }, { "sub", "\xe2\x8a\x82" },
+ { "sup", "\xe2\x8a\x83"}, { "nsub", "\xe2\x8a\x84" }, { "sube", "\xe2\x8a\x86" },
+ { "supe", "\xe2\x8a\x87"}, { "oplus", "\xe2\x8a\x95" }, { "otimes", "\xe2\x8a\x97" },
+ { "perp", "\xe2\x8a\xa5"}, { "sdot", "\xe2\x8b\x85" }, { "lceil", "\xe2\x8c\x88" },
+ { "rceil", "\xe2\x8c\x89"}, { "lfloor", "\xe2\x8c\x8a" }, { "rfloor", "\xe2\x8c\x8b" },
+ { "lang", "\xe2\x8c\xa9"}, { "rang", "\xe2\x8c\xaa" }, { "loz", "\xe2\x97\x8a" },
+ { "spades", "\xe2\x99\xa0"}, { "clubs", "\xe2\x99\xa3" }, { "hearts", "\xe2\x99\xa5" },
+ { "diams", "\xe2\x99\xa6"}, { "OElig", "\xc5\x92" }, { "oelig", "\xc5\x93" },
+ { "Scaron", "\xc5\xa0"}, { "scaron", "\xc5\xa1" }, { "Yuml", "\xc5\xb8" },
+ { "circ", "\xcb\x86"}, { "tilde", "\xcb\x9c" }, { "ensp", "\xe2\x80\x82" },
+ { "emsp", "\xe2\x80\x83"}, { "thinsp", "\xe2\x80\x89" }, { "zwnj", "\xe2\x80\x8c" },
+ { "zwj", "\xe2\x80\x8d"}, { "lrm", "\xe2\x80\x8e" }, { "rlm", "\xe2\x80\x8f" },
+ { "ndash", "\xe2\x80\x93"}, { "mdash", "\xe2\x80\x94" }, { "lsquo", "\xe2\x80\x98" },
+ { "rsquo", "\xe2\x80\x99"}, { "sbquo", "\xe2\x80\x9a" }, { "ldquo", "\xe2\x80\x9c" },
+ { "rdquo", "\xe2\x80\x9d"}, { "bdquo", "\xe2\x80\x9e" }, { "dagger", "\xe2\x80\xa0" },
+ { "Dagger", "\xe2\x80\xa1" }, { "permil", "\xe2\x80\xb0" }, { "lsaquo", "\xe2\x80\xb9" },
+ { "rsaquo", "\xe2\x80\xba" }, { "euro", "\xe2\x82\xac"},
+ { 0, 0 }
+};
+
+
+int coolrain_html_filter(struct coolrain_writer *writer, char const **pbuffer, size_t length)
+{
+ struct html_entity const *e;
+ size_t utf8_len;
+
+ while (length) {
+ int n, m;
+ char *p;
+
+ n = m = 1;
+ for (e = html_entities; e->name != NULL; e++) {
+ utf8_len = strlen(e->utf8_str);
+ if (length >= utf8_len && memcmp(*pbuffer, e->utf8_str, utf8_len) == 0) {
+ n = utf8_len;
+ m = strlen(e->name) + 2;
+ break;
+ }
+ }
+
+ if ((p = coolrain_writer_reserve(writer, m)) == NULL)
+ return COOLRAIN_ERROR_BUFFER_FULL;
+
+ if (e->name != NULL) {
+ p[0] = '&';
+ strcpy(p + 1, e->name);
+ p[m - 1] = ';';
+ } else /* FIXME: other unprintable charactors. */
+ *p = **pbuffer;
+
+ (*pbuffer) += n;
+ length -= n;
+ }
+ return COOLRAIN_SUCCESS;
+}
+
+
+
+
+static inline bool is_valid_url_char(int ch)
+{
+ return ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
+ (ch >= '0' && ch <= '9') ||
+ strchr("$,.;:=!*~@_()", ch) != NULL);
+}
+
+
+
+int coolrain_url_filter(struct coolrain_writer *writer, char const **pbuffer, size_t length)
+{
+ while (length) {
+ int ch = **pbuffer;
+ char *p;
+ if (! is_valid_url_char(ch) && ch != ' ') {
+ if ((p = coolrain_writer_reserve(writer, 3)) == NULL)
+ return COOLRAIN_ERROR_BUFFER_FULL;
+
+ p[0] = '%';
+ p[1] = "0123456789ABCDEF"[(ch >> 4) & 0x0f];
+ p[2] = "0123456789ABCDEF"[ch & 0x0f];
+ } else {
+ if ((p = coolrain_writer_reserve(writer, 1)) == NULL)
+ return COOLRAIN_ERROR_BUFFER_FULL;
+ *p = (ch == ' ')? '+' : ch;
+ }
+
+ (*pbuffer)++;
+ length--;
+ }
+
+ return COOLRAIN_SUCCESS;
+}
+
+
+
+
+
+
+
+int coolrain_javascript_filter(struct coolrain_writer *writer, char const **pbuffer, size_t length)
+{
+ while (length) {
+ int ch = **pbuffer;
+ char *p;
+ if (ch < 0x20 || strchr("/\"'\\><&;", ch) != NULL) {
+ if ((p = coolrain_writer_reserve(writer, 4)) == NULL)
+ return COOLRAIN_ERROR_BUFFER_FULL;
+
+ p[0] = '\\';
+ p[1] = 'x';
+ p[2] = "0123456789abcdef"[(ch >> 4) & 0x0f];
+ p[3] = "0123456789abcdef"[ch & 0x0f];
+ } else {
+ if ((p = coolrain_writer_reserve(writer, 1)) == NULL)
+ return COOLRAIN_ERROR_BUFFER_FULL;
+ *p = ch;
+ }
+
+ (*pbuffer)++;
+ length--;
+ }
+
+ return COOLRAIN_SUCCESS;
+}
+
+
--- /dev/null
+/**
+ *
+ */
+#if defined(HAVE_CONFIG_H)
+# include <CoolRain/config.h>
+#endif
+
+#include <glib/gmem.h>
+#include <CoolRain/fragment.h>
+#include <CoolRain/memory.h>
+
+
+struct coolrain_fragment_attribute *coolrain_fragment_attribute_alloc(void)
+{
+ struct coolrain_fragment_attribute *attr =
+ coolrain_malloc(sizeof(struct coolrain_fragment_attribute));
+ if (attr != NULL){
+ attr->next = NULL;
+ }
+ return attr;
+}
+
+
+void coolrain_fragment_attribute_free(struct coolrain_fragment_attribute *attrs)
+{
+ while (attrs != NULL) {
+ struct coolrain_fragment_attribute *next = attrs->next;
+ coolrain_free(attrs);
+ attrs = next;
+ }
+}
+
+
+
+struct coolrain_fragment *coolrain_fragment_alloc(
+ coolrain_fragment_type type)
+{
+ struct coolrain_fragment *fr = coolrain_malloc(sizeof(struct coolrain_fragment));
+ if (fr != NULL) {
+ fr->type = type;
+ fr->next = NULL;
+ }
+ return fr;
+}
+
+
+void coolrain_fragment_free(struct coolrain_fragment *fragment)
+{
+ while (fragment != NULL) {
+ struct coolrain_fragment *next = fragment->next;
+
+ if (fragment->type == COOLRAIN_FRAGMENT_TYPE_TAG) {
+ if (fragment->data.tag.attrs != NULL)
+ coolrain_fragment_attribute_free(fragment->data.tag.attrs);
+
+ if (fragment->data.tag.content != NULL)
+ coolrain_fragment_free(fragment->data.tag.content);
+ }
+
+ coolrain_free(fragment);
+
+ fragment = next;
+ }
+}
+
+
+
+coolrain_refstring_t const *coolrain_fragment_find_attribute(struct coolrain_fragment const* fragment, char const *name)
+{
+ struct coolrain_fragment_attribute const *attr;
+ coolrain_refstring_t tmp;
+
+ if (fragment->type != COOLRAIN_FRAGMENT_TYPE_TAG)
+ return NULL;
+
+ tmp.begin = name;
+ tmp.end = name + strlen(name);
+
+ attr = fragment->data.tag.attrs;
+ for (attr = fragment->data.tag.attrs; attr != NULL; attr = attr->next) {
+ if (coolrain_refstring_equal(&attr->name, &tmp))
+ return &attr->value;
+ }
+
+ return NULL;
+}
+
+
--- /dev/null
+/**
+ *
+ */
+#if defined(HAVE_CONFIG_H)
+# include <CoolRain/config.h>
+#endif
+
+#include <stdarg.h>
+#include <glib/gmessages.h>
+
+#include <CoolRain/error.h>
+#include <CoolRain/stash.h>
+#include <CoolRain/memory.h>
+#include <CoolRain/refstring.h>
+#include <CoolRain/variant.h>
+
+
+
+/**
+ *
+ */
+int coolrain_stash_initialize(struct coolrain_stash *stash)
+{
+ stash->hash = g_hash_table_new(
+ (GHashFunc)coolrain_refstring_hash,
+ (GEqualFunc)coolrain_refstring_equal);
+ stash->context_stack = NULL;
+ stash->lookup_handler = NULL;
+ stash->alloced_values = NULL;
+
+ coolrain_mutex_init(&stash->mutex);
+
+ return COOLRAIN_SUCCESS;
+}
+
+
+
+void coolrain_stash_destroy(struct coolrain_stash *stash)
+{
+ coolrain_mutex_lock(&stash->mutex);
+ GSList *p;
+
+ g_assert(stash->context_stack == NULL);
+ g_hash_table_destroy(stash->hash);
+
+ for (p = stash->alloced_values; p != NULL; p = g_slist_next(p)) {
+ coolrain_variant_destroy(p->data);
+ coolrain_free(p->data);
+ }
+ g_slist_free(stash->alloced_values);
+
+ coolrain_mutex_unlock(&stash->mutex);
+ coolrain_mutex_destroy(&stash->mutex);
+}
+
+
+
+coolrain_variant_t const *coolrain_stash_get_value(struct coolrain_stash * restrict stash, coolrain_refstring_t const * restrict key)
+{
+ coolrain_variant_t const *v;
+
+ coolrain_mutex_lock(&stash->mutex);
+
+ v = g_hash_table_lookup(stash->hash, key);
+ if (v == NULL && stash->lookup_handler != NULL) {
+ coolrain_variant_t *new_value;
+
+ new_value = coolrain_malloc(sizeof(coolrain_variant_t));
+ coolrain_variant_initialize(new_value);
+ if (stash->lookup_handler(new_value, key) != 0 || new_value->type == COOLRAIN_VARIANT_TYPE_NULL) {
+ coolrain_variant_destroy(new_value);
+ coolrain_free(new_value);
+ } else {
+ g_hash_table_insert(stash->hash, (gpointer)key, new_value);
+ stash->alloced_values = g_slist_prepend(stash->alloced_values, new_value);
+ v = new_value;
+ }
+ }
+
+ coolrain_mutex_unlock(&stash->mutex);
+ return v;
+}
+
+
+
+
+
+void coolrain_stash_set_values(
+ struct coolrain_stash * restrict stash,
+ struct coolrain_stash_value_list * restrict values,
+ size_t count)
+{
+ coolrain_mutex_lock(&stash->mutex);
+
+ while (count--) {
+ coolrain_variant_t const *v = g_hash_table_lookup(stash->hash, (gpointer)&values->name);
+ values->old_value = v;
+ g_hash_table_insert(stash->hash, &values->name, (gpointer)&values->value);
+
+ values++;
+ }
+
+ coolrain_mutex_unlock(&stash->mutex);
+}
+
+
+void coolrain_stash_restore(
+ struct coolrain_stash * restrict stash,
+ struct coolrain_stash_value_list * restrict values,
+ size_t count)
+{
+ coolrain_mutex_lock(&stash->mutex);
+
+ while (count--) {
+ if (values->old_value == NULL)
+ g_hash_table_remove(stash->hash, &values->name);
+ else
+ g_hash_table_insert(stash->hash, &values->name, (gpointer)values->old_value);
+
+ values++;
+ }
+
+ coolrain_mutex_unlock(&stash->mutex);
+}
+
+
--- /dev/null
+/**
+ * \brief Tagset object module for CoolRain.
+ *
+ */
+#include <CoolRain/config.h>
+#include <stdlib.h>
+
+#include <glib/gtypes.h>
+#include <glib/ghash.h>
+#include <glib/gmem.h>
+
+#include <CoolRain/error.h>
+#include <CoolRain/refstring.h>
+#include <CoolRain/thread.h>
+#include <CoolRain/tagset.h>
+#include <CoolRain/writer.h>
+#include <CoolRain/memory.h>
+#include <CoolRain/template.h>
+
+
+
+static void on_tags_value_destroy(gpointer data)
+{
+ if (((struct coolrain_tag_desc*)data)->flags & COOLRAIN_TAG_DESC_FLAGS_ALLOCED)
+ coolrain_free(data);
+}
+
+static void on_filters_value_destroy(gpointer data)
+{
+ if (((struct coolrain_filter_desc*)data)->flags & COOLRAIN_FILTER_DESC_FLAGS_ALLOCED)
+ coolrain_free(data);
+}
+
+
+
+/**
+ * \brief Initialize tagset object.
+ *
+ * \param tagset Pointer to new tagset object memory
+ * \return Always COOLRAIN_SUCCESS
+ *
+ * This function is thread safe.
+ */
+int coolrain_tagset_initialize(struct coolrain_tagset *tagset)
+{
+ tagset->tags = g_hash_table_new_full(
+ (GHashFunc)coolrain_refstring_hash,
+ (GEqualFunc)coolrain_refstring_equal,
+ NULL,
+ on_tags_value_destroy);
+ tagset->filters = g_hash_table_new_full(
+ (GHashFunc)coolrain_refstring_hash,
+ (GEqualFunc)coolrain_refstring_equal,
+ NULL,
+ on_filters_value_destroy);
+
+ coolrain_mutex_init(&tagset->mutex);
+
+ return COOLRAIN_SUCCESS;
+}
+
+
+
+
+/**
+ * \brief Destroy tagset object.
+ *
+ * \param tagset Tagset object
+ * \return Always COOLRAIN_SUCCESS
+ *
+ * The memory of *target does not free.
+ *
+ * This function is thread safe.
+ */
+void coolrain_tagset_destroy(struct coolrain_tagset *tagset)
+{
+ coolrain_mutex_lock(&tagset->mutex);
+
+ if (tagset->tags != NULL) g_hash_table_destroy(tagset->tags);
+ if (tagset->filters != NULL) g_hash_table_destroy(tagset->filters);
+
+ tagset->tags = NULL;
+ tagset->filters = NULL;
+
+ coolrain_mutex_unlock(&tagset->mutex);
+
+ coolrain_mutex_destroy(&tagset->mutex);
+}
+
+
+
+/**
+ * \brief Add custom tag.
+ *
+ * \param tagset Tagset object
+ * \param name Tag name
+ * \param handler Callback procedure
+ * \param flags Falgs
+ * \retval COOLRAIN_SUCCESS
+ * \retval COOLRAIN_ERROR_NOMEM
+ *
+ * This function is thread safe.
+ */
+int coolrain_tagset_add_tag(struct coolrain_tagset *tagset, char const* name, coolrain_tag_handler_t handler, unsigned int flags)
+{
+ struct coolrain_tag_desc *p = coolrain_malloc(sizeof(struct coolrain_tag_desc));
+
+ p->name.begin = name;
+ p->name.end = name + strlen(name);
+ p->handler = handler;
+ p->flags = flags | COOLRAIN_TAG_DESC_FLAGS_ALLOCED;
+
+ coolrain_mutex_lock(&tagset->mutex);
+ g_hash_table_replace(tagset->tags, &p->name, p);
+ coolrain_mutex_unlock(&tagset->mutex);
+
+ return COOLRAIN_SUCCESS;
+}
+
+
+
+/**
+ * \brief Register tag handler table
+ *
+ * \param tagset Tagset object
+ * \param table Callback table
+ *
+ * \return Always COOLRAIN_SUCCESS
+ *
+ * This function is thread safe.
+ */
+int coolrain_tagset_register_tags(
+ struct coolrain_tagset *tagset,
+ struct coolrain_tag_desc *table)
+{
+ coolrain_mutex_lock(&tagset->mutex);
+
+ while (table->name.begin != NULL) {
+ coolrain_refstring_fix(&table->name);
+
+ table->flags &= ~(COOLRAIN_TAG_DESC_FLAGS_ALLOCED);
+
+ g_hash_table_insert(tagset->tags, &table->name, table);
+
+ table++;
+ }
+
+ coolrain_mutex_unlock(&tagset->mutex);
+
+ return COOLRAIN_SUCCESS;
+}
+
+
+
+
+/**
+ * \brief Lookup tagproc for tag
+ *
+ * \param tagset Tagset object
+ * \param name Tagname.
+ * \return Pointer to tag descriptor if found, otherwise NULL.
+ *
+ * \par Threading;
+ * This API is thread safe.
+ */
+struct coolrain_tag_desc const *coolrain_tagset_lookup_tag(
+ struct coolrain_tagset *tagset,
+ coolrain_refstring_t const *name)
+{
+ struct coolrain_tag_desc *p;
+
+ coolrain_mutex_lock(&tagset->mutex);
+
+ p = g_hash_table_lookup(tagset->tags, name);
+
+ coolrain_mutex_unlock(&tagset->mutex);
+
+ return p;
+}
+
+
+
+
+
+
+/**
+ * \brief Add filter handler
+ * \param tagset Tagsetobject
+ * \param name Filter name
+ * \param handler Filter handler
+ * \param flags Flags
+ * \return Always COOLRAIN_SUCCESS
+ *
+ * \par Threading:
+ * This API is thread safe.
+ */
+int coolrain_tagset_add_filter(
+ struct coolrain_tagset *tagset,
+ char const *name,
+ coolrain_filter_handler_t handler,
+ unsigned int flags)
+{
+ struct coolrain_filter_desc *p = coolrain_malloc(sizeof(struct coolrain_filter_desc));
+
+ p->name.begin = name;
+ p->name.end = name + strlen(name);
+ p->handler = handler;
+ p->flags = flags | COOLRAIN_FILTER_DESC_FLAGS_ALLOCED;
+
+ coolrain_mutex_lock(&tagset->mutex);
+ g_hash_table_replace(tagset->filters, &p->name, p);
+ coolrain_mutex_unlock(&tagset->mutex);
+
+ return COOLRAIN_SUCCESS;
+
+
+}
+
+
+
+/**
+ * \brief Register filter handler table
+ *
+ * \param tagset Tagset object
+ * \param table Filter table
+ *
+ * \return Always COOLRAIN_SUCCESS
+ *
+ * This function is thread safe.
+ */
+int coolrain_tagset_register_filters(
+ struct coolrain_tagset *tagset,
+ struct coolrain_filter_desc *table)
+{
+ coolrain_mutex_lock(&tagset->mutex);
+
+ while (table->name.begin != NULL) {
+ coolrain_refstring_fix(&table->name);
+
+ table->flags &= ~(COOLRAIN_FILTER_DESC_FLAGS_ALLOCED);
+
+ g_hash_table_insert(tagset->filters, &table->name, table);
+
+ table++;
+ }
+
+ coolrain_mutex_unlock(&tagset->mutex);
+
+ return COOLRAIN_SUCCESS;
+}
+
+
+
+
+/**
+ * \brief Lookup filter handler
+ * \param tagset Tagset object
+ * \param name Filter name
+ * \return Pointer to filter_desc if found, otherwise NULL.
+ */
+struct coolrain_filter_desc const* coolrain_tagset_lookup_filter(
+ struct coolrain_tagset *tagset,
+ coolrain_refstring_t const *name)
+{
+ struct coolrain_filter_desc *desc;
+
+ coolrain_mutex_lock(&tagset->mutex);
+ desc = g_hash_table_lookup(tagset->filters, name);
+ coolrain_mutex_unlock(&tagset->mutex);
+
+ return desc;
+}
+
+
+
+
+
+struct coolrain_tag_desc coolrain_tagset_default_tags[] = {
+ { { "CR:ignore", NULL }, coolrain_builtin_tag_handler_ignore, 0 },
+ { { "CR:pass", NULL }, coolrain_builtin_tag_handler_pass, 0 },
+ { { "CR:let", NULL }, coolrain_builtin_tag_handler_let, 0 },
+ { { "CR:var", NULL }, coolrain_builtin_tag_handler_var, 0 },
+ { { "CR:loop", NULL }, coolrain_builtin_tag_handler_loop, 0 },
+ { { NULL, NULL }, NULL, 0 }
+};
+
+struct coolrain_filter_desc coolrain_tagset_default_filters[] = {
+ { { "escape_html", NULL }, coolrain_html_filter, 0 },
+ { { "escape_url", NULL }, coolrain_url_filter, 0 },
+ { { "escape_js", NULL }, coolrain_javascript_filter, 0 },
+ { { NULL, NULL }, NULL, 0 }
+};
+
+
+
+
+
--- /dev/null
+/**
+ * Template loader/parser module for CoolRain
+ */
+#define _GNU_SOURCE
+#include <CoolRain/config.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <glib/gmacros.h>
+#include <glib/gmessages.h>
+
+#include <CoolRain/error.h>
+#include <CoolRain/thread.h>
+#include <CoolRain/fragment.h>
+#include <CoolRain/tagset.h>
+#include <CoolRain/template.h>
+#include <CoolRain/memory.h>
+
+
+
+/**
+ * Default error handler
+ *
+ * The default handler report to stderr.
+ */
+static void default_error_handler(struct coolrain_template *tmpl G_GNUC_UNUSED, char const* message)
+{
+ fputs(message, stderr);
+}
+
+/**
+ * Destroy handler for compiling from file.
+ */
+static void munmap_handler(struct coolrain_template *tmpl)
+{
+ munmap((void*)tmpl->source.begin, tmpl->source.end - tmpl->source.begin);
+}
+
+
+
+
+
+/**
+ * Initialize template object
+ * \param tmpl Pointer to new object
+ * \param tagset Tagset object
+ * \return Always COOLRAIN_SUCCESS
+ */
+int coolrain_template_initialize(struct coolrain_template *tmpl, struct coolrain_tagset *tagset)
+{
+ coolrain_mutex_init(&tmpl->mutex);
+
+ tmpl->root = NULL;
+
+ tmpl->name[0] = '\0';
+ tmpl->source.begin = NULL;
+ tmpl->source.end = NULL;
+ tmpl->parser_state.lineno = 0;
+
+ tmpl->destroy_callback = NULL;
+ tmpl->destroy_data = NULL;
+
+ tmpl->tagset = tagset;
+
+ tmpl->error_handler = default_error_handler;
+ tmpl->error_user_data = NULL;
+
+ return COOLRAIN_SUCCESS;
+}
+
+
+
+/**
+ * Destroy template object
+ *
+ * \param tmpl Template object
+ * \return Always COOLRAIN_SUCCESS
+ *
+ * Must be free an object memory by caller.
+ */
+int coolrain_template_destroy(struct coolrain_template *tmpl)
+{
+ coolrain_template_clear(tmpl);
+ coolrain_mutex_destroy(&tmpl->mutex);
+ return COOLRAIN_SUCCESS;
+}
+
+
+
+
+static int coolrain_template_clear_(struct coolrain_template *tmpl)
+{
+ if (tmpl->root != NULL)
+ coolrain_fragment_free(tmpl->root);
+ tmpl->root = NULL;
+
+ if (tmpl->destroy_callback != NULL)
+ (*tmpl->destroy_callback)(tmpl);
+
+ tmpl->name[0] = '\0';
+ tmpl->source.begin = NULL;
+ tmpl->source.end = NULL;
+ tmpl->parser_state.lineno = 0;
+
+ tmpl->destroy_callback = NULL;
+ tmpl->destroy_data = NULL;
+
+ return COOLRAIN_SUCCESS;
+}
+
+
+/**
+ * Reset parsed tree
+ * \param tmpl Template object
+ * \return Always COOLRAIN_SUCCESS
+ *
+ * This API is thread safe.
+ */
+int coolrain_template_clear(struct coolrain_template *tmpl)
+{
+ int r;
+ coolrain_mutex_lock(&tmpl->mutex);
+ r = coolrain_template_clear_(tmpl);
+ coolrain_mutex_unlock(&tmpl->mutex);
+
+ return r;
+}
+
+
+
+
+/**
+ * Assign error callback handler.
+ * \param tmpl Template object
+ * \param handler Pointer to callback function
+ * \param error_user_data User data for callback
+ * \return Always COOLRAIN_SUCCESS
+ *
+ * See error_user_data member at coolrain_template structuer.
+ *
+ * \par Threading:
+ * This API is thread safe.
+ */
+int coolrain_template_set_error_handler(
+ struct coolrain_template *tmpl,
+ coolrain_template_error_handler_t handler,
+ void *error_user_data)
+{
+ coolrain_mutex_lock(&tmpl->mutex);
+
+ tmpl->error_handler = handler;
+ tmpl->error_user_data = error_user_data;
+
+ coolrain_mutex_unlock(&tmpl->mutex);
+
+ return COOLRAIN_SUCCESS;
+}
+
+
+
+/**
+ *
+ */
+void coolrain_template_raise_error(
+ struct coolrain_template *tmpl,
+ char const *fmt,
+ ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ coolrain_template_vraise_error(tmpl, fmt, va);
+ va_end(va);
+}
+
+
+void coolrain_template_vraise_error(
+ struct coolrain_template *tmpl,
+ char const *fmt,
+ va_list va)
+{
+ char *message = NULL;
+
+ vasprintf(&message, fmt, va);
+
+ (*tmpl->error_handler)(tmpl, message);
+
+ free(message);
+}
+
+
+
+/**
+ * Compile template file.
+ * \param tmpl Template object
+ * \param path Path to file
+ * \return COOLRAIN_SUCCESS if success, otherwise error code.
+ *
+ * \par Errors:
+ * \li COOLRAIN_ERROR_NOFILE
+ * \li COOLRAIN_ERROR_BAD_LENGTH
+ * \li COOLRAIN_ERROR_CANT_READ
+ * \li COOLRAIN_ERROR_NOMEM
+ * \li COOLRAIN_ERROR_PARSE_ERROR
+ *
+ * \par Threading:
+ * This API is thread safe.
+ */
+int coolrain_template_compile(struct coolrain_template *tmpl, char const *path)
+{
+ off_t length;
+ char *p;
+ int fd = open(path, O_RDONLY);
+ int r;
+ if (fd < 0) return COOLRAIN_ERROR_NOFILE;
+
+ length = lseek(fd, 0, SEEK_END);
+ if (length < 0) {
+ close(fd);
+ return COOLRAIN_ERROR_BAD_LENGTH;
+ }
+
+ p = mmap(0, (size_t)length, PROT_READ, MAP_PRIVATE, fd, 0);
+ close(fd);
+ if (p == NULL) return COOLRAIN_ERROR_CANT_READ;
+
+ r = coolrain_template_parse(tmpl, path, p, (size_t)length, munmap_handler, NULL);
+
+ return r;
+}
+
+
+
+
+// TODO:
+//int coolrain_template_save(struct coolrain_template *tmpl, char const* path);
+//int coolrain_template_load(struct coolrain_template *tmpl, char const* path);
+
+
+static inline char const *skip_symbol(struct coolrain_template *tmpl, char const *p)
+{
+ while (p < tmpl->source.end &&
+ (isalnum(*p) || *p == ':' || *p == '_' || *p == '-' || *p == '.'))
+ p++;
+ return p;
+}
+
+
+
+static inline char const *skip_whitespace(struct coolrain_template *tmpl, char const* p)
+{
+ while (p < tmpl->source.end && isspace(*p)) {
+ if (*p == '\n') tmpl->parser_state.lineno++;
+ p++;
+ }
+ return p;
+}
+
+
+
+
+
+static int parse_attributes(
+ struct coolrain_template *tmpl,
+ struct coolrain_fragment_attribute **head)
+{
+ coolrain_refstring_t name;
+ coolrain_refstring_t value;
+ char const *p = tmpl->parser_state.cursor;
+ unsigned long start_lineno = tmpl->parser_state.lineno;
+
+ do {
+ p = skip_whitespace(tmpl, p);
+ if (p >= tmpl->source.end) goto unexpected_eof;
+ if (*p == '>') {
+ tmpl->parser_state.cursor = p + 1;
+ return COOLRAIN_SUCCESS;
+ }
+
+ name.begin = p;
+ name.end = skip_symbol(tmpl, p);
+ if (name.end == p) { p++; continue; /* Invalid charactor */}
+ p = name.end;
+ if (p >= tmpl->source.end) goto unexpected_eof;
+
+ /* Detect attribute name */
+ p = skip_whitespace(tmpl, p);
+ if (p >= tmpl->source.end) goto unexpected_eof;
+ if (*p != '=') { p++; continue; /* No attr value */ }
+
+ p = skip_whitespace(tmpl, p + 1);
+ if (p >= tmpl->source.end) goto unexpected_eof;
+ if (*p != '\'' && *p != '\"') {
+ value.begin = p;
+ while (p < tmpl->source.end && !isspace(*p) && *p != '>' && *p != '/') p++;
+ if (p >= tmpl->source.end) goto unexpected_eof;
+ value.end = p;
+ } else {
+ char delim = *(p++);
+
+ value.begin = p;
+ while (p < tmpl->source.end && *p != delim) {
+ if (*p == '\n') tmpl->parser_state.lineno++;
+ p++;
+ }
+ if (p >= tmpl->source.end) goto unexpected_eof;
+ value.end = p++;
+ }
+
+ {
+ struct coolrain_fragment_attribute *attr
+ = coolrain_fragment_attribute_alloc();
+ attr->name.begin = name.begin;
+ attr->name.end = name.end;
+ attr->value.begin = value.begin;
+ attr->value.end = value.end;
+ attr->next = *head;
+ *head = attr;
+ }
+ } while (1);
+
+unexpected_eof:
+ coolrain_template_raise_error(tmpl,
+ "Unexpeced EOF white parsing attributes at line %d.",
+ start_lineno);
+ return COOLRAIN_ERROR_PARSE_ERROR;
+}
+
+
+
+
+
+static inline void fragment_list_add(
+ struct coolrain_fragment **head,
+ struct coolrain_fragment **tail,
+ struct coolrain_fragment *x)
+{
+ x->next = NULL;
+ if (*head == NULL)
+ *head = *tail = x;
+ else {
+ (*tail)->next = x;
+ *tail = x;
+ }
+}
+
+
+
+/**
+ * \brief Parse contents in block tag
+ * \param tmpl Template object
+ * \param current_tag Tag descriptor of current element.
+ * \param head Pointer to retrieve fragment object of contents
+ * \return COOLRAIN_SUCCESS if success, otherwise negative error code
+ */
+static int parse_block(
+ struct coolrain_template *tmpl,
+ struct coolrain_tag_desc const *current_tag,
+ struct coolrain_fragment **head)
+{
+ struct coolrain_fragment *tail = NULL;
+ char const *p = tmpl->parser_state.cursor;
+ unsigned int start_lineno = tmpl->parser_state.lineno;
+ int r;
+ coolrain_refstring_t tagname;
+ *head = NULL;
+
+ do {
+ while (p < tmpl->source.end && *p != '<' && *p != '$') {
+ if (*p == '\n') tmpl->parser_state.lineno++;
+ p++;
+ }
+ if (p >= tmpl->source.end - 1) {
+ /* Reaches to EOF */
+ struct coolrain_fragment *fr;
+ if (current_tag != NULL) {
+ coolrain_template_raise_error(
+ tmpl,
+ "No close tag for %s at line %d.", current_tag->name.begin, start_lineno);
+ r = COOLRAIN_ERROR_PARSE_ERROR;
+ goto failed;
+ }
+
+ if (tmpl->parser_state.cursor != tmpl->source.end) {
+ fr = coolrain_fragment_alloc(COOLRAIN_FRAGMENT_TYPE_CONSTANT_STRING);
+
+ fr->data.str.begin = tmpl->parser_state.cursor;
+ fr->data.str.end = tmpl->source.end;
+ fragment_list_add(head, &tail, fr);
+ }
+ return COOLRAIN_SUCCESS;
+ }
+
+
+ /*
+ *
+ */
+ if (p[0] == '$') {
+ struct coolrain_fragment *fr;
+ unsigned int tmp_lineno = tmpl->parser_state.lineno;
+ if (p[1] != '{') continue;
+
+ tagname.end = tagname.begin = p + 2;
+
+ while (tagname.end < tmpl->source.end && *tagname.end != '}') {
+ if (*tagname.end == '\n') tmpl->parser_state.lineno++;
+ tagname.end++;
+ }
+ if (tagname.end >= tmpl->source.end) {
+ coolrain_template_raise_error(
+ tmpl,
+ "Unexpected EOF while variable reference at line %d", tmp_lineno);
+ r = COOLRAIN_ERROR_PARSE_ERROR;
+ goto failed;
+ }
+
+ if (p != tmpl->parser_state.cursor) {
+ fr = coolrain_fragment_alloc(COOLRAIN_FRAGMENT_TYPE_CONSTANT_STRING);
+
+ fr->data.str.begin = tmpl->parser_state.cursor;
+ fr->data.str.end = p;
+ fragment_list_add(head, &tail, fr);
+ }
+
+ fr = coolrain_fragment_alloc(COOLRAIN_FRAGMENT_TYPE_VARREF);
+ fr->data.varname.begin = tagname.begin;
+ fr->data.varname.end = tagname.end;
+ fragment_list_add(head, &tail, fr);
+
+ tmpl->parser_state.cursor = p = tagname.end + 1;
+
+ continue;
+ }
+
+
+ /*
+ *
+ */
+ tagname.begin = p + 1;
+
+ if (p[1] == '/') {
+ tagname.begin++;
+ tagname.end = skip_symbol(tmpl, tagname.begin);
+ if (current_tag == NULL) {
+ /* FIXME: Unexpected close tag */
+
+ p += 2;
+ } else {
+ if (coolrain_refstring_equal(¤t_tag->name, &tagname)) {
+ if (tmpl->parser_state.cursor != p) {
+ struct coolrain_fragment *fr = coolrain_fragment_alloc(COOLRAIN_FRAGMENT_TYPE_CONSTANT_STRING);
+ fr->data.str.begin = tmpl->parser_state.cursor;
+ fr->data.str.end = p;
+ fragment_list_add(head, &tail, fr);
+ }
+
+ /* Close current block. */
+ p = tagname.end;
+ while (p < tmpl->source.end - 1 && *p != '>')
+ p++;
+ tmpl->parser_state.cursor = p + 1;
+ return COOLRAIN_SUCCESS;
+ }
+ //p = tmpl->parser_state.cursor;
+ p = tagname.end;
+ }
+ continue;
+ }
+
+
+
+ tagname.end = skip_symbol(tmpl, tagname.begin);
+ if (tagname.end <= tagname.begin) {
+ p++;
+ } else {
+ struct coolrain_tag_desc const* tagdesc =
+ coolrain_tagset_lookup_tag(tmpl->tagset, &tagname);
+ if (tagdesc == NULL) {
+ p = tagname.end;
+ } else {
+ /* Element open tag was detected */
+ struct coolrain_fragment *fr;
+
+ if (tmpl->parser_state.cursor != p) {
+ fr = coolrain_fragment_alloc(COOLRAIN_FRAGMENT_TYPE_CONSTANT_STRING);
+ fr->data.str.begin = tmpl->parser_state.cursor;
+ fr->data.str.end = p;
+ fragment_list_add(head, &tail, fr);
+
+ }
+ tmpl->parser_state.cursor = tagname.end;
+
+ fr = coolrain_fragment_alloc(COOLRAIN_FRAGMENT_TYPE_TAG);
+
+ fr->data.tag.attrs = NULL;
+ r = parse_attributes(tmpl, &fr->data.tag.attrs);
+ if (r != COOLRAIN_SUCCESS) {
+ coolrain_fragment_free(fr);
+ goto failed;
+ }
+
+ fr->data.tag.tagdesc = tagdesc;
+ fr->data.tag.content = NULL;
+ fragment_list_add(head, &tail, fr);
+
+ p = tmpl->parser_state.cursor;
+
+ /* Parse children if no empty tag. */
+ if (p[-1] == '>' && p[-2] != '/') {
+ r = parse_block(tmpl, tagdesc, &fr->data.tag.content);
+ if (r != COOLRAIN_SUCCESS) goto failed;
+ p = tmpl->parser_state.cursor;
+ }
+
+ }
+ }
+ } while (1);
+
+failed:
+ coolrain_fragment_free(*head);
+ *head = NULL;
+ return r;
+}
+
+
+
+
+/**
+ * Parse memory block as template
+ * \param tmpl Template object
+ * \param name Name of template (for error message)
+ * \param source Pointer to template data
+ * \param length Length of source
+ * \param destroy_callback Callback to release *source
+ * \param destroy_data Additional argument for destroy callback.
+ * \return COOLRAIN_SUCCESS if success, otherwise error code.
+ *
+ * \par Errors:
+ * \li COOLRAIN_ERROR_NOMEM
+ * \li COOLRAIN_ERROR_PARSE_ERROR
+ *
+ * \par Threading:
+ * This API is thread safe.
+ */
+int coolrain_template_parse(
+ struct coolrain_template *tmpl,
+ char const *name,
+ char const *source,
+ size_t length,
+ void (*destroy_callback)(struct coolrain_template*),
+ void *destroy_data)
+{
+ int result = 0;
+
+ coolrain_mutex_lock(&tmpl->mutex);
+
+ coolrain_template_clear_(tmpl);
+
+ strncpy(tmpl->name, name, PATH_MAX);
+ tmpl->source.begin = source;
+ tmpl->source.end = source + length;
+ tmpl->destroy_callback = destroy_callback;
+ tmpl->destroy_data = destroy_data;
+
+ tmpl->parser_state.lineno = 0;
+ tmpl->parser_state.cursor = source;
+
+ tmpl->root = NULL;
+ result = parse_block(tmpl, NULL, &tmpl->root);
+
+ coolrain_mutex_unlock(&tmpl->mutex);
+
+ return result;
+}
+
+
+
+/**
+ *
+ * \par Threading:
+ * This API is thread safe.
+ */
+int coolrain_template_run(
+ struct coolrain_template *tmpl,
+ struct coolrain_stash *stash,
+ struct coolrain_writer *writer)
+{
+ int r;
+ struct coolrain_eval_context cntx;
+
+ cntx.tmpl = tmpl;
+ cntx.fragment = tmpl->root;
+ cntx.stash = stash;
+ cntx.writer = writer;
+
+ // FIXME: Check recursive call
+
+ coolrain_mutex_lock(&tmpl->mutex);
+ r = coolrain_template_eval_block(&cntx);
+ coolrain_mutex_unlock(&tmpl->mutex);
+
+ return r;
+}
+
+
+
+int coolrain_template_eval_block(struct coolrain_eval_context const *cntx)
+{
+ int r = COOLRAIN_SUCCESS;
+ struct coolrain_eval_context my_cntx;
+
+ coolrain_eval_context_copy(&my_cntx, cntx);
+
+
+ for (; my_cntx.fragment != NULL && r >= 0; my_cntx.fragment = my_cntx.fragment->next) {
+ switch (my_cntx.fragment->type) {
+ case COOLRAIN_FRAGMENT_TYPE_CONSTANT_STRING:
+ r = coolrain_writer_write(
+ my_cntx.writer,
+ my_cntx.fragment->data.str.begin,
+ my_cntx.fragment->data.str.end - my_cntx.fragment->data.str.begin);
+ if (r < 0) return r;
+ break;
+
+ case COOLRAIN_FRAGMENT_TYPE_TAG:
+ {
+ GSList *filters = NULL;
+
+ /* Build filter chain */
+ {
+ struct coolrain_fragment_attribute *attr = my_cntx.fragment->data.tag.attrs;
+
+ while (attr != NULL) {
+ struct coolrain_filter_desc const *desc =
+ coolrain_tagset_lookup_filter(my_cntx.tmpl->tagset, &attr->name);
+ if (desc != NULL) {
+ struct coolrain_writer *new_writer = coolrain_malloc(sizeof(struct coolrain_writer));
+ coolrain_writer_initialize(new_writer, NULL, 4096);
+ coolrain_writer_chain(new_writer, my_cntx.writer);
+ new_writer->write_handler = desc->handler;
+
+ filters = g_slist_prepend(filters, new_writer);
+ my_cntx.writer = new_writer;
+ }
+
+ attr = attr->next;
+ }
+ }
+
+ r = (*my_cntx.fragment->data.tag.tagdesc->handler)(&my_cntx);
+
+ /* Release filters */
+ g_slist_foreach(filters, (GFunc)coolrain_writer_destroy, NULL);
+ g_slist_foreach(filters, (GFunc)coolrain_free, NULL);
+ g_slist_free(filters);
+
+ my_cntx.writer = cntx->writer;
+
+ if (r < 0) return r;
+ break;
+ }
+
+ case COOLRAIN_FRAGMENT_TYPE_VARREF:
+ {
+ coolrain_variant_t const *var = coolrain_stash_get_value(my_cntx.stash, &my_cntx.fragment->data.varname);
+ if (var != NULL) {
+ r = coolrain_variant_write(var, my_cntx.writer);
+ if (r < 0) return r;
+ }
+ }
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+ }
+
+ return COOLRAIN_SUCCESS;
+}
+
+
+int coolrain_template_eval_content(struct coolrain_eval_context const *cntx)
+{
+ struct coolrain_eval_context new_cntx;
+ coolrain_eval_context_copy(&new_cntx, cntx);
+ new_cntx.fragment = new_cntx.fragment->data.tag.content;
+
+ return coolrain_template_eval_block(&new_cntx);
+}
+
+
--- /dev/null
+/**
+ *
+ */
+#if defined(HAVE_CONFIG_H)
+# include <CoolRain/config.h>
+#endif
+#include <stdio.h>
+
+#include <glib/gmessages.h>
+
+#include <CoolRain/variant.h>
+#include <CoolRain/memory.h>
+#include <CoolRain/writer.h>
+
+
+
+/**
+ *
+ */
+void coolrain_variant_unset(coolrain_variant_t *v)
+{
+ switch (v->type) {
+ case COOLRAIN_VARIANT_TYPE_DYNAMIC_STRING:
+ coolrain_free(v->data.v_dynamic_string);
+ break;
+ case COOLRAIN_VARIANT_TYPE_POINTER:
+ v->data.v_pointer.destroy_handler(v->data.v_pointer.p);
+ break;
+ }
+
+ v->type = COOLRAIN_VARIANT_TYPE_NULL;
+}
+
+
+
+
+/**
+ *
+ */
+void coolrain_variant_set_refstring(coolrain_variant_t *v, coolrain_refstring_t const *new_value)
+{
+ coolrain_variant_unset(v);
+ v->type = COOLRAIN_VARIANT_TYPE_REFSTRING;
+ v->data.v_refstring.begin = new_value->begin;
+ v->data.v_refstring.end = new_value->end;
+
+ coolrain_refstring_fix(&v->data.v_refstring);
+}
+
+
+
+char const *coolrain_variant_get_string(coolrain_variant_t const *v)
+{
+ if (v->type == COOLRAIN_VARIANT_TYPE_STATIC_STRING)
+ return v->data.v_static_string;
+ else if (v->type == COOLRAIN_VARIANT_TYPE_DYNAMIC_STRING)
+ return v->data.v_dynamic_string;
+ else
+ return NULL;
+}
+
+
+
+
+/**
+ *
+ */
+void coolrai_variant_copy(coolrain_variant_t *dst, coolrain_variant_t const *src)
+{
+ coolrain_variant_unset(dst);
+
+ dst->type = src->type;
+ switch (src->type) {
+ case COOLRAIN_VARIANT_TYPE_NULL:
+ break;
+ case COOLRAIN_VARIANT_TYPE_INT:
+ dst->data.v_int = src->data.v_int;
+ break;
+ case COOLRAIN_VARIANT_TYPE_DOUBLE:
+ dst->data.v_double = src->data.v_double;
+ break;
+ case COOLRAIN_VARIANT_TYPE_REFSTRING:
+ dst->data.v_refstring = src->data.v_refstring;
+ break;
+ case COOLRAIN_VARIANT_TYPE_STATIC_STRING:
+ dst->data.v_static_string = src->data.v_static_string;
+ break;
+ case COOLRAIN_VARIANT_TYPE_DYNAMIC_STRING:
+ dst->data.v_dynamic_string = coolrain_strdup(src->data.v_dynamic_string);
+ break;
+ case COOLRAIN_VARIANT_TYPE_POINTER:
+ dst->data.v_pointer = src->data.v_pointer;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+
+
+/**
+ *
+ */
+int coolrain_variant_write(coolrain_variant_t const *v, struct coolrain_writer *writer)
+{
+ char buf[128];
+
+ memset(buf, 0, sizeof(buf));
+
+ switch (v->type) {
+ case COOLRAIN_VARIANT_TYPE_INT:
+ snprintf(buf, 128, "%d", v->data.v_int);
+ return coolrain_writer_write(writer, buf, -1);
+ case COOLRAIN_VARIANT_TYPE_DOUBLE:
+ snprintf(buf, 128, "%f", v->data.v_double);
+ return coolrain_writer_write(writer, buf, -1);
+ case COOLRAIN_VARIANT_TYPE_REFSTRING:
+ return coolrain_writer_write(writer, v->data.v_refstring.begin, coolrain_refstring_length(&v->data.v_refstring));
+ case COOLRAIN_VARIANT_TYPE_STATIC_STRING:
+ return coolrain_writer_write(writer, v->data.v_static_string, -1);
+ case COOLRAIN_VARIANT_TYPE_DYNAMIC_STRING:
+ return coolrain_writer_write(writer, v->data.v_dynamic_string, -1);
+ }
+ return COOLRAIN_SUCCESS;
+}
+
+
--- /dev/null
+/**
+ *
+ */
+#if defined(HAVE_CONFIG_H)
+# include <CoolRain/config.h>
+#endif
+#include <string.h>
+
+#include <glib/gmacros.h>
+#include <glib/gmessages.h>
+
+#include <CoolRain/error.h>
+#include <CoolRain/thread.h>
+#include <CoolRain/writer.h>
+#include <CoolRain/memory.h>
+
+
+static int default_buffer_full_handler(struct coolrain_writer *writer G_GNUC_UNUSED)
+{
+ return COOLRAIN_ERROR_BUFFER_FULL;
+}
+
+
+
+
+static int default_write_handler(struct coolrain_writer *writer, char const **src, size_t length)
+{
+ while (length > 0) {
+ size_t dlen = coolrain_writer_unused_length(writer);
+ if (dlen == 0) {
+ coolrain_writer_flush(writer);
+ dlen = coolrain_writer_unused_length(writer);
+ if (dlen == 0) return COOLRAIN_ERROR_BUFFER_FULL;
+ }
+
+ if (dlen > length)
+ dlen = length;
+ memcpy(writer->cursor, *src, dlen);
+ writer->cursor += dlen;
+ *src += dlen;
+ length -= dlen;
+ }
+
+ return COOLRAIN_SUCCESS;
+}
+
+
+
+
+
+/**
+ * Initialize writer
+ * \param writer Pointer to new writer object.
+ * \param buffer Pointer to buffer, chained writer or NULL
+ * Allocate autometically If buffer is NULL.
+ * \param length Buffer size.
+ *
+ * \retval COOLRAIN_SUCCESS Success
+ */
+int coolrain_writer_initialize(struct coolrain_writer *writer, char *buffer, size_t length)
+{
+ writer->flags = 0;
+
+ /* Assing raw buffer */
+ if (buffer == NULL) {
+ writer->buffer = coolrain_malloc(length);
+ writer->flags |= COOLRAIN_WRITER_FLAGS_AUTO_FREE;
+ } else
+ writer->buffer = buffer;
+
+ writer->cursor = writer->buffer;
+ writer->alloc_length = length;
+
+ /* Setup default handlers */
+ writer->buffer_full_handler = default_buffer_full_handler;
+ writer->write_handler = default_write_handler;
+ writer->destroy_handler = NULL;
+
+ /* */
+ coolrain_mutex_init(&writer->mutex);
+
+ return COOLRAIN_SUCCESS;
+}
+
+
+
+
+
+
+
+
+/**
+ * Destroy writer object
+ * \param writer Writer object
+ *
+ * Remaining data will be flush autometically.
+ */
+int coolrain_writer_destroy(struct coolrain_writer *writer)
+{
+ coolrain_mutex_lock(&writer->mutex);
+
+
+ /* Flush remaining data */
+ coolrain_writer_flush(writer);
+
+ /* Release user_data */
+ if (writer->destroy_handler != NULL)
+ writer->destroy_handler(writer);
+
+ /* Release raw buffer */
+ if (writer->flags & COOLRAIN_WRITER_FLAGS_AUTO_FREE) {
+ writer->flags &= ~COOLRAIN_WRITER_FLAGS_AUTO_FREE;
+ coolrain_free(writer->buffer);
+ }
+
+ writer->buffer = NULL;
+ writer->cursor = NULL;
+ writer->alloc_length = 0;
+
+
+ coolrain_mutex_unlock(&writer->mutex);
+
+ coolrain_mutex_destroy(&writer->mutex);
+
+ return COOLRAIN_SUCCESS;
+}
+
+
+
+
+/**
+ * Writer data to writer
+ * \param writer Writer object
+ * \param buffer Pointer of buffer to put
+ * \param length Length in bytes
+ * \return Wrote size in bytes.
+ */
+int coolrain_writer_write(struct coolrain_writer *writer, char const* buffer, size_t length)
+{
+ int r;
+ char const *src = buffer;
+
+ if (length == (size_t)(-1)) length = strlen(buffer);
+
+ coolrain_mutex_lock(&writer->mutex);
+ r = writer->write_handler(writer, &src, length);
+ coolrain_mutex_unlock(&writer->mutex);
+
+ /* TODO: Save error code */
+
+ return src - buffer;
+}
+
+
+
+/**
+ * Discard N bytes data from internal buffer.
+ * \param writer Writer object.
+ * \param length Bytes
+ * \retval COOLRAIN_SUCCESS Success
+ * \retval COOLRAIN_INVALID_ARG The length parameter was greater than actual data.
+ *
+ * Use in buffer full handler only.
+ *
+ * \par Threading:
+ * This API is not thread safe.
+ */
+int coolrain_writer_drop(struct coolrain_writer *writer, size_t length)
+{
+ size_t n = writer->cursor - writer->buffer;
+
+ g_return_val_if_fail(n >= length, COOLRAIN_ERROR_INVALID_ARG);
+
+ if (n > length)
+ memcpy(writer->buffer, writer->buffer + length, n - length);
+ writer->cursor -= length;
+
+ return COOLRAIN_SUCCESS;
+}
+
+
+
+static int chain_handler(struct coolrain_writer *writer)
+{
+ int r = coolrain_writer_write(writer->buffer_full_handler_data.pointee, writer->buffer, writer->cursor - writer->buffer);
+ if (r > 0)
+ coolrain_writer_drop(writer, r);
+ return r;
+}
+
+
+/**
+ *
+ */
+int coolrain_writer_chain(struct coolrain_writer *writer, struct coolrain_writer *next_writer)
+{
+ coolrain_mutex_lock(&writer->mutex);
+ writer->buffer_full_handler_data.pointee = next_writer;
+ writer->buffer_full_handler = chain_handler;
+ coolrain_mutex_unlock(&writer->mutex);
+
+ return COOLRAIN_SUCCESS;
+}
--- /dev/null
+/**
+ *
+ */
+#if HAVE_CONFIG_H
+# include <CoolRain/config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <glib/gmessages.h>
+
+#include <CoolRain/error.h>
+#include <CoolRain/thread.h>
+#include <CoolRain/writer.h>
+
+#define BUFFER_SIZE 32768
+
+
+static int buffer_full_handler(struct coolrain_writer *writer)
+{
+ char const *p = writer->buffer;
+
+ while (p < writer->cursor) {
+ int r = write(writer->write_handler_data.handle, p, (writer->cursor - p));
+ if (r < 0) {
+ g_warning("IO error on %s [%d]. errno=%d\n", __FILE__, __LINE__, errno);
+ return COOLRAIN_ERROR_IO;
+ }
+ p += r;
+ }
+
+ return COOLRAIN_SUCCESS;
+}
+
+
+static int destroy_handler(struct coolrain_writer *writer)
+{
+ if (writer->flags & COOLRAIN_WRITER_FLAGS_AUTO_CLOSE) {
+ writer->flags &= ~COOLRAIN_WRITER_FLAGS_AUTO_CLOSE;
+ close(writer->write_handler_data.handle);
+ }
+
+ return COOLRAIN_SUCCESS;
+}
+
+
+
+
+int coolrain_fd_writer_initialize(struct coolrain_writer *writer, int fd, bool autoclose)
+{
+ int r;
+
+ g_assert(fd >= 0);
+
+ r = coolrain_writer_initialize(writer, NULL, BUFFER_SIZE);
+ if (r == COOLRAIN_SUCCESS) {
+ writer->write_handler_data.handle = fd;
+ if (autoclose)
+ writer->flags |= COOLRAIN_WRITER_FLAGS_AUTO_CLOSE;
+
+ writer->buffer_full_handler = buffer_full_handler;
+ writer->destroy_handler = destroy_handler;
+ }
+
+ return COOLRAIN_SUCCESS;
+}
+
+
+
--- /dev/null
+
+check_PROGRAMS=
+TESTS=${check_PROGRAMS}
+SOURCES= runtest.c test_writer.c test_tagset.c test_stash.c test_template.c
+LIBS= -L${top_builddir}/src -lcunit
+
+CONFIG_CLEAN_FILES=CUnitAutomated-Listing.xml CUnitAutomated-Results.xml
+
+if HAVE_GLIB
+check_PROGRAMS+= runtest-glib-nonthread
+runtest_glib_nonthread_SOURCES=${SOURCES}
+runtest_glib_nonthread_CFLAGS= -DCOOLRAIN_USE_GLIB=1 @GLIB_CFLAGS@ @GOBJECT_CFLAGS@
+runtest_glib_nonthread_LDADD= -lcoolrain-glib-nonthread @GOBJECT_LIBS@ @GLIB_LIBS@
+runtest_glib_nonthread_DEPENDENCIES=../src/libcoolrain-glib-nonthread.a
+endif
+
+if HAVE_GTHREAD
+check_PROGRAMS+= runtest-glib-gthread
+runtest_glib_gthread_SOURCES=${SOURCES}
+runtest_glib_gthread_CFLAGS= -DCOOLRAIN_USE_GLIB=1 -DCOOLRAIN_USE_GTHREAD=1 @GLIB_CFLAGS@ @GOBJECT_CFLAGS@ @GTHREAD_CFLAGS@
+runtest_glib_gthread_LDADD= -lcoolrain-glib-gthread @GOBJECT_LIBS@ @GLIB_LIBS@ @GTHREAD_LIBS@
+runtest_glib_gthread_DEPENDENCIES=../src/libcoolrain-glib-gthread.a
+endif
+
--- /dev/null
+/**
+ *
+ *
+ */
+#include <stdlib.h>
+#include <stdbool.h>
+
+#if defined(COOLRAIN_USE_GTHREAD)
+# include <glib/gthread.h>
+#endif
+
+#include <CUnit/Basic.h>
+#include <CUnit/Console.h>
+#include <CUnit/Automated.h>
+
+extern bool setup_test_writer(void);
+extern bool setup_test_tagset(void);
+extern bool setup_test_template(void);
+extern bool setup_test_stash(void);
+
+int main(int argc, char *argv[])
+{
+#if defined(COOLRAIN_USE_GTHREAD)
+ g_thread_init(NULL);
+#endif
+ if (CUE_SUCCESS != CU_initialize_registry())
+ return CU_get_error();
+
+ if (! setup_test_writer() ||
+ ! setup_test_tagset() ||
+ ! setup_test_stash() ||
+ ! setup_test_template()
+ ) {
+ CU_cleanup_registry();
+ return CU_get_error();
+ }
+
+ CU_basic_set_mode(CU_BRM_VERBOSE);
+ CU_basic_run_tests();
+ CU_basic_show_failures(CU_get_failure_list());
+
+ CU_automated_run_tests();
+ CU_list_tests_to_file();
+
+ CU_cleanup_registry();
+ return CU_get_error();
+}
--- /dev/null
+/**
+ *
+ */
+#include <stdbool.h>
+#include <CUnit/CUnit.h>
+
+#include <CoolRain/stash.h>
+
+int init_test_stash(void)
+{
+ return 0;
+}
+
+int clean_test_stash(void)
+{
+ return 0;
+}
+
+
+void test_stash_simple(void)
+{
+ struct coolrain_stash stash;
+ coolrain_variant_t const *v;
+ struct coolrain_stash_value_list vl[] = {
+ { { "foo", NULL }, COOLRAIN_VARIANT_INITIALIZER, NULL },
+ { { "bar", NULL }, COOLRAIN_VARIANT_INITIALIZER, NULL },
+ { { "baz", NULL }, COOLRAIN_VARIANT_INITIALIZER, NULL },
+ };
+
+ CU_ASSERT_EQUAL_FATAL(coolrain_stash_initialize(&stash), COOLRAIN_SUCCESS);
+
+ coolrain_stash_set_values(&stash, vl, sizeof(vl) / sizeof(vl[0]));
+
+ coolrain_variant_set_int(&vl[0].value, 0xdeadbeef);
+ coolrain_variant_set_static_string(&vl[1].value, "hogehoge");
+ coolrain_variant_set_dynamic_string(&vl[2].value, "fugafuga");
+
+ v = coolrain_stash_get_value_(&stash, "foo");
+ CU_ASSERT_PTR_NOT_NULL(v);
+ if (v != NULL) {
+ CU_ASSERT_EQUAL(coolrain_variant_get_int(v), 0xdeadbeef);
+ }
+
+ v = coolrain_stash_get_value_(&stash, "bar");
+ CU_ASSERT_PTR_NOT_NULL(v);
+ if (v != NULL) {
+ CU_ASSERT_STRING_EQUAL(coolrain_variant_get_string(v), "hogehoge");
+ }
+
+ coolrain_stash_restore(&stash, vl, sizeof(vl) / sizeof(vl[0]));
+
+ coolrain_stash_destroy(&stash);
+}
+
+
+
+bool setup_test_stash(void)
+{
+ CU_pSuite s = CU_add_suite("Stash", init_test_stash, clean_test_stash);
+ if (NULL == s) return false;
+
+ if (NULL == CU_add_test(s, "simple", test_stash_simple))
+ return false;
+
+ return true;
+}
+
--- /dev/null
+/**
+ *
+ *
+ */
+#include <stdbool.h>
+
+#include <CUnit/CUnit.h>
+
+#include <CoolRain/tagset.h>
+
+
+int init_test_tagset(void)
+{
+ return 0;
+}
+
+int clean_test_tagset(void)
+{
+ return 0;
+}
+
+
+
+static int dummy_tag_handler_1(struct coolrain_eval_context const *cntx)
+{
+ return 0;
+}
+
+static int dummy_tag_handler_2(struct coolrain_eval_context const *cntx)
+{
+ return 0;
+}
+
+
+void test_tagset_default_handlers(void)
+{
+ struct coolrain_tagset tagset;
+
+ coolrain_tagset_initialize(&tagset);
+ CU_ASSERT_EQUAL(coolrain_tagset_register_filters(&tagset, coolrain_tagset_default_filters), COOLRAIN_SUCCESS);
+ CU_ASSERT_EQUAL(coolrain_tagset_register_tags(&tagset, coolrain_tagset_default_tags), COOLRAIN_SUCCESS);
+ coolrain_tagset_destroy(&tagset);
+}
+
+void test_tagset_add_tag(void)
+{
+ struct coolrain_tagset tagset;
+ struct coolrain_tag_desc const *tagdesc;
+ coolrain_refstring_t str;
+
+ CU_ASSERT_EQUAL(coolrain_tagset_initialize(&tagset), COOLRAIN_SUCCESS);
+
+ CU_ASSERT_EQUAL_FATAL(coolrain_tagset_add_tag(&tagset, "dummy1", dummy_tag_handler_1, 0xffffffffl), COOLRAIN_SUCCESS);
+ CU_ASSERT_EQUAL_FATAL(coolrain_tagset_add_tag(&tagset, "dummy2", dummy_tag_handler_2, 0), COOLRAIN_SUCCESS);
+
+ str.begin = "foo";
+ str.end = NULL;
+ CU_ASSERT_PTR_NULL(coolrain_tagset_lookup_tag(&tagset, &str));
+
+ str.begin = "dummy1";
+ tagdesc = coolrain_tagset_lookup_tag(&tagset, &str);
+ CU_ASSERT_PTR_NOT_NULL(tagdesc);
+ if (tagdesc != NULL) {
+ CU_ASSERT_PTR_EQUAL(tagdesc->handler, dummy_tag_handler_1);
+ CU_ASSERT_EQUAL(tagdesc->flags, 0xffffffffl);
+ }
+
+ str.begin = "dummy2";
+ tagdesc = coolrain_tagset_lookup_tag(&tagset, &str);
+ CU_ASSERT_PTR_NOT_NULL(tagdesc);
+ if (tagdesc != NULL) {
+ CU_ASSERT_PTR_EQUAL(tagdesc->handler, dummy_tag_handler_2);
+ CU_ASSERT_EQUAL(tagdesc->flags, COOLRAIN_TAG_DESC_FLAGS_ALLOCED);
+ }
+
+ coolrain_tagset_destroy(&tagset);
+}
+
+
+
+bool setup_test_tagset(void)
+{
+ CU_pSuite s = CU_add_suite("Tagset", init_test_tagset, clean_test_tagset);
+ if (NULL == s)
+ return false;
+
+ if (NULL == CU_add_test(s, "default_handlers", test_tagset_default_handlers) ||
+ NULL == CU_add_test(s, "add_tag", test_tagset_add_tag))
+ return false;
+
+ return true;
+}
--- /dev/null
+/*
+ */
+#include <stdbool.h>
+#include <CUnit/Basic.h>
+#include <CoolRain/tagset.h>
+#include <CoolRain/tagset.h>
+#include <CoolRain/CoolRain.h>
+
+
+static struct coolrain_tagset empty_tagset;
+static struct coolrain_tagset tagset_1;
+
+
+static int handler_one(struct coolrain_eval_context const *cntx)
+{
+ return coolrain_writer_write(cntx->writer, "-ONE-", -1);
+}
+
+static int handler_two(struct coolrain_eval_context const *cntx)
+{
+ return coolrain_writer_write(cntx->writer, "-TWO-", -1);
+}
+
+static int handler_four(struct coolrain_eval_context const *cntx)
+{
+ int i = 0;
+ int r;
+ while (i < 5) {
+ r = coolrain_template_eval_content(cntx);
+ if (r < 0) return r;
+ i++;
+ }
+}
+
+
+static int handler_html_specials(struct coolrain_eval_context const *cntx)
+{
+ return coolrain_writer_write(cntx->writer, "&<<SPECIAL>>\"'", -1);
+}
+
+
+
+static struct coolrain_tag_desc tags[] = {
+ { { "one", NULL }, handler_one, 0 },
+ { { "two", NULL }, handler_two, 0 },
+ { { "four", NULL }, handler_four, 0 },
+ { { "html_specials", NULL }, handler_html_specials, 0 },
+ /* */
+ { { NULL, NULL}, NULL, 0 }
+};
+
+
+int init_test_template(void)
+{
+ coolrain_tagset_initialize(&empty_tagset);
+ coolrain_tagset_initialize(&tagset_1);
+ coolrain_tagset_register_tags(&tagset_1, tags);
+ coolrain_tagset_register_filters(&tagset_1, coolrain_tagset_default_filters);
+ coolrain_tagset_register_tags(&tagset_1, coolrain_tagset_default_tags);
+
+ return 0;
+}
+
+int clean_test_template(void)
+{
+ coolrain_tagset_destroy(&empty_tagset);
+ coolrain_tagset_destroy(&tagset_1);
+ return 0;
+}
+
+
+static char template_1[] =
+"one <two/> <two /><two three='yes' />"
+"<four escape_html='yes'>four-> </four>"
+"<html_specials />"
+"<html_specials escape_html='1' />";
+
+static char obuffer[4096];
+
+
+void test_template_parse(void)
+{
+ struct coolrain_template tmpl;
+ CU_ASSERT_EQUAL_FATAL(coolrain_template_initialize(&tmpl, &empty_tagset), COOLRAIN_SUCCESS);
+ CU_ASSERT_EQUAL(coolrain_template_parse(&tmpl, "template_1", template_1, strlen(template_1), NULL, NULL), COOLRAIN_SUCCESS);
+ coolrain_template_destroy(&tmpl);
+}
+
+void test_template_nonprocess(void)
+{
+ struct coolrain_template tmpl;
+ struct coolrain_stash stash;
+ struct coolrain_writer writer;
+
+ CU_ASSERT_EQUAL_FATAL(coolrain_stash_initialize(&stash), COOLRAIN_SUCCESS);
+
+ CU_ASSERT_EQUAL_FATAL(coolrain_template_initialize(&tmpl, &empty_tagset), COOLRAIN_SUCCESS);
+ CU_ASSERT_EQUAL_FATAL(coolrain_template_parse(&tmpl, "template_1", template_1, strlen(template_1), NULL, NULL), COOLRAIN_SUCCESS);
+
+ CU_ASSERT_EQUAL_FATAL(coolrain_writer_initialize(&writer, obuffer, sizeof(obuffer)), COOLRAIN_SUCCESS);
+
+ CU_ASSERT_FATAL(coolrain_template_run(&tmpl, &stash, &writer) >= 0);
+
+ coolrain_template_destroy(&tmpl);
+ coolrain_stash_destroy(&stash);
+ coolrain_writer_destroy(&writer);
+
+ CU_ASSERT_NSTRING_EQUAL(obuffer, template_1, strlen(template_1));
+}
+
+
+
+void test_template_simple(void)
+{
+ struct coolrain_template tmpl;
+ struct coolrain_stash stash;
+ struct coolrain_writer writer;
+ static char const result[] =
+ "one -TWO- -TWO--TWO-"
+ "four-> four-> four-> four-> four-> "
+ "&<<SPECIAL>>\"'"
+ "&<<SPECIAL>>"'";
+
+ CU_ASSERT_EQUAL_FATAL(coolrain_stash_initialize(&stash), COOLRAIN_SUCCESS);
+
+ CU_ASSERT_EQUAL_FATAL(coolrain_template_initialize(&tmpl, &tagset_1), COOLRAIN_SUCCESS);
+ CU_ASSERT_EQUAL_FATAL(coolrain_template_parse(&tmpl, "template_1", template_1, strlen(template_1), NULL, NULL), COOLRAIN_SUCCESS);
+
+ CU_ASSERT_EQUAL_FATAL(coolrain_writer_initialize(&writer, obuffer, sizeof(obuffer)), COOLRAIN_SUCCESS);
+
+ CU_ASSERT_FATAL(coolrain_template_run(&tmpl, &stash, &writer) >= 0);
+
+ coolrain_template_destroy(&tmpl);
+ coolrain_stash_destroy(&stash);
+ coolrain_writer_destroy(&writer);
+
+ CU_ASSERT_NSTRING_EQUAL(obuffer, result, strlen(result));
+
+}
+
+
+void test_template_ignore(void)
+{
+ struct coolrain_template tmpl;
+ struct coolrain_stash stash;
+ struct coolrain_writer writer;
+ static char const source[] = "foo<CR:ignore>bar</CR:ignore>baz";
+ static char const result[] = "foobaz";
+
+ coolrain_stash_initialize(&stash);
+ coolrain_writer_initialize(&writer, obuffer, sizeof(obuffer));
+
+ coolrain_template_initialize(&tmpl, &tagset_1);
+ CU_ASSERT_EQUAL_FATAL(coolrain_template_parse(&tmpl, "ignore_test", source, sizeof(source) - 1, NULL, NULL), COOLRAIN_SUCCESS);
+ CU_ASSERT_FATAL(coolrain_template_run(&tmpl, &stash, &writer) >= 0);
+
+ coolrain_template_destroy(&tmpl);
+ coolrain_stash_destroy(&stash);
+ coolrain_writer_destroy(&writer);
+
+ CU_ASSERT_NSTRING_EQUAL(obuffer, result, 6);
+}
+
+
+static int test_stash_lookup_handler(coolrain_variant_t * restrict v, coolrain_refstring_t * restrict key)
+{
+ coolrain_variant_set_int(v, 25);
+ return 0;
+}
+
+
+void test_template_let_get(void)
+{
+ struct coolrain_template tmpl;
+ struct coolrain_stash stash;
+ struct coolrain_writer writer;
+ struct coolrain_stash_value_list vl[1];
+ static char const source[] =
+ "<CR:let hello='hello' world=world><CR:var name=\"hello\" />, <CR:var name='world' />! #<CR:var name='onethree' /> (${onethree})</CR:let>${UNKNOWN}";
+ static char const result[] = "hello, world! #13 (13)25";
+
+ coolrain_stash_initialize(&stash);
+ coolrain_stash_set_lookup_handler(&stash, test_stash_lookup_handler);
+ vl[0].name.begin = "onethree";
+ vl[0].name.end = NULL;
+ coolrain_variant_initialize(&vl[0].value);
+ coolrain_variant_set_int(&vl[0].value, 13);
+ vl[0].old_value = NULL;
+ coolrain_stash_set_values(&stash, vl, 1);
+
+ coolrain_writer_initialize(&writer, obuffer, sizeof(obuffer));
+
+ coolrain_template_initialize(&tmpl, &tagset_1);
+ CU_ASSERT_EQUAL_FATAL(coolrain_template_parse(&tmpl, "let_get_test", source, sizeof(source) - 1, NULL, NULL), COOLRAIN_SUCCESS);
+ CU_ASSERT_FATAL(coolrain_template_run(&tmpl, &stash, &writer) >= 0);
+
+ coolrain_stash_restore(&stash, vl, 1);
+
+ coolrain_template_destroy(&tmpl);
+ coolrain_stash_destroy(&stash);
+ coolrain_writer_destroy(&writer);
+
+ CU_ASSERT_NSTRING_EQUAL(obuffer, result, strlen(result));
+}
+
+
+
+
+bool setup_test_template(void)
+{
+
+ CU_pSuite s = CU_add_suite("Template", init_test_template, clean_test_template);
+ if (NULL == s) return false;
+
+ if (NULL == CU_add_test(s, "parse", test_template_parse) ||
+ NULL == CU_add_test(s, "nonprocess", test_template_nonprocess) ||
+ NULL == CU_add_test(s, "simple", test_template_simple) ||
+ NULL == CU_add_test(s, "ignore", test_template_ignore) ||
+ NULL == CU_add_test(s, "let_get", test_template_let_get)
+ )
+ return false;
+ return true;
+}
+
+
--- /dev/null
+/**
+ *
+ */
+#include <stdbool.h>
+
+#include <CUnit/Basic.h>
+
+#include <CoolRain/writer.h>
+
+
+int init_test_writer(void) { return 0; }
+int clean_test_writer(void) { return 0; }
+
+
+void test_writer_simple_buffering(void)
+{
+ char buf[1024];
+ struct coolrain_writer w;
+
+ CU_ASSERT_EQUAL(coolrain_writer_initialize(&w, buf, 1024), COOLRAIN_SUCCESS);
+ CU_ASSERT_EQUAL(coolrain_writer_write(&w, "foobar", (size_t)-1), 6);
+ CU_ASSERT_EQUAL(coolrain_writer_write(&w, "baz", 4), 4);
+ CU_ASSERT_STRING_EQUAL(buf, "foobarbaz");
+ CU_ASSERT_EQUAL(coolrain_writer_destroy(&w), COOLRAIN_SUCCESS);
+}
+
+
+
+void test_writer_buffer_full(void)
+{
+ char buf[10];
+ struct coolrain_writer w;
+
+ CU_ASSERT_EQUAL(coolrain_writer_initialize(&w, buf, 10), COOLRAIN_SUCCESS);
+ CU_ASSERT_EQUAL(coolrain_writer_write(&w, "0123456789", (size_t)-1), 10);
+ CU_ASSERT_EQUAL(coolrain_writer_write(&w, "abcdefg", (size_t)-1), 0);
+ CU_ASSERT_EQUAL(coolrain_writer_destroy(&w), COOLRAIN_SUCCESS);
+
+ CU_ASSERT_EQUAL(coolrain_writer_initialize(&w, buf, 10), COOLRAIN_SUCCESS);
+ CU_ASSERT_EQUAL(coolrain_writer_write(&w, "0123456789abcdef", (size_t)-1), 10);
+ CU_ASSERT_EQUAL(coolrain_writer_write(&w, "abcdefg", (size_t)-1), 0);
+ CU_ASSERT_EQUAL(coolrain_writer_destroy(&w), COOLRAIN_SUCCESS);
+}
+
+
+void test_writer_html_filter(void)
+{
+ char buf[1024];
+ struct coolrain_writer w;
+
+ CU_ASSERT_EQUAL(coolrain_writer_initialize(&w, buf, 1024),COOLRAIN_SUCCESS);
+ w.write_handler = coolrain_html_filter;
+
+ CU_ASSERT_EQUAL(coolrain_writer_write(&w, "abc<def>ghi&", (size_t)-1), 12);
+ CU_ASSERT_NSTRING_EQUAL(buf, "abc<def>ghi&", 22);
+
+ CU_ASSERT_EQUAL(coolrain_writer_destroy(&w), COOLRAIN_SUCCESS);
+}
+
+void test_writer_url_filter(void)
+{
+ char buf[1024];
+ struct coolrain_writer w;
+
+ CU_ASSERT_EQUAL(coolrain_writer_initialize(&w, buf, 1024), COOLRAIN_SUCCESS);
+ w.write_handler = coolrain_url_filter;
+
+ CU_ASSERT_EQUAL(coolrain_writer_write(&w, "abc&def#ghi?klmn/opq +", 23), 23);
+ CU_ASSERT_STRING_EQUAL(buf, "abc%26def%23ghi%3Fklmn%2Fopq+%2B");
+
+ CU_ASSERT_EQUAL(coolrain_writer_destroy(&w), COOLRAIN_SUCCESS);
+}
+
+
+
+void test_writer_chain(void)
+{
+ char buf[2][16];
+ struct coolrain_writer w[2];
+
+ memset(buf, 0, sizeof(buf));
+
+ coolrain_writer_initialize(&w[0], buf[0], 16);
+ coolrain_writer_initialize(&w[1], buf[1], 10);
+
+ CU_ASSERT_EQUAL(coolrain_writer_chain(&w[0], &w[1]), COOLRAIN_SUCCESS);
+
+ CU_ASSERT_EQUAL(coolrain_writer_write(&w[0], "0123456789abcdef", -1), 16);
+ CU_ASSERT_NSTRING_EQUAL(buf[0], "0123456789abcdef", 16);
+ CU_ASSERT_EQUAL(buf[1][0], '\0');
+
+ CU_ASSERT_EQUAL(coolrain_writer_write(&w[0], "0123456789", -1), 10);
+ CU_ASSERT_NSTRING_EQUAL(buf[0], "abcdef0123456789", 16);
+ CU_ASSERT_NSTRING_EQUAL(buf[1], "0123456789", 10);
+
+ CU_ASSERT_EQUAL(coolrain_writer_write(&w[0], "0", 1), 0);
+
+ coolrain_writer_destroy(&w[0]);
+ coolrain_writer_destroy(&w[1]);
+}
+
+
+
+bool setup_test_writer(void)
+{
+ /* add a suite to the registry */
+ CU_pSuite s = CU_add_suite("Writer", init_test_writer, clean_test_writer);
+ if (NULL == s)
+ return false;
+
+ if (NULL == CU_add_test(s, "simple_buffering", test_writer_simple_buffering) ||
+ NULL == CU_add_test(s, "buffer_full", test_writer_buffer_full) ||
+ NULL == CU_add_test(s, "chain", test_writer_chain) ||
+ NULL == CU_add_test(s, "html_filter", test_writer_html_filter) ||
+ NULL == CU_add_test(s, "url_filter", test_writer_url_filter)) {
+ return false;
+ }
+
+ return true;
+}
+
+
+
+