1 /* windres.c -- a program to manipulate Windows resources
2 Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
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., 51 Franklin Street - Fifth Floor, 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>. */
46 #include "libiberty.h"
47 #include "safe-ctype.h"
51 /* Used by resrc.c at least. */
55 /* An enumeration of format types. */
61 /* Textual RC file. */
63 /* Binary RES file. */
69 /* A structure used to map between format types and strings. */
74 enum res_format format
;
77 /* A mapping between names and format types. */
79 static const struct format_map format_names
[] =
81 { "rc", RES_FORMAT_RC
},
82 { "res", RES_FORMAT_RES
},
83 { "coff", RES_FORMAT_COFF
},
84 { NULL
, RES_FORMAT_UNKNOWN
}
87 /* A mapping from file extensions to format types. */
89 static const struct format_map format_fileexts
[] =
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
}
99 /* A list of include directories. */
103 struct include_dir
*next
;
107 static struct include_dir
*include_dirs
;
109 /* Static functions. */
111 static void res_init (void);
112 static int extended_menuitems (const struct menuitem
*);
113 static enum res_format
format_from_name (const char *, int);
114 static enum res_format
format_from_filename (const char *, int);
115 static void usage (FILE *, int);
116 static int cmp_res_entry (const void *, const void *);
117 static struct res_directory
*sort_resources (struct res_directory
*);
118 static void reswr_init (void);
119 static const char * quot (const char *);
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. */
124 #define obstack_chunk_alloc xmalloc
125 #define obstack_chunk_free free
127 /* The resource building obstack. */
129 static struct obstack res_obstack
;
131 /* Initialize the resource building obstack. */
136 obstack_init (&res_obstack
);
139 /* Allocate space on the resource building obstack. */
142 res_alloc (size_t bytes
)
144 return (void *) obstack_alloc (&res_obstack
, bytes
);
147 /* We also use an obstack to save memory used while writing out a set
150 static struct obstack reswr_obstack
;
152 /* Initialize the resource writing obstack. */
157 obstack_init (&reswr_obstack
);
160 /* Allocate space on the resource writing obstack. */
163 reswr_alloc (size_t bytes
)
165 return (void *) obstack_alloc (&reswr_obstack
, bytes
);
168 /* Open a file using the include directory search list. */
171 open_file_search (const char *filename
, const char *mode
, const char *errmsg
,
172 char **real_filename
)
175 struct include_dir
*d
;
177 e
= fopen (filename
, mode
);
180 *real_filename
= xstrdup (filename
);
186 for (d
= include_dirs
; d
!= NULL
; d
= d
->next
)
190 n
= (char *) xmalloc (strlen (d
->dir
) + strlen (filename
) + 2);
191 sprintf (n
, "%s/%s", d
->dir
, filename
);
204 fatal (_("can't open %s `%s': %s"), errmsg
, filename
, strerror (errno
));
206 /* Return a value to avoid a compiler warning. */
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
215 res_id_cmp (struct res_id a
, struct res_id b
)
223 else if (a
.u
.id
< b
.u
.id
)
230 unichar
*as
, *ase
, *bs
, *bse
;
236 ase
= as
+ a
.u
.n
.length
;
238 bse
= bs
+ b
.u
.n
.length
;
246 i
= (int) *as
- (int) *bs
;
260 /* Print a resource ID. */
263 res_id_print (FILE *stream
, struct res_id id
, int quote
)
266 fprintf (stream
, "%lu", id
.u
.id
);
271 unicode_print (stream
, id
.u
.n
.name
, id
.u
.n
.length
);
277 /* Print a list of resource ID's. */
280 res_ids_print (FILE *stream
, int cids
, const struct res_id
*ids
)
284 for (i
= 0; i
< cids
; i
++)
286 res_id_print (stream
, ids
[i
], 1);
288 fprintf (stream
, ": ");
292 /* Convert an ASCII string to a resource ID. */
295 res_string_to_id (struct res_id
*res_id
, const char *string
)
298 unicode_from_ascii (&res_id
->u
.n
.length
, &res_id
->u
.n
.name
, string
);
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
309 struct res_resource
*
310 define_resource (struct res_directory
**resources
, int cids
,
311 const struct res_id
*ids
, int dupok
)
313 struct res_entry
*re
= NULL
;
317 for (i
= 0; i
< cids
; i
++)
319 struct res_entry
**pp
;
321 if (*resources
== NULL
)
323 static unsigned long timeval
;
325 /* Use the same timestamp for every resource created in a
328 timeval
= time (NULL
);
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
;
339 for (pp
= &(*resources
)->entries
; *pp
!= NULL
; pp
= &(*pp
)->next
)
340 if (res_id_cmp ((*pp
)->id
, ids
[i
]) == 0)
347 re
= (struct res_entry
*) res_alloc (sizeof *re
);
368 fprintf (stderr
, "%s: ", program_name
);
369 res_ids_print (stderr
, i
, ids
);
370 fprintf (stderr
, _(": expected to be a directory\n"));
374 resources
= &re
->u
.dir
;
380 fprintf (stderr
, "%s: ", program_name
);
381 res_ids_print (stderr
, cids
, ids
);
382 fprintf (stderr
, _(": expected to be a leaf\n"));
386 if (re
->u
.res
!= NULL
)
391 fprintf (stderr
, _("%s: warning: "), program_name
);
392 res_ids_print (stderr
, cids
, ids
);
393 fprintf (stderr
, _(": duplicate value\n"));
396 re
->u
.res
= ((struct res_resource
*)
397 res_alloc (sizeof (struct res_resource
)));
398 memset (re
->u
.res
, 0, sizeof (struct res_resource
));
400 re
->u
.res
->type
= RES_TYPE_UNINITIALIZED
;
404 /* Define a standard resource. This is a version of define_resource
405 that just takes type, name, and language arguments. */
407 struct res_resource
*
408 define_standard_resource (struct res_directory
**resources
, int type
,
409 struct res_id name
, int language
, int dupok
)
417 a
[2].u
.id
= language
;
418 return define_resource (resources
, 3, a
, dupok
);
421 /* Comparison routine for resource sorting. */
424 cmp_res_entry (const void *p1
, const void *p2
)
426 const struct res_entry
**re1
, **re2
;
428 re1
= (const struct res_entry
**) p1
;
429 re2
= (const struct res_entry
**) p2
;
430 return res_id_cmp ((*re1
)->id
, (*re2
)->id
);
433 /* Sort the resources. */
435 static struct res_directory
*
436 sort_resources (struct res_directory
*resdir
)
439 struct res_entry
*re
;
440 struct res_entry
**a
;
442 if (resdir
->entries
== NULL
)
446 for (re
= resdir
->entries
; re
!= NULL
; re
= re
->next
)
449 /* This is a recursive routine, so using xmalloc is probably better
451 a
= (struct res_entry
**) xmalloc (c
* sizeof (struct res_entry
*));
453 for (i
= 0, re
= resdir
->entries
; re
!= NULL
; re
= re
->next
, i
++)
456 qsort (a
, c
, sizeof (struct res_entry
*), cmp_res_entry
);
458 resdir
->entries
= a
[0];
459 for (i
= 0; i
< c
- 1; i
++)
460 a
[i
]->next
= a
[i
+ 1];
465 /* Now sort the subdirectories. */
467 for (re
= resdir
->entries
; re
!= NULL
; re
= re
->next
)
469 re
->u
.dir
= sort_resources (re
->u
.dir
);
474 /* Return whether the dialog resource DIALOG is a DIALOG or a
478 extended_dialog (const struct dialog
*dialog
)
480 const struct dialog_control
*c
;
482 if (dialog
->ex
!= NULL
)
485 for (c
= dialog
->controls
; c
!= NULL
; c
= c
->next
)
486 if (c
->data
!= NULL
|| c
->help
!= 0)
492 /* Return whether MENUITEMS are a MENU or a MENUEX. */
495 extended_menu (const struct menu
*menu
)
497 return extended_menuitems (menu
->items
);
501 extended_menuitems (const struct menuitem
*menuitems
)
503 const struct menuitem
*mi
;
505 for (mi
= menuitems
; mi
!= NULL
; mi
= mi
->next
)
507 if (mi
->help
!= 0 || mi
->state
!= 0)
509 if (mi
->popup
!= NULL
&& mi
->id
!= 0)
512 & ~ (MENUITEM_CHECKED
516 | MENUITEM_MENUBARBREAK
517 | MENUITEM_MENUBREAK
))
520 if (mi
->popup
!= NULL
)
522 if (extended_menuitems (mi
->popup
))
530 /* Convert a string to a format type, or exit if it can't be done. */
532 static enum res_format
533 format_from_name (const char *name
, int exit_on_error
)
535 const struct format_map
*m
;
537 for (m
= format_names
; m
->name
!= NULL
; m
++)
538 if (strcasecmp (m
->name
, name
) == 0)
541 if (m
->name
== NULL
&& exit_on_error
)
543 non_fatal (_("unknown format type `%s'"), name
);
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");
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. */
557 static enum res_format
558 format_from_filename (const char *filename
, int input
)
562 unsigned char b1
, b2
, b3
, b4
, b5
;
565 /* If we have an extension, see if we recognize it as implying a
566 particular format. */
567 ext
= strrchr (filename
, '.');
570 const struct format_map
*m
;
573 for (m
= format_fileexts
; m
->name
!= NULL
; m
++)
574 if (strcasecmp (m
->name
, ext
) == 0)
578 /* If we don't recognize the name of an output file, assume it's a
581 return RES_FORMAT_COFF
;
583 /* Read the first few bytes of the file to see if we can guess what
585 e
= fopen (filename
, FOPEN_RB
);
587 fatal ("%s: %s", filename
, strerror (errno
));
597 /* A PE executable starts with 0x4d 0x5a. */
598 if (b1
== 0x4d && b2
== 0x5a)
599 return RES_FORMAT_COFF
;
601 /* A COFF .o file starts with a COFF magic number. */
602 magic
= (b2
<< 8) | b1
;
605 case 0x14c: /* i386 */
606 case 0x166: /* MIPS */
607 case 0x184: /* Alpha */
608 case 0x268: /* 68k */
609 case 0x1f0: /* PowerPC */
611 return RES_FORMAT_COFF
;
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
;
618 /* If every character is printable or space, assume it's an RC file. */
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
)))
624 return RES_FORMAT_RC
;
626 /* Otherwise, we give up. */
627 fatal (_("can not determine type of file `%s'; use the -J option"),
630 /* Return something to silence the compiler warning. */
631 return RES_FORMAT_UNKNOWN
;
634 /* Print a usage message and exit. */
637 usage (FILE *stream
, int status
)
639 fprintf (stream
, _("Usage: %s [option(s)] [input-file] [output-file]\n"),
641 fprintf (stream
, _(" The options are:\n\
642 -i --input=<file> Name input file\n\
643 -o --output=<file> Name output file\n\
644 -J --input-format=<format> Specify input format\n\
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\
648 -I --include-dir=<dir> Include directory when preprocessing rc file\n\
649 -D --define <sym>[=<val>] Define SYM when preprocessing rc file\n\
650 -U --undefine <sym> Undefine SYM when preprocessing rc file\n\
651 -v --verbose Verbose - tells you what it's doing\n\
652 -l --language=<val> Set language when reading rc file\n\
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"));
657 fprintf (stream
, _("\
658 --yydebug Turn on parser debugging\n"));
660 fprintf (stream
, _("\
661 -r Ignored for compatibility with rc\n\
662 @<file> Read options from <file>\n\
663 -h --help Print this help message\n\
664 -V --version Print version information\n"));
665 fprintf (stream
, _("\
666 FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
667 extension if not specified. A single file name is an input file.\n\
668 No input-file is stdin, default rc. No output-file is stdout, default rc.\n"));
670 list_supported_targets (program_name
, stream
);
673 fprintf (stream
, _("Report bugs to %s\n"), REPORT_BUGS_TO
);
678 /* Quote characters that will confuse the shell when we run the preprocessor. */
681 quot (const char *string
)
683 static char *buf
= 0;
684 static int buflen
= 0;
685 int slen
= strlen (string
);
689 if ((buflen
< slen
* 2 + 2) || !buf
)
691 buflen
= slen
* 2 + 2;
694 buf
= (char *) xmalloc (buflen
);
697 for (src
=string
, dest
=buf
; *src
; src
++, dest
++)
699 if (*src
== '(' || *src
== ')' || *src
== ' ')
709 /* 150 isn't special; it's just an arbitrary non-ASCII char value. */
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)
716 static const struct option long_options
[] =
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}
737 /* This keeps gcc happy when using -Wmissing-prototypes -Wstrict-prototypes. */
738 int main (int, char **);
740 /* The main function. */
743 main (int argc
, char **argv
)
746 char *input_filename
;
747 char *output_filename
;
748 enum res_format input_format
;
749 enum res_format input_format_tmp
;
750 enum res_format output_format
;
754 const char *quotedarg
;
756 struct res_directory
*resources
;
759 #if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
760 setlocale (LC_MESSAGES
, "");
762 #if defined (HAVE_SETLOCALE)
763 setlocale (LC_CTYPE
, "");
765 bindtextdomain (PACKAGE
, LOCALEDIR
);
766 textdomain (PACKAGE
);
768 program_name
= argv
[0];
769 xmalloc_set_program_name (program_name
);
771 expandargv (&argc
, &argv
);
774 set_default_bfd_target ();
778 input_filename
= NULL
;
779 output_filename
= NULL
;
780 input_format
= RES_FORMAT_UNKNOWN
;
781 output_format
= RES_FORMAT_UNKNOWN
;
785 language
= 0x409; /* LANG_ENGLISH, SUBLANG_ENGLISH_US. */
788 while ((c
= getopt_long (argc
, argv
, "f:i:l:o:I:J:O:F:D:U:rhHvV", long_options
,
794 input_filename
= optarg
;
798 /* For compatibility with rc we accept "-fo <name>" as being the
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
803 fatal (_("invalid option -f\n"));
808 fatal (_("No filename following the -fo option.\n"));
809 optarg
= argv
[optind
++];
814 output_filename
= optarg
;
818 input_format
= format_from_name (optarg
, 1);
822 output_format
= format_from_name (optarg
, 1);
829 case OPTION_PREPROCESSOR
:
830 preprocessor
= optarg
;
835 if (preprocargs
== NULL
)
837 quotedarg
= quot (optarg
);
838 preprocargs
= xmalloc (strlen (quotedarg
) + 3);
839 sprintf (preprocargs
, "-%c%s", c
, quotedarg
);
845 quotedarg
= quot (optarg
);
846 n
= xmalloc (strlen (preprocargs
) + strlen (quotedarg
) + 4);
847 sprintf (n
, "%s -%c%s", preprocargs
, c
, quotedarg
);
854 /* Ignored for compatibility with rc. */
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
)
866 fprintf (stderr
, _("Option -I is deprecated for setting the input format, please use -J instead.\n"));
867 input_format
= input_format_tmp
;
871 if (preprocargs
== NULL
)
873 quotedarg
= quot (optarg
);
874 preprocargs
= xmalloc (strlen (quotedarg
) + 3);
875 sprintf (preprocargs
, "-I%s", quotedarg
);
881 quotedarg
= quot (optarg
);
882 n
= xmalloc (strlen (preprocargs
) + strlen (quotedarg
) + 4);
883 sprintf (n
, "%s -I%s", preprocargs
, quotedarg
);
889 struct include_dir
*n
, **pp
;
891 n
= (struct include_dir
*) xmalloc (sizeof *n
);
895 for (pp
= &include_dirs
; *pp
!= NULL
; pp
= &(*pp
)->next
)
903 language
= strtol (optarg
, (char **) NULL
, 16);
906 case OPTION_USE_TEMP_FILE
:
910 case OPTION_NO_USE_TEMP_FILE
:
926 print_version ("windres");
935 if (input_filename
== NULL
&& optind
< argc
)
937 input_filename
= argv
[optind
];
941 if (output_filename
== NULL
&& optind
< argc
)
943 output_filename
= argv
[optind
];
950 if (input_format
== RES_FORMAT_UNKNOWN
)
952 if (input_filename
== NULL
)
953 input_format
= RES_FORMAT_RC
;
955 input_format
= format_from_filename (input_filename
, 1);
958 if (output_format
== RES_FORMAT_UNKNOWN
)
960 if (output_filename
== NULL
)
961 output_format
= RES_FORMAT_RC
;
963 output_format
= format_from_filename (output_filename
, 0);
966 /* Read the input file. */
967 switch (input_format
)
972 resources
= read_rc_file (input_filename
, preprocessor
, preprocargs
,
973 language
, use_temp_file
);
976 resources
= read_res_file (input_filename
);
978 case RES_FORMAT_COFF
:
979 resources
= read_coff_rsrc (input_filename
, target
);
983 if (resources
== NULL
)
984 fatal (_("no resources"));
986 /* Sort the resources. This is required for COFF, convenient for
987 rc, and unimportant for res. */
988 resources
= sort_resources (resources
);
990 /* Write the output file. */
993 switch (output_format
)
998 write_rc_file (output_filename
, resources
);
1000 case RES_FORMAT_RES
:
1001 write_res_file (output_filename
, resources
);
1003 case RES_FORMAT_COFF
:
1004 write_coff_file (output_filename
, target
, resources
);