| /* The IGEN simulator generator for GDB, the GNU Debugger. |
| |
| Copyright 2002-2021 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 "misc.h" |
| #include "lf.h" |
| #include "table.h" |
| |
| #include <unistd.h> |
| #include <stdlib.h> |
| |
| 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 |