1 /* windres.c -- a program to manipulate Windows resources
2 Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007
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>. */
42 #include "libiberty.h"
43 #include "safe-ctype.h"
48 /* Used by resrc.c at least. */
52 /* An enumeration of format types. */
58 /* Textual RC file. */
60 /* Binary RES file. */
66 /* A structure used to map between format types and strings. */
71 enum res_format format
;
74 /* A mapping between names and format types. */
76 static const struct format_map format_names
[] =
78 { "rc", RES_FORMAT_RC
},
79 { "res", RES_FORMAT_RES
},
80 { "coff", RES_FORMAT_COFF
},
81 { NULL
, RES_FORMAT_UNKNOWN
}
84 /* A mapping from file extensions to format types. */
86 static const struct format_map format_fileexts
[] =
88 { "rc", RES_FORMAT_RC
},
89 { "res", RES_FORMAT_RES
},
90 { "exe", RES_FORMAT_COFF
},
91 { "obj", RES_FORMAT_COFF
},
92 { "o", RES_FORMAT_COFF
},
93 { NULL
, RES_FORMAT_UNKNOWN
}
96 /* A list of include directories. */
100 struct include_dir
*next
;
104 static struct include_dir
*include_dirs
;
106 /* Static functions. */
108 static void res_init (void);
109 static int extended_menuitems (const struct menuitem
*);
110 static enum res_format
format_from_name (const char *, int);
111 static enum res_format
format_from_filename (const char *, int);
112 static void usage (FILE *, int);
113 static int cmp_res_entry (const void *, const void *);
114 static struct res_directory
*sort_resources (struct res_directory
*);
115 static void reswr_init (void);
116 static const char * quot (const char *);
118 /* When we are building a resource tree, we allocate everything onto
119 an obstack, so that we can free it all at once if we want. */
121 #define obstack_chunk_alloc xmalloc
122 #define obstack_chunk_free free
124 /* The resource building obstack. */
126 static struct obstack res_obstack
;
128 /* Initialize the resource building obstack. */
133 obstack_init (&res_obstack
);
136 /* Allocate space on the resource building obstack. */
139 res_alloc (size_t bytes
)
141 return (void *) obstack_alloc (&res_obstack
, bytes
);
144 /* We also use an obstack to save memory used while writing out a set
147 static struct obstack reswr_obstack
;
149 /* Initialize the resource writing obstack. */
154 obstack_init (&reswr_obstack
);
157 /* Allocate space on the resource writing obstack. */
160 reswr_alloc (size_t bytes
)
162 return (void *) obstack_alloc (&reswr_obstack
, bytes
);
165 /* Open a file using the include directory search list. */
168 open_file_search (const char *filename
, const char *mode
, const char *errmsg
,
169 char **real_filename
)
172 struct include_dir
*d
;
174 e
= fopen (filename
, mode
);
177 *real_filename
= xstrdup (filename
);
183 for (d
= include_dirs
; d
!= NULL
; d
= d
->next
)
187 n
= (char *) xmalloc (strlen (d
->dir
) + strlen (filename
) + 2);
188 sprintf (n
, "%s/%s", d
->dir
, filename
);
201 fatal (_("can't open %s `%s': %s"), errmsg
, filename
, strerror (errno
));
203 /* Return a value to avoid a compiler warning. */
207 /* Compare two resource ID's. We consider name entries to come before
208 numeric entries, because that is how they appear in the COFF .rsrc
212 res_id_cmp (struct res_id a
, struct res_id b
)
220 else if (a
.u
.id
< b
.u
.id
)
227 unichar
*as
, *ase
, *bs
, *bse
;
233 ase
= as
+ a
.u
.n
.length
;
235 bse
= bs
+ b
.u
.n
.length
;
243 i
= (int) *as
- (int) *bs
;
257 /* Print a resource ID. */
260 res_id_print (FILE *stream
, struct res_id id
, int quote
)
263 fprintf (stream
, "%lu", id
.u
.id
);
268 unicode_print (stream
, id
.u
.n
.name
, id
.u
.n
.length
);
274 /* Print a list of resource ID's. */
277 res_ids_print (FILE *stream
, int cids
, const struct res_id
*ids
)
281 for (i
= 0; i
< cids
; i
++)
283 res_id_print (stream
, ids
[i
], 1);
285 fprintf (stream
, ": ");
289 /* Convert an ASCII string to a resource ID. */
292 res_string_to_id (struct res_id
*res_id
, const char *string
)
295 unicode_from_ascii (&res_id
->u
.n
.length
, &res_id
->u
.n
.name
, string
);
298 /* Define a resource. The arguments are the resource tree, RESOURCES,
299 and the location at which to put it in the tree, CIDS and IDS.
300 This returns a newly allocated res_resource structure, which the
301 caller is expected to initialize. If DUPOK is non-zero, then if a
302 resource with this ID exists, it is returned. Otherwise, a warning
303 is issued, and a new resource is created replacing the existing
306 struct res_resource
*
307 define_resource (struct res_directory
**resources
, int cids
,
308 const struct res_id
*ids
, int dupok
)
310 struct res_entry
*re
= NULL
;
314 for (i
= 0; i
< cids
; i
++)
316 struct res_entry
**pp
;
318 if (*resources
== NULL
)
320 static unsigned long timeval
;
322 /* Use the same timestamp for every resource created in a
325 timeval
= time (NULL
);
327 *resources
= ((struct res_directory
*)
328 res_alloc (sizeof **resources
));
329 (*resources
)->characteristics
= 0;
330 (*resources
)->time
= timeval
;
331 (*resources
)->major
= 0;
332 (*resources
)->minor
= 0;
333 (*resources
)->entries
= NULL
;
336 for (pp
= &(*resources
)->entries
; *pp
!= NULL
; pp
= &(*pp
)->next
)
337 if (res_id_cmp ((*pp
)->id
, ids
[i
]) == 0)
344 re
= (struct res_entry
*) res_alloc (sizeof *re
);
365 fprintf (stderr
, "%s: ", program_name
);
366 res_ids_print (stderr
, i
, ids
);
367 fprintf (stderr
, _(": expected to be a directory\n"));
371 resources
= &re
->u
.dir
;
377 fprintf (stderr
, "%s: ", program_name
);
378 res_ids_print (stderr
, cids
, ids
);
379 fprintf (stderr
, _(": expected to be a leaf\n"));
383 if (re
->u
.res
!= NULL
)
388 fprintf (stderr
, _("%s: warning: "), program_name
);
389 res_ids_print (stderr
, cids
, ids
);
390 fprintf (stderr
, _(": duplicate value\n"));
393 re
->u
.res
= ((struct res_resource
*)
394 res_alloc (sizeof (struct res_resource
)));
395 memset (re
->u
.res
, 0, sizeof (struct res_resource
));
397 re
->u
.res
->type
= RES_TYPE_UNINITIALIZED
;
401 /* Define a standard resource. This is a version of define_resource
402 that just takes type, name, and language arguments. */
404 struct res_resource
*
405 define_standard_resource (struct res_directory
**resources
, int type
,
406 struct res_id name
, int language
, int dupok
)
414 a
[2].u
.id
= language
;
415 return define_resource (resources
, 3, a
, dupok
);
418 /* Comparison routine for resource sorting. */
421 cmp_res_entry (const void *p1
, const void *p2
)
423 const struct res_entry
**re1
, **re2
;
425 re1
= (const struct res_entry
**) p1
;
426 re2
= (const struct res_entry
**) p2
;
427 return res_id_cmp ((*re1
)->id
, (*re2
)->id
);
430 /* Sort the resources. */
432 static struct res_directory
*
433 sort_resources (struct res_directory
*resdir
)
436 struct res_entry
*re
;
437 struct res_entry
**a
;
439 if (resdir
->entries
== NULL
)
443 for (re
= resdir
->entries
; re
!= NULL
; re
= re
->next
)
446 /* This is a recursive routine, so using xmalloc is probably better
448 a
= (struct res_entry
**) xmalloc (c
* sizeof (struct res_entry
*));
450 for (i
= 0, re
= resdir
->entries
; re
!= NULL
; re
= re
->next
, i
++)
453 qsort (a
, c
, sizeof (struct res_entry
*), cmp_res_entry
);
455 resdir
->entries
= a
[0];
456 for (i
= 0; i
< c
- 1; i
++)
457 a
[i
]->next
= a
[i
+ 1];
462 /* Now sort the subdirectories. */
464 for (re
= resdir
->entries
; re
!= NULL
; re
= re
->next
)
466 re
->u
.dir
= sort_resources (re
->u
.dir
);
471 /* Return whether the dialog resource DIALOG is a DIALOG or a
475 extended_dialog (const struct dialog
*dialog
)
477 const struct dialog_control
*c
;
479 if (dialog
->ex
!= NULL
)
482 for (c
= dialog
->controls
; c
!= NULL
; c
= c
->next
)
483 if (c
->data
!= NULL
|| c
->help
!= 0)
489 /* Return whether MENUITEMS are a MENU or a MENUEX. */
492 extended_menu (const struct menu
*menu
)
494 return extended_menuitems (menu
->items
);
498 extended_menuitems (const struct menuitem
*menuitems
)
500 const struct menuitem
*mi
;
502 for (mi
= menuitems
; mi
!= NULL
; mi
= mi
->next
)
504 if (mi
->help
!= 0 || mi
->state
!= 0)
506 if (mi
->popup
!= NULL
&& mi
->id
!= 0)
509 & ~ (MENUITEM_CHECKED
513 | MENUITEM_MENUBARBREAK
514 | MENUITEM_MENUBREAK
))
517 if (mi
->popup
!= NULL
)
519 if (extended_menuitems (mi
->popup
))
527 /* Convert a string to a format type, or exit if it can't be done. */
529 static enum res_format
530 format_from_name (const char *name
, int exit_on_error
)
532 const struct format_map
*m
;
534 for (m
= format_names
; m
->name
!= NULL
; m
++)
535 if (strcasecmp (m
->name
, name
) == 0)
538 if (m
->name
== NULL
&& exit_on_error
)
540 non_fatal (_("unknown format type `%s'"), name
);
541 fprintf (stderr
, _("%s: supported formats:"), program_name
);
542 for (m
= format_names
; m
->name
!= NULL
; m
++)
543 fprintf (stderr
, " %s", m
->name
);
544 fprintf (stderr
, "\n");
551 /* Work out a format type given a file name. If INPUT is non-zero,
552 it's OK to look at the file itself. */
554 static enum res_format
555 format_from_filename (const char *filename
, int input
)
559 unsigned char b1
, b2
, b3
, b4
, b5
;
562 /* If we have an extension, see if we recognize it as implying a
563 particular format. */
564 ext
= strrchr (filename
, '.');
567 const struct format_map
*m
;
570 for (m
= format_fileexts
; m
->name
!= NULL
; m
++)
571 if (strcasecmp (m
->name
, ext
) == 0)
575 /* If we don't recognize the name of an output file, assume it's a
578 return RES_FORMAT_COFF
;
580 /* Read the first few bytes of the file to see if we can guess what
582 e
= fopen (filename
, FOPEN_RB
);
584 fatal ("%s: %s", filename
, strerror (errno
));
594 /* A PE executable starts with 0x4d 0x5a. */
595 if (b1
== 0x4d && b2
== 0x5a)
596 return RES_FORMAT_COFF
;
598 /* A COFF .o file starts with a COFF magic number. */
599 magic
= (b2
<< 8) | b1
;
602 case 0x14c: /* i386 */
603 case 0x166: /* MIPS */
604 case 0x184: /* Alpha */
605 case 0x268: /* 68k */
606 case 0x1f0: /* PowerPC */
608 return RES_FORMAT_COFF
;
611 /* A RES file starts with 0x0 0x0 0x0 0x0 0x20 0x0 0x0 0x0. */
612 if (b1
== 0 && b2
== 0 && b3
== 0 && b4
== 0 && b5
== 0x20)
613 return RES_FORMAT_RES
;
615 /* If every character is printable or space, assume it's an RC file. */
616 if ((ISPRINT (b1
) || ISSPACE (b1
))
617 && (ISPRINT (b2
) || ISSPACE (b2
))
618 && (ISPRINT (b3
) || ISSPACE (b3
))
619 && (ISPRINT (b4
) || ISSPACE (b4
))
620 && (ISPRINT (b5
) || ISSPACE (b5
)))
621 return RES_FORMAT_RC
;
623 /* Otherwise, we give up. */
624 fatal (_("can not determine type of file `%s'; use the -J option"),
627 /* Return something to silence the compiler warning. */
628 return RES_FORMAT_UNKNOWN
;
631 /* Print a usage message and exit. */
634 usage (FILE *stream
, int status
)
636 fprintf (stream
, _("Usage: %s [option(s)] [input-file] [output-file]\n"),
638 fprintf (stream
, _(" The options are:\n\
639 -i --input=<file> Name input file\n\
640 -o --output=<file> Name output file\n\
641 -J --input-format=<format> Specify input format\n\
642 -O --output-format=<format> Specify output format\n\
643 -F --target=<target> Specify COFF target\n\
644 --preprocessor=<program> Program to use to preprocess rc file\n\
645 -I --include-dir=<dir> Include directory when preprocessing rc file\n\
646 -D --define <sym>[=<val>] Define SYM when preprocessing rc file\n\
647 -U --undefine <sym> Undefine SYM when preprocessing rc file\n\
648 -v --verbose Verbose - tells you what it's doing\n\
649 -l --language=<val> Set language when reading rc file\n\
650 --use-temp-file Use a temporary file instead of popen to read\n\
651 the preprocessor output\n\
652 --no-use-temp-file Use popen (default)\n"));
654 fprintf (stream
, _("\
655 --yydebug Turn on parser debugging\n"));
657 fprintf (stream
, _("\
658 -r Ignored for compatibility with rc\n\
659 @<file> Read options from <file>\n\
660 -h --help Print this help message\n\
661 -V --version Print version information\n"));
662 fprintf (stream
, _("\
663 FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
664 extension if not specified. A single file name is an input file.\n\
665 No input-file is stdin, default rc. No output-file is stdout, default rc.\n"));
667 list_supported_targets (program_name
, stream
);
669 if (REPORT_BUGS_TO
[0] && status
== 0)
670 fprintf (stream
, _("Report bugs to %s\n"), REPORT_BUGS_TO
);
675 /* Quote characters that will confuse the shell when we run the preprocessor. */
678 quot (const char *string
)
680 static char *buf
= 0;
681 static int buflen
= 0;
682 int slen
= strlen (string
);
686 if ((buflen
< slen
* 2 + 2) || !buf
)
688 buflen
= slen
* 2 + 2;
691 buf
= (char *) xmalloc (buflen
);
694 for (src
=string
, dest
=buf
; *src
; src
++, dest
++)
696 if (*src
== '(' || *src
== ')' || *src
== ' ')
706 /* 150 isn't special; it's just an arbitrary non-ASCII char value. */
708 #define OPTION_PREPROCESSOR 150
709 #define OPTION_USE_TEMP_FILE (OPTION_PREPROCESSOR + 1)
710 #define OPTION_NO_USE_TEMP_FILE (OPTION_USE_TEMP_FILE + 1)
711 #define OPTION_YYDEBUG (OPTION_NO_USE_TEMP_FILE + 1)
713 static const struct option long_options
[] =
715 {"input", required_argument
, 0, 'i'},
716 {"output", required_argument
, 0, 'o'},
717 {"input-format", required_argument
, 0, 'J'},
718 {"output-format", required_argument
, 0, 'O'},
719 {"target", required_argument
, 0, 'F'},
720 {"preprocessor", required_argument
, 0, OPTION_PREPROCESSOR
},
721 {"include-dir", required_argument
, 0, 'I'},
722 {"define", required_argument
, 0, 'D'},
723 {"undefine", required_argument
, 0, 'U'},
724 {"verbose", no_argument
, 0, 'v'},
725 {"language", required_argument
, 0, 'l'},
726 {"use-temp-file", no_argument
, 0, OPTION_USE_TEMP_FILE
},
727 {"no-use-temp-file", no_argument
, 0, OPTION_NO_USE_TEMP_FILE
},
728 {"yydebug", no_argument
, 0, OPTION_YYDEBUG
},
729 {"version", no_argument
, 0, 'V'},
730 {"help", no_argument
, 0, 'h'},
731 {0, no_argument
, 0, 0}
734 /* This keeps gcc happy when using -Wmissing-prototypes -Wstrict-prototypes. */
735 int main (int, char **);
737 /* The main function. */
740 main (int argc
, char **argv
)
743 char *input_filename
;
744 char *output_filename
;
745 enum res_format input_format
;
746 enum res_format input_format_tmp
;
747 enum res_format output_format
;
751 const char *quotedarg
;
753 struct res_directory
*resources
;
756 #if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
757 setlocale (LC_MESSAGES
, "");
759 #if defined (HAVE_SETLOCALE)
760 setlocale (LC_CTYPE
, "");
762 bindtextdomain (PACKAGE
, LOCALEDIR
);
763 textdomain (PACKAGE
);
765 program_name
= argv
[0];
766 xmalloc_set_program_name (program_name
);
768 expandargv (&argc
, &argv
);
771 set_default_bfd_target ();
775 input_filename
= NULL
;
776 output_filename
= NULL
;
777 input_format
= RES_FORMAT_UNKNOWN
;
778 output_format
= RES_FORMAT_UNKNOWN
;
782 language
= 0x409; /* LANG_ENGLISH, SUBLANG_ENGLISH_US. */
785 while ((c
= getopt_long (argc
, argv
, "f:i:l:o:I:J:O:F:D:U:rhHvV", long_options
,
791 input_filename
= optarg
;
795 /* For compatibility with rc we accept "-fo <name>" as being the
796 equivalent of "-o <name>". We do not advertise this fact
797 though, as we do not want users to use non-GNU like command
800 fatal (_("invalid option -f\n"));
805 fatal (_("No filename following the -fo option.\n"));
806 optarg
= argv
[optind
++];
811 output_filename
= optarg
;
815 input_format
= format_from_name (optarg
, 1);
819 output_format
= format_from_name (optarg
, 1);
826 case OPTION_PREPROCESSOR
:
827 preprocessor
= optarg
;
832 if (preprocargs
== NULL
)
834 quotedarg
= quot (optarg
);
835 preprocargs
= xmalloc (strlen (quotedarg
) + 3);
836 sprintf (preprocargs
, "-%c%s", c
, quotedarg
);
842 quotedarg
= quot (optarg
);
843 n
= xmalloc (strlen (preprocargs
) + strlen (quotedarg
) + 4);
844 sprintf (n
, "%s -%c%s", preprocargs
, c
, quotedarg
);
851 /* Ignored for compatibility with rc. */
859 /* For backward compatibility, should be removed in the future. */
860 input_format_tmp
= format_from_name (optarg
, 0);
861 if (input_format_tmp
!= RES_FORMAT_UNKNOWN
)
863 fprintf (stderr
, _("Option -I is deprecated for setting the input format, please use -J instead.\n"));
864 input_format
= input_format_tmp
;
868 if (preprocargs
== NULL
)
870 quotedarg
= quot (optarg
);
871 preprocargs
= xmalloc (strlen (quotedarg
) + 3);
872 sprintf (preprocargs
, "-I%s", quotedarg
);
878 quotedarg
= quot (optarg
);
879 n
= xmalloc (strlen (preprocargs
) + strlen (quotedarg
) + 4);
880 sprintf (n
, "%s -I%s", preprocargs
, quotedarg
);
886 struct include_dir
*n
, **pp
;
888 n
= (struct include_dir
*) xmalloc (sizeof *n
);
892 for (pp
= &include_dirs
; *pp
!= NULL
; pp
= &(*pp
)->next
)
900 language
= strtol (optarg
, (char **) NULL
, 16);
903 case OPTION_USE_TEMP_FILE
:
907 case OPTION_NO_USE_TEMP_FILE
:
923 print_version ("windres");
932 if (input_filename
== NULL
&& optind
< argc
)
934 input_filename
= argv
[optind
];
938 if (output_filename
== NULL
&& optind
< argc
)
940 output_filename
= argv
[optind
];
947 if (input_format
== RES_FORMAT_UNKNOWN
)
949 if (input_filename
== NULL
)
950 input_format
= RES_FORMAT_RC
;
952 input_format
= format_from_filename (input_filename
, 1);
955 if (output_format
== RES_FORMAT_UNKNOWN
)
957 if (output_filename
== NULL
)
958 output_format
= RES_FORMAT_RC
;
960 output_format
= format_from_filename (output_filename
, 0);
963 /* Read the input file. */
964 switch (input_format
)
969 resources
= read_rc_file (input_filename
, preprocessor
, preprocargs
,
970 language
, use_temp_file
);
973 resources
= read_res_file (input_filename
);
975 case RES_FORMAT_COFF
:
976 resources
= read_coff_rsrc (input_filename
, target
);
980 if (resources
== NULL
)
981 fatal (_("no resources"));
983 /* Sort the resources. This is required for COFF, convenient for
984 rc, and unimportant for res. */
985 resources
= sort_resources (resources
);
987 /* Write the output file. */
990 switch (output_format
)
995 write_rc_file (output_filename
, resources
);
998 write_res_file (output_filename
, resources
);
1000 case RES_FORMAT_COFF
:
1001 write_coff_file (output_filename
, target
, resources
);