1999-09-11 Donn Terry <donn@interix.com>
[binutils.git] / binutils / windres.c
blob0ccd909404c45ee5802ad15c9560d8021bc1fabc
1 /* windres.c -- a program to manipulate Windows resources
2 Copyright 1997, 1998, 1999 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 "obstack.h"
43 #include "windres.h"
45 #include <assert.h>
46 #include <ctype.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_VERSION (OPTION_PREPROCESSOR + 1)
117 #define OPTION_YYDEBUG (OPTION_VERSION + 1)
119 static const struct option long_options[] =
121 {"define", required_argument, 0, OPTION_DEFINE},
122 {"help", no_argument, 0, OPTION_HELP},
123 {"include-dir", required_argument, 0, OPTION_INCLUDE_DIR},
124 {"input-format", required_argument, 0, 'I'},
125 {"language", required_argument, 0, OPTION_LANGUAGE},
126 {"output-format", required_argument, 0, 'O'},
127 {"preprocessor", required_argument, 0, OPTION_PREPROCESSOR},
128 {"target", required_argument, 0, 'F'},
129 {"verbose", no_argument, 0, 'v'},
130 {"version", no_argument, 0, OPTION_VERSION},
131 {"yydebug", no_argument, 0, OPTION_YYDEBUG},
132 {0, no_argument, 0, 0}
135 /* Static functions. */
137 static void res_init PARAMS ((void));
138 static int extended_menuitems PARAMS ((const struct menuitem *));
139 static enum res_format format_from_name PARAMS ((const char *));
140 static enum res_format format_from_filename PARAMS ((const char *, int));
141 static void usage PARAMS ((FILE *, int));
142 static int cmp_res_entry PARAMS ((const PTR, const PTR));
143 static struct res_directory *sort_resources PARAMS ((struct res_directory *));
145 /* When we are building a resource tree, we allocate everything onto
146 an obstack, so that we can free it all at once if we want. */
148 #define obstack_chunk_alloc xmalloc
149 #define obstack_chunk_free free
151 /* The resource building obstack. */
153 static struct obstack res_obstack;
155 /* Initialize the resource building obstack. */
157 static void
158 res_init ()
160 obstack_init (&res_obstack);
163 /* Allocate space on the resource building obstack. */
166 res_alloc (bytes)
167 size_t bytes;
169 return (PTR) obstack_alloc (&res_obstack, bytes);
172 /* We also use an obstack to save memory used while writing out a set
173 of resources. */
175 static struct obstack reswr_obstack;
177 /* Initialize the resource writing obstack. */
179 static void
180 reswr_init ()
182 obstack_init (&reswr_obstack);
185 /* Allocate space on the resource writing obstack. */
188 reswr_alloc (bytes)
189 size_t bytes;
191 return (PTR) obstack_alloc (&reswr_obstack, bytes);
194 /* Open a file using the include directory search list. */
196 FILE *
197 open_file_search (filename, mode, errmsg, real_filename)
198 const char *filename;
199 const char *mode;
200 const char *errmsg;
201 char **real_filename;
203 FILE *e;
204 struct include_dir *d;
206 e = fopen (filename, mode);
207 if (e != NULL)
209 *real_filename = xstrdup (filename);
210 return e;
213 if (errno == ENOENT)
215 for (d = include_dirs; d != NULL; d = d->next)
217 char *n;
219 n = (char *) xmalloc (strlen (d->dir) + strlen (filename) + 2);
220 sprintf (n, "%s/%s", d->dir, filename);
221 e = fopen (n, mode);
222 if (e != NULL)
224 *real_filename = n;
225 return e;
228 if (errno != ENOENT)
229 break;
233 fatal (_("can't open %s `%s': %s"), errmsg, filename, strerror (errno));
235 /* Return a value to avoid a compiler warning. */
236 return NULL;
239 /* Compare two resource ID's. We consider name entries to come before
240 numeric entries, because that is how they appear in the COFF .rsrc
241 section. */
244 res_id_cmp (a, b)
245 struct res_id a;
246 struct res_id b;
248 if (! a.named)
250 if (b.named)
251 return 1;
252 if (a.u.id > b.u.id)
253 return 1;
254 else if (a.u.id < b.u.id)
255 return -1;
256 else
257 return 0;
259 else
261 unichar *as, *ase, *bs, *bse;
263 if (! b.named)
264 return -1;
266 as = a.u.n.name;
267 ase = as + a.u.n.length;
268 bs = b.u.n.name;
269 bse = bs + b.u.n.length;
271 while (as < ase)
273 int i;
275 if (bs >= bse)
276 return 1;
277 i = (int) *as - (int) *bs;
278 if (i != 0)
279 return i;
280 ++as;
281 ++bs;
284 if (bs < bse)
285 return -1;
287 return 0;
291 /* Print a resource ID. */
293 void
294 res_id_print (stream, id, quote)
295 FILE *stream;
296 struct res_id id;
297 int quote;
299 if (! id.named)
300 fprintf (stream, "%lu", id.u.id);
301 else
303 if (quote)
304 putc ('"', stream);
305 unicode_print (stream, id.u.n.name, id.u.n.length);
306 if (quote)
307 putc ('"', stream);
311 /* Print a list of resource ID's. */
313 void
314 res_ids_print (stream, cids, ids)
315 FILE *stream;
316 int cids;
317 const struct res_id *ids;
319 int i;
321 for (i = 0; i < cids; i++)
323 res_id_print (stream, ids[i], 1);
324 if (i + 1 < cids)
325 fprintf (stream, ": ");
329 /* Convert an ASCII string to a resource ID. */
331 void
332 res_string_to_id (res_id, string)
333 struct res_id *res_id;
334 const char *string;
336 res_id->named = 1;
337 unicode_from_ascii (&res_id->u.n.length, &res_id->u.n.name, string);
340 /* Define a resource. The arguments are the resource tree, RESOURCES,
341 and the location at which to put it in the tree, CIDS and IDS.
342 This returns a newly allocated res_resource structure, which the
343 caller is expected to initialize. If DUPOK is non-zero, then if a
344 resource with this ID exists, it is returned. Otherwise, a warning
345 is issued, and a new resource is created replacing the existing
346 one. */
348 struct res_resource *
349 define_resource (resources, cids, ids, dupok)
350 struct res_directory **resources;
351 int cids;
352 const struct res_id *ids;
353 int dupok;
355 struct res_entry *re = NULL;
356 int i;
358 assert (cids > 0);
359 for (i = 0; i < cids; i++)
361 struct res_entry **pp;
363 if (*resources == NULL)
365 static unsigned long timeval;
367 /* Use the same timestamp for every resource created in a
368 single run. */
369 if (timeval == 0)
370 timeval = time (NULL);
372 *resources = ((struct res_directory *)
373 res_alloc (sizeof **resources));
374 (*resources)->characteristics = 0;
375 (*resources)->time = timeval;
376 (*resources)->major = 0;
377 (*resources)->minor = 0;
378 (*resources)->entries = NULL;
381 for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
382 if (res_id_cmp ((*pp)->id, ids[i]) == 0)
383 break;
385 if (*pp != NULL)
386 re = *pp;
387 else
389 re = (struct res_entry *) res_alloc (sizeof *re);
390 re->next = NULL;
391 re->id = ids[i];
392 if ((i + 1) < cids)
394 re->subdir = 1;
395 re->u.dir = NULL;
397 else
399 re->subdir = 0;
400 re->u.res = NULL;
403 *pp = re;
406 if ((i + 1) < cids)
408 if (! re->subdir)
410 fprintf (stderr, "%s: ", program_name);
411 res_ids_print (stderr, i, ids);
412 fprintf (stderr, _(": expected to be a directory\n"));
413 xexit (1);
416 resources = &re->u.dir;
420 if (re->subdir)
422 fprintf (stderr, "%s: ", program_name);
423 res_ids_print (stderr, cids, ids);
424 fprintf (stderr, _(": expected to be a leaf\n"));
425 xexit (1);
428 if (re->u.res != NULL)
430 if (dupok)
431 return re->u.res;
433 fprintf (stderr, _("%s: warning: "), program_name);
434 res_ids_print (stderr, cids, ids);
435 fprintf (stderr, _(": duplicate value\n"));
438 re->u.res = ((struct res_resource *)
439 res_alloc (sizeof (struct res_resource)));
441 re->u.res->type = RES_TYPE_UNINITIALIZED;
442 memset (&re->u.res->res_info, 0, sizeof (struct res_res_info));
443 memset (&re->u.res->coff_info, 0, sizeof (struct res_coff_info));
445 return re->u.res;
448 /* Define a standard resource. This is a version of define_resource
449 that just takes type, name, and language arguments. */
451 struct res_resource *
452 define_standard_resource (resources, type, name, language, dupok)
453 struct res_directory **resources;
454 int type;
455 struct res_id name;
456 int language;
457 int dupok;
459 struct res_id a[3];
461 a[0].named = 0;
462 a[0].u.id = type;
463 a[1] = name;
464 a[2].named = 0;
465 a[2].u.id = language;
466 return define_resource (resources, 3, a, dupok);
469 /* Comparison routine for resource sorting. */
471 static int
472 cmp_res_entry (p1, p2)
473 const PTR p1;
474 const PTR p2;
476 const struct res_entry **re1, **re2;
478 re1 = (const struct res_entry **) p1;
479 re2 = (const struct res_entry **) p2;
480 return res_id_cmp ((*re1)->id, (*re2)->id);
483 /* Sort the resources. */
485 static struct res_directory *
486 sort_resources (resdir)
487 struct res_directory *resdir;
489 int c, i;
490 struct res_entry *re;
491 struct res_entry **a;
493 if (resdir->entries == NULL)
494 return resdir;
496 c = 0;
497 for (re = resdir->entries; re != NULL; re = re->next)
498 ++c;
500 /* This is a recursive routine, so using xmalloc is probably better
501 than alloca. */
502 a = (struct res_entry **) xmalloc (c * sizeof (struct res_entry *));
504 for (i = 0, re = resdir->entries; re != NULL; re = re->next, i++)
505 a[i] = re;
507 qsort (a, c, sizeof (struct res_entry *), cmp_res_entry);
509 resdir->entries = a[0];
510 for (i = 0; i < c - 1; i++)
511 a[i]->next = a[i + 1];
512 a[i]->next = NULL;
514 free (a);
516 /* Now sort the subdirectories. */
518 for (re = resdir->entries; re != NULL; re = re->next)
519 if (re->subdir)
520 re->u.dir = sort_resources (re->u.dir);
522 return resdir;
525 /* Return whether the dialog resource DIALOG is a DIALOG or a
526 DIALOGEX. */
529 extended_dialog (dialog)
530 const struct dialog *dialog;
532 const struct dialog_control *c;
534 if (dialog->ex != NULL)
535 return 1;
537 for (c = dialog->controls; c != NULL; c = c->next)
538 if (c->data != NULL || c->help != 0)
539 return 1;
541 return 0;
544 /* Return whether MENUITEMS are a MENU or a MENUEX. */
547 extended_menu (menu)
548 const struct menu *menu;
550 return extended_menuitems (menu->items);
553 static int
554 extended_menuitems (menuitems)
555 const struct menuitem *menuitems;
557 const struct menuitem *mi;
559 for (mi = menuitems; mi != NULL; mi = mi->next)
561 if (mi->help != 0 || mi->state != 0)
562 return 1;
563 if (mi->popup != NULL && mi->id != 0)
564 return 1;
565 if ((mi->type
566 & ~ (MENUITEM_CHECKED
567 | MENUITEM_GRAYED
568 | MENUITEM_HELP
569 | MENUITEM_INACTIVE
570 | MENUITEM_MENUBARBREAK
571 | MENUITEM_MENUBREAK))
572 != 0)
573 return 1;
574 if (mi->popup != NULL)
576 if (extended_menuitems (mi->popup))
577 return 1;
581 return 0;
584 /* Convert a string to a format type, or exit if it can't be done. */
586 static enum res_format
587 format_from_name (name)
588 const char *name;
590 const struct format_map *m;
592 for (m = format_names; m->name != NULL; m++)
593 if (strcasecmp (m->name, name) == 0)
594 break;
596 if (m->name == NULL)
598 fprintf (stderr, _("%s: unknown format type `%s'\n"), program_name, name);
599 fprintf (stderr, _("%s: supported formats:"), program_name);
600 for (m = format_names; m->name != NULL; m++)
601 fprintf (stderr, " %s", m->name);
602 fprintf (stderr, "\n");
603 xexit (1);
606 return m->format;
609 /* Work out a format type given a file name. If INPUT is non-zero,
610 it's OK to look at the file itself. */
612 static enum res_format
613 format_from_filename (filename, input)
614 const char *filename;
615 int input;
617 const char *ext;
618 FILE *e;
619 unsigned char b1, b2, b3, b4, b5;
620 int magic;
622 /* If we have an extension, see if we recognize it as implying a
623 particular format. */
624 ext = strrchr (filename, '.');
625 if (ext != NULL)
627 const struct format_map *m;
629 ++ext;
630 for (m = format_fileexts; m->name != NULL; m++)
631 if (strcasecmp (m->name, ext) == 0)
632 return m->format;
635 /* If we don't recognize the name of an output file, assume it's a
636 COFF file. */
638 if (! input)
639 return RES_FORMAT_COFF;
641 /* Read the first few bytes of the file to see if we can guess what
642 it is. */
644 e = fopen (filename, FOPEN_RB);
645 if (e == NULL)
646 fatal ("%s: %s", filename, strerror (errno));
648 b1 = getc (e);
649 b2 = getc (e);
650 b3 = getc (e);
651 b4 = getc (e);
652 b5 = getc (e);
654 fclose (e);
656 /* A PE executable starts with 0x4d 0x5a. */
657 if (b1 == 0x4d && b2 == 0x5a)
658 return RES_FORMAT_COFF;
660 /* A COFF .o file starts with a COFF magic number. */
661 magic = (b2 << 8) | b1;
662 switch (magic)
664 case 0x14c: /* i386 */
665 case 0x166: /* MIPS */
666 case 0x184: /* Alpha */
667 case 0x268: /* 68k */
668 case 0x1f0: /* PowerPC */
669 case 0x290: /* PA */
670 return RES_FORMAT_COFF;
673 /* A RES file starts with 0x0 0x0 0x0 0x0 0x20 0x0 0x0 0x0. */
674 if (b1 == 0 && b2 == 0 && b3 == 0 && b4 == 0 && b5 == 0x20)
675 return RES_FORMAT_RES;
677 /* If every character is printable or space, assume it's an RC file. */
678 if ((isprint (b1) || isspace (b1))
679 && (isprint (b2) || isspace (b2))
680 && (isprint (b3) || isspace (b3))
681 && (isprint (b4) || isspace (b4))
682 && (isprint (b5) || isspace (b5)))
683 return RES_FORMAT_RC;
685 /* Otherwise, we give up. */
686 fatal (_("can not determine type of file `%s'; use the -I option"),
687 filename);
689 /* Return something to silence the compiler warning. */
690 return RES_FORMAT_UNKNOWN;
693 /* Print a usage message and exit. */
695 static void
696 usage (stream, status)
697 FILE *stream;
698 int status;
700 fprintf (stream, _("Usage: %s [options] [input-file] [output-file]\n"),
701 program_name);
702 fprintf (stream, _("\
703 Options:\n\
704 -i FILE, --input FILE Name input file\n\
705 -o FILE, --output FILE Name output file\n\
706 -I FORMAT, --input-format FORMAT\n\
707 Specify input format\n\
708 -O FORMAT, --output-format FORMAT\n\
709 Specify output format\n\
710 -F TARGET, --target TARGET Specify COFF target\n\
711 --preprocessor PROGRAM Program to use to preprocess rc file\n\
712 --include-dir DIR Include directory when preprocessing rc file\n\
713 -DSYM[=VAL], --define SYM[=VAL]\n\
714 Define SYM when preprocessing rc file\n\
715 -v Verbose - tells you what it's doing\n\
716 --language VAL Set language when reading rc file\n"));
717 #ifdef YYDEBUG
718 fprintf (stream, _("\
719 --yydebug Turn on parser debugging\n"));
720 #endif
721 fprintf (stream, _("\
722 --help Print this help message\n\
723 --version Print version information\n"));
724 fprintf (stream, _("\
725 FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
726 extension if not specified. A single file name is an input file.\n\
727 No input-file is stdin, default rc. No output-file is stdout, default rc.\n"));
728 list_supported_targets (program_name, stream);
729 if (status == 0)
730 fprintf (stream, _("Report bugs to bug-gnu-utils@gnu.org\n"));
731 exit (status);
734 /* Quote characters that will confuse the shell when we run the preprocessor */
735 static const char *quot (string)
736 const char *string;
738 static char *buf = 0;
739 static int buflen = 0;
740 int slen = strlen (string);
741 const char *src;
742 char *dest;
744 if ((buflen < slen * 2 + 2) || !buf)
746 buflen = slen * 2 + 2;
747 if (buf)
748 free (buf);
749 buf = (char *) xmalloc (buflen);
752 for (src=string, dest=buf; *src; src++, dest++)
754 if (*src == '(' || *src == ')' || *src == ' ')
755 *dest++ = '\\';
756 *dest = *src;
758 *dest = 0;
759 return buf;
762 /* The main function. */
765 main (argc, argv)
766 int argc;
767 char **argv;
769 int c;
770 char *input_filename;
771 char *output_filename;
772 enum res_format input_format;
773 enum res_format output_format;
774 char *target;
775 char *preprocessor;
776 char *preprocargs;
777 const char *quotedarg;
778 int language;
779 struct res_directory *resources;
781 #if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
782 setlocale (LC_MESSAGES, "");
783 #endif
784 bindtextdomain (PACKAGE, LOCALEDIR);
785 textdomain (PACKAGE);
787 program_name = argv[0];
788 xmalloc_set_program_name (program_name);
790 bfd_init ();
791 set_default_bfd_target ();
793 res_init ();
795 input_filename = NULL;
796 output_filename = NULL;
797 input_format = RES_FORMAT_UNKNOWN;
798 output_format = RES_FORMAT_UNKNOWN;
799 target = NULL;
800 preprocessor = NULL;
801 preprocargs = NULL;
802 language = -1;
804 while ((c = getopt_long (argc, argv, "i:o:I:O:F:D:v", long_options,
805 (int *) 0)) != EOF)
807 switch (c)
809 case 'i':
810 input_filename = optarg;
811 break;
813 case 'o':
814 output_filename = optarg;
815 break;
817 case 'I':
818 input_format = format_from_name (optarg);
819 break;
821 case 'O':
822 output_format = format_from_name (optarg);
823 break;
825 case 'F':
826 target = optarg;
827 break;
829 case OPTION_PREPROCESSOR:
830 preprocessor = optarg;
831 break;
833 case 'D':
834 case OPTION_DEFINE:
835 if (preprocargs == NULL)
837 quotedarg = quot (optarg);
838 preprocargs = xmalloc (strlen (quotedarg) + 3);
839 sprintf (preprocargs, "-D%s", quotedarg);
841 else
843 char *n;
845 quotedarg = quot (optarg);
846 n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
847 sprintf (n, "%s -D%s", preprocargs, quotedarg);
848 free (preprocargs);
849 preprocargs = n;
851 break;
853 case 'v':
854 verbose ++;
855 break;
857 case OPTION_INCLUDE_DIR:
858 if (preprocargs == NULL)
860 quotedarg = quot (optarg);
861 preprocargs = xmalloc (strlen (quotedarg) + 3);
862 sprintf (preprocargs, "-I%s", quotedarg);
864 else
866 char *n;
868 quotedarg = quot (optarg);
869 n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
870 sprintf (n, "%s -I%s", preprocargs, quotedarg);
871 free (preprocargs);
872 preprocargs = n;
876 struct include_dir *n, **pp;
878 n = (struct include_dir *) xmalloc (sizeof *n);
879 n->next = NULL;
880 n->dir = optarg;
882 for (pp = &include_dirs; *pp != NULL; pp = &(*pp)->next)
884 *pp = n;
887 break;
889 case OPTION_LANGUAGE:
890 language = strtol (optarg, (char **) NULL, 16);
891 break;
893 #ifdef YYDEBUG
894 case OPTION_YYDEBUG:
895 yydebug = 1;
896 break;
897 #endif
899 case OPTION_HELP:
900 usage (stdout, 0);
901 break;
903 case OPTION_VERSION:
904 print_version ("windres");
905 break;
907 default:
908 usage (stderr, 1);
909 break;
913 if (input_filename == NULL && optind < argc)
915 input_filename = argv[optind];
916 ++optind;
919 if (output_filename == NULL && optind < argc)
921 output_filename = argv[optind];
922 ++optind;
925 if (argc != optind)
926 usage (stderr, 1);
928 if (input_format == RES_FORMAT_UNKNOWN)
930 if (input_filename == NULL)
931 input_format = RES_FORMAT_RC;
932 else
933 input_format = format_from_filename (input_filename, 1);
936 if (output_format == RES_FORMAT_UNKNOWN)
938 if (output_filename == NULL)
939 output_format = RES_FORMAT_RC;
940 else
941 output_format = format_from_filename (output_filename, 0);
944 /* Read the input file. */
946 switch (input_format)
948 default:
949 abort ();
950 case RES_FORMAT_RC:
951 resources = read_rc_file (input_filename, preprocessor, preprocargs,
952 language);
953 break;
954 case RES_FORMAT_RES:
955 resources = read_res_file (input_filename);
956 break;
957 case RES_FORMAT_COFF:
958 resources = read_coff_rsrc (input_filename, target);
959 break;
962 if (resources == NULL)
963 fatal (_("no resources"));
965 /* Sort the resources. This is required for COFF, convenient for
966 rc, and unimportant for res. */
968 resources = sort_resources (resources);
970 /* Write the output file. */
972 reswr_init ();
974 switch (output_format)
976 default:
977 abort ();
978 case RES_FORMAT_RC:
979 write_rc_file (output_filename, resources);
980 break;
981 case RES_FORMAT_RES:
982 write_res_file (output_filename, resources);
983 break;
984 case RES_FORMAT_COFF:
985 write_coff_file (output_filename, target, resources);
986 break;
989 xexit (0);
990 return 0;