1 /*
2  * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
3  *
4  * This file is part of NetSurf, http://www.netsurf-browser.org/
5  *
6  * NetSurf is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * NetSurf is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /**
20  * \file
21  * Implementation of HTML image maps
22  *
23  * \todo should this should use the general hashmap instead of its own
24  */
25 
26 #include <assert.h>
27 #include <stdbool.h>
28 #include <string.h>
29 #include <strings.h>
30 
31 #include <dom/dom.h>
32 
33 #include "utils/log.h"
34 #include "utils/corestrings.h"
35 #include "content/content_protected.h"
36 #include "content/hlcache.h"
37 
38 #include "html/box.h"
39 #include "html/box_construct.h"
40 #include "html/private.h"
41 #include "html/imagemap.h"
42 
43 #define HASH_SIZE 31 /* fixed size hash table */
44 
45 typedef enum {
46 	IMAGEMAP_DEFAULT,
47 	IMAGEMAP_RECT,
48 	IMAGEMAP_CIRCLE,
49 	IMAGEMAP_POLY
50 } imagemap_entry_type;
51 
52 struct mapentry {
53 	imagemap_entry_type type;	/**< type of shape */
54 	nsurl *url;			/**< absolute url to go to */
55 	char *target;			/**< target frame (if any) */
56 	union {
57 		struct {
58 			int x;		/**< x coordinate of centre */
59 			int y;		/**< y coordinate of center */
60 			int r;		/**< radius of circle */
61 		} circle;
62 		struct {
63 			int x0;		/**< left hand edge */
64 			int y0;		/**< top edge */
65 			int x1;		/**< right hand edge */
66 			int y1;		/**< bottom edge */
67 		} rect;
68 		struct {
69 			int num;	/**< number of points */
70 			float *xcoords;	/**< x coordinates */
71 			float *ycoords;	/**< y coordinates */
72 		} poly;
73 	} bounds;
74 	struct mapentry *next;		/**< next entry in list */
75 };
76 
77 struct imagemap {
78 	char *key;		/**< key for this entry */
79 	struct mapentry *list;	/**< pointer to linked list of entries */
80 	struct imagemap *next;	/**< next entry in this hash chain */
81 };
82 
83 /**
84  * Create hashtable of imagemaps
85  *
86  * \param c The containing content
87  * \return true on success, false otherwise
88  */
imagemap_create(html_content * c)89 static bool imagemap_create(html_content *c)
90 {
91 	assert(c != NULL);
92 
93 	if (c->imagemaps == NULL) {
94 		c->imagemaps = calloc(HASH_SIZE, sizeof(struct imagemap *));
95 		if (c->imagemaps == NULL) {
96 			return false;
97 		}
98 	}
99 
100 	return true;
101 }
102 
103 /**
104  * Hash function.
105  *
106  * \param key The key to hash.
107  * \return The hashed value.
108  */
imagemap_hash(const char * key)109 static unsigned int imagemap_hash(const char *key)
110 {
111 	unsigned int z = 0;
112 
113 	if (key == 0) return 0;
114 
115 	for (; *key != 0; key++) {
116 		z += *key & 0x1f;
117 	}
118 
119 	return (z % (HASH_SIZE - 1)) + 1;
120 }
121 
122 /**
123  * Add an imagemap to the hashtable, creating it if it doesn't exist
124  *
125  * \param c The containing content
126  * \param key The name of the imagemap
127  * \param list List of map regions
128  * \return true on succes, false otherwise
129  */
130 static bool
imagemap_add(html_content * c,dom_string * key,struct mapentry * list)131 imagemap_add(html_content *c, dom_string *key, struct mapentry *list)
132 {
133 	struct imagemap *map;
134 	unsigned int slot;
135 
136 	assert(c != NULL);
137 	assert(key != NULL);
138 	assert(list != NULL);
139 
140 	if (imagemap_create(c) == false)
141 		return false;
142 
143 	map = calloc(1, sizeof(*map));
144 	if (map == NULL)
145 		return false;
146 
147 	/* \todo Stop relying on NULL termination of dom_string */
148 	map->key = strdup(dom_string_data(key));
149 	if (map->key == NULL) {
150 		free(map);
151 		return false;
152 	}
153 
154 	map->list = list;
155 
156 	slot = imagemap_hash(map->key);
157 
158 	map->next = c->imagemaps[slot];
159 	c->imagemaps[slot] = map;
160 
161 	return true;
162 }
163 
164 /**
165  * Free list of imagemap entries
166  *
167  * \param list Pointer to head of list
168  */
imagemap_freelist(struct mapentry * list)169 static void imagemap_freelist(struct mapentry *list)
170 {
171 	struct mapentry *entry, *prev;
172 
173 	assert(list != NULL);
174 
175 	entry = list;
176 
177 	while (entry != NULL) {
178 		prev = entry;
179 
180 		nsurl_unref(entry->url);
181 
182 		if (entry->target)
183 			free(entry->target);
184 
185 		if (entry->type == IMAGEMAP_POLY) {
186 			free(entry->bounds.poly.xcoords);
187 			free(entry->bounds.poly.ycoords);
188 		}
189 
190 		entry = entry->next;
191 		free(prev);
192 	}
193 }
194 
195 /**
196  * Destroy hashtable of imagemaps
197  *
198  * \param c The containing content
199  */
imagemap_destroy(html_content * c)200 void imagemap_destroy(html_content *c)
201 {
202 	unsigned int i;
203 
204 	assert(c != NULL);
205 
206 	/* no imagemaps -> return */
207 	if (c->imagemaps == NULL)
208 		return;
209 
210 	for (i = 0; i != HASH_SIZE; i++) {
211 		struct imagemap *map, *next;
212 
213 		map = c->imagemaps[i];
214 		while (map != NULL) {
215 			next = map->next;
216 			imagemap_freelist(map->list);
217 			free(map->key);
218 			free(map);
219 			map = next;
220 		}
221 	}
222 
223 	free(c->imagemaps);
224 }
225 
226 /**
227  * Dump imagemap data to the log
228  *
229  * \param c The containing content
230  */
imagemap_dump(html_content * c)231 void imagemap_dump(html_content *c)
232 {
233 	unsigned int i;
234 
235 	int j;
236 
237 	assert(c != NULL);
238 
239 	if (c->imagemaps == NULL)
240 		return;
241 
242 	for (i = 0; i != HASH_SIZE; i++) {
243 		struct imagemap *map;
244 		struct mapentry *entry;
245 
246 		map = c->imagemaps[i];
247 		while (map != NULL) {
248 			NSLOG(netsurf, INFO, "Imagemap: %s", map->key);
249 
250 			for (entry = map->list; entry; entry = entry->next) {
251 				switch (entry->type) {
252 				case IMAGEMAP_DEFAULT:
253 					NSLOG(netsurf, INFO, "\tDefault: %s",
254 					      nsurl_access(entry->url));
255 					break;
256 				case IMAGEMAP_RECT:
257 					NSLOG(netsurf, INFO,
258 					      "\tRectangle: %s: [(%d,%d),(%d,%d)]",
259 					      nsurl_access(entry->url),
260 					      entry->bounds.rect.x0,
261 					      entry->bounds.rect.y0,
262 					      entry->bounds.rect.x1,
263 					      entry->bounds.rect.y1);
264 					break;
265 				case IMAGEMAP_CIRCLE:
266 					NSLOG(netsurf, INFO,
267 					      "\tCircle: %s: [(%d,%d),%d]",
268 					      nsurl_access(entry->url),
269 					      entry->bounds.circle.x,
270 					      entry->bounds.circle.y,
271 					      entry->bounds.circle.r);
272 					break;
273 				case IMAGEMAP_POLY:
274 					NSLOG(netsurf, INFO,
275 					      "\tPolygon: %s:",
276 					      nsurl_access(entry->url));
277 					for (j = 0; j != entry->bounds.poly.num;
278 							j++) {
279 						fprintf(stderr, "(%d,%d) ",
280 							(int)entry->bounds.poly.xcoords[j],
281 							(int)entry->bounds.poly.ycoords[j]);
282 					}
283 					fprintf(stderr,"\n");
284 					break;
285 				}
286 			}
287 			map = map->next;
288 		}
289 	}
290 }
291 
292 /**
293  * Adds an imagemap entry to the list
294  *
295  * \param c  The html content that the imagemap belongs to
296  * \param n  The xmlNode representing the entry to add
297  * \param base_url  Base URL for resolving relative URLs
298  * \param entry  Pointer to list of entries
299  * \param tagtype  The type of tag
300  * \return false on memory exhaustion, true otherwise
301  */
302 static bool
imagemap_addtolist(const struct html_content * c,dom_node * n,nsurl * base_url,struct mapentry ** entry,dom_string * tagtype)303 imagemap_addtolist(const struct html_content *c,
304 		   dom_node *n,
305 		   nsurl *base_url,
306 		   struct mapentry **entry,
307 		   dom_string *tagtype)
308 {
309 	dom_exception exc;
310 	dom_string *href = NULL, *target = NULL, *shape = NULL;
311 	dom_string *coords = NULL;
312 	struct mapentry *new_map, *temp;
313 	bool ret = true;
314 
315 	if (dom_string_caseless_isequal(tagtype, corestring_dom_area)) {
316 		bool nohref = false;
317 		exc = dom_element_has_attribute(n,
318 				corestring_dom_nohref, &nohref);
319 		if ((exc != DOM_NO_ERR) || nohref)
320 			/* Skip <area nohref="anything" /> */
321 			goto ok_out;
322 	}
323 
324 	exc = dom_element_get_attribute(n, corestring_dom_href, &href);
325 	if (exc != DOM_NO_ERR || href == NULL) {
326 		/* No href="" attribute, skip this element */
327 		goto ok_out;
328 	}
329 
330 	exc = dom_element_get_attribute(n, corestring_dom_target, &target);
331 	if (exc != DOM_NO_ERR) {
332 		goto ok_out;
333 	}
334 
335 	exc = dom_element_get_attribute(n, corestring_dom_shape, &shape);
336 	if (exc != DOM_NO_ERR) {
337 		goto ok_out;
338 	}
339 
340 	/* If there's no shape, we default to rectangles */
341 	if (shape == NULL)
342 		shape = dom_string_ref(corestring_dom_rect);
343 
344 	if (!dom_string_caseless_lwc_isequal(shape, corestring_lwc_default)) {
345 		/* If not 'default' and there's no 'coords' give up */
346 		exc = dom_element_get_attribute(n, corestring_dom_coords,
347 						&coords);
348 		if (exc != DOM_NO_ERR || coords == NULL) {
349 			goto ok_out;
350 		}
351 	}
352 
353 	new_map = calloc(1, sizeof(*new_map));
354 	if (new_map == NULL) {
355 		goto bad_out;
356 	}
357 
358 	if (dom_string_caseless_lwc_isequal(shape, corestring_lwc_rect) ||
359 	    dom_string_caseless_lwc_isequal(shape, corestring_lwc_rectangle))
360 		new_map->type = IMAGEMAP_RECT;
361 	else if (dom_string_caseless_lwc_isequal(shape, corestring_lwc_circle))
362 		new_map->type = IMAGEMAP_CIRCLE;
363 	else if (dom_string_caseless_lwc_isequal(shape, corestring_lwc_poly) ||
364 		 dom_string_caseless_lwc_isequal(shape, corestring_lwc_polygon))
365 		new_map->type = IMAGEMAP_POLY;
366 	else if (dom_string_caseless_lwc_isequal(shape, corestring_lwc_default))
367 		new_map->type = IMAGEMAP_DEFAULT;
368 	else
369 		goto bad_out;
370 
371 	if (box_extract_link(c, href, base_url, &new_map->url) == false)
372 		goto bad_out;
373 
374 	if (new_map->url == NULL) {
375 		/* non-fatal error -> ignore this */
376 		goto ok_free_map_out;
377 	}
378 
379 	if (target != NULL) {
380 		/* Copy target dom string into the map data */
381 		new_map->target = malloc(dom_string_byte_length(target) + 1);
382 		if (new_map->target == NULL)
383 			goto bad_out;
384 
385 		memcpy(new_map->target,
386 		       dom_string_data(target),
387 		       dom_string_byte_length(target));
388 
389 		new_map->target[dom_string_byte_length(target)] = 0;
390 	}
391 
392 	if (new_map->type != IMAGEMAP_DEFAULT) {
393 		int x, y;
394 		float *xcoords, *ycoords;
395 		/* coordinates are a comma-separated list of values */
396 		char *val = strtok((char *)dom_string_data(coords), ",");
397 		int num = 1;
398 
399 		switch (new_map->type) {
400 		case IMAGEMAP_RECT:
401 			/* (left, top, right, bottom) */
402 			while (val != NULL && num <= 4) {
403 				switch (num) {
404 				case 1:
405 					new_map->bounds.rect.x0 = atoi(val);
406 					break;
407 				case 2:
408 					new_map->bounds.rect.y0 = atoi(val);
409 					break;
410 				case 3:
411 					new_map->bounds.rect.x1 = atoi(val);
412 					break;
413 				case 4:
414 					new_map->bounds.rect.y1 = atoi(val);
415 					break;
416 				}
417 
418 				num++;
419 				val = strtok(NULL, ",");
420 			}
421 			break;
422 		case IMAGEMAP_CIRCLE:
423 			/* (x, y, radius ) */
424 			while (val != NULL && num <= 3) {
425 				switch (num) {
426 				case 1:
427 					new_map->bounds.circle.x = atoi(val);
428 					break;
429 				case 2:
430 					new_map->bounds.circle.y = atoi(val);
431 					break;
432 				case 3:
433 					new_map->bounds.circle.r = atoi(val);
434 					break;
435 				}
436 
437 				num++;
438 				val = strtok(NULL, ",");
439 			}
440 			break;
441 		case IMAGEMAP_POLY:
442 			new_map->bounds.poly.xcoords = NULL;
443 			new_map->bounds.poly.ycoords = NULL;
444 
445 			while (val != NULL) {
446 				x = atoi(val);
447 
448 				val = strtok(NULL, ",");
449 				if (val == NULL)
450 					break;
451 
452 				y = atoi(val);
453 
454 				xcoords = realloc(new_map->bounds.poly.xcoords,
455 						num * sizeof(float));
456 				if (xcoords == NULL) {
457 					goto bad_out;
458 				}
459 				new_map->bounds.poly.xcoords = xcoords;
460 
461 				ycoords = realloc(new_map->bounds.poly.ycoords,
462 					num * sizeof(float));
463 				if (ycoords == NULL) {
464 					goto bad_out;
465 				}
466 				new_map->bounds.poly.ycoords = ycoords;
467 
468 				new_map->bounds.poly.xcoords[num - 1] = x;
469 				new_map->bounds.poly.ycoords[num - 1] = y;
470 
471 				num++;
472 				val = strtok(NULL, ",");
473 			}
474 
475 			new_map->bounds.poly.num = num - 1;
476 
477 			break;
478 		default:
479 			break;
480 		}
481 	}
482 
483 	new_map->next = NULL;
484 
485 	if (*entry) {
486 		/* add to END of list */
487 		for (temp = (*entry); temp->next != NULL; temp = temp->next)
488 			;
489 		temp->next = new_map;
490 	} else {
491 		(*entry) = new_map;
492 	}
493 
494 	/* All good, linked in, let's clean up */
495 	goto ok_out;
496 
497 bad_out:
498 	ret = false;
499 ok_free_map_out:
500 	if (new_map != NULL) {
501 		if (new_map->url != NULL)
502 			nsurl_unref(new_map->url);
503 		if (new_map->type == IMAGEMAP_POLY &&
504 				new_map->bounds.poly.ycoords != NULL)
505 			free(new_map->bounds.poly.ycoords);
506 		if (new_map->type == IMAGEMAP_POLY &&
507 				new_map->bounds.poly.xcoords != NULL)
508 			free(new_map->bounds.poly.xcoords);
509 		if (new_map->target != NULL)
510 			free(new_map->target);
511 
512 		free(new_map);
513 	}
514 ok_out:
515 	if (href != NULL)
516 		dom_string_unref(href);
517 	if (target != NULL)
518 		dom_string_unref(target);
519 	if (shape != NULL)
520 		dom_string_unref(shape);
521 	if (coords != NULL)
522 		dom_string_unref(coords);
523 
524 	return ret;
525 }
526 
527 /**
528  * Extract an imagemap from html source
529  *
530  * \param node  XML node containing map
531  * \param c     Content containing document
532  * \param entry List of map entries
533  * \param tname The sub-tags to consider on this pass
534  * \return false on memory exhaustion, true otherwise
535  */
536 static bool
imagemap_extract_map_entries(dom_node * node,html_content * c,struct mapentry ** entry,dom_string * tname)537 imagemap_extract_map_entries(dom_node *node, html_content *c,
538 			     struct mapentry **entry, dom_string *tname)
539 {
540 	dom_nodelist *nlist;
541 	dom_exception exc;
542 	unsigned long ent;
543 	uint32_t tag_count;
544 
545 	exc = dom_element_get_elements_by_tag_name(node, tname, &nlist);
546 	if (exc != DOM_NO_ERR) {
547 		return false;
548 	}
549 
550 	exc = dom_nodelist_get_length(nlist, &tag_count);
551 	if (exc != DOM_NO_ERR) {
552 		dom_nodelist_unref(nlist);
553 		return false;
554 	}
555 
556 	for (ent = 0; ent < tag_count; ++ent) {
557 		dom_node *subnode;
558 
559 		exc = dom_nodelist_item(nlist, ent, &subnode);
560 		if (exc != DOM_NO_ERR) {
561 			dom_nodelist_unref(nlist);
562 			return false;
563 		}
564 		if (imagemap_addtolist(c, subnode, c->base_url,
565 				       entry, tname) == false) {
566 			dom_node_unref(subnode);
567 			dom_nodelist_unref(nlist);
568 			return false;
569 		}
570 		dom_node_unref(subnode);
571 	}
572 
573 	dom_nodelist_unref(nlist);
574 
575 	return true;
576 }
577 
578 /**
579  * Extract an imagemap from html source
580  *
581  * \param node  XML node containing map
582  * \param c     Content containing document
583  * \param entry List of map entries
584  * \return false on memory exhaustion, true otherwise
585  */
imagemap_extract_map(dom_node * node,html_content * c,struct mapentry ** entry)586 static bool imagemap_extract_map(dom_node *node, html_content *c,
587 		struct mapentry **entry)
588 {
589 	if (imagemap_extract_map_entries(node, c, entry,
590 			corestring_dom_area) == false)
591 		return false;
592 	return imagemap_extract_map_entries(node, c, entry,
593 			corestring_dom_a);
594 }
595 
596 /**
597  * Extract all imagemaps from a document tree
598  *
599  * \param c The content to extract imagemaps from.
600  * \return false on memory exhaustion, true otherwise
601  */
602 nserror
imagemap_extract(html_content * c)603 imagemap_extract(html_content *c)
604 {
605 	dom_nodelist *nlist;
606 	dom_exception exc;
607 	unsigned long mapnr;
608 	uint32_t maybe_maps;
609 	nserror ret = NSERROR_OK;
610 
611 	exc = dom_document_get_elements_by_tag_name(c->document,
612 						    corestring_dom_map,
613 						    &nlist);
614 	if (exc != DOM_NO_ERR) {
615 		return NSERROR_DOM;
616 	}
617 
618 	exc = dom_nodelist_get_length(nlist, &maybe_maps);
619 	if (exc != DOM_NO_ERR) {
620 		ret = NSERROR_DOM;
621 		goto out_nlist;
622 	}
623 
624 	for (mapnr = 0; mapnr < maybe_maps; ++mapnr) {
625 		dom_node *node;
626 		dom_string *name;
627 		exc = dom_nodelist_item(nlist, mapnr, &node);
628 		if (exc != DOM_NO_ERR) {
629 			ret = NSERROR_DOM;
630 			goto out_nlist;
631 		}
632 
633 		exc = dom_element_get_attribute(node, corestring_dom_id,
634 						&name);
635 		if (exc != DOM_NO_ERR) {
636 			dom_node_unref(node);
637 			ret = NSERROR_DOM;
638 			goto out_nlist;
639 		}
640 
641 		if (name == NULL) {
642 			exc = dom_element_get_attribute(node,
643 							corestring_dom_name,
644 							&name);
645 			if (exc != DOM_NO_ERR) {
646 				dom_node_unref(node);
647 				ret = NSERROR_DOM;
648 				goto out_nlist;
649 			}
650 		}
651 
652 		if (name != NULL) {
653 			struct mapentry *entry = NULL;
654 			if (imagemap_extract_map(node, c, &entry) == false) {
655 				if (entry != NULL) {
656 					imagemap_freelist(entry);
657 				}
658 
659 				dom_string_unref(name);
660 				dom_node_unref(node);
661 				ret = NSERROR_NOMEM; /** @todo check this */
662 				goto out_nlist;
663 			}
664 
665 			/* imagemap_extract_map may not extract anything,
666 			 * so entry can still be NULL here. This isn't an
667 			 * error as it just means that we've encountered
668 			 * an incorrectly defined <map>...</map> block
669 			 */
670 			if ((entry != NULL) &&
671 			    (imagemap_add(c, name, entry) == false)) {
672 				imagemap_freelist(entry);
673 
674 				dom_string_unref(name);
675 				dom_node_unref(node);
676 				ret = NSERROR_NOMEM; /** @todo check this */
677 				goto out_nlist;
678 			}
679 		}
680 
681 		dom_string_unref(name);
682 		dom_node_unref(node);
683 	}
684 
685 out_nlist:
686 
687 	dom_nodelist_unref(nlist);
688 
689 	return ret;
690 }
691 
692 /**
693  * Test if a point lies within an arbitrary polygon
694  * Modified from comp.graphics.algorithms FAQ 2.03
695  *
696  * \param num Number of vertices
697  * \param xpt Array of x coordinates
698  * \param ypt Array of y coordinates
699  * \param x Left hand edge of containing box
700  * \param y Top edge of containing box
701  * \param click_x X coordinate of click
702  * \param click_y Y coordinate of click
703  * \return 1 if point is in polygon, 0 if outside. 0 or 1 if on boundary
704  */
705 static int
imagemap_point_in_poly(int num,float * xpt,float * ypt,unsigned long x,unsigned long y,unsigned long click_x,unsigned long click_y)706 imagemap_point_in_poly(int num, float *xpt, float *ypt, unsigned long x,
707 		unsigned long y, unsigned long click_x,	unsigned long click_y)
708 {
709 	int i, j, c = 0;
710 
711 	assert(xpt != NULL);
712 	assert(ypt != NULL);
713 
714 	for (i = 0, j = num - 1; i < num; j = i++) {
715 		if ((((ypt[i] + y <= click_y) && (click_y < ypt[j] + y)) ||
716 		     ((ypt[j] + y <= click_y) && (click_y < ypt[i] + y))) &&
717 		     (click_x < (xpt[j] - xpt[i]) *
718 		     (click_y - (ypt[i] + y)) / (ypt[j] - ypt[i]) + xpt[i] + x))
719 			c = !c;
720 	}
721 
722 	return c;
723 }
724 
725 /**
726  * Retrieve url associated with imagemap entry
727  *
728  * \param c        The containing content
729  * \param key      The map name to search for
730  * \param x        The left edge of the containing box
731  * \param y        The top edge of the containing box
732  * \param click_x  The horizontal location of the click
733  * \param click_y  The vertical location of the click
734  * \param target   Pointer to location to receive target pointer (if any)
735  * \return The url associated with this area, or NULL if not found
736  */
imagemap_get(struct html_content * c,const char * key,unsigned long x,unsigned long y,unsigned long click_x,unsigned long click_y,const char ** target)737 nsurl *imagemap_get(struct html_content *c, const char *key,
738 		unsigned long x, unsigned long y,
739 		unsigned long click_x, unsigned long click_y,
740 		const char **target)
741 {
742 	unsigned int slot = 0;
743 	struct imagemap *map;
744 	struct mapentry *entry;
745 	unsigned long cx, cy;
746 
747 	assert(c != NULL);
748 
749 	if (key == NULL)
750 		return NULL;
751 
752 	if (c->imagemaps == NULL)
753 		return NULL;
754 
755 	slot = imagemap_hash(key);
756 
757 	for (map = c->imagemaps[slot]; map != NULL; map = map->next) {
758 		if (map->key != NULL && strcasecmp(map->key, key) == 0)
759 			break;
760 	}
761 
762 	if (map == NULL || map->list == NULL)
763 		return NULL;
764 
765 	for (entry = map->list; entry; entry = entry->next) {
766 		switch (entry->type) {
767 		case IMAGEMAP_DEFAULT:
768 			/* just return the URL. no checks required */
769 			if (target)
770 				*target = entry->target;
771 			return entry->url;
772 			break;
773 		case IMAGEMAP_RECT:
774 			if (click_x >= x + entry->bounds.rect.x0 &&
775 				    click_x <= x + entry->bounds.rect.x1 &&
776 				    click_y >= y + entry->bounds.rect.y0 &&
777 				    click_y <= y + entry->bounds.rect.y1) {
778 				if (target)
779 					*target = entry->target;
780 				return entry->url;
781 			}
782 			break;
783 		case IMAGEMAP_CIRCLE:
784 			cx = x + entry->bounds.circle.x - click_x;
785 			cy = y + entry->bounds.circle.y - click_y;
786 			if ((cx * cx + cy * cy) <=
787 				(unsigned long) (entry->bounds.circle.r *
788 					entry->bounds.circle.r)) {
789 				if (target)
790 					*target = entry->target;
791 				return entry->url;
792 			}
793 			break;
794 		case IMAGEMAP_POLY:
795 			if (imagemap_point_in_poly(entry->bounds.poly.num,
796 					entry->bounds.poly.xcoords,
797 					entry->bounds.poly.ycoords, x, y,
798 					click_x, click_y)) {
799 				if (target)
800 					*target = entry->target;
801 				return entry->url;
802 			}
803 			break;
804 		}
805 	}
806 
807 	if (target)
808 		*target = NULL;
809 
810 	return NULL;
811 }
812