/*

     Copyright (c) 2004 Kelly Black.
     For more information see http://www.cyclismo.org/cgiClass/
     This code developed by Kelly Black at the University of New Hampshire
     and later at Union College (Schenectady NY).

     This code is used to provide a c++ class to do basic html output. 
     It was originally designed to simplify the way cgi scripts are made
     and was first written back before perl was widely available.


    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation;  version 2 of the License.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


    More information is also available at http://www.gnu.org/copyleft/gpl.html.



     There are additional copyright considerations associated with
     this code.  The image map aspects of this code were copied from
     imagemap.c which was written at the National Center for
     Supercomputer Applications at the University of Illinois at
     Urbana-Champaign:

         Portions of this code have been  developed at the National Center for 
         Supercomputing Applications at the University of Illinois at Urbana-Champaign.

         Several routines have been modified from the imagemap.c program distributed
         by the NCSA.  These routines include comments about where I got them and
	 can be found below.

	 For more information see
	 http://hoohoo.ncsa.uiuc.edu/docs-1.4/tutorials/imagemapping.html
         
*/


#include <ostream>
#include <iostream>
#include <iomanip>
#include <ctype.h>
#include <string.h>
//#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>

using std::cout;
using std::endl;



// definitions used for map files.
// taken from imagemap.c from NCSA (see below)
#define CONFFILE "/usr/local/etc/httpd/conf/imagemap.conf"
#define MAXLINE 500
#define MAXVERTS 100
#define X 0
#define Y 1


class cgi {

public:
    // constructor
    cgi(void);

    // start page.  Must be first thing called.
    // If non-null argument given it will load the
    // URL given in string.
    void initPage(char* = NULL);

    // Print head of document.  Include title and a link
    void header(char* = NULL,char* = NULL,char* = NULL,
                char* = NULL,char* = NULL,char* = NULL);

    // End the document
    void endHTML(void);

    // List commands
    enum ListType{
	NUMBERED = 0,
	UNNUMBERED = 1,
	DIRECTORY = 2,
	MENU = 4
    };
    void startList(ListType=UNNUMBERED);
    void endList(ListType=UNNUMBERED);
    void listItem(char*);

    // Definitions
    void startDefinitionList(void);
    void endDefinitionList(void);
    void listTerm(char*);
    void listDefinition(char*);

    // Various line formatting
    void paragraph(void);
    void rule(void);
    void lineBreak(void);

    // Print an image.  Give the url of the image and
    // alternate text.
    void image(char*,char* = NULL,int=0);

    // Start a hyper-text link
    // Give the url and optional name within document
    void startAnchor(char* = NULL,char* = NULL);
    void endAnchor(void);

    // define a name within a document
    void startLabel(char*);
    void endLabel(void);

    // Start an indented field
    void startIndent(void);
    void indentLine(char*);
    void endIndent(void);


    // Make a heading field
    void startHeading(int);
    void endHeading(int);


    // ********************************
    // Methods for text formatting.
    // indivual methods kept for backwards compatibility.
    //
    // Text formatting methods will allow the following:
    //
    // Write italic text
    // Write in bold
    // Write in strong
    // make a quote field
    // make an address field
    // Make a preformatted field
    //


    enum Formatting {
	ITALIC = 0,
	BOLD=1,
	STRONG=2,
	QUOTE=3,
	ADDRESS=4,
	TELETYPE=5,
	UNDERLINE=6,
	CITE=7,
	EMPHASIZED=8,
	CODE=9,
	KEYBOARD=10,
	SAMPLE=11,
	STRIKE=12,
	VARIABLE=13,
	PREFORMAT=14
    };

    //  Change type style
    void startFormat(Formatting=ITALIC);
    void endFormat(Formatting=ITALIC);

    // Write italic text
    inline void startItalics(void);
    inline void endItalics(void);

    // Write in bold
    inline void startBold(void);
    inline void endBold(void);

    // Write in strong
    inline void startStrong(void);
    inline void endStrong(void);

    // make a quote field
    inline void startQuote(void);
    inline void endQuote(void);

    // make an address field
    inline void startAddress(void);
    inline void endAddress(void);

    // Make a preformatted field
    inline void startPreformat(void);
    inline void endPreformat(void);


    // Environment variables of interest
    const char* getRefer(void);
    const char* getAgent(void);
    const char* getPragma(void);
    const char* getAccept(void);
    const char* getPath(void);
    const char* getPathInfo(void);
    const char* getPathTranslated(void);
    const char* getServerSoftware(void);
    const char* getServerNamer(void);
    const char* getServerPort(void);
    const char* getRemoteHost(void);
    const char* getRemoteAddress(void);
    const char* getGateway(void);
    const char* getProtocol(void);
    const char* getRequestMethod(void);
    const char* getScriptName(void);
    const char* getQueryString(void);
    const char* getSHLVL(void);
    const char* getPWD(void);
    const char* getLogName(void);
    const char* getUser(void);
    const char* getHost(void);
    const char* getHostType(void);

    // Get data from a form.
    // returns number of fields as well as
    // character arrays to the fields.  The
    // first argument returns as the data within the
    // fields and the second argument returns the names
    // of the fields.
    void getFormData(char** &,char** &,int &);
    long getContentLength(void);


  // image map informaion.
  // Routines used for image maps.
  // These routines taken from the NCSA image-map distribution.
  // 
  // The routine openImageMapFile finds the text file with the image map.
  //      The routine returns opens the file and returns a file pointer.
  // The routine findMapLocation reads the specified file and returns
  //      the correct string from the image map file.
  // The remaining methods are service routines used by findMapLocation.
  // 
  // The reason that openImageMap and findMapLocation are separate is
  // that in some scripts you might know in advance which image map file
  // to open.  This way you can pass arguments through the PATH_INFO
  // environment variable.
  // 
  // The imagemap.c program was found at NCSA at 
  //        http://hoohoo.ncsa.uiuc.edu/docs/setup/admin/Imagemap.html
  //
  // Original creators include: Rob McCool,Chris Hyams,Rick Troth,
  //                             Craig Milo Rogers,Carlos Varela
  //
  //  Old-style lookup in imagemap table:
  //    <a href="http://foo.edu/cgi-bin/imagemap/oldmap">
  //
  //  New-style specification of mapfile relative to DocumentRoot:
  //    <a href="http://foo.edu/cgi-bin/imagemap/path/for/new.map">
  //
  //  New-style specification of mapfile in user's public HTML directory:
  //    <a href="http://foo.edu/cgi-bin/imagemap/~username/path/for/new.map">
  //
  FILE* openImageMapFile(void);
  int findMapLocation(FILE *,char [],int=-1,int=-1);
  int convertArg2Point(int numArgs, char **args,int &xpos,int &ypos);
  int pointinrect(int xpos,int ypos,int coords[MAXVERTS][2]);
  int pointincircle(int xpos,int ypos,int coords[MAXVERTS][2]);
  int pointinpoly(int xpos,int ypos,int intPgon[MAXVERTS][2]);


    // Build your own forms.
    enum InputType {
	TEXT = 0,
	PASSWORD = 1,
	CHECKBOX = 2,
	RADIO = 3,
	HIDDEN = 4
    };

    enum Method {
	GET = 0,
	POST = 1
    };

    void startForm(char* ,Method = GET);
    void submitButton(char* = NULL);
    void resetButton(char* = NULL);
    void inputField(char* = NULL,char* = NULL,
		    InputType = TEXT,
		    int=0,int=20,int=40);
    void selectionField(char* = NULL,char** = NULL,
			int = 0,
			int = 1,int=0);
    void textareaField(char* = NULL,char* = NULL,int=1,int=20);
    void endForm(void);


    void turnErrorPrintingOn(void);
    void turnErrorPrintingOff(void);

protected:
private:
    void Error(const char* ,const char* = NULL,const char* = NULL);

    const char* typeString[5];
    int headings[6],numHeadings;

    const char* listString[MENU+1];
    const char* listCommand[MENU+1];
    int list[MENU+1],numList;
    
    int definition;
    int anchor;
    int label;
    int body;
    int title;
    int indent;

    int format[PREFORMAT+1],numFormat;
    const char* formatString[PREFORMAT+1];
    const char* formatCommand[PREFORMAT+1];

    int form;
    int submit;
    int reset;

    int outputError;

};



// Constructor.  Not much to do here but
// initialize the variables
cgi::cgi(void) {

    register int i;

    typeString[TEXT] = "text";
    typeString[PASSWORD] = "password";
    typeString[CHECKBOX] = "checkbox";
    typeString[RADIO] = "radio";
    typeString[HIDDEN] = "hidden";
    
    numList=MENU+1;
    listString[NUMBERED] = "numbered list";
    listString[UNNUMBERED] = "unnumbered list";
    listString[DIRECTORY] = "directory list";
    listString[MENU] = "menu list";
    listCommand[NUMBERED] = "ol";
    listCommand[UNNUMBERED] = "ul";
    listCommand[DIRECTORY] = "dir";
    listCommand[MENU] = "menu";
    for(i=0;i<numList;++i)
	list[i] = 0;

    numHeadings = 6;
    for(i=0;i<numHeadings;++i)
	headings[i] = 0;

    definition = 0;
    label=0;
    anchor = 0;
    body = 0;
    title = 0;
    indent = 0;

    numFormat = PREFORMAT+1;
    formatString[ITALIC] = "italic";
    formatString[BOLD] = "bold";
    formatString[STRONG] = "strong";
    formatString[QUOTE] = "quote";
    formatString[ADDRESS] = "address";
    formatString[TELETYPE] = "teletype";
    formatString[UNDERLINE] = "underline";
    formatString[CITE] = "citation";
    formatString[EMPHASIZED] = "emphasized";
    formatString[CODE] = "code";
    formatString[KEYBOARD] = "keyboard";
    formatString[SAMPLE] = "sample";
    formatString[STRIKE] = "strike";
    formatString[VARIABLE] = "variable";
    formatString[PREFORMAT] = "preformatting";

    formatCommand[ITALIC] = "i";
    formatCommand[BOLD] = "b";
    formatCommand[STRONG] = "strong";
    formatCommand[QUOTE] = "quote";
    formatCommand[ADDRESS] = "address";
    formatCommand[TELETYPE] = "tt";
    formatCommand[UNDERLINE] = "u";
    formatCommand[CITE] = "cite";
    formatCommand[EMPHASIZED] = "em";
    formatCommand[CODE] = "code";
    formatCommand[KEYBOARD] = "kbd";
    formatCommand[SAMPLE] = "samp";
    formatCommand[STRIKE] = "strike";
    formatCommand[VARIABLE] = "var";
    formatCommand[PREFORMAT] = "pre";
    for(i=ITALIC;i<=PREFORMAT;++i)
	format[i] = 0;


    form = 0;
    submit = 0;
    reset = 0;

    outputError = 1;
}



// Start of the web page.
// if passed a null pointer then assume that the
// following text is html, otherwise treat it as
// a link to another page.
void cgi::initPage(char* location) {

    if(location==NULL) 
	cout << "Content-type:text/html" << endl << endl;
    else
	cout << "Location:http:" << location << endl << endl;
    
}


// Print out the header.
// If a string is passed treat it as the title of the
// page.  If a link is passed it is included in the link command.
void cgi::header(char* titleString,char* link,
                 char* content,char* bgColor,
                 char* fgColor,char* linkColor) {

    if(title) {
	Error("Already gave a title");
	return;
    }

    ++title;
    cout << "<HTML>" << endl;
    cout << "<HEAD>" << endl;

    cout << "<TITLE>" << endl;
    if(titleString != NULL)
	cout << titleString << endl;
    cout << "</TITLE>" << endl;

    if(link!=NULL)
	cout << "<link href=\"" << link << "\">" << endl;

    if(content!=NULL)
        cout << "<META HTTP-EQUIV=\"Keywords\" CONTENT=\""
             << content << "\">" << endl;

    cout << "</HEAD>" << endl;
    cout << "<body";

    if(bgColor!=NULL)
        cout << " BGCOLOR=\"" 
             << bgColor 
             << "\"" ;

    if(fgColor != NULL)
        cout << " TEXT=\""
             << fgColor
             << "\"";


    if(linkColor != NULL)
        cout << " LINK=\""
             << linkColor
             << "\"";

    cout << ">" << endl;
    ++body;

} 


// Print out the end of body statement and check
// to see if everything has been closed up.
void cgi::endHTML(void) {
    register int i;

    if(body) {
	--body;
	cout << "</body>" << endl;
    }
    else
	Error("Tried to end text twice");

    for(i=0;i<numList;++i) {
	if(list[i])
	    Error("Forgot to close a list");
    }

    for(i=0;i<numHeadings;++i) {
	if(headings[i])
	    Error("Forgot to close a heading");
    }

    if(definition)
	Error("Forgot to close a definition");
    if(label)
	Error("Forgot to close a label");
    if(anchor)
	Error("Forgot to close a link");
    if(!title)
	Error("Forgot the title\n");
    if(indent)
	Error("Forgot to close an indent");

    for(i=ITALIC;i<=PREFORMAT;++i) {
	if(format[i]) 
	    Error("Forgot to close ",formatString[i]," formatting.");
    }

}


// Start a list.  0 for unnumbered, 1 for numbered.
void cgi::startList(ListType which) {
    ++list[which];
    cout << "<" << listCommand[which] << ">" << endl;
}



// End the list.
void cgi::endList(ListType which) {

    if(!list[which]) {
	Error("Too many 'end list' markers");
	return;
    }

    --list[which];
    cout << "</" << listCommand[which] << ">" << endl;
}


// Add another item to the list
void cgi::listItem(char* item) {
    int open = 0;
    register int i;

    for(i=0;i<numList;++i)
	open += list[i];

    if(open) {
	if(item==NULL)
	    cout << "<li> " << endl;
	else
	    cout << "<li> " << item << endl;
    }
    else 
	Error("No open list available");

}



void cgi::startDefinitionList(void){
    ++definition;
    cout << "<dl>" << endl;
}

void cgi::endDefinitionList(void) {
    if(!definition) {
	Error("Too many 'end definition list' markers");
	return;
    }

    --definition;
    cout << "</dl>" << endl;
}

void cgi::listTerm(char* item) {
    if(definition) {
	if(item==NULL)
	    cout << "<dt> " << endl;
	else
	    cout << "<dt> " << item << endl;
    }
    else 
	Error("No open definition list available");
}

void cgi::listDefinition(char* item) {
    if(definition) {
	if(item==NULL)
	    cout << "<dd> " << endl;
	else
	    cout << "<dd> " << item << endl;
    }
    else 
	Error("No open definition list available");
}




// paragaraph separator
void cgi::paragraph(void) {
    cout << "<p>" << endl;
}


// Horizontal rule
void cgi::rule(void) {
    cout << "<hr>" << endl;
}


// print out a line break.
void cgi::lineBreak(void) {
    cout << "<br>" << endl;
}


// include an inline image.  
void cgi::image(char* image,char* alternate,int map) {

    if(image==NULL) {
	Error("No image name given");
	return;
    }

    cout << "<img src=\"" << image;
    if(alternate != NULL) 
	cout << "\" alt=\"" << alternate;
    cout << "\" ";
    if(map!=0)
      cout << " ismap ";
    cout << ">" << endl;

}


// Start a link.  dest is document title and particular
// is a location within the document.  If particular
// is not null the # will be printed
void cgi::startAnchor(char* dest,char* particular) {
    if(anchor) {
	Error("Trying to begin an anchor within an open anchor or label");
	return;
    }

    if((dest==NULL)&&(particular==NULL)) {
	Error("No destination for link given");
	return;
    }

    ++anchor;
    cout << "<a href=\"";
    if(dest!=NULL)
	cout << dest ;
    if(particular!=NULL)
	cout << "#" << particular;
    cout << "\">" << endl;

}


// Mark the end the anchor
void cgi::endAnchor(void){
    if((anchor)||(label)) {
	--anchor;
	cout << "</a>" << endl;
    }
    else
	Error("No anchor to end");

}


// Start a named label for within a page
void cgi::startLabel(char* dest){
    if((anchor)||(label)) {
	Error("Trying to begin a label within an open anchor or label");
	return;
    }

    if(dest==NULL) {
	Error("No label given");
	return;
    }

    ++label;
    cout << "<a name=\"" << dest << "\">" << endl;
}


// Mark the end of label
void cgi::endLabel(void) {
    if((anchor)||(label)) {
	--label;
	cout << "</a>" << endl;
    }
    else
	Error("No label to end");
}


// Begin indented printing
void cgi::startIndent(void) {
    ++indent;
    cout << "<dl>" << endl;
}


// indent this line
void cgi::indentLine(char* item) {
    cout << "<dd> ";
    if(item!=NULL)
	cout << item ;
    cout << endl;
}


// End the indent field
void cgi::endIndent(void){
    if(indent) {
	--indent;
	cout << "</dl>" << endl;
    }
    else
	Error("Did not start an indented field");
}


void cgi::startFormat(Formatting which){
    if(format[which]) {
	cout << "</" << formatCommand[which] << ">" << endl;
	Error("Already in ",formatString[which]," mode");
	cout << "<" << formatCommand[which] << ">" << endl;
	return;
    }
    ++format[which];
    cout << "<" << formatCommand[which] << ">" << endl;
}

void cgi::endFormat(Formatting which){
    if(format[which]) {
	--format[which];
	cout << "</" << formatCommand[which] << ">" << endl;
    }
    else
	Error("Not in ",formatString[which]," mode");
}

//Start printing in italics
inline void cgi::startItalics(void) {
    startFormat(ITALIC);
}


// Stop printing in italics
inline void cgi::endItalics(void) {
    endFormat(ITALIC);
}


// Start printing bold
inline void cgi::startBold(void) {
    startFormat(BOLD);
}


// End bold printing
inline void cgi::endBold(void){
    endFormat(BOLD);
}


// Start printing strong
inline void cgi::startStrong(void) {
    startFormat(STRONG);
}


// End strong printing
inline void cgi::endStrong(void){
    endFormat(BOLD);
}

inline void cgi::startQuote(void){
    startFormat(QUOTE);
}


inline void cgi::endQuote(void){
    endFormat(QUOTE);
}

inline void cgi::startAddress(void){
    startFormat(ADDRESS);
}


inline void cgi::endAddress(void){
    endFormat(ADDRESS);
}

// Start preformatted printing
inline void cgi::startPreformat(void) {
    startFormat(PREFORMAT);
}


// End preformatted printing
inline void cgi::endPreformat(void){
    endFormat(PREFORMAT);
}


// Make text print as a heading
void cgi::startHeading(int which) {

    if((which<1)||(which>numHeadings)) {
	Error("Bad range on headings (startheading)");
	return;
    }

    if(headings[which-1]) {
	cout << "</h" << which << ">" << endl;
	Error("Already in this heading");
	cout << "<h" << which << ">" << endl;
	return;
    }
    ++headings[which-1];
    cout << "<h" << which << ">" << endl;
}


// Mark end of heading
void cgi::endHeading(int which){

    if((which<1)||(which>numHeadings)) {
	Error("Bad range on headings (endHeading)");
	return;
    }

    if(headings[which-1]) {
	--headings[which-1];
	cout << "</h" << which << ">" << endl;
    }
    else
	Error("Not in the right Heading");
}




//Get the desired environment variable

const char* cgi::getRefer(void) {
return(getenv("HTTP_REFERER"));
}


const char* cgi::getAgent(void){
return(getenv("HTTP_USER_AGENT"));
}


const char* cgi::getPragma(void){
return(getenv("HTTP_PRAGMA"));
}


const char* cgi::getAccept(void){
return(getenv("HTTP_ACCEPT"));
}


const char* cgi::getPath(void){
return(getenv("PATH"));
}

const char* cgi::getPathInfo(void){
   return(getenv("PATH_INFO"));
}

const char* cgi::getPathTranslated(void){
return(getenv("PATH_TRANSLATED"));
}


const char* cgi::getServerSoftware(void){
return(getenv("SERVER_SOFTWARE"));
}


const char* cgi::getServerNamer(void){
return(getenv("SERVER_NAME"));
}


const char* cgi::getServerPort(void){
return(getenv("SERVER_PORT"));
}


const char* cgi::getRemoteHost(void){
return(getenv("REMOTE_HOST"));
}


const char* cgi::getRemoteAddress(void){
return(getenv("REMOTE_ADDR"));
}


const char* cgi::getGateway(void){
return(getenv("GATEWAY_INTERFACE"));
}


const char* cgi::getProtocol(void){
return(getenv("SERVER_PROTOCOL"));
}


const char* cgi::getRequestMethod(void){
return(getenv("REQUEST_METHOD"));
}


const char* cgi::getScriptName(void){
return(getenv("SCRIPT_NAME"));
}


const char* cgi::getQueryString(void){
return(getenv("QUERY_STRING"));
}


const char* cgi::getSHLVL(void){
return(getenv("SHLVL"));
}


const char* cgi::getPWD(void){
return(getenv("PWD"));
}


const char* cgi::getLogName(void){
return(getenv("LOGNAME"));
}


const char* cgi::getUser(void){
return(getenv("USER"));
}


const char* cgi::getHost(void){
return(getenv("HOST"));
}


const char* cgi::getHostType(void){
return(getenv("HOSTTYPE"));
}


long cgi::getContentLength(void) {
    long length;
    const char* content;
    const char *c;
    
    content = getenv("CONTENT_LENGTH");
    length = 0;

    if(content==NULL)
	return(0);

    for(c = content;*c != '\0';)
	length = 10*length + ((long)(*c++-'0'));

    return(length);

}

void cgi::getFormData(char** &formData,char** &formName,int &numItems) {
    // formData is the information from the form
    // formName is the name of each entry in the form.

    const char* request;
    const char* informationGET;
    char *c;
    char *p;
    char* informationPOST;
    long contentLength;
    char* isGet;
    int i,j,k;
    char hexVal;

    request = getRequestMethod();
    if(request==NULL) {
	formData = NULL;
	formName = NULL;
	numItems = 0;
	return;
    }

    // Check to see how the information was posted.
    // if GET then read in query string.
    // Else read in from stdin.
    if(isGet = strstr(request,"GET")) {
	informationGET = getQueryString();
	c = (char *) informationGET;
    }
    else {
	contentLength = getContentLength();
	informationPOST = new char[contentLength+1];
	p = informationPOST;
	for(i=0;i<contentLength;++i)
	    *p++ = getchar();
	*p = '\0';
	c = informationPOST;
    }

    // If no information posted return NULL
    if(c==NULL) {
	numItems = 0;
	formData = NULL;
	formName = NULL;
    }
    else {

	// Figure out how many items by counting the '&'
	numItems = 1;
	for(i=0;i<strlen(c);++i) {
	    if(c[i]=='&')
		++numItems;
	}

	// Allocate numItem pointers for the strings
	formData = new char*[numItems];
	formName = new char*[numItems];

	// For each field get the name and the data in the field
	for(j=0;j<numItems;++j) {

	    // Get the name of the field
	    p = c;
	    while((*p != '=')&&(*p++ != '\0'));
	    formName[j] = new char[p-c+2];
	    for(i=0;c<p;++c,++i) {
		switch(*c) {
		case '+':
		    // It's a space
		    formName[j][i] = ' ';
		    break;
		case '%':
		    // It's an escape character
		    hexVal = 0;
		    ++c;
		    for(k=0;(k<2)&&(isxdigit(*c));++k,++c) {
			if(isdigit(*c))
			    hexVal = 16*hexVal + (*c-'0');
			else
			    hexVal = 16*hexVal + (10+toupper(*c)-'A');
		    }
		    --c;
		    formName[j][i] = hexVal;
		    break;
		default:
		    // plain old character
		    formName[j][i] = *c;
		    break;
		}
	    }
	    formName[j][i] = '\0';

	    // Get the information in the field
	    ++c;
	    ++p;
	    while((*p != '&')&&(*p++ != '\0'));
	    formData[j] = new char[p-c+2];
	    for(i=0;c<p;++c,++i) {
		switch(*c) {
		case '+':
		    // It's a space
		    formData[j][i] = ' ';
		    break;
		case '%':
		    // It's an escape character
		    hexVal = 0;
		    ++c;
		    for(k=0;(k<2)&&(isxdigit(*c));++k,++c) {
			if(isdigit(*c))
			    hexVal = 16*hexVal + (*c-'0');
			else
			    hexVal = 16*hexVal + (10+toupper(*c)-'A');
		    }
		    --c;
		    formData[j][i] = hexVal;
		    break;
		default:
		    // plain old character
		    formData[j][i] = *c;
		    break;
		}
	    }
	    formData[j][i] = '\0';

	    ++c;
	}

    }

    if(isGet==NULL) 
	delete [] informationPOST;

}



void cgi::startForm(char* action,Method how) {

    if(form) {
	Error("Already within a form - Nesting not allowed");
	return;
    }

    ++form;
    if(action==NULL) {
	Error("No url given for Form");
	return;
    }

    cout << "<form action=\"" << action << "\" " ;
    if(how==GET)
	cout << "method=\"GET\"";
    else
	cout << "method=\"POST\"";
    cout << ">" << endl;


}

void cgi::submitButton(char* title) {

    if(submit) {
	Error("Submit Button already applied");
	return;
    }

    ++submit;
    if (title == NULL)
	cout << "<input type=\"submit\" value=\"Submit\">" << endl;
    else {
	cout << "<input type=\"submit\" value=\"";
	cout << title << "\">" << endl;
    }

}

void cgi::resetButton(char* title){

    if(reset) {
	Error("Reset Button already applied");
	return;
    }


    ++reset;
    if (title == NULL)
	cout << "<input type=\"reset\" value=\"Reset\">" << endl;
    else {
	cout << "<input type=\"reset\" value=\"";
	cout << title << "\">" << endl;
    }

}



void cgi::endForm(void) {

    if(form) {
	--form;
	cout << "</form>" << endl;
	if(!submit) 
	    Error("No submit button given");
	submit = 0;
	if(!reset)
	    Error("No reset button given");
	reset = 0;
    }
    else
	Error("Form not started");

}



void cgi::inputField(char* name,char* value,
		     InputType type,
		     int checked,int size,int max){
  char *p;

    if(!form) {
	Error("Form not started");
	return;
    }

    if(name==NULL) {
	Error("No field name given for input field");
	return;
    }

    cout << "<input name=\"" << name << "\" ";
    cout << "type=\"" << typeString[type] << "\" ";
    if(value != NULL) {
	cout << "value=\""; 
	for(p=value;*p!='\0';++p) {
	  if(*p!='"')
	    cout << *p;
	  else
	    cout << '\'' << '\'';
	    // cout << '%' << hex << setw(3) << setfill('0') << (int) *p;
	}
	cout << "\" ";
    }


    switch(type) {
    case TEXT:
    case PASSWORD:
	if(size>0) 
	    cout << "size=" << size << " ";
	if(max>0)
	    cout << "maxlength=" << max << " ";
	break;

    case CHECKBOX:
    case RADIO:
	if(checked)
	    cout << "checked ";
	break;

    default:
	break;
    }

    cout << "> " << endl;
}


void cgi::selectionField(char* name,char** options,
			 int numSelectionFields,
			 int size,int multiple){

    if(!form) {
	Error("Form not started");
	return;
    }

    if(name==NULL) {
	Error("No field name given for selection");
	return;
    }

    if((options==NULL)||(numSelectionFields<=0)) {
	Error("No options given in selection");
	return;
    }

    int i;

    if(size<=0) size = 1;

    cout << "<select name=\"" << name << "\"";
    cout << " size=" << size << " ";
    if(multiple)
	cout << " multiple ";
    cout << ">" << endl;
    for(i=0;i<numSelectionFields;++i)
	cout << "<option> " << options[i] << endl;
    cout << "</select> " << endl;


}

void cgi::textareaField(char* name,char* initial,
			int rows,int cols){

    if(!form) {
	Error("Form not started");
	return;
    }

    if(name==NULL) {
	Error("No field name given for text area");
	return;
    }

    if(rows<=0) rows = 1;
    if(cols<=0) cols = 20;

    cout << "<textarea name=\"" << name << "\" rows="
	 << rows << " cols=" << cols << "> ";
    if(initial!=NULL)
	cout << initial << endl;
    cout << "</textarea>" << endl;

}


void cgi::turnErrorPrintingOn(void) {
    outputError = 1;
}

void cgi::turnErrorPrintingOff(void){
    outputError = 0;
}


// image map informaion.
// Routines used for image maps.
//
// These routines taken from the NCSA image-map distribution.
// 
// The routine openImageMapFile finds the text file with the image map.
//      The routine returns opens the file and returns a file pointer.
// The routine findMapLocation reads the specified file and returns
//      the correct string from the image map file.
// The remaining methods are service routines used by findMapLocation.
// 
// The reason that openImageMap and findMapLocation are separate is
// that in some scripts you might know in advance which image map file
// to open.  This way you can pass arguments through the PATH_INFO
// environment variable.
// 
// The imagemap.c program was found at NCSA at 
//        http://hoohoo.ncsa.uiuc.edu/docs/setup/admin/Imagemap.html
//
// Original creators include: Rob McCool,Chris Hyams,Rick Troth,
//                             Craig Milo Rogers,Carlos Varela
//
//  Old-style lookup in imagemap table:
//    <a href="http://foo.edu/cgi-bin/imagemap/oldmap">
//
//  New-style specification of mapfile relative to DocumentRoot:
//    <a href="http://foo.edu/cgi-bin/imagemap/path/for/new.map">
//
//  New-style specification of mapfile in user's public HTML directory:
//    <a href="http://foo.edu/cgi-bin/imagemap/~username/path/for/new.map">
//
FILE* cgi::openImageMapFile(void){
  FILE *fp;
  int i,j;
  const char *mapName;
  char input[MAXLINE];
  char confName[MAXLINE];
  char conf[MAXLINE];
  struct stat sbuf;



  mapName = getPathInfo();
  if((!mapName) || (!mapName[0])){
    Error("No image map name found.  (openImageFile())");
    return(NULL);
  }
  mapName++;

  // if '/' is in file name it is a path
  if (strchr(mapName,'/')) 
    mapName = getPathTranslated();

  // if the file exists return pointer to the file
  if (!stat(mapName,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
    fp = fopen(mapName,"r");
    if(fp!=NULL)
      return(fp);
  }


  // Can't find file.  Open configuration file and check there.
  if ((fp = fopen(CONFFILE, "r")) == NULL) {
    Error("Could not open configuration file.  (openImageFile())");
    return(NULL);
  }

  // search configuration file for the image file.
  while(fgets(input,MAXLINE,fp)) {

    if((input[0] == '#') || (!input[0]))
      continue;
    for(i=0;!isspace(input[i]) && (input[i] != ':');i++)
      confName[i] = input[i];
    confName[i] = '\0';

    if(!strcmp(confName,mapName)) {

      while(isspace(input[i]) || input[i] == ':') ++i;

      for(j=0;input[i] && !isspace(input[i]);++i,++j)
        conf[j] = input[i];
      conf[j] = '\0';

      if(fp=fopen(conf,"r")) 
	return(fp);

    }

  }


  // test to see if file is under cgi-bin directory
  // if the file exists return pointer to the file
  mapName = getPathInfo();
  mapName++;
  if (!stat(mapName,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
    fp = fopen(mapName,"r");
    if(fp!=NULL)
      return(fp);
  }


  Error("Could not find image map file.  (openImageFile())");
  return(NULL);


}


// ripped this off of the NCSA imagemap program.
// converts the argument passed to the script
// tp two integers.
int cgi::convertArg2Point(int numArgs, char **args,int &xpos,int &ypos) {

  char *p;

  if (numArgs != 2) {
    Error("Wrong number of arguments passed.",
	  "Could not convert argument to \"point\"\n");
    return(0);
  }

  if(!(p = strchr(args[1],','))) {
    Error("Could not convert argument to \"point\"\n");
    return(0);
  }

  *p++ = '\0';
  xpos = (int) atoi(args[1]);
  ypos = (int) atoi(p);
  return(1);

}

// ripped this off of the NCSA imagemap program.
// reads through the image map file and returns
// the first name that matches the geometry
int cgi::findMapLocation(FILE *fp,char mapLocation[],int xpos,int ypos){
  int sawpoint = 0;
  int i,j,k;

  int pointarray[MAXVERTS][2];
  int dist=0, mindist=0;

  char type[MAXLINE];
  char url[MAXLINE];
  char num[10];
  char input[MAXLINE];
  char def[MAXLINE];

  if((xpos<0)||(ypos<0)) {
    Error("Invalid numbers passed to findMapLocation.");
    return(0);
  }

  if (fp==NULL) {
    Error("NULL file pointer passes to findMapLoation.");
    return(0);
  }

  while(fgets(input,MAXLINE,fp)) {

    if((input[0] == '#') || (!input[0]))
      continue;

    type[0] = '\0';url[0] = '\0';

    for(i=0;!isspace(input[i]) && (input[i]);i++)
      type[i] = input[i];
    type[i] = '\0';

    while(isspace(input[i])) ++i;
    for(j=0;input[i] && !isspace(input[i]);++i,++j)
      url[j] = input[i];
    url[j] = '\0';

    if(!strcmp(type,"default") && !sawpoint) {
      strcpy(def,url);
      continue;
    }

    k=0;
    while (input[i]) {
      while (isspace(input[i]) || input[i] == ',') i++;

      j = 0;
      while (isdigit(input[i]))
	num[j++] = input[i++];
      num[j] = '\0';

      if (num[0] != '\0')
	pointarray[k][0] = (int) atoi(num);
      else
	break;

      while (isspace(input[i]) || input[i] == ',') i++;

      j = 0;
      while (isdigit(input[i]))
	num[j++] = input[i++];
      num[j] = '\0';

      if (num[0] != '\0')
	pointarray[k++][1] = (int) atoi(num);
      else {
	Error("Missing y value.");
	return(0);
      }

    }
    
    pointarray[k][0] = -1;
    if(!strcmp(type,"poly")) 
      if(pointinpoly(xpos,ypos,pointarray)) {
	strcpy(mapLocation,url);
	return(1);
      }


    if(!strcmp(type,"circle"))
      if(pointincircle(xpos,ypos,pointarray)){
	strcpy(mapLocation,url);
	return(1);
      }


    if(!strcmp(type,"rect")) 
      if(pointinrect(xpos,ypos,pointarray)){
	strcpy(mapLocation,url);
	return(1);
      }

    if(!strcmp(type,"point")) {
      /* Don't need to take square root. */
      dist = ((xpos - pointarray[0][0])
	      * (xpos - pointarray[0][0]))
	+ ((ypos - pointarray[0][1])
	   * (ypos - pointarray[0][1]));

      /* If this is the first point, or the nearest, set the default. */
      if ((! sawpoint) || (dist < mindist)) {
	mindist = dist;
	strcpy(def,url);
      }
      sawpoint++;

    }
  }

  if(def[0]!='\0') {
    strcpy(mapLocation,def);
    return(1);
  }

  Error("No default specified.");
  return(0);

}


// ripped this off of the NCSA imagemap program
// returns true if point is in the rectangle
int cgi::pointinrect(int xpos,int ypos,int coords[MAXVERTS][2]) {

        return ((xpos >= coords[0][X] && xpos <= coords[1][X]) &&
        (ypos >= coords[0][Y] && ypos <= coords[1][Y]));
}


// ripped this off of the NCSA imagemap program
// returns true if point is in the circle
int cgi::pointincircle(int xpos,int ypos,int coords[MAXVERTS][2]) {
        int radius1, radius2;

        radius1 = ((coords[0][Y] - coords[1][Y]) * (coords[0][Y] -
        coords[1][Y])) + ((coords[0][X] - coords[1][X]) * (coords[0][X] -
        coords[1][X]));
        radius2 = ((coords[0][Y] - ypos) * (coords[0][Y] - ypos)) +
        ((coords[0][X] - xpos) * (coords[0][X] - xpos));
        return (radius2 <= radius1);
}

// ripped this off of the NCSA imagemap program
// returns true if point is in the polygon
int cgi::pointinpoly(int xpos,int ypos,int intPgon[MAXVERTS][2]) {

        int i,numverts, inside_flag, xflag0;
        int crossings;
        double *p, *stop;
        double tx, ty, y;
	double pgon[MAXVERTS][2];

        for (i = 0; intPgon[i][X] != -1 && i < MAXVERTS; i++) {
	  pgon[i][X] = (double) intPgon[i][X];
	  pgon[i][Y] = (double) intPgon[i][Y];
	}
        numverts = i;
        crossings = 0;

        tx = (double) xpos;
        ty = (double) ypos;
        y = pgon[numverts - 1][Y];

        p = (double *) pgon + 1;
        if ((y >= ty) != (*p >= ty)) {
                if ((xflag0 = (pgon[numverts - 1][X] >= tx)) ==
                (*(double *) pgon >= tx)) {
                        if (xflag0)
                                crossings++;
                }
                else {
                        crossings += (pgon[numverts - 1][X] - (y - ty) *
                        (*(double *) pgon - pgon[numverts - 1][X]) /
                        (*p - y)) >= tx;
                }
        }

        stop = pgon[numverts];

        for (y = *p, p += 2; p < stop; y = *p, p += 2) {
                if (y >= ty) {
                        while ((p < stop) && (*p >= ty))
                                p += 2;
                        if (p >= stop)
                                break;
                        if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx)) {
                                if (xflag0)
                                        crossings++;
                        }
                        else {
                                crossings += (*(p - 3) - (*(p - 2) - ty) *
                                (*(p - 1) - *(p - 3)) / (*p - *(p - 2))) >= tx;
                        }
                }
                else {
                        while ((p < stop) && (*p < ty))
                                p += 2;
                        if (p >= stop)
                                break;
                        if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx)) {
                                if (xflag0)
                                        crossings++;
                        }
                        else {
                                crossings += (*(p - 3) - (*(p - 2) - ty) *
                                (*(p - 1) - *(p - 3)) / (*p - *(p - 2))) >= tx;
                        }
                }
        }
        inside_flag = crossings & 0x01;
        return (inside_flag);
}




// Print out an error message
void cgi::Error(const char* start,const char* specific,const char* end){

    if(outputError) {
	paragraph();
	rule();
	if(start != NULL)
	    cout << start;
	cout << " ";
	if(specific != NULL)
	    cout << specific;
	cout << " ";
	if(end != NULL)
	    cout << end;
	cout << " ";
	cout << endl;
	rule();
	paragraph();
    }
    
}



