1/* Copyright 2007  Free Software Foundation, Inc.
2
3   This file is part of the GNU opcodes library.
4
5   This library is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 3, or (at your option)
8   any later version.
9
10   It is distributed in the hope that it will be useful, but WITHOUT
11   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
13   License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
18   MA 02110-1301, USA.  */
19
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <errno.h>
24#include "getopt.h"
25#include "libiberty.h"
26#include "safe-ctype.h"
27
28#include "i386-opc.h"
29
30#include <libintl.h>
31#define _(String) gettext (String)
32
33static const char *program_name = NULL;
34static int debug = 0;
35
36static void
37fail (const char *message, ...)
38{
39  va_list args;
40
41  va_start (args, message);
42  fprintf (stderr, _("%s: Error: "), program_name);
43  vfprintf (stderr, message, args);
44  va_end (args);
45  xexit (1);
46}
47
48static void
49process_copyright (FILE *fp)
50{
51  fprintf (fp, "/* This file is automatically generated by i386-gen.  Do not edit!  */\n\
52/* Copyright 2007  Free Software Foundation, Inc.\n\
53\n\
54   This file is part of the GNU opcodes library.\n\
55\n\
56   This library is free software; you can redistribute it and/or modify\n\
57   it under the terms of the GNU General Public License as published by\n\
58   the Free Software Foundation; either version 3, or (at your option)\n\
59   any later version.\n\
60\n\
61   It is distributed in the hope that it will be useful, but WITHOUT\n\
62   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\n\
63   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public\n\
64   License for more details.\n\
65\n\
66   You should have received a copy of the GNU General Public License\n\
67   along with this program; if not, write to the Free Software\n\
68   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,\n\
69   MA 02110-1301, USA.  */\n");
70}
71
72/* Remove leading white spaces.  */
73
74static char *
75remove_leading_whitespaces (char *str)
76{
77  while (ISSPACE (*str))
78    str++;
79  return str;
80}
81
82/* Remove trailing white spaces.  */
83
84static void
85remove_trailing_whitespaces (char *str)
86{
87  size_t last = strlen (str);
88
89  if (last == 0)
90    return;
91
92  do
93    {
94      last--;
95      if (ISSPACE (str [last]))
96	str[last] = '\0';
97      else
98	break;
99    }
100  while (last != 0);
101}
102
103/* Find next field separated by SEP and terminate it. Return a
104   pointer to the one after it.  */
105
106static char *
107next_field (char *str, char sep, char **next)
108{
109  char *p;
110
111  p = remove_leading_whitespaces (str);
112  for (str = p; *str != sep && *str != '\0'; str++);
113
114  *str = '\0';
115  remove_trailing_whitespaces (p);
116
117  *next = str + 1;
118
119  return p;
120}
121
122static void
123process_i386_opcodes (FILE *table)
124{
125  FILE *fp = fopen ("i386-opc.tbl", "r");
126  char buf[2048];
127  unsigned int i;
128  char *str, *p, *last;
129  char *name, *operands, *base_opcode, *extension_opcode;
130  char *cpu_flags, *opcode_modifier, *operand_types [MAX_OPERANDS];
131
132  if (fp == NULL)
133    fail (_("can't find i386-opc.tbl for reading, errno = %s\n"),
134	  strerror (errno));
135
136  fprintf (table, "\n/* i386 opcode table.  */\n\n");
137  fprintf (table, "const template i386_optab[] =\n{\n");
138
139  while (!feof (fp))
140    {
141      if (fgets (buf, sizeof (buf), fp) == NULL)
142	break;
143
144      p = remove_leading_whitespaces (buf);
145
146      /* Skip comments.  */
147      str = strstr (p, "//");
148      if (str != NULL)
149	str[0] = '\0';
150
151      /* Remove trailing white spaces.  */
152      remove_trailing_whitespaces (p);
153
154      switch (p[0])
155	{
156	case '#':
157	  fprintf (table, "%s\n", p);
158	case '\0':
159	  continue;
160	  break;
161	default:
162	  break;
163	}
164
165      last = p + strlen (p);
166
167      /* Find name.  */
168      name = next_field (p, ',', &str);
169
170      if (str >= last)
171	abort ();
172
173      /* Find number of operands.  */
174      operands = next_field (str, ',', &str);
175
176      if (str >= last)
177	abort ();
178
179      /* Find base_opcode.  */
180      base_opcode = next_field (str, ',', &str);
181
182      if (str >= last)
183	abort ();
184
185      /* Find extension_opcode.  */
186      extension_opcode = next_field (str, ',', &str);
187
188      if (str >= last)
189	abort ();
190
191      /* Find cpu_flags.  */
192      cpu_flags = next_field (str, ',', &str);
193
194      if (str >= last)
195	abort ();
196
197      /* Find opcode_modifier.  */
198      opcode_modifier = next_field (str, ',', &str);
199
200      if (str >= last)
201	abort ();
202
203      /* Remove the first {.  */
204      str = remove_leading_whitespaces (str);
205      if (*str != '{')
206	abort ();
207      str = remove_leading_whitespaces (str + 1);
208
209      i = strlen (str);
210
211      /* There are at least "X}".  */
212      if (i < 2)
213	abort ();
214
215      /* Remove trailing white spaces and }. */
216      do
217	{
218	  i--;
219	  if (ISSPACE (str[i]) || str[i] == '}')
220	    str[i] = '\0';
221	  else
222	    break;
223	}
224      while (i != 0);
225
226      last = str + i;
227
228      /* Find operand_types.  */
229      for (i = 0; i < ARRAY_SIZE (operand_types); i++)
230	{
231	  if (str >= last)
232	    {
233	      operand_types [i] = NULL;
234	      break;
235	    }
236
237	  operand_types [i] = next_field (str, ',', &str);
238	  if (*operand_types[i] == '0')
239	    {
240	      if (i != 0)
241		operand_types[i] = NULL;
242	      break;
243	    }
244	}
245
246      fprintf (table, "  { \"%s\", %s, %s, %s, %s,\n",
247	       name, operands, base_opcode, extension_opcode,
248	       cpu_flags);
249
250      fprintf (table, "    %s,\n", opcode_modifier);
251
252      fprintf (table, "    { ");
253
254      for (i = 0; i < ARRAY_SIZE (operand_types); i++)
255	{
256	  if (operand_types[i] == NULL
257	      || *operand_types[i] == '0')
258	    {
259	      if (i == 0)
260		fprintf (table, "0");
261	      break;
262	    }
263
264	  if (i != 0)
265	    fprintf (table, ",\n      ");
266
267	  fprintf (table, "%s", operand_types[i]);
268	}
269      fprintf (table, " } },\n");
270    }
271
272  fclose (fp);
273
274  fprintf (table, "  { NULL, 0, 0, 0, 0, 0, { 0 } }\n");
275  fprintf (table, "};\n");
276}
277
278static void
279process_i386_registers (FILE *table)
280{
281  FILE *fp = fopen ("i386-reg.tbl", "r");
282  char buf[2048];
283  char *str, *p, *last;
284  char *reg_name, *reg_type, *reg_flags, *reg_num;
285
286  if (fp == NULL)
287    fail (_("can't find i386-reg.tbl for reading, errno = %s\n"),
288	  strerror (errno));
289
290  fprintf (table, "\n/* i386 register table.  */\n\n");
291  fprintf (table, "const reg_entry i386_regtab[] =\n{\n");
292
293  while (!feof (fp))
294    {
295      if (fgets (buf, sizeof (buf), fp) == NULL)
296	break;
297
298      p = remove_leading_whitespaces (buf);
299
300      /* Skip comments.  */
301      str = strstr (p, "//");
302      if (str != NULL)
303	str[0] = '\0';
304
305      /* Remove trailing white spaces.  */
306      remove_trailing_whitespaces (p);
307
308      switch (p[0])
309	{
310	case '#':
311	  fprintf (table, "%s\n", p);
312	case '\0':
313	  continue;
314	  break;
315	default:
316	  break;
317	}
318
319      last = p + strlen (p);
320
321      /* Find reg_name.  */
322      reg_name = next_field (p, ',', &str);
323
324      if (str >= last)
325	abort ();
326
327      /* Find reg_type.  */
328      reg_type = next_field (str, ',', &str);
329
330      if (str >= last)
331	abort ();
332
333      /* Find reg_flags.  */
334      reg_flags = next_field (str, ',', &str);
335
336      if (str >= last)
337	abort ();
338
339      /* Find reg_num.  */
340      reg_num = next_field (str, ',', &str);
341
342      fprintf (table, "  { \"%s\", %s, %s, %s },\n",
343	       reg_name, reg_type, reg_flags, reg_num);
344    }
345
346  fclose (fp);
347
348  fprintf (table, "};\n");
349
350  fprintf (table, "\nconst unsigned int i386_regtab_size = ARRAY_SIZE (i386_regtab);\n");
351}
352
353/* Program options.  */
354#define OPTION_SRCDIR	200
355
356struct option long_options[] =
357{
358  {"srcdir",  required_argument, NULL, OPTION_SRCDIR},
359  {"debug",   no_argument,       NULL, 'd'},
360  {"version", no_argument,       NULL, 'V'},
361  {"help",    no_argument,       NULL, 'h'},
362  {0,         no_argument,       NULL, 0}
363};
364
365static void
366print_version (void)
367{
368  printf ("%s: version 1.0\n", program_name);
369  xexit (0);
370}
371
372static void
373usage (FILE * stream, int status)
374{
375  fprintf (stream, "Usage: %s [-V | --version] [-d | --debug] [--srcdir=dirname] [--help]\n",
376	   program_name);
377  xexit (status);
378}
379
380int
381main (int argc, char **argv)
382{
383  extern int chdir (char *);
384  char *srcdir = NULL;
385  int c;
386  FILE *table;
387
388  program_name = *argv;
389  xmalloc_set_program_name (program_name);
390
391  while ((c = getopt_long (argc, argv, "vVdh", long_options, 0)) != EOF)
392    switch (c)
393      {
394      case OPTION_SRCDIR:
395	srcdir = optarg;
396	break;
397      case 'V':
398      case 'v':
399	print_version ();
400	break;
401      case 'd':
402	debug = 1;
403	break;
404      case 'h':
405      case '?':
406	usage (stderr, 0);
407      default:
408      case 0:
409	break;
410      }
411
412  if (optind != argc)
413    usage (stdout, 1);
414
415  if (srcdir != NULL)
416    if (chdir (srcdir) != 0)
417      fail (_("unable to change directory to \"%s\", errno = %s\n"),
418	    srcdir, strerror (errno));
419
420  table = fopen ("i386-tbl.h", "w");
421  if (table == NULL)
422    fail (_("can't create i386-tbl.h, errno = %s\n"), strerror (errno));
423
424  process_copyright (table);
425
426  process_i386_opcodes (table);
427  process_i386_registers (table);
428
429  fclose (table);
430
431  exit (0);
432}
433