Overview
SHA1 Hash: | b312f5ff5b0c4ef57eaf21ecb274928a87817ce6 |
---|---|
Date: | 2008-02-03 02:41:50 |
User: | drh |
Comment: | Merge in some changes to the CGI reply generator that we made back in early December but got lost on an abandoned branch. Distributed version control is nice, but it also leaves open the real danger of losing changes this way. We need to work on interface features to minimize the risk of losing changes like this, and to identify lost changes quickly. |
Timelines: | ancestors | descendants | both | trunk |
Other Links: | files | ZIP archive | manifest |
Tags And Properties
- branch=trunk inherited from [a28c83647d]
- sym-trunk inherited from [a28c83647d]
Changes
[hide diffs]Modified src/cgi.c from [1c05503871] to [a795dc49f1].
@@ -57,10 +57,17 @@ #define P(x) cgi_parameter((x),0) #define PD(x,y) cgi_parameter((x),(y)) #define QP(x) quotable_string(cgi_parameter((x),0)) #define QPD(x,y) quotable_string(cgi_parameter((x),(y))) + +/* +** Destinations for output text. +*/ +#define CGI_HEADER 0 +#define CGI_BODY 1 + #endif /* INTERFACE */ /* ** Provide a reliable implementation of a caseless string comparison ** function. @@ -67,40 +74,80 @@ */ #define stricmp sqlite3StrICmp extern int sqlite3StrICmp(const char*, const char*); /* -** The body of the HTTP reply text is stored here. -*/ -static Blob cgiContent = BLOB_INITIALIZER; +** The HTTP reply is generated in two pieces: the header and the body. +** These pieces are generated separately because they are not necessary +** produced in order. Parts of the header might be built after all or +** part of the body. The header and body are accumulated in separate +** Blob structures then output sequentially once everything has been +** built. +** +** The cgi_destination() interface switch between the buffers. +*/ +static Blob *pContent; +static Blob cgiContent[2] = { BLOB_INITIALIZER, BLOB_INITIALIZER }; + +/* +** Set the destination buffer into which to accumulate CGI content. +*/ +void cgi_destination(int dest){ + switch( dest ){ + case CGI_HEADER: { + pContent = &cgiContent[0]; + break; + } + case CGI_BODY: { + pContent = &cgiContent[1]; + break; + } + default: { + cgi_panic("bad destination"); + } + } +} /* ** Append reply content to what already exists. */ void cgi_append_content(const char *zData, int nAmt){ - blob_append(&cgiContent, zData, nAmt); + blob_append(pContent, zData, nAmt); } /* ** Reset the HTTP reply text to be an empty string. */ void cgi_reset_content(void){ - blob_reset(&cgiContent); + blob_reset(&cgiContent[0]); + blob_reset(&cgiContent[1]); } /* ** Return a pointer to the CGI output blob. */ Blob *cgi_output_blob(void){ - return &cgiContent; + return pContent; +} + +/* +** Combine the header and body of the CGI into a single string. +*/ +static void cgi_combine_header_and_body(void){ + int size = blob_size(&cgiContent[1]); + if( size>0 ){ + blob_append(&cgiContent[0], blob_buffer(&cgiContent[1]), size); + blob_reset(&cgiContent[1]); + } } /* ** Return a pointer to the HTTP reply text. */ char *cgi_extract_content(int *pnAmt){ - return blob_buffer(&cgiContent); + cgi_combine_header_and_body(); + return blob_buffer(&cgiContent[0]); } /* ** Additional information used to form the HTTP reply */ @@ -119,12 +166,13 @@ /* ** Set the reply content to the specified BLOB. */ void cgi_set_content(Blob *pNewContent){ - blob_reset(&cgiContent); - cgiContent = *pNewContent; + cgi_reset_content(); + cgi_destination(CGI_HEADER); + cgiContent[0] = *pNewContent; blob_zero(pNewContent); } /* ** Set the reply status code @@ -220,10 +268,11 @@ /* ** Do a normal HTTP reply */ void cgi_reply(void){ + int total_size; if( iReplyStatus<=0 ){ iReplyStatus = 200; zReplyStatus = "OK"; } @@ -273,19 +322,27 @@ printf( "Content-Type: %s; charset=%s\r\n", zContentType, nl_langinfo(CODESET)); #else printf( "Content-Type: %s; charset=ISO-8859-1\r\n", zContentType); #endif if( strcmp(zContentType,"application/x-fossil")==0 ){ - blob_compress(&cgiContent, &cgiContent); + cgi_combine_header_and_body(); + blob_compress(&cgiContent[0], &cgiContent[0]); } if( iReplyStatus != 304 ) { - printf( "Content-Length: %d\r\n", blob_size(&cgiContent) ); + total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]); + printf( "Content-Length: %d\r\n", total_size); } printf("\r\n"); - if( blob_size(&cgiContent)>0 && iReplyStatus != 304 ){ - fwrite(blob_buffer(&cgiContent), 1, blob_size(&cgiContent), stdout); + if( total_size>0 && iReplyStatus != 304 ){ + int i, size; + for(i=0; i<2; i++){ + size = blob_size(&cgiContent[i]); + if( size>0 ){ + fwrite(blob_buffer(&cgiContent[i]), 1, size, stdout); + } + } } CGIDEBUG(("DONE\n")); } /* @@ -611,10 +668,11 @@ */ void cgi_init(void){ char *z; const char *zType; int len; + cgi_destination(CGI_BODY); z = (char*)P("QUERY_STRING"); if( z ){ z = mprintf("%s",z); add_param_list(z, '&'); } @@ -939,20 +997,20 @@ ** extra formatting capabilities such as %h and %t. */ void cgi_printf(const char *zFormat, ...){ va_list ap; va_start(ap,zFormat); - vxprintf(&cgiContent,zFormat,ap); + vxprintf(pContent,zFormat,ap); va_end(ap); } /* ** This routine works like "vprintf" except that it has the ** extra formatting capabilities such as %h and %t. */ void cgi_vprintf(const char *zFormat, va_list ap){ - vxprintf(&cgiContent,zFormat,ap); + vxprintf(pContent,zFormat,ap); } /* ** Send a reply indicating that the HTTP request was malformed @@ -976,11 +1034,11 @@ cgi_printf( "<html><body><h1>Internal Server Error</h1>\n" "<plaintext>" ); va_start(ap, zFormat); - vxprintf(&cgiContent,zFormat,ap); + vxprintf(pContent,zFormat,ap); va_end(ap); cgi_reply(); exit(1); }
Modified src/style.c from [c26a6ac246] to [71b6719cbf].
@@ -82,10 +82,11 @@ const char *zLogInOut = "Login"; const char *zHeader = db_get("header", (char*)zDefaultHeader); login_check_credentials(); if( pInterp ) return; + cgi_destination(CGI_HEADER); /* Generate the header up through the main menu */ pInterp = SbS_Create(); SbS_Store(pInterp, "project_name", db_get("project-name","Unnamed Fossil Project"), 0); @@ -97,11 +98,11 @@ SbS_Store(pInterp, "login", g.zLogin, 0); zLogInOut = "Logout"; } SbS_Render(pInterp, zHeader); - /* Generate the main menu and the submenu (if any) */ + /* Generate the main menu */ @ <div class="mainmenu"> @ <a href="%s(g.zBaseURL)/home">Home</a> if( g.okRead ){ @ <a href="%s(g.zBaseURL)/leaves">Leaves</a> @ <a href="%s(g.zBaseURL)/timeline">Timeline</a> @@ -120,12 +121,29 @@ } if( !g.noPswd ){ @ <a href="%s(g.zBaseURL)/login">%s(zLogInOut)</a> } @ </div> + cgi_destination(CGI_BODY); + g.cgiPanic = 1; +} + +/* +** Draw the footer at the bottom of the page. +*/ +void style_footer(void){ + const char *zFooter; + + if( pInterp==0 ) return; + + /* Go back and put the submenu at the top of the page. We delay the + ** creation of the submenu until the end so that we can add elements + ** to the submenu while generating page text. + */ if( nSubmenu>0 ){ int i; + cgi_destination(CGI_HEADER); @ <div class="submenu"> qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare); for(i=0; i<nSubmenu; i++){ struct Submenu *p = &aSubmenu[i]; if( p->zLink==0 ){ @@ -133,22 +151,16 @@ }else{ @ <a class="label" href="%s(p->zLink)">%h(p->zLabel)</a> } } @ </div> - } - @ <div class="content"> - g.cgiPanic = 1; -} + cgi_destination(CGI_BODY); + } -/* -** Draw the footer at the bottom of the page. -*/ -void style_footer(void){ - const char *zFooter; - - if( pInterp==0 ) return; + /* Put the footer at the bottom of the page. + */ + @ <div class="content"> zFooter = db_get("footer", (char*)zDefaultFooter); @ </div> SbS_Render(pInterp, zFooter); SbS_Destroy(pInterp); pInterp = 0;