summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathan Angelacos <nangel@alpinelinux.org>2008-12-17 01:34:15 +0000
committerNathan Angelacos <nangel@alpinelinux.org>2008-12-17 01:34:15 +0000
commit64b823303af9dcb002370d0611c5783a8c610442 (patch)
tree883d4d70a5a4f3c593063db13c1e1c2e19b52129
parentb6de69e02e1a4b2ffebc93cc609bbd000949591d (diff)
downloadhaserl-64b823303af9dcb002370d0611c5783a8c610442.tar.bz2
haserl-64b823303af9dcb002370d0611c5783a8c610442.tar.xz
Reverted back to 0.9.24 codebase, releasing 0.9.25 off of it. 0.9.25 pre had new llist libraries that seem to be borked
-rw-r--r--AUTHORS5
-rw-r--r--COPYING340
-rw-r--r--ChangeLog395
-rw-r--r--INSTALL36
-rw-r--r--Makefile.am7
-rw-r--r--NEWS0
-rw-r--r--README19
-rw-r--r--README.BashExtensions161
-rw-r--r--THANKS34
-rw-r--r--TODO18
-rw-r--r--aclocal.m4868
-rw-r--r--configure.ac191
-rw-r--r--doc/Makefile.am9
-rw-r--r--doc/haserl.1647
-rw-r--r--haserl.doxy1357
-rw-r--r--src/Makefile.am66
-rw-r--r--src/common.c370
-rw-r--r--src/common.h62
-rw-r--r--src/config.h.in103
-rw-r--r--src/h_bash.c651
-rw-r--r--src/h_bash.h41
-rw-r--r--src/h_error.c103
-rw-r--r--src/h_error.h19
-rw-r--r--src/h_lua.c155
-rw-r--r--src/h_lua.h12
-rw-r--r--src/h_lua_common.c117
-rw-r--r--src/h_lua_common.h7
-rw-r--r--src/h_luac.c55
-rw-r--r--src/h_luac.h7
-rw-r--r--src/h_script.c614
-rw-r--r--src/h_script.h52
-rw-r--r--src/haserl.c917
-rw-r--r--src/haserl.h77
-rw-r--r--src/haserl_lualib.inc90
-rw-r--r--src/haserl_lualib.lua54
-rw-r--r--src/lua2c.c97
-rw-r--r--src/rfc2388.c508
-rw-r--r--src/rfc2388.h28
-rw-r--r--src/sliding_buffer.c180
-rw-r--r--src/sliding_buffer.h27
-rw-r--r--src/stamp-h.in1
-rw-r--r--tests/Makefile.am14
-rw-r--r--tests/minunit.h6
-rw-r--r--tests/utest_common.c46
44 files changed, 8566 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..5e497c8
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,5 @@
+Authors of Haserl
+
+ Nathan Angelacos <nangel@users.sourcefore.net>
+
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) 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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. 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.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+ 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
+convey 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 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This 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 Library General
+Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..3cfdc26
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,395 @@
+2008-12-16
+ 0.9.25
+ * Somehow version control failed, and the -d debug short
+ option reverted to -D again. fixed.
+ * haserl.c - "command-line" handling was broken on OSX and BSD
+ fixed. (Mark Blythe)
+ * haserl.c - fix bug where CONTENT_LENGTH=0 would hang haserl
+ (bug tracker bug #1959379)
+ text data bss dec hex filename
+ 18949 820 172 19941 4de5 haserl
+2008-04-14
+ 0.9.24
+ * haserl.c - myputenv caused a segfault when a variable without
+ an = (assignment) was passed. (Scott)
+ * doc/haserl.1 - Misc typos fixed (Scott)
+ * scott's bash-extensions are included if configured with
+ --enable-bash-extensions
+ * running haserl with no args now tells you if lua or
+ the bash extensions are enabled.
+ * configure.ac - FORM_ now hardcoded
+ * tests/* - beginnings of unit tests (make check)
+ * haserl.c - varibles now stored as FORM_ + GET_, POST_, COOKIE_
+ (scott) - WIP - need to clean up the putenv so that only one
+ copy is saved in the host, and the child gets all the
+ duplicates
+ text data bss dec hex filename
+ 18519 824 172 19515 4c3b src/haserl
+
+2008-03-22
+ 0.9.23
+ * haserl.c - short option for debug id -d (was incorrectly -D)
+ * haserl.c - remove use of legacy "index" clib function.
+ * haserl.c - myputenv makes a newline delimited "array" out
+ of variables that end in []. (php-style for multi-selects)
+ Lua variables will have same behavior - they are not treated
+ as true arrays yet. (WIP - need to handle the env variables
+ as abstracts until subshell startup for this to work)
+ text data bss dec hex filename
+ 18237 820 128 19185 4af1 src/haserl
+2008-01-28
+ 0.9.22
+ * h_bash.c - close the input side of the pipe, so that if the
+ child dies early the parent doesn't deadlock (patch by Diego
+ Santa Cruz)
+ * rfc2388.c, sliding_buffer.c, haserl.c - read CONTENT_LENGTH,
+ and stop reading input when CONTENT_LENGTH reached (fixes odd
+ problems where the client may not close the connection
+ properly at the end of a request [guess which clients are
+ MiStaken?]) (patch by Diego Santa Cruz)
+ * Allow "-" to be used in a field identifier (e.g. FORM["-foo"])
+ text data bss dec hex filename
+ 17813 812 128 18753 4941 src/haserl
+2007-11-22
+ 0.9.21
+ * haserl.c - fixed a stupid test for strlen that caused CGI post
+ requests to take exponentially longer amounts of time based on
+ the size of the upload
+ * h_bash.c - removed open_bash function; found that a CGI post
+ of > ~150K will cause bash to fail on the subshell. This
+ appears to be a limitation of execv, not haserl. AFAICT
+ * h_script.c - Fixed stupid error where the leading html was
+ dropped if a comment tag was used.
+ * Update TODO list
+ text data bss dec hex filename
+ 17425 808 128 18361 47b9 src/haserl
+2007-09-22
+ 0.9.20
+ * h_script.c - Added Comment tag (<%#)
+ * various - Removed syslog calls
+ text data bss dec hex filename
+ 17425 808 128 18361 47b9 src/haserl
+2007-09-16
+ 0.9.19
+ * haserl.c - If CONTENT_LENGTH not set, don't try to read POST
+ data (caused haserl to hang on --accept-all)
+ * configure.ac - Don't require dl on BSD/OSX (N Copa)
+ * h_script.c - Don't switch <% %> <? ?> in include files. Check
+ only on the original script (Hinko Kocevar)
+ * Workaround for cross-compiling haserl_lualib.inc (reported by
+ Andreas Schultz)
+ * haserl.* - add extern definitions to shell function pointers
+ so uClinux (gcc 3.2.3) can compile haserl (glhs329 at gmail)
+ text data bss dec hex filename
+ 17584 784 128 18496 4840 src/haserl
+2007-07-23
+ 0.9.18
+ * h_script.c, lua2c.c - Fixed two typos that prevented
+ compilation.
+ * configure.ac patch to compile with pkg-config, if found
+ (ubuntu calls it lua51, not lua) - submitted by Natanael Copa
+2007-07-22
+ 0.9.17
+ * h_bash.c - rc3 commented out the signal call, so an <? exit ?>
+ would cause the script to hang.
+ * h_bash.c - don't wait for the echo statement to signal end of
+ script. Do a waitpid instead. This means fd 5 is now not
+ used.
+ * *lua.* - precompiled lua support, with reorganization of all
+ lua source code (Robin Haberkorn)
+ * configure.ac / makefile.am - Ability to turn off
+ shell/lua/luac (Robin Haberkorn)
+ * lua2c.c - simple luac + bin2c replacement.
+ * h_script.c / h_error.c - Use <% as the prefered tag element
+ use <? only when <% is not found in the script.
+ text data bss dec hex filename
+ 17580 784 136 18500 4844 src/haserl
+
+207-07-14
+ 0.9.17_rc3
+ * haserl.c - Setuid/gid security fix from Timo Teras
+ * rfc2388.c - upload-handler; fixed problem if boundary
+ is not last tag in the line.
+ * haserl.c - fix in myputenv (Robin Haberkorn)
+ * sliding_buffer.c - Fixes for short reads vs eof (Andreas
+ Shultz)
+ * h_bash.c - don't exit when child dies - prevents proper
+ cleanup; argv[19] was incorrectly overwritten. (Timo Teras)
+ * text data bss dec hex filename
+ 17581 784 136 18501 4845 src/haserl
+2007-07-08
+ 0.9.17_rc2
+ * sliding_buffer added to reduce memory requirements on large
+ uploads
+ * rfc2388 completely replaces old mimedecoder; adds ability to
+ upload to a FIFO or program. (This may be a mistake)
+ * text data bss dec hex filename
+ size before 15612 716 132 16460 404c src/haserl
+ size after 17068 712 132 17912 45f8 src/haserl
+
+2007-02-19
+ 0.9.16_rc
+ 2 code patches from Juris Kalnins based on a code audit
+ * h_errror.c : die_with_message does not write all error to stdout
+ when running under httpd
+ * haserl.c: decode_url oversteps string end on trailing or near trailing %
+ * haserl.c - If mime block doesn't have a content disposition, skip it
+ (workaround for bug in Opera 9.10)
+2007-02-05
+ 0.9.15
+ * revert back to "\n" instead of ";" for echo & eval - too many edge cases
+ where it doesn't work
+ * haserl+lua compiles on FreeBSD (and possibly OSX - not tested yet)
+2007-02-02
+ 0.9.14
+ * added lua info to manpage
+2007-02-01
+ 0.9.14_rc
+ * h_lua.c - ]] (or ]=] ]==] etc) are now "echo command" safe
+ * h_lua.c h_bash.c - don't add extra linebreaks in echo or eval commands.
+ use "; " instead
+ * haserl.c - fixed --accept-all (was --Accept-all)
+ * h_lua.* h_bash.* haserl.h - now prints the name of the file when
+ reporting syntax or runtime error. Previously, the script was an
+ anonymous string
+ * haserl.c - removed debug message in file-upload unlink code. (oops)
+ * h_script.c - shell_exec runs "code; ", not "code\n"
+ * h_script.c - make the error reporting match the line in the source script
+ (Not perfect, but should be closer than before)
+2006-11-14
+ 0.9.13
+ * Public Release
+2006-10-28
+ 0.9.13_rc [major code refactoring]
+ * haserldb removed
+ * haserll removed; lua code is now part of the main haserl program.
+ * <?if <?el <?fi <?ab tags removed.
+ * removed the extras directory
+ * changed license to GPL2 only
+2006-09-26
+ 0.9.12
+ * haserl.c Alexander Bigga pointed out the accept-all / accept-none short
+ options were wrong, and the optional argument for -u is a gnu extension.
+ * h_luascript.c if a form variable has a number component, force the variable
+ to be numeric -> foo.1.var = foo[1].var, not foo["1"].var
+2006-09-12
+ 0.9.11
+ * a bash shell cannot be opened until AFTER all the environment variables are
+ placed in the environ. Now have a "pre_open..." set of functions to handle
+ the difference between lua and shell
+2006-09-09
+ 0.9.10_rc2
+ * Special linking options so that lua "require" will work when linking c libraries
+ * remove <?if <?el <?fi from lua shell
+2006-09-06
+ 0.9.10_rc1
+ * html outputting now uses io.write with [[ ]], so the html function
+ is much simpler and faster.
+ * added <?= eval instruction
+2006-09-05
+ 0.9.10_alpha1
+ * FORM and ENV global tables are populated by haserl. The code will create a
+ nested table (e.g. FORM[eth][0][type] = static; FORM[eth][1][type] = dhcp
+ * configure now knows to inlcude libm for math.
+2006-09-04
+ 0.9.10_alpha0 version
+ * haserll is built if lua is found. This version will run lua scripts,
+ and handles printing html code, but does not create the FORM or ENV tables
+ yet.
+2006-08-10
+ 0.9.9 version
+ * Public Release (no changes from rc1)
+2006-07-31
+ 0.9.9_rc1
+ * haserl.c remove check for LABEL and GOTO tags (they don't exist, and
+ cause segfaults on secure OS'es)
+ 0.9.9-rc0
+ * haserldb.c - added strftime strptime functions
+2006-05-19
+ 0.9.8 version
+ * haserl.c - unlink all uploaded files at the end of the script
+ * haserl.c - --upload-none option added to prevent parsing stdin as
+ web server content (for haserl scripts called from haserl scripts)
+ * haserl.c - if its a GET request and --accept-all is set, then silently
+ ignore the post content if CONTENT_LENGTH is not set.
+2006-05-17
+ 0.9.7 version
+ * h_subshell.c - renable code to run user-specified shell
+2006-05-15
+ 0.9.6 version
+ * Fix memory overwrite bug when processing NULL length HTML tags
+ * Fix syntax typos in man page examples
+ (Both reported by Martin Begheyn)
+
+2006-05-03
+ 0.9.5 version
+ * common.c, haserldb.c, haserl.c
+ argc_argv now passes a argv_t pointer around; which includes
+ an indicator if the string was quoted or not. This allows
+ keywords to be used as literal strings:
+ haserldb \"fetch"\ username store
+ * Large chunks of haserl were refactored.
+ * haserl.c, h_script.c
+ If an HTML token is entirely whitespace, output is suppressed.
+ The "Verbatim" flag is no longer supported.
+ * h_subshell.c
+ HTML tokens are passed as "echo -n" commands, so the "debug"
+ option can be used to actually print out the shell script that would
+ be used.
+ All tokens are passed to the subshell, and then the parent haserl waits
+ for the script to end. This means that the <?if <?el <?fi tokens are
+ no longer needed, although still supported (for now).
+ <? while ... ?>stuff<? do ?>
+ is now possible.
+
+2005-11-21
+ 0.9.4 version
+ * haserldb.c - sort, rsort, merge and split functions
+ * haserl.c - fix off-by-one error on include files (included
+ files were incorrectly truncated)
+ * silently rejects argv beyond argv[2]
+ http://192.168.0.1/test.cgi?--upload-limit%3d2059&foo%3dbar
+ should not not reset the upload limit. This duplicates
+ 0.8.0 behaviour, broke in 0.9.3
+ * haserl.c --silent option (don't print errors advertizing
+ we are haserl)
+ * haserldb.c - realloc might not allocate enough space for
+ the new token in getCommandString - fixed
+2005-11-04
+ 0.9.3 version
+ * haserl.c - command-line parsing now uses optarg - "haserl foo.txt"
+ now works; new command-line options supported.
+ * haserl.c new command-line options (upload-limit, verbatim,
+ etc)
+ * haserl.c - found some memory-overwrite errors in loadscript
+ (man valgrind)
+ * haserl.c - token parsing routine (BuildTokenList) refactored.
+ * HASERL_* vars are now populated from above command-line
+ options.
+ * haserl.c - --accept-all functionalitiy added
+ * haserl.c - <?ab?> command now causes non-zero return code
+ * haserl.c - <?include .... ?> function added
+ * haserl.1 - man page updated
+2005-10-29
+ 0.9.2 version
+ * extras dirs contains a example login system -
+ login.cgi, index.cgi, loginlib.sh, haserldb-howto.txt
+ * haserldb.c - RAM-db is now live throughout a run, so
+ the RAM-db is now extra storage through run of system
+ * Some language keyword changes to make the language a
+ little more orthogonal - repl->sub, clear->empty
+2005-10-25
+ 0.9.1 version (not released)
+ * common.c - Improved parsing - now handles empty strings
+ and arbitrary comment delimiters correctly. This improved
+ haserldb's command parsing considerably.
+ * haserldb.c - New functions: ifstack ifempty if
+2005-10-23
+ 0.9.0 version
+ * added haserldb (common.* lists.* sha256.* haserldb.*)
+ * An example of using haserldb is in the extras directory:
+ (haserldb-howto.txt, loginlib.sh, login.cgi)
+2005-03-22
+ 0.8.0 version
+ * configure.ac - Remove check for malloc, as it fails
+ building with uclibc
+ * No other changes, upgraded the version number to a
+ "stable" release because no real problems have been
+ reported with this code.
+2004-12-14
+ 0.7.3 version
+ * configure.ac / configure - include signal.h define in config.h
+ to compile properly with gcc 2.95.3
+ * extras/* - tutorial removed; buttons and a few examples moved here
+ * doc/haserl.1 - a real manual page
+2004-11-10
+ 0.7.2 version
+ * haserl.c - misc fixes from Eric Titl to compile with gcc 2.95.3
+ and glibc 2.0.7
+ (include SIGNAL_H for sigchild; move variable declaration to
+ top of function in ReadMimeEncodedInput)
+2004-11-02
+ 0.7.1 version
+ No change from 0.7.0 - version # incremented because
+ of a sourceforge upload fault.
+ 0.7.0 version
+ * haserl.c - The interpreter now starts a single subshell, and
+ all commands are run from that shell. State is now
+ preserved between code blocks. (Thanks to Arne Bernin
+ for suggestions on getting this working.)
+ * haserl.c - a "u" must be on the command line (#!/usr/bin/haserl -u)
+ to allow file uploads. (Security feature - prevent
+ malicious clients from uploading abitrary data to /tmp)
+ * haserl.c - attempt to set uid/gid to the owner/group of the
+ script.
+ * tutorial/*.in - fix the tutorial to reflect changes above
+2004-10-25
+ 0.6.0 version
+ * haserl.c - HASERL_VAR_PREFIX (config.h) prefixed to
+ all user supplied strings. This is to prevent
+ the client from easily polluting global namespace,
+ such as "foo?SCRIPT_NAME=/blah.txt"
+
+2004-10-06
+ 0.5.1 version
+ * haserl.c - <? (run) tags can now be delimited by
+ space, tab, or newline. This means <?\n will
+ now work correctly.
+2004-09-28
+ 0.5.0 version
+ * haserl.c - HTTP_COOKIE is now parsed and the contents
+ placed in env variables before any other
+ parsing is done.
+2004-09-27
+ 0.4.3 version
+ * haserl.c - "abort" doesn't follow the standard of 2 chars
+ (if/el/fi); changed to "ab"
+2004-09-24
+ 0.4.2 version
+ * haserl.c/.h: Added the "abort" directive.
+ * tutorial/language.cgi.in - documented the abort function
+ * tutorial/source.cgi.in - added the code to make the "source"
+ link at the bottom of each web page work.
+2004-09-02
+ 0.4.1 version
+ * haserl.c: The name of the tempfile created by a mime-upload
+ was not stored in the variable by that name. Fixed.
+
+2004-07-25
+ 0.4.0 version ------------------------------------
+
+2004-07-26
+ * haserl.c: Fixed problem with POST data reading stdin "twice"
+ * tutorial/* all web pages are now in the tutorial
+
+2004-07-25
+ * haserl.c: WCVER is now HASERLVER; fixed problem with POSTs returning
+ "unable to read from stdin"
+
+
+2004-07-23
+ * haserl.c / haserl.h: Project renamed to haserl
+ (Html And Shell Embedded Runtime Language)
+ (Html And Shell Extraction and Report Language) (a.k.a pERL)
+
+ Added code to specify maximum upload size, to prevent
+ 20GB file uploads. Current compile default is 2MB
+
+ * Makefile.am:
+ Make -Wall -O2 CFLAG defaults
+
+
+2004-07-14
+ * Added autoconf/automake support
+
+ * webconf.c: added code to support and use autoconf/automake constructs,
+
+ added code to protect from uploads of arbitrary size (compile-time
+ setting up MAX_UPLOAD_MB)
+
+ added logic to allow a "zero-length" upload to proceed - its
+ /possible/ someone does a post without any form elements
+ selected.
+
+0.2.0
+ Initial release.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..c5357f0
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,36 @@
+To build this package, you will need the autoconf/automake tools.
+
+BUILDING FROM SVN SOURCES
+--------------------------
+aclocal && autoheader && automake -a && autoconf
+
+
+BUILDING HASERL (without lua)
+-----------------------------
+./configure
+make
+make install
+
+You might need to be root to do the 'make install'.
+
+
+BUILDING WITH EMBEDDED LUA VM
+-----------------------------
+If lua 5.1.x dev libraries are already installed, then
+
+./configure --with-lua
+make
+make install
+
+If you don't have the lua 5.1.x dev libraries, or you have
+a different version than 5.1.x, then:
+
+tar zxvf lua-5.1.1.tar.gz
+cd lua-5.1.1
+make generic | make posix | make linux | ...
+mkdir /tmp/lua
+cp src/* /tmp/lua
+cd <haserl path>
+./configure --with-lua=/tmp/lua --with-lua-headers=/tmp/lua
+make
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..574b847
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,7 @@
+PACKAGE=@PACKAGE_TARNAME@
+
+LIBS=
+SUBDIRS = src doc tests
+EXTRA_DIST = README.BashExtensions
+docdir=$(datadir)/$(PACKAGE)
+datarootdir=@datarootdir@
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/NEWS
diff --git a/README b/README
new file mode 100644
index 0000000..e2a7ac0
--- /dev/null
+++ b/README
@@ -0,0 +1,19 @@
+haserl (Html And Shell Embedded Runtime Language) is a cgi
+program that runs interpreted scripts. It combines three
+elements into a single CGI interpreter:
+
+1. It parses POST and GET requests, placing form-elements as name=value
+pairs into the environment for the CGI script to use. It is similar
+to uncgi (http://www.midwinter.com/~koreth/uncgi.html) in this respect
+
+2. It prints the contents of the script as html, and conditionally
+interpets text within <% ... %> as shell script. In this case haserl
+scripts are like a poor-man's version of PHP (http://www.php.net)
+
+3. It is very small, and so can be used in embedded environments
+
+
+Read INSTALL on how to compile haserl
+
+
+
diff --git a/README.BashExtensions b/README.BashExtensions
new file mode 100644
index 0000000..6c961ad
--- /dev/null
+++ b/README.BashExtensions
@@ -0,0 +1,161 @@
+April 2008
+
+The following documents the modifications to the haserl parser
+when --enable-bash-extensions is enabled. The following was
+written by Scott, and should not be considered as a core part
+of haserl. (Ask on the mailing list if you need help)
+
+--------------------------------------------------------------
+
+Haserl supports four tags by default. These are generic and
+suitable for use with a great variety of shells. If, like
+me, you write your scripts with vi (vim) with the syntax
+highlighting enabled, then you will have noted how badly
+this looks in the editor.
+
+I also prefer the tags to be a bit more intelligent, as I
+find it makes the source that much more readable.
+
+To this end I have added the following tags (again, these
+are designed with bash in mind, may not work with other
+sh-type shells, and are not at all supported if you are
+using LUA/LUAC.)
+
+
+ Tag version: Expands to:
+ ------------------------ ------------------------
+ <%if list %> if [[ list ]] ; then
+ <%elif list %> elif [[ list ]] ; then
+ <%else [comment] %> else [# comment]
+ <%endif [comment] %> fi [# comment]
+
+ <%case word %> case word in
+ <%when pattern %> pattern)
+ <%otherwise [comment] %> *) : [comment] ;;
+ <%endcase [comment] %> esac [# comment]
+
+ <%while list %> while list ; do
+ <%endwhile %> done [# comment]
+
+ <%until list %> until list ; do
+ <%enduntil [comment] %> done [# comment]
+
+ <%for name in word %> for name in word ; do
+ <%endfor [comment] %> done [# comment]
+
+ <%unless list %> if [[ ! list ]] ; then
+ <%elun list %> elif [[ ! list ]] ; then
+ <%unelse [comment] %> else [#comment]
+ <%endunless [comment %> fi [# comment]
+
+
+To simplify parsing, and to reduce confusion when reading
+your source, unique words are used. For example, the use of
+endwhile, enduntil and endfor instead of done. Also, for
+clarity I have used endif/endcase instead of fi/esac.
+
+------
+UNLESS
+------
+
+Note the last command, the unless...endunless. I find it is
+often more clear to express an unless condition than an if
+not condition. For example, and to show the streamlining of
+code these tags provide:
+
+ <% if [[ ! IsLogged ]] ; then %>
+ something
+ <% fi %>
+
+This is the improved (IMHO) version:
+
+ <%unless IsLogged %>
+ something
+ <%endunless %>
+
+Personally, I find no use for "else unless", but I decided
+to include it for consistency.
+
+--------------------
+CONDITIONAL COMMANDS
+--------------------
+
+There is one syntactic problem, concerning the use of the
+<%if %>, <%while %>, <%until %> and <%unless %> tags. The
+default action is to create a test-style condition; however,
+sometime you need to do this:
+
+ <% if grep "text" file &>/dev/null ; then %>
+
+If you simply write this:
+
+ <%if grep "text" file &>/dev/null %>
+
+Then it is expanded to:
+
+ if [[ grep "text" file &>/dev/null ]] %>
+
+That is obviously incorrect. To allow for this the parser
+provides a bit of syntactic sugar. It checks the first
+character of the expression list and if it is, then it
+rewrites the list as a command. For example:
+
+ <%if |grep "text" file &>/dev/null %>
+
+Becomes:
+
+ if grep "text" file &>/dev/null ; then
+
+--------------
+CASE STATMENTS
+--------------
+
+The case statement provided another challenge, namely how to
+handle the pesky ;; required at the end of each case. I am
+not a C person, so personally I find the next instance of a
+case to be sufficient to terminate the first. Given that the
+shell version of case can accept multiple conditions, there
+is really no reason to worry with cases falling through as
+they do in C. In other words, I'm all for ditching the ;;
+construct altogether.
+
+So I did.
+
+The enhanced <%case %> tag operates without an explicit ;;
+being required. It is perhaps easier to show than explain,
+so here's an example:
+
+ <%case "$FRUIT" %>
+ <%when apple %>
+ echo "It's an apple!"
+ <%when orange %>
+ echo "It's an orange!"
+ <%otherwise something else %>
+ echo "Not what I expected."
+ <%endcase %>
+
+The parser renders this as follows:
+
+ case "$FRUIT" in
+ "\000\012\004") :
+ ;;
+ apple)
+ echo "It's an apple!"
+ ;;
+ orange)
+ echo "It's an orange!"
+ ;;
+ *) # something else
+ echo "Not what I expected."
+ ;;
+ esac
+
+Note the odd first case. The parser inserts this so ensure
+that each subsequent when/otherwise/endcase can be preceded
+by ;;. This eliminates the need to remember the ;;, and I
+believe makes for cleaner code. There is, of course, a small
+performance penalty for always having to evaluate this extra
+case. In real life I find this not to be a problem, however,
+if you are processing inside a loop and expect a large chunk
+of items, then it is something to be aware of.
+
diff --git a/THANKS b/THANKS
new file mode 100644
index 0000000..da1ecdc
--- /dev/null
+++ b/THANKS
@@ -0,0 +1,34 @@
+Haserl THANKS file
+
+Haserl was written by Nathan Angelacos.
+
+It was the result of work done by Cameron Banta and David Krause, who
+wrote a web-configuration engine using AWK scripts.
+
+Natanael Copa assisted with the autoconf scripts, as well as general
+support of the project.
+
+Arne Bernin submitted the first patch to make the shell variables
+survive code blocks. His comments and ideas got the whole sub-shell
+thing going.
+
+Erich Titl helped with some of the configure automake checks.
+
+Vladislav Moskovets suggested the include directive.
+
+Andreas Brodmann provided the incentive to add lua support.
+
+Robin Haberkorn supplied the luac (precompiled lua) support, and then
+did so a second time after the entire source tree was reformatted.
+
+Diego Santa Cruz from spinetix.com provided fixes to some really
+annoying (and sporadic) deadlocks with IE, thttpd, POST requests.
+
+Scott did the COOKIE_, POST_, GET_ parsing (he's also responsible for
+the bash extensions)
+
+Mark Blythe identified differences between BSD/OSX and Linux shell
+handling of #! scripts, and how argv[1] looks. Then he supplied the
+fix.
+
+
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..af53332
--- /dev/null
+++ b/TODO
@@ -0,0 +1,18 @@
+15 Feb 2008
+* multiselect needs to return an array of values. PHP handles it like this:
+ if the variable is foo[] then an array is returned. If the variable name
+ in the form is foo, then the last value is returned. For shell, make it
+ a \n delimited array, so that you can do for a in $foo; do (bb ash doesnt
+ support real arrays.) Lua could though. (Scott) - DONE - but lua is newline
+ terminated, not as a table.
+25 Jan 2008
+* change sliding_buffer into a circular buffer to reduce mem moves.
+* turn haserl core into a busybox applet
+20 Nov 2007
+* Add option to separate FORM into COOKIE, GET, POST tables (DONE - Scott)
+* Make it easier for people like Scott to make new haserl tags
+* Release a 0.10.0 or 1.0.0 version already
+12 Jul 2007
+* combine all the llist stuff into one generic library. (save space)
+6 Feb 2007
+* Investigate fastcgi for the lua version of haserl
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644
index 0000000..eb3b99a
--- /dev/null
+++ b/aclocal.m4
@@ -0,0 +1,868 @@
+# generated automatically by aclocal 1.10.1 -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+m4_if(AC_AUTOCONF_VERSION, [2.61],,
+[m4_warning([this file was generated for autoconf 2.61.
+You have another version of autoconf. It may work, but is not guaranteed to.
+If you have problems, you may need to regenerate the build system entirely.
+To do so, use the procedure documented by the package, typically `autoreconf'.])])
+
+# Copyright (C) 2002, 2003, 2005, 2006, 2007 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_AUTOMAKE_VERSION(VERSION)
+# ----------------------------
+# Automake X.Y traces this macro to ensure aclocal.m4 has been
+# generated from the m4 files accompanying Automake X.Y.
+# (This private macro should not be called outside this file.)
+AC_DEFUN([AM_AUTOMAKE_VERSION],
+[am__api_version='1.10'
+dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
+dnl require some minimum version. Point them to the right macro.
+m4_if([$1], [1.10.1], [],
+ [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
+])
+
+# _AM_AUTOCONF_VERSION(VERSION)
+# -----------------------------
+# aclocal traces this macro to find the Autoconf version.
+# This is a private macro too. Using m4_define simplifies
+# the logic in aclocal, which can simply ignore this definition.
+m4_define([_AM_AUTOCONF_VERSION], [])
+
+# AM_SET_CURRENT_AUTOMAKE_VERSION
+# -------------------------------
+# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
+# This function is AC_REQUIREd by AC_INIT_AUTOMAKE.
+AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
+[AM_AUTOMAKE_VERSION([1.10.1])dnl
+m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+_AM_AUTOCONF_VERSION(AC_AUTOCONF_VERSION)])
+
+# AM_AUX_DIR_EXPAND -*- Autoconf -*-
+
+# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
+# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to
+# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
+#
+# Of course, Automake must honor this variable whenever it calls a
+# tool from the auxiliary directory. The problem is that $srcdir (and
+# therefore $ac_aux_dir as well) can be either absolute or relative,
+# depending on how configure is run. This is pretty annoying, since
+# it makes $ac_aux_dir quite unusable in subdirectories: in the top
+# source directory, any form will work fine, but in subdirectories a
+# relative path needs to be adjusted first.
+#
+# $ac_aux_dir/missing
+# fails when called from a subdirectory if $ac_aux_dir is relative
+# $top_srcdir/$ac_aux_dir/missing
+# fails if $ac_aux_dir is absolute,
+# fails when called from a subdirectory in a VPATH build with
+# a relative $ac_aux_dir
+#
+# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
+# are both prefixed by $srcdir. In an in-source build this is usually
+# harmless because $srcdir is `.', but things will broke when you
+# start a VPATH build or use an absolute $srcdir.
+#
+# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
+# iff we strip the leading $srcdir from $ac_aux_dir. That would be:
+# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
+# and then we would define $MISSING as
+# MISSING="\${SHELL} $am_aux_dir/missing"
+# This will work as long as MISSING is not called from configure, because
+# unfortunately $(top_srcdir) has no meaning in configure.
+# However there are other variables, like CC, which are often used in
+# configure, and could therefore not use this "fixed" $ac_aux_dir.
+#
+# Another solution, used here, is to always expand $ac_aux_dir to an
+# absolute PATH. The drawback is that using absolute paths prevent a
+# configured tree to be moved without reconfiguration.
+
+AC_DEFUN([AM_AUX_DIR_EXPAND],
+[dnl Rely on autoconf to set up CDPATH properly.
+AC_PREREQ([2.50])dnl
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+])
+
+# AM_CONDITIONAL -*- Autoconf -*-
+
+# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 8
+
+# AM_CONDITIONAL(NAME, SHELL-CONDITION)
+# -------------------------------------
+# Define a conditional.
+AC_DEFUN([AM_CONDITIONAL],
+[AC_PREREQ(2.52)dnl
+ ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
+ [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_SUBST([$1_TRUE])dnl
+AC_SUBST([$1_FALSE])dnl
+_AM_SUBST_NOTMAKE([$1_TRUE])dnl
+_AM_SUBST_NOTMAKE([$1_FALSE])dnl
+if $2; then
+ $1_TRUE=
+ $1_FALSE='#'
+else
+ $1_TRUE='#'
+ $1_FALSE=
+fi
+AC_CONFIG_COMMANDS_PRE(
+[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
+ AC_MSG_ERROR([[conditional "$1" was never defined.
+Usually this means the macro was only invoked conditionally.]])
+fi])])
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 9
+
+# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery. Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+# _AM_DEPENDENCIES(NAME)
+# ----------------------
+# See how the compiler implements dependency checking.
+# NAME is "CC", "CXX", "GCJ", or "OBJC".
+# We try a few techniques and use that to set a single cache variable.
+#
+# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
+# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
+# dependency, and given that the user is not expected to run this macro,
+# just rely on AC_PROG_CC.
+AC_DEFUN([_AM_DEPENDENCIES],
+[AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+ifelse([$1], CC, [depcc="$CC" am_compiler_list=],
+ [$1], CXX, [depcc="$CXX" am_compiler_list=],
+ [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+ [$1], UPC, [depcc="$UPC" am_compiler_list=],
+ [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
+ [depcc="$$1" am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+ [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named `D' -- because `-MD' means `put the output
+ # in D'.
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_$1_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+ fi
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+ # Solaris 8's {/usr,}/bin/sh.
+ touch sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ case $depmode in
+ nosideeffect)
+ # after this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ none) break ;;
+ esac
+ # We check with `-c' and `-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle `-M -o', and we need to detect this.
+ if depmode=$depmode \
+ source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_$1_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_$1_dependencies_compiler_type=none
+fi
+])
+AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
+AM_CONDITIONAL([am__fastdep$1], [
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
+])
+
+
+# AM_SET_DEPDIR
+# -------------
+# Choose a directory name for dependency files.
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES
+AC_DEFUN([AM_SET_DEPDIR],
+[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
+])
+
+
+# AM_DEP_TRACK
+# ------------
+AC_DEFUN([AM_DEP_TRACK],
+[AC_ARG_ENABLE(dependency-tracking,
+[ --disable-dependency-tracking speeds up one-time build
+ --enable-dependency-tracking do not reject slow dependency extractors])
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+AC_SUBST([AMDEPBACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
+])
+
+# Generate code to set up dependency tracking. -*- Autoconf -*-
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+#serial 3
+
+# _AM_OUTPUT_DEPENDENCY_COMMANDS
+# ------------------------------
+AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
+[for mf in $CONFIG_FILES; do
+ # Strip MF so we end up with the name of the file.
+ mf=`echo "$mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile or not.
+ # We used to match only the files named `Makefile.in', but
+ # some people rename them; so instead we look at the file content.
+ # Grep'ing the first line is not enough: some people post-process
+ # each Makefile.in and add a new line on top of each file to say so.
+ # Grep'ing the whole file is not good either: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+ dirpart=`AS_DIRNAME("$mf")`
+ else
+ continue
+ fi
+ # Extract the definition of DEPDIR, am__include, and am__quote
+ # from the Makefile without running `make'.
+ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+ test -z "$DEPDIR" && continue
+ am__include=`sed -n 's/^am__include = //p' < "$mf"`
+ test -z "am__include" && continue
+ am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+ # When using ansi2knr, U may be empty or an underscore; expand it
+ U=`sed -n 's/^U = //p' < "$mf"`
+ # Find all dependency output files, they are included files with
+ # $(DEPDIR) in their names. We invoke sed twice because it is the
+ # simplest approach to changing $(DEPDIR) to its actual value in the
+ # expansion.
+ for file in `sed -n "
+ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+ # Make sure the directory exists.
+ test -f "$dirpart/$file" && continue
+ fdir=`AS_DIRNAME(["$file"])`
+ AS_MKDIR_P([$dirpart/$fdir])
+ # echo "creating $dirpart/$file"
+ echo '# dummy' > "$dirpart/$file"
+ done
+done
+])# _AM_OUTPUT_DEPENDENCY_COMMANDS
+
+
+# AM_OUTPUT_DEPENDENCY_COMMANDS
+# -----------------------------
+# This macro should only be invoked once -- use via AC_REQUIRE.
+#
+# This code is only required when automatic dependency tracking
+# is enabled. FIXME. This creates each `.P' file that we will
+# need in order to bootstrap the dependency handling code.
+AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
+[AC_CONFIG_COMMANDS([depfiles],
+ [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
+ [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
+])
+
+# Do all the work for Automake. -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005, 2006, 2008 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 13
+
+# This macro actually does too much. Some checks are only needed if
+# your package does certain things. But this isn't really a big deal.
+
+# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
+# AM_INIT_AUTOMAKE([OPTIONS])
+# -----------------------------------------------
+# The call with PACKAGE and VERSION arguments is the old style
+# call (pre autoconf-2.50), which is being phased out. PACKAGE
+# and VERSION should now be passed to AC_INIT and removed from
+# the call to AM_INIT_AUTOMAKE.
+# We support both call styles for the transition. After
+# the next Automake release, Autoconf can make the AC_INIT
+# arguments mandatory, and then we can depend on a new Autoconf
+# release and drop the old call support.
+AC_DEFUN([AM_INIT_AUTOMAKE],
+[AC_PREREQ([2.60])dnl
+dnl Autoconf wants to disallow AM_ names. We explicitly allow
+dnl the ones we care about.
+m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
+AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
+AC_REQUIRE([AC_PROG_INSTALL])dnl
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+AC_SUBST([CYGPATH_W])
+
+# Define the identity of the package.
+dnl Distinguish between old-style and new-style calls.
+m4_ifval([$2],
+[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+ AC_SUBST([PACKAGE], [$1])dnl
+ AC_SUBST([VERSION], [$2])],
+[_AM_SET_OPTIONS([$1])dnl
+dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
+m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
+ [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
+ AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
+ AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
+
+_AM_IF_OPTION([no-define],,
+[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+ AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
+AM_MISSING_PROG(AUTOCONF, autoconf)
+AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
+AM_MISSING_PROG(AUTOHEADER, autoheader)
+AM_MISSING_PROG(MAKEINFO, makeinfo)
+AM_PROG_INSTALL_SH
+AM_PROG_INSTALL_STRIP
+AC_REQUIRE([AM_PROG_MKDIR_P])dnl
+# We need awk for the "check" target. The system "awk" is bad on
+# some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
+ [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+ [_AM_PROG_TAR([v7])])])
+_AM_IF_OPTION([no-dependencies],,
+[AC_PROVIDE_IFELSE([AC_PROG_CC],
+ [_AM_DEPENDENCIES(CC)],
+ [define([AC_PROG_CC],
+ defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [_AM_DEPENDENCIES(CXX)],
+ [define([AC_PROG_CXX],
+ defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJC],
+ [_AM_DEPENDENCIES(OBJC)],
+ [define([AC_PROG_OBJC],
+ defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
+])
+])
+
+
+# When config.status generates a header, we must update the stamp-h file.
+# This file resides in the same directory as the config header
+# that is generated. The stamp files are numbered to have different names.
+
+# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
+# loop where config.status creates the headers, so we can generate
+# our stamp files there.
+AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
+[# Compute $1's index in $config_headers.
+_am_arg=$1
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
+
+# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_SH
+# ------------------
+# Define $install_sh.
+AC_DEFUN([AM_PROG_INSTALL_SH],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+install_sh=${install_sh-"\$(SHELL) $am_aux_dir/install-sh"}
+AC_SUBST(install_sh)])
+
+# Copyright (C) 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# Check whether the underlying file-system supports filenames
+# with a leading dot. For instance MS-DOS doesn't.
+AC_DEFUN([AM_SET_LEADING_DOT],
+[rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+AC_SUBST([am__leading_dot])])
+
+# Check to see how 'make' treats includes. -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 3
+
+# AM_MAKE_INCLUDE()
+# -----------------
+# Check to see how make treats includes.
+AC_DEFUN([AM_MAKE_INCLUDE],
+[am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+ @echo done
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+AC_MSG_CHECKING([for style of include used by $am_make])
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# We grep out `Entering directory' and `Leaving directory'
+# messages which can occur if `w' ends up in MAKEFLAGS.
+# In particular we don't look at `^make:' because GNU make might
+# be invoked under some other name (usually "gmake"), in which
+# case it prints its new name instead of `make'.
+if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then
+ am__include=include
+ am__quote=
+ _am_result=GNU
+fi
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+ echo '.include "confinc"' > confmf
+ if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then
+ am__include=.include
+ am__quote="\""
+ _am_result=BSD
+ fi
+fi
+AC_SUBST([am__include])
+AC_SUBST([am__quote])
+AC_MSG_RESULT([$_am_result])
+rm -f confinc confmf
+])
+
+# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
+
+# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 5
+
+# AM_MISSING_PROG(NAME, PROGRAM)
+# ------------------------------
+AC_DEFUN([AM_MISSING_PROG],
+[AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+
+
+# AM_MISSING_HAS_RUN
+# ------------------
+# Define MISSING if not defined so far and test if it supports --run.
+# If it does, set am_missing_run to use it, otherwise, to nothing.
+AC_DEFUN([AM_MISSING_HAS_RUN],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([missing])dnl
+test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+ am_missing_run="$MISSING --run "
+else
+ am_missing_run=
+ AC_MSG_WARN([`missing' script is too old or missing])
+fi
+])
+
+# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_MKDIR_P
+# ---------------
+# Check for `mkdir -p'.
+AC_DEFUN([AM_PROG_MKDIR_P],
+[AC_PREREQ([2.60])dnl
+AC_REQUIRE([AC_PROG_MKDIR_P])dnl
+dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P,
+dnl while keeping a definition of mkdir_p for backward compatibility.
+dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
+dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
+dnl Makefile.ins that do not define MKDIR_P, so we do our own
+dnl adjustment using top_builddir (which is defined more often than
+dnl MKDIR_P).
+AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
+case $mkdir_p in
+ [[\\/$]]* | ?:[[\\/]]*) ;;
+ */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+])
+
+# Helper functions for option handling. -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 3
+
+# _AM_MANGLE_OPTION(NAME)
+# -----------------------
+AC_DEFUN([_AM_MANGLE_OPTION],
+[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+
+# _AM_SET_OPTION(NAME)
+# ------------------------------
+# Set option NAME. Presently that only means defining a flag for this option.
+AC_DEFUN([_AM_SET_OPTION],
+[m4_define(_AM_MANGLE_OPTION([$1]), 1)])
+
+# _AM_SET_OPTIONS(OPTIONS)
+# ----------------------------------
+# OPTIONS is a space-separated list of Automake options.
+AC_DEFUN([_AM_SET_OPTIONS],
+[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
+
+# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
+# -------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+AC_DEFUN([_AM_IF_OPTION],
+[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
+
+# Check to make sure that the build environment is sane. -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 4
+
+# AM_SANITY_CHECK
+# ---------------
+AC_DEFUN([AM_SANITY_CHECK],
+[AC_MSG_CHECKING([whether build environment is sane])
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null`
+ if test "$[*]" = "X"; then
+ # -L didn't work.
+ set X `ls -t $srcdir/configure conftest.file`
+ fi
+ rm -f conftest.file
+ if test "$[*]" != "X $srcdir/configure conftest.file" \
+ && test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
+alias in your environment])
+ fi
+
+ test "$[2]" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT(yes)])
+
+# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_STRIP
+# ---------------------
+# One issue with vendor `install' (even GNU) is that you can't
+# specify the program used to strip binaries. This is especially
+# annoying in cross-compiling environments, where the build's strip
+# is unlikely to handle the host's binaries.
+# Fortunately install-sh will honor a STRIPPROG variable, so we
+# always use install-sh in `make install-strip', and initialize
+# STRIPPROG with the value of the STRIP variable (set by the user).
+AC_DEFUN([AM_PROG_INSTALL_STRIP],
+[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'. However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
+if test "$cross_compiling" != no; then
+ AC_CHECK_TOOL([STRIP], [strip], :)
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+
+# Copyright (C) 2006 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
+# This macro is traced by Automake.
+AC_DEFUN([_AM_SUBST_NOTMAKE])
+
+# Check how to create a tarball. -*- Autoconf -*-
+
+# Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# _AM_PROG_TAR(FORMAT)
+# --------------------
+# Check how to create a tarball in format FORMAT.
+# FORMAT should be one of `v7', `ustar', or `pax'.
+#
+# Substitute a variable $(am__tar) that is a command
+# writing to stdout a FORMAT-tarball containing the directory
+# $tardir.
+# tardir=directory && $(am__tar) > result.tar
+#
+# Substitute a variable $(am__untar) that extract such
+# a tarball read from stdin.
+# $(am__untar) < result.tar
+AC_DEFUN([_AM_PROG_TAR],
+[# Always define AMTAR for backward compatibility.
+AM_MISSING_PROG([AMTAR], [tar])
+m4_if([$1], [v7],
+ [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
+ [m4_case([$1], [ustar],, [pax],,
+ [m4_fatal([Unknown tar format])])
+AC_MSG_CHECKING([how to create a $1 tar archive])
+# Loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
+_am_tools=${am_cv_prog_tar_$1-$_am_tools}
+# Do not fold the above two line into one, because Tru64 sh and
+# Solaris sh will not grok spaces in the rhs of `-'.
+for _am_tool in $_am_tools
+do
+ case $_am_tool in
+ gnutar)
+ for _am_tar in tar gnutar gtar;
+ do
+ AM_RUN_LOG([$_am_tar --version]) && break
+ done
+ am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+ am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+ am__untar="$_am_tar -xf -"
+ ;;
+ plaintar)
+ # Must skip GNU tar: if it does not support --format= it doesn't create
+ # ustar tarball either.
+ (tar --version) >/dev/null 2>&1 && continue
+ am__tar='tar chf - "$$tardir"'
+ am__tar_='tar chf - "$tardir"'
+ am__untar='tar xf -'
+ ;;
+ pax)
+ am__tar='pax -L -x $1 -w "$$tardir"'
+ am__tar_='pax -L -x $1 -w "$tardir"'
+ am__untar='pax -r'
+ ;;
+ cpio)
+ am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+ am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+ am__untar='cpio -i -H $1 -d'
+ ;;
+ none)
+ am__tar=false
+ am__tar_=false
+ am__untar=false
+ ;;
+ esac
+
+ # If the value was cached, stop now. We just wanted to have am__tar
+ # and am__untar set.
+ test -n "${am_cv_prog_tar_$1}" && break
+
+ # tar/untar a dummy directory, and stop if the command works
+ rm -rf conftest.dir
+ mkdir conftest.dir
+ echo GrepMe > conftest.dir/file
+ AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+ rm -rf conftest.dir
+ if test -s conftest.tar; then
+ AM_RUN_LOG([$am__untar <conftest.tar])
+ grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+ fi
+done
+rm -rf conftest.dir
+
+AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+AC_SUBST([am__tar])
+AC_SUBST([am__untar])
+]) # _AM_PROG_TAR
+
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..1bcdadd
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,191 @@
+# Process this file with autoconf to produce a configure script.
+AC_INIT([haserl],[0.9.25],[Nathan Angelacos <nangel@users.sourceforge.net>],[haserl])
+AM_INIT_AUTOMAKE([haserl],[$PACKAGE_VERSION])
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_LN_S
+
+# Checks for libraries.
+
+# Checks for header files.
+AC_HEADER_STDC
+AC_CONFIG_HEADERS(src/config.h)
+AC_CHECK_HEADERS([stdlib.h string.h unistd.h signal.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_SIZE_T
+
+# Checks for library functions.
+AC_FUNC_MEMCMP
+AC_CHECK_FUNCS([memset putenv strcasecmp strdup strncasecmp strstr])
+
+AC_DEFINE([_GNU_SOURCE], [], [Enable GNU Extensions])
+
+
+dnl **************************************************************
+AC_ARG_WITH(lua-headers,
+ [ --with-lua-headers=DIR lua include files location],
+ [LUA_HDR_DIR="$withval"]
+ [CFLAGS="$CFLAGS -I$withval"]
+)
+
+
+dnl Checks for lua
+
+luashell=false
+luacshell=false
+ac_report_have_lua=disabled
+ac_report_bash_extensions=disabled
+
+
+AC_ARG_WITH(lua,
+ AC_HELP_STRING([--with-lua[[=DIR]]], [use lua in DIR]),
+[ case "$withval" in
+ no) ac_report_have_lua=disabled
+ ;;
+ *) AC_SEARCH_LIBS(dlopen, dl)
+ # ubuntu has lua5.1 rather than just lua
+ if pkg-config --exists lua5.1; then
+ LUALIB=lua5.1
+ else
+ LUALIB=lua
+ fi
+ if test -z "$LUA_HDR_DIR"; then
+ CFLAGS="$CFLAGS `pkg-config $LUALIB --cflags`"
+ fi
+ LIBS="$LIBS -lm"
+ LDFLAGS="$LDFLAGS -Wl,-E -L$withval"
+ AC_DEFINE(USE_LUA, , [Enable Lua])
+
+ AC_CHECK_LIB($LUALIB, luaL_newstate, , [
+ AC_MSG_ERROR([The Lua runtime library cannot be found!])
+ ], $LIBS)
+ luashell=true
+ luacshell=true
+ ac_report_have_lua=enabled
+ ;;
+ esac ], [
+ ac_report_have_lua=disabled
+])
+AM_CONDITIONAL(USE_LUA, test x$ac_report_have_lua = xenabled)
+
+# If Lua is enabled, the user can choose between two different shells
+
+# shell: ordinary Haserl code with embedded Lua
+AC_ARG_ENABLE(luashell,
+ AC_HELP_STRING([--enable-luashell], [Includes the standard Lua shell - Haserl with embedded Lua (default is yes if Lua's enabled)]),
+[case "${enableval}" in
+ yes) luashell=true ;;
+ no) luashell=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-luashell) ;;
+esac], [])
+AM_CONDITIONAL(INCLUDE_LUASHELL, test x$luashell = xtrue)
+if test x$luashell = xtrue; then
+ AC_DEFINE(INCLUDE_LUASHELL, , [Include ordinary Lua shell])
+fi
+
+# shell: precompiled Haserl code - compiled Lua code
+AC_ARG_ENABLE(luacshell,
+ AC_HELP_STRING([--enable-luacshell], [Includes the compiled Lua shell - precompiled Haserl/Lua (default is yes if Lua's enabled)]),
+[case "${enableval}" in
+ yes) luacshell=true ;;
+ no) luacshell=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-luacshell) ;;
+esac], [])
+AM_CONDITIONAL(INCLUDE_LUACSHELL, test x$luacshell = xtrue)
+if test x$luacshell = xtrue; then
+ AC_DEFINE(INCLUDE_LUACSHELL, , [Include shell for precompiled Haserl/Lua])
+fi
+
+if test \(x$luashell = xtrue -o x$luacshell = xtrue\) -a x$ac_report_have_lua = xdisabled; then
+ AC_MSG_ERROR([Lua is not enabled so you cannot build a Lua shell.])
+fi
+
+# the Linux shell is always available
+
+AC_ARG_ENABLE(bashshell,
+ AC_HELP_STRING([--enable-bashshell], [Includes the Bash or another Linux shell (default is yes)]),
+[case "${enableval}" in
+ yes) bashshell=true ;;
+ no) bashshell=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-bashshell) ;;
+esac], [bashshell=true])
+AM_CONDITIONAL(INCLUDE_BASHSHELL, test x$bashshell = xtrue)
+if test x$bashshell = xtrue; then
+ AC_DEFINE(INCLUDE_BASHSHELL, , [Include Bash/Linux shell])
+fi
+
+# set additional macros
+
+if test x$luashell = xfalse -a x$bashshell = xfalse; then
+ if test x$luacshell = xtrue; then
+ AC_DEFINE(JUST_LUACSHELL, , [Include just the compiled Lua shell])
+ else
+ AC_MSG_ERROR([All shells disabled.])
+ fi
+fi
+
+if test x$ac_report_have_lua = xenabled -a x$luashell = xfalse -a x$luacshell = xfalse; then
+ AC_MSG_ERROR([Cannot enable Lua if no Lua shell is included.])
+fi
+
+
+# Do we include scott's bash extensions
+AC_ARG_ENABLE(bash-extensions,
+ AC_HELP_STRING([--enable-bash-extensions], [Includes bash extensions to the haserl script language (default is no)]),
+[case "${enableval}" in
+ yes) bashextensions=true
+ ac_report_bash_extensions=enabled
+ ;;
+ no) bashextensions=false
+ ac_report_bash_extensions=disabled
+ ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-bash-extensions) ;;
+esac], [bashextensions=false])
+AM_CONDITIONAL(BASHEXTENSIONS, test x$bashextensions = xtrue)
+if test x$bashextensions = xtrue; then
+ AC_DEFINE(BASHEXTENSIONS, , [Include bash extensions])
+fi
+
+
+# What subshell should we use
+AC_ARG_ENABLE(subshell,
+ [ --enable-subshell=cmd - enable the subshell to use (default is /bin/sh)])
+
+# Handle "--disable-subshell"
+case $enable_subshell in
+ no ) enable_subshell=
+ ;;
+ "" ) enable_subshell="/bin/sh"
+ ;;
+ esac
+
+AC_DEFINE_UNQUOTED( [SUBSHELL_CMD], "$enable_subshell",
+ [the subshell to start up])
+
+CFLAGS="$CFLAGS -Wall"
+
+AC_CONFIG_FILES([Makefile
+ src/Makefile
+ doc/Makefile
+ tests/Makefile
+ ])
+
+
+
+
+AC_OUTPUT
+
+dnl report configuration
+AC_MSG_RESULT([
+** Configuration summary for $PACKAGE $VERSION:
+
+ Building haserl with with lua $ac_report_have_lua
+ Building haserl with with bash-extensions $ac_report_bash_extensions
+ libs: $LIBS
+])
+
+
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 0000000..9127ef0
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,9 @@
+PACKAGE = @PACKAGE_TARNAME@
+EXTRA_DIST = haserl.1
+
+docdir=$(datadir)/$(PACKAGE)
+datarootdir=@datarootdir@
+
+man_MANS = haserl.1
+
+
diff --git a/doc/haserl.1 b/doc/haserl.1
new file mode 100644
index 0000000..c683405
--- /dev/null
+++ b/doc/haserl.1
@@ -0,0 +1,647 @@
+.\" Process with groff -man -Tascii haserl.1
+.TH haserl 1 "July 2007"
+.SH NAME
+haserl \- A cgi scripting program for embedded environments
+.SH SYNOPSIS
+.BI "#!/usr/bin/haserl [--shell=" pathspec "] [--upload-dir=" dirspec "] [--upload-handler=" handler "] [--upload-limit=" limit "] [--accept-all] [--accept-none] [--silent] [--debug]"
+
+[ text ] [ <% shell script %> ] [ text ] ...
+
+.SH DESCRIPTION
+Haserl is a small cgi wrapper that allows "PHP" style cgi programming,
+but uses a UNIX bash-like shell or Lua as the programming language. It is
+very small, so it can be used in embedded environments, or where
+something like PHP is too big.
+
+It combines three features into a small cgi engine:
+
+.IP
+It parses POST and GET requests, placing form-elements as
+name=value
+pairs into the environment for the CGI script to use. This is somewhat like
+the
+.IR uncgi " wrapper."
+.IP
+It opens a shell, and translates all text into printable statements.
+All text within <% ... %> constructs are passed verbatim to the shell.
+This is somewhat like
+.RI writing " PHP " "scripts."
+.IP
+It can optionally be installed to drop its permissions to the owner of the
+script, giving
+it some of the security features of
+.IR suexec " or " cgiwrapper .
+.SH OPTIONS SUMMARY
+
+This is a summary of the command-line options. Please see the
+.B OPTIONS
+section under the long option name for a complete description.
+
+-a --accept-all
+.br
+-n --accept-none
+.br
+-d --debug
+.br
+-s, --shell
+.br
+-S, --silent
+.br
+-U, --upload-dir
+.br
+-u, --upload-limit
+.br
+-H, --upload-handler
+.br
+
+.SH OPTIONS
+
+.TP
+.BI --accept-all
+The program normally accepts POST data only when the REQUEST_METHOD is POST and only accepts
+data on the URL data when the REQUEST_METHOD is GET. This option allows both POST and
+URL data to be accepted regardless of the REQUEST_METHOD. When this option is set,
+the REQUEST_METHOD takes precedence (e.g. if the method is POST, FORM_variables are taken
+from COOKIE data, GET data, and POST data, in that order. If the method is GET, FORM_variables
+are taken from COOKIE data, POST data, and GET data.) The default is not to accept all
+input methods - just the COOKIE data and the REQUEST_METHOD.
+
+.TP
+.BI --accept-none
+If given, haserl will not parse standard input as http content before
+processing the script. This is useful if calling a haserl script from
+another haserl script.
+
+.TP
+.BI --debug
+Instead of executing the script, print out the script that would be executed. If the environment variable 'REQUEST_METHOD' is set, the data is sent with the plain/text content type. Otherwise, the shell script is printed verbatim.
+
+.TP
+.BI --shell= "pathspec "
+Specify an alternative bash-like shell to use. Defaults to "/bin/sh"
+
+To include shell parameters do not use the --shell=/bin/sh format. Instead, use the alternative format without the "=", as in --shell "/bin/bash --norc". Be sure to quote the option string to protect any special characters.
+
+If compiled with Lua libraries, then the string "lua" is used to use an integrated Lua vm. This string is case sensitive. Example:
+.BI --shell= lua
+
+An alternative is "luac". This causes the haserl and lua parsers to be disabled, and the
+script is assumed to be a precompiled lua chunk. See
+.B LUAC
+below for more information.
+
+.TP
+.BI --silent
+Haserl normally prints an informational message on error conditions. This
+suppresses the error message, so that the use of haserl is not advertised.
+
+.TP
+.BI --upload-dir= "dirspec "
+Defaults to "/tmp". All uploaded files are created with temporary filename in this
+directory
+.BR FORM_xxx " contains the name of the temporary file." FORM_xxx_name
+contains the original name of the file, as specified by the client.
+
+.TP
+.BI --upload-handler= "pathspec "
+When specified, file uploads are handled by this handler, rather than written
+to temporary files. The full pathspec must be given (the PATH is not
+searched), and the upload-handler is given one command-line parameter:
+The name of the FIFO on which the upload file
+will be sent. In addition, the handler may receive 3 environment variables:
+.BR CONTENT_TYPE ", " FILENAME ", and " NAME .
+These reflect the MIME content-disposition headers for the content. Haserl
+will fork the handler for each file uploaded, and will send the contents
+of the upload file to the specified FIFO. Haserl will then block until
+the handler terminates. This method is for experts only.
+
+.TP
+.BI --upload-limit= "limit "
+Allow a mime-encoded file up to
+.I limit KB
+to be uploaded. The default is
+.I 0KB
+(no uploads allowed).
+Note that mime-encoding adds 33% to the size of the data.
+
+.SH OVERVIEW OF OPERATION
+
+In general, the web server sets up several environment variables, and then uses
+.I fork
+or another method to run the CGI script. If the script uses the
+.I haserl
+interpreter, the following happens:
+
+.IP
+If
+.I haserl
+is installed suid root, then uid/gid is set to the owner of the script.
+
+The environment is scanned for
+.IR HTTP_COOKIE ,
+which may have been set by the web server. If it exists, the parsed contents
+are placed in the local environment.
+
+The environment is scanned for
+.IR REQUEST_METHOD ,
+which was set by the web server. Based on the request method, standard input
+is read and parsed. The parsed contents are placed in the local environment.
+
+The script is tokenized, parsing
+.I haserl
+code blocks from raw text. Raw text is converted into "echo" statements, and
+then all tokens are sent to the sub-shell.
+
+.I haserl
+forks and a sub-shell (typically
+.IR /bin/sh )
+is started.
+
+All tokens are sent to the STDIN of the sub-shell, with a trailing
+.B exit
+command.
+
+When the sub-shell terminates, the
+.I haserl
+interpreter performs final cleanup and then terminates.
+
+
+.SH CLIENT SIDE INPUT
+The
+.I haserl
+interpreter will decode data sent via the HTTP_COOKIE environment variable, and the GET or POST method from the client,
+and store them as environment variables that can be accessed by haserl.
+The name of the variable follows the name given in the source, except that a prefix (
+.IR FORM_ )
+is prepended. For example, if the client sends "foo=bar", the environment variable is
+.BR FORM_foo = bar .
+
+For the HTTP_COOKIE method, variables are also stored with the prefix (
+.IR COOKIE_ )
+added. For example, if HTTP_COOKIE includes "foo=bar", the environment variable is
+.BR COOKIE_foo = bar .
+
+For the GET method, data sent in the form %xx is translated into the characters
+they represent, and variables are also stored with the prefix (
+.IR GET_ )
+added. For example, if QUERY_STRING includes "foo=bar", the environment variable is
+.BR GET_foo = bar .
+
+For the POST method, variables are also stored with the prefix (
+.IR POST_ )
+added. For example, if the post stream includes "foo=bar", the environment variable is
+.BR POST_foo = bar .
+
+Also, for the POST method, if the data is sent using
+.I "multipart/form-data"
+encoding, the data is automatically decoded. This is typically used when
+files are uploaded from a web client using <input type=file>.
+
+.TP
+.B NOTE
+When a file is uploaded to the web server, it is stored in the
+.I upload-target
+directory.
+.RI The FORM_variable= "variable contains the name of the file uploaded"
+(as specified by the client.) The
+.I FORM_variable_name=
+variable contains the name of the file in
+.I upload-target
+that contains the uploaded content. To prevent malicious clients from
+filling up
+.I upload-target
+on your web server, file uploads are only allowed when the
+.I --upload-limit
+option is used to specify how large a file can be uploaded. Haserl automatically
+deletes the temporary file when the script is finished. To keep the file, move it
+or rename it somewhere in the script.
+
+.P
+If the client sends data
+.I both
+by POST and GET methods, then
+.I haserl
+will parse only the data that corresponds with the
+.I REQUEST_METHOD
+variable set by the web server, unless the
+.I accept-all
+option has been set. For example, a form called via POST method, but having a
+URI of some.cgi?foo=bar&otherdata=something will have the POST data parsed, and the
+.IR foo " and " otherdata
+variables are ignored.
+
+.P
+If the web server defines a
+.I HTTP_COOKIE
+environment variable, the cookie data is parsed. Cookie data is parsed
+.I before
+the GET or POST data, so in the event of two variables of the same name, the
+GET or POST data overwrites the cookie information.
+
+.P
+When multiple instances of the same variable are sent from different sources, the FORM_variable will be set according to the order in which variables are processed. HTTP_COOKIE is always processed first, followed by the REQUEST_METHOD. If the accept-all option has been set, then HTTP_COOKIE is processed first, followed by the method not specified by REQUEST_METHOD, followed by the REQUEST_METHOD. The last instance of the variable will be used to set FORM_variable. Note that the variables are also separately creates as COOKIE_variable, GET_variable and POST_variable. This allows the use of overlapping names from each source.
+
+.P
+When multiple instances of the same variable are sent from the same source,
+only the last one is saved. To keep all copies (for multi-selects, for
+instance), add "[]" to the end of the
+variable name. All results will be returned, separated by newlines. For example,
+host=Enoch&host=Esther&host=Joshua results in "FORM_host=Joshua".
+host[]=Enoch&host[]Esther&host[]=Joshua results in "FORM_host=Enoch\\nEsther\\nJoshua"
+
+.SH LANGUAGE
+The following language structures are recognized by
+.IR haserl .
+
+.TP
+.B "RUN"
+.nf
+<% [shell script] %>
+.sp
+.fi
+Anything enclosed by <% %> tags is sent to the sub-shell for execution. The
+text is sent verbatim.
+
+.TP
+.B "INCLUDE"
+.nf
+<%in pathspec %>
+.sp
+.fi
+Include another file verbatim in this script. The file is included when the script is
+initially parsed.
+
+.TP
+.B "EVAL"
+.nf
+<%= expression %>
+.sp
+.fi
+print the shell expression. Syntactic sugar for "echo expr".
+
+.TP
+.B "COMMENT"
+.nf
+<%# comment %>
+.sp
+.fi
+Comment block. Anything in a comment block is not parsed. Comments can be nested and can contain
+other haserl elements.
+
+.SH EXAMPLES
+.TP
+.B WARNING
+The examples below are simplified to show how to use
+.IR haserl .
+You should be familiar with basic web scripting security before using
+.I haserl
+(or any scripting language) in a production environment.
+
+.TP
+.B Simple Command
+.nf
+#!/usr/local/bin/haserl
+content-type: text/plain
+.sp
+<%# This is a sample "env" script %>
+<% env %>
+.fi
+
+Prints the results of the
+.I env
+command as a mime-type "text/plain" document. This is the
+.I haserl
+version of the common
+.I printenv
+cgi.
+
+.TP
+.B Looping with dynamic output
+.nf
+#!/usr/local/bin/haserl
+Content-type: text/html
+.sp
+<html>
+<body>
+<table border=1><tr>
+<% for a in Red Blue Yellow Cyan; do %>
+ <td bgcolor="<% echo -n "$a" %>"><% echo -n "$a" %></td>
+ <% done %>
+</tr></table>
+</body>
+</html>
+.fi
+
+Sends a mime-type "text/html" document to the client, with an html table
+of with elements labeled with the background color.
+
+.TP
+.B Use Shell defined functions.
+.nf
+#!/usr/local/bin/haserl
+content-type: text/html
+.sp
+<% # define a user function
+ table_element() {
+ echo "<td bgcolor=\\"$1\\">$1</td>"
+ }
+ %>
+<html>
+<body>
+<table border=1><tr>
+<% for a in Red Blue Yellow Cyan; do %>
+ <% table_element $a %>
+ <% done %>
+</tr></table>
+</body>
+</html>
+.fi
+
+Same as above, but uses a shell function instead of embedded html.
+
+.TP
+.B Self Referencing CGI with a form
+.nf
+#!/usr/local/bin/haserl
+content-type: text/html
+.sp
+<html><body>
+<h1>Sample Form</h1>
+<form action="<% echo -n $SCRIPT_NAME %>" method="GET">
+<% # Do some basic validation of FORM_textfield
+ # To prevent common web attacks
+ FORM_textfield=$( echo "$FORM_textfield" | sed "s/[^A-Za-z0-9 ]//g" )
+ %>
+<input type=text name=textfield
+ Value="<% echo -n "$FORM_textfield" | tr a-z A-Z %>" cols=20>
+<input type=submit value=GO>
+</form></html>
+</body>
+.fi
+
+Prints a form. If the client enters text in the form, the CGI is reloaded (defined by
+.IR $SCRIPT_NAME )
+and the textfield is sanitized to prevent web attacks, then the form is redisplayed with the text the user entered. The text is uppercased.
+
+.TP
+.B Uploading a File
+.nf
+#!/usr/local/bin/haserl --upload-limit=4096 --upload-target=/tmp
+content-type: text/html
+.sp
+<html><body>
+<form action="<% echo -n $SCRIPT_NAME %>" method=POST enctype="multipart/form-data" >
+<input type=file name=uploadfile>
+<input type=submit value=GO>
+<br>
+<% if test -n "$FORM_uploadfile"; then %>
+ <p>
+ You uploaded a file named <b><% echo -n $FORM_uploadfile_name %></b>, and it was
+ temporarily stored on the server as <i><% echo $FORM_uploadfile %></i>. The
+ file was <% cat $FORM_uploadfile | wc -c %> bytes long.</p>
+ <% rm -f $FORM_uploadfile %><p>Don't worry, the file has just been deleted
+ from the web server.</p>
+<% else %>
+ You haven't uploaded a file yet.
+<% fi %>
+</form>
+</body></html>
+.fi
+
+Displays a form that allows for file uploading. This is accomplished by using the
+.B --upload-limit
+and by setting the form
+.I enctype
+.RI "to " multipart/form-data.
+If the client sends a file, then some information regarding the file is printed, and then deleted. Otherwise, the form states that the client has not uploaded a file.
+
+
+
+.SH ENVIRONMENT
+In addition to the environment variables inherited from the web server, the following environment variables are always defined at startup:
+
+.IP HASERLVER
+.I haserl
+version - an informational tag.
+.IP SESSIONID
+A hexadecimal tag that is unique for the life of the CGI (it is generated when the cgi starts; and does not change until another POST or GET query is generated.)
+.IP HASERL_ACCEPT_ALL
+.RI "If the " --accept-all " flag was set, " -1 ", otherwise " 0 "."
+.IP HASERL_SHELL
+The name of the shell haserl started to run sub-shell commands in.
+.IP HASERL_UPLOAD_DIR
+The directory haserl will use to store uploaded files.
+.IP HASERL_UPLOAD_LIMIT
+The number of KB that are allowed to be sent from the client to the server.
+
+.P
+These variables can be modified or overwritten within the script, although the ones starting with
+"HASERL_" are informational only, and do not affect the running script.
+
+.SH SAFETY FEATURES
+There is much literature regarding the dangers of using shell to program CGI scripts.
+.IR haserl " contains " some
+protections to mitigate this risk.
+
+.TP
+.B Environment Variables
+The code to populate the environment variables is outside the scope of the sub-shell. It parses on the characters ? and &, so it is harder for a client to do "injection" attacks. As an example,
+.I foo.cgi?a=test;cat /etc/passwd
+could result in a variable being assigned the value
+.B test
+and then the results of running
+.I cat /etc/passwd
+being sent to the client.
+.I Haserl
+will assign the variable the complete value:
+.B test;cat /etc/passwd
+
+It is safe to use this "dangerous" variable in shell scripts by enclosing it in quotes; although validation should be done on all input fields.
+
+.TP
+.B Privilege Dropping
+If installed as a suid script,
+.I haserl
+will set its uid/gid to that of the owner of the script. This can be used to have a set of CGI scripts that have various privilege. If the
+.I haserl
+binary is not installed suid, then the CGI scripts will run with the uid/gid of the web server.
+
+.TP
+.B Reject command line parameters given on the URL
+If the URL does not contain an unencoded "=", then the CGI spec states the options are to be
+used as command-line parameters to the program. For instance, according to the CGI spec:
+.I http://192.168.0.1/test.cgi?--upload-limit%3d2000&foo%3dbar
+Should set the upload-limit to 2000KB in addition to setting "Foo=bar".
+To protect against clients enabling their own uploads,
+.I haserl
+rejects any command-line options beyond argv[2]. If invoked as a #!
+script, the interpreter is argv[0], all command-line options listed in the #! line are
+combined into argv[1], and the script name is argv[2].
+
+.SH LUA
+
+If compiled with lua support,
+.B --shell=lua
+will enable lua as the script language instead of bash shell. The environment variables
+(SCRIPT_NAME, SERVER_NAME, etc) are placed in the ENV table, and the form variables are
+placed in the FORM table. For example, the self-referencing form above can be written like this:
+
+.RS
+.nf
+#!/usr/local/bin/haserl --shell=lua
+content-type: text/html
+.sp
+<html><body>
+<h1>Sample Form</h1>
+<form action="<% io.write(ENV["SCRIPT_NAME"]) %>" method="GET">
+<% # Do some basic validation of FORM_textfield
+ # To prevent common web attacks
+ FORM.textfield=string.gsub(FORM.textfield, "[^%a%d]", "")
+ %>
+<input type=text name=textfield
+ Value="<% io.write (string.upper(FORM.textfield)) %>" cols=20>
+<input type=submit value=GO>
+</form></html>
+</body>
+.fi
+.RE
+
+The <%= operator is syntactic sugar for
+.I io.write (tostring( ... ))
+So, for example, the Value= line above could be written:
+.B Value="<%= string.upper(FORM.textfield) %>" cols=20>
+
+haserl lua scripts can use the function
+.BI haserl.loadfile( filename )
+to process a target script as a haserl (lua) script. The function returns a type of "function".
+
+For example,
+
+bar.lsp
+.RS
+.nf
+<% io.write ("Hello World" ) %>
+.sp
+Your message is <%= gvar %>
+.sp
+-- End of Include file --
+.fi
+.RE
+
+foo.haserl
+.RS
+.nf
+#!/usr/local/bin/haserl --shell=lua
+<% m = haserl.loadfile("bar.lsp")
+ gvar = "Run as m()"
+ m()
+
+ gvar = "Load and run in one step"
+ haserl.loadfile("bar.lsp")()
+%>
+.fi
+.RE
+
+Running
+.I foo
+will produce:
+
+.RS
+.nf
+Hello World
+Your message is Run as m()
+-- End of Include file --
+Hello World
+Your message is Load and run in one step
+-- End of Include file --
+.fi
+.TE
+
+This function makes it possible to have nested haserl server pages - page snippets that are
+processed by the haserl tokenizer.
+
+.SH LUAC
+
+The
+.I luac
+"shell" is a precompiled lua chunk, so interactive editing and testing of scripts is
+not possible. However, haserl can be compiled with luac support only, and this allows
+lua support even in a small memory environment. All haserl lua features listed above
+are still available. (If luac is the only shell built into haserl, the haserl.loadfile is
+disabled, as the haserl parser is not compiled in.)
+
+Here is an example of a trivial script, converted into a luac cgi script:
+
+Given the file test.lua:
+.RS
+.nf
+print ("Content-Type: text/plain\n\n")
+print ("Your UUID for this run is: " .. ENV.SESSIONID)
+.fi
+.RE
+
+It can be compiled with luac:
+.RS
+luac -o test.luac -s test.lua
+.RE
+
+And then the haserl header added to it:
+.RS
+echo '#!/usr/bin/haserl --shell=luac' | cat - test.luac >luac.cgi
+.RE
+
+Alternatively, it is possible to develop an entire website using the standard lua shell,
+and then have haserl itself preprocess the scripts for the luac compiler as part of a build
+process. To do this, use --shell=lua, and develop the website. When ready to build
+the runtime environment, add the --debug line to your lua scripts, and run them outputting
+the results to .lua source files. For example:
+
+Given the haserl script test.cgi:
+.RS
+.nf
+#!/usr/bin/haserl --shell=lua --debug
+Content-Type: text/plain
+
+Your UUID for this run is <%= ENV.SESSIONID %>
+.fi
+.RE
+
+Precompile, compile, and add the haserl luac header:
+.RS
+.nf
+./test.cgi > test.lua
+luac -s -o test.luac test.lua
+echo '#!/usr/bin/haserl --shell=luac' | cat - test.luac >luac.cgi
+.fi
+.RS
+
+.SH BUGS
+Old versions of haserl used <? ?> as token markers, instead of <% %>. Haserl
+will fall back to using <? ?>
+.I if <% does not appear anywhere in the script.
+
+.SH NAME
+The name "haserl" comes from the Bavarian word for "bunny." At first glance it
+may be small and cute, but
+.I haserl
+is more like the bunny from
+.IR "Monty Python & The Holy Grail" .
+In the words of Tim the Wizard,
+.I That's the most foul, cruel & bad-tempered rodent you ever set eyes on!
+
+Haserl can be thought of the cgi equivalent to
+.IR netcat .
+Both are small, powerful, and have very little in the way of extra features. Like
+.IR netcat ", " haserl
+attempts to do its job with the least amount of extra "fluff".
+
+
+.SH AUTHOR
+Nathan Angelacos <nangel@users.sourceforge.net>
+
+.SH SEE ALSO
+
+.BR php (http://www.php.net)
+.BR uncgi (http://www.midwinter.com/~koreth/uncgi.html)
+.BR cgiwrapper (http://cgiwrapper.sourceforge.net)
+
diff --git a/haserl.doxy b/haserl.doxy
new file mode 100644
index 0000000..77596f7
--- /dev/null
+++ b/haserl.doxy
@@ -0,0 +1,1357 @@
+# Doxyfile 1.5.5
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = haserl
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = /var/www/haserl
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek,
+# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish,
+# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish,
+# and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = ./src
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS = *.c \
+ *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code. Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is enabled by default, which results in a transparent
+# background. Warning: Depending on the platform used, enabling this option
+# may lead to badly anti-aliased labels on the edges of a graph (i.e. they
+# become hard to read).
+
+DOT_TRANSPARENT = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..5d38878
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,66 @@
+PACKAGE=@PACKAGE_TARNAME@
+
+EXTRA_DIST = haserl_lualib.lua haserl_lualib.inc lua2c.c
+
+datarootdir=@datarootdir@
+
+if USE_LUA
+LUASOURCE = haserl_lualib.inc h_lua_common.c h_lua_common.h
+if INCLUDE_LUASHELL
+LUASOURCE += h_lua.c h_lua.h
+endif
+if INCLUDE_LUACSHELL
+LUASOURCE += h_luac.c h_luac.h
+endif
+endif
+
+if INCLUDE_BASHSHELL
+BASHSOURCE = h_bash.c h_bash.h
+endif
+
+if USE_LUA
+
+haserl_lualib.inc : haserl_lualib.lua
+ @echo '-----------------------------------------------------'
+ @echo 'Whoops. haserl_lualib.inc is old. You will need'
+ @echo 'to compile lua2c by hand, or help the maintainer'
+ @echo 'get automake to do it for you.'
+ @echo ''
+ @echo 'For now, to compile lua2c:'
+ @echo ''
+ @echo 'gcc -I<luaheaderdir> -Wl,-E -L<lualibdir> -o lua2c lua2c.c -llua -ldl -lm'
+ @echo ''
+ @echo ''
+ @echo 'Then follow the instructions in lua2c.c to create a'
+ @echo 'new haserl_lualib.inc'
+ @echo ''
+ @echo 'Sorry.'
+ @echo '-----------------------------------------------------'
+ @exit 1
+
+h_lua_common.c : haserl_lualib.inc
+
+endif
+
+bin_PROGRAMS = haserl
+
+haserl_SOURCES = common.c common.h sliding_buffer.c sliding_buffer.h \
+ h_error.c h_error.h h_script.c h_script.h rfc2388.c rfc2388.h \
+ $(BASHSOURCE) $(LUASOURCE) haserl.c haserl.h
+
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+
+
+#--------- Unit Tests ----------------------
+#TESTS = utest_common
+
+#check_PROGRAMS = utest_common
+
+#utest_common: utest_common.c
+
+#--------- End Unit Tests -------------------
+
diff --git a/src/common.c b/src/common.c
new file mode 100644
index 0000000..2ee4afb
--- /dev/null
+++ b/src/common.c
@@ -0,0 +1,370 @@
+/*
+ * --------------------------------------------------------------------------
+ * Common library functions for the haserl suite
+ * Copyright (c) 2005-2007 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 2, as published by the
+ * Free Software Foundation.
+ *
+ * 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
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "common.h"
+#include "h_error.h"
+
+
+/* we define this here, but should use the header files instead. */
+void *xrealloc (void *buf, size_t size);
+
+
+/*
+ * split a string into an argv[] array, and return the number of elements.
+ * Warning: Overwrites instr with nulls (to make ASCIIZ strings) and mallocs
+ * space for the argv array. The argv array will point to the offsets in
+ * instr where the elements occur. The calling function must free the argv
+ * array, and should do so before freeing instr memory.
+ *
+ * If comment points to a non-null string, then any character in the string
+ * will mark the beginning of a comment until the end-of-line:
+ *
+ * comment="#;"
+ * foo bar # This is a comment
+ * foo baz ; This is a comment as well
+ *
+ * Example of use:
+ *
+ * int argc, count; argv_t *argv; char string[2000];
+ *
+ * strcpy (string, "This\\ string will be \"separated into\" '6' elements.", "");
+ * argc = argc_argv (string, &argv);
+ * for (count = 0; count < argc; count++) {
+ * printf ("%03d: %s\n", count, argv[count].string);
+ * }
+ * free (argv);
+ *
+ */
+
+
+int
+argc_argv (char *instr, argv_t ** argv, char *commentstr)
+{
+ char quote = '\0';
+ int arg_count = 0;
+ enum state_t
+ {
+ WHITESPACE, WORDSPACE, TOKENSTART
+ } state = WHITESPACE;
+ argv_t *argv_array = NULL;
+ int argc_slots = 0;
+ size_t len, pos;
+
+ len = strlen (instr);
+ pos = 0;
+
+ while (pos < len)
+ {
+ // printf ("%3d of %3d: %s\n", pos, len, instr);
+
+ /* Comments are really, really special */
+
+ if ((state == WHITESPACE) && (strchr (commentstr, *instr)))
+ {
+ while ((*instr != '\n') && (*instr != '\0'))
+ {
+ instr++;
+ pos++;
+ }
+ }
+
+ switch (*instr)
+ {
+
+ /* quoting */
+ case '"':
+ case '\'':
+ /* Begin quoting */
+ if (state == WHITESPACE)
+ {
+ quote = *instr;
+ state = TOKENSTART;
+ if (*(instr + 1) == quote)
+ { /* special case for NULL quote */
+ quote = '\0';
+ *instr = '\0';
+ argv_array[arg_count].quoted = -1;
+ }
+ else
+ {
+ instr++;
+ pos++;
+ }
+ }
+ else
+ { /* WORDSPACE, so quotes end or quotes within quotes */
+
+ /* Is it the same kind of quote? */
+ if (*instr == quote)
+ {
+ argv_array[arg_count - 1].quoted = -1;
+ quote = '\0';
+ *instr = '\0';
+ state = WHITESPACE;
+ }
+ }
+ break;
+
+ /* backslash - if escaping a quote within a quote */
+ case '\\':
+ if ((quote) && (*(instr + 1) == quote))
+ {
+ memmove (instr, instr + 1, strlen (instr));
+ len--;
+ }
+ /* otherwise, its just a normal character */
+ else
+ {
+ if (state == WHITESPACE)
+ {
+ state = TOKENSTART;
+ }
+ }
+ break;
+
+
+ /* whitepsace */
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ if ((state == WORDSPACE) && (quote == '\0'))
+ {
+ state = WHITESPACE;
+ *instr = '\0';
+ }
+ break;
+
+ case '\0':
+ break;
+
+ default:
+ if (state == WHITESPACE)
+ {
+ state = TOKENSTART;
+ }
+
+ } /* end switch */
+
+ if (state == TOKENSTART)
+ {
+ arg_count++;
+ if (arg_count > argc_slots)
+ {
+ argc_slots += ALLOC_CHUNK;
+ argv_array =
+ (argv_t *) xrealloc (argv_array,
+ sizeof (argv_t) * (argc_slots +
+ ALLOC_CHUNK));
+ }
+
+ if (argv_array == NULL)
+ {
+ return (-1);
+ }
+ argv_array[arg_count - 1].string = instr;
+ argv_array[arg_count].quoted = 0;
+ state = WORDSPACE;
+ }
+
+ instr++;
+ pos++;
+ }
+
+ argv_array[arg_count].string = NULL;
+ *argv = argv_array;
+ return (arg_count);
+}
+
+/* Expandable Buffer is a reimplementation based on buffer.c in GCC
+ originally by Per Bother */
+
+void
+buffer_init (buffer_t * buf)
+{
+ buf->data = NULL;
+ buf->ptr = NULL;
+ buf->limit = NULL;
+}
+
+void
+buffer_destroy (buffer_t * buf)
+{
+ if (buf->data)
+ {
+ free (buf->data);
+ }
+ buffer_init (buf);
+}
+
+/* don't reallocate - just forget about the current contents */
+void
+buffer_reset (buffer_t * buf)
+{
+ if (buf->data)
+ {
+ buf->ptr = buf->data;
+ }
+ else
+ {
+ buf->ptr = NULL;
+ }
+}
+
+
+void
+buffer_add (buffer_t * buf, const void *data, unsigned long size)
+{
+ unsigned long newsize;
+ unsigned long index;
+
+ /* if we need to grow the buffer, do so now */
+ if ((buf->ptr + size) >= buf->limit)
+ {
+ index = (buf->limit - buf->data);
+ newsize = index;
+ while (newsize <= index + size)
+ {
+ newsize += 1024;
+ }
+ index = buf->ptr - buf->data;
+ buf->data = realloc (buf->data, newsize);
+ buf->limit = buf->data + newsize;
+ buf->ptr = buf->data + index;
+ }
+
+ memcpy (buf->ptr, data, size);
+ buf->ptr += size;
+}
+
+#ifndef JUST_LUACSHELL
+
+/* uppercase an entire string, using toupper */
+void
+uppercase (char *instr)
+{
+ while (*instr != '\0')
+ {
+ *instr = toupper (*instr);
+ instr++;
+ }
+}
+
+
+/* lowercase an entire string, using tolower */
+void
+lowercase (char *instr)
+{
+ while (*instr != '\0')
+ {
+ *instr = tolower (*instr);
+ instr++;
+ }
+}
+
+/* return ptr to first non-whitespace character */
+char *
+skip_whitespace (char *instr)
+{
+ while (isspace (*instr) && *instr)
+ instr++;
+ return instr;
+}
+
+
+/* return ptr to first whitespace character */
+char *
+find_whitespace (char *instr)
+{
+ while (!isspace (*instr) && *instr)
+ instr++;
+ return instr;
+}
+
+
+
+/* Counts the number of newlines in a buffer */
+int
+count_lines (char *instr, size_t len, char *where)
+{
+ size_t line = 1;
+ while ((where > instr) && (len))
+ {
+ if (*instr == '\n')
+ line++;
+ len--;
+ instr++;
+ }
+ return line;
+}
+
+#endif
+
+#ifdef TEST_FRAMEWORK
+
+main ()
+{
+
+ int argc, count;
+ argv_t *argv;
+ char string[2000];
+
+
+ strcpy (string,
+ "\\This\\ string will be '' \"separated into\" \"'\\\"'\" ' 15 ' elements.\n"
+ "' including a multi-line\n"
+ "element' with a comment. # This should not be parsed\n"
+ ";Nor should this\n" "The End.");
+
+ printf ("%s\n", string);
+
+ argc = argc_argv (string, &argv, "#;");
+ for (count = 0; count < argc; count++)
+ {
+ printf ("%03d: [%s] ", count, argv[count].string, "");
+ if (argv[count].quoted)
+ {
+ printf ("(it was quoted)");
+ }
+ printf ("\n");
+ }
+ if (argc != 15)
+ {
+ puts ("Test FAILED");
+ }
+ else
+ {
+ puts ("Test PASSED");
+ }
+ free (argv);
+}
+
+#endif
diff --git a/src/common.h b/src/common.h
new file mode 100644
index 0000000..dbf33aa
--- /dev/null
+++ b/src/common.h
@@ -0,0 +1,62 @@
+/*
+ * $Id: common.h,v 1.5 2005/11/21 22:05:34 nangel Exp $
+ */
+#ifndef _COMMON_H
+#define _COMMON_H 1
+
+
+/* how many argv slots to allocate at once */
+#define ALLOC_CHUNK 10
+
+
+#define STDIN 0
+#define STDOUT 1
+#define STDERR 2
+
+
+#define TRUE -1
+#define FALSE 0
+#define NONE 1
+
+/* linked list */
+typedef struct
+{
+ char *buf;
+ void *next;
+} list_t;
+
+/* name/value pairs */
+typedef struct
+{
+ char *string; /* the string */
+ unsigned char quoted; /* non-zero if the string was quoted */
+} argv_t;
+
+/* expandable buffer structure */
+typedef struct
+{
+ unsigned char *data; /* the data */
+ unsigned char *ptr; /* where to write to next */
+ unsigned char *limit; /* maximal allocated buffer pos */
+} buffer_t;
+
+
+/* common.c */
+
+int argc_argv (char *instr, argv_t ** argv, char *commentstr);
+void buffer_init (buffer_t * buf);
+void buffer_reset (buffer_t * buf);
+void buffer_destroy (buffer_t * buf);
+void buffer_add (buffer_t * buf, const void *data, unsigned long size);
+
+#ifndef JUST_LUACSHELL
+
+void uppercase (char *instr);
+void lowercase (char *instr);
+char *skip_whitespace (char *instr);
+char *find_whitespace (char *instr);
+int count_lines (char *instr, size_t len, char *where);
+
+#endif
+
+#endif /* !_COMMON_H */
diff --git a/src/config.h.in b/src/config.h.in
new file mode 100644
index 0000000..215b591
--- /dev/null
+++ b/src/config.h.in
@@ -0,0 +1,103 @@
+/* src/config.h.in. Generated from configure.ac by autoheader. */
+
+/* Include bash extensions */
+#undef BASHEXTENSIONS
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `memset' function. */
+#undef HAVE_MEMSET
+
+/* Define to 1 if you have the `putenv' function. */
+#undef HAVE_PUTENV
+
+/* Define to 1 if you have the <signal.h> header file. */
+#undef HAVE_SIGNAL_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#undef HAVE_STRCASECMP
+
+/* Define to 1 if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strncasecmp' function. */
+#undef HAVE_STRNCASECMP
+
+/* Define to 1 if you have the `strstr' function. */
+#undef HAVE_STRSTR
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Include Bash/Linux shell */
+#undef INCLUDE_BASHSHELL
+
+/* Include shell for precompiled Haserl/Lua */
+#undef INCLUDE_LUACSHELL
+
+/* Include ordinary Lua shell */
+#undef INCLUDE_LUASHELL
+
+/* Include just the compiled Lua shell */
+#undef JUST_LUACSHELL
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* the subshell to start up */
+#undef SUBSHELL_CMD
+
+/* Enable Lua */
+#undef USE_LUA
+
+/* Version number of package */
+#undef VERSION
+
+/* Enable GNU Extensions */
+#undef _GNU_SOURCE
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
diff --git a/src/h_bash.c b/src/h_bash.c
new file mode 100644
index 0000000..dcb371c
--- /dev/null
+++ b/src/h_bash.c
@@ -0,0 +1,651 @@
+/*-----------------------------------------------------------------
+ * haserl functions specific to a bash/ash/dash shell
+ * Copyright (c) 2003-2007 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 2, as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ *
+ ------------------------------------------------------------------------- */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+#include <getopt.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#include "common.h"
+#include "h_error.h"
+#include "h_bash.h"
+#include "h_script.h"
+#include "haserl.h"
+
+/* Local subshell variables */
+static int subshell_pipe[2];
+static int subshell_pid;
+
+
+void
+bash_setup (char *shell, list_t * env)
+{
+ int retcode = 0;
+ int count;
+ argv_t *argv;
+ char *av[20];
+ list_t *next;
+
+ if (shell == NULL)
+ return;
+
+ retcode = pipe (&subshell_pipe[PARENT_IN]);
+ if (retcode == 0)
+ {
+ subshell_pid = fork ();
+ if (subshell_pid == -1)
+ {
+ die_with_message (NULL, NULL, g_err_msg[E_SUBSHELL_FAIL]);
+ }
+
+ if (subshell_pid == 0)
+ {
+ /* I'm the child, connect stdin to the parent */
+ dup2 (subshell_pipe[PARENT_IN], STDIN_FILENO);
+ close (subshell_pipe[PARENT_IN]);
+ close (subshell_pipe[PARENT_OUT]);
+ count = argc_argv (shell, &argv, "");
+ if (count > 19)
+ {
+ /* over 20 command line args, silently truncate */
+ av[19] = "\0";
+ count = 18;
+ }
+ while (count >= 0)
+ {
+ av[count] = argv[count].string;
+ count--;
+ }
+
+
+ /* populate the environment */
+ while (env)
+ {
+ next = env->next;
+ putenv (env->buf);
+ env = next;
+ }
+
+ execv (argv[0].string, av);
+ free (argv);
+
+ /* if we get here, we had a failure */
+ die_with_message (NULL, NULL, g_err_msg[E_SUBSHELL_FAIL]);
+ }
+ else
+ {
+ /* I'm parent, move along please */
+ close (subshell_pipe[PARENT_IN]);
+ }
+ }
+
+ /* control should get to this point only in the parent.
+ */
+}
+
+void
+bash_destroy (void)
+{
+ int status;
+ waitpid (subshell_pid, &status, 0);
+}
+
+
+void
+bash_exec (buffer_t * buf, char *str)
+{
+ buffer_add (buf, str, strlen (str));
+ return;
+}
+
+/* Run the echo command in a subshell */
+void
+bash_echo (buffer_t * buf, char *str, size_t len)
+{
+/* limits.h would tell us the ARG_MAX characters we COULD send to the echo command, but
+ * we will take the (ancient) POSIX1 standard of 4K, subtract 1K from it and use that
+ * as the maxmimum. The Linux limit appears to be 128K, so 3K will fit. */
+
+ static char echo_start[] = "echo -n '";
+ static char echo_quote[] = "'\\''";
+ static char echo_end[] = "'\n";
+ const size_t maxlen = 3096;
+ size_t pos;
+
+ if (len == 0)
+ return;
+ pos = 0;
+
+ buffer_add (buf, echo_start, strlen (echo_start));
+ while (pos < len)
+ {
+ if (str[pos] == '\'')
+ buffer_add (buf, echo_quote, strlen (echo_quote));
+ else
+ buffer_add (buf, str + pos, 1);
+ pos++;
+ if ((pos % maxlen) == 0)
+ {
+ buffer_add (buf, echo_end, strlen (echo_end));
+ buffer_add (buf, echo_start, strlen (echo_start));
+ }
+ }
+ buffer_add (buf, echo_end, strlen (echo_end));
+}
+
+
+/* do an evaluation in a subshell */
+void
+bash_eval (buffer_t * buf, char *str, size_t len)
+{
+ static char echo_start[] = "echo -n ";
+ static char echo_end[] = "\n";
+ if (len == 0)
+ return;
+
+ buffer_add (buf, echo_start, strlen (echo_start));
+ buffer_add (buf, str, len);
+ buffer_add (buf, echo_end, strlen (echo_end));
+}
+
+#ifdef BASHEXTENSIONS
+/* generate an IF statment */
+void
+bash_if (buffer_t * buf, char *str, size_t len)
+{
+ static char err_msg[] =
+ "echo 'error: missing expression for if'\nexit 99\n";
+ static char if_start[] = "if [[ ";
+ static char if_end[] = " ]]\nthen\n";
+ static char ex_start[] = "if ";
+ static char ex_end[] = "\nthen\n";
+
+ if (len == 0)
+ {
+ buffer_add (buf, err_msg, strlen (err_msg));
+ }
+ else if (str[0] == '|')
+ {
+ str[0] = ' ';
+ buffer_add (buf, ex_start, strlen (ex_start));
+ buffer_add (buf, str, len);
+ buffer_add (buf, ex_end, strlen (ex_end));
+ }
+ else
+ {
+ buffer_add (buf, if_start, strlen (if_start));
+ buffer_add (buf, str, len);
+ buffer_add (buf, if_end, strlen (if_end));
+ }
+}
+
+
+/* generate an ELIF statment */
+void
+bash_elif (buffer_t * buf, char *str, size_t len)
+{
+ static char err_msg[] =
+ "echo 'error: missing expression for elif'\nexit 99\n";
+ static char elif_start[] = "elif [[ ";
+ static char elif_end[] = " ]]\nthen\n";
+ static char ex_start[] = "elif ";
+ static char ex_end[] = "\nthen\n";
+
+ if (len == 0)
+ {
+ buffer_add (buf, err_msg, strlen (err_msg));
+ }
+ else if (str[0] == '|')
+ {
+ str[0] = ' ';
+ buffer_add (buf, ex_start, strlen (ex_start));
+ buffer_add (buf, str, len);
+ buffer_add (buf, ex_end, strlen (ex_end));
+ }
+ else
+ {
+ buffer_add (buf, elif_start, strlen (elif_start));
+ buffer_add (buf, str, len);
+ buffer_add (buf, elif_end, strlen (elif_end));
+ }
+}
+
+
+/* generate an ELSE statment */
+void
+bash_else (buffer_t * buf, char *str, size_t len)
+{
+ static char else_start[] = "else";
+ static char else_start2[] = "else #";
+ static char else_end[] = "\n";
+
+ if (len == 0)
+ {
+ buffer_add (buf, else_start, strlen (else_start));
+ }
+ else
+ {
+ buffer_add (buf, else_start2, strlen (else_start2));
+ buffer_add (buf, str, len);
+ }
+ buffer_add (buf, else_end, strlen (else_end));
+}
+
+
+/* generate a FI statment */
+void
+bash_endif (buffer_t * buf, char *str, size_t len)
+{
+ static char fi_start[] = "fi";
+ static char fi_start2[] = "fi #";
+ static char fi_end[] = "\n";
+
+ if (len == 0)
+ {
+ buffer_add (buf, fi_start, strlen (fi_start));
+ }
+ else
+ {
+ buffer_add (buf, fi_start2, strlen (fi_start2));
+ buffer_add (buf, str, len);
+ }
+ buffer_add (buf, fi_end, strlen (fi_end));
+}
+
+
+/* generate a CASE statment */
+void
+bash_case (buffer_t * buf, char *str, size_t len)
+{
+ static char err_msg[] =
+ "echo 'error: missing expression for case'\nexit 99\n";
+ static char case_start[] = "case ";
+ static char case_end[] = " in\n";
+ /*
+ create a bogus case condition, nul+esc+eof, so nl+;;+nl
+ can be prepended to each when/otherwise/endcase, which
+ eliminates the need for ;; or <% ;; %> in the page source
+ */
+ static char case_bogus[] = "\"\\000\\040\\004\") :\n";
+
+ if (len == 0)
+ {
+ buffer_add (buf, err_msg, strlen (err_msg));
+ }
+ else
+ {
+ buffer_add (buf, case_start, strlen (case_start));
+ buffer_add (buf, str, len);
+ buffer_add (buf, case_end, strlen (case_end));
+ buffer_add (buf, case_bogus, strlen (case_bogus));
+ }
+}
+
+
+/* generate a WHEN statment */
+void
+bash_when (buffer_t * buf, char *str, size_t len)
+{
+ static char err_msg[] =
+ "echo 'error: missing expression for when'\nexit 99\n";
+ static char when_start[] = "\n;;\n";
+ static char when_end[] = ")\n";
+
+ if (len == 0)
+ {
+ buffer_add (buf, err_msg, strlen (err_msg));
+ }
+ else
+ {
+ buffer_add (buf, when_start, strlen (when_start));
+ buffer_add (buf, str, len - 1);
+ buffer_add (buf, when_end, strlen (when_end));
+ }
+}
+
+
+/* generate an OTHERWISE statment */
+void
+bash_otherwise (buffer_t * buf, char *str, size_t len)
+{
+ static char otherwise_start[] = "\n;;\n";
+ static char otherwise_start1[] = "*)";
+ static char otherwise_start2[] = "*) #";
+ static char otherwise_end[] = "\n";
+
+ buffer_add (buf, otherwise_start, strlen (otherwise_start));
+
+ if (len == 0)
+ {
+ buffer_add (buf, otherwise_start1, strlen (otherwise_start1));
+ }
+ else
+ {
+ buffer_add (buf, otherwise_start2, strlen (otherwise_start2));
+ buffer_add (buf, str, len);
+ }
+ buffer_add (buf, otherwise_end, strlen (otherwise_end));
+}
+
+
+/* generate a ENDCASE statment */
+void
+bash_endcase (buffer_t * buf, char *str, size_t len)
+{
+ static char endcase_start[] = "\n;;\n";
+ static char endcase_start1[] = "esac";
+ static char endcase_start2[] = "esac #";
+ static char endcase_end[] = "\n";
+
+ buffer_add (buf, endcase_start, strlen (endcase_start));
+
+ if (len == 0)
+ {
+ buffer_add (buf, endcase_start1, strlen (endcase_start1));
+ }
+ else
+ {
+ buffer_add (buf, endcase_start2, strlen (endcase_start2));
+ buffer_add (buf, str, len);
+ }
+ buffer_add (buf, endcase_end, strlen (endcase_end));
+}
+
+
+/* generate a WHILE statment */
+void
+bash_while (buffer_t * buf, char *str, size_t len)
+{
+ static char err_msg[] =
+ "echo 'error: missing expression for while'\nexit 99\n";
+ static char while_start[] = "while [[ ";
+ static char while_end[] = " ]]\ndo\n";
+ static char ex_start[] = "while ";
+ static char ex_end[] = "\ndo\n";
+
+ if (len == 0)
+ {
+ buffer_add (buf, err_msg, strlen (err_msg));
+ }
+ else if (str[0] == '|')
+ {
+ str[0] = ' ';
+ buffer_add (buf, ex_start, strlen (ex_start));
+ buffer_add (buf, str, len);
+ buffer_add (buf, ex_end, strlen (ex_end));
+ }
+ else
+ {
+ buffer_add (buf, while_start, strlen (while_start));
+ buffer_add (buf, str, len);
+ buffer_add (buf, while_end, strlen (while_end));
+ }
+}
+
+
+/* generate an ENDWHILE statment */
+void
+bash_endwhile (buffer_t * buf, char *str, size_t len)
+{
+ static char endwhile_start[] = "done";
+ static char endwhile_start2[] = "done #";
+ static char endwhile_end[] = "\n";
+
+ if (len == 0)
+ {
+ buffer_add (buf, endwhile_start, strlen (endwhile_start));
+ }
+ else
+ {
+ buffer_add (buf, endwhile_start2, strlen (endwhile_start2));
+ buffer_add (buf, str, len);
+ }
+ buffer_add (buf, endwhile_end, strlen (endwhile_end));
+}
+
+
+/* generate an UNTIL statment */
+void
+bash_until (buffer_t * buf, char *str, size_t len)
+{
+ static char err_msg[] =
+ "echo 'error: missing expression for until'\nexit 99\n";
+ static char until_start[] = "until [[ ";
+ static char until_end[] = " ]]\ndo\n";
+ static char ex_start[] = "until ";
+ static char ex_end[] = "\ndo\n";
+
+ if (len == 0)
+ {
+ buffer_add (buf, err_msg, strlen (err_msg));
+ }
+ else if (str[0] == '|')
+ {
+ str[0] = ' ';
+ buffer_add (buf, ex_start, strlen (ex_start));
+ buffer_add (buf, str, len);
+ buffer_add (buf, ex_end, strlen (ex_end));
+ }
+ else
+ {
+ buffer_add (buf, until_start, strlen (until_start));
+ buffer_add (buf, str, len);
+ buffer_add (buf, until_end, strlen (until_end));
+ }
+}
+
+
+/* generate an ENDUNTIL statment */
+void
+bash_enduntil (buffer_t * buf, char *str, size_t len)
+{
+ static char enduntil_start[] = "done";
+ static char enduntil_start2[] = "done #";
+ static char enduntil_end[] = "\n";
+
+ if (len == 0)
+ {
+ buffer_add (buf, enduntil_start, strlen (enduntil_start));
+ }
+ else
+ {
+ buffer_add (buf, enduntil_start2, strlen (enduntil_start2));
+ buffer_add (buf, str, len);
+ }
+ buffer_add (buf, enduntil_end, strlen (enduntil_end));
+}
+
+
+/* generate a FOR statment */
+void
+bash_for (buffer_t * buf, char *str, size_t len)
+{
+ static char err_msg[] =
+ "echo 'error: missing expression for for'\nexit 99\n";
+ static char for_start[] = "for ";
+ static char for_end[] = "\ndo\n";
+
+ if (len == 0)
+ {
+ buffer_add (buf, err_msg, strlen (err_msg));
+ }
+ else
+ {
+ buffer_add (buf, for_start, strlen (for_start));
+ buffer_add (buf, str, len);
+ buffer_add (buf, for_end, strlen (for_end));
+ }
+}
+
+
+/* generate an ENDFOR statment */
+void
+bash_endfor (buffer_t * buf, char *str, size_t len)
+{
+ static char endfor_start[] = "done";
+ static char endfor_start2[] = "done #";
+ static char endfor_end[] = "\n";
+
+ if (len == 0)
+ {
+ buffer_add (buf, endfor_start, strlen (endfor_start));
+ }
+ else
+ {
+ buffer_add (buf, endfor_start2, strlen (endfor_start2));
+ buffer_add (buf, str, len);
+ }
+ buffer_add (buf, endfor_end, strlen (endfor_end));
+}
+
+
+/* generate an UNLESS statment */
+void
+bash_unless (buffer_t * buf, char *str, size_t len)
+{
+ static char err_msg[] =
+ "echo 'error: missing expression for unless'\nexit 99\n";
+ static char unless_start[] = "if [[ ! ( ";
+ static char unless_end[] = " ) ]]\nthen\n";
+ static char ex_start[] = "if ! ";
+ static char ex_end[] = "\nthen\n";
+
+ if (len == 0)
+ {
+ buffer_add (buf, err_msg, strlen (err_msg));
+ }
+ else if (str[0] == '|')
+ {
+ str[0] = ' ';
+ buffer_add (buf, ex_start, strlen (ex_start));
+ buffer_add (buf, str, len);
+ buffer_add (buf, ex_end, strlen (ex_end));
+ }
+ else
+ {
+ buffer_add (buf, unless_start, strlen (unless_start));
+ buffer_add (buf, str, len);
+ buffer_add (buf, unless_end, strlen (unless_end));
+ }
+}
+
+
+/* generate an ELUN statment */
+void
+bash_elun (buffer_t * buf, char *str, size_t len)
+{
+ static char err_msg[] =
+ "echo 'error: missing expression for elun'\nexit 99\n";
+ static char elun_start[] = "elif [[ ! ( ";
+ static char elun_end[] = " ) ]]\nthen\n";
+ static char ex_start[] = "elif ! ";
+ static char ex_end[] = "\nthen\n";
+
+ if (len == 0)
+ {
+ buffer_add (buf, err_msg, strlen (err_msg));
+ }
+ else if (str[0] == '|')
+ {
+ str[0] = ' ';
+ buffer_add (buf, ex_start, strlen (ex_start));
+ buffer_add (buf, str, len);
+ buffer_add (buf, ex_end, strlen (ex_end));
+ }
+ else
+ {
+ buffer_add (buf, elun_start, strlen (elun_start));
+ buffer_add (buf, str, len);
+ buffer_add (buf, elun_end, strlen (elun_end));
+ }
+}
+
+
+/* generate an UNELSE statment */
+void
+bash_unelse (buffer_t * buf, char *str, size_t len)
+{
+ static char unelse_start[] = "else";
+ static char unelse_start2[] = "else #";
+ static char unelse_end[] = "\n";
+
+ if (len == 0)
+ {
+ buffer_add (buf, unelse_start, strlen (unelse_start));
+ }
+ else
+ {
+ buffer_add (buf, unelse_start2, strlen (unelse_start2));
+ buffer_add (buf, str, len);
+ }
+ buffer_add (buf, unelse_end, strlen (unelse_end));
+}
+
+
+/* generate a ENDUNLESS statment */
+void
+bash_endunless (buffer_t * buf, char *str, size_t len)
+{
+ static char endunless_start[] = "fi";
+ static char endunless_start2[] = "fi #";
+ static char endunless_end[] = "\n";
+
+ if (len == 0)
+ {
+ buffer_add (buf, endunless_start, strlen (endunless_start));
+ }
+ else
+ {
+ buffer_add (buf, endunless_start2, strlen (endunless_start2));
+ buffer_add (buf, str, len);
+ }
+ buffer_add (buf, endunless_end, strlen (endunless_end));
+}
+#endif
+
+void
+bash_doscript (buffer_t * script, char *name)
+{
+ static char postfix[] = "\nexit\n";
+
+ /* dump the script to the subshell */
+ write (subshell_pipe[PARENT_OUT], script->data, script->ptr - script->data);
+
+ /* write the postfix */
+ write (subshell_pipe[PARENT_OUT], postfix, strlen (postfix));
+
+
+ return;
+
+}
diff --git a/src/h_bash.h b/src/h_bash.h
new file mode 100644
index 0000000..d827aa6
--- /dev/null
+++ b/src/h_bash.h
@@ -0,0 +1,41 @@
+/*
+ * $Id: haserl.h,v 1.14 2005/11/18 14:43:10 nangel Exp $
+ */
+#ifndef H_SUBSHELL_H
+#define H_SUBSHELL_H 1
+
+
+/* the "names" for the pipes to the subshell */
+enum pipe_t { PARENT_IN, PARENT_OUT };
+
+/* h_bash.c */
+void bash_destroy(void);
+void bash_exec(buffer_t *buf, char *str);
+void bash_wait(buffer_t *buf, char *str);
+void bash_echo(buffer_t *buf, char *str, size_t len);
+void bash_eval(buffer_t *buf, char *str, size_t len);
+void bash_setup(char *shell, list_t *env);
+void bash_doscript(buffer_t *script, char *name);
+
+#ifdef BASHEXTENSIONS
+void bash_if(buffer_t *buf, char *str, size_t len);
+void bash_elif(buffer_t *buf, char *str, size_t len);
+void bash_else(buffer_t *buf, char *str, size_t len);
+void bash_endif(buffer_t *buf, char *str, size_t len);
+void bash_case(buffer_t *buf, char *str, size_t len);
+void bash_when(buffer_t *buf, char *str, size_t len);
+void bash_otherwise(buffer_t *buf, char *str, size_t len);
+void bash_endcase(buffer_t *buf, char *str, size_t len);
+void bash_while(buffer_t *buf, char *str, size_t len);
+void bash_endwhile(buffer_t *buf, char *str, size_t len);
+void bash_until(buffer_t *buf, char *str, size_t len);
+void bash_enduntil(buffer_t *buf, char *str, size_t len);
+void bash_for(buffer_t *buf, char *str, size_t len);
+void bash_endfor(buffer_t *buf, char *str, size_t len);
+void bash_unless(buffer_t *buf, char *str, size_t len);
+void bash_elun(buffer_t *buf, char *str, size_t len);
+void bash_unelse(buffer_t *buf, char *str, size_t len);
+void bash_endunless(buffer_t *buf, char *str, size_t len);
+#endif
+
+#endif /* !H_SUBSHELL_H */
diff --git a/src/h_error.c b/src/h_error.c
new file mode 100644
index 0000000..46bd6e1
--- /dev/null
+++ b/src/h_error.c
@@ -0,0 +1,103 @@
+/*
+ * --------------------------------------------------------------------------
+ * Error functions for haserl
+ * Copyright (c) 2003-2006 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 2, as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+#include "common.h"
+#include "h_script.h"
+#include "haserl.h"
+#include "h_error.h"
+
+char *g_err_msg[] = {
+ "",
+ "Memory Allocation Failure",
+ "Unable to open file %s",
+ "%c&gt; before &lt;%c",
+ "Missing %c&gt;",
+ "Unknown operation",
+ "Unable to start subshell",
+ "Unspecified Error",
+};
+
+
+
+/*
+ * abort the program
+ */
+void
+die_with_error (char *msg)
+{
+ fprintf (stderr, "Error: %s\n", msg);
+ exit (-1);
+}
+
+
+/* print an error message and die. If sp or where are non-null pointers, then
+ a line is added saying where in the script buffer the error occured. If
+ there's a request method, then http headers are added.
+ */
+void
+die_with_message (void *sp, char *where, const char *s, ...)
+{
+ #ifndef JUST_LUACSHELL
+ script_t *script = sp;
+ #endif
+ va_list p;
+ FILE *fo = stderr;
+
+ if (global.silent == FALSE)
+ {
+ if (getenv ("REQUEST_METHOD"))
+ {
+ fo = stdout;
+ fprintf (fo, "HTTP/1.0 500 Server Error\n"
+ "Content-Type: text/html\n\n"
+ "<html><body><b><font color=#CC0000>" PACKAGE_NAME
+ " CGI Error</font></b><br><pre>\n");
+ }
+ va_start (p, s);
+ vfprintf (fo, s, p);
+ va_end (p);
+ #ifndef JUST_LUACSHELL
+ if (where && sp)
+ {
+ fprintf (fo, " near line %d of %s\n",
+ count_lines (script->buf, script->size, where),
+ script->name);
+ }
+ #endif
+ printf ("\n");
+
+ if (getenv ("REQUEST_METHOD"))
+ fprintf (fo, "</pre></body></html>\n");
+ }
+ exit (-1);
+
+}
diff --git a/src/h_error.h b/src/h_error.h
new file mode 100644
index 0000000..af8d9aa
--- /dev/null
+++ b/src/h_error.h
@@ -0,0 +1,19 @@
+/*
+ * $Id: haserl.h,v 1.14 2005/11/18 14:43:10 nangel Exp $
+ */
+#ifndef H_ERROR_H
+#define H_ERROR_H 1
+
+
+enum error_types { E_NO_ERROR, E_MALLOC_FAIL, E_FILE_OPEN_FAIL,
+ E_END_BEFORE_BEGIN, E_NO_END_MARKER ,
+ E_NO_OP, E_SUBSHELL_FAIL, E_WHATEVER };
+
+extern char *g_err_msg[];
+
+/* h_error.c */
+void die_with_error(char *msg);
+void die_with_syntax(void *script, char *where, int error);
+void
+die_with_message ( void *sp, char *where, const char *s, ...);
+#endif /* !H_ERROR_H */
diff --git a/src/h_lua.c b/src/h_lua.c
new file mode 100644
index 0000000..6bee6e2
--- /dev/null
+++ b/src/h_lua.c
@@ -0,0 +1,155 @@
+/* --------------------------------------------------------------------------
+ * lua language specific functions
+ * Copyright (c) 2003-2007 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ ------------------------------------------------------------------------- */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+#include <getopt.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include "common.h"
+#include "h_error.h"
+#include "h_lua.h"
+#include "h_script.h"
+#include "haserl.h"
+
+extern lua_State *lua_vm;
+
+/* attempts to open a file, tokenize and then process it as a haserl script */
+int
+h_lua_loadfile (lua_State * L)
+{
+ script_t *scriptchain;
+ token_t *tokenchain;
+ buffer_t script_text;
+ int status;
+
+ /* get the filename */
+ const char *filename = luaL_checkstring (L, 1);
+
+
+ scriptchain = load_script ((char *) filename, NULL);
+ tokenchain = build_token_list (scriptchain, NULL);
+ preprocess_token_list (tokenchain);
+ process_token_list (&script_text, tokenchain);
+ free_token_list (tokenchain);
+ free_script_list (scriptchain);
+
+ /* script_text has the include file */
+ status = luaL_loadbuffer (L, (char *) script_text.data,
+ script_text.ptr - script_text.data, filename);
+ buffer_destroy (&script_text);
+ if (status)
+ {
+ lua_error (L);
+ }
+ return (1); /* we return one value, the buffer, as a function */
+}
+
+void
+lua_exec (buffer_t * buf, char *str)
+{
+ buffer_add (buf, str, strlen (str));
+}
+
+void
+lua_doscript (buffer_t * script, char *name)
+{
+ int status;
+ /* force the string to be null terminated */
+
+ buffer_add (script, "\0", 1);
+
+ status = luaL_loadbuffer (lua_vm, (char *) script->data,
+ strlen ((char *) script->data), name) ||
+ lua_pcall (lua_vm, 0, LUA_MULTRET, 0);
+
+ if (status && !lua_isnil (lua_vm, -1))
+ {
+ const char *msg = lua_tostring (lua_vm, -1);
+ if (msg == NULL)
+ msg = "(error object is not a string)";
+ die_with_message (NULL, NULL, msg);
+ }
+}
+
+
+/* Run the echo command in a subshell */
+void
+lua_echo (buffer_t * buf, char *str, size_t len)
+{
+ static char echo_start[] = " io.write ";
+ char quote[200] = "]=]"; /* 197 nested comments is a problem */
+
+ if (len == 0)
+ return;
+
+ /* figure out if the string uses ]] ]=] ]==] etc in it */
+ while ((strstr (str, quote)) && (strlen (quote) < 198))
+ {
+ memmove (quote + strlen (quote) - 1, quote + strlen (quote) - 2, 3);
+ }
+
+ /* As of 5.1, nested comments are depreciated... sigh */
+ quote[0] = '[';
+ quote[strlen (quote) - 1] = quote[0];
+ while ((strstr (str, quote)) && (strlen (quote) < 198))
+ {
+ memmove (quote + strlen (quote) - 1, quote + strlen (quote) - 2, 3);
+ }
+
+ buffer_add (buf, echo_start, strlen (echo_start));
+ buffer_add (buf, quote, strlen (quote));
+ buffer_add (buf, str, len);
+ quote[0] = ']';
+ quote[strlen (quote) - 1] = quote[0];
+ buffer_add (buf, quote, strlen (quote));
+ buffer_add (buf, "\n", 1);
+
+}
+
+
+/* do an evaluation */
+void
+lua_eval (buffer_t * buf, char *str, size_t len)
+{
+ static char start[] = " io.write(tostring(";
+ static char end[] = "))\n";
+ if (len == 0)
+ return;
+
+ buffer_add (buf, start, strlen (start));
+ buffer_add (buf, str, len);
+ buffer_add (buf, end, strlen (end));
+}
+
diff --git a/src/h_lua.h b/src/h_lua.h
new file mode 100644
index 0000000..1b1e8af
--- /dev/null
+++ b/src/h_lua.h
@@ -0,0 +1,12 @@
+#ifndef H_LUA_H
+#define H_LUA_H 1
+
+
+void lua_exec(buffer_t *buf, char *str);
+void lua_echo(buffer_t *buf, char *str, size_t len);
+void lua_eval(buffer_t *buf, char *str, size_t len);
+void lua_doscript(buffer_t *script, char *name);
+int h_lua_loadfile(lua_State *L);
+
+
+#endif
diff --git a/src/h_lua_common.c b/src/h_lua_common.c
new file mode 100644
index 0000000..e29c97b
--- /dev/null
+++ b/src/h_lua_common.c
@@ -0,0 +1,117 @@
+/* --------------------------------------------------------------------------
+ * functions shared among both Lua shells
+ * Copyright (c) 2003-2007 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ ------------------------------------------------------------------------- */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include "common.h"
+#include "h_lua_common.h"
+#ifdef INCLUDE_LUASHELL
+#include "h_lua.h"
+#endif
+#ifdef INCLUDE_LUACSHELL
+#include "h_luac.h"
+#endif
+#include "h_error.h"
+
+/* this is not a mistake. We are including the
+ * definition of the lualib here
+ */
+#include "haserl_lualib.inc"
+
+lua_State *lua_vm = NULL;
+
+void
+lua_common_putenv (char *str)
+{
+ char *value;
+ value = memchr (str, '=', strlen (str));
+ if (value)
+ {
+ *value = (char) '\0';
+ value++;
+ }
+ else
+ {
+ value = str + strlen (str);
+ }
+
+
+ lua_getglobal (lua_vm, "haserl");
+ lua_pushstring (lua_vm, "myputenv");
+ lua_gettable (lua_vm, -2);
+ lua_pushstring (lua_vm, str);
+ lua_pushstring (lua_vm, value);
+ lua_call (lua_vm, 2, 0);
+ return;
+}
+
+void
+lua_common_setup (char *shell, list_t * env)
+{
+ /* create a lua instance */
+ lua_vm = luaL_newstate ();
+ luaL_openlibs (lua_vm);
+
+ /* and load our haserl library */
+ if (luaL_loadbuffer
+ (lua_vm, (const char *) &haserl_lualib, sizeof (haserl_lualib),
+ "luascript.lua") || lua_pcall (lua_vm, 0, 0, 0))
+ {
+ die_with_message (NULL, NULL,
+ "Error passing the lua library to the lua vm: %s",
+ lua_tostring (lua_vm, -1));
+ }
+
+ /* and put the vars in the vm */
+ while (env)
+ {
+ lua_common_putenv (env->buf);
+ env = env->next;
+ }
+
+
+ /* register our open function in the haserl table */
+ lua_getglobal (lua_vm, "haserl");
+ lua_pushstring (lua_vm, "loadfile");
+#if defined(INCLUDE_LUASHELL) && defined(INCLUDE_LUACSHELL)
+ lua_pushcfunction (lua_vm,
+ shell[3] == 'c' ? h_luac_loadfile : h_lua_loadfile);
+#elif defined(INCLUDE_LUASHELL)
+ lua_pushcfunction (lua_vm, h_lua_loadfile);
+#else /* INCLUDE_LUACSHELL */
+ lua_pushcfunction (lua_vm, h_luac_loadfile);
+#endif
+ lua_settable (lua_vm, -3);
+
+}
+
+void
+lua_common_destroy (void)
+{
+ /* close the lua instance */
+ lua_close (lua_vm);
+}
diff --git a/src/h_lua_common.h b/src/h_lua_common.h
new file mode 100644
index 0000000..18b0e68
--- /dev/null
+++ b/src/h_lua_common.h
@@ -0,0 +1,7 @@
+#ifndef _H_LUA_COMMON_H
+#define _H_LUA_COMMON_H
+
+void lua_common_setup(char *shell, list_t *env);
+void lua_common_destroy(void);
+
+#endif
diff --git a/src/h_luac.c b/src/h_luac.c
new file mode 100644
index 0000000..55e4083
--- /dev/null
+++ b/src/h_luac.c
@@ -0,0 +1,55 @@
+/* --------------------------------------------------------------------------
+ * lua language specific functions (for compiled Lua chunks)
+ * Copyright (c) 2003-2007 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ ------------------------------------------------------------------------- */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include "common.h"
+#include "h_luac.h"
+#include "h_error.h"
+
+extern lua_State *lua_vm;
+
+ /* tokenizer was not used, so script is NULL, but name is the filename to process */
+void
+luac_doscript (buffer_t *script, char *name)
+{
+ if (luaL_loadfile (lua_vm, name) || lua_pcall(lua_vm, 0, LUA_MULTRET, 0))
+ {
+ die_with_message (NULL, NULL, "Cannot load lua and execute chunk: %s", lua_tostring(lua_vm, -1));
+ }
+}
+
+int
+h_luac_loadfile (lua_State *L)
+{
+ const char *filename = luaL_checkstring (L, 1);
+
+ if (luaL_loadfile (L, filename))
+ {
+ die_with_message (NULL, NULL, "Cannot load file '%s': %s", filename, lua_tostring(L, -1));
+ } /* no error: function is on the stack */
+
+ return 1;
+}
diff --git a/src/h_luac.h b/src/h_luac.h
new file mode 100644
index 0000000..37c6536
--- /dev/null
+++ b/src/h_luac.h
@@ -0,0 +1,7 @@
+#ifndef _H_LUAC_H
+#define _H_LUAC_H
+
+void luac_doscript(buffer_t *script, char *name);
+int h_luac_loadfile(lua_State * L);
+
+#endif
diff --git a/src/h_script.c b/src/h_script.c
new file mode 100644
index 0000000..029fc41
--- /dev/null
+++ b/src/h_script.c
@@ -0,0 +1,614 @@
+/*--------------------------------------------------------------------------
+ * functions related to opening, tokenizing and parsing a haserl script
+ * Copyright (c) 2006-2007 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ *
+ ------------------------------------------------------------------------- */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "common.h"
+#include "h_error.h"
+#include "h_script.h"
+#include "h_bash.h"
+#include "haserl.h"
+
+#ifdef BASHEXTENSIONS
+/* HTML, RUN, INCLUDE, EVAL, COMMENT, IF, ELIF, ELSE, ENDIF, CASE, WHEN,
+ OTHERWISE, ENDCASE, WHILE, ENDWHILE, UNTIL, ENDUNTIL, FOR, ENDFOR,
+ UNLESS, ELUN, UNELSE, ENDUNLESS, NOOP }; */
+#else
+/* HTML, RUN, INCLUDE, EVAL, COMMENT, NOOP */
+#endif
+
+const char *g_tag[] = {
+ "",
+ "",
+ "in",
+ "=",
+ "#",
+#ifdef BASHEXTENSIONS
+ "if",
+ "elif",
+ "else",
+ "endif",
+ "case",
+ "when",
+ "otherwise",
+ "endcase",
+ "while",
+ "endwhile",
+ "until",
+ "enduntil",
+ "for",
+ "endfor",
+ "unless",
+ "elun",
+ "unelse",
+ "endunless",
+#endif
+ ""
+};
+
+/* the open and close tags */
+char open_tag[3] = "<%";
+char close_tag[3] = "%>";
+
+
+/* Open a script and return a populated script_t structure
+ */
+script_t *
+load_script (char *filename, script_t * scriptlist)
+{
+ script_t *scriptbuf;
+ int scriptfp;
+ struct stat filestat;
+
+ scriptfp = open (filename, O_NONBLOCK + O_RDONLY);
+ if (scriptfp == -1)
+ { /* open failed */
+ die_with_message (NULL, NULL, g_err_msg[E_FILE_OPEN_FAIL], filename);
+ }
+
+ fstat (scriptfp, &filestat);
+ scriptbuf = (script_t *) xmalloc (sizeof (script_t));
+ scriptbuf->name = (char *) xmalloc (strlen (filename) + 1);
+ scriptbuf->buf = (char *) xmalloc (filestat.st_size + 1);
+
+ memset (scriptbuf->name, 0, strlen (filename) + 1);
+ memcpy (scriptbuf->name, filename, strlen (filename));
+ memset (scriptbuf->buf, 0, filestat.st_size + 1);
+ read (scriptfp, scriptbuf->buf, filestat.st_size);
+
+ scriptbuf->size = filestat.st_size;
+ scriptbuf->uid = filestat.st_uid;
+ scriptbuf->gid = filestat.st_gid;
+ scriptbuf->curpos = 0;
+ scriptbuf->next = NULL;
+
+ /* if we already have scripts, add this one to the end */
+ if (scriptlist != NULL)
+ {
+ while (scriptlist->next)
+ scriptlist = scriptlist->next;
+ scriptlist->next = scriptbuf;
+ }
+
+ /* skip over the first line, if the file starts with a #!
+ * This means that offset will start at the beginning of
+ * the second line in most cases.
+ */
+ if (memcmp (scriptbuf->buf, "#!", 2) == 0)
+ {
+ while ((scriptbuf->curpos < scriptbuf->size) &&
+ ((char) scriptbuf->buf[scriptbuf->curpos] != '\n'))
+ {
+ (scriptbuf->curpos)++;
+ }
+ (scriptbuf->curpos)++;
+ }
+
+ /* If this is the first script, switch to <? ?> mode only
+ * if <% is not used anywhere else in the script. Otherwise
+ * don't change the tagging method
+ */
+ if (scriptlist == NULL)
+ {
+ if (strstr (scriptbuf->buf + scriptbuf->curpos, open_tag) == NULL)
+ {
+ open_tag[1] = '?';
+ close_tag[0] = '?';
+ }
+ }
+ close (scriptfp);
+ return (scriptbuf);
+}
+
+/* Free the script structures */
+void
+free_script_list (script_t * script)
+{
+ script_t *next;
+ while (script)
+ {
+ next = script->next;
+ if (script->name)
+ free (script->name);
+ if (script->buf)
+ free (script->buf);
+ free (script);
+ script = next;
+ }
+}
+
+/* Insert this token into the token chain.
+ * If tokenlist is null, create a new one
+ */
+token_t *
+push_token_on_list (token_t * tokenlist, script_t * scriptbuf,
+ char *start, size_t len)
+{
+ token_t *me, *next;
+ if (len == 0)
+ return (tokenlist);
+
+ me = (token_t *) xmalloc (sizeof (token_t));
+
+ if (tokenlist == NULL)
+ {
+ next = NULL;
+ }
+ else
+ {
+ next = tokenlist->next;
+ tokenlist->next = me;
+ }
+
+ me->next = next;
+ me->script = scriptbuf;
+ me->buf = start;
+ me->len = len;
+
+ return (me);
+}
+
+/* Free a token chain */
+void
+free_token_list (token_t * tokenlist)
+{
+ token_t *next;
+
+ while (tokenlist)
+ {
+ next = tokenlist->next;
+ free (tokenlist);
+ tokenlist = next;
+ }
+}
+
+#ifndef JUST_LUACSHELL
+
+/* return the point in a script where the "comment" ends */
+char *
+skip_comment (char *startbuf, char *endbuf)
+{
+ unsigned int c_lev = 1;
+ char *s_tag, *e_tag;
+
+ startbuf += 2;
+ while (startbuf < endbuf)
+ {
+ s_tag = strstr (startbuf, open_tag);
+ e_tag = strstr (startbuf, close_tag);
+
+ if (!e_tag)
+ {
+ break;
+ }
+
+ if ((s_tag) && (s_tag < e_tag))
+ {
+ c_lev++;
+ startbuf = s_tag + 2;
+ continue;
+ }
+ /* otherwise, we have an end tag first */
+ c_lev--;
+ startbuf = e_tag;
+ if (c_lev == 0)
+ {
+ return (startbuf);
+ }
+ startbuf += 2;
+
+ }
+ return NULL;
+}
+
+/* build a tokenchain from a script. This step just
+ * splits a script buf into parts that are separated
+ * by <% %>. If the <% %> are out of order, then it
+ * flags that, but doesn't try to do anything else.
+ *
+ * For nesting comments, this function IS aware of
+ * the comment tag, and will try accomodate commented
+ * tags. Commented script never enters the token list
+ */
+token_t *
+build_token_list (script_t * scriptbuf, token_t * tokenlist)
+{
+
+ char *start, *end, *curpos, *endpos;
+ token_t *curtoken, *firsttoken;
+
+ curtoken = tokenlist;
+ firsttoken = tokenlist;
+
+ curpos = scriptbuf->buf + scriptbuf->curpos;
+ endpos = scriptbuf->buf + scriptbuf->size;
+
+ while (curpos < endpos)
+ {
+ start = strstr (curpos, open_tag);
+ end = strstr (curpos, close_tag);
+
+ /* if this is a comment tag, the end is at the end of the comment */
+ if ((start) && (memcmp (start + 2, g_tag[COMMENT], 1) == 0))
+ {
+ /* save any bare html before the comment */
+ curtoken = push_token_on_list (curtoken, scriptbuf, curpos,
+ start - curpos);
+ if (firsttoken == NULL)
+ firsttoken = curtoken;
+ /* push start of token to end of token */
+ end = skip_comment (start, endpos);
+ if (end)
+ {
+ curpos = end + 2;
+ continue;
+ }
+ }
+
+ if (start && !end)
+ die_with_message (scriptbuf, start, g_err_msg[E_NO_END_MARKER],
+ open_tag[1]);
+
+ if ((start > end) || (!start && end))
+ die_with_message (scriptbuf, end, g_err_msg[E_END_BEFORE_BEGIN],
+ open_tag[1], open_tag[1]);
+
+ if (start && (strstr (start + 1, open_tag)
+ && (strstr (start + 1, open_tag) < end)))
+ die_with_message (scriptbuf, start, g_err_msg[E_NO_END_MARKER],
+ open_tag[1]);
+ if (end)
+ {
+ /* push curpos to the start of the token */
+ curtoken = push_token_on_list (curtoken, scriptbuf, curpos,
+ start - curpos);
+ if (firsttoken == NULL)
+ firsttoken = curtoken;
+ /* push start of token to end of token */
+ curtoken =
+ push_token_on_list (curtoken, scriptbuf, start, end - start);
+ if (firsttoken == NULL)
+ firsttoken = curtoken;
+ curpos = end + 2;
+ }
+ else
+ {
+ /* push curpos to end of script */
+ curtoken =
+ push_token_on_list (curtoken, scriptbuf, curpos, endpos - curpos);
+ if (firsttoken == NULL)
+ firsttoken = curtoken;
+ curpos = endpos;
+ }
+ }
+
+ return (firsttoken);
+}
+
+/* syntax check a script */
+void
+preprocess_token_list (token_t * tokenlist)
+{
+ script_t *newscript;
+ token_t *me;
+ char *cp;
+
+ me = tokenlist;
+ /* walk the chain to fill in the tags */
+ while (me)
+ {
+ if (memcmp (me->buf, open_tag, 2))
+ {
+ me->tag = HTML;
+ }
+ else
+ {
+ me->tag = NOOP;
+ me->buf[me->len] = '\0';
+ cp = me->buf + 2; /* skip <? */
+ if (memcmp (cp, g_tag[INCLUDE], 2) == 0)
+ {
+ me->tag = INCLUDE;
+ /* skip the first word - in, include, include-file, etc */
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ cp = find_whitespace (me->buf);
+ *cp = '\0';
+ me->len = strlen (me->buf) + 1;
+ newscript = load_script (me->buf, me->script);
+ build_token_list (newscript, me);
+ }
+ else if (memcmp (cp, g_tag[EVAL], 1) == 0)
+
+ {
+ me->tag = EVAL;
+ me->buf = find_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+#ifdef BASHEXTENSIONS
+ else if (memcmp (cp, g_tag[IF], 2) == 0)
+ {
+ me->tag = IF;
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+ else if (memcmp (cp, g_tag[ELIF], 4) == 0)
+ {
+ me->tag = ELIF;
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+ else if (memcmp (cp, g_tag[ELSE], 4) == 0)
+ {
+ me->tag = ELSE;
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+ else if (memcmp (cp, g_tag[ENDIF], 5) == 0)
+ {
+ me->tag = ENDIF;
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+ else if (memcmp (cp, g_tag[CASE], 4) == 0)
+ {
+ me->tag = CASE;
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+ else if (memcmp (cp, g_tag[WHEN], 4) == 0)
+ {
+ me->tag = WHEN;
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+ else if (memcmp (cp, g_tag[OTHERWISE], 9) == 0)
+ {
+ me->tag = OTHERWISE;
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+ else if (memcmp (cp, g_tag[ENDCASE], 7) == 0)
+ {
+ me->tag = ENDCASE;
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+ else if (memcmp (cp, g_tag[WHILE], 5) == 0)
+ {
+ me->tag = WHILE;
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+ else if (memcmp (cp, g_tag[ENDWHILE], 8) == 0)
+ {
+ me->tag = ENDWHILE;
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+ else if (memcmp (cp, g_tag[UNTIL], 5) == 0)
+ {
+ me->tag = UNTIL;
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+ else if (memcmp (cp, g_tag[ENDUNTIL], 8) == 0)
+ {
+ me->tag = ENDUNTIL;
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+ else if (memcmp (cp, g_tag[FOR], 3) == 0)
+ {
+ me->tag = FOR;
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+ else if (memcmp (cp, g_tag[ENDFOR], 6) == 0)
+ {
+ me->tag = ENDFOR;
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+ else if (memcmp (cp, g_tag[UNLESS], 6) == 0)
+ {
+ me->tag = UNLESS;
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+ else if (memcmp (cp, g_tag[ELUN], 4) == 0)
+ {
+ me->tag = ELUN;
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+ else if (memcmp (cp, g_tag[UNELSE], 6) == 0)
+ {
+ me->tag = UNELSE;
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+ else if (memcmp (cp, g_tag[ENDUNLESS], 9) == 0)
+ {
+ me->tag = ENDUNLESS;
+ me->buf = find_whitespace (me->buf);
+ me->buf = skip_whitespace (me->buf);
+ me->len = strlen (me->buf);
+ }
+#endif /* BASHEXTENSIONS */
+ if (isspace (*cp))
+ {
+ me->tag = RUN;
+ me->buf = cp;
+ }
+ if (me->tag == NOOP)
+ {
+ die_with_message (me->script, cp, g_err_msg[E_NO_OP]);
+ }
+ me->len = strlen (me->buf);
+ }
+ me = me->next;
+ }
+}
+
+
+token_t *
+process_token_list (buffer_t * buf, token_t * token)
+{
+ char *c;
+
+ buffer_init (buf);
+ shell_exec (buf, "\n"); /* try to get the error reporting match the line number */
+
+ while (token)
+ {
+ switch (token->tag)
+ {
+ case HTML:
+ /* Change from 0.8.0 - if the whole thing is just
+ whitespace, don't print it */
+ c = token->buf;
+ while ((c < (token->buf + token->len)) && (isspace (*c)))
+ c++;
+ if (c != token->buf + token->len)
+ shell_echo (buf, token->buf, token->len);
+ break;
+ case RUN:
+ shell_exec (buf, token->buf);
+ shell_exec (buf, "\n");
+ break;
+ case EVAL:
+ shell_eval (buf, token->buf, token->len);
+ break;
+#ifdef BASHEXTENSIONS
+ case IF:
+ shell_if (buf, token->buf, token->len);
+ break;
+ case ELIF:
+ shell_elif (buf, token->buf, token->len);
+ break;
+ case ELSE:
+ shell_else (buf, token->buf, token->len);
+ break;
+ case ENDIF:
+ shell_endif (buf, token->buf, token->len);
+ break;
+ case CASE:
+ shell_case (buf, token->buf, token->len);
+ break;
+ case WHEN:
+ shell_when (buf, token->buf, token->len);
+ break;
+ case OTHERWISE:
+ shell_otherwise (buf, token->buf, token->len);
+ break;
+ case ENDCASE:
+ shell_endcase (buf, token->buf, token->len);
+ break;
+ case WHILE:
+ shell_while (buf, token->buf, token->len);
+ break;
+ case ENDWHILE:
+ shell_endwhile (buf, token->buf, token->len);
+ break;
+ case UNTIL:
+ shell_until (buf, token->buf, token->len);
+ break;
+ case ENDUNTIL:
+ shell_enduntil (buf, token->buf, token->len);
+ break;
+ case FOR:
+ shell_for (buf, token->buf, token->len);
+ break;
+ case ENDFOR:
+ shell_endfor (buf, token->buf, token->len);
+ break;
+ case UNLESS:
+ shell_unless (buf, token->buf, token->len);
+ break;
+ case ELUN:
+ shell_elun (buf, token->buf, token->len);
+ break;
+ case UNELSE:
+ shell_unelse (buf, token->buf, token->len);
+ break;
+ case ENDUNLESS:
+ shell_endunless (buf, token->buf, token->len);
+ break;
+#endif
+ default:
+ break;
+ }
+ token = token->next;
+ }
+
+ return (token);
+
+}
+
+#endif
diff --git a/src/h_script.h b/src/h_script.h
new file mode 100644
index 0000000..e1098fe
--- /dev/null
+++ b/src/h_script.h
@@ -0,0 +1,52 @@
+/*
+ * $Id: haserl.h,v 1.14 2005/11/18 14:43:10 nangel Exp $
+ */
+#ifndef H_SCRIPT_H
+#define H_SCRIPT_H 1
+
+
+/* Everything we care to know about a script */
+typedef struct {
+ char *name; /* pointer to name of script */
+ int size; /* size of script in bytes */
+ uid_t uid; /* user owner */
+ gid_t gid; /* group owner */
+ char *buf; /* pointer to malloc'ed buffer */
+ size_t curpos; /* current position in buffer */
+ void *next; /* next script in our chain */
+ } script_t;
+
+/* tag types */
+#ifdef BASHEXTENSIONS
+enum tag_t { HTML, RUN, INCLUDE, EVAL, COMMENT, IF, ELIF, ELSE, ENDIF, CASE, WHEN, OTHERWISE, ENDCASE, WHILE, ENDWHILE, UNTIL, ENDUNTIL, FOR, ENDFOR, UNLESS, ELUN, UNELSE, ENDUNLESS, NOOP };
+#else
+enum tag_t { HTML, RUN, INCLUDE, EVAL, COMMENT, NOOP };
+#endif
+
+
+/* token structure */
+typedef struct {
+ script_t *script; /* the parent script */
+ enum tag_t tag; /* the token type */
+ size_t len; /* length of token */
+ char *buf; /* pointer to start of token */
+ void *next; /* the next token in the chain */
+ } token_t;
+
+
+
+/* h_script.c */
+script_t *load_script(char *filename, script_t *scriptlist);
+void free_script_list(script_t *script);
+token_t *push_token_on_list(token_t *tokenlist, script_t *scriptbuf, char *start, size_t len);
+void free_token_list(token_t *tokenlist);
+
+#ifndef JUST_LUACSHELL
+
+token_t *build_token_list(script_t *scriptbuf, token_t *tokenlist);
+void preprocess_token_list(token_t *tokenlist);
+token_t *process_token_list(buffer_t *buf, token_t *tokenlist);
+
+#endif
+
+#endif /* !H_SCRIPT_H */
diff --git a/src/haserl.c b/src/haserl.c
new file mode 100644
index 0000000..da7c755
--- /dev/null
+++ b/src/haserl.c
@@ -0,0 +1,917 @@
+/* --------------------------------------------------------------------------
+ * core of haserl.cgi - a poor-man's php for embedded/lightweight environments
+ * Copyright (c) 2003-2007 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 2, as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ *
+ * -----
+ * The x2c() and unescape_url() routines were taken from
+ * http://www.jmarshall.com/easy/cgi/getcgi.c.txt
+ *
+ * The comments in that text file state:
+ *
+ *** Written in 1996 by James Marshall, james@jmarshall.com, except
+ *** that the x2c() and unescape_url() routines were lifted directly
+ *** from NCSA's sample program util.c, packaged with their HTTPD.
+ *** For the latest, see http://www.jmarshall.com/easy/cgi/
+ * -----
+ *
+ ------------------------------------------------------------------------- */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+#include <getopt.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <grp.h>
+
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#include "common.h"
+#include "h_error.h"
+#include "h_script.h"
+#include "sliding_buffer.h"
+#include "rfc2388.h"
+#ifdef INCLUDE_BASHSHELL
+#include "h_bash.h"
+#endif
+
+#ifdef USE_LUA
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+#include "h_lua_common.h"
+#endif
+#ifdef INCLUDE_LUASHELL
+#include "h_lua.h"
+#endif
+#ifdef INCLUDE_LUACSHELL
+#include "h_luac.h"
+#endif
+
+#include "haserl.h"
+
+#ifndef TEMPDIR
+#define TEMPDIR "/tmp"
+#endif
+
+#ifndef MAX_UPLOAD_KB
+#define MAX_UPLOAD_KB 2048
+#endif
+
+/* Refuse to disable the subshell */
+#ifndef SUBSHELL_CMD
+#define SUBSHELL_CMD "/bin/sh"
+#endif
+
+haserl_t global;
+
+
+/* declare the shell_ function pointers here */
+void (*shell_exec) (buffer_t * buf, char *str);
+void (*shell_echo) (buffer_t * buf, char *str, size_t len);
+void (*shell_eval) (buffer_t * buf, char *str, size_t len);
+void (*shell_setup) (char *, list_t *);
+void (*shell_doscript) (buffer_t *, char *);
+void (*shell_destroy) (void);
+
+#ifdef BASHEXTENSIONS
+void (*shell_if) (buffer_t * buf, char *str, size_t len);
+void (*shell_elif) (buffer_t * buf, char *str, size_t len);
+void (*shell_else) (buffer_t * buf, char *str, size_t len);
+void (*shell_endif) (buffer_t * buf, char *str, size_t len);
+void (*shell_case) (buffer_t * buf, char *str, size_t len);
+void (*shell_when) (buffer_t * buf, char *str, size_t len);
+void (*shell_otherwise) (buffer_t * buf, char *str, size_t len);
+void (*shell_endcase) (buffer_t * buf, char *str, size_t len);
+void (*shell_while) (buffer_t * buf, char *str, size_t len);
+void (*shell_endwhile) (buffer_t * buf, char *str, size_t len);
+void (*shell_until) (buffer_t * buf, char *str, size_t len);
+void (*shell_enduntil) (buffer_t * buf, char *str, size_t len);
+void (*shell_for) (buffer_t * buf, char *str, size_t len);
+void (*shell_endfor) (buffer_t * buf, char *str, size_t len);
+void (*shell_unless) (buffer_t * buf, char *str, size_t len);
+void (*shell_elun) (buffer_t * buf, char *str, size_t len);
+void (*shell_unelse) (buffer_t * buf, char *str, size_t len);
+void (*shell_endunless) (buffer_t * buf, char *str, size_t len);
+#endif
+
+/* global shell execution function pointers. These point to the actual functions
+ that do the job, based on the language */
+
+/*
+ * Command line / Config file directives When adding a long option, make sure
+ * to update the short_options as well
+ */
+
+struct option ga_long_options[] = {
+ {"version", no_argument, 0, 'v'},
+ {"help", no_argument, 0, 'h'},
+ {"debug", no_argument, 0, 'd'},
+ {"upload-limit", required_argument, 0, 'u'},
+ {"upload-dir", required_argument, 0, 'U'},
+ {"upload-handler", required_argument, 0, 'H'},
+ {"accept-all", no_argument, 0, 'a'},
+ {"accept-none", no_argument, 0, 'n'},
+ {"shell", required_argument, 0, 's'},
+ {"silent", no_argument, 0, 'S'},
+ {0, 0, 0, 0}
+};
+
+const char *gs_short_options = "+vhdu:U:H:ans:S";
+
+/*
+ * Convert 2 char hex string into char it represents
+ * (from http://www.jmarshall.com/easy/cgi)
+ */
+char
+x2c (char *what)
+{
+ char digit;
+
+ digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
+ digit *= 16;
+ digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
+ return (digit);
+}
+
+/*
+ * unsescape %xx to the characters they represent
+ */
+/* Modified by Juris Feb 2007 */
+void
+unescape_url (char *url)
+{
+ int i, j;
+ for (i = 0, j = 0; url[j]; ++i, ++j)
+ {
+ if ((url[i] = url[j]) != '%')
+ continue;
+ if (!url[j + 1] || !url[j + 2])
+ break;
+ url[i] = x2c (&url[j + 1]);
+ j += 2;
+ }
+ url[i] = '\0';
+}
+
+
+
+
+/*
+ * allocate memory or die, busybox style.
+ */
+void *
+xmalloc (size_t size)
+{
+ void *buf;
+ if ((buf = malloc (size)) == NULL)
+ {
+ die_with_message (NULL, NULL, g_err_msg[E_MALLOC_FAIL]);
+ }
+ memset (buf, 0, size);
+ return buf;
+}
+
+
+/*
+ * realloc memory, or die xmalloc style.
+ */
+void *
+xrealloc (void *buf, size_t size)
+{
+ if ((buf = realloc (buf, size)) == NULL)
+ {
+ die_with_message (NULL, NULL, g_err_msg[E_MALLOC_FAIL]);
+ }
+ return buf;
+}
+
+
+/*
+ * adds or replaces the "key=value" value in the env_list chain
+ * prefix is appended to the key (e.g. FORM_key=value)
+ */
+list_t *
+myputenv (list_t * cur, char *str, char *prefix)
+{
+ list_t *prev = NULL;
+ size_t keylen;
+ char *entry = NULL;
+ char *temp = NULL;
+ int array = 0;
+ int len;
+
+ temp = memchr (str, '=', strlen (str));
+ /* if we don't have an equal sign, exit early */
+ if (temp == 0)
+ {
+ return (cur);
+ }
+
+ keylen = (size_t) (temp - str);
+
+ /* is this an array */
+ if (memcmp (str + keylen - 2, "[]", 2) == 0)
+ {
+ keylen = keylen - 2;
+ array = 1;
+ }
+
+ entry = xmalloc (strlen (str) + strlen (prefix) + 1);
+ entry[0] = '\0';
+ if (strlen (prefix))
+ {
+ strncat (entry, prefix, strlen (prefix));
+ }
+
+ if (array == 1)
+ {
+ strncat (entry, str, keylen);
+ strcat (entry, str + keylen + 2);
+ }
+ else
+ {
+ strcat (entry, str);
+ }
+
+ /* does the value already exist? */
+ len = keylen + strlen (prefix) + 1;
+ while (cur != NULL)
+ {
+ if (memcmp (cur->buf, entry, len) == 0)
+ {
+ if (array == 1)
+ {
+ /* if an array, create a new string with this
+ * value added to the end of the old value(s)
+ */
+ temp = xmalloc (strlen (cur->buf) + len + 1);
+ memmove (temp, cur->buf, strlen (cur->buf) + 1);
+ strcat (temp, "\n");
+ strcat (temp, str + keylen + 3);
+ free (entry);
+ entry = temp;
+ }
+ /* delete the old entry */
+ free (cur->buf);
+ if (prev != NULL)
+ prev->next = cur->next;
+ free (cur);
+ cur = prev;
+ } /* end if found a matching key */
+ prev = cur;
+ if (cur)
+ {
+ cur = (list_t *) cur->next;
+ }
+ } /* end if matching key */
+
+ /* add the value to the end of the chain */
+ cur = xmalloc (sizeof (list_t));
+ cur->buf = entry;
+ if (prev != NULL)
+ prev->next = cur;
+
+ return (cur);
+}
+
+/* free list_t chain */
+void
+free_list_chain (list_t * list)
+{
+ list_t *next;
+
+ while (list)
+ {
+ next = list->next;
+ free (list->buf);
+ free (list);
+ list = next;
+ }
+}
+
+
+
+/* readenv
+ * reads the current environment and popluates our environment chain
+ */
+
+void
+readenv (list_t * env)
+{
+ extern char **environ;
+ int count = 0;
+
+ while (environ[count] != NULL)
+ {
+ myputenv (env, environ[count], global.nul_prefix);
+ count++;
+ }
+}
+
+
+/* CookieVars ()
+ * if HTTP_COOKIE is passed as an environment variable,
+ * attempt to parse its values into environment variables
+ */
+void
+CookieVars (list_t * env)
+{
+ char *qs;
+ char *token;
+
+ if (getenv ("HTTP_COOKIE") != NULL)
+ {
+ qs = strdup (getenv ("HTTP_COOKIE"));
+ }
+ else
+ {
+ return;
+ }
+
+ /** split on; to extract name value pairs */
+ token = strtok (qs, ";");
+ while (token)
+ {
+ // skip leading spaces
+ while (token[0] == ' ')
+ {
+ token++;
+ }
+ myputenv (env, token, global.var_prefix);
+ myputenv (env, token, global.cookie_prefix);
+ token = strtok (NULL, ";");
+ }
+ free (qs);
+}
+
+
+/* SessionID
+ * Makes a uniqe SESSIONID environment variable for this script
+ */
+
+void
+sessionid (list_t * env)
+{
+ char session[29];
+
+ sprintf (session, "SESSIONID=%x%x", getpid (), (int) time (NULL));
+ myputenv (env, session, global.nul_prefix);
+}
+
+list_t *
+wcversion (list_t * env)
+{
+ char version[200];
+ sprintf (version, "HASERLVER=%s", PACKAGE_VERSION);
+ return (myputenv (env, version, global.nul_prefix));
+}
+
+
+void
+haserlflags (list_t * env)
+{
+ char buf[200];
+
+ snprintf (buf, 200, "HASERL_UPLOAD_DIR=%s", global.uploaddir);
+ myputenv (env, buf, global.nul_prefix);
+
+ snprintf (buf, 200, "HASERL_UPLOAD_LIMIT=%lu", global.uploadkb);
+ myputenv (env, buf, global.nul_prefix);
+
+ snprintf (buf, 200, "HASERL_ACCEPT_ALL=%d", global.acceptall);
+ myputenv (env, buf, global.nul_prefix);
+
+ snprintf (buf, 200, "HASERL_SHELL=%s", global.shell);
+ myputenv (env, buf, global.nul_prefix);
+
+}
+
+/*
+ * Read cgi variables from query string, and put in environment
+ */
+
+int
+ReadCGIQueryString (list_t * env)
+{
+ char *qs;
+ char *token;
+ int i;
+
+ if (getenv ("QUERY_STRING") != NULL)
+ {
+ qs = strdup (getenv ("QUERY_STRING"));
+ }
+ else
+ {
+ return (0);
+ }
+
+ /* change plusses into spaces */
+ for (i = 0; qs[i]; i++)
+ {
+ if (qs[i] == '+')
+ {
+ qs[i] = ' ';
+ }
+ };
+
+ /** split on & and ; to extract name value pairs */
+
+ token = strtok (qs, "&;");
+ while (token)
+ {
+ unescape_url (token);
+ myputenv (env, token, global.var_prefix);
+ myputenv (env, token, global.get_prefix);
+ token = strtok (NULL, "&;");
+ }
+ free (qs);
+ return (0);
+}
+
+
+/*
+ * Read cgi variables from stdin (for POST queries)
+ */
+
+int
+ReadCGIPOSTValues (list_t * env)
+{
+ size_t content_length = 0;
+ size_t max_len;
+ size_t i, j, x;
+ sliding_buffer_t sbuf;
+ buffer_t token;
+ unsigned char *data;
+ const char *CONTENT_LENGTH = "CONTENT_LENGTH";
+
+ if (getenv (CONTENT_LENGTH) == NULL)
+ return (0);
+
+ if (getenv ("CONTENT_TYPE"))
+ {
+ if (strncasecmp (getenv ("CONTENT_TYPE"), "multipart/form-data", 19)
+ == 0)
+ {
+ /* This is a mime request, we need to go to the mime handler */
+ i = rfc2388_handler (env);
+ return (i);
+ }
+ }
+
+ s_buffer_init (&sbuf, 32768);
+ sbuf.fh = STDIN;
+ if ((getenv (CONTENT_LENGTH)) ||
+ (strtoul (getenv(CONTENT_LENGTH), NULL, 10) == 0 ))
+ {
+ sbuf.maxread = strtoul (getenv (CONTENT_LENGTH), NULL, 10);
+ }
+ buffer_init (&token);
+
+
+ /* Allow 2MB content, unless they have a global upload set */
+ max_len = ((global.uploadkb == 0) ? 2048 : global.uploadkb) *1024;
+
+ do
+ {
+ /* x is true if this token ends with a matchstr or is at the end of stream */
+ x = s_buffer_read (&sbuf, "&");
+ content_length += sbuf.len;
+ if (content_length > max_len)
+ {
+ die_with_message (NULL, NULL,
+ "Attempted to send content larger than allowed limits.");
+ }
+
+ if ((x == 0) || (token.data))
+ {
+ buffer_add (&token, (char *) sbuf.segment, sbuf.len);
+ }
+
+ if (x)
+ {
+ data = sbuf.segment;
+ sbuf.segment[sbuf.len] = '\0';
+ if (token.data)
+ {
+ /* add the ASCIIZ */
+ buffer_add (&token, sbuf.segment + sbuf.len, 1);
+ data = token.data;
+ }
+
+ /* change plusses into spaces */
+ j = strlen ((char *) data);
+ for (i = 0; i <= j; i++)
+ {
+ if (data[i] == '+')
+ {
+ data[i] = ' ';
+ }
+ }
+ unescape_url ((char *) data);
+ myputenv (env, (char *) data, global.var_prefix);
+ myputenv (env, (char *) data, global.post_prefix);
+ if (token.data)
+ {
+ buffer_reset (&token);
+ }
+ }
+ }
+ while (!sbuf.eof);
+ s_buffer_destroy (&sbuf);
+ buffer_destroy (&token);
+ return (0);
+}
+
+
+int
+parseCommandLine (int argc, char *argv[])
+{
+ int c;
+ int option_index = 0;
+
+ /* set optopt and optind to 0 to reset getopt_long -
+ * we may call it multiple times
+ */
+ optopt = 0;
+ optind = 0;
+
+ while ((c = getopt_long (argc, argv, gs_short_options,
+ ga_long_options, &option_index)) != -1)
+ {
+ switch (c)
+ {
+ case 'd':
+ global.debug = TRUE;
+ break;
+ case 's':
+ global.shell = optarg;
+ break;
+ case 'S':
+ global.silent = TRUE;
+ break;
+ case 'u':
+ if (optarg)
+ {
+ global.uploadkb = atoi (optarg);
+ }
+ else
+ {
+ global.uploadkb = MAX_UPLOAD_KB;
+ }
+ break;
+ case 'a':
+ global.acceptall = TRUE;
+ break;
+ case 'n':
+ global.acceptall = NONE;
+ break;
+ case 'U':
+ global.uploaddir = optarg;
+ break;
+ case 'H':
+ global.uploadhandler = optarg;
+ break;
+ case 'v':
+ case 'h':
+ printf ("This is " PACKAGE_NAME " version " PACKAGE_VERSION ""
+ " (http://haserl.sourceforge.net)\n");
+ exit (0);
+ break;
+ }
+ }
+ return (optind);
+}
+
+int
+BecomeUser (uid_t uid, gid_t gid)
+{
+ /* This silently fails if it doesn't work */
+ /* Following is from Timo Teras */
+ if (getuid () == 0)
+ setgroups (1, &gid);
+
+ setgid (gid);
+ setgid (getgid ());
+
+ setuid (uid);
+ setuid (getuid ());
+
+ return (0);
+}
+
+/*
+ * Assign default values to the global structure
+ */
+
+void
+assignGlobalStartupValues ()
+{
+ global.uploadkb = 0; /* how big an upload do we allow (0 for none) */
+ global.shell = SUBSHELL_CMD; /* The shell we use */
+ global.silent = FALSE; /* We do print errors if we find them */
+ global.uploaddir = TEMPDIR; /* where to upload to */
+ global.uploadhandler = NULL; /* the upload handler */
+ global.debug = FALSE; /* Not in debug mode. */
+ global.acceptall = FALSE; /* don't allow POST data for GET method */
+ global.uploadlist = NULL; /* we don't have any uploaded files */
+ global.var_prefix = "FORM_";
+ global.get_prefix = "GET_";
+ global.post_prefix = "POST_";
+ global.cookie_prefix = "COOKIE_";
+ global.nul_prefix = "";
+
+}
+
+
+void
+unlink_uploadlist ()
+{
+ token_t *me;
+ me = global.uploadlist;
+
+ while (me)
+ {
+ unlink (me->buf);
+ free (me->buf);
+ me = me->next;
+ }
+
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Main
+ *
+ *------------------------------------------------------------------------*/
+
+int
+main (int argc, char *argv[])
+{
+#ifndef JUST_LUACSHELL
+ token_t *tokenchain = NULL;
+ buffer_t script_text;
+#endif
+ script_t *scriptchain;
+
+ int retval = 0;
+ char *filename = NULL;
+
+ argv_t *av = NULL;
+ char **av2 = argv;
+ int av2c = argc;
+
+ int command;
+ int count;
+
+ list_t *env = NULL;
+
+ assignGlobalStartupValues ();
+#ifndef JUST_LUACSHELL
+ buffer_init (&script_text);
+#endif
+
+ /* if more than argv[1] and argv[1] is not a file */
+ switch (argc)
+ {
+ case 1:
+ /* we were run, instead of called as a shell script */
+ puts ("This is " PACKAGE_NAME " version " PACKAGE_VERSION "\n"
+ "This program runs as a cgi interpeter, not interactively\n"
+ "Please see: http://haserl.sourceforge.net\n"
+#ifdef USE_LUA
+ "This version includes Lua (precompiled"
+#ifdef INCLUDE_LUASHELL
+ " and interpreted"
+#endif
+ ")\n"
+#endif
+#ifdef BASHEXTENSIONS
+ "Unsupported bash extensions supplied by simnux enabled\n"
+#endif
+ );
+ return (0);
+ break;
+ default: /* more than one */
+ /* split combined #! args - linux bundles them as one */
+ command = argc_argv (argv[1], &av, "");
+
+ if (command > 1)
+ {
+ /* rebuild argv into new av2 */
+ av2c = argc - 1 + command;
+ av2 = xmalloc (sizeof (char *) * av2c);
+ av2[0] = argv[0];
+ for (count = 1; count <= command; count++)
+ {
+ av2[count] = av[count-1].string;
+ }
+ for (; count < av2c; count++) {
+ av2[count] = argv[count - command + 1];
+ }
+ }
+
+ parseCommandLine (av2c, av2);
+ free (av);
+ if (av2 != argv) free (av2);
+
+ if (optind < av2c)
+ {
+ filename = av2[optind];
+ }
+ else
+ {
+ die_with_message (NULL, NULL, "No script file specified");
+ }
+
+ break;
+ }
+
+
+ scriptchain = load_script (filename, NULL);
+/* drop permissions */
+ BecomeUser (scriptchain->uid, scriptchain->gid);
+
+ /* populate the function pointers based on the shell selected */
+ if (strcmp (global.shell, "lua") && strcmp (global.shell, "luac"))
+ /* default to "bash" */
+ {
+#ifdef INCLUDE_BASHSHELL
+ shell_exec = &bash_exec;
+ shell_echo = &bash_echo;
+ shell_eval = &bash_eval;
+ shell_setup = &bash_setup;
+ shell_doscript = &bash_doscript;
+ shell_destroy = &bash_destroy;
+
+#ifdef BASHEXTENSIONS
+ shell_if = &bash_if;
+ shell_elif = &bash_elif;
+ shell_else = &bash_else;
+ shell_endif = &bash_endif;
+ shell_case = &bash_case;
+ shell_when = &bash_when;
+ shell_otherwise = &bash_otherwise;
+ shell_endcase = &bash_endcase;
+ shell_while = &bash_while;
+ shell_endwhile = &bash_endwhile;
+ shell_until = &bash_until;
+ shell_enduntil = &bash_enduntil;
+ shell_for = &bash_for;
+ shell_endfor = &bash_endfor;
+ shell_unless = &bash_unless;
+ shell_elun = &bash_elun;
+ shell_unelse = &bash_unelse;
+ shell_endunless = &bash_endunless;
+#endif
+
+#else
+ die_with_message (NULL, NULL, "Bash shell is not enabled.");
+#endif
+ }
+ else
+ {
+#ifdef USE_LUA
+ shell_setup = &lua_common_setup;
+ shell_destroy = &lua_common_destroy;
+ global.var_prefix = "FORM.";
+ global.nul_prefix = "ENV.";
+
+ if (global.shell[3] == 'c') /* luac only */
+#ifdef INCLUDE_LUACSHELL
+ shell_doscript = &luac_doscript;
+#else
+ die_with_message (NULL, NULL, "Compiled Lua shell is not enabled.");
+#endif
+ else
+ {
+#ifdef INCLUDE_LUASHELL
+ shell_exec = &lua_exec;
+ shell_echo = &lua_echo;
+ shell_eval = &lua_eval;
+ shell_doscript = &lua_doscript;
+#else
+ die_with_message (NULL, NULL, "Standard Lua shell is not enabled.");
+#endif
+ }
+#else
+ die_with_message (NULL, NULL, "Lua shells are not enabled.");
+#endif
+ }
+
+/* Read the current environment into our chain */
+ env = wcversion (env);
+ readenv (env);
+ sessionid (env);
+ haserlflags (env);
+
+#ifndef JUST_LUACSHELL
+ if (strcmp (global.shell, "luac"))
+ {
+ tokenchain = build_token_list (scriptchain, NULL);
+ preprocess_token_list (tokenchain);
+ }
+#endif
+
+/* Read the request data */
+ if (global.acceptall != NONE)
+ {
+ /* If we have a request method, and we were run as a #! style script */
+ CookieVars (env);
+ if (getenv ("REQUEST_METHOD"))
+ {
+ if (strcasecmp (getenv ("REQUEST_METHOD"), "GET") == 0)
+ {
+ if (global.acceptall == TRUE)
+ ReadCGIPOSTValues (env);
+ ReadCGIQueryString (env);
+ }
+
+ if (strcasecmp (getenv ("REQUEST_METHOD"), "POST") == 0)
+ {
+ if (global.acceptall == TRUE)
+ retval = ReadCGIQueryString (env);
+ retval = ReadCGIPOSTValues (env);
+ }
+ }
+ }
+
+/* build a copy of the script to send to the shell */
+#ifndef JUST_LUACSHELL
+ if (strcmp (global.shell, "luac"))
+ {
+ process_token_list (&script_text, tokenchain);
+ }
+#endif
+
+ /* run the script */
+ if (global.debug == TRUE)
+ {
+#ifndef JUST_LUACSHELL
+ if (getenv ("REQUEST_METHOD"))
+ {
+ write (1, "Content-Type: text/plain\n\n", 26);
+ }
+ write (1, script_text.data, script_text.ptr - script_text.data);
+#else
+ die_with_message (NULL, NULL,
+ "Debugging output doesn't work with the compiled Lua shell.");
+#endif
+ }
+ else
+ {
+ shell_setup (global.shell, env);
+#ifdef JUST_LUACSHELL
+ shell_doscript (NULL, scriptchain->name);
+#else
+ shell_doscript (&script_text, scriptchain->name);
+#endif
+ shell_destroy ();
+ }
+
+
+ if (global.uploadlist)
+ {
+ unlink_uploadlist ();
+ free_token_list (global.uploadlist);
+ }
+
+#ifndef JUST_LUACSHELL
+ /* destroy the script */
+ buffer_destroy (&script_text);
+ free_token_list (tokenchain);
+#endif
+
+ free_list_chain (env);
+ free_script_list (scriptchain);
+
+ return (0);
+
+}
diff --git a/src/haserl.h b/src/haserl.h
new file mode 100644
index 0000000..e7b0e1f
--- /dev/null
+++ b/src/haserl.h
@@ -0,0 +1,77 @@
+#ifndef _HASERL_H
+#define _HASERL_H 1
+
+
+/* Just a silly construct to contain global variables */
+typedef struct
+{
+ unsigned long uploadkb; /* how big an upload do we allow (0 for none)*/
+ char *shell; /* The shell we use */
+ char *uploaddir; /* where we upload to */
+ char *uploadhandler; /* a handler for uploads */
+ char *var_prefix; /* what name we give to FORM variables */
+ char *get_prefix; /* what name we give to POST variables */
+ char *post_prefix; /* what name we give to POST variables */
+ char *cookie_prefix; /* what name we give to COOKIE variables */
+ char *nul_prefix; /* what name we give to environment variables*/
+ token_t *uploadlist; /* a linked list of pathspecs */
+ int debug; /* true if in "debug" mode */
+ int acceptall; /* true if we'll accept POST data on
+ GETs and vice versa */
+ int silent; /* true if we never print errors */
+} haserl_t;
+
+extern haserl_t global;
+
+char x2c(char *what);
+void unescape_url(char *url);
+void *xmalloc (size_t size);
+void *xrealloc (void *buf, size_t size);
+list_t *myputenv(list_t *cur, char *str, char *prefix);
+void free_list_chain ( list_t *);
+void readenv(list_t *env);
+void CookieVars(list_t *env);
+void sessionid(list_t *env);
+list_t *wcversion(list_t *env);
+void haserlflags(list_t *env);
+int ReadCGIQueryString(list_t *env);
+int ReadCGIPOSTValues(list_t *env);
+int LineToStr(char *string, size_t max);
+int ReadMimeEncodedInput(list_t *env);
+void PrintParseError(char *error, int linenum);
+int parseCommandLine(int argc, char *argv[]);
+int BecomeUser(uid_t uid, gid_t gid);
+void assignGlobalStartupValues(void);
+void unlink_uploadlist (void);
+int main(int argc, char *argv[]);
+
+
+extern void (*shell_exec)(buffer_t *buf, char *str);
+extern void (*shell_echo)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_eval)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_setup)( char *, list_t *);
+extern void (*shell_doscript)( buffer_t *, char *);
+extern void (*shell_destroy) (void);
+
+#ifdef BASHEXTENSIONS
+extern void (*shell_if)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_elif)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_else)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_endif)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_case)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_when)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_otherwise)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_endcase)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_while)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_endwhile)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_until)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_enduntil)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_for)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_endfor)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_unless)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_elun)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_unelse)(buffer_t *buf, char *str, size_t len);
+extern void (*shell_endunless)(buffer_t *buf, char *str, size_t len);
+#endif
+#endif /* !_HASERL_H */
+
diff --git a/src/haserl_lualib.inc b/src/haserl_lualib.inc
new file mode 100644
index 0000000..e533384
--- /dev/null
+++ b/src/haserl_lualib.inc
@@ -0,0 +1,90 @@
+/* This file was automatically generated from haserl_lualib.lua. DO NOT EDIT */
+
+static const unsigned char haserl_lualib[] = {
+ 27, 76,117, 97, 81, 0, 1, 4, 4, 4, 8, 0, 19, 0, 0, 0,
+ 64,104, 97,115,101,114,108, 95,108,117, 97,108,105, 98, 46,108,
+ 117, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 16,
+ 0, 0, 0, 10, 0, 0, 0, 74, 0, 0, 0,138, 0, 0, 0,135,
+ 128, 0, 0, 71, 64, 0, 0, 7, 0, 0, 0, 5, 0, 0, 0,100,
+ 0, 0, 0, 9, 64,128,129, 5, 0, 0, 0,100, 64, 0, 0, 9,
+ 64, 0,130, 5, 0, 0, 0,100,128, 0, 0, 9, 64,128,130, 30,
+ 0,128, 0, 6, 0, 0, 0, 4, 7, 0, 0, 0,104, 97,115,101,
+ 114,108, 0, 4, 5, 0, 0, 0, 70, 79, 82, 77, 0, 4, 4, 0,
+ 0, 0, 69, 78, 86, 0, 4, 9, 0, 0, 0,115,101,116,102,105,
+ 101,108,100, 0, 4, 9, 0, 0, 0,103,101,116,102,105,101,108,
+ 100, 0, 4, 9, 0, 0, 0,109,121,112,117,116,101,110,118, 0,
+ 3, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 37, 0, 0, 0,
+ 0, 2, 0, 10, 29, 0, 0, 0,133, 0, 0, 0,197, 64, 0, 0,
+ 198,128,192, 1, 0, 1, 0, 0, 65,193, 0, 0,220, 0,129, 1,
+ 22,128, 4,128, 5, 2, 1, 0, 64, 2, 0, 3, 28,130, 0, 1,
+ 26, 2, 0, 0, 22,192, 0,128, 5, 2, 1, 0, 64, 2, 0, 3,
+ 28,130, 0, 1,128, 1, 0, 4, 23, 64,193, 3, 22,128, 1,128,
+ 6,130, 1, 1, 26, 66, 0, 0, 22, 0, 0,128, 10, 2, 0, 0,
+ 137, 0, 2, 3,134,128, 1, 1, 22, 0, 0,128,137, 64, 0, 3,
+ 225,128, 0, 0, 22,128,250,127, 30, 0,128, 0, 6, 0, 0, 0,
+ 4, 3, 0, 0, 0, 95, 71, 0, 4, 7, 0, 0, 0,115,116,114,
+ 105,110,103, 0, 4, 6, 0, 0, 0,103,102,105,110,100, 0, 4,
+ 15, 0, 0, 0, 40, 91, 37,119, 95, 37, 45, 93, 43, 41, 40, 46,
+ 63, 41, 0, 4, 9, 0, 0, 0,116,111,110,117,109, 98,101,114,
+ 0, 4, 2, 0, 0, 0, 46, 0, 0, 0, 0, 0, 29, 0, 0, 0,
+ 25, 0, 0, 0, 26, 0, 0, 0, 26, 0, 0, 0, 26, 0, 0, 0,
+ 26, 0, 0, 0, 26, 0, 0, 0, 26, 0, 0, 0, 27, 0, 0, 0,
+ 27, 0, 0, 0, 27, 0, 0, 0, 27, 0, 0, 0, 27, 0, 0, 0,
+ 28, 0, 0, 0, 28, 0, 0, 0, 28, 0, 0, 0, 28, 0, 0, 0,
+ 30, 0, 0, 0, 30, 0, 0, 0, 31, 0, 0, 0, 31, 0, 0, 0,
+ 31, 0, 0, 0, 31, 0, 0, 0, 31, 0, 0, 0, 32, 0, 0, 0,
+ 32, 0, 0, 0, 34, 0, 0, 0, 26, 0, 0, 0, 35, 0, 0, 0,
+ 37, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0,102, 0, 0, 0,
+ 0, 0, 28, 0, 0, 0, 2, 0, 0, 0,118, 0, 0, 0, 0, 0,
+ 28, 0, 0, 0, 2, 0, 0, 0,116, 0, 1, 0, 0, 0, 28, 0,
+ 0, 0, 16, 0, 0, 0, 40,102,111,114, 32,103,101,110,101,114,
+ 97,116,111,114, 41, 0, 6, 0, 0, 0, 28, 0, 0, 0, 12, 0,
+ 0, 0, 40,102,111,114, 32,115,116, 97,116,101, 41, 0, 6, 0,
+ 0, 0, 28, 0, 0, 0, 14, 0, 0, 0, 40,102,111,114, 32, 99,
+ 111,110,116,114,111,108, 41, 0, 6, 0, 0, 0, 28, 0, 0, 0,
+ 2, 0, 0, 0,119, 0, 7, 0, 0, 0, 26, 0, 0, 0, 2, 0,
+ 0, 0,100, 0, 7, 0, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 39, 0, 0, 0, 45, 0, 0, 0, 0, 1, 0, 8,
+ 12, 0, 0, 0, 69, 0, 0, 0,133, 64, 0, 0,134,128, 64, 1,
+ 192, 0, 0, 0, 1,193, 0, 0,156, 0,129, 1, 22, 0, 0,128,
+ 70, 64,129, 0,161, 64, 0, 0, 22, 0,255,127, 94, 0, 0, 1,
+ 30, 0,128, 0, 4, 0, 0, 0, 4, 3, 0, 0, 0, 95, 71, 0,
+ 4, 7, 0, 0, 0,115,116,114,105,110,103, 0, 4, 6, 0, 0,
+ 0,103,102,105,110,100, 0, 4, 7, 0, 0, 0, 91, 37,119, 95,
+ 93, 43, 0, 0, 0, 0, 0, 12, 0, 0, 0, 40, 0, 0, 0, 41,
+ 0, 0, 0, 41, 0, 0, 0, 41, 0, 0, 0, 41, 0, 0, 0, 41,
+ 0, 0, 0, 41, 0, 0, 0, 42, 0, 0, 0, 41, 0, 0, 0, 42,
+ 0, 0, 0, 44, 0, 0, 0, 45, 0, 0, 0, 6, 0, 0, 0, 2,
+ 0, 0, 0,102, 0, 0, 0, 0, 0, 11, 0, 0, 0, 2, 0, 0,
+ 0,118, 0, 1, 0, 0, 0, 11, 0, 0, 0, 16, 0, 0, 0, 40,
+ 102,111,114, 32,103,101,110,101,114, 97,116,111,114, 41, 0, 6,
+ 0, 0, 0, 10, 0, 0, 0, 12, 0, 0, 0, 40,102,111,114, 32,
+ 115,116, 97,116,101, 41, 0, 6, 0, 0, 0, 10, 0, 0, 0, 14,
+ 0, 0, 0, 40,102,111,114, 32, 99,111,110,116,114,111,108, 41,
+ 0, 6, 0, 0, 0, 10, 0, 0, 0, 2, 0, 0, 0,119, 0, 7,
+ 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47,
+ 0, 0, 0, 53, 0, 0, 0, 0, 2, 0, 6, 20, 0, 0, 0,133,
+ 0, 0, 0,134, 64, 64, 1,192, 0, 0, 0, 1,129, 0, 0, 65,
+ 193, 0, 0,156,128, 0, 2, 0, 0, 0, 1,133, 0, 0, 0,134,
+ 64, 64, 1,192, 0, 0, 0, 1, 1, 1, 0, 65,193, 0, 0,156,
+ 128, 0, 2, 0, 0, 0, 1,133, 64, 1, 0,134,128, 65, 1,192,
+ 0, 0, 0, 0, 1,128, 0,156, 64,128, 1, 30, 0,128, 0, 7,
+ 0, 0, 0, 4, 7, 0, 0, 0,115,116,114,105,110,103, 0, 4,
+ 5, 0, 0, 0,103,115,117, 98, 0, 4, 7, 0, 0, 0, 91, 92,
+ 93, 92, 91, 93, 0, 4, 2, 0, 0, 0, 46, 0, 4, 6, 0, 0,
+ 0, 91, 92, 46, 93, 43, 0, 4, 7, 0, 0, 0,104, 97,115,101,
+ 114,108, 0, 4, 9, 0, 0, 0,115,101,116,102,105,101,108,100,
+ 0, 0, 0, 0, 0, 20, 0, 0, 0, 49, 0, 0, 0, 49, 0, 0,
+ 0, 49, 0, 0, 0, 49, 0, 0, 0, 49, 0, 0, 0, 49, 0, 0,
+ 0, 49, 0, 0, 0, 50, 0, 0, 0, 50, 0, 0, 0, 50, 0, 0,
+ 0, 50, 0, 0, 0, 50, 0, 0, 0, 50, 0, 0, 0, 50, 0, 0,
+ 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0, 0, 52, 0, 0,
+ 0, 52, 0, 0, 0, 53, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0,
+ 0,107,101,121, 0, 0, 0, 0, 0, 19, 0, 0, 0, 6, 0, 0,
+ 0,118, 97,108,117,101, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0,
+ 0, 0, 0, 16, 0, 0, 0, 21, 0, 0, 0, 21, 0, 0, 0, 21,
+ 0, 0, 0, 21, 0, 0, 0, 21, 0, 0, 0, 21, 0, 0, 0, 23,
+ 0, 0, 0, 37, 0, 0, 0, 23, 0, 0, 0, 39, 0, 0, 0, 45,
+ 0, 0, 0, 39, 0, 0, 0, 47, 0, 0, 0, 53, 0, 0, 0, 47,
+ 0, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
diff --git a/src/haserl_lualib.lua b/src/haserl_lualib.lua
new file mode 100644
index 0000000..94e0e43
--- /dev/null
+++ b/src/haserl_lualib.lua
@@ -0,0 +1,54 @@
+-- --------------------------------------------------------------------------
+-- haserl luascript library
+-- $Id: haserl.c,v 1.32 2005/11/22 15:56:42 nangel Exp $
+-- Copyright (c) 2003-2007 Nathan Angelacos (nangel@users.sourceforge.net)
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License, version 2,
+-- as published by the Free Software Foundation.
+--
+-- 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
+--
+-- --------------------------------------------------------------------------
+
+haserl, FORM, ENV = {}, {}, {}
+
+function haserl.setfield (f, v)
+ -- From programming in Lua 1st Ed.
+ local t = _G -- start with the table of globals
+ for w, d in string.gfind(f, '([%w_%-]+)(.?)') do
+ if (tonumber(w)) then
+ w = tonumber(w)
+ end
+ if d == '.' then -- not last field?
+ t[w] = t[w] or {} -- create table if absent
+ t = t[w] -- get the table
+ else -- last field
+ t[w] = v -- do the assignment
+ end
+ end
+end
+
+function haserl.getfield (f)
+ local v = _G -- start with the table of globals
+ for w in string.gfind(f, '[%w_]+') do
+ v = v[w]
+ end
+ return v
+end
+
+function haserl.myputenv(key, value)
+ -- convert key to dotted form
+ key = string.gsub(key, '[\\]\\[]', '.' )
+ key = string.gsub(key, '[\\.]+', '.' )
+ -- and create a table if necessary
+ haserl.setfield (key, value)
+end
+
diff --git a/src/lua2c.c b/src/lua2c.c
new file mode 100644
index 0000000..f1f7abe
--- /dev/null
+++ b/src/lua2c.c
@@ -0,0 +1,97 @@
+/* --------------------------------------------------------------------------
+ * a simple lua 2 c function converter - a simple luac + bin2c
+ * Copyright (c) 2007 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ * ------------------------------------------------------------------------- */
+
+
+/* This program loads the source file, and then uses lua_dump to output it
+ * to a c const char array. Because lua_dump is used instead of the internal
+ * luaU_dump function, debugging info is in the output array. If you want
+ * to compile the array without debugging information, first use luac on the
+ * .lua source, and then run lua2c on that output:
+ * luac -s -o foo haserl_lualib.lua
+ * lua2c haserl_lualib foo >haserl_lualib.inc
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+lua_State *lua_vm = NULL;
+
+
+static void
+loadit (char *filename)
+{
+
+lua_vm = luaL_newstate();
+luaL_openlibs (lua_vm);
+
+if (luaL_loadfile(lua_vm, filename)) {
+ puts (lua_tostring(lua_vm, -1));
+ exit (-1);
+ }
+
+}
+
+static int writer (lua_State* L, const void* p, size_t size, void* u)
+{
+ static int count = 0;
+ int i;
+ for (i=0; i < size; i++ ) {
+ if ((count) && (count % 16) == 0 ) printf ("\n ");
+ printf ("%3d,", *((unsigned char *) (p+i)));
+ count++;
+ }
+
+ return (0);
+}
+
+
+static void
+dumpit()
+{
+lua_dump (lua_vm, writer, NULL);
+}
+
+
+int
+main (int argc, char *argv[]) {
+ if (argc != 3) {
+ printf("usage: %s varname luasource >output\n", argv[0]);
+ return (-1);
+ }
+
+ loadit (argv[2]);
+
+
+ printf ("/* This file was automatically generated from %s. DO NOT EDIT */\n\n", argv[2]);
+ printf ("static const unsigned char %s[] = { \n ", argv[1]);
+ dumpit();
+ printf ("\n};\n");
+
+ return (0);
+}
diff --git a/src/rfc2388.c b/src/rfc2388.c
new file mode 100644
index 0000000..8aa5d19
--- /dev/null
+++ b/src/rfc2388.c
@@ -0,0 +1,508 @@
+/* --------------------------------------------------------------------------
+ * multipart/form-data handler functions (obviously, see rfc2388 for info)
+ * Copyright (c) 2007 Nathan Angelacos (nangel@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 2, as published by the Free
+ * Software Foundation.
+ *
+ * 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
+ *
+ ------------------------------------------------------------------------- */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#include "common.h"
+#include "h_error.h"
+#include "h_script.h"
+#include "h_bash.h"
+#include "sliding_buffer.h"
+#include "rfc2388.h"
+
+#include "haserl.h"
+
+void
+empty_stdin (void)
+{
+ char c[2000];
+ while (read (STDIN_FILENO, &c, 2000))
+ {
+ };
+}
+
+
+void
+mime_var_init (mime_var_t * obj)
+{
+ obj->name = NULL;
+ obj->filename = NULL;
+ obj->type = NULL;
+ obj->tempname = NULL;
+ buffer_init (&(obj->value));
+ obj->fh = 0;
+}
+
+void
+mime_var_destroy (mime_var_t * obj)
+{
+ int status;
+
+ if (obj->name)
+ {
+ free (obj->name);
+ obj->name = NULL;
+ }
+ if (obj->filename)
+ {
+ free (obj->filename);
+ obj->filename = NULL;
+ }
+ if (obj->type)
+ {
+ free (obj->type);
+ obj->type = NULL;
+ }
+ if (obj->tempname)
+ {
+ free (obj->tempname);
+ obj->tempname = NULL;
+ }
+ buffer_destroy (&(obj->value));
+ if (obj->fh)
+ {
+ close (obj->fh);
+ if (global.uploadhandler)
+ {
+ wait (&status);
+ }
+ obj->fh = 0;
+ }
+}
+
+char *
+mime_substr (char *start, int len)
+{
+ char *ptr;
+
+ if (!start)
+ return NULL;
+ if (len < 0)
+ return NULL;
+ ptr = xmalloc (len + 2);
+ memcpy (ptr, start, len);
+ return ptr;
+
+}
+
+void
+mime_tag_add (mime_var_t * obj, char *str)
+{
+ char *a = NULL;
+ char *b = NULL;
+ static char *tag[] = { "name=\"", "filename=\"", "Content-Type: " };
+
+ a = strcasestr (str, tag[0]);
+ if (a)
+ {
+ a += strlen (tag[0]);
+ b = strchr (a, '"');
+ if (!obj->name)
+ obj->name = mime_substr (a, b - a);
+ }
+
+ a = strcasestr (str, tag[1]);
+ if (a)
+ {
+ a += strlen (tag[1]);
+ b = strchr (a, '"');
+ if (!obj->filename)
+ obj->filename = mime_substr (a, b - a);
+ }
+
+ a = strcasestr (str, tag[2]);
+ if (a)
+ {
+ a += strlen (tag[2]);
+ b = a + strlen (a);
+ if (!obj->type)
+ obj->type = mime_substr (a, b - a);
+ }
+}
+
+void
+mime_var_putenv (list_t * env, mime_var_t * obj)
+{
+ buffer_t buf;
+ buffer_init (&buf);
+ if (obj->name)
+ {
+ buffer_add (&(obj->value), "", 1);
+ buffer_add (&buf, obj->name, strlen (obj->name));
+ buffer_add (&buf, "=", 1);
+ buffer_add (&buf, (char *) obj->value.data,
+ strlen ((char *) obj->value.data) + 1);
+ myputenv (env, (char *) buf.data, global.var_prefix);
+ myputenv (env, (char *) buf.data, global.post_prefix);
+ buffer_reset (&buf);
+ }
+ if (obj->filename)
+ {
+ buffer_add (&buf, obj->name, strlen (obj->name));
+ buffer_add (&buf, "_name=", 6);
+ buffer_add (&buf, obj->filename, strlen (obj->filename) + 1);
+ myputenv (env, (char *) buf.data, global.var_prefix);
+ myputenv (env, (char *) buf.data, global.post_prefix);
+ buffer_reset (&buf);
+ }
+ buffer_destroy (&buf);
+}
+
+void
+mime_exec (mime_var_t * obj, char *fifo)
+{
+
+ int pid;
+ char *av[4];
+ char *type, *filename, *name;
+ char *c;
+ int fh;
+
+ pid = fork ();
+ if (pid == -1)
+ {
+ empty_stdin ();
+ die_with_message (NULL, NULL, g_err_msg[E_SUBSHELL_FAIL]);
+ }
+
+ if (pid == 0)
+ {
+
+ /* store the content type, filename, and form name */
+ /* we do not use global.var_prefix because it could be lua or shell
+ * or something else, and we are only shell here */
+ if (obj->type)
+ {
+
+ type = xmalloc (13 + strlen (obj->type) + 1);
+ sprintf (type, "CONTENT_TYPE=%s", obj->type);
+ putenv (type);
+ }
+ if (obj->filename)
+ {
+ filename = xmalloc (9 + strlen (obj->filename) + 1);
+ sprintf (filename, "FILENAME=%s", obj->filename);
+ putenv (filename);
+ }
+
+ if (obj->name)
+ {
+ name = xmalloc (5 + strlen (obj->name) + 1);
+ sprintf (name, "NAME=%s", obj->name);
+ putenv (name);
+ }
+
+ av[0] = global.uploadhandler;
+ av[1] = fifo;
+ av[2] = NULL;
+ execv (av[0], av);
+ /* if we get here, we had a failure. Not much we can do.
+ * We are the child, so we can't even warn the parent */
+ fh = open (fifo, O_RDONLY);
+ while (read (fh, &c, 1))
+ {
+ }
+ exit (-1);
+ }
+ else
+ {
+ /* I'm parent */
+ }
+
+ /* control should get to this point only in the parent.
+ */
+} /* end mime_exec */
+
+void
+mime_var_open_target (mime_var_t * obj)
+{
+ char *tmpname;
+ token_t *curtoken;
+ curtoken = global.uploadlist;
+ int ok;
+
+ /* if upload_limit is zero, we die right here */
+ if (global.uploadkb == 0)
+ {
+ empty_stdin ();
+ die_with_message (NULL, NULL, "File uploads are not allowed.");
+ }
+
+
+ ok = -1;
+ tmpname = xmalloc (strlen (global.uploaddir) + 8);
+ strcpy (tmpname, global.uploaddir);
+ strcat (tmpname, "/XXXXXX");
+ obj->fh = mkstemp (tmpname);
+
+ if (obj->fh == -1)
+ {
+ ok = 0;
+ }
+
+ /* reuse the name as a fifo if we have a handler. We do this
+ * because tempnam uses TEMPDIR if defined, among other bugs
+ */
+ if ((ok) && global.uploadhandler)
+ {
+
+ /* I have a handler */
+ close (obj->fh);
+ unlink (tmpname);
+ if (mkfifo (tmpname, 0600))
+ ok = 0;
+ /* you must open the fifo for reading before writing
+ * on non linux systems
+ */
+ if (ok)
+ {
+ mime_exec (obj, tmpname);
+ obj->fh = open (tmpname, O_WRONLY);
+ }
+ if (obj->fh == -1)
+ ok = 0;
+ }
+ else
+ {
+ buffer_add (&(obj->value), tmpname, strlen (tmpname));
+ }
+
+ if (!ok)
+ {
+ empty_stdin ();
+ die_with_message (NULL, NULL, g_err_msg[E_FILE_OPEN_FAIL], tmpname);
+ }
+
+ curtoken =
+ push_token_on_list (curtoken, NULL, tmpname, strlen (tmpname) + 1);
+ if (global.uploadlist == NULL)
+ {
+ global.uploadlist = curtoken;
+ }
+
+}
+
+
+
+
+void
+mime_var_writer (mime_var_t * obj, char *str, int len)
+{
+ /* if not a file upload, then just a normal variable */
+ if (!obj->filename)
+ {
+ buffer_add (&(obj->value), str, len);
+ }
+
+ /* if a file upload, but don't have an open filehandle, open one */
+ if ((!obj->fh) && (obj->filename))
+ mime_var_open_target (obj);
+
+ /* if we have an open file, write the chunk */
+ if (obj->fh)
+ {
+ write (obj->fh, str, len);
+ }
+}
+
+/*
+ * Read multipart/form-data input (RFC2388), typically used when
+ * uploading a file.
+ */
+
+int
+rfc2388_handler (list_t * env)
+{
+ enum mime_state_t
+ { DISCARD, BOUNDARY, HEADER, CONTENT };
+
+
+ int state;
+ int i, x;
+ unsigned long max_len, content_length;
+ sliding_buffer_t sbuf;
+ char *crlf = "\r\n";
+ char *boundary;
+ char *str;
+ buffer_t buf;
+ mime_var_t var;
+
+ /* get the boundary info */
+ str = getenv ("CONTENT_TYPE");
+ i = strlen (str) - 9;
+ while ((i >= 0) && (memcmp ("boundary=", str + i, 9)))
+ {
+ i--;
+ }
+ if (i == -1)
+ {
+ empty_stdin ();
+ die_with_message (NULL, NULL, "No Mime Boundary Information Found");
+ }
+
+ i = i + 9;
+ if (str[i] == '"')
+ i++;
+
+ boundary = xmalloc (strlen (str + i) + 5); /* \r\n-- + NULL */
+ memcpy (boundary, crlf, 2);
+ memcpy (boundary + 2, "--", 2);
+ memcpy (boundary + 4, str + i, strlen (str + i) + 1);
+ if ((i > 0) && (str[i - 1] == '"'))
+ {
+ while ((boundary[i]) && (boundary[i] != '"'))
+ i++;
+ boundary[i] = '\0';
+ }
+
+ /* Allow 2MB content, unless they have a global upload set */
+ max_len = ((global.uploadkb == 0) ? 2048 : global.uploadkb) *1024;
+ content_length = 0;
+
+ /* initialize a 128K sliding buffer */
+ s_buffer_init (&sbuf, 1024 * 128);
+ sbuf.fh = STDIN;
+ if (getenv ("CONTENT_LENGTH"))
+ {
+ sbuf.maxread = strtoul (getenv ("CONTENT_LENGTH"), NULL, 10);
+ }
+
+ /* initialize the buffer, and make sure it doesn't point to null */
+ buffer_init (&buf);
+ buffer_add (&buf, "", 1);
+ buffer_reset (&buf);
+
+ state = DISCARD;
+ str = boundary + 2; /* skip the leading crlf */
+ do
+ {
+ /* x is true if this token ends with a matchstr or is at the end of stream */
+ x = s_buffer_read (&sbuf, str);
+ content_length += sbuf.len;
+ if (content_length >= max_len)
+ {
+ empty_stdin ();
+ free (boundary);
+ s_buffer_destroy (&sbuf);
+ buffer_destroy (&buf);
+ if (var.name)
+ {
+ mime_var_destroy (&var);
+ }
+ die_with_message (NULL, NULL,
+ "Attempted to send content larger than allowed limits.");
+ }
+
+ switch (state)
+ {
+
+ case DISCARD:
+ /* discard any text - used for first mime boundary */
+ if (x)
+ {
+ state = BOUNDARY;
+ str = crlf;
+ buffer_reset (&buf); /* reinitializes the buffer */
+ }
+ break;
+
+
+ case BOUNDARY:
+ if (!x)
+ {
+ buffer_add (&buf, sbuf.segment, sbuf.len);
+ }
+ if (x)
+ {
+ buffer_add (&buf, sbuf.segment, sbuf.len);
+ if (!memcmp (buf.data, boundary + 2, 2))
+ { /* "--" */
+ /* all done... what does that mean? */
+ str = boundary + 2;
+ state = DISCARD;
+ }
+ else
+ {
+ buffer_reset (&buf);
+ mime_var_init (&var);
+ state = HEADER;
+ str = crlf;
+ }
+ }
+ break;
+
+ case HEADER:
+ buffer_add (&buf, sbuf.segment, sbuf.len);
+ if (x)
+ {
+ if (sbuf.len == 0)
+ { /* blank line */
+ buffer_reset (&buf);
+ state = CONTENT;
+ str = boundary;
+ }
+ else
+ {
+ buffer_add (&buf, "", 1);
+ mime_tag_add (&var, (char *) buf.data);
+ buffer_reset (&buf);
+ }
+ }
+ break;
+
+ case CONTENT:
+ /* write to writer process, regardless */
+ mime_var_writer (&var, (char *) sbuf.segment, sbuf.len);
+ if (x)
+ {
+ buffer_reset (&buf);
+ mime_var_putenv (env, &var);
+ mime_var_destroy (&var);
+ state = BOUNDARY;
+ str = crlf;
+ }
+
+ break;
+
+ } /* end switch */
+
+ }
+ while (!sbuf.eof);
+ free (boundary);
+ s_buffer_destroy (&sbuf);
+ buffer_destroy (&buf);
+ return (0);
+}
diff --git a/src/rfc2388.h b/src/rfc2388.h
new file mode 100644
index 0000000..acad1ac
--- /dev/null
+++ b/src/rfc2388.h
@@ -0,0 +1,28 @@
+#ifndef _RFC2388_H
+#define _RFC2388_H 1
+
+typedef struct
+{
+ char *name; /* the variable name */
+ char *filename; /* the client-specified filename */
+ char *type; /* the mime-type */
+ char *tempname; /* the tempfilename */
+ buffer_t value; /* the value of the variable */
+ int fh; /* the output file handle */
+ } mime_var_t;
+
+
+/* rfc2388.c */
+void empty_stdin(void);
+void mime_var_init(mime_var_t *obj);
+void mime_var_destroy(mime_var_t *obj);
+char *mime_substr(char *start, int len);
+void mime_tag_add(mime_var_t *obj, char *str);
+void mime_var_putenv(list_t *env, mime_var_t *obj);
+void mime_exec(mime_var_t *obj, char *fifo);
+void mime_var_open_target(mime_var_t *obj);
+void mime_var_writer(mime_var_t *obj, char *str, int len);
+int rfc2388_handler(list_t *env);
+
+
+#endif /* _RFC2388_H */
diff --git a/src/sliding_buffer.c b/src/sliding_buffer.c
new file mode 100644
index 0000000..2b5ef33
--- /dev/null
+++ b/src/sliding_buffer.c
@@ -0,0 +1,180 @@
+/*
+ * --------------------------------------------------------------------------
+ * Sliding Buffer functions for haserl Copyright (c) 2007 Nathan Angelacos
+ * (nangel@users.sourceforge.net) This program is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License, version 2, as published by the Free Software Foundation. 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
+ * -------------------------------------------------------------------------
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "sliding_buffer.h"
+
+/*
+ * initialize a sliding buffer structure
+ */
+int
+s_buffer_init (sliding_buffer_t * sbuf, int size)
+{
+ sbuf->maxsize = size;
+ sbuf->buf = malloc (sbuf->maxsize);
+ /* reduce maxsize by one, so that you can add a NULL to the end of any
+ returned token and not have a memory overwrite */
+ sbuf->maxsize -= 1;
+ sbuf->fh = 0; /* use stdin by default */
+ sbuf->eof = 0;
+ sbuf->len = 0;
+ sbuf->ptr = sbuf->buf;
+ sbuf->bufsize = 0;
+ sbuf->maxread = 0;
+ sbuf->nrread = 0;
+ return (sbuf->buf != NULL); /* return true if the alloc succeeded */
+}
+
+/*
+ * destroy a sliding buffer structure
+ */
+void
+s_buffer_destroy (sliding_buffer_t * sbuf)
+{
+ free (sbuf->buf);
+}
+
+
+/*
+ * read the next segment from a sliding buffer. returns !=0 if the
+ * segment ends at a matchstr token, or if we are at the end of the string
+ * returns 0 if the segment does not end
+ */
+int
+s_buffer_read (sliding_buffer_t * sbuf, char *matchstr)
+{
+ int len, pos;
+ int r;
+
+ /*
+ * if eof and ptr ran off the buffer, then we are done
+ */
+ if ((sbuf->eof) && (sbuf->ptr > sbuf->buf))
+ {
+ return 0;
+ }
+
+ /*
+ * if need to fill the buffer, do so
+ */
+ if ((sbuf->bufsize == 0) ||
+ (sbuf->ptr >= (sbuf->buf + sbuf->bufsize - strlen (matchstr))))
+ {
+ len = sbuf->bufsize - (sbuf->ptr - sbuf->buf);
+ if (len)
+ {
+ memmove (sbuf->buf, sbuf->ptr, len);
+ }
+ sbuf->ptr = sbuf->buf;
+ sbuf->bufsize = len;
+ /* if the filedescriptor is invalid, we are obviously
+ * at an end of file condition.
+ */
+ if (fcntl (sbuf->fh, F_GETFL) == -1)
+ {
+ r = 0;
+ }
+ else
+ {
+ size_t n = sbuf->maxsize - len;
+ if ( sbuf->maxread && sbuf->maxread < sbuf->nrread + n)
+ n = sbuf->maxread - sbuf->nrread;
+ r = read (sbuf->fh, sbuf->buf + len, n);
+ }
+ /*
+ * only report eof when we've done a read of 0.
+ */
+ if (r == 0 || (r < 0 && errno != EINTR))
+ {
+ sbuf->eof = -1;
+ }
+ else
+ {
+ sbuf->bufsize += (r > 0) ? r : 0;
+ sbuf->nrread += (r > 0) ? r : 0;
+ }
+ }
+
+ /*
+ * look for the matchstr
+ */
+ pos = 0;
+ len = sbuf->bufsize - (int) (sbuf->ptr - sbuf->buf) - strlen (matchstr);
+ while (memcmp (matchstr, sbuf->ptr + pos, strlen (matchstr)) && (pos < len))
+ {
+ pos++;
+ }
+
+ /*
+ * if we found it
+ */
+ if (pos < len)
+ {
+ sbuf->len = pos;
+ sbuf->segment = sbuf->ptr;
+ sbuf->ptr = sbuf->segment + pos + strlen (matchstr);
+ return -1;
+ }
+
+ if (sbuf->eof)
+ {
+ len += strlen (matchstr);
+ }
+
+ /*
+ * ran off the end, didn't find the matchstr
+ */
+ sbuf->segment = sbuf->ptr;
+ sbuf->len = len;
+ sbuf->ptr += sbuf->len;
+ return (sbuf->eof) ? (-1) : (0);
+}
+
+
+
+#ifdef TEST_FRAMEWORK
+
+main ()
+{
+ int x;
+ sliding_buffer_t sb;
+ char foo[200];
+
+ s_buffer_init (&sb, 32);
+
+ do
+ {
+ x = s_buffer_read (&sb, "&");
+ sprintf (foo, "%03d- %03d - %03d", x, sb.eof, sb.len);
+ write (1, foo, strlen (foo));
+ write (1, sb.segment, sb.len);
+ write (1, "\n", 1);
+ }
+ while ((!sb.eof));
+ s_buffer_destroy (&sb);
+}
+
+#endif
diff --git a/src/sliding_buffer.h b/src/sliding_buffer.h
new file mode 100644
index 0000000..167e461
--- /dev/null
+++ b/src/sliding_buffer.h
@@ -0,0 +1,27 @@
+#ifndef _SLIDING_BUF_H
+#define _SLIDING_BUF_H 1
+
+
+/* sliding buffer structure */
+typedef struct {
+ int fh; /* the input filehandle for the buffer */
+ unsigned char *buf; /* pointer to the buffer */
+ unsigned char *ptr; /* start positon (used internally) */
+ unsigned char *segment; /* the start position of this segment */
+ size_t len; /* length of this segment */
+ size_t maxsize; /* max size of buffer */
+ size_t bufsize; /* current size of buffer */
+ size_t maxread; /* maximum number of bytes to read from fh, ignored if 0 */
+ size_t nrread; /* number of bytes read from fh */
+ int eof; /* true if there is no more to read */
+ } sliding_buffer_t;
+
+
+
+/* sliding_buffer.c */
+int s_buffer_init(sliding_buffer_t *sbuf, int size);
+void s_buffer_destroy(sliding_buffer_t *sbuf);
+int s_buffer_read(sliding_buffer_t *sbuf, char *matchstr);
+
+
+#endif /* !_SLIDING_BUF_H */
diff --git a/src/stamp-h.in b/src/stamp-h.in
new file mode 100644
index 0000000..9788f70
--- /dev/null
+++ b/src/stamp-h.in
@@ -0,0 +1 @@
+timestamp
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..358ac94
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,14 @@
+PACKAGE=@PACKAGE_TARNAME@
+EXTRA_DIST=minunit.h
+datarootdir=@datarootdir@
+
+
+#--------- Unit Tests ----------------------
+TESTS = utest_common
+
+check_PROGRAMS = utest_common
+
+utest_common: ../src/common.c utest_common.c
+
+#--------- End Unit Tests -------------------
+
diff --git a/tests/minunit.h b/tests/minunit.h
new file mode 100644
index 0000000..749c746
--- /dev/null
+++ b/tests/minunit.h
@@ -0,0 +1,6 @@
+/* file: minunit.h */
+
+#define mu_assert(message, test) do { if (!(test)) return message; } while (0)
+#define mu_run_test(test) do { char *message = test(); tests_run++; \
+ if (message) return message; } while (0)
+extern int tests_run;
diff --git a/tests/utest_common.c b/tests/utest_common.c
new file mode 100644
index 0000000..0085cb4
--- /dev/null
+++ b/tests/utest_common.c
@@ -0,0 +1,46 @@
+/* file minunit_example.c */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "../src/common.h"
+#include "minunit.h"
+
+int tests_run = 0;
+
+
+void * xmalloc=malloc;
+void * xrealloc=realloc;
+
+
+char *test_lowercase() {
+ char source[] = "This is a Test!";
+ int result;
+
+ lowercase(source);
+ result=memcmp("this is a test!", source, strlen(source));
+
+ mu_assert("lowercase failed",result == 0);
+ return 0;
+ }
+
+
+char *all_tests() {
+ mu_run_test(test_lowercase);
+ return 0;
+ }
+
+int main(int argc, char **argv) {
+ char *result = all_tests();
+ printf ("%d\n", (int) result);
+ if (result != 0) {
+ printf("%s\n", result);
+ }
+ else {
+ printf("ALL TESTS PASSED\n");
+ }
+ printf("Tests run: %d\n", tests_run);
+
+ return result != 0;
+ }
+