// 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: // // : // // Note that this provides a logical to physical mapping facility // for the mapfiles. The mapfiles themselves list regions in the // target bitmap as follows: // // // // where is "rect", "poly", "ellipse" or "circle", // is the URL of the document to return if the testpoint // is within that region, and are the defining x-y // coordinates for that type of region. // // NOTE: The 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 // // 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. // 07-Feb-95 rbd Get ofile before first opportunity to call error // routine, so can report error properly. Get as much // as possible from INI file not the command line, // as the command line can get smashed by the OS // or the CRT. Header lines have CRLF termination. // Add , , and to error message. //------------------------------------------------------------------------ #include #include #include #include #include #include "util.h" #define DEFAULT_CONF_DIR "c:\\httpd\\conf" #define CONF_FILE_NAME "imagemap.cnf" #define PROGRAM_VERSION "V2.1 (09-Feb-95)" #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[256]; 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("Windows httpd image mapper %s\n", PROGRAM_VERSION); printf("Debugging mode set by server.\n"); printf("Command Line: argc = %d\n", argc); for(i = 0; i < argc; i++) printf(" argv[%d] = \"%s\"\n", i, argv[i]); 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); // // 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). Use the "Output File" variable because the // command line can be smashed by the OS or CRT. // GetPrivateProfileString("System", "Output File", "", input, MAXLINE, argv[1]); strcpy(ofile, input); // // 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 // // 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. // GetPrivateProfileString("CGI", "Query String", "", input, MAXLINE, argv[1]); cp = input; 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

offending line: %s

", 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. Check your TEMP env. var.\n"); exit(-1); } fprintf(outfile,"Location: %s\015\012",url); fprintf(outfile,"URI: <%s>\015\012\015\012",url); fprintf(outfile, "This document has moved here\012", url); 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. Check your TEMP env. var.\n"); exit(-1); } fprintf(outfile,"Content-type: text/html\012\015\012\015"); fprintf(outfile,"Mapping Server Error"); fprintf(outfile,"

Mapping Server Error


"); fprintf(outfile,"This server encountered an error:

"); fprintf(outfile,"%s", 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; } }