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