1 %{
2 /*
3  * This is a plug-in for GIMP.
4  *
5  * Generates clickable image maps.
6  *
7  * Copyright (C) 1998-2005 Maurits Rijk  lpeek.mrijk@consunet.nl
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
21  *
22  */
23 
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include <glib/gstdio.h>
28 
29 #include <gtk/gtk.h>
30 
31 #include "imap_circle.h"
32 #include "imap_file.h"
33 #include "imap_main.h"
34 #include "imap_polygon.h"
35 #include "imap_rectangle.h"
36 #include "imap_string.h"
37 
38 extern int csim_lex(void);
39 extern int csim_restart(FILE *csim_in);
40 static void csim_error(char* s);
41 static gchar * unescape_text(gchar *input);
42 
43 static enum {UNDEFINED, RECTANGLE, CIRCLE, POLYGON} current_type;
44 static Object_t *current_object;
45 static MapInfo_t *_map_info;
46 
47 %}
48 
49 %union {
50   int val;
51   double value;
52   char *id;
53 }
54 
55 %token<val> IMG SRC WIDTH HEIGHT BORDER USEMAP
56 %token<val> START_MAP END_MAP NAME AREA SHAPE COORDS ALT HREF NOHREF
57 %token<val> TARGET ONMOUSEOVER ONMOUSEOUT ONFOCUS ONBLUR
58 %token<val> AUTHOR DESCRIPTION BEGIN_COMMENT END_COMMENT
59 %token<value> FLOAT
60 %token<id> STRING
61 
62 %type<val> integer_value
63 
64 %%
65 
66 csim_file	: image start_map comment_lines area_list end_map
67 		;
68 
69 image		: '<' IMG SRC '=' STRING image_tags xhtml_close
70 		{
71 		   g_strreplace(&_map_info->image_name, $5);
72 		   g_free ($5);
73 		}
74 		;
75 
76 image_tags	: /* Empty */
77 		| image_tags image_tag
78 		;
79 
80 image_tag	: image_width
81 		| image_height
82 		| BORDER '=' integer_value {}
83 		| USEMAP '=' STRING { g_free ($3); }
84 		| ALT '=' STRING { g_free ($3); }
85 		;
86 
87 image_width	: WIDTH '=' integer_value
88 		{
89 		   _map_info->old_image_width = $3;
90 		}
91 		;
92 
93 image_height	: HEIGHT '=' integer_value
94 		{
95 		   _map_info->old_image_height = $3;
96 		}
97 		;
98 
99 integer_value	: FLOAT
100 		{
101 		  $$ = (gint) $1;
102 		}
103 		| STRING
104 		{
105 		  $$ = (gint) g_ascii_strtod ($1, NULL);
106 		  g_free ($1);
107 		}
108 		;
109 
110 start_map	: '<' START_MAP NAME '=' STRING '>'
111 		{
112 		   g_strreplace(&_map_info->title, $5);
113 		   g_free ($5);
114 		}
115 		;
116 
117 comment_lines	: /* empty */
118 		| comment_lines comment_line
119 		;
120 
121 comment_line	: author_line
122 		| description_line
123 		| real_comment
124 		;
125 
126 real_comment	: BEGIN_COMMENT STRING END_COMMENT
127 		{
128 		  g_free ($2);
129 		}
130 		;
131 
132 author_line	: AUTHOR STRING END_COMMENT
133 		{
134 		   g_strreplace(&_map_info->author, $2);
135 		   g_free ($2);
136 		}
137 		;
138 
139 description_line: DESCRIPTION STRING END_COMMENT
140 		{
141 		   gchar *description;
142 
143 		   description = g_strconcat(_map_info->description, $2, "\n",
144 					     NULL);
145 		   g_strreplace(&_map_info->description, description);
146 		   g_free ($2);
147 		}
148 		;
149 
150 area_list	: /* empty */
151 		| area_list area
152 		;
153 
154 area		: '<' AREA tag_list xhtml_close
155 		{
156 		   if (current_type != UNDEFINED)
157 		      add_shape(current_object);
158 		}
159 		;
160 
161 xhtml_close	: '>'
162 		| '/' '>'
163 		;
164 
165 tag_list	: /* Empty */
166 		| tag_list tag
167 		;
168 
169 tag		: shape_tag
170 		| coords_tag
171 		| href_tag
172 		| nohref_tag
173 		| alt_tag
174 		| target_tag
175 		| onmouseover_tag
176 		| onmouseout_tag
177 		| onfocus_tag
178 		| onblur_tag
179 		;
180 
181 shape_tag	: SHAPE '=' STRING
182 		{
183 		   if (!g_ascii_strcasecmp($3, "RECT")) {
184 		      current_object = create_rectangle(0, 0, 0, 0);
185 		      current_type = RECTANGLE;
186 		   } else if (!g_ascii_strcasecmp($3, "CIRCLE")) {
187 		      current_object = create_circle(0, 0, 0);
188 		      current_type = CIRCLE;
189 		   } else if (!g_ascii_strcasecmp($3, "POLY")) {
190 		      current_object = create_polygon(NULL);
191 		      current_type = POLYGON;
192 		   } else if (!g_ascii_strcasecmp($3, "DEFAULT")) {
193 		      current_type = UNDEFINED;
194 		   }
195 		   g_free ($3);
196 		}
197 		;
198 
199 coords_tag	: COORDS '=' STRING
200 		{
201 		   char *p;
202 		   if (current_type == RECTANGLE) {
203 		      Rectangle_t *rectangle;
204 
205 		      rectangle = ObjectToRectangle(current_object);
206 		      p = strtok($3, ",");
207 		      rectangle->x = atoi(p);
208 		      p = strtok(NULL, ",");
209 		      rectangle->y = atoi(p);
210 		      p = strtok(NULL, ",");
211 		      rectangle->width = atoi(p) - rectangle->x;
212 		      p = strtok(NULL, ",");
213 		      rectangle->height = atoi(p) - rectangle->y;
214 		   } else if (current_type == CIRCLE) {
215 		      Circle_t *circle;
216 
217 		      circle = ObjectToCircle(current_object);
218 		      p = strtok($3, ",");
219 		      circle->x = atoi(p);
220 		      p = strtok(NULL, ",");
221 		      circle->y = atoi(p);
222 		      p = strtok(NULL, ",");
223 		      circle->r = atoi(p);
224 		   } else if (current_type == POLYGON) {
225 		      Polygon_t *polygon = ObjectToPolygon(current_object);
226 		      GList *points;
227 		      GdkPoint *point, *first;
228 		      gint x, y;
229 
230 		      p = strtok($3, ",");
231 		      x = atoi(p);
232 		      p = strtok(NULL, ",");
233 		      y = atoi(p);
234 		      point = new_point(x, y);
235 		      points = g_list_append(NULL, (gpointer) point);
236 
237 		      while(1) {
238 			 p = strtok(NULL, ",");
239 			 if (!p)
240 			    break;
241 			 x = atoi(p);
242 			 p = strtok(NULL, ",");
243 			 y = atoi(p);
244 			 point = new_point(x, y);
245 			 points = g_list_append(points, (gpointer) point);
246 		      }
247 		      /* Remove last point if duplicate */
248 		      first = (GdkPoint*) points->data;
249 		      polygon->points = points;
250 		      if (first->x == point->x && first->y == point->y)
251 			 polygon_remove_last_point(polygon);
252 		      polygon->points = points;
253 		   }
254 
255 		   g_free ($3);
256 		}
257 		;
258 
259 href_tag	: HREF '=' STRING
260 		{
261 		   if (current_type == UNDEFINED) {
262 		      g_strreplace(&_map_info->default_url, $3);
263 		   } else {
264 		      object_set_url(current_object, unescape_text($3));
265 		   }
266 		   g_free ($3);
267 		}
268 		;
269 
270 nohref_tag	: NOHREF optional_value
271 		{
272 		}
273 		;
274 
275 optional_value	: /* Empty */
276 		| '=' STRING
277 		{
278 		   g_free ($2);
279 		}
280 		;
281 
282 alt_tag		: ALT '=' STRING
283 		{
284 		   object_set_comment(current_object, unescape_text($3));
285 		   g_free ($3);
286 		}
287 		;
288 
289 target_tag	: TARGET '=' STRING
290 		{
291 		   object_set_target(current_object, unescape_text($3));
292 		   g_free ($3);
293 		}
294 		;
295 
296 onmouseover_tag	: ONMOUSEOVER '=' STRING
297 		{
298 		   object_set_mouse_over(current_object, unescape_text($3));
299 		   g_free ($3);
300 		}
301 		;
302 
303 onmouseout_tag	: ONMOUSEOUT '=' STRING
304 		{
305 		   object_set_mouse_out(current_object, unescape_text($3));
306 		   g_free ($3);
307 		}
308 		;
309 
310 onfocus_tag	: ONFOCUS '=' STRING
311 		{
312 		   object_set_focus(current_object, unescape_text($3));
313 		   g_free ($3);
314 		}
315 		;
316 
317 onblur_tag	: ONBLUR '=' STRING
318 		{
319 		   object_set_blur(current_object, unescape_text($3));
320 		   g_free ($3);
321 		}
322 		;
323 
324 end_map		: '<' END_MAP '>'
325 		;
326 
327 %%
328 
329 static void
330 csim_error(char* s)
331 {
332    extern FILE *csim_in;
333    csim_restart(csim_in);
334 }
335 
336 gboolean
load_csim(const char * filename)337 load_csim (const char* filename)
338 {
339   gboolean status;
340   extern FILE *csim_in;
341   csim_in = g_fopen(filename, "r");
342   if (csim_in) {
343     _map_info = get_map_info();
344     status = !csim_parse();
345     fclose(csim_in);
346   } else {
347     status = FALSE;
348   }
349   return status;
350 }
351 
352 static gchar*
unescape_text(gchar * input)353 unescape_text (gchar *input)
354 {
355  /*
356   * We "unescape" simple things "in place", knowing that unescaped
357   * strings always are shorter than the original input.
358   *
359   * It is a shame there is no g_markup_unescape_text() function, but
360   * instead you have to create a full GMarkupParser/Context.
361   */
362   struct token {
363     const char *escaped;
364     const char  unescaped;
365   };
366   const struct token tab[] = {
367     { "&quot;", '"'  },
368     { "&apos;", '\'' },
369     { "&amp;",  '&'  },
370     { "&lt;",   '<'  },
371     { "&gt;",   '>'  }
372   };
373 
374   size_t i;
375   for (i = 0; i < (sizeof tab / sizeof tab[0]); i++)
376     {
377       const size_t escaped_len = strlen (tab[i].escaped);
378       char *p;
379 
380       /* FIXME: The following code does not perform a UTF-8 substring
381          search. */
382       for (p = strstr (input, tab[i].escaped);
383            p != NULL;
384            p = strstr (p, tab[i].escaped))
385         {
386           size_t copy_len;
387           *p++ = tab[i].unescaped;
388           copy_len = strlen (p) - escaped_len + 2;
389           memmove (p, p + escaped_len - 1, copy_len);
390           if (*p == 0)
391             break;
392         }
393     }
394 
395   return input;
396 }
397