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 /* used by resrc.c at least */
50
51 int verbose = 0;
52
53 /* An enumeration of format types.  */
54
55 enum res_format
56 {
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
65 };
66
67 /* A structure used to map between format types and strings.  */
68
69 struct format_map
70 {
71   const char *name;
72   enum res_format format;
73 };
74
75 /* A mapping between names and format types.  */
76
77 static const struct format_map format_names[] =
78 {
79   { "rc", RES_FORMAT_RC },
80   { "res", RES_FORMAT_RES },
81   { "coff", RES_FORMAT_COFF },
82   { NULL, RES_FORMAT_UNKNOWN }
83 };
84
85 /* A mapping from file extensions to format types.  */
86
87 static const struct format_map format_fileexts[] =
88 {
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 }
95 };
96
97 /* A list of include directories.  */
98
99 struct include_dir
100 {
101   struct include_dir *next;
102   char *dir;
103 };
104
105 static struct include_dir *include_dirs;
106
107 /* Long options.  */
108
109 /* 150 isn't special; it's just an arbitrary non-ASCII char value.  */
110
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)
120
121 static const struct option long_options[] =
122 {
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}
137 };
138
139 /* Static functions.  */
140
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 \f
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.  */
151
152 #define obstack_chunk_alloc xmalloc
153 #define obstack_chunk_free free
154
155 /* The resource building obstack.  */
156
157 static struct obstack res_obstack;
158
159 /* Initialize the resource building obstack.  */
160
161 static void
162 res_init ()
163 {
164   obstack_init (&res_obstack);
165 }
166
167 /* Allocate space on the resource building obstack.  */
168
169 PTR
170 res_alloc (bytes)
171      size_t bytes;
172 {
173   return (PTR) obstack_alloc (&res_obstack, bytes);
174 }
175
176 /* We also use an obstack to save memory used while writing out a set
177    of resources.  */
178
179 static struct obstack reswr_obstack;
180
181 /* Initialize the resource writing obstack.  */
182
183 static void
184 reswr_init ()
185 {
186   obstack_init (&reswr_obstack);
187 }
188
189 /* Allocate space on the resource writing obstack.  */
190
191 PTR
192 reswr_alloc (bytes)
193      size_t bytes;
194 {
195   return (PTR) obstack_alloc (&reswr_obstack, bytes);
196 }
197 \f
198 /* Open a file using the include directory search list.  */
199
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;
206 {
207   FILE *e;
208   struct include_dir *d;
209
210   e = fopen (filename, mode);
211   if (e != NULL)
212     {
213       *real_filename = xstrdup (filename);
214       return e;
215     }
216
217   if (errno == ENOENT)
218     {
219       for (d = include_dirs; d != NULL; d = d->next)
220         {
221           char *n;
222
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)
227             {
228               *real_filename = n;
229               return e;
230             }
231
232           if (errno != ENOENT)
233             break;
234         }
235     }
236
237   fatal (_("can't open %s `%s': %s"), errmsg, filename, strerror (errno));
238
239   /* Return a value to avoid a compiler warning.  */
240   return NULL;
241 }
242 \f
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.  */
246
247 int
248 res_id_cmp (a, b)
249      struct res_id a;
250      struct res_id b;
251 {
252   if (! a.named)
253     {
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;
262     }
263   else
264     {
265       unichar *as, *ase, *bs, *bse;
266
267       if (! b.named)
268         return -1;
269
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;
274
275       while (as < ase)
276         {
277           int i;
278
279           if (bs >= bse)
280             return 1;
281           i = (int) *as - (int) *bs;
282           if (i != 0)
283             return i;
284           ++as;
285           ++bs;
286         }
287
288       if (bs < bse)
289         return -1;
290
291       return 0;
292     }
293 }
294
295 /* Print a resource ID.  */
296
297 void
298 res_id_print (stream, id, quote)
299      FILE *stream;
300      struct res_id id;
301      int quote;
302 {
303   if (! id.named)
304     fprintf (stream, "%lu", id.u.id);
305   else
306     {
307       if (quote)
308         putc ('"', stream);
309       unicode_print (stream, id.u.n.name, id.u.n.length);
310       if (quote)
311         putc ('"', stream);
312     }
313 }
314
315 /* Print a list of resource ID's.  */
316
317 void
318 res_ids_print (stream, cids, ids)
319      FILE *stream;
320      int cids;
321      const struct res_id *ids;
322 {
323   int i;
324
325   for (i = 0; i < cids; i++)
326     {
327       res_id_print (stream, ids[i], 1);
328       if (i + 1 < cids)
329         fprintf (stream, ": ");
330     }
331 }
332
333 /* Convert an ASCII string to a resource ID.  */
334
335 void
336 res_string_to_id (res_id, string)
337      struct res_id *res_id;
338      const char *string;
339 {
340   res_id->named = 1;
341   unicode_from_ascii (&res_id->u.n.length, &res_id->u.n.name, string);
342 }
343
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.  */
351
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;
358 {
359   struct res_entry *re = NULL;
360   int i;
361
362   assert (cids > 0);
363   for (i = 0; i < cids; i++)
364     {
365       struct res_entry **pp;
366
367       if (*resources == NULL)
368         {
369           static unsigned long timeval;
370
371           /* Use the same timestamp for every resource created in a
372              single run.  */
373           if (timeval == 0)
374             timeval = time (NULL);
375
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;
383         }
384
385       for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
386         if (res_id_cmp ((*pp)->id, ids[i]) == 0)
387           break;
388
389       if (*pp != NULL)
390         re = *pp;
391       else
392         {
393           re = (struct res_entry *) res_alloc (sizeof *re);
394           re->next = NULL;
395           re->id = ids[i];
396           if ((i + 1) < cids)
397             {
398               re->subdir = 1;
399               re->u.dir = NULL;
400             }
401           else
402             {
403               re->subdir = 0;
404               re->u.res = NULL;
405             }
406
407           *pp = re;
408         }
409
410       if ((i + 1) < cids)
411         {
412           if (! re->subdir)
413             {
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);
418             }
419
420           resources = &re->u.dir;
421         }
422     }
423
424   if (re->subdir)
425     {
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);
430     }
431
432   if (re->u.res != NULL)
433     {
434       if (dupok)
435         return re->u.res;
436
437       fprintf (stderr, _("%s: warning: "), program_name);
438       res_ids_print (stderr, cids, ids);
439       fprintf (stderr, _(": duplicate value\n"));
440     }
441
442   re->u.res = ((struct res_resource *)
443                res_alloc (sizeof (struct res_resource)));
444
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));
448
449   return re->u.res;
450 }
451
452 /* Define a standard resource.  This is a version of define_resource
453    that just takes type, name, and language arguments.  */
454
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;
462 {
463   struct res_id a[3];
464
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);
471 }
472
473 /* Comparison routine for resource sorting.  */
474
475 static int
476 cmp_res_entry (p1, p2)
477      const PTR p1;
478      const PTR p2;
479 {
480   const struct res_entry **re1, **re2;
481
482   re1 = (const struct res_entry **) p1;
483   re2 = (const struct res_entry **) p2;
484   return res_id_cmp ((*re1)->id, (*re2)->id);
485 }
486
487 /* Sort the resources.  */
488
489 static struct res_directory *
490 sort_resources (resdir)
491      struct res_directory *resdir;
492 {
493   int c, i;
494   struct res_entry *re;
495   struct res_entry **a;
496
497   if (resdir->entries == NULL)
498     return resdir;
499
500   c = 0;
501   for (re = resdir->entries; re != NULL; re = re->next)
502     ++c;
503
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 *));
507
508   for (i = 0, re = resdir->entries; re != NULL; re = re->next, i++)
509     a[i] = re;
510
511   qsort (a, c, sizeof (struct res_entry *), cmp_res_entry);
512
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;
517
518   free (a);
519
520   /* Now sort the subdirectories.  */
521
522   for (re = resdir->entries; re != NULL; re = re->next)
523     if (re->subdir)
524       re->u.dir = sort_resources (re->u.dir);
525
526   return resdir;
527 }
528 \f
529 /* Return whether the dialog resource DIALOG is a DIALOG or a
530    DIALOGEX.  */
531
532 int
533 extended_dialog (dialog)
534      const struct dialog *dialog;
535 {
536   const struct dialog_control *c;
537
538   if (dialog->ex != NULL)
539     return 1;
540
541   for (c = dialog->controls; c != NULL; c = c->next)
542     if (c->data != NULL || c->help != 0)
543       return 1;
544
545   return 0;
546 }
547
548 /* Return whether MENUITEMS are a MENU or a MENUEX.  */
549
550 int
551 extended_menu (menu)
552      const struct menu *menu;
553 {
554   return extended_menuitems (menu->items);
555 }
556
557 static int
558 extended_menuitems (menuitems)
559      const struct menuitem *menuitems;
560 {
561   const struct menuitem *mi;
562
563   for (mi = menuitems; mi != NULL; mi = mi->next)
564     {
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)
579         {
580           if (extended_menuitems (mi->popup))
581             return 1;
582         }
583     }
584
585   return 0;
586 }
587 \f
588 /* Convert a string to a format type, or exit if it can't be done.  */
589
590 static enum res_format
591 format_from_name (name)
592      const char *name;
593 {
594   const struct format_map *m;
595
596   for (m = format_names; m->name != NULL; m++)
597     if (strcasecmp (m->name, name) == 0)
598       break;
599
600   if (m->name == NULL)
601     {
602       fprintf (stderr, _("%s: unknown format type `%s'\n"), program_name, 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);
608     }
609
610   return m->format;
611 }
612
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.  */
615
616 static enum res_format
617 format_from_filename (filename, input)
618      const char *filename;
619      int input;
620 {
621   const char *ext;
622   FILE *e;
623   unsigned char b1, b2, b3, b4, b5;
624   int magic;
625
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)
630     {
631       const struct format_map *m;
632
633       ++ext;
634       for (m = format_fileexts; m->name != NULL; m++)
635         if (strcasecmp (m->name, ext) == 0)
636           return m->format;
637     }
638
639   /* If we don't recognize the name of an output file, assume it's a
640      COFF file.  */
641
642   if (! input)
643     return RES_FORMAT_COFF;
644
645   /* Read the first few bytes of the file to see if we can guess what
646      it is.  */
647
648   e = fopen (filename, FOPEN_RB);
649   if (e == NULL)
650     fatal ("%s: %s", filename, strerror (errno));
651
652   b1 = getc (e);
653   b2 = getc (e);
654   b3 = getc (e);
655   b4 = getc (e);
656   b5 = getc (e);
657
658   fclose (e);
659
660   /* A PE executable starts with 0x4d 0x5a.  */
661   if (b1 == 0x4d && b2 == 0x5a)
662     return RES_FORMAT_COFF;
663
664   /* A COFF .o file starts with a COFF magic number.  */
665   magic = (b2 << 8) | b1;
666   switch (magic)
667     {
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;
675     }
676
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;
680
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;
688
689   /* Otherwise, we give up.  */
690   fatal (_("can not determine type of file `%s'; use the -I option"),
691          filename);
692
693   /* Return something to silence the compiler warning.  */
694   return RES_FORMAT_UNKNOWN;
695 }
696
697 /* Print a usage message and exit.  */
698
699 static void
700 usage (stream, status)
701      FILE *stream;
702      int status;
703 {
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 bug-gnu-utils@gnu.org\n"));
738   exit (status);
739 }
740
741 /* Quote characters that will confuse the shell when we run the preprocessor */
742 static const char *quot (string)
743      const char *string;
744 {
745   static char *buf = 0;
746   static int buflen = 0;
747   int slen = strlen (string);
748   const char *src;
749   char *dest;
750
751   if ((buflen < slen * 2 + 2) || !buf)
752     {
753       buflen = slen * 2 + 2;
754       if (buf)
755         free (buf);
756       buf = (char *) xmalloc (buflen);
757     }
758
759   for (src=string, dest=buf; *src; src++, dest++)
760     {
761       if (*src == '(' || *src == ')' || *src == ' ')
762         *dest++ = '\\';
763       *dest = *src;
764     }
765   *dest = 0;
766   return buf;
767 }
768
769 /* The main function.  */
770
771 int
772 main (argc, argv)
773      int argc;
774      char **argv;
775 {
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;
788
789 #if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
790   setlocale (LC_MESSAGES, "");
791 #endif
792   bindtextdomain (PACKAGE, LOCALEDIR);
793   textdomain (PACKAGE);
794
795   program_name = argv[0];
796   xmalloc_set_program_name (program_name);
797
798   bfd_init ();
799   set_default_bfd_target ();
800
801   res_init ();
802
803   input_filename = NULL;
804   output_filename = NULL;
805   input_format = RES_FORMAT_UNKNOWN;
806   output_format = RES_FORMAT_UNKNOWN;
807   target = NULL;
808   preprocessor = NULL;
809   preprocargs = NULL;
810   language = -1;
811   use_temp_file = 0;
812
813   while ((c = getopt_long (argc, argv, "i:o:I:O:F:D:v", long_options,
814                            (int *) 0)) != EOF)
815     {
816       switch (c)
817         {
818         case 'i':
819           input_filename = optarg;
820           break;
821
822         case 'o':
823           output_filename = optarg;
824           break;
825
826         case 'I':
827           input_format = format_from_name (optarg);
828           break;
829
830         case 'O':
831           output_format = format_from_name (optarg);
832           break;
833
834         case 'F':
835           target = optarg;
836           break;
837
838         case OPTION_PREPROCESSOR:
839           preprocessor = optarg;
840           break;
841
842         case 'D':
843         case OPTION_DEFINE:
844           if (preprocargs == NULL)
845             {
846               quotedarg = quot (optarg);
847               preprocargs = xmalloc (strlen (quotedarg) + 3);
848               sprintf (preprocargs, "-D%s", quotedarg);
849             }
850           else
851             {
852               char *n;
853
854               quotedarg = quot (optarg);
855               n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
856               sprintf (n, "%s -D%s", preprocargs, quotedarg);
857               free (preprocargs);
858               preprocargs = n;
859             }
860           break;
861
862         case 'v':
863           verbose ++;
864           break;
865
866         case OPTION_INCLUDE_DIR:
867           if (preprocargs == NULL)
868             {
869               quotedarg = quot (optarg);
870               preprocargs = xmalloc (strlen (quotedarg) + 3);
871               sprintf (preprocargs, "-I%s", quotedarg);
872             }
873           else
874             {
875               char *n;
876
877               quotedarg = quot (optarg);
878               n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
879               sprintf (n, "%s -I%s", preprocargs, quotedarg);
880               free (preprocargs);
881               preprocargs = n;
882             }
883
884           {
885             struct include_dir *n, **pp;
886
887             n = (struct include_dir *) xmalloc (sizeof *n);
888             n->next = NULL;
889             n->dir = optarg;
890
891             for (pp = &include_dirs; *pp != NULL; pp = &(*pp)->next)
892               ;
893             *pp = n;
894           }
895
896           break;
897
898         case OPTION_LANGUAGE:
899           language = strtol (optarg, (char **) NULL, 16);
900           break;
901
902         case OPTION_USE_TEMP_FILE:
903           use_temp_file = 1;
904           break;
905
906         case OPTION_NO_USE_TEMP_FILE:
907           use_temp_file = 0;
908           break;
909
910 #ifdef YYDEBUG
911         case OPTION_YYDEBUG:
912           yydebug = 1;
913           break;
914 #endif
915
916         case OPTION_HELP:
917           usage (stdout, 0);
918           break;
919
920         case OPTION_VERSION:
921           print_version ("windres");
922           break;
923
924         default:
925           usage (stderr, 1);
926           break;
927         }
928     }
929
930   if (input_filename == NULL && optind < argc)
931     {
932       input_filename = argv[optind];
933       ++optind;
934     }
935
936   if (output_filename == NULL && optind < argc)
937     {
938       output_filename = argv[optind];
939       ++optind;
940     }
941
942   if (argc != optind)
943     usage (stderr, 1);
944
945   if (input_format == RES_FORMAT_UNKNOWN)
946     {
947       if (input_filename == NULL)
948         input_format = RES_FORMAT_RC;
949       else
950         input_format = format_from_filename (input_filename, 1);
951     }
952
953   if (output_format == RES_FORMAT_UNKNOWN)
954     {
955       if (output_filename == NULL)
956         output_format = RES_FORMAT_RC;
957       else
958         output_format = format_from_filename (output_filename, 0);
959     }
960
961   /* Read the input file.  */
962
963   switch (input_format)
964     {
965     default:
966       abort ();
967     case RES_FORMAT_RC:
968       resources = read_rc_file (input_filename, preprocessor, preprocargs,
969                                 language, use_temp_file);
970       break;
971     case RES_FORMAT_RES:
972       resources = read_res_file (input_filename);
973       break;
974     case RES_FORMAT_COFF:
975       resources = read_coff_rsrc (input_filename, target);
976       break;
977     }
978
979   if (resources == NULL)
980     fatal (_("no resources"));
981
982   /* Sort the resources.  This is required for COFF, convenient for
983      rc, and unimportant for res.  */
984
985   resources = sort_resources (resources);
986
987   /* Write the output file.  */
988
989   reswr_init ();
990
991   switch (output_format)
992     {
993     default:
994       abort ();
995     case RES_FORMAT_RC:
996       write_rc_file (output_filename, resources);
997       break;
998     case RES_FORMAT_RES:
999       write_res_file (output_filename, resources);
1000       break;
1001     case RES_FORMAT_COFF:
1002       write_coff_file (output_filename, target, resources);
1003       break;
1004     }
1005
1006   xexit (0);
1007   return 0;
1008 }
1009