Commit | Line | Data |
---|---|---|
40b8e679 L |
1 | /* Copyright 2007 Free Software Foundation, Inc. |
2 | ||
9b201bb5 | 3 | This file is part of the GNU opcodes library. |
40b8e679 | 4 | |
9b201bb5 | 5 | This library is free software; you can redistribute it and/or modify |
40b8e679 | 6 | it under the terms of the GNU General Public License as published by |
9b201bb5 NC |
7 | the Free Software Foundation; either version 3, or (at your option) |
8 | any later version. | |
40b8e679 | 9 | |
9b201bb5 NC |
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. | |
40b8e679 L |
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 | |
9b201bb5 NC |
17 | Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
18 | MA 02110-1301, USA. */ | |
40b8e679 L |
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 | ||
33 | static const char *program_name = NULL; | |
34 | static int debug = 0; | |
35 | ||
36 | static void | |
37 | fail (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 | ||
72ffa0fb L |
48 | static void |
49 | process_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 | ||
40b8e679 L |
72 | /* Remove leading white spaces. */ |
73 | ||
74 | static char * | |
75 | remove_leading_whitespaces (char *str) | |
76 | { | |
77 | while (ISSPACE (*str)) | |
78 | str++; | |
79 | return str; | |
80 | } | |
81 | ||
82 | /* Remove trailing white spaces. */ | |
83 | ||
84 | static void | |
85 | remove_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 '.' and terminate it. Return a | |
104 | pointer to the one after it. */ | |
105 | ||
106 | static char * | |
107 | next_field (char *str, char **next) | |
108 | { | |
109 | char *p; | |
110 | ||
111 | p = remove_leading_whitespaces (str); | |
112 | for (str = p; *str != ',' && *str != '\0'; str++); | |
113 | ||
114 | *str = '\0'; | |
115 | remove_trailing_whitespaces (p); | |
116 | ||
117 | *next = str + 1; | |
118 | ||
119 | return p; | |
120 | } | |
121 | ||
122 | static void | |
72ffa0fb | 123 | process_i386_opcodes (FILE *table) |
40b8e679 L |
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) | |
34edb9ad L |
133 | fail (_("can't find i386-opc.tbl for reading, errno = %s\n"), |
134 | strerror (errno)); | |
40b8e679 | 135 | |
34edb9ad L |
136 | fprintf (table, "\n/* i386 opcode table. */\n\n"); |
137 | fprintf (table, "const template i386_optab[] =\n{\n"); | |
40b8e679 L |
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 '#': | |
34edb9ad | 157 | fprintf (table, "%s\n", p); |
40b8e679 L |
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 | ||
34edb9ad L |
246 | fprintf (table, " { \"%s\", %s, %s, %s, %s,\n", |
247 | name, operands, base_opcode, extension_opcode, | |
248 | cpu_flags); | |
40b8e679 | 249 | |
34edb9ad | 250 | fprintf (table, " %s,\n", opcode_modifier); |
40b8e679 | 251 | |
34edb9ad | 252 | fprintf (table, " { "); |
40b8e679 L |
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) | |
34edb9ad | 260 | fprintf (table, "0"); |
40b8e679 L |
261 | break; |
262 | } | |
263 | ||
264 | if (i != 0) | |
34edb9ad | 265 | fprintf (table, ",\n "); |
40b8e679 | 266 | |
34edb9ad | 267 | fprintf (table, "%s", operand_types[i]); |
40b8e679 | 268 | } |
34edb9ad | 269 | fprintf (table, " } },\n"); |
40b8e679 L |
270 | } |
271 | ||
34edb9ad L |
272 | fclose (fp); |
273 | ||
274 | fprintf (table, " { NULL, 0, 0, 0, 0, 0, { 0 } }\n"); | |
275 | fprintf (table, "};\n"); | |
40b8e679 L |
276 | } |
277 | ||
278 | static void | |
72ffa0fb | 279 | process_i386_registers (FILE *table) |
40b8e679 L |
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) | |
34edb9ad L |
287 | fail (_("can't find i386-reg.tbl for reading, errno = %s\n"), |
288 | strerror (errno)); | |
40b8e679 | 289 | |
34edb9ad L |
290 | fprintf (table, "\n/* i386 register table. */\n\n"); |
291 | fprintf (table, "const reg_entry i386_regtab[] =\n{\n"); | |
40b8e679 L |
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 '#': | |
34edb9ad | 311 | fprintf (table, "%s\n", p); |
40b8e679 L |
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 | ||
34edb9ad L |
342 | fprintf (table, " { \"%s\", %s, %s, %s },\n", |
343 | reg_name, reg_type, reg_flags, reg_num); | |
40b8e679 L |
344 | } |
345 | ||
34edb9ad L |
346 | fclose (fp); |
347 | ||
348 | fprintf (table, "};\n"); | |
40b8e679 | 349 | |
34edb9ad | 350 | fprintf (table, "\nconst unsigned int i386_regtab_size = ARRAY_SIZE (i386_regtab);\n"); |
40b8e679 L |
351 | } |
352 | ||
353 | /* Program options. */ | |
354 | #define OPTION_SRCDIR 200 | |
355 | ||
356 | struct 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 | ||
365 | static void | |
366 | print_version (void) | |
367 | { | |
368 | printf ("%s: version 1.0\n", program_name); | |
369 | xexit (0); | |
370 | } | |
371 | ||
372 | static void | |
373 | usage (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 | ||
380 | int | |
381 | main (int argc, char **argv) | |
382 | { | |
383 | extern int chdir (char *); | |
384 | char *srcdir = NULL; | |
385 | int c; | |
72ffa0fb | 386 | FILE *table; |
40b8e679 L |
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 | ||
34edb9ad L |
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 | ||
72ffa0fb | 424 | process_copyright (table); |
40b8e679 | 425 | |
72ffa0fb L |
426 | process_i386_opcodes (table); |
427 | process_i386_registers (table); | |
40b8e679 | 428 | |
34edb9ad L |
429 | fclose (table); |
430 | ||
40b8e679 L |
431 | exit (0); |
432 | } |