1
/* windres.c -- a program to manipulate Windows resources
2
Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003
3
Free Software Foundation, Inc.
4
Written by Ian Lance Taylor, Cygnus Support.
6
This file is part of GNU Binutils.
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.
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.
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
20
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
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
28
It is based on information taken from the following sources:
30
* Microsoft documentation.
32
* The rcl program, written by Gunther Ebert
33
<gunther.ebert@ixos-leipzig.de>.
35
* The res2coff program, written by Pedro A. Aranda <paag@tid.es>. */
40
#include "libiberty.h"
41
#include "safe-ctype.h"
47
/* Used by resrc.c at least. */
51
/* An enumeration of format types. */
57
/* Textual RC file. */
59
/* Binary RES file. */
65
/* A structure used to map between format types and strings. */
70
enum res_format format;
73
/* A mapping between names and format types. */
75
static const struct format_map format_names[] =
77
{ "rc", RES_FORMAT_RC },
78
{ "res", RES_FORMAT_RES },
79
{ "coff", RES_FORMAT_COFF },
80
{ NULL, RES_FORMAT_UNKNOWN }
83
/* A mapping from file extensions to format types. */
85
static const struct format_map format_fileexts[] =
87
{ "rc", RES_FORMAT_RC },
88
{ "res", RES_FORMAT_RES },
89
{ "exe", RES_FORMAT_COFF },
90
{ "obj", RES_FORMAT_COFF },
91
{ "o", RES_FORMAT_COFF },
92
{ NULL, RES_FORMAT_UNKNOWN }
95
/* A list of include directories. */
99
struct include_dir *next;
103
static struct include_dir *include_dirs;
105
/* Static functions. */
107
static void res_init (void);
108
static int extended_menuitems (const struct menuitem *);
109
static enum res_format format_from_name (const char *, int);
110
static enum res_format format_from_filename (const char *, int);
111
static void usage (FILE *, int);
112
static int cmp_res_entry (const void *, const void *);
113
static struct res_directory *sort_resources (struct res_directory *);
114
static void reswr_init (void);
115
static const char * quot (const char *);
117
/* When we are building a resource tree, we allocate everything onto
118
an obstack, so that we can free it all at once if we want. */
120
#define obstack_chunk_alloc xmalloc
121
#define obstack_chunk_free free
123
/* The resource building obstack. */
125
static struct obstack res_obstack;
127
/* Initialize the resource building obstack. */
132
obstack_init (&res_obstack);
135
/* Allocate space on the resource building obstack. */
138
res_alloc (size_t bytes)
140
return (void *) obstack_alloc (&res_obstack, bytes);
143
/* We also use an obstack to save memory used while writing out a set
146
static struct obstack reswr_obstack;
148
/* Initialize the resource writing obstack. */
153
obstack_init (&reswr_obstack);
156
/* Allocate space on the resource writing obstack. */
159
reswr_alloc (size_t bytes)
161
return (void *) obstack_alloc (&reswr_obstack, bytes);
164
/* Open a file using the include directory search list. */
167
open_file_search (const char *filename, const char *mode, const char *errmsg,
168
char **real_filename)
171
struct include_dir *d;
173
e = fopen (filename, mode);
176
*real_filename = xstrdup (filename);
182
for (d = include_dirs; d != NULL; d = d->next)
186
n = (char *) xmalloc (strlen (d->dir) + strlen (filename) + 2);
187
sprintf (n, "%s/%s", d->dir, filename);
200
fatal (_("can't open %s `%s': %s"), errmsg, filename, strerror (errno));
202
/* Return a value to avoid a compiler warning. */
206
/* Compare two resource ID's. We consider name entries to come before
207
numeric entries, because that is how they appear in the COFF .rsrc
211
res_id_cmp (struct res_id a, struct res_id b)
219
else if (a.u.id < b.u.id)
226
unichar *as, *ase, *bs, *bse;
232
ase = as + a.u.n.length;
234
bse = bs + b.u.n.length;
242
i = (int) *as - (int) *bs;
256
/* Print a resource ID. */
259
res_id_print (FILE *stream, struct res_id id, int quote)
262
fprintf (stream, "%lu", id.u.id);
267
unicode_print (stream, id.u.n.name, id.u.n.length);
273
/* Print a list of resource ID's. */
276
res_ids_print (FILE *stream, int cids, const struct res_id *ids)
280
for (i = 0; i < cids; i++)
282
res_id_print (stream, ids[i], 1);
284
fprintf (stream, ": ");
288
/* Convert an ASCII string to a resource ID. */
291
res_string_to_id (struct res_id *res_id, const char *string)
294
unicode_from_ascii (&res_id->u.n.length, &res_id->u.n.name, string);
297
/* Define a resource. The arguments are the resource tree, RESOURCES,
298
and the location at which to put it in the tree, CIDS and IDS.
299
This returns a newly allocated res_resource structure, which the
300
caller is expected to initialize. If DUPOK is non-zero, then if a
301
resource with this ID exists, it is returned. Otherwise, a warning
302
is issued, and a new resource is created replacing the existing
305
struct res_resource *
306
define_resource (struct res_directory **resources, int cids,
307
const struct res_id *ids, int dupok)
309
struct res_entry *re = NULL;
313
for (i = 0; i < cids; i++)
315
struct res_entry **pp;
317
if (*resources == NULL)
319
static unsigned long timeval;
321
/* Use the same timestamp for every resource created in a
324
timeval = time (NULL);
326
*resources = ((struct res_directory *)
327
res_alloc (sizeof **resources));
328
(*resources)->characteristics = 0;
329
(*resources)->time = timeval;
330
(*resources)->major = 0;
331
(*resources)->minor = 0;
332
(*resources)->entries = NULL;
335
for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
336
if (res_id_cmp ((*pp)->id, ids[i]) == 0)
343
re = (struct res_entry *) res_alloc (sizeof *re);
364
fprintf (stderr, "%s: ", program_name);
365
res_ids_print (stderr, i, ids);
366
fprintf (stderr, _(": expected to be a directory\n"));
370
resources = &re->u.dir;
376
fprintf (stderr, "%s: ", program_name);
377
res_ids_print (stderr, cids, ids);
378
fprintf (stderr, _(": expected to be a leaf\n"));
382
if (re->u.res != NULL)
387
fprintf (stderr, _("%s: warning: "), program_name);
388
res_ids_print (stderr, cids, ids);
389
fprintf (stderr, _(": duplicate value\n"));
392
re->u.res = ((struct res_resource *)
393
res_alloc (sizeof (struct res_resource)));
394
memset (re->u.res, 0, sizeof (struct res_resource));
396
re->u.res->type = RES_TYPE_UNINITIALIZED;
400
/* Define a standard resource. This is a version of define_resource
401
that just takes type, name, and language arguments. */
403
struct res_resource *
404
define_standard_resource (struct res_directory **resources, int type,
405
struct res_id name, int language, int dupok)
413
a[2].u.id = language;
414
return define_resource (resources, 3, a, dupok);
417
/* Comparison routine for resource sorting. */
420
cmp_res_entry (const void *p1, const void *p2)
422
const struct res_entry **re1, **re2;
424
re1 = (const struct res_entry **) p1;
425
re2 = (const struct res_entry **) p2;
426
return res_id_cmp ((*re1)->id, (*re2)->id);
429
/* Sort the resources. */
431
static struct res_directory *
432
sort_resources (struct res_directory *resdir)
435
struct res_entry *re;
436
struct res_entry **a;
438
if (resdir->entries == NULL)
442
for (re = resdir->entries; re != NULL; re = re->next)
445
/* This is a recursive routine, so using xmalloc is probably better
447
a = (struct res_entry **) xmalloc (c * sizeof (struct res_entry *));
449
for (i = 0, re = resdir->entries; re != NULL; re = re->next, i++)
452
qsort (a, c, sizeof (struct res_entry *), cmp_res_entry);
454
resdir->entries = a[0];
455
for (i = 0; i < c - 1; i++)
456
a[i]->next = a[i + 1];
461
/* Now sort the subdirectories. */
463
for (re = resdir->entries; re != NULL; re = re->next)
465
re->u.dir = sort_resources (re->u.dir);
470
/* Return whether the dialog resource DIALOG is a DIALOG or a
474
extended_dialog (const struct dialog *dialog)
476
const struct dialog_control *c;
478
if (dialog->ex != NULL)
481
for (c = dialog->controls; c != NULL; c = c->next)
482
if (c->data != NULL || c->help != 0)
488
/* Return whether MENUITEMS are a MENU or a MENUEX. */
491
extended_menu (const struct menu *menu)
493
return extended_menuitems (menu->items);
497
extended_menuitems (const struct menuitem *menuitems)
499
const struct menuitem *mi;
501
for (mi = menuitems; mi != NULL; mi = mi->next)
503
if (mi->help != 0 || mi->state != 0)
505
if (mi->popup != NULL && mi->id != 0)
508
& ~ (MENUITEM_CHECKED
512
| MENUITEM_MENUBARBREAK
513
| MENUITEM_MENUBREAK))
516
if (mi->popup != NULL)
518
if (extended_menuitems (mi->popup))
526
/* Convert a string to a format type, or exit if it can't be done. */
528
static enum res_format
529
format_from_name (const char *name, int exit_on_error)
531
const struct format_map *m;
533
for (m = format_names; m->name != NULL; m++)
534
if (strcasecmp (m->name, name) == 0)
537
if (m->name == NULL && exit_on_error)
539
non_fatal (_("unknown format type `%s'"), name);
540
fprintf (stderr, _("%s: supported formats:"), program_name);
541
for (m = format_names; m->name != NULL; m++)
542
fprintf (stderr, " %s", m->name);
543
fprintf (stderr, "\n");
550
/* Work out a format type given a file name. If INPUT is non-zero,
551
it's OK to look at the file itself. */
553
static enum res_format
554
format_from_filename (const char *filename, int input)
558
unsigned char b1, b2, b3, b4, b5;
561
/* If we have an extension, see if we recognize it as implying a
562
particular format. */
563
ext = strrchr (filename, '.');
566
const struct format_map *m;
569
for (m = format_fileexts; m->name != NULL; m++)
570
if (strcasecmp (m->name, ext) == 0)
574
/* If we don't recognize the name of an output file, assume it's a
577
return RES_FORMAT_COFF;
579
/* Read the first few bytes of the file to see if we can guess what
581
e = fopen (filename, FOPEN_RB);
583
fatal ("%s: %s", filename, strerror (errno));
593
/* A PE executable starts with 0x4d 0x5a. */
594
if (b1 == 0x4d && b2 == 0x5a)
595
return RES_FORMAT_COFF;
597
/* A COFF .o file starts with a COFF magic number. */
598
magic = (b2 << 8) | b1;
601
case 0x14c: /* i386 */
602
case 0x166: /* MIPS */
603
case 0x184: /* Alpha */
604
case 0x268: /* 68k */
605
case 0x1f0: /* PowerPC */
607
return RES_FORMAT_COFF;
610
/* A RES file starts with 0x0 0x0 0x0 0x0 0x20 0x0 0x0 0x0. */
611
if (b1 == 0 && b2 == 0 && b3 == 0 && b4 == 0 && b5 == 0x20)
612
return RES_FORMAT_RES;
614
/* If every character is printable or space, assume it's an RC file. */
615
if ((ISPRINT (b1) || ISSPACE (b1))
616
&& (ISPRINT (b2) || ISSPACE (b2))
617
&& (ISPRINT (b3) || ISSPACE (b3))
618
&& (ISPRINT (b4) || ISSPACE (b4))
619
&& (ISPRINT (b5) || ISSPACE (b5)))
620
return RES_FORMAT_RC;
622
/* Otherwise, we give up. */
623
fatal (_("can not determine type of file `%s'; use the -I option"),
626
/* Return something to silence the compiler warning. */
627
return RES_FORMAT_UNKNOWN;
630
/* Print a usage message and exit. */
633
usage (FILE *stream, int status)
635
fprintf (stream, _("Usage: %s [option(s)] [input-file] [output-file]\n"),
637
fprintf (stream, _(" The options are:\n\
638
-i --input=<file> Name input file\n\
639
-o --output=<file> Name output file\n\
640
-J --input-format=<format> Specify input format\n\
641
-O --output-format=<format> Specify output format\n\
642
-F --target=<target> Specify COFF target\n\
643
--preprocessor=<program> Program to use to preprocess rc file\n\
644
-I --include-dir=<dir> Include directory when preprocessing rc file\n\
645
-D --define <sym>[=<val>] Define SYM when preprocessing rc file\n\
646
-U --undefine <sym> Undefine SYM when preprocessing rc file\n\
647
-v --verbose Verbose - tells you what it's doing\n\
648
-l --language=<val> Set language when reading rc file\n\
649
--use-temp-file Use a temporary file instead of popen to read\n\
650
the preprocessor output\n\
651
--no-use-temp-file Use popen (default)\n"));
653
fprintf (stream, _("\
654
--yydebug Turn on parser debugging\n"));
656
fprintf (stream, _("\
657
-r Ignored for compatibility with rc\n\
658
-h --help Print this help message\n\
659
-V --version Print version information\n"));
660
fprintf (stream, _("\
661
FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
662
extension if not specified. A single file name is an input file.\n\
663
No input-file is stdin, default rc. No output-file is stdout, default rc.\n"));
665
list_supported_targets (program_name, stream);
668
fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO);
673
/* Quote characters that will confuse the shell when we run the preprocessor. */
676
quot (const char *string)
678
static char *buf = 0;
679
static int buflen = 0;
680
int slen = strlen (string);
684
if ((buflen < slen * 2 + 2) || !buf)
686
buflen = slen * 2 + 2;
689
buf = (char *) xmalloc (buflen);
692
for (src=string, dest=buf; *src; src++, dest++)
694
if (*src == '(' || *src == ')' || *src == ' ')
704
/* 150 isn't special; it's just an arbitrary non-ASCII char value. */
706
#define OPTION_PREPROCESSOR 150
707
#define OPTION_USE_TEMP_FILE (OPTION_PREPROCESSOR + 1)
708
#define OPTION_NO_USE_TEMP_FILE (OPTION_USE_TEMP_FILE + 1)
709
#define OPTION_YYDEBUG (OPTION_NO_USE_TEMP_FILE + 1)
711
static const struct option long_options[] =
713
{"input", required_argument, 0, 'i'},
714
{"output", required_argument, 0, 'o'},
715
{"input-format", required_argument, 0, 'J'},
716
{"output-format", required_argument, 0, 'O'},
717
{"target", required_argument, 0, 'F'},
718
{"preprocessor", required_argument, 0, OPTION_PREPROCESSOR},
719
{"include-dir", required_argument, 0, 'I'},
720
{"define", required_argument, 0, 'D'},
721
{"undefine", required_argument, 0, 'U'},
722
{"verbose", no_argument, 0, 'v'},
723
{"language", required_argument, 0, 'l'},
724
{"use-temp-file", no_argument, 0, OPTION_USE_TEMP_FILE},
725
{"no-use-temp-file", no_argument, 0, OPTION_NO_USE_TEMP_FILE},
726
{"yydebug", no_argument, 0, OPTION_YYDEBUG},
727
{"version", no_argument, 0, 'V'},
728
{"help", no_argument, 0, 'h'},
729
{0, no_argument, 0, 0}
732
/* This keeps gcc happy when using -Wmissing-prototypes -Wstrict-prototypes. */
733
int main (int, char **);
735
/* The main function. */
738
main (int argc, char **argv)
741
char *input_filename;
742
char *output_filename;
743
enum res_format input_format;
744
enum res_format input_format_tmp;
745
enum res_format output_format;
749
const char *quotedarg;
751
struct res_directory *resources;
754
#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
755
setlocale (LC_MESSAGES, "");
757
#if defined (HAVE_SETLOCALE)
758
setlocale (LC_CTYPE, "");
760
bindtextdomain (PACKAGE, LOCALEDIR);
761
textdomain (PACKAGE);
763
program_name = argv[0];
764
xmalloc_set_program_name (program_name);
767
set_default_bfd_target ();
771
input_filename = NULL;
772
output_filename = NULL;
773
input_format = RES_FORMAT_UNKNOWN;
774
output_format = RES_FORMAT_UNKNOWN;
778
language = 0x409; /* LANG_ENGLISH, SUBLANG_ENGLISH_US. */
781
while ((c = getopt_long (argc, argv, "f:i:l:o:I:J:O:F:D:U:rhHvV", long_options,
787
input_filename = optarg;
791
/* For compatibility with rc we accept "-fo <name>" as being the
792
equivalent of "-o <name>". We do not advertise this fact
793
though, as we do not want users to use non-GNU like command
796
fatal (_("invalid option -f\n"));
801
fatal (_("No filename following the -fo option.\n"));
802
optarg = argv [optind++];
807
output_filename = optarg;
811
input_format = format_from_name (optarg, 1);
815
output_format = format_from_name (optarg, 1);
822
case OPTION_PREPROCESSOR:
823
preprocessor = optarg;
828
if (preprocargs == NULL)
830
quotedarg = quot (optarg);
831
preprocargs = xmalloc (strlen (quotedarg) + 3);
832
sprintf (preprocargs, "-%c%s", c, quotedarg);
838
quotedarg = quot (optarg);
839
n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
840
sprintf (n, "%s -%c%s", preprocargs, c, quotedarg);
847
/* Ignored for compatibility with rc. */
855
/* For backward compatibility, should be removed in the future. */
856
input_format_tmp = format_from_name (optarg, 0);
857
if (input_format_tmp != RES_FORMAT_UNKNOWN)
859
fprintf (stderr, _("Option -I is deprecated for setting the input format, please use -J instead.\n"));
860
input_format = input_format_tmp;
864
if (preprocargs == NULL)
866
quotedarg = quot (optarg);
867
preprocargs = xmalloc (strlen (quotedarg) + 3);
868
sprintf (preprocargs, "-I%s", quotedarg);
874
quotedarg = quot (optarg);
875
n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
876
sprintf (n, "%s -I%s", preprocargs, quotedarg);
882
struct include_dir *n, **pp;
884
n = (struct include_dir *) xmalloc (sizeof *n);
888
for (pp = &include_dirs; *pp != NULL; pp = &(*pp)->next)
896
language = strtol (optarg, (char **) NULL, 16);
899
case OPTION_USE_TEMP_FILE:
903
case OPTION_NO_USE_TEMP_FILE:
919
print_version ("windres");
928
if (input_filename == NULL && optind < argc)
930
input_filename = argv[optind];
934
if (output_filename == NULL && optind < argc)
936
output_filename = argv[optind];
943
if (input_format == RES_FORMAT_UNKNOWN)
945
if (input_filename == NULL)
946
input_format = RES_FORMAT_RC;
948
input_format = format_from_filename (input_filename, 1);
951
if (output_format == RES_FORMAT_UNKNOWN)
953
if (output_filename == NULL)
954
output_format = RES_FORMAT_RC;
956
output_format = format_from_filename (output_filename, 0);
959
/* Read the input file. */
960
switch (input_format)
965
resources = read_rc_file (input_filename, preprocessor, preprocargs,
966
language, use_temp_file);
969
resources = read_res_file (input_filename);
971
case RES_FORMAT_COFF:
972
resources = read_coff_rsrc (input_filename, target);
976
if (resources == NULL)
977
fatal (_("no resources"));
979
/* Sort the resources. This is required for COFF, convenient for
980
rc, and unimportant for res. */
981
resources = sort_resources (resources);
983
/* Write the output file. */
986
switch (output_format)
991
write_rc_file (output_filename, resources);
994
write_res_file (output_filename, resources);
996
case RES_FORMAT_COFF:
997
write_coff_file (output_filename, target, resources);