Forgot the actual patch. Better performance than first try, but really
the API needs to change to use
a single pass through the post data to make real headway
w/performance. Also it is
possible to avoid the memcpy() & allocation as pointer directly to
post data + length can be returned for each form variable is possible as the
data is not encoded.


-- 
Øyvind Harboe
http://www.zylin.com - eCos ARM & FPGA  developer kit
### Eclipse Workspace Patch 1.0
#P ecos
Index: net/athttpd/current/include/forms.h
===================================================================
RCS file: /cvs/ecos/ecos-opt/net/net/athttpd/current/include/forms.h,v
retrieving revision 1.1
diff -u -r1.1 forms.h
--- net/athttpd/current/include/forms.h 18 Jul 2006 16:37:24 -0000      1.1
+++ net/athttpd/current/include/forms.h 15 Nov 2007 19:20:56 -0000
@@ -73,6 +73,8 @@
 
 // Prototypes.
 char *cyg_httpd_store_form_data(char*);
+void
+cyg_httpd_fetch_form_data(char *name, char *buffer, int len, int *actual);
 void cyg_httpd_handle_method_POST(void);
 cyg_int8 cyg_httpd_from_hex (cyg_int8);
 char *cyg_httpd_find_form_variable(char*);
Index: net/athttpd/current/include/http.h
===================================================================
RCS file: /cvs/ecos/ecos-opt/net/net/athttpd/current/include/http.h,v
retrieving revision 1.2
diff -u -r1.2 http.h
--- net/athttpd/current/include/http.h  27 Nov 2006 15:41:56 -0000      1.2
+++ net/athttpd/current/include/http.h  15 Nov 2007 19:20:56 -0000
@@ -102,6 +102,7 @@
 #define CYG_HTTPD_MODE_SEND_HEADER_ONLY       0x0004
 #define CYG_HTTPD_MODE_NO_CACHE               0x0008
 #define CYG_HTTPD_MODE_FORM_DATA              0x0010
+#define CYG_HTTPD_MODE_MULTIPART_FORM_DATA    0x0020
 
 // This must be generated at random...
 #define CYG_HTTPD_MD5_AUTH_NAME                "MD5"
@@ -175,6 +176,8 @@
     //  data (it might come in more than one frame)  and must be visible to
     //  handlers and cgi scripts.
     char        *post_data;
+    // the boundary string used for multipart/form-data
+    char               *boundary;
 
     // This pointer points to the information about the domain that needs
     //  to be authenticated. It is only used by the function that builds the
Index: net/athttpd/current/src/cgi.c
===================================================================
RCS file: /cvs/ecos/ecos-opt/net/net/athttpd/current/src/cgi.c,v
retrieving revision 1.3
diff -u -r1.3 cgi.c
--- net/athttpd/current/src/cgi.c       27 Nov 2006 15:41:56 -0000      1.3
+++ net/athttpd/current/src/cgi.c       15 Nov 2007 19:20:57 -0000
@@ -157,10 +157,89 @@
 }
 #endif
 
+/* We're not going to write ad-hoc CGI scripts without good old printf! */
+void cyg_httpd_fprintf(char *format, ...)
+{
+       char *buffer = NULL;
+       int size = 1;
+       int n=-1;
+       va_list ap;
+       va_start(ap, format);
+       diag_vprintf(format, ap);
+       va_end(ap);
+       
+       
+       /* process format string */
+       for (;;)
+       {
+               buffer=malloc(size);
+               if (buffer==NULL)
+               {
+                       return;
+               }
+               
+               va_start(ap, format);
+               n = vsnprintf(buffer, size, format, ap);
+               va_end(ap);
+               if (n>0 && n < (size-1))
+                       break;
+               size=size*2+1;
+               free(buffer);
+       }
+       
+       if (n > 0)
+       {
+               cyg_httpd_write_chunked(buffer, strlen(buffer));
+       } else
+       {
+               /* vsnprintf failed */
+       }
+       
+       if (buffer)
+               free(buffer);
+}
+
+
 // 
=============================================================================
 // tcl CGI Support
 // 
=============================================================================
 #ifdef CYGOPT_NET_ATHTTPD_USE_CGIBIN_TCL
+
+
+/* Error message has to go to HTTP to make debugging less of a torturous 
process */
+void cyg_httpd_exec_cgi_tcl_error(Jim_Interp *interp)
+{
+    int len, i;
+
+    cyg_httpd_start_chunked("html");
+    cyg_httpd_fprintf("<html><body>\n");
+    
+    cyg_httpd_fprintf("Runtime error, file \"%s\", line %d:<br>" ,
+            interp->errorFileName, interp->errorLine);
+    cyg_httpd_fprintf("    %s<br>" ,
+            Jim_GetString(interp->result, NULL));
+    Jim_ListLength(interp, interp->stackTrace, &len);
+    for (i = 0; i < len; i+= 3) {
+        Jim_Obj *objPtr;
+        const char *proc, *file, *line;
+
+        Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
+        proc = Jim_GetString(objPtr, NULL);
+        Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr,
+                JIM_NONE);
+        file = Jim_GetString(objPtr, NULL);
+        Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
+                JIM_NONE);
+        line = Jim_GetString(objPtr, NULL);
+        cyg_httpd_fprintf("In procedure '%s' called at file \"%s\", line 
%s<br>" ,
+                proc, file, line);
+    }
+    cyg_httpd_fprintf("</html></body>\n");
+    
+    cyg_httpd_end_chunked();
+}
+
+
 int Jim_AioInit(Jim_Interp *);
 cyg_int32
 cyg_httpd_exec_cgi_tcl(char *file_name)
@@ -184,7 +263,12 @@
                                   "post_data", 
                                   httpstate.post_data);
      
-    Jim_Eval(httpstate.jim_interp, tcl_cmd);
+    int err;
+    err=Jim_Eval(httpstate.jim_interp, tcl_cmd);
+    if (err!=JIM_OK)
+    {
+       cyg_httpd_exec_cgi_tcl_error(httpstate.jim_interp);
+    }
     return 0;
 }
 
@@ -217,10 +301,45 @@
     cyg_httpd_end_chunked();
     return JIM_OK;
 }
-  
+
+int
+cyg_httpd_Jim_Command_formfetch(Jim_Interp *interp, 
+                                   int argc,
+                                   Jim_Obj *const *argv)
+{
+    char *name = (char*)Jim_GetString(argv[1], NULL);
+       
+       // Find length
+       char buf;
+    int actual;
+    cyg_httpd_fetch_form_data(name, &buf, 1, &actual);
+
+    int len=actual+1;
+    char *destBuffer=malloc(len);
+    if (destBuffer==NULL)
+        return JIM_ERR;
+               
+    cyg_httpd_fetch_form_data(name, destBuffer, len, &actual);
+           
+    Jim_Obj *objPtr;
+       objPtr = Jim_NewStringObj(interp, destBuffer, actual);
+    Jim_SetResult(interp, objPtr);
+    free(destBuffer);
+    return JIM_OK;
+}
+
+
+// If the application wants to add commands, it will need to do so after the
+// interpreter has been initialized. However, the initialization of the http
+// server is asynchronous. This function is safe to invoke *before* the http
+// server is started.
 void
 cyg_httpd_init_tcl_interpreter(void)
 {
+       static bool initDone=false;
+       if (initDone)
+               return;
+       initDone=true;
     // Start the TCL interpreter.
     Jim_InitEmbedded();
     httpstate.jim_interp = Jim_CreateInterp();
@@ -243,6 +362,11 @@
                       cyg_httpd_Jim_Command_endchunked,
                       NULL, 
                       NULL);
+    Jim_CreateCommand(httpstate.jim_interp,
+                      "formfetch",
+                      cyg_httpd_Jim_Command_formfetch,
+                      NULL, 
+                      NULL);
 }                      
 #endif    
 
Index: net/athttpd/current/src/forms.c
===================================================================
RCS file: /cvs/ecos/ecos-opt/net/net/athttpd/current/src/forms.c,v
retrieving revision 1.4
diff -u -r1.4 forms.c
--- net/athttpd/current/src/forms.c     14 Nov 2007 14:39:13 -0000      1.4
+++ net/athttpd/current/src/forms.c     15 Nov 2007 19:20:57 -0000
@@ -83,44 +83,75 @@
             : c - 'a' + 10;     
 }
 
+/* Scan through the entire string, but only store as much as the buffer can 
hold.
+ * That way a string that is too long does stop subsequent variables from 
+ * being read
+ * 
+ * buflen - length of buffer, including space for \0. must be >= 1
+ * actual - actual length of string excluding \0
+ */
 char*
-cyg_httpd_store_form_variable(char *query, cyg_httpd_fvars_table_entry *entry)
+cyg_httpd_store_form_variable(char *query, char *q, int buflen, int * actual)
 {
     char *p = query;
-    char *q = entry->buf;
     int   len = 0;
-    
-    while (len < (entry->buflen - 1))
+    char c=0;
+    int gotChar;
+
+    for (;;)
+    {
+       int done = (len >= (buflen - 1));
         switch(*p)
         {
         case '%':
             p++;
+            gotChar=0;
             if (*p) 
-                *q = cyg_httpd_from_hex(*p++) * 16;
+            {
+                c = cyg_httpd_from_hex(*p++) * 16;
+                gotChar=1;
+            }
             if (*p) 
-                *q = (*q + cyg_httpd_from_hex(*p++));
-            q++;
+            {
+                c = (c + cyg_httpd_from_hex(*p++));
+                gotChar=1;
+            }
+            if (!done&&gotChar)
+            {
+               *q++=c;
+            }
             len++;
             break;
         case '+':
-            *q++ = ' ';
+            if (!done)
+            {
+               *q++=' ';
+            }
             p++;
             len++;
             break;
+        case 0: // Hmmm... I'm  not quite sure how, in previous versions, the 
\0 sentinel was caught...
         case '&':
         case ' ':
-            *q++ = '\0';
-            return p;
-        default:    
-            *q++ = *p++;
+            goto doneLoop;
+        default:
+               c=*p++;
+               if (!done)
+               {
+                       *q++ = c;
+               }
             len++;
+            break;
         }
-        *q = '\0';
-        while ((*p != ' ') && (*p != '&'))
-            p++;
-        return p;
+    }
+doneLoop:
+       *q = '\0';
+       *actual=len;
+    return p;
 } 
 
+
+
 // We'll try to parse the data from the form, and store it in the variables
 //  that have been defined by the user in the 'form_variable_table'.
 char*
@@ -164,7 +195,8 @@
         }
             
         // Found the variable, store the name.
-        p = cyg_httpd_store_form_variable(++p2, entry);
+        int actual;
+        p = cyg_httpd_store_form_variable(++p2, entry->buf, entry->buflen, 
&actual);
 #if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
         diag_printf("Stored form variable: %s Value: %s\n",
                     entry->name,
@@ -176,6 +208,313 @@
     return p;
 }
 
+/* If the string contains *all* of prefix, return true. 
+ * \n in prefix can mean \n or \r\n in 'p'.
+ */
+static char *contains(char *p, char *prefix, int len)
+{
+       int i;
+       for (i=0; i<strlen(prefix); i++)
+       {
+               if (len<=0)
+               {
+//                     diag_printf("mismatch\n");
+                       return NULL;
+               }
+//             diag_printf("p=%02xprefix=%02x\n", p[i], prefix[i]); 
+//             diag_printf("p=%cprefix=%c\n", p[i], prefix[i]); 
+               if (*p!=prefix[i])
+               {
+                       if 
((len>=2)&&(prefix[i]=='\n')&&(*p=='\r')&&(p[1]=='\n'))
+                       {
+                               p+=2;
+                               len-=2;
+                               continue;
+                       }
+//                     diag_printf("mismatch\n");
+                       return NULL;
+               }
+               p++;
+       }
+//     diag_printf("match\n");
+       return p;
+}
+
+/* if we're looking at this text, skip it, otherwise return NULL. 
+ * If p==NULL, return NULL. 
+ * If the function returns NULL, then *len is untouched
+ */
+static char *skip(char *p, int *len, char *expect)
+{
+//     diag_printf("skip p=%08x %s %d\n", p, expect, *len);
+       if (p==NULL)
+               return NULL;
+//     int i;
+//     for (i=0; i<strlen(expect); i++)
+//     {
+//             diag_printf("%c", p[i]);
+//     }
+//     diag_printf("strncmp starting\n");
+       char *after;
+       after=contains(p, expect, *len);
+       if (after!=NULL)
+    {
+       // skip boundary
+       int l=after-p;
+       p+=l;
+       *len-=l;
+       return p;
+    }
+    return NULL;
+}
+
+// Same as skip() except that we skip forwards to the expected string
+static char *skipTo(char *p, int *len, char *expect)
+{
+//     diag_printf("skipTo p=%08x %s\n", p, expect);
+       if (p==NULL)
+               return NULL;
+       
+       if (strcmp(expect, "\n")==0)
+       {
+               int i;
+               // Special case to improve performance. This is the code
+               // that will skip large amounts of data
+               for (i=0; i<*len; i++)
+               {
+                       if (p[i]=='\n')
+                       {
+                               break;
+                       }
+               }
+               if (i!=*len)
+               {
+                       // Found \n. Do we need to back up one?
+                       if ((i>0)&&(p[i-1]=='\r'))
+                       {
+                               i--;
+                       }
+                       // found string.
+                       *len-=i;
+                       return p+i;
+               }
+       } else
+       {
+               int i;
+               int strLen=strlen(expect);
+               for (i=0; i<*len; i++)
+               {
+                       int j;
+                       char *cmp=p;
+                       for (j=0; j<strLen; j++)
+                       {
+                               if (cmp[i+j]!=expect[j])
+                               {
+                                       if 
(((*len-i)>=2)&&(expect[j]=='\n')&&(cmp[i+j]=='\r')&&(cmp[i+j+1]=='\n'))
+                                       {
+                                               // interpret \r\n as line end
+                                               cmp++;
+                                               continue;
+                                       }
+                                       break;
+                               }
+                       }
+                       if (j==strLen)
+                       {
+                               // found string.
+                               *len-=i;
+                               return p+i;
+                       }
+               }
+       }
+       // did not find string
+    return NULL;
+}
+
+// Returns pointer to the beginning of a boundary, 
+// i.e. the byte *after* the last char in 
+// the value
+static char *skipUntilBoundary(char *p, int *origLen)
+{
+//     diag_printf("skipUntilBoundary\n");
+       if (p==NULL)
+               return NULL;
+       int len=*origLen;
+       for (;;)
+       {
+               p=skipTo(p, &len, "\n");
+               // We're now at the beginning of the boundary
+               char *t=p;
+               int l=len;
+               p=skip(p, &len, "\n");
+               if (p==NULL)
+                       return NULL;
+               char *p2;
+               p2=skip(p, &len, "--");
+               if (p2!=NULL)
+               {
+                       if (contains(p2, httpstate.boundary, len))
+                       {
+                               // We back up a bit
+                               *origLen=l;
+                               return t;
+                       }
+                       // Not the boundary, continue
+               }
+       }
+       return NULL;
+}
+
+// Example(From Firefox):
+//
+//-----------------------------107852697226440
+//Content-Disposition: form-data; name="form_filename"
+//
+//test
+//-----------------------------107852697226440
+//Content-Disposition: form-data; name="form_filecontent"; filename="abc.txt"
+//Content-Type: text/plain
+//
+//file line 1
+//file line 2
+//file line 3
+//-----------------------------107852697226440
+//Content-Disposition: form-data; name="form_action"
+//
+//Upload
+//-----------------------------107852697226440--
+
+// Skip to variable and fish out contents
+static void
+cyg_httpd_fetch_multipart_form_data(char *name, char *buffer, int bufferlen, 
int *actual)
+{
+       char *p=httpstate.post_data;
+    int len=httpstate.content_len;
+    
+    if (httpstate.boundary==NULL)
+    {
+       return;
+    }
+
+//     diag_printf("2YYYY %d\n", len);
+//     int i;
+//     for (i=0; i<strlen(p); i++)
+//     {
+//             diag_printf("%c", p[i]);
+//     }
+//     diag_printf("2YYYY\n");
+    
+//    diag_printf("2XXXXXX\nFinding %s\n", name);
+    
+    for (;;)
+    {
+       p=skipTo(p, &len, "--");
+       p=skip(p, &len, "--");
+       p=skip(p, &len, httpstate.boundary);
+       p=skip(p, &len, "\n");
+       p=skip(p, &len, "Content-Disposition: form-data; name=\"");
+       char *start=p;
+       p=skipTo(p, &len, "\"");
+       if (p==NULL)
+               return;
+       char *end=p;
+       p=skip(p, &len, "\"");
+       if (contains(start, name, end-start))
+       {
+               // Found variable! 
+               // Is this a file or just data? 
+               // We're ignoring the filename
+               char *p2;
+               p2=skip(p, &len, "; filename=\"");
+               if (p2!=NULL)
+               {
+                       p=p2;
+               p=skipTo(p, &len, "Content-Type: ");
+               } 
+               p=skipTo(p, &len, "\n");
+               p=skip(p, &len, "\n");
+               p=skip(p, &len, "\n"); // extra blank line before data
+               start=p;
+               for (;;)
+               {
+                       p=skipUntilBoundary(p, &len);
+               if (p==NULL)
+                       return;
+                       // Found end!
+                       *actual=p-start;
+                       int t;
+                       t=bufferlen-1; // always leave space for a 0.
+                       if (t>*actual)
+                               t=*actual;
+                       memcpy(buffer, start, t);
+                       buffer[t]=0; // always poke a 0 afterwards. Note that 
the actual data can also contain 0's.
+//                     diag_printf("tttt\n%s\n\nFound 
name=%s\nvalue=\"%s\"\n", p, name, buffer); 
+                       return;
+               }
+       } else
+       {
+               // Skip this variable
+                       p=skipUntilBoundary(p, &len);
+               if (p==NULL)
+                       return;
+       }
+    }
+}
+
+// Find form variable, copy it to the buffer and return the actual length of 
the variable
+// 
+// If a variable is not found(or there are no post data) an empty string
+// is returned.
+void
+cyg_httpd_fetch_form_data(char *name, char *buffer, int len, int *actual)
+{
+    char      *p2;
+    
+    char *p=httpstate.post_data;
+    
+    
+    buffer[0]=0;
+    *actual=0;
+    
+    if (!p)    /* No form data? just return after clearing variables */
+        return;
+
+    if (httpstate.mode & CYG_HTTPD_MODE_MULTIPART_FORM_DATA)
+    {
+       cyg_httpd_fetch_multipart_form_data(name, buffer, len, actual);
+       return;
+    }
+    
+
+    while (*p && *p != ' ')
+    {
+        if (!(p2 = strchr(p, '=')))
+            return;        /* Malformed post? */
+        int var_length = (cyg_int32)p2 - (cyg_int32)p;
+
+        if (strncmp((const char*)p, name, var_length))
+        {
+            // No such variable. Run through the data.
+            while ((*p != '&') && (*p && *p != ' '))
+                p++;
+            if(*p == '&')
+                p++;
+            continue;
+        }
+            
+        // Found the variable, store the name.
+        p = cyg_httpd_store_form_variable(++p2, buffer, len, actual);
+#if CYGOPT_NET_ATHTTPD_DEBUG_LEVEL > 1
+        diag_printf("Stored form variable: %s Value: %s\n",
+                    name,
+                    buffer);
+#endif
+        
+        return;
+    }
+}
+
+
 char*
 cyg_httpd_find_form_variable(char *p)
 {
Index: net/athttpd/current/src/http.c
===================================================================
RCS file: /cvs/ecos/ecos-opt/net/net/athttpd/current/src/http.c,v
retrieving revision 1.4
diff -u -r1.4 http.c
--- net/athttpd/current/src/http.c      14 Nov 2007 14:39:13 -0000      1.4
+++ net/athttpd/current/src/http.c      15 Nov 2007 19:20:58 -0000
@@ -851,7 +851,7 @@
 {
     // The deafult for HTTP 1.1 is keep-alive connections, unless specifically
     //  closed by the far end.
-    httpstate.mode &= ~(CYG_HTTPD_MODE_CLOSE_CONN | CYG_HTTPD_MODE_FORM_DATA);
+    httpstate.mode &= ~(CYG_HTTPD_MODE_CLOSE_CONN | CYG_HTTPD_MODE_FORM_DATA | 
CYG_HTTPD_MODE_MULTIPART_FORM_DATA);
     httpstate.modified_since = -1;
     httpstate.content_len = 0;
     while ((*p != '\r') && (*p != '\n') & (*p != '\0'))
@@ -892,14 +892,51 @@
         }
         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;
+            p = p + strlen("Content-Type: ");
+            // 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",
+                            strlen("application/x-www-form-urlencoded")) == 0)
+                httpstate.mode |= CYG_HTTPD_MODE_FORM_DATA;
+            if (strncasecmp(p,
+                            "multipart/form-data",
+                            strlen("multipart/form-data")) == 0)
+            {
+                httpstate.mode |= CYG_HTTPD_MODE_MULTIPART_FORM_DATA;
+                p+=strlen("multipart/form-data");
+            }
+
+               // Fish out boundary
+            // Content-type: multipart/form-data, boundary=AaB03x
+               if (httpstate.boundary!=NULL)
+                       free(httpstate.boundary);
+               httpstate.boundary=NULL; // no boundary by default
+               
+               while ((*p == ' ')||(*p == ';'))
+               {
+                       p++;
+               }
+               if (strncmp(p, "boundary", strlen("boundary"))==0)
+               {
+                       p+=strlen("boundary");
+                       if (*p=='=')
+                       {
+                               p++;
+                               
+                               char *start=p;
+                               while 
((*p!='\n')&&(*p!='\r')&&(*p!=';')&&(*p!=' '))
+                               {
+                                       p++;
+                               }
+                               httpstate.boundary=malloc(p-start+1);
+                               if (httpstate.boundary!=NULL)
+                               {
+                                       memcpy(httpstate.boundary, start, 
p-start);
+                                       httpstate.boundary[p-start]=0;
+                               }
+                       }
+               }
             while (*p++ != '\n');
         }
         else if (strncasecmp("Host:", p, 5) == 0)
@@ -941,28 +978,28 @@
             }
             else if (strncasecmp(p, "Digest", 6) == 0)
             {
-                p += 6;
-                while (*p == ' ')
-                   p++;
+                p += 6;
+                while (*p == ' ')
+                   p++;
                 while ((*p != '\r') && (*p != '\n'))
-                {
-                    if (strncasecmp(p, "realm=", 6) == 0)
+                {
+                    if (strncasecmp(p, "realm=", 6) == 0)
                         p = cyg_httpd_digest_skip(p + 6);
-                    else if (strncasecmp(p, "username=", 9) == 0)
+                    else if (strncasecmp(p, "username=", 9) == 0)
                         p = cyg_httpd_digest_skip(p + 9);
                     else if (strncasecmp(p, "nonce=", 6) == 0)
                         p = cyg_httpd_digest_skip(p + 6);
-                    else if (strncasecmp(p, "response=", 9) == 0)
+                    else if (strncasecmp(p, "response=", 9) == 0)
                         p = cyg_httpd_digest_data(cyg_httpd_md5_response, 
                                                   p + 9);
-                    else if (strncasecmp(p, "cnonce=", 7) == 0)
+                    else if (strncasecmp(p, "cnonce=", 7) == 0)
                         p = cyg_httpd_digest_data(cyg_httpd_md5_cnonce, p + 7);
-                    else if (strncasecmp(p, "qop=", 4) == 0)
+                    else if (strncasecmp(p, "qop=", 4) == 0)
                         p = cyg_httpd_digest_skip(p + 4);
-                    else if (strncasecmp(p, "nc=", 3) == 0)
+                    else if (strncasecmp(p, "nc=", 3) == 0)
                         p = cyg_httpd_digest_data(cyg_httpd_md5_noncecount, 
                                                   p + 3);
-                    else if (strncasecmp(p, "algorithm=", 10) == 0)
+                    else if (strncasecmp(p, "algorithm=", 10) == 0)
                         p = cyg_httpd_digest_skip(p + 10);
                     else if (strncasecmp(p, "opaque=", 7) == 0)
                         p = cyg_httpd_digest_skip(p + 7);
Index: net/athttpd/current/ChangeLog
===================================================================
RCS file: /cvs/ecos/ecos-opt/net/net/athttpd/current/ChangeLog,v
retrieving revision 1.9
diff -u -r1.9 ChangeLog
--- net/athttpd/current/ChangeLog       14 Nov 2007 14:39:13 -0000      1.9
+++ net/athttpd/current/ChangeLog       15 Nov 2007 19:20:56 -0000
@@ -1,3 +1,10 @@
+2007-11-15  Oyvind Harboe  <[EMAIL PROTECTED]>
+
+       * src/cgi.c: print error message to HTML response if tcl script fails.
+       Less torturous to debug tcl scripts.
+       * include/forms.h,include/http.h,src/cgi.c,src/forms.c,src/http.c:
+       file upload support.
+               
 2007-11-12  Oyvind Harboe  <[EMAIL PROTECTED]>
 2007-11-12  Jonathan Larmour  <[EMAIL PROTECTED]>
 
-- 
Before posting, please read the FAQ: http://ecos.sourceware.org/fom/ecos
and search the list archive: http://ecos.sourceware.org/ml/ecos-discuss

Reply via email to