Commit | Line | Data |
---|---|---|
1d371d35 ILT |
1 | /* windres.c -- a program to manipulate Windows resources |
2 | Copyright 1997 Free Software Foundation, Inc. | |
3 | Written by Ian Lance Taylor, Cygnus Support. | |
4 | ||
5 | This file is part of GNU Binutils. | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 2 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program; if not, write to the Free Software | |
19 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
20 | 02111-1307, USA. */ | |
21 | ||
22 | /* This program can read and write Windows resources in various | |
23 | formats. In particular, it can act like the rc resource compiler | |
24 | program, and it can act like the cvtres res to COFF conversion | |
25 | program. | |
26 | ||
27 | It is based on information taken from the following sources: | |
28 | ||
29 | * Microsoft documentation. | |
30 | ||
31 | * The rcl program, written by Gunther Ebert | |
32 | <gunther.ebert@ixos-leipzig.de>. | |
33 | ||
34 | * The res2coff program, written by Pedro A. Aranda <paag@tid.es>. | |
35 | ||
36 | */ | |
37 | ||
38 | #include "bfd.h" | |
39 | #include "getopt.h" | |
40 | #include "bucomm.h" | |
41 | #include "libiberty.h" | |
42 | #include "windres.h" | |
43 | ||
44 | #include <assert.h> | |
45 | #include <ctype.h> | |
46 | ||
47 | /* An enumeration of format types. */ | |
48 | ||
49 | enum res_format | |
50 | { | |
51 | /* Unknown format. */ | |
52 | RES_FORMAT_UNKNOWN, | |
53 | /* Textual RC file. */ | |
54 | RES_FORMAT_RC, | |
55 | /* Binary RES file. */ | |
56 | RES_FORMAT_RES, | |
57 | /* COFF file. */ | |
58 | RES_FORMAT_COFF | |
59 | }; | |
60 | ||
61 | /* A structure used to map between format types and strings. */ | |
62 | ||
63 | struct format_map | |
64 | { | |
65 | const char *name; | |
66 | enum res_format format; | |
67 | }; | |
68 | ||
69 | /* A mapping between names and format types. */ | |
70 | ||
71 | static const struct format_map format_names[] = | |
72 | { | |
73 | { "rc", RES_FORMAT_RC }, | |
74 | { "res", RES_FORMAT_RES }, | |
75 | { "coff", RES_FORMAT_COFF }, | |
76 | { NULL, RES_FORMAT_UNKNOWN } | |
77 | }; | |
78 | ||
79 | /* A mapping from file extensions to format types. */ | |
80 | ||
81 | static const struct format_map format_fileexts[] = | |
82 | { | |
83 | { "rc", RES_FORMAT_RC }, | |
84 | { "res", RES_FORMAT_RES }, | |
85 | { "exe", RES_FORMAT_COFF }, | |
86 | { "obj", RES_FORMAT_COFF }, | |
87 | { "o", RES_FORMAT_COFF }, | |
88 | { NULL, RES_FORMAT_UNKNOWN } | |
89 | }; | |
90 | ||
91 | /* A list of include directories. */ | |
92 | ||
93 | struct include_dir | |
94 | { | |
95 | struct include_dir *next; | |
96 | char *dir; | |
97 | }; | |
98 | ||
99 | static struct include_dir *include_dirs; | |
100 | ||
101 | /* Long options. */ | |
102 | ||
103 | /* 150 isn't special; it's just an arbitrary non-ASCII char value. */ | |
104 | ||
105 | #define OPTION_DEFINE 150 | |
106 | #define OPTION_HELP (OPTION_DEFINE + 1) | |
107 | #define OPTION_INCLUDE_DIR (OPTION_HELP + 1) | |
108 | #define OPTION_LANGUAGE (OPTION_INCLUDE_DIR + 1) | |
109 | #define OPTION_PREPROCESSOR (OPTION_LANGUAGE + 1) | |
110 | #define OPTION_VERSION (OPTION_PREPROCESSOR + 1) | |
111 | #define OPTION_YYDEBUG (OPTION_VERSION + 1) | |
112 | ||
113 | static const struct option long_options[] = | |
114 | { | |
115 | {"define", required_argument, 0, OPTION_DEFINE}, | |
116 | {"help", no_argument, 0, OPTION_HELP}, | |
117 | {"include-dir", required_argument, 0, OPTION_INCLUDE_DIR}, | |
118 | {"input-format", required_argument, 0, 'I'}, | |
119 | {"language", required_argument, 0, OPTION_LANGUAGE}, | |
120 | {"output-format", required_argument, 0, 'O'}, | |
121 | {"preprocessor", required_argument, 0, OPTION_PREPROCESSOR}, | |
122 | {"target", required_argument, 0, 'F'}, | |
123 | {"version", no_argument, 0, OPTION_VERSION}, | |
124 | {"yydebug", no_argument, 0, OPTION_YYDEBUG}, | |
125 | {0, no_argument, 0, 0} | |
126 | }; | |
127 | ||
128 | /* Static functions. */ | |
129 | ||
130 | static enum res_format format_from_name PARAMS ((const char *)); | |
131 | static enum res_format format_from_filename PARAMS ((const char *, int)); | |
132 | static void usage PARAMS ((FILE *, int)); | |
133 | \f | |
134 | /* Open a file using the include directory search list. */ | |
135 | ||
136 | FILE * | |
137 | open_file_search (filename, mode, errmsg, real_filename) | |
138 | const char *filename; | |
139 | const char *mode; | |
140 | const char *errmsg; | |
141 | char **real_filename; | |
142 | { | |
143 | FILE *e; | |
144 | struct include_dir *d; | |
145 | ||
146 | e = fopen (filename, mode); | |
147 | if (e != NULL) | |
148 | { | |
149 | *real_filename = xstrdup (filename); | |
150 | return e; | |
151 | } | |
152 | ||
153 | if (errno == ENOENT) | |
154 | { | |
155 | for (d = include_dirs; d != NULL; d = d->next) | |
156 | { | |
157 | char *n; | |
158 | ||
159 | n = (char *) xmalloc (strlen (d->dir) + strlen (filename) + 2); | |
160 | sprintf (n, "%s/%s", d->dir, filename); | |
161 | e = fopen (n, mode); | |
162 | if (e != NULL) | |
163 | { | |
164 | *real_filename = n; | |
165 | return e; | |
166 | } | |
167 | ||
168 | if (errno != ENOENT) | |
169 | break; | |
170 | } | |
171 | } | |
172 | ||
173 | fatal ("can't open %s `%s': %s", errmsg, filename, strerror (errno)); | |
174 | ||
175 | /* Return a value to avoid a compiler warning. */ | |
176 | return NULL; | |
177 | } | |
178 | \f | |
179 | /* Unicode support. */ | |
180 | ||
181 | /* Convert an ASCII string to a unicode string. We just copy it, | |
182 | expanding chars to shorts, rather than doing something intelligent. */ | |
183 | ||
184 | void | |
185 | unicode_from_ascii (length, unicode, ascii) | |
186 | unsigned short *length; | |
187 | unsigned short **unicode; | |
188 | const char *ascii; | |
189 | { | |
190 | int len; | |
191 | const char *s; | |
192 | unsigned short *w; | |
193 | ||
194 | len = strlen (ascii); | |
195 | ||
196 | if (length != NULL) | |
197 | { | |
198 | if (len > 0xffff) | |
199 | fatal ("string too long (%d chars > 0xffff)", len); | |
200 | *length = len; | |
201 | } | |
202 | ||
203 | *unicode = (unsigned short *) xmalloc ((len + 1) * sizeof (unsigned short)); | |
204 | ||
205 | for (s = ascii, w = *unicode; *s != '\0'; s++, w++) | |
206 | *w = *s & 0xff; | |
207 | *w = 0; | |
208 | } | |
209 | ||
210 | /* Print the unicode string UNICODE to the file E. LENGTH is the | |
211 | number of characters to print, or -1 if we should print until the | |
212 | end of the string. */ | |
213 | ||
214 | void | |
215 | unicode_print (e, unicode, length) | |
216 | FILE *e; | |
217 | const unsigned short *unicode; | |
218 | int length; | |
219 | { | |
220 | while (1) | |
221 | { | |
222 | unsigned short ch; | |
223 | ||
224 | if (length == 0) | |
225 | return; | |
226 | if (length > 0) | |
227 | --length; | |
228 | ||
229 | ch = *unicode; | |
230 | ||
231 | if (ch == 0) | |
232 | return; | |
233 | ||
234 | ++unicode; | |
235 | ||
236 | if ((ch & 0x7f) == ch && isprint (ch)) | |
237 | putc (ch, e); | |
238 | else if ((ch & 0xff) == ch) | |
239 | fprintf (e, "\\%03o", (unsigned int) ch); | |
240 | else | |
241 | fprintf (e, "\\x%x", (unsigned int) ch); | |
242 | } | |
243 | } | |
244 | \f | |
245 | /* Compare two resource ID's. We consider name entries to come before | |
246 | numeric entries, because that is how they appear in the COFF .rsrc | |
247 | section. */ | |
248 | ||
249 | int | |
250 | res_id_cmp (a, b) | |
251 | struct res_id a; | |
252 | struct res_id b; | |
253 | { | |
254 | if (! a.named) | |
255 | { | |
256 | if (b.named) | |
257 | return 1; | |
258 | if (a.u.id > b.u.id) | |
259 | return 1; | |
260 | else if (a.u.id < b.u.id) | |
261 | return -1; | |
262 | else | |
263 | return 0; | |
264 | } | |
265 | else | |
266 | { | |
267 | unsigned short *as, *ase, *bs, *bse; | |
268 | ||
269 | if (! b.named) | |
270 | return -1; | |
271 | ||
272 | as = a.u.n.name; | |
273 | ase = as + a.u.n.length; | |
274 | bs = b.u.n.name; | |
275 | bse = bs + b.u.n.length; | |
276 | ||
277 | while (as < ase) | |
278 | { | |
279 | int i; | |
280 | ||
281 | if (bs >= bse) | |
282 | return 1; | |
283 | i = (int) *as - (int) *bs; | |
284 | if (i != 0) | |
285 | return i; | |
286 | ++as; | |
287 | ++bs; | |
288 | } | |
289 | ||
290 | if (bs < bse) | |
291 | return -1; | |
292 | ||
293 | return 0; | |
294 | } | |
295 | } | |
296 | ||
297 | /* Print a resource ID. */ | |
298 | ||
299 | void | |
300 | res_id_print (stream, id, quote) | |
301 | FILE *stream; | |
302 | struct res_id id; | |
303 | int quote; | |
304 | { | |
305 | if (! id.named) | |
306 | fprintf (stream, "%lu", id.u.id); | |
307 | else | |
308 | { | |
309 | unsigned short *s, *se; | |
310 | ||
311 | if (quote) | |
312 | putc ('"', stream); | |
313 | s = id.u.n.name; | |
314 | se = s + id.u.n.length; | |
315 | while (s < se) | |
316 | { | |
317 | if (*s == '"') | |
318 | fprintf (stream, "\\\""); | |
319 | else if ((*s & 0xff) == *s && isprint (*s)) | |
320 | putc (*s, stream); | |
321 | else | |
322 | fprintf (stream, "\\%03o", *s); | |
323 | ++s; | |
324 | } | |
325 | if (quote) | |
326 | putc ('"', stream); | |
327 | } | |
328 | } | |
329 | ||
330 | /* Print a list of resource ID's. */ | |
331 | ||
332 | void | |
333 | res_ids_print (stream, cids, ids) | |
334 | FILE *stream; | |
335 | int cids; | |
336 | const struct res_id *ids; | |
337 | { | |
338 | int i; | |
339 | ||
340 | for (i = 0; i < cids; i++) | |
341 | { | |
342 | res_id_print (stream, ids[i], 1); | |
343 | if (i + 1 < cids) | |
344 | fprintf (stream, ": "); | |
345 | } | |
346 | } | |
347 | ||
348 | /* Convert an ASCII string to a resource ID. */ | |
349 | ||
350 | void | |
351 | res_string_to_id (res_id, string) | |
352 | struct res_id *res_id; | |
353 | const char *string; | |
354 | { | |
355 | res_id->named = 1; | |
356 | unicode_from_ascii (&res_id->u.n.length, &res_id->u.n.name, string); | |
357 | } | |
358 | ||
359 | /* Define a resource. The arguments are the resource tree, RESOURCES, | |
360 | and the location at which to put it in the tree, CIDS and IDS. | |
361 | This returns a newly allocated res_resource structure, which the | |
362 | caller is expected to initialize. If DUPOK is non-zero, then if a | |
363 | resource with this ID exists, it is returned. Otherwise, a warning | |
364 | is issued, and a new resource is created replacing the existing | |
365 | one. */ | |
366 | ||
367 | struct res_resource * | |
368 | define_resource (resources, cids, ids, dupok) | |
369 | struct res_directory **resources; | |
370 | int cids; | |
371 | const struct res_id *ids; | |
372 | int dupok; | |
373 | { | |
374 | struct res_entry *re = NULL; | |
375 | int i; | |
376 | ||
377 | assert (cids > 0); | |
378 | for (i = 0; i < cids; i++) | |
379 | { | |
380 | struct res_entry **pp; | |
381 | ||
382 | if (*resources == NULL) | |
383 | { | |
384 | *resources = (struct res_directory *) xmalloc (sizeof **resources); | |
385 | (*resources)->characteristics = 0; | |
386 | (*resources)->time = 0; | |
387 | (*resources)->major = 0; | |
388 | (*resources)->minor = 0; | |
389 | (*resources)->entries = NULL; | |
390 | } | |
391 | ||
392 | for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next) | |
393 | if (res_id_cmp ((*pp)->id, ids[i]) == 0) | |
394 | break; | |
395 | ||
396 | if (*pp != NULL) | |
397 | re = *pp; | |
398 | else | |
399 | { | |
400 | re = (struct res_entry *) xmalloc (sizeof *re); | |
401 | re->next = NULL; | |
402 | re->id = ids[i]; | |
403 | if ((i + 1) < cids) | |
404 | { | |
405 | re->subdir = 1; | |
406 | re->u.dir = NULL; | |
407 | } | |
408 | else | |
409 | { | |
410 | re->subdir = 0; | |
411 | re->u.res = NULL; | |
412 | } | |
413 | ||
414 | *pp = re; | |
415 | } | |
416 | ||
417 | if ((i + 1) < cids) | |
418 | { | |
419 | if (! re->subdir) | |
420 | { | |
421 | fprintf (stderr, "%s: ", program_name); | |
422 | res_ids_print (stderr, i, ids); | |
423 | fprintf (stderr, ": expected to be a directory\n"); | |
424 | xexit (1); | |
425 | } | |
426 | ||
427 | resources = &re->u.dir; | |
428 | } | |
429 | } | |
430 | ||
431 | if (re->subdir) | |
432 | { | |
433 | fprintf (stderr, "%s: ", program_name); | |
434 | res_ids_print (stderr, cids, ids); | |
435 | fprintf (stderr, ": expected to be a leaf\n"); | |
436 | xexit (1); | |
437 | } | |
438 | ||
439 | if (re->u.res != NULL) | |
440 | { | |
441 | if (dupok) | |
442 | return re->u.res; | |
443 | ||
444 | fprintf (stderr, "%s: warning: ", program_name); | |
445 | res_ids_print (stderr, cids, ids); | |
446 | fprintf (stderr, ": duplicate value\n"); | |
447 | } | |
448 | ||
449 | re->u.res = (struct res_resource *) xmalloc (sizeof (struct res_resource)); | |
450 | ||
451 | re->u.res->type = RES_TYPE_UNINITIALIZED; | |
452 | memset (&re->u.res->res_info, 0, sizeof (struct res_res_info)); | |
453 | memset (&re->u.res->coff_info, 0, sizeof (struct res_coff_info)); | |
454 | ||
455 | return re->u.res; | |
456 | } | |
457 | ||
458 | /* Define a standard resource. This is a version of define_resource | |
459 | that just takes type, name, and language arguments. */ | |
460 | ||
461 | struct res_resource * | |
462 | define_standard_resource (resources, type, name, language, dupok) | |
463 | struct res_directory **resources; | |
464 | int type; | |
465 | struct res_id name; | |
466 | int language; | |
467 | int dupok; | |
468 | { | |
469 | struct res_id a[3]; | |
470 | ||
471 | a[0].named = 0; | |
472 | a[0].u.id = type; | |
473 | a[1] = name; | |
474 | a[2].named = 0; | |
475 | a[2].u.id = language; | |
476 | return define_resource (resources, 3, a, dupok); | |
477 | } | |
478 | \f | |
479 | /* Return whether the dialog resource DIALOG is a DIALOG or a | |
480 | DIALOGEX. */ | |
481 | ||
482 | int | |
483 | extended_dialog (dialog) | |
484 | const struct dialog *dialog; | |
485 | { | |
486 | const struct dialog_control *c; | |
487 | ||
488 | if (dialog->ex != NULL) | |
489 | return 1; | |
490 | ||
491 | for (c = dialog->controls; c != NULL; c = c->next) | |
492 | if (c->data != NULL || c->help != 0) | |
493 | return 1; | |
494 | ||
495 | return 0; | |
496 | } | |
497 | ||
498 | /* Return whether MENUITEMS are a MENU or a MENUEX. */ | |
499 | ||
500 | int | |
501 | extended_menu (menuitems) | |
502 | const struct menuitem *menuitems; | |
503 | { | |
504 | const struct menuitem *mi; | |
505 | ||
506 | for (mi = menuitems; mi != NULL; mi = mi->next) | |
507 | { | |
508 | if (mi->help != 0 || mi->state != 0) | |
509 | return 1; | |
510 | if (mi->popup != NULL && mi->id != 0) | |
511 | return 1; | |
512 | if ((mi->type | |
513 | & ~ (MENUITEM_CHECKED | |
514 | | MENUITEM_GRAYED | |
515 | | MENUITEM_HELP | |
516 | | MENUITEM_INACTIVE | |
517 | | MENUITEM_MENUBARBREAK | |
518 | | MENUITEM_MENUBREAK)) | |
519 | != 0) | |
520 | return 1; | |
521 | if (mi->popup != NULL) | |
522 | { | |
523 | if (extended_menu (mi->popup)) | |
524 | return 1; | |
525 | } | |
526 | } | |
527 | ||
528 | return 0; | |
529 | } | |
530 | \f | |
531 | /* Convert a string to a format type, or exit if it can't be done. */ | |
532 | ||
533 | static enum res_format | |
534 | format_from_name (name) | |
535 | const char *name; | |
536 | { | |
537 | const struct format_map *m; | |
538 | ||
539 | for (m = format_names; m->name != NULL; m++) | |
540 | if (strcasecmp (m->name, name) == 0) | |
541 | break; | |
542 | ||
543 | if (m->name == NULL) | |
544 | { | |
545 | fprintf (stderr, "%s: unknown format type `%s'\n", program_name, name); | |
546 | fprintf (stderr, "%s: supported formats:", program_name); | |
547 | for (m = format_names; m->name != NULL; m++) | |
548 | fprintf (stderr, " %s", m->name); | |
549 | fprintf (stderr, "\n"); | |
550 | xexit (1); | |
551 | } | |
552 | ||
553 | return m->format; | |
554 | } | |
555 | ||
556 | /* Work out a format type given a file name. If INPUT is non-zero, | |
557 | it's OK to look at the file itself. */ | |
558 | ||
559 | static enum res_format | |
560 | format_from_filename (filename, input) | |
561 | const char *filename; | |
562 | int input; | |
563 | { | |
564 | const char *ext; | |
565 | FILE *e; | |
566 | unsigned char b1, b2, b3, b4, b5; | |
567 | int magic; | |
568 | ||
569 | /* If we have an extension, see if we recognize it as implying a | |
570 | particular format. */ | |
571 | ext = strrchr (filename, '.'); | |
572 | if (ext != NULL) | |
573 | { | |
574 | const struct format_map *m; | |
575 | ||
576 | ++ext; | |
577 | for (m = format_fileexts; m->name != NULL; m++) | |
578 | if (strcasecmp (m->name, ext) == 0) | |
579 | return m->format; | |
580 | } | |
581 | ||
582 | /* If we don't recognize the name of an output file, assume it's a | |
583 | COFF file. */ | |
584 | ||
585 | if (! input) | |
586 | return RES_FORMAT_COFF; | |
587 | ||
588 | /* Read the first few bytes of the file to see if we can guess what | |
589 | it is. */ | |
590 | ||
591 | e = fopen (filename, FOPEN_RB); | |
592 | if (e == NULL) | |
593 | fatal ("%s: %s", filename, strerror (errno)); | |
594 | ||
595 | b1 = getc (e); | |
596 | b2 = getc (e); | |
597 | b3 = getc (e); | |
598 | b4 = getc (e); | |
599 | b5 = getc (e); | |
600 | ||
601 | fclose (e); | |
602 | ||
603 | /* A PE executable starts with 0x4d 0x5a 0x90 0x00. */ | |
604 | if (b1 == 0x4d && b2 == 0x5a && b3 == 0x90 && b4 == 0) | |
605 | return RES_FORMAT_COFF; | |
606 | ||
607 | /* A COFF .o file starts with a COFF magic number. */ | |
608 | magic = (b2 << 8) | b1; | |
609 | switch (magic) | |
610 | { | |
611 | case 0x14c: /* i386 */ | |
612 | case 0x166: /* MIPS */ | |
613 | case 0x184: /* Alpha */ | |
614 | case 0x268: /* 68k */ | |
615 | case 0x1f0: /* PowerPC */ | |
616 | case 0x290: /* PA */ | |
617 | return RES_FORMAT_COFF; | |
618 | } | |
619 | ||
620 | /* A RES file starts with 0x0 0x0 0x0 0x0 0x20 0x0 0x0 0x0. */ | |
621 | if (b1 == 0 && b2 == 0 && b3 == 0 && b4 == 0 && b5 == 0x20) | |
622 | return RES_FORMAT_RES; | |
623 | ||
624 | /* If every character is printable or space, assume it's an RC file. */ | |
625 | if ((isprint (b1) || isspace (b1)) | |
626 | && (isprint (b2) || isspace (b2)) | |
627 | && (isprint (b3) || isspace (b3)) | |
628 | && (isprint (b4) || isspace (b4)) | |
629 | && (isprint (b5) || isspace (b5))) | |
630 | return RES_FORMAT_RC; | |
631 | ||
632 | /* Otherwise, we give up. */ | |
633 | fatal ("can not determine type of file `%s'; use the -I option", | |
634 | filename); | |
635 | ||
636 | /* Return something to silence the compiler warning. */ | |
637 | return RES_FORMAT_UNKNOWN; | |
638 | } | |
639 | ||
640 | /* Print a usage message and exit. */ | |
641 | ||
642 | static void | |
643 | usage (stream, status) | |
644 | FILE *stream; | |
645 | int status; | |
646 | { | |
647 | fprintf (stream, "Usage: %s [options] [input-file] [output-file]\n", | |
648 | program_name); | |
649 | fprintf (stream, "\ | |
650 | Options:\n\ | |
651 | -i FILE, --input FILE Name input file\n\ | |
652 | -o FILE, --output FILE Name output file\n\ | |
653 | -I FORMAT, --input-format FORMAT\n\ | |
654 | Specify input format\n\ | |
655 | -O FORMAT, --output-format FORMAT\n\ | |
656 | Specify output format\n\ | |
657 | -F TARGET, --target TARGET Specify COFF target\n\ | |
658 | --preprocessor PROGRAM Program to use to preprocess rc file\n\ | |
659 | --include-dir DIR Include directory when preprocessing rc file\n\ | |
660 | --define SYM[=VAL] Define SYM when preprocessing rc file\n\ | |
661 | --language VAL Set language when reading rc file\n\ | |
662 | #ifdef YYDEBUG | |
663 | --yydebug Turn on parser debugging\n\ | |
664 | #endif | |
665 | --help Print this help message\n\ | |
666 | --version Print version information\n"); | |
667 | fprintf (stream, "\ | |
668 | FORMAT is one of rc, res, or coff, and is deduced from the file name\n\ | |
669 | extension if not specified. A single file name is an input file.\n\ | |
670 | No input-file is stdin, default rc. No output-file is stdout, default rc.\n"); | |
671 | list_supported_targets (program_name, stream); | |
672 | if (status == 0) | |
673 | fprintf (stream, "Report bugs to bug-gnu-utils@prep.ai.mit.edu\n"); | |
674 | exit (status); | |
675 | } | |
676 | ||
677 | /* The main function. */ | |
678 | ||
679 | int | |
680 | main (argc, argv) | |
681 | int argc; | |
682 | char **argv; | |
683 | { | |
684 | int c; | |
685 | char *input_filename; | |
686 | char *output_filename; | |
687 | enum res_format input_format; | |
688 | enum res_format output_format; | |
689 | char *target; | |
690 | char *preprocessor; | |
691 | char *preprocargs; | |
692 | int language; | |
693 | struct res_directory *resources; | |
694 | ||
695 | program_name = argv[0]; | |
696 | xmalloc_set_program_name (program_name); | |
697 | ||
698 | bfd_init (); | |
699 | set_default_bfd_target (); | |
700 | ||
701 | input_filename = NULL; | |
702 | output_filename = NULL; | |
703 | input_format = RES_FORMAT_UNKNOWN; | |
704 | output_format = RES_FORMAT_UNKNOWN; | |
705 | target = NULL; | |
706 | preprocessor = NULL; | |
707 | preprocargs = NULL; | |
708 | language = -1; | |
709 | ||
710 | while ((c = getopt_long (argc, argv, "i:o:I:O:F:", long_options, | |
711 | (int *) 0)) != EOF) | |
712 | { | |
713 | switch (c) | |
714 | { | |
715 | case 'i': | |
716 | input_filename = optarg; | |
717 | break; | |
718 | ||
719 | case 'o': | |
720 | output_filename = optarg; | |
721 | break; | |
722 | ||
723 | case 'I': | |
724 | input_format = format_from_name (optarg); | |
725 | break; | |
726 | ||
727 | case 'O': | |
728 | output_format = format_from_name (optarg); | |
729 | break; | |
730 | ||
731 | case 'F': | |
732 | target = optarg; | |
733 | break; | |
734 | ||
735 | case OPTION_PREPROCESSOR: | |
736 | preprocessor = optarg; | |
737 | break; | |
738 | ||
739 | case OPTION_DEFINE: | |
740 | if (preprocargs == NULL) | |
741 | { | |
742 | preprocargs = xmalloc (strlen (optarg) + 3); | |
743 | sprintf (preprocargs, "-D%s", optarg); | |
744 | } | |
745 | else | |
746 | { | |
747 | char *n; | |
748 | ||
749 | n = xmalloc (strlen (preprocargs) + strlen (optarg) + 4); | |
750 | sprintf (n, "%s -D%s", preprocargs, optarg); | |
751 | free (preprocargs); | |
752 | preprocargs = n; | |
753 | } | |
754 | break; | |
755 | ||
756 | case OPTION_INCLUDE_DIR: | |
757 | if (preprocargs == NULL) | |
758 | { | |
759 | preprocargs = xmalloc (strlen (optarg) + 3); | |
760 | sprintf (preprocargs, "-I%s", optarg); | |
761 | } | |
762 | else | |
763 | { | |
764 | char *n; | |
765 | ||
766 | n = xmalloc (strlen (preprocargs) + strlen (optarg) + 4); | |
767 | sprintf (n, "%s -I%s", preprocargs, optarg); | |
768 | free (preprocargs); | |
769 | preprocargs = n; | |
770 | } | |
771 | ||
772 | { | |
773 | struct include_dir *n, **pp; | |
774 | ||
775 | n = (struct include_dir *) xmalloc (sizeof *n); | |
776 | n->next = NULL; | |
777 | n->dir = optarg; | |
778 | ||
779 | for (pp = &include_dirs; *pp != NULL; pp = &(*pp)->next) | |
780 | ; | |
781 | *pp = n; | |
782 | } | |
783 | ||
784 | break; | |
785 | ||
786 | case OPTION_LANGUAGE: | |
787 | language = strtol (optarg, (char **) NULL, 16); | |
788 | break; | |
789 | ||
790 | #ifdef YYDEBUG | |
791 | case OPTION_YYDEBUG: | |
792 | yydebug = 1; | |
793 | break; | |
794 | #endif | |
795 | ||
796 | case OPTION_HELP: | |
797 | usage (stdout, 0); | |
798 | break; | |
799 | ||
800 | case OPTION_VERSION: | |
801 | print_version ("windres"); | |
802 | break; | |
803 | ||
804 | default: | |
805 | usage (stderr, 1); | |
806 | break; | |
807 | } | |
808 | } | |
809 | ||
810 | if (input_filename == NULL && optind < argc) | |
811 | { | |
812 | input_filename = argv[optind]; | |
813 | ++optind; | |
814 | } | |
815 | ||
816 | if (output_filename == NULL && optind < argc) | |
817 | { | |
818 | output_filename = argv[optind]; | |
819 | ++optind; | |
820 | } | |
821 | ||
822 | if (argc != optind) | |
823 | usage (stderr, 1); | |
824 | ||
825 | if (input_format == RES_FORMAT_UNKNOWN) | |
826 | { | |
827 | if (input_filename == NULL) | |
828 | input_format = RES_FORMAT_RC; | |
829 | else | |
830 | input_format = format_from_filename (input_filename, 1); | |
831 | } | |
832 | ||
833 | if (output_format == RES_FORMAT_UNKNOWN) | |
834 | { | |
835 | if (output_filename == NULL) | |
836 | output_format = RES_FORMAT_RC; | |
837 | else | |
838 | output_format = format_from_filename (output_filename, 0); | |
839 | } | |
840 | ||
841 | /* Read the input file. */ | |
842 | ||
843 | switch (input_format) | |
844 | { | |
845 | default: | |
846 | abort (); | |
847 | case RES_FORMAT_RC: | |
848 | resources = read_rc_file (input_filename, preprocessor, preprocargs, | |
849 | language); | |
850 | break; | |
851 | case RES_FORMAT_RES: | |
852 | resources = read_res_file (input_filename); | |
853 | break; | |
854 | case RES_FORMAT_COFF: | |
855 | resources = read_coff_rsrc (input_filename, target); | |
856 | break; | |
857 | } | |
858 | ||
859 | /* Write the output file. */ | |
860 | ||
861 | switch (output_format) | |
862 | { | |
863 | default: | |
864 | abort (); | |
865 | case RES_FORMAT_RC: | |
866 | write_rc_file (output_filename, resources); | |
867 | break; | |
868 | case RES_FORMAT_RES: | |
869 | write_res_file (output_filename, resources); | |
870 | break; | |
871 | case RES_FORMAT_COFF: | |
872 | write_coff_file (output_filename, target, resources); | |
873 | break; | |
874 | } | |
875 | ||
876 | xexit (0); | |
877 | return 0; | |
878 | } | |
879 | ||
880 | struct res_directory * | |
881 | read_res_file (filename) | |
882 | const char *filename; | |
883 | { | |
884 | fatal ("read_res_file unimplemented"); | |
885 | return NULL; | |
886 | } | |
887 | ||
888 | struct res_directory * | |
889 | read_coff_rsrc (filename, target) | |
890 | const char *filename; | |
891 | const char *target; | |
892 | { | |
893 | fatal ("read_coff_rsrc unimplemented"); | |
894 | return NULL; | |
895 | } | |
896 | ||
897 | void | |
898 | write_res_file (filename, resources) | |
899 | const char *filename; | |
900 | const struct res_directory *resources; | |
901 | { | |
902 | fatal ("write_res_file unimplemented"); | |
903 | } | |
904 | ||
905 | void | |
906 | write_coff_file (filename, target, resources) | |
907 | const char *filename; | |
908 | const char *target; | |
909 | const struct res_directory *resources; | |
910 | { | |
911 | fatal ("write_coff_file unimplemented"); | |
912 | } |