| /* Copyright 2007 Free Software Foundation, Inc. |
| |
| This file is part of the GNU opcodes library. |
| |
| This library 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, or (at your option) |
| any later version. |
| |
| It 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, write to the Free Software |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <errno.h> |
| #include "getopt.h" |
| #include "libiberty.h" |
| #include "safe-ctype.h" |
| |
| #include "i386-opc.h" |
| |
| #include <libintl.h> |
| #define _(String) gettext (String) |
| |
| static const char *program_name = NULL; |
| static int debug = 0; |
| |
| static void |
| fail (const char *message, ...) |
| { |
| va_list args; |
| |
| va_start (args, message); |
| fprintf (stderr, _("%s: Error: "), program_name); |
| vfprintf (stderr, message, args); |
| va_end (args); |
| xexit (1); |
| } |
| |
| static void |
| process_copyright (FILE *fp) |
| { |
| fprintf (fp, "/* This file is automatically generated by i386-gen. Do not edit! */\n\ |
| /* Copyright 2007 Free Software Foundation, Inc.\n\ |
| \n\ |
| This file is part of the GNU opcodes library.\n\ |
| \n\ |
| This library is free software; you can redistribute it and/or modify\n\ |
| it under the terms of the GNU General Public License as published by\n\ |
| the Free Software Foundation; either version 3, or (at your option)\n\ |
| any later version.\n\ |
| \n\ |
| It is distributed in the hope that it will be useful, but WITHOUT\n\ |
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\n\ |
| or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public\n\ |
| License for more details.\n\ |
| \n\ |
| You should have received a copy of the GNU General Public License\n\ |
| along with this program; if not, write to the Free Software\n\ |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,\n\ |
| MA 02110-1301, USA. */\n"); |
| } |
| |
| /* Remove leading white spaces. */ |
| |
| static char * |
| remove_leading_whitespaces (char *str) |
| { |
| while (ISSPACE (*str)) |
| str++; |
| return str; |
| } |
| |
| /* Remove trailing white spaces. */ |
| |
| static void |
| remove_trailing_whitespaces (char *str) |
| { |
| size_t last = strlen (str); |
| |
| if (last == 0) |
| return; |
| |
| do |
| { |
| last--; |
| if (ISSPACE (str [last])) |
| str[last] = '\0'; |
| else |
| break; |
| } |
| while (last != 0); |
| } |
| |
| /* Find next field separated by SEP and terminate it. Return a |
| pointer to the one after it. */ |
| |
| static char * |
| next_field (char *str, char sep, char **next) |
| { |
| char *p; |
| |
| p = remove_leading_whitespaces (str); |
| for (str = p; *str != sep && *str != '\0'; str++); |
| |
| *str = '\0'; |
| remove_trailing_whitespaces (p); |
| |
| *next = str + 1; |
| |
| return p; |
| } |
| |
| static void |
| process_i386_opcodes (FILE *table) |
| { |
| FILE *fp = fopen ("i386-opc.tbl", "r"); |
| char buf[2048]; |
| unsigned int i; |
| char *str, *p, *last; |
| char *name, *operands, *base_opcode, *extension_opcode; |
| char *cpu_flags, *opcode_modifier, *operand_types [MAX_OPERANDS]; |
| |
| if (fp == NULL) |
| fail (_("can't find i386-opc.tbl for reading, errno = %s\n"), |
| strerror (errno)); |
| |
| fprintf (table, "\n/* i386 opcode table. */\n\n"); |
| fprintf (table, "const template i386_optab[] =\n{\n"); |
| |
| while (!feof (fp)) |
| { |
| if (fgets (buf, sizeof (buf), fp) == NULL) |
| break; |
| |
| p = remove_leading_whitespaces (buf); |
| |
| /* Skip comments. */ |
| str = strstr (p, "//"); |
| if (str != NULL) |
| str[0] = '\0'; |
| |
| /* Remove trailing white spaces. */ |
| remove_trailing_whitespaces (p); |
| |
| switch (p[0]) |
| { |
| case '#': |
| fprintf (table, "%s\n", p); |
| case '\0': |
| continue; |
| break; |
| default: |
| break; |
| } |
| |
| last = p + strlen (p); |
| |
| /* Find name. */ |
| name = next_field (p, ',', &str); |
| |
| if (str >= last) |
| abort (); |
| |
| /* Find number of operands. */ |
| operands = next_field (str, ',', &str); |
| |
| if (str >= last) |
| abort (); |
| |
| /* Find base_opcode. */ |
| base_opcode = next_field (str, ',', &str); |
| |
| if (str >= last) |
| abort (); |
| |
| /* Find extension_opcode. */ |
| extension_opcode = next_field (str, ',', &str); |
| |
| if (str >= last) |
| abort (); |
| |
| /* Find cpu_flags. */ |
| cpu_flags = next_field (str, ',', &str); |
| |
| if (str >= last) |
| abort (); |
| |
| /* Find opcode_modifier. */ |
| opcode_modifier = next_field (str, ',', &str); |
| |
| if (str >= last) |
| abort (); |
| |
| /* Remove the first {. */ |
| str = remove_leading_whitespaces (str); |
| if (*str != '{') |
| abort (); |
| str = remove_leading_whitespaces (str + 1); |
| |
| i = strlen (str); |
| |
| /* There are at least "X}". */ |
| if (i < 2) |
| abort (); |
| |
| /* Remove trailing white spaces and }. */ |
| do |
| { |
| i--; |
| if (ISSPACE (str[i]) || str[i] == '}') |
| str[i] = '\0'; |
| else |
| break; |
| } |
| while (i != 0); |
| |
| last = str + i; |
| |
| /* Find operand_types. */ |
| for (i = 0; i < ARRAY_SIZE (operand_types); i++) |
| { |
| if (str >= last) |
| { |
| operand_types [i] = NULL; |
| break; |
| } |
| |
| operand_types [i] = next_field (str, ',', &str); |
| if (*operand_types[i] == '0') |
| { |
| if (i != 0) |
| operand_types[i] = NULL; |
| break; |
| } |
| } |
| |
| fprintf (table, " { \"%s\", %s, %s, %s, %s,\n", |
| name, operands, base_opcode, extension_opcode, |
| cpu_flags); |
| |
| fprintf (table, " %s,\n", opcode_modifier); |
| |
| fprintf (table, " { "); |
| |
| for (i = 0; i < ARRAY_SIZE (operand_types); i++) |
| { |
| if (operand_types[i] == NULL |
| || *operand_types[i] == '0') |
| { |
| if (i == 0) |
| fprintf (table, "0"); |
| break; |
| } |
| |
| if (i != 0) |
| fprintf (table, ",\n "); |
| |
| fprintf (table, "%s", operand_types[i]); |
| } |
| fprintf (table, " } },\n"); |
| } |
| |
| fclose (fp); |
| |
| fprintf (table, " { NULL, 0, 0, 0, 0, 0, { 0 } }\n"); |
| fprintf (table, "};\n"); |
| } |
| |
| static void |
| process_i386_registers (FILE *table) |
| { |
| FILE *fp = fopen ("i386-reg.tbl", "r"); |
| char buf[2048]; |
| char *str, *p, *last; |
| char *reg_name, *reg_type, *reg_flags, *reg_num; |
| |
| if (fp == NULL) |
| fail (_("can't find i386-reg.tbl for reading, errno = %s\n"), |
| strerror (errno)); |
| |
| fprintf (table, "\n/* i386 register table. */\n\n"); |
| fprintf (table, "const reg_entry i386_regtab[] =\n{\n"); |
| |
| while (!feof (fp)) |
| { |
| if (fgets (buf, sizeof (buf), fp) == NULL) |
| break; |
| |
| p = remove_leading_whitespaces (buf); |
| |
| /* Skip comments. */ |
| str = strstr (p, "//"); |
| if (str != NULL) |
| str[0] = '\0'; |
| |
| /* Remove trailing white spaces. */ |
| remove_trailing_whitespaces (p); |
| |
| switch (p[0]) |
| { |
| case '#': |
| fprintf (table, "%s\n", p); |
| case '\0': |
| continue; |
| break; |
| default: |
| break; |
| } |
| |
| last = p + strlen (p); |
| |
| /* Find reg_name. */ |
| reg_name = next_field (p, ',', &str); |
| |
| if (str >= last) |
| abort (); |
| |
| /* Find reg_type. */ |
| reg_type = next_field (str, ',', &str); |
| |
| if (str >= last) |
| abort (); |
| |
| /* Find reg_flags. */ |
| reg_flags = next_field (str, ',', &str); |
| |
| if (str >= last) |
| abort (); |
| |
| /* Find reg_num. */ |
| reg_num = next_field (str, ',', &str); |
| |
| fprintf (table, " { \"%s\", %s, %s, %s },\n", |
| reg_name, reg_type, reg_flags, reg_num); |
| } |
| |
| fclose (fp); |
| |
| fprintf (table, "};\n"); |
| |
| fprintf (table, "\nconst unsigned int i386_regtab_size = ARRAY_SIZE (i386_regtab);\n"); |
| } |
| |
| /* Program options. */ |
| #define OPTION_SRCDIR 200 |
| |
| struct option long_options[] = |
| { |
| {"srcdir", required_argument, NULL, OPTION_SRCDIR}, |
| {"debug", no_argument, NULL, 'd'}, |
| {"version", no_argument, NULL, 'V'}, |
| {"help", no_argument, NULL, 'h'}, |
| {0, no_argument, NULL, 0} |
| }; |
| |
| static void |
| print_version (void) |
| { |
| printf ("%s: version 1.0\n", program_name); |
| xexit (0); |
| } |
| |
| static void |
| usage (FILE * stream, int status) |
| { |
| fprintf (stream, "Usage: %s [-V | --version] [-d | --debug] [--srcdir=dirname] [--help]\n", |
| program_name); |
| xexit (status); |
| } |
| |
| int |
| main (int argc, char **argv) |
| { |
| extern int chdir (char *); |
| char *srcdir = NULL; |
| int c; |
| FILE *table; |
| |
| program_name = *argv; |
| xmalloc_set_program_name (program_name); |
| |
| while ((c = getopt_long (argc, argv, "vVdh", long_options, 0)) != EOF) |
| switch (c) |
| { |
| case OPTION_SRCDIR: |
| srcdir = optarg; |
| break; |
| case 'V': |
| case 'v': |
| print_version (); |
| break; |
| case 'd': |
| debug = 1; |
| break; |
| case 'h': |
| case '?': |
| usage (stderr, 0); |
| default: |
| case 0: |
| break; |
| } |
| |
| if (optind != argc) |
| usage (stdout, 1); |
| |
| if (srcdir != NULL) |
| if (chdir (srcdir) != 0) |
| fail (_("unable to change directory to \"%s\", errno = %s\n"), |
| srcdir, strerror (errno)); |
| |
| table = fopen ("i386-tbl.h", "w"); |
| if (table == NULL) |
| fail (_("can't create i386-tbl.h, errno = %s\n"), strerror (errno)); |
| |
| process_copyright (table); |
| |
| process_i386_opcodes (table); |
| process_i386_registers (table); |
| |
| fclose (table); |
| |
| exit (0); |
| } |