2001-12-18 Michael Snyder <msnyder@redhat.com>
[binutils.git] / binutils / windres.c
blobf802706e36f68e8c15b13bf40720337d94550c38
1 /* windres.c -- a program to manipulate Windows resources
2 Copyright 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
3 Written by Ian Lance Taylor, Cygnus Support.
5 This file is part of GNU Binutils.
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.
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.
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. */
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.
27 It is based on information taken from the following sources:
29 * Microsoft documentation.
31 * The rcl program, written by Gunther Ebert
32 <gunther.ebert@ixos-leipzig.de>.
34 * The res2coff program, written by Pedro A. Aranda <paag@tid.es>.
38 #include "bfd.h"
39 #include "getopt.h"
40 #include "bucomm.h"
41 #include "libiberty.h"
42 #include "safe-ctype.h"
43 #include "obstack.h"
44 #include "windres.h"
46 #include <assert.h>
47 #include <time.h>
49 /* used by resrc.c at least */
51 int verbose = 0;
53 /* An enumeration of format types. */
55 enum res_format
57 /* Unknown format. */
58 RES_FORMAT_UNKNOWN,
59 /* Textual RC file. */
60 RES_FORMAT_RC,
61 /* Binary RES file. */
62 RES_FORMAT_RES,
63 /* COFF file. */
64 RES_FORMAT_COFF
67 /* A structure used to map between format types and strings. */
69 struct format_map
71 const char *name;
72 enum res_format format;
75 /* A mapping between names and format types. */
77 static const struct format_map format_names[] =
79 { "rc", RES_FORMAT_RC },
80 { "res", RES_FORMAT_RES },
81 { "coff", RES_FORMAT_COFF },
82 { NULL, RES_FORMAT_UNKNOWN }
85 /* A mapping from file extensions to format types. */
87 static const struct format_map format_fileexts[] =
89 { "rc", RES_FORMAT_RC },
90 { "res", RES_FORMAT_RES },
91 { "exe", RES_FORMAT_COFF },
92 { "obj", RES_FORMAT_COFF },
93 { "o", RES_FORMAT_COFF },
94 { NULL, RES_FORMAT_UNKNOWN }
97 /* A list of include directories. */
99 struct include_dir
101 struct include_dir *next;
102 char *dir;
105 static struct include_dir *include_dirs;
107 /* Long options. */
109 /* 150 isn't special; it's just an arbitrary non-ASCII char value. */
111 #define OPTION_DEFINE 150
112 #define OPTION_HELP (OPTION_DEFINE + 1)
113 #define OPTION_INCLUDE_DIR (OPTION_HELP + 1)
114 #define OPTION_LANGUAGE (OPTION_INCLUDE_DIR + 1)
115 #define OPTION_PREPROCESSOR (OPTION_LANGUAGE + 1)
116 #define OPTION_USE_TEMP_FILE (OPTION_PREPROCESSOR + 1)
117 #define OPTION_NO_USE_TEMP_FILE (OPTION_USE_TEMP_FILE + 1)
118 #define OPTION_VERSION (OPTION_NO_USE_TEMP_FILE + 1)
119 #define OPTION_YYDEBUG (OPTION_VERSION + 1)
121 static const struct option long_options[] =
123 {"define", required_argument, 0, OPTION_DEFINE},
124 {"help", no_argument, 0, OPTION_HELP},
125 {"include-dir", required_argument, 0, OPTION_INCLUDE_DIR},
126 {"input-format", required_argument, 0, 'I'},
127 {"language", required_argument, 0, OPTION_LANGUAGE},
128 {"output-format", required_argument, 0, 'O'},
129 {"preprocessor", required_argument, 0, OPTION_PREPROCESSOR},
130 {"target", required_argument, 0, 'F'},
131 {"use-temp-file", no_argument, 0, OPTION_USE_TEMP_FILE},
132 {"no-use-temp-file", no_argument, 0, OPTION_NO_USE_TEMP_FILE},
133 {"verbose", no_argument, 0, 'v'},
134 {"version", no_argument, 0, OPTION_VERSION},
135 {"yydebug", no_argument, 0, OPTION_YYDEBUG},
136 {0, no_argument, 0, 0}
139 /* Static functions. */
141 static void res_init PARAMS ((void));
142 static int extended_menuitems PARAMS ((const struct menuitem *));
143 static enum res_format format_from_name PARAMS ((const char *));
144 static enum res_format format_from_filename PARAMS ((const char *, int));
145 static void usage PARAMS ((FILE *, int));
146 static int cmp_res_entry PARAMS ((const PTR, const PTR));
147 static struct res_directory *sort_resources PARAMS ((struct res_directory *));
149 /* When we are building a resource tree, we allocate everything onto
150 an obstack, so that we can free it all at once if we want. */
152 #define obstack_chunk_alloc xmalloc
153 #define obstack_chunk_free free
155 /* The resource building obstack. */
157 static struct obstack res_obstack;
159 /* Initialize the resource building obstack. */
161 static void
162 res_init ()
164 obstack_init (&res_obstack);
167 /* Allocate space on the resource building obstack. */
170 res_alloc (bytes)
171 size_t bytes;
173 return (PTR) obstack_alloc (&res_obstack, bytes);
176 /* We also use an obstack to save memory used while writing out a set
177 of resources. */
179 static struct obstack reswr_obstack;
181 /* Initialize the resource writing obstack. */
183 static void
184 reswr_init ()
186 obstack_init (&reswr_obstack);
189 /* Allocate space on the resource writing obstack. */
192 reswr_alloc (bytes)
193 size_t bytes;
195 return (PTR) obstack_alloc (&reswr_obstack, bytes);
198 /* Open a file using the include directory search list. */
200 FILE *
201 open_file_search (filename, mode, errmsg, real_filename)
202 const char *filename;
203 const char *mode;
204 const char *errmsg;
205 char **real_filename;
207 FILE *e;
208 struct include_dir *d;
210 e = fopen (filename, mode);
211 if (e != NULL)
213 *real_filename = xstrdup (filename);
214 return e;
217 if (errno == ENOENT)
219 for (d = include_dirs; d != NULL; d = d->next)
221 char *n;
223 n = (char *) xmalloc (strlen (d->dir) + strlen (filename) + 2);
224 sprintf (n, "%s/%s", d->dir, filename);
225 e = fopen (n, mode);
226 if (e != NULL)
228 *real_filename = n;
229 return e;
232 if (errno != ENOENT)
233 break;
237 fatal (_("can't open %s `%s': %s"), errmsg, filename, strerror (errno));
239 /* Return a value to avoid a compiler warning. */
240 return NULL;
243 /* Compare two resource ID's. We consider name entries to come before
244 numeric entries, because that is how they appear in the COFF .rsrc
245 section. */
248 res_id_cmp (a, b)
249 struct res_id a;
250 struct res_id b;
252 if (! a.named)
254 if (b.named)
255 return 1;
256 if (a.u.id > b.u.id)
257 return 1;
258 else if (a.u.id < b.u.id)
259 return -1;
260 else
261 return 0;
263 else
265 unichar *as, *ase, *bs, *bse;
267 if (! b.named)
268 return -1;
270 as = a.u.n.name;
271 ase = as + a.u.n.length;
272 bs = b.u.n.name;
273 bse = bs + b.u.n.length;
275 while (as < ase)
277 int i;
279 if (bs >= bse)
280 return 1;
281 i = (int) *as - (int) *bs;
282 if (i != 0)
283 return i;
284 ++as;
285 ++bs;
288 if (bs < bse)
289 return -1;
291 return 0;
295 /* Print a resource ID. */
297 void
298 res_id_print (stream, id, quote)
299 FILE *stream;
300 struct res_id id;
301 int quote;
303 if (! id.named)
304 fprintf (stream, "%lu", id.u.id);
305 else
307 if (quote)
308 putc ('"', stream);
309 unicode_print (stream, id.u.n.name, id.u.n.length);
310 if (quote)
311 putc ('"', stream);
315 /* Print a list of resource ID's. */
317 void
318 res_ids_print (stream, cids, ids)
319 FILE *stream;
320 int cids;
321 const struct res_id *ids;
323 int i;
325 for (i = 0; i < cids; i++)
327 res_id_print (stream, ids[i], 1);
328 if (i + 1 < cids)
329 fprintf (stream, ": ");
333 /* Convert an ASCII string to a resource ID. */
335 void
336 res_string_to_id (res_id, string)
337 struct res_id *res_id;
338 const char *string;
340 res_id->named = 1;
341 unicode_from_ascii (&res_id->u.n.length, &res_id->u.n.name, string);
344 /* Define a resource. The arguments are the resource tree, RESOURCES,
345 and the location at which to put it in the tree, CIDS and IDS.
346 This returns a newly allocated res_resource structure, which the
347 caller is expected to initialize. If DUPOK is non-zero, then if a
348 resource with this ID exists, it is returned. Otherwise, a warning
349 is issued, and a new resource is created replacing the existing
350 one. */
352 struct res_resource *
353 define_resource (resources, cids, ids, dupok)
354 struct res_directory **resources;
355 int cids;
356 const struct res_id *ids;
357 int dupok;
359 struct res_entry *re = NULL;
360 int i;
362 assert (cids > 0);
363 for (i = 0; i < cids; i++)
365 struct res_entry **pp;
367 if (*resources == NULL)
369 static unsigned long timeval;
371 /* Use the same timestamp for every resource created in a
372 single run. */
373 if (timeval == 0)
374 timeval = time (NULL);
376 *resources = ((struct res_directory *)
377 res_alloc (sizeof **resources));
378 (*resources)->characteristics = 0;
379 (*resources)->time = timeval;
380 (*resources)->major = 0;
381 (*resources)->minor = 0;
382 (*resources)->entries = NULL;
385 for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
386 if (res_id_cmp ((*pp)->id, ids[i]) == 0)
387 break;
389 if (*pp != NULL)
390 re = *pp;
391 else
393 re = (struct res_entry *) res_alloc (sizeof *re);
394 re->next = NULL;
395 re->id = ids[i];
396 if ((i + 1) < cids)
398 re->subdir = 1;
399 re->u.dir = NULL;
401 else
403 re->subdir = 0;
404 re->u.res = NULL;
407 *pp = re;
410 if ((i + 1) < cids)
412 if (! re->subdir)
414 fprintf (stderr, "%s: ", program_name);
415 res_ids_print (stderr, i, ids);
416 fprintf (stderr, _(": expected to be a directory\n"));
417 xexit (1);
420 resources = &re->u.dir;
424 if (re->subdir)
426 fprintf (stderr, "%s: ", program_name);
427 res_ids_print (stderr, cids, ids);
428 fprintf (stderr, _(": expected to be a leaf\n"));
429 xexit (1);
432 if (re->u.res != NULL)
434 if (dupok)
435 return re->u.res;
437 fprintf (stderr, _("%s: warning: "), program_name);
438 res_ids_print (stderr, cids, ids);
439 fprintf (stderr, _(": duplicate value\n"));
442 re->u.res = ((struct res_resource *)
443 res_alloc (sizeof (struct res_resource)));
445 re->u.res->type = RES_TYPE_UNINITIALIZED;
446 memset (&re->u.res->res_info, 0, sizeof (struct res_res_info));
447 memset (&re->u.res->coff_info, 0, sizeof (struct res_coff_info));
449 return re->u.res;
452 /* Define a standard resource. This is a version of define_resource
453 that just takes type, name, and language arguments. */
455 struct res_resource *
456 define_standard_resource (resources, type, name, language, dupok)
457 struct res_directory **resources;
458 int type;
459 struct res_id name;
460 int language;
461 int dupok;
463 struct res_id a[3];
465 a[0].named = 0;
466 a[0].u.id = type;
467 a[1] = name;
468 a[2].named = 0;
469 a[2].u.id = language;
470 return define_resource (resources, 3, a, dupok);
473 /* Comparison routine for resource sorting. */
475 static int
476 cmp_res_entry (p1, p2)
477 const PTR p1;
478 const PTR p2;
480 const struct res_entry **re1, **re2;
482 re1 = (const struct res_entry **) p1;
483 re2 = (const struct res_entry **) p2;
484 return res_id_cmp ((*re1)->id, (*re2)->id);
487 /* Sort the resources. */
489 static struct res_directory *
490 sort_resources (resdir)
491 struct res_directory *resdir;
493 int c, i;
494 struct res_entry *re;
495 struct res_entry **a;
497 if (resdir->entries == NULL)
498 return resdir;
500 c = 0;
501 for (re = resdir->entries; re != NULL; re = re->next)
502 ++c;
504 /* This is a recursive routine, so using xmalloc is probably better
505 than alloca. */
506 a = (struct res_entry **) xmalloc (c * sizeof (struct res_entry *));
508 for (i = 0, re = resdir->entries; re != NULL; re = re->next, i++)
509 a[i] = re;
511 qsort (a, c, sizeof (struct res_entry *), cmp_res_entry);
513 resdir->entries = a[0];
514 for (i = 0; i < c - 1; i++)
515 a[i]->next = a[i + 1];
516 a[i]->next = NULL;
518 free (a);
520 /* Now sort the subdirectories. */
522 for (re = resdir->entries; re != NULL; re = re->next)
523 if (re->subdir)
524 re->u.dir = sort_resources (re->u.dir);
526 return resdir;
529 /* Return whether the dialog resource DIALOG is a DIALOG or a
530 DIALOGEX. */
533 extended_dialog (dialog)
534 const struct dialog *dialog;
536 const struct dialog_control *c;
538 if (dialog->ex != NULL)
539 return 1;
541 for (c = dialog->controls; c != NULL; c = c->next)
542 if (c->data != NULL || c->help != 0)
543 return 1;
545 return 0;
548 /* Return whether MENUITEMS are a MENU or a MENUEX. */
551 extended_menu (menu)
552 const struct menu *menu;
554 return extended_menuitems (menu->items);
557 static int
558 extended_menuitems (menuitems)
559 const struct menuitem *menuitems;
561 const struct menuitem *mi;
563 for (mi = menuitems; mi != NULL; mi = mi->next)
565 if (mi->help != 0 || mi->state != 0)
566 return 1;
567 if (mi->popup != NULL && mi->id != 0)
568 return 1;
569 if ((mi->type
570 & ~ (MENUITEM_CHECKED
571 | MENUITEM_GRAYED
572 | MENUITEM_HELP
573 | MENUITEM_INACTIVE
574 | MENUITEM_MENUBARBREAK
575 | MENUITEM_MENUBREAK))
576 != 0)
577 return 1;
578 if (mi->popup != NULL)
580 if (extended_menuitems (mi->popup))
581 return 1;
585 return 0;
588 /* Convert a string to a format type, or exit if it can't be done. */
590 static enum res_format
591 format_from_name (name)
592 const char *name;
594 const struct format_map *m;
596 for (m = format_names; m->name != NULL; m++)
597 if (strcasecmp (m->name, name) == 0)
598 break;
600 if (m->name == NULL)
602 non_fatal (_("unknown format type `%s'"), name);
603 fprintf (stderr, _("%s: supported formats:"), program_name);
604 for (m = format_names; m->name != NULL; m++)
605 fprintf (stderr, " %s", m->name);
606 fprintf (stderr, "\n");
607 xexit (1);
610 return m->format;
613 /* Work out a format type given a file name. If INPUT is non-zero,
614 it's OK to look at the file itself. */
616 static enum res_format
617 format_from_filename (filename, input)
618 const char *filename;
619 int input;
621 const char *ext;
622 FILE *e;
623 unsigned char b1, b2, b3, b4, b5;
624 int magic;
626 /* If we have an extension, see if we recognize it as implying a
627 particular format. */
628 ext = strrchr (filename, '.');
629 if (ext != NULL)
631 const struct format_map *m;
633 ++ext;
634 for (m = format_fileexts; m->name != NULL; m++)
635 if (strcasecmp (m->name, ext) == 0)
636 return m->format;
639 /* If we don't recognize the name of an output file, assume it's a
640 COFF file. */
642 if (! input)
643 return RES_FORMAT_COFF;
645 /* Read the first few bytes of the file to see if we can guess what
646 it is. */
648 e = fopen (filename, FOPEN_RB);
649 if (e == NULL)
650 fatal ("%s: %s", filename, strerror (errno));
652 b1 = getc (e);
653 b2 = getc (e);
654 b3 = getc (e);
655 b4 = getc (e);
656 b5 = getc (e);
658 fclose (e);
660 /* A PE executable starts with 0x4d 0x5a. */
661 if (b1 == 0x4d && b2 == 0x5a)
662 return RES_FORMAT_COFF;
664 /* A COFF .o file starts with a COFF magic number. */
665 magic = (b2 << 8) | b1;
666 switch (magic)
668 case 0x14c: /* i386 */
669 case 0x166: /* MIPS */
670 case 0x184: /* Alpha */
671 case 0x268: /* 68k */
672 case 0x1f0: /* PowerPC */
673 case 0x290: /* PA */
674 return RES_FORMAT_COFF;
677 /* A RES file starts with 0x0 0x0 0x0 0x0 0x20 0x0 0x0 0x0. */
678 if (b1 == 0 && b2 == 0 && b3 == 0 && b4 == 0 && b5 == 0x20)
679 return RES_FORMAT_RES;
681 /* If every character is printable or space, assume it's an RC file. */
682 if ((ISPRINT (b1) || ISSPACE (b1))
683 && (ISPRINT (b2) || ISSPACE (b2))
684 && (ISPRINT (b3) || ISSPACE (b3))
685 && (ISPRINT (b4) || ISSPACE (b4))
686 && (ISPRINT (b5) || ISSPACE (b5)))
687 return RES_FORMAT_RC;
689 /* Otherwise, we give up. */
690 fatal (_("can not determine type of file `%s'; use the -I option"),
691 filename);
693 /* Return something to silence the compiler warning. */
694 return RES_FORMAT_UNKNOWN;
697 /* Print a usage message and exit. */
699 static void
700 usage (stream, status)
701 FILE *stream;
702 int status;
704 fprintf (stream, _("Usage: %s [options] [input-file] [output-file]\n"),
705 program_name);
706 fprintf (stream, _("\
707 Options:\n\
708 -i FILE, --input FILE Name input file\n\
709 -o FILE, --output FILE Name output file\n\
710 -I FORMAT, --input-format FORMAT\n\
711 Specify input format\n\
712 -O FORMAT, --output-format FORMAT\n\
713 Specify output format\n\
714 -F TARGET, --target TARGET Specify COFF target\n\
715 --preprocessor PROGRAM Program to use to preprocess rc file\n\
716 --include-dir DIR Include directory when preprocessing rc file\n\
717 -DSYM[=VAL], --define SYM[=VAL]\n\
718 Define SYM when preprocessing rc file\n\
719 -v Verbose - tells you what it's doing\n\
720 --language VAL Set language when reading rc file\n\
721 --use-temp-file Use a temporary file instead of popen to read\n\
722 the preprocessor output\n\
723 --no-use-temp-file Use popen (default)\n"));
724 #ifdef YYDEBUG
725 fprintf (stream, _("\
726 --yydebug Turn on parser debugging\n"));
727 #endif
728 fprintf (stream, _("\
729 --help Print this help message\n\
730 --version Print version information\n"));
731 fprintf (stream, _("\
732 FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
733 extension if not specified. A single file name is an input file.\n\
734 No input-file is stdin, default rc. No output-file is stdout, default rc.\n"));
735 list_supported_targets (program_name, stream);
736 if (status == 0)
737 fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO);
738 exit (status);
741 /* Quote characters that will confuse the shell when we run the preprocessor */
742 static const char *quot (string)
743 const char *string;
745 static char *buf = 0;
746 static int buflen = 0;
747 int slen = strlen (string);
748 const char *src;
749 char *dest;
751 if ((buflen < slen * 2 + 2) || !buf)
753 buflen = slen * 2 + 2;
754 if (buf)
755 free (buf);
756 buf = (char *) xmalloc (buflen);
759 for (src=string, dest=buf; *src; src++, dest++)
761 if (*src == '(' || *src == ')' || *src == ' ')
762 *dest++ = '\\';
763 *dest = *src;
765 *dest = 0;
766 return buf;
769 /* The main function. */
772 main (argc, argv)
773 int argc;
774 char **argv;
776 int c;
777 char *input_filename;
778 char *output_filename;
779 enum res_format input_format;
780 enum res_format output_format;
781 char *target;
782 char *preprocessor;
783 char *preprocargs;
784 const char *quotedarg;
785 int language;
786 struct res_directory *resources;
787 int use_temp_file;
789 #if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
790 setlocale (LC_MESSAGES, "");
791 #endif
792 #if defined (HAVE_SETLOCALE)
793 setlocale (LC_CTYPE, "");
794 #endif
795 bindtextdomain (PACKAGE, LOCALEDIR);
796 textdomain (PACKAGE);
798 program_name = argv[0];
799 xmalloc_set_program_name (program_name);
801 bfd_init ();
802 set_default_bfd_target ();
804 res_init ();
806 input_filename = NULL;
807 output_filename = NULL;
808 input_format = RES_FORMAT_UNKNOWN;
809 output_format = RES_FORMAT_UNKNOWN;
810 target = NULL;
811 preprocessor = NULL;
812 preprocargs = NULL;
813 language = -1;
814 use_temp_file = 0;
816 while ((c = getopt_long (argc, argv, "i:o:I:O:F:D:v", long_options,
817 (int *) 0)) != EOF)
819 switch (c)
821 case 'i':
822 input_filename = optarg;
823 break;
825 case 'o':
826 output_filename = optarg;
827 break;
829 case 'I':
830 input_format = format_from_name (optarg);
831 break;
833 case 'O':
834 output_format = format_from_name (optarg);
835 break;
837 case 'F':
838 target = optarg;
839 break;
841 case OPTION_PREPROCESSOR:
842 preprocessor = optarg;
843 break;
845 case 'D':
846 case OPTION_DEFINE:
847 if (preprocargs == NULL)
849 quotedarg = quot (optarg);
850 preprocargs = xmalloc (strlen (quotedarg) + 3);
851 sprintf (preprocargs, "-D%s", quotedarg);
853 else
855 char *n;
857 quotedarg = quot (optarg);
858 n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
859 sprintf (n, "%s -D%s", preprocargs, quotedarg);
860 free (preprocargs);
861 preprocargs = n;
863 break;
865 case 'v':
866 verbose ++;
867 break;
869 case OPTION_INCLUDE_DIR:
870 if (preprocargs == NULL)
872 quotedarg = quot (optarg);
873 preprocargs = xmalloc (strlen (quotedarg) + 3);
874 sprintf (preprocargs, "-I%s", quotedarg);
876 else
878 char *n;
880 quotedarg = quot (optarg);
881 n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
882 sprintf (n, "%s -I%s", preprocargs, quotedarg);
883 free (preprocargs);
884 preprocargs = n;
888 struct include_dir *n, **pp;
890 n = (struct include_dir *) xmalloc (sizeof *n);
891 n->next = NULL;
892 n->dir = optarg;
894 for (pp = &include_dirs; *pp != NULL; pp = &(*pp)->next)
896 *pp = n;
899 break;
901 case OPTION_LANGUAGE:
902 language = strtol (optarg, (char **) NULL, 16);
903 break;
905 case OPTION_USE_TEMP_FILE:
906 use_temp_file = 1;
907 break;
909 case OPTION_NO_USE_TEMP_FILE:
910 use_temp_file = 0;
911 break;
913 #ifdef YYDEBUG
914 case OPTION_YYDEBUG:
915 yydebug = 1;
916 break;
917 #endif
919 case OPTION_HELP:
920 usage (stdout, 0);
921 break;
923 case OPTION_VERSION:
924 print_version ("windres");
925 break;
927 default:
928 usage (stderr, 1);
929 break;
933 if (input_filename == NULL && optind < argc)
935 input_filename = argv[optind];
936 ++optind;
939 if (output_filename == NULL && optind < argc)
941 output_filename = argv[optind];
942 ++optind;
945 if (argc != optind)
946 usage (stderr, 1);
948 if (input_format == RES_FORMAT_UNKNOWN)
950 if (input_filename == NULL)
951 input_format = RES_FORMAT_RC;
952 else
953 input_format = format_from_filename (input_filename, 1);
956 if (output_format == RES_FORMAT_UNKNOWN)
958 if (output_filename == NULL)
959 output_format = RES_FORMAT_RC;
960 else
961 output_format = format_from_filename (output_filename, 0);
964 /* Read the input file. */
966 switch (input_format)
968 default:
969 abort ();
970 case RES_FORMAT_RC:
971 resources = read_rc_file (input_filename, preprocessor, preprocargs,
972 language, use_temp_file);
973 break;
974 case RES_FORMAT_RES:
975 resources = read_res_file (input_filename);
976 break;
977 case RES_FORMAT_COFF:
978 resources = read_coff_rsrc (input_filename, target);
979 break;
982 if (resources == NULL)
983 fatal (_("no resources"));
985 /* Sort the resources. This is required for COFF, convenient for
986 rc, and unimportant for res. */
988 resources = sort_resources (resources);
990 /* Write the output file. */
992 reswr_init ();
994 switch (output_format)
996 default:
997 abort ();
998 case RES_FORMAT_RC:
999 write_rc_file (output_filename, resources);
1000 break;
1001 case RES_FORMAT_RES:
1002 write_res_file (output_filename, resources);
1003 break;
1004 case RES_FORMAT_COFF:
1005 write_coff_file (output_filename, target, resources);
1006 break;
1009 xexit (0);
1010 return 0;