]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - binutils/windres.c
top level:
[thirdparty/binutils-gdb.git] / binutils / windres.c
CommitLineData
252b5132 1/* windres.c -- a program to manipulate Windows resources
92f01d61 2 Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007
29b058f1 3 Free Software Foundation, Inc.
252b5132
RH
4 Written by Ian Lance Taylor, Cygnus Support.
5
6 This file is part of GNU Binutils.
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
b43b5d5f
NC
20 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
21 02110-1301, USA. */
252b5132
RH
22
23/* This program can read and write Windows resources in various
24 formats. In particular, it can act like the rc resource compiler
25 program, and it can act like the cvtres res to COFF conversion
26 program.
27
28 It is based on information taken from the following sources:
29
30 * Microsoft documentation.
31
32 * The rcl program, written by Gunther Ebert
33 <gunther.ebert@ixos-leipzig.de>.
34
29b058f1 35 * The res2coff program, written by Pedro A. Aranda <paag@tid.es>. */
252b5132 36
0af6db78
AM
37#include "config.h"
38#ifdef HAVE_UNISTD_H
39#include <unistd.h>
40#endif
41#include <assert.h>
42#include <time.h>
252b5132
RH
43#include "bfd.h"
44#include "getopt.h"
45#include "bucomm.h"
46#include "libiberty.h"
3882b010 47#include "safe-ctype.h"
252b5132
RH
48#include "obstack.h"
49#include "windres.h"
252b5132 50
29b058f1 51/* Used by resrc.c at least. */
751d21b5
DD
52
53int verbose = 0;
54
252b5132
RH
55/* An enumeration of format types. */
56
57enum res_format
58{
59 /* Unknown format. */
60 RES_FORMAT_UNKNOWN,
61 /* Textual RC file. */
62 RES_FORMAT_RC,
63 /* Binary RES file. */
64 RES_FORMAT_RES,
65 /* COFF file. */
66 RES_FORMAT_COFF
67};
68
69/* A structure used to map between format types and strings. */
70
71struct format_map
72{
73 const char *name;
74 enum res_format format;
75};
76
77/* A mapping between names and format types. */
78
79static const struct format_map format_names[] =
80{
81 { "rc", RES_FORMAT_RC },
82 { "res", RES_FORMAT_RES },
83 { "coff", RES_FORMAT_COFF },
84 { NULL, RES_FORMAT_UNKNOWN }
85};
86
87/* A mapping from file extensions to format types. */
88
89static const struct format_map format_fileexts[] =
90{
91 { "rc", RES_FORMAT_RC },
92 { "res", RES_FORMAT_RES },
93 { "exe", RES_FORMAT_COFF },
94 { "obj", RES_FORMAT_COFF },
95 { "o", RES_FORMAT_COFF },
96 { NULL, RES_FORMAT_UNKNOWN }
97};
98
99/* A list of include directories. */
100
101struct include_dir
102{
103 struct include_dir *next;
104 char *dir;
105};
106
107static struct include_dir *include_dirs;
108
252b5132
RH
109/* Static functions. */
110
2da42df6
AJ
111static void res_init (void);
112static int extended_menuitems (const struct menuitem *);
113static enum res_format format_from_name (const char *, int);
114static enum res_format format_from_filename (const char *, int);
115static void usage (FILE *, int);
116static int cmp_res_entry (const void *, const void *);
117static struct res_directory *sort_resources (struct res_directory *);
118static void reswr_init (void);
119static const char * quot (const char *);
252b5132
RH
120\f
121/* When we are building a resource tree, we allocate everything onto
122 an obstack, so that we can free it all at once if we want. */
123
124#define obstack_chunk_alloc xmalloc
125#define obstack_chunk_free free
126
127/* The resource building obstack. */
128
129static struct obstack res_obstack;
130
131/* Initialize the resource building obstack. */
132
133static void
2da42df6 134res_init (void)
252b5132
RH
135{
136 obstack_init (&res_obstack);
137}
138
139/* Allocate space on the resource building obstack. */
140
2da42df6
AJ
141void *
142res_alloc (size_t bytes)
252b5132 143{
2da42df6 144 return (void *) obstack_alloc (&res_obstack, bytes);
252b5132
RH
145}
146
147/* We also use an obstack to save memory used while writing out a set
148 of resources. */
149
150static struct obstack reswr_obstack;
151
152/* Initialize the resource writing obstack. */
153
154static void
2da42df6 155reswr_init (void)
252b5132
RH
156{
157 obstack_init (&reswr_obstack);
158}
159
160/* Allocate space on the resource writing obstack. */
161
2da42df6
AJ
162void *
163reswr_alloc (size_t bytes)
252b5132 164{
2da42df6 165 return (void *) obstack_alloc (&reswr_obstack, bytes);
252b5132
RH
166}
167\f
168/* Open a file using the include directory search list. */
169
170FILE *
2da42df6
AJ
171open_file_search (const char *filename, const char *mode, const char *errmsg,
172 char **real_filename)
252b5132
RH
173{
174 FILE *e;
175 struct include_dir *d;
176
177 e = fopen (filename, mode);
178 if (e != NULL)
179 {
180 *real_filename = xstrdup (filename);
181 return e;
182 }
183
184 if (errno == ENOENT)
185 {
186 for (d = include_dirs; d != NULL; d = d->next)
187 {
188 char *n;
189
190 n = (char *) xmalloc (strlen (d->dir) + strlen (filename) + 2);
191 sprintf (n, "%s/%s", d->dir, filename);
192 e = fopen (n, mode);
193 if (e != NULL)
194 {
195 *real_filename = n;
196 return e;
197 }
198
199 if (errno != ENOENT)
200 break;
201 }
202 }
203
204 fatal (_("can't open %s `%s': %s"), errmsg, filename, strerror (errno));
205
206 /* Return a value to avoid a compiler warning. */
207 return NULL;
208}
209\f
210/* Compare two resource ID's. We consider name entries to come before
211 numeric entries, because that is how they appear in the COFF .rsrc
212 section. */
213
214int
2da42df6 215res_id_cmp (struct res_id a, struct res_id b)
252b5132
RH
216{
217 if (! a.named)
218 {
219 if (b.named)
220 return 1;
221 if (a.u.id > b.u.id)
222 return 1;
223 else if (a.u.id < b.u.id)
224 return -1;
225 else
226 return 0;
227 }
228 else
229 {
230 unichar *as, *ase, *bs, *bse;
231
232 if (! b.named)
233 return -1;
234
235 as = a.u.n.name;
236 ase = as + a.u.n.length;
237 bs = b.u.n.name;
238 bse = bs + b.u.n.length;
239
240 while (as < ase)
241 {
242 int i;
243
244 if (bs >= bse)
245 return 1;
246 i = (int) *as - (int) *bs;
247 if (i != 0)
248 return i;
249 ++as;
250 ++bs;
251 }
252
253 if (bs < bse)
254 return -1;
255
256 return 0;
257 }
258}
259
260/* Print a resource ID. */
261
262void
2da42df6 263res_id_print (FILE *stream, struct res_id id, int quote)
252b5132
RH
264{
265 if (! id.named)
266 fprintf (stream, "%lu", id.u.id);
267 else
268 {
269 if (quote)
270 putc ('"', stream);
271 unicode_print (stream, id.u.n.name, id.u.n.length);
272 if (quote)
273 putc ('"', stream);
274 }
275}
276
277/* Print a list of resource ID's. */
278
279void
2da42df6 280res_ids_print (FILE *stream, int cids, const struct res_id *ids)
252b5132
RH
281{
282 int i;
283
284 for (i = 0; i < cids; i++)
285 {
286 res_id_print (stream, ids[i], 1);
287 if (i + 1 < cids)
288 fprintf (stream, ": ");
289 }
290}
291
292/* Convert an ASCII string to a resource ID. */
293
294void
2da42df6 295res_string_to_id (struct res_id *res_id, const char *string)
252b5132
RH
296{
297 res_id->named = 1;
298 unicode_from_ascii (&res_id->u.n.length, &res_id->u.n.name, string);
299}
300
301/* Define a resource. The arguments are the resource tree, RESOURCES,
302 and the location at which to put it in the tree, CIDS and IDS.
303 This returns a newly allocated res_resource structure, which the
304 caller is expected to initialize. If DUPOK is non-zero, then if a
305 resource with this ID exists, it is returned. Otherwise, a warning
306 is issued, and a new resource is created replacing the existing
307 one. */
308
309struct res_resource *
2da42df6
AJ
310define_resource (struct res_directory **resources, int cids,
311 const struct res_id *ids, int dupok)
252b5132
RH
312{
313 struct res_entry *re = NULL;
314 int i;
315
316 assert (cids > 0);
317 for (i = 0; i < cids; i++)
318 {
319 struct res_entry **pp;
320
321 if (*resources == NULL)
322 {
323 static unsigned long timeval;
324
325 /* Use the same timestamp for every resource created in a
326 single run. */
327 if (timeval == 0)
328 timeval = time (NULL);
329
330 *resources = ((struct res_directory *)
331 res_alloc (sizeof **resources));
332 (*resources)->characteristics = 0;
333 (*resources)->time = timeval;
334 (*resources)->major = 0;
335 (*resources)->minor = 0;
336 (*resources)->entries = NULL;
337 }
338
339 for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
340 if (res_id_cmp ((*pp)->id, ids[i]) == 0)
341 break;
342
343 if (*pp != NULL)
344 re = *pp;
345 else
346 {
347 re = (struct res_entry *) res_alloc (sizeof *re);
348 re->next = NULL;
349 re->id = ids[i];
350 if ((i + 1) < cids)
351 {
352 re->subdir = 1;
353 re->u.dir = NULL;
354 }
355 else
356 {
357 re->subdir = 0;
358 re->u.res = NULL;
359 }
360
361 *pp = re;
362 }
363
364 if ((i + 1) < cids)
365 {
366 if (! re->subdir)
367 {
368 fprintf (stderr, "%s: ", program_name);
369 res_ids_print (stderr, i, ids);
370 fprintf (stderr, _(": expected to be a directory\n"));
371 xexit (1);
372 }
373
374 resources = &re->u.dir;
375 }
376 }
377
378 if (re->subdir)
379 {
380 fprintf (stderr, "%s: ", program_name);
381 res_ids_print (stderr, cids, ids);
382 fprintf (stderr, _(": expected to be a leaf\n"));
383 xexit (1);
384 }
385
386 if (re->u.res != NULL)
387 {
388 if (dupok)
389 return re->u.res;
390
391 fprintf (stderr, _("%s: warning: "), program_name);
392 res_ids_print (stderr, cids, ids);
393 fprintf (stderr, _(": duplicate value\n"));
394 }
395
396 re->u.res = ((struct res_resource *)
397 res_alloc (sizeof (struct res_resource)));
e80ff7de 398 memset (re->u.res, 0, sizeof (struct res_resource));
252b5132
RH
399
400 re->u.res->type = RES_TYPE_UNINITIALIZED;
252b5132
RH
401 return re->u.res;
402}
403
404/* Define a standard resource. This is a version of define_resource
405 that just takes type, name, and language arguments. */
406
407struct res_resource *
2da42df6
AJ
408define_standard_resource (struct res_directory **resources, int type,
409 struct res_id name, int language, int dupok)
252b5132
RH
410{
411 struct res_id a[3];
412
413 a[0].named = 0;
414 a[0].u.id = type;
415 a[1] = name;
416 a[2].named = 0;
417 a[2].u.id = language;
418 return define_resource (resources, 3, a, dupok);
419}
420
421/* Comparison routine for resource sorting. */
422
423static int
2da42df6 424cmp_res_entry (const void *p1, const void *p2)
252b5132
RH
425{
426 const struct res_entry **re1, **re2;
427
428 re1 = (const struct res_entry **) p1;
429 re2 = (const struct res_entry **) p2;
430 return res_id_cmp ((*re1)->id, (*re2)->id);
431}
432
433/* Sort the resources. */
434
435static struct res_directory *
2da42df6 436sort_resources (struct res_directory *resdir)
252b5132
RH
437{
438 int c, i;
439 struct res_entry *re;
440 struct res_entry **a;
441
442 if (resdir->entries == NULL)
443 return resdir;
444
445 c = 0;
446 for (re = resdir->entries; re != NULL; re = re->next)
447 ++c;
448
449 /* This is a recursive routine, so using xmalloc is probably better
450 than alloca. */
451 a = (struct res_entry **) xmalloc (c * sizeof (struct res_entry *));
452
453 for (i = 0, re = resdir->entries; re != NULL; re = re->next, i++)
454 a[i] = re;
455
456 qsort (a, c, sizeof (struct res_entry *), cmp_res_entry);
457
458 resdir->entries = a[0];
459 for (i = 0; i < c - 1; i++)
460 a[i]->next = a[i + 1];
461 a[i]->next = NULL;
462
463 free (a);
464
465 /* Now sort the subdirectories. */
466
467 for (re = resdir->entries; re != NULL; re = re->next)
468 if (re->subdir)
469 re->u.dir = sort_resources (re->u.dir);
470
471 return resdir;
472}
473\f
474/* Return whether the dialog resource DIALOG is a DIALOG or a
475 DIALOGEX. */
476
477int
2da42df6 478extended_dialog (const struct dialog *dialog)
252b5132
RH
479{
480 const struct dialog_control *c;
481
482 if (dialog->ex != NULL)
483 return 1;
484
485 for (c = dialog->controls; c != NULL; c = c->next)
486 if (c->data != NULL || c->help != 0)
487 return 1;
488
489 return 0;
490}
491
492/* Return whether MENUITEMS are a MENU or a MENUEX. */
493
494int
2da42df6 495extended_menu (const struct menu *menu)
252b5132
RH
496{
497 return extended_menuitems (menu->items);
498}
499
500static int
2da42df6 501extended_menuitems (const struct menuitem *menuitems)
252b5132
RH
502{
503 const struct menuitem *mi;
504
505 for (mi = menuitems; mi != NULL; mi = mi->next)
506 {
507 if (mi->help != 0 || mi->state != 0)
508 return 1;
509 if (mi->popup != NULL && mi->id != 0)
510 return 1;
511 if ((mi->type
512 & ~ (MENUITEM_CHECKED
513 | MENUITEM_GRAYED
514 | MENUITEM_HELP
515 | MENUITEM_INACTIVE
516 | MENUITEM_MENUBARBREAK
517 | MENUITEM_MENUBREAK))
518 != 0)
519 return 1;
520 if (mi->popup != NULL)
521 {
522 if (extended_menuitems (mi->popup))
523 return 1;
524 }
525 }
526
527 return 0;
528}
529\f
530/* Convert a string to a format type, or exit if it can't be done. */
531
532static enum res_format
2da42df6 533format_from_name (const char *name, int exit_on_error)
252b5132
RH
534{
535 const struct format_map *m;
536
537 for (m = format_names; m->name != NULL; m++)
538 if (strcasecmp (m->name, name) == 0)
539 break;
540
85eb5110 541 if (m->name == NULL && exit_on_error)
252b5132 542 {
37cc8ec1 543 non_fatal (_("unknown format type `%s'"), name);
252b5132
RH
544 fprintf (stderr, _("%s: supported formats:"), program_name);
545 for (m = format_names; m->name != NULL; m++)
546 fprintf (stderr, " %s", m->name);
547 fprintf (stderr, "\n");
548 xexit (1);
549 }
550
551 return m->format;
552}
553
554/* Work out a format type given a file name. If INPUT is non-zero,
555 it's OK to look at the file itself. */
556
557static enum res_format
2da42df6 558format_from_filename (const char *filename, int input)
252b5132
RH
559{
560 const char *ext;
561 FILE *e;
562 unsigned char b1, b2, b3, b4, b5;
563 int magic;
564
565 /* If we have an extension, see if we recognize it as implying a
566 particular format. */
567 ext = strrchr (filename, '.');
568 if (ext != NULL)
569 {
570 const struct format_map *m;
571
572 ++ext;
573 for (m = format_fileexts; m->name != NULL; m++)
574 if (strcasecmp (m->name, ext) == 0)
575 return m->format;
576 }
577
578 /* If we don't recognize the name of an output file, assume it's a
579 COFF file. */
252b5132
RH
580 if (! input)
581 return RES_FORMAT_COFF;
582
583 /* Read the first few bytes of the file to see if we can guess what
584 it is. */
252b5132
RH
585 e = fopen (filename, FOPEN_RB);
586 if (e == NULL)
587 fatal ("%s: %s", filename, strerror (errno));
588
589 b1 = getc (e);
590 b2 = getc (e);
591 b3 = getc (e);
592 b4 = getc (e);
593 b5 = getc (e);
594
595 fclose (e);
596
597 /* A PE executable starts with 0x4d 0x5a. */
598 if (b1 == 0x4d && b2 == 0x5a)
599 return RES_FORMAT_COFF;
600
601 /* A COFF .o file starts with a COFF magic number. */
602 magic = (b2 << 8) | b1;
603 switch (magic)
604 {
605 case 0x14c: /* i386 */
606 case 0x166: /* MIPS */
607 case 0x184: /* Alpha */
608 case 0x268: /* 68k */
609 case 0x1f0: /* PowerPC */
610 case 0x290: /* PA */
611 return RES_FORMAT_COFF;
612 }
613
614 /* A RES file starts with 0x0 0x0 0x0 0x0 0x20 0x0 0x0 0x0. */
615 if (b1 == 0 && b2 == 0 && b3 == 0 && b4 == 0 && b5 == 0x20)
616 return RES_FORMAT_RES;
617
618 /* If every character is printable or space, assume it's an RC file. */
3882b010
L
619 if ((ISPRINT (b1) || ISSPACE (b1))
620 && (ISPRINT (b2) || ISSPACE (b2))
621 && (ISPRINT (b3) || ISSPACE (b3))
622 && (ISPRINT (b4) || ISSPACE (b4))
623 && (ISPRINT (b5) || ISSPACE (b5)))
252b5132
RH
624 return RES_FORMAT_RC;
625
626 /* Otherwise, we give up. */
d412a550 627 fatal (_("can not determine type of file `%s'; use the -J option"),
252b5132
RH
628 filename);
629
630 /* Return something to silence the compiler warning. */
631 return RES_FORMAT_UNKNOWN;
632}
633
634/* Print a usage message and exit. */
635
636static void
2da42df6 637usage (FILE *stream, int status)
252b5132 638{
8b53311e 639 fprintf (stream, _("Usage: %s [option(s)] [input-file] [output-file]\n"),
252b5132 640 program_name);
8b53311e
NC
641 fprintf (stream, _(" The options are:\n\
642 -i --input=<file> Name input file\n\
643 -o --output=<file> Name output file\n\
85eb5110 644 -J --input-format=<format> Specify input format\n\
8b53311e
NC
645 -O --output-format=<format> Specify output format\n\
646 -F --target=<target> Specify COFF target\n\
647 --preprocessor=<program> Program to use to preprocess rc file\n\
85eb5110 648 -I --include-dir=<dir> Include directory when preprocessing rc file\n\
8b53311e 649 -D --define <sym>[=<val>] Define SYM when preprocessing rc file\n\
29b058f1 650 -U --undefine <sym> Undefine SYM when preprocessing rc file\n\
8b53311e 651 -v --verbose Verbose - tells you what it's doing\n\
85eb5110 652 -l --language=<val> Set language when reading rc file\n\
8b53311e
NC
653 --use-temp-file Use a temporary file instead of popen to read\n\
654 the preprocessor output\n\
655 --no-use-temp-file Use popen (default)\n"));
252b5132
RH
656#ifdef YYDEBUG
657 fprintf (stream, _("\
8b53311e 658 --yydebug Turn on parser debugging\n"));
252b5132
RH
659#endif
660 fprintf (stream, _("\
3126d709 661 -r Ignored for compatibility with rc\n\
07012eee 662 @<file> Read options from <file>\n\
8b53311e
NC
663 -h --help Print this help message\n\
664 -V --version Print version information\n"));
252b5132
RH
665 fprintf (stream, _("\
666FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
667extension if not specified. A single file name is an input file.\n\
668No input-file is stdin, default rc. No output-file is stdout, default rc.\n"));
8b53311e 669
252b5132 670 list_supported_targets (program_name, stream);
8b53311e 671
92f01d61 672 if (REPORT_BUGS_TO[0] && status == 0)
8ad3436c 673 fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO);
8b53311e 674
252b5132
RH
675 exit (status);
676}
677
8b53311e
NC
678/* Quote characters that will confuse the shell when we run the preprocessor. */
679
680static const char *
2da42df6 681quot (const char *string)
09cda596
DD
682{
683 static char *buf = 0;
684 static int buflen = 0;
685 int slen = strlen (string);
686 const char *src;
687 char *dest;
688
689 if ((buflen < slen * 2 + 2) || !buf)
690 {
691 buflen = slen * 2 + 2;
692 if (buf)
693 free (buf);
694 buf = (char *) xmalloc (buflen);
695 }
696
697 for (src=string, dest=buf; *src; src++, dest++)
698 {
699 if (*src == '(' || *src == ')' || *src == ' ')
700 *dest++ = '\\';
701 *dest = *src;
702 }
703 *dest = 0;
704 return buf;
705}
706
32df8966
NC
707/* Long options. */
708
709/* 150 isn't special; it's just an arbitrary non-ASCII char value. */
710
711#define OPTION_PREPROCESSOR 150
712#define OPTION_USE_TEMP_FILE (OPTION_PREPROCESSOR + 1)
713#define OPTION_NO_USE_TEMP_FILE (OPTION_USE_TEMP_FILE + 1)
714#define OPTION_YYDEBUG (OPTION_NO_USE_TEMP_FILE + 1)
715
716static const struct option long_options[] =
717{
718 {"input", required_argument, 0, 'i'},
719 {"output", required_argument, 0, 'o'},
720 {"input-format", required_argument, 0, 'J'},
721 {"output-format", required_argument, 0, 'O'},
722 {"target", required_argument, 0, 'F'},
723 {"preprocessor", required_argument, 0, OPTION_PREPROCESSOR},
724 {"include-dir", required_argument, 0, 'I'},
725 {"define", required_argument, 0, 'D'},
726 {"undefine", required_argument, 0, 'U'},
727 {"verbose", no_argument, 0, 'v'},
728 {"language", required_argument, 0, 'l'},
729 {"use-temp-file", no_argument, 0, OPTION_USE_TEMP_FILE},
730 {"no-use-temp-file", no_argument, 0, OPTION_NO_USE_TEMP_FILE},
731 {"yydebug", no_argument, 0, OPTION_YYDEBUG},
732 {"version", no_argument, 0, 'V'},
733 {"help", no_argument, 0, 'h'},
734 {0, no_argument, 0, 0}
735};
736
f7d63484 737/* This keeps gcc happy when using -Wmissing-prototypes -Wstrict-prototypes. */
2da42df6 738int main (int, char **);
f7d63484 739
252b5132
RH
740/* The main function. */
741
742int
2da42df6 743main (int argc, char **argv)
252b5132
RH
744{
745 int c;
746 char *input_filename;
747 char *output_filename;
748 enum res_format input_format;
85eb5110 749 enum res_format input_format_tmp;
252b5132
RH
750 enum res_format output_format;
751 char *target;
752 char *preprocessor;
753 char *preprocargs;
09cda596 754 const char *quotedarg;
252b5132
RH
755 int language;
756 struct res_directory *resources;
5a298d2d 757 int use_temp_file;
252b5132
RH
758
759#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
760 setlocale (LC_MESSAGES, "");
3882b010
L
761#endif
762#if defined (HAVE_SETLOCALE)
763 setlocale (LC_CTYPE, "");
252b5132
RH
764#endif
765 bindtextdomain (PACKAGE, LOCALEDIR);
766 textdomain (PACKAGE);
767
768 program_name = argv[0];
769 xmalloc_set_program_name (program_name);
770
c843b1bb 771 expandargv (&argc, &argv);
869b9d07 772
252b5132
RH
773 bfd_init ();
774 set_default_bfd_target ();
775
776 res_init ();
777
778 input_filename = NULL;
779 output_filename = NULL;
780 input_format = RES_FORMAT_UNKNOWN;
781 output_format = RES_FORMAT_UNKNOWN;
782 target = NULL;
783 preprocessor = NULL;
784 preprocargs = NULL;
f7d63484 785 language = 0x409; /* LANG_ENGLISH, SUBLANG_ENGLISH_US. */
5a298d2d 786 use_temp_file = 0;
252b5132 787
32df8966 788 while ((c = getopt_long (argc, argv, "f:i:l:o:I:J:O:F:D:U:rhHvV", long_options,
252b5132
RH
789 (int *) 0)) != EOF)
790 {
791 switch (c)
792 {
793 case 'i':
794 input_filename = optarg;
795 break;
796
32df8966 797 case 'f':
50c2245b 798 /* For compatibility with rc we accept "-fo <name>" as being the
32df8966
NC
799 equivalent of "-o <name>". We do not advertise this fact
800 though, as we do not want users to use non-GNU like command
801 line switches. */
802 if (*optarg != 'o')
803 fatal (_("invalid option -f\n"));
804 optarg++;
805 if (* optarg == 0)
806 {
807 if (optind == argc)
2da42df6 808 fatal (_("No filename following the -fo option.\n"));
32df8966
NC
809 optarg = argv [optind++];
810 }
811 /* Fall through. */
812
252b5132
RH
813 case 'o':
814 output_filename = optarg;
815 break;
816
85eb5110
NC
817 case 'J':
818 input_format = format_from_name (optarg, 1);
252b5132
RH
819 break;
820
821 case 'O':
85eb5110 822 output_format = format_from_name (optarg, 1);
252b5132
RH
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':
29b058f1 834 case 'U':
252b5132
RH
835 if (preprocargs == NULL)
836 {
09cda596
DD
837 quotedarg = quot (optarg);
838 preprocargs = xmalloc (strlen (quotedarg) + 3);
29b058f1 839 sprintf (preprocargs, "-%c%s", c, 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);
29b058f1 847 sprintf (n, "%s -%c%s", preprocargs, c, quotedarg);
252b5132
RH
848 free (preprocargs);
849 preprocargs = n;
850 }
851 break;
852
3126d709 853 case 'r':
29b058f1 854 /* Ignored for compatibility with rc. */
3126d709
CF
855 break;
856
751d21b5
DD
857 case 'v':
858 verbose ++;
859 break;
860
85eb5110
NC
861 case 'I':
862 /* For backward compatibility, should be removed in the future. */
863 input_format_tmp = format_from_name (optarg, 0);
864 if (input_format_tmp != RES_FORMAT_UNKNOWN)
865 {
866 fprintf (stderr, _("Option -I is deprecated for setting the input format, please use -J instead.\n"));
867 input_format = input_format_tmp;
868 break;
869 }
2da42df6 870
252b5132
RH
871 if (preprocargs == NULL)
872 {
09cda596
DD
873 quotedarg = quot (optarg);
874 preprocargs = xmalloc (strlen (quotedarg) + 3);
875 sprintf (preprocargs, "-I%s", quotedarg);
252b5132
RH
876 }
877 else
878 {
879 char *n;
880
09cda596
DD
881 quotedarg = quot (optarg);
882 n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
883 sprintf (n, "%s -I%s", preprocargs, quotedarg);
252b5132
RH
884 free (preprocargs);
885 preprocargs = n;
886 }
887
888 {
889 struct include_dir *n, **pp;
890
891 n = (struct include_dir *) xmalloc (sizeof *n);
892 n->next = NULL;
893 n->dir = optarg;
894
895 for (pp = &include_dirs; *pp != NULL; pp = &(*pp)->next)
896 ;
897 *pp = n;
898 }
899
900 break;
901
3077f5d8 902 case 'l':
252b5132
RH
903 language = strtol (optarg, (char **) NULL, 16);
904 break;
905
5a298d2d
NC
906 case OPTION_USE_TEMP_FILE:
907 use_temp_file = 1;
908 break;
909
910 case OPTION_NO_USE_TEMP_FILE:
911 use_temp_file = 0;
912 break;
913
252b5132
RH
914#ifdef YYDEBUG
915 case OPTION_YYDEBUG:
916 yydebug = 1;
917 break;
918#endif
919
8b53311e
NC
920 case 'h':
921 case 'H':
252b5132
RH
922 usage (stdout, 0);
923 break;
924
8b53311e 925 case 'V':
252b5132
RH
926 print_version ("windres");
927 break;
928
929 default:
930 usage (stderr, 1);
931 break;
932 }
933 }
934
935 if (input_filename == NULL && optind < argc)
936 {
937 input_filename = argv[optind];
938 ++optind;
939 }
940
941 if (output_filename == NULL && optind < argc)
942 {
943 output_filename = argv[optind];
944 ++optind;
945 }
946
947 if (argc != optind)
948 usage (stderr, 1);
949
950 if (input_format == RES_FORMAT_UNKNOWN)
951 {
952 if (input_filename == NULL)
953 input_format = RES_FORMAT_RC;
954 else
955 input_format = format_from_filename (input_filename, 1);
956 }
957
958 if (output_format == RES_FORMAT_UNKNOWN)
959 {
960 if (output_filename == NULL)
961 output_format = RES_FORMAT_RC;
962 else
963 output_format = format_from_filename (output_filename, 0);
964 }
965
966 /* Read the input file. */
252b5132
RH
967 switch (input_format)
968 {
969 default:
970 abort ();
971 case RES_FORMAT_RC:
972 resources = read_rc_file (input_filename, preprocessor, preprocargs,
5a298d2d 973 language, use_temp_file);
252b5132
RH
974 break;
975 case RES_FORMAT_RES:
976 resources = read_res_file (input_filename);
977 break;
978 case RES_FORMAT_COFF:
979 resources = read_coff_rsrc (input_filename, target);
980 break;
981 }
982
983 if (resources == NULL)
984 fatal (_("no resources"));
985
986 /* Sort the resources. This is required for COFF, convenient for
987 rc, and unimportant for res. */
252b5132
RH
988 resources = sort_resources (resources);
989
990 /* Write the output file. */
252b5132
RH
991 reswr_init ();
992
993 switch (output_format)
994 {
995 default:
996 abort ();
997 case RES_FORMAT_RC:
998 write_rc_file (output_filename, resources);
999 break;
1000 case RES_FORMAT_RES:
1001 write_res_file (output_filename, resources);
1002 break;
1003 case RES_FORMAT_COFF:
1004 write_coff_file (output_filename, target, resources);
1005 break;
1006 }
1007
1008 xexit (0);
1009 return 0;
1010}