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
PARAMS ((void));
108 static int extended_menuitems
PARAMS ((const struct menuitem
*));
109 static enum res_format format_from_name
PARAMS ((const char *, int));
110 static enum res_format format_from_filename
PARAMS ((const char *, int));
111 static void usage
PARAMS ((FILE *, int));
112 static int cmp_res_entry
PARAMS ((const PTR
, const PTR
));
113 static struct res_directory
*sort_resources
PARAMS ((struct res_directory
*));
114 static void reswr_init
PARAMS ((void));
115 static const char * quot
PARAMS ((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. */
141 return (PTR
) 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. */
163 return (PTR
) obstack_alloc (&reswr_obstack
, bytes
);
166 /* Open a file using the include directory search list. */
169 open_file_search (filename
, mode
, errmsg
, real_filename
)
170 const char *filename
;
173 char **real_filename
;
176 struct include_dir
*d
;
178 e
= fopen (filename
, mode
);
181 *real_filename
= xstrdup (filename
);
187 for (d
= include_dirs
; d
!= NULL
; d
= d
->next
)
191 n
= (char *) xmalloc (strlen (d
->dir
) + strlen (filename
) + 2);
192 sprintf (n
, "%s/%s", d
->dir
, filename
);
205 fatal (_("can't open %s `%s': %s"), errmsg
, filename
, strerror (errno
));
207 /* Return a value to avoid a compiler warning. */
211 /* Compare two resource ID's. We consider name entries to come before
212 numeric entries, because that is how they appear in the COFF .rsrc
226 else if (a
.u
.id
< b
.u
.id
)
233 unichar
*as
, *ase
, *bs
, *bse
;
239 ase
= as
+ a
.u
.n
.length
;
241 bse
= bs
+ b
.u
.n
.length
;
249 i
= (int) *as
- (int) *bs
;
263 /* Print a resource ID. */
266 res_id_print (stream
, id
, quote
)
272 fprintf (stream
, "%lu", id
.u
.id
);
277 unicode_print (stream
, id
.u
.n
.name
, id
.u
.n
.length
);
283 /* Print a list of resource ID's. */
286 res_ids_print (stream
, cids
, ids
)
289 const struct res_id
*ids
;
293 for (i
= 0; i
< cids
; i
++)
295 res_id_print (stream
, ids
[i
], 1);
297 fprintf (stream
, ": ");
301 /* Convert an ASCII string to a resource ID. */
304 res_string_to_id (res_id
, string
)
305 struct res_id
*res_id
;
309 unicode_from_ascii (&res_id
->u
.n
.length
, &res_id
->u
.n
.name
, string
);
312 /* Define a resource. The arguments are the resource tree, RESOURCES,
313 and the location at which to put it in the tree, CIDS and IDS.
314 This returns a newly allocated res_resource structure, which the
315 caller is expected to initialize. If DUPOK is non-zero, then if a
316 resource with this ID exists, it is returned. Otherwise, a warning
317 is issued, and a new resource is created replacing the existing
320 struct res_resource
*
321 define_resource (resources
, cids
, ids
, dupok
)
322 struct res_directory
**resources
;
324 const struct res_id
*ids
;
327 struct res_entry
*re
= NULL
;
331 for (i
= 0; i
< cids
; i
++)
333 struct res_entry
**pp
;
335 if (*resources
== NULL
)
337 static unsigned long timeval
;
339 /* Use the same timestamp for every resource created in a
342 timeval
= time (NULL
);
344 *resources
= ((struct res_directory
*)
345 res_alloc (sizeof **resources
));
346 (*resources
)->characteristics
= 0;
347 (*resources
)->time
= timeval
;
348 (*resources
)->major
= 0;
349 (*resources
)->minor
= 0;
350 (*resources
)->entries
= NULL
;
353 for (pp
= &(*resources
)->entries
; *pp
!= NULL
; pp
= &(*pp
)->next
)
354 if (res_id_cmp ((*pp
)->id
, ids
[i
]) == 0)
361 re
= (struct res_entry
*) res_alloc (sizeof *re
);
382 fprintf (stderr
, "%s: ", program_name
);
383 res_ids_print (stderr
, i
, ids
);
384 fprintf (stderr
, _(": expected to be a directory\n"));
388 resources
= &re
->u
.dir
;
394 fprintf (stderr
, "%s: ", program_name
);
395 res_ids_print (stderr
, cids
, ids
);
396 fprintf (stderr
, _(": expected to be a leaf\n"));
400 if (re
->u
.res
!= NULL
)
405 fprintf (stderr
, _("%s: warning: "), program_name
);
406 res_ids_print (stderr
, cids
, ids
);
407 fprintf (stderr
, _(": duplicate value\n"));
410 re
->u
.res
= ((struct res_resource
*)
411 res_alloc (sizeof (struct res_resource
)));
412 memset (re
->u
.res
, 0, sizeof (struct res_resource
));
414 re
->u
.res
->type
= RES_TYPE_UNINITIALIZED
;
418 /* Define a standard resource. This is a version of define_resource
419 that just takes type, name, and language arguments. */
421 struct res_resource
*
422 define_standard_resource (resources
, type
, name
, language
, dupok
)
423 struct res_directory
**resources
;
435 a
[2].u
.id
= language
;
436 return define_resource (resources
, 3, a
, dupok
);
439 /* Comparison routine for resource sorting. */
442 cmp_res_entry (p1
, p2
)
446 const struct res_entry
**re1
, **re2
;
448 re1
= (const struct res_entry
**) p1
;
449 re2
= (const struct res_entry
**) p2
;
450 return res_id_cmp ((*re1
)->id
, (*re2
)->id
);
453 /* Sort the resources. */
455 static struct res_directory
*
456 sort_resources (resdir
)
457 struct res_directory
*resdir
;
460 struct res_entry
*re
;
461 struct res_entry
**a
;
463 if (resdir
->entries
== NULL
)
467 for (re
= resdir
->entries
; re
!= NULL
; re
= re
->next
)
470 /* This is a recursive routine, so using xmalloc is probably better
472 a
= (struct res_entry
**) xmalloc (c
* sizeof (struct res_entry
*));
474 for (i
= 0, re
= resdir
->entries
; re
!= NULL
; re
= re
->next
, i
++)
477 qsort (a
, c
, sizeof (struct res_entry
*), cmp_res_entry
);
479 resdir
->entries
= a
[0];
480 for (i
= 0; i
< c
- 1; i
++)
481 a
[i
]->next
= a
[i
+ 1];
486 /* Now sort the subdirectories. */
488 for (re
= resdir
->entries
; re
!= NULL
; re
= re
->next
)
490 re
->u
.dir
= sort_resources (re
->u
.dir
);
495 /* Return whether the dialog resource DIALOG is a DIALOG or a
499 extended_dialog (dialog
)
500 const struct dialog
*dialog
;
502 const struct dialog_control
*c
;
504 if (dialog
->ex
!= NULL
)
507 for (c
= dialog
->controls
; c
!= NULL
; c
= c
->next
)
508 if (c
->data
!= NULL
|| c
->help
!= 0)
514 /* Return whether MENUITEMS are a MENU or a MENUEX. */
518 const struct menu
*menu
;
520 return extended_menuitems (menu
->items
);
524 extended_menuitems (menuitems
)
525 const struct menuitem
*menuitems
;
527 const struct menuitem
*mi
;
529 for (mi
= menuitems
; mi
!= NULL
; mi
= mi
->next
)
531 if (mi
->help
!= 0 || mi
->state
!= 0)
533 if (mi
->popup
!= NULL
&& mi
->id
!= 0)
536 & ~ (MENUITEM_CHECKED
540 | MENUITEM_MENUBARBREAK
541 | MENUITEM_MENUBREAK
))
544 if (mi
->popup
!= NULL
)
546 if (extended_menuitems (mi
->popup
))
554 /* Convert a string to a format type, or exit if it can't be done. */
556 static enum res_format
557 format_from_name (name
, exit_on_error
)
561 const struct format_map
*m
;
563 for (m
= format_names
; m
->name
!= NULL
; m
++)
564 if (strcasecmp (m
->name
, name
) == 0)
567 if (m
->name
== NULL
&& exit_on_error
)
569 non_fatal (_("unknown format type `%s'"), name
);
570 fprintf (stderr
, _("%s: supported formats:"), program_name
);
571 for (m
= format_names
; m
->name
!= NULL
; m
++)
572 fprintf (stderr
, " %s", m
->name
);
573 fprintf (stderr
, "\n");
580 /* Work out a format type given a file name. If INPUT is non-zero,
581 it's OK to look at the file itself. */
583 static enum res_format
584 format_from_filename (filename
, input
)
585 const char *filename
;
590 unsigned char b1
, b2
, b3
, b4
, b5
;
593 /* If we have an extension, see if we recognize it as implying a
594 particular format. */
595 ext
= strrchr (filename
, '.');
598 const struct format_map
*m
;
601 for (m
= format_fileexts
; m
->name
!= NULL
; m
++)
602 if (strcasecmp (m
->name
, ext
) == 0)
606 /* If we don't recognize the name of an output file, assume it's a
609 return RES_FORMAT_COFF
;
611 /* Read the first few bytes of the file to see if we can guess what
613 e
= fopen (filename
, FOPEN_RB
);
615 fatal ("%s: %s", filename
, strerror (errno
));
625 /* A PE executable starts with 0x4d 0x5a. */
626 if (b1
== 0x4d && b2
== 0x5a)
627 return RES_FORMAT_COFF
;
629 /* A COFF .o file starts with a COFF magic number. */
630 magic
= (b2
<< 8) | b1
;
633 case 0x14c: /* i386 */
634 case 0x166: /* MIPS */
635 case 0x184: /* Alpha */
636 case 0x268: /* 68k */
637 case 0x1f0: /* PowerPC */
639 return RES_FORMAT_COFF
;
642 /* A RES file starts with 0x0 0x0 0x0 0x0 0x20 0x0 0x0 0x0. */
643 if (b1
== 0 && b2
== 0 && b3
== 0 && b4
== 0 && b5
== 0x20)
644 return RES_FORMAT_RES
;
646 /* If every character is printable or space, assume it's an RC file. */
647 if ((ISPRINT (b1
) || ISSPACE (b1
))
648 && (ISPRINT (b2
) || ISSPACE (b2
))
649 && (ISPRINT (b3
) || ISSPACE (b3
))
650 && (ISPRINT (b4
) || ISSPACE (b4
))
651 && (ISPRINT (b5
) || ISSPACE (b5
)))
652 return RES_FORMAT_RC
;
654 /* Otherwise, we give up. */
655 fatal (_("can not determine type of file `%s'; use the -I option"),
658 /* Return something to silence the compiler warning. */
659 return RES_FORMAT_UNKNOWN
;
662 /* Print a usage message and exit. */
665 usage (stream
, status
)
669 fprintf (stream
, _("Usage: %s [option(s)] [input-file] [output-file]\n"),
671 fprintf (stream
, _(" The options are:\n\
672 -i --input=<file> Name input file\n\
673 -o --output=<file> Name output file\n\
674 -J --input-format=<format> Specify input format\n\
675 -O --output-format=<format> Specify output format\n\
676 -F --target=<target> Specify COFF target\n\
677 --preprocessor=<program> Program to use to preprocess rc file\n\
678 -I --include-dir=<dir> Include directory when preprocessing rc file\n\
679 -D --define <sym>[=<val>] Define SYM when preprocessing rc file\n\
680 -U --undefine <sym> Undefine SYM when preprocessing rc file\n\
681 -v --verbose Verbose - tells you what it's doing\n\
682 -l --language=<val> Set language when reading rc file\n\
683 --use-temp-file Use a temporary file instead of popen to read\n\
684 the preprocessor output\n\
685 --no-use-temp-file Use popen (default)\n"));
687 fprintf (stream
, _("\
688 --yydebug Turn on parser debugging\n"));
690 fprintf (stream
, _("\
691 -r Ignored for compatibility with rc\n\
692 -h --help Print this help message\n\
693 -V --version Print version information\n"));
694 fprintf (stream
, _("\
695 FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
696 extension if not specified. A single file name is an input file.\n\
697 No input-file is stdin, default rc. No output-file is stdout, default rc.\n"));
699 list_supported_targets (program_name
, stream
);
702 fprintf (stream
, _("Report bugs to %s\n"), REPORT_BUGS_TO
);
707 /* Quote characters that will confuse the shell when we run the preprocessor. */
713 static char *buf
= 0;
714 static int buflen
= 0;
715 int slen
= strlen (string
);
719 if ((buflen
< slen
* 2 + 2) || !buf
)
721 buflen
= slen
* 2 + 2;
724 buf
= (char *) xmalloc (buflen
);
727 for (src
=string
, dest
=buf
; *src
; src
++, dest
++)
729 if (*src
== '(' || *src
== ')' || *src
== ' ')
739 /* 150 isn't special; it's just an arbitrary non-ASCII char value. */
741 #define OPTION_PREPROCESSOR 150
742 #define OPTION_USE_TEMP_FILE (OPTION_PREPROCESSOR + 1)
743 #define OPTION_NO_USE_TEMP_FILE (OPTION_USE_TEMP_FILE + 1)
744 #define OPTION_YYDEBUG (OPTION_NO_USE_TEMP_FILE + 1)
746 static const struct option long_options
[] =
748 {"input", required_argument
, 0, 'i'},
749 {"output", required_argument
, 0, 'o'},
750 {"input-format", required_argument
, 0, 'J'},
751 {"output-format", required_argument
, 0, 'O'},
752 {"target", required_argument
, 0, 'F'},
753 {"preprocessor", required_argument
, 0, OPTION_PREPROCESSOR
},
754 {"include-dir", required_argument
, 0, 'I'},
755 {"define", required_argument
, 0, 'D'},
756 {"undefine", required_argument
, 0, 'U'},
757 {"verbose", no_argument
, 0, 'v'},
758 {"language", required_argument
, 0, 'l'},
759 {"use-temp-file", no_argument
, 0, OPTION_USE_TEMP_FILE
},
760 {"no-use-temp-file", no_argument
, 0, OPTION_NO_USE_TEMP_FILE
},
761 {"yydebug", no_argument
, 0, OPTION_YYDEBUG
},
762 {"version", no_argument
, 0, 'V'},
763 {"help", no_argument
, 0, 'h'},
764 {0, no_argument
, 0, 0}
767 /* This keeps gcc happy when using -Wmissing-prototypes -Wstrict-prototypes. */
768 int main
PARAMS ((int, char **));
770 /* The main function. */
778 char *input_filename
;
779 char *output_filename
;
780 enum res_format input_format
;
781 enum res_format input_format_tmp
;
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
, "f:i:l:o:I:J:O:F:D:U:rhHvV", long_options
,
824 input_filename
= optarg
;
828 /* For compatability with rc we accept "-fo <name>" as being the
829 equivalent of "-o <name>". We do not advertise this fact
830 though, as we do not want users to use non-GNU like command
833 fatal (_("invalid option -f\n"));
838 fatal (_("No filename following the -fo option.\n"));
839 optarg
= argv
[optind
++];
844 output_filename
= optarg
;
848 input_format
= format_from_name (optarg
, 1);
852 output_format
= format_from_name (optarg
, 1);
859 case OPTION_PREPROCESSOR
:
860 preprocessor
= optarg
;
865 if (preprocargs
== NULL
)
867 quotedarg
= quot (optarg
);
868 preprocargs
= xmalloc (strlen (quotedarg
) + 3);
869 sprintf (preprocargs
, "-%c%s", c
, quotedarg
);
875 quotedarg
= quot (optarg
);
876 n
= xmalloc (strlen (preprocargs
) + strlen (quotedarg
) + 4);
877 sprintf (n
, "%s -%c%s", preprocargs
, c
, quotedarg
);
884 /* Ignored for compatibility with rc. */
892 /* For backward compatibility, should be removed in the future. */
893 input_format_tmp
= format_from_name (optarg
, 0);
894 if (input_format_tmp
!= RES_FORMAT_UNKNOWN
)
896 fprintf (stderr
, _("Option -I is deprecated for setting the input format, please use -J instead.\n"));
897 input_format
= input_format_tmp
;
901 if (preprocargs
== NULL
)
903 quotedarg
= quot (optarg
);
904 preprocargs
= xmalloc (strlen (quotedarg
) + 3);
905 sprintf (preprocargs
, "-I%s", quotedarg
);
911 quotedarg
= quot (optarg
);
912 n
= xmalloc (strlen (preprocargs
) + strlen (quotedarg
) + 4);
913 sprintf (n
, "%s -I%s", preprocargs
, quotedarg
);
919 struct include_dir
*n
, **pp
;
921 n
= (struct include_dir
*) xmalloc (sizeof *n
);
925 for (pp
= &include_dirs
; *pp
!= NULL
; pp
= &(*pp
)->next
)
933 language
= strtol (optarg
, (char **) NULL
, 16);
936 case OPTION_USE_TEMP_FILE
:
940 case OPTION_NO_USE_TEMP_FILE
:
956 print_version ("windres");
965 if (input_filename
== NULL
&& optind
< argc
)
967 input_filename
= argv
[optind
];
971 if (output_filename
== NULL
&& optind
< argc
)
973 output_filename
= argv
[optind
];
980 if (input_format
== RES_FORMAT_UNKNOWN
)
982 if (input_filename
== NULL
)
983 input_format
= RES_FORMAT_RC
;
985 input_format
= format_from_filename (input_filename
, 1);
988 if (output_format
== RES_FORMAT_UNKNOWN
)
990 if (output_filename
== NULL
)
991 output_format
= RES_FORMAT_RC
;
993 output_format
= format_from_filename (output_filename
, 0);
996 /* Read the input file. */
997 switch (input_format
)
1002 resources
= read_rc_file (input_filename
, preprocessor
, preprocargs
,
1003 language
, use_temp_file
);
1005 case RES_FORMAT_RES
:
1006 resources
= read_res_file (input_filename
);
1008 case RES_FORMAT_COFF
:
1009 resources
= read_coff_rsrc (input_filename
, target
);
1013 if (resources
== NULL
)
1014 fatal (_("no resources"));
1016 /* Sort the resources. This is required for COFF, convenient for
1017 rc, and unimportant for res. */
1018 resources
= sort_resources (resources
);
1020 /* Write the output file. */
1023 switch (output_format
)
1028 write_rc_file (output_filename
, resources
);
1030 case RES_FORMAT_RES
:
1031 write_res_file (output_filename
, resources
);
1033 case RES_FORMAT_COFF
:
1034 write_coff_file (output_filename
, target
, resources
);