Mercurial > nand-ecoscentric
changeset 2326:bd648d8929f6
* cdl/httpd.cdl: Improve CDL dependencies.
* 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'
* 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.
| author | jlarmour |
|---|---|
| date | Mon, 27 Nov 2006 15:41:55 +0000 |
| parents | 29ca0a2d4a0f |
| children | 540c8c9cd3d7 |
| files | packages/net/athttpd/current/ChangeLog packages/net/athttpd/current/cdl/httpd.cdl packages/net/athttpd/current/doc/athttpd.sgml packages/net/athttpd/current/include/http.h packages/net/athttpd/current/include/jim.h packages/net/athttpd/current/src/cgi.c packages/net/athttpd/current/src/forms.c packages/net/athttpd/current/src/handler.c packages/net/athttpd/current/src/http.c packages/net/athttpd/current/src/jim-aio.c packages/net/athttpd/current/src/jim.c packages/net/athttpd/current/src/socket.c |
| diffstat | 12 files changed, 1159 insertions(+), 543 deletions(-) [+] |
line wrap: on
line diff
--- a/packages/net/athttpd/current/ChangeLog +++ b/packages/net/athttpd/current/ChangeLog @@ -1,3 +1,52 @@ +2006-11-27 Jonathan Larmour <jifl@eCosCentric.com> + + * cdl/httpd.cdl: Improve CDL dependencies. + +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: @@ -5,15 +54,15 @@ 2006-07-19 Anthony Tonizzo <atonizzo@g * 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 + 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. - Modified slightly the source of cyg_httpd_write and cyg_httpd_writev - to make the code more consistent as to when assertions are thrown. + 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> @@ -59,7 +108,7 @@ 2006-06-13 Anthony Tonizzo <atonizzo@g //####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. -// Copyright (C) 2005 eCosCentric Ltd. +// Copyright (C) 2005, 2006 eCosCentric Ltd. // // eCos is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free
--- a/packages/net/athttpd/current/cdl/httpd.cdl +++ b/packages/net/athttpd/current/cdl/httpd.cdl @@ -39,7 +39,7 @@ ######DESCRIPTIONBEGIN#### # # Author(s): Anthony Tonizzo (atonizzo@gmail.com) -# Contributors: +# Contributors: Lars Povlsen (lpovlsen@vitesse.com) # Date: 2006-06-09 # #####DESCRIPTIONEND#### @@ -54,7 +54,18 @@ cdl_package CYGPKG_ATHTTPD { include_files auth.h digcalc.h forms.h global.h handler.h http.h md5.h socket.h cgi.h jim.h compile forms.c handler.c http.c socket.c cgi.c requires CYGPKG_NET + requires CYGPKG_NET_STACK_INET requires !CYGPKG_HTTPD + requires CYGINT_ISO_STDIO_STREAMS + requires CYGINT_ISO_STDIO_FILEACCESS + requires CYGINT_ISO_STDIO_FORMATTED_IO + requires CYGINT_ISO_STRING_MEMFUNCS + requires CYGINT_ISO_STRING_STRFUNCS + requires CYGINT_ISO_STRING_BSD_FUNCS + requires CYGINT_ISO_C_CLOCK_FUNCS + requires CYGINT_ISO_MALLOC + requires CYGINT_ISO_CTYPE + # ==================================================================== cdl_component CYGPKG_NET_ATHTTPD_THREADOPT { @@ -118,6 +129,14 @@ cdl_package CYGPKG_ATHTTPD { 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 @@ -185,6 +204,7 @@ cdl_package CYGPKG_ATHTTPD { flavor bool default_value 1 active_if CYGPKG_IO_FILEIO + requires description " This option enables the use of a file system to access the pages to be served. It is the responsibility of the user to properly @@ -205,6 +225,36 @@ cdl_package CYGPKG_ATHTTPD { 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 @@ -242,9 +292,9 @@ cdl_package CYGPKG_ATHTTPD { 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 } }
--- a/packages/net/athttpd/current/doc/athttpd.sgml +++ b/packages/net/athttpd/current/doc/athttpd.sgml @@ -85,7 +85,7 @@ 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> @@ -155,12 +155,6 @@ contains, among others, a buffer (outbuf 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> @@ -179,7 +173,6 @@ cyg_int32 myForm(CYG_HTTPS_STATE* p) 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> @@ -459,12 +452,21 @@ resources (gif files) which are also com <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 @@ -496,18 +498,22 @@ for "foo" and "bar" (if they were found 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)
--- a/packages/net/athttpd/current/include/http.h +++ b/packages/net/athttpd/current/include/http.h @@ -69,7 +69,7 @@ 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; @@ -87,6 +87,7 @@ typedef enum cyg_httpd_req_type #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 @@ -98,13 +99,17 @@ typedef enum cyg_httpd_req_type #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; @@ -122,19 +127,25 @@ typedef struct 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. @@ -142,7 +153,7 @@ typedef struct char *mime_type; cyg_int32 payload_len; char outbuffer[CYG_HTTPD_MAXOUTBUFFER+1]; - + socket_entry sockets[CYGNUM_FILEIO_NFILE]; cyg_int32 fdmax; @@ -156,6 +167,15 @@ typedef struct 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. @@ -184,17 +204,15 @@ cyg_httpd_mime_table_entry __name CYG_HA 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);
--- a/packages/net/athttpd/current/include/jim.h +++ b/packages/net/athttpd/current/include/jim.h @@ -2,7 +2,7 @@ * 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. @@ -29,6 +29,7 @@ extern "C" { #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 */ /* ----------------------------------------------------------------------------- @@ -117,6 +118,7 @@ extern "C" { #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 @@ -139,6 +141,13 @@ extern "C" { /* 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 * ---------------------------------------------------------------------------*/ @@ -442,6 +451,7 @@ typedef struct Jim_Interp { 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 */ @@ -481,6 +491,9 @@ typedef struct Jim_Interp { 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. @@ -638,6 +651,10 @@ JIM_STATIC int JIM_API(Jim_GetFinalizer) /* 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); @@ -786,7 +803,7 @@ JIM_STATIC void JIM_API(Jim_PrintErrorMe 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 @@ -798,7 +815,7 @@ JIM_STATIC void JIM_API(Jim_Panic) (cons #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; @@ -845,6 +862,10 @@ static __inline__ void Jim_InitExtension 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); @@ -916,14 +937,14 @@ static __inline__ void Jim_InitExtension #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
--- a/packages/net/athttpd/current/src/cgi.c +++ b/packages/net/athttpd/current/src/cgi.c @@ -62,6 +62,7 @@ #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> @@ -160,41 +161,14 @@ cyg_int32 cyg_httpd_exec_cgi_objloader(c // 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) { @@ -204,51 +178,54 @@ cyg_int32 cyg_httpd_exec_cgi_tcl(char *f 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) +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 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, @@ -272,8 +249,8 @@ void cyg_httpd_init_tcl_interpreter(void // ============================================================================= // 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) {
--- a/packages/net/athttpd/current/src/forms.c +++ b/packages/net/athttpd/current/src/forms.c @@ -44,6 +44,7 @@ * * Author(s): Anthony Tonizzo (atonizzo@gmail.com) * Contributors: Sergei Gavrikov (w3sg@SoftHome.net) + * Lars Povlsen (lpovlsen@vitesse.com) * Date: 2006-06-12 * Purpose: * Description: @@ -136,9 +137,13 @@ cyg_httpd_store_form_data(char *p) 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) @@ -151,23 +156,20 @@ cyg_httpd_store_form_data(char *p) 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++; } @@ -192,28 +194,71 @@ cyg_httpd_find_form_variable(char *p) 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; - char *cp = httpstate.inbuffer; - while ((*cp == '\r') || (*cp == '\n')) - cp++; - cp[httpstate.inbuffer_len] = ' '; - cyg_httpd_store_form_data(cp); + // 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'; + + // 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; } @@ -232,12 +277,17 @@ cyg_httpd_handle_method_POST(void) // 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; }
--- a/packages/net/athttpd/current/src/handler.c +++ b/packages/net/athttpd/current/src/handler.c @@ -367,7 +367,7 @@ cyg_httpd_send_ires(cyg_httpd_ires_table 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); } @@ -387,7 +387,7 @@ 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++; }
--- a/packages/net/athttpd/current/src/http.c +++ b/packages/net/athttpd/current/src/http.c @@ -44,6 +44,7 @@ * * Author(s): Anthony Tonizzo (atonizzo@gmail.com) * Contributors: Sergei Gavrikov (w3sg@SoftHome.net) + * Lars Povlsen (lpovlsen@vitesse.com) * Date: 2006-06-12 * Purpose: * Description: @@ -77,12 +78,12 @@ 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); @@ -97,32 +98,17 @@ CYG_HTTPD_MIME_TABLE_ENTRY(hal_html_entr "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); @@ -180,64 +166,84 @@ cyg_httpd_send_error(cyg_int32 err_type) 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], @@ -245,17 +251,11 @@ cyg_httpd_send_error(cyg_int32 err_type) 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) { @@ -265,8 +265,8 @@ cyg_httpd_parse_date(char *time) // 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++; @@ -293,7 +293,8 @@ cyg_httpd_parse_date(char *time) 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, @@ -328,7 +329,7 @@ cyg_httpd_find_mime_string(char *ext) 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++; } @@ -339,9 +340,7 @@ cyg_httpd_find_mime_string(char *ext) 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); @@ -367,11 +366,12 @@ cyg_httpd_cleanup_filename(char *filenam 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; } @@ -411,9 +411,6 @@ cyg_httpd_send_file(char *name) 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. @@ -499,10 +496,10 @@ cyg_httpd_send_file(char *name) } 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; @@ -520,9 +517,13 @@ cyg_httpd_send_file(char *name) 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, @@ -530,8 +531,11 @@ cyg_httpd_send_file(char *name) 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); @@ -545,32 +549,13 @@ cyg_httpd_send_file(char *name) 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) @@ -581,7 +566,6 @@ cyg_httpd_send_file(char *name) 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); @@ -594,7 +578,15 @@ cyg_httpd_format_header(void) 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); @@ -619,7 +611,10 @@ cyg_httpd_format_header(void) 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), @@ -628,25 +623,21 @@ cyg_httpd_format_header(void) 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"); @@ -657,42 +648,54 @@ cyg_httpd_format_header(void) break; } - cyg_httpd_format_time_string(date, &time_val); + 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), - "Date: %s\r\n", - date); - 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, @@ -705,21 +708,12 @@ cyg_httpd_format_header(void) 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 @@ -783,7 +777,7 @@ cyg_httpd_get_URL(char* 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 == '%') @@ -801,123 +795,116 @@ cyg_httpd_get_URL(char* p) // 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); + // 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 past white spaces. - while (*p == ' ') - p++; - return p; + // 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", @@ -925,19 +912,15 @@ cyg_httpd_process_header(void) &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) @@ -945,7 +928,7 @@ cyg_httpd_process_header(void) p += 14; while (*p == ' ') p++; - if (!strncasecmp("Basic", p, 5)) + if (strncasecmp("Basic", p, 5) == 0) { char *cr = cyg_httpd_md5_digest; p += 5; @@ -957,7 +940,6 @@ cyg_httpd_process_header(void) } else if (strncasecmp(p, "Digest", 6) == 0) { - diag_printf(httpstate.inbuffer); p += 6; while (*p == ' ') p++; @@ -988,11 +970,7 @@ cyg_httpd_process_header(void) } } else - { - while (*p != '\n') - p++; - p++; - } + while (*p++ != '\n'); } #endif // CYGOPT_NET_ATHTTPD_USE_AUTH else if (strncasecmp(p, "Connection:", 11) == 0) @@ -1002,41 +980,76 @@ cyg_httpd_process_header(void) 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; + } + } }
new file mode 100644 --- /dev/null +++ b/packages/net/athttpd/current/src/jim-aio.c @@ -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; +}
--- a/packages/net/athttpd/current/src/jim.c +++ b/packages/net/athttpd/current/src/jim.c @@ -2,7 +2,7 @@ * 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. @@ -20,11 +20,11 @@ * 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 */ @@ -450,14 +450,19 @@ static jim_wide JimPowWide(jim_wide b, j /* ----------------------------------------------------------------------------- * 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 { @@ -468,9 +473,9 @@ void Jim_Panic(const char *fmt, ...) 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(); @@ -493,7 +498,7 @@ 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; } @@ -505,7 +510,7 @@ 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; } @@ -1406,7 +1411,7 @@ int JimParseComment(struct JimParserCtx /* 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; @@ -1748,7 +1753,7 @@ void Jim_FreeObj(Jim_Interp *interp, Jim { /* 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 */ @@ -1835,7 +1840,7 @@ const char *Jim_GetString(Jim_Obj *objPt 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); @@ -1883,8 +1888,7 @@ void DupStringInternalRep(Jim_Interp *in 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. */ @@ -1966,7 +1970,7 @@ void Jim_AppendString(Jim_Interp *interp 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); @@ -2330,9 +2334,9 @@ static void JimSetSourceInfo(Jim_Interp 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; @@ -2698,7 +2702,8 @@ int SetScriptFromAny(Jim_Interp *interp, 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) @@ -2727,7 +2732,8 @@ int SetScriptFromAny(Jim_Interp *interp, 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++; } @@ -2921,11 +2927,18 @@ int Jim_CreateProcedure(Jim_Interp *inte } /* 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: @@ -3525,10 +3538,6 @@ Jim_Obj *Jim_ExpandDictSugar(Jim_Interp 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: @@ -3885,7 +3894,8 @@ int Jim_Collect(Jim_Interp *interp) 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 @@ -3923,7 +3933,7 @@ int Jim_Collect(Jim_Interp *interp) * 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; } @@ -3943,7 +3953,7 @@ int Jim_Collect(Jim_Interp *interp) * 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 @@ -4020,6 +4030,7 @@ Jim_Interp *Jim_CreateInterp(void) 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; @@ -4030,6 +4041,9 @@ Jim_Interp *Jim_CreateInterp(void) 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 @@ -4098,24 +4112,24 @@ void Jim_FreeInterp(Jim_Interp *i) 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; @@ -4296,6 +4310,28 @@ int Jim_DeleteAssocData(Jim_Interp *inte 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 @@ -4335,7 +4371,7 @@ void Jim_ReleaseSharedString(Jim_Interp 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--; @@ -4438,7 +4474,7 @@ int Jim_GetLong(Jim_Interp *interp, Jim_ 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; @@ -4518,7 +4554,7 @@ int Jim_GetDouble(Jim_Interp *interp, Ji 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; @@ -4870,7 +4906,7 @@ static void ListSortElements(Jim_Interp 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); @@ -4883,7 +4919,7 @@ static void ListSortElements(Jim_Interp 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); @@ -4973,7 +5009,7 @@ void ListAppendList(Jim_Obj *listPtr, Ji 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); @@ -4983,7 +5019,7 @@ void Jim_ListAppendElement(Jim_Interp *i 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); @@ -5001,7 +5037,7 @@ void Jim_ListInsertElements(Jim_Interp * 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) @@ -5435,7 +5471,7 @@ int Jim_DictAddElement(Jim_Interp *inter 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; @@ -5451,7 +5487,7 @@ Jim_Obj *Jim_NewDictObj(Jim_Interp *inte 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; @@ -5704,6 +5740,8 @@ int SetReturnCodeFromAny(Jim_Interp *int 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), @@ -5922,7 +5960,10 @@ int JimParseExprNumber(struct JimParserC 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--; @@ -6112,7 +6153,7 @@ static int ExprCheckCorrectness(ExprByte stacklen--; break; default: - Jim_Panic("Default opcode reached ExprCheckCorrectness"); + Jim_Panic(NULL,"Default opcode reached ExprCheckCorrectness"); break; } } @@ -6195,7 +6236,7 @@ static void ExprMakeLazy(Jim_Interp *int 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; @@ -6337,7 +6378,7 @@ int SetExprFromAny(Jim_Interp *interp, s Jim_Free(token); break; default: - Jim_Panic("Default reached in SetExprFromAny()"); + Jim_Panic(interp,"Default reached in SetExprFromAny()"); break; } } @@ -6722,7 +6763,7 @@ trydouble_unary: Jim_IncrRefCount(stack[stacklen]); stacklen++; } else { - Jim_Panic("Unknown opcode in Jim_EvalExpression"); + Jim_Panic(interp,"Unknown opcode in Jim_EvalExpression"); } } err: @@ -8001,7 +8042,7 @@ int Jim_InterpolateTokens(Jim_Interp *in intv[i] = Jim_GetResult(interp); break; default: - Jim_Panic( + Jim_Panic(interp, "default token type reached " "in Jim_InterpolateTokens()."); break; @@ -8178,7 +8219,7 @@ int Jim_EvalObj(Jim_Interp *interp, Jim_ argv[j] = Jim_GetResult(interp); break; default: - Jim_Panic( + Jim_Panic(interp, "default token type reached " "in Jim_EvalObj()."); break; @@ -8411,7 +8452,7 @@ int Jim_EvalObjBackground(Jim_Interp *in 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]); @@ -8650,7 +8691,7 @@ int Jim_SubstObj(Jim_Interp *interp, Jim 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; @@ -8743,6 +8784,10 @@ void JimRegisterCoreApi(Jim_Interp *inte 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); @@ -8940,8 +8985,8 @@ static int Jim_PutsCoreCommand(Jim_Inter } } 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; } @@ -8987,9 +9032,12 @@ static int Jim_SubDivHelper(Jim_Interp * 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) @@ -9218,7 +9266,8 @@ static int Jim_WhileCoreCommand(Jim_Inte } break; default: - Jim_Panic("Unexpected default reached in Jim_WhileCoreCommand()"); + Jim_Panic(interp, + "Unexpected default reached in Jim_WhileCoreCommand()"); break; } @@ -10793,8 +10842,8 @@ static int Jim_ExitCoreCommand(Jim_Inter 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] */ @@ -11669,9 +11718,10 @@ void Jim_PrintErrorMessage(Jim_Interp *i { 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; @@ -11685,7 +11735,8 @@ void Jim_PrintErrorMessage(Jim_Interp *i 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); } } @@ -11695,27 +11746,29 @@ int Jim_InteractivePrompt(Jim_Interp *in 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) { @@ -11723,7 +11776,7 @@ int Jim_InteractivePrompt(Jim_Interp *in char state; int len; - if (fgets(buf, 1024, stdin) == NULL) { + if (fgets(buf, 1024, interp->stdin) == NULL) { Jim_DecrRefCount(interp, scriptObjPtr); goto out; } @@ -11731,7 +11784,7 @@ int Jim_InteractivePrompt(Jim_Interp *in 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); @@ -11739,10 +11792,12 @@ int Jim_InteractivePrompt(Jim_Interp *in 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); } } }
--- a/packages/net/athttpd/current/src/socket.c +++ b/packages/net/athttpd/current/src/socket.c @@ -43,7 +43,8 @@ * #####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: @@ -85,7 +86,6 @@ cyg_httpd_write(char* buf, int buf_len) buf, buf_len, 0); - CYG_ASSERT(sent == buf_len, "send() did not send out all bytes"); return sent; } @@ -99,7 +99,12 @@ cyg_httpd_writev(cyg_iovec *iovec_bufs, 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; } @@ -125,24 +130,27 @@ 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 the CGI script. In case they are used to display - // dynamic data we want them to be executed any every time they are - // requested. + // 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); + httpstate.last_modified = -1; httpstate.mime_type = cyg_httpd_find_mime_string(extension); cyg_int32 header_length = cyg_httpd_format_header(); @@ -159,7 +167,7 @@ cyg_httpd_write_chunked(char* buf, int l 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); @@ -168,7 +176,7 @@ cyg_httpd_write_chunked(char* buf, int l 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); @@ -188,7 +196,7 @@ 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. @@ -199,67 +207,77 @@ cyg_httpd_create_std_header(char *extens 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. + // 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 - close(descr); - FD_CLR(descr, &httpstate.rfds); - httpstate.sockets[index].descriptor = 0; 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 @@ -267,14 +285,13 @@ cyg_httpd_handle_new_connection(cyg_int3 { 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. @@ -373,7 +390,9 @@ cyg_httpd_daemon(cyg_addrword_t data) 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++) { @@ -417,7 +436,8 @@ cyg_httpd_daemon(cyg_addrword_t data) 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); } } } @@ -429,7 +449,7 @@ cyg_httpd_daemon(cyg_addrword_t data) { #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",
