Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

875 lines
21 KiB

/*
Enhanced NCSA Mosaic from Spyglass
"Guitar"
Copyright 1994 Spyglass, Inc.
All Rights Reserved
Author(s):
Jim Seidman [email protected]
*/
#include "all.h"
#ifdef FEATURE_CLIENT_IMAGEMAP
/* TODO: Fix this */
#define MAP_CACHE_SIZE 15
static struct hash_table gMapCache;
static BOOL bMapCacheInit = FALSE;
static void Map_Init(void)
{
if (!bMapCacheInit)
{
Hash_Init(&gMapCache);
bMapCacheInit = TRUE;
}
}
static void x_DisposeMap(struct MapInfo *map)
{
int n;
if (map->pAreas)
{
/* Free any lists of polygon vertices */
for (n = 0; n < map->nAreaCount; n++)
{
if (map->pAreas[n].type == SHAPE_POLYGON)
{
GTR_FREE(map->pAreas[n].shape.thePoly.pVertices);
}
}
/* Free the main shape array. */
GTR_FREE(map->pAreas);
map->pAreas = NULL;
map->nAreaCount = 0;
}
if (map->pool)
{
GTR_FREE(map->pool);
map->pool = NULL;
map->nPoolSize = 0;
}
map->flags = MAP_NOTLOADED;
}
static int Map_AddToCache(const char *url, struct MapInfo *myMap)
{
struct MapInfo *map;
int ndx;
int count;
int deleteMe;
count = Hash_Count(&gMapCache);
if (count >= MAP_CACHE_SIZE)
{
deleteMe = -1;
for (ndx = 0; ndx < count; ndx++)
{
Hash_GetIndexedEntry(&gMapCache, ndx, NULL, NULL, (void **) &map);
if (!map->refCount)
{
deleteMe = ndx;
break;
}
}
if (deleteMe >= 0)
{
XX_DMsg(DBG_IMAGE, ("Deleting entry %d from the map cache\n", deleteMe));
/* If this is merely a placeholder, there may not be any map to delete */
x_DisposeMap(map);
GTR_FREE(map);
Hash_DeleteIndexedEntry(&gMapCache, deleteMe);
}
else
{
/* The cache is now overfull */
XX_DMsg(DBG_IMAGE, ("Need to delete a map from the cache, but cannot\n"));
}
}
return Hash_Add(&gMapCache, (char *) url, NULL, (void *) myMap);
}
/* Create a placeholder if this is the first time we've found a reference for this
map. If this map (or a placeholder for it) already exists, load it in now. */
struct MapInfo *Map_CreatePlaceholder(const char *src)
{
struct MapInfo *pMap;
Map_Init();
pMap = NULL;
Hash_Find(&gMapCache, (char *) src, NULL, (void **)&pMap);
if (!pMap)
{
pMap = (struct MapInfo *)GTR_MALLOC(sizeof(struct MapInfo));
memset(pMap, 0, sizeof(struct MapInfo));
pMap->flags = MAP_NOTLOADED;
Map_AddToCache(src, pMap);
}
return pMap;
}
int Map_Fetch_Async(struct Mwin *tw, int nState, void **ppInfo)
{
struct Params_Map_Fetch *pParams = *ppInfo;
int count;
int ndx;
struct MapInfo *pMap;
char *pszURL;
#ifndef FEATURE_IMG_THREADS
char buf[512 + 64];
#endif
struct Params_LoadAsync *pla;
struct DestInfo *pDest;
if (tw == NULL) return STATE_DONE;
#ifdef FEATURE_IMG_THREADS
/* we only want to deal with "REAL" Mwin */
while (tw->twParent)
{
tw = tw->twParent;
}
#endif
switch (nState)
{
case STATE_INIT:
Map_Init();
if (!(pParams->pMap->flags & MAP_NOTLOADED) || pParams->pMap->bLoading)
return STATE_DONE;
/* Find the URL for this map */
count = Hash_Count(&gMapCache);
for (ndx = 0; ndx < count; ndx++)
{
Hash_GetIndexedEntry(&gMapCache, ndx, &pszURL, NULL, (void **)&pMap);
if (pMap == pParams->pMap)
break;
}
if (pMap != pParams->pMap)
{
/* Something is very wrong - we should have a placeholder map from
when we parsed the document. */
return STATE_DONE;
}
/* Now we know the URL - create a destination */
pDest = Dest_CreateDest(pszURL);
if (!pDest)
{
pParams->pMap->flags = MAP_MISSING;
return STATE_DONE;
}
/* Load the URL */
#ifndef FEATURE_IMG_THREADS
GTR_formatmsg(RES_STRING_MAPCACHE1,buf,sizeof(buf),pDest->szRequestedURL);
WAIT_Push(tw, waitNoInteract, buf);
#endif
pParams->request = HTRequest_new();
pParams->request->fNotFromCache = pParams->fNotFromCache;
HTFormatInit(pParams->request->conversions);
pParams->request->output_format = HTAtom_for("www/map");
if (tw && tw->w3doc && tw->w3doc->szActualURL)
pParams->request->referer = tw->w3doc->szActualURL;
else
pParams->request->referer = NULL;
pParams->request->destination = pDest;
/* Go load the document */
pla = GTR_MALLOC(sizeof(*pla));
pla->request = pParams->request;
pla->pStatus = &pParams->nStatus;
pla->fLoadFromDCacheOK = FALSE;
Async_DoCall(HTLoadDocument_Async, pla);
return STATE_OTHER;
case STATE_OTHER:
if (pParams->pMap->flags & MAP_NOTLOADED && pParams->nStatus >= 0)
{
pParams->pMap->flags = MAP_MISSING;
}
Dest_DestroyDest(pParams->request->destination);
HTRequest_delete(pParams->request);
WAIT_Pop(tw);
return STATE_DONE;
case STATE_ABORT:
Dest_DestroyDest(pParams->request->destination);
HTRequest_delete(pParams->request);
WAIT_Pop(tw);
return STATE_DONE;
}
XX_Assert((0), ("Function called with illegal state: %d", nState));
return STATE_DONE;
}
BOOL Map_IsValid(struct MapInfo *map)
{
return (!map->flags) && (map->nAreaCount > 0);
}
/* This function determines whether the point given by (tx,ty)
is inside the given polygon. It does this by drawing an
imaginary ray from the point off to (infinity,ty). If this
line crosses the segments of the polygon an odd number of
times, the point is inside the polygon. */
static BOOL x_IsPointInPolygon(int tx, int ty, struct _poly *pPoly)
{
BOOL xflag0;
int i, ip1;
int crossings;
int y;
struct _vertex *pVerts;
short nCount;
/* First we check if the point is inside the bounding box */
if (!(pPoly->rBound.left <= tx &&
pPoly->rBound.right >= tx &&
pPoly->rBound.top <= ty &&
pPoly->rBound.bottom >= ty))
{
return FALSE;
}
crossings = 0;
/* This saves us lots of dereferences for speed */
pVerts = pPoly->pVertices;
nCount = pPoly->nVertCount;
y = pVerts[nCount - 1].y;
/* Check the side between the first and last vertices separately
since it doesn't fit conveniently into the loop. */
i = 0;
if ((y >= ty) != (pVerts[0].y >= ty))
{
/* This side of the polygon extends vertically across the point
we're testing. We need to look at the x coordinates to see
if the side rests to the right of our point. */
xflag0 = (pVerts[nCount - 1].x >= tx);
if (xflag0 == (pVerts[0].x >= tx))
{
/* Both vertices are on the same side of our point. If that's
the right side, our imaginary ray will cross it. */
if (xflag0)
crossings++;
}
else
{
/* One vertex is to the left of us, one is to the right. We
need to interpolate between the points to find out whether
or not the line is to the right of us. */
crossings += (pVerts[nCount - 1].x -
((y - ty) *
(float) (pVerts[0].x - pVerts[nCount - 1].x) /
(float) (pVerts[0].y - y))) >= tx;
}
}
for (i = 0, ip1 = 1; ip1 < nCount; i++, ip1++)
{
if (pVerts[i].y >= ty)
{
/* This vertex is below the point we're checking. As long as
vertices continue to be below the point, we know a line
extending to the right won't cross a side. */
while (pVerts[ip1].y >= ty)
{
if (ip1 < nCount)
{
i++;
ip1++;
}
else
break;
}
if (ip1 >= nCount)
break;
}
else
{
/* Same argument as previous comment, except now we're above
the point we're checking. */
while (pVerts[ip1].y < ty)
{
if (ip1 < nCount)
{
i++;
ip1++;
}
else
break;
}
if (ip1 >= nCount)
break;
}
/* We've found a pair of vertices whose side extends vertically
across the point we're testing. */
xflag0 = (pVerts[i].x >= tx);
if (xflag0 == (pVerts[ip1].x >= tx))
{
/* Both points are on the same side of us. */
if (xflag0)
crossings++;
}
else
{
/* The points are on different sides and we need to interpolate
to determine where the side lies. */
if ((pVerts[i].x -
((pVerts[i].y - ty) *
(float) (pVerts[ip1].x - pVerts[i].x) /
(float) (pVerts[ip1].y - pVerts[i].y))) >= tx)
{
crossings++;
}
}
}
/* If there were an odd number of crossings, we were inside. */
return ((crossings & 0x01) == 0x01);
}
/* Return the pointer that a given point in a map points to, or NULL if none */
const char *Map_FindLink(struct MapInfo *pmap, int x, int y)
{
const char *result = NULL;
BOOL bFound = FALSE;
int n;
for (n = 0; n < pmap->nAreaCount; n++)
{
switch (pmap->pAreas[n].type)
{
case SHAPE_RECT:
if (pmap->pAreas[n].shape.theRect.left <= x &&
pmap->pAreas[n].shape.theRect.right >= x &&
pmap->pAreas[n].shape.theRect.top <= y &&
pmap->pAreas[n].shape.theRect.bottom >= y)
{
bFound = TRUE;
}
break;
case SHAPE_CIRCLE:
{
int xdiff, ydiff;
xdiff = x - pmap->pAreas[n].shape.theCirc.x;
ydiff = y - pmap->pAreas[n].shape.theCirc.y;
if (xdiff * xdiff + ydiff * ydiff <= pmap->pAreas[n].shape.theCirc.r2)
{
bFound = TRUE;
}
break;
}
case SHAPE_POLYGON:
bFound = x_IsPointInPolygon(x, y, &pmap->pAreas[n].shape.thePoly);
break;
}
if (bFound)
{
if (pmap->pAreas[n].hrefOffset != -1)
{
result = pmap->pool + pmap->pAreas[n].hrefOffset;
}
/* else we return NULL, indicating no link */
break;
}
}
return result;
}
/***********************************************************************************/
/* This is the code to actually build the map */
struct _MapContext {
struct MapInfo *pmap;
int nAreaSpace;
int nPoolSpace;
char base[MAX_URL_STRING + 1];
};
/* In this function call, the url and name together specify the name
of the map (i.e. "url#name"), while the base gives the base url
used to expand relative pathnames. */
struct _MapContext *Map_StartMap(const char *name, const char *url, const char *base)
{
char szURL[MAX_URL_STRING + 1];
struct _MapContext *mc;
Map_Init();
mc = GTR_CALLOC(sizeof(struct _MapContext), 1);
if (!mc)
{
return NULL;
}
sprintf(szURL, "%s#%s", url, name);
/* See if there is already a map (or placeholder) by this name */
/* mc->pmap == NULL after the Map_EndMap() */
Hash_Find(&gMapCache, szURL, NULL, (void **)&mc->pmap);
if (mc->pmap)
{
if (mc->pmap->bLoading)
{
/* Another thread is in the process of loading this same map */
GTR_FREE(mc);
return NULL;
}
x_DisposeMap(mc->pmap);
}
else
{
mc->pmap = (struct MapInfo *)GTR_MALLOC(sizeof(struct MapInfo));
memset(mc->pmap, 0, sizeof(struct MapInfo));
mc->pmap->flags = MAP_NOTLOADED;
Map_AddToCache(szURL, mc->pmap);
}
mc->pmap->bLoading = TRUE;
mc->nPoolSpace = 0;
mc->nAreaSpace = 0;
strcpy(mc->base, base);
return mc;
}
void Map_EndMap(struct _MapContext *mc)
{
XX_Assert((mc->pmap), ("Map_EndMap: map pointer is NULL!"));
if (mc->nAreaSpace > mc->pmap->nAreaCount)
{
mc->pmap->pAreas = (struct MapArea *)GTR_REALLOC(mc->pmap->pAreas, mc->pmap->nAreaCount * sizeof(struct MapArea));
}
if (mc->nPoolSpace > mc->pmap->nPoolSize)
{
mc->pmap->pool = GTR_REALLOC(mc->pmap->pool, mc->pmap->nPoolSize);
}
mc->pmap->flags = 0;
mc->pmap->bLoading = FALSE;
GTR_FREE(mc);
}
void Map_AbortMap(struct _MapContext *mc)
{
XX_Assert((mc->pmap), ("Map_EndMap: map pointer is NULL!"));
x_DisposeMap(mc->pmap);
mc->pmap->bLoading = FALSE;
GTR_FREE(mc);
}
/* Parse the coords for a polygon into our polygon structure. It
returns FALSE on failure. */
static BOOL x_ParsePolygon(struct _poly *pPoly, const char *coords)
{
int nNumCoords; /* Number of coordinates we have (# of points x 2) */
const char *p;
int n;
short x, y;
/* Count how many coordinates we have. There must be at least three
points for a valid polygon, and there must be an even number of numbers. */
nNumCoords = 1;
p = strchr(coords, ',');
while (p)
{
nNumCoords++;
p = strchr(p + 1, ',');
}
if ((nNumCoords < 6) || ((nNumCoords & 1) != 0))
{
return FALSE;
}
/* Allocate the memory to store the array of vertices */
pPoly->pVertices = GTR_CALLOC(nNumCoords / 2, sizeof(struct _vertex));
if (!pPoly->pVertices)
{
/* Out of memory */
return FALSE;
}
pPoly->nVertCount = nNumCoords / 2;
/* Set our bounding rectangle to initial ridiculous values */
pPoly->rBound.top = 32767;
pPoly->rBound.bottom = -32768;
pPoly->rBound.right = -32768;
pPoly->rBound.left = 32767;
/* Go through and parse the coordinates out of the string */
p = coords;
for (n = 0; n < pPoly->nVertCount; n++)
{
x = (short) atoi(p);
pPoly->pVertices[n].x = x;
if (pPoly->rBound.right < x)
pPoly->rBound.right = x;
if (pPoly->rBound.left > x)
pPoly->rBound.left = x;
p = strchr(p, ',');
if (!p)
{
/* Should never happen! */
GTR_FREE(pPoly->pVertices);
return FALSE;
}
p++;
y = (short) atoi(p);
pPoly->pVertices[n].y = y;
if (pPoly->rBound.top > y)
pPoly->rBound.top = y;
if (pPoly->rBound.bottom < y)
pPoly->rBound.bottom = y;
/* If this is the last vertex, there won't be another comma */
if (n != (pPoly->nVertCount - 1))
{
p = strchr(p, ',');
if (!p)
{
/* Should never happen! */
GTR_FREE(pPoly->pVertices);
return FALSE;
}
p++;
}
}
/* Make sure the first and last vertices aren't identical */
if (pPoly->pVertices[0].x == pPoly->pVertices[pPoly->nVertCount - 1].x &&
pPoly->pVertices[0].y == pPoly->pVertices[pPoly->nVertCount - 1].y)
{
pPoly->nVertCount--;
if (pPoly->nVertCount < 3)
{
/* There were only two unique points */
GTR_FREE(pPoly->pVertices);
return FALSE;
}
}
return TRUE;
}
void Map_AddToMap(struct _MapContext *mc, const char *coords, const char *href, BOOL nohref, const char *shape)
{
char *p;
if (!mc || !mc->pmap || !coords || !*coords)
return;
if (mc->pmap->nAreaCount >= mc->nAreaSpace)
{
if (mc->pmap->pAreas)
mc->pmap->pAreas = (struct MapArea *)GTR_REALLOC(mc->pmap->pAreas, (mc->nAreaSpace + 8) * sizeof(struct MapArea));
else
mc->pmap->pAreas = (struct MapArea *)GTR_MALLOC(8 * sizeof(struct MapArea));
mc->nAreaSpace += 8;
}
memset(&mc->pmap->pAreas[mc->pmap->nAreaCount], 0, sizeof(struct MapArea));
/* If the shape isn't present, we assume RECT, as per spec */
if (!shape || !*shape || !GTR_strcmpi(shape, "rect") || !GTR_strcmpi(shape, "rectangle"))
{
mc->pmap->pAreas[mc->pmap->nAreaCount].type = SHAPE_RECT;
}
else if (!GTR_strcmpi(shape, "circle") || !GTR_strcmpi(shape, "circ"))
{
mc->pmap->pAreas[mc->pmap->nAreaCount].type = SHAPE_CIRCLE;
}
else if ( !GTR_strcmpi(shape, "polygon") || !GTR_strcmpi(shape, "poly"))
{
mc->pmap->pAreas[mc->pmap->nAreaCount].type = SHAPE_POLYGON;
}
else
{
mc->pmap->pAreas[mc->pmap->nAreaCount].type = SHAPE_UNKNOWN;
}
switch (mc->pmap->pAreas[mc->pmap->nAreaCount].type)
{
case SHAPE_RECT:
/* Process the "left,top,right,bottom" sequence */
mc->pmap->pAreas[mc->pmap->nAreaCount].shape.theRect.left = atoi(coords);
p = strchr(coords, ',');
if (p)
{
p++;
mc->pmap->pAreas[mc->pmap->nAreaCount].shape.theRect.top = atoi(p);
p = strchr(p, ',');
if (p)
{
p++;
mc->pmap->pAreas[mc->pmap->nAreaCount].shape.theRect.right = atoi(p);
p = strchr(p, ',');
if (p)
{
p++;
mc->pmap->pAreas[mc->pmap->nAreaCount].shape.theRect.bottom = atoi(p);
}
}
}
if (!mc->pmap->pAreas[mc->pmap->nAreaCount].shape.theRect.right || !mc->pmap->pAreas[mc->pmap->nAreaCount].shape.theRect.bottom)
{
/* It wasn't a valid rectangle */
mc->pmap->pAreas[mc->pmap->nAreaCount].type = SHAPE_UNKNOWN;
}
break;
case SHAPE_CIRCLE:
/* Process the "x,y,r" sequence */
mc->pmap->pAreas[mc->pmap->nAreaCount].shape.theCirc.x = atoi(coords);
p = strchr(coords, ',');
if (p)
{
p++;
mc->pmap->pAreas[mc->pmap->nAreaCount].shape.theCirc.y = atoi(p);
p = strchr(p, ',');
if (p)
{
p++;
mc->pmap->pAreas[mc->pmap->nAreaCount].shape.theCirc.r2 = atoi(p);
/* Square it */
mc->pmap->pAreas[mc->pmap->nAreaCount].shape.theCirc.r2 = mc->pmap->pAreas[mc->pmap->nAreaCount].shape.theCirc.r2 * mc->pmap->pAreas[mc->pmap->nAreaCount].shape.theCirc.r2;
}
}
if (!mc->pmap->pAreas[mc->pmap->nAreaCount].shape.theCirc.r2)
{
/* It wasn't a valid circle */
mc->pmap->pAreas[mc->pmap->nAreaCount].type = SHAPE_UNKNOWN;
}
break;
case SHAPE_POLYGON:
/* Process the series of coordinates */
if (x_ParsePolygon(&mc->pmap->pAreas[mc->pmap->nAreaCount].shape.thePoly, coords))
{
mc->pmap->pAreas[mc->pmap->nAreaCount].type = SHAPE_POLYGON;
}
else
{
/* It wasn't a valid polygon for some reason */
mc->pmap->pAreas[mc->pmap->nAreaCount].type = SHAPE_UNKNOWN;
}
break;
}
if (href && href[0])
{
int len;
char *mycopy = 0;
char *stripped = 0;
char *url = 0;
mycopy = GTR_strdup(href);
stripped = HTStrip(mycopy);
url = HTParse(stripped,
mc->base,
PARSE_ACCESS | PARSE_HOST | PARSE_PATH | PARSE_PUNCTUATION | PARSE_ANCHOR);
GTR_FREE(mycopy);
len = strlen(url);
if (mc->pmap->nPoolSize + len >= mc->nPoolSpace)
{
if (mc->pmap->pool)
{
mc->nPoolSpace += MAX_URL_STRING + 1;
mc->pmap->pool = GTR_REALLOC(mc->pmap->pool, mc->nPoolSpace);
}
else
{
mc->nPoolSpace = 4 * (MAX_URL_STRING + 1);
mc->pmap->pool = GTR_MALLOC(mc->nPoolSpace);
}
}
strcpy(mc->pmap->pool + mc->pmap->nPoolSize, url);
mc->pmap->pAreas[mc->pmap->nAreaCount].hrefOffset = mc->pmap->nPoolSize;
mc->pmap->nPoolSize += len + 1;
GTR_FREE(url);
}
else
{
mc->pmap->pAreas[mc->pmap->nAreaCount].hrefOffset = (unsigned long) -1;
}
mc->pmap->nAreaCount++;
}
void Map_Unload(struct MapInfo *pmap)
{
if (!pmap->bLoading)
{
x_DisposeMap(pmap);
}
}
/***********************************************************************************/
/* The code from here on down is the stream stuff for retrieving a map */
struct _HTStructured
{
CONST HTStructuredClass *isa;
struct _MapContext *mc;
char href[MAX_URL_STRING + 1];
char my_url[MAX_URL_STRING + 1];
char base_url[MAX_URL_STRING + 1];
};
PRIVATE void HTMap_free(HTStructured * me)
{
if (me->mc)
{
/* Implicitly terminate previous map */
Map_EndMap(me->mc);
}
GTR_FREE(me);
}
PRIVATE void HTMap_abort(HTStructured * me, HTError e)
{
if (me->mc)
{
Map_AbortMap(me->mc);
me->mc = NULL;
}
HTMap_free(me);
}
PRIVATE void HTMap_put_character(HTStructured *me, char c)
{
}
PRIVATE void HTMap_put_entity(HTStructured * me, int entity_number)
{
}
PRIVATE void HTMap_put_string(HTStructured * me, CONST char *s)
{
}
PRIVATE void HTMap_write(HTStructured * me, CONST char *s, int l)
{
}
PRIVATE void HTMap_start_element(HTStructured * me, int element_number, CONST BOOL * present, CONST char **value)
{
switch (element_number)
{
case HTML_MAP:
if (me->mc)
{
/* Implicitly terminate previous map */
Map_EndMap(me->mc);
me->mc = NULL;
}
if (present[HTML_MAP_NAME])
me->mc = Map_StartMap(value[HTML_MAP_NAME], me->my_url, me->base_url);
break;
case HTML_AREA:
if (me->mc) {
Map_AddToMap(
me->mc,
present[HTML_AREA_COORDS] ? value[HTML_AREA_COORDS] : NULL,
present[HTML_AREA_HREF] ? value[HTML_AREA_HREF] : NULL,
present[HTML_AREA_NOHREF],
present[HTML_AREA_SHAPE] ? value[HTML_AREA_SHAPE] : NULL);
}
break;
case HTML_BASE:
if (present[HTML_BASE_HREF])
{
char *mycopy = 0;
char *stripped;
mycopy = GTR_strdup(value[HTML_BASE_HREF]);
stripped = HTStrip(mycopy);
strcpy(me->base_url, stripped);
GTR_FREE(mycopy);
}
break;
}
}
PRIVATE void HTMap_end_element(HTStructured * me, int element_number)
{
switch (element_number)
{
case HTML_MAP:
if (me->mc)
{
Map_EndMap(me->mc);
me->mc = NULL;
}
break;
}
}
PRIVATE CONST HTStructuredClass HTMap =
{
"HTMLToMap",
HTMap_free,
HTMap_abort,
HTMap_put_character, HTMap_put_string, HTMap_write,
HTMap_start_element, HTMap_end_element,
HTMap_put_entity, NULL, NULL, NULL
};
PUBLIC HTStream *HTMLToMap(struct Mwin *tw, HTRequest * request, void *param, HTFormat input_format, HTFormat output_format, HTStream * output_stream)
{
HTStructured *me = (HTStructured *) GTR_CALLOC(1, sizeof(*me));
if (me)
{
strcpy(me->my_url, request->destination->szRequestedURL);
strcpy(me->base_url, request->destination->szRequestedURL);
me->mc = NULL;
me->isa = (HTStructuredClass *) &HTMap;
return SGML_new(tw, &HTMLP_dtd, me, request);
}
else
{
return NULL;
}
}
void Map_DeleteAll(void)
{
int i;
int count;
struct MapInfo *map;
if (bMapCacheInit)
{
count = Hash_Count(&gMapCache);
XX_DMsg(DBG_IMAGE, ("Map_DeleteAll: count=%d\n", count));
for (i = 0; i < count; i++)
{
Hash_GetIndexedEntry(&gMapCache, i, NULL, NULL, (void **) &map);
x_DisposeMap(map);
GTR_FREE(map);
}
Hash_FreeContents(&gMapCache);
}
}
#endif /* FEATURE_CLIENT_IMAGEMAP */