1 /* pk-map.c - Support for map files.  */
2 
3 /* Copyright (C) 2020, 2021 Jose E. Marchesi */
4 
5 /* This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program 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 #include <config.h>
20 
21 #include <stdio.h>
22 #include <inttypes.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <xalloc.h>
26 #include <assert.h>
27 #include "basename-lgpl.h"
28 
29 #include "poke.h"
30 #include "pk-utils.h"
31 #include "pk-term.h"
32 #include "pk-map.h"
33 #include "pk-map-parser.h"
34 
35 /* Unique map ID.
36    This number is unique per map in a poke session.  */
37 
38 static uint64_t next_map_id;
39 
40 /* Maps for a given IOS.
41 
42    IOS_ID is the identifier of the ios.
43    MAPS is a list of chained maps.
44    CHAIN is a pointer to another next map_ios, or NULL.  */
45 
46 #define PK_MAP_IOS_ID(MAP_IOS) ((MAP_IOS)->ios_id)
47 #define PK_MAP_IOS_MAPS(MAP_IOS) ((MAP_IOS)->maps)
48 #define PK_MAP_IOS_CHAIN(MAP_IOS) ((MAP_IOS)->chain)
49 
50 struct pk_map_ios
51 {
52   int ios_id;
53   struct pk_map *maps;
54   struct pk_map_ios *chain;
55 };
56 
57 typedef struct pk_map_ios *pk_map_ios;
58 
59 /* Global containing the IOS maps defined in the running poke.  */
60 static struct pk_map_ios *poke_maps;
61 
62 static pk_map_ios
search_map_ios(int ios_id)63 search_map_ios (int ios_id)
64 {
65   pk_map_ios map_ios;
66 
67   for (map_ios = poke_maps;
68        map_ios;
69        map_ios = PK_MAP_IOS_CHAIN (map_ios))
70     {
71       if (PK_MAP_IOS_ID (map_ios) == ios_id)
72         break;
73     }
74 
75   return map_ios;
76 }
77 
78 static void
free_entry(pk_map_entry entry)79 free_entry (pk_map_entry entry)
80 {
81   free (PK_MAP_ENTRY_VARNAME (entry));
82   free (entry);
83 }
84 
85 static void
free_map(pk_map map)86 free_map (pk_map map)
87 {
88   pk_map_entry entry, tmp;
89 
90   for (entry = PK_MAP_ENTRIES (map); entry; entry = tmp)
91     {
92       tmp = PK_MAP_CHAIN (entry);
93       free_entry (entry);
94     }
95 
96   free (PK_MAP_NAME (map));
97   free (PK_MAP_SOURCE (map));
98   free (map);
99 }
100 
101 static pk_map
search_map(pk_map_ios map_ios,const char * mapname)102 search_map (pk_map_ios map_ios, const char *mapname)
103 {
104   pk_map map;
105 
106   for (map = PK_MAP_IOS_MAPS (map_ios);
107        map;
108        map = PK_MAP_CHAIN (map))
109     {
110       if (STREQ (PK_MAP_NAME (map), mapname))
111         break;
112     }
113 
114   return map;
115 }
116 
117 static pk_map_entry
search_map_entry(pk_map map,const char * name)118 search_map_entry (pk_map map, const char *name)
119 {
120   pk_map_entry map_entry;
121 
122   for (map_entry = PK_MAP_ENTRIES (map);
123        map_entry;
124        map_entry = PK_MAP_ENTRY_CHAIN (map_entry))
125     {
126       if (STREQ (PK_MAP_ENTRY_NAME (map_entry), name))
127         break;
128     }
129 
130   return map_entry;
131 }
132 
133 static char *
entry_name_to_varname(const char * name)134 entry_name_to_varname (const char *name)
135 {
136   char *varname;
137 
138   if (asprintf (&varname, "__map_entry_%" PRIu64 "_%s", next_map_id, name) == -1)
139     pk_fatal (_("out of memory"));
140   return varname;
141 }
142 
143 static char *
pk_map_alien_token_handler(const char * id,char ** errmsg)144 pk_map_alien_token_handler (const char *id, char **errmsg)
145 {
146   char *map_name = NULL;
147   char *entry_name = NULL;
148   pk_ios cur_ios;
149 
150   *errmsg = NULL;
151 
152   /* No point on going ahead if there is no current IOS.  */
153   cur_ios = pk_ios_cur (poke_compiler);
154   if (!cur_ios)
155     goto error;
156 
157   /* The format of the identifier should be:
158 
159      $MAPNAME::ENTRYNAME
160 
161      i.e. the identifier should have two components.  Verify this
162      holds for ID, and extract the fields.  */
163 
164   entry_name = strstr (id, "::");
165   if (!entry_name || strstr (entry_name + 2, "::"))
166     goto error;
167 
168   if (entry_name)
169     {
170       pk_map map;
171       int ios_id;
172 
173       map_name = xmalloc (entry_name - id + 1);
174       strncpy (map_name, id, entry_name - id);
175       map_name[entry_name - id] = '\0';
176       entry_name += 2;
177       ios_id = pk_ios_get_id (cur_ios);
178 
179       map = pk_map_search (ios_id, map_name);
180       if (map)
181         {
182           pk_map_entry entry;
183 
184           for (entry = PK_MAP_ENTRIES (map);
185                entry;
186                entry = PK_MAP_ENTRY_CHAIN (entry))
187             {
188               if (STREQ (PK_MAP_ENTRY_NAME (entry), entry_name))
189                 return xstrdup (PK_MAP_ENTRY_VARNAME (entry));
190             }
191         }
192 
193       free (map_name);
194 
195     }
196 
197  error:
198   *errmsg = xstrdup ("invalid map entry");
199   return NULL;
200 }
201 
202 void
pk_map_init(void)203 pk_map_init (void)
204 {
205   poke_maps = NULL;
206 
207   /* Install the handler for alien variables that recognizes map
208      entries.  */
209   pk_set_alien_token_fn (poke_compiler, pk_map_alien_token_handler);
210 }
211 
212 void
pk_map_shutdown(void)213 pk_map_shutdown (void)
214 {
215   struct pk_map_ios *map_ios, *next_map_ios;
216 
217   for (map_ios = poke_maps; map_ios; map_ios = next_map_ios)
218     {
219       struct pk_map *map, *next_map;
220 
221       for (map = PK_MAP_IOS_MAPS (map_ios); map; map = next_map)
222         {
223           struct pk_map_entry *entry, *next_entry;
224 
225           for (entry = PK_MAP_ENTRIES (map);
226                entry;
227                entry = next_entry)
228             {
229               next_entry = PK_MAP_ENTRY_CHAIN (entry);
230               free (entry->varname);
231               free (entry);
232             }
233 
234           next_map = PK_MAP_CHAIN (map);
235           free (map->name);
236           free (map);
237         }
238 
239       next_map_ios = PK_MAP_IOS_CHAIN (map_ios);
240       free (map_ios);
241     }
242 
243   poke_maps = NULL;
244 }
245 
246 int
pk_map_create(int ios_id,const char * mapname,const char * source)247 pk_map_create (int ios_id, const char *mapname,
248                const char *source)
249 {
250   pk_map_ios map_ios;
251 
252   /* Search for the right map_ios in poke_maps.  */
253   map_ios = search_map_ios (ios_id);
254 
255   /* If there is not a map_ios entry for this IO space, create
256      one.  */
257   if (!map_ios)
258     {
259       map_ios = xmalloc (sizeof (struct pk_map_ios));
260       PK_MAP_IOS_ID (map_ios) = ios_id;
261       PK_MAP_IOS_MAPS (map_ios) = NULL;
262 
263       PK_MAP_IOS_CHAIN (map_ios) = poke_maps;
264       poke_maps = map_ios;
265     }
266 
267   /* Create a new map and add it to the chain of maps in the
268      map_ios.  */
269   {
270     pk_map maps, map;
271 
272     maps = pk_map_get_maps (ios_id);
273 
274     /* Make sure there is not already a map with the given name.  */
275     for (map = maps; map; map = PK_MAP_CHAIN (map))
276       {
277         if (STREQ (PK_MAP_NAME (map), mapname))
278           return 0;
279       }
280 
281     /* Create an empty map and add it to the sequence.  */
282     map = xmalloc (sizeof (struct pk_map));
283     PK_MAP_ID (map) = next_map_id++;
284     PK_MAP_NAME (map) = xstrdup (mapname);
285     if (source)
286       PK_MAP_SOURCE (map) = xstrdup (source);
287     else
288       PK_MAP_SOURCE (map) = NULL;
289     PK_MAP_ENTRIES (map) = NULL;
290 
291     PK_MAP_CHAIN (map) = PK_MAP_IOS_MAPS (map_ios);
292     PK_MAP_IOS_MAPS (map_ios) = map;
293   }
294 
295   return 1;
296 }
297 
298 int
pk_map_remove(int ios_id,const char * mapname)299 pk_map_remove (int ios_id, const char *mapname)
300 {
301   pk_map_ios map_ios;
302   pk_map prev, map;
303 
304   /* Search for the right map_ios in poke_maps.  */
305   map_ios = search_map_ios (ios_id);
306 
307   if (!map_ios)
308     return 0;
309 
310   for (prev = NULL, map = PK_MAP_IOS_MAPS (map_ios);
311        map;
312        prev = map, map = PK_MAP_CHAIN (map))
313     {
314       if (STREQ (PK_MAP_NAME (map), mapname))
315         break;
316     }
317 
318   if (map == NULL)
319     /* Map not found.  */
320     return 0;
321 
322   if (prev)
323     PK_MAP_CHAIN (prev) = PK_MAP_CHAIN (map);
324   else
325     PK_MAP_IOS_MAPS (map_ios) = PK_MAP_CHAIN (map);
326 
327   free_map (map);
328 
329   return 1;
330 }
331 
332 pk_map
pk_map_search(int ios_id,const char * name)333 pk_map_search (int ios_id, const char *name)
334 {
335   pk_map maps = pk_map_get_maps (ios_id);
336 
337   if (maps)
338     {
339       pk_map map;
340 
341       for (map = maps; map; map = PK_MAP_CHAIN (map))
342         if (STREQ (PK_MAP_NAME (map), name))
343           return map;
344     }
345 
346   return NULL;
347 }
348 
349 int
pk_map_add_entry(int ios_id,const char * mapname,const char * name,const char * varname,pk_val offset)350 pk_map_add_entry (int ios_id, const char *mapname,
351                   const char *name, const char *varname,
352                   pk_val offset)
353 {
354   pk_map_ios map_ios;
355   pk_map map;
356   pk_map_entry entry;
357 
358   map_ios = search_map_ios (ios_id);
359   if (!map_ios)
360     return 0;
361 
362   map = search_map (map_ios, mapname);
363   if (!map)
364     return 0;
365 
366   entry = search_map_entry (map, name);
367   if (entry)
368     return 0;
369 
370   /* Create a new entry and chain it in the map.  The entries are kept
371      sorted by offset.  */
372   entry = xmalloc (sizeof (struct pk_map_entry));
373   PK_MAP_ENTRY_NAME (entry) = xstrdup (name);
374   PK_MAP_ENTRY_VARNAME (entry) = xstrdup (varname);
375   PK_MAP_ENTRY_OFFSET (entry) = offset;
376 
377   if (PK_MAP_ENTRIES (map) == NULL)
378     {
379       PK_MAP_ENTRY_CHAIN (entry) = NULL;
380       PK_MAP_ENTRIES (map) = entry;
381     }
382   else
383     {
384       pk_map_entry e, p;
385       uint64_t offset_bits
386         = (pk_uint_value (pk_offset_magnitude (offset))
387            * pk_uint_value (pk_offset_unit (offset)));
388 
389       for (p = NULL, e = PK_MAP_ENTRIES (map);
390            e;
391            p = e, e = PK_MAP_ENTRY_CHAIN (e))
392         {
393           pk_val e_offset = PK_MAP_ENTRY_OFFSET (e);
394 
395           if ((pk_uint_value (pk_offset_magnitude (e_offset))
396                * pk_uint_value (pk_offset_unit (e_offset)))
397               > offset_bits)
398             break;
399         }
400 
401 
402       PK_MAP_ENTRY_CHAIN (entry) = e;
403       if (p)
404         PK_MAP_ENTRY_CHAIN (p) = entry;
405       else
406         PK_MAP_ENTRIES (map) = entry;
407     }
408 
409   return 1;
410 }
411 
412 int
pk_map_remove_entry(int ios_id,const char * mapname,const char * entryname)413 pk_map_remove_entry (int ios_id, const char *mapname,
414                      const char *entryname)
415 {
416   pk_map_ios map_ios;
417   pk_map map;
418   pk_map_entry entry, prev;
419 
420   map_ios = search_map_ios (ios_id);
421   if (!map_ios)
422     return 0;
423 
424   map = search_map (map_ios, mapname);
425   if (!map)
426     return 0;
427 
428   for (prev = NULL, entry = PK_MAP_ENTRIES (map);
429        entry;
430        prev = entry, entry = PK_MAP_ENTRY_CHAIN (entry))
431     {
432       if (STREQ (PK_MAP_ENTRY_NAME (entry), entryname))
433         {
434           if (prev)
435             PK_MAP_ENTRY_CHAIN (prev) = PK_MAP_ENTRY_CHAIN (entry);
436           else
437             PK_MAP_ENTRIES (map) = PK_MAP_ENTRY_CHAIN (entry);
438 
439           free_entry (entry);
440           return 1;
441         }
442     }
443 
444   return 0;
445 }
446 
447 pk_map
pk_map_get_maps(int ios_id)448 pk_map_get_maps (int ios_id)
449 {
450   pk_map_ios map_ios;
451 
452   for (map_ios = poke_maps;
453        map_ios;
454        map_ios = PK_MAP_IOS_CHAIN (map_ios))
455     {
456       if (PK_MAP_IOS_ID (map_ios) == ios_id)
457         return PK_MAP_IOS_MAPS (map_ios);
458     }
459 
460   return NULL;
461 }
462 
463 static int
pk_map_load_parsed_map(int ios_id,const char * mapname,const char * filename,pk_map_parsed_map map)464 pk_map_load_parsed_map (int ios_id, const char *mapname,
465                         const char *filename,
466                         pk_map_parsed_map map)
467 {
468   pk_map_parsed_entry entry;
469 
470   /* First, compile the prologue.  */
471   /* XXX set error location and disable verbose error messages in
472      poke_compiler.  */
473   if (pk_compile_buffer (poke_compiler,
474                          PK_MAP_PARSED_MAP_PROLOGUE (map),
475                          NULL) != PK_OK)
476     return 0;
477 
478   /* Process the map entries and create the mapped global
479      variables.  */
480 
481   for (entry = PK_MAP_PARSED_MAP_ENTRIES (map);
482        entry;
483        entry = PK_MAP_PARSED_ENTRY_CHAIN (entry))
484     {
485       pk_val val;
486       int process_p = 1;
487       const char *condition = PK_MAP_PARSED_ENTRY_CONDITION (entry);
488 
489       /* Evaluate the condition.  */
490       if (condition)
491         {
492           /* XXX set error location... */
493           if (pk_compile_expression (poke_compiler,
494                                      condition,
495                                      NULL /* end */,
496                                      &val) != PK_OK)
497             goto error;
498 
499           if (pk_type_code (pk_typeof (val)) != PK_INT
500               && pk_type_code (pk_typeof (val)) != PK_UINT)
501             {
502               /* XXX error location.  */
503               pk_printf ("error: invalid condition expression\n");
504               goto error;
505             }
506 
507           if (!(pk_type_code (pk_typeof (val)) == PK_INT
508                 ? pk_int_value (val) : pk_uint_value (val)))
509             process_p = 0;
510         }
511 
512       PK_MAP_PARSED_ENTRY_SKIPPED_P (entry) = !process_p;
513       if (process_p)
514         {
515           const char *name = PK_MAP_PARSED_ENTRY_NAME (entry);
516           const char *type = PK_MAP_PARSED_ENTRY_TYPE (entry);
517           const char *offset = PK_MAP_PARSED_ENTRY_OFFSET (entry);
518 
519           PK_MAP_PARSED_ENTRY_VARNAME (entry)
520             = entry_name_to_varname (name);
521 
522           /* XXX set error location with compiler pragmas... */
523           char *defvar_str
524             = pk_str_concat ("var ",
525                              PK_MAP_PARSED_ENTRY_VARNAME (entry),
526                              " = ", type, " @ ", offset, ";", NULL);
527 
528           /* XXX what about constraints?  */
529           if (pk_compile_buffer (poke_compiler,
530                                  defvar_str,
531                                  NULL /* end */) != PK_OK)
532             goto error;
533         }
534     }
535 
536   /* Create the map.  */
537   if (!pk_map_create (ios_id, mapname, filename))
538     return 0;
539 
540   /* Add the map entries.  */
541   for (entry = PK_MAP_PARSED_MAP_ENTRIES (map);
542        entry;
543        entry = PK_MAP_PARSED_ENTRY_CHAIN (entry))
544     {
545       if (!PK_MAP_PARSED_ENTRY_SKIPPED_P (entry))
546         {
547           char *name = PK_MAP_PARSED_ENTRY_NAME (entry);
548           char *varname = PK_MAP_PARSED_ENTRY_VARNAME (entry);
549           pk_val offset;
550 
551           offset = pk_decl_val (poke_compiler, varname);
552           assert (offset != PK_NULL);
553           offset = pk_val_offset (offset);
554 
555           if (!pk_map_add_entry (ios_id, mapname, name,
556                                  varname, offset))
557             goto error;
558         }
559     }
560 
561   return 1;
562 
563  error:
564   return 0;
565 }
566 
567 char *
pk_map_normalize_name(const char * str)568 pk_map_normalize_name (const char *str)
569 {
570   char *mapname, *p;
571 
572   mapname = xstrdup (str);
573 
574   /* Strip the ".map" extension.  */
575   if (strlen (mapname) > 4
576       && STREQ (mapname + strlen (mapname) - 4, ".map"))
577     mapname[strlen (mapname) - 4] = '\0';
578 
579   /* Normalize the name, which basically consists on turning any
580      character not in [0-9a-zA-Z_] into _ */
581   for (p = mapname; *p != '\0'; ++p)
582     {
583       if (!((*p >= '0' && *p <= '9')
584             || (*p >= 'a' && *p <= 'z')
585             || (*p >= 'A' && *p <= 'Z')
586             || (*p == '_')))
587         *p = '_';
588     }
589 
590   return mapname;
591 }
592 
593 int
pk_map_load_file(int ios_id,const char * path,char ** errmsg)594 pk_map_load_file (int ios_id,
595                   const char *path, char **errmsg)
596 {
597   char *emsg, *mapname;
598   FILE *fp;
599   pk_map_parsed_map parsed_map;
600 
601   /* Do not attempt to load the mapfile if there is already a map with
602      the same name defined in the IO space.  */
603   mapname = pk_map_normalize_name (last_component (path));
604   if (pk_map_search (ios_id, mapname) != NULL)
605     {
606       *errmsg = "map already loaded";
607       return 0;
608     }
609 
610   /* Open the file whose contents are to be parsed.  */
611   if ((emsg = pk_file_readable (path)) != NULL)
612     {
613       *errmsg = emsg;
614       return 0;
615     }
616 
617   fp = fopen (path, "r");
618   if (!fp)
619     {
620       *errmsg = strerror (errno);
621       free (mapname);
622       return 0;
623     }
624 
625   /* Parse the file contents and close the file.  */
626   parsed_map = pk_map_parse_file (path, fp);
627   if (!parsed_map)
628     {
629       if (errmsg)
630         *errmsg = "";
631       return 0;
632     }
633 
634   if (fclose (fp) == EOF)
635     {
636       *errmsg = strerror (errno);
637       free (mapname);
638       return 0;
639     }
640 
641   /* XXX */
642   //  pk_map_print_parsed_map (parsed_map);
643 
644   /* Process the result.  */
645   if (!pk_map_load_parsed_map (ios_id,
646                                mapname,
647                                path,
648                                parsed_map))
649     {
650       free (mapname);
651       return 0;
652     }
653 
654   free (mapname);
655   return 1;
656 }
657 
658 char *
pk_map_resolve_map(const char * mapname,int filename_p)659 pk_map_resolve_map (const char *mapname, int filename_p)
660 {
661   pk_val val;
662   const char *map_load_path;
663   char *full_filename = NULL;
664 
665   val = pk_decl_val (poke_compiler, "map_load_path");
666   if (val == PK_NULL)
667     pk_fatal ("couldn't get `map_load_path'");
668 
669   if (pk_type_code (pk_typeof (val)) != PK_STRING)
670     pk_fatal ("map_load_path should be a string");
671 
672   map_load_path = pk_string_str (val);
673 
674   /* Traverse the directories in the load path and try to load the
675      requested map.  */
676   {
677     const char *ext = filename_p ? "" : ".map";
678     const char *s, *e;
679 
680     char *fixed_load_path
681       = pk_str_replace (map_load_path, "%DATADIR%", PKGDATADIR);
682 
683     for (s = fixed_load_path, e = s; *e; s = e + 1)
684       {
685         /* Ignore empty entries. */
686         if ((e = strchrnul (s, ':')) == s)
687           continue;
688 
689         asprintf (&full_filename, "%.*s/%s%s", (int) (e - s), s, mapname, ext);
690 
691         if (pk_file_readable (full_filename) == NULL)
692           break;
693 
694         free (full_filename);
695         full_filename = NULL;
696       }
697 
698     if (fixed_load_path != map_load_path)
699       free (fixed_load_path);
700   }
701 
702   return full_filename;
703 }
704