daily update
[binutils.git] / binutils / windres.c
blob27cc0f0e48575bc68da3371312e0003ab74b6fd4
1 /* windres.c -- a program to manipulate Windows resources
2 Copyright 1997, 1998, 1999, 2000, 2001, 2002 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 *));
148 static void reswr_init PARAMS ((void));
149 static const char * quot PARAMS ((const char *));
151 /* When we are building a resource tree, we allocate everything onto
152 an obstack, so that we can free it all at once if we want. */
154 #define obstack_chunk_alloc xmalloc
155 #define obstack_chunk_free free
157 /* The resource building obstack. */
159 static struct obstack res_obstack;
161 /* Initialize the resource building obstack. */
163 static void
164 res_init ()
166 obstack_init (&res_obstack);
169 /* Allocate space on the resource building obstack. */
172 res_alloc (bytes)
173 size_t bytes;
175 return (PTR) obstack_alloc (&res_obstack, bytes);
178 /* We also use an obstack to save memory used while writing out a set
179 of resources. */
181 static struct obstack reswr_obstack;
183 /* Initialize the resource writing obstack. */
185 static void
186 reswr_init ()
188 obstack_init (&reswr_obstack);
191 /* Allocate space on the resource writing obstack. */
194 reswr_alloc (bytes)
195 size_t bytes;
197 return (PTR) obstack_alloc (&reswr_obstack, bytes);
200 /* Open a file using the include directory search list. */
202 FILE *
203 open_file_search (filename, mode, errmsg, real_filename)
204 const char *filename;
205 const char *mode;
206 const char *errmsg;
207 char **real_filename;
209 FILE *e;
210 struct include_dir *d;
212 e = fopen (filename, mode);
213 if (e != NULL)
215 *real_filename = xstrdup (filename);
216 return e;
219 if (errno == ENOENT)
221 for (d = include_dirs; d != NULL; d = d->next)
223 char *n;
225 n = (char *) xmalloc (strlen (d->dir) + strlen (filename) + 2);
226 sprintf (n, "%s/%s", d->dir, filename);
227 e = fopen (n, mode);
228 if (e != NULL)
230 *real_filename = n;
231 return e;
234 if (errno != ENOENT)
235 break;
239 fatal (_("can't open %s `%s': %s"), errmsg, filename, strerror (errno));
241 /* Return a value to avoid a compiler warning. */
242 return NULL;
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. */
250 res_id_cmp (a, b)
251 struct res_id a;
252 struct res_id b;
254 if (! a.named)
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;
265 else
267 unichar *as, *ase, *bs, *bse;
269 if (! b.named)
270 return -1;
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;
277 while (as < ase)
279 int i;
281 if (bs >= bse)
282 return 1;
283 i = (int) *as - (int) *bs;
284 if (i != 0)
285 return i;
286 ++as;
287 ++bs;
290 if (bs < bse)
291 return -1;
293 return 0;
297 /* Print a resource ID. */
299 void
300 res_id_print (stream, id, quote)
301 FILE *stream;
302 struct res_id id;
303 int quote;
305 if (! id.named)
306 fprintf (stream, "%lu", id.u.id);
307 else
309 if (quote)
310 putc ('"', stream);
311 unicode_print (stream, id.u.n.name, id.u.n.length);
312 if (quote)
313 putc ('"', stream);
317 /* Print a list of resource ID's. */
319 void
320 res_ids_print (stream, cids, ids)
321 FILE *stream;
322 int cids;
323 const struct res_id *ids;
325 int i;
327 for (i = 0; i < cids; i++)
329 res_id_print (stream, ids[i], 1);
330 if (i + 1 < cids)
331 fprintf (stream, ": ");
335 /* Convert an ASCII string to a resource ID. */
337 void
338 res_string_to_id (res_id, string)
339 struct res_id *res_id;
340 const char *string;
342 res_id->named = 1;
343 unicode_from_ascii (&res_id->u.n.length, &res_id->u.n.name, string);
346 /* Define a resource. The arguments are the resource tree, RESOURCES,
347 and the location at which to put it in the tree, CIDS and IDS.
348 This returns a newly allocated res_resource structure, which the
349 caller is expected to initialize. If DUPOK is non-zero, then if a
350 resource with this ID exists, it is returned. Otherwise, a warning
351 is issued, and a new resource is created replacing the existing
352 one. */
354 struct res_resource *
355 define_resource (resources, cids, ids, dupok)
356 struct res_directory **resources;
357 int cids;
358 const struct res_id *ids;
359 int dupok;
361 struct res_entry *re = NULL;
362 int i;
364 assert (cids > 0);
365 for (i = 0; i < cids; i++)
367 struct res_entry **pp;
369 if (*resources == NULL)
371 static unsigned long timeval;
373 /* Use the same timestamp for every resource created in a
374 single run. */
375 if (timeval == 0)
376 timeval = time (NULL);
378 *resources = ((struct res_directory *)
379 res_alloc (sizeof **resources));
380 (*resources)->characteristics = 0;
381 (*resources)->time = timeval;
382 (*resources)->major = 0;
383 (*resources)->minor = 0;
384 (*resources)->entries = NULL;
387 for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
388 if (res_id_cmp ((*pp)->id, ids[i]) == 0)
389 break;
391 if (*pp != NULL)
392 re = *pp;
393 else
395 re = (struct res_entry *) res_alloc (sizeof *re);
396 re->next = NULL;
397 re->id = ids[i];
398 if ((i + 1) < cids)
400 re->subdir = 1;
401 re->u.dir = NULL;
403 else
405 re->subdir = 0;
406 re->u.res = NULL;
409 *pp = re;
412 if ((i + 1) < cids)
414 if (! re->subdir)
416 fprintf (stderr, "%s: ", program_name);
417 res_ids_print (stderr, i, ids);
418 fprintf (stderr, _(": expected to be a directory\n"));
419 xexit (1);
422 resources = &re->u.dir;
426 if (re->subdir)
428 fprintf (stderr, "%s: ", program_name);
429 res_ids_print (stderr, cids, ids);
430 fprintf (stderr, _(": expected to be a leaf\n"));
431 xexit (1);
434 if (re->u.res != NULL)
436 if (dupok)
437 return re->u.res;
439 fprintf (stderr, _("%s: warning: "), program_name);
440 res_ids_print (stderr, cids, ids);
441 fprintf (stderr, _(": duplicate value\n"));
444 re->u.res = ((struct res_resource *)
445 res_alloc (sizeof (struct res_resource)));
446 memset (re->u.res, 0, sizeof (struct res_resource));
448 re->u.res->type = RES_TYPE_UNINITIALIZED;
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. */
641 if (! input)
642 return RES_FORMAT_COFF;
644 /* Read the first few bytes of the file to see if we can guess what
645 it is. */
646 e = fopen (filename, FOPEN_RB);
647 if (e == NULL)
648 fatal ("%s: %s", filename, strerror (errno));
650 b1 = getc (e);
651 b2 = getc (e);
652 b3 = getc (e);
653 b4 = getc (e);
654 b5 = getc (e);
656 fclose (e);
658 /* A PE executable starts with 0x4d 0x5a. */
659 if (b1 == 0x4d && b2 == 0x5a)
660 return RES_FORMAT_COFF;
662 /* A COFF .o file starts with a COFF magic number. */
663 magic = (b2 << 8) | b1;
664 switch (magic)
666 case 0x14c: /* i386 */
667 case 0x166: /* MIPS */
668 case 0x184: /* Alpha */
669 case 0x268: /* 68k */
670 case 0x1f0: /* PowerPC */
671 case 0x290: /* PA */
672 return RES_FORMAT_COFF;
675 /* A RES file starts with 0x0 0x0 0x0 0x0 0x20 0x0 0x0 0x0. */
676 if (b1 == 0 && b2 == 0 && b3 == 0 && b4 == 0 && b5 == 0x20)
677 return RES_FORMAT_RES;
679 /* If every character is printable or space, assume it's an RC file. */
680 if ((ISPRINT (b1) || ISSPACE (b1))
681 && (ISPRINT (b2) || ISSPACE (b2))
682 && (ISPRINT (b3) || ISSPACE (b3))
683 && (ISPRINT (b4) || ISSPACE (b4))
684 && (ISPRINT (b5) || ISSPACE (b5)))
685 return RES_FORMAT_RC;
687 /* Otherwise, we give up. */
688 fatal (_("can not determine type of file `%s'; use the -I option"),
689 filename);
691 /* Return something to silence the compiler warning. */
692 return RES_FORMAT_UNKNOWN;
695 /* Print a usage message and exit. */
697 static void
698 usage (stream, status)
699 FILE *stream;
700 int status;
702 fprintf (stream, _("Usage: %s [option(s)] [input-file] [output-file]\n"),
703 program_name);
704 fprintf (stream, _(" The options are:\n\
705 -i --input=<file> Name input file\n\
706 -o --output=<file> Name output file\n\
707 -I --input-format=<format> Specify input format\n\
708 -O --output-format=<format> Specify output format\n\
709 -F --target=<target> Specify COFF target\n\
710 --preprocessor=<program> Program to use to preprocess rc file\n\
711 --include-dir=<dir> Include directory when preprocessing rc file\n\
712 -D --define <sym>[=<val>] Define SYM when preprocessing rc file\n\
713 -v --verbose Verbose - tells you what it's doing\n\
714 --language=<val> Set language when reading rc file\n\
715 --use-temp-file Use a temporary file instead of popen to read\n\
716 the preprocessor output\n\
717 --no-use-temp-file Use popen (default)\n"));
718 #ifdef YYDEBUG
719 fprintf (stream, _("\
720 --yydebug Turn on parser debugging\n"));
721 #endif
722 fprintf (stream, _("\
723 -h --help Print this help message\n\
724 -V --version Print version information\n"));
725 fprintf (stream, _("\
726 FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
727 extension if not specified. A single file name is an input file.\n\
728 No input-file is stdin, default rc. No output-file is stdout, default rc.\n"));
730 list_supported_targets (program_name, stream);
732 if (status == 0)
733 fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO);
735 exit (status);
738 /* Quote characters that will confuse the shell when we run the preprocessor. */
740 static const char *
741 quot (string)
742 const char *string;
744 static char *buf = 0;
745 static int buflen = 0;
746 int slen = strlen (string);
747 const char *src;
748 char *dest;
750 if ((buflen < slen * 2 + 2) || !buf)
752 buflen = slen * 2 + 2;
753 if (buf)
754 free (buf);
755 buf = (char *) xmalloc (buflen);
758 for (src=string, dest=buf; *src; src++, dest++)
760 if (*src == '(' || *src == ')' || *src == ' ')
761 *dest++ = '\\';
762 *dest = *src;
764 *dest = 0;
765 return buf;
768 /* This keeps gcc happy when using -Wmissing-prototypes -Wstrict-prototypes. */
769 int main PARAMS ((int, char **));
771 /* The main function. */
774 main (argc, argv)
775 int argc;
776 char **argv;
778 int c;
779 char *input_filename;
780 char *output_filename;
781 enum res_format input_format;
782 enum res_format output_format;
783 char *target;
784 char *preprocessor;
785 char *preprocargs;
786 const char *quotedarg;
787 int language;
788 struct res_directory *resources;
789 int use_temp_file;
791 #if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
792 setlocale (LC_MESSAGES, "");
793 #endif
794 #if defined (HAVE_SETLOCALE)
795 setlocale (LC_CTYPE, "");
796 #endif
797 bindtextdomain (PACKAGE, LOCALEDIR);
798 textdomain (PACKAGE);
800 program_name = argv[0];
801 xmalloc_set_program_name (program_name);
803 bfd_init ();
804 set_default_bfd_target ();
806 res_init ();
808 input_filename = NULL;
809 output_filename = NULL;
810 input_format = RES_FORMAT_UNKNOWN;
811 output_format = RES_FORMAT_UNKNOWN;
812 target = NULL;
813 preprocessor = NULL;
814 preprocargs = NULL;
815 language = 0x409; /* LANG_ENGLISH, SUBLANG_ENGLISH_US. */
816 use_temp_file = 0;
818 while ((c = getopt_long (argc, argv, "i:o:I:O:F:D:hHvV", long_options,
819 (int *) 0)) != EOF)
821 switch (c)
823 case 'i':
824 input_filename = optarg;
825 break;
827 case 'o':
828 output_filename = optarg;
829 break;
831 case 'I':
832 input_format = format_from_name (optarg);
833 break;
835 case 'O':
836 output_format = format_from_name (optarg);
837 break;
839 case 'F':
840 target = optarg;
841 break;
843 case OPTION_PREPROCESSOR:
844 preprocessor = optarg;
845 break;
847 case 'D':
848 case OPTION_DEFINE:
849 if (preprocargs == NULL)
851 quotedarg = quot (optarg);
852 preprocargs = xmalloc (strlen (quotedarg) + 3);
853 sprintf (preprocargs, "-D%s", quotedarg);
855 else
857 char *n;
859 quotedarg = quot (optarg);
860 n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
861 sprintf (n, "%s -D%s", preprocargs, quotedarg);
862 free (preprocargs);
863 preprocargs = n;
865 break;
867 case 'v':
868 verbose ++;
869 break;
871 case OPTION_INCLUDE_DIR:
872 if (preprocargs == NULL)
874 quotedarg = quot (optarg);
875 preprocargs = xmalloc (strlen (quotedarg) + 3);
876 sprintf (preprocargs, "-I%s", quotedarg);
878 else
880 char *n;
882 quotedarg = quot (optarg);
883 n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
884 sprintf (n, "%s -I%s", preprocargs, quotedarg);
885 free (preprocargs);
886 preprocargs = n;
890 struct include_dir *n, **pp;
892 n = (struct include_dir *) xmalloc (sizeof *n);
893 n->next = NULL;
894 n->dir = optarg;
896 for (pp = &include_dirs; *pp != NULL; pp = &(*pp)->next)
898 *pp = n;
901 break;
903 case OPTION_LANGUAGE:
904 language = strtol (optarg, (char **) NULL, 16);
905 break;
907 case OPTION_USE_TEMP_FILE:
908 use_temp_file = 1;
909 break;
911 case OPTION_NO_USE_TEMP_FILE:
912 use_temp_file = 0;
913 break;
915 #ifdef YYDEBUG
916 case OPTION_YYDEBUG:
917 yydebug = 1;
918 break;
919 #endif
921 case 'h':
922 case 'H':
923 case OPTION_HELP:
924 usage (stdout, 0);
925 break;
927 case 'V':
928 case OPTION_VERSION:
929 print_version ("windres");
930 break;
932 default:
933 usage (stderr, 1);
934 break;
938 if (input_filename == NULL && optind < argc)
940 input_filename = argv[optind];
941 ++optind;
944 if (output_filename == NULL && optind < argc)
946 output_filename = argv[optind];
947 ++optind;
950 if (argc != optind)
951 usage (stderr, 1);
953 if (input_format == RES_FORMAT_UNKNOWN)
955 if (input_filename == NULL)
956 input_format = RES_FORMAT_RC;
957 else
958 input_format = format_from_filename (input_filename, 1);
961 if (output_format == RES_FORMAT_UNKNOWN)
963 if (output_filename == NULL)
964 output_format = RES_FORMAT_RC;
965 else
966 output_format = format_from_filename (output_filename, 0);
969 /* Read the input file. */
971 switch (input_format)
973 default:
974 abort ();
975 case RES_FORMAT_RC:
976 resources = read_rc_file (input_filename, preprocessor, preprocargs,
977 language, use_temp_file);
978 break;
979 case RES_FORMAT_RES:
980 resources = read_res_file (input_filename);
981 break;
982 case RES_FORMAT_COFF:
983 resources = read_coff_rsrc (input_filename, target);
984 break;
987 if (resources == NULL)
988 fatal (_("no resources"));
990 /* Sort the resources. This is required for COFF, convenient for
991 rc, and unimportant for res. */
993 resources = sort_resources (resources);
995 /* Write the output file. */
997 reswr_init ();
999 switch (output_format)
1001 default:
1002 abort ();
1003 case RES_FORMAT_RC:
1004 write_rc_file (output_filename, resources);
1005 break;
1006 case RES_FORMAT_RES:
1007 write_res_file (output_filename, resources);
1008 break;
1009 case RES_FORMAT_COFF:
1010 write_coff_file (output_filename, target, resources);
1011 break;
1014 xexit (0);
1015 return 0;