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_PREPROCESSOR 150
110 #define OPTION_USE_TEMP_FILE (OPTION_PREPROCESSOR + 1)
111 #define OPTION_NO_USE_TEMP_FILE (OPTION_USE_TEMP_FILE + 1)
112 #define OPTION_YYDEBUG (OPTION_NO_USE_TEMP_FILE + 1)
114 static const struct option long_options
[] =
116 {"define", required_argument
, 0, 'D'},
117 {"help", no_argument
, 0, 'h'},
118 {"include-dir", required_argument
, 0, 'I'},
119 {"input-format", required_argument
, 0, 'J'},
120 {"language", required_argument
, 0, 'l'},
121 {"output-format", required_argument
, 0, 'O'},
122 {"preprocessor", required_argument
, 0, OPTION_PREPROCESSOR
},
123 {"target", required_argument
, 0, 'F'},
124 {"undefine", required_argument
, 0, 'U'},
125 {"use-temp-file", no_argument
, 0, OPTION_USE_TEMP_FILE
},
126 {"no-use-temp-file", no_argument
, 0, OPTION_NO_USE_TEMP_FILE
},
127 {"verbose", no_argument
, 0, 'v'},
128 {"version", no_argument
, 0, 'V'},
129 {"yydebug", no_argument
, 0, OPTION_YYDEBUG
},
130 {0, no_argument
, 0, 0}
133 /* Static functions. */
135 static void res_init
PARAMS ((void));
136 static int extended_menuitems
PARAMS ((const struct menuitem
*));
137 static enum res_format format_from_name
PARAMS ((const char *, int));
138 static enum res_format format_from_filename
PARAMS ((const char *, int));
139 static void usage
PARAMS ((FILE *, int));
140 static int cmp_res_entry
PARAMS ((const PTR
, const PTR
));
141 static struct res_directory
*sort_resources
PARAMS ((struct res_directory
*));
142 static void reswr_init
PARAMS ((void));
143 static const char * quot
PARAMS ((const char *));
145 /* When we are building a resource tree, we allocate everything onto
146 an obstack, so that we can free it all at once if we want. */
148 #define obstack_chunk_alloc xmalloc
149 #define obstack_chunk_free free
151 /* The resource building obstack. */
153 static struct obstack res_obstack
;
155 /* Initialize the resource building obstack. */
160 obstack_init (&res_obstack
);
163 /* Allocate space on the resource building obstack. */
169 return (PTR
) obstack_alloc (&res_obstack
, bytes
);
172 /* We also use an obstack to save memory used while writing out a set
175 static struct obstack reswr_obstack
;
177 /* Initialize the resource writing obstack. */
182 obstack_init (&reswr_obstack
);
185 /* Allocate space on the resource writing obstack. */
191 return (PTR
) obstack_alloc (&reswr_obstack
, bytes
);
194 /* Open a file using the include directory search list. */
197 open_file_search (filename
, mode
, errmsg
, real_filename
)
198 const char *filename
;
201 char **real_filename
;
204 struct include_dir
*d
;
206 e
= fopen (filename
, mode
);
209 *real_filename
= xstrdup (filename
);
215 for (d
= include_dirs
; d
!= NULL
; d
= d
->next
)
219 n
= (char *) xmalloc (strlen (d
->dir
) + strlen (filename
) + 2);
220 sprintf (n
, "%s/%s", d
->dir
, filename
);
233 fatal (_("can't open %s `%s': %s"), errmsg
, filename
, strerror (errno
));
235 /* Return a value to avoid a compiler warning. */
239 /* Compare two resource ID's. We consider name entries to come before
240 numeric entries, because that is how they appear in the COFF .rsrc
254 else if (a
.u
.id
< b
.u
.id
)
261 unichar
*as
, *ase
, *bs
, *bse
;
267 ase
= as
+ a
.u
.n
.length
;
269 bse
= bs
+ b
.u
.n
.length
;
277 i
= (int) *as
- (int) *bs
;
291 /* Print a resource ID. */
294 res_id_print (stream
, id
, quote
)
300 fprintf (stream
, "%lu", id
.u
.id
);
305 unicode_print (stream
, id
.u
.n
.name
, id
.u
.n
.length
);
311 /* Print a list of resource ID's. */
314 res_ids_print (stream
, cids
, ids
)
317 const struct res_id
*ids
;
321 for (i
= 0; i
< cids
; i
++)
323 res_id_print (stream
, ids
[i
], 1);
325 fprintf (stream
, ": ");
329 /* Convert an ASCII string to a resource ID. */
332 res_string_to_id (res_id
, string
)
333 struct res_id
*res_id
;
337 unicode_from_ascii (&res_id
->u
.n
.length
, &res_id
->u
.n
.name
, string
);
340 /* Define a resource. The arguments are the resource tree, RESOURCES,
341 and the location at which to put it in the tree, CIDS and IDS.
342 This returns a newly allocated res_resource structure, which the
343 caller is expected to initialize. If DUPOK is non-zero, then if a
344 resource with this ID exists, it is returned. Otherwise, a warning
345 is issued, and a new resource is created replacing the existing
348 struct res_resource
*
349 define_resource (resources
, cids
, ids
, dupok
)
350 struct res_directory
**resources
;
352 const struct res_id
*ids
;
355 struct res_entry
*re
= NULL
;
359 for (i
= 0; i
< cids
; i
++)
361 struct res_entry
**pp
;
363 if (*resources
== NULL
)
365 static unsigned long timeval
;
367 /* Use the same timestamp for every resource created in a
370 timeval
= time (NULL
);
372 *resources
= ((struct res_directory
*)
373 res_alloc (sizeof **resources
));
374 (*resources
)->characteristics
= 0;
375 (*resources
)->time
= timeval
;
376 (*resources
)->major
= 0;
377 (*resources
)->minor
= 0;
378 (*resources
)->entries
= NULL
;
381 for (pp
= &(*resources
)->entries
; *pp
!= NULL
; pp
= &(*pp
)->next
)
382 if (res_id_cmp ((*pp
)->id
, ids
[i
]) == 0)
389 re
= (struct res_entry
*) res_alloc (sizeof *re
);
410 fprintf (stderr
, "%s: ", program_name
);
411 res_ids_print (stderr
, i
, ids
);
412 fprintf (stderr
, _(": expected to be a directory\n"));
416 resources
= &re
->u
.dir
;
422 fprintf (stderr
, "%s: ", program_name
);
423 res_ids_print (stderr
, cids
, ids
);
424 fprintf (stderr
, _(": expected to be a leaf\n"));
428 if (re
->u
.res
!= NULL
)
433 fprintf (stderr
, _("%s: warning: "), program_name
);
434 res_ids_print (stderr
, cids
, ids
);
435 fprintf (stderr
, _(": duplicate value\n"));
438 re
->u
.res
= ((struct res_resource
*)
439 res_alloc (sizeof (struct res_resource
)));
440 memset (re
->u
.res
, 0, sizeof (struct res_resource
));
442 re
->u
.res
->type
= RES_TYPE_UNINITIALIZED
;
446 /* Define a standard resource. This is a version of define_resource
447 that just takes type, name, and language arguments. */
449 struct res_resource
*
450 define_standard_resource (resources
, type
, name
, language
, dupok
)
451 struct res_directory
**resources
;
463 a
[2].u
.id
= language
;
464 return define_resource (resources
, 3, a
, dupok
);
467 /* Comparison routine for resource sorting. */
470 cmp_res_entry (p1
, p2
)
474 const struct res_entry
**re1
, **re2
;
476 re1
= (const struct res_entry
**) p1
;
477 re2
= (const struct res_entry
**) p2
;
478 return res_id_cmp ((*re1
)->id
, (*re2
)->id
);
481 /* Sort the resources. */
483 static struct res_directory
*
484 sort_resources (resdir
)
485 struct res_directory
*resdir
;
488 struct res_entry
*re
;
489 struct res_entry
**a
;
491 if (resdir
->entries
== NULL
)
495 for (re
= resdir
->entries
; re
!= NULL
; re
= re
->next
)
498 /* This is a recursive routine, so using xmalloc is probably better
500 a
= (struct res_entry
**) xmalloc (c
* sizeof (struct res_entry
*));
502 for (i
= 0, re
= resdir
->entries
; re
!= NULL
; re
= re
->next
, i
++)
505 qsort (a
, c
, sizeof (struct res_entry
*), cmp_res_entry
);
507 resdir
->entries
= a
[0];
508 for (i
= 0; i
< c
- 1; i
++)
509 a
[i
]->next
= a
[i
+ 1];
514 /* Now sort the subdirectories. */
516 for (re
= resdir
->entries
; re
!= NULL
; re
= re
->next
)
518 re
->u
.dir
= sort_resources (re
->u
.dir
);
523 /* Return whether the dialog resource DIALOG is a DIALOG or a
527 extended_dialog (dialog
)
528 const struct dialog
*dialog
;
530 const struct dialog_control
*c
;
532 if (dialog
->ex
!= NULL
)
535 for (c
= dialog
->controls
; c
!= NULL
; c
= c
->next
)
536 if (c
->data
!= NULL
|| c
->help
!= 0)
542 /* Return whether MENUITEMS are a MENU or a MENUEX. */
546 const struct menu
*menu
;
548 return extended_menuitems (menu
->items
);
552 extended_menuitems (menuitems
)
553 const struct menuitem
*menuitems
;
555 const struct menuitem
*mi
;
557 for (mi
= menuitems
; mi
!= NULL
; mi
= mi
->next
)
559 if (mi
->help
!= 0 || mi
->state
!= 0)
561 if (mi
->popup
!= NULL
&& mi
->id
!= 0)
564 & ~ (MENUITEM_CHECKED
568 | MENUITEM_MENUBARBREAK
569 | MENUITEM_MENUBREAK
))
572 if (mi
->popup
!= NULL
)
574 if (extended_menuitems (mi
->popup
))
582 /* Convert a string to a format type, or exit if it can't be done. */
584 static enum res_format
585 format_from_name (name
, exit_on_error
)
589 const struct format_map
*m
;
591 for (m
= format_names
; m
->name
!= NULL
; m
++)
592 if (strcasecmp (m
->name
, name
) == 0)
595 if (m
->name
== NULL
&& exit_on_error
)
597 non_fatal (_("unknown format type `%s'"), name
);
598 fprintf (stderr
, _("%s: supported formats:"), program_name
);
599 for (m
= format_names
; m
->name
!= NULL
; m
++)
600 fprintf (stderr
, " %s", m
->name
);
601 fprintf (stderr
, "\n");
608 /* Work out a format type given a file name. If INPUT is non-zero,
609 it's OK to look at the file itself. */
611 static enum res_format
612 format_from_filename (filename
, input
)
613 const char *filename
;
618 unsigned char b1
, b2
, b3
, b4
, b5
;
621 /* If we have an extension, see if we recognize it as implying a
622 particular format. */
623 ext
= strrchr (filename
, '.');
626 const struct format_map
*m
;
629 for (m
= format_fileexts
; m
->name
!= NULL
; m
++)
630 if (strcasecmp (m
->name
, ext
) == 0)
634 /* If we don't recognize the name of an output file, assume it's a
637 return RES_FORMAT_COFF
;
639 /* Read the first few bytes of the file to see if we can guess what
641 e
= fopen (filename
, FOPEN_RB
);
643 fatal ("%s: %s", filename
, strerror (errno
));
653 /* A PE executable starts with 0x4d 0x5a. */
654 if (b1
== 0x4d && b2
== 0x5a)
655 return RES_FORMAT_COFF
;
657 /* A COFF .o file starts with a COFF magic number. */
658 magic
= (b2
<< 8) | b1
;
661 case 0x14c: /* i386 */
662 case 0x166: /* MIPS */
663 case 0x184: /* Alpha */
664 case 0x268: /* 68k */
665 case 0x1f0: /* PowerPC */
667 return RES_FORMAT_COFF
;
670 /* A RES file starts with 0x0 0x0 0x0 0x0 0x20 0x0 0x0 0x0. */
671 if (b1
== 0 && b2
== 0 && b3
== 0 && b4
== 0 && b5
== 0x20)
672 return RES_FORMAT_RES
;
674 /* If every character is printable or space, assume it's an RC file. */
675 if ((ISPRINT (b1
) || ISSPACE (b1
))
676 && (ISPRINT (b2
) || ISSPACE (b2
))
677 && (ISPRINT (b3
) || ISSPACE (b3
))
678 && (ISPRINT (b4
) || ISSPACE (b4
))
679 && (ISPRINT (b5
) || ISSPACE (b5
)))
680 return RES_FORMAT_RC
;
682 /* Otherwise, we give up. */
683 fatal (_("can not determine type of file `%s'; use the -I option"),
686 /* Return something to silence the compiler warning. */
687 return RES_FORMAT_UNKNOWN
;
690 /* Print a usage message and exit. */
693 usage (stream
, status
)
697 fprintf (stream
, _("Usage: %s [option(s)] [input-file] [output-file]\n"),
699 fprintf (stream
, _(" The options are:\n\
700 -i --input=<file> Name input file\n\
701 -o --output=<file> Name output file\n\
702 -J --input-format=<format> Specify input format\n\
703 -O --output-format=<format> Specify output format\n\
704 -F --target=<target> Specify COFF target\n\
705 --preprocessor=<program> Program to use to preprocess rc file\n\
706 -I --include-dir=<dir> Include directory when preprocessing rc file\n\
707 -D --define <sym>[=<val>] Define SYM when preprocessing rc file\n\
708 -U --undefine <sym> Undefine SYM when preprocessing rc file\n\
709 -v --verbose Verbose - tells you what it's doing\n\
710 -l --language=<val> Set language when reading rc file\n\
711 --use-temp-file Use a temporary file instead of popen to read\n\
712 the preprocessor output\n\
713 --no-use-temp-file Use popen (default)\n"));
715 fprintf (stream
, _("\
716 --yydebug Turn on parser debugging\n"));
718 fprintf (stream
, _("\
719 -r Ignored for compatibility with rc\n\
720 -h --help Print this help message\n\
721 -V --version Print version information\n"));
722 fprintf (stream
, _("\
723 FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
724 extension if not specified. A single file name is an input file.\n\
725 No input-file is stdin, default rc. No output-file is stdout, default rc.\n"));
727 list_supported_targets (program_name
, stream
);
730 fprintf (stream
, _("Report bugs to %s\n"), REPORT_BUGS_TO
);
735 /* Quote characters that will confuse the shell when we run the preprocessor. */
741 static char *buf
= 0;
742 static int buflen
= 0;
743 int slen
= strlen (string
);
747 if ((buflen
< slen
* 2 + 2) || !buf
)
749 buflen
= slen
* 2 + 2;
752 buf
= (char *) xmalloc (buflen
);
755 for (src
=string
, dest
=buf
; *src
; src
++, dest
++)
757 if (*src
== '(' || *src
== ')' || *src
== ' ')
765 /* This keeps gcc happy when using -Wmissing-prototypes -Wstrict-prototypes. */
766 int main
PARAMS ((int, char **));
768 /* The main function. */
776 char *input_filename
;
777 char *output_filename
;
778 enum res_format input_format
;
779 enum res_format input_format_tmp
;
780 enum res_format output_format
;
784 const char *quotedarg
;
786 struct res_directory
*resources
;
789 #if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
790 setlocale (LC_MESSAGES
, "");
792 #if defined (HAVE_SETLOCALE)
793 setlocale (LC_CTYPE
, "");
795 bindtextdomain (PACKAGE
, LOCALEDIR
);
796 textdomain (PACKAGE
);
798 program_name
= argv
[0];
799 xmalloc_set_program_name (program_name
);
802 set_default_bfd_target ();
806 input_filename
= NULL
;
807 output_filename
= NULL
;
808 input_format
= RES_FORMAT_UNKNOWN
;
809 output_format
= RES_FORMAT_UNKNOWN
;
813 language
= 0x409; /* LANG_ENGLISH, SUBLANG_ENGLISH_US. */
816 while ((c
= getopt_long (argc
, argv
, "i:l:o:I:J:O:F:D:U:rhHvV", long_options
,
822 input_filename
= optarg
;
826 output_filename
= optarg
;
830 input_format
= format_from_name (optarg
, 1);
834 output_format
= format_from_name (optarg
, 1);
841 case OPTION_PREPROCESSOR
:
842 preprocessor
= optarg
;
847 if (preprocargs
== NULL
)
849 quotedarg
= quot (optarg
);
850 preprocargs
= xmalloc (strlen (quotedarg
) + 3);
851 sprintf (preprocargs
, "-%c%s", c
, quotedarg
);
857 quotedarg
= quot (optarg
);
858 n
= xmalloc (strlen (preprocargs
) + strlen (quotedarg
) + 4);
859 sprintf (n
, "%s -%c%s", preprocargs
, c
, quotedarg
);
866 /* Ignored for compatibility with rc. */
874 /* For backward compatibility, should be removed in the future. */
875 input_format_tmp
= format_from_name (optarg
, 0);
876 if (input_format_tmp
!= RES_FORMAT_UNKNOWN
)
878 fprintf (stderr
, _("Option -I is deprecated for setting the input format, please use -J instead.\n"));
879 input_format
= input_format_tmp
;
883 if (preprocargs
== NULL
)
885 quotedarg
= quot (optarg
);
886 preprocargs
= xmalloc (strlen (quotedarg
) + 3);
887 sprintf (preprocargs
, "-I%s", quotedarg
);
893 quotedarg
= quot (optarg
);
894 n
= xmalloc (strlen (preprocargs
) + strlen (quotedarg
) + 4);
895 sprintf (n
, "%s -I%s", preprocargs
, quotedarg
);
901 struct include_dir
*n
, **pp
;
903 n
= (struct include_dir
*) xmalloc (sizeof *n
);
907 for (pp
= &include_dirs
; *pp
!= NULL
; pp
= &(*pp
)->next
)
915 language
= strtol (optarg
, (char **) NULL
, 16);
918 case OPTION_USE_TEMP_FILE
:
922 case OPTION_NO_USE_TEMP_FILE
:
938 print_version ("windres");
947 if (input_filename
== NULL
&& optind
< argc
)
949 input_filename
= argv
[optind
];
953 if (output_filename
== NULL
&& optind
< argc
)
955 output_filename
= argv
[optind
];
962 if (input_format
== RES_FORMAT_UNKNOWN
)
964 if (input_filename
== NULL
)
965 input_format
= RES_FORMAT_RC
;
967 input_format
= format_from_filename (input_filename
, 1);
970 if (output_format
== RES_FORMAT_UNKNOWN
)
972 if (output_filename
== NULL
)
973 output_format
= RES_FORMAT_RC
;
975 output_format
= format_from_filename (output_filename
, 0);
978 /* Read the input file. */
979 switch (input_format
)
984 resources
= read_rc_file (input_filename
, preprocessor
, preprocargs
,
985 language
, use_temp_file
);
988 resources
= read_res_file (input_filename
);
990 case RES_FORMAT_COFF
:
991 resources
= read_coff_rsrc (input_filename
, target
);
995 if (resources
== NULL
)
996 fatal (_("no resources"));
998 /* Sort the resources. This is required for COFF, convenient for
999 rc, and unimportant for res. */
1000 resources
= sort_resources (resources
);
1002 /* Write the output file. */
1005 switch (output_format
)
1010 write_rc_file (output_filename
, resources
);
1012 case RES_FORMAT_RES
:
1013 write_res_file (output_filename
, resources
);
1015 case RES_FORMAT_COFF
:
1016 write_coff_file (output_filename
, target
, resources
);