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