]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - binutils/windres.c
* lib/insight-support.exp (_gdbtk_export_target_info): Add
[thirdparty/binutils-gdb.git] / binutils / windres.c
CommitLineData
252b5132 1/* windres.c -- a program to manipulate Windows resources
8c2bc687 2 Copyright 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
252b5132
RH
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
751d21b5
DD
49/* used by resrc.c at least */
50
51int verbose = 0;
52
252b5132
RH
53/* An enumeration of format types. */
54
55enum 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
69struct format_map
70{
71 const char *name;
72 enum res_format format;
73};
74
75/* A mapping between names and format types. */
76
77static 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
87static 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
99struct include_dir
100{
101 struct include_dir *next;
102 char *dir;
103};
104
105static 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)
5a298d2d
NC
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)
252b5132
RH
119#define OPTION_YYDEBUG (OPTION_VERSION + 1)
120
121static 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'},
5a298d2d
NC
131 {"use-temp-file", no_argument, 0, OPTION_USE_TEMP_FILE},
132 {"no-use-temp-file", no_argument, 0, OPTION_NO_USE_TEMP_FILE},
751d21b5 133 {"verbose", no_argument, 0, 'v'},
252b5132
RH
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
141static void res_init PARAMS ((void));
142static int extended_menuitems PARAMS ((const struct menuitem *));
143static enum res_format format_from_name PARAMS ((const char *));
144static enum res_format format_from_filename PARAMS ((const char *, int));
145static void usage PARAMS ((FILE *, int));
146static int cmp_res_entry PARAMS ((const PTR, const PTR));
147static 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
157static struct obstack res_obstack;
158
159/* Initialize the resource building obstack. */
160
161static void
162res_init ()
163{
164 obstack_init (&res_obstack);
165}
166
167/* Allocate space on the resource building obstack. */
168
169PTR
170res_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
179static struct obstack reswr_obstack;
180
181/* Initialize the resource writing obstack. */
182
183static void
184reswr_init ()
185{
186 obstack_init (&reswr_obstack);
187}
188
189/* Allocate space on the resource writing obstack. */
190
191PTR
192reswr_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
200FILE *
201open_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
247int
248res_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
297void
298res_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
317void
318res_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
335void
336res_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
352struct res_resource *
353define_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
455struct res_resource *
456define_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
475static int
476cmp_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
489static struct res_directory *
490sort_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
532int
533extended_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
550int
551extended_menu (menu)
552 const struct menu *menu;
553{
554 return extended_menuitems (menu->items);
555}
556
557static int
558extended_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
590static enum res_format
591format_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 {
37cc8ec1 602 non_fatal (_("unknown format type `%s'"), name);
252b5132
RH
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
616static enum res_format
617format_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
699static void
700usage (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, _("\
707Options:\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\
751d21b5
DD
717 -DSYM[=VAL], --define SYM[=VAL]\n\
718 Define SYM when preprocessing rc file\n\
32a5c94a 719 -v Verbose - tells you what it's doing\n\
5a298d2d
NC
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"));
252b5132
RH
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, _("\
732FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
733extension if not specified. A single file name is an input file.\n\
734No 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)
8ad3436c 737 fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO);
252b5132
RH
738 exit (status);
739}
740
09cda596
DD
741/* Quote characters that will confuse the shell when we run the preprocessor */
742static 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
252b5132
RH
769/* The main function. */
770
771int
772main (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;
09cda596 784 const char *quotedarg;
252b5132
RH
785 int language;
786 struct res_directory *resources;
5a298d2d 787 int use_temp_file;
252b5132
RH
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;
5a298d2d 811 use_temp_file = 0;
252b5132 812
751d21b5 813 while ((c = getopt_long (argc, argv, "i:o:I:O:F:D:v", long_options,
252b5132
RH
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
09cda596 842 case 'D':
252b5132
RH
843 case OPTION_DEFINE:
844 if (preprocargs == NULL)
845 {
09cda596
DD
846 quotedarg = quot (optarg);
847 preprocargs = xmalloc (strlen (quotedarg) + 3);
848 sprintf (preprocargs, "-D%s", quotedarg);
252b5132
RH
849 }
850 else
851 {
852 char *n;
853
09cda596
DD
854 quotedarg = quot (optarg);
855 n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
856 sprintf (n, "%s -D%s", preprocargs, quotedarg);
252b5132
RH
857 free (preprocargs);
858 preprocargs = n;
859 }
860 break;
861
751d21b5
DD
862 case 'v':
863 verbose ++;
864 break;
865
252b5132
RH
866 case OPTION_INCLUDE_DIR:
867 if (preprocargs == NULL)
868 {
09cda596
DD
869 quotedarg = quot (optarg);
870 preprocargs = xmalloc (strlen (quotedarg) + 3);
871 sprintf (preprocargs, "-I%s", quotedarg);
252b5132
RH
872 }
873 else
874 {
875 char *n;
876
09cda596
DD
877 quotedarg = quot (optarg);
878 n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
879 sprintf (n, "%s -I%s", preprocargs, quotedarg);
252b5132
RH
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
5a298d2d
NC
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
252b5132
RH
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,
5a298d2d 969 language, use_temp_file);
252b5132
RH
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