/* File: genFlkPair.c: 
 * 
 *    Proxy server - generate Flicker Pair HTML code for invoking Flicker
 *    Java program on two images
 *
 * Peter Lemkin
 * Image Processing Section
 * Laboratory of Mathematical Biology
 * National Cancer Institute, FCRDC
 * Frederick, MD 21702
 *
 *    Written 1997  by Peter F. Lemkin. lemkin@ncifcrf.gov
 *

 * $Date: 1998/11/20 22:55:30 $  $Revision: 2.5 $
 *
 * NOTE: (1) change all references to www.yourServer.org to your server.
 *       (2) set DBUG_HOST to your IP addres if debugging...
 */

/*  
 * Introduction
 * ============

  There are several ways for images to be specified using the CGI-BIN
  (NAME,VALUE) pairs from the form.  Two images are required: These
  include:

  NAME             VALUE

  "PARAM.<name>"   (opt) generate a <PARAM NAME=name VALUE=value> for the 
                   APPLET

  "PARAMS.list"    n1=v1;n2=v2;...;nk=vk. This lets you pack k (N,V) pairs
                   of PARAM.<name> entries into a single parameter which
		   is useful for SELECT lists. These are unpacked and used
		   as if they were entered separately.

  "WebServer"      (opt) Use this Web server base address instead of NCI' F.S.
                   eg. <PARAM NAME=WebServer 
		        VALUE=http://www.yourServer.org>

  "relDir"         (opt) Add this to the WebServer instead of "."
                   The alternative is 
		   eg. <PARAM NAME=ImgCodebase
		        VALUE=http://www.yourServer.org/otherDB/Gifs/>

  "JavaPlugin"     (opt) Generate Javascript/HTML to load JavaPlugin 1.1.1
                   rather than simple <APPLET> code. NOT YET...

  "ZoomImg"        (opt) Zoom size 1X to 8X.
                   eg. <PARAM NAME=ZoomImg VALUE=1>

  "CanvasSize"     (opt) Change the default canvas size from 220 to new size
                   with a max value of 352 pixels (1.6X)
                   if not 220, then resize HEIGHT and WIDTH by
		       height= 650 + 2*(canvasSize - 220));
		       width= 700 + 2*(canvasSize - 220);	
		   eg. <PARAM NAME=CanvasSize VALUE=220>

  "image1"         the gif image relative to /flicker/ in F.S.
                   eg. <PARAM NAME=image1 VALUE=a00001.gif>
  "image2"         the gif image relative to /flicker/ in F.S.

  Radio Buttons
  =============
  "leftImage"      "image1_FS", "image1_2DWG", "image1_SEL_URL", "image1_URL"
  "rightImage"     "image2_FS", "image2_2DWG", "image2_SEL_URL", "image2_URL"

  Single instances
  ================
  "image1_FS"       gif image (w/o ".gif") rel. to /flicker/ in F.S
  "image1_2DWG"     full [-cp] http:// or [-cp] ftp:// URL
  "image1_SEL_URL"  full [-cp] http:// or [-cp] ftp:// URL
  "image1_URL"      full [-cp] http:// or [-cp] ftp:// URL

  "image2_FS"       gif image (w/o ".gif") rel. to /flicker/ in F.S
  "image2_2DWG"     full [-cp] http:// or [-cp] ftp:// URL
  "image2_SEL_URL"  full [-cp] http:// or [-cp] ftp:// URL
  "image2_URL"      full [-cp] http:// or [-cp] ftp:// URL

  multiple instances (exactly 2)
  ==============================
  "image_FS"       gif image (w/o ".gif") rel. to /flicker/ in F.S
  "image_2DWG"     full [-cp] http:// or [-cp] ftp:// URL
  "image_SEL_URL"  full [-cp] http:// or [-cp] ftp:// URL
  "image1_URL"      full [-cp] http:// or [-cp] ftp:// URL
 */

				
/* Example of CanvasSize menu

<SELECT NAME="CanvasSize">
<OPTION VALUE="220 SELECTED"> 1.0X (default)
<OPTION VALUE="176"> 0.8X
<OPTION VALUE="198"> 0.9X
<OPTION VALUE="242"> 1.1X
<OPTION VALUE="264"> 1.2X
<OPTION VALUE="286"> 1.3X
<OPTION VALUE="308"> 1.4X
<OPTION VALUE="330"> 1.5X
<OPTION VALUE="352"> 1.6X
</SELECT>Canvas Size

  */


/*
   Manifest 

   strMinCaseCmp() - compare strings s1 with s2 as with strcasecmp()
E  main() - process /cgi-bin/MdbName> command which must be a POST.
*/

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>


/* ----------------------------- */
/*        T Y P E D E F S        */
/* ----------------------------- */
typedef char *STRING;
typedef int BOOL;
typedef unsigned char BYTE;
typedef unsigned long ULONG;
#define TRUE 1
#define FALSE 0

/* #define DBUG_HOST "123.123.123.123"	/* restrict certain ops to this host */
#ifndef DBUG_HOST
#define DBUG_HOST "123.123.123.123"	/* restrict certain ops to this host */
#endif

#define NUMBER_ELEMENTS(arr) ((int) (sizeof(arr) / sizeof(arr[0])))

#ifndef MAX
#define MAX(x,y) (((x)>(y)) ? (x) : (y))
#endif

#ifndef MIN
#define MIN(x,y) (((x)<(y)) ? (x) : (y))
#endif

#ifndef ABS
#define ABS(x) (((x)<0) ? -(x) : (x))
#endif


/* C library forward declars */
STRING calloc(unsigned int nObjs, int sizeOfObj);
int isname(char s);

#define MAX_BUF_SIZE 50000
#define MAX_LINE 3000
#define MAX_FIELDS 50		/* max # fields that can use */
#define MAXENTRIES 120
#define MAX_JAVA_APPLET_PARAMS_NAMES 100 /* max # of <APPLET> params */

#define CANVAS_SIZE 220		/* Flicker canvas size in pixels */
#define BROWSER_HEIGHT 650	/* Flicker browser size in pixels */
#define BROWSER_WIDTH 700	/* Flicker browser size in pixels */

/* NOTE: no trailing '/' */
#define WSERVER_BASE "http://www.yourServer.org"

#define WGET_BASE "cgi-bin/getImageByWget?"

#define FLICKER_SERVER "flicker"

#define NCI_FLICKER_HOME_PAGE "http://www.yourServer.org/flicker"


#define PLUGINADDRESS "No JDK 1.1 Support for Applet!\
 use <A HREF=http://java.sun.com/products/plugin/1.1.1/index.html>\
 Download JavaPlug-in</A>"






/* Object for passing argument data to create JavaPlugin javascript 
 * strings for use with generating browser independent <APPLET> code. 
 */
typedef struct __JAVADATA_T__
  { 
    /* Optional args to a particular <APPLET> */
    int
     nEntries;			/* [0:nEntires-1] */
    STRING 
     paramName[MAX_JAVA_APPLET_PARAMS_NAMES], /*<PARAM> [0:nEntires-1]*/
     paramValue[MAX_JAVA_APPLET_PARAMS_NAMES]; /*<PARAM> arg if not NULL */

    /* These MUST be filled in */
    STRING
      align,			/* attribute for alignment */
      width,			/* width of window */
      height,			/* height of window */
      code,			/* mapped to PARAM code */
      codebase,			/* mapped to PARAM codebase */
      java_code,		/* remapped from code */
      java_codebase;		/* remapped from codebase */

    STRING 
      headerJavaScriptStr,	/* RTN: javascript before <BODY> */
      appletStr;		/* RTN: javascript after <BODY> */
  } javadata_t;




typedef struct __ENTRY_T__
  { /* for saving data  (name,value) from POST stream */
    STRING name;		/* name */
    STRING val;			/* associated value */
    int lenName;		/* strlen(name) */
    int lenVal;			/* strlen(val) */
  } entry_t;


typedef struct __DB__
  {
    int
      argc;			/* # of args in UNIX cmd line */
    STRING 
      *argv;			/* Array of args[0:argc-1] from UNIX cmd line*/
    entry_t
      entries[MAXENTRIES];	/* [0:nEntries] name, value pairs from POST */
    int
      nEntries,			/* # of (n,v) entries[] */
      lastGenIdx;		/* starting (N,V) GENERATOR index */
    STRING
      paramName[MAXENTRIES],	/* PARAM.name entries NAME */
      paramVal[MAXENTRIES];	/* PARAM.name entries VALUE */
    int
      nParamNames;		/* index for PARAM.name */
    int
      zoomImg,			/* flicker Zoom size 1X to 8X.*/
      canvasSize,		/* flicker canvas size in pixels */
      width,			/* flicker Browser size in pixels */
      height;			/* flicker Browser size in pixels */
    STRING
      webServer,		/* GUI: WebServer to use if not NCI. F.S. */
      relDir,			/* GUI: add this to WebServer instead of "." */
      ImgCodebase,		/* GUI: add this to WebServer instead of "." */
      imagePath,		/* either ImgCodebase or relDir */
      JavaPlugin,		/* GUI: generate JavaPlugin HTML instead of 
				 * simple <APPLET> */
      ZoomImg,			/* GUI Zoom size 1X to 8X.*/
      CanvasSize,		/* GUI: flicker canvas size */
      wget_base,		/* Full URL base address for 
				 * cgi-bin/getImageByWget */
      flicker_server,		/* Full URL base address for flicker */
      image1,			/* to display */
      image2,			/* to display */
      leftImage,		/* radio: image1_ (FS,_2DWG, _SEL_URL, _URL) */
      rightImage,		/* radio: image2_ (FS,_2DWG, _SEL_URL, _URL) */
      image1_FS,		/* Flicker server GIF image without .gif */
      image2_FS,		/* Flicker server GIF image without .gif */
      image1_2DWG,		/* URL from 2DWG list */
      image2_2DWG,		/* URL from 2DWG list */
      image1_SEL_URL,		/* URL from SEL_URL list */
      image2_SEL_URL,		/* URL from SEL_URL list */
      image1_URL,		/* typed in URL */
      image2_URL,		/* typed in URL */
      image1Title,		/* title to display */
      image2Title,		/* title to display */
      sampleTitle,		/* title for all HTML docs generated */
      authorHref,		/* HREF for author */
      authorName,		/* author */
      arg2,			/* NOT USED */
      arg3,			/* NOT USED */
      listOfImagesFile;		/* arg[1] is list of files - NOT USED */
    /* Env var from the WWW server */
    STRING
      path_info,		/* map name */
      query_string,		/* GET query string */
      remote_host,		/* host name for WWW user */
      remote_addr,		/* IP address for WWW user */
      remote_user,		/* name of remote WWW user if did PW auth */
      request_method,		/* "GET" or "POST" */
      content_type,		/* mime type */
      content_length;		/* mime length */
    BOOL
      dbugFlag;			/* Debug if DB.dbugFlag=="on" */
  } db_t;



/* Lookup table to map chars to upper case */
static int
  chMap[256];





/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * readEntireFile() - read file into a dynamic allocated buffer and
 * terminate with a '\0'.
 * Return NULL if fail, else return the full file as one string.
 */
static STRING readEntireFile(STRING file)
{/* readEntireFile */
  char
    *sBuf= NULL;		/* Will contain entire file. */
  FILE
    *fp;
  struct stat 
    statBuf;			/* status for file gives file size */
  unsigned int
    fileSize;

  if (file == NULL)
    goto destructor;
  fp= fopen(file, "r");
  if (fp == NULL)
    goto destructor;

  if (fstat(fileno(fp), &statBuf) == -1)
    goto destructor;

  fileSize= statBuf.st_size;

  sBuf= calloc((fileSize+1), sizeof(char));
  if (sBuf == NULL)
    goto destructor;

  if (fread(sBuf, sizeof(char), fileSize, fp) != fileSize)
    {/* error */
      free(sBuf);
      sBuf= NULL;
    }/* error */
  else
    sBuf[fileSize]= '\0';

  fclose(fp);

 destructor:
  return(sBuf);
}/* readEntireFile */





/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * strMinCaseCmp() - compare strings s1 with s2 as with strcasecmp()
 * But stop comparison with shorter of the two strings.
 *
 * Return -2 if either s1 or s2 is NULL or strlen(s2 ) < lth
 * Return -1 if s1 < s2
 * Return 0 if s1 = s2 and |s1|==lth and |s2|>=lth
 * Return +1 if s1 > s2
 */
static int strMinCaseCmp(STRING s1, STRING s2, int lth)
{/* strMinCaseCmp */
  int
    n= 0;
  char
    ch1, ch2;

  if(s1==NULL || s2==NULL)
    return(-2);

  while((ch1= *s1)!='\0' && (ch2= *s2)!='\0')
    if(chMap[ch1] > chMap[ch2])
      return(1);
    else if(chMap[ch1] < chMap[ch2])
      return(-1);
    else
      {
	s1++;
	s2++;
	n++;
      }

  if(n<lth)
    return(-2);

  return(0);
}/* strMinCaseCmp */




/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * removeQuotes() - remove "'s and multiple spaces from string.
*/
static void removeQuotes(STRING str)
{/*removeQuotes */
  STRING
    p= str,
    q= str;
  char
    ch,
    lastch= '\0';

  while((ch = *p++))
    {
      if (ch=='"' || (ch==' ' && lastch==' '))
	continue;
      else
	{ /* copy it */
	  lastch= ch;
	  *q++ = ch;
	}
    }
  *q= '\0';			/* terminate it. */
}/*removeQuotes */




/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * copyStr() - make dynamic copy of the string arg
 * Return dynamic copy of the string else NULL.
*/
static STRING copyStr(STRING oldS)
{/* copyStr*/
  STRING
    newS= NULL;

  if(oldS!=NULL)
    {
      newS= (STRING)malloc(strlen(oldS)+1);
      strcpy(newS, oldS);
    }

  return(newS);
}/* copyStr*/





/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * printContentType() - print "Content-type: text/html\n\n" iff first time
 */
static void printContentType()
{/* printContentType*/ 
  static BOOL
    onceOnly= FALSE;

  if(!onceOnly)
    {
      onceOnly= TRUE;
      printf("Content-type: text/html\n\n");
    }
}/* printContentType*/ 




/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * todays_date() - Compute and return static time of day string.
 * EXAMPLE:
 *        06/11/1987, 10:51:52AM
 * Return date as STATIC string.
*/
static STRING todays_date()
{/* todays_date */
  struct tm
    *tim;
  static char
    sR[60];
  STRING
    ampm= NULL;
  long
    seconds, 
    minutes, 
    hours, 
    hyear, 
    hmonth,
    hday;

  seconds= (long)time((long)0);	/* go get the time... */

  /* Use UNIX system call, localtime(), to break up time elapsed since
   *  1/1/70 from gettimeofday() call above into year/month/day/etc.
   *  As of 7/25/88 UNIX is supplying a month value that is consistently
   *  one less than it ought to be. (e.g. July has value 6)
   */
  tim= localtime(&seconds);
  seconds= tim->tm_sec;
  minutes= tim->tm_min;
  hours= tim->tm_hour;
  hday= tim->tm_mday;
  hmonth= tim->tm_mon + 1;
  hyear= tim->tm_year + 1900;

  if (hours>12)
    {/* PM */
      hours -= 12;
      ampm= "PM";
    }/* PM */
  else ampm= "AM";

  sprintf(sR,"%02d/%02d/%4d, %02d:%02d:%02d%s",
	  hmonth,hday,hyear,hours,minutes,seconds,ampm);

  return(sR);
}/* todays_date */





/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * serverErr() - print the mapping server error and exit with error (-1)
 */
static void serverErr(STRING msg)
{/*serverErr*/ 
  printContentType();
  printf("<TITLE>%s</TITLE>\n",msg);
  printf("<H1>%s</H1>\n",msg);

  exit(-1);
}/*serverErr*/ 







/* ***********************************************************************
 * chkNull() - if NULL, then return 'NULL' else string.
 */
STRING chkNull(STRING msg)
{/*chkNull*/ 
  static char
    ch,
    sBuf[4*MAX_LINE];
  STRING
    s= (msg==NULL) ? "NULL" : msg,
    p= s,
    q= sBuf;
  
  *q= '\0';
  while((ch= *s++))
    {
      if(ch!='<')
	*q++= ch;
      else
	{/* map '<' to "&lt; */
	  *q++= '&';
	  *q++= 'l';
	  *q++= 't';
	  *q++= ';';
	}
    }

  *q= '\0';			/* terminate it */

  return(sBuf);
}/*chkNull*/ 




/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * printDebug() - print debug state
 */
static void printDebug(db_t *db, STRING msg, int line)
{/* printDebug */
  int
    i;
  
  printContentType();	/* DEBUG */
  printf("<H1>DBUG: %s:line#%d</H1>", chkNull(msg), line);

  printf("<LI><B>db-&gt;query_string</B>='%s'",chkNull(db->query_string));
  printf("<LI><B>db-&gt;db->path_info</B>='%s'",chkNull(db->path_info));
  printf("<LI><B>db-&gt;db->remote_addr</B>='%s'",chkNull(db->remote_addr));
  printf("<LI><B>db-&gt;db->remote_host</B>='%s'",chkNull(db->remote_host));
  printf("<LI><B>argc</B>='%d'",db->argc);
  if(db->argc>=1)
    printf("<LI><B>argv[0]</B>='%s'", chkNull(db->argv[0]));
  if(db->argc>=2 && db->argv[1]) 
    printf("<LI><B>argv[1]</B>='%s'", chkNull(db->argv[1]));
  printf("<LI><B>db-&gt;dbugFlag</B>='%d'",db->dbugFlag);
  printf("<LI><B>db-&gt;nEntries</B>='%d'",db->nEntries);
  printf("<LI><B>db-&gt;webServer</B>='%s'", chkNull(db->webServer));
  printf("<LI><B>db-&gt;relDir</B>='%s'", chkNull(db->relDir));
  printf("<LI><B>db-&gt;ImgCodebase</B>='%s'", chkNull(db->ImgCodebase));
  printf("<LI><B>db-&gt;wget_base</B>='%s'", chkNull(db->wget_base));
  printf("<LI><B>db-&gt;flicker_server</B>='%s'", chkNull(db->flicker_server));
  printf("<LI><B>db-&gt;listOfImagesFile</B>='%s'",
	 chkNull(db->listOfImagesFile));
  printf("<LI><B>db-&gt;image1</B>='%s'", chkNull(db->image1));
  printf("<LI><B>db-&gt;image2</B>='%s'", chkNull(db->image2));
  printf("<LI><B>db-&gt;image1Title</B>='%s'", 
	 chkNull(db->image1Title));
  printf("<LI><B>db-&gt;image2Title</B>='%s'", 
	 chkNull(db->image2Title));
  printf("<LI><B>db-&gt;leftImage</B>='%s'", chkNull(db->leftImage));
  printf("<LI><B>db-&gt;rightImage</B>='%s'", chkNull(db->rightImage));
  printf("<LI><B>db-&gt;sampleTitle</B>='%s'", chkNull(db->sampleTitle));
  printf("<LI><B>db-&gt;authorHref</B>='%s'", chkNull(db->authorHref));
  printf("<LI><B>db-&gt;authorName</B>='%s'", chkNull(db->authorName));
  printf("<HR>\n");
  
  for(i= 0; i<db->nEntries; i++)
    {
      STRING 
	name= db->entries[i].name,
	val= db->entries[i].val;

      printf("<LI><B>entries[%d].name</B>='%s' entries[%d].val</B>='%s'", 
	     i, chkNull(name), i, chkNull(val));
    }
} /* printDebug */




/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * getline() -  get line from input stream and return EOF status
 * Derived from the NCSA HTTP util.c code.
 * REturn data in preallocated buffer of max size [0:n-1].
 * Return value of feof(f).
*/
static int getline(STRING s, int n, FILE *f)
{/*getline*/
  register int 
    nMinus1= (n-1),
    i= 0;

  while(TRUE) 
    {
      register int
	ch= (char)getc(f);
      
      if(ch == '\r')
	ch= getc(f);
      
      if((ch == '\n') || (ch == EOF) || (i == nMinus1))
	{
	  *s= '\0';
	  return(feof(f) ? TRUE : FALSE);
        }
      else *s++ = ch;
      ++i;
    }
}/*getline*/






/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * fmakeword() - scan "word" to stop char from FILE stream  
 * Derived from the NCSA HTTP util.c code.
 * Return word as dynamic string.
*/
static STRING fmakeword(FILE *f, char stop, int *contentLth)
{/*fmakeword*/
  int 
    ll= 0,
    deltaSize= 256,
    wsize= deltaSize;
  STRING 
    word;
  
    word= (STRING)malloc(sizeof(char) * (wsize + 1));
  
  while(TRUE) 
    {
      char
	ch= (char)getc(f);

      word[ll]= ch;
      if(ll==wsize)
	{
	  word[ll+1]= '\0';
	  wsize += deltaSize;
	  word= (STRING)realloc(word,sizeof(char)*(wsize+1));
        }

      --(*contentLth);

      if((word[ll] == stop) || feof(f) || !(*contentLth))
	{
	  STRING
	    sWord;

	  if(word[ll] != stop)
	    ll++;
	  word[ll]= '\0';
	  sWord= copyStr(word);
	  free(word);
	  return(sWord);
        }
      ++ll;
    }
}/*fmakeword*/




/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * plustospace() - map '+' or '\t' to ' ' in string to map data from browser 
 * Derived from the NCSA HTTP util.c code.
*/
static void plustospace(STRING str)
{/*plustospace*/
  register int
    x;
  
  for(x=0;str[x];x++)
    if(str[x] == '+' || str[x] == '\t')
      str[x]= ' ';
}/*plustospace*/





/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * x2c() - convert 2 hex string digits to an 8-bit byte.
 * Derived from the NCSA HTTP util.c code.
 * Return 8-bit byte.
 */
static BYTE x2c(STRING hexStr)
{/*x2c*/
  register char
    digit;
  
  digit= ((hexStr[0] >= 'A')
	  ? ((hexStr[0] & 0xdf) - 'A')+10 
	  : (hexStr[0] - '0'));
  digit= (digit << 4);		/* move to left nibble */
  digit += ((hexStr[1] >= 'A')
	    ? ((hexStr[1] & 0xdf) - 'A')+10
	    : (hexStr[1] - '0'));
  return(digit);
}/*x2c*/




/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * unescape_url() - unescape the  '%' escaped characters in URL string
 * Derived from the NCSA HTTP util.c code.
*/
static void unescape_url(STRING url)
{/*unescape_url*/
  register int
    x,
    y;
  
  for(x=0,y=0;url[y];++x,++y) 
    {
      if((url[x]= url[y]) == '%')
	{
	  url[x]= x2c(&url[y+1]);
	  y += 2;
        }
    }
  url[x]= '\0';
}/*unescape_url*/





/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * makeword() - scan "word" to stop char from line which is shortend
 * Derived from the NCSA HTTP util.c code.
 * Return dynamic STRING. "\0" if lines is "\0".
*/
static STRING makeword(STRING line, char stop)
{/*makeword */
  int 
    x= 0,
    y;
  STRING
    word= (STRING)malloc(sizeof(char) * (strlen(line) + 1));

  for(x=0;((line[x]) && (line[x] != stop));x++)
    word[x]= line[x];

  word[x]= '\0';
  if(line[x]) 
    ++x;
  y= 0;
  
  while(line[y++]= line[x++])
    ;
  return(word);
}/*makeword */





/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * getValFromNVlist() - find VALUE string  if name in entries[] (N,V) list.
 * Return VALUE field of (N,V) pair if found N.
*/
static STRING getValFromNVlist(db_t *db, STRING chkName)
{/*getValFromNVlist*/
  STRING
    sVal= NULL;			/* fail is default */
  int
    i;

  /* Brute force search for matching entry to get its value. */
  for(i=0; i<db->nEntries; i++)
    {
      STRING
	s= db->entries[i].name;
      
      if(strcmp(s, chkName)==0)
	{
	  sVal= db->entries[i].val;
	  break;
	}
    }

  return(sVal);
}/*getValFromNVlist*/



/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * pushPARAMnameValue(db,name,eVal);
 */
static void pushPARAMnameValue(db_t *db, STRING name, STRING eVal)
{ /* pushPARAMnameValue */
  db->paramName[db->nParamNames]= copyStr(name);
  db->paramVal[(db->nParamNames)++]= copyStr(eVal);

#ifdef TEST_PARAM
  if(strcmp(db->remote_addr,DBUG_HOST)==0)
  { 
    int
      i= db->nParamNames-1;

    printContentType("text/html");
    printf("<H1>DBUG[1] pushPARAMnameValue():line#%d</H1>", __LINE__);
    printf("<LI><B>db->nParamNames</B>='%d'",db->nParamNames);
    printf("<LI><B>db->paramName[i]</B>='%s'",chkNull(db->paramName[i]));
    printf("<LI><B>db->paramVal[i]</B>='%s'",chkNull(db->paramVal[i]));
    printf("<P>\n");
    fflush(stdout);
  }
#endif

} /* pushPARAMnameValue */



/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * isNameInEntries() - test if string is in the entries[] (N,V) list.
 * Return TRUE if found
*/
static BOOL isNameInEntries(db_t *db, STRING chkName)
{/*isNameInEntries*/
  BOOL
    flag= FALSE;

  flag= (getValFromNVlist(db, chkName) != NULL);

  return(flag);
}/*isNameInEntries*/




/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * getNthValFromNVlist() - find VALUE string  if name in entries[] (N,V) list.
 * Return VALUE field of (N,V) pair if found N. If nthItem>0,
 * then get the nth one
*/
static STRING getNthValFromNVlist(db_t *db, STRING chkName, int nthItem)
{/*getNthValFromNVlist*/
  STRING
    sVal= NULL;			/* fail is default */
  int
    i;

  /* Brute force search for matching entry to get its value. */
  for(i=0; i<db->nEntries; i++)
    {
      STRING
	s= db->entries[i].name;
      
      if(strcmp(s, chkName)==0)
	{
	  if(--nthItem>0)
	    continue;		/* keep looking */
	  sVal= db->entries[i].val;
	  break;
	}
    }

  return(sVal);
}/*getNthValFromNVlist*/




/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * pushNVentry()- push(eName, eVal) ==>db->entries[db->nEntries] PDL.
 */
static void pushNVentry(db_t *db, STRING eName, STRING eVal)
{/* pushNVentry */
  int
    i= (db->nEntries);

  if(i>=MAXENTRIES)
    serverErr("Opps- you tried to pushNVentry() more than MAXENTIRES");

  /* Push data */
  db->entries[i].name= eName;
  db->entries[i].val= eVal;
  
  /* Save lengths */
  db->entries[i].lenName= strlen(eName);
  db->entries[i].lenVal= strlen(eVal);

  ++(db->nEntries);		/* # pushed */
}/* pushNVentry */





/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * readNVlist() - read (N,V) list from stdin into entries.
*/
static BOOL readNVlist(db_t *db)
{/* readNVlist */
  int
    contentLth,
    i,
    x;
  static BOOL
    onceOnlyFlag= FALSE;

  if(onceOnlyFlag)
    serverErr("DRYROT - called readNVlist() more than once!");
  else onceOnlyFlag= TRUE;

  /* Init and parse */
  db->nEntries= 0;		        /* clear the list */
  if(strcmp(db->request_method,"POST")!=0)
    serverErr("Must use POST method");

  contentLth= atoi(db->content_length);	/* # bytes from server on stdin */



  /* Read (name,value) pairs from stdin piped from the HTTP server. */
  for(x=0;contentLth && (!feof(stdin));x++)
    {/* Read the (N,V) entries from stdin */
      STRING
	eName,
	eVal;

      eVal= fmakeword(stdin,'&',&contentLth);
      plustospace(eVal);	/* cvt '+' or '\t' to space */
      unescape_url(eVal);
      eName= makeword(eVal, '=');

      pushNVentry(db,eName,eVal); /* push (eName, eVal)
				   * ==>db->entries[] PDL.*/
#ifdef NOTE_STOP_HERE
      {
	printContentType();		/* DEBUG */
	printf("<LI>DBUG[readNVlist()] <B>#%d, eName='%s'",
	       db->nEntries,chkNull(eName));
	printf(", eVal='%s'",
	       chkNull(eVal));
	printf("<BR>\n");
	fflush(stdout);
      }
#endif

      /* Process any addition special "PARAMS.list=n1=v1;n2=v2;..."
       * NV entries. Parse them and push into NV list.
       */
      if(strncasecmp(eName,"PARAMS.list",11)==0)
	{/* push the escape list */
	  char
	    nvPair[MAX_LINE],
	    sTmp[MAX_LINE];
	  STRING
	    s1,
	    s2;
	  
	  strcpy(sTmp,eVal);
	  while(*sTmp!='\0')
	    {
	      STRING
		nvPair= makeword(sTmp,';'), /* delimit by ';' */
		name= makeword(nvPair,'='),
		val= nvPair;
	      if(name!=NULL)
		pushNVentry(db,name,val);
	    }
	}

    }/* Read the (N,V) entries from stdin */


  /* Post-process any addition special "PARAM.name=value" NV entries */
  for(i=0;i<db->nEntries;i++)
    {
      STRING
	eName= db->entries[i].name,
	eVal= db->entries[i].val;

      if(strncasecmp(eName,"PARAM.",6)==0)
	{/* push the escape list */
	  STRING
	    name= &(eName[6]);
	  
	  pushPARAMnameValue(db,name,eVal);
	}	    
    }

  return(TRUE);
}/* readNVlist */





/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * getEnvVar() - get environment variables set by the server
 */
static void getEnvVar(db_t *db)
{/*getEnvVar*/
  db->query_string= (STRING)getenv("QUERY_STRING");
  db->path_info= (STRING)getenv("PATH_INFO");

  /* The rest of these are use only for POST */
  db->remote_host= (STRING)getenv("REMOTE_HOST");
  db->remote_addr= (STRING)getenv("REMOTE_ADDR");
  db->remote_user= (STRING)getenv("REMOTE_USER");
  db->request_method= (STRING)getenv("REQUEST_METHOD");
  db->content_type= (STRING)getenv("CONTENT_TYPE");
  db->content_length= (STRING)getenv("CONTENT_LENGTH");

  /* Make sure not NULL */
  db->query_string= (db->query_string==NULL) ? "" : db->query_string;
  db->path_info=    (db->path_info==NULL)    ? "" : db->path_info;
  db->remote_host=  (db->remote_host==NULL)  ? "" : db->remote_host;
  db->remote_addr=  (db->remote_addr==NULL)  ? "" : db->remote_addr;
  db->remote_user=  (db->remote_user==NULL)  ? "" : db->remote_user;
  db->request_method= (db->request_method==NULL) 
                          ? "" : db->request_method;
  db->content_type=  (db->content_type==NULL)    
                          ? "" : db->content_type;
  db->content_length= (db->content_length==NULL) 
                          ? "" : db->content_length;

}/*getEnvVar*/





/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * init_db() - init the db_t data structure
 */
static void init_db(db_t *db, int argc, STRING argv[])
{/* init_db */
  BOOL
    flag= FALSE;
  int
    nbr_image_FS= 0,
    nbr_image_URL= 0,
    nbr_image_2DWG= 0,
    nbr_image_SEL_URL= 0,
    i;
  STRING
    sT,
    s;
  char
    tmp[MAX_LINE],
    sBuf[MAX_LINE];
  FILE
    *fp;

  /* [1] Defaults  */
  db->nEntries= 0;		/* for # entries[] */
  db->nParamNames= 0;		/* # of PARAM.name's */

  db->argc= argc;		/* save arg list */
  db->argv= argv;

  /* Setup character map to map all characters to UPPER CASE*/
  for(i=0; i<256; i++)
    chMap[i]= ('a'<=i && i<='z') ? (i+'A'-'a') : i;


  /* [2] Get environment variables set by the server. */
  getEnvVar(db);		/* get environment variables */


  /* [2.0.1] */
  if(db->query_string==NULL)
    serverErr("Need QUERY_STRING variable");
  else
    {/* Normal FORM processing */
      char
	sTmp[MAX_LINE];

      /* Note: all args come from the query_string in FORMS */
      strncpy(sTmp, db->query_string, (size_t)(MAX_LINE-1));
      db->listOfImagesFile= makeword(sTmp, ',');
      db->arg2= makeword(sTmp, ','); /* NOT USED */
      db->arg3= makeword(sTmp, ','); /* NOT USED */
    }/* Normal FORM processing */


  /* [2.1] Read arglist and (name,value) pairs from stdin piped 
   * from the HTTP server.
   */
  readNVlist(db);		/* read the (N,V) list from WWW client */


  /* [2.2] Make sure there are no more than 2 generic items from any one
   * list.
   */
  for(i=0; i<db->nEntries; i++)
    if(strcmp(db->entries[i].name,"image_FS")==0)
      nbr_image_FS++;	/* count them */
    else if(strcmp(db->entries[i].name,"image_URL")==0)
      nbr_image_URL++;	/* count them */
    else if(strcmp(db->entries[i].name,"image_2DWG")==0)
      nbr_image_2DWG++;	/* count them */
    else if(strcmp(db->entries[i].name,"image_SEL_URL")==0)
      nbr_image_SEL_URL++;	/* count them */

  if(nbr_image_FS>2 || nbr_image_URL>2 || nbr_image_2DWG>2 ||
     nbr_image_SEL_URL>2)
    serverErr("You should select at most 2 images from the any list\n\
 - backup and change selections and try again.");  


  /* [2.3] Setup special values */
  db->webServer= getValFromNVlist(db, "WebServer");
  db->relDir= getValFromNVlist(db, "relDir");
  db->ImgCodebase= getValFromNVlist(db, "ImgCodebase");
  db->JavaPlugin= getValFromNVlist(db, "JavaPlugin");
  db->ZoomImg= getValFromNVlist(db, "ZoomImg");
  db->CanvasSize= getValFromNVlist(db, "CanvasSize");
  db->dbugFlag= (getValFromNVlist(db, "DB.dbugFlag")!=NULL);
  db->sampleTitle= getValFromNVlist(db, "sampleTitle");
  db->authorHref=  getValFromNVlist(db, "authorHref");
  db->authorName= getValFromNVlist(db, "authorName");  

  /* Get radio buttons if define. */
  db->leftImage= getValFromNVlist(db, "leftImage");  
  db->rightImage= getValFromNVlist(db, "rightImage");  


  /* [2.3.1] Set defaults */
  if(db->leftImage==NULL)
    db->leftImage= "";
  if(db->rightImage==NULL)
    db->rightImage= "";

  if(db->sampleTitle==NULL)
    db->sampleTitle= "samples";

  if(db->authorHref==NULL)
    db->authorHref= "";

  if(db->authorName==NULL)
    db->authorName= "";

 /* Pick the proper codebase */
  if(db->ImgCodebase!=NULL && db->relDir==NULL)
    db->imagePath= db->ImgCodebase;
  else if(db->ImgCodebase==NULL && db->relDir!=NULL)
    db->imagePath= db->relDir;
  else if(db->ImgCodebase!=NULL)
    db->imagePath= db->ImgCodebase; /* NOTE: default if both sepcified. */
  else db->imagePath= "";           /* NOTE: default to htdocs/flicker */


  /* [2.3.2] Resize Canvas and browser window */
  db->canvasSize= CANVAS_SIZE;	/* default size */
  db->height= BROWSER_HEIGHT;	/* default size */
  db->width= BROWSER_WIDTH;	/* default size */

  if(db->ZoomImg==NULL)
    db->zoomImg= 1;
  else
    {/* make sure in range of 1 to 8X */
      int
	mag= atoi(db->ZoomImg);

      db->zoomImg= MAX(8, MIN(mag,8));
    }

  if(db->CanvasSize!=NULL && isdigit(*(db->CanvasSize)))
    {/* resize it and make sure in legal range */
      int
       size= atoi(db->CanvasSize);

      db->canvasSize= MAX(100, MIN(size,512));
      if(db->canvasSize>CANVAS_SIZE)
	{/* Scale size of Flicker browser window */
	  db->height= BROWSER_HEIGHT + 2*(db->canvasSize - CANVAS_SIZE);
	  db->width= BROWSER_WIDTH + 2*(db->canvasSize - CANVAS_SIZE);	
	}
    }

#ifdef NOTE_STOP_HERE
  if(strcmp(db->remote_addr,DBUG_HOST)==0)
  { 
    printContentType("text/html");
    printf("<H1>DBUG[2.3.2] init_db():line#%d</H1>", __LINE__);
    printf("<LI><B>db->CanvasSize</B>='%s'",chkNull(db->CanvasSize));
    printf("<LI><B>db->canvasSize</B>='%d'", db->canvasSize);
    printf("<LI><B>db->width</B>='%d'",db->width);
    printf("<LI><B>db->height</B>='%d'",db->height);
    fflush(stdout);
  }
#endif


  /* [2.4] Get default (image1,image2) values if any */
  s= getValFromNVlist(db, "image1");  
  if(s!=NULL)
    {/* specify it directly */
      db->image1Title= copyStr(s);
      sT= makeword(s,' ');	          /* up to " -..." */
      if(strncmp(s,"http://", 7)==0 || strncmp(s,"ftp://", 5)==0 ||
	 strncmp(&s[1],"http://", 7)==0 || strncmp(&s[1],"ftp://", 5)==0)
	sprintf(tmp,"%s",sT);
      else sprintf(tmp,"%s.gif",sT);       /* ADD on .gif */
      db->image1= copyStr(tmp);
    }
  else
    {
      db->image1= NULL;
      db->image1Title= NULL;
    }
  
  s= getValFromNVlist(db, "image2");  
  if(s!=NULL)
    {/* specify it directly */
      db->image2Title= copyStr(s);
      sT= makeword(s,' ');	          /* up to " -..." */
      if(strncmp(s,"http://", 7)==0 || strncmp(s,"ftp://", 5)==0 ||
	 strncmp(&s[1],"http://", 7)==0 || strncmp(&s[1],"ftp://", 5)==0)
	sprintf(tmp,"%s",sT);
      else sprintf(tmp,"%s.gif",sT);       /* ADD on .gif */
      db->image2= copyStr(tmp);
    }
  else 
    {
      db->image2= NULL;
      db->image2Title= NULL;
    }


  /* [2.5] Test if Flicker Server _FS data - note .gif missing */
  if(db->leftImage!=NULL && strcmp(db->leftImage,"image1_FS")==0)
    { /* Get images and strip off title if any */
      s= getValFromNVlist(db, "image1_FS");
      if(s==NULL)
	serverErr(
         "You forgot to set the LEFT image in the NCI Flicker Server list");
      else
	{
	  db->image1Title= copyStr(s);
	  sT= makeword(s,' ');	          /* up to " -..." */
	  sprintf(tmp,"%s.gif",sT);       /* ADD on .gif */
	  db->image1= copyStr(tmp);
	}
    }

  if(db->rightImage!=NULL && strcmp(db->rightImage,"image2_FS")==0)
    { /* Get images and strip off title if any */
      s= getValFromNVlist(db, "image2_FS");
      if(s==NULL)
	serverErr(
     "You forgot to set the RIGHT image in the NCI Flicker Server list");
      else
	{
	  db->image2Title= copyStr(s);
	  sT= makeword(s,' ');	          /* up to " -..." */
	  sprintf(tmp,"%s.gif",sT);       /* ADD on .gif */
	  db->image2= copyStr(tmp);
	}
    }


  /* [2.5.1] Get the LEFT image from the list multiple "image_FS" list */
  if(db->leftImage!=NULL && strcmp(db->leftImage,"image_FS")==0)
    { /* Get images and strip off title if any */
      s= getNthValFromNVlist(db, "image_FS", 1);
      if(s==NULL)
	serverErr("You forgot to select the LEFT image in the FS list\n\
 - backup and change it and try again.");
      else
	{
	  db->image1Title= copyStr(s);
	  sT= makeword(s,' ');	          /* up to " -..." */
	  sprintf(tmp,"%s.gif",sT);
	  db->image1= copyStr(tmp);
	}
    }

  /* Test if 2DWG, then it is a 2ND image_FS entry */
  if(db->rightImage!=NULL && strcmp(db->rightImage,"image_FS")==0 &&
     db->leftImage!=NULL && strcmp(db->leftImage,"image_FS")==0 &&
     nbr_image_FS==2)
    { /* Get images and strip off title if any */
      s= getNthValFromNVlist(db, "image_FS", 2); /* NOTE: get 2ND one */
      if(s==NULL)
	serverErr("You forgot to select the RIGHT image in the FS list\n\
 - backup and change it and try again.");
      else
	{
	  db->image2Title= copyStr(s);
	  sT= makeword(s,' ');	          /* up to " -..." */
	  sprintf(tmp,"%s.gif",sT);
	  db->image2= copyStr(tmp);
	}
    }


  /* [2.6] Test if 2DWG, then it is a 2DWG http/ftp entry */
  if(db->leftImage!=NULL && strcmp(db->leftImage,"image1_2DWG")==0)
    { /* Get images and strip off title if any */
      s= getValFromNVlist(db, "image1_2DWG");
      if(s==NULL)
	serverErr(
		  "You forgot to set the LEFT image in the 2DWG list");
      else
	{
	  STRING
	    sColon= strchr(s,':'); /* check if http:// or ftp:// */

	  db->image1Title= copyStr(s);
	  sT= makeword(s,' ');	          /* up to " -..." */
	  if(sColon!=NULL)
	    sprintf(tmp,"%s",sT);
	  else sprintf(tmp,"%s.gif",sT);       /* ADD on .gif */
	  db->image1= copyStr(tmp);
	}
    }

  if(db->rightImage!=NULL && strcmp(db->rightImage,"image2_2DWG")==0)
    { /* Get images and strip off title if any */
      s= getValFromNVlist(db, "image2_2DWG");
      if(s==NULL)
	serverErr(
        "You forgot to set the RIGHT image in the 2DWG list");
      else
	{
	  STRING
	    sColon= strchr(s,':'); /* check if http:// or ftp:// */

	  db->image2Title= copyStr(s);
	  sT= makeword(s,' ');	          /* up to " -..." */
	  if(sColon!=NULL)
	    sprintf(tmp,"%s",sT);
	  else sprintf(tmp,"%s.gif",sT);       /* ADD on .gif */
	  db->image2= copyStr(tmp);
	}
    }


  /* [2.6.1] Get the LEFT image from the list multiple "image_2DWG" list */
  if(db->leftImage!=NULL && strcmp(db->leftImage,"image_2DWG")==0)
    { /* Get images and strip off title if any */
      s= getNthValFromNVlist(db, "image_2DWG", 1);
      if(s==NULL)
	serverErr(
       "You forgot to select the LEFT image in the 2DWG list\n\
 - backup and change it and try again.");
      else
	{
	  db->image1Title= copyStr(s);
	  sT= makeword(s,' ');	          /* up to " -..." */
	  sprintf(tmp,"%s",sT);
	  db->image1= copyStr(tmp);
	}
    }

  /* Test if 2DWG, then it is a 2ND image_2DWG entry */
  if(db->rightImage!=NULL && strcmp(db->rightImage,"image_2DWG")==0 &&
     db->leftImage!=NULL && strcmp(db->leftImage,"image_2DWG")==0 &&
     nbr_image_2DWG==2)
    { /* Get images and strip off title if any */
      s= getNthValFromNVlist(db, "image_2DWG", 2); /* NOTE: get 2ND one */
      if(s==NULL)
	serverErr(
     "You forgot to select the RIGHT image in the 2DWG list\n\
 - backup and change it and try again.");
      else
	{
	  db->image2Title= copyStr(s);
	  sT= makeword(s,' ');	          /* up to " -..." */
	  sprintf(tmp,"%s",sT);
	  db->image2= copyStr(tmp);
	}
    }


  /* [2.7] Test if SEL_URL, then it is a SEL_URL http/ftp entry */
  if(db->leftImage!=NULL && strcmp(db->leftImage,"image1_SEL_URL")==0)
    { /* Get images and strip off title if any */
      s= getValFromNVlist(db, "image1_SEL_URL");
      if(s==NULL)
	serverErr(
        "You forgot to set the LEFT image in the SELECT URL list");
      else
	{
	  STRING
	    sColon= strchr(s,':'); /* check if http:// or ftp:// */

	  db->image1Title= copyStr(s);
	  sT= makeword(s,' ');	          /* up to " -..." */
	  if(sColon!=NULL)
	    sprintf(tmp,"%s",sT);
	  else sprintf(tmp,"%s.gif",sT);  /* ADD on .gif */
	  db->image1= copyStr(tmp);
	}
    }

  if(db->rightImage!=NULL && strcmp(db->rightImage,"image2_SEL_URL")==0)
    { /* Get images and strip off title if any */
      s= getValFromNVlist(db, "image2_SEL_URL");
      if(s==NULL)
	serverErr(
        "You forgot to set the RIGHT image in the SELECT URL list");
      else
	{
	  STRING
	    sColon= strchr(s,':'); /* check if http:// or ftp:// */

	  db->image2Title= copyStr(s);
	  sT= makeword(s,' ');	          /* up to " -..." */
	  if(sColon!=NULL)
	    sprintf(tmp,"%s",sT);
	  else sprintf(tmp,"%s.gif",sT);  /* ADD on .gif */
	  db->image2= copyStr(tmp);
	}
    }


  /* [2.7.1] Get the LEFT image from the list multiple "image_SEL_URL" list */
  if(db->leftImage!=NULL && strcmp(db->leftImage,"image_SEL_URL")==0)
    { /* Get images and strip off title if any */
      s= getNthValFromNVlist(db, "image_SEL_URL", 1);
      if(s==NULL)
	serverErr(
       "You forgot to select the LEFT image in the SELECT URL list\n\
 - backup and change it and try again.");
      else
	{
	  STRING
	    sColon= strchr(s,':'); /* check if http:// or ftp:// */

	  db->image1Title= copyStr(s);
	  sT= makeword(s,' ');	          /* up to " -..." */
	  if(sColon!=NULL)
	    sprintf(tmp,"%s",sT);
	  else sprintf(tmp,"%s.gif",sT);  /* ADD on .gif */
	  db->image1= copyStr(tmp);
	}
    }

  /* Test if SEL_URL, then it is a 2ND image_SEL_URL entry */
  if(db->rightImage!=NULL && strcmp(db->rightImage,"image_SEL_URL")==0 &&
    db->leftImage!=NULL &&  strcmp(db->leftImage,"image_SEL_URL")==0 && 
     nbr_image_SEL_URL==2)
    { /* Get images and strip off title if any */
      s= getNthValFromNVlist(db, "image_SEL_URL", 2); /* NOTE: get 2ND one */
      if(s==NULL)
	serverErr(
       "You forgot to select the RIGHT image in the SELECT URL list\n\
 - backup and change it and try again.");
      else
	{
	  STRING
	    sColon= strchr(s,':'); /* check if http:// or ftp:// */

	  db->image2Title= copyStr(s);
	  sT= makeword(s,' ');	          /* up to " -..." */
	  if(sColon!=NULL)
	    sprintf(tmp,"%s",sT);
	  else sprintf(tmp,"%s.gif",sT);  /* ADD on .gif */
	  db->image2= copyStr(tmp);
	}
    }


  /* [2.8] Test if full URL, then it is a URL */
  if(db->leftImage!=NULL && strcmp(db->leftImage,"image1_URL")==0)
    { /* Get images and strip off title if any */
      s= getValFromNVlist(db, "image1_URL");
      if(s==NULL)
	serverErr(
        "You forgot to set the LEFT image URL");
      else
	{
	  db->image1Title= copyStr(s);
	  sT= makeword(s,' ');	          /* up to " -..." */
	  sprintf(tmp,"%s",sT);
	  db->image1= copyStr(tmp);
	}
    }

  if(db->rightImage!=NULL && strcmp(db->rightImage,"image2_URL")==0)
    { /* Get images and strip off title if any */
      s= getValFromNVlist(db, "image2_URL");
      if(s==NULL)
	serverErr(
        "You forgot to set the RIGHT image URL");
      else
	{
	  db->image2Title= copyStr(s);
	  sT= makeword(s,' ');	          /* up to " -..." */
	  sprintf(tmp,"%s",sT);
	  db->image2= copyStr(tmp);
	}
    }


  /* [2.8.1] Get the LEFT image from the list multiple "image_URL" list */
  if(db->leftImage!=NULL && strcmp(db->leftImage,"image_URL")==0)
    { /* Get images and strip off title if any */
      s= getNthValFromNVlist(db, "image_URL", 1);
      if(s==NULL)
	serverErr("You forgot to select the LEFT image in the URL list\n\
 - backup and change it and try again.");
      else
	{
	  db->image1Title= copyStr(s);
	  sT= makeword(s,' ');	          /* up to " -..." */
	  sprintf(tmp,"%s",sT);
	  db->image1= copyStr(tmp);
	}
    }

  /* Test if URL, then it is a 2ND image_URL entry */
  if(db->rightImage!=NULL && strcmp(db->rightImage,"image_URL")==0 &&
     db->leftImage!=NULL && strcmp(db->leftImage,"image_URL")==0 &&
     nbr_image_URL==2)
    { /* Get images and strip off title if any */
      s= getNthValFromNVlist(db, "image_URL", 2); /* NOTE: get 2ND one */
      if(s==NULL)
	serverErr("You forgot to select the RIGHT image in the URL list\n\
 - backup and change it and try again.");
      else
	{
	  db->image2Title= copyStr(s);
	  sT= makeword(s,' ');	          /* up to " -..." */
	  sprintf(tmp,"%s",sT);
	  db->image2= copyStr(tmp);
	}
    }


  /* [2.9] generate paths */
  /* Use overide Web server base address if specified, else use NCI server */
  if(db->webServer!=NULL && *(db->webServer)!='\0')
    {
      STRING
	s= &db->webServer[strlen(db->webServer)-1];
      if(s!=NULL && *s=='/')
	*s= '\0';		/* do not terminate with '/' */
    }
  else db->webServer= copyStr(WSERVER_BASE); /* base addr of the server */

  sprintf(tmp,"%s/%s",
	  db->webServer,WGET_BASE);      /* base addr for cgi getImageByWget */
  db->wget_base= copyStr(tmp);

  sprintf(tmp,"%s/%s",
	  db->webServer,FLICKER_SERVER); /* base addr for flicker */
  db->flicker_server= copyStr(tmp);


  /* [3] Set title to image file names if not found */
  if(db->image1Title==NULL)
    db->image1Title= copyStr(db->image1);
  if(db->image2Title==NULL)
    db->image2Title= copyStr(db->image2);
 
#ifdef NOTE_STOP_HERE
  {
    printDebug(db, "[3]: in init_db()", __LINE__);
    fflush(stdout);
  }
#endif

}/* init_db */




/* *************************************************************
 * newJavaData() - new (NV) data into JavaData object NV-list.
 * Stuff Applet dependent data .
 * Return FALSE if the stack is full.
 */
javadata_t *newJavaData(STRING width,   /* Applet height */
			STRING height,  /* Applet width */
			STRING align,   /* eg. absmiddle */
			STRING code,    /* Class name no ".class" */
			STRING codebase /* code base */
			)
{ /* newJavaData */
  javadata_t 
    *jData= (javadata_t *)calloc(1,sizeof(javadata_t));;

  jData->width= width;
  jData->height= height;
  jData->align= align;
  jData->code= code;
  jData->codebase= codebase;

  return(jData);
} /* newJavaData */




/* *************************************************************
 * pushJavaData() - push (NV) data into JavaData object NV-list.
 * Stuff Applet dependent data .
 * Return FALSE if the stack is full.
 */
BOOL pushJavaData(javadata_t *jData, STRING name, STRING value)
{ /* pushJavaData */
  if(jData->nEntries >= MAX_JAVA_APPLET_PARAMS_NAMES)
    return(FALSE);

  jData->paramName[jData->nEntries]= name;
  jData->paramValue[(jData->nEntries)++]= value;
  return(TRUE);
} /* pushJavaData */




/* *****************************************************************
 * flkJavaPlugin() - create javascript code for Java plugin
 * adaptation so it will work with Netscape and IE. 
 * Returns: headerJavaScriptStr, javascript for HTML above <BODY> tag.
 *          appletStr, javascript for HTML below <BODY> tag.
 * Return FALSE if there is an error with the javaData_t data.
 */
BOOL flkJavaPlugin(javadata_t *data, /* ARGS: */
		   STRING *headerJavaScriptStr, /* RTN: javascript HTML
						 * above <BODY> */
		   STRING *appletStr  /* RTN: javascript HTML below <BODY> */
		   )
{ /* flkJavaPlugin */
  int
    n;
  char
    s2Buf[20000],
    temp[20000];


  /* [1] Validate data */
  if(data->code==NULL || data->codebase==NULL || data->width==NULL ||
     data->height==NULL || data->align==NULL)
    return(FALSE);
   
  data->java_code= data->code;
  data->java_codebase= data->codebase;


  /* [2] HTML headers go here before javascript code below */
  sprintf(temp, "\n\
  <!-- The following code is specified at beginning of the <BODY> tag. -->\n\
  <!-- look for browser type IE or netscape -->\n\
\n\
     <SCRIPT LANGUAGE=\"JavaScript\"> <!--\n\
       var _info = navigator.userAgent; var _ns = false;\n\
       var _ie = (_info.indexOf(\"MSIE\") > 0 && _info.indexOf(\"Win\") > 0\n\
         && _info.indexOf(\"Windows 3.1\") < 0);\n\
     //--></SCRIPT>\n\
     <COMMENT><SCRIPT LANGUAGE=\"JavaScript1.1\"><!--\n\
       var _ns = (navigator.appName.indexOf(\"Netscape\") >= 0\n\
         && ((_info.indexOf(\"Win\") > 0 && _info.indexOf(\"Win16\") < 0\n\
         && java.lang.System.getProperty(\"os.version\").indexOf(\"3.5\")<0)\n\
             || _info.indexOf(\"Sun\") > 0));\n\
     //--></SCRIPT></COMMENT> \n\n");
  
  *headerJavaScriptStr= copyStr(temp);	/* java script code that goes 
				 * before <BODY> tag */



  /* [3] Generate string for insertion below <BODY tag. */
  sprintf(s2Buf, "<SCRIPT LANGUAGE=\"JavaScript\">\n\
if (_ie == true)\n document.writeln(\"<OBJECT \
 classid=clsid:8AD9C840-044E-11D1-B3E9-00805F499D93 \
width=%s  height=%s align=%s \
codebase=http://java.sun.com/products/plugin/1.1/jinstall-11-win32.cab#Version=1,1,0,0> ",
	  data->width, data->height, data->align);


  /* [4] Add the variable PARAMS to the <OBJECT>*/
  for(n=0;n<data->nEntries;n++)
    if(data->paramName[n]!=NULL && data->paramValue[n]!=NULL)
      {
	sprintf(temp, 
		"<PARAM NAME=%s VALUE=%s> ",
		data->paramName[n], data->paramValue[n]);
	strcat(s2Buf,temp);
      }


  /* [4.1] Get rest of static part of <OBJECT> which encrypts the
   * (code, codebase and type) with "java_" prefixes.
  */
 sprintf(temp, 
"<PARAM NAME=java_code VALUE=%s.class> \
<PARAM NAME=java_archive VALUE=%s.jar> \
<PARAM NAME=java_codebase VALUE=%s> \
<PARAM NAME=java_type VALUE=application/x-java-applet;version=1.1> \
%s </OBJECT>\");\n",
	 data->java_code, data->java_code, data->java_codebase, PLUGINADDRESS);
  strcat(s2Buf,temp);
 
  
  /* [5] For netscape, rest of the java script code that goes 
   * after <BODY> tag.
   */
  sprintf(temp, "\nelse if (_ns == true)\n\
document.writeln(\"<EMBED type=application/x-java-applet;version=1.1 \
code=%s.class archive=%s.jar codebase=%s \
width=%s height=%s align=%s \
pluginspage=http://java.sun.com/products/plugin/1.1/plugin-install.html ",
         data->code, data->code, data->codebase, 
         data->width, data->height, data->align);
   strcat(s2Buf,temp);


  /* [5.1] Add the variable PARAMS to the <EMBED>*/
  for(n=0;n<data->nEntries;n++)
    if(data->paramName[n]!=NULL && data->paramValue[n]!=NULL)
      {
	sprintf(temp, "%s=%s ",
		data->paramName[n], data->paramValue[n]);
	strcat(s2Buf,temp);
      }


  /* [5.2] Get rest of static part of <EMBED> */
  sprintf(temp, 
	  "%s> <NOEMBED><XMP>\");\n</SCRIPT>\n<APPLET code=%s.class\n \
codebase= %s\n align=%s\n width=%s height=%s>\n </XMP> \
<PARAM NAME= java_code  VALUE=%s.class>\n\
<PARAM NAME= java_codebase  VALUE=%s\n\
<PARAM NAME= java_type  VALUE=\"application/x-java-applet;version=1.1\">\n \
%s \n\
</APPLET>\n\n </NOEMBED> </EMBED> </OBJECT>\n", 
	  PLUGINADDRESS,
	  data->code,
	  data->codebase,
	  data->align,
	  data->width, 
	  data->height, 
	  data->code,
	  data->codebase,
	  PLUGINADDRESS);
  strcat(s2Buf,temp);


  /* [6] Finish returning strings. */
  *appletStr= copyStr(s2Buf);

  data->headerJavaScriptStr= *headerJavaScriptStr; /* copy to javaData 
						    * obj as well */
  data->appletStr= *appletStr;

  return(TRUE);
} /* flkJavaPlugin */





/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * createAppletStr() - create <APPLET> string for running Flicker
 * if the doJavaPlugin switch is set, then generate JavaPlugin
 * request code else just generate <APPLET> code.
 * If generating JavaPlugin code, return:
 *      headerJavaScriptStr - the javascript before <BODY>,
 * Always return:
 *      appletStr - the Applet or JavaPlugin Applet string in <BODY>.
 * Return the generated TRUE if successful.
 */
static BOOL createAppletStr(db_t *db,
			    BOOL doJavaPlugin,     /* ARG: */ 
			    STRING appletName,     /* ARG: */ 
			    STRING code,           /* ARG: */ 
			    STRING codeBaseSubDir, /* ARG */
			    int width, int height, /* ARG: */  
			    STRING serverURL,      /* ARG: */ 
			    STRING imageCodeBase,  /* ARG: */ 
			    STRING image1,         /* ARG: */ 
			    STRING image2,  	   /* ARG: */ 
			    STRING GifURL,	   /* ARG: opt. */
			    int zoomImg, 	   /* ARG: */ 
			    int canvasSize,        /* ARG: */
			    STRING *headerJavaScriptStr, /* RTN: */
			    STRING *appletStr	   /* RTN: */
			    )
{ /* createAppletStr */
  int
    i;
  char
    tmpBuf[MAX_LINE],
    aBuf[MAX_BUF_SIZE];

  if(width<=0 || height<=0 || serverURL==NULL || image1==NULL ||
     (image2==NULL && GifURL==NULL) || 
     (doJavaPlugin && headerJavaScriptStr==NULL) ||
     appletStr==NULL)
    return(FALSE);

  if(!doJavaPlugin)
    {/* generate <APPLET> code */
      sprintf(aBuf,
	     "<APPLET NAME=%s CODE=%s.class ARCHIVE=%s.jar\n",
	     appletName, code, code);

      sprintf(tmpBuf, "   WIDTH=%d HEIGHT=%d ALIGN=absmiddle\n", 
	      width, height);
      strcat(aBuf,tmpBuf);

      sprintf(tmpBuf,  "   CODEBASE=\"%s/%s\"\n", 
	      serverURL, codeBaseSubDir);
      strcat(aBuf,tmpBuf);

      strcat(aBuf,
      "ALT=\"You need a java-enabled browser to view Applet.\"\n");
      strcat(aBuf, ">\n");
      
      sprintf(tmpBuf, "<PARAM NAME=WebServer VALUE=%s>\n", serverURL);
      strcat(aBuf,tmpBuf);

      sprintf(tmpBuf,
	      "<PARAM NAME=ImgCodebase VALUE=%s/%s/>\n",
	      serverURL, imageCodeBase);
      strcat(aBuf,tmpBuf);

      sprintf(tmpBuf, "<PARAM NAME=image1 VALUE=%s.gif>\n", image1);
      strcat(aBuf,tmpBuf);
      
      if(GifURL!=NULL)
	sprintf(tmpBuf,
		"<PARAM NAME=image2 VALUE=%s>\n", GifURL);
      else sprintf(tmpBuf,
		   "<PARAM NAME=image2 VALUE=%s.gif>\n", image2);
      strcat(aBuf,tmpBuf);

      
      if(zoomImg!=0)
	{
	  sprintf(tmpBuf, "<PARAM NAME=ZoomImg VALUE=%d>\n", zoomImg); 
	  strcat(aBuf,tmpBuf);
	}
      
      if(canvasSize!=0)
	{
	  sprintf(tmpBuf,
		  "<PARAM NAME=CanvasSize VALUE=%d>\n", canvasSize);
	  strcat(aBuf,tmpBuf);
	}

      /* Process any addition special "PARAM.name=value" NV entries */
      for(i=0;i<db->nParamNames;i++)
	{	    
	  sprintf(tmpBuf, "<PARAM NAME=%s VALUE=%s>\n", 
		  db->paramName[i], db->paramVal[i]);
	  strcat(aBuf, tmpBuf);
#ifdef TESTING
  if(strcmp(db->remote_addr,DBUG_HOST)==0)
  { 
    printContentType("text/html");
    printf("<H1>DBUG[1] !javaPlugin createAppletStr():line#%d</H1>",
	   __LINE__);
    printf("<LI><B>db->paramName[i]</B>='%s'",chkNull(db->paramName[i]));
    printf("<LI><B>db->paramVal[i]</B>='%s'",chkNull(db->paramVal[i]));
    fflush(stdout);
  }
#endif
	  }
      
      sprintf(tmpBuf,
	      "</APPLET>\n\n");
      strcat(aBuf,tmpBuf);
      
      *appletStr= copyStr(aBuf); /* RETURN applet string */
    }/* createAppletStr */

  else
    {/* Generate the Applet using Java Plugin code */
      char
	sImage1[256],
	sImage2[256],
	sZoomImg[50],
	sCanvasSize[50],
	sHeight[50],
	sWidth[50],
	cb[256],		/* codebase */
	icb[256];		/* imageCodeBase */
      javadata_t 
	*jData;
      
      /* [1] Stuff Applet independent */ 
      sprintf(sImage1, "%s.gif",image1);
      sprintf(sImage2, "%s.gif",image2);
      sprintf(sZoomImg, "%d",zoomImg);
      sprintf(sCanvasSize, "%d",canvasSize);
      sprintf(sHeight, "%d",height);
      sprintf(sWidth, "%d",width);
      sprintf(cb, "%s/%s", serverURL, codeBaseSubDir);
      sprintf(icb, "%s/%s",serverURL, imageCodeBase);
      jData= newJavaData(sHeight, sWidth, "absmiddle", code, cb);

      
      /* [2] Stuff Applet dependent data.  */
      pushJavaData(jData, "webServer", serverURL);
      pushJavaData(jData, "ZoomImg", sZoomImg);
      pushJavaData(jData, "CanvasSize", sCanvasSize);
      pushJavaData(jData, "image1", sImage1);
      pushJavaData(jData, "image2", 
		   (GifURL!=NULL) ? GifURL : sImage2);
      pushJavaData(jData, "imgCodebase", icb);
      
      /* Process any addition special "PARAM.name=value" NV entries */
      for(i=0;i<db->nParamNames;i++)
	{	    
	  pushJavaData(jData, db->paramName[i], db->paramVal[i]);
	}


      /* [3] Generate scripts */
      flkJavaPlugin(jData, headerJavaScriptStr, appletStr);
    }/* Generate the Applet using Java Plugin code */

  return(TRUE);
}/* createAppletStr */



  
/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * genFlickerHTML() - generate flicker java page which may use JDK1.1.1.
 * This generates the Java Plugin HTML if needed by (db->JavaPlugin!=NULL).
 */
static void genFlickerHTML(db_t *db)
{ /* genFlickerHTML*/
  char
    sTitle[MAX_LINE],
    sTmp1[MAX_LINE],
    sBuf[MAX_BUF_SIZE];
  BOOL
    ok;
  int
    i,
    height= 650,		/* Flicker browser window */
    width= 700;			/* Flicker browser window */
  STRING
    s,
    headerJavaScriptStr= NULL,	/* optional javascript before <BODY> */
    appletStr= "";		/* Applet or JavaPlugin Applet STRING
				 * in <BODY> */

  
  /* [1] Setup up default strings. */
  if(!db->dbugFlag && (db->image1==NULL || db->image2==NULL))
    {
      serverErr(
  "Sorry, you must select BOTH left and right images - backup and try again");
    }
  else if(!db->dbugFlag && strcmp(db->image1,db->image2)==0)
    {
      serverErr(
	   "Sorry, you must select DIFFERENT images - backup and try again");
    }


  /* [2] Generate Applet code */
  if(db->ImgCodebase!=NULL && db->relDir==NULL)
    db->imagePath= db->ImgCodebase;
  else if(db->ImgCodebase==NULL && db->relDir!=NULL)
    db->imagePath= db->relDir;
  else db->imagePath= db->ImgCodebase; /* NOTE: default!!!! */

  if(db->JavaPlugin!=NULL)
    ok= createAppletStr(db,	/* need state */
			TRUE,	/* JDK1.1 */
			"Flicker", "FlkJ", "Flicker",
			width, height, db->webServer, db->imagePath,
			db->image1, db->image2, NULL,
			db->zoomImg, db->canvasSize,
			&headerJavaScriptStr,
			&appletStr);
  else ok= createAppletStr(db,
			   FALSE,           /* JDK1.0 */
			   "Flicker", "FlkJ2", "flicker",
			   width, height, db->webServer, db->imagePath,
			   db->image1, db->image2, NULL,
			   db->zoomImg, db->canvasSize,
			   &headerJavaScriptStr,
			   &appletStr);
  if(!ok)
    serverErr("DRYROT error - Can't create &lt;APPLET> string");



  /* [3] Generate code */
  strcpy(sBuf, "Content-type: text/html\n\n");
  
  strcat(sBuf, "<!DOCTYPE HTML PUBLIC\"-//W3C//DTD HTML 3.2 Final//EN\">\n");
  strcat(sBuf, "<HTML>\n");
  strcat(sBuf, "<HEAD>\n");

  sprintf(sTitle, "FLICKER - comparison of %s", db->sampleTitle);
  sprintf(sTmp1, "<TITLE>%s</TITLE>\n",sTitle);
  strcat(sBuf,sTmp1);

  if(headerJavaScriptStr!=NULL)
    strcat(sBuf,headerJavaScriptStr);

  strcat(sBuf,"</HEAD>\n");

  strcat(sBuf, "<BODY BGCOLOR=#F0F0F0 ");
  strcat(sBuf, "TEXT=Black LINK=#0000F0 VLINK=#6000B0 ALINK=#F00000>\n");

  sprintf(sTitle,
	  "<CENTER>FLICKER - comparison of %s</CENTER><BR>\n",
	  db->sampleTitle);
  sprintf(sTmp1, "<H2>%s</H2>\n\n",sTitle);
  strcat(sBuf,sTmp1);

  sprintf(sTmp1, "The data consists of <TT>%s</TT>\n",db->sampleTitle);
  strcat(sBuf,sTmp1);

  if(*(db->authorHref)!='\0' || *(db->authorName)!='\0')
    {
      sprintf(sTmp1, "(<A HREF=\"%s\"> %s</A>).\n", 
	      db->authorHref, db->authorName);
      strcat(sBuf,sTmp1);
    }
  else strcat(sBuf,".\n");
  
  sprintf(sTmp1,
     "See the <A HREF=\"%s/flkInfo.html\"> Flicker Reference Manual</A>\n",
	  NCI_FLICKER_HOME_PAGE);
  strcat(sBuf,sTmp1);

  strcat(sBuf, " for more details.<P>\n");

  sprintf(sTmp1, "<P>Left image is: <B>%s</B><BR>\n", db->image1Title);
  strcat(sBuf,sTmp1);

  sprintf(sTmp1, "Right image is: <B>%s</B><P>\n", db->image2Title);
  strcat(sBuf,sTmp1);

  strcat(sBuf,"<B>Please <FONT color=\"magenta\"><BLINK>WAIT</BLINK></FONT> ");

  strcat(sBuf,"while images load.</B>\n");

  strcat(sBuf,"Depending on the access speed of the Web site(s) ");

  strcat(sBuf, "where the images reside, this may take several minutes.\n");

  sprintf(sTmp1, "If you require the <A HREF=\"%s/flicker/JavaPlugin.html\">",
	 db->webServer);
  strcat(sBuf,sTmp1);

  strcat(sBuf, "JavaPlugin</A>, you may ");
  strcat(sBuf,
	 "<A HREF=\"http://java.sun.com/products/plugin/download/all.html\">");
  strcat(sBuf, 
	 "download it from Sun</A> and install it on your Web browser.\n");

  strcat(sBuf, "<HR>\n");
  
  if(appletStr==NULL)
    serverErr("Missing &lt;APPLET> string");
  else strcat(sBuf, appletStr);

  strcat(sBuf, "<P><HR><P>\n\n");
  
  strcat(sBuf, "<ADDRESS>\n$Date: 1998/11/20 22:55:30 $ /\n"); 

  sprintf(sTmp1, "<A HREF=\"%s/flicker\"> Flicker Page</A>\n", 
	  db->webServer);
  strcat(sBuf, sTmp1);

  strcat(sBuf, "</ADDRESS>\n</BODY>\n</HTML>\n");


  /* [4] Dump the assembled web page. */
  printContentType("text/html");
  printf("%s",sBuf);		

} /* genFlickerHTML*/




/* @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 * genFlkPairHTML() - Gen. HTML for invoking Flicker on 2 selected images
*/
static void genFlkPairHTML(db_t *db)
{ /* genFlkPairHTML */
  int
    i;
  STRING
    s;
  char
    sBuf[MAX_BUF_SIZE],
    sTmp[MAX_LINE],
    sTmp1[MAX_LINE];

  /* [1] Get names of images */
  if(!db->dbugFlag && (db->image1==NULL || db->image2==NULL))
    {
      serverErr(
  "Sorry, you must select BOTH left and right images - backup and try again");
    }
  else if(!db->dbugFlag && strcmp(db->image1,db->image2)==0)
    {
      serverErr(
	   "Sorry, you must select DIFFERENT images - backup and try again");
    }


  /* [2] Print header */
  strcpy(sBuf, "Content-type: text/html\n\n");
  
  strcat(sBuf, "<!DOCTYPE HTML PUBLIC\"-//W3C//DTD HTML 3.2 Final//EN\">\n");
  strcat(sBuf, "<HTML>\n");
  strcat(sBuf, "<HEAD>\n");

  sprintf(sTmp, "FLICKER - comparison of %s", db->sampleTitle);
  sprintf(sTmp1, "<TITLE>%s</TITLE>\n",sTmp);
  strcat(sBuf,sTmp1);

  strcat(sBuf, "<BODY BGCOLOR=#F0F0F0 ");
  strcat(sBuf, "TEXT=Black LINK=#0000F0 VLINK=#6000B0 ALINK=#F00000>\n");

  sprintf(sTmp,
	  "<CENTER>FLICKER - comparison of %s</CENTER><BR>\n",
	  db->sampleTitle);
  sprintf(sTmp1, "<H2>%s</H2>\n\n",sTmp);
  strcat(sBuf,sTmp1);

  sprintf(sTmp1, "The data consists of two <TT>%s</TT>\n",db->sampleTitle);
  strcat(sBuf,sTmp1);

  if(*(db->authorHref)!='\0' || *(db->authorName)!='\0')
    {
      sprintf(sTmp1, "(<A HREF=\"%s\"> %s</A>).\n", 
	      db->authorHref, db->authorName);
      strcat(sBuf,sTmp1);
    }
  else strcat(sBuf,".\n");
  
  sprintf(sTmp1,
     "See the <A HREF=\"%s/flkInfo.html\"> Flicker Reference Manual</A>\n",
	  NCI_FLICKER_HOME_PAGE);
  strcat(sBuf,sTmp1);

 strcat(sBuf, " for more details.<P>\n");

  sprintf(sTmp1, "<P>Left image is: <B>%s</B><BR>\n", db->image1Title);
  strcat(sBuf,sTmp1);

  sprintf(sTmp1, "Right image is: <B>%s</B><P>\n", db->image2Title);
  strcat(sBuf,sTmp1);

  strcat(sBuf,"<B>Please <FONT color=\"magenta\"><BLINK>WAIT</BLINK></FONT> ");

  strcat(sBuf,"while images load.</B>\n");

  strcat(sBuf,"Depending on the access speed of the Web site(s) ");

  strcat(sBuf, "where the images reside, this may take several minutes.\n");

  strcat(sBuf,"<HR>\n");

  sprintf(sTmp1, 
	  "<APPLET NAME=Flicker CODE=FlkJ2.class  ARCHIVE=FlkJ2.jar\n");
  strcat(sBuf,sTmp1);

  sprintf(sTmp1, 
	  "    WIDTH=%d HEIGHT=%d\n",
	  db->width, db->height);
  strcat(sBuf,sTmp1);

  sprintf(sTmp1, 
	  "    ALIGN=absmiddle CODEBASE=\"%s\"\n",
	  db->flicker_server);	/* could live on another server */
  strcat(sBuf,sTmp1);

  sprintf(sTmp1,
  "ALT=\"You need a java-enabled browser to view Flicker applet.\"\n");
  strcat(sBuf,sTmp1);

  sprintf(sTmp1, ">\n");
  strcat(sBuf,sTmp1);

  sprintf(sTmp1, "<PARAM NAME=WebServer VALUE=%s>\n", db->webServer);
  strcat(sBuf,sTmp1);

  sprintf(sTmp1, "<PARAM NAME=CanvasSize VALUE=%d\n>", db->canvasSize);
  strcat(sBuf,sTmp1);


  if(db->relDir!=NULL)
    {
      sprintf(sTmp1, "<PARAM NAME=ImgCodebase VALUE=%s/%s>\n",
	      db->webServer, db->relDir); /* could live on another dir. */
      strcat(sBuf,sTmp1);
    }

  sprintf(sTmp1, "<PARAM NAME=image1 VALUE=%s>\n",db->image1);
  strcat(sBuf,sTmp1);

  sprintf(sTmp1, "<PARAM NAME=image2 VALUE=%s>\n",db->image2);
  strcat(sBuf,sTmp1);


      /* Process any addition special "PARAM.name=value" NV entries */
      for(i=0;i<db->nParamNames;i++)
	{	    
	  sprintf(sTmp1, "<PARAM NAME=%s VALUE=%s>\n", 
		  db->paramName[i], db->paramVal[i]);
	  strcat(sBuf, sTmp1);
#ifdef TESTING
  if(strcmp(db->remote_addr,DBUG_HOST)==0)
  { 
    printContentType("text/html");
    printf("<H1>DBUG[2] genFlkPairHTML():line#%d</H1>",
	   __LINE__);
    printf("<LI><B>db->paramName[i]</B>='%s'<BR>",chkNull(db->paramName[i]));
    printf("<LI><B>db->paramVal[i]</B>='%s'<BR>",chkNull(db->paramVal[i]));
    fflush(stdout);
  }
#endif
	  }
      
  sprintf(sTmp1, "</APPLET>\n\n<HR>\n\n");
  strcat(sBuf,sTmp1);

  sprintf(sTmp1,
  "<ADDRESS>\n$Date: 1998/11/20 22:55:30 $ / \n");
  strcat(sBuf,sTmp1);

  sprintf(sTmp1, "<A HREF=\"%s\">Flicker Home Page</A>\n",
	  NCI_FLICKER_HOME_PAGE);
  strcat(sBuf,sTmp1);

  sprintf(sTmp1,"</ADDRESS>\n");
  strcat(sBuf,sTmp1);

  strcat(sBuf, "</BODY>\n");
  strcat(sBuf, "</HTML>\n");

  fprintf(stdout, "%s", sBuf);
} /* genFlkPairHTML */





/* *****************************************************************
 * main() - process /cgi-bin/getFlkPair?listOfImagesFile
 * command which must be a POST.
 * If the request_method is POST, then process commands
 * else server error.
 */
int main(int argc, STRING argv[])
{/* main*/
  db_t
    *db= (db_t *)calloc(1,sizeof(db_t));;

  /* [1] Init database variables */
  init_db(db, argc, argv);	/* set up initial state. */


  /* [2] Generate Document */
  if(db->JavaPlugin!=NULL)
    genFlickerHTML(db);
  else genFlkPairHTML(db);
  

  return(0);			/* just in case we get here. */
}/* main*/

/* ----------> end of genFlkPair.c <----------- */