tema
 
(Mikhail Kryshen)
2008-02-19: Tema 0.3 (imported from CVS). release_0_3

Tema 0.3 (imported from CVS).

diff --git a/.cvsignore b/.cvsignore
new file mode 100644
--- /dev/null
+++ b/.cvsignore
@@ -0,0 +1,1 @@
+build
diff --git a/COPYING b/COPYING
new file mode 100644
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/COPYING.LESSER b/COPYING.LESSER
new file mode 100644
--- /dev/null
+++ b/COPYING.LESSER
@@ -0,0 +1,165 @@
+		   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions. 
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version. 
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/COPYING.apache b/COPYING.apache
new file mode 100644
--- /dev/null
+++ b/COPYING.apache
@@ -0,0 +1,202 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/NOTICE b/NOTICE
new file mode 100644
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,13 @@
+Tema
+Copyright 2006-2008 Mikhail Kryshen <mikhail@kryshen.pp.ru>
+
+This is free software. You may redistribute copies of it under the terms of
+the GNU Lesser General Public License version 3 or later.
+
+
+
+Apache Commons CLI
+Copyright 2001-2007 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/TODO b/TODO
deleted file mode 100644
--- a/TODO
+++ /dev/null
@@ -1,1 +0,0 @@
-if, <, >, =, +, -, *, /
diff --git a/build.xml b/build.xml
--- a/build.xml
+++ b/build.xml
@@ -1,44 +1,113 @@
 <?xml version="1.0"?>
 <project name="tema" default="dist" basedir=".">
-
+    
+    <property name="version"  value="0.3"/>
+    
     <property name="src"      value="src"/>
     <property name="build"    value="build"/>
     <property name="dist"     value="dist"/>
     <property name="res"      value="res"/>
     <property name="doc"      value="doc"/>
+    <property name="lib"      value="${dist}/lib"/>
     <property name="jar_file" value="tema.jar"/>
-
+    
+    <property name="ant_jar" value="/usr/share/java/ant.jar"/>
+    
+    <path id="project.libs">
+        <fileset dir="${lib}">
+            <include name="**/*.jar"/>
+        </fileset>
+    </path>
+    
     <target name="init">
-	<tstamp/>
-	<mkdir dir="${build}"/>
+        <tstamp/>
+        <mkdir dir="${build}"/>
     </target>
-
+    
     <target name="compile" depends="init">
         <javac srcdir="${src}" destdir="${build}"
                deprecation="on" optimize="on" debug="on">
-          <!-- <compilerarg value="-Xlint:unchecked"/> -->
+            <compilerarg value="-Xlint:unchecked"/>
+            <classpath refid="project.libs"/>
         </javac>
+        
+        <copy todir="${build}">
+            <fileset dir="${res}"/>
+        </copy>
+    </target>
+    
+    <target name="dist" depends="compile">
+        <jar jarfile="${dist}/${jar_file}" manifest="${src}/Manifest.mf">
+            <fileset dir="${build}" includes="**/*.class"/>
+            <fileset dir="${build}" includes="**/*.template"/>
+        </jar>
+    </target>
+      
+    <target name="run" depends="compile">
+        <java classname="kryshen.tema.Tema" fork="true">
+            <classpath>
+                <pathelement location="${build}"/>
+                <path refid="project.libs"/>
+            </classpath>
+        </java>
+    </target>
+    
+    <target name="run.demo" depends="compile">
+        <java classname="kryshen.tema.Tema" fork="true">
+            <classpath>
+                <pathelement location="${build}"/>
+                <path refid="project.libs"/>
+            </classpath>
+            <arg value="-demo"/>
+        </java>        
+    </target>
+    
+    <target name="doc.manual" depends="dist">
+        <taskdef name="tema"
+                 classname="kryshen.tema.ant.TemaTask"
+                 classpath="${dist}/tema.jar"/>
+                 
+        <tema infile="${doc}/manual/manual.tema"
+              outfile="${doc}/manual/manual.html"
+              basedir="${doc}/manual"/>
     </target>
 
-    <target name="dist" depends="compile">
-	<jar jarfile="${dist}/${jar_file}" manifest="${src}/Manifest.mf">
-	    <fileset dir="${build}" includes="**/*.class"/>
-	    <fileset dir="${res}" includes="**/*"/>
-	</jar>
-    </target>
-
-    <target name="javadoc" depends="init">
+    <target name="doc.api" depends="init">
         <javadoc destdir="${doc}/api" sourcepath="${src}"
                  packagenames="kryshen.tema.*">
+            <classpath> 
+                <path refid="project.libs"/>
+                <pathelement location="${ant_jar}"/>
+            </classpath>
         </javadoc>
     </target>
- 
-    <target name="clean">
-	<delete>
-	    <fileset dir="${build}" includes="**/*.class"/>
-	</delete>
-	<delete file="${dist}/${jar_file}"/>
-	<delete dir="${doc}/api"/>
+    
+    <target name="package.tar" depends="dist,doc.manual,doc.api">
+        <tar destfile="${dist}/${ant.project.name}-${version}.tar.gz"
+             compression="gzip">
+            <tarfileset dir="${basedir}"
+                        prefix="${ant.project.name}-${version}">
+                <exclude name="${build}/**"/>
+                <exclude name="misc/**"/>
+                <exclude name="test/**"/>
+                <exclude name="nbproject/**"/>
+                <exclude name="${dist}/${ant.project.name}-*.tar.gz"/>
+                <exclude name="${dist}/tema"/>
+            </tarfileset>
+            <tarfileset dir="${basedir}"
+                        prefix="${ant.project.name}-${version}"
+                        mode="755">
+                <include name="${dist}/tema"/>
+            </tarfileset>
+        </tar>
+    </target>
+    
+    <target name="clean" depends="init">
+        <delete>
+            <fileset dir="${build}" includes="**/*.class"/>
+        </delete>
+        <delete file="${dist}/${jar_file}"/>
+        <delete dir="${doc}/api"/>
     </target>
     
 </project>
diff --git a/dist/.cvsignore b/dist/.cvsignore
new file mode 100644
--- /dev/null
+++ b/dist/.cvsignore
@@ -0,0 +1,2 @@
+tema.jar
+tema-*.tar.gz
diff --git a/dist/biotopes/biotope-top.sql b/dist/biotopes/biotope-top.sql
deleted file mode 100644
--- a/dist/biotopes/biotope-top.sql
+++ /dev/null
@@ -1,20 +0,0 @@
-SELECT t1.*,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1) AND
-Right(t2.rangcode, 8) = '00000000') AS class0,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 3) = Left(t1.rangcode, 3) AND
-Right(t2.rangcode, 6) = '000000') AS class1,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 5) = Left(t1.rangcode, 5) AND
-Right(t2.rangcode, 4) = '0000') AS class2,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 7) = Left(t1.rangcode, 7) AND
-Right(t2.rangcode, 2) = '00') AS class3
-
-FROM biotopelist AS t1
-WHERE Right(t1.rangcode, 2) <> '00' AND rusname
diff --git a/dist/biotopes/biotope.sql b/dist/biotopes/biotope.sql
deleted file mode 100644
--- a/dist/biotopes/biotope.sql
+++ /dev/null
@@ -1,24 +0,0 @@
-SELECT t1.*,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1)
-AND Right(t2.rangcode, 8)  = '00000000'
-AND Right(t1.rangcode, 8) <> '00000000') AS class0,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 3) = Left(t1.rangcode, 3)
-AND Right(t2.rangcode, 6)  = '000000'
-AND Right(t1.rangcode, 6) <> '000000') AS class1,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 5) = Left(t1.rangcode, 5)
-AND Right(t2.rangcode, 4)  = '0000'
-AND Right(t1.rangcode, 4) <> '0000') AS class2,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 7) = Left(t1.rangcode, 7)
-AND Right(t2.rangcode, 2)  = '00'
-AND Right(t1.rangcode, 2) <> '00') AS class3
-
-FROM biotopelist AS t1
-WHERE rusname
diff --git a/dist/biotopes/biotope.template b/dist/biotopes/biotope.template
deleted file mode 100644
--- a/dist/biotopes/biotope.template
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-5"?>
-<!DOCTYPE _ SYSTEM "brief.dtd">
-<!-- rangcode: <%xml_escape db\rangcode%> -->
-<_ show="0">
-  < display="0">
-    <%optional:<_ ="1" file="<%xml_escape image:maps/<%db\rangcode%>.gif maps/<%db\code%>.png png%>"/>%>
-<%query:photo_sql [%\<%include\photo.template%>%] <%get\code%>%>
-  </>
-  <><%xml_escape db\rusname%></>
-  <><%xml_escape db\class0%></>
-  <1><%xml_escape db\class1%></1>
-  <2><%xml_escape db\class2%></2>
-  <3><%xml_escape db\class3%></3>
-  <_ rows="3"><%xml_cdata db\descript%></_>
-  <_ rows="3"><%xml_cdata db\geobotdescr%></_>
-  <_><%xml_escape db\contributors%></_>
-</_>
diff --git a/dist/biotopes/brief.dtd b/dist/biotopes/brief.dtd
deleted file mode 100644
--- a/dist/biotopes/brief.dtd
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml encoding="ISO-8859-5"?>
-<!ELEMENT _ (?, , , 1, 2, 3, _, _, _)>
-<!ELEMENT  (|_)*>
-<!ELEMENT  EMPTY>
-<!ELEMENT _ EMPTY>
-<!ELEMENT  (#PCDATA)>
-<!ELEMENT  (#PCDATA)>
-<!ELEMENT 1 (#PCDATA)>
-<!ELEMENT 2 (#PCDATA)>
-<!ELEMENT 3 (#PCDATA)>
-<!ELEMENT _ (#PCDATA)>
-<!ELEMENT _ (#PCDATA)>
-<!ELEMENT _ (#PCDATA)>
-<!ATTLIST _ show (0|1) "1"> 
-<!ATTLIST 
-	display CDATA #FIXED "0">
-<!ATTLIST 
-	num ID #REQUIRED
-	file CDATA #REQUIRED
-	big CDATA #REQUIRED
-	text CDATA #IMPLIED
-	 CDATA #IMPLIED>
-<!ATTLIST _
-	num ID #REQUIRED
-	file CDATA #REQUIRED>
-<!ATTLIST _ rows CDATA #FIXED "3">
-<!ATTLIST _ rows CDATA #FIXED "3">
diff --git a/dist/biotopes/class.sql b/dist/biotopes/class.sql
deleted file mode 100644
--- a/dist/biotopes/class.sql
+++ /dev/null
@@ -1,7 +0,0 @@
-SELECT t1.*
-FROM biotopelist AS t1
-WHERE rusname AND Right(t1.rangcode, 8)  = '00000000'
-  AND EXISTS
-    (SELECT NULL FROM biotopelist AS t2
-     WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1)
-     AND Right(t2.rangcode, 8) <> '00000000')
diff --git a/dist/biotopes/classes.template b/dist/biotopes/classes.template
deleted file mode 100644
--- a/dist/biotopes/classes.template
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-5"?>
-<><%query:class_sql [%\
-  <!-- rangcode: <%xml_escape get\rangcode%> -->
-  < id="<%get\NUMBER%>"><%xml_escape db\rusname%></>%]%>
-</>
diff --git a/dist/biotopes/doc/article.txt b/dist/biotopes/doc/article.txt
deleted file mode 100644
--- a/dist/biotopes/doc/article.txt
+++ /dev/null
@@ -1,391 +0,0 @@
-*     XML-  .
-
-      " 
-"   Microsoft Access  XML- 
-       DbReader.
-
- DbReader     
-      .  
-        
-    .   
- SQL-,     
-.
-
-
-*  :
-
-    <%<_>{:|\}<>%>
-   
-
-<_> -   ,  .
-      .
-<> - ,  .
-
-
-*  :
-
-    [<_>][<>]
-  
-
-<_> -   ,  .
-      .
-<> - ,      .
-      .  ,   
-    ,   .
-
-      ,  
-  :
-: -  ,
-\ -   .
-
-        ,  ,
-  ,        
-   .
-
-     -  .  
- -      .  ,
-    -    
-.
-
-  '<', '>'    '[', ']'.
-
-
-*  :
-
-:      set
-:    <>
-:        <>
-:        <>
-:    <>
- : 1
-
-:      get
-:    <>
-:      <>. 
- : 1,     , 0 - .
-
-:      prepare
-:    <>
-:        <>
-:      SQL- <>  ,
-                   <>.
-:    <>
- : 1
-
-:      query
-:    <_> <> <1> ... <N>
-:        ,   
-               prepare.    
-                 '?'.    
-              ,    <>.  
-               <>    NUMBER,
-                  . ,
-                 ,   
-              <>   SUPER.<_>.
-
-:       <>   
-              .
- :    .
-
-:      optional
-:        <>
-:    <>,      
-                  0,  -  .
- : 1,   -  , 0 - .
-
-:      image
-:    <_> <_> <> [<_> [<_>]]
-:         <_> ( 
-                 "resource_base") 
-                  ,   
-              <_>.      , 
-                . 
-:    <_>   ,   - .
- : 1   , 0 - .
-
-:      copy
-:    <_> <_>
-:       <_>   <_>
-	      ( <_>  
-                "resource_base").
-:    <_>   ,   - .
- : 1   , 0 - .
-
-:      write
-:    <_>
-:        <>
-:      <>   <_>.
-:    <_>   ,   - .
- : 1   , 0 - .
-
-:      read
-:        <_>
-:       <_>.
-:        , 
-               - .
- : 1   , 0 - .
-
-:      include
-:        <_>
-:         <_>.
-:      .
- :  ,    .
-
-:      !
-:        <>
-:     .
-:    .
- :  ,     .
-
-:      replace
-:    <1> <2>
-:        <>
-:    ,     <1>
-	       <2>.
- :  ,     .
-
-:      xml_escape
-:        <>
-:     <>,    
-              '&', '<', '>', '`', '\'   
-	       XML.
- :  ,     .
-
-:      xml_cdata
-:        <>
-:        XML CDATA.
- :  ,     .
-
- DbReader :     
-,       Java.
-
-
-*   DbReader    
-  "  "   Microsoft Access 
-  XML-    
-
-       "
- " (   ):
-
-biotopelist (code, rangcode, rusname, engname, descript, geobotdescr,
-             contributors, descrdate)
-
-  code - ,  ;
-  rangcode -    ABBCCDDEE,  , BB, CC, DD,  - 
-             ,   ;
-  rusname, engname, descript, geobotdescr -  ;
-  descrdate - .
-
-photos (biotope, photo, authphoto)
-
-  biotope - ,   code  biotopelist;
-  photo -   ;
-  authphoto - .
-
-  xml-     biotopelist, 
-  ,    ,  
-   .
-
-  xml-     
- DbReader ( biotope.template):
-
-----------------------------------------------------------------------
-<?xml version="1.0" encoding="ISO-8859-5"?>
-<!DOCTYPE _ SYSTEM "brief.dtd">
-<!-- rangcode: <%xml_escape get\rangcode%> -->
-<_ show="0">
-  < display="0">
-    <%optional:<_ ="1"
-    file="<%xml_escape image:maps/<%get\rangcode%>.gif maps/<%get\code%>.png png%>"/>%>
-<%query:photo_sql [%\<%include\photo.template%>%] <%get\code%>%>
-  </>
-  <><%xml_escape get\rusname%></>
-  <><%xml_escape get\class0%></>
-  <1><%xml_escape get\class1%></1>
-  <2><%xml_escape get\class2%></2>
-  <3><%xml_escape get\class3%></3>
-  <_ rows="3"><%xml_cdata get\descript%></_>
-  <_ rows="3"><%xml_cdata get\geobotdescr%></_>
-  <_><%xml_escape get\contributors%></_>
-</_>
-----------------------------------------------------------------------
-
- SQL-,    
-   rangcode, rusname, class0, class1, class2,
-class3, descript, geobotdescr  contributors ( biotope.sql):
-
-----------------------------------------------------------------------
-SELECT t1.*,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1)
-AND Right(t2.rangcode, 8)  = '00000000'
-AND Right(t1.rangcode, 8) <> '00000000') AS class0,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 3) = Left(t1.rangcode, 3)
-AND Right(t2.rangcode, 6)  = '000000'
-AND Right(t1.rangcode, 6) <> '000000') AS class1,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 5) = Left(t1.rangcode, 5)
-AND Right(t2.rangcode, 4)  = '0000'
-AND Right(t1.rangcode, 4) <> '0000') AS class2,
-
-(SELECT t2.rusname FROM biotopelist AS t2
-WHERE Left(t2.rangcode, 7) = Left(t1.rangcode, 7)
-AND Right(t2.rangcode, 2)  = '00'
-AND Right(t1.rangcode, 2) <> '00') AS class3
-
-FROM biotopelist AS t1
-WHERE rusname
-----------------------------------------------------------------------
-
- biotope.template   <%include\photo.template%>,
-    ( photo.template):
-
-----------------------------------------------------------------------
-    < num="<%xml_escape get\NUMBER%>"
-       <%optional:="<%xml_escape get\authphoto%>" %>text="" 
-       file="<%xml_escape image:biotopephotos/<%get\photo%> images/<%get\SUPER.code%>_<%get\NUMBER%>.jpg jpg 300 300%>" 
-        big="<%xml_escape copy:biotopephotos/<%get\photo%> images/big/<%get\SUPER.code%>_<%get\NUMBER%>.jpg%>"/>
-----------------------------------------------------------------------
-
- SQL-    
-(photo.sql):
-
-----------------------------------------------------------------------
-SELECT * from photos WHERE biotope = ?
-----------------------------------------------------------------------
-
-,    ( classes.template):
-
-----------------------------------------------------------------------
-<?xml version="1.0" encoding="ISO-8859-5"?>
-<><%query:class_sql [%\
-  <!-- rangcode: <%xml_escape get\rangcode%> -->
-  < id="<%get\NUMBER%>"><%xml_escape get\rusname%></>%]%>
-</>
-----------------------------------------------------------------------
-
- SQL- (class.sql):
-
-----------------------------------------------------------------------
-SELECT t1.*
-FROM biotopelist AS t1
-WHERE rusname AND Right(t1.rangcode, 8)  = '00000000'
-  AND EXISTS
-    (SELECT NULL FROM biotopelist AS t2
-     WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1)
-     AND Right(t2.rangcode, 8) <> '00000000')
-----------------------------------------------------------------------
-
-,    ( plants.template):
-
-----------------------------------------------------------------------
-<?xml version="1.0" encoding="ISO-8859-5"?>
-<><%query:plant_sql [%\
-  <!-- rangcode: <%xml_escape get\rangcode%> -->
-  <><%xml_escape get\rusname%></>%]%>
-</>
-----------------------------------------------------------------------
-
- SQL- (plant.sql):
-
-----------------------------------------------------------------------
-SELECT t1.*
-FROM biotopelist AS t1
-WHERE rusname AND Right(t1.rangcode, 2) <> '00'
-----------------------------------------------------------------------
-
- DbReader      index.template,
-     SQL-  
-:
-
-----------------------------------------------------------------------
-<%!:
-  <%prepare:biotope_sql <%read\biotope.sql%>%>
-  <%prepare:photo_sql <%read\photo.sql%>%>
-  <%prepare:class_sql <%read\class.sql%>%>
-  <%prepare:plant_sql <%read\plant.sql%>%>
-
-  <%query:biotope_sql [%\<%write:<%get\code%>.xml <%include\biotope.template%>%> %]%>
-
-  <%write:classes.xml <%include\classes.template%>%>
-  <%write:plants.xml <%include\plants.template%>%>
-%>
-----------------------------------------------------------------------
-
-  XML-:
-
-----------------------------------------------------------------------
-<?xml version="1.0" encoding="ISO-8859-5"?>
-<!DOCTYPE _ SYSTEM "brief.dtd">
-<!-- rangcode: E04020302 -->
-<_ show="0">
-  < display="0">    
-    < num="1" =" .." text="" 
-                file="images/3_1.jpg" 
-                big="images/big/3_1.jpg"/>
-    < num="2" =" .." text="" 
-                file="images/3_2.jpg" 
-                big="images/big/3_2.jpg"/>
-  </>
-  <>Betula sp. - Avenella flexuosa - Polytrichum commune</>
-  <>E.  </>
-  <1> </1>
-  <2></2>
-  <3></3>
-  <_ rows="3"><![CDATA[       .   5 - 7            ,   ,   ,   .]]></_>
-  <_ rows="3"></_>
-  <_></_>
-</_>
-----------------------------------------------------------------------
-
-
-*   DbReader
-
- DbReader     Java  
-   :
-
-ru.karrc.dbreader.DbReader: c   . 
-,       
- JDBC,    .
-
-ru.karrc.dbreader.TemplateParser:   ,
-    .
-
-ru.karrc.dbreader.Function:  .   
-DbReader   .
-
-ru.karrc.dbreader.FunctionDataParser:  
-.      , 
-.       
- TemplateParser.
-
-ru.karrc.dbreader.Functions:     
- ,  Function.
-
-ru.karrc.dbreader.TemplateParser: ,   
-    .
-
- ,    , 
-    .
-
-
-*    .
-
-   : 8,
- Java- ( ): 27,
-    : 1311,
-   (    ): 812,
-  : 180.
-
-
-* 
-
-     DbReader, 
-      
-      .   
-DbReader   "  " 
-  XML-    .
diff --git a/dist/biotopes/doc/readme.html b/dist/biotopes/doc/readme.html
deleted file mode 100644
--- a/dist/biotopes/doc/readme.html
+++ /dev/null
@@ -1,86 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
-          "DTD/xhtml1-transitional.dtd">
-
-<html>
-
-<head>
-  <title>DbReader</title>
-  <meta http-equiv="Content-Type" content="text/html; charset=koi8-r" />
-</head>
-
-<body>
-<h1>DbReader</h1>
-
-<h2> </h2>
-<ol>
-<li> .</li>
-
-<li>  :<br />
- Windows 2000:  //  (ODBC),  biotopes-data.mdb.</li>
-
-<li> :<br />
-  dbreader.properties  ,   :<br />
-    <code>resource: jdbc:odbc:<em></em></code><br />
-   ,       ( biotopephotos  maps):<br />
-    <code>resource_base: C:\\biotopes</code><br />
-( '\'  )</li>
-
-<li> :<br />
-<code>dbreader.bat</code> <br />
-<code>java -jar dbreader.jar</code>.<br />
-   Java Runtime Environment (JRE) 5.0,    <a href="http://java.sun.com/j2se/1.5.0/download.jsp">http://java.sun.com/j2se/1.5.0/download.jsp</a>. ,       JRE -  .
-
-        :<br />
- <code>java -Dru.karrc.dbreader.<em></em>=<em></em> -jar dbreader.jar</code><br />
-(: <code>java -Dru.karrc.dbreader.log=dbreader.log -jar dbreader.jar</code>)</li>
-</ol>
-
-<h2> </h2>
-
-<p>   biotope.sql ( ,   dbreader.properties),     biotope.template,         .</p>
-<p> : <code><%<em>_</em>[:|\]<em></em>%></code>, <em> </em> -    ,    .    ,     .</p>
-
-<h3>     </h3>
-
-<ul>
-  <li><code>:</code> -    .</li>
-  <li><code>\</code> -      .       <code><%</code>  <code>%></code> (<code><%\<%%></code>  <code><%\%%>></code>).</li>
-</ul>
-
-<h3></h3>
-
-<ul>
-  <li><code><%get:<em></em>%></code><br />
-         .</li>
-
-  <li><code><%optional:<em></em>%></code><br />
-    <em></em>,     ,    ,     .</li>
-
-  <li><code><%escape:<em></em>%></code><br />
-     ""  (     ).</li>
-
-  <li><code><%invoke:<em></em>; <em>1</em> <em>2</em> ... <em>N</em>%></code><br />
-      <em></em>.sql       <em></em>.template.        '?'.</li>
-
-  <li><code><%image:<em>_</em> <em>_</em> <em></em> [<em>_</em>] [<em>_</em>]%></code><br />
-      <em>_</em> (     "resource_base")      ,    <em>_</em>.      ,    .
-  ,  <code><%image:...%></code>     <em>_</em>,  -   .</li>
-
-  <li><code><%copy:<em>_</em> <em>_</em>%></code><br />
-    <em>_</em> (     "resource_base")  <em>_</em>.   ,  <code><%copy:...%></code>     <em>_</em>,  -   .</li>
-</ul>
-
-<h3> </h3>
-
-<p><code>NUMBER</code> -      .
-    ,          invoke,      <code>SUPER.</code> (: <code>SUPER.NUMBER</code>).</p>
-
-<h3></h3>
-
-<p>      name      :</p>
-<p><code><%escape get:name%></code></p>
-
-<p align="right"><i><a href="mailto:kryshen@cs.karelia.ru"> </a></i></p>
-
-</body>
-</html>
diff --git a/dist/biotopes/doc/readme.txt b/dist/biotopes/doc/readme.txt
deleted file mode 100644
--- a/dist/biotopes/doc/readme.txt
+++ /dev/null
@@ -1,63 +0,0 @@
-text/plain; charset=UTF-8
-
-Установка программы
--------------------
-
-1. Распаковать архив.
-
-2. Установить источник данных:
-в Windows 2000: Панель управления/Администрирование/Источники данных (ODBC), добавить biotopes.mdb.
-
-3. Настроить программу:
-В файле dbreader.properties указать имя, присвоенное источнику данных:
-    "resource: jdbc:odbc:<имя>"
-и путь к каталогу, относительно которого программа будет искать изображения (содержащий biotopephotos и maps):
-    "resource_base: C:\\biotopes"
-(символ '\' необходимо дублировать)
-
-4. Запуск программы: dbreader.bat или "java -jar dbreader.jar"
-Для запуска необходима Java Runtime Environment (JRE) 5.0, можно скачать с http://java.sun.com/j2se/1.5.0/download.jsp. Возможно, будет работать и со старыми версиями JRE - не проверял.
-
-Все значения файла конфигурации можно переопределять при запуске программы:
-    команда "java -Dru.karrc.dbreader.<ключ>=<значение> -jar dbreader.jar"
-(например: "java -Dru.karrc.dbreader.log=dbreader.log -jar dbreader.jar")
-
-
-Работа программы
-----------------
-
-Программа выполняет запрос biotope.sql (или другой, указанный в dbreader.properties), после чего обрабатывает файл biotope.template, заменяя найденные в нем инструкции на результаты их выполнения.
-
-Формат инструкций: "<%список_функций[:|\]данные%>", список функций - имена функций разделенные пробелом, список может быть пустым. Если указано несколько функций, они выполняются начиная с последней.
-
-Разделители между списком функций и данными:
-':' - данные будут обрабатываться рекурсивно.
-'\' - данные будут переданы функции без обработки. Можно использовать для экранирования комбинаций символов "<%" и "%>" ("<%\<%%>" и "<%\%%>>").
-
-Функции:
-<%get:<имя>%> - заменяется на значение встроенной переменной или колонки запроса.
-
-<%optional:<текст>%>
-Заменяется на <текст>, если в нем найдены инструкции, заменившиеся на непустые строки, иначе заменяется на пустую строку.
-
-<%escape:<текст>%>
-Заменяет в тексте "опасные" символы (таблица замен задается в файле конфигурации).
-
-<%invoke:<имя> <парам1> <парам2> ... <парамN>%>
-Выполняет запрос с параметрами <имя>.sql и заменяется на результаты обработки шаблона <имя>.template. Значения параметров подставляются в запрос вместо символа '?'.
-
-<%image:<исх_файл> <кон_файл> <формат> [<макс_ширина>] [<макс_высота>]%>
-Загружает изображение из файла <исх_файл> (путь определяется относительно конфигурационного параметра "resource_base") и преобразует его в указанный формат, сохраняя результат в <кон_файл>. Если заданы максимальная высота и ширина, большие изображения будут уменьшены.
-При успешном выполнении, инструкция "<%!image ...%>" будет заменена на значение <кон_файл>, иначе - на пустую строку.
-
-<%copy:<исх_файл> <кон_файл>%>
-Копирует файл <исх_файл> (путь определяется относительно конфигурационного параметра "resource_base").
-При успешном выполнении, инструкция "<%!copy ...%>" будет заменена на значение <кон_файл>, иначе - на пустую строку.
-
-Встроенные переменные:
-NUMBER - порядковый номер строки результата выполнения запроса.
-Чтобы обратиться к переменной запроса, из которого обрабатываемый шаблон был вызван с помощью функции invoke, перед именем переменной нужно добавить "SUPER." (например: "SUPER.NUMBER").
-
-Пример:
-Следующая инструкция получит значение из столбца name и заменит в нем специальные символы:
-<%escape get:name%>
diff --git a/dist/biotopes/main.template b/dist/biotopes/main.template
deleted file mode 100644
--- a/dist/biotopes/main.template
+++ /dev/null
@@ -1,11 +0,0 @@
-<%!:
-  <%prepare:biotope_sql <%read\biotope.sql%>%>
-  <%prepare:photo_sql <%read\photo.sql%>%>
-  <%prepare:class_sql <%read\class.sql%>%>
-  <%prepare:plant_sql <%read\plant.sql%>%>
-
-  <%query:biotope_sql [%\<%write:<%db\code%>.xml <%include\biotope.template%>%> %]%>
-
-  <%write:classes.xml <%include\classes.template%>%>
-  <%write:plants.xml <%include\plants.template%>%>
-%>
diff --git a/dist/biotopes/photo.sql b/dist/biotopes/photo.sql
deleted file mode 100644
--- a/dist/biotopes/photo.sql
+++ /dev/null
@@ -1,1 +0,0 @@
-SELECT * from photos WHERE biotope = ?
diff --git a/dist/biotopes/photo.template b/dist/biotopes/photo.template
deleted file mode 100644
--- a/dist/biotopes/photo.template
+++ /dev/null
@@ -1,3 +0,0 @@
-    < num="<%number\%>" <%optional:="<%xml_escape db\authphoto%>" %>text="" 
-                file="<%xml_escape image:biotopephotos/<%db\photo%> images/<%super.db\code%>_<%number\%>.jpg jpg 300 300%>" 
-                big="<%xml_escape copy:biotopephotos/<%db\photo%> images/big/<%super.db\code%>_<%number\%>.jpg%>"/>
diff --git a/dist/biotopes/plant.sql b/dist/biotopes/plant.sql
deleted file mode 100644
--- a/dist/biotopes/plant.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-SELECT t1.*
-FROM biotopelist AS t1
-WHERE rusname AND Right(t1.rangcode, 2) <> '00'
diff --git a/dist/biotopes/plants.template b/dist/biotopes/plants.template
deleted file mode 100644
--- a/dist/biotopes/plants.template
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-5"?>
-<><%query:plant_sql [%\
-  <!-- rangcode: <%xml_escape db\rangcode%> -->
-  <><%xml_escape db\rusname%></>%]%>
-</>
diff --git a/dist/biotopes/tema.properties b/dist/biotopes/tema.properties
deleted file mode 100644
--- a/dist/biotopes/tema.properties
+++ /dev/null
@@ -1,22 +0,0 @@
-# Data source configuration
-resource          : jdbc:odbc:biotopes-data
-driver            : sun.jdbc.odbc.JdbcOdbcDriver
-
-# Base directory for images and files
-resource_base     : C:\\biotopes\\db
-
-# Template to start processing with
-main_template     : main.template
-
-# File encodings
-input_encoding    : ISO-8859-5
-output_encoding   : ISO-8859-5
-
-# Cache templates
-cache_read        : true
-
-# Output main_template parsing result to stderr
-#output            : stderr
-
-# File to output error messages (redirect stderr)
-#log               : tema.log
diff --git a/dist/lib/commons-cli-1.1.jar b/dist/lib/commons-cli-1.1.jar
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e633afbe6842aa92b1a8f0ff3f5b8c0e3283961b
GIT binary patch
literal 36174
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
W8WilWZ{Ypong5R!njZYur~d={{1Y(%

diff --git a/dist/tema.properties b/dist/tema.properties
deleted file mode 100644
--- a/dist/tema.properties
+++ /dev/null
@@ -1,22 +0,0 @@
-# Data source configuration
-# resource          : jdbc:odbc:biotopes-data
-# driver            : sun.jdbc.odbc.JdbcOdbcDriver
-
-# Base directory for images and files
-# resource_base     : C:\\biotopes\\db
-
-# Template to start processing with
-main_template     : main.template
-
-# File encodings
-# input_encoding    : UTF-8
-# output_encoding   : UTF-8
-
-# Cache templates
-# cache_read        : true
-
-# Output main_template parsing result to stderr
-# output            : stderr
-
-# File to output error messages (redirect stderr)
-# log               : tema.log
diff --git a/doc/.cvsignore b/doc/.cvsignore
new file mode 100644
--- /dev/null
+++ b/doc/.cvsignore
@@ -0,0 +1,1 @@
+api
diff --git a/doc/manual/.cvsignore b/doc/manual/.cvsignore
new file mode 100644
--- /dev/null
+++ b/doc/manual/.cvsignore
@@ -0,0 +1,1 @@
+manual.html
diff --git a/doc/manual/document.tema b/doc/manual/document.tema
new file mode 100644
--- /dev/null
+++ b/doc/manual/document.tema
@@ -0,0 +1,107 @@
+[%\
+  Macro definitions used in manual.tema to generate HTML document.
+%]
+
+[%!\
+  Create HTML document.
+  Arguments are read in pairs (name value) until "content" name is found.
+  Then follows the document content.
+
+  TODO: generate TOC.
+%]
+<%define#document
+  <%silent while#
+    <%if:<%not equal:<%set:arg <%next_arg:%>%> content%>
+      <%export:<%arg:%> <%next_arg:%>%>
+    %>
+  %>\\
+<%if:<%not embedded:%>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+
+<head>
+  <title><%html_escape title:%></title>
+  <meta http-equiv="Content-Type" content="text/html; charset=<%charset:%>" />
+  <meta name="generator" content="<%tema:%>" />
+</head>
+
+<body>
+<h1><%html_escape title:%><%optional:<br /><%html_escape subtitle:%>%></h1>
+%>\
+<%html_escape data:%>
+<%if:<%not embedded:%>
+</body>
+</html>
+%>\
+%>
+
+<%set:charset iso-8859-1%>
+<%set:escape <%char:0%>%>
+
+[%!\
+  Create HTML tag.
+%]
+<%define#tag <%false:<%escape:%><<%data:%><%escape:%>>%>%>
+
+[%!\
+  Escape special HTML characters (except those preceded with <%escape:%>).
+%]
+<%define#html_escape
+  <%replace:<%escape:%> <%:%>
+  <%regex_replace_all:(?<!<%escape:%>)-- –
+  <%regex_replace_all:(?<!<%escape:%>)--- —
+  <%regex_replace_all:(?<!<%escape:%>)< <
+  <%regex_replace_all:(?<!<%escape:%>)> >
+  <%regex_replace_all:(?<!<%escape:%>)& &
+    <%data:%>\\
+  %>%>%>%>%>%>\
+%>
+
+<%define#section
+  <%tag:h2<%if:<%first_section:%> <%: class="first"%>%>%><%data:%><%tag:/h2%>\\
+  <%unset:first_section%>\\
+%>
+
+<%define#subsection <%tag:h3%><%data:%><%tag:/h3%>%>
+
+<%define#par <%tag:p%><%data:%><%tag:/p%>%>
+
+<%define#itemize
+<%tag:ul%>
+<%while#\
+  <%tag:li%><%next_arg:%><%tag:/li%>
+%>\
+<%tag:/ul%>
+%>
+
+<%define#code <%tag:code%><%replace:-- <%escape:%>-- <%data:%>%><%tag:/code%>%>
+
+<%define#emph <%tag:i%><%data:%><%tag:/i%>%>
+
+<%define#link
+  <%silent set:url <%next_arg:%>%>\\
+  <%tag:a href="<%url:%>"%>\\
+    <%if_else:<%has_more_data:%>
+      <%data:%>
+      <%url:%>%>\\
+  <%tag:/a%>\\
+%>
+
+<%define#function
+  <%silent set:name <%next_arg:%>%>\\
+  <%silent while#
+    <%set:<%next_arg:%> <%next_arg:%>%>
+  %>\\
+  <%tag:div%>
+    <%tag:b%><%name:%><%tag:/b%><%tag:br /%>
+  <%tag:/div%>
+  <%optional:<%tag:div style="padding-left:1em; margin-bottom: 0.5em;"%>
+    <%description:%>
+  <%tag:/div%>%>
+  <%tag:div style="padding-left:1em; margin-bottom: 1em;"%>
+    <%optional:<%tag:b%>Arguments:<%tag:/b%> <%arguments:%><%tag:br /%>%>
+    <%optional:<%tag:b%>Data:<%tag:/b%> <%input_data:%><%tag:br /%>%>
+    <%optional:<%tag:b%>Output:<%tag:/b%> <%output:%><%tag:br /%>%>
+  <%tag:/div%>
+%>
diff --git a/doc/manual/index.html b/doc/manual/index.html
deleted file mode 100644
--- a/doc/manual/index.html
+++ /dev/null
@@ -1,358 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
-          "DTD/xhtml1-transitional.dtd">
-
-<html>
-
-<head>
-  <title>Макропроцессор TEMA</title>
-  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-</head>
-
-<body>
-<h1>Макропроцессор TEMA</h1>
-
-Макропроцессор TEMA обрабатывает заданные шаблоны текстовых файлов и
-заменяет найденные в них инструкции на результаты их выполнения.
-
-<p><b>Формат инструкций</b></p>
-
-<blockquote>
-  <code><%<i>список_функций</i>{:|\|`}<i>данные</i>%></code>  
-</blockquote>
-<p>
-где<br />
-
-<code><i>список_функций</i></code> - список имен функций, разделенных
-    пробелами. Может быть пустым.<br />
-<code><i>данные</i></code> - данные, передаваемые функции.
-</p>
-
-<p><b>Формат данных</b></p>
-
-<blockquote>
-  <code>
-    [<i>список_аргументов</i>][<i>текст</i>]
-  </code>
-</blockquote>
-<p>
-где<br />
-
-<code><i>список_аргументов</i></code> - список аргументов функции, разделенных пробелами.
-    Может быть пустым.<br />
-<code><i>текст</i></code> - текст, передаваемый функции без разбиения на аргументы.
-    Может быть пустым. Количество аргументов, после которого следует
-    текст, зависит от функции.
-</p><p>
-Разделитель между списком функций и данными определяет, как должны
-обрабатываться данные функции:
-</p><p>
-<code>:</code> - рекурсивная обработка,<br />
-<code>\</code> или <code>`</code> - передать без обработки.
-</p><p>
-Если в списке функций задано две и более функции, они выполняются,
-начиная с последней, так что каждая функция получает в качестве данных
-результат выполнения следующей функции.
-</p><p>
-Каждая функция имеет код возврата - целое число. Код возврата
-инструкции - код возврата первой в списке функции. Код возврата,
-получаемый при обработке текста - сумма кодов возврата обработанных
-инструкций (как правило, смысл этого значения - количество инструкций,
-замененных на непустой текст).
-</p><p>
-Кроме скобок '<', '>', можно использовать скобки '[', ']'.
-</p>
-
-
-<h2>Функции</h2>
-
-<p><code><b>set</b></code></p>
-
-<table>
-<tr><td>Аргументы:</td>
-<td><i>имя</i></td></tr>
-
-<tr><td>Текст:</td>
-<td><i>значение</i></td></tr>
-
-<tr><td>Действие:</td>
-<td>Устанавливает значение переменной <i>имя</i>.</td></tr>
-
-<tr><td>Результат:</td>
-<td><i>имя</i></td></tr>
-
-<tr><td>Код возврата:</td>
-<td>1</td></tr>
-</table>
-
-<p><code><b>define</b></code></p>
-
-<table>
-<tr><td>Аргументы:</td>
-<td><i>имя</i></td></tr>
-
-<tr><td>Текст:</td>
-<td><i>шаблон</i></td></tr>
-
-<tr><td>Действие:</td><td>Определяет новую функцию <i>имя</i>, при
-вызове которой обрабатывается <i>шаблон</i>. При обработке доступны
-функции <code>nextarg</code> для получения очередного аргумента
-вызываемой функции и <code>data</code> для получения текста.</td></tr>
-
-<tr><td>Результат:</td>
-<td><i>имя</i></td></tr>
-
-<tr><td>Код возврата:</td>
-<td>1</td></tr>
-</table>
-
-<p><code><b>load</b></code></p>
-
-<table>
-<tr><td>Аргументы:</td>
-<td><i>имя</i> <i>имя_класса</i></td></tr>
-
-<tr><td>Действие:</td>
-
-<td>Определяет новую функцию <i>имя</i>. Реализация функции определена
-Java-классом <i>имя_класса</i>, наследующим класс
-<code>kryshen.tema.Function</code>.</td></tr>
-
-<tr><td>Результат:</td>
-<td><i>имя</i></td></tr>
-
-<tr><td>Код возврата:</td>
-<td>1</td></tr>
-</table>
-
-<p><code><b>prepare</b></code></p>
-
-<table>
-<tr><td>Аргументы:</td>
-<td><i>имя</i></td></tr>
-
-<tr><td>Текст:</td>
-<td><i>запрос</i></td></tr>
-
-<tr><td>Действие:</td>
-<td>Подготавливает SQL-запрос <i>запрос</i> для выполнения, записывает
-подготовленный запрос в переменную <i>имя</i>.</td></tr>
-
-<tr><td>Результат:</td>
-<td><i>имя</i></td></tr>
-
-<tr><td>Код возврата:</td>
-<td>1</td></tr>
-</table>
-
-<p><code><b>query</b></code></p>
-
-<table>
-<tr><td>Аргументы:</td>
-<td><i>имя_запроса</i> <i>шаблон</i> <i>парам1</i> ... <i>парамN</i></td></tr>
-
-<tr><td>Действие:</td>
-<td>Выполняет запрос с параметрами, подготовленный с помощью функции
-prepare. Значения параметров подставляются в запрос вместо символа
-'?'. Значения полей ответа доступны с помощью функции <code>db</code>,
-как переменные шаблона <i>шаблон</i>. При обработки шаблона также
-определяется переменная <code>number</code>, содержащая номер текущей
-строки ответа.</td></tr>
-
-<tr><td>Результат:</td>
-<td>результат обработки шаблона <i>шаблон</i> для каждой строки
-ответа.</td></tr>
-
-<tr><td>Код возврата:</td>
-<td>Количество полученных строк ответа.</td></tr>
-</table>
-
-<p><code><b>optional</b></code></p>
-
-<table>
-<tr><td>Текст:</td>
-<td><i>данные</i></td></tr>
-
-<tr><td>Результат:</td><td><i>данные</i>, если при обработке данных
-был получен код возврата отличный от 0, иначе - пустая
-строка.</td></tr>
-
-<tr><td>Код возврата:</td>
-<td>1, если результат - пустая строка, 0 - иначе.</td></tr>
-</table>
-
-<p><code><b>image</b></code></p>
-
-<table>
-<tr><td>Аргументы:</td>
-<td><i>исх_файл</i> <i>кон_файл</i> <i>формат</i> [<i>макс_ширина</i>
-[<i>макс_высота</i>]]</td></tr>
-
-<tr><td>Действие:</td>
-<td>Загружает изображение из файла <i>исх_файл</i> (путь определяется
-относительно конфигурационного параметра "resource_base") и
-преобразует его в указанный формат, сохраняя результат в
-<i>кон_файл</i>. Если заданы максимальная высота и ширина, большие
-изображения будут уменьшены.</td></tr>
-
-<tr><td>Результат:</td>
-<td><i>кон_файл</i> при успешном выполнении, пустая строка - иначе.</td></tr>
-
-<tr><td>Код возврата:</td>
-<td>1 при успешном выполнении, 0 - иначе.</td></tr>
-</table>
-
-
-<p><code><b>copy</b></code></p>
-
-<table>
-<tr><td>Аргументы:</td><td><i>исх_файл</i> <i>кон_файл</i></td></tr>
-
-<tr><td>Действие:</td><td>Копирует файл <i>исх_файл</i> в файл
-<i>кон_файл</i> (путь <i>исх_файл</i> определяется относительно
-конфигурационного параметра "resource_base").</td></tr>
-
-<tr><td>Результат:</td><td><i>кон_файл</i> при успешном выполнении,
-пустая строка - иначе.</td></tr>
-
-<tr><td>Код возврата:</td><td>1 при успешном выполнении, 0 -
-иначе.</td></tr>
-</table>
-
-<p><code><b>write</b></code></p>
-
-<table>
-<tr><td>Аргументы:</td><td><i>имя_файла</i></td></tr>
-
-<tr><td>Текст:</td><td><i>данные</i></td></tr>
-
-<tr><td>Действие:</td><td>Записывает <i>данные</i> в файл
-<i>исх_файл</i>.</td></tr>
-
-<tr><td>Результат:</td><td><i>кон_файл</i> при успешном выполнении,
-пустая строка - иначе.</td></tr>
-
-<tr><td>Код возврата:</td><td>1 при успешном выполнении, 0 -
-иначе.</td></tr>
-</table>
-
-<p><code><b>read</b></code></p>
-
-<table>
-<tr><td>Текст:</td><td><i>имя_файла</i></td></tr>
-<tr><td>Действие:</td><td>Читает файл <i>имя_файла</i>.</td></tr>
-<tr><td>Результат:</td><td>прочитанные данные при успешном выполнении,
-  пустая строка - иначе.</td></tr>
-<tr><td>Код возврата:</td><td>1 при успешном выполнении, 0 -
-иначе.</td></tr>
-</table>
-
-<p><code><b>include</b></code></p>
-
-<table>
-<tr><td>Текст:</td><td><i>имя_файла</i></td></tr>
-<tr><td>Действие:</td><td>включает шаблон из файла
-<i>имя_файла</i>.</td></tr>
-<tr><td>Результат:</td><td>результат обработки шаблона.</td></tr>
-<tr><td>Код возврата:</td><td>код возврата, полученный при обработке
-шаблона.</td></tr>
-</table>
-
-<p><code><b>!</b></code></p>
-
-<table>
-<tr><td>Текст:</td><td><i>данные</i></td></tr>
-
-<tr><td>Действие:</td><td>нет.</td></tr>
-
-<tr><td>Результат:</td><td>нет.</td></tr>
-
-<tr><td>Код возврата:</td><td>код возврата, полученный при обработке
-текста данных.</td></tr>
-</table>
-
-<p><code><b>replace</b></code></p>
-
-<table>
-<tr><td>Аргументы:</td><td><i>стр1</i> <i>стр2</i></td></tr>
-
-<tr><td>Текст:</td><td><i>данные</i></td></tr>
-
-<tr><td>Результат:</td><td>данные, с замененными вхождениями подстроки
-<i>стр1</i> на <i>стр2</i>.</td></tr>
-
-<tr><td>Код возврата:</td><td> код возврата, полученный при обработке
-текста данных.</td></tr>
-</table>
-
-<p><code><b>xml_escape</b></code></p>
-
-<table>
-<tr><td>Текст:</td><td><i>данные</i></td></tr>
-<tr><td>Результат:</td><td>текст <i>данные</i>, в котором символы
-'&', '<', '>', '`', '\' заменены на соответствующие сущности
-XML.</td></tr>
-
-<tr><td>Код возврата:</td><td> код возврата, полученный при обработке
-текста данных.</td></tr>
-</table>
-
-<p><code><b>xml_cdata</b></code></p>
-
-<table>
-<tr><td>Текст:</td><td><i>данные</i></td></tr>
-
-<tr><td>Результат:</td><td>данные в виде блока XML CDATA.</td></tr>
-
-<tr><td>Код возврата:</td><td> код возврата, полученный при обработке
-текста данных.  </td></tr>
-</table>
-
-<p>
-Макропроцессор TEMA расширяем: возможно добавление в систему новых
-функций, реализованных в виде классов на языке Java.
-</p>
-
-<h2>Запуск</h2>
-
-<p>java -jar tema.jar [<i>опции</i>]
-</p><p>
-Опции:
-</p>
-<table>
-<tr><td>-d[emo]</td><td>Демонстрационный режим</td></tr>
-<tr><td>-v[ersion]</td><td>Вывод версии</td></tr>
-<tr><td>-h[help] -u[sage]</td><td>Вывод справки</td></tr>
-</table>
-
-<p>
-При запуске читается файл "tema.properties" из текущего каталога.<br />
-Пример файла "tema.properties":
-</p>
-
-<pre>
-# Настройка источника данных
-# resource          : jdbc:odbc:database
-# driver            : sun.jdbc.odbc.JdbcOdbcDriver
-
-# Базовый каталог ресурсов
-# resource_base     : .
-
-# Шаблон, с которого начинается обработка
-main_template     : main.template
-
-# Кодировки файлов
-# input_encoding    : UTF-8
-# output_encoding   : UTF-8
-
-# Кэширование шаблонов
-# cache_read        : true
-
-# Вывод результата разбора шаблона main.template в stderr
-# output            : stderr
-
-# Вывод сообщений об ошибках в файл
-# log               : dbreader.log
-</pre>
-
-</body>
-</html>
diff --git a/doc/manual/manual.tema b/doc/manual/manual.tema
new file mode 100644
--- /dev/null
+++ b/doc/manual/manual.tema
@@ -0,0 +1,253 @@
+[%!\
+  Tema User Manual.
+%]\\
+
+<%silent include:document.tema%>\\
+
+<%document:
+  title    <%:Tema -- template macro processor%>
+  author   <%:Mikhail Kryshen%>
+  content
+
+<%section:Introduction%>
+
+<%par:Tema is a macro processor and template engine. It reads the input file 
+recursively expanding any macro found in the text.%>
+
+<%par:Distinctive features:%>
+<%itemize:
+  <%:Extensible: implement new functions as Java classes.%>
+  <%:Database access.%>
+  <%:Processes and outputs data as early as possible (function could start 
+     to output data before all it's arguments are read and parsed).%>
+  <%:Apache Ant integration.%>
+%>
+
+<%par:Tema macro has the following syntax:%>
+
+<%par:[%code\<%function_list{:|#|\|`}[escape]text%>[escape]%]%>
+
+<%par:<%code:function_list%> is the list of text-processing functions.
+Functions will be applied to text in reverse order.%>
+
+<%par:<%code:text%> could contain space-separated list of arguments 
+followed by arbitrary data. Number of arguments is determined by the last
+function in the <%code:function_list%>.%>
+
+<%par:<%code\[arg1 arg2...] [data]%>%>
+
+<%par:The text is separated from the list of functions by one of the 
+following characters:%>
+
+<%itemize:
+  <%\':' -- process text recursively.%>
+  <%\'#' -- parse text but do not replace any embedded macro.%>
+  <%\'\', '`' -- do not parse text (ignore any '<%' sequences).%>
+%>
+
+<%par:<%code:escape%> is either '\' or '\\':%>
+
+<%itemize:
+  <%:<%:\%> -- the following newline symbol will be removed;%>
+  <%:<%:\\%> -- the following sequence of the whitespace characters will be removed.%>
+%>
+
+<%par:You could use <%\'[%' and '%]'%> character sequences to denote macro
+as an alternative to [%\'<%' and '%>'%].%>
+
+<%par:Every function returns integer value. The general convention is to 
+return non-zero for successful operation. The value of the macro substitution 
+is the return value of the first function in the function list. The value of
+any Tema text is the sum value of all macro in the text, except the value of
+the text without any macro equals -1.%>
+
+<%par:Internally, Tema function could be represented by any Java object.
+Instances of kryshen.tema.Function and kryshen.tema.Context are handled
+specially. For any other type of object, function output would be the value 
+returned by the method <%code:toString()%>.%>
+
+<%section:Built-in functions%>
+
+<%par emph:Notice: some functions are not yet described in this manual.
+For the complete list of built-in functions refer to the API documentation
+and source code.%>
+
+<%function:tema
+  output  <%:Tema version%>
+%>
+
+<%function:echo  
+  output  <%:function data%>
+%>
+
+<%function:!
+  output      nothing
+  description <%:Ignores the input text (':' separator works as '#').
+    Use for commenting (e.g. <%code\[%!\ comment %]%>).%>
+%>
+
+<%function:silent  
+  output  nothing
+%>
+
+<%function:set
+  arguments   <%:definition name%>
+  input_data  <%:variable value%>
+  output      <%:function data%>
+  description <%:Defines new function as a static variable.%>
+%>
+
+<%function:define
+  arguments   <%:name, code%>
+  description <%:\\
+    Defines new function. 
+    Use <%code:next_arg%>, <%code:data%>, and <%code:has_more_data%> 
+    functions to access function arguments.%>
+  output      <%:name%> 
+%>
+
+[%!\ TODO: next_arg, data, has_more_data %]\\
+
+<%function:export
+  arguments   <%:definition name%>
+  input_data  <%:static variable value (optional)%>
+  output      <%:function data%>
+  description <%:Exports the definition to the global (outermost) context.%>
+%>
+
+<%function:unset
+  arguments   <%:def1, def2, ...%>
+  output      <%:nothing%>
+  description <%:Unsets the definitions.%>
+%>
+
+<%function:invoke
+  arguments   <%:function, arg1, arg2, ..., data%>
+  output      <%:function output%>
+  description <%:Invokes function.%>
+%>
+
+<%function:load
+  arguments   <%:name, class, url1, url2, ...%>
+  output      <%:name%>
+  description <%:Instantiate specified class as a Tema function definition.
+    URL arguments are optional.%>
+%>
+
+<%function:super
+  input_data  <%:code%>
+  output      <%:output generated by the code%>
+  description <%:Evaluates the specified template code in the super-context
+    (i.e. context of the calling function or context executing 
+    the database query).%>
+%>
+
+<%function:replace
+  arguments   <%:s1, s2, data%>
+  description <%:\\
+    Replaces all occurrences of <%code:s1%> with <%code:s2%> in data.%>
+%>
+
+[%!\ TODO: all functions defined in Strings.java %]\\
+
+<%subsection:Input / output%>
+
+<%function:copy
+  arguments   <%:src, dest%>
+  description <%:Copies file <%code:src%> to <%code:dest%>.%>
+  output      <%:dest%> 
+%>
+
+<%function:write
+  arguments   <%:name%>
+  input_data  <%:text%>
+  description <%:Writes text to file.%> 
+  output      <%:name%>
+%>
+
+<%function:read
+  arguments   <%:name%>
+  description <%:Reads file.%> 
+  output      <%:File contents.%>
+%>
+
+<%function:include
+  arguments   <%:name%>
+  description <%:Includes template.%> 
+  output      <%:Result of processing the template.%>
+%>
+
+<%function:file
+  arguments   <%:base, name%>
+  output      <%:Path constructed from the base directory and file name.%> 
+%>
+
+<%subsection:Conditionals%>
+
+<%function:optional
+  input_data  <%:text%>
+  output      <%:Function data if it's value is non-zero or empty text.%>
+%>
+
+<%function:while
+  input_data  <%:code%>
+  description <%:Repeatedly outputs it's evaluated data while it has non-zero value.%>
+%>
+
+[%!\ TODO: all functions defined in Control.java %]\\
+
+<%subsection:Database%>
+
+<%function:db_connect
+  arguments   <%:connection name%>
+  input_data  <%:resource%>
+  description <%:Establishes connection with the database.
+    Load the appropriate database driver using the <%code:load%> function
+    before using <%code:db_connect%> 
+    (e.g. [%code\<%load:driver sun.jdbc.odbc.JdbcOdbcDriver%>%]).%>
+%>
+
+<%function:db_prepare
+  arguments   <%:query name, connection name%>
+  input_data  <%:SQL statement%>
+%>
+
+<%function:db_query
+  arguments   <%:query, template, arg1, arg2, ...%>
+  description <%:Executes query, evaluating the template for each result row.%>
+%>
+
+<%function:db
+  input_data  <%:column name%>
+  output      <%:value from the query result.%>
+  description <%:Available in the template for the <%code:db_query%> function.%>  
+%>
+
+<%function:db_row
+  output      <%:current row number in the result set.%>
+  description <%:Available in the template for the <%code:db_query%> function.%>  
+%>
+
+[%!\ TODO: all functions defined in ImageConverter.java %]\\
+
+<%section:Running Tema%>
+
+<%par:Change to the dist subdirectory in the distribution package and
+issue the following command:%>
+
+<%par:<%code:java -jar tema.jar [options] [files]%>%>
+
+<%par:The following options are recognized:%>
+<%itemize:
+  <%:<%code:--demo%> --- run demo console with a code example;%>
+  <%:<%code:-h, --help%> --- print help message;%>
+  <%:<%code:--input-encoding <arg>%> --- set the input encoding;%>
+  <%:<%code:--log <arg>%> --- log all error messages to the specified file;%>
+  <%:<%code:-o, --output <arg>%> --- set the output file;%>
+  <%:<%code:--output-encoding <arg>%> --- set the output encoding;%>
+  <%:<%code:-v, --version%> --- print the version information and exit.%>
+%>
+
+<%section:Homepage%>
+
+<%par link:http://kryshen.pp.ru/tema/%>
diff --git a/misc/biotopes/biotope-top.sql b/misc/biotopes/biotope-top.sql
new file mode 100644
--- /dev/null
+++ b/misc/biotopes/biotope-top.sql
@@ -0,0 +1,20 @@
+SELECT t1.*,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1) AND
+Right(t2.rangcode, 8) = '00000000') AS class0,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 3) = Left(t1.rangcode, 3) AND
+Right(t2.rangcode, 6) = '000000') AS class1,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 5) = Left(t1.rangcode, 5) AND
+Right(t2.rangcode, 4) = '0000') AS class2,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 7) = Left(t1.rangcode, 7) AND
+Right(t2.rangcode, 2) = '00') AS class3
+
+FROM biotopelist AS t1
+WHERE Right(t1.rangcode, 2) <> '00' AND rusname
diff --git a/misc/biotopes/biotope.sql b/misc/biotopes/biotope.sql
new file mode 100644
--- /dev/null
+++ b/misc/biotopes/biotope.sql
@@ -0,0 +1,24 @@
+SELECT t1.*,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1)
+AND Right(t2.rangcode, 8)  = '00000000'
+AND Right(t1.rangcode, 8) <> '00000000') AS class0,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 3) = Left(t1.rangcode, 3)
+AND Right(t2.rangcode, 6)  = '000000'
+AND Right(t1.rangcode, 6) <> '000000') AS class1,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 5) = Left(t1.rangcode, 5)
+AND Right(t2.rangcode, 4)  = '0000'
+AND Right(t1.rangcode, 4) <> '0000') AS class2,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 7) = Left(t1.rangcode, 7)
+AND Right(t2.rangcode, 2)  = '00'
+AND Right(t1.rangcode, 2) <> '00') AS class3
+
+FROM biotopelist AS t1
+WHERE rusname
diff --git a/misc/biotopes/biotope.template b/misc/biotopes/biotope.template
new file mode 100644
--- /dev/null
+++ b/misc/biotopes/biotope.template
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="ISO-8859-5"?>
+<!DOCTYPE _ SYSTEM "brief.dtd">
+<!-- rangcode: <%xml_escape db\rangcode%> -->
+<_ show="0">
+  < display="0">
+    <%optional:<_ ="1" file="<%xml_escape image:maps/<%db\rangcode%>.gif maps/<%db\code%>.png png%>"/>%>
+<%query:photo_sql [%\<%include\photo.template%>%] <%get\code%>%>
+  </>
+  <><%xml_escape db\rusname%></>
+  <><%xml_escape db\class0%></>
+  <1><%xml_escape db\class1%></1>
+  <2><%xml_escape db\class2%></2>
+  <3><%xml_escape db\class3%></3>
+  <_ rows="3"><%xml_cdata db\descript%></_>
+  <_ rows="3"><%xml_cdata db\geobotdescr%></_>
+  <_><%xml_escape db\contributors%></_>
+</_>
diff --git a/misc/biotopes/brief.dtd b/misc/biotopes/brief.dtd
new file mode 100644
--- /dev/null
+++ b/misc/biotopes/brief.dtd
@@ -0,0 +1,27 @@
+<?xml encoding="ISO-8859-5"?>
+<!ELEMENT _ (?, , , 1, 2, 3, _, _, _)>
+<!ELEMENT  (|_)*>
+<!ELEMENT  EMPTY>
+<!ELEMENT _ EMPTY>
+<!ELEMENT  (#PCDATA)>
+<!ELEMENT  (#PCDATA)>
+<!ELEMENT 1 (#PCDATA)>
+<!ELEMENT 2 (#PCDATA)>
+<!ELEMENT 3 (#PCDATA)>
+<!ELEMENT _ (#PCDATA)>
+<!ELEMENT _ (#PCDATA)>
+<!ELEMENT _ (#PCDATA)>
+<!ATTLIST _ show (0|1) "1"> 
+<!ATTLIST 
+	display CDATA #FIXED "0">
+<!ATTLIST 
+	num ID #REQUIRED
+	file CDATA #REQUIRED
+	big CDATA #REQUIRED
+	text CDATA #IMPLIED
+	 CDATA #IMPLIED>
+<!ATTLIST _
+	num ID #REQUIRED
+	file CDATA #REQUIRED>
+<!ATTLIST _ rows CDATA #FIXED "3">
+<!ATTLIST _ rows CDATA #FIXED "3">
diff --git a/misc/biotopes/class.sql b/misc/biotopes/class.sql
new file mode 100644
--- /dev/null
+++ b/misc/biotopes/class.sql
@@ -0,0 +1,7 @@
+SELECT t1.*
+FROM biotopelist AS t1
+WHERE rusname AND Right(t1.rangcode, 8)  = '00000000'
+  AND EXISTS
+    (SELECT NULL FROM biotopelist AS t2
+     WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1)
+     AND Right(t2.rangcode, 8) <> '00000000')
diff --git a/misc/biotopes/classes.template b/misc/biotopes/classes.template
new file mode 100644
--- /dev/null
+++ b/misc/biotopes/classes.template
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="ISO-8859-5"?>
+<><%query:class_sql [%\
+  <!-- rangcode: <%xml_escape get\rangcode%> -->
+  < id="<%get\NUMBER%>"><%xml_escape db\rusname%></>%]%>
+</>
diff --git a/misc/biotopes/doc/article.txt b/misc/biotopes/doc/article.txt
new file mode 100644
--- /dev/null
+++ b/misc/biotopes/doc/article.txt
@@ -0,0 +1,391 @@
+*     XML-  .
+
+      " 
+"   Microsoft Access  XML- 
+       DbReader.
+
+ DbReader     
+      .  
+        
+    .   
+ SQL-,     
+.
+
+
+*  :
+
+    <%<_>{:|\}<>%>
+   
+
+<_> -   ,  .
+      .
+<> - ,  .
+
+
+*  :
+
+    [<_>][<>]
+  
+
+<_> -   ,  .
+      .
+<> - ,      .
+      .  ,   
+    ,   .
+
+      ,  
+  :
+: -  ,
+\ -   .
+
+        ,  ,
+  ,        
+   .
+
+     -  .  
+ -      .  ,
+    -    
+.
+
+  '<', '>'    '[', ']'.
+
+
+*  :
+
+:      set
+:    <>
+:        <>
+:        <>
+:    <>
+ : 1
+
+:      get
+:    <>
+:      <>. 
+ : 1,     , 0 - .
+
+:      prepare
+:    <>
+:        <>
+:      SQL- <>  ,
+                   <>.
+:    <>
+ : 1
+
+:      query
+:    <_> <> <1> ... <N>
+:        ,   
+               prepare.    
+                 '?'.    
+              ,    <>.  
+               <>    NUMBER,
+                  . ,
+                 ,   
+              <>   SUPER.<_>.
+
+:       <>   
+              .
+ :    .
+
+:      optional
+:        <>
+:    <>,      
+                  0,  -  .
+ : 1,   -  , 0 - .
+
+:      image
+:    <_> <_> <> [<_> [<_>]]
+:         <_> ( 
+                 "resource_base") 
+                  ,   
+              <_>.      , 
+                . 
+:    <_>   ,   - .
+ : 1   , 0 - .
+
+:      copy
+:    <_> <_>
+:       <_>   <_>
+	      ( <_>  
+                "resource_base").
+:    <_>   ,   - .
+ : 1   , 0 - .
+
+:      write
+:    <_>
+:        <>
+:      <>   <_>.
+:    <_>   ,   - .
+ : 1   , 0 - .
+
+:      read
+:        <_>
+:       <_>.
+:        , 
+               - .
+ : 1   , 0 - .
+
+:      include
+:        <_>
+:         <_>.
+:      .
+ :  ,    .
+
+:      !
+:        <>
+:     .
+:    .
+ :  ,     .
+
+:      replace
+:    <1> <2>
+:        <>
+:    ,     <1>
+	       <2>.
+ :  ,     .
+
+:      xml_escape
+:        <>
+:     <>,    
+              '&', '<', '>', '`', '\'   
+	       XML.
+ :  ,     .
+
+:      xml_cdata
+:        <>
+:        XML CDATA.
+ :  ,     .
+
+ DbReader :     
+,       Java.
+
+
+*   DbReader    
+  "  "   Microsoft Access 
+  XML-    
+
+       "
+ " (   ):
+
+biotopelist (code, rangcode, rusname, engname, descript, geobotdescr,
+             contributors, descrdate)
+
+  code - ,  ;
+  rangcode -    ABBCCDDEE,  , BB, CC, DD,  - 
+             ,   ;
+  rusname, engname, descript, geobotdescr -  ;
+  descrdate - .
+
+photos (biotope, photo, authphoto)
+
+  biotope - ,   code  biotopelist;
+  photo -   ;
+  authphoto - .
+
+  xml-     biotopelist, 
+  ,    ,  
+   .
+
+  xml-     
+ DbReader ( biotope.template):
+
+----------------------------------------------------------------------
+<?xml version="1.0" encoding="ISO-8859-5"?>
+<!DOCTYPE _ SYSTEM "brief.dtd">
+<!-- rangcode: <%xml_escape get\rangcode%> -->
+<_ show="0">
+  < display="0">
+    <%optional:<_ ="1"
+    file="<%xml_escape image:maps/<%get\rangcode%>.gif maps/<%get\code%>.png png%>"/>%>
+<%query:photo_sql [%\<%include\photo.template%>%] <%get\code%>%>
+  </>
+  <><%xml_escape get\rusname%></>
+  <><%xml_escape get\class0%></>
+  <1><%xml_escape get\class1%></1>
+  <2><%xml_escape get\class2%></2>
+  <3><%xml_escape get\class3%></3>
+  <_ rows="3"><%xml_cdata get\descript%></_>
+  <_ rows="3"><%xml_cdata get\geobotdescr%></_>
+  <_><%xml_escape get\contributors%></_>
+</_>
+----------------------------------------------------------------------
+
+ SQL-,    
+   rangcode, rusname, class0, class1, class2,
+class3, descript, geobotdescr  contributors ( biotope.sql):
+
+----------------------------------------------------------------------
+SELECT t1.*,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1)
+AND Right(t2.rangcode, 8)  = '00000000'
+AND Right(t1.rangcode, 8) <> '00000000') AS class0,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 3) = Left(t1.rangcode, 3)
+AND Right(t2.rangcode, 6)  = '000000'
+AND Right(t1.rangcode, 6) <> '000000') AS class1,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 5) = Left(t1.rangcode, 5)
+AND Right(t2.rangcode, 4)  = '0000'
+AND Right(t1.rangcode, 4) <> '0000') AS class2,
+
+(SELECT t2.rusname FROM biotopelist AS t2
+WHERE Left(t2.rangcode, 7) = Left(t1.rangcode, 7)
+AND Right(t2.rangcode, 2)  = '00'
+AND Right(t1.rangcode, 2) <> '00') AS class3
+
+FROM biotopelist AS t1
+WHERE rusname
+----------------------------------------------------------------------
+
+ biotope.template   <%include\photo.template%>,
+    ( photo.template):
+
+----------------------------------------------------------------------
+    < num="<%xml_escape get\NUMBER%>"
+       <%optional:="<%xml_escape get\authphoto%>" %>text="" 
+       file="<%xml_escape image:biotopephotos/<%get\photo%> images/<%get\SUPER.code%>_<%get\NUMBER%>.jpg jpg 300 300%>" 
+        big="<%xml_escape copy:biotopephotos/<%get\photo%> images/big/<%get\SUPER.code%>_<%get\NUMBER%>.jpg%>"/>
+----------------------------------------------------------------------
+
+ SQL-    
+(photo.sql):
+
+----------------------------------------------------------------------
+SELECT * from photos WHERE biotope = ?
+----------------------------------------------------------------------
+
+,    ( classes.template):
+
+----------------------------------------------------------------------
+<?xml version="1.0" encoding="ISO-8859-5"?>
+<><%query:class_sql [%\
+  <!-- rangcode: <%xml_escape get\rangcode%> -->
+  < id="<%get\NUMBER%>"><%xml_escape get\rusname%></>%]%>
+</>
+----------------------------------------------------------------------
+
+ SQL- (class.sql):
+
+----------------------------------------------------------------------
+SELECT t1.*
+FROM biotopelist AS t1
+WHERE rusname AND Right(t1.rangcode, 8)  = '00000000'
+  AND EXISTS
+    (SELECT NULL FROM biotopelist AS t2
+     WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1)
+     AND Right(t2.rangcode, 8) <> '00000000')
+----------------------------------------------------------------------
+
+,    ( plants.template):
+
+----------------------------------------------------------------------
+<?xml version="1.0" encoding="ISO-8859-5"?>
+<><%query:plant_sql [%\
+  <!-- rangcode: <%xml_escape get\rangcode%> -->
+  <><%xml_escape get\rusname%></>%]%>
+</>
+----------------------------------------------------------------------
+
+ SQL- (plant.sql):
+
+----------------------------------------------------------------------
+SELECT t1.*
+FROM biotopelist AS t1
+WHERE rusname AND Right(t1.rangcode, 2) <> '00'
+----------------------------------------------------------------------
+
+ DbReader      index.template,
+     SQL-  
+:
+
+----------------------------------------------------------------------
+<%!:
+  <%prepare:biotope_sql <%read\biotope.sql%>%>
+  <%prepare:photo_sql <%read\photo.sql%>%>
+  <%prepare:class_sql <%read\class.sql%>%>
+  <%prepare:plant_sql <%read\plant.sql%>%>
+
+  <%query:biotope_sql [%\<%write:<%get\code%>.xml <%include\biotope.template%>%> %]%>
+
+  <%write:classes.xml <%include\classes.template%>%>
+  <%write:plants.xml <%include\plants.template%>%>
+%>
+----------------------------------------------------------------------
+
+  XML-:
+
+----------------------------------------------------------------------
+<?xml version="1.0" encoding="ISO-8859-5"?>
+<!DOCTYPE _ SYSTEM "brief.dtd">
+<!-- rangcode: E04020302 -->
+<_ show="0">
+  < display="0">    
+    < num="1" =" .." text="" 
+                file="images/3_1.jpg" 
+                big="images/big/3_1.jpg"/>
+    < num="2" =" .." text="" 
+                file="images/3_2.jpg" 
+                big="images/big/3_2.jpg"/>
+  </>
+  <>Betula sp. - Avenella flexuosa - Polytrichum commune</>
+  <>E.  </>
+  <1> </1>
+  <2></2>
+  <3></3>
+  <_ rows="3"><![CDATA[       .   5 - 7            ,   ,   ,   .]]></_>
+  <_ rows="3"></_>
+  <_></_>
+</_>
+----------------------------------------------------------------------
+
+
+*   DbReader
+
+ DbReader     Java  
+   :
+
+ru.karrc.dbreader.DbReader: c   . 
+,       
+ JDBC,    .
+
+ru.karrc.dbreader.TemplateParser:   ,
+    .
+
+ru.karrc.dbreader.Function:  .   
+DbReader   .
+
+ru.karrc.dbreader.FunctionDataParser:  
+.      , 
+.       
+ TemplateParser.
+
+ru.karrc.dbreader.Functions:     
+ ,  Function.
+
+ru.karrc.dbreader.TemplateParser: ,   
+    .
+
+ ,    , 
+    .
+
+
+*    .
+
+   : 8,
+ Java- ( ): 27,
+    : 1311,
+   (    ): 812,
+  : 180.
+
+
+* 
+
+     DbReader, 
+      
+      .   
+DbReader   "  " 
+  XML-    .
diff --git a/misc/biotopes/doc/readme.html b/misc/biotopes/doc/readme.html
new file mode 100644
--- /dev/null
+++ b/misc/biotopes/doc/readme.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
+          "DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<head>
+  <title>DbReader</title>
+  <meta http-equiv="Content-Type" content="text/html; charset=koi8-r" />
+</head>
+
+<body>
+<h1>DbReader</h1>
+
+<h2> </h2>
+<ol>
+<li> .</li>
+
+<li>  :<br />
+ Windows 2000:  //  (ODBC),  biotopes-data.mdb.</li>
+
+<li> :<br />
+  dbreader.properties  ,   :<br />
+    <code>resource: jdbc:odbc:<em></em></code><br />
+   ,       ( biotopephotos  maps):<br />
+    <code>resource_base: C:\\biotopes</code><br />
+( '\'  )</li>
+
+<li> :<br />
+<code>dbreader.bat</code> <br />
+<code>java -jar dbreader.jar</code>.<br />
+   Java Runtime Environment (JRE) 5.0,    <a href="http://java.sun.com/j2se/1.5.0/download.jsp">http://java.sun.com/j2se/1.5.0/download.jsp</a>. ,       JRE -  .
+
+        :<br />
+ <code>java -Dru.karrc.dbreader.<em></em>=<em></em> -jar dbreader.jar</code><br />
+(: <code>java -Dru.karrc.dbreader.log=dbreader.log -jar dbreader.jar</code>)</li>
+</ol>
+
+<h2> </h2>
+
+<p>   biotope.sql ( ,   dbreader.properties),     biotope.template,         .</p>
+<p> : <code><%<em>_</em>[:|\]<em></em>%></code>, <em> </em> -    ,    .    ,     .</p>
+
+<h3>     </h3>
+
+<ul>
+  <li><code>:</code> -    .</li>
+  <li><code>\</code> -      .       <code><%</code>  <code>%></code> (<code><%\<%%></code>  <code><%\%%>></code>).</li>
+</ul>
+
+<h3></h3>
+
+<ul>
+  <li><code><%get:<em></em>%></code><br />
+         .</li>
+
+  <li><code><%optional:<em></em>%></code><br />
+    <em></em>,     ,    ,     .</li>
+
+  <li><code><%escape:<em></em>%></code><br />
+     ""  (     ).</li>
+
+  <li><code><%invoke:<em></em>; <em>1</em> <em>2</em> ... <em>N</em>%></code><br />
+      <em></em>.sql       <em></em>.template.        '?'.</li>
+
+  <li><code><%image:<em>_</em> <em>_</em> <em></em> [<em>_</em>] [<em>_</em>]%></code><br />
+      <em>_</em> (     "resource_base")      ,    <em>_</em>.      ,    .
+  ,  <code><%image:...%></code>     <em>_</em>,  -   .</li>
+
+  <li><code><%copy:<em>_</em> <em>_</em>%></code><br />
+    <em>_</em> (     "resource_base")  <em>_</em>.   ,  <code><%copy:...%></code>     <em>_</em>,  -   .</li>
+</ul>
+
+<h3> </h3>
+
+<p><code>NUMBER</code> -      .
+    ,          invoke,      <code>SUPER.</code> (: <code>SUPER.NUMBER</code>).</p>
+
+<h3></h3>
+
+<p>      name      :</p>
+<p><code><%escape get:name%></code></p>
+
+<p align="right"><i><a href="mailto:kryshen@cs.karelia.ru"> </a></i></p>
+
+</body>
+</html>
diff --git a/misc/biotopes/doc/readme.txt b/misc/biotopes/doc/readme.txt
new file mode 100644
--- /dev/null
+++ b/misc/biotopes/doc/readme.txt
@@ -0,0 +1,63 @@
+text/plain; charset=UTF-8
+
+Установка программы
+-------------------
+
+1. Распаковать архив.
+
+2. Установить источник данных:
+в Windows 2000: Панель управления/Администрирование/Источники данных (ODBC), добавить biotopes.mdb.
+
+3. Настроить программу:
+В файле dbreader.properties указать имя, присвоенное источнику данных:
+    "resource: jdbc:odbc:<имя>"
+и путь к каталогу, относительно которого программа будет искать изображения (содержащий biotopephotos и maps):
+    "resource_base: C:\\biotopes"
+(символ '\' необходимо дублировать)
+
+4. Запуск программы: dbreader.bat или "java -jar dbreader.jar"
+Для запуска необходима Java Runtime Environment (JRE) 5.0, можно скачать с http://java.sun.com/j2se/1.5.0/download.jsp. Возможно, будет работать и со старыми версиями JRE - не проверял.
+
+Все значения файла конфигурации можно переопределять при запуске программы:
+    команда "java -Dru.karrc.dbreader.<ключ>=<значение> -jar dbreader.jar"
+(например: "java -Dru.karrc.dbreader.log=dbreader.log -jar dbreader.jar")
+
+
+Работа программы
+----------------
+
+Программа выполняет запрос biotope.sql (или другой, указанный в dbreader.properties), после чего обрабатывает файл biotope.template, заменяя найденные в нем инструкции на результаты их выполнения.
+
+Формат инструкций: "<%список_функций[:|\]данные%>", список функций - имена функций разделенные пробелом, список может быть пустым. Если указано несколько функций, они выполняются начиная с последней.
+
+Разделители между списком функций и данными:
+':' - данные будут обрабатываться рекурсивно.
+'\' - данные будут переданы функции без обработки. Можно использовать для экранирования комбинаций символов "<%" и "%>" ("<%\<%%>" и "<%\%%>>").
+
+Функции:
+<%get:<имя>%> - заменяется на значение встроенной переменной или колонки запроса.
+
+<%optional:<текст>%>
+Заменяется на <текст>, если в нем найдены инструкции, заменившиеся на непустые строки, иначе заменяется на пустую строку.
+
+<%escape:<текст>%>
+Заменяет в тексте "опасные" символы (таблица замен задается в файле конфигурации).
+
+<%invoke:<имя> <парам1> <парам2> ... <парамN>%>
+Выполняет запрос с параметрами <имя>.sql и заменяется на результаты обработки шаблона <имя>.template. Значения параметров подставляются в запрос вместо символа '?'.
+
+<%image:<исх_файл> <кон_файл> <формат> [<макс_ширина>] [<макс_высота>]%>
+Загружает изображение из файла <исх_файл> (путь определяется относительно конфигурационного параметра "resource_base") и преобразует его в указанный формат, сохраняя результат в <кон_файл>. Если заданы максимальная высота и ширина, большие изображения будут уменьшены.
+При успешном выполнении, инструкция "<%!image ...%>" будет заменена на значение <кон_файл>, иначе - на пустую строку.
+
+<%copy:<исх_файл> <кон_файл>%>
+Копирует файл <исх_файл> (путь определяется относительно конфигурационного параметра "resource_base").
+При успешном выполнении, инструкция "<%!copy ...%>" будет заменена на значение <кон_файл>, иначе - на пустую строку.
+
+Встроенные переменные:
+NUMBER - порядковый номер строки результата выполнения запроса.
+Чтобы обратиться к переменной запроса, из которого обрабатываемый шаблон был вызван с помощью функции invoke, перед именем переменной нужно добавить "SUPER." (например: "SUPER.NUMBER").
+
+Пример:
+Следующая инструкция получит значение из столбца name и заменит в нем специальные символы:
+<%escape get:name%>
diff --git a/misc/biotopes/main.template b/misc/biotopes/main.template
new file mode 100644
--- /dev/null
+++ b/misc/biotopes/main.template
@@ -0,0 +1,11 @@
+<%!:
+  <%prepare:biotope_sql <%read\biotope.sql%>%>
+  <%prepare:photo_sql <%read\photo.sql%>%>
+  <%prepare:class_sql <%read\class.sql%>%>
+  <%prepare:plant_sql <%read\plant.sql%>%>
+
+  <%query:biotope_sql [%\<%write:<%db\code%>.xml <%include\biotope.template%>%> %]%>
+
+  <%write:classes.xml <%include\classes.template%>%>
+  <%write:plants.xml <%include\plants.template%>%>
+%>
diff --git a/misc/biotopes/photo.sql b/misc/biotopes/photo.sql
new file mode 100644
--- /dev/null
+++ b/misc/biotopes/photo.sql
@@ -0,0 +1,1 @@
+SELECT * from photos WHERE biotope = ?
diff --git a/misc/biotopes/photo.template b/misc/biotopes/photo.template
new file mode 100644
--- /dev/null
+++ b/misc/biotopes/photo.template
@@ -0,0 +1,3 @@
+    < num="<%number\%>" <%optional:="<%xml_escape db\authphoto%>" %>text="" 
+                file="<%xml_escape image:biotopephotos/<%db\photo%> images/<%super.db\code%>_<%number\%>.jpg jpg 300 300%>" 
+                big="<%xml_escape copy:biotopephotos/<%db\photo%> images/big/<%super.db\code%>_<%number\%>.jpg%>"/>
diff --git a/misc/biotopes/plant.sql b/misc/biotopes/plant.sql
new file mode 100644
--- /dev/null
+++ b/misc/biotopes/plant.sql
@@ -0,0 +1,3 @@
+SELECT t1.*
+FROM biotopelist AS t1
+WHERE rusname AND Right(t1.rangcode, 2) <> '00'
diff --git a/misc/biotopes/plants.template b/misc/biotopes/plants.template
new file mode 100644
--- /dev/null
+++ b/misc/biotopes/plants.template
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="ISO-8859-5"?>
+<><%query:plant_sql [%\
+  <!-- rangcode: <%xml_escape db\rangcode%> -->
+  <><%xml_escape db\rusname%></>%]%>
+</>
diff --git a/misc/biotopes/tema.properties b/misc/biotopes/tema.properties
new file mode 100644
--- /dev/null
+++ b/misc/biotopes/tema.properties
@@ -0,0 +1,22 @@
+# Data source configuration
+resource          : jdbc:odbc:biotopes-data
+driver            : sun.jdbc.odbc.JdbcOdbcDriver
+
+# Base directory for images and files
+resource_base     : C:\\biotopes\\db
+
+# Template to start processing with
+main_template     : main.template
+
+# File encodings
+input_encoding    : ISO-8859-5
+output_encoding   : ISO-8859-5
+
+# Cache templates
+cache_read        : true
+
+# Output main_template parsing result to stderr
+#output            : stderr
+
+# File to output error messages (redirect stderr)
+#log               : tema.log
diff --git a/misc/manual-ru-old/index.html b/misc/manual-ru-old/index.html
new file mode 100644
--- /dev/null
+++ b/misc/manual-ru-old/index.html
@@ -0,0 +1,370 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
+          "DTD/xhtml1-transitional.dtd">
+
+<html>
+
+<head>
+  <title>Макропроцессор TEMA</title>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+</head>
+
+<body>
+<h1>Макропроцессор TEMA</h1>
+
+<p>Макропроцессор TEMA обрабатывает заданные шаблоны текстовых файлов и
+заменяет найденные в них инструкции на результаты их выполнения.
+</p><p>
+Особенности:
+</p>
+<ul>
+  <li>Расширяемость: возможно динамически подключать новые функии,
+  реализованные в виде классов на Java.</li>
+  <li>Работа с базами данных: использование SQL-запросов для получения
+  данных.</li>
+  <li>Поточная обработка данных: инструкции обрабатываются по мере
+  чтения текста без необходимости загрузки файла в оперативную память
+  и выполнения полного синтаксического разбора.</li>
+</ul>
+
+<p><b>Формат инструкций</b></p>
+
+<blockquote>
+  <code><%<i>список_функций</i>{:|\|`}<i>данные</i>%></code>  
+</blockquote>
+<p>
+где<br />
+
+<code><i>список_функций</i></code> - список имен функций, разделенных
+    пробелами. Может быть пустым.<br />
+<code><i>данные</i></code> - данные, передаваемые функции.
+</p>
+
+<p><b>Формат данных</b></p>
+
+<blockquote>
+  <code>
+    [<i>список_аргументов</i>][<i>текст</i>]
+  </code>
+</blockquote>
+<p>
+где<br />
+
+<code><i>список_аргументов</i></code> - список аргументов функции, разделенных пробелами.
+    Может быть пустым.<br />
+<code><i>текст</i></code> - текст, передаваемый функции без разбиения на аргументы.
+    Может быть пустым. Количество аргументов, после которого следует
+    текст, зависит от функции.
+</p><p>
+Разделитель между списком функций и данными определяет, как должны
+обрабатываться данные функции:
+</p><p>
+<code>:</code> - рекурсивная обработка,<br />
+<code>\</code> или <code>`</code> - передать без обработки.
+</p><p>
+Если в списке функций задано две и более функции, они выполняются,
+начиная с последней, так что каждая функция получает в качестве данных
+результат выполнения следующей функции.
+</p><p>
+Каждая функция имеет код возврата - целое число. Код возврата
+инструкции - код возврата первой в списке функции. Код возврата,
+получаемый при обработке текста - сумма кодов возврата обработанных
+инструкций (как правило, смысл этого значения - количество инструкций,
+замененных на непустой текст).
+</p><p>
+Кроме скобок '<', '>', можно использовать скобки '[', ']'.
+</p>
+
+
+<h2>Функции</h2>
+
+<p><code><b>set</b></code></p>
+
+<table>
+<tr><td>Аргументы:</td>
+<td><i>имя</i></td></tr>
+
+<tr><td>Текст:</td>
+<td><i>значение</i></td></tr>
+
+<tr><td>Действие:</td>
+<td>Устанавливает значение переменной <i>имя</i>.</td></tr>
+
+<tr><td>Результат:</td>
+<td><i>имя</i></td></tr>
+
+<tr><td>Код возврата:</td>
+<td>1</td></tr>
+</table>
+
+<p><code><b>define</b></code></p>
+
+<table>
+<tr><td>Аргументы:</td>
+<td><i>имя</i></td></tr>
+
+<tr><td>Текст:</td>
+<td><i>шаблон</i></td></tr>
+
+<tr><td>Действие:</td><td>Определяет новую функцию <i>имя</i>, при
+вызове которой обрабатывается <i>шаблон</i>. При обработке доступны
+функции <code>nextarg</code> для получения очередного аргумента
+вызываемой функции и <code>data</code> для получения текста.</td></tr>
+
+<tr><td>Результат:</td>
+<td><i>имя</i></td></tr>
+
+<tr><td>Код возврата:</td>
+<td>1</td></tr>
+</table>
+
+<p><code><b>load</b></code></p>
+
+<table>
+<tr><td>Аргументы:</td>
+<td><i>имя</i> <i>имя_класса</i></td></tr>
+
+<tr><td>Действие:</td>
+
+<td>Определяет новую функцию <i>имя</i>. Реализация функции определена
+Java-классом <i>имя_класса</i>, наследующим класс
+<code>kryshen.tema.Function</code>.</td></tr>
+
+<tr><td>Результат:</td>
+<td><i>имя</i></td></tr>
+
+<tr><td>Код возврата:</td>
+<td>1</td></tr>
+</table>
+
+<p><code><b>prepare</b></code></p>
+
+<table>
+<tr><td>Аргументы:</td>
+<td><i>имя</i></td></tr>
+
+<tr><td>Текст:</td>
+<td><i>запрос</i></td></tr>
+
+<tr><td>Действие:</td>
+<td>Подготавливает SQL-запрос <i>запрос</i> для выполнения, записывает
+подготовленный запрос в переменную <i>имя</i>.</td></tr>
+
+<tr><td>Результат:</td>
+<td><i>имя</i></td></tr>
+
+<tr><td>Код возврата:</td>
+<td>1</td></tr>
+</table>
+
+<p><code><b>query</b></code></p>
+
+<table>
+<tr><td>Аргументы:</td>
+<td><i>имя_запроса</i> <i>шаблон</i> <i>парам1</i> ... <i>парамN</i></td></tr>
+
+<tr><td>Действие:</td>
+<td>Выполняет запрос с параметрами, подготовленный с помощью функции
+prepare. Значения параметров подставляются в запрос вместо символа
+'?'. Значения полей ответа доступны с помощью функции <code>db</code>,
+как переменные шаблона <i>шаблон</i>. При обработки шаблона также
+определяется переменная <code>number</code>, содержащая номер текущей
+строки ответа.</td></tr>
+
+<tr><td>Результат:</td>
+<td>результат обработки шаблона <i>шаблон</i> для каждой строки
+ответа.</td></tr>
+
+<tr><td>Код возврата:</td>
+<td>Количество полученных строк ответа.</td></tr>
+</table>
+
+<p><code><b>optional</b></code></p>
+
+<table>
+<tr><td>Текст:</td>
+<td><i>данные</i></td></tr>
+
+<tr><td>Результат:</td><td><i>данные</i>, если при обработке данных
+был получен код возврата отличный от 0, иначе - пустая
+строка.</td></tr>
+
+<tr><td>Код возврата:</td>
+<td>1, если результат - пустая строка, 0 - иначе.</td></tr>
+</table>
+
+<p><code><b>image</b></code></p>
+
+<table>
+<tr><td>Аргументы:</td>
+<td><i>исх_файл</i> <i>кон_файл</i> <i>формат</i> [<i>макс_ширина</i>
+[<i>макс_высота</i>]]</td></tr>
+
+<tr><td>Действие:</td>
+<td>Загружает изображение из файла <i>исх_файл</i> (путь определяется
+относительно конфигурационного параметра "resource_base") и
+преобразует его в указанный формат, сохраняя результат в
+<i>кон_файл</i>. Если заданы максимальная высота и ширина, большие
+изображения будут уменьшены.</td></tr>
+
+<tr><td>Результат:</td>
+<td><i>кон_файл</i> при успешном выполнении, пустая строка - иначе.</td></tr>
+
+<tr><td>Код возврата:</td>
+<td>1 при успешном выполнении, 0 - иначе.</td></tr>
+</table>
+
+
+<p><code><b>copy</b></code></p>
+
+<table>
+<tr><td>Аргументы:</td><td><i>исх_файл</i> <i>кон_файл</i></td></tr>
+
+<tr><td>Действие:</td><td>Копирует файл <i>исх_файл</i> в файл
+<i>кон_файл</i> (путь <i>исх_файл</i> определяется относительно
+конфигурационного параметра "resource_base").</td></tr>
+
+<tr><td>Результат:</td><td><i>кон_файл</i> при успешном выполнении,
+пустая строка - иначе.</td></tr>
+
+<tr><td>Код возврата:</td><td>1 при успешном выполнении, 0 -
+иначе.</td></tr>
+</table>
+
+<p><code><b>write</b></code></p>
+
+<table>
+<tr><td>Аргументы:</td><td><i>имя_файла</i></td></tr>
+
+<tr><td>Текст:</td><td><i>данные</i></td></tr>
+
+<tr><td>Действие:</td><td>Записывает <i>данные</i> в файл
+<i>исх_файл</i>.</td></tr>
+
+<tr><td>Результат:</td><td><i>кон_файл</i> при успешном выполнении,
+пустая строка - иначе.</td></tr>
+
+<tr><td>Код возврата:</td><td>1 при успешном выполнении, 0 -
+иначе.</td></tr>
+</table>
+
+<p><code><b>read</b></code></p>
+
+<table>
+<tr><td>Текст:</td><td><i>имя_файла</i></td></tr>
+<tr><td>Действие:</td><td>Читает файл <i>имя_файла</i>.</td></tr>
+<tr><td>Результат:</td><td>прочитанные данные при успешном выполнении,
+  пустая строка - иначе.</td></tr>
+<tr><td>Код возврата:</td><td>1 при успешном выполнении, 0 -
+иначе.</td></tr>
+</table>
+
+<p><code><b>include</b></code></p>
+
+<table>
+<tr><td>Текст:</td><td><i>имя_файла</i></td></tr>
+<tr><td>Действие:</td><td>включает шаблон из файла
+<i>имя_файла</i>.</td></tr>
+<tr><td>Результат:</td><td>результат обработки шаблона.</td></tr>
+<tr><td>Код возврата:</td><td>код возврата, полученный при обработке
+шаблона.</td></tr>
+</table>
+
+<p><code><b>!</b></code></p>
+
+<table>
+<tr><td>Текст:</td><td><i>данные</i></td></tr>
+
+<tr><td>Действие:</td><td>нет.</td></tr>
+
+<tr><td>Результат:</td><td>нет.</td></tr>
+
+<tr><td>Код возврата:</td><td>код возврата, полученный при обработке
+текста данных.</td></tr>
+</table>
+
+<p><code><b>replace</b></code></p>
+
+<table>
+<tr><td>Аргументы:</td><td><i>стр1</i> <i>стр2</i></td></tr>
+
+<tr><td>Текст:</td><td><i>данные</i></td></tr>
+
+<tr><td>Результат:</td><td>данные, с замененными вхождениями подстроки
+<i>стр1</i> на <i>стр2</i>.</td></tr>
+
+<tr><td>Код возврата:</td><td> код возврата, полученный при обработке
+текста данных.</td></tr>
+</table>
+
+<p><code><b>xml_escape</b></code></p>
+
+<table>
+<tr><td>Текст:</td><td><i>данные</i></td></tr>
+<tr><td>Результат:</td><td>текст <i>данные</i>, в котором символы
+'&', '<', '>', '`', '\' заменены на соответствующие сущности
+XML.</td></tr>
+
+<tr><td>Код возврата:</td><td> код возврата, полученный при обработке
+текста данных.</td></tr>
+</table>
+
+<p><code><b>xml_cdata</b></code></p>
+
+<table>
+<tr><td>Текст:</td><td><i>данные</i></td></tr>
+
+<tr><td>Результат:</td><td>данные в виде блока XML CDATA.</td></tr>
+
+<tr><td>Код возврата:</td><td> код возврата, полученный при обработке
+текста данных.  </td></tr>
+</table>
+
+<p>
+Макропроцессор TEMA расширяем: возможно добавление в систему новых
+функций, реализованных в виде классов на языке Java.
+</p>
+
+<h2>Запуск</h2>
+
+<p>java -jar tema.jar [<i>опции</i>]
+</p><p>
+Опции:
+</p>
+<table>
+<tr><td>-d[emo]</td><td>Демонстрационный режим</td></tr>
+<tr><td>-v[ersion]</td><td>Вывод версии</td></tr>
+<tr><td>-h[help] -u[sage]</td><td>Вывод справки</td></tr>
+</table>
+
+<p>
+При запуске читается файл "tema.properties" из текущего каталога.<br />
+Пример файла "tema.properties":
+</p>
+
+<pre>
+# Настройка источника данных
+# resource          : jdbc:odbc:database
+# driver            : sun.jdbc.odbc.JdbcOdbcDriver
+
+# Базовый каталог ресурсов
+# resource_base     : .
+
+# Шаблон, с которого начинается обработка
+main_template     : main.template
+
+# Кодировки файлов
+# input_encoding    : UTF-8
+# output_encoding   : UTF-8
+
+# Кэширование шаблонов
+# cache_read        : true
+
+# Вывод результата разбора шаблона main.template в stderr
+# output            : stderr
+
+# Вывод сообщений об ошибках в файл
+# log               : dbreader.log
+</pre>
+
+</body>
+</html>
diff --git a/misc/util/header b/misc/util/header
new file mode 100644
--- /dev/null
+++ b/misc/util/header
@@ -0,0 +1,21 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: $
+ */
diff --git a/misc/util/header.gpl b/misc/util/header.gpl
new file mode 100644
--- /dev/null
+++ b/misc/util/header.gpl
@@ -0,0 +1,20 @@
+/*
+ *  Copyright (C) 2006, 2007, 2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Tema.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: header.gpl,v 1.1 2008/02/16 16:56:34 mikhail Exp $
+ */
diff --git a/misc/util/update_headers.pl b/misc/util/update_headers.pl
new file mode 100644
--- /dev/null
+++ b/misc/util/update_headers.pl
@@ -0,0 +1,36 @@
+#!/usr/bin/perl
+
+use File::Find;
+
+undef $/;
+
+my $src = "../../src";
+my $header_file = "header";
+
+open (FILE, "$header_file");
+my $header = <FILE>;
+close(FILE);
+
+find (\&update_header, $src);
+
+sub update_header
+{
+   my $code;
+   
+   if ( $File::Find::name !~ /^.*\.java$/ )
+   {
+     return;
+   }
+
+   print $File::Find::name."\n";   
+   open(FILE, "$_") or die "error opening file";
+  
+   $code = <FILE>;
+   $code =~ s|(^[[:space:]]*/\*.*?\*/[[:space:]]*?\n\|^)|$header|s;
+   
+   close(FILE);
+
+   open(FILE, ">$_") or die "error opening file";
+   print FILE $code;
+   close(FILE);
+}
diff --git a/nbproject/.cvsignore b/nbproject/.cvsignore
new file mode 100644
--- /dev/null
+++ b/nbproject/.cvsignore
@@ -0,0 +1,1 @@
+private
diff --git a/nbproject/project.xml b/nbproject/project.xml
new file mode 100644
--- /dev/null
+++ b/nbproject/project.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.ant.freeform</type>
+    <configuration>
+        <general-data xmlns="http://www.netbeans.org/ns/freeform-project/1">
+            <!-- Do not use Project Properties customizer when editing this file manually. -->
+            <name>TEMA</name>
+            <properties/>
+            <folders>
+                <source-folder>
+                    <label>src</label>
+                    <type>java</type>
+                    <location>src</location>
+                </source-folder>
+                <source-folder>
+                    <label>test</label>
+                    <type>java</type>
+                    <location>test</location>
+                </source-folder>
+            </folders>
+            <ide-actions>
+                <action name="build">
+                    <target>compile</target>
+                </action>
+                <action name="clean">
+                    <target>clean</target>
+                </action>
+                <action name="javadoc">
+                    <target>doc.api</target>
+                </action>
+                <action name="rebuild">
+                    <target>clean</target>
+                    <target>compile</target>
+                </action>
+                <action name="run">
+                    <target>run.demo</target>
+                </action>
+            </ide-actions>
+            <view>
+                <items>
+                    <source-folder style="packages">
+                        <label>src</label>
+                        <location>src</location>
+                    </source-folder>
+                    <source-folder style="packages">
+                        <label>test</label>
+                        <location>test</location>
+                    </source-folder>
+                    <source-file>
+                        <location>build.xml</location>
+                    </source-file>
+                </items>
+                <context-menu>
+                    <ide-action name="build"/>
+                    <ide-action name="rebuild"/>
+                    <ide-action name="clean"/>
+                    <ide-action name="javadoc"/>
+                    <ide-action name="run"/>
+                    <action>
+                        <label>Compile manual</label>
+                        <target>doc.manual</target>
+                    </action>
+                </context-menu>
+            </view>
+            <subprojects/>
+        </general-data>
+        <java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/2">
+            <compilation-unit>
+                <package-root>src</package-root>
+                <classpath mode="compile">build:dist/lib/commons-cli-1.1.jar:../../../../../usr/share/java/ant.jar</classpath>
+                <source-level>1.5</source-level>
+            </compilation-unit>
+            <compilation-unit>
+                <package-root>test</package-root>
+                <unit-tests/>
+                <classpath mode="compile">build</classpath>
+                <source-level>1.5</source-level>
+            </compilation-unit>
+        </java-data>
+    </configuration>
+</project>
diff --git a/res/kryshen/tema/demo/demo.template b/res/kryshen/tema/demo/demo.template
--- a/res/kryshen/tema/demo/demo.template
+++ b/res/kryshen/tema/demo/demo.template
@@ -1,25 +1,34 @@
-[%!\ TEMA demo template %]
+[%!\
+  Tema demo template 
+%]\\
 
-Вывод текста:
-test text
+<%tema:%>
 
-Вывод специальных символов:
-[%\<%test%>%]
+Escaping special character sequences:
+[%\<%test:%>%]
 
-Объявление функции:
-<%define\test test arg1:[%nextarg:%], test arg2:[%nextarg:%], test data:[%data:%].%>   
+Macro definition:
+<%define#test
+  arg1:<%next_arg:%>, arg2:<%next_arg:%>, data:<%data:%>.%>
 
-Вызов новой функции:
-<%test:1 2 3%>
+Invoke defined macro:
+<%test:1 2 3 4 5%>
 
-Загрузка функции из класса:
+Define macro implemented as a Java class:
 <%load\hello kryshen.tema.demo.Hello%>
 
-Выполенение загруженной функции:
-<%hello\TEMA%>
+Invoke defined macro:
+<%hello\Tema%>
 
-Условное выполнение:
-<%optional:<%true:%>True%> <%optional:<%false:%>False%>
+Conditional output:
+<%optional:<%true:%>True%>\
+<%optional:<%false:%>False%>
 
-Тестирование xml_escape:
+Cycle:
+<%silent define#cycle_test
+  Arguments list: <%while#"<%next_arg:%>" %>\
+%>\
+<%cycle_test:1 2 3 <%:4 5%>%>
+
+Escaping special characters in XML:
 <%xml_escape\x < a & b%>
diff --git a/src/Manifest.mf b/src/Manifest.mf
--- a/src/Manifest.mf
+++ b/src/Manifest.mf
@@ -1,2 +1,3 @@
 Manifest-Version: 1.0
 Main-Class: kryshen.tema.Tema
+Class-Path: lib/commons-cli-1.1.jar
diff --git a/src/kryshen/tema/Context.java b/src/kryshen/tema/Context.java
new file mode 100644
--- /dev/null
+++ b/src/kryshen/tema/Context.java
@@ -0,0 +1,130 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: Context.java,v 1.11 2008/02/19 16:21:00 mikhail Exp $
+ */
+
+package kryshen.tema;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Tema template context.
+ *
+ * @author Mikhail Kryshen
+ */
+public class Context {
+    private Map<String, Object> definitions = new HashMap<String, Object>();
+    private Context superContext;
+    private File baseDir;
+    
+    public Context() {
+        this(null, null);
+    }
+    
+    public Context(Context superContext) {
+        this(superContext, null);
+    }
+    
+    public Context(File baseDir) {
+        this(null, baseDir);
+    }
+    
+    public Context(Context superContext, File baseDir) {
+        this.superContext = superContext;
+        this.baseDir = baseDir;
+        
+        definitions.put("this", this);
+        
+        if (superContext != null)
+            definitions.put("super", superContext);
+    }
+    
+    public void clear() {
+        definitions.clear();
+    }
+    
+    public boolean export(String name) {
+        Object value = definitions.get(name);
+        
+        if (value == null)
+            return false;
+        
+        export(name, value);
+        return true;
+    }
+    
+    public void export(String name, Object value) {
+        if (superContext == null) {
+            set(name, value);
+            return;
+        }
+        
+        definitions.remove(name);
+        superContext.export(name, value);
+    }
+    
+    public Object get(String name) throws TemplateException {
+        Object value = definitions.get(name);
+        
+        if (value == null && superContext != null)
+            return superContext.get(name);
+        
+        return value;
+    }
+    
+    /**
+     * Set the definition value.
+     *
+     * @param name Variable name.
+     * @param value New variable value.
+     */
+    public void set(String name, Object value) {
+        definitions.put(name, value);
+    }
+    
+    /**
+     * Remove the definition recursively.
+     * 
+     * @param name Definition name.
+     * @return true if the definition was found and removed.
+     */
+    public boolean unset(String name) {
+        if (definitions.remove(name) != null)
+            return true;
+        
+        if (superContext != null)
+            return superContext.unset(name);
+        
+        return false;
+    }
+    
+    /**
+     * Returns base directory which should be used to resolve
+     * relative path names in template.
+     */
+    public File getBaseDirectory() {
+        if (baseDir == null && superContext != null)
+            return superContext.getBaseDirectory();
+        
+        return baseDir;
+    }
+}
diff --git a/src/kryshen/tema/Function.java b/src/kryshen/tema/Function.java
--- a/src/kryshen/tema/Function.java
+++ b/src/kryshen/tema/Function.java
@@ -1,33 +1,34 @@
 /*
- *  Copyright (C) 2005, 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This file is part of Tema.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
+ *  GNU Lesser General Public License for more details.
  *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: Function.java,v 1.5 2006/12/14 14:39:22 mikhail Exp $
+ *  $Id: Function.java,v 1.13 2008/02/19 16:21:00 mikhail Exp $
  */
 
 package kryshen.tema;
 
-import java.io.*;
-import java.util.*;
-import java.sql.ResultSet;
+import java.io.IOException;
+import java.io.Writer;
 
 /**
- * Abstact class for TEMA functions.
+ * Abstact class for Tema functions.
  *
- * @author Mikhail A. Kryshen
+ * @author Mikhail Kryshen
  */
 public abstract class Function {
 
diff --git a/src/kryshen/tema/FunctionDataParser.java b/src/kryshen/tema/FunctionDataParser.java
--- a/src/kryshen/tema/FunctionDataParser.java
+++ b/src/kryshen/tema/FunctionDataParser.java
@@ -1,140 +1,204 @@
 /*
- *  Copyright (C) 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This file is part of Tema.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
+ *  GNU Lesser General Public License for more details.
  *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: FunctionDataParser.java,v 1.5 2006/12/14 14:39:22 mikhail Exp $
+ *  $Id: FunctionDataParser.java,v 1.27 2008/02/16 17:56:34 mikhail Exp $
  */
 
 package kryshen.tema;
 
-import java.io.*;
-import java.util.*;
-
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
 import static kryshen.tema.TemplateParser.Result;
-import static kryshen.tema.TemplateParser.FunctionData;
+import static kryshen.tema.TemplateParser.DataFormat;
+import kryshen.tema.TemplateParser.DataFormat;
 import static kryshen.tema.TemplateParser.Terminator;
+import kryshen.tema.io.CopyWriter;
+import kryshen.tema.io.NullWriter;
+import kryshen.tema.io.TemplateReader;
 
 /**
  * Parser for a function data.
  *
- * @author Mikhail A. Kryshen
+ * @author Mikhail Kryshen
  */
 public class FunctionDataParser {
     static final char[] ARG_SEPARATORS = TemplateParser.LIST_SEPARATORS;
-
+    
     private TemplateParser tp;
-    private FunctionData fd;
+    private DataFormat fd;
     private TemplateReader in;
-
+    
     private boolean available = true;
-    private boolean first = true;
-
-    FunctionDataParser(TemplateParser tp, FunctionData fd,
-                       TemplateReader in) {
+    
+    private int lastReturnCode = 0;
+    
+    FunctionDataParser(TemplateParser tp, DataFormat fd, TemplateReader in)
+    throws IOException, TemplateException {
         this.tp = tp;
         this.fd = fd;
         this.in = in;
+        
+        // Check for empty function data.
+        checkAvailable();
     }
-
-    private TemplateParser.Result parseData(Writer out, boolean argument)
-	throws IOException, TemplateException {
+    
+    private void checkAvailable() throws IOException, TemplateException {
+        if (tp.checkCloseBracket(in)) {
+            // Complete parsing the instruction.
+            skipData();
+        }
+    }
+    
+    private int parseData(Writer out, boolean argument, boolean skip)
+    throws IOException, TemplateException {
         
         if (!available)
             throw new TemplateException
-		("Unexpected end of instruction data.", in);
-
+                    ("Unexpected end of instruction data.", in);
+        
+        DataFormat fd = skip ? this.fd.noCall() : this.fd;
+        
         TemplateParser.Result r;
-
-        if (fd == FunctionData.RECURSIVE) {
-            r = tp.parse(in, out, true, 
-                         argument ? ARG_SEPARATORS : null,
-                         (argument || !first) ? ARG_SEPARATORS : null);
-        } else if (fd == FunctionData.NONRECURSIVE) {
-            r = tp.parse(in, out, false, 
-                         argument ? ARG_SEPARATORS : null,
-                         (argument || !first) ? ARG_SEPARATORS : null);
-        } else if (fd == FunctionData.SUBFUNCTION && argument) {
-            // Subfunction can pass a list of arguments
-            StringWriter sw = new StringWriter();
-            tp.parseFunction(in, sw);
-            sw.close();
-            in = new TemplateReader(new StringReader(sw.toString()), in);
-            fd = FunctionData.NONRECURSIVE;
-            r = parseData(out, true);
-        } else if (fd == FunctionData.SUBFUNCTION) {
-            r = new Result(null, tp.parseFunction(in, out), false);
+        
+//        if (fd.subfunction && argument) {
+//            // Allow subfunction to pass a list of arguments
+//            StringWriter sw = new StringWriter();
+//            tp.parse(in, sw, fd);
+//            sw.close();
+//            in = new TemplateReader(new StringReader(sw.toString()), in);
+//            this.fd = DataFormat.VERBATIM;
+//            r = parseData(out, true, false);
+//        } else
+        if (fd.subfunction) {
+            r = tp.parse(in, out, fd);
+        } else if (argument) {
+            // Skip duplicate separators before argument.
+            tp.skip(in, ARG_SEPARATORS);
+            
+            r = tp.parse(in, out, fd, ARG_SEPARATORS);
+            
+            // Skip duplicate separators after argument.
+            if (r.terminator == Terminator.SEPARATOR) {
+                tp.skip(in, ARG_SEPARATORS);
+                checkAvailable();
+            }
         } else {
-            throw new IllegalArgumentException("Invalid FunctiodData: " + fd);
+            r = tp.parse(in, out, fd, null);
         }
-
-        if (r.terminator != TemplateParser.Terminator.SEPARATOR)
+        
+        if (r.terminator != Terminator.SEPARATOR)
             available = false; // No more function data available.
-
-        first = false;
-        return r;
+        
+        lastReturnCode = r.retCode;
+        return r.retCode;
     }
-
+    
+    /**
+     * Parse function data.
+     */
     public int parseData(Writer out) throws IOException, TemplateException {
-        return parseData(out, false).retCode; 
+        return parseData(out, false, false);
     }
-
-    public String getData() throws IOException, TemplateException {        
+    
+    /**
+     * Skip function data (do not call any functions).
+     */
+    public void skipData() throws IOException, TemplateException {
+        parseData(NullWriter.INSTANCE, false, true);
+    }
+    
+    /**
+     * Get function data as string.
+     */
+    public String getData() throws IOException, TemplateException {
         StringWriter sw = new StringWriter();
         parseData(sw);
         sw.close();
         return sw.toString();
     }
-
+    
+    /**
+     * Parse function data into specified <code>Writer</code>.
+     * Returns copy of the parsed data as string.
+     */
+    public String getData(Writer out) throws IOException, TemplateException {
+        StringWriter sw = new StringWriter();
+        parseData(new CopyWriter(sw, out));
+        sw.close();
+        return sw.toString();
+    }
+    
     public int parseNextArg(Writer out) throws IOException, TemplateException {
-        TemplateParser.Result r = parseData(out, true);
-
-        // Ignore empty arguments.
-        while (r.empty && available) {
-            r = parseData(out, true);
-        }
-
-        return r.retCode;
+        return parseData(out, true, false);
     }
-
-    public String getNextArg() throws IOException, TemplateException {        
+    
+    public void skipNextArg() throws IOException, TemplateException {
+        parseData(NullWriter.INSTANCE, true, true);
+    }
+    
+    public String getNextArg() throws IOException, TemplateException {
         StringWriter sw = new StringWriter();
         parseNextArg(sw);
         sw.close();
         return sw.toString();
     }
-
+    
     public List<String> getArgs() throws IOException, TemplateException {
-	List<String> l = new ArrayList<String>();
-
-	while (available) {
-	    l.add(getNextArg());
-	}
-	
-	return l;
+        List<String> l = new ArrayList<String>();
+        
+        while (available) {
+            l.add(getNextArg());
+        }
+        
+        return l;
     }
-
+    
     public boolean hasMoreData() {
         return available;
     }
-
+    
+    public int getLastReturnCode() {
+        return lastReturnCode;
+    }
+    
     public TemplateParser getTemplateParser() {
         return tp;
     }
     
+    public Context getContext() {
+        return tp.getContext();
+    }
+    
     public TemplateReader getTemplateReader() {
         return in;
     }
+    
+    public File createFile(String path) {
+        File file = new File(path);
+        
+        if (file.isAbsolute())
+            return file;
+        
+        return new File(tp.getContext().getBaseDirectory(), path);
+    }
 }
diff --git a/src/kryshen/tema/Functions.java b/src/kryshen/tema/Functions.java
deleted file mode 100644
--- a/src/kryshen/tema/Functions.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- *  Copyright (C) 2005, 2006 Mikhail A. Kryshen
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- *  $Id: Functions.java,v 1.11 2006/12/14 19:44:31 mikhail Exp $
- */
-
-package kryshen.tema;
-
-import java.io.*;
-import java.util.*;
-
-import kryshen.tema.functions.*;
-
-/**
- * Standard TEMA functions.
- *
- * @author Mikhail A. Kryshen
- */
-public class Functions {
-    // TODO: arithmetics, conditions
-
-    /**
-     * Register all standard functions.
-     *
-     * @param p TemplateParser to register functions in.
-     */
-    static void registerAllFunctions(TemplateParser p) {
-        p.registerFunction("!",          Standard.NULL_OUTPUT);
-        p.registerFunction("set",        Standard.SET);
-        p.registerFunction("load",       Standard.LOAD);
-        p.registerFunction("query",      Standard.QUERY);
-        p.registerFunction("prepare",    Standard.PREPARE);
-        p.registerFunction("replace",    Standard.REPLACE);
-        p.registerFunction("xml_escape", Standard.XML_ESCAPE);
-        p.registerFunction("xml_cdata",  Standard.XML_CDATA);
-
-        p.registerFunction("define",     Define.DEFINE);
-
-        p.registerFunction("write",      IO.WRITE);
-        p.registerFunction("read",       IO.READ);
-        p.registerFunction("include",    IO.INCLUDE);
-        p.registerFunction("copy",       IO.COPY);
-
-	p.registerFunction("false",      Logics.FALSE);
-	p.registerFunction("true",       Logics.TRUE);
-        p.registerFunction("optional",   Logics.OPTIONAL);
-
-        p.registerFunction("image",      ImageConverter.IMAGE);
-
-	/* 
-	   Other functuons:
-	   db - defined in Tema.query().
-	   nextarg, data - defined in Define.invoke().
-	*/
-    }
-}
diff --git a/src/kryshen/tema/GlobalContext.java b/src/kryshen/tema/GlobalContext.java
new file mode 100644
--- /dev/null
+++ b/src/kryshen/tema/GlobalContext.java
@@ -0,0 +1,95 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: GlobalContext.java,v 1.16 2008/02/19 16:21:00 mikhail Exp $
+ */
+
+package kryshen.tema;
+
+import java.io.File;
+import kryshen.tema.functions.Database;
+import kryshen.tema.functions.Define;
+import kryshen.tema.functions.IO;
+import kryshen.tema.functions.ImageConverter;
+import kryshen.tema.functions.Control;
+import kryshen.tema.functions.Standard;
+import kryshen.tema.functions.Strings;
+
+/**
+ * Context to hold exported variables and standard Tema definitions.
+ *
+ * @author Mikhail Kryshen
+ */
+class GlobalContext extends Context {    
+    
+    GlobalContext() {
+        super(new File(System.getProperty("user.dir")));
+        
+        // Register all standard functions.
+        // TODO: arithmetics
+        
+        set("tema",       Standard.TEMA);
+        set("echo",       Standard.ECHO);
+        set("",           Standard.ECHO);
+        set("!",          Standard.SKIP);
+        set("silent",     Standard.SILENT);
+        set("set",        Standard.SET);        
+        set("unset",      Standard.UNSET);
+        set("export",     Standard.EXPORT);
+        set("assign",     Standard.ASSIGN);
+        set("invoke",     Standard.INVOKE);
+        set("load",       Standard.LOAD);
+        
+        set("to_upper",   Strings.TO_UPPER);
+        set("to_lower",   Strings.TO_LOWER);
+        set("substring",  Strings.SUBSTRING);
+        set("equal",      Strings.EQUAL);
+        set("xml_escape", Strings.XML_ESCAPE);
+        set("xml_cdata",  Strings.XML_CDATA);
+        set("char",       Strings.CHAR);
+        set("replace",    Strings.REPLACE);
+        set("regex_match",         Strings.REGEX_MATCH);
+        set("regex_replace_first", Strings.REGEX_REPLACE_FIRST);
+        set("regex_replace_all",   Strings.REGEX_REPLACE_ALL);
+        
+        set("define",     Define.DEFINE);
+        
+        set("write",      IO.WRITE);
+        set("read",       IO.READ);
+        set("include",    IO.INCLUDE);
+        set("copy",       IO.COPY);
+        set("file",       IO.FILE);
+        
+        set("false",      Control.FALSE);
+        set("true",       Control.TRUE);
+        set("not",        Control.NOT);
+        set("optional",   Control.OPTIONAL);
+        set("if",         Control.IF);
+        set("if_else",    Control.IF_ELSE);
+        set("while",      Control.WHILE);
+        
+        set("db_connect",    Database.CONNECT);
+        set("db_prepare",    Database.PREPARE);
+        set("db_query",      Database.QUERY);
+        
+        set("convert_image", ImageConverter.CONVERT_IMAGE);
+        
+        // Other functions are defined in classes Database and Define.
+    }
+}
diff --git a/src/kryshen/tema/Tema.java b/src/kryshen/tema/Tema.java
--- a/src/kryshen/tema/Tema.java
+++ b/src/kryshen/tema/Tema.java
@@ -1,364 +1,248 @@
 /*
- *  Copyright (C) 2005, 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This file is part of Tema.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
+ *  GNU Lesser General Public License for more details.
  *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: Tema.java,v 1.17 2006/12/14 19:44:31 mikhail Exp $
+ *  $Id: Tema.java,v 1.42 2008/02/19 17:14:34 mikhail Exp $
  */
 
 package kryshen.tema;
 
-import java.sql.Driver;
-import java.sql.DriverManager;
-import java.sql.Connection;
-import java.sql.Statement;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.Writer;
+import java.nio.charset.Charset;
 import java.sql.SQLException;
-
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Properties;
-import java.util.List;
-import java.util.Collections;
-
-import java.io.*;
-
 import kryshen.tema.demo.DemoFrame;
+import kryshen.tema.io.TemplateReader;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
 
 /**
  * Tema main class.
  *
- * @author Mikhail A. Kryshen
+ * @author Mikhail Kryshen
  */
 public class Tema {
-    public static final String TITLE = "TEMA";
-    public static final String VERSION = "0.1";
-    public static final String AUTHOR = "Mikhail A. Kryshen";
-
-    private static final String CONFIG_FILE = "tema.properties";
-    private static final String ENV_PREFIX = "kryshen.tema.";
-
-    private static Properties config = new Properties();
-    private static Connection connection = null;
-
-    private static String inputEncoding;
-    private static String outputEncoding;
-
-    private static Map<File, String> fileCache = null;
-
-    public static void main(String[] args) 
-	throws IOException, SQLException,
-	       ClassNotFoundException, InstantiationException, 
-	       IllegalAccessException {
-	
+    public static final String TITLE = "Tema";
+    public static final String VERSION = "0.3";
+    public static final String AUTHOR = "Mikhail Kryshen";    
+    public static final String COPYRIGHT = 
+            "Copyright 2008 Mikhail Kryshen <mikhail@kryshen.pp.ru>";
+    public static final String LICENSE = 
+            "This is free software. " + 
+            "You may redistribute copies of it under the terms of" + 
+            System.getProperty("line.separator") + 
+            "the GNU Lesser General Public License version 3 or later" + 
+            System.getProperty("line.separator") + 
+            "<http://gnu.org/licenses/lgpl.html>.";
+        
+    private static final String DEFAULT_CHARSET =
+            Charset.defaultCharset().name();
+    
+    private static String inputEncoding = DEFAULT_CHARSET;
+    private static String outputEncoding = DEFAULT_CHARSET;
+    
+    public static void main(String[] args)
+    throws IOException, SQLException,
+            ClassNotFoundException, InstantiationException,
+            IllegalAccessException {
+        
         boolean demo = false;
         boolean version = false;
         boolean help = false;
-
-        // Parse arguments.
-        for (int i = 0; i < args.length; i++) {
-            if ("-demo".equals(args[i]) || 
-                "-d".equals(args[i])) {
-                demo = true;
-            } else if ("-version".equals(args[i]) ||
-                       "-v".equals(args[i])) {
-                version = true;
-            } else if ("-help".equals(args[i]) ||
-                       "-h".equals(args[i]) ||
-                       "-usage".equals(args[i]) ||
-                       "-u".equals(args[i])) {
-                help = true;
-            } else {
-                System.err.println("Invalid option: " + args[i]);
-                return;
-            }
+        
+        Options options = new Options();
+        
+        options.addOption(null, "demo", false, "demo mode");
+        options.addOption("v", "version", false,
+                "print the version information and exit");
+        options.addOption("h", "help", false,
+                "print this help message");
+        options.addOption("o", "output", true,
+                "output file");
+        options.addOption(null, "log", true,
+                "log all error messages to file");
+        options.addOption(null, "input-encoding", true,
+                "input encoding");
+        options.addOption(null, "output-encoding", true,
+                "output encoding");
+        
+        CommandLineParser parser = new GnuParser();
+        CommandLine line;
+        
+        try {
+            line = parser.parse(options, args );
+        } catch (ParseException ex) {
+            System.err.println(ex.getMessage());
+            System.exit(1);
+            return; // to avoid "not initialized" compile-time errors.
         }
-
-        if (version || help) {
-            System.err.println(TITLE + " " + VERSION + " by " + AUTHOR);        
-
-            if (help) {
-                PrintStream err = System.err;
-                
-                err.println();
-                err.println("Usage: tema [OPTIONS]");
-                err.println();
-                err.println("Options:");
-                err.println("  -d[emo]             Demo mode");
-                err.println("  -v[ersion]          Print version");
-                err.println("  -h[help] -u[sage]   Print this help message");
-            }
+        
+        if (line.hasOption("version")) {
+            System.err.println(TITLE + " " + VERSION);
+            System.err.println(COPYRIGHT);
+            System.err.println(LICENSE);
             return;
         }
-
-        if (demo) {
-            // Run in demo mode.
+        
+        if (line.hasOption("help")) {
+            HelpFormatter formatter = new HelpFormatter();
+            formatter.printHelp("tema [options] [files]", options);
+            return;
+        }       
+        
+        if (line.hasOption("input-encoding")) {
+            inputEncoding = line.getOptionValue("input-encoding");
+        }
+        
+        if (line.hasOption("output-encoding")) {
+            outputEncoding = line.getOptionValue("output-encoding");
+        }
+        
+        if (line.hasOption("log")) {
+            System.setErr(new PrintStream
+                    (new FileOutputStream(line.getOptionValue("log"))));
+        }
+        
+        if (line.hasOption("demo")) {
+            // Open the demo console.
             DemoFrame df = new DemoFrame();
             df.pack();
             df.setVisible(true);
             return;
         }
-
-        process();
+        
+        Writer out;
+        
+        if (line.hasOption("output")) {
+            File outfile = new File(line.getOptionValue("output"));
+            out = createFileWriter(outfile);
+        } else {
+            out = new OutputStreamWriter(System.out, outputEncoding) {
+                @Override
+                public void write(int c) throws IOException {
+                    super.write(c);
+                    if (c == '\r' || c == '\n')
+                        flush();
+                }
+                
+                @Override
+                public void write(char cbuf[], int off, int len)
+                throws IOException {
+                    super.write(cbuf, off, len);
+                    flush();
+                }
+                
+                @Override
+                public void write(String str, int off, int len)
+                throws IOException {
+                    super.write(str, off, len);
+                    flush();
+                }
+            };
+        }
+        
+        String[] files = line.getArgs();
+        
+        if (files.length == 0) {
+            TemplateReader in = createTemplateReader(System.in);
+            if (!process(in, out)) {
+                System.exit(1);
+            }
+        }
+        
+        for (String file: files) {
+            TemplateReader in = createTemplateReader(new File(file));
+            if (!process(in, out)) {
+                System.exit(1);
+            }
+        }
+        
+        out.close();
     }
-
-    /**
-     * Standard execution.
-     */
-    private static void process()
-        throws IOException, SQLException,
-               ClassNotFoundException, InstantiationException, 
-               IllegalAccessException {
-
-        InputStream configIn = new FileInputStream(CONFIG_FILE);
-        config.load(configIn);
-        configIn.close();
+    
+    private static boolean process(TemplateReader in, Writer out)
+    throws IOException {
         
-        configure();
-	
-	String main = getProperty("main_template");
-	String output = getProperty("output");
-	
-	Writer out;
-
-	if ("stderr".equals(output))
-	    out = new OutputStreamWriter(System.err);
-	else
-	    out = new OutputStreamWriter(System.out);
-
-        TemplateReader in = createTemplateReader(new File(main));
         TemplateParser p = new TemplateParser();
-        TemplateException te = null;
-
-	try {
-	    p.parse(in, out);
+        TemplateException ex = null;
+                        
+        p.getContext().set("input_encoding",  inputEncoding);
+        p.getContext().set("output_encoding", outputEncoding);
+        
+        try {
+            p.parse(in, out);
         } catch (TemplateException e) {
-            te = e;
-	} finally {
-	    in.close();
-	    out.flush();
-	}  
-
-        if (te != null) {
-            System.err.println(te.getMessage());
-            if (te.getCause() != null) {
-                System.err.println("Caused by " + te.getCause());
+            ex = e;
+        } finally {
+            in.close();
+        }
+        
+        if (ex != null) {
+            System.err.println(ex.getMessage());
+            if (ex.getCause() != null) {
+                System.err.println("Caused by " + ex.getCause());
             }
-        } else {
-            System.err.println("Done");
+            return false;
         }
+        
+        return true;
     }
-
-    private static void configure() 
-        throws IOException, SQLException, IllegalAccessException,
-               ClassNotFoundException, InstantiationException {
-
-        String logFile = getProperty("log");
-        if (logFile != null && logFile.length() > 0)
-            System.setErr(new PrintStream(new FileOutputStream(logFile)));
-
-	if (Boolean.parseBoolean(getProperty("cache_read")) == true)
-	    fileCache = new HashMap<File, String>();
-
-	inputEncoding = getProperty("input_encoding", "iso-8859-1");
-	outputEncoding = getProperty("output_encoding", "iso-8859-1");
-
-	String resourceProperty = getProperty("resource");
-
-	if (resourceProperty != null) {
-	    String driverProperty = getProperty("driver");
-	    if (driverProperty != null) {
-		// Register driver.  
-		Driver d = (Driver)Class.forName(driverProperty).newInstance();
-	    }
-	
-	    // Open connection.
-	    connection = DriverManager.getConnection(resourceProperty);
-	}
+    
+    public static BufferedWriter createFileWriter(File file)
+    throws IOException {
+        
+        OutputStream os = new FileOutputStream(file);
+        return new BufferedWriter
+                (new OutputStreamWriter(os, Tema.outputEncoding));
     }
-
     
-
-    /**
-     * Get configuration property.
-     */
-    public static String getProperty(String name) {
-        String value = System.getProperty(ENV_PREFIX + name);
-        if (value != null) return value;
-        return config.getProperty(name);
+    public static BufferedReader createFileReader(File file)
+    throws IOException {
+        
+        InputStream is = new FileInputStream(file);
+        return new BufferedReader
+                (new InputStreamReader(is, Tema.inputEncoding));
     }
-
-    /**
-     * Get configuration property.
-     */
-    public static String getProperty(String name, String fallback) {
-	String value = getProperty(name);
-	if (value != null) return value;
-	return fallback;
+    
+    public static TemplateReader createTemplateReader(File file)
+    throws IOException {
+        
+        return new TemplateReader(createFileReader(file), file.getPath());
     }
-
-    /**
-     * Process database query.
-     *
-     * @param templateReader reader for the template 
-     *                       to fill with data.
-     * @param ps query statement to execute.
-     * @param args list of query parameters.
-     * @param superParser invoking object.
-     * @param out Writer to output processed data.
-     *
-     * @return number of processed rows in query result.
-     */
-    public static int query(TemplateReader templateReader,
-                            PreparedStatement ps,
-                            List<String> args,
-                            TemplateParser superParser, 
-                            Writer out)
-	throws IOException, SQLException, TemplateException {
-
-	int i = 1;
-
-	for (String arg : args) {
-	    ps.setString(i++, arg);
-	}
-	
-	ResultSet r = ps.executeQuery();
-	ResultSetMetaData rm = r.getMetaData();
-	int columnCount = rm.getColumnCount();
-
-	String[] names = new String[columnCount];
-	
-	for (int j = 0; j < columnCount; j++) {
-	    names[j] = rm.getColumnName(j + 1);
-	}
-
-	final TemplateParser p = new TemplateParser(superParser);
-	final HashMap<String, Object> fields = new HashMap<String, Object>();
-
-        p.registerFunction("db", new Function() {
-                public int invoke(FunctionDataParser fdp, 
-                                  final Writer out)
-                    throws IOException, TemplateException {
-                    
-                    String name = fdp.getData();
-                    Object value = fields.get(name);
-		    
-                    return p.parseValue(value, out);
-                }
-            });
-
-	for (i = 1; r.next(); i++) {
-
-	    for (int j = 0; j < columnCount; j++) {
-		Object value = r.getObject(j + 1);
-		if (r.wasNull()) value = null;
-                
-		fields.put(names[j], value);
-	    }
-
-	    p.setValue("number", i);
-            p.parse(templateReader, out);
-	    templateReader.reset();
-            fields.clear();
-	}
-
-	r.close();
-	ps.clearParameters();
-	return i - 1;
-    }
-
-    public static BufferedWriter createFileWriter(String filename) 
-        throws IOException {
-
-	OutputStream os = new FileOutputStream(filename);
-	return new BufferedWriter
-            (new OutputStreamWriter(os, Tema.outputEncoding));
-    }
-
-    public static BufferedReader createFileReader(File file)
-        throws IOException {
-
-	InputStream is = new FileInputStream(file);
-	return new BufferedReader
-            (new InputStreamReader(is, Tema.inputEncoding));
-    }
-
-    public static TemplateReader createTemplateReader(File file)
-        throws IOException {
-
-        return new TemplateReader
-            (new LineNumberReader(createCachedFileReader(file)), file.getPath());
-    }
-
-    /**
-     * Creates StringReader for a cached file if caching is enabled or
-     * a BufferedReader.
-     *
-     * @param file File to read.
-     * @return Reader for a file.
-     */
-    public static Reader createCachedFileReader(File file) 
-        throws IOException {
+    
+    public static TemplateReader createTemplateReader(InputStream in)
+    throws IOException {
         
-	if (fileCache != null) return new StringReader(readFile(file));
-	else return createFileReader(file);
-    }
-
-    /**
-     * Read text file.
-     *
-     * @return file contents.
-     */
-    public static String readFile(File file) throws IOException {
-	String data;
-        
-	if (fileCache != null) {
-	    data = fileCache.get(file);	    
-	    if (data != null) return data;
-	}
-        
-        Reader r = new BufferedReader(createFileReader(file));
-
-        // FIXME: possible overflow (long -> int).
-	StringBuffer sb = new StringBuffer((int)file.length());
-
-	try {
- 	    for (int c = r.read(); c >= 0; c = r.read()) {
- 		sb.append((char)c);
- 	    }
-	} finally {
-	    r.close();
-	}
-
-	data = sb.toString();
-
-	if (fileCache != null) {
-	    fileCache.put(file, data);
-	}
-
-	return data;
-    }
-
-    /**
-     * Get database connection.
-     *
-     * @return database connection if any was configured.
-     */
-    public static Connection getDbConnection() {
-        return connection;
+        return new TemplateReader(
+                new InputStreamReader(in, Tema.inputEncoding));
     }
 }
diff --git a/src/kryshen/tema/TemplateException.java b/src/kryshen/tema/TemplateException.java
--- a/src/kryshen/tema/TemplateException.java
+++ b/src/kryshen/tema/TemplateException.java
@@ -1,29 +1,33 @@
 /*
- *  Copyright (C) 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This file is part of Tema.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
+ *  GNU Lesser General Public License for more details.
  *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: TemplateException.java,v 1.5 2006/12/14 14:39:22 mikhail Exp $
+ *  $Id: TemplateException.java,v 1.12 2008/02/16 17:56:34 mikhail Exp $
  */
 
 package kryshen.tema;
 
+import kryshen.tema.io.TemplateReader;
+
 /**
  * Signals that template parsing fails.
  *
- * @author Mikhail A. Kryshen
+ * @author Mikhail Kryshen
  */
 public class TemplateException extends Exception {
     //private String source;
diff --git a/src/kryshen/tema/TemplateParser.java b/src/kryshen/tema/TemplateParser.java
--- a/src/kryshen/tema/TemplateParser.java
+++ b/src/kryshen/tema/TemplateParser.java
@@ -1,296 +1,445 @@
 /*
- *  Copyright (C) 2005, 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This file is part of Tema.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
+ *  GNU Lesser General Public License for more details.
  *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: TemplateParser.java,v 1.11 2006/12/14 19:44:31 mikhail Exp $
+ *  $Id: TemplateParser.java,v 1.58 2008/02/19 00:20:48 mikhail Exp $
  */
 
 package kryshen.tema;
 
-import java.io.*;
-import java.util.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.Writer;
+import kryshen.tema.io.TemplateReader;
 
 /**
- * Parser for TEMA templates.
+ * Parser for Tema templates.
  *
- * @author Mikhail A. Kryshen
+ * @author Mikhail Kryshen
  */
 public class TemplateParser {
-    // TODO: report non-critical errors and warnings.
-
-    static final String SUPER_PREFIX = "super.";
-
     /* Brackets. */
-    static final char[] BR_LEFT = {'<', '['};
-    static final char[] BR_RIGHT = {'>', ']'};
-
-    static final char BR_IN = '%';
+    public static final String[] BRACKET_OPEN = {"<%", "[%"};
+    public static final String[] BRACKET_CLOSE = {"%>", "%]"};
+    
+    public static final String ESCAPE_NEWLINE = "\\";
+    public static final String ESCAPE_WHITESPACE = "\\\\";
     
     /* Separators. */
-    static final char[] REC_DATA_SEPARATORS = {':'};
-    static final char[] NONREC_DATA_SEPARATORS = {'\\', '`'}; 
-    static final char[] LIST_SEPARATORS = {' ', '\t', '\r', '\n'};
-
+    public static final char[] REC_DATA_SEPARATORS = {':'};
+    public static final char[] VERBATIM_DATA_SEPARATORS = {'\\', '`'};
+    public static final char[] NOCALL_DATA_SEPARATORS = {'#'};
+    public static final char[] LIST_SEPARATORS = {' ', '\t', '\r', '\n'};
+    
     /**
-     * Methods of parsing function arguments and data.
+     * Specifies how to parse function arguments and data.
      */
-    static enum FunctionData {
-	SUBFUNCTION, RECURSIVE, NONRECURSIVE;
+    static enum DataFormat {
+        RECURSIVE(true, false),
+        VERBATIM(false, false),
+        NOCALL(false, false),
+        SUBFUNCTION(true, true),
+        SUBFUNCTION_NOCALL(false, true);
+        
+        public final boolean call;
+        public final boolean subfunction;
+        
+        DataFormat(boolean call, boolean subfunction) {
+            this.call = call;
+            this.subfunction = subfunction;
+        }
+        
+        /**
+         * Returns no-call equivalent to this format.
+         */
+        public DataFormat noCall() {
+            if (this == RECURSIVE)
+                return NOCALL;
+            
+            if (this == SUBFUNCTION)
+                return SUBFUNCTION_NOCALL;
+            
+            return this;
+        }
     };
-
+    
+    /**
+     * Specifies how the template instruction was terminated.
+     */
     static enum Terminator {
-	EOF, BRACKET, SEPARATOR;
+        EOF, BRACKET, SEPARATOR;
     };
-
+    
+    /**
+     * The result of evaluating template instruction.
+     */
     static class Result {
-	Terminator terminator;
-	int retCode;
-
+        Terminator terminator;
+        int retCode;
+        
         /** No text had been parsed. */
-        boolean empty; 
-
-	Result(Terminator terminator, int retCode, boolean empty) {
+        boolean empty;
+        
+        Result(Terminator terminator, int retCode, boolean empty) {
             this.terminator = terminator;
             this.retCode = retCode;
             this.empty = empty;
         }
-
+        
         Result() {
             this(null, -1, true);
-        }       
-    };    
-
-    private Map<String, Object> variables = new HashMap<String, Object>();
-    private TemplateParser superParser;
-
+        }
+    };
+    
+    protected final Context context;
+    
     private int termBracket = -1;
-
+    
     public TemplateParser() {
-        this(null);
+        this.context = new Context(new GlobalContext());
     }
-
+    
+    public TemplateParser(File baseDir) {
+        this.context = new Context(new GlobalContext(), baseDir);
+    }
+    
     public TemplateParser(TemplateParser superParser) {
-	this.superParser = superParser;
-
-        Functions.registerAllFunctions(this);
+        this.context = new Context(superParser.context);
     }
-
-    public void registerFunction(String name, Function f) {
-        variables.put(name, f);
+    
+    public TemplateParser(TemplateParser superParser, File baseDir) {
+        this.context = new Context(superParser.context, baseDir);
     }
-
-    public int parse(TemplateReader in, Writer out) 
-	throws IOException, TemplateException {
-	
-	return parse(in, out, true, null, null).retCode;
+    
+    public TemplateParser(Context context) {
+        this.context = context;
     }
-
+    
     /**
-     *  Parse template.
+     * Parse template
+     *
+     * @param in TemplateReader to read template data.
+     * @param out Writer for the processed data.
+     * @throws IOException if case of I/O error.
+     * @throws TemplateException in case of parsing error (syntax
+     * error or invalid arguments in template instructions).
+     */
+    public int parse(TemplateReader in, Writer out)
+    throws IOException, TemplateException {
+        return parse(in, out, DataFormat.RECURSIVE, null).retCode;
+    }
+    
+    Result parse(TemplateReader in, Writer out, DataFormat format)
+    throws IOException, TemplateException {
+        return parse(in, out, format, null);
+    }
+    
+    /**
+     * Parse template.
      *
      * @param in TemplateReader to read template data.
      * @param out Writer to write processed data.
-     * @param recursive enables recursive processing.
-     * @param separators characters which terminate parsing block.
-     * @param leading characters to ignore at the beginning of the block.
+     * @param DataFormat specifies parsing mode.
      */
-    Result parse(TemplateReader in, Writer out, boolean recursive, 
-                 char[] separators, char[] leading)
-	throws IOException, TemplateException {
-
-	Result result = new Result();
-	int lc = -1;
-
-	while (true) {
-	    int c = in.read();
+    Result parse(TemplateReader in, Writer out, DataFormat format,
+            char[] separators)
+            throws IOException, TemplateException {
+        
+        Result result = new Result();
+        
+        if (format == DataFormat.SUBFUNCTION ||
+                format == DataFormat.SUBFUNCTION_NOCALL) {
             
-            if (leading != null && isSeparator(c, leading))
-                continue;
-            else
-                leading = null;
-
-            boolean leftBracket = false;
-            int index;
-
-            for (index = 0; index < BR_LEFT.length; index++) {
-                if (lc == BR_LEFT[index]) {
-                    leftBracket = true;
-                    break;
+            result.retCode = parseFunction(in, out, format);
+            result.empty = false;
+            return result;
+        }
+        
+        while (true) {
+            if (format != DataFormat.VERBATIM) {
+                int openBracket = matchInput(in, BRACKET_OPEN);
+                
+                if (openBracket >= 0) {
+                    if (result.retCode < 0)
+                        result.retCode = 0;
+                    
+                    if (!format.call) {
+                        out.write(BRACKET_OPEN[openBracket]);
+                    }
+                    
+                    int tb = termBracket;
+                    termBracket = openBracket;
+                    result.retCode += Math.abs(parseFunction(in, out, format));
+                    result.empty = false;
+                    termBracket = tb;
+                    
+                    if (!format.call) {
+                        out.write(BRACKET_CLOSE[openBracket]);
+                    }
+                    
+                    continue;
                 }
             }
-
-	    if (recursive && leftBracket && c == BR_IN) {
-		
-                if (result.retCode < 0)
-                    result.retCode = 0;
-
-		lc = -1;
-                int tb = termBracket;
-                termBracket = BR_RIGHT[index];
-		result.retCode += parseFunction(in, out);
-                termBracket = tb;
-		
-	    } else if (lc == BR_IN && c == termBracket) {
-		
-		lc = -1;
-		result.terminator = Terminator.BRACKET;
-		break;
-		
-	    } else {
-
-		if (lc >= 0)
-		    out.write(lc);
-	    
-		lc = c;
-	    }
-
-	    if (c < 0) {
-		result.terminator = Terminator.EOF;
-		break; 
-	    } else if (separators != null && isSeparator(c, separators)) {
+            
+            if (matchCloseBracket(in)) {
+                result.terminator = Terminator.BRACKET;
+                skipEscaped(in);
+                break;
+            }
+            
+            int c = in.read();
+            
+            if (c < 0) {
+//                if (termBracket >= 0) {
+//                    throw new TemplateException("Unexpected end of file", in);
+//                }
+                
+                result.terminator = Terminator.EOF;
+                break;
+            }
+            
+            if (separators != null && isSeparator(c, separators)) {
                 result.terminator = Terminator.SEPARATOR;
                 break;
             }
-
+            
+            out.write(c);
             result.empty = false;
-	}
-
-	return result;
+        }
+        
+        return result;
     }
-
+    
+    /**
+     * Check if the closing bracket sequence follows in the reader.
+     */
+    boolean checkCloseBracket(TemplateReader in) throws IOException {
+        boolean match = matchCloseBracket(in);
+        
+        if (!match)
+            return false;
+        
+        // Put the matched characters back to the stream
+        in.unread(BRACKET_CLOSE[termBracket].toCharArray());
+        
+        return true;
+    }
+    
+    private boolean matchCloseBracket(TemplateReader in)
+    throws IOException {
+        
+        if (termBracket < 0)
+            return false;
+        
+        return matchInput(in, BRACKET_CLOSE[termBracket]);
+    }
+    
+    private int matchInput(TemplateReader in, String[] s)
+    throws IOException {
+        for (int i = 0; i < s.length; i++) {
+            if (matchInput(in, s[i]))
+                return i;
+        }
+        
+        return -1;
+    }
+    
+    private boolean matchInput(TemplateReader in, String s)
+    throws IOException {
+        int n = s.length();
+        for (int k = 0; k < n; k++) {
+            int c = in.read();
+            
+            if (c != s.charAt(k)) {
+                if (c >= 0) {
+                    in.unread(c);
+                }
+                
+                for (int j = k - 1; j >= 0; j--) {
+                    in.unread(s.charAt(j));
+                }
+                
+                return false;
+            }
+        }
+        
+        return true;
+    }
+    
+    void skip(TemplateReader in, char[] chars) throws IOException {
+        readLoop:
+            while (true) {
+                int c = in.read();
+                
+                if (c < 0)
+                    break;
+                
+                for (int i = 0; i < chars.length; i++) {
+                    if (c == chars[i])
+                        continue readLoop;
+                }
+                
+                in.unread(c);
+                break;
+            }
+    }
+    
+    private void skipEscaped(TemplateReader in) throws IOException {
+        if (matchInput(in, ESCAPE_WHITESPACE)) {
+            int c = in.read();
+            
+            if (c < 0)
+                return;
+            
+            if (!Character.isWhitespace(c)) {
+                in.unread(c);
+                in.unread(ESCAPE_WHITESPACE.toCharArray());
+                return;
+            }
+            
+            do {
+                c = in.read();
+            } while (Character.isWhitespace(c));
+            
+            if (c >= 0)
+                in.unread(c);
+            
+            return;
+        }
+        
+        matchInput(in,
+                new String[] {
+            ESCAPE_NEWLINE + "\r\n",
+            ESCAPE_NEWLINE + "\r",
+            ESCAPE_NEWLINE + "\n"});
+    }
+    
     boolean isSeparator(int c, char[] separators) {
         for (char p : separators) {
             if (c == p) return true;
         }
-
+        
         return false;
     }
-
-    int parseFunction(TemplateReader in, Writer out)
-	throws IOException, TemplateException {
+    
+    private int parseFunction(TemplateReader in, Writer out, DataFormat fd)
+    throws IOException, TemplateException {
         
-	StringBuffer sb = new StringBuffer();
+        StringBuffer sb = new StringBuffer();
         
-	while (true) {
-	    int c = in.read();
+        while (true) {
+            if (termBracket >= 0 &&
+                    matchInput(in, BRACKET_CLOSE[termBracket])) {
+                throw new TemplateException(
+                        "Unexpected end of instruction", in);
+            }
+            
+            int c = in.read();
+            
+            if (c < 0)
+                throw new TemplateException("Unexpected end of file.", in);
+            
+            if (!fd.call)
+                out.write(c);
             
             if (isSeparator(c, LIST_SEPARATORS)) {
-		return invokeFunction(sb.toString(), 
-				      FunctionData.SUBFUNCTION,
-				      in, out);
+                return invoke(
+                        fd.call ? sb.toString() : "true",
+                        fd.call ?
+                            DataFormat.SUBFUNCTION :
+                            DataFormat.SUBFUNCTION_NOCALL,
+                        in, out);
             } else if (isSeparator(c, REC_DATA_SEPARATORS)) {
-		return invokeFunction(sb.toString(), 
-				      FunctionData.RECURSIVE,
-				      in, out);
-            } else if (isSeparator(c, NONREC_DATA_SEPARATORS)) {
-		return invokeFunction(sb.toString(), 
-				      FunctionData.NONRECURSIVE, 
-				      in, out);
-            } else if (c < 0) {
-		throw new TemplateException("Unexpected end of file.", in);
+                skipEscaped(in);
+                return invoke(
+                        fd.call ? sb.toString() : "true",
+                        fd.call ?
+                            DataFormat.RECURSIVE :
+                            DataFormat.NOCALL,
+                        in, out);
+            } else if (isSeparator(c, VERBATIM_DATA_SEPARATORS)) {
+                return invoke(
+                        fd.call ? sb.toString() : "true",
+                        DataFormat.VERBATIM, in, out);
+            } else if (isSeparator(c, NOCALL_DATA_SEPARATORS)) {
+                skipEscaped(in);
+                return invoke(
+                        fd.call ? sb.toString() : "true",
+                        DataFormat.NOCALL, in, out);
             } else {
-		sb.append((char)c);
-	    }
-	}
+                sb.append((char)c);
+            }
+        }
     }
-
-    int parseValue(Object value, Writer out) throws IOException {
+    
+    public int parseValue(Object value, Writer out) throws IOException {
         if (value == null)
             return 0;
-
+        
         String svalue = value.toString();
         
         if (svalue == null || svalue.length() == 0)
             return 0;
-
+        
         out.write(svalue);
         return 1;
     }
-
-    private int invokeFunction(String name, FunctionData fd,
-			       TemplateReader in, Writer out)
-	throws IOException, TemplateException {
-
+    
+    private int invoke(String name, DataFormat fd,
+            TemplateReader in, Writer out)
+            throws IOException, TemplateException {
+        
         FunctionDataParser fdp = new FunctionDataParser(this, fd, in);
-
-	if ("".equals(name)) {
-            return fdp.parseData(out);
-	}
+                
+        int r = invoke(name, fdp, out);                
         
-        Object value = getValue(name);
-        int r;
-
-        if (value instanceof Function) {            
-            r = ((Function) value).invoke(fdp, out);           
-        } else { 
-            r = parseValue(value, out);
+        if (fdp.hasMoreData()) {
+            /* Skip remaining function data. */
+            fdp.skipData();
         }
-
-	if (fdp.hasMoreData()) {
-	    /* Skip remaining function data. */
-	    fdp.parseData(new Writer() {
-		    public void close() {}
-		    public void flush() {}
-		    public void write(char[] cbuf, int off, int len) {}
-		});
-	}
-	
-	return r;
+        
+        return r;
     }
-
-    public Object getValue(String name) throws TemplateException {
-	Object value = variables.get(name);
-	if (value != null) return value;   
-
-	if (superParser != null) {
-	    if (name.startsWith(SUPER_PREFIX))
-		return superParser.getValue
-                    (name.substring(SUPER_PREFIX.length()));
-	    else
-		return superParser.getValue(name);
-	}
-
-	return null;
+          
+    public int invoke(String name, FunctionDataParser fdp, Writer out)
+    throws IOException, TemplateException {
+        Object value = context.get(name);
+        
+        if (value instanceof Function) {
+            return ((Function) value).invoke(fdp, out);
+        }
+        
+        if (value instanceof Context) {
+            String code = fdp.getData();
+            
+            if (fdp.getLastReturnCode() == 0)
+                return 0;
+            
+            TemplateParser parser = new TemplateParser((Context) value);
+            return parser.parse(
+                    new TemplateReader(new StringReader(code)), out);
+        }
+        
+        return parseValue(value, out);
     }
-
-    public void setValue(String name, Object value) {
-	variables.put(name, value);
-    }
-
-    public void clearValues() {
-	variables.clear();
-        Functions.registerAllFunctions(this);
-    }
-
-    // TEST
-    public static void main(String[] args)
-	throws IOException, TemplateException {
-	
-	TemplateParser p = new TemplateParser();
-	
-	TemplateReader in = 
-            new TemplateReader
-            (new LineNumberReader
-             (new FileReader("parser.in")), "parser.in");
-
-	FileWriter out = new FileWriter("parser.out");
-
-	p.parse(in, out);
-
-	in.close();
-	out.close();
+    
+    public Context getContext() {
+        return context;
     }
 }
diff --git a/src/kryshen/tema/TemplateReader.java b/src/kryshen/tema/TemplateReader.java
deleted file mode 100644
--- a/src/kryshen/tema/TemplateReader.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- *  Copyright (C) 2006 Mikhail A. Kryshen
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- *  $Id: TemplateReader.java,v 1.5 2006/12/14 14:39:22 mikhail Exp $
- */
-
-package kryshen.tema;
-
-import java.io.Reader;
-import java.io.LineNumberReader;
-import java.io.FilterReader;
-import java.io.IOException;
-
-/**
- * Reader for TEMA templates. Stores data source name (commonly
- * filename) and tracks line numbers for error reporting.
- *
- * @author Mikhail A. Kryshen
- */
-public class TemplateReader extends FilterReader {
-    private String source = null;
-
-    private LineNumberReader lnReader = null;
-    private TemplateReader parentReader = null;
-
-    public TemplateReader(LineNumberReader in, String source) {
-        super(in);
-        this.lnReader = in;
-        this.source = source;
-    }
-
-    public TemplateReader(Reader in, TemplateReader parent) {
-        super(in);
-        this.source = source;
-    }
-
-    public String getSource() {
-        if (source != null)
-            return source;
-        
-        return parentReader.getSource();
-    }
-    
-    public int getLineNumber() {
-        if (lnReader != null)
-            return lnReader.getLineNumber();
-        
-        return parentReader.getLineNumber();
-    }
-}
diff --git a/src/kryshen/tema/ant/TemaTask.java b/src/kryshen/tema/ant/TemaTask.java
new file mode 100644
--- /dev/null
+++ b/src/kryshen/tema/ant/TemaTask.java
@@ -0,0 +1,145 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: TemaTask.java,v 1.17 2008/02/17 23:58:01 mikhail Exp $
+ */
+
+package kryshen.tema.ant;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+import kryshen.tema.TemplateException;
+import kryshen.tema.TemplateParser;
+import kryshen.tema.io.NullWriter;
+import kryshen.tema.io.TemplateReader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * Tema support for ant.
+ *
+ * @author Mikhail Kryshen
+ */
+public class TemaTask extends Task {
+    
+    public static class Variable {
+        private String name;
+        private String value;
+        
+        public void setName(String name) {
+            this.name = name;
+        }
+        
+        public void setValue(String value) {
+            this.value = value;
+        }
+        
+        public void setPath(File file) {
+            this.value = file.getAbsolutePath();
+        }
+    }
+    
+    private File basedir = null;
+    
+    private File infile = null;
+    private File outfile = null;
+    
+    private Collection<Variable> variables = new ArrayList<Variable>();
+    
+    @Override
+    public void execute() throws BuildException {
+        if (basedir == null) {
+            basedir = getProject().getBaseDir();
+        }
+        
+        if (infile == null) {
+            throw new BuildException("no infile specified", getLocation());
+        }
+        
+        try {
+            executeTema();
+        } catch (IOException e) {
+            throw new BuildException(e);
+        } catch (TemplateException e) {
+            throw new BuildException(e);
+        }
+    }
+    
+    private void executeTema() throws IOException, TemplateException {
+        TemplateParser parser = new TemplateParser(basedir);
+        
+        for (Variable var : variables) {
+            if (var.name == null) {
+                throw new BuildException(
+                        "no name specified for variable", getLocation());
+            }
+            
+            if (var.value == null) {
+                throw new BuildException(
+                        "no value specified for variable", getLocation());
+            }
+            
+            parser.getContext().set(var.name, var.value);
+        }
+        
+        TemplateReader in = new TemplateReader(
+                new BufferedReader(new FileReader(infile)),
+                infile.getName());
+        try {
+            Writer out;
+            
+            if (outfile != null) {
+                out = new BufferedWriter(new FileWriter(outfile));
+            } else {
+                out = NullWriter.INSTANCE;
+            }
+            
+            try {
+                parser.parse(in, out);
+            } finally {
+                out.close();
+            }
+        } finally {
+            in.close();
+        }
+    }
+    
+    public void setBasedir(File basedir) {
+        this.basedir = basedir;
+    }
+    
+    public void setInfile(File infile) {
+        this.infile = infile;
+    }
+    
+    public void setOutfile(File outfile) {
+        this.outfile = outfile;
+    }
+    
+    public void addVariable(Variable variable) {
+        variables.add(variable);
+    }
+}
diff --git a/src/kryshen/tema/demo/DemoFrame.java b/src/kryshen/tema/demo/DemoFrame.java
--- a/src/kryshen/tema/demo/DemoFrame.java
+++ b/src/kryshen/tema/demo/DemoFrame.java
@@ -1,181 +1,228 @@
 /*
- *  Copyright (C) 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This file is part of Tema.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
+ *  GNU Lesser General Public License for more details.
  *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: DemoFrame.java,v 1.3 2006/12/14 15:59:48 mikhail Exp $
+ *  $Id: DemoFrame.java,v 1.13 2008/02/16 17:38:03 mikhail Exp $
  */
 
 package kryshen.tema.demo;
 
-import java.awt.*;
-import java.awt.event.*;
-import java.io.*;
+import java.awt.Button;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Label;
+import java.awt.Panel;
+import java.awt.TextArea;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
 import java.net.URL;
-
-import kryshen.tema.*;
+import kryshen.tema.TemplateParser;
+import kryshen.tema.io.TemplateReader;
 
 /**
- * TEMA demonstation console.
+ * Tema demonstation console.
  */
 public class DemoFrame extends Frame {
+    private static final Font TEXT_FONT =
+            new Font("monospaced", Font.PLAIN, 12);
+    
     private TextArea input;
     private TextArea output;
     private TextArea error;
     
+    private String sample;
+    
     public DemoFrame() {
-        super("TEMA demo console");
-
+        super("Tema demo console");
+        
         GridBagLayout gbl = new GridBagLayout();
-
+        
         GridBagConstraints c = new GridBagConstraints();
-
+        
         setLayout(gbl);
-
-        input = new TextArea(25, 40);
+        
+        input = new TextArea(28, 50);
         input.setEditable(true);
-
-        output = new TextArea(25, 40);
+        input.setFont(TEXT_FONT);
+        
+        output = new TextArea(28, 50);
         output.setEditable(false);
-
-        error = new TextArea(5, 80);
+        output.setFont(TEXT_FONT);
+        
+        error = new TextArea(5, 90);
         error.setEditable(false);
-
+        error.setFont(TEXT_FONT);
+        
         Label l;
-
+        
         c.insets = new Insets(3, 3, 3, 3);
         c.fill = GridBagConstraints.BOTH;
         c.gridwidth = GridBagConstraints.RELATIVE;
-
-        l = new Label("Enter your code and click \"Process\".");
-
+        
+        l = new Label("Enter your code and press \"Process\".");
+        
         gbl.setConstraints(l, c);
         add(l);
-
+        
         Panel buttons = new Panel();
         buttons.setLayout(new FlowLayout(FlowLayout.LEFT));
         
         Button process = new Button("Process");
         Button clear = new Button("Clear");
+        Button reset = new Button("Reset");
         Button close = new Button("Close");
-
+        
         buttons.add(process);
         buttons.add(clear);
+        buttons.add(reset);
         buttons.add(close);
-
-        c.gridwidth = GridBagConstraints.REMAINDER; 
-
+        
+        c.gridwidth = GridBagConstraints.REMAINDER;
+        
         gbl.setConstraints(buttons, c);
         add(buttons);
-
+        
         c.fill = GridBagConstraints.BOTH;
         c.anchor = GridBagConstraints.CENTER;
         c.gridwidth = GridBagConstraints.RELATIVE;
-
+        
         l = new Label("Input:");
-
+        
         gbl.setConstraints(l, c);
         add(l);
-
-        c.gridwidth = GridBagConstraints.REMAINDER; 
-
+        
+        c.gridwidth = GridBagConstraints.REMAINDER;
+        
         l = new Label("Output:");
-
+        
         gbl.setConstraints(l, c);
         add(l);
-
-        c.gridwidth = GridBagConstraints.RELATIVE; 
-
+        
+        c.gridwidth = GridBagConstraints.RELATIVE;
+        c.weightx = 1.0;
+        c.weighty = 1.0;
+        
         gbl.setConstraints(input, c);
         add(input);
-       
-        c.gridwidth = GridBagConstraints.REMAINDER; 
-
+        
+        c.gridwidth = GridBagConstraints.REMAINDER;
+        
         gbl.setConstraints(output, c);
         add(output);
-
+        
+        c.weightx = 0.0;
+        c.weighty = 0.0;
+        
         l = new Label("Errors:");
-
+        
         gbl.setConstraints(l, c);
         add(l);
-
+        
+        c.weightx = 1.0;
+        c.weighty = 0.25;
+        
         gbl.setConstraints(error, c);
-        add(error);      
-
+        add(error);
+        
         process.addActionListener(new ActionListener() {
-                public void actionPerformed(ActionEvent e) {
-                    process();
-                }
-            });
-
+            public void actionPerformed(ActionEvent e) {
+                process();
+            }
+        });
+        
         clear.addActionListener(new ActionListener() {
-                public void actionPerformed(ActionEvent e) {
-                    error.setText("");
-                    output.setText("");
-                    input.setText("");
-                    
-                }
-            });
-
+            public void actionPerformed(ActionEvent e) {
+                error.setText("");
+                output.setText("");
+                input.setText("");
+                input.requestFocus();
+            }
+        });
+        
+        reset.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                error.setText("");
+                output.setText("");
+                input.setText(sample);
+            }
+        });
+        
         close.addActionListener(new ActionListener() {
-                public void actionPerformed(ActionEvent e) {
-                    dispose();
-                }
-            });
-
+            public void actionPerformed(ActionEvent e) {
+                dispose();
+            }
+        });
+        
         addWindowListener(new WindowAdapter() {
-                public void windowClosing(WindowEvent e) {
-                    dispose();
-                }
-            });
+            public void windowClosing(WindowEvent e) {
+                dispose();
+            }
+        });
         
         URL source = getClass().getResource("demo.template");
         StringBuffer buffer = new StringBuffer();
-
+        
         try {
             Reader in = new BufferedReader
-                (new InputStreamReader(source.openStream(), "UTF-8"));           
+                    (new InputStreamReader(source.openStream(), "UTF-8"));
             
             for (int ch = in.read(); ch >= 0; ch = in.read()) {
                 buffer.append((char)ch);
             }
-
+            
             in.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
         
-        input.setText(buffer.toString());
+        sample = buffer.toString();
+        
+        input.setText(sample);
     }
-
+    
     private void process() {
         String s = input.getText();
         
-        TemplateReader tr = new TemplateReader
-            (new LineNumberReader(new StringReader(s)), "input");
+        TemplateReader tr = new TemplateReader(new StringReader(s), "input");
         TemplateParser tp = new TemplateParser();
         StringWriter sw = new StringWriter();
-
+        
         error.setText("");
-
+        
         try {
             tp.parse(tr, sw);
         } catch (Exception e) {
             error.setText(e.toString());
+            e.printStackTrace();
         }
-
+        
         output.setText(sw.toString());
     }
 }
diff --git a/src/kryshen/tema/demo/Hello.java b/src/kryshen/tema/demo/Hello.java
--- a/src/kryshen/tema/demo/Hello.java
+++ b/src/kryshen/tema/demo/Hello.java
@@ -1,21 +1,23 @@
 /*
- *  Copyright (C) 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This file is part of Tema.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
+ *  GNU Lesser General Public License for more details.
  *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: Hello.java,v 1.2 2006/12/14 14:39:24 mikhail Exp $
+ *  $Id: Hello.java,v 1.9 2008/02/16 17:56:34 mikhail Exp $
  */
 
 package kryshen.tema.demo;
@@ -27,7 +29,7 @@
 /**
  * Example function implementation.
  * 
- * @author Mikhail A. Kryshen
+ * @author Mikhail Kryshen
  */
 public class Hello extends Function {
     public Hello() {}
@@ -36,8 +38,6 @@
         throws IOException, TemplateException {
         
         out.write("Hello, ");
-        fdp.parseData(out);
-
-        return 1;
+        return fdp.parseData(out);
     }
 }
diff --git a/src/kryshen/tema/functions/Control.java b/src/kryshen/tema/functions/Control.java
new file mode 100644
--- /dev/null
+++ b/src/kryshen/tema/functions/Control.java
@@ -0,0 +1,166 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: Control.java,v 1.18 2008/02/19 16:21:00 mikhail Exp $
+ */
+
+package kryshen.tema.functions;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import kryshen.tema.Function;
+import kryshen.tema.FunctionDataParser;
+import kryshen.tema.TemplateException;
+import kryshen.tema.TemplateParser;
+import kryshen.tema.io.NullWriter;
+import kryshen.tema.io.TemplateReader;
+
+/**
+ * Logical and conditional functions.
+ *
+ * @author Mikhail Kryshen
+ */
+public class Control {
+    
+    /**
+     * Copy input to output and return zero value.
+     */
+    public static final Function FALSE =
+            new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            if (fdp.hasMoreData())
+                fdp.parseData(out);
+            
+            return 0;
+        }
+    };
+
+    /**
+     * Copy input to output and return non-zero value.
+     */
+    public static final Function TRUE =
+            new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            if (fdp.hasMoreData())
+                fdp.parseData(out);
+            
+            return 1;
+        }
+    };
+    
+    /**
+     * Outputs it's data if it has non-zero value.
+     */
+    public static final Function OPTIONAL = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            StringWriter sw = new StringWriter();
+            int s = fdp.parseData(sw);
+            sw.close();
+            String data = sw.toString();
+            
+            if (s != 0) {
+                out.write(data);
+                return 1;
+            } else {
+                return 0;
+            }
+        }
+    };
+    
+    /**
+     * Logical negation (applied to the return value).
+     */
+    public static final Function NOT = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            return (fdp.parseData(out) == 0) ? 1 : 0;
+        }
+    };
+    
+    public static final Function IF = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            int value = fdp.parseNextArg(NullWriter.INSTANCE);
+            
+            if (value != 0) {
+                return fdp.parseData(out);
+            }
+            
+            return 0;
+        }
+    };
+    
+    public static final Function IF_ELSE = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            int value = fdp.parseNextArg(NullWriter.INSTANCE);
+            
+            if (value != 0) {
+                return fdp.parseNextArg(out);
+            } else {
+                fdp.skipNextArg();
+                return fdp.parseData(out);
+            }
+        }
+    };
+    
+    /**
+     * Outputs it's evaluated data while the evaluation result is non-zero.
+     */
+    public static final Function WHILE = new Function() {
+        public int invoke(FunctionDataParser fdp, final Writer out)
+        throws IOException, TemplateException {
+            
+            String data = fdp.getData();
+            StringReader dataReader = new StringReader(data);
+            
+            TemplateParser parser = fdp.getTemplateParser();
+            
+            int r = 0; // summarized return value
+            int e = 0; // evaluation value
+            
+            while (true) {
+                StringWriter sw = new StringWriter();
+                
+                e = parser.parse(new TemplateReader(dataReader), sw);
+                
+                if (e == 0)
+                    break;
+                
+                r += Math.abs(e);
+                out.write(sw.toString());
+                dataReader.reset();
+            }
+            
+            dataReader.close();
+            return r;
+        }
+    };
+}
diff --git a/src/kryshen/tema/functions/Database.java b/src/kryshen/tema/functions/Database.java
new file mode 100644
--- /dev/null
+++ b/src/kryshen/tema/functions/Database.java
@@ -0,0 +1,208 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: Database.java,v 1.12 2008/02/17 23:58:01 mikhail Exp $
+ */
+
+package kryshen.tema.functions;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.List;
+import kryshen.tema.Context;
+import kryshen.tema.Function;
+import kryshen.tema.FunctionDataParser;
+import kryshen.tema.TemplateException;
+import kryshen.tema.TemplateParser;
+import kryshen.tema.io.TemplateReader;
+
+/**
+ * Data access functions.
+ *
+ * @author Mikhail Kryshen
+ */
+public class Database {   
+    /* db_connect:connection_name resource */
+    public static final Function CONNECT = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String arg0 = fdp.getNextArg();
+            String data = fdp.getData();
+            
+            Connection connection;
+            
+            try {
+                connection = DriverManager.getConnection(data);
+            } catch (SQLException e) {
+                throw new TemplateException
+                        (e.getMessage(), e, fdp.getTemplateReader());
+            }
+            
+            fdp.getContext().set(arg0, connection);
+            
+            out.write(arg0);
+            return 1;
+        }
+    };
+    
+    /* db_prepare:qury_name connection sql_statement */
+    public static final Function PREPARE = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String arg0 = fdp.getNextArg(); // resulting variable
+            String arg1 = fdp.getNextArg(); // connection
+            String data = fdp.getData();    // statement
+            
+            Connection connection;
+            PreparedStatement statement;
+            
+            try {
+                connection = (Connection) fdp.getContext().get(arg1);
+                statement = connection.prepareStatement(data);
+            } catch (ClassCastException e) {
+                throw new TemplateException
+                        (e.getMessage(), e, fdp.getTemplateReader());
+            } catch (SQLException e) {
+                throw new TemplateException
+                        (e.getMessage(), e, fdp.getTemplateReader());
+            }
+            
+            fdp.getContext().set(arg0, statement);
+            
+            out.write(arg0);
+            return 1;
+        }
+    };
+    
+    /* db_query:sql_query template arg1 arg2 ... argN */
+    public static final Function QUERY = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String query = fdp.getNextArg();
+            String template = fdp.getNextArg();
+            List<String> args = fdp.getArgs();
+            
+            TemplateParser tp = fdp.getTemplateParser();
+            Context context = fdp.getContext();
+            
+            try {
+                PreparedStatement statement =
+                        (PreparedStatement) context.get(query);
+                return query(template, fdp.getTemplateReader(), 
+                        statement, args, tp, out);
+            } catch (ClassCastException e) {
+                throw new TemplateException
+                        (e.getMessage(), e, fdp.getTemplateReader());
+            } catch (SQLException e) {
+                throw new TemplateException
+                        (e.getMessage(), e, fdp.getTemplateReader());
+            }
+        }
+    };
+    
+    /**
+     * Process database query.
+     *
+     * @param template template to fill with data.
+     * @param parentReader parent TemplateReader.
+     * @param ps query statement to execute.
+     * @param args list of query parameters.
+     * @param superParser invoking object.
+     * @param out Writer to output processed data.
+     *
+     * @return number of processed rows in query result.
+     */
+    private static int query(String template,
+            TemplateReader parentReader,
+            PreparedStatement ps,
+            List<String> args,
+            TemplateParser superParser,
+            Writer out)
+            throws IOException, SQLException, TemplateException {
+        
+        int i = 1;
+        
+        for (String arg : args) {
+            ps.setString(i++, arg);
+        }
+        
+        ResultSet resultSet = ps.executeQuery();
+        ResultSetMetaData metaData = resultSet.getMetaData();
+        int columnCount = metaData.getColumnCount();
+        
+        String[] names = new String[columnCount];
+        
+        for (int j = 0; j < columnCount; j++) {
+            names[j] = metaData.getColumnName(j + 1);
+        }
+        
+        final TemplateParser p = new TemplateParser(superParser);
+        final HashMap<String, Object> fields = new HashMap<String, Object>();
+        
+        p.getContext().set("db", new Function() {
+            public int invoke(FunctionDataParser fdp,
+                    final Writer out)
+                    throws IOException, TemplateException {
+                
+                String name = fdp.getData();
+                Object value = fields.get(name);
+                
+                return p.parseValue(value, out);
+            }
+        });
+        
+        Reader input = new StringReader(template);
+        
+        for (i = 1; resultSet.next(); i++) {
+            
+            for (int j = 0; j < columnCount; j++) {
+                Object value = resultSet.getObject(j + 1);
+                if (resultSet.wasNull()) value = null;
+                
+                fields.put(names[j], value);
+            }
+            
+            p.getContext().set("db_row", i);
+            
+            TemplateReader tr = new TemplateReader(input, parentReader);
+            
+            p.parse(tr, out);
+
+            input.reset();            
+            fields.clear();
+        }
+        
+        resultSet.close();
+        input.close();
+        ps.clearParameters();
+        return i - 1;
+    }
+}
diff --git a/src/kryshen/tema/functions/Define.java b/src/kryshen/tema/functions/Define.java
--- a/src/kryshen/tema/functions/Define.java
+++ b/src/kryshen/tema/functions/Define.java
@@ -1,84 +1,126 @@
 /*
- *  Copyright (C) 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This file is part of Tema.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
+ *  GNU Lesser General Public License for more details.
  *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: Define.java,v 1.7 2006/12/14 14:35:00 mikhail Exp $
+ *  $Id: Define.java,v 1.25 2008/02/17 23:58:01 mikhail Exp $
  */
 
 package kryshen.tema.functions;
 
-import java.io.*;
-import java.util.*;
-
-import kryshen.tema.*;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import kryshen.tema.Context;
+import kryshen.tema.Function;
+import kryshen.tema.FunctionDataParser;
+import kryshen.tema.TemplateException;
+import kryshen.tema.TemplateParser;
+import kryshen.tema.io.TemplateReader;
 
 /**
  * Define function implementation.
  *
- * @author Mikhail A. Kryshen
+ * @author Mikhail Kryshen
  */
 public class Define extends Function {
     public static final Function DEFINE = new Define();
-
+    
     public Define() {
     }
-
+    
     public int invoke(FunctionDataParser fdp, Writer out)
-        throws IOException, TemplateException {
+    throws IOException, TemplateException {
         
-        final String arg0 = fdp.getNextArg();
-        final String data = fdp.getData();
-
+        StringWriter nameWriter = new StringWriter();
+        int r = fdp.parseNextArg(nameWriter);
+        
+        if (r == 0)
+            return 0;                
+        
+        StringWriter dataWriter = new StringWriter();
+        r = fdp.parseData(dataWriter);
+        
+        if (r == 0)
+            return 0;
+               
+        final String name = nameWriter.toString();
+        final String data = dataWriter.toString();
+        
         Function newFunction = new Function() {
-                public int invoke(final FunctionDataParser fdp, final Writer out)
-                    throws IOException, TemplateException {
-		    
-                    TemplateParser functionParser = 
-                        new TemplateParser(fdp.getTemplateParser());
-                    
-                    functionParser.registerFunction
-			("nextarg", new Function() {
-				public int invoke(FunctionDataParser unusedFdp, 
-						  final Writer out)
-				    throws IOException, TemplateException {
-				    
-				    return fdp.parseNextArg(out);
-				}
-			    });
-		    
-                    functionParser.registerFunction
-			("data",new Function() {
-				public int invoke(FunctionDataParser unusedFdp, 
-						  final Writer out)
-				    throws IOException, TemplateException {
-				    
-				    return fdp.parseData(out);
-				}
-			    });
-		    
-                    TemplateReader dataReader = new TemplateReader
-                        (new StringReader(data), fdp.getTemplateReader());
-
-                    return functionParser.parse(dataReader, out);
-                }
-            };
-	
-        fdp.getTemplateParser().registerFunction(arg0, newFunction);
-	
-        out.write(arg0);
-        return 1;
+            public int invoke(final FunctionDataParser fdp1, final Writer out)
+            throws IOException, TemplateException {
+                
+                final TemplateParser functionParser =
+                        new TemplateParser(fdp1.getTemplateParser());
+                
+                final Context functionContext = 
+                        functionParser.getContext();                
+                
+                functionContext.set
+                        ("next_arg", new Function() {
+                    public int invoke(FunctionDataParser unusedFdp,
+                            final Writer out)
+                            throws IOException, TemplateException {
+                        
+                        if (fdp1.hasMoreData())
+                            return fdp1.parseNextArg(out);
+                        
+                        return 0;
+                    }
+                });
+                
+                functionContext.set
+                        ("data", new Function() {
+                    public int invoke(FunctionDataParser unusedFdp,
+                            final Writer out)
+                            throws IOException, TemplateException {
+                        
+                        return fdp1.parseData(out);
+                    }
+                });
+                
+                functionContext.set
+                        ("has_more_data", new Function() {
+                    public int invoke(FunctionDataParser unusedFdp,
+                            final Writer out)
+                            throws IOException, TemplateException {
+                        
+                        if (fdp1.hasMoreData()) {
+                            out.write("true");
+                            return 1;
+                        }
+                        
+                        out.write("false");
+                        return 0;
+                    }
+                });
+                
+                TemplateReader dataReader = new TemplateReader
+                        (new StringReader(data), fdp1.getTemplateReader());
+                
+                return functionParser.parse(dataReader, out);
+            }
+        };
+        
+        fdp.getContext().set(name, newFunction);
+        
+        out.write(name);
+        return r;
     }
 }
diff --git a/src/kryshen/tema/functions/IO.java b/src/kryshen/tema/functions/IO.java
--- a/src/kryshen/tema/functions/IO.java
+++ b/src/kryshen/tema/functions/IO.java
@@ -1,133 +1,168 @@
 /*
- *  Copyright (C) 2005, 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This file is part of Tema.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
+ *  GNU Lesser General Public License for more details.
  *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: IO.java,v 1.4 2006/12/14 15:59:49 mikhail Exp $
+ *  $Id: IO.java,v 1.19 2008/02/19 16:21:00 mikhail Exp $
  */
 
 package kryshen.tema.functions;
 
-import java.io.*;
-import java.util.*;
-
-import kryshen.tema.*;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import kryshen.tema.Function;
+import kryshen.tema.FunctionDataParser;
+import kryshen.tema.Tema;
+import kryshen.tema.TemplateException;
+import kryshen.tema.io.TemplateReader;
 
 /**
  * I/O functions.
  */
 public class IO {
-    public static final Function COPY =
-        new Function() {
-           public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-
-                String arg0 = fdp.getNextArg();
-                String arg1 = fdp.getNextArg();
-
-                File source = new File
-                    (Tema.getProperty("resource_base"), arg0);
-                File dest = new File(arg1);
-                
-                try {
-                    copyFile(source, dest);
-                } catch (IOException e) {
-                    System.err.println(e);
-                    return 0;
-                }
-                out.write(arg1);
-                return 1;
+    /**
+     * Copy files.
+     */
+    public static final Function COPY = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String arg0 = fdp.getNextArg();
+            String arg1 = fdp.getNextArg();
+            
+            File source = fdp.createFile(arg0);
+            File dest = fdp.createFile(arg1);
+            
+            try {
+                copyFile(source, dest);
+            } catch (IOException e) {
+                System.err.println(e);
+                return 0;
             }
-        };
-
-    public static final Function WRITE =
-        new Function() {
-           public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-
-                String arg0 = fdp.getNextArg();
-
-		System.err.println("Writing " + arg0 + "...");
-
-                Writer fw;
-
-                try {
-		    fw = Tema.createFileWriter(arg0);
-                } catch (IOException e) {
-                    System.err.println(e);
-                    //throw new TemplateException(e.getMessage(), e, in);
-                    return 0;
-                }
-
-                try {
-		    fdp.parseData(fw);
-                } finally {
-                    fw.close();
-                }
-
-                out.write(arg0);
-
-		System.err.println("Saved " + arg0 + ".");
-                return 1;
+            out.write(arg1);
+            return 1;
+        }
+    };
+    
+    /**
+     * Write data to file.
+     */
+    public static final Function WRITE = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String arg0 = fdp.getNextArg();
+            
+            System.err.println("Writing " + arg0 + "...");
+            
+            Writer fw;
+            
+            try {
+                fw = Tema.createFileWriter(fdp.createFile(arg0));
+            } catch (IOException e) {
+                System.err.println(e);
+                //throw new TemplateException(e.getMessage(), e, in);
+                return 0;
             }
-        };
-
-    public static final Function READ =
-        new Function() {
-           public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-
-	       String filename = fdp.getData();
-	       String file;
-	       
-                try {
-                    file = Tema.readFile(new File(filename));
-                } catch (IOException e) {
-                    System.err.println(e);
-                    //throw new TemplateException(e.getMessage(), e, in);
-                    return 0;
-                }
-		
-                out.write(file);
-                return 1;
+            
+            try {
+                fdp.parseData(fw);
+            } finally {
+                fw.close();
             }
-        };
-
-    public static final Function INCLUDE =
-        new Function() {
-           public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-                
-                String filename = fdp.getData();
-                
-                TemplateReader fin;
-
-                try {
-                    fin = Tema.createTemplateReader(new File(filename));
-                } catch (IOException e) {
-                    throw new TemplateException(e.getMessage(), e,
-                                                fdp.getTemplateReader());
-                }
-
-                int r = fdp.getTemplateParser().parse(fin, out);
-
-                fin.close();
-                return r;
+            
+            out.write(arg0);
+            
+            return 1;
+        }
+    };
+    
+    /**
+     * Read from file.
+     */
+    public static final Function READ = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String filename = fdp.getData();
+            String file;
+            
+            try {
+                readFile(fdp.createFile(filename), out);
+            } catch (IOException e) {
+                System.err.println(e);
+                //throw new TemplateException(e.getMessage(), e, in);
+                return 0;
             }
-        };
-
+            
+            return 1;
+        }
+    };
+    
+    /**
+     * Evaluate the code read from file.
+     */
+    public static final Function INCLUDE = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String filename = fdp.getData();
+            
+            TemplateReader fin;
+            
+            try {
+                fin = Tema.createTemplateReader(fdp.createFile(filename));
+            } catch (IOException e) {
+                throw new TemplateException(e.getMessage(), e,
+                        fdp.getTemplateReader());
+            }
+            
+            int r = fdp.getTemplateParser().parse(fin, out);
+            
+            fin.close();
+            return r;
+        }
+    };
+    
+    /**
+     * Create path from base path and file name.
+     */
+    public static final Function FILE = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String base = fdp.getNextArg();
+            String name = fdp.getNextArg();                        
+            
+            if (fdp.getLastReturnCode() == 0)
+                return 0;                        
+                       
+            out.write((new File(fdp.createFile(base), name)).getPath());
+            
+            return fdp.getLastReturnCode();
+        }
+    };
+    
     /**
      * Update file (copy if newer).
      *
@@ -137,27 +172,51 @@
      * @trows IOException on copying error.
      */
     private static void copyFile(File src, File dest) throws IOException {
-	System.err.print("Copying " + src + "... ");
-	
-	if (src.lastModified() < dest.lastModified()) {
-	    System.err.println(dest + " is up to date.");
-	    return;
-	}
-
-	File parent = dest.getParentFile();
-	if (parent != null) parent.mkdirs();
-
+        System.err.print("Copying " + src + "... ");
+        
+        if (src.lastModified() < dest.lastModified()) {
+            System.err.println(dest + " is up to date.");
+            return;
+        }
+        
+        File parent = dest.getParentFile();
+        if (parent != null) parent.mkdirs();
+        
         InputStream in = new FileInputStream(src);
         OutputStream out = new FileOutputStream(dest);
         
+        copy(in, out);
+        
+        in.close();
+        out.close();
+    }
+    
+    private static void copy(InputStream in, OutputStream out)
+    throws IOException {
+        
         byte[] buffer = new byte[1024];
         int len;
+        
         while ((len = in.read(buffer)) > 0) {
             out.write(buffer, 0, len);
         }
-        in.close();
-        out.close();
-
-	System.err.println("saved " + dest + ".");
+    }
+    
+    private static void copy(Reader in, Writer out)
+    throws IOException {
+        
+        char[] buffer = new char[1024];
+        int len;
+        
+        while ((len = in.read(buffer)) > 0) {
+            out.write(buffer, 0, len);
+        }
+    }
+    
+    private static void readFile(File src, Writer out)
+    throws IOException {
+        
+        Reader in = Tema.createFileReader(src);
+        copy(in, out);
     }
 }
diff --git a/src/kryshen/tema/functions/ImageConverter.java b/src/kryshen/tema/functions/ImageConverter.java
--- a/src/kryshen/tema/functions/ImageConverter.java
+++ b/src/kryshen/tema/functions/ImageConverter.java
@@ -1,21 +1,23 @@
 /*
- *  Copyright (C) 2005, 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This file is part of Tema.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
+ *  GNU Lesser General Public License for more details.
  *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: ImageConverter.java,v 1.4 2006/12/14 19:44:32 mikhail Exp $
+ *  $Id: ImageConverter.java,v 1.13 2008/02/16 17:56:34 mikhail Exp $
  */
 
 package kryshen.tema.functions;
@@ -33,11 +35,11 @@
 /**
  * Convert images to specified format.
  *
- * @author Mikhail A. Kryshen
+ * @author Mikhail Kryshen
  */
 public class ImageConverter extends Function {    
 
-    public static final Function IMAGE = new ImageConverter();
+    public static final Function CONVERT_IMAGE = new ImageConverter();
 
     public int invoke(FunctionDataParser fdp, Writer out)
         throws IOException, TemplateException {
@@ -49,14 +51,11 @@
             Integer.parseInt(fdp.getNextArg()) : -1;
         int arg4 = fdp.hasMoreData() ? 
             Integer.parseInt(fdp.getNextArg()) : -1;
-        
-        File source = new File
-            (Tema.getProperty("resource_base"), arg0);
-        
+
         try {
             convert
-                (source,                  /* source file */
-                 new File(arg1),          /* dest file */
+                (fdp.createFile(arg0),    /* source file */
+                 fdp.createFile(arg1),    /* dest file */
                  arg2,                    /* format */
                  arg3,                    /* max width */
                  arg4);                   /* max height */
diff --git a/src/kryshen/tema/functions/Logics.java b/src/kryshen/tema/functions/Logics.java
deleted file mode 100644
--- a/src/kryshen/tema/functions/Logics.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- *  Copyright (C) 2006 Mikhail A. Kryshen
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- *  $Id: Logics.java,v 1.6 2006/12/14 14:39:26 mikhail Exp $
- */
-
-package kryshen.tema.functions;
-
-import java.io.*;
-import java.util.*;
-
-import kryshen.tema.*;
-
-/**
- * Logical and conditional functions.
- *
- * @author Mikhail A. Kryshen
- */
-public class Logics {
-    public static final Function FALSE =
-        new Function() {
-            public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-		
-		fdp.parseData(out);
-                return 0;
-            }
-        };
-
-    public static final Function TRUE =
-        new Function() {
-            public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-		
-		fdp.parseData(out);
-                return 1;
-            }
-        };
-
-    /**
-     * Outputs it's data it has non-zero value.
-     */
-    public static final Function OPTIONAL =
-        new Function() {
-            public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-
-                StringWriter sw = new StringWriter();
-                int s = fdp.parseData(sw);
-                sw.close();
-                String data = sw.toString();
-
-                if (s != 0) {
-                    out.write(data);
-                    return 1;
-                } else {
-                    return 0;
-                }
-            }
-        };
-}
diff --git a/src/kryshen/tema/functions/ReplaceWriter.java b/src/kryshen/tema/functions/ReplaceWriter.java
deleted file mode 100644
--- a/src/kryshen/tema/functions/ReplaceWriter.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- *  Copyright (C) 2006 Mikhail A. Kryshen
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- *  $Id: ReplaceWriter.java,v 1.2 2006/12/14 14:39:26 mikhail Exp $
- */
-
-package kryshen.tema.functions;
-
-import java.io.Writer;
-import java.io.FilterWriter;
-import java.io.IOException;
-
-import java.util.LinkedList;
-import java.util.Iterator;
-
-/**
- * FilterWriter which replaces characters with escape strings.
- *
- * @author Mikhail A. Kryshen
- */
-class ReplaceWriter extends FilterWriter {
-    private String[] patterns;
-    private String[] replaces;
-
-    private String prefix;
-    private String postfix;
-
-    private int maxPatternLength = 0;
-
-    /** Have this Writer processed any data? */
-    private boolean processedData = false;
-
-    private StringBuffer buffer = new StringBuffer();
-
-    public ReplaceWriter(Writer w, String pattern, String replace) {
-        this(w, new String[]{pattern}, new String[]{replace});
-    }
-
-    public ReplaceWriter(Writer w, String pattern, String replace,
-                         String prefix, String postfix) {
-
-        this(w, new String[]{pattern}, new String[]{replace}, 
-             prefix, postfix);
-    }
-
-    public ReplaceWriter(Writer w, String[] patterns, String[] replaces) {
-        this(w, patterns, replaces, null, null);
-    }
-
-    public ReplaceWriter(Writer w, String[] patterns, String[] replaces, 
-                         String prefix, String postfix) {
-	super(w);
-        
-	this.patterns = patterns;
-	this.replaces = replaces;
-        
-        this.prefix = prefix;
-        this.postfix = postfix;
-        
-	for (int i = 0; i < patterns.length; i++) {
-	    int length = patterns[i].length();
-	    if (length > maxPatternLength)
-		maxPatternLength = length;
-	}
-    }
-
-    private boolean processBuffer(int minLength) throws IOException {
-        if (buffer.length() == 0) return false;
-
-        if (!processedData) {
-            if (prefix != null)
-                super.write(prefix, 0, prefix.length());
-            processedData = true;
-        }
-
-        int k;
-	for (k = 0; minLength + k < buffer.length(); k++) {
-	    for (int i = 0; i < patterns.length; i++) {
-		String pattern = patterns[i];
-		int length = pattern.length();
-
-                if (length + k > buffer.length())
-                    continue;
-
-		boolean match = true;
-
-		for (int j = 0; j < length; j++) {
-		    if (pattern.charAt(j) != buffer.charAt(j + k)) {
-			match = false;
-                        break;
-		    }
-		}
-
-		if (match) {
-                    super.write(buffer.substring(0, k), 0, k);
-                    buffer.delete(0, k + length);
-                    super.write(replaces[i], 0, replaces[i].length());
-		    return true;
-		}
-	    }
-	}
-
-        super.write(buffer.substring(0, k), 0, k);
-        buffer.delete(0, k);
-
-        return false;
-    }
-
-    public void write(int c) throws IOException {
-	buffer.append((char)c);
-	processBuffer(maxPatternLength);
-    }
-
-    public void write(char[] cbuf, int off, int len) throws IOException {
-	for (int i = off; i < off + len; i++) {
-	    buffer.append(cbuf[i]);
-	}
-	processBuffer(maxPatternLength);
-    }
-
-    public void write(String str, int off, int len) throws IOException {
-        buffer.append(str.substring(off, off + len));
-	processBuffer(maxPatternLength);
-    }
-
-    public void finish() throws IOException {
-        while (processBuffer(0));
-
-//         super.write(buffer.toString(), 0, buffer.length());
-//         buffer.delete(0, buffer.length());
-
-        if (processedData && postfix != null)
-            super.write(postfix, 0, postfix.length());
-    }
-
-    public void close() throws IOException {
-        finish();
-        super.close();
-    }
-}
diff --git a/src/kryshen/tema/functions/Standard.java b/src/kryshen/tema/functions/Standard.java
--- a/src/kryshen/tema/functions/Standard.java
+++ b/src/kryshen/tema/functions/Standard.java
@@ -1,258 +1,259 @@
 /*
- *  Copyright (C) 2005, 2006 Mikhail A. Kryshen
+ *  Copyright 2006-2008 Mikhail Kryshen
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This file is part of Tema.
  *
- *  This program is distributed in the hope that it will be useful,
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
+ *  GNU Lesser General Public License for more details.
  *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
  *
- *  $Id: Standard.java,v 1.5 2006/12/14 14:39:26 mikhail Exp $
+ *  $Id: Standard.java,v 1.34 2008/02/19 17:32:17 mikhail Exp $
  */
 
 package kryshen.tema.functions;
 
-import java.io.*;
-import java.util.*;
-import java.net.*;
-
-import java.sql.SQLException;
-import java.sql.PreparedStatement;
-
-import kryshen.tema.*;
+import java.io.IOException;
+import java.io.Writer;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+import kryshen.tema.Context;
+import kryshen.tema.Function;
+import kryshen.tema.FunctionDataParser;
+import kryshen.tema.Tema;
+import kryshen.tema.TemplateException;
+import kryshen.tema.io.NullWriter;
 
 /**
  * Standard TEMA functions.
  */
 public class Standard {
-    public static final Function SET =
-        new Function() {
-            public int invoke(FunctionDataParser fdp, Writer out)
+    
+    /**
+     * Output Tema version.
+     */
+    public static final Function TEMA = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            out.write(Tema.TITLE + " " + Tema.VERSION
+                    /*+ " by " + Tema.AUTHOR*/);
+            return 1;
+        }
+    };
+    
+    /**
+     * Define static variable.
+     */
+    public static final Function SET = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String name = fdp.getNextArg();
+            
+            if (fdp.getLastReturnCode() == 0)
+                return 0;
+            
+            String data = fdp.getData(out);
+            fdp.getContext().set(name, data);
+            
+            return fdp.getLastReturnCode();
+        }
+    };
+    
+    /**
+     * Recursively remove definition.
+     */
+    public static final Function UNSET = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            int r = 0;
+            
+            while (fdp.hasMoreData()) {
+                String name = fdp.getNextArg();
+                
+                if (fdp.getLastReturnCode() == 0)
+                    continue;
+                
+                if (fdp.getContext().unset(name))
+                    r++;
+            }
+            
+            return r;
+        }
+    };
+    
+    /**
+     * Export variable to the global (outermost) context.
+     */
+    public static final Function EXPORT = new Function() {
+        public int invoke(FunctionDataParser fdp,
+                final Writer out)
                 throws IOException, TemplateException {
-
-                String arg0 = fdp.getNextArg();
-                String arg1 = fdp.getData();
+            
+            String name = fdp.getNextArg();
+            
+            if (fdp.getLastReturnCode() == 0)
+                return 0;
+            
+            // Set and export.
+            if (fdp.hasMoreData()) {
+                Object value = fdp.getData(out);
                 
-                fdp.getTemplateParser().setValue(arg0, arg1);
-                
-		out.write(arg0);
-                return 1;
+                fdp.getContext().export(name, value);
+                return fdp.getLastReturnCode();
             }
-        };
-
-    /* prepare:qury_name sql_statement */
-    public static final Function PREPARE =
-        new Function() {
-            public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-
-                String arg0 = fdp.getNextArg();
-                String data = fdp.getData();
-
-		PreparedStatement statement;
-
-		try {
-		    statement = Tema.getDbConnection()
-                        .prepareStatement(data);
-		} catch (SQLException e) {
-		    throw new TemplateException
-                        (e.getMessage(), e, fdp.getTemplateReader());
-		}
-                
-                fdp.getTemplateParser().setValue(arg0, statement);   
-             
-		out.write(arg0);
-                return 1;
-            }
-        };
-
-    /* query:sql_query template arg1 arg2 ... argN */
-    public static final Function QUERY =
-        new Function() {
-           public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-                
-                String arg0 = fdp.getNextArg();
-                String arg1 = fdp.getNextArg();
-		List<String> args = fdp.getArgs();				
-
-		TemplateParser tp = fdp.getTemplateParser();
-
-                TemplateReader tr = 
-                    new TemplateReader(new StringReader(arg1),
-                                       fdp.getTemplateReader());
-
-		try {
-		    return Tema.query
-			(tr, (PreparedStatement)tp.getValue(arg0),
-			 args, tp, out);
-		} catch (SQLException e) {
-		    throw new TemplateException
-                        (e.getMessage(), e, fdp.getTemplateReader());
-		}
-            }
-        };
-
-    public static final Function REPLACE =
-        new Function() {
-	    public int invoke(FunctionDataParser fdp, Writer out)
-		throws IOException, TemplateException {		
-
-                String arg0 = fdp.getNextArg();
-                String arg1 = fdp.getNextArg();
-
-                ReplaceWriter rw = new ReplaceWriter(out, arg0, arg1);
-
-                int ret = fdp.parseData(rw);
-                rw.finish();
-                return ret;
-            }
-        };
-
-    public static final Function LOAD =
-
-        new Function() {
-            public int invoke(FunctionDataParser fdp, Writer out)
-                throws IOException, TemplateException {
-                
-                final String name = fdp.getNextArg();
-                final String className = fdp.getNextArg();
-
-                List<URL> urls = new ArrayList<URL>(1);
-
-                while (fdp.hasMoreData()) {
-                    urls.add(new URL(fdp.getNextArg()));                    
-                }
-                
-                ClassLoader loader = this.getClass().getClassLoader();
-
-                if (urls.size() > 0) {
-                    loader = new URLClassLoader
-                        (urls.toArray(new URL[0]), loader);
-                }                
-
-                Class<Function> functionClass;
-                Function function;
-
-                try {
-                    functionClass = loadFunctionClass(loader, className);
-                } catch (ClassNotFoundException e) {
-                    throw new TemplateException
-                        ("Class not found", e, fdp.getTemplateReader());
-                } catch (ClassCastException e) {
-                    throw new TemplateException
-                        ("Not a function class", e, fdp.getTemplateReader());
-                }
-                
-                try {
-                    function = functionClass.newInstance();
-                } catch (InstantiationException e) {
-                    throw new TemplateException
-                        ("Could not load class", e, fdp.getTemplateReader());
-                } catch (IllegalAccessException e) {
-                    throw new TemplateException
-                        ("Could not load class", e, fdp.getTemplateReader());
-                }
-
-                fdp.getTemplateParser().registerFunction(name, function);
-                                
-
+            
+            // Only export.
+            if (fdp.getContext().export(name)) {
                 out.write(name);
                 return 1;
             }
-        };
+            
+            return 0;
+        }
+    };
+    
+    /**
+     * Copy macro definitions.
+     */
+    public static final Function ASSIGN = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+                throws IOException, TemplateException {
 
-    public static final Function NULL_OUTPUT =
-        new Function() {
-	    public int invoke(FunctionDataParser fdp, Writer out)
-		throws IOException, TemplateException {		
+            String name1 = fdp.getNextArg();
 
-		/* Write nothing. */
-		return fdp.parseData(new Writer() {
-                    public void close() {}
-                    public void flush() {}
-                    public void write(char[] cbuf, int off, int len) {}
-                });
+            if (fdp.getLastReturnCode() == 0)
+                return 0;
+            
+            String name2 = fdp.getData();
+            
+            if (fdp.getLastReturnCode() == 0)
+                return 0;
+
+            Context context = fdp.getContext();
+            Object value = context.get(name2);
+            
+            if (value == null)
+                return 0;
+            
+            context.set(name1, value);
+            
+            return 1;
+        }
+    };
+    
+    /**
+     * Invoke function specified by name.
+     */
+    public static final Function INVOKE = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String name = fdp.getNextArg();
+            
+            if (fdp.getLastReturnCode() == 0)
+                return 0;
+            
+            return fdp.getTemplateParser().invoke(name, fdp, out);            
+        }
+    };
+    
+    /**
+     * Create definiton as a new Java object.
+     */
+    public static final Function LOAD = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            final String name = fdp.getNextArg();
+            final String className = fdp.getNextArg();
+            
+            List<URL> urls = new ArrayList<URL>(1);
+            
+            while (fdp.hasMoreData()) {
+                urls.add(new URL(fdp.getNextArg()));
             }
-        };
-
-    public static final Function XML_ESCAPE =
-        new Function() {
-	    public int invoke(FunctionDataParser fdp, Writer out)
-		throws IOException, TemplateException {		
-
-                final String[] chars = {"&", "<", ">", "`", "\""};
-                final String[] escape = {"&", "<", ">", "'", """};
-
-                ReplaceWriter rw = new ReplaceWriter(out, chars, escape);
-
-                int ret = fdp.parseData(rw);
-                rw.finish();
-                return ret;
+            
+            ClassLoader loader = this.getClass().getClassLoader();
+            
+            if (urls.size() > 0) {
+                loader = new URLClassLoader
+                        (urls.toArray(new URL[0]), loader);
             }
-        };
-
-    public static final Function XML_CDATA =
-        new Function() {
-	    public int invoke(FunctionDataParser fdp, Writer out)
-		throws IOException, TemplateException {		
-
-                ReplaceWriter rw = new ReplaceWriter
-                    (out, "]]>", "]]]><![CDATA[]>", "<![CDATA[", "]]>");
-
-                int ret = fdp.parseData(rw);
-                rw.finish();
-                return ret;
+            
+            Class<?> loadedClass;
+            Object instance;
+            
+            try {
+                loadedClass = loader.loadClass(className);
+            } catch (ClassNotFoundException e) {
+                throw new TemplateException
+                        ("Class not found", e, fdp.getTemplateReader());
             }
-        };
-
-
-    @SuppressWarnings("unchecked")
-    private static Class<Function> loadFunctionClass
-        (ClassLoader loader, String className)
-        throws ClassNotFoundException {
-        
-        return (Class<Function>)loader.loadClass(className);
-    }
-
+            
+            try {
+                instance = loadedClass.newInstance();
+            } catch (InstantiationException e) {
+                throw new TemplateException
+                        ("Could not load class", e, fdp.getTemplateReader());
+            } catch (IllegalAccessException e) {
+                throw new TemplateException
+                        ("Could not load class", e, fdp.getTemplateReader());
+            }
+            
+            fdp.getContext().set(name, instance);
+                        
+            out.write(name);
+            return 1;
+        }
+    };
+    
+    public static final Function ECHO = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            if (!fdp.hasMoreData())
+                return 0;
+            
+            return fdp.parseData(out);
+        }
+    };
+    
     /**
-     * Update file (copy if newer).
-     *
-     * @param src source file.
-     * @param dest destination file.
-     *
-     * @trows IOException on copying error.
+     * Process arguments, but output nothing.
      */
-    private static void copyFile(File src, File dest) throws IOException {
-	System.err.print("Copying " + src + "... ");
-	
-	if (src.lastModified() < dest.lastModified()) {
-	    System.err.println(dest + " is up to date.");
-	    return;
-	}
-
-	File parent = dest.getParentFile();
-	if (parent != null) parent.mkdirs();
-
-        InputStream in = new FileInputStream(src);
-        OutputStream out = new FileOutputStream(dest);
-        
-        byte[] buffer = new byte[1024];
-        int len;
-        while ((len = in.read(buffer)) > 0) {
-            out.write(buffer, 0, len);
+    public static final Function SILENT = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            if (!fdp.hasMoreData())
+                return 0;
+            
+            return fdp.parseData(NullWriter.INSTANCE);
         }
-        in.close();
-        out.close();
-
-	System.err.println("saved " + dest + ".");
-    }
+    };
+    
+    /**
+     * Skip arguments.
+     */
+    public static final Function SKIP = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            // Ignore arguments.
+            return 0;
+        }
+    };
 }
diff --git a/src/kryshen/tema/functions/Strings.java b/src/kryshen/tema/functions/Strings.java
new file mode 100644
--- /dev/null
+++ b/src/kryshen/tema/functions/Strings.java
@@ -0,0 +1,225 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: Strings.java,v 1.13 2008/02/16 17:38:03 mikhail Exp $
+ */
+
+package kryshen.tema.functions;
+
+import java.io.IOException;
+import java.io.Writer;
+import kryshen.tema.Function;
+import kryshen.tema.FunctionDataParser;
+import kryshen.tema.TemplateException;
+import kryshen.tema.io.ReplaceWriter;
+
+/**
+ * Functions for string manipulation.
+ */
+public class Strings {
+    public static final Function TO_UPPER = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String data = fdp.getData();
+            out.write(data.toUpperCase());
+            return fdp.getLastReturnCode();
+        }
+    };
+    
+    public static final Function TO_LOWER = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String data = fdp.getData();
+            out.write(data.toLowerCase());
+            return fdp.getLastReturnCode();
+        }
+    };
+    
+    public static final Function SUBSTRING = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String is = fdp.getNextArg();
+            String js = fdp.getNextArg();
+            
+            String data = fdp.getData();
+            
+            try {
+                if (js.equals("$")) {
+                    out.write(data.substring(Integer.parseInt(is)));
+                } else {
+                    out.write(data.substring(
+                            Integer.parseInt(is),
+                            Integer.parseInt(js)));
+                }
+            } catch (NumberFormatException e) {
+                throw new TemplateException(
+                        e.toString(), e, fdp.getTemplateReader());
+            } catch (IndexOutOfBoundsException e) {
+                throw new TemplateException(
+                        e.toString(), e, fdp.getTemplateReader());
+            }
+            
+            return fdp.getLastReturnCode();
+        }
+    };
+    
+    public static final Function REPLACE = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String arg0 = fdp.getNextArg();
+            String arg1 = fdp.getNextArg();
+            
+            ReplaceWriter rw = new ReplaceWriter(out, arg0, arg1);
+            
+            int ret = fdp.parseData(rw);
+            rw.finish();
+            return ret;
+        }
+    };
+    
+    public static final Function XML_ESCAPE = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            final String[] chars = {"&", "<", ">", "`", "\""};
+            final String[] escape = {"&", "<", ">", "'", """};
+            
+            ReplaceWriter rw = new ReplaceWriter(out, chars, escape);
+            
+            int ret = fdp.parseData(rw);
+            rw.finish();
+            return ret;
+        }
+    };
+    
+    public static final Function XML_CDATA = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            ReplaceWriter rw = new ReplaceWriter
+                    (out, "]]>", "]]]><![CDATA[]>", "<![CDATA[", "]]>");
+            
+            int ret = fdp.parseData(rw);
+            rw.finish();
+            return ret;
+        }
+    };
+    
+    public static final Function REGEX_MATCH = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String regex = fdp.getNextArg();            
+            String data = fdp.getData(out);
+            
+            if (data.matches(regex)) {
+                return 1;
+            }
+            
+            return 0;
+        }
+    };
+    
+    public static final Function REGEX_REPLACE_FIRST = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String regex = fdp.getNextArg();
+            String replacement = fdp.getNextArg();
+            String data = fdp.getData();
+            
+            if (fdp.getLastReturnCode() == 0)
+                return 0;
+            
+            out.write(data.replaceFirst(regex, replacement));
+            
+            return fdp.getLastReturnCode();
+        }
+    };
+    
+    public static final Function REGEX_REPLACE_ALL = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String regex = fdp.getNextArg();
+            String replacement = fdp.getNextArg();
+            String data = fdp.getData();
+            
+            if (fdp.getLastReturnCode() == 0)
+                return 0;
+            
+            out.write(data.replaceAll(regex, replacement));
+            
+            return fdp.getLastReturnCode();
+        }
+    };
+    
+    public static final Function EQUAL = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+        throws IOException, TemplateException {
+            
+            String s1 = fdp.getNextArg();
+            
+            while (fdp.hasMoreData()) {
+                String s2 = fdp.getNextArg();
+                
+                if (!s1.equals(s2)) {
+                    return 0;
+                }
+                
+                s1 = s2;
+            }
+
+            return 1;
+        }
+    };
+    
+    public static final Function CHAR = new Function() {
+        public int invoke(FunctionDataParser fdp, Writer out)
+                throws IOException, TemplateException {           
+
+            if (!fdp.hasMoreData())
+                return 0;
+            
+            do {
+                String s = fdp.getNextArg();
+                int code;
+
+                try {
+                    if (s.startsWith("0x")) {
+                        code = Integer.parseInt(s.substring(2), 16);
+                    } else {
+                        code = Integer.parseInt(s);
+                    }
+                } catch (NumberFormatException e) {
+                    throw new TemplateException(e.getMessage(), e,
+                            fdp.getTemplateReader());
+                }
+
+                out.write(code);                
+            } while (fdp.hasMoreData());
+                
+            return 1;
+        }
+    };
+}
diff --git a/src/kryshen/tema/io/CopyWriter.java b/src/kryshen/tema/io/CopyWriter.java
new file mode 100644
--- /dev/null
+++ b/src/kryshen/tema/io/CopyWriter.java
@@ -0,0 +1,69 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: CopyWriter.java,v 1.7 2008/02/16 17:56:34 mikhail Exp $
+ */
+
+package kryshen.tema.io;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Copies it's output to several writers.
+ *
+ * @author Mikhail Kryshen
+ */
+public class CopyWriter extends Writer {
+    private final Writer[] writers;
+    
+    public CopyWriter(Writer... writers) {
+        this.writers = writers;
+    }
+    
+    public void write(char[] cbuf, int off, int len) throws IOException {
+        for (Writer w : writers) {
+            w.write(cbuf, off, len);
+        }
+    }
+    
+    public void write(int c) throws IOException {
+        for (Writer w : writers) {
+            w.write(c);
+        }
+    }
+    
+    public void write(String str, int off, int len) throws IOException {
+        for (Writer w : writers) {
+            w.write(str, off, len);
+        }
+    }
+    
+    public void flush() throws IOException {
+        for (Writer w : writers) {
+            w.flush();
+        }
+    }
+    
+    public void close() throws IOException {
+        for (Writer w : writers) {
+            w.close();
+        }
+    }
+}
diff --git a/src/kryshen/tema/io/NullWriter.java b/src/kryshen/tema/io/NullWriter.java
new file mode 100644
--- /dev/null
+++ b/src/kryshen/tema/io/NullWriter.java
@@ -0,0 +1,46 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: NullWriter.java,v 1.6 2008/02/16 17:38:03 mikhail Exp $
+ */
+
+package kryshen.tema.io;
+
+import java.io.Writer;
+
+/**
+ * No-op writer.
+ *
+ * @author Mikhail Kryshen
+ */
+public class NullWriter extends Writer{
+    public static final NullWriter INSTANCE = new NullWriter();
+    
+    private NullWriter() {        
+    }
+    
+    public void close() {
+    }
+    
+    public void flush() {
+    }
+    
+    public void write(char[] cbuf, int off, int len) {
+    }
+}
diff --git a/src/kryshen/tema/io/ReplaceWriter.java b/src/kryshen/tema/io/ReplaceWriter.java
new file mode 100644
--- /dev/null
+++ b/src/kryshen/tema/io/ReplaceWriter.java
@@ -0,0 +1,153 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: ReplaceWriter.java,v 1.8 2008/02/19 16:21:00 mikhail Exp $
+ */
+
+package kryshen.tema.io;
+
+import java.io.Writer;
+import java.io.FilterWriter;
+import java.io.IOException;
+
+/**
+ * FilterWriter which replaces characters with escape strings.
+ *
+ * @author Mikhail Kryshen
+ */
+public class ReplaceWriter extends FilterWriter {
+    private String[] patterns;
+    private String[] replaces;
+
+    private String prefix;
+    private String postfix;
+
+    private int maxPatternLength = 0;
+
+    /** Have this Writer processed any data? */
+    private boolean processedData = false;
+
+    private StringBuffer buffer = new StringBuffer();
+
+    public ReplaceWriter(Writer w, String pattern, String replace) {
+        this(w, new String[]{pattern}, new String[]{replace});
+    }
+
+    public ReplaceWriter(Writer w, String pattern, String replace,
+                         String prefix, String postfix) {
+
+        this(w, new String[]{pattern}, new String[]{replace}, 
+             prefix, postfix);
+    }
+
+    public ReplaceWriter(Writer w, String[] patterns, String[] replaces) {
+        this(w, patterns, replaces, null, null);
+    }
+
+    public ReplaceWriter(Writer w, String[] patterns, String[] replaces, 
+                         String prefix, String postfix) {
+	super(w);
+        
+	this.patterns = patterns;
+	this.replaces = replaces;
+        
+        this.prefix = prefix;
+        this.postfix = postfix;
+        
+	for (int i = 0; i < patterns.length; i++) {
+	    int length = patterns[i].length();
+	    if (length > maxPatternLength)
+		maxPatternLength = length;
+	}
+    }
+
+    private boolean processBuffer(int minLength) throws IOException {
+        if (buffer.length() == 0) return false;
+
+        if (!processedData) {
+            if (prefix != null)
+                super.write(prefix, 0, prefix.length());
+            processedData = true;
+        }
+
+        int k;
+	for (k = 0; minLength + k < buffer.length(); k++) {
+	    for (int i = 0; i < patterns.length; i++) {
+		String pattern = patterns[i];
+		int length = pattern.length();
+
+                if (length + k > buffer.length())
+                    continue;
+
+		boolean match = true;
+
+		for (int j = 0; j < length; j++) {
+		    if (pattern.charAt(j) != buffer.charAt(j + k)) {
+			match = false;
+                        break;
+		    }
+		}
+
+		if (match) {
+                    super.write(buffer.substring(0, k), 0, k);
+                    buffer.delete(0, k + length);
+                    super.write(replaces[i], 0, replaces[i].length());
+		    return true;
+		}
+	    }
+	}
+
+        super.write(buffer.substring(0, k), 0, k);
+        buffer.delete(0, k);
+
+        return false;
+    }
+
+    public void write(int c) throws IOException {
+	buffer.append((char)c);
+	processBuffer(maxPatternLength);
+    }
+
+    public void write(char[] cbuf, int off, int len) throws IOException {
+	for (int i = off; i < off + len; i++) {
+	    buffer.append(cbuf[i]);
+	}
+	processBuffer(maxPatternLength);
+    }
+
+    public void write(String str, int off, int len) throws IOException {
+        buffer.append(str.substring(off, off + len));
+	processBuffer(maxPatternLength);
+    }
+
+    public void finish() throws IOException {
+        while (processBuffer(0));
+
+//         super.write(buffer.toString(), 0, buffer.length());
+//         buffer.delete(0, buffer.length());
+
+        if (processedData && postfix != null)
+            super.write(postfix, 0, postfix.length());
+    }
+
+    public void close() throws IOException {
+        finish();
+        super.close();
+    }
+}
diff --git a/src/kryshen/tema/io/TemplateReader.java b/src/kryshen/tema/io/TemplateReader.java
new file mode 100644
--- /dev/null
+++ b/src/kryshen/tema/io/TemplateReader.java
@@ -0,0 +1,102 @@
+/*
+ *  Copyright 2006-2008 Mikhail Kryshen
+ *
+ *  This file is part of Tema.
+ *
+ *  Tema is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Lesser General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
+ *
+ *  Tema is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the 
+ *  GNU Lesser General Public License along with Tema.  
+ *  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  $Id: TemplateReader.java,v 1.8 2008/02/19 00:20:48 mikhail Exp $
+ */
+
+package kryshen.tema.io;
+
+import java.io.Reader;
+import java.io.LineNumberReader;
+import java.io.PushbackReader;
+import kryshen.tema.TemplateParser;
+
+/**
+ * Reader for Tema templates. Stores data source name (commonly
+ * filename) and tracks line numbers for error reporting.
+ *
+ * @author Mikhail Kryshen
+ */
+public class TemplateReader extends PushbackReader {
+    static final int UNREAD_BUFFER_SIZE;
+    
+    /* Calculate UNREAD_BUFFER_SIZE value. */
+    static {
+        int max = 0;
+        
+        for (String s : TemplateParser.BRACKET_OPEN) {
+            max = Math.max(max, s.length());
+        }
+        
+        for (String s : TemplateParser.BRACKET_CLOSE) {
+            max = Math.max(max, s.length());
+        }
+        
+        max = Math.max(max, TemplateParser.ESCAPE_NEWLINE.length() + 2);
+        max = Math.max(max, TemplateParser.ESCAPE_WHITESPACE.length());
+        
+        UNREAD_BUFFER_SIZE = max;
+    }
+    
+    private final String source;
+    private final LineNumberReader lnReader;
+    private final TemplateReader parentReader;
+    
+    public TemplateReader(Reader in) {
+        this(new LineNumberReader(in));
+    }
+    
+    public TemplateReader(Reader in, String source) {
+        this(new LineNumberReader(in), source);
+    }
+    
+    public TemplateReader(LineNumberReader in) {
+        this(in, "");
+    }
+    
+    public TemplateReader(LineNumberReader in, String source) {
+        super(in, UNREAD_BUFFER_SIZE);
+        
+        this.parentReader = null;
+        this.lnReader = in;
+        this.source = source;
+    }
+    
+    public TemplateReader(Reader in, TemplateReader parent) {
+        super(in, UNREAD_BUFFER_SIZE);
+        
+        this.parentReader = parent;
+        this.lnReader = null;
+        this.source = null;
+    }
+    
+    public String getSource() {
+        if (source != null)
+            return source;
+        
+        return parentReader.getSource();
+    }
+    
+    public int getLineNumber() {
+        if (lnReader != null)
+            return lnReader.getLineNumber();
+        
+        return parentReader.getLineNumber();
+    }
+}
diff --git a/test/demo b/test/demo
--- a/test/demo
+++ b/test/demo
@@ -1,4 +1,4 @@
 #!/bin/sh
 
 #java -jar ../dist/tema.jar
-java -classpath .:../dist/tema.jar kryshen.tema.Tema
+java -classpath .:../dist/tema.jar kryshen.tema.Tema main.template
diff --git a/test/demo.bat b/test/demo.bat
--- a/test/demo.bat
+++ b/test/demo.bat
@@ -1,1 +1,1 @@
-java -classpath .:..\dist\tema.jar kryshen.tema.Tema
+java -classpath .:..\dist\tema.jar kryshen.tema.Tema main.template
diff --git a/test/include.template b/test/include.template
--- a/test/include.template
+++ b/test/include.template
@@ -1,1 +1,1 @@
-<%define\test test arg1:[%nextarg:%], test arg2:[%nextarg:%], test data:[%data:%].%>
\ No newline at end of file
+<%define#test test arg1:<%next_arg:%>, test arg2:<%next_arg:%>, test data:<%data:%>.%>
\ No newline at end of file
diff --git a/test/main.template b/test/main.template
--- a/test/main.template
+++ b/test/main.template
@@ -1,4 +1,4 @@
-[%!\ UTF-8 text %]
+[%!\ UTF-8 text %]\
 
 Вывод текста:
 test text
@@ -10,7 +10,7 @@
 <%include:include.template%>
 
 Вызов новой функции:
-<%test:1 2 3%>
+<%test:1 2 3 4 5%>
 
 Тестирование read:
 <%read\include.template%>
diff --git a/test/tema.properties b/test/tema.properties
deleted file mode 100644
--- a/test/tema.properties
+++ /dev/null
@@ -1,22 +0,0 @@
-# Data source configuration
-# resource          : jdbc:odbc:database
-# driver            : sun.jdbc.odbc.JdbcOdbcDriver
-
-# Base directory for images and files
-# resource_base     : .
-
-# Template to start processing with
-main_template     : main.template
-
-# File encodings
-input_encoding    : UTF-8
-output_encoding   : UTF-8
-
-# Cache templates
-# cache_read        : true
-
-# Output main_template parsing result to stderr
-# output            : stderr
-
-# File to output error messages (redirect stderr)
-# log               : dbreader.log