home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Serving the Web
/
ServingTheWeb1995.disc1of1.iso
/
systems
/
windows
/
whttpd
/
cgi-src
/
imagemap
/
imagemap.c
next >
Wrap
C/C++ Source or Header
|
1994-12-18
|
14KB
|
486 lines
// tabs = 4
//------------------------------------------------------------------------
// TITLE: IMAGEMAP.C
//
// FACILITY: Image Mapper
//
// ABSTRACT: This program is intended to serve as a "back end" to a
// world-wide web server. It takes the name of a "map" and
// a set of coordinates, and returns a URL (document address)
// specific to the coordinates and the information in the map.
//
// The maps themselves are in separate files. The maps are
// listed in a configuration file called "imagemap.cnf"
// that is located on the path given by the environment
// variable HTTPD_CONFDIR, defaulting to "c:/httpd/conf".
// Each entry in this file must be for the form:
//
// <mapname> : <mapfile pathname>
//
// Note that this provides a logical to physical mapping facility
// for the mapfiles. The mapfiles themselves list regions in the
// target bitmap as follows:
//
// <rtype> <URL> <coords>
//
// where <rtype> is "rect", "poly", "ellipse" or "circle",
// <URL> is the URL of the document to return if the testpoint
// is within that region, and <coords> are the defining x-y
// coordinates for that type of region.
//
// NOTE: The <rtype> can also be "default", in which case the URL
// is the one to send if the test point is not in any of the
// regions. In this case, no coordinates are needed.
//
//
// ENVIRONMENT: Microsoft Windows 3.1/3.11 (16-bit)
// Developed under Borland C++ 4.0
//
// AUTHOR: Bob Denny <rdenny@netcom.com>
//
// Edit Log:
//
// When Who What
//---------- --- --------------------------------------------------
// 21-Nov-94 rbd Adapted from Kevin Hughes & Casey Barton version,
// and the version I made that used doubles.
// Convert to use ints and the Windows built-in
// region handling functions.
// 17-Dec-94 rbd Normalize rects before doing the test so that
// corners may be given in any order.
//------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <windows.h>
#include "util.h"
#define DEFAULT_CONF_DIR "c:\\httpd\\conf"
#define CONF_FILE_NAME "imagemap.cnf"
#define MAXLINE 500
#define MAXVERTS 100
#define X 0
#define Y 1
#define END_SIGNAL 0xFFFFFFFF
static char *bad_tgt_msg = "Your client probably doesn't support image maps.";
static char *ofile;
static BOOL fDebug = FALSE;
void sendmesg(char *url);
void servererr(char *msg);
void debug_wait(void);
BOOL pointinrect(int point[2], int coords[MAXVERTS][2]);
BOOL pointincircle(int point[2], int coords[MAXVERTS][2]);
BOOL pointinpoly(int point[2], int pgon[MAXVERTS][2], int nvert);
BOOL pointinellipse(int point[2], int coords[MAXVERTS][2]);
static void NormalizeRect(RECT *rp);
//========================================================================
// From httpd (windows CGI 1.1):
//
// argv[1] CGI .INI file pathname
// argv[2] Input file (does not exist for Imagemap)
// argv[3] Output file
// argv[4] Coordinates
//
//========================================================================
#pragma argsused
int main(int argc, char *argv[])
{
char input[MAXLINE], mapname[MAXLINE], def[MAXLINE], conf[256];
int testpoint[2], pointarray[MAXVERTS][2];
int i, j, k;
FILE *fp;
char *t, *cp;
MSG msg;
//
// Yield to the system for a bit so the server has a chance
// to synchronize with our exit...
//
for(i=0; i<10; i++)
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
//
// Locate the configuration file via this environment variable.
//
if((t = getenv("HTTPD_CONFDIR")) != NULL)
strcpy(conf, t);
else
strcpy(conf, DEFAULT_CONF_DIR);
i = strlen(conf) - 1;
if((conf[i] != '/') && (conf[i] != '\\'))
strcat(conf, "\\"); // Assure trailing slash
strcat(conf, CONF_FILE_NAME); // Full path to our config file
//
// Run according to fDebugging mode
//
GetPrivateProfileString("System", "Debug Mode", "No",
input, MAXLINE, argv[1]);
if(tolower(input[0]) == 'y')
{
printf("Debugging mode set by server.\n");
fDebug = TRUE;
}
//
// If web server wants back-end debugging, install an atexit() handler
// that holds the console open until a key is pressed
//
if(fDebug)
atexit(debug_wait);
//
// The "Logical Path" (URL extension) contains the map name.
//
GetPrivateProfileString("CGI", "Logical Path", "",
input, MAXLINE, argv[1]);
strcpy(mapname, &input[1]); // Skip leading slash
if(mapname[0] == '\0') // No map name?
servererr(bad_tgt_msg); // Client doesn't support imagemapping
//
// argv[3] (and the "Output File") contains the name of the file
// into which we put the result (a "Location:" document or an error message).
//
ofile = argv[3];
//
// Get target coordinates. The only requirement for syntax is
// that there be two numeric strings acceptable to strtol()
// and that they be separated by ONE CHARACTER that is not.
// Browsers SHOULD send "x,y". Use base=0 so strtol() can
// deal with decimal, octal and hex values.
//
cp = argv[4];
if(cp == NULL) // Missing arg = access violation
servererr(bad_tgt_msg); // Bogus URL, no doubt
testpoint[X] = strtol(cp, &t, 0); // Attempt to convert X
if(t == cp)
servererr(bad_tgt_msg);
cp = t + 1;
testpoint[Y] = strtol(cp, &t, 0); // Attempt to convert y
if(t == cp)
servererr(bad_tgt_msg);
if(fDebug)
printf("Map = %s\nOutput = %s\nCoord = [%d,%d]\n",
mapname, ofile, testpoint[X], testpoint[Y]);
//
// Open the config file and find the line that matches the map
// name given in the URL extension.
//
if ((fp = fopen(conf,"r")) == NULL)
servererr("Couldn't open imagemap config. file.");
while(!(getline(input, MAXLINE, fp))) {
char buf[MAXLINE];
if((input[0] == '#') || (!input[0])) // # lines are comments
continue;
for(i=0; !isspace(input[i]) && (input[i] != ':'); i++)
buf[i] = input[i];
buf[i] = '\0';
if(!strcmp(buf, mapname)) // Preserve mapname case
break;
}
if(feof(fp)) {
char buf[256];
sprintf(buf, "Map \"%s\" not found in configuration file.", mapname);
fclose(fp);
servererr(buf);
}
fclose(fp);
while(isspace(input[i]) || input[i] == ':') ++i; // Skip past ":" on index line
//
// Now input[i] -> physical pathname for the mapfile. Collect it and
// open the mapfile.
//
for(j=0;input[i] && !isspace(input[i]);++i,++j)
conf[j] = input[i];
conf[j] = '\0';
if((fp=fopen(conf,"r")) == NULL)
servererr("Couldn't open map file.");
//
// Here's where we read in the regions, URLs and defining coordinates
// and for each region, perform the hit test for that region type.
//
while(!(getline(input,MAXLINE,fp))) {
char type[MAXLINE];
char url[MAXLINE];
if((input[0] == '#') || (!input[0])) // Skip comment lines
continue;
type[0] = '\0';url[0] = '\0';
for(i=0; !isspace(input[i]) && (input[i]); i++) // Get the type
type[i] = input[i];
type[i] = '\0';
while(isspace(input[i])) ++i;
for(j=0; input[i] && !isspace(input[i]); ++i, ++j)// Get the URL
url[j] = input[i];
url[j] = '\0';
if(!stricmp(type, "default")) {
strcpy(def, url);
continue;
}
//
// (rbd) Use the features of strtol() to scan off coordinate pairs
//
k = 0; // Indexes coordinate pairs
cp = &input[i]; // Switch to pointer
while (*cp != '\0')
{
pointarray[k][X] = (int)strtol(cp, &t, 0);
if(t == cp) // No number converted
{
if(*cp != '\0') // If not at end yet
cp += 1; // Skip past this stopper
continue; // Try again, stop if end of string
}
cp = t + 1; // Skip past stopper (should be ",")
pointarray[k][Y] = (int)strtol(cp, &t, 0);
if(t == cp) // If no Y, this is bad.
{
char buf[256];
fclose(fp);
sprintf(buf,
"Missing Y value in map file %s<P>offending line: %s<P>",
conf, input);
servererr(buf);
}
if(*t != '\0')
cp = t + 1;
else
cp = t;
k += 1;
}
//
// If sendmesg() is called, it never returns. It exit()s.
//
pointarray[k][X] = END_SIGNAL; // Add signal value
if(!strcmpi(type,"poly"))
if(pointinpoly(testpoint, pointarray, k))
sendmesg(url);
if(!strcmpi(type,"circle"))
if(pointincircle(testpoint, pointarray))
sendmesg(url);
if(!strcmpi(type,"ellipse"))
if(pointinellipse(testpoint, pointarray))
sendmesg(url);
if(!strcmpi(type,"rect"))
if(pointinrect(testpoint, pointarray))
sendmesg(url);
}
//
// If we get here, the testpoint was not in any of the regions.
// Send the default, unless we didn't get one, in which case,
// send an error message.
//
if(def[0])
sendmesg(def);
servererr("No default specified.");
//NOTREACHED
return(0); // Shut compiler up
}
//=============================================================================
//
// sendmesg() - Return the URL for the selected region.
//
//=============================================================================
void sendmesg(char *url) // Output destination URLs directly to OUTPUT_FILE
{
FILE *outfile;
if(fDebug)
printf("Resolved to:\n %s\n", url);
if ((outfile = fopen(ofile,"w")) == NULL)
{
printf("Couldn't open output file."); // This is ugly!
exit(-1);
}
fprintf(outfile,"Location: %s%c%c",url,10, 10);
fprintf(outfile,
"This document has moved <A HREF=\"%s\">here</A>%c", url, 10);
fclose(outfile);
exit(0);
}
//=============================================================================
//
// servererr() - Return an HTTP error message.
//
//=============================================================================
void servererr(char *msg) // Output server errors directly to OUTPUT_FILE
{
FILE *outfile;
if(fDebug)
printf("An error occurred:\n %s\n", msg);
if ((outfile = fopen(ofile,"w")) == NULL)
{
printf("Couldn't open output file.");
exit(-1);
}
fprintf(outfile,"Content-type: text/html%c%c",10,10);
fprintf(outfile,"<title>Mapping Server Error</title>");
fprintf(outfile,"<h1>Mapping Server Error</h1><HR>");
fprintf(outfile,"This server encountered an error:<p>");
fprintf(outfile,"<code>%s</code>", msg);
fclose(outfile);
exit(-1);
}
//=============================================================================
//
// sendmesg() - Return the URL for the selected region.
//
//=============================================================================
void debug_wait(void)
{
char buf[32];
printf("\nPress [enter] to exit...");
fflush(stdout);
gets(buf);
}
//=============================================================================
//
// pointinrect() - Return TRUE if point is in rectangle
//
// Rectangle is defined as top,left bottom,right
//
//=============================================================================
BOOL pointinrect(int point[2], int coords[MAXVERTS][2])
{
RECT r;
POINT p;
p.x = point[0];
p.y = point[1];
SetRect(&r, coords[0][X], coords[0][Y], coords[1][X], coords[1][Y]);
NormalizeRect(&r);
return(PtInRect(&r, p));
}
//=============================================================================
//
// pointincircle() - Return TRUE if point is in circle
//
// For compatibility with old-style maps. Circle is defined as centerpoint,
// and any point on circumference. For new maps, use ellipse (below).
//
//=============================================================================
BOOL pointincircle(int point[2], int coords[MAXVERTS][2])
{
unsigned 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] - point[Y]) * (coords[0][Y] - point[Y]))
+ ((coords[0][X] - point[X]) * (coords[0][X] - point[X]));
return (radius2 <= radius1);
}
//=============================================================================
//
// pointinellipse() - Return TRUE if point is in ellipse
//
// Ellipse is given by the bounding rectangle top,left bottom,right.
//
//=============================================================================
BOOL pointinellipse(int point[2], int coords[MAXVERTS][2])
{
RECT r;
HRGN e;
BOOL f;
SetRect(&r, coords[0][X], coords[0][Y], coords[1][X], coords[1][Y]);
NormalizeRect(&r);
e = CreateEllipticRgn(r.left, r.top, r.right, r.bottom);
f = PtInRegion(e, point[0], point[1]);
DeleteObject(e);
return(f);
}
//=============================================================================
//
// pointinpoly() - Return TRUE if point is in polygon
//
// Polygon is given by a series of vertices (x,y). WARNING: Complex
// overlapping polygons may not act like you think. See the docs on
// SetPolyFillMode() for more info. This function is intended to be
// used on non-overlapping polygons, and will work fine for them.
//
//=============================================================================
int pointinpoly(int point[2], int pgon[MAXVERTS][2], int nvert)
{
HRGN p = CreatePolygonRgn((POINT FAR *)pgon, nvert, ALTERNATE);
BOOL f = PtInRegion(p, point[0], point[1]);
DeleteObject(p);
return(f);
}
//=============================================================================
//
// NormalizeRect() - Assure topleft is really left and above
//
//=============================================================================
static void NormalizeRect(RECT *rp)
{
int i, j;
if(rp->left > rp->right)
{
i = rp->left;
j = rp->top;
rp->left = rp->right;
rp->top = rp->bottom;
rp->right = i;
rp->bottom = j;
}
}