| /* The IGEN simulator generator for GDB, the GNU Debugger. | 
 |  | 
 |    Copyright 2002-2016 Free Software Foundation, Inc. | 
 |  | 
 |    Contributed by Andrew Cagney. | 
 |  | 
 |    This file is part of GDB. | 
 |  | 
 |    This program is free software; you can redistribute it and/or modify | 
 |    it under the terms of the GNU General Public License as published by | 
 |    the Free Software Foundation; either version 3 of the License, or | 
 |    (at your option) any later version. | 
 |  | 
 |    This program is distributed in the hope that it will be useful, | 
 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |    GNU General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU General Public License | 
 |    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 |  | 
 |  | 
 | #include <sys/types.h> | 
 | #include <sys/stat.h> | 
 | #include <stdio.h> | 
 | #include <fcntl.h> | 
 | #include <ctype.h> | 
 |  | 
 | #include "config.h" | 
 | #include "misc.h" | 
 | #include "lf.h" | 
 | #include "table.h" | 
 |  | 
 | #ifdef HAVE_UNISTD_H | 
 | #include <unistd.h> | 
 | #endif | 
 |  | 
 | #ifdef HAVE_STDLIB_H | 
 | #include <stdlib.h> | 
 | #endif | 
 |  | 
 | typedef struct _open_table open_table; | 
 | struct _open_table | 
 | { | 
 |   size_t size; | 
 |   char *buffer; | 
 |   char *pos; | 
 |   line_ref pseudo_line; | 
 |   line_ref real_line; | 
 |   open_table *parent; | 
 |   table *root; | 
 | }; | 
 | struct _table | 
 | { | 
 |   open_table *current; | 
 | }; | 
 |  | 
 |  | 
 | static line_ref * | 
 | current_line (open_table * file) | 
 | { | 
 |   line_ref *entry = ZALLOC (line_ref); | 
 |   *entry = file->pseudo_line; | 
 |   return entry; | 
 | } | 
 |  | 
 | static table_entry * | 
 | new_table_entry (open_table * file, table_entry_type type) | 
 | { | 
 |   table_entry *entry; | 
 |   entry = ZALLOC (table_entry); | 
 |   entry->file = file->root; | 
 |   entry->line = current_line (file); | 
 |   entry->type = type; | 
 |   return entry; | 
 | } | 
 |  | 
 | static void | 
 | set_nr_table_entry_fields (table_entry *entry, int nr_fields) | 
 | { | 
 |   entry->field = NZALLOC (char *, nr_fields + 1); | 
 |   entry->nr_fields = nr_fields; | 
 | } | 
 |  | 
 |  | 
 | void | 
 | table_push (table *root, | 
 | 	    line_ref *line, table_include *includes, const char *file_name) | 
 | { | 
 |   FILE *ff; | 
 |   open_table *file; | 
 |   table_include dummy; | 
 |   table_include *include = &dummy; | 
 |  | 
 |   /* dummy up a search of this directory */ | 
 |   dummy.next = includes; | 
 |   dummy.dir = ""; | 
 |  | 
 |   /* create a file descriptor */ | 
 |   file = ZALLOC (open_table); | 
 |   if (file == NULL) | 
 |     { | 
 |       perror (file_name); | 
 |       exit (1); | 
 |     } | 
 |   file->root = root; | 
 |   file->parent = root->current; | 
 |   root->current = file; | 
 |  | 
 |   while (1) | 
 |     { | 
 |       /* save the file name */ | 
 |       char *dup_name = | 
 | 	NZALLOC (char, strlen (include->dir) + strlen (file_name) + 2); | 
 |       if (dup_name == NULL) | 
 | 	{ | 
 | 	  perror (file_name); | 
 | 	  exit (1); | 
 | 	} | 
 |       if (include->dir[0] != '\0') | 
 | 	{ | 
 | 	  strcat (dup_name, include->dir); | 
 | 	  strcat (dup_name, "/"); | 
 | 	} | 
 |       strcat (dup_name, file_name); | 
 |       file->real_line.file_name = dup_name; | 
 |       file->pseudo_line.file_name = dup_name; | 
 |       /* open the file */ | 
 |  | 
 |       ff = fopen (dup_name, "rb"); | 
 |       if (ff) | 
 | 	break; | 
 |       /* free (dup_name); */ | 
 |       if (include->next == NULL) | 
 | 	{ | 
 | 	  if (line != NULL) | 
 | 	    error (line, "Problem opening file `%s'\n", file_name); | 
 | 	  perror (file_name); | 
 | 	  exit (1); | 
 | 	} | 
 |       include = include->next; | 
 |     } | 
 |  | 
 |  | 
 |   /* determine the size */ | 
 |   fseek (ff, 0, SEEK_END); | 
 |   file->size = ftell (ff); | 
 |   fseek (ff, 0, SEEK_SET); | 
 |  | 
 |   /* allocate this much memory */ | 
 |   file->buffer = (char *) zalloc (file->size + 1); | 
 |   if (file->buffer == NULL) | 
 |     { | 
 |       perror (file_name); | 
 |       exit (1); | 
 |     } | 
 |   file->pos = file->buffer; | 
 |  | 
 |   /* read it all in */ | 
 |   if (fread (file->buffer, 1, file->size, ff) < file->size) | 
 |     { | 
 |       perror (file_name); | 
 |       exit (1); | 
 |     } | 
 |   file->buffer[file->size] = '\0'; | 
 |  | 
 |   /* set the initial line numbering */ | 
 |   file->real_line.line_nr = 1;	/* specifies current line */ | 
 |   file->pseudo_line.line_nr = 1;	/* specifies current line */ | 
 |  | 
 |   /* done */ | 
 |   fclose (ff); | 
 | } | 
 |  | 
 | table * | 
 | table_open (const char *file_name) | 
 | { | 
 |   table *root; | 
 |  | 
 |   /* create a file descriptor */ | 
 |   root = ZALLOC (table); | 
 |   if (root == NULL) | 
 |     { | 
 |       perror (file_name); | 
 |       exit (1); | 
 |     } | 
 |  | 
 |   table_push (root, NULL, NULL, file_name); | 
 |   return root; | 
 | } | 
 |  | 
 | char * | 
 | skip_spaces (char *chp) | 
 | { | 
 |   while (1) | 
 |     { | 
 |       if (*chp == '\0' || *chp == '\n' || !isspace (*chp)) | 
 | 	return chp; | 
 |       chp++; | 
 |     } | 
 | } | 
 |  | 
 |  | 
 | char * | 
 | back_spaces (char *start, char *chp) | 
 | { | 
 |   while (1) | 
 |     { | 
 |       if (chp <= start || !isspace (chp[-1])) | 
 | 	return chp; | 
 |       chp--; | 
 |     } | 
 | } | 
 |  | 
 | char * | 
 | skip_digits (char *chp) | 
 | { | 
 |   while (1) | 
 |     { | 
 |       if (*chp == '\0' || *chp == '\n' || !isdigit (*chp)) | 
 | 	return chp; | 
 |       chp++; | 
 |     } | 
 | } | 
 |  | 
 | char * | 
 | skip_to_separator (char *chp, char *separators) | 
 | { | 
 |   while (1) | 
 |     { | 
 |       char *sep = separators; | 
 |       while (1) | 
 | 	{ | 
 | 	  if (*chp == *sep) | 
 | 	    return chp; | 
 | 	  if (*sep == '\0') | 
 | 	    break; | 
 | 	  sep++; | 
 | 	} | 
 |       chp++; | 
 |     } | 
 | } | 
 |  | 
 | static char * | 
 | skip_to_null (char *chp) | 
 | { | 
 |   return skip_to_separator (chp, ""); | 
 | } | 
 |  | 
 |  | 
 | static char * | 
 | skip_to_nl (char *chp) | 
 | { | 
 |   return skip_to_separator (chp, "\n"); | 
 | } | 
 |  | 
 |  | 
 | static void | 
 | next_line (open_table * file) | 
 | { | 
 |   file->pos = skip_to_nl (file->pos); | 
 |   if (*file->pos == '0') | 
 |     error (&file->pseudo_line, "Missing <nl> at end of line\n"); | 
 |   *file->pos = '\0'; | 
 |   file->pos += 1; | 
 |   file->real_line.line_nr += 1; | 
 |   file->pseudo_line.line_nr += 1; | 
 | } | 
 |  | 
 |  | 
 | extern table_entry * | 
 | table_read (table *root) | 
 | { | 
 |   open_table *file = root->current; | 
 |   table_entry *entry = NULL; | 
 |   while (1) | 
 |     { | 
 |  | 
 |       /* end-of-file? */ | 
 |       while (*file->pos == '\0') | 
 | 	{ | 
 | 	  if (file->parent != NULL) | 
 | 	    { | 
 | 	      file = file->parent; | 
 | 	      root->current = file; | 
 | 	    } | 
 | 	  else | 
 | 	    return NULL; | 
 | 	} | 
 |  | 
 |       /* code_block? */ | 
 |       if (*file->pos == '{') | 
 | 	{ | 
 | 	  char *chp; | 
 | 	  next_line (file);	/* discard leading brace */ | 
 | 	  entry = new_table_entry (file, table_code_entry); | 
 | 	  chp = file->pos; | 
 | 	  /* determine how many lines are involved - look for <nl> "}" */ | 
 | 	  { | 
 | 	    int nr_lines = 0; | 
 | 	    while (*file->pos != '}') | 
 | 	      { | 
 | 		next_line (file); | 
 | 		nr_lines++; | 
 | 	      } | 
 | 	    set_nr_table_entry_fields (entry, nr_lines); | 
 | 	  } | 
 | 	  /* now enter each line */ | 
 | 	  { | 
 | 	    int line_nr; | 
 | 	    for (line_nr = 0; line_nr < entry->nr_fields; line_nr++) | 
 | 	      { | 
 | 		if (strncmp (chp, "  ", 2) == 0) | 
 | 		  entry->field[line_nr] = chp + 2; | 
 | 		else | 
 | 		  entry->field[line_nr] = chp; | 
 | 		chp = skip_to_null (chp) + 1; | 
 | 	      } | 
 | 	    /* skip trailing brace */ | 
 | 	    ASSERT (*file->pos == '}'); | 
 | 	    next_line (file); | 
 | 	  } | 
 | 	  break; | 
 | 	} | 
 |  | 
 |       /* tab block? */ | 
 |       if (*file->pos == '\t') | 
 | 	{ | 
 | 	  char *chp = file->pos; | 
 | 	  entry = new_table_entry (file, table_code_entry); | 
 | 	  /* determine how many lines are involved - look for <nl> !<tab> */ | 
 | 	  { | 
 | 	    int nr_lines = 0; | 
 | 	    int nr_blank_lines = 0; | 
 | 	    while (1) | 
 | 	      { | 
 | 		if (*file->pos == '\t') | 
 | 		  { | 
 | 		    nr_lines = nr_lines + nr_blank_lines + 1; | 
 | 		    nr_blank_lines = 0; | 
 | 		    next_line (file); | 
 | 		  } | 
 | 		else | 
 | 		  { | 
 | 		    file->pos = skip_spaces (file->pos); | 
 | 		    if (*file->pos != '\n') | 
 | 		      break; | 
 | 		    nr_blank_lines++; | 
 | 		    next_line (file); | 
 | 		  } | 
 | 	      } | 
 | 	    set_nr_table_entry_fields (entry, nr_lines); | 
 | 	  } | 
 | 	  /* now enter each line */ | 
 | 	  { | 
 | 	    int line_nr; | 
 | 	    for (line_nr = 0; line_nr < entry->nr_fields; line_nr++) | 
 | 	      { | 
 | 		if (*chp == '\t') | 
 | 		  entry->field[line_nr] = chp + 1; | 
 | 		else | 
 | 		  entry->field[line_nr] = "";	/* blank */ | 
 | 		chp = skip_to_null (chp) + 1; | 
 | 	      } | 
 | 	  } | 
 | 	  break; | 
 | 	} | 
 |  | 
 |       /* cpp directive? */ | 
 |       if (file->pos[0] == '#') | 
 | 	{ | 
 | 	  char *chp = skip_spaces (file->pos + 1); | 
 |  | 
 | 	  /* cpp line-nr directive - # <line-nr> "<file>" */ | 
 | 	  if (isdigit (*chp) | 
 | 	      && *skip_digits (chp) == ' ' | 
 | 	      && *skip_spaces (skip_digits (chp)) == '"') | 
 | 	    { | 
 | 	      int line_nr; | 
 | 	      char *file_name; | 
 | 	      file->pos = chp; | 
 | 	      /* parse the number */ | 
 | 	      line_nr = atoi (file->pos) - 1; | 
 | 	      /* skip to the file name */ | 
 | 	      while (file->pos[0] != '0' | 
 | 		     && file->pos[0] != '"' && file->pos[0] != '\0') | 
 | 		file->pos++; | 
 | 	      if (file->pos[0] != '"') | 
 | 		error (&file->real_line, | 
 | 		       "Missing opening quote in cpp directive\n"); | 
 | 	      /* parse the file name */ | 
 | 	      file->pos++; | 
 | 	      file_name = file->pos; | 
 | 	      while (file->pos[0] != '"' && file->pos[0] != '\0') | 
 | 		file->pos++; | 
 | 	      if (file->pos[0] != '"') | 
 | 		error (&file->real_line, | 
 | 		       "Missing closing quote in cpp directive\n"); | 
 | 	      file->pos[0] = '\0'; | 
 | 	      file->pos++; | 
 | 	      file->pos = skip_to_nl (file->pos); | 
 | 	      if (file->pos[0] != '\n') | 
 | 		error (&file->real_line, | 
 | 		       "Missing newline in cpp directive\n"); | 
 | 	      file->pseudo_line.file_name = file_name; | 
 | 	      file->pseudo_line.line_nr = line_nr; | 
 | 	      next_line (file); | 
 | 	      continue; | 
 | 	    } | 
 |  | 
 | 	  /* #define and #undef - not implemented yet */ | 
 |  | 
 | 	  /* Old style # comment */ | 
 | 	  next_line (file); | 
 | 	  continue; | 
 | 	} | 
 |  | 
 |       /* blank line or end-of-file? */ | 
 |       file->pos = skip_spaces (file->pos); | 
 |       if (*file->pos == '\0') | 
 | 	error (&file->pseudo_line, "Missing <nl> at end of file\n"); | 
 |       if (*file->pos == '\n') | 
 | 	{ | 
 | 	  next_line (file); | 
 | 	  continue; | 
 | 	} | 
 |  | 
 |       /* comment - leading // or # - skip */ | 
 |       if ((file->pos[0] == '/' && file->pos[1] == '/') | 
 | 	  || (file->pos[0] == '#')) | 
 | 	{ | 
 | 	  next_line (file); | 
 | 	  continue; | 
 | 	} | 
 |  | 
 |       /* colon field */ | 
 |       { | 
 | 	char *chp = file->pos; | 
 | 	entry = new_table_entry (file, table_colon_entry); | 
 | 	next_line (file); | 
 | 	/* figure out how many fields */ | 
 | 	{ | 
 | 	  int nr_fields = 1; | 
 | 	  char *tmpch = chp; | 
 | 	  while (1) | 
 | 	    { | 
 | 	      tmpch = skip_to_separator (tmpch, "\\:"); | 
 | 	      if (*tmpch == '\\') | 
 | 		{ | 
 | 		  /* eat the escaped character */ | 
 | 		  char *cp = tmpch; | 
 | 		  while (cp[1] != '\0') | 
 | 		    { | 
 | 		      cp[0] = cp[1]; | 
 | 		      cp++; | 
 | 		    } | 
 | 		  cp[0] = '\0'; | 
 | 		  tmpch++; | 
 | 		} | 
 | 	      else if (*tmpch != ':') | 
 | 		break; | 
 | 	      else | 
 | 		{ | 
 | 		  *tmpch = '\0'; | 
 | 		  tmpch++; | 
 | 		  nr_fields++; | 
 | 		} | 
 | 	    } | 
 | 	  set_nr_table_entry_fields (entry, nr_fields); | 
 | 	} | 
 | 	/* now parse them */ | 
 | 	{ | 
 | 	  int field_nr; | 
 | 	  for (field_nr = 0; field_nr < entry->nr_fields; field_nr++) | 
 | 	    { | 
 | 	      chp = skip_spaces (chp); | 
 | 	      entry->field[field_nr] = chp; | 
 | 	      chp = skip_to_null (chp); | 
 | 	      *back_spaces (entry->field[field_nr], chp) = '\0'; | 
 | 	      chp++; | 
 | 	    } | 
 | 	} | 
 | 	break; | 
 |       } | 
 |  | 
 |     } | 
 |  | 
 |   ASSERT (entry == NULL || entry->field[entry->nr_fields] == NULL); | 
 |   return entry; | 
 | } | 
 |  | 
 | extern void | 
 | table_print_code (lf *file, table_entry *entry) | 
 | { | 
 |   int field_nr; | 
 |   int nr = 0; | 
 |   for (field_nr = 0; field_nr < entry->nr_fields; field_nr++) | 
 |     { | 
 |       char *chp = entry->field[field_nr]; | 
 |       int in_bit_field = 0; | 
 |       if (*chp == '#') | 
 | 	lf_indent_suppress (file); | 
 |       while (*chp != '\0') | 
 | 	{ | 
 | 	  if (chp[0] == '{' && !isspace (chp[1]) && chp[1] != '\0') | 
 | 	    { | 
 | 	      in_bit_field = 1; | 
 | 	      nr += lf_putchr (file, '_'); | 
 | 	    } | 
 | 	  else if (in_bit_field && chp[0] == ':') | 
 | 	    { | 
 | 	      nr += lf_putchr (file, '_'); | 
 | 	    } | 
 | 	  else if (in_bit_field && *chp == '}') | 
 | 	    { | 
 | 	      nr += lf_putchr (file, '_'); | 
 | 	      in_bit_field = 0; | 
 | 	    } | 
 | 	  else | 
 | 	    { | 
 | 	      nr += lf_putchr (file, *chp); | 
 | 	    } | 
 | 	  chp++; | 
 | 	} | 
 |       if (in_bit_field) | 
 | 	{ | 
 | 	  line_ref line = *entry->line; | 
 | 	  line.line_nr += field_nr; | 
 | 	  error (&line, "Bit field brace miss match\n"); | 
 | 	} | 
 |       nr += lf_putchr (file, '\n'); | 
 |     } | 
 | } | 
 |  | 
 |  | 
 |  | 
 | void | 
 | dump_line_ref (lf *file, char *prefix, const line_ref *line, char *suffix) | 
 | { | 
 |   lf_printf (file, "%s(line_ref*) 0x%lx", prefix, (long) line); | 
 |   if (line != NULL) | 
 |     { | 
 |       lf_indent (file, +1); | 
 |       lf_printf (file, "\n(line_nr %d)", line->line_nr); | 
 |       lf_printf (file, "\n(file_name %s)", line->file_name); | 
 |       lf_indent (file, -1); | 
 |     } | 
 |   lf_printf (file, "%s", suffix); | 
 | } | 
 |  | 
 |  | 
 | static const char * | 
 | table_entry_type_to_str (table_entry_type type) | 
 | { | 
 |   switch (type) | 
 |     { | 
 |     case table_code_entry: | 
 |       return "code-entry"; | 
 |     case table_colon_entry: | 
 |       return "colon-entry"; | 
 |     } | 
 |   return "*invalid*"; | 
 | } | 
 |  | 
 | void | 
 | dump_table_entry (lf *file, | 
 | 		  char *prefix, const table_entry *entry, char *suffix) | 
 | { | 
 |   lf_printf (file, "%s(table_entry*) 0x%lx", prefix, (long) entry); | 
 |   if (entry != NULL) | 
 |     { | 
 |       int field; | 
 |       lf_indent (file, +1); | 
 |       dump_line_ref (file, "\n(line ", entry->line, ")"); | 
 |       lf_printf (file, "\n(type %s)", table_entry_type_to_str (entry->type)); | 
 |       lf_printf (file, "\n(nr_fields %d)", entry->nr_fields); | 
 |       lf_printf (file, "\n(fields"); | 
 |       lf_indent (file, +1); | 
 |       for (field = 0; field < entry->nr_fields; field++) | 
 | 	lf_printf (file, "\n\"%s\"", entry->field[field]); | 
 |       lf_indent (file, -1); | 
 |       lf_printf (file, ")"); | 
 |       lf_indent (file, -1); | 
 |     } | 
 |   lf_printf (file, "%s", suffix); | 
 | } | 
 |  | 
 |  | 
 | #ifdef MAIN | 
 | int | 
 | main (int argc, char **argv) | 
 | { | 
 |   table *t; | 
 |   table_entry *entry; | 
 |   lf *l; | 
 |   int line_nr; | 
 |  | 
 |   if (argc != 2) | 
 |     { | 
 |       printf ("Usage: table <file>\n"); | 
 |       exit (1); | 
 |     } | 
 |  | 
 |   t = table_open (argv[1]); | 
 |   l = lf_open ("-", "stdout", lf_omit_references, lf_is_text, "tmp-table"); | 
 |  | 
 |   line_nr = 0; | 
 |   do | 
 |     { | 
 |       char line[10]; | 
 |       entry = table_read (t); | 
 |       line_nr++; | 
 |       sprintf (line, "(%d ", line_nr); | 
 |       dump_table_entry (l, line, entry, ")\n"); | 
 |     } | 
 |   while (entry != NULL); | 
 |  | 
 |   return 0; | 
 | } | 
 | #endif |