ismap.c a mapper program

frans van hoesel (hoesel@chem.rug.nl)
Tue, 24 Aug 1993 13:57:42 +0100 (DST)


Hi,

below I include the ismap.c program I use from the EXPO terrain map.
You can use it as you like. If time comes to include it in the httpd itself,
I would prefer to do that myself (some good ideas!!) but if you
use it at a seperate port number, it is all yours.

no garantees!!

frans van hoesel

hoesel@chem.rug.nl
(on vacattion for the next two weeks)

------------------------ ismap.c ---------------------------

/*___________________________________________________________________
| ismap.c
|
| ismap decoding program written by
|
| Frans van Hoesel (c) 1993
|
| ismap works by comparing the requested filename in the URL (without leading
| path components) with the in files the directory ISMAP_PATH.
| The mapfile found in that directory have a very simple syntax
| it has lines in the form of
| <some test function> <some file to send>
| were <some test function> is one of:
| rect (xmin, ymin, xmax, ymax)
| returns true if the mouse coordinates are within
| the rectangle (mind the order of the corners)
| circle (xcenter, ycenter, radius)
| returns true if the coordinates are inside the circle
| noismap
| returns true if the browser does not support the ismap
| feature.
| default
| always true and *MUST* be present, for ismap to work
| properly
| and <some file to send> is the absolute path to the file which is
| returned to the browser if the test returned true.
| all test are performed in a top-down order, and only the first that
| returns true is executed. Therefore the default must be the last
| clause specified.
| comments start at '#'
|
| for efficienty reasons, it is best to put the test in such an order
| that the one that is most likely to return true is near the top.
|
| IMPORTANT: as this ismap decoder runs from another port number you will
| need to convert any relative links you might have to full URLs, but only
| for documents referenced via ismap.
|
| compile this using an ansi compiler (gcc?)
|
| you should set this up by editing your /etc/services file
| It should contain a line like:
| ismap 8988/tcp # mapping that works
| and in your /etc/inetd.conf:
| ismap stream tcp nowait http /usr/local/bin/ismap ismap
| where http is the user-id which has access to the map files and the
| documents returned by the ismap program; /usr/local/bin is the asumed
| location of the binairy (you might consider /usr/local/etc/httpd/ismap)
| you'll need a kill -HUP <process id of inetd>
| and if you are running YP, you'll need to push the sevices over the network
|
| Some of the code in here is untested, so if you find any bugs
| send them to hoesel@chem.rug.nl
| In near future I'll add poly(x1,y1,x2,y2,....) that test for abritrary
| polygons. (for now you can use multiple rect() to approximate the polygone)
|
| ++++++++++++++++++++++ READ THIS IF YOU ARE A DEVELOPPER +++++++++++++++++
|
| If only the browser all around the world would recognize the file type
| by looking at the correct place for the extension then somefile.gif?234,345
| would known to return a gif image, then the ismap would be served from
| the httpd directly (and we could add a feature for searchable index too).
| (this is ofcourse a hint to all the browser programmers out there )
| Having one single and simple httpd that would serve all this is needed so
| people will use ismap and searchable index much more. I'll be happy to
| modify the httpd. // in fact I could already offer you a modified
| httpd, but that would only work for html files //

| example map file:
| # this is an example mapfile
| default /usr/data/default.html
| rect(10,20,100,200) /usr/data/rect1.html
| rect(20,30,100,400) /usr/data/rect2.html
| noismap /usr/data/sorry.html

| store this file in the path specified below
| eg. /usr/local/etc/httpd/mapfiles/mymap.html
| (note that this extension is html (so browser might know that you are
| returning html files (but that could well be gifs, au, etc)
| And reference it in a document as:
| <a href="http://site.dom:8988/some_path_which_is_not_used/mymap.html">
| <img src="http://site.dom/your_image.gif" ismap></a>

| it is expected that you offer an alternative way to access rect1.html and
| rect2.html from the document sorry.html, so people without ismap support
| can still use it.

| error message are send as html files. so if you have problems in using
| this, change your returned type to html (instead of gif or au) so you can
| read these messages. I guess most people will returns html files, so
| you will not have to do anything special to get the error messages.

| frans van hoesel 1993
| hoesel@chem.rug.nl
*/

/*
* the only thing you need to configure:
* set ISMAP_PATH to the directory where the mapfiles can be found
* make sure to add an ending '/' and that the files are readable by
* the owner of this program (most likely set in inetd.conf)
*/
#define ISMAP_PATH "/usr/local/etc/httpd/mapfiles/"

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>

#define MAXLEN 256

static char text[MAXLEN + 2];
static char *chptr;
static FILE *mapfile;
static char filename[MAXLEN];
static int linenum;

void errormsg(char *format, ...) {
va_list args;

va_start(args, format);
puts("<title>Error</title>");
puts("<h1>Error from server:</h1>");
vprintf(format, args);
va_end(args);
exit (0);
}

void sendfile (char *file) {
FILE *f;
int c;

f = fopen(file, "rb");
if (f == NULL) {
errormsg("cannot open file '%s'", file);
exit(0);
}
while (!feof(f)) {
c=fgetc(f);
putchar(c);
}
}

void skipblanks(void) {

loop:
while (*chptr == ' ' || *chptr == '\t' || *chptr == '\n')
chptr++;
if (*chptr == '\0' || *chptr == '#') {
/* time to read the next line */
if (fgets(text, MAXLEN+1, mapfile) == NULL) {
errormsg("bad map file '%s' line %d",
filename, linenum);
}
linenum++;
chptr = text;
goto loop;
}
}

void expect_ch(char c) {

skipblanks();
if (*chptr == c)
chptr++;
else
errormsg("syntax error in map file '%s' line %d : '%c' expected",
filename, linenum, c);
}

int accept_int(int *x) {

char number[11];
int n;

skipblanks();
if (! isdigit(*chptr))
errormsg("error in map file '%s' line %d : digit expected", filename,
linenum);
number[0] = *chptr++;
n=1;
while (n<10 && isdigit (*chptr)) {
number[n] = *chptr;
chptr++;
n++;
}
number[n] = '\0';
*x = atoi(number);
return (*x);
}

char *accept_string(char *str) {

char *st;

skipblanks();
st = str;
while(*chptr != '\0' && *chptr != ' ' && *chptr != '\t' &&
*chptr != '\n') {
*st++ = *chptr++;
}
*st = '\0';
if (strlen(str) == 0)
errormsg("error in map file '%s' line %d: emtpy string",
filename, linenum);
return (str);
}


main() {

int x,y;
int noismap;
char *qmark;
int len;
char *first;
char *last;
int n;

char fullpath[MAXLEN * 2];

if (fgets(text, MAXLEN, stdin) == NULL) {
/* apparently nothing, this is very unexpected
* but there is nothing I can do
* I'll return some html file, which may be is
* the wrong file type, but who cares
*/
errormsg("no connection");
}
text[MAXLEN+1] = '\0';

len = strlen(text);
/* strip of trailing blanks */
while (len > 0 && (text[len-1] == ' ' || text[len-1] == '\t' ||
text[len-1] =='\n' || text[len-1] =='\r' )) {
len--;
}
text[len] = '\0';
qmark = strrchr(text, '?');
if (qmark == NULL) {
/* browser doesn't support ismap */
noismap = 1;
/* find the end of filename */
last = text+len;
} else {
noismap = 0;
last = qmark - 1;
}
first = last;
while (first >= text && *first != '/' &&
*first != ' ' && *first != '\t')
first--;
if (first < text || *first == ' ' || *first == '\t') {
errormsg("no such file");
}
if (*first == '/')
first++;

/* ok assume that the file name is from text[first] to text[last] */

strcpy(fullpath, ISMAP_PATH);
strncpy(filename, first, last-first+1);
filename[last-first+1] = '\0';
if (strlen(filename) == 0)
errormsg("error in href to map file");
strcat(fullpath, filename);

mapfile = fopen(fullpath,"r");
if (mapfile == NULL)
errormsg("cannot open requested map file '%s'", fullpath);

/* when there is a question mark, read the coordinates */
x = y = 0;
if (qmark != NULL) {
n = sscanf(qmark+1, "%d,%d", &x, &y);
if (n != 2) {
/* couldn't read the coordinates, act as
* if ismap was unsupported
*/
qmark = NULL;
noismap = 1;
}
}
/* start reading the map file */
/* it is asumed that no line in the mapfile is longer than
* MAXLEN; it won't break memory allocation if there are
* longer lines, but these lines won't work (maybe in some
* rare case the next few lines too.
* really nothing to get upset about, just fold your lines (at
* positions were balnks are allowed)
* This loop re-uses the variable fullpath
*/
linenum = 0;
while (fgets(text, MAXLEN+1, mapfile) != NULL) {
linenum ++;
chptr = text;
skipblanks();
/* find the keywords, ignoring any other lines .
* switch on the first character, and compare the rest
*/
switch (*chptr) {

case 'r': {
int xmin, ymin, xmax, ymax;

if (strncmp(chptr+1,"ect",3) == 0) {
chptr+=4;
expect_ch('(');
accept_int(&xmin);
expect_ch(',');
accept_int(&ymin);
expect_ch(',');
accept_int(&xmax);
expect_ch(',');
accept_int(&ymax);
expect_ch(')');
if (x <= xmax && x >= xmin && y >= ymin
&& y<=ymax) {
accept_string(fullpath);
sendfile(fullpath);
return (0);
}
};
break;
}
case 'c': {
int xcenter, ycenter;
int radius;

if (strncmp(chptr+1,"ircle",5) == 0) {

chptr+=6;
expect_ch('(');
accept_int(&xcenter);
expect_ch(',');
accept_int(&ycenter);
expect_ch(',');
accept_int(&radius);
expect_ch(')');
if ((x-xcenter)*(x-xcenter) +
(y-ycenter)*(y-ycenter) <=
radius*radius) {
accept_string(fullpath);
sendfile(fullpath);
return (0);
}
}
break;
}
case 'd': {
if (strncmp(chptr+1,"efault",6) == 0) {
chptr+=7;
accept_string(fullpath);
sendfile(fullpath);
return (0);
}
break;
}
case 'n': {
if (strncmp(chptr+1,"oismap",6) == 0) {
chptr+=7;
if (noismap == 1) {
accept_string(fullpath);
sendfile(fullpath);
return (0);
}
}
break;
}
}
}
errormsg("error in mapfile '%s': EOF before default", filename);
return (0);
}