Mailing List Archive

imagemap.c (and some various thoughts on imagemaping)
Reference: http://edgar.stern.nyu.edu/web95/214_small.html

I found this version of imagemap.c with a few changes to it and I
thought someone might want to include it with the apache distribution.

Basically, it lets you put %s's in your URLs in the map file that
are substituted by arguments in the link (see the above URL for details).

If you really want to do this right you can modify it so that
it supports arbitrary variable substituions like so:
<A HREF="/cgi-bin/imagemap/mapfile/arg1/arg2/arg3"> <IMG...> </A>
and in the map file use shell-like variables.
rect http://$1/$2/${3}000.gif X,Y x,y

It would also be really neat to have conditionals in the map file.
Perhaps even something simple like:
rect http://server/path/bar.html X,Y x,y if $1 == "foo"
rect http://server/path/baz.html X,Y x,y if $1 == "bat"
rect http://server/path/wow.html X,Y x,y if $1 == $2
== != > < would probably cover it pretty well.

Anyway, here is the source...

/*
** mapper 1.2
** 7/26/93 Kevin Hughes, kevinh@pulua.hcc.hawaii.edu
** "macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com
** All suggestions, help, etc. gratefully accepted!
**
** 1.1 : Better formatting, added better polygon code.
** 1.2 : Changed isname(), added config file specification.
**
** 11/13/93: Rob McCool, robm@ncsa.uiuc.edu
**
** 1.3 : Rewrote configuration stuff for NCSA /htbin script
**
** 12/05/93: Rob McCool, robm@ncsa.uiuc.edu
**
** 1.4 : Made CGI/1.0 compliant.
**
** 06/27/94: Chris Hyams, cgh@rice.edu
** Based on an idea by Rick Troth (troth@rice.edu)
**
** 1.5 : Imagemap configuration file in PATH_INFO. Backwards compatible.
**
** 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">
**
** 07/11/94: Craig Milo Rogers, Rogers@ISI.Edu
**
** 1.6 : Added "point" datatype: the nearest point wins. Overrides "default".
**
** 08/28/94: Carlos Varela, cvarela@ncsa.uiuc.edu
**
** 1.7 : Fixed bug: virtual URLs are now understood.
** Better error reporting when not able to open configuration file.
**
** 03/07/95: Carlos Varela, cvarela@ncsa.uiuc.edu
**
** 1.8 : Fixed bug (strcat->sprintf) when reporting error.
** Included getline() function from util.c in NCSA httpd distribution.
**
** 05/25/95: Jan Erik Odegard, odegard@ece.rice.edu
**
** 1.9 : Fixed "bug" (in sendmesg()) where default port 80 was unnecessary
** appended to URL. The old implementation typically confused the history
** keeping of most Web browsers.
**
** 06/09/95: Victor Boyko, boykov@archimedes.nyu.edu
** 2.0: Make it possible to give parameters to image maps through
** the last element of PATH_INFO (%s in the mapping file is
** substituted for the last component of PATH_INFO).
*/

#include <stdio.h>
#include <string.h>
#if !defined(pyr) && !defined(NO_STDLIB_H)
#include <stdlib.h>
#else
#include <sys/types.h>
#include <ctype.h>
char *getenv();
#endif
#include <sys/types.h>
#include <sys/stat.h>

#define CONF_FILE "/usr/local/etc/httpd/conf/imagemap.conf"

#define MAXLINE 500
#define MAXVERTS 100
#define X 0
#define Y 1
#define LF 10
#define CR 13

int isname(char);

char *pathinfo = 0, *translated;

FILE *open_map(char *mapname, char *conf)
{
FILE *fp;
int i, j;
char errstr[MAXLINE], input[MAXLINE];

/*
* if the mapname contains a '/', it represents a unix path -
* we get the translated path, and skip reading the configuration file.
*/
if (strchr(mapname,'/')) {
strcpy(conf,translated);
goto openconf;
}

if ((fp = fopen(CONF_FILE, "r")) == NULL){
sprintf(errstr, "Couldn't open configuration file: %s", CONF_FILE);
servererr(errstr);
}

while(!(getline(input,MAXLINE,fp))) {
char confname[MAXLINE];
if((input[0] == '#') || (!input[0]))
continue;
for(i=0;isname(input[i]) && (input[i] != ':');i++)
confname[i] = input[i];
confname[i] = '\0';
if(!strcmp(confname,mapname))
goto found;
}
/*
* if mapname was not found in the configuration file, it still
* might represent a file in the server root directory -
* we get the translated path, and check to see if a file of that
* name exists, jumping to the opening of the map file if it does.
*/
if(feof(fp)) {
struct stat sbuf;
strcpy(conf,getenv("PATH_TRANSLATED"));
if (!stat(conf,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG))
goto openconf;
else
servererr("Map not found in configuration file.");
}

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

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

openconf:
return fopen(conf,"r");
}

int main(int argc, char **argv)
{
char input[MAXLINE], *mapname, def[MAXLINE], conf[MAXLINE], errstr[MAXLINE];
double testpoint[2], pointarray[MAXVERTS][2];
int i, j, k;
char *t, *ot, *t2;
FILE *fp;
double dist, mindist;
int sawpoint = 0;

if (argc != 2)
servererr("Wrong number of arguments, client may not support ISMAP.");
mapname=getenv("PATH_INFO");

if((!mapname) || (!mapname[0]))
servererr("No map name given. Please read the <A HREF=\"http://hoohoo.ncsa.uiuc.edu/docs/setup/admin/Imagemap.html\">instructions</A>.<P>");


mapname++;
if(!(t = strchr(argv[1],',')))
servererr("Your client doesn't support image mapping properly.");
*t++ = '\0';
testpoint[X] = (double) atoi(argv[1]);
testpoint[Y] = (double) atoi(t);

translated = getenv("PATH_TRANSLATED");
if (!(fp = open_map(mapname, conf)))
{
ot = 0;
t = strrchr(mapname, '/');
while (t)
{
*t = 0;
pathinfo = t+1;
if (t2 = strrchr(translated, '/'))
*t2 = 0;
if (ot)
*ot = '/';
if (fp = open_map(mapname, conf))
break;
ot = t;
t = strrchr(mapname, '/');
}
if (!fp)
{
servererr("Couldn't open configuration file.");
}
}

while(!(getline(input,MAXLINE,fp))) {
char type[MAXLINE];
char url[MAXLINE];
char num[10];

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

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

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

while(isspace(input[i])) ++i;
for(j=0;input[i] && isname(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][X] = (double) 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++][Y] = (double) atoi(num);
else {
fclose(fp);
servererr("Missing y value.");
}
}
pointarray[k][X] = -1;
if(!strcmp(type,"poly"))
if(pointinpoly(testpoint,pointarray))
sendmesg(url);
if(!strcmp(type,"circle"))
if(pointincircle(testpoint,pointarray))
sendmesg(url);
if(!strcmp(type,"rect"))
if(pointinrect(testpoint,pointarray))
sendmesg(url);
if(!strcmp(type,"point")) {
/* Don't need to take square root. */
dist = ((testpoint[X] - pointarray[0][X])
* (testpoint[X] - pointarray[0][X]))
+ ((testpoint[Y] - pointarray[0][Y])
* (testpoint[Y] - pointarray[0][Y]));
/* 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])
sendmesg(def);
servererr("No default specified.");
}

sendmesg(char *url)
{
char new_url[MAXLINE];

if (strchr(url, ':')) /*** It is a full URL ***/
printf("Location: ");
else if (!strcmp(getenv("SERVER_PORT"),"80")) /* It is a virtual URL */
/* at port 80 */
printf("Location: http://%s",getenv("SERVER_NAME"));
else /* It is a virtual URL not at port 80 */
printf("Location: http://%s:%s", getenv("SERVER_NAME"),
getenv("SERVER_PORT"));

/* If additional pathinfo was supplied, then use it
in constructing the location URL
else, just use the standard URL. The construction is
a simple substitution of
pathinfo in place of the %s in the given map file's entry.
*/

if (pathinfo)
sprintf(new_url, url, pathinfo);
else
strcpy(new_url, url);

printf("%s%c%c",new_url,10,10);
printf("This document has moved <A HREF=\"%s\">here</A>%c",new_url,10);
exit(1);
}

int pointinrect(double point[2], double coords[MAXVERTS][2])
{
return ((point[X] >= coords[0][X] && point[X] <= coords[1][X]) &&
(point[Y] >= coords[0][Y] && point[Y] <= coords[1][Y]));
}

int pointincircle(double point[2], double 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] - point[Y]) * (coords[0][Y] - point[Y])) +
((coords[0][X] - point[X]) * (coords[0][X] - point[X]));
return (radius2 <= radius1);
}

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

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

tx = point[X];
ty = point[Y];
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);
}

servererr(char *msg)
{
printf("Content-type: text/html%c%c",10,10);
printf("<title>Mapping Server Error</title>");
printf("<h1>Mapping Server Error</h1>");
printf("This server encountered an error:<p>");
printf("%s", msg);
exit(-1);
}

int isname(char c)
{
return (!isspace(c));
}

int getline(char *s, int n, FILE *f) {
register int i=0;

while(1) {
s[i] = (char)fgetc(f);

if(s[i] == CR)
s[i] = fgetc(f);

if((s[i] == 0x4) || (s[i] == LF) || (i == (n-1))) {
s[i] = '\0';
return (feof(f) ? 1 : 0);
}
++i;
}
}