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