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
;
107 /* 150 isn't special; it's just an arbitrary non-ASCII char value. */
109 #define OPTION_HELP 150
110 #define OPTION_INCLUDE_DIR (OPTION_HELP + 1)
111 #define OPTION_LANGUAGE (OPTION_INCLUDE_DIR + 1)
112 #define OPTION_PREPROCESSOR (OPTION_LANGUAGE + 1)
113 #define OPTION_USE_TEMP_FILE (OPTION_PREPROCESSOR + 1)
114 #define OPTION_NO_USE_TEMP_FILE (OPTION_USE_TEMP_FILE + 1)
115 #define OPTION_VERSION (OPTION_NO_USE_TEMP_FILE + 1)
116 #define OPTION_YYDEBUG (OPTION_VERSION + 1)
118 static const struct option long_options
[] =
120 {"define", required_argument
, 0, 'D'},
121 {"help", no_argument
, 0, OPTION_HELP
},
122 {"include-dir", required_argument
, 0, OPTION_INCLUDE_DIR
},
123 {"input-format", required_argument
, 0, 'I'},
124 {"language", required_argument
, 0, OPTION_LANGUAGE
},
125 {"output-format", required_argument
, 0, 'O'},
126 {"preprocessor", required_argument
, 0, OPTION_PREPROCESSOR
},
127 {"target", required_argument
, 0, 'F'},
128 {"undefine", required_argument
, 0, 'U'},
129 {"use-temp-file", no_argument
, 0, OPTION_USE_TEMP_FILE
},
130 {"no-use-temp-file", no_argument
, 0, OPTION_NO_USE_TEMP_FILE
},
131 {"verbose", no_argument
, 0, 'v'},
132 {"version", no_argument
, 0, OPTION_VERSION
},
133 {"yydebug", no_argument
, 0, OPTION_YYDEBUG
},
134 {0, no_argument
, 0, 0}
137 /* Static functions. */
139 static void res_init
PARAMS ((void));
140 static int extended_menuitems
PARAMS ((const struct menuitem
*));
141 static enum res_format format_from_name
PARAMS ((const char *));
142 static enum res_format format_from_filename
PARAMS ((const char *, int));
143 static void usage
PARAMS ((FILE *, int));
144 static int cmp_res_entry
PARAMS ((const PTR
, const PTR
));
145 static struct res_directory
*sort_resources
PARAMS ((struct res_directory
*));
146 static void reswr_init
PARAMS ((void));
147 static const char * quot
PARAMS ((const char *));
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. */
152 #define obstack_chunk_alloc xmalloc
153 #define obstack_chunk_free free
155 /* The resource building obstack. */
157 static struct obstack res_obstack
;
159 /* Initialize the resource building obstack. */
164 obstack_init (&res_obstack
);
167 /* Allocate space on the resource building obstack. */
173 return (PTR
) obstack_alloc (&res_obstack
, bytes
);
176 /* We also use an obstack to save memory used while writing out a set
179 static struct obstack reswr_obstack
;
181 /* Initialize the resource writing obstack. */
186 obstack_init (&reswr_obstack
);
189 /* Allocate space on the resource writing obstack. */
195 return (PTR
) obstack_alloc (&reswr_obstack
, bytes
);
198 /* Open a file using the include directory search list. */
201 open_file_search (filename
, mode
, errmsg
, real_filename
)
202 const char *filename
;
205 char **real_filename
;
208 struct include_dir
*d
;
210 e
= fopen (filename
, mode
);
213 *real_filename
= xstrdup (filename
);
219 for (d
= include_dirs
; d
!= NULL
; d
= d
->next
)
223 n
= (char *) xmalloc (strlen (d
->dir
) + strlen (filename
) + 2);
224 sprintf (n
, "%s/%s", d
->dir
, filename
);
237 fatal (_("can't open %s `%s': %s"), errmsg
, filename
, strerror (errno
));
239 /* Return a value to avoid a compiler warning. */
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
258 else if (a
.u
.id
< b
.u
.id
)
265 unichar
*as
, *ase
, *bs
, *bse
;
271 ase
= as
+ a
.u
.n
.length
;
273 bse
= bs
+ b
.u
.n
.length
;
281 i
= (int) *as
- (int) *bs
;
295 /* Print a resource ID. */
298 res_id_print (stream
, id
, quote
)
304 fprintf (stream
, "%lu", id
.u
.id
);
309 unicode_print (stream
, id
.u
.n
.name
, id
.u
.n
.length
);
315 /* Print a list of resource ID's. */
318 res_ids_print (stream
, cids
, ids
)
321 const struct res_id
*ids
;
325 for (i
= 0; i
< cids
; i
++)
327 res_id_print (stream
, ids
[i
], 1);
329 fprintf (stream
, ": ");
333 /* Convert an ASCII string to a resource ID. */
336 res_string_to_id (res_id
, string
)
337 struct res_id
*res_id
;
341 unicode_from_ascii (&res_id
->u
.n
.length
, &res_id
->u
.n
.name
, string
);
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
352 struct res_resource
*
353 define_resource (resources
, cids
, ids
, dupok
)
354 struct res_directory
**resources
;
356 const struct res_id
*ids
;
359 struct res_entry
*re
= NULL
;
363 for (i
= 0; i
< cids
; i
++)
365 struct res_entry
**pp
;
367 if (*resources
== NULL
)
369 static unsigned long timeval
;
371 /* Use the same timestamp for every resource created in a
374 timeval
= time (NULL
);
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
;
385 for (pp
= &(*resources
)->entries
; *pp
!= NULL
; pp
= &(*pp
)->next
)
386 if (res_id_cmp ((*pp
)->id
, ids
[i
]) == 0)
393 re
= (struct res_entry
*) res_alloc (sizeof *re
);
414 fprintf (stderr
, "%s: ", program_name
);
415 res_ids_print (stderr
, i
, ids
);
416 fprintf (stderr
, _(": expected to be a directory\n"));
420 resources
= &re
->u
.dir
;
426 fprintf (stderr
, "%s: ", program_name
);
427 res_ids_print (stderr
, cids
, ids
);
428 fprintf (stderr
, _(": expected to be a leaf\n"));
432 if (re
->u
.res
!= NULL
)
437 fprintf (stderr
, _("%s: warning: "), program_name
);
438 res_ids_print (stderr
, cids
, ids
);
439 fprintf (stderr
, _(": duplicate value\n"));
442 re
->u
.res
= ((struct res_resource
*)
443 res_alloc (sizeof (struct res_resource
)));
444 memset (re
->u
.res
, 0, sizeof (struct res_resource
));
446 re
->u
.res
->type
= RES_TYPE_UNINITIALIZED
;
450 /* Define a standard resource. This is a version of define_resource
451 that just takes type, name, and language arguments. */
453 struct res_resource
*
454 define_standard_resource (resources
, type
, name
, language
, dupok
)
455 struct res_directory
**resources
;
467 a
[2].u
.id
= language
;
468 return define_resource (resources
, 3, a
, dupok
);
471 /* Comparison routine for resource sorting. */
474 cmp_res_entry (p1
, p2
)
478 const struct res_entry
**re1
, **re2
;
480 re1
= (const struct res_entry
**) p1
;
481 re2
= (const struct res_entry
**) p2
;
482 return res_id_cmp ((*re1
)->id
, (*re2
)->id
);
485 /* Sort the resources. */
487 static struct res_directory
*
488 sort_resources (resdir
)
489 struct res_directory
*resdir
;
492 struct res_entry
*re
;
493 struct res_entry
**a
;
495 if (resdir
->entries
== NULL
)
499 for (re
= resdir
->entries
; re
!= NULL
; re
= re
->next
)
502 /* This is a recursive routine, so using xmalloc is probably better
504 a
= (struct res_entry
**) xmalloc (c
* sizeof (struct res_entry
*));
506 for (i
= 0, re
= resdir
->entries
; re
!= NULL
; re
= re
->next
, i
++)
509 qsort (a
, c
, sizeof (struct res_entry
*), cmp_res_entry
);
511 resdir
->entries
= a
[0];
512 for (i
= 0; i
< c
- 1; i
++)
513 a
[i
]->next
= a
[i
+ 1];
518 /* Now sort the subdirectories. */
520 for (re
= resdir
->entries
; re
!= NULL
; re
= re
->next
)
522 re
->u
.dir
= sort_resources (re
->u
.dir
);
527 /* Return whether the dialog resource DIALOG is a DIALOG or a
531 extended_dialog (dialog
)
532 const struct dialog
*dialog
;
534 const struct dialog_control
*c
;
536 if (dialog
->ex
!= NULL
)
539 for (c
= dialog
->controls
; c
!= NULL
; c
= c
->next
)
540 if (c
->data
!= NULL
|| c
->help
!= 0)
546 /* Return whether MENUITEMS are a MENU or a MENUEX. */
550 const struct menu
*menu
;
552 return extended_menuitems (menu
->items
);
556 extended_menuitems (menuitems
)
557 const struct menuitem
*menuitems
;
559 const struct menuitem
*mi
;
561 for (mi
= menuitems
; mi
!= NULL
; mi
= mi
->next
)
563 if (mi
->help
!= 0 || mi
->state
!= 0)
565 if (mi
->popup
!= NULL
&& mi
->id
!= 0)
568 & ~ (MENUITEM_CHECKED
572 | MENUITEM_MENUBARBREAK
573 | MENUITEM_MENUBREAK
))
576 if (mi
->popup
!= NULL
)
578 if (extended_menuitems (mi
->popup
))
586 /* Convert a string to a format type, or exit if it can't be done. */
588 static enum res_format
589 format_from_name (name
)
592 const struct format_map
*m
;
594 for (m
= format_names
; m
->name
!= NULL
; m
++)
595 if (strcasecmp (m
->name
, name
) == 0)
600 non_fatal (_("unknown format type `%s'"), name
);
601 fprintf (stderr
, _("%s: supported formats:"), program_name
);
602 for (m
= format_names
; m
->name
!= NULL
; m
++)
603 fprintf (stderr
, " %s", m
->name
);
604 fprintf (stderr
, "\n");
611 /* Work out a format type given a file name. If INPUT is non-zero,
612 it's OK to look at the file itself. */
614 static enum res_format
615 format_from_filename (filename
, input
)
616 const char *filename
;
621 unsigned char b1
, b2
, b3
, b4
, b5
;
624 /* If we have an extension, see if we recognize it as implying a
625 particular format. */
626 ext
= strrchr (filename
, '.');
629 const struct format_map
*m
;
632 for (m
= format_fileexts
; m
->name
!= NULL
; m
++)
633 if (strcasecmp (m
->name
, ext
) == 0)
637 /* If we don't recognize the name of an output file, assume it's a
640 return RES_FORMAT_COFF
;
642 /* Read the first few bytes of the file to see if we can guess what
644 e
= fopen (filename
, FOPEN_RB
);
646 fatal ("%s: %s", filename
, strerror (errno
));
656 /* A PE executable starts with 0x4d 0x5a. */
657 if (b1
== 0x4d && b2
== 0x5a)
658 return RES_FORMAT_COFF
;
660 /* A COFF .o file starts with a COFF magic number. */
661 magic
= (b2
<< 8) | b1
;
664 case 0x14c: /* i386 */
665 case 0x166: /* MIPS */
666 case 0x184: /* Alpha */
667 case 0x268: /* 68k */
668 case 0x1f0: /* PowerPC */
670 return RES_FORMAT_COFF
;
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
;
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
;
685 /* Otherwise, we give up. */
686 fatal (_("can not determine type of file `%s'; use the -I option"),
689 /* Return something to silence the compiler warning. */
690 return RES_FORMAT_UNKNOWN
;
693 /* Print a usage message and exit. */
696 usage (stream
, status
)
700 fprintf (stream
, _("Usage: %s [option(s)] [input-file] [output-file]\n"),
702 fprintf (stream
, _(" The options are:\n\
703 -i --input=<file> Name input file\n\
704 -o --output=<file> Name output file\n\
705 -I --input-format=<format> Specify input format\n\
706 -O --output-format=<format> Specify output format\n\
707 -F --target=<target> Specify COFF target\n\
708 --preprocessor=<program> Program to use to preprocess rc file\n\
709 --include-dir=<dir> Include directory when preprocessing rc file\n\
710 -D --define <sym>[=<val>] Define SYM when preprocessing rc file\n\
711 -U --undefine <sym> Undefine SYM when preprocessing rc file\n\
712 -v --verbose Verbose - tells you what it's doing\n\
713 --language=<val> Set language when reading rc file\n\
714 --use-temp-file Use a temporary file instead of popen to read\n\
715 the preprocessor output\n\
716 --no-use-temp-file Use popen (default)\n"));
718 fprintf (stream
, _("\
719 --yydebug Turn on parser debugging\n"));
721 fprintf (stream
, _("\
722 -r Ignored for compatibility with rc\n\
723 -h --help Print this help message\n\
724 -V --version Print version information\n"));
725 fprintf (stream
, _("\
726 FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
727 extension if not specified. A single file name is an input file.\n\
728 No input-file is stdin, default rc. No output-file is stdout, default rc.\n"));
730 list_supported_targets (program_name
, stream
);
733 fprintf (stream
, _("Report bugs to %s\n"), REPORT_BUGS_TO
);
738 /* Quote characters that will confuse the shell when we run the preprocessor. */
744 static char *buf
= 0;
745 static int buflen
= 0;
746 int slen
= strlen (string
);
750 if ((buflen
< slen
* 2 + 2) || !buf
)
752 buflen
= slen
* 2 + 2;
755 buf
= (char *) xmalloc (buflen
);
758 for (src
=string
, dest
=buf
; *src
; src
++, dest
++)
760 if (*src
== '(' || *src
== ')' || *src
== ' ')
768 /* This keeps gcc happy when using -Wmissing-prototypes -Wstrict-prototypes. */
769 int main
PARAMS ((int, char **));
771 /* The main function. */
779 char *input_filename
;
780 char *output_filename
;
781 enum res_format input_format
;
782 enum res_format output_format
;
786 const char *quotedarg
;
788 struct res_directory
*resources
;
791 #if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
792 setlocale (LC_MESSAGES
, "");
794 #if defined (HAVE_SETLOCALE)
795 setlocale (LC_CTYPE
, "");
797 bindtextdomain (PACKAGE
, LOCALEDIR
);
798 textdomain (PACKAGE
);
800 program_name
= argv
[0];
801 xmalloc_set_program_name (program_name
);
804 set_default_bfd_target ();
808 input_filename
= NULL
;
809 output_filename
= NULL
;
810 input_format
= RES_FORMAT_UNKNOWN
;
811 output_format
= RES_FORMAT_UNKNOWN
;
815 language
= 0x409; /* LANG_ENGLISH, SUBLANG_ENGLISH_US. */
818 while ((c
= getopt_long (argc
, argv
, "i:o:I:O:F:D:U:rhHvV", long_options
,
824 input_filename
= optarg
;
828 output_filename
= optarg
;
832 input_format
= format_from_name (optarg
);
836 output_format
= format_from_name (optarg
);
843 case OPTION_PREPROCESSOR
:
844 preprocessor
= optarg
;
849 if (preprocargs
== NULL
)
851 quotedarg
= quot (optarg
);
852 preprocargs
= xmalloc (strlen (quotedarg
) + 3);
853 sprintf (preprocargs
, "-%c%s", c
, quotedarg
);
859 quotedarg
= quot (optarg
);
860 n
= xmalloc (strlen (preprocargs
) + strlen (quotedarg
) + 4);
861 sprintf (n
, "%s -%c%s", preprocargs
, c
, quotedarg
);
868 /* Ignored for compatibility with rc. */
875 case OPTION_INCLUDE_DIR
:
876 if (preprocargs
== NULL
)
878 quotedarg
= quot (optarg
);
879 preprocargs
= xmalloc (strlen (quotedarg
) + 3);
880 sprintf (preprocargs
, "-I%s", quotedarg
);
886 quotedarg
= quot (optarg
);
887 n
= xmalloc (strlen (preprocargs
) + strlen (quotedarg
) + 4);
888 sprintf (n
, "%s -I%s", preprocargs
, quotedarg
);
894 struct include_dir
*n
, **pp
;
896 n
= (struct include_dir
*) xmalloc (sizeof *n
);
900 for (pp
= &include_dirs
; *pp
!= NULL
; pp
= &(*pp
)->next
)
907 case OPTION_LANGUAGE
:
908 language
= strtol (optarg
, (char **) NULL
, 16);
911 case OPTION_USE_TEMP_FILE
:
915 case OPTION_NO_USE_TEMP_FILE
:
933 print_version ("windres");
942 if (input_filename
== NULL
&& optind
< argc
)
944 input_filename
= argv
[optind
];
948 if (output_filename
== NULL
&& optind
< argc
)
950 output_filename
= argv
[optind
];
957 if (input_format
== RES_FORMAT_UNKNOWN
)
959 if (input_filename
== NULL
)
960 input_format
= RES_FORMAT_RC
;
962 input_format
= format_from_filename (input_filename
, 1);
965 if (output_format
== RES_FORMAT_UNKNOWN
)
967 if (output_filename
== NULL
)
968 output_format
= RES_FORMAT_RC
;
970 output_format
= format_from_filename (output_filename
, 0);
973 /* Read the input file. */
974 switch (input_format
)
979 resources
= read_rc_file (input_filename
, preprocessor
, preprocargs
,
980 language
, use_temp_file
);
983 resources
= read_res_file (input_filename
);
985 case RES_FORMAT_COFF
:
986 resources
= read_coff_rsrc (input_filename
, target
);
990 if (resources
== NULL
)
991 fatal (_("no resources"));
993 /* Sort the resources. This is required for COFF, convenient for
994 rc, and unimportant for res. */
995 resources
= sort_resources (resources
);
997 /* Write the output file. */
1000 switch (output_format
)
1005 write_rc_file (output_filename
, resources
);
1007 case RES_FORMAT_RES
:
1008 write_res_file (output_filename
, resources
);
1010 case RES_FORMAT_COFF
:
1011 write_coff_file (output_filename
, target
, resources
);