1/* The IGEN simulator generator for GDB, the GNU Debugger.
2
3   Copyright 2002-2023 Free Software Foundation, Inc.
4
5   Contributed by Andrew Cagney.
6
7   This file is part of GDB.
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 <http://www.gnu.org/licenses/>.  */
21
22
23
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <stdio.h>
27#include <fcntl.h>
28#include <ctype.h>
29
30#include "misc.h"
31#include "lf.h"
32#include "table.h"
33
34#include <unistd.h>
35#include <stdlib.h>
36
37typedef struct _open_table open_table;
38struct _open_table
39{
40  size_t size;
41  char *buffer;
42  char *pos;
43  line_ref pseudo_line;
44  line_ref real_line;
45  open_table *parent;
46  table *root;
47};
48struct _table
49{
50  open_table *current;
51};
52
53
54static line_ref *
55current_line (open_table * file)
56{
57  line_ref *entry = ZALLOC (line_ref);
58  *entry = file->pseudo_line;
59  return entry;
60}
61
62static table_entry *
63new_table_entry (open_table * file, table_entry_type type)
64{
65  table_entry *entry;
66  entry = ZALLOC (table_entry);
67  entry->file = file->root;
68  entry->line = current_line (file);
69  entry->type = type;
70  return entry;
71}
72
73static void
74set_nr_table_entry_fields (table_entry *entry, int nr_fields)
75{
76  entry->field = NZALLOC (char *, nr_fields + 1);
77  entry->nr_fields = nr_fields;
78}
79
80
81void
82table_push (table *root,
83	    const line_ref *line,
84	    table_include *includes,
85	    const char *file_name)
86{
87  FILE *ff;
88  open_table *file;
89  table_include dummy;
90  table_include *include = &dummy;
91
92  /* dummy up a search of this directory */
93  dummy.next = includes;
94  dummy.dir = "";
95
96  /* create a file descriptor */
97  file = ZALLOC (open_table);
98  if (file == NULL)
99    {
100      perror (file_name);
101      exit (1);
102    }
103  file->root = root;
104  file->parent = root->current;
105  root->current = file;
106
107  while (1)
108    {
109      /* save the file name */
110      char *dup_name =
111	NZALLOC (char, strlen (include->dir) + strlen (file_name) + 2);
112      if (dup_name == NULL)
113	{
114	  perror (file_name);
115	  exit (1);
116	}
117      if (include->dir[0] != '\0')
118	{
119	  strcat (dup_name, include->dir);
120	  strcat (dup_name, "/");
121	}
122      strcat (dup_name, file_name);
123      file->real_line.file_name = dup_name;
124      file->pseudo_line.file_name = dup_name;
125      /* open the file */
126
127      ff = fopen (dup_name, "rb");
128      if (ff)
129	break;
130      /* free (dup_name); */
131      if (include->next == NULL)
132	{
133	  if (line != NULL)
134	    error (line, "Problem opening file `%s'\n", file_name);
135	  perror (file_name);
136	  exit (1);
137	}
138      include = include->next;
139    }
140
141
142  /* determine the size */
143  fseek (ff, 0, SEEK_END);
144  file->size = ftell (ff);
145  fseek (ff, 0, SEEK_SET);
146
147  /* allocate this much memory */
148  file->buffer = (char *) zalloc (file->size + 1);
149  if (file->buffer == NULL)
150    {
151      perror (file_name);
152      exit (1);
153    }
154  file->pos = file->buffer;
155
156  /* read it all in */
157  if (fread (file->buffer, 1, file->size, ff) < file->size)
158    {
159      perror (file_name);
160      exit (1);
161    }
162  file->buffer[file->size] = '\0';
163
164  /* set the initial line numbering */
165  file->real_line.line_nr = 1;	/* specifies current line */
166  file->pseudo_line.line_nr = 1;	/* specifies current line */
167
168  /* done */
169  fclose (ff);
170}
171
172table *
173table_open (const char *file_name)
174{
175  table *root;
176
177  /* create a file descriptor */
178  root = ZALLOC (table);
179  if (root == NULL)
180    {
181      perror (file_name);
182      exit (1);
183    }
184
185  table_push (root, NULL, NULL, file_name);
186  return root;
187}
188
189char *
190skip_spaces (char *chp)
191{
192  while (1)
193    {
194      if (*chp == '\0' || *chp == '\n' || !isspace (*chp))
195	return chp;
196      chp++;
197    }
198}
199
200
201char *
202back_spaces (char *start, char *chp)
203{
204  while (1)
205    {
206      if (chp <= start || !isspace (chp[-1]))
207	return chp;
208      chp--;
209    }
210}
211
212char *
213skip_digits (char *chp)
214{
215  while (1)
216    {
217      if (*chp == '\0' || *chp == '\n' || !isdigit (*chp))
218	return chp;
219      chp++;
220    }
221}
222
223char *
224skip_to_separator (char *chp, char *separators)
225{
226  while (1)
227    {
228      char *sep = separators;
229      while (1)
230	{
231	  if (*chp == *sep)
232	    return chp;
233	  if (*sep == '\0')
234	    break;
235	  sep++;
236	}
237      chp++;
238    }
239}
240
241static char *
242skip_to_null (char *chp)
243{
244  return skip_to_separator (chp, "");
245}
246
247
248static char *
249skip_to_nl (char *chp)
250{
251  return skip_to_separator (chp, "\n");
252}
253
254
255static void
256next_line (open_table * file)
257{
258  file->pos = skip_to_nl (file->pos);
259  if (*file->pos == '0')
260    error (&file->pseudo_line, "Missing <nl> at end of line\n");
261  *file->pos = '\0';
262  file->pos += 1;
263  file->real_line.line_nr += 1;
264  file->pseudo_line.line_nr += 1;
265}
266
267
268extern table_entry *
269table_read (table *root)
270{
271  open_table *file = root->current;
272  table_entry *entry = NULL;
273  while (1)
274    {
275
276      /* end-of-file? */
277      while (*file->pos == '\0')
278	{
279	  if (file->parent != NULL)
280	    {
281	      file = file->parent;
282	      root->current = file;
283	    }
284	  else
285	    return NULL;
286	}
287
288      /* code_block? */
289      if (*file->pos == '{')
290	{
291	  char *chp;
292	  next_line (file);	/* discard leading brace */
293	  entry = new_table_entry (file, table_code_entry);
294	  chp = file->pos;
295	  /* determine how many lines are involved - look for <nl> "}" */
296	  {
297	    int nr_lines = 0;
298	    while (*file->pos != '}')
299	      {
300		next_line (file);
301		nr_lines++;
302	      }
303	    set_nr_table_entry_fields (entry, nr_lines);
304	  }
305	  /* now enter each line */
306	  {
307	    int line_nr;
308	    for (line_nr = 0; line_nr < entry->nr_fields; line_nr++)
309	      {
310		if (strncmp (chp, "  ", 2) == 0)
311		  entry->field[line_nr] = chp + 2;
312		else
313		  entry->field[line_nr] = chp;
314		chp = skip_to_null (chp) + 1;
315	      }
316	    /* skip trailing brace */
317	    ASSERT (*file->pos == '}');
318	    next_line (file);
319	  }
320	  break;
321	}
322
323      /* tab block? */
324      if (*file->pos == '\t')
325	{
326	  char *chp = file->pos;
327	  entry = new_table_entry (file, table_code_entry);
328	  /* determine how many lines are involved - look for <nl> !<tab> */
329	  {
330	    int nr_lines = 0;
331	    int nr_blank_lines = 0;
332	    while (1)
333	      {
334		if (*file->pos == '\t')
335		  {
336		    nr_lines = nr_lines + nr_blank_lines + 1;
337		    nr_blank_lines = 0;
338		    next_line (file);
339		  }
340		else
341		  {
342		    file->pos = skip_spaces (file->pos);
343		    if (*file->pos != '\n')
344		      break;
345		    nr_blank_lines++;
346		    next_line (file);
347		  }
348	      }
349	    set_nr_table_entry_fields (entry, nr_lines);
350	  }
351	  /* now enter each line */
352	  {
353	    int line_nr;
354	    for (line_nr = 0; line_nr < entry->nr_fields; line_nr++)
355	      {
356		if (*chp == '\t')
357		  entry->field[line_nr] = chp + 1;
358		else
359		  entry->field[line_nr] = "";	/* blank */
360		chp = skip_to_null (chp) + 1;
361	      }
362	  }
363	  break;
364	}
365
366      /* cpp directive? */
367      if (file->pos[0] == '#')
368	{
369	  char *chp = skip_spaces (file->pos + 1);
370
371	  /* cpp line-nr directive - # <line-nr> "<file>" */
372	  if (isdigit (*chp)
373	      && *skip_digits (chp) == ' '
374	      && *skip_spaces (skip_digits (chp)) == '"')
375	    {
376	      int line_nr;
377	      char *file_name;
378	      file->pos = chp;
379	      /* parse the number */
380	      line_nr = atoi (file->pos) - 1;
381	      /* skip to the file name */
382	      while (file->pos[0] != '0'
383		     && file->pos[0] != '"' && file->pos[0] != '\0')
384		file->pos++;
385	      if (file->pos[0] != '"')
386		error (&file->real_line,
387		       "Missing opening quote in cpp directive\n");
388	      /* parse the file name */
389	      file->pos++;
390	      file_name = file->pos;
391	      while (file->pos[0] != '"' && file->pos[0] != '\0')
392		file->pos++;
393	      if (file->pos[0] != '"')
394		error (&file->real_line,
395		       "Missing closing quote in cpp directive\n");
396	      file->pos[0] = '\0';
397	      file->pos++;
398	      file->pos = skip_to_nl (file->pos);
399	      if (file->pos[0] != '\n')
400		error (&file->real_line,
401		       "Missing newline in cpp directive\n");
402	      file->pseudo_line.file_name = file_name;
403	      file->pseudo_line.line_nr = line_nr;
404	      next_line (file);
405	      continue;
406	    }
407
408	  /* #define and #undef - not implemented yet */
409
410	  /* Old style # comment */
411	  next_line (file);
412	  continue;
413	}
414
415      /* blank line or end-of-file? */
416      file->pos = skip_spaces (file->pos);
417      if (*file->pos == '\0')
418	error (&file->pseudo_line, "Missing <nl> at end of file\n");
419      if (*file->pos == '\n')
420	{
421	  next_line (file);
422	  continue;
423	}
424
425      /* comment - leading // or # - skip */
426      if ((file->pos[0] == '/' && file->pos[1] == '/')
427	  || (file->pos[0] == '#'))
428	{
429	  next_line (file);
430	  continue;
431	}
432
433      /* colon field */
434      {
435	char *chp = file->pos;
436	entry = new_table_entry (file, table_colon_entry);
437	next_line (file);
438	/* figure out how many fields */
439	{
440	  int nr_fields = 1;
441	  char *tmpch = chp;
442	  while (1)
443	    {
444	      tmpch = skip_to_separator (tmpch, "\\:");
445	      if (*tmpch == '\\')
446		{
447		  /* eat the escaped character */
448		  char *cp = tmpch;
449		  while (cp[1] != '\0')
450		    {
451		      cp[0] = cp[1];
452		      cp++;
453		    }
454		  cp[0] = '\0';
455		  tmpch++;
456		}
457	      else if (*tmpch != ':')
458		break;
459	      else
460		{
461		  *tmpch = '\0';
462		  tmpch++;
463		  nr_fields++;
464		}
465	    }
466	  set_nr_table_entry_fields (entry, nr_fields);
467	}
468	/* now parse them */
469	{
470	  int field_nr;
471	  for (field_nr = 0; field_nr < entry->nr_fields; field_nr++)
472	    {
473	      chp = skip_spaces (chp);
474	      entry->field[field_nr] = chp;
475	      chp = skip_to_null (chp);
476	      *back_spaces (entry->field[field_nr], chp) = '\0';
477	      chp++;
478	    }
479	}
480	break;
481      }
482
483    }
484
485  ASSERT (entry == NULL || entry->field[entry->nr_fields] == NULL);
486  return entry;
487}
488
489extern void
490table_print_code (lf *file, const table_entry *entry)
491{
492  int field_nr;
493  int nr = 0;
494  for (field_nr = 0; field_nr < entry->nr_fields; field_nr++)
495    {
496      char *chp = entry->field[field_nr];
497      int in_bit_field = 0;
498      if (*chp == '#')
499	lf_indent_suppress (file);
500      while (*chp != '\0')
501	{
502	  if (chp[0] == '{' && !isspace (chp[1]) && chp[1] != '\0')
503	    {
504	      in_bit_field = 1;
505	      nr += lf_putchr (file, '_');
506	    }
507	  else if (in_bit_field && chp[0] == ':')
508	    {
509	      nr += lf_putchr (file, '_');
510	    }
511	  else if (in_bit_field && *chp == '}')
512	    {
513	      nr += lf_putchr (file, '_');
514	      in_bit_field = 0;
515	    }
516	  else
517	    {
518	      nr += lf_putchr (file, *chp);
519	    }
520	  chp++;
521	}
522      if (in_bit_field)
523	{
524	  line_ref line = *entry->line;
525	  line.line_nr += field_nr;
526	  error (&line, "Bit field brace miss match\n");
527	}
528      nr += lf_putchr (file, '\n');
529    }
530}
531
532
533void
534dump_line_ref (lf *file,
535	       const char *prefix,
536	       const line_ref *line,
537	       const char *suffix)
538{
539  lf_printf (file, "%s(line_ref*) %p", prefix, line);
540  if (line != NULL)
541    {
542      lf_indent (file, +1);
543      lf_printf (file, "\n(line_nr %d)", line->line_nr);
544      lf_printf (file, "\n(file_name %s)", line->file_name);
545      lf_indent (file, -1);
546    }
547  lf_printf (file, "%s", suffix);
548}
549
550
551static const char *
552table_entry_type_to_str (table_entry_type type)
553{
554  switch (type)
555    {
556    case table_code_entry:
557      return "code-entry";
558    case table_colon_entry:
559      return "colon-entry";
560    }
561  return "*invalid*";
562}
563
564void
565dump_table_entry (lf *file,
566		  const char *prefix,
567		  const table_entry *entry,
568		  const char *suffix)
569{
570  lf_printf (file, "%s(table_entry*) %p", prefix, entry);
571  if (entry != NULL)
572    {
573      int field;
574      lf_indent (file, +1);
575      dump_line_ref (file, "\n(line ", entry->line, ")");
576      lf_printf (file, "\n(type %s)", table_entry_type_to_str (entry->type));
577      lf_printf (file, "\n(nr_fields %d)", entry->nr_fields);
578      lf_printf (file, "\n(fields");
579      lf_indent (file, +1);
580      for (field = 0; field < entry->nr_fields; field++)
581	lf_printf (file, "\n\"%s\"", entry->field[field]);
582      lf_indent (file, -1);
583      lf_printf (file, ")");
584      lf_indent (file, -1);
585    }
586  lf_printf (file, "%s", suffix);
587}
588
589
590#ifdef MAIN
591int
592main (int argc, char **argv)
593{
594  table *t;
595  table_entry *entry;
596  lf *l;
597  int line_nr;
598
599  if (argc != 2)
600    {
601      printf ("Usage: table <file>\n");
602      exit (1);
603    }
604
605  t = table_open (argv[1]);
606  l = lf_open ("-", "stdout", lf_omit_references, lf_is_text, "tmp-table");
607
608  line_nr = 0;
609  do
610    {
611      char line[10];
612      entry = table_read (t);
613      line_nr++;
614      sprintf (line, "(%d ", line_nr);
615      dump_table_entry (l, line, entry, ")\n");
616    }
617  while (entry != NULL);
618
619  return 0;
620}
621#endif
622