This is the mail archive of the ecos-patches@sourceware.org mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

ATHTTPD Patch


Another patch for the ATHTTPD.

Too many changes to detail here, the last three entries of
the changelog detail all of them. Most of these changes
are the work of Lars Povlsen and come from his experience
working with the package.

Anthony Tonizzo
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/eCos.hhp /home/atonizzo/ecos/devo/eCos.hhp
--- /home/atonizzo/ecos/clean/eCos.hhp	2006-10-20 11:33:04.000000000 -0700
+++ /home/atonizzo/ecos/devo/eCos.hhp	2006-10-11 15:15:30.000000000 -0700
@@ -3,11 +3,11 @@
 Binary Index=No
 Compatibility=1.1 or later
 Compiled file=eCos.chm
 Contents file=eCos.hhc
 Default Window=mainwin
-Default topic=/home/atonizzo/ecos/clean/doc/index.html
+Default topic=/home/atonizzo/ecos/devo/doc/index.html
 Display compile progress=Yes
 Full-text search=Yes
 Language=0x409 English (United States)
 Title=eCos
 [WINDOWS]
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/cdl/httpd.cdl /home/atonizzo/ecos/devo/packages/net/athttpd/current/cdl/httpd.cdl
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/cdl/httpd.cdl	2006-08-10 10:37:48.000000000 -0700
+++ /home/atonizzo/ecos/devo/packages/net/athttpd/current/cdl/httpd.cdl	2006-11-08 09:44:43.000000000 -0800
@@ -37,11 +37,11 @@
 #####ECOSGPLCOPYRIGHTEND####
 # ====================================================================
 ######DESCRIPTIONBEGIN####
 #
 # Author(s):      Anthony Tonizzo (atonizzo@gmail.com)
-# Contributors:   
+# Contributors:   Lars Povlsen    (lpovlsen@vitesse.com)
 # Date:           2006-06-09
 #
 #####DESCRIPTIONEND####
 #
 # ====================================================================
@@ -116,10 +116,18 @@
           description "This option defines the size of the buffers used to 
                        receive and transmit transmit data to and from the TCP/IP
                        stack."
       }
 
+      cdl_option CYGNUM_ATHTTPD_SERVER_MAX_POST {
+          display "Maximum data received upon POST"
+          flavor data
+          default_value 2048
+          description "This option defines the size of maximum amount of data 
+                       the web server will accept from POST'ed forms."
+      }
+
       cdl_option CYGDAT_NET_ATHTTPD_SERVEROPT_ROOTDIR {
           display "HTTPD root directory"
           flavor data
           default_value {"\"/\""}
           description "This is the absolute path in the eCos file system to the 
@@ -203,10 +211,40 @@
             extensively tested." 
        compile md5c.c
        compile auth.c
     }
 
+    cdl_option CYGOPT_NET_ATHTTPD_CLOSE_CHUNKED_CONNECTIONS {
+        display       "Close connections used for chunked transfers"
+        flavor        bool
+        default_value 1
+        description   "
+            This option causes connections used for chunked transfer to be
+             closed after use. Persisting the connection will use less
+             network resources and will improve latency, but may do so at
+             the risk of compatibility with older browsers."
+    }
+
+    cdl_option CYGOPT_NET_ATHTTPD_DOCUMENT_EXPIRATION_TIME {
+        display          "Maximum lifetime of a document in seconds"
+        flavor           data
+        default_value    0
+        description      "
+            This options causes documents to 'expire' after a set number
+            of seconds. If certain pages are updated frequently, it might be a
+            good idea to assign them an expitation time in seconds. If the
+            client needs to reload a page that has expired, it will request
+            it again, otherwise it will use the copy in the cache. A value
+            of 0 means that this option is disabled. Any other value
+            represents the number of seconds (after the last modification to
+            the document) after which the page becomes stale. This option
+            applies to all the pages (including those that reside in ROM
+            and thus, by definition, won't change) and thus is it preferable
+            to use the CYG_HTTPD_MODE_NO_CACHE mode for pages that need to be
+            refreshed frequently."
+    }
+
     cdl_option CYGOPT_NET_ATHTTPD_USE_DIRLIST {
         display       "Support for directory listing"
         flavor        bool
         default_value 0
         active_if     CYGPKG_IO_FILEIO
@@ -240,13 +278,13 @@
           default_value 0
           description   "This option enables the use of a small tcl
                          interpreter as a means of providing a simple cgi
                          capability. Checking this option increases 
                          considerably the size of the executable." 
-          compile       jim.c
+          compile       jim.c jim-aio.c
           define        JIM_ANSIC
-          define        JIM_EMBEDDED
+          define        JIM_STATICEXT
       }
     }  
 
     cdl_option CYGOPT_NET_ATHTTPD_DEBUG_LEVEL {
         display          "Verbosity of debug output"
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/ChangeLog /home/atonizzo/ecos/devo/packages/net/athttpd/current/ChangeLog
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/ChangeLog	2006-08-10 10:37:48.000000000 -0700
+++ /home/atonizzo/ecos/devo/packages/net/athttpd/current/ChangeLog	2006-11-15 09:40:25.000000000 -0800
@@ -1,62 +1,107 @@
+2006-11-08  Anthony Tonizzo  <atonizzo@gmail.com>
+
+    * doc/athttpd.sgml: Updated to describe lasted changes and corrected
+                         minor typos.
+    * src/http.c: Check for "Content-Type" header. This is needed if we
+                   want to support parsing form variables in POST requests.
+    * src/jim.c: Updated with latest release from Jim CVS.
+    * src/cgi.c: streamlined cyg_httpd_exec_cgi_tcl(), now uses the 'source'
+                 command of tcl to execute a tcl script.
+    * src/forms.c: Modified cyg_handle_method_POST so that the variables in the
+                    payload are scanned only if the request has a Content-Type 
+                    of 'application/x-www-form-urlencoded'
+    * src/jim-aio.c: Added to package. Now tcl has IO functions to access a
+                      file system.
+    * include/httpd.h: Added a new mode, CYG_HTTPD_MODE_FORM_DATA which is set
+                       when a POST request has a Content-Type of 
+                       'application/x-www-form-urlencoded'
+        
+2006-10-16  Lars Povlsen  <lpovlsen@vitesse.com> and Anthony Tonizzo  <atonizzo@gmail.com>
+
+    * cdl/httpd.cdl: add CYGOPT_NET_ATHTTPD_CLOSE_CHUNKED_CONNECTIONS. Default
+                      is set to CLOSE, so it is backward compatible with 
+                      previous versions of the browser.
+    * src/socket.c: cyg_httpd_process_request() uses a loop to collect at least
+        one full frame (til a header terminator is found), 
+        cyg_httpd_start_chunked() only close if configured to do so.
+    * src/httpd.c: Overhaul of cyg_httpd_send_error to avoid the use of
+        inbuffer as temporary storage (conflicts with pipelined frames),
+        removed the option to send a page after calling a C language
+        handler
+    * include/httpd.h: Added a new mode, CYG_HTTPD_MODE_NO_CACHE
+        
+2006-10-12  Lars Povlsen  <lpovlsen@vitesse.com> and Anthony Tonizzo  <atonizzo@gmail.com>
+
+    * cdl/httpd.cdl: add CYGNUM_ATHTTPD_SERVER_MAX_POST to limit POST'ed data
+    * include/http.h: Added header_end, post_data fields to httpstate,
+                Added "302 Found" for POST handler redirect (CYG_HTTPD_STATUS_MOVED_TEMPORARILY)
+    * src/forms.c: Fixed variable decoding, fixed large POST processing
+    * src/http.c: Fixed some debug ouptuts, cleanup after POST processing, 
+                      overhaul of the pipelined requests code which can now
+                      handle multiple requests per frame.
+    * src/socket.c: Removed assert for socket write failure, Accumulating receiving
+    of requests (Browsers (Firefox) may pass partial headers in separate 
+    fragments). Fixed some diagnostics output.
+
 2006-07-19  Anthony Tonizzo  <atonizzo@gmail.com> and Sergei Gavrikov  <w3sg@softhome.net>
 
-	* cdl/httpd.cdl: 
-	* doc/athttpd.sgml: Corrected some typos and rectified some omissions.
-	* include/jim.h:    Added an inline to Jim_InitExtension()
-	* src/cgi.c:        used malloc() instead of cyg_ldr_malloc();
-	* src/forms.c:      Added a #define to be able tio use the TCL scripting
-	                     without OBJLOADER
-	* src/http.c:       
-	* src/socket.c:     Lots of typos stamped out, and some better comments too.
-	                     
+    * cdl/httpd.cdl: 
+    * doc/athttpd.sgml: Corrected some typos and rectified some omissions.
+    * include/jim.h:    Added an inline to Jim_InitExtension()
+    * src/cgi.c:        used malloc() instead of cyg_ldr_malloc();
+    * src/forms.c:      Added a #define to be able tio use the TCL scripting
+                         without OBJLOADER
+    * src/http.c:       
+    * src/socket.c:     Lots of typos stamped out, and some better comments too.
+                         
 2006-07-19  Anthony Tonizzo  <atonizzo@gmail.com>
 
-	* src/socket.c: Corrected a typo that generated an assertion.
+    * src/socket.c: Corrected a typo that generated an assertion.
     Modified slightly the source of cyg_httpd_write and cyg_httpd_writev
      to make the code more consistent as to when assertions are thrown.
 
 2006-07-19  Sergei Gavrikov  <w3sg@softhome.net>
 
-	* doc/athttpd.sgml: jade doesn't allow underscores in
-	id attributes, so fix.
+    * doc/athttpd.sgml: jade doesn't allow underscores in
+    id attributes, so fix.
 
 2006-07-18  Jonathan Larmour  <jifl@eCosCentric.com>
 
-	* cdl/httpd.cdl: Add -D __ECOS in compiler flags.
-	Change include_dir to cyg/athttpd.
-	Rename all CDL options from *_HTTPD_* to *_ATHTTPD_*.
-	* All files: Rename cyg/httpd include directory to cyg/athttpd.
-	Rename all CDL options from *_HTTPD_* to *_ATHTTPD_*.
-	* doc/athttpd.sgml: Document MD5 algorithm licensing.
-	* src/socket.c: Remove unused SNTP include.
+    * cdl/httpd.cdl: Add -D __ECOS in compiler flags.
+    Change include_dir to cyg/athttpd.
+    Rename all CDL options from *_HTTPD_* to *_ATHTTPD_*.
+    * All files: Rename cyg/httpd include directory to cyg/athttpd.
+    Rename all CDL options from *_HTTPD_* to *_ATHTTPD_*.
+    * doc/athttpd.sgml: Document MD5 algorithm licensing.
+    * src/socket.c: Remove unused SNTP include.
 
 2006-06-13  Anthony Tonizzo  <atonizzo@gmail.com>
 
-	* cdl/httpd.cdl: 
-	* doc/athttpd.sgml: 
-	* doc/mime_types.txt: 
-	* include/auth.h: 
-	* include/cgi.h: 
-	* include/digcalc.h: 
-	* include/forms.h: 
-	* include/global.h: 
-	* include/handler.h: 
-	* include/http.h: 
-	* include/jim.h: 
-	* include/md5.h: 
-	* include/socket.h: 
-	* src/auth.c: 
-	* src/cgi.c: 
-	* src/forms.c: 
-	* src/handler.c: 
-	* src/http.c: 
-	* src/jim.c: 
-	* src/md5c.c: 
-	* src/socket.c: 
-	Created ATHTTPD package.
-	
+    * cdl/httpd.cdl: 
+    * doc/athttpd.sgml: 
+    * doc/mime_types.txt: 
+    * include/auth.h: 
+    * include/cgi.h: 
+    * include/digcalc.h: 
+    * include/forms.h: 
+    * include/global.h: 
+    * include/handler.h: 
+    * include/http.h: 
+    * include/jim.h: 
+    * include/md5.h: 
+    * include/socket.h: 
+    * src/auth.c: 
+    * src/cgi.c: 
+    * src/forms.c: 
+    * src/handler.c: 
+    * src/http.c: 
+    * src/jim.c: 
+    * src/md5c.c: 
+    * src/socket.c: 
+    Created ATHTTPD package.
+    
 //===========================================================================
 //####ECOSGPLCOPYRIGHTBEGIN####
 // -------------------------------------------
 // This file is part of eCos, the Embedded Configurable Operating System.
 // Copyright (C) 2005 eCosCentric Ltd.
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/doc/athttpd.sgml /home/atonizzo/ecos/devo/packages/net/athttpd/current/doc/athttpd.sgml
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/doc/athttpd.sgml	2006-08-10 10:37:48.000000000 -0700
+++ /home/atonizzo/ecos/devo/packages/net/athttpd/current/doc/athttpd.sgml	2006-11-08 10:39:55.000000000 -0800
@@ -83,11 +83,11 @@
 searched in this table and if a match is found, the associated MIME type is
 then sent out in the header. 
 
 The server already provides entries for the following standard file extensions:
 
-'html', 'htm', 'gif', 'jpg', 'css', 'js'
+'html', 'htm', 'gif', 'jpg', 'css', 'js', 'png'
 
 and the user is responsible for adding any further entry. The syntax for 
 adding an entry is the following:</para>
 
 <para><programlisting width=72>
@@ -153,16 +153,10 @@
 <command>CYG_HTTPS_STATE*</command> is a pointer to a structure that
 contains, among others, a buffer (outbuffer) that can be used to send data
 out. The definitions of the structure is in http.h.</para>
 
 <para>
-If the callback function returns the value of 0, the server will try to find 
-the file with the same URL in the file system and send it. Any other value 
-returned causes the sever to stop further processing of this request. It is 
-assumed that in this case the user has send the response back to the client
-inside the body of the callback function.
-
 The following is an example of how to add a callback to a function myForm()
 whenever the URL /myform.cgi is requested:
 </para>
 
 <programlisting width=72>
@@ -177,11 +171,10 @@
 {
    cyg_httpd_start_chunked("html");
    strcpy(p->outbuffer, "eCos Web Server");
    cyg_httpd_write_chunked(p->outbuffer, strlen(p->outbuffer))
    cyg_httpd_end_chunked();
-   return -1; // Do not further search the file system.
 }  
 </programlisting>
 
 <para>This function also shows the correct method of using the chunked frames
 API inside a c language callback and also shows the use of outbuffer to
@@ -457,16 +450,25 @@
 
 <sect1 id="athttpd-formvars">
 <title>Form Variables</title>
 
 <para>The server will automatically try to parse form variables when a form is
-submitted.The variable names to look for during the parsing are held in
+submitted in the following cases:
+
+<itemizedlist>
+  <listitem><para>In a GET request, when the URL is followed by a question
+                  mark sign</para></listitem>
+  <listitem><para>In a POST request, when the the 'Content-Type' header line
+                  is set to 'application/x-www-form-urlencoded'</para></listitem>
+</itemizedlist>
+
+The variable names to look for during the parsing are held in
 an eCos table. In order to take advantage of this feature, the user first
-adds the variable names to the table, also providing a buffer where the parsed
-value will eventually be stored. The values will then be available in
-the buffers during the processing of the request, presumably in the body
-of a c language callback or CGI script.</para>
+adds the variable names to the table, which also requires providing a buffer 
+where the parsed value will eventually be stored. The values will then be
+available in the buffers during the processing of the request, presumably in
+the body of a c language callback or CGI script.</para>
 
 <para>For example, if the user wants two form variables, "foo" and "bar", to 
 be parsed automatically, those variable names must be added to the table 
 with the following macro:</para>
 
@@ -494,22 +496,26 @@
 <para>and after the GET or POST submissions, the list will contain the value 
 for "foo" and "bar" (if they were found in the form data.) It is the 
 responsability of the user to make sure that the buffer is large enough
 to hold all the data parsed (including the string terminator). The parser will
 write only up to the length of the buffer minus one (the last being the
-terminator).</para>
+terminator) and discard any additional data.</para>
 
 <para>The values parsed are likely going to be used in c language callback, or
-in CGI files. The user can access the pointers of individual variable
-for further processing, keeping in mind that the parsing always result
-in a string of characters to be produced, and any conversion from strings to
-integer (i.e. atoi()) must be performed in the callback.</para>
-
-<para>In CGI functions implemented using the objloader the pointers to the
+in CGI files. In a c language callback the user can directly access the pointers
+of individual variables for further processing, keeping in mind that the parsing 
+always result in a string of characters to be produced, and any conversion
+(e.g. from strings to integer) must be performed within the callback. In
+a TCL script the user can just access a variable by its name. For example,
+in the case of the variables 'foo' and 'bar' shown above, it is possible
+to do something like 'write_chunked "You wrote $foo". The data that was sent in
+the body of a POST request is accessible in through a variable called
+'post_data'. In CGI functions
+implemented using the objloader the pointers to the
 variables cannot be accessed directly, since the library will likely not
 know their location in memory. The proper way to access them is by using the
-cyg_httpd_find_form_variable() function from withing the library:</para>
+cyg_httpd_find_form_variable() function from within the library:</para>
 
 <programlisting>
 char* cyg_httpd_find_form_variable(char* name)
 
 name             : name of the form variable to look up
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/include/http.h /home/atonizzo/ecos/devo/packages/net/athttpd/current/include/http.h
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/include/http.h	2006-07-18 09:37:24.000000000 -0700
+++ /home/atonizzo/ecos/devo/packages/net/athttpd/current/include/http.h	2006-11-08 10:15:46.000000000 -0800
@@ -67,11 +67,11 @@
 #endif
 
 
 typedef enum cyg_httpd_req_type
 {
-    CYG_HTTPD_METHOD_GET = 1,
+    CYG_HTTPD_METHOD_GET  = 1,
     CYG_HTTPD_METHOD_HEAD = 2,
     CYG_HTTPD_METHOD_POST = 3
 } cyg_httpd_req_type;
 
 #define CYG_HTTPD_MAXURL                    128
@@ -85,10 +85,11 @@
 
 #define CYG_HTTPD_TIME_STRING_LEN                 32
 
 #define CYG_HTTPD_STATUS_OK                      200
 #define CYG_HTTPD_STATUS_MOVED_PERMANENTLY       301
+#define CYG_HTTPD_STATUS_MOVED_TEMPORARILY       302
 #define CYG_HTTPD_STATUS_NOT_MODIFIED            304
 #define CYG_HTTPD_STATUS_BAD_REQUEST             400
 #define CYG_HTTPD_STATUS_NOT_AUTHORIZED          401
 #define CYG_HTTPD_STATUS_FORBIDDEN               403
 #define CYG_HTTPD_STATUS_NOT_FOUND               404
@@ -96,17 +97,21 @@
 #define CYG_HTTPD_STATUS_SYSTEM_ERROR            500
 #define CYG_HTTPD_STATUS_NOT_IMPLEMENTED         501
 
 #define CYG_HTTPD_MODE_CLOSE_CONN             0x0001
 #define CYG_HTTPD_MODE_TRANSFER_CHUNKED       0x0002
-#define CYG_HTTPD_SEND_HEADER_ONLY            0x0004
+#define CYG_HTTPD_MODE_SEND_HEADER_ONLY       0x0004
+#define CYG_HTTPD_MODE_NO_CACHE               0x0008
+#define CYG_HTTPD_MODE_FORM_DATA              0x0010
 
 // This must be generated at random...
 #define CYG_HTTPD_MD5_AUTH_NAME                "MD5"
 #define CYG_HTTPD_MD5_AUTH_QOP                 "auth"
 #define CYG_HTTPD_MD5_AUTH_OPAQUE              "0000000000000000"
 
+#define TIME_FORMAT_RFC1123                    "%a, %d %b %Y %H:%H:%S GMT"
+
 typedef struct __socket_entry
 {
     cyg_int32 descriptor;
     time_t    timestamp;
 } socket_entry; 
@@ -120,31 +125,37 @@
     fd_set       rfds;
 
     cyg_int32    host[4];
 
     char         url[CYG_HTTPD_MAXURL+1];
-    char         inbuffer[CYG_HTTPD_MAXINBUFFER+1];  
-    cyg_int32    inbuffer_len;
-
-
+    char         inbuffer[CYG_HTTPD_MAXINBUFFER+1];
+    cyg_int32    inbuffer_len, content_len;
+    
     // Packet status.
     //
     //   bit      Description
     // -------------------------------------------------------------------------
-    //    0       When set inserts 'Pragma: no-cache' and 
-    //             'Cache-control: no-cache' in the header. Default for callback
-    //             functions.
-    //    1       When to close the connection: 
-    //             'Connection: close' when 0, 'Connection: keep-alive' when 1.
+    //    0       A 1 means that the connection will be closed after the request
+    //             has been served (i.e. Connection: close" will appear in
+    //             the header. Otherwise the connection will be kept alive
+    //             "Connection: keep-alive"
+    //    1       Set when the transfer will be chunked
+    //    2       Set when the we need to send only the header
+    //    3       Set when the we do not want this document to be cached (i.e.
+    //             "Cache-Control: no-cache" will appear in the header) which
+    //             is meant for c language callbacks and GCI.
+    //    4       Set when the the frame we just received contains form data.
+    //             In this case we call the function that parsed the data into
+    //             user-defined form variables.
     cyg_uint16   mode;
     
     // Ouptut data.
     cyg_uint16   status_code;
     char        *mime_type;
     cyg_int32    payload_len;
     char         outbuffer[CYG_HTTPD_MAXOUTBUFFER+1];
-
+    
     socket_entry sockets[CYGNUM_FILEIO_NFILE];
     cyg_int32    fdmax;
     
     // Socket handle.
     cyg_int32    client_index;
@@ -154,10 +165,19 @@
     
 #ifdef CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL
     Jim_Interp *jim_interp;
 #endif    
 
+    // Pointer to the data immediately following the last byte of the header.
+    // In a POST request, this is where the goods are.
+    char        *header_end;
+
+    // This pointer points to the buffer where we collected all the post
+    //  data (it might come in more than one frame)  and must be visible to
+    //  handlers and cgi scripts.
+    char        *post_data;
+
     // This pointer points to the information about the domain that needs
     //  to be authenticated. It is only used by the function that builds the
     //  header.
     cyg_httpd_auth_table_entry *needs_auth;
 
@@ -182,21 +202,19 @@
 cyg_httpd_mime_table_entry __name CYG_HAL_TABLE_ENTRY(httpd_mime_table) = \
                                                           { __pattern, __arg } 
 
 extern cyg_int32 debug_print;
 
-void cyg_httpd_format_time_string(char*, time_t*);
 void cyg_httpd_set_home_page(cyg_int8*);
 char* cyg_httpd_find_mime_string(char*);
 cyg_int32 cyg_httpd_initialize(void);
 void cyg_httpd_cleanup_filename(char*);
 char* cyg_httpd_parse_GET(char*);
 char* cyg_httpd_parse_POST(char*);
-char* cyg_httpd_parse_HEAD(char*);
 void cyg_httpd_handle_method_GET(void);
 void cyg_httpd_handle_method_HEAD(void);
-cyg_int32 cyg_httpd_process_method(void);
+void cyg_httpd_process_method(void);
 void cyg_httpd_send_file(char*);
 void cyg_httpd_send_error(cyg_int32);
 cyg_int32 cyg_httpd_format_header(void);
 
 #endif
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/include/jim.h /home/atonizzo/ecos/devo/packages/net/athttpd/current/include/jim.h
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/include/jim.h	2006-08-10 10:37:48.000000000 -0700
+++ /home/atonizzo/ecos/devo/packages/net/athttpd/current/include/jim.h	2006-11-06 12:37:24.000000000 -0800
@@ -1,10 +1,10 @@
 /* Jim - A small embeddable Tcl interpreter
  * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
  * Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
  *
- * $Id: jim.h,v 1.73 2005/04/18 08:31:26 antirez Exp $
+ * $Id: jim.h,v 1.76 2006/11/06 20:29:15 antirez Exp $
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
@@ -27,10 +27,11 @@
 extern "C" {
 #endif
 
 #include <time.h>
 #include <limits.h>
+#include <stdio.h>  /* for the FILE typedef definition */
 #include <stdlib.h> /* In order to export the Jim_Free() macro */
 
 /* -----------------------------------------------------------------------------
 * Some /very/ old compiler maybe do not know how to
 * handle 'const'. They even do not know, how to ignore
@@ -115,10 +116,11 @@
 #define JIM_ERR 1
 #define JIM_RETURN 2
 #define JIM_BREAK 3
 #define JIM_CONTINUE 4
 #define JIM_EVAL 5
+#define JIM_EXIT 6
 #define JIM_MAX_NESTING_DEPTH 10000 /* default max nesting depth */
 
 /* Some function get an integer argument with flags to change
  * the behaviour. */
 #define JIM_NONE 0    /* no flags set */
@@ -137,10 +139,17 @@
 #define JIM_NOCASE      1   /* no case */
 
 /* Filesystem related */
 #define JIM_PATH_LEN 1024
 
+/* Newline, some embedded system may need -DJIM_CRLF */
+#ifdef JIM_CRLF
+#define JIM_NL "\r\n"
+#else
+#define JIM_NL "\n"
+#endif
+
 /* -----------------------------------------------------------------------------
  * Stack
  * ---------------------------------------------------------------------------*/
 
 typedef struct Jim_Stack {
@@ -440,10 +449,11 @@
     int errorLine; /* Error line where an error occurred. */
     const char *errorFileName; /* Error file where an error occurred. */
     int numLevels; /* Number of current nested calls. */
     int maxNestingDepth; /* Used for infinite loop detection. */
     int returnCode; /* Completion code to return on JIM_RETURN. */
+    int exitCode; /* Code to return to the OS on JIM_EXIT. */
     Jim_CallFrame *framePtr; /* Pointer to the current call frame */
     Jim_CallFrame *topFramePtr; /* toplevel/global frame pointer. */
     struct Jim_HashTable commands; /* Commands hash table */
     unsigned jim_wide procEpoch; /* Incremented every time the result
                 of procedures names lookup caching
@@ -479,10 +489,13 @@
     int (*getApiFuncPtr)(struct Jim_Interp *, const char *, void *);
     struct Jim_CallFrame *freeFramesList; /* list of CallFrame structures. */
     struct Jim_HashTable assocData; /* per-interp storage for use by packages */
     Jim_PrngState *prngState; /* per interpreter Random Number Gen. state. */
     struct Jim_HashTable packages; /* Provided packages hash table */
+    FILE *stdin; /* input file pointer, 'stdin' by default */
+    FILE *stdout; /* output file pointer, 'stdout' by default */
+    FILE *stderr; /* errors file pointer, 'stderr' by default */
 } Jim_Interp;
 
 /* Currently provided as macro that performs the increment.
  * At some point may be a real function doing more work.
  * The proc epoch is used in order to know when a command lookup
@@ -636,10 +649,14 @@
 JIM_STATIC int JIM_API(Jim_GetFinalizer) (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr);
 
 /* interpreter */
 JIM_STATIC Jim_Interp * JIM_API(Jim_CreateInterp) (void);
 JIM_STATIC void JIM_API(Jim_FreeInterp) (Jim_Interp *i);
+JIM_STATIC int JIM_API(Jim_GetExitCode) (Jim_Interp *interp);
+JIM_STATIC FILE * JIM_API(Jim_SetStdin) (Jim_Interp *interp, FILE *fp);
+JIM_STATIC FILE * JIM_API(Jim_SetStdout) (Jim_Interp *interp, FILE *fp);
+JIM_STATIC FILE * JIM_API(Jim_SetStderr) (Jim_Interp *interp, FILE *fp);
 
 /* commands */
 JIM_STATIC void JIM_API(Jim_RegisterCoreCommands) (Jim_Interp *interp);
 JIM_STATIC int JIM_API(Jim_CreateCommand) (Jim_Interp *interp, 
         const char *cmdName, Jim_CmdProc cmdProc, void *privData,
@@ -784,11 +801,11 @@
 
 /* interactive mode */
 JIM_STATIC int JIM_API(Jim_InteractivePrompt) (Jim_Interp *interp);
 
 /* Misc */
-JIM_STATIC void JIM_API(Jim_Panic) (const char *fmt, ...);
+JIM_STATIC void JIM_API(Jim_Panic) (Jim_Interp *interp, const char *fmt, ...);
 
 #undef JIM_STATIC
 #undef JIM_API
 
 #ifndef __JIM_CORE__
@@ -796,11 +813,11 @@
 #define JIM_GET_API(name) \
     Jim_GetApi(interp, "Jim_" #name, ((void *)&Jim_ ## name))
 
 #if defined JIM_EXTENSION || defined JIM_EMBEDDED
 /* This must be included "inline" inside the extension */
-static __inline__ void Jim_InitExtension(Jim_Interp *interp)
+static void Jim_InitExtension(Jim_Interp *interp)
 {
   Jim_GetApi = interp->getApiFuncPtr;
 
   JIM_GET_API(Alloc);
   JIM_GET_API(Free);
@@ -843,10 +860,14 @@
   JIM_GET_API(GetReference);
   JIM_GET_API(SetFinalizer);
   JIM_GET_API(GetFinalizer);
   JIM_GET_API(CreateInterp);
   JIM_GET_API(FreeInterp);
+  JIM_GET_API(GetExitCode);
+  JIM_GET_API(SetStdin);
+  JIM_GET_API(SetStdout);
+  JIM_GET_API(SetStderr);
   JIM_GET_API(CreateCommand);
   JIM_GET_API(CreateProcedure);
   JIM_GET_API(DeleteCommand);
   JIM_GET_API(RenameCommand);
   JIM_GET_API(GetCommand);
@@ -914,18 +935,18 @@
 }
 #endif /* defined JIM_EXTENSION || defined JIM_EMBEDDED */
 
 #undef JIM_GET_API
 
-//#ifdef JIM_EMBEDDED
-//Jim_Interp *ExportedJimCreateInterp(void);
-//static void Jim_InitEmbedded(void) {
-//    Jim_Interp *i = ExportedJimCreateInterp();
-//    Jim_InitExtension(i);
-//    Jim_FreeInterp(i);
-//}
-//#endif /* JIM_EMBEDDED */
+#ifdef JIM_EMBEDDED
+Jim_Interp *ExportedJimCreateInterp(void);
+static void Jim_InitEmbedded(void) {
+    Jim_Interp *i = ExportedJimCreateInterp();
+    Jim_InitExtension(i);
+    Jim_FreeInterp(i);
+}
+#endif /* JIM_EMBEDDED */
 #endif /* __JIM_CORE__ */
 
 #ifdef __cplusplus
 }
 #endif
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/cgi.c /home/atonizzo/ecos/devo/packages/net/athttpd/current/src/cgi.c
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/cgi.c	2006-08-10 10:37:48.000000000 -0700
+++ /home/atonizzo/ecos/devo/packages/net/athttpd/current/src/cgi.c	2006-11-08 10:38:40.000000000 -0800
@@ -60,10 +60,11 @@
 #include <network.h>
 
 #include <string.h>
 #include <stdio.h>                     // sprintf().
 
+#define JIM_EMBEDDED
 #include <cyg/athttpd/http.h>
 #include <cyg/athttpd/socket.h>
 #include <cyg/athttpd/handler.h>
 #include <cyg/athttpd/forms.h>
 
@@ -158,99 +159,75 @@
 
 // =============================================================================
 // tcl CGI Support
 // =============================================================================
 #ifdef CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL
-cyg_int32 cyg_httpd_exec_cgi_tcl(char *file_name)
+int Jim_AioInit(Jim_Interp *);
+cyg_int32
+cyg_httpd_exec_cgi_tcl(char *file_name)
 {
-    struct stat sp;
-    // Check if the file is in the file system.
-    cyg_int32 rc = stat(file_name, &sp);
-    if (rc < 0)
-    {
-        cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_FOUND);
-        return 0;
-    }    
-    
-    FILE *fp = fopen(file_name, "rb");
-    CYG_ASSERT(fp != NULL, "Cannot open file()");
-    if (fp == NULL)
-    {
-        cyg_httpd_send_error(CYG_HTTPD_STATUS_SYSTEM_ERROR);
-        return 0;
-    }
-    
-    char* tcl_buffer = (char*)malloc(sp.st_size);
-    CYG_ASSERT(tcl_buffer != NULL, "Cannot malloc() for tcl CGI");
-    if (tcl_buffer == NULL)
-    {
-        cyg_httpd_send_error(CYG_HTTPD_STATUS_SYSTEM_ERROR);
-        return 0;
-    }
-    
-    rc = fread(tcl_buffer, sizeof(char), sp.st_size, fp);
-    if (rc != sp.st_size)
-    {
-        cyg_httpd_send_error(CYG_HTTPD_STATUS_SYSTEM_ERROR);
-        return 0;
-    }
+    char tcl_cmd[CYG_HTTPD_MAXPATH];
+    sprintf(tcl_cmd, "source %s", file_name);
 
-    // Make sure that tcl sees the internal variables.
+    // Make sure that tcl sees the internal variables including the post_data.
     cyg_httpd_fvars_table_entry *entry = cyg_httpd_fvars_table;
     while (entry != cyg_httpd_fvars_table_end)
     {
         if (strlen(entry->buf) != 0)
             Jim_SetVariableStrWithStr(httpstate.jim_interp, 
                                       entry->name, 
                                       entry->buf);
         entry++;
     }
+
+    if (httpstate.post_data != NULL)
+        Jim_SetVariableStrWithStr(httpstate.jim_interp, 
+                                  "post_data", 
+                                  httpstate.post_data);
      
-    Jim_Eval(httpstate.jim_interp, tcl_buffer);
-    free(tcl_buffer);
+    Jim_Eval(httpstate.jim_interp, tcl_cmd);
     return 0;
 }
 
-Jim_Interp *ExportedJimCreateInterp(void);
-static void Jim_InitEmbedded(void) {
-    Jim_Interp *i = ExportedJimCreateInterp();
-    Jim_InitExtension(i);
-    Jim_FreeInterp(i);
-}    
-
-int cyg_httpd_Jim_Command_startchunked(Jim_Interp *interp, 
-                                       int argc,
-                                       Jim_Obj *const *argv)
+int
+cyg_httpd_Jim_Command_startchunked(Jim_Interp *interp, 
+                                   int argc,
+                                   Jim_Obj *const *argv)
 {
-    Jim_Obj *objPtr = (Jim_Obj *)argv[1];
-    cyg_httpd_start_chunked(objPtr->bytes);
+    char *buf = (char*)Jim_GetString(argv[1], NULL);
+    cyg_httpd_start_chunked(buf);
     return JIM_OK;
 }
   
-int cyg_httpd_Jim_Command_writechunked(Jim_Interp *interp, 
-                                       int argc,
-                                       Jim_Obj *const *argv)
-{
-    Jim_Obj *objPtr = (Jim_Obj *)argv[1];
-    cyg_httpd_write_chunked(objPtr->bytes, objPtr->length);
+int
+cyg_httpd_Jim_Command_writechunked(Jim_Interp *interp, 
+                                   int argc,
+                                   Jim_Obj *const *argv)
+{
+    int len;
+    char *buf = (char*)Jim_GetString(argv[1], &len);
+    cyg_httpd_write_chunked(buf, len);
     return JIM_OK;
 }
   
-int cyg_httpd_Jim_Command_endchunked(Jim_Interp *interp, 
-                                     int argc,
-                                     Jim_Obj *const *argv)
+int
+cyg_httpd_Jim_Command_endchunked(Jim_Interp *interp, 
+                                 int argc,
+                                 Jim_Obj *const *argv)
 {
     cyg_httpd_end_chunked();
     return JIM_OK;
 }
   
-void cyg_httpd_init_tcl_interpreter(void)
+void
+cyg_httpd_init_tcl_interpreter(void)
 {
     // Start the TCL interpreter.
     Jim_InitEmbedded();
     httpstate.jim_interp = Jim_CreateInterp();
     Jim_RegisterCoreCommands(httpstate.jim_interp);
+    Jim_AioInit(httpstate.jim_interp);
     
     // Add a new cyg_httpd_write_chunked command for tcl.
     Jim_CreateCommand(httpstate.jim_interp,
                       "start_chunked",
                       cyg_httpd_Jim_Command_startchunked,
@@ -270,12 +247,12 @@
 #endif    
 
 // =============================================================================
 // CGI Support
 // =============================================================================
-#if (CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL == 1 ) || \
-                                 (CYGOPT_NET_ATHTTPD_USE_CGIBIN_OBJLOADER == 1 )
+#if defined(CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL ) || \
+    defined(CYGOPT_NET_ATHTTPD_USE_CGIBIN_OBJLOADER)
 cyg_int32 
 cyg_httpd_exec_cgi(void)
 {
     char  file_name[CYG_HTTPD_MAXPATH];
     
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/forms.c /home/atonizzo/ecos/devo/packages/net/athttpd/current/src/forms.c
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/forms.c	2006-08-10 10:37:48.000000000 -0700
+++ /home/atonizzo/ecos/devo/packages/net/athttpd/current/src/forms.c	2006-11-08 08:28:10.000000000 -0800
@@ -42,10 +42,11 @@
  * =================================================================
  * #####DESCRIPTIONBEGIN####
  * 
  *  Author(s):    Anthony Tonizzo (atonizzo@gmail.com)
  *  Contributors: Sergei Gavrikov (w3sg@SoftHome.net)
+ *                Lars Povlsen    (lpovlsen@vitesse.com)
  *  Date:         2006-06-12
  *  Purpose:      
  *  Description:  
  *               
  * ####DESCRIPTIONEND####
@@ -134,13 +135,17 @@
     {
         entry->buf[0] = '\0';
         entry++;
     }
 
-    while(*p != ' ')
+    if (!p)    /* No form data? just return after clearing variables */
+        return NULL;
+
+    while (*p && *p != ' ')
     {
-        p2 = strchr(p, '=');
+        if (!(p2 = strchr(p, '=')))
+            return NULL;        /* Malformed post? */
         var_length = (cyg_int32)p2 - (cyg_int32)p;
         entry = cyg_httpd_fvars_table;
         while (entry != cyg_httpd_fvars_table_end)
         {
             if (!strncmp((const char*)p, entry->name, var_length ))
@@ -149,27 +154,24 @@
         }
                 
         if (entry == cyg_httpd_fvars_table_end)
         {
             // No such variable. Run through the data.
-            while ((*p != '&') && (*p != ' '))
+            while ((*p != '&') && (*p && *p != ' '))
+                p++;
+            if(*p == '&')
                 p++;
             continue;
         }
             
-        p = p2;
-        if (*p == '=')
-        {
-            ++p;
-            // Found the variable, store the name.
-            p = cyg_httpd_store_form_variable(p, entry);
+        // Found the variable, store the name.
+        p = cyg_httpd_store_form_variable(++p2, entry);
 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
-            diag_printf("Stored form variable: %s Value: %s\n",
-                        entry->name,
-                        entry->buf);
+        diag_printf("Stored form variable: %s Value: %s\n",
+                    entry->name,
+                    entry->buf);
 #endif    
-        }
         if (*p == '&')
             p++;
     }
     return p;
 }
@@ -190,32 +192,75 @@
 }
 
 void
 cyg_httpd_handle_method_POST(void)
 {
-    // TODO: Need to support the case in which a POST request will send
-    //  LOTS of data that does not fit into a single inbuffer. For example,
-    //  file transfer over HTTP.
-    cyg_int32 len = read(httpstate.sockets[httpstate.client_index].descriptor,
-                          httpstate.inbuffer, 
-                          CYG_HTTPD_MAXINBUFFER);
-    CYG_ASSERT(len > 0, "Cannot read POST data");
-    if (len == 0)
+    CYG_ASSERT(httpstate.post_data == NULL, "Leftover content data");
+    CYG_ASSERT(httpstate.header_end != NULL, "Cannot see POST data");
+    if (httpstate.content_len == 0 || 
+        httpstate.content_len > CYGNUM_ATHTTPD_SERVER_MAX_POST) {
+        cyg_httpd_send_error(CYG_HTTPD_STATUS_BAD_REQUEST);
+        return;
+    }
+    
+    // The content data is only valid during a POST.
+    httpstate.post_data = (char*)malloc(httpstate.content_len + 1);
+    CYG_ASSERT(httpstate.post_data != NULL, "Cannot malloc POST buffer");
+    if (httpstate.post_data == NULL)
+    {
+        cyg_httpd_send_error(CYG_HTTPD_STATUS_SYSTEM_ERROR);
         return;
+    }
+
+    /* Grab partial/all content from data read with headers */
+    /*
+     * TODO: This does NOT (yet) support multipart/form-data!
+     */
+    int header_len = (int)httpstate.header_end - (int)httpstate.inbuffer;
+    int post_data_len = httpstate.inbuffer_len - header_len;
+    
+    // Some POST data might have come along with the header frame, and the
+    //  rest is coming in on following frames. Copy the data that already
+    //  arriced into the post buffer.
+    memcpy(httpstate.post_data, httpstate.header_end, post_data_len);
+    // Do we need additional data?
+    if (post_data_len < httpstate.content_len) 
+    {   
+        while (post_data_len < httpstate.content_len)
+        {
+            cyg_int32 len = read(httpstate.sockets[httpstate.client_index].
+                                                                   descriptor,
+                                 httpstate.post_data + post_data_len,
+                                 httpstate.content_len - post_data_len);
+            if (len < 0)
+            {
+                /* This releases POST data area*/
+                free(httpstate.post_data);
+                httpstate.post_data = NULL;
+                return;
+            }    
+            post_data_len += len;
+        }    
+    }
+    CYG_ASSERT(post_data_len == httpstate.content_len, "Partial read");
+
+    /* httpstate.content remains available in handler */
+    httpstate.post_data[httpstate.content_len] = '\0';
     
-    char  *cp = httpstate.inbuffer;
-    while ((*cp == '\r') || (*cp == '\n'))
-        cp++;
-    cp[httpstate.inbuffer_len] = ' ';
-    cyg_httpd_store_form_data(cp);
+    // This assumes that the data that arrived in the POST body is of
+    //  multipart/form-data MIME type. We need to change this, if we are to
+    //  support things such as HTTP file transfer.
+    if (httpstate.mode & CYG_HTTPD_MODE_FORM_DATA)
+        cyg_httpd_store_form_data(httpstate.post_data);
 
     handler h = cyg_httpd_find_handler();
     if (h != 0)
     {
-        // A handler was found. We'll call the function associated to it. If 
-        //  the return value is 0 we'll also call cyg_httpd_send_file().
+        // A handler was found. We'll call the function associated to it.
         h(&httpstate);
+        free(httpstate.post_data);
+        httpstate.post_data = NULL;
         return;
     }
 
 #if defined(CYGOPT_NET_ATHTTPD_USE_CGIBIN_OBJLOADER) || \
                            defined(CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL)
@@ -230,14 +275,19 @@
                               strlen(CYGDAT_NET_ATHTTPD_SERVEROPT_CGIDIR)))
     {                              
         // Here we'll look for extension to the file. We'll call the cgi
         //  handler only if the extension is '.o'.
         cyg_httpd_exec_cgi();
+        free(httpstate.post_data);
+        httpstate.post_data = NULL;
         return;
     }
 #endif    
 
     // No handler of any kind for a post request. Must send 404.
     cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_FOUND);
+    free(httpstate.post_data);
+    httpstate.post_data = NULL;
+    return;
 }
 
 
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/handler.c /home/atonizzo/ecos/devo/packages/net/athttpd/current/src/handler.c
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/handler.c	2006-07-18 09:37:24.000000000 -0700
+++ /home/atonizzo/ecos/devo/packages/net/athttpd/current/src/handler.c	2006-10-20 12:29:04.000000000 -0700
@@ -365,11 +365,11 @@
     // As always, the header is always sent out.
     send(httpstate.sockets[httpstate.client_index].descriptor, 
          httpstate.outbuffer, 
          header_length,
          0);
-    if (httpstate.mode & CYG_HTTPD_SEND_HEADER_ONLY) 
+    if (httpstate.mode & CYG_HTTPD_MODE_SEND_HEADER_ONLY) 
         return;
     cyg_httpd_write(entry->f_ptr, entry->f_size);
 }
 
 // =============================================================================
@@ -385,11 +385,11 @@
 cyg_httpd_find_handler(void)
 {
     cyg_httpd_handler_table_entry *entry = cyg_httpd_handler_table;
     while (entry != cyg_httpd_handler_table_end)
     {
-        if (!strcmp((const char*)httpstate.url, entry->path))
+        if (strcmp((const char*)httpstate.url, entry->path) == 0)
             return entry->h;
         entry++;
     }
             
     return (handler)NULL;
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/http.c /home/atonizzo/ecos/devo/packages/net/athttpd/current/src/http.c
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/http.c	2006-08-10 10:37:48.000000000 -0700
+++ /home/atonizzo/ecos/devo/packages/net/athttpd/current/src/http.c	2006-11-09 10:23:33.000000000 -0800
@@ -42,10 +42,11 @@
  * =================================================================
  * #####DESCRIPTIONBEGIN####
  * 
  *  Author(s):    Anthony Tonizzo (atonizzo@gmail.com)
  *  Contributors: Sergei Gavrikov (w3sg@SoftHome.net)
+ *                Lars Povlsen    (lpovlsen@vitesse.com)
  *  Date:         2006-06-12
  *  Purpose:      
  *  Description:  
  *               
  * ####DESCRIPTIONEND####
@@ -75,16 +76,16 @@
 #include <cyg/athttpd/handler.h>
 #include <cyg/athttpd/forms.h>
 
 cyg_int32 debug_print = 0;
 
-const char *day_of_week[7] = { "Sun", "Mon", "Tue", "Wed", 
-                                 "Thu", "Fri", "Sat" };
-const char *month_of_year[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", 
-                                    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
-const char *home_pages[] = { "index.html",   "index.htm",
-                             "default.html", "home.html" };
+const char *day_of_week[7] = {"Sun", "Mon", "Tue", "Wed", 
+                                 "Thu", "Fri", "Sat"};
+const char *month_of_year[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", 
+                                    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+const char *home_pages[] = {"index.html",   "index.htm",
+                            "default.html", "home.html"};
 CYG_HAL_TABLE_BEGIN(cyg_httpd_mime_table, httpd_mime_table);
 CYG_HAL_TABLE_END(cyg_httpd_mime_table_end, httpd_mime_table);
 
 __externC cyg_httpd_mime_table_entry cyg_httpd_mime_table[];
 __externC cyg_httpd_mime_table_entry cyg_httpd_mime_table_end[];
@@ -95,36 +96,21 @@
                                        "text/html; charset=iso-8859-1");
 CYG_HTTPD_MIME_TABLE_ENTRY(hal_html_entry, "html", 
                                        "text/html; charset=iso-8859-1");
 CYG_HTTPD_MIME_TABLE_ENTRY(hal_gif_entry, "gif", "image/gif");
 CYG_HTTPD_MIME_TABLE_ENTRY(hal_jpg_entry, "jpg", "image/jpg");
+CYG_HTTPD_MIME_TABLE_ENTRY(hal_png_entry, "png", "image/png");
 CYG_HTTPD_MIME_TABLE_ENTRY(hal_css_entry, "css", "text/css");
 CYG_HTTPD_MIME_TABLE_ENTRY(hal_js_entry, "js", "application/x-javascript");
 
-void
-cyg_httpd_format_time_string(char *ptr, time_t *t)
-{
-    struct tm *gmt_time = gmtime(t);
-    if (gmt_time != NULL)
-        sprintf(ptr, 
-                "%s, %02d %s %d %02d:%02d:%02d GMT",
-                day_of_week[gmt_time->tm_wday],
-                gmt_time->tm_mday,
-                month_of_year[gmt_time->tm_mon],
-                1900 + gmt_time->tm_year,
-                gmt_time->tm_hour,
-                gmt_time->tm_min,
-                gmt_time->tm_sec);
-    else     
-        // If anything fails, then we'll just create a bogus date.
-        sprintf(ptr, "Thu, 01 Jan 1970 00:00:01 GMT");
-}
-
 void 
 cyg_httpd_send_error(cyg_int32 err_type)
 {
     httpstate.status_code = err_type;
+    
+    // Errors pages close the socket and are never cached.
+    httpstate.mode |= CYG_HTTPD_MODE_NO_CACHE;
 
 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
     diag_printf("Sending error: %d\n", err_type);
 #endif    
 
@@ -178,97 +164,111 @@
 
         fclose(fp);
         return;
     }
 #endif    
+
+    // Because the size of the frame is not known upfront (every error message
+    //  is different and thus has different length) we use chunked frames to
+    //  send the message out.
+#if defined(CYGOPT_NET_ATHTTPD_CLOSE_CHUNKED_CONNECTIONS)
+    httpstate.mode |= CYG_HTTPD_MODE_CLOSE_CONN;
+#endif
     
-    // If no file has been defined, send a simple notification. We use inbuffer
-    //  for temporary storage of the error messages, so that we know the length
-    //  of the frame prior to building the header, thus avoiding the use of 
-    //  chunked frames.
-    strcpy(httpstate.inbuffer, 
+    httpstate.mode |= CYG_HTTPD_MODE_TRANSFER_CHUNKED;
+    httpstate.status_code = err_type;
+    httpstate.last_modified = -1;
+    httpstate.mime_type = "text/html";
+    cyg_int32 header_length = cyg_httpd_format_header();
+    cyg_httpd_write(httpstate.outbuffer, header_length);
+    
+    // If no file has been defined, send a simple notification. We must use
+    //  chunked frames, because we do not know upfron the length of the
+    //  packet we have to send.
+    strcpy(httpstate.outbuffer,
            "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n");
     switch (err_type)
     {
     case CYG_HTTPD_STATUS_MOVED_PERMANENTLY:
-        strcat(httpstate.inbuffer, 
+        strcat(httpstate.outbuffer,
                "<html><head><title>301 Moved Permanently</title></head>\r\n"
                "<body><h1>Moved Permanently</h1>\r\n"
-               "<p>The documenthas moved <a href=\"" );
-        strcat(httpstate.inbuffer, httpstate.url);
-        strcat(httpstate.inbuffer, "\">here</a>.\r\n");
+               "<p>The document has moved <a href=\"");
+        strcat(httpstate.outbuffer, httpstate.url);
+        strcat(httpstate.outbuffer, "\">here</a>.\r\n");
+        break;
+    case CYG_HTTPD_STATUS_MOVED_TEMPORARILY:
+        strcat(httpstate.outbuffer, 
+               "<html><head><title>302 Found</title></head>\r\n"
+               "<body><h1>Redirect</h1>\r\n"
+               "<p>Please continue <a href=\"");
+        strcat(httpstate.outbuffer, httpstate.url);
+        strcat(httpstate.outbuffer, "\">here</a>.\r\n");
         break;
     case CYG_HTTPD_STATUS_NOT_AUTHORIZED:
-        strcat(httpstate.inbuffer, 
+        strcat(httpstate.outbuffer, 
                "<html><head><title>401 Not Authorized</title></head>\r\n");
-        strcat(httpstate.inbuffer, 
-               "<p>Authorization required to access this URL.</p>");
+        strcat(httpstate.outbuffer, 
+               "<body><p>Authorization required to access this URL.</p>\r\n");
         break;    
     case CYG_HTTPD_STATUS_NOT_MODIFIED:
-        httpstate.mode |= CYG_HTTPD_SEND_HEADER_ONLY;
-        break;    
+        cyg_httpd_end_chunked();
+        return;
     case CYG_HTTPD_STATUS_NOT_FOUND:
-        strcat(httpstate.inbuffer, 
+        strcat(httpstate.outbuffer, 
                "<html><head><title>404 Not Found</title></head>\r\n");
-        sprintf(httpstate.inbuffer + strlen(httpstate.inbuffer),
-                 "<p>The requested URL: %s was not found on"
-                 " this server</p>",
-                 httpstate.url);
+        sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+                "<p>The requested URL: %s was not found on this server</p>\r\n",
+                httpstate.url);
         break;
     case CYG_HTTPD_STATUS_SYSTEM_ERROR:
-        strcat(httpstate.inbuffer, 
+        strcat(httpstate.outbuffer, 
                "<html><head><title>500 Server Error</title></head>\r\n");
-        strcat(httpstate.inbuffer, 
+        strcat(httpstate.outbuffer, 
                "<p>The server encountered an unexpected condition that "
-               "prevented it from fulfilling the request by the client</p>");
+               "prevented it from fulfilling the request"
+               " by the client</p>\r\n");
         break;
     case CYG_HTTPD_STATUS_NOT_IMPLEMENTED:
-        strcat(httpstate.inbuffer, 
+        strcat(httpstate.outbuffer, 
                "<html><head><title>501 Not Implemented</title></head>\r\n");
-        strcat(httpstate.inbuffer, 
-               "<p>The method requested is not implemented</p>");
+        strcat(httpstate.outbuffer, 
+               "<p>The method requested is not implemented</p>\r\n");
         break;
     default:
-        strcat(httpstate.inbuffer, 
+        strcat(httpstate.outbuffer, 
                "<html><head><title>400 Bad Request</title></head>\r\n");
-        strcat(httpstate.inbuffer, 
-               "<p>Bad request</p>");
+        strcat(httpstate.outbuffer, 
+               "<p>Bad request</p>\r\n");
         break;
     }
     
-    sprintf(httpstate.inbuffer + strlen(httpstate.inbuffer),
-            "</p><hr><address>%s at %d.%d.%d.%d Port %d</address>\r\n"
-            "</body></html>\r\n",
+    sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+            "<hr>%s at %d.%d.%d.%d Port %d\r\n</body></html>\r\n",
             CYGDAT_NET_ATHTTPD_SERVEROPT_SERVERID,
             httpstate.host[0],
             httpstate.host[1],
             httpstate.host[2],
             httpstate.host[3],
             CYGNUM_NET_ATHTTPD_SERVEROPT_PORT);
     
-    httpstate.last_modified = -1;
-    httpstate.mime_type = cyg_httpd_find_mime_string("html");
-    httpstate.mode |= CYG_HTTPD_MODE_CLOSE_CONN;
-    httpstate.payload_len  = strlen(httpstate.inbuffer);
-    cyg_int32 header_length = cyg_httpd_format_header();
-    cyg_httpd_write(httpstate.outbuffer, header_length);
-    if (httpstate.mode & CYG_HTTPD_SEND_HEADER_ONLY)
-        return;
-    cyg_httpd_write(httpstate.inbuffer, strlen(httpstate.inbuffer));
+    cyg_httpd_write_chunked(httpstate.outbuffer, strlen(httpstate.outbuffer));
+    cyg_httpd_end_chunked();
 }
 
+// Return a time_t that is always UTC (aka GMT).
 time_t
 cyg_httpd_parse_date(char *time)
 {
     int    i;
     char   month[4];
     struct tm tm_mod;
 
     // We are going to get rid of the day of the week. This is always the first
     //  part of the string, separated by a blank.
-    time = strchr( time, ' ' );
-    if ( time == NULL )
+    time = strchr( time, ' ');
+    if ( time == NULL)
         return 0;
     time++;
 
     /// RFC1123. The date is in the format: Sun, 06 Nov 1994 08:49:37 GMT.
     cyg_int32 rc = sscanf(time,
@@ -291,11 +291,12 @@
                     &tm_mod.tm_min,
                     &tm_mod.tm_sec);
         if (rc != 6)
         {
             // asctime() in the stdlibc library.
-            // The date is in the format: Sun Nov 6 08:49:37 1994.
+            // The date is in the format: Sun Nov 6 08:49:37 1994
+            //  and needs to be converted to GMT.
             rc = sscanf(time,"%3s %2d %2d:%2d:%2d %4d",
                         month,
                         &tm_mod.tm_mday,
                         &tm_mod.tm_hour,
                         &tm_mod.tm_min,
@@ -326,24 +327,22 @@
 {
     cyg_httpd_mime_table_entry *entry = cyg_httpd_mime_table;
 
     while (entry != cyg_httpd_mime_table_end)
     {
-        if (!strcmp((const char*)ext, entry->extension ))
+        if (!strcmp((const char*)ext, entry->extension))
             return entry->mime_string;
         entry++;
     }
             
     return (char*)0;
 }
 
 void
 cyg_httpd_cleanup_filename(char *filename)
 {
-    char *src  = filename;
-
-    src = strstr(filename, "//");
+    char *src = strstr(filename, "//");
     while (src != 0)
     {
         strcpy(src + 1, src + 2);
         src = strstr(filename, "//");
     }
@@ -365,15 +364,16 @@
             comp2 = ++comp1;
 
         strcpy(comp2, src + 4);
         src = strstr(filename, "/../");
     }
- }
+}
 
 cyg_int32
 cyg_httpd_initialize(void)
 {
+    httpstate.post_data = NULL;
     httpstate.needs_auth = (cyg_httpd_auth_table_entry *)0;
     return 0;
 }
 
 void
@@ -409,13 +409,10 @@
     cyg_int32  err;
     FILE      *fp;
     struct stat sp;
     char       file_name[CYG_HTTPD_MAXPATH];
 
-#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
-    diag_printf("Sending file: %s\n", name);
-#endif    
 #ifdef CYGOPT_NET_ATHTTPD_USE_AUTH
     // Let's check that the requested URL is not inside some directory that 
     //  needs authentication.
     cyg_httpd_auth_table_entry* auth = cyg_httpd_is_authenticated(name);
     if (auth != 0)
@@ -497,14 +494,14 @@
         }
         stat(file_name, &sp);
     }
     
     httpstate.last_modified = sp.st_mtime;
-    
+
     // Let's see if we luck out and can send a 304.
     if ((httpstate.modified_since != -1) && 
-                   (httpstate.modified_since >= sp.st_mtime))
+                   (httpstate.modified_since >= httpstate.last_modified))
     {                   
         cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_MODIFIED);
         return;
     }    
     else    
@@ -518,22 +515,29 @@
         httpstate.mime_type = 0;
     else    
         httpstate.mime_type = cyg_httpd_find_mime_string(++extension);
 
     httpstate.payload_len  = sp.st_size;
+    httpstate.mode &= ~CYG_HTTPD_MODE_NO_CACHE;
     cyg_int32 payload_size = cyg_httpd_format_header();
-    if ((httpstate.mode & CYG_HTTPD_SEND_HEADER_ONLY) != 0)
+    if ((httpstate.mode & CYG_HTTPD_MODE_SEND_HEADER_ONLY) != 0)
     {                 
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
+    diag_printf("Sending header only for URL: %s\n", file_name);
+#endif    
         send(httpstate.sockets[httpstate.client_index].descriptor, 
              httpstate.outbuffer, 
              payload_size,
              0);
         return;
     }
 
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
+    diag_printf("Sending file: %s\n", file_name);
+#endif    
     fp = fopen(file_name, "r");
-    if(fp == NULL)
+    if (fp == NULL)
     {
         // We should really read errno and send messages accordingly...
         cyg_httpd_send_error(CYG_HTTPD_STATUS_SYSTEM_ERROR);
         return;
     }
@@ -543,47 +547,27 @@
                             1, 
                             CYG_HTTPD_MAXOUTBUFFER - payload_size,
                             fp);
     cyg_httpd_write(httpstate.outbuffer, payload_size + bread);
 
-    cyg_iovec bufs[2] = { { httpstate.outbuffer, 0 }, 
-                          { httpstate.inbuffer,  0 } };
     ssize_t bytes_written = 0;
     sp.st_size -= bread;
     while (bytes_written < sp.st_size)
-        if (sp.st_size - bytes_written <= CYG_HTTPD_MAXOUTBUFFER)
-        {
-            bufs[0].iov_len = fread(httpstate.outbuffer,
-                                    1, 
-                                    CYG_HTTPD_MAXOUTBUFFER, 
-                                    fp);
-            bytes_written += cyg_httpd_write(httpstate.outbuffer, 
-                                             bufs[0].iov_len);
-        }        
-        else    
-        {
-            bufs[0].iov_len = fread(httpstate.outbuffer, 
-                                    1, 
-                                    CYG_HTTPD_MAXOUTBUFFER, 
-                                    fp);
-            bufs[1].iov_len = fread(httpstate.inbuffer, 
-                                    1, 
-                                    CYG_HTTPD_MAXINBUFFER, 
-                                    fp);
-            bytes_written += cyg_httpd_writev(bufs, 2);
-        }    
+    {
+        bread = fread(httpstate.outbuffer, 1, CYG_HTTPD_MAXOUTBUFFER, fp);
+        bytes_written += cyg_httpd_write(httpstate.outbuffer, bread);
+    }    
     
     err = fclose(fp);
     if(err < 0)
         cyg_httpd_send_error(CYG_HTTPD_STATUS_SYSTEM_ERROR);
 }
 #endif
 
 cyg_int32
 cyg_httpd_format_header(void)
 {
-    char date[CYG_HTTPD_TIME_STRING_LEN];
     sprintf(httpstate.outbuffer, "HTTP/1.1 %d", httpstate.status_code);
     time_t time_val = time(NULL);
     
     // Error messages (i.e. with status other than OK, automatically add
     //  the no-cache header.
@@ -592,11 +576,19 @@
     case CYG_HTTPD_STATUS_MOVED_PERMANENTLY:
         strcat(httpstate.outbuffer, " Moved Permanently\r\n");
         strcat(httpstate.outbuffer, "Location: ");
         strcat(httpstate.outbuffer, httpstate.url);
         strcat(httpstate.outbuffer, "\r\n");
-        strcat(httpstate.outbuffer, "Cache-Control: no-cache\r\n");
+        sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+                "Content-Length: %d\r\n",
+                httpstate.payload_len);
+        break;
+    case CYG_HTTPD_STATUS_MOVED_TEMPORARILY:
+        strcat(httpstate.outbuffer, " Found\r\n");
+        strcat(httpstate.outbuffer, "Location: ");
+        strcat(httpstate.outbuffer, httpstate.url);
+        strcat(httpstate.outbuffer, "\r\n");
         sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
                 "Content-Length: %d\r\n",
                 httpstate.payload_len);
         break;
 #ifdef CYGOPT_NET_ATHTTPD_USE_AUTH
@@ -617,84 +609,95 @@
         else             
         {
             sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
                      "WWW-Authenticate: Digest realm=\"%s\" ",
                      httpstate.needs_auth->auth_domainname);
-            cyg_httpd_format_time_string(cyg_httpd_md5_nonce, &time_val);
+            strftime(cyg_httpd_md5_nonce, 
+                     33,
+                     TIME_FORMAT_RFC1123,
+                     gmtime(&time_val));
             sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
                     "nonce=\"%s\" ", cyg_httpd_md5_nonce);
             sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
                     "opaque=\"%s\", ", 
                     CYG_HTTPD_MD5_AUTH_OPAQUE);
             sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
                     "stale=false, algorithm=%s, qop=\"%s\"\r\n",
                     CYG_HTTPD_MD5_AUTH_NAME,
-                    CYG_HTTPD_MD5_AUTH_QOP );
+                    CYG_HTTPD_MD5_AUTH_QOP);
         }
-        strcat(httpstate.outbuffer, "Cache-Control: no-cache\r\n");
         break;
 #endif
     case CYG_HTTPD_STATUS_NOT_MODIFIED:
         strcat(httpstate.outbuffer, " Not Modified\r\n");
-        strcat(httpstate.outbuffer, "Cache-Control: no-cache\r\n");
         break;
     case CYG_HTTPD_STATUS_NOT_FOUND:
         strcat(httpstate.outbuffer, " Not Found\r\n");
-        strcat(httpstate.outbuffer, "Cache-Control: no-cache\r\n");
         sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
                 "Content-Length: %d\r\n", 
                 httpstate.payload_len);
         break;
     case CYG_HTTPD_STATUS_METHOD_NOT_ALLOWED:
         strcat(httpstate.outbuffer, " Method Not Allowed\r\n");
-        strcat(httpstate.outbuffer, "Cache-Control: no-cache\r\n");
         break;
     default:
         strcat(httpstate.outbuffer, " OK\r\n");
         if ((httpstate.mode & CYG_HTTPD_MODE_TRANSFER_CHUNKED) == 0)
             sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
                     "Content-Length: %d\r\n", 
                     httpstate.payload_len);
         break;
     }
 
-    cyg_httpd_format_time_string(date, &time_val);
-    sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer), 
-             "Date: %s\r\n", 
-             date);
+    strcat(httpstate.outbuffer, "Date: ");
+    strftime(httpstate.outbuffer + strlen(httpstate.outbuffer), 
+             CYG_HTTPD_MAXOUTBUFFER - strlen(httpstate.outbuffer),
+             TIME_FORMAT_RFC1123,
+             gmtime(&time_val));
+    strcat(httpstate.outbuffer, "\r\n");
+    
     sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer), 
-             "Server: %s\r\n", 
-             CYGDAT_NET_ATHTTPD_SERVEROPT_SERVERID);
+            "Server: %s\r\n", 
+            CYGDAT_NET_ATHTTPD_SERVEROPT_SERVERID);
     
     if (httpstate.mode & CYG_HTTPD_MODE_CLOSE_CONN)
         strcat(httpstate.outbuffer, "Connection: close\r\n");
     else
         strcat(httpstate.outbuffer, "Connection: keep-alive\r\n");
 
-    if (httpstate.mime_type != 0)
-    {
-        sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
-                 "Content-Type: %s\r\n", 
-                 httpstate.mime_type);
-    }             
-    else
-        // When we cannot find the appropriate MIME type, we'll send a
-        //  default type.
-        sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
-                "Content-Type: %s\r\n", 
-                CYGDAT_NET_ATHTTPD_DEFAULT_MIME_TYPE);
+    // When we cannot find the appropriate MIME type, we'll send a default type.
+    if (httpstate.mime_type == 0)
+        httpstate.mime_type = CYGDAT_NET_ATHTTPD_DEFAULT_MIME_TYPE;
+    sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
+            "Content-Type: %s\r\n", 
+            httpstate.mime_type);
 
     if (httpstate.mode & CYG_HTTPD_MODE_TRANSFER_CHUNKED)
         strcat(httpstate.outbuffer, "Transfer-Encoding: chunked\r\n");
 
+    if (httpstate.mode & CYG_HTTPD_MODE_NO_CACHE)
+        strcat(httpstate.outbuffer, "Cache-Control: no-cache\r\n");
+        
     if (httpstate.last_modified != -1)
     {
         time_val = httpstate.last_modified;
-        cyg_httpd_format_time_string(httpstate.inbuffer, &time_val);
-        sprintf(httpstate.outbuffer + strlen(httpstate.outbuffer),
-                 "Last-Modified: %s\r\n", 
-                 httpstate.inbuffer);
+        strcat(httpstate.outbuffer, "Last-Modified: "); 
+        strftime(httpstate.outbuffer + strlen(httpstate.outbuffer), 
+                 CYG_HTTPD_MAXOUTBUFFER - strlen(httpstate.outbuffer),
+                 TIME_FORMAT_RFC1123,
+                 gmtime(&time_val));
+        strcat(httpstate.outbuffer, "\r\n");
+
+#if (CYGOPT_NET_ATHTTPD_DOCUMENT_EXPIRATION_TIME != 0)                 
+        time_val += CYGOPT_NET_ATHTTPD_DOCUMENT_EXPIRATION_TIME;
+        strcat(httpstate.outbuffer, "Expires: "); 
+        strftime(httpstate.outbuffer + strlen(httpstate.outbuffer), 
+                 CYG_HTTPD_MAXOUTBUFFER - strlen(httpstate.outbuffer),
+                 TIME_FORMAT_RFC1123,
+                 gmtime(&time_val));
+        strcat(httpstate.outbuffer, "\r\n");
+#endif
     }        
                  
     // There must be 2 carriage returns between the header and the body, 
     //  so if you modify this function make sure that there is another 
     //  CRLF already terminating the buffer thus far.
@@ -703,25 +706,16 @@
 }
 
 void
 cyg_httpd_handle_method_GET(void)
 {
-    // Handlers are executed first.
+    // Use defined handlers take precedence over other forms of response.
     handler h = cyg_httpd_find_handler();
     if (h != 0)
     {
-        // A handler was found. We'll call the function associated to it. If 
-        //  the return value is 0 we'll also try to see if the file by the same
-        //  name is available in the file system or internal resources.
-#ifdef CYGOPT_NET_ATHTTPD_USE_FS
-        cyg_int32 rc = h(&httpstate);
-        if (rc != 0)
-            return;
-#else            
         h(&httpstate);
         return;
-#endif        
     }
     
 #ifdef CYGOPT_NET_ATHTTPD_USE_CGIBIN_OBJLOADER
     // If the URL is a CGI script, there is a different directory...
     if (httpstate.url[0] == '/' &&
@@ -781,11 +775,11 @@
     while ((p[0] == '/') && (p[1] == '/'))
        p++;
 
     // Store the url, and check if there is a form result in it.
     while ((*p != ' ') && (*p != '?') &&
-            ((dest - httpstate.url) < CYG_HTTPD_MAXURL - 1))
+            ((dest - httpstate.url) <= CYG_HTTPD_MAXURL))
     {
         // Look for encoded characters in the URL.
         if (*p == '%') 
         {
             p++;
@@ -799,155 +793,144 @@
     }
 
     // Terminate the file name...
     *dest = '\0';
 
-    // The URL must start with a leadng slash.
+    // The URL must start with a leading slash.
     if (httpstate.url[0] != '/') 
     {
         cyg_httpd_send_error(CYG_HTTPD_STATUS_BAD_REQUEST);
         return (char*)0;
     }
-
-    // Run past white spaces.
-    while ((*p == ' ') && (*p != '\0'))
-        p++;
     return p;
 }
 
 char*
 cyg_httpd_parse_POST(char* p)
 {
+    httpstate.method = CYG_HTTPD_METHOD_POST;
+    httpstate.mode &= ~CYG_HTTPD_MODE_SEND_HEADER_ONLY;
+    char *cp = cyg_httpd_get_URL(p);
+    if (cp == 0)
+        return (char*)0;
 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
     diag_printf("POST Request URL: %s\n", httpstate.url);
 #endif    
-    httpstate.method = CYG_HTTPD_METHOD_POST;
-    httpstate.mode &= ~CYG_HTTPD_SEND_HEADER_ONLY;
-    p = cyg_httpd_get_URL(p);
-    if (p == 0)
-        return 0;
 
-    while (*p++ != '\n');
-    return p;
+    while (*cp++ != '\n');
+    return cp;
 }
 
 char*
 cyg_httpd_parse_GET(char* p)
 {
+    char *cp = cyg_httpd_get_URL(p);
+    if (cp == 0)
+        return 0;
 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
-    diag_printf("GET Request URL: %s\n", httpstate.url);
+    if ( httpstate.method == CYG_HTTPD_METHOD_GET)
+        diag_printf("GET Request URL: %s\n", httpstate.url);
+    else    
+        diag_printf("HEAD Request URL: %s\n", httpstate.url);
 #endif    
-    httpstate.method = CYG_HTTPD_METHOD_GET;
-    httpstate.mode &= ~CYG_HTTPD_SEND_HEADER_ONLY;
-    p = cyg_httpd_get_URL(p);
-    if (p == 0)
-        return 0;
-    
-    if (*p == '?')
+
+    if (*cp == '?')
         // If we have a GET header with form variables we'll get the
         //  variables out of it and store them in the variable table.
-        p = cyg_httpd_store_form_data(++p);
-
-    // Run past white spaces.
-    while (*p == ' ')
-        p++;
-    return p;
+        // Can we assume that HEAD request can have form variables?
+        // That will be a yes until I learn otherwise.
+        cp = cyg_httpd_store_form_data(++cp);
+
+    // Run to end of line.
+    while (*cp++ != '\n');
+    return cp;
 }
 
 char*
-cyg_httpd_parse_HEAD(char* p)
+cyg_httpd_process_header(char *p)
 {
-#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
-    diag_printf("HEAD Request URL: %s\n", httpstate.url);
-#endif    
-    httpstate.method = CYG_HTTPD_METHOD_HEAD;
-    httpstate.mode |= CYG_HTTPD_SEND_HEADER_ONLY;
-    p = cyg_httpd_get_URL(p);
-    if (p == 0)
-        return 0;
-
-    // Can we have variables in a HEAD request? I'll assume the answer is a yes
-    //  for now.
-    if (*p == '?')
-        p = cyg_httpd_store_form_data(++p);
-
-    // Run past white spaces.
-    while (*p == ' ')
-        p++;
-    return p;
-}
-
-cyg_int32
-cyg_httpd_process_header(void)
-{
-    char *cp;
-    char *p = httpstate.inbuffer;
-    
     // The deafult for HTTP 1.1 is keep-alive connections, unless specifically
     //  closed by the far end.
-    httpstate.mode &= ~CYG_HTTPD_MODE_CLOSE_CONN;
+    httpstate.mode &= ~(CYG_HTTPD_MODE_CLOSE_CONN | CYG_HTTPD_MODE_FORM_DATA);
     httpstate.modified_since = -1;
-    while (*p)
+    httpstate.content_len = 0;
+    while ((*p != '\r') && (*p != '\n') & (*p != '\0'))
     {
-        if (!strncasecmp("GET ", p, 4))
+        if (strncasecmp("GET ", p, 4) == 0)
         {
-            p = cyg_httpd_parse_GET(httpstate.inbuffer + 4);
+            // We need separate flags for HEAD and SEND_HEADERS_ONLY since
+            //  we can send a header only even in the case of a GET request
+            //  (as a 304 response.)
+            httpstate.method = CYG_HTTPD_METHOD_GET;
+            httpstate.mode &= ~CYG_HTTPD_MODE_SEND_HEADER_ONLY;
+            p = cyg_httpd_parse_GET(p + 4);
             if (p ==0)
-                return 0;
+                return (char*)0;
         }
-        else if (!strncasecmp("POST ", p, 5))
+        else if (strncasecmp("POST ", p, 5) == 0)
         {
-            p = cyg_httpd_parse_POST(httpstate.inbuffer + 5);
+            p = cyg_httpd_parse_POST(p + 5);
             if (p ==0)
-                return 0;
+                return (char*)0;
         }
-        else if (!strncasecmp("HEAD ", p, 5))
+        else if (strncasecmp("HEAD ", p, 5) == 0)
         {
-            p = cyg_httpd_parse_HEAD(httpstate.inbuffer + 5);
+            httpstate.method = CYG_HTTPD_METHOD_HEAD;
+            httpstate.mode |= CYG_HTTPD_MODE_SEND_HEADER_ONLY;
+            p = cyg_httpd_parse_GET(p + 5);
             if (p ==0)
-                return 0;
+                return (char*)0;
         }
         else if (strncasecmp(p, "Content-Length: ", 16) == 0)
         {
-            cp = strchr(p, ':') + 2;
-            if(cp)
-                httpstate.inbuffer_len = atoi(cp);
-            while(*p != '\n')
-                p++;
+            p = strchr(p, ':') + 2;
+            if (p)
+                // In the case of a POST request, this is the total length of
+                //  the payload, which might be spread across several frames.
+                httpstate.content_len = atoi(p);
+            while (*p++ != '\n');
+        }
+        else if (strncasecmp(p, "Content-Type: ", 14) == 0)
+        {
+            p = strchr(p, ':') + 2;
+            if (p)
+                // In the case of a POST request, this is the total length of
+                //  the payload, which might be spread across several frames.
+                if (strncasecmp(p,
+                                "application/x-www-form-urlencoded",
+                                33) == 0)
+                    httpstate.mode |= CYG_HTTPD_MODE_FORM_DATA;
+            while (*p++ != '\n');
         }
         else if (strncasecmp("Host:", p, 5) == 0)
         {
             p += 5;
-            if ( *p == ' ' )
+            if (*p == ' ')
                 p++;
             sscanf(p,
                    "%d.%d.%d.%d",
                    &httpstate.host[0],
                    &httpstate.host[1],
                    &httpstate.host[2],
                    &httpstate.host[3]);
-            while ( *p != '\n')
-               p++;
-            p++;
+            while (*p++ != '\n');
         }
         else if (strncasecmp("If-Modified-Since:", p, 18) == 0)
         {
             p += 18;
-            if ( *p == ' ' )
+            if ( *p == ' ')
                 p++;
             httpstate.modified_since = cyg_httpd_parse_date(p);
-            while ( *p != '\n')
-               p++;
-            p++;
+            while (*p++ != '\n');
         }
 #ifdef CYGOPT_NET_ATHTTPD_USE_AUTH
         else if (strncasecmp("Authorization:", p, 14) == 0)
         {
             p += 14;
             while (*p == ' ')
                 p++;
-            if (!strncasecmp("Basic", p, 5))
+            if (strncasecmp("Basic", p, 5) == 0)
             {
                 char *cr = cyg_httpd_md5_digest;
                 p += 5;
                 while (*p == ' ')
                     p++;
@@ -955,11 +938,10 @@
                     *cr++ = *p++;
                 *cr = '\0';
             }
             else if (strncasecmp(p, "Digest", 6) == 0)
             {
-                diag_printf(httpstate.inbuffer);
                 p += 6;
                 while (*p == ' ')
                    p++;
                 while ((*p != '\r') && (*p != '\n'))
                 {
@@ -986,57 +968,88 @@
                     else if (strncasecmp(p, "uri=", 4) == 0)
                         p = cyg_httpd_digest_skip(p + 4);
                 }
             }    
             else
-            {
-                while (*p != '\n')
-                   p++;
-                p++;
-            }    
+                while (*p++ != '\n');
         }   
 #endif // CYGOPT_NET_ATHTTPD_USE_AUTH
         else if (strncasecmp(p, "Connection:", 11) == 0)
         {
             p += 11;
             while (*p == ' ')
                p++;
             if (strncasecmp(p, "close", 5) == 0)
                 httpstate.mode |= CYG_HTTPD_MODE_CLOSE_CONN;
-            while(*p != '\n')
-                p++;
-            p++;    
+            while (*p++ != '\n');
         }
         else
-        {
             // We'll just dump the rest of the line and move on to the next.
-            while(*p != '\n')
-                p++;
-            p++;
-        }
+            while (*p++ != '\n');
     }
-    return 1;
+    
+    if (*p == '\0')
+    {
+        // This is the case of a header that is split in two or more frames.
+        // We cannot process it right away and will have to wait for the rest
+        //  of the data. This has _major_ implications because we implicitly
+        //  and tacitly assume that the next frame that will be handled by the
+        //  server is the continuation of this one. But if the next frame is
+        //  for instance, a request from another client, we are in trouble
+        //  since the new request will be processed and this request will
+        //  be dropped.
+        // Caveat: This is all untested. While theoretically this is possible,
+        //  as much as I tried, I could not coerce any of the popular browser
+        //  to split a header in multiple frames.
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
+        diag_printf("Split header found.\r\n");
+#endif        
+        return 0;
+    }    
+
+    // If this is the end of this request, but there might be other queued up
+    //  because of pipelining of two requests in a single frame. This while()
+    //  will get rid of the \r\n that terminates the header section of a
+    //  request.
+    while (*p++ != '\n');
+    
+    // In the case of large POST the payload comes with the header (and
+    //  possibly further frames.) Here is where we mark the start of the
+    //  POST data.
+    httpstate.header_end = p;
+    return p;
 }
 
-cyg_int32
+void
 cyg_httpd_process_method(void)
 {
-    cyg_int32 rc = cyg_httpd_process_header();
-    if (rc == 0)
-        return 0;
-        
-    switch (httpstate.method)
+    char* p = httpstate.inbuffer;
+    
+    // Some browsers send an extra '\r\n' after the POST data that is not
+    //  accounted in the "Content-Length:" field. We are going to junk all
+    //  the leading returns and line carriages we find.
+    while ((*p == '\r') || (*p =='\n'))
+        p++;
+    while (*p != '\0')
     {
-    case CYG_HTTPD_METHOD_GET:
-    case CYG_HTTPD_METHOD_HEAD:
-        cyg_httpd_handle_method_GET();
-        break;
-    case CYG_HTTPD_METHOD_POST:
-        cyg_httpd_handle_method_POST();
-        break;
-    default:
-        cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_IMPLEMENTED);
-        break;
-    }
-    return 0;
+        p = cyg_httpd_process_header(p);
+        if (p == 0)
+            return;
+        
+        switch (httpstate.method)
+        {
+            case CYG_HTTPD_METHOD_GET:
+            case CYG_HTTPD_METHOD_HEAD:
+                cyg_httpd_handle_method_GET();
+                break;
+            case CYG_HTTPD_METHOD_POST:
+                cyg_httpd_handle_method_POST();
+                return;
+                break;
+            default:
+                cyg_httpd_send_error(CYG_HTTPD_STATUS_NOT_IMPLEMENTED);
+                return;
+            break;
+        }
+    }    
 }
 
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/jim-aio.c /home/atonizzo/ecos/devo/packages/net/athttpd/current/src/jim-aio.c
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/jim-aio.c	1969-12-31 16:00:00.000000000 -0800
+++ /home/atonizzo/ecos/devo/packages/net/athttpd/current/src/jim-aio.c	2006-11-08 09:45:08.000000000 -0800
@@ -0,0 +1,357 @@
+/* Jim - ANSI I/O extension
+ * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
+ *
+ * $Id: jim-aio.c,v 1.10 2006/11/06 16:54:48 antirez Exp $
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * A copy of the license is also included in the source distribution
+ * of Jim, as a TXT file name called LICENSE.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#ifndef JIM_STATICEXT
+#define JIM_EXTENSION
+#endif
+#include <pkgconf/athttpd.h>
+#include <cyg/athttpd/jim.h>
+
+#define AIO_CMD_LEN 128
+#define AIO_BUF_LEN 1024
+
+typedef struct AioFile {
+    FILE *fp;
+    int keepOpen; /* If set, the file is not fclosed on cleanup (stdin, ...) */
+} AioFile;
+
+static void JimAioSetError(Jim_Interp *interp)
+{
+    Jim_SetResultString(interp, strerror(errno), -1);
+}
+
+static void JimAioDelProc(Jim_Interp *interp, void *privData)
+{
+    AioFile *af = privData;
+    JIM_NOTUSED(interp);
+
+    if (!af->keepOpen)
+        fclose(af->fp);
+    Jim_Free(af);
+}
+
+/* Calls to [aio.file] create commands that are implemented by this
+ * C command. */
+static int JimAioHandlerCommand(Jim_Interp *interp, int argc,
+        Jim_Obj *const *argv)
+{
+    AioFile *af = Jim_CmdPrivData(interp);
+    int option;
+    const char *options[] = {
+        "close", "seek", "tell", "gets", "read", "puts", "flush", "eof", NULL
+    };
+    enum {OPT_CLOSE, OPT_SEEK, OPT_TELL, OPT_GETS, OPT_READ, OPT_PUTS,
+          OPT_FLUSH, OPT_EOF};
+
+    if (argc < 2) {
+        Jim_WrongNumArgs(interp, 1, argv, "method ?args ...?");
+        return JIM_ERR;
+    }
+    if (Jim_GetEnum(interp, argv[1], options, &option, "AIO method",
+                JIM_ERRMSG) != JIM_OK)
+        return JIM_ERR;
+    /* CLOSE */
+    if (option == OPT_CLOSE) {
+        if (argc != 2) {
+            Jim_WrongNumArgs(interp, 2, argv, "");
+            return JIM_ERR;
+        }
+        Jim_DeleteCommand(interp, Jim_GetString(argv[0], NULL));
+        return JIM_OK;
+    } else if (option == OPT_SEEK) {
+    /* SEEK */
+        int orig = SEEK_SET;
+        long offset;
+
+        if (argc != 3 && argc != 4) {
+            Jim_WrongNumArgs(interp, 2, argv, "offset ?origin?");
+            return JIM_ERR;
+        }
+        if (argc == 4) {
+            if (Jim_CompareStringImmediate(interp, argv[3], "start"))
+                orig = SEEK_SET;
+            else if (Jim_CompareStringImmediate(interp, argv[3], "current"))
+                orig = SEEK_CUR;
+            else if (Jim_CompareStringImmediate(interp, argv[3], "end"))
+                orig = SEEK_END;
+            else {
+                Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+                Jim_AppendStrings(interp, Jim_GetResult(interp),
+                        "bad origin \"", Jim_GetString(argv[3], NULL),
+                        "\" must be: start, current, or end", NULL);
+                return JIM_ERR;
+            }
+        }
+        if (Jim_GetLong(interp, argv[2], &offset) != JIM_OK)
+            return JIM_ERR;
+        if (fseek(af->fp, offset, orig) == -1) {
+            JimAioSetError(interp);
+            return JIM_ERR;
+        }
+        return JIM_OK;
+    } else if (option == OPT_TELL) {
+    /* TELL */
+        long position;
+
+        if (argc != 2) {
+            Jim_WrongNumArgs(interp, 2, argv, "");
+            return JIM_ERR;
+        }
+        position = ftell(af->fp);
+        Jim_SetResult(interp, Jim_NewIntObj(interp, position));
+        return JIM_OK;
+    } else if (option == OPT_GETS) {
+    /* GETS */
+        char buf[AIO_BUF_LEN];
+        Jim_Obj *objPtr;
+
+        if (argc != 2 && argc != 3) {
+            Jim_WrongNumArgs(interp, 2, argv, "?varName?");
+            return JIM_ERR;
+        }
+        objPtr = Jim_NewStringObj(interp, NULL, 0);
+        while (1) {
+            int more = 0;
+            buf[AIO_BUF_LEN-1] = '_';
+            if (fgets(buf, AIO_BUF_LEN, af->fp) == NULL)
+                break;
+            if (buf[AIO_BUF_LEN-1] == '\0' && buf[AIO_BUF_LEN] == '\n')
+                more = 1;
+            if (more) {
+                Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN-1);
+            } else {
+                /* strip "\n" */
+                Jim_AppendString(interp, objPtr, buf, strlen(buf)-1);
+            }
+            if (!more)
+                break;
+        }
+        if (ferror(af->fp)) {
+            /* I/O error */
+            Jim_IncrRefCount(objPtr);
+            Jim_DecrRefCount(interp, objPtr);
+            JimAioSetError(interp);
+            return JIM_ERR;
+        }
+        /* On EOF returns -1 if varName was specified, or the empty string. */
+        if (feof(af->fp) && Jim_Length(objPtr) == 0) {
+            Jim_IncrRefCount(objPtr);
+            Jim_DecrRefCount(interp, objPtr);
+            if (argc == 3)
+                Jim_SetResult(interp, Jim_NewIntObj(interp, -1));
+            return JIM_OK;
+        }
+        if (argc == 3) {
+            int totLen;
+
+            Jim_GetString(objPtr, &totLen);
+            if (Jim_SetVariable(interp, argv[2], objPtr) != JIM_OK) {
+                Jim_IncrRefCount(objPtr);
+                Jim_DecrRefCount(interp, objPtr);
+                return JIM_ERR;
+            }
+            Jim_SetResult(interp, Jim_NewIntObj(interp, totLen));
+        } else {
+            Jim_SetResult(interp, objPtr);
+        }
+        return JIM_OK;
+    } else if (option == OPT_READ) {
+    /* READ */
+        char buf[AIO_BUF_LEN];
+        Jim_Obj *objPtr;
+        int nonewline = 0;
+        int neededLen = -1; /* -1 is "read as much as possible" */
+
+        if (argc != 2 && argc != 3) {
+            Jim_WrongNumArgs(interp, 2, argv, "?-nonewline? ?len?");
+            return JIM_ERR;
+        }
+        if (argc == 3 &&
+            Jim_CompareStringImmediate(interp, argv[2], "-nonewline"))
+        {
+            nonewline = 1;
+            argv++;
+            argc--;
+        }
+        if (argc == 3) {
+            jim_wide wideValue;
+            if (Jim_GetWide(interp, argv[2], &wideValue) != JIM_OK)
+                return JIM_ERR;
+            if (wideValue < 0) {
+                Jim_SetResultString(interp, "invalid parameter: negative len",
+                        -1);
+                return JIM_ERR;
+            }
+            neededLen = (int) wideValue;
+        }
+        objPtr = Jim_NewStringObj(interp, NULL, 0);
+        while (neededLen != 0) {
+            int retval;
+            int readlen;
+           
+            if (neededLen == -1) {
+                readlen = AIO_BUF_LEN;
+            } else {
+                readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
+            }
+            retval = fread(buf, 1, readlen, af->fp);
+            if (retval > 0) {
+                Jim_AppendString(interp, objPtr, buf, retval);
+                if (neededLen != -1) {
+                    neededLen -= retval;
+                }
+            }
+            if (retval != readlen) break;
+        }
+        /* Check for error conditions */
+        if (ferror(af->fp)) {
+            /* I/O error */
+            Jim_FreeNewObj(interp, objPtr);
+            JimAioSetError(interp);
+            return JIM_ERR;
+        }
+        if (nonewline) {
+            int len;
+            const char *s = Jim_GetString(objPtr, &len);
+
+            if (len > 0 && s[len-1] == '\n') {
+                objPtr->length--;
+                objPtr->bytes[objPtr->length] = '\0';
+            }
+        }
+        Jim_SetResult(interp, objPtr);
+        return JIM_OK;
+    } else if (option == OPT_PUTS) {
+    /* PUTS */
+        int wlen;
+        const char *wdata;
+
+        if (argc != 3 && (argc != 4 || !Jim_CompareStringImmediate(
+                        interp, argv[2], "-nonewline"))) {
+            Jim_WrongNumArgs(interp, 2, argv, "?-nonewline? string");
+            return JIM_ERR;
+        }
+        wdata = Jim_GetString(argv[2+(argc==4)], &wlen);
+        if (fwrite(wdata, 1, wlen, af->fp) != (unsigned)wlen ||
+            (argc == 3 && fwrite("\n", 1, 1, af->fp) != 1)) {
+            JimAioSetError(interp);
+            return JIM_ERR;
+        }
+        return JIM_OK;
+    } else if (option  == OPT_FLUSH) {
+    /* FLUSH */
+        if (argc != 2) {
+            Jim_WrongNumArgs(interp, 2, argv, "");
+            return JIM_ERR;
+        }
+        if (fflush(af->fp) == EOF) {
+            JimAioSetError(interp);
+            return JIM_ERR;
+        }
+        return JIM_OK;
+    } else if (option  == OPT_EOF) {
+    /* EOF */
+        if (argc != 2) {
+            Jim_WrongNumArgs(interp, 2, argv, "");
+            return JIM_ERR;
+        }
+        Jim_SetResult(interp, Jim_NewIntObj(interp, feof(af->fp)));
+        return JIM_OK;
+    }
+    return JIM_OK;
+}
+
+static int JimAioOpenCommand(Jim_Interp *interp, int argc, 
+        Jim_Obj *const *argv)
+{
+    FILE *fp;
+    AioFile *af;
+    char buf[AIO_CMD_LEN];
+    const char *mode = "r";
+    Jim_Obj *objPtr;
+    long fileId;
+    const char *options[] = {"input", "output", "error"};
+    enum {OPT_INPUT, OPT_OUTPUT, OPT_ERROR};
+    int keepOpen = 0, modeLen;
+
+    if (argc != 2 && argc != 3) {
+        Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?");
+        return JIM_ERR;
+    }
+    if (argc == 3)
+        mode = Jim_GetString(argv[2], &modeLen);
+    if (argc == 3 && Jim_CompareStringImmediate(interp, argv[1], "standard") &&
+            modeLen >= 3) {
+            int option;
+        if (Jim_GetEnum(interp, argv[2], options, &option, "standard channel",
+                    JIM_ERRMSG) != JIM_OK)
+            return JIM_ERR;
+        keepOpen = 1;
+        switch (option) {
+        case OPT_INPUT: fp = stdin; break;
+        case OPT_OUTPUT: fp = stdout; break;
+        case OPT_ERROR: fp = stderr; break;
+        default: fp = NULL; Jim_Panic(interp,"default reached in JimAioOpenCommand()");
+                 break;
+        }
+    } else {
+        fp = fopen(Jim_GetString(argv[1], NULL), mode);
+        if (fp == NULL) {
+            JimAioSetError(interp);
+            return JIM_ERR;
+        }
+    }
+    /* Get the next file id */
+    if (Jim_EvalGlobal(interp,
+                "if {[catch {incr aio.fileId}]} {set aio.fileId 0}") != JIM_OK)
+        return JIM_ERR;
+    objPtr = Jim_GetGlobalVariableStr(interp, "aio.fileId", JIM_ERRMSG);
+    if (objPtr == NULL) return JIM_ERR;
+    if (Jim_GetLong(interp, objPtr, &fileId) != JIM_OK) return JIM_ERR;
+
+    /* Create the file command */
+    af = Jim_Alloc(sizeof(*af));
+    af->fp = fp;
+    af->keepOpen = keepOpen;
+    sprintf(buf, "aio.handle%ld", fileId);
+    Jim_CreateCommand(interp, buf, JimAioHandlerCommand, af, JimAioDelProc);
+    Jim_SetResultString(interp, buf, -1);
+    return JIM_OK;
+}
+
+#ifndef JIM_STATICEXT
+int Jim_OnLoad(Jim_Interp *interp)
+#else
+int Jim_AioInit(Jim_Interp *interp)
+#endif
+{
+    #ifndef JIM_STATICEXT
+    Jim_InitExtension(interp);
+    #endif
+    if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG) != JIM_OK)
+        return JIM_ERR;
+    Jim_CreateCommand(interp, "aio.open", JimAioOpenCommand, NULL, NULL);
+    return JIM_OK;
+}
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/jim.c /home/atonizzo/ecos/devo/packages/net/athttpd/current/src/jim.c
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/jim.c	2006-08-10 10:37:48.000000000 -0700
+++ /home/atonizzo/ecos/devo/packages/net/athttpd/current/src/jim.c	2006-11-08 09:40:50.000000000 -0800
@@ -1,10 +1,10 @@
 /* Jim - A small embeddable Tcl interpreter
  * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
  * Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
  *
- * $Id: jim.c,v 1.163 2005/09/19 15:47:15 antirez Exp $
+ * $Id: jim.c,v 1.170 2006/11/06 21:48:57 antirez Exp $
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
@@ -18,15 +18,15 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-#include <pkgconf/athttpd.h>
-
 #define __JIM_CORE__
 #define JIM_OPTIMIZATION /* comment to avoid optimizations and reduce size */
 
+#include <pkgconf/athttpd.h>
+
 #ifndef JIM_ANSIC
 #define JIM_DYNLIB      /* Dynamic library support for UNIX and WIN32 */
 #endif /* JIM_ANSIC */
 
 #include <stdio.h>
@@ -448,31 +448,36 @@
 }
 
 /* -----------------------------------------------------------------------------
  * Special functions
  * ---------------------------------------------------------------------------*/
-void Jim_Panic(const char *fmt, ...)
+
+/* Note that 'interp' may be NULL if not available in the
+ * context of the panic. It's only useful to get the error
+ * file descriptor, it will default to stderr otherwise. */
+void Jim_Panic(Jim_Interp *interp, const char *fmt, ...)
 {
     va_list ap;
+    FILE *fp = interp ? interp->stderr : stderr;
 
     va_start(ap, fmt);
-    fprintf(stderr, "\nJIM INTERPRETER PANIC: ");
-    vfprintf(stderr, fmt, ap);
-    fprintf(stderr, "\n\n");
+    fprintf(fp, JIM_NL "JIM INTERPRETER PANIC: ");
+    vfprintf(fp, fmt, ap);
+    fprintf(fp, JIM_NL JIM_NL);
     va_end(ap);
 #ifdef HAVE_BACKTRACE
     {
         void *array[40];
         int size, i;
         char **strings;
 
         size = backtrace(array, 40);
         strings = backtrace_symbols(array, size);
         for (i = 0; i < size; i++)
-            printf("[backtrace] %s\n", strings[i]);
-        printf("[backtrace] Include the above lines and the output\n");
-        printf("[backtrace] of 'nm <executable>' in the bug report.\n");
+            fprintf(fp,"[backtrace] %s" JIM_NL, strings[i]);
+        fprintf(fp,"[backtrace] Include the above lines and the output" JIM_NL);
+        fprintf(fp,"[backtrace] of 'nm <executable>' in the bug report." JIM_NL);
     }
 #endif
     abort();
 }
 
@@ -491,11 +496,11 @@
 
 void *Jim_Alloc(int size)
 {
     void *p = malloc(size);
     if (p == NULL)
-        Jim_Panic("Out of memory");
+        Jim_Panic(NULL,"Out of memory");
     return p;
 }
 
 void Jim_Free(void *ptr) {
     free(ptr);
@@ -503,11 +508,11 @@
 
 void *Jim_Realloc(void *ptr, int size)
 {
     void *p = realloc(ptr, size);
     if (p == NULL)
-        Jim_Panic("Out of memory");
+        Jim_Panic(NULL,"Out of memory");
     return p;
 }
 
 char *Jim_StrDup(const char *s)
 {
@@ -1404,11 +1409,11 @@
 }
 
 /* xdigitval and odigitval are helper functions for JimParserGetToken() */
 static int xdigitval(int c)
 {
-    if (c >= '0' && c <= '7') return c-'0';
+    if (c >= '0' && c <= '9') return c-'0';
     if (c >= 'a' && c <= 'f') return c-'a'+10;
     if (c >= 'A' && c <= 'F') return c-'A'+10;
     return -1;
 }
 
@@ -1746,11 +1751,11 @@
  * reused by Jim_NewObj(). */
 void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
 {
     /* Check if the object was already freed, panic. */
     if (objPtr->refCount != 0)  {
-        Jim_Panic("!!!Object %p freed with bad refcount %d", objPtr,
+        Jim_Panic(interp,"!!!Object %p freed with bad refcount %d", objPtr,
                 objPtr->refCount);
     }
     /* Free the internal representation */
     Jim_FreeIntRep(interp, objPtr);
     /* Free the string representation */
@@ -1833,11 +1838,11 @@
 const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
 {
     if (objPtr->bytes == NULL) {
         /* Invalid string repr. Generate it. */
         if (objPtr->typePtr->updateStringProc == NULL) {
-            Jim_Panic("UpdataStringProc called against '%s' type.",
+            Jim_Panic(NULL,"UpdataStringProc called against '%s' type.",
                 objPtr->typePtr->name);
         }
         objPtr->typePtr->updateStringProc(objPtr);
     }
     if (lenPtr)
@@ -1881,12 +1886,11 @@
 }
 
 int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
 {
     /* Get a fresh string representation. */
-//    (void*) Jim_GetString(objPtr, NULL);
-    Jim_GetString(objPtr, NULL);
+    (void) Jim_GetString(objPtr, NULL);
     /* Free any other internal representation. */
     Jim_FreeIntRep(interp, objPtr);
     /* Set it as string, i.e. just set the maxLength field. */
     objPtr->typePtr = &stringObjType;
     objPtr->internalRep.strValue.maxLength = objPtr->length;
@@ -1964,11 +1968,11 @@
 /* Higher level API to append strings to objects. */
 void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str,
         int len)
 {
     if (Jim_IsShared(objPtr))
-        Jim_Panic("Jim_AppendString called with shared object");
+        Jim_Panic(interp,"Jim_AppendString called with shared object");
     if (objPtr->typePtr != &stringObjType)
         SetStringFromAny(interp, objPtr);
     StringAppendString(objPtr, str, len);
 }
 
@@ -2328,13 +2332,13 @@
 
 static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
         const char *fileName, int lineNumber)
 {
     if (Jim_IsShared(objPtr))
-        Jim_Panic("JimSetSourceInfo called with shared object");
+        Jim_Panic(interp,"JimSetSourceInfo called with shared object");
     if (objPtr->typePtr != NULL)
-        Jim_Panic("JimSetSourceInfo called with typePtr != NULL");
+        Jim_Panic(interp,"JimSetSourceInfo called with typePtr != NULL");
     objPtr->internalRep.sourceValue.fileName =
         Jim_GetSharedString(interp, fileName);
     objPtr->internalRep.sourceValue.lineNumber = lineNumber;
     objPtr->typePtr = &sourceObjType;
 }
@@ -2696,11 +2700,12 @@
                     token[end-1].type == JIM_TT_EOL)
             {
                 if (token[end].type == JIM_TT_STR &&
                     token[end+1].type != JIM_TT_SEP &&
                     token[end+1].type != JIM_TT_EOL &&
-                    !strcmp(token[end].objPtr->bytes, "expand"))
+                    (!strcmp(token[end].objPtr->bytes, "expand") ||
+                     !strcmp(token[end].objPtr->bytes, "*")))
                     expand++;
             }
             if (token[end].type == JIM_TT_SEP)
                 args++;
             end++;
@@ -2725,11 +2730,12 @@
 
                 expand = 0;
                 tokens = 0;
                 continue;
             } else if (tokens == 0 && token[i].type == JIM_TT_STR &&
-                   !strcmp(token[i].objPtr->bytes, "expand"))
+                   (!strcmp(token[i].objPtr->bytes, "expand") ||
+                    !strcmp(token[i].objPtr->bytes, "*")))
             {
                 expand++;
             }
             tokens++;
         }
@@ -2919,15 +2925,22 @@
             }
         }
     }
 
     /* Add the new command */
-    Jim_DeleteHashEntry(&interp->commands, cmdName); /* it may already exist */
+
+    /* it may already exist, so we try to delete the old one */
+    if (Jim_DeleteHashEntry(&interp->commands, cmdName) != JIM_ERR) {
+        /* There was an old procedure with the same name, this requires
+         * a 'proc epoch' update. */
+        Jim_InterpIncrProcEpoch(interp);
+    }
+    /* If a procedure with the same name didn't existed there is no need
+     * to increment the 'proc epoch' because creation of a new procedure
+     * can never affect existing cached commands. We don't do
+     * negative caching. */
     Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
-    /* There is no need to increment the 'proc epoch' because
-     * creation of a new procedure can never affect existing
-     * cached commands. We don't do negative caching. */
     return JIM_OK;
 
 err:
     Jim_FreeHashTable(cmdPtr->staticVars);
     Jim_Free(cmdPtr->staticVars);
@@ -3523,14 +3536,10 @@
         goto err;
     }
     if (Jim_DictKey(interp, dictObjPtr, substKeyObjPtr, &resObjPtr, JIM_ERRMSG)
             != JIM_OK) {
         resObjPtr = NULL;
-        Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
-        Jim_AppendStrings(interp, Jim_GetResult(interp),
-                "Variable '", Jim_GetString(varObjPtr, NULL),
-                "' does not contain a valid dictionary", NULL);
         goto err;
     }
 err:
     if (substKeyObjPtr) Jim_DecrRefCount(interp, substKeyObjPtr);
     return resObjPtr;
@@ -3883,11 +3892,12 @@
              * Id is simple... */
             if (objPtr->typePtr == &referenceObjType) {
                 Jim_AddHashEntry(&marks,
                     &objPtr->internalRep.refValue.id, NULL);
 #ifdef JIM_DEBUG_GC
-                printf("MARK (reference): %d refcount: %d\n", 
+                fprintf(interp->stdout,
+                    "MARK (reference): %d refcount: %d" JIM_NL, 
                     (int) objPtr->internalRep.refValue.id,
                     objPtr->refCount);
 #endif
                 objPtr = objPtr->nextObjPtr;
                 continue;
@@ -3921,11 +3931,11 @@
 
                 /* Ok, a reference for the given ID
                  * was found. Mark it. */
                 Jim_AddHashEntry(&marks, &id, NULL);
 #ifdef JIM_DEBUG_GC
-                printf("MARK: %d\n", (int)id);
+                fprintf(interp->stdout,"MARK: %d" JIM_NL, (int)id);
 #endif
                 p += JIM_REFERENCE_SPACE;
             }
         }
         objPtr = objPtr->nextObjPtr;
@@ -3941,11 +3951,11 @@
         refId = he->key;
         /* Check if in the mark phase we encountered
          * this reference. */
         if (Jim_FindHashEntry(&marks, refId) == NULL) {
 #ifdef JIM_DEBUG_GC
-            printf("COLLECTING %d\n", (int)*refId);
+            fprintf(interp->stdout,"COLLECTING %d" JIM_NL, (int)*refId);
 #endif
             collected++;
             /* Drop the reference, but call the
              * finalizer first if registered. */
             refPtr = he->val;
@@ -4018,20 +4028,24 @@
     i->errorLine = 0;
     i->errorFileName = Jim_StrDup("");
     i->numLevels = 0;
     i->maxNestingDepth = JIM_MAX_NESTING_DEPTH;
     i->returnCode = JIM_OK;
+    i->exitCode = 0;
     i->procEpoch = 0;
     i->callFrameEpoch = 0;
     i->liveList = i->freeList = NULL;
     i->scriptFileName = Jim_StrDup("");
     i->referenceNextId = 0;
     i->lastCollectId = 0;
     i->lastCollectTime = time(NULL);
     i->freeFramesList = NULL;
     i->prngState = NULL;
     i->evalRetcodeLevel = -1;
+    i->stdin = stdin;
+    i->stdout = stdout;
+    i->stderr = stderr;
 
     /* Note that we can create objects only after the
      * interpreter liveList and freeList pointers are
      * initialized to NULL. */
     Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
@@ -4096,28 +4110,28 @@
     /* Check that the live object list is empty, otherwise
      * there is a memory leak. */
     if (i->liveList != NULL) {
         Jim_Obj *objPtr = i->liveList;
     
-        printf("\n-------------------------------------\n");
-        printf("Objects still in the free list:\n");
+        fprintf(i->stdout,JIM_NL "-------------------------------------" JIM_NL);
+        fprintf(i->stdout,"Objects still in the free list:" JIM_NL);
         while(objPtr) {
             const char *type = objPtr->typePtr ?
                 objPtr->typePtr->name : "";
-            printf("%p \"%-10s\": '%.20s' (refCount: %d)\n",
+            fprintf(i->stdout,"%p \"%-10s\": '%.20s' (refCount: %d)" JIM_NL,
                     objPtr, type,
                     objPtr->bytes ? objPtr->bytes
                     : "(null)", objPtr->refCount);
             if (objPtr->typePtr == &sourceObjType) {
-                printf("FILE %s LINE %d\n",
+                fprintf(i->stdout, "FILE %s LINE %d" JIM_NL,
                 objPtr->internalRep.sourceValue.fileName,
                 objPtr->internalRep.sourceValue.lineNumber);
             }
             objPtr = objPtr->nextObjPtr;
         }
-        printf("-------------------------------------\n\n");
-        Jim_Panic("Live list non empty freeing the interpreter! Leak?");
+        fprintf(stdout, "-------------------------------------" JIM_NL JIM_NL);
+        Jim_Panic(i,"Live list non empty freeing the interpreter! Leak?");
     }
     /* Free all the freed objects. */
     objPtr = i->freeList;
     while (objPtr) {
         nextObjPtr = objPtr->nextObjPtr;
@@ -4294,10 +4308,32 @@
 int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
 {
     return Jim_DeleteHashEntry(&interp->assocData, key);
 }
 
+int Jim_GetExitCode(Jim_Interp *interp) {
+    return interp->exitCode;
+}
+
+FILE *Jim_SetStdin(Jim_Interp *interp, FILE *fp)
+{
+    if (fp != NULL) interp->stdin = fp;
+    return interp->stdin;
+}
+
+FILE *Jim_SetStdout(Jim_Interp *interp, FILE *fp)
+{
+    if (fp != NULL) interp->stdout = fp;
+    return interp->stdout;
+}
+
+FILE *Jim_SetStderr(Jim_Interp *interp, FILE *fp)
+{
+    if (fp != NULL) interp->stderr = fp;
+    return interp->stderr;
+}
+
 /* -----------------------------------------------------------------------------
  * Shared strings.
  * Every interpreter has an hash table where to put shared dynamically
  * allocate strings that are likely to be used a lot of times.
  * For example, in the 'source' object type, there is a pointer to
@@ -4333,11 +4369,11 @@
 {
     long refCount;
     Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str);
 
     if (he == NULL)
-        Jim_Panic("Jim_ReleaseSharedString called with "
+        Jim_Panic(interp,"Jim_ReleaseSharedString called with "
               "unknown shared string '%s'", str);
     refCount = (long) he->val;
     refCount--;
     if (refCount == 0) {
         Jim_DeleteHashEntry(&interp->sharedStrings, str);
@@ -4436,11 +4472,11 @@
 }
 
 void Jim_SetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide wideValue)
 {
     if (Jim_IsShared(objPtr))
-        Jim_Panic("Jim_SetWide called with shared object");
+        Jim_Panic(interp,"Jim_SetWide called with shared object");
     if (objPtr->typePtr != &intObjType) {
         Jim_FreeIntRep(interp, objPtr);
         objPtr->typePtr = &intObjType;
     }
     Jim_InvalidateStringRep(objPtr);
@@ -4516,11 +4552,11 @@
 }
 
 void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double doubleValue)
 {
     if (Jim_IsShared(objPtr))
-        Jim_Panic("Jim_SetDouble called with shared object");
+        Jim_Panic(interp,"Jim_SetDouble called with shared object");
     if (objPtr->typePtr != &doubleObjType) {
         Jim_FreeIntRep(interp, objPtr);
         objPtr->typePtr = &doubleObjType;
     }
     Jim_InvalidateStringRep(objPtr);
@@ -4868,11 +4904,11 @@
     int (*fn)(Jim_Obj**, Jim_Obj**);
     Jim_Obj **vector;
     int len;
 
     if (Jim_IsShared(listObjPtr))
-        Jim_Panic("Jim_ListSortElements called with shared object");
+        Jim_Panic(interp,"Jim_ListSortElements called with shared object");
     if (listObjPtr->typePtr != &listObjType)
         SetListFromAny(interp, listObjPtr);
 
     vector = listObjPtr->internalRep.listValue.ele;
     len = listObjPtr->internalRep.listValue.len;
@@ -4881,11 +4917,11 @@
         case JIM_LSORT_NOCASE: fn = ListSortStringNoCase;  break;
         case JIM_LSORT_ASCII_DECR: fn = ListSortStringDecr;  break;
         case JIM_LSORT_NOCASE_DECR: fn = ListSortStringNoCaseDecr;  break;
         default:
             fn = NULL; /* avoid warning */
-            Jim_Panic("ListSort called with invalid sort type");
+            Jim_Panic(interp,"ListSort called with invalid sort type");
     }
     qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *)fn);
     Jim_InvalidateStringRep(listObjPtr);
 }
 
@@ -4971,21 +5007,21 @@
 }
 
 void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr)
 {
     if (Jim_IsShared(listPtr))
-        Jim_Panic("Jim_ListAppendElement called with shared object");
+        Jim_Panic(interp,"Jim_ListAppendElement called with shared object");
     if (listPtr->typePtr != &listObjType)
         SetListFromAny(interp, listPtr);
     Jim_InvalidateStringRep(listPtr);
     ListAppendElement(listPtr, objPtr);
 }
 
 void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr)
 {
     if (Jim_IsShared(listPtr))
-        Jim_Panic("Jim_ListAppendList called with shared object");
+        Jim_Panic(interp,"Jim_ListAppendList called with shared object");
     if (listPtr->typePtr != &listObjType)
         SetListFromAny(interp, listPtr);
     Jim_InvalidateStringRep(listPtr);
     ListAppendList(listPtr, appendListPtr);
 }
@@ -4999,11 +5035,11 @@
 
 void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int index,
         int objc, Jim_Obj *const *objVec)
 {
     if (Jim_IsShared(listPtr))
-        Jim_Panic("Jim_ListInsertElement called with shared object");
+        Jim_Panic(interp,"Jim_ListInsertElement called with shared object");
     if (listPtr->typePtr != &listObjType)
         SetListFromAny(interp, listPtr);
     if (index >= 0 && index > listPtr->internalRep.listValue.len)
         index = listPtr->internalRep.listValue.len;
     else if (index < 0 ) 
@@ -5433,11 +5469,11 @@
  * If valueObjPtr == NULL, the key is removed if it exists. */
 int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
         Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
 {
     if (Jim_IsShared(objPtr))
-        Jim_Panic("Jim_DictAddElement called with shared object");
+        Jim_Panic(interp,"Jim_DictAddElement called with shared object");
     if (objPtr->typePtr != &dictObjType) {
         if (SetDictFromAny(interp, objPtr) != JIM_OK)
             return JIM_ERR;
     }
     DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
@@ -5449,11 +5485,11 @@
 {
     Jim_Obj *objPtr;
     int i;
 
     if (len % 2)
-        Jim_Panic("Jim_NewDicObj() 'len' argument must be even");
+        Jim_Panic(interp,"Jim_NewDicObj() 'len' argument must be even");
 
     objPtr = Jim_NewObj(interp);
     objPtr->typePtr = &dictObjType;
     objPtr->bytes = NULL;
     objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable));
@@ -5702,10 +5738,12 @@
         returnCode = JIM_BREAK;
     else if (!JimStringCompare(str, strLen, "continue", 8, JIM_NOCASE))
         returnCode = JIM_CONTINUE;
     else if (!JimStringCompare(str, strLen, "eval", 4, JIM_NOCASE))
         returnCode = JIM_EVAL;
+    else if (!JimStringCompare(str, strLen, "exit", 4, JIM_NOCASE))
+        returnCode = JIM_EXIT;
     else {
         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
         Jim_AppendStrings(interp, Jim_GetResult(interp),
                 "expected return code but got '", str, "'",
                 NULL);
@@ -5920,11 +5958,14 @@
     pc->tstart = pc->p;
     pc->tline = pc->linenr;
     if (*pc->p == '-') {
         pc->p++; pc->len--;
     }
-    while (isdigit((int)*pc->p) || (allowdot && *pc->p == '.')) {
+    while (isdigit((int)*pc->p) || (allowdot && *pc->p == '.') ||
+           (pc->p-pc->tstart == 1 && *pc->tstart == '0' &&
+              (*pc->p == 'x' || *pc->p == 'X')))
+    {
         if (*pc->p == '.')
             allowdot = 0;
         pc->p++; pc->len--;
         if (!allowdot && *pc->p == 'e' && *(pc->p+1) == '-') {
             pc->p += 2; pc->len -= 2;
@@ -6110,11 +6151,11 @@
             /* binary operations */
             if (stacklen < 2) return JIM_ERR;
             stacklen--;
             break;
         default:
-            Jim_Panic("Default opcode reached ExprCheckCorrectness");
+            Jim_Panic(NULL,"Default opcode reached ExprCheckCorrectness");
             break;
         }
     }
     if (stacklen != 1) return JIM_ERR;
     return JIM_OK;
@@ -6193,11 +6234,11 @@
             case JIM_EXPROP_STRING:
                 break;
             default:
                 op = JimExprOperatorInfoByOpcode(expr->opcode[i]);
                 if (op == NULL) {
-                    Jim_Panic("Default reached in ExprMakeLazy()");
+                    Jim_Panic(interp,"Default reached in ExprMakeLazy()");
                 }
                 arity += op->arity;
                 break;
             }
             arity--;
@@ -6335,11 +6376,11 @@
                 }
             }
             Jim_Free(token);
             break;
         default:
-            Jim_Panic("Default reached in SetExprFromAny()");
+            Jim_Panic(interp,"Default reached in SetExprFromAny()");
             break;
         }
     }
     while (Jim_StackLen(&stack)) {
         char *opstr = Jim_StackPop(&stack);
@@ -6720,11 +6761,11 @@
             }
             stack[stacklen] = Jim_NewDoubleObj(interp, dC);
             Jim_IncrRefCount(stack[stacklen]);
             stacklen++;
         } else {
-            Jim_Panic("Unknown opcode in Jim_EvalExpression");
+            Jim_Panic(interp,"Unknown opcode in Jim_EvalExpression");
         }
     }
 err:
     /* There is no need to decerement the inUse field because
      * this reference is transfered back into the exprObjPtr. */
@@ -7999,11 +8040,11 @@
             if (retcode != JIM_OK)
                 goto err;
             intv[i] = Jim_GetResult(interp);
             break;
         default:
-            Jim_Panic(
+            Jim_Panic(interp,
               "default token type reached "
               "in Jim_InterpolateTokens().");
             break;
         }
         Jim_IncrRefCount(intv[i]);
@@ -8176,11 +8217,11 @@
                     if (retcode != JIM_OK)
                         goto err;
                     argv[j] = Jim_GetResult(interp);
                     break;
                 default:
-                    Jim_Panic(
+                    Jim_Panic(interp,
                       "default token type reached "
                       "in Jim_EvalObj().");
                     break;
                 }
                 Jim_IncrRefCount(argv[j]);
@@ -8409,11 +8450,11 @@
         objv[1] = Jim_GetResult(interp);
         Jim_IncrRefCount(objv[0]);
         Jim_IncrRefCount(objv[1]);
         if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
             /* Report the error to stderr. */
-            fprintf(stderr, "Background error:\n");
+            fprintf(interp->stderr, "Background error:" JIM_NL);
             Jim_PrintErrorMessage(interp);
         }
         Jim_DecrRefCount(interp, objv[0]);
         Jim_DecrRefCount(interp, objv[1]);
     }
@@ -8648,11 +8689,11 @@
             if (Jim_EvalObj(interp, token[i].objPtr) != JIM_OK)
                 goto err;
             Jim_AppendObj(interp, resObjPtr, interp->result);
             break;
         default:
-            Jim_Panic(
+            Jim_Panic(interp,
               "default token type (%d) reached "
               "in Jim_SubstObj().", token[i].type);
             break;
         }
     }
@@ -8741,10 +8782,14 @@
   JIM_REGISTER_API(GetReference);
   JIM_REGISTER_API(SetFinalizer);
   JIM_REGISTER_API(GetFinalizer);
   JIM_REGISTER_API(CreateInterp);
   JIM_REGISTER_API(FreeInterp);
+  JIM_REGISTER_API(GetExitCode);
+  JIM_REGISTER_API(SetStdin);
+  JIM_REGISTER_API(SetStdout);
+  JIM_REGISTER_API(SetStderr);
   JIM_REGISTER_API(CreateCommand);
   JIM_REGISTER_API(CreateProcedure);
   JIM_REGISTER_API(DeleteCommand);
   JIM_REGISTER_API(RenameCommand);
   JIM_REGISTER_API(GetCommand);
@@ -8938,12 +8983,12 @@
             nonewline = 1;
             argv++;
         }
     }
     str = Jim_GetString(argv[1], &len);
-    fwrite(str, 1, len, stdout);
-    if (!nonewline) printf("\n");
+    fwrite(str, 1, len, interp->stdout);
+    if (!nonewline) fprintf(interp->stdout, JIM_NL);
     return JIM_OK;
 }
 
 /* Helper for [+] and [*] */
 static int Jim_AddMulHelper(Jim_Interp *interp, int argc, 
@@ -8985,13 +9030,16 @@
 {
     jim_wide wideValue, res = 0;
     double doubleValue, doubleRes = 0;
     int i = 2;
 
-    /* The arity = 2 case is different. For [- x] returns -x,
-     * while [/ x] returns 1/x. */
-    if (argc == 2) {
+    if (argc < 2) {
+        Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
+        return JIM_ERR;
+    } else if (argc == 2) {
+        /* The arity = 2 case is different. For [- x] returns -x,
+         * while [/ x] returns 1/x. */
         if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
             if (Jim_GetDouble(interp, argv[1], &doubleValue) !=
                     JIM_OK)
             {
                 return JIM_ERR;
@@ -9216,11 +9264,12 @@
             default:
                 goto noopt;
             }
             break;
         default:
-            Jim_Panic("Unexpected default reached in Jim_WhileCoreCommand()");
+            Jim_Panic(interp,
+                "Unexpected default reached in Jim_WhileCoreCommand()");
             break;
         }
 
         /* STEP 2 -- conditions meet. Initialization. Take different
          * branches for different expression lengths. */
@@ -10791,12 +10840,12 @@
     }
     if (argc == 2) {
         if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
             return JIM_ERR;
     }
-    exit(exitCode);
-    return JIM_OK; /* unreached */
+    interp->exitCode = exitCode;
+    return JIM_EXIT;
 }
 
 /* [catch] */
 static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, 
         Jim_Obj *const *argv)
@@ -11667,13 +11716,14 @@
  * ---------------------------------------------------------------------------*/
 void Jim_PrintErrorMessage(Jim_Interp *interp)
 {
     int len, i;
 
-    fprintf(stderr, "Runtime error, file \"%s\", line %d:\n",
+    fprintf(interp->stderr, "Runtime error, file \"%s\", line %d:" JIM_NL,
             interp->errorFileName, interp->errorLine);
-    fprintf(stderr, "    %s\n", Jim_GetString(interp->result, NULL));
+    fprintf(interp->stderr, "    %s" JIM_NL,
+            Jim_GetString(interp->result, NULL));
     Jim_ListLength(interp, interp->stackTrace, &len);
     for (i = 0; i < len; i+= 3) {
         Jim_Obj *objPtr;
         const char *proc, *file, *line;
 
@@ -11683,68 +11733,73 @@
                 JIM_NONE);
         file = Jim_GetString(objPtr, NULL);
         Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
                 JIM_NONE);
         line = Jim_GetString(objPtr, NULL);
-        fprintf(stderr, "In procedure '%s' called at file \"%s\", line %s\n",
+        fprintf(interp->stderr,
+                "In procedure '%s' called at file \"%s\", line %s" JIM_NL,
                 proc, file, line);
     }
 }
 
 int Jim_InteractivePrompt(Jim_Interp *interp)
 {
     int retcode = JIM_OK;
     Jim_Obj *scriptObjPtr;
 
-    printf("Welcome to Jim version %d.%d, "
-           "Copyright (c) 2005 Salvatore Sanfilippo\n",
+    fprintf(interp->stdout, "Welcome to Jim version %d.%d, "
+           "Copyright (c) 2005 Salvatore Sanfilippo" JIM_NL,
            JIM_VERSION / 100, JIM_VERSION % 100);
-    printf("CVS ID: $Id: jim.c,v 1.1 2006/07/18 16:37:24 jlarmour Exp $\n");
+    fprintf(interp->stdout,
+            "CVS ID: $Id: jim.c,v 1.170 2006/11/06 21:48:57 antirez Exp $"
+            JIM_NL);
     Jim_SetVariableStrWithStr(interp, "jim_interactive", "1");
     while (1) {
         char buf[1024];
         const char *result;
         const char *retcodestr[] = {
-            "ok", "error", "return", "break", "continue", "eval"
+            "ok", "error", "return", "break", "continue", "eval", "exit"
         };
         int reslen;
 
         if (retcode != 0) {
-            if (retcode >= 2 && retcode <= 5)
-                printf("[%s] . ", retcodestr[retcode]);
+            if (retcode >= 2 && retcode <= 6)
+                fprintf(interp->stdout, "[%s] . ", retcodestr[retcode]);
             else
-                printf("[%d] . ", retcode);
+                fprintf(interp->stdout, "[%d] . ", retcode);
         } else
-            printf(". ");
-        fflush(stdout);
+            fprintf(interp->stdout, ". ");
+        fflush(interp->stdout);
         scriptObjPtr = Jim_NewStringObj(interp, "", 0);
         Jim_IncrRefCount(scriptObjPtr);
         while(1) {
             const char *str;
             char state;
             int len;
 
-            if (fgets(buf, 1024, stdin) == NULL) {
+            if (fgets(buf, 1024, interp->stdin) == NULL) {
                 Jim_DecrRefCount(interp, scriptObjPtr);
                 goto out;
             }
             Jim_AppendString(interp, scriptObjPtr, buf, -1);
             str = Jim_GetString(scriptObjPtr, &len);
             if (Jim_ScriptIsComplete(str, len, &state))
                 break;
-            printf("%c> ", state);
+            fprintf(interp->stdout, "%c> ", state);
             fflush(stdout);
         }
         retcode = Jim_EvalObj(interp, scriptObjPtr);
         Jim_DecrRefCount(interp, scriptObjPtr);
         result = Jim_GetString(Jim_GetResult(interp), &reslen);
         if (retcode == JIM_ERR) {
             Jim_PrintErrorMessage(interp);
+        } else if (retcode == JIM_EXIT) {
+            exit(Jim_GetExitCode(interp));
         } else {
             if (reslen) {
-                fwrite(result, 1, reslen, stdout);
-                printf("\n");
+                fwrite(result, 1, reslen, interp->stdout);
+                fprintf(interp->stdout, JIM_NL);
             }
         }
     }
 out:
     return 0;
diff -r -U 5 -N -x CVS -x '*~' -x '.#~' /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/socket.c /home/atonizzo/ecos/devo/packages/net/athttpd/current/src/socket.c
--- /home/atonizzo/ecos/clean/packages/net/athttpd/current/src/socket.c	2006-08-10 10:37:48.000000000 -0700
+++ /home/atonizzo/ecos/devo/packages/net/athttpd/current/src/socket.c	2006-11-08 08:59:11.000000000 -0800
@@ -41,11 +41,12 @@
  * ####ECOSGPLCOPYRIGHTEND####
  * =================================================================
  * #####DESCRIPTIONBEGIN####
  * 
  *  Author(s):    Anthony Tonizzo (atonizzo@gmail.com)
- *  Contributors: Sergei Gavrikov (w3sg@SoftHome.net)
+ *  Contributors: Sergei Gavrikov (w3sg@SoftHome.net), 
+ *                Lars Povlsen    (lpovlsen@vitesse.com)
  *  Date:         2006-06-12
  *  Purpose:      
  *  Description:  
  *               
  * ####DESCRIPTIONEND####
@@ -83,11 +84,10 @@
     // We are not going to write anything in case
     ssize_t sent = send(httpstate.sockets[httpstate.client_index].descriptor, 
                         buf, 
                         buf_len,
                         0);
-    CYG_ASSERT(sent == buf_len, "send() did not send out all bytes");
     return sent;
 }
 
 __inline__ ssize_t
 cyg_httpd_writev(cyg_iovec *iovec_bufs, int count)
@@ -97,11 +97,16 @@
                           iovec_bufs, 
                           count);
     ssize_t buf_len = 0;
     for (i = 0; i < count; i++)
         buf_len += iovec_bufs[i].iov_len;
-    CYG_ASSERT(sent == buf_len, "writev() did not send out all bytes");
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
+    if (sent != buf_len)
+        diag_printf("writev() did not send out all bytes (%ld of %ld)\n", 
+                    sent,
+                    buf_len);
+#endif    
     return sent;
 }
     
 // The need for chunked transfers arises from the fact that with dinamic
 //  pages it is not always possible to know the packet size upfront, and thus
@@ -123,28 +128,31 @@
 // -----------------------------------------------------------------------------
 ssize_t
 cyg_httpd_start_chunked(char *extension)
 {
     httpstate.status_code = CYG_HTTPD_STATUS_OK;
-    httpstate.mode        |= CYG_HTTPD_MODE_TRANSFER_CHUNKED;
 
-    // I am not really sure that this is necessary, but even if it isn't, the
+#if defined(CYGOPT_NET_ATHTTPD_CLOSE_CHUNKED_CONNECTIONS)
+     // I am not really sure that this is necessary, but even if it isn't, the
     //  added overhead is not such a big deal. In simple terms, I am not sure 
-    //  how much I can rely the client to understand that the frame has ended 
+    //  how much I can rely on the client to understand that the frame has ended 
     //  with the last 5 bytes sent out. In an ideal world, the data '0\r\n\r\n'
-    //  should be enough, but several posting I read seem to imply otherwise,
-    //  at least with early generation browsers that supported the
-    //  "Transfer-Encoding: chunked" mechanism. Things might be getting better
-    //  now but I snooped some sites that use the chunked stuff (Yahoo! for one)
-    /// and all of them with no exception issue a "Connection: close" on 
-    //  chunked frames even if there is nothing in the HTTP 1.1 spec that
+    //  should be enough, but several posting on the subject I read seem to
+    //  imply otherwise, at least with early generation browsers that supported
+    //  the "Transfer-Encoding: chunked" mechanism. Things might be getting 
+    //  better now but I snooped some sites that use the chunked stuff (Yahoo!
+    //  for one) and all of them with no exception issue a "Connection: close" 
+    //  on chunked frames even if there is nothing in the HTTP 1.1 spec that
     //  requires it.
-    httpstate.mode        |= CYG_HTTPD_MODE_CLOSE_CONN;
+    httpstate.mode |= CYG_HTTPD_MODE_CLOSE_CONN;
+#endif
+    
+    // We do not cache chunked frames. In case they are used to display dynamic
+    //  data we want them to be executed any every time they are requested.
+    httpstate.mode |= 
+              (CYG_HTTPD_MODE_TRANSFER_CHUNKED | CYG_HTTPD_MODE_NO_CACHE);
     
-    // We do not cache the CGI script. In case they are used to display
-    //  dynamic data we want them to be executed any every time they are 
-    //  requested.
     httpstate.last_modified = -1;
     httpstate.mime_type = cyg_httpd_find_mime_string(extension);
     cyg_int32 header_length = cyg_httpd_format_header();
     return cyg_httpd_write(httpstate.outbuffer, header_length);
 }
@@ -157,20 +165,20 @@
     cyg_iovec iovec_bufs[] = { {leader, 0}, {buf, 0}, {trailer, 2} };
     sprintf(leader, "%x\r\n", len);
     iovec_bufs[0].iov_len = strlen(leader);
     iovec_bufs[1].iov_len = len;
     iovec_bufs[2].iov_len = 2;
-    if (httpstate.mode & CYG_HTTPD_SEND_HEADER_ONLY)
+    if (httpstate.mode & CYG_HTTPD_MODE_SEND_HEADER_ONLY)
         return (iovec_bufs[0].iov_len + iovec_bufs[1].iov_len + 
                                                   iovec_bufs[2].iov_len);
     return cyg_httpd_writev(iovec_bufs, 3);
 }
 
 void
 cyg_httpd_end_chunked(void)
 {
-    if (httpstate.mode & CYG_HTTPD_SEND_HEADER_ONLY)
+    if (httpstate.mode & CYG_HTTPD_MODE_SEND_HEADER_ONLY)
         return;
     strcpy(httpstate.outbuffer, "0\r\n\r\n");
     cyg_httpd_write(httpstate.outbuffer, 5);
     httpstate.mode &= ~CYG_HTTPD_MODE_TRANSFER_CHUNKED;
 }    
@@ -186,97 +194,106 @@
 //  be used whenever possible.
 void
 cyg_httpd_create_std_header(char *extension, int len)
 {
     httpstate.status_code = CYG_HTTPD_STATUS_OK;
-    httpstate.mode        = 0;
+    httpstate.mode |= CYG_HTTPD_MODE_NO_CACHE;
 
     // We do not want to send out a "Last-Modified:" field for c language
     //  callbacks.
     httpstate.last_modified = -1;
     httpstate.mime_type = cyg_httpd_find_mime_string(extension);
     httpstate.payload_len = len;
     cyg_int32 header_length = cyg_httpd_format_header();
     cyg_httpd_write(httpstate.outbuffer, header_length);
 }
 
-void 
+void
 cyg_httpd_process_request(cyg_int32 index)
 {
-    int rc = 1;
     httpstate.client_index = index;
     cyg_int32 descr = httpstate.sockets[index].descriptor;
-    fd_set rfds_tmp;
-    FD_ZERO( &rfds_tmp);
-    FD_SET( descr, &rfds_tmp);
-    while (FD_ISSET(descr, &rfds_tmp))
-    {
-        cyg_int32 n = recv(descr, httpstate.inbuffer, CYG_HTTPD_MAXINBUFFER, 0);
-        if (n == 0)
+    
+    // By placing a terminating '\0' not only we have a safe stopper point
+    //  for our parsing, but also we can detect if we have a split header.
+    // Since headers always end with an extra '\r\n', if we find a '\0'
+    //  before the terminator than we can safely assume that the header has
+    //  not been received completely and more is following (i.e. split headers.)
+    httpstate.inbuffer[0] = '\0';
+    httpstate.inbuffer_len = 0;
+    while ((strstr(httpstate.inbuffer, "\r\n\r\n") == 0) &&
+                (strstr(httpstate.inbuffer, "\n\n") == 0))
+    {            
+        int len = recv(descr,
+                       httpstate.inbuffer + httpstate.inbuffer_len,
+                       CYG_HTTPD_MAXINBUFFER - httpstate.inbuffer_len,
+                       0);
+        if (len == 0)
         {
             // This is the client that has closed its TX socket, possibly as
             //  a response from a shutdown() initiated by the server. Another
             //  possibility is that the client was closed altogether, in
-            //  which case EOFs are sent on each open sockets.
-#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
-            printf("EOF received on descriptor: %d. Closing it.\n", descr);
-#endif    
+            //  which case the client sent EOFs on each open sockets before 
+            //  dying.
             close(descr);
             FD_CLR(descr, &httpstate.rfds);
             httpstate.sockets[index].descriptor = 0;
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
+            printf("EOF received on descriptor: %d. Closing it.\n", descr);
+#endif    
             return;
         }    
         
-        if (n < 0)
+        if (len < 0)
         {
-            diag_printf("ERROR reading from socket. read() returned: %d\n", n);
+            // There was an error reading from this socket. Play it safe and
+            //  close it. This will force the client to generate a shutdown
+            //  and we will read a len = 0 the next time around.
+            shutdown(descr, SHUT_WR);
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
+            diag_printf("ERROR reading from socket. read() returned: %d\n", 
+                        httpstate.inbuffer_len);
+#endif    
             return;
         }  
-        httpstate.inbuffer[n] = '\0';
-      
-        // Timestamp the socket. 
-        httpstate.sockets[index].timestamp = time(NULL);
+    
+        httpstate.inbuffer_len += len;
+    }
+    
+    httpstate.inbuffer[httpstate.inbuffer_len] = '\0';
+
+    // Timestamp the socket. 
+    httpstate.sockets[index].timestamp = time(NULL);
         
-        // This is where it all happens.
-        cyg_httpd_process_method();
+    // This is where it all happens.
+    cyg_httpd_process_method();
         
-        if (httpstate.mode & CYG_HTTPD_MODE_CLOSE_CONN)
-            // There are 2 cases we can be here:
-            // 1) chunked frames close their connection by default
-            // 2) The client requested the connection be terminated with a
-            //     "Connection: close" in the header
-            // In any case, we close the TX pipe and wait for the client to
-            //  send us an EOF on the receive pipe. This is a more graceful way
-            //  to handle the closing of the socket, compared to just calling
-            //  close() without first asking the opinion of the client, and 
-            //  running the risk of stray data lingering around.
-            shutdown(descr, SHUT_WR);
-        
-        // Now check if we have pipelined frames. We'll set up a 
-        //  select() to see if more data is waiting to be read. Pipeline is
-        //  still not enabled by default in most browsers.
-        struct timeval tv_tmp = {0, 20000};
-        rc = select(descr + 1, &rfds_tmp, NULL, NULL, &tv_tmp);
-        CYG_ASSERT(rc != -1, "select() return -1");
-        if (rc == 0)
-            return;
-    }
+    if (httpstate.mode & CYG_HTTPD_MODE_CLOSE_CONN)
+        // There are 2 cases we can be here:
+        // 1) chunked frames close their connection by default
+        // 2) The client requested the connection be terminated with a
+        //     "Connection: close" in the header
+        // In any case, we close the TX pipe and wait for the client to
+        //  send us an EOF on the receive pipe. This is a more graceful way
+        //  to handle the closing of the socket, compared to just calling
+        //  close() without first asking the opinion of the client, and 
+        //  running the risk of stray data lingering around.
+        shutdown(descr, SHUT_WR);
 }
 
 void
 cyg_httpd_handle_new_connection(cyg_int32 listener)
 {
     cyg_int32 i;
 
-
     int fd_client = accept(listener, NULL, NULL);
     CYG_ASSERT(listener != -1, "accept() failed");
     if (fd_client == -1) 
         return;
     
 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
-    printf("Opening descriptor: %d\n", fd_client);
+    diag_printf("Opening descriptor: %d\n", fd_client);
 #endif    
     // Timestamp the socket and process the frame immediately, since the accept
     //  guarantees the presence of valid data on the newly opened socket.
     for (i = 0; i < CYGPKG_NET_MAXSOCKETS; i++)
         if (httpstate.sockets[i].descriptor == 0)
@@ -371,11 +388,13 @@
     rc = listen(listener, SOMAXCONN);
     CYG_ASSERT(rc == 0, "listen() returned error");
     if (rc != 0)
         return;
 
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
     diag_printf("Web server Started and listening...\n");
+#endif
     cyg_int32 i;
     for (i = 0; i < CYGNUM_FILEIO_NFILE; i++)
     {
         httpstate.sockets[i].descriptor  = 0;
         httpstate.sockets[i].timestamp   = (time_t)0;
@@ -415,11 +434,12 @@
                     //  descriptors that must be listened for.
                     if (FD_ISSET(descr, &httpstate.rfds))
                         cyg_httpd_process_request(i);
                     else       
                         FD_SET(descr, &httpstate.rfds); 
-                    httpstate.fdmax = MAX(httpstate.fdmax, descr);
+                    if (httpstate.sockets[i].descriptor != 0)
+                        httpstate.fdmax = MAX(httpstate.fdmax, descr);
                 }
             }
         }
         else if (rc == 0)
         {
@@ -427,11 +447,11 @@
         }
         else
         {
 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 0
             cyg_int8 *ptr = (cyg_int8*)&httpstate.rfds;
-            printf("rfds: %x %x %x %x\n", ptr[0], ptr[1], ptr[2], ptr[3] );
+            diag_printf("rfds: %x %x %x %x\n", ptr[0], ptr[1], ptr[2], ptr[3] );
             for (i = 0; i < CYGPKG_NET_MAXSOCKETS; i++)
                 if (httpstate.sockets[i].descriptor != 0)
                      diag_printf("Socket in list: %d\n", 
                                  httpstate.sockets[i].descriptor);
 #endif                                 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]