src/lib/htmshell.c 1.63
1.63 2009/08/18 21:22:15 tdreszer
Fixed warnBox to go to prev page when page halted by errorAbort. Restored multiple message suppoprt. Still supports selectAll not selecting empty warnBox.
Index: src/lib/htmshell.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/lib/htmshell.c,v
retrieving revision 1.62
retrieving revision 1.63
diff -b -B -U 1000000 -r1.62 -r1.63
--- src/lib/htmshell.c 11 Aug 2009 23:00:47 -0000 1.62
+++ src/lib/htmshell.c 18 Aug 2009 21:22:15 -0000 1.63
@@ -1,601 +1,596 @@
/* htmshell - a shell to wrap around programs that generate
* html files. Write the html initial stuff (<head>, <body>, etc.)
* and the final stuff too. Also catch errors here so that
* the html final stuff is written even if the program has
* to abort.
*
* This also includes a few routines to write commonly used
* html constructs such as images, horizontal lines. etc.
*
* This file is copyright 2002 Jim Kent, but license is hereby
* granted for all use - public, private or commercial. */
#include "common.h"
#include "obscure.h"
#include "cheapcgi.h"
#include "htmshell.h"
#include "errabort.h"
#include "dnautil.h"
static char const rcsid[] = "$Id$";
jmp_buf htmlRecover;
static bool NoEscape = FALSE;
void htmlNoEscape()
{
NoEscape = TRUE;
}
void htmlDoEscape()
{
NoEscape = FALSE;
}
void htmlVaParagraph(char *line, va_list args)
/* Print a line in it's own paragraph. */
{
fputs("<P>", stdout);
vfprintf(stdout, line, args);
fputs("</P>\n", stdout);
}
void htmlParagraph(char *line, ...)
{
va_list args;
va_start(args, line);
htmlVaParagraph(line, args);
va_end(args);
}
void htmlVaCenterParagraph(char *line, va_list args)
/* Center a line in it's own paragraph. */
{
fputs("<P ALIGN=\"CENTER\">", stdout);
vfprintf(stdout, line, args);
fputs("</P>\n", stdout);
}
void htmlCenterParagraph(char *line, ...)
{
va_list args;
va_start(args, line);
htmlVaCenterParagraph(line, args);
va_end(args);
}
void htmlHorizontalLine()
/* Print a horizontal line. */
{
printf("<HR ALIGN=\"CENTER\">");
}
void htmHorizontalLine(FILE *f)
/* Print a horizontal line. */
{
fprintf(f, "<HR ALIGN=\"CENTER\">");
}
void htmlNbSpaces(int count)
/* Print a number of non-breaking spaces. */
{
int i;
for (i=0; i<count; ++i)
printf(" ");
}
void htmTextOut(FILE *f, char *s)
/* Print out string to file, if necessary replacing > with > and the like */
{
char c;
if (NoEscape)
{
fputs(s, f);
return;
}
while ((c = *s++) != 0)
{
switch (c)
{
case '>':
fputs(">", f);
break;
case '<':
fputs("<", f);
break;
case '&':
fputs("&", f);
break;
case '"':
fputs(""", f);
break;
default:
fputc(c, f);
break;
}
}
}
void htmlTextOut(char *s)
/* Print out string, if necessary replacing > with > and the like */
{
htmTextOut(stdout, s);
}
char *htmlEncode(char *s)
/* Return a clone of s but if necessary replacing > with > and the like */
{
size_t len = 0;
char c;
char *encStr;
char *p = s;
if (s == NULL) /* do not try to encode a NULL pointer */
return NULL;
/* First pass through s to determine encoded length to allocate: */
/* [as a shortcut, we could simply allocate 6*length of s] */
while ((c = *p++) != 0)
{
switch (c)
{
case '>':
case '<':
len += 4;
break;
case '&':
len += 5;
break;
case '"':
len += 6;
break;
default:
len++;
break;
}
}
encStr = needMem(len+1);
/* Second pass through s to encode: */
len = 0;
p = s;
while ((c = *p++) != 0)
{
switch (c)
{
case '>':
strcat(encStr+len, ">");
len += 4;
break;
case '<':
strcat(encStr+len, "<");
len += 4;
break;
case '&':
strcat(encStr+len, "&");
len += 5;
break;
case '"':
strcat(encStr+len, """);
len += 6;
break;
default:
encStr[len++] = c;
break;
}
}
return encStr;
}
char *htmlWarnStartPattern()
/* Return starting pattern for warning message. */
{
return "<!-- HGERROR-START -->\n";
}
char *htmlWarnEndPattern()
/* Return ending pattern for warning message. */
{
return "<!-- HGERROR-END -->\n";
}
#define WARNBOX_IN_USE
#ifdef WARNBOX_IN_USE
static void htmlWarnBoxSetup(FILE *f, int dirDepth)
/* Creates an invisible, empty warning box than can be filled with errors
* and then made visible. dirDepth is the number of levels beneath apache
* root that caller's HTML will appear to the web client. E.g. if writing
* HTML from cgi-bin, dirDepth is 1; if trash/body/, 2. */
{
+// Only set this up once per page
+static boolean htmlWarnBoxSetUpAlready=FALSE;
+if(htmlWarnBoxSetUpAlready)
+ return;
+htmlWarnBoxSetUpAlready=TRUE;
+
char relPath[PATH_LEN];
relPath[0] = '\0';
while (dirDepth-- > 0)
safecat(relPath, sizeof(relPath), "../");
// NOTE: Making both IE and FF work is almost impossible. Currently, in IE, if the message
// is forced to the top (calling this routine after <BODY> then the box is not resizable
// (dynamically adjusting to its contents). But if this setup is done later in the page
// (at first warning), then IE does resize it. Why?
-// FF is resizable now, but it took some experimentation.
-fprintf(f, "<script type='text/javascript'>"
- "if(document.getElementById('warnBox')==undefined) {"
- "document.write(\"<center>"
+// FF3.0 (but not FF2.0) was resizable with the following, but it took some experimentation.
+// Remember what worked nicely on FF3.0:
+// "var app=navigator.appName.substr(0,9); "
+// "if(app == 'Microsoft') {warnBox.style.display='';} else {warnBox.style.display=''; warnBox.style.width='auto';}"
+fprintf(f, "<script type='text/javascript'>\n");
+fprintf(f, "document.write(\"<center>"
"<div id='warnBox' style='display:none; background-color:Beige; "
"border: 3px ridge DarkRed; width:640px; padding:10px; margin:10px; "
"text-align:left;'>"
- "</div></center>\");\n");
-// Remember what worked nicely on FF3.0:
-// "function showWarnBox() {"
-// "var warnBox=document.getElementById('warnBox');"
-// "if(warnBox!=undefined) {"
-// "var app=navigator.appName.substr(0,9); "
-// "if(app == 'Microsoft') {warnBox.style.display='';} else {warnBox.style.display=''; "
-// "warnBox.style.width='auto';}}}"
-fprintf(f,
- "function showWarnBox() {"
+ "<CENTER><B id='warnHead' style='color:DarkRed;'></B></CENTER><UL id='warnList'></UL>"
+ "<CENTER><img id='warnOK' src='%simages/ok.jpg' onclick='hideWarnBox();return false;'></CENTER>"
+ "</div></center>\");\n", relPath);
+fprintf(f,"function showWarnBox() {"
"var warnBox=document.getElementById('warnBox');"
- "if(warnBox!=undefined) {"
- "warnBox.innerHTML=\""
- "<CENTER><B style='color:DarkRed;'>Error(s):</B></CENTER><UL id='warnList'></UL>"
- "<CENTER><img src='%simages/ok.jpg' onclick='hideWarnBox();return false;'></CENTER>"
- "\";"
- "warnBox.style.display=''; warnBox.style.width='65%%';}};\n"
- "function hideWarnBox() {"
+ "warnBox.style.display=''; warnBox.style.width='65%%';"
+ "document.getElementById('warnHead').innerHTML='Error(s):';"
+ "}\n");
+fprintf(f,"function hideWarnBox() {"
"var warnBox=document.getElementById('warnBox');"
- "if(warnBox!=undefined) {"
- "warnBox.style.display='none';"
- "warnBox.innerHTML='';}};"
- "}</script>\n", relPath);
+ "warnBox.style.display='none';warnBox.innerHTML='';"
+ "var endOfPage = document.body.innerHTML.substr(document.body.innerHTML.length-20);"
+ "if(endOfPage.lastIndexOf('-- ERROR --') > 0) { history.back(); }"
+ "}\n"); // Note that the OK button goes to prev page when this page is interrupted by the error.
+fprintf(f,"</script>\n");
}
#endif//ifdef WARNBOX_IN_USE
void htmlVaWarn(char *format, va_list args)
/* Write an error message. */
{
va_list argscp;
va_copy(argscp, args);
#ifdef WARNBOX_IN_USE
-static boolean noWarningsYet = TRUE;
-if(noWarningsYet)
- {
- htmlWarnBoxSetup(stdout, 1);
- noWarningsYet=FALSE;
- }
-
+htmlWarnBoxSetup(stdout,1); // sets up the warnBox if it hasn't already been done.
char warning[512];
vsnprintf(warning,sizeof(warning),format, args);
// NOTE: While some internal HTML should work, a single quote (') will will screw it all up!
if( strSwapStrs(warning, sizeof(warning),"'","'") == -1) // Sheild single quotes
strSwapChar(warning,'\'','`'); // ran out of memory, replacing them with (`)
if( strSwapStrs(warning, sizeof(warning),"\n","<BR>") == -1) // new lines also break the code
strSwapChar(warning,'\n',' '); // ran out of memory, replacing them with ( )
-printf("<script type='text/javascript'>{showWarnBox();var warnList=document.getElementById('warnList'); warnList.innerHTML += '<li>%s</li>';}</script>\n",warning);
-
+printf("<script type='text/javascript'>{showWarnBox();"
+ "var warnList=document.getElementById('warnList');"
+ "warnList.innerHTML += '<li>%s</li>';}</script><!-- ERROR -->\n",warning); // NOTE that "--ERROR --" is needed at the end of this print!!
#else//ifndef WARNBOX_IN_USE
htmlHorizontalLine();
printf("%s", htmlWarnStartPattern());
htmlVaParagraph(format,args);
printf("%s", htmlWarnEndPattern());
htmlHorizontalLine();
#endif//def WARNBOX_IN_USE
/* Log useful CGI info to stderr */
logCgiToStderr();
/* write warning/error message to stderr so they get logged. */
vfprintf(stderr, format, argscp);
va_end(argscp);
fflush(stderr);
}
void htmlAbort()
/* Terminate HTML file. */
{
longjmp(htmlRecover, -1);
}
void htmlMemDeath()
{
errAbort("Out of memory.");
}
static void earlyWarningHandler(char *format, va_list args)
/* Write an error message so user can see it before page is really started. */
{
static boolean initted = FALSE;
if (!initted)
{
htmlStart("Very Early Error");
initted = TRUE;
}
printf("%s", htmlWarnStartPattern());
htmlVaParagraph(format,args);
printf("%s", htmlWarnEndPattern());
}
static void earlyAbortHandler()
/* Exit close web page during early abort. */
{
printf("</BODY></HTML>");
exit(0);
}
void htmlPushEarlyHandlers()
/* Push stuff to close out web page to make sensible error
* message during initialization. */
{
pushWarnHandler(earlyWarningHandler);
pushAbortHandler(earlyAbortHandler);
}
static char *htmlStyle =
"<STYLE TYPE=\"text/css\">"
".hiddenText {background-color: silver}"
".normalText {background-color: white}"
"</STYLE>\n";
char *htmlStyleUndecoratedLink =
/* Style that gets rid of underline of links. */
"<STYLE TYPE=\"text/css\"> "
"<!-- "
"A {text-decoration: none} "
"-->"
"</STYLE>\n";
void htmlSetStyle(char *style)
/* Set document wide style. A favorite style to
* use for many purposes is htmlStyleUndecoratedLink
* which will remove underlines from links.
* Needs to be called before htmlStart or htmShell. */
{
htmlStyle = style;
}
static char *htmlBackground = NULL;
void htmlSetBackground(char *imageFile)
/* Set background - needs to be called before htmlStart
* or htmShell. */
{
htmlBackground = imageFile;
}
static int htmlBgColor = 0xFFFFFF;
boolean gotBgColor = FALSE;
void htmlSetBgColor(int color)
/* Set background color - needs to be called before htmlStart
* or htmShell. */
{
htmlBgColor = color;
gotBgColor = TRUE;
}
void htmlSetCookie(char* name, char* value, char* expires, char* path, char* domain, boolean isSecure)
/* create a cookie with the given stats */
{
char* encoded_name;
char* encoded_value;
char* encoded_path = NULL;
encoded_name = cgiEncode(name);
encoded_value = cgiEncode(value);
if(path != NULL)
encoded_path = cgiEncode(path);
printf("Set-Cookie: %s=%s; ", encoded_name, encoded_value);
if(expires != NULL)
printf("expires=%s; ", expires);
if(path != NULL)
printf("path=%s; ", encoded_path);
if(domain != NULL)
printf("domain=%s; ", domain);
if(isSecure == TRUE)
printf("secure");
printf("\n");
}
void _htmStartWithHead(FILE *f, char *head, char *title, boolean printDocType, int dirDepth)
/* Write out bits of header that both stand-alone .htmls
* and CGI returned .htmls need, including optional head info */
{
if (printDocType)
fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n", f);
fputs("<HTML>", f);
fprintf(f,"<HEAD>\n%s<TITLE>%s</TITLE>\n", head, title);
fprintf(f, "\t<META http-equiv=\"Content-Script-Type\" content=\"text/javascript\">\n");
if (htmlStyle != NULL)
fputs(htmlStyle, f);
fputs("</HEAD>\n\n",f);
fputs("<BODY",f);
if (htmlBackground != NULL )
fprintf(f, " BACKGROUND=\"%s\"", htmlBackground);
if (gotBgColor)
fprintf(f, " BGCOLOR=\"%X\"", htmlBgColor);
fputs(">\n",f);
#ifdef WARNBOX_IN_USE
htmlWarnBoxSetup(f, dirDepth);
#endif//def WARNBOX_IN_USE
}
void htmlStart(char *title)
/* Write the start of an html from CGI */
{
puts("Content-Type:text/html");
puts("\n");
_htmStartWithHead(stdout, "", title, TRUE, 1);
}
void htmStartWithHead(FILE *f, char *head, char *title)
/* Write the start of a stand alone .html file, plus head info */
{
_htmStartWithHead(f, head, title, TRUE, 1);
}
void htmStart(FILE *f, char *title)
/* Write the start of a stand alone .html file. */
{
htmStartWithHead(f, "", title);
}
void htmStartDirDepth(FILE *f, char *title, int dirDepth)
/* Write the start of a stand alone .html file. dirDepth is the number of levels
* beneath apache root that caller's HTML will appear to the web client.
* E.g. if writing HTML from cgi-bin, dirDepth is 1; if trash/body/, 2. */
{
_htmStartWithHead(f, "", title, TRUE, dirDepth);
}
/* Write the end of an html file */
void htmEnd(FILE *f)
{
fputs("\n</BODY>\n</HTML>\n", f);
}
/* Write the end of a stand-alone html file */
void htmlEnd()
{
htmEnd(stdout);
}
void htmlBadVar(char *varName)
{
cgiBadVar(varName);
}
/* Display centered image file. */
void htmlImage(char *fileName, int width, int height)
{
printf("<P ALIGN=\"CENTER\"><IMG SRC=\"%s\" WIDTH=\"%d\" HEIGHT=\"%d\" ALIGN=\"BOTTOM\" BORDER=\"0\"></P>", fileName, width, height);
}
void htmErrOnlyShell(void (*doMiddle)())
/* Wrap error recovery around call to doMiddle. */
{
int status;
/* Set up error recovery. */
status = setjmp(htmlRecover);
/* Do your main thing. */
if (status == 0)
{
doMiddle();
}
}
void htmEmptyShell(void (*doMiddle)(), char *method)
/* Wrap error recovery and and input processing around call to doMiddle. */
{
int status;
/* Set up error recovery (for out of memory and the like)
* so that we finish web page regardless of problems. */
pushAbortHandler(htmlAbort);
pushWarnHandler(htmlVaWarn);
status = setjmp(htmlRecover);
/* Do your main thing. */
if (status == 0)
{
doMiddle();
}
popWarnHandler();
popAbortHandler();
}
/* Wrap an html file around the passed in function.
* The passed in function is already in the body. It
* should just make paragraphs and return.
*/
void htmShell(char *title, void (*doMiddle)(), char *method)
{
/* Preamble. */
dnaUtilOpen();
htmlStart(title);
/* Call wrapper for error handling. */
htmEmptyShell(doMiddle, method);
/* Post-script. */
htmlEnd();
}
/* Wrap an html file around the passed in function.
* The passed in function is already in the body. It
* should just make paragraphs and return.
* Method should be "query" or "get" or "post".
param title - The HTML page title
param head - The head text: can be a refresh directive or javascript
param method - The function pointer to execute in the middle
param method - The browser request method to use
*/
void htmShellWithHead( char *title, char *head, void (*doMiddle)(), char *method)
{
/* Preamble. */
dnaUtilOpen();
puts("Content-Type:text/html");
puts("\n");
puts("<HTML>");
printf("<HEAD>%s<TITLE>%s</TITLE>\n</HEAD>\n\n", head, title);
if (htmlBackground == NULL)
puts("<BODY>\n");
else
printf("<BODY BACKGROUND=\"%s\">\n", htmlBackground);
#ifdef WARNBOX_IN_USE
htmlWarnBoxSetup(stdout, 1);// Sets up a warning box which can be filled with errors as they occur
#endif//def WARNBOX_IN_USE
/* Call wrapper for error handling. */
htmEmptyShell(doMiddle, method);
/* Post-script. */
htmlEnd();
}
/* Include an HTML file in a CGI */
void htmlIncludeFile(char *path)
{
char *str = NULL;
size_t len = 0;
if (path == NULL)
errAbort("Program error: including null file");
if (!fileExists(path))
errAbort("Missing file %s", path);
readInGulp(path, &str, &len);
if (len <= 0)
errAbort("Error reading included file: %s", path);
puts(str);
freeMem(str);
}
/* Include an HTML file in a CGI.
* The file path is relative to the web server document root */
void htmlIncludeWebFile(char *file)
{
char path[256];
char *docRoot = "/usr/local/apache/htdocs";
safef(path, sizeof path, "%s/%s", docRoot, file);
htmlIncludeFile(path);
}