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_INCLUDE_DIR 150
110 #define OPTION_PREPROCESSOR (OPTION_INCLUDE_DIR + 1)
111 #define OPTION_USE_TEMP_FILE (OPTION_PREPROCESSOR + 1)
112 #define OPTION_NO_USE_TEMP_FILE (OPTION_USE_TEMP_FILE + 1)
113 #define OPTION_YYDEBUG (OPTION_NO_USE_TEMP_FILE + 1)
115 static const struct option long_options
[] =
117 {"define", required_argument
, 0, 'D'},
118 {"help", no_argument
, 0, 'h'},
119 {"include-dir", required_argument
, 0, OPTION_INCLUDE_DIR
},
120 {"input-format", required_argument
, 0, 'I'},
121 {"language", required_argument
, 0, 'l'},
122 {"output-format", required_argument
, 0, 'O'},
123 {"preprocessor", required_argument
, 0, OPTION_PREPROCESSOR
},
124 {"target", required_argument
, 0, 'F'},
125 {"undefine", required_argument
, 0, 'U'},
126 {"use-temp-file", no_argument
, 0, OPTION_USE_TEMP_FILE
},
127 {"no-use-temp-file", no_argument
, 0, OPTION_NO_USE_TEMP_FILE
},
128 {"verbose", no_argument
, 0, 'v'},
129 {"version", no_argument
, 0, 'V'},
130 {"yydebug", no_argument
, 0, OPTION_YYDEBUG
},
131 {0, no_argument
, 0, 0}
134 /* Static functions. */
136 static void res_init
PARAMS ((void));
137 static int extended_menuitems
PARAMS ((const struct menuitem
*));
138 static enum res_format format_from_name
PARAMS ((const char *));
139 static enum res_format format_from_filename
PARAMS ((const char *, int));
140 static void usage
PARAMS ((FILE *, int));
141 static int cmp_res_entry
PARAMS ((const PTR
, const PTR
));
142 static struct res_directory
*sort_resources
PARAMS ((struct res_directory
*));
143 static void reswr_init
PARAMS ((void));
144 static const char * quot
PARAMS ((const char *));
146 /* When we are building a resource tree, we allocate everything onto
147 an obstack, so that we can free it all at once if we want. */
149 #define obstack_chunk_alloc xmalloc
150 #define obstack_chunk_free free
152 /* The resource building obstack. */
154 static struct obstack res_obstack
;
156 /* Initialize the resource building obstack. */
161 obstack_init (&res_obstack
);
164 /* Allocate space on the resource building obstack. */
170 return (PTR
) obstack_alloc (&res_obstack
, bytes
);
173 /* We also use an obstack to save memory used while writing out a set
176 static struct obstack reswr_obstack
;
178 /* Initialize the resource writing obstack. */
183 obstack_init (&reswr_obstack
);
186 /* Allocate space on the resource writing obstack. */
192 return (PTR
) obstack_alloc (&reswr_obstack
, bytes
);
195 /* Open a file using the include directory search list. */
198 open_file_search (filename
, mode
, errmsg
, real_filename
)
199 const char *filename
;
202 char **real_filename
;
205 struct include_dir
*d
;
207 e
= fopen (filename
, mode
);
210 *real_filename
= xstrdup (filename
);
216 for (d
= include_dirs
; d
!= NULL
; d
= d
->next
)
220 n
= (char *) xmalloc (strlen (d
->dir
) + strlen (filename
) + 2);
221 sprintf (n
, "%s/%s", d
->dir
, filename
);
234 fatal (_("can't open %s `%s': %s"), errmsg
, filename
, strerror (errno
));
236 /* Return a value to avoid a compiler warning. */
240 /* Compare two resource ID's. We consider name entries to come before
241 numeric entries, because that is how they appear in the COFF .rsrc
255 else if (a
.u
.id
< b
.u
.id
)
262 unichar
*as
, *ase
, *bs
, *bse
;
268 ase
= as
+ a
.u
.n
.length
;
270 bse
= bs
+ b
.u
.n
.length
;
278 i
= (int) *as
- (int) *bs
;
292 /* Print a resource ID. */
295 res_id_print (stream
, id
, quote
)
301 fprintf (stream
, "%lu", id
.u
.id
);
306 unicode_print (stream
, id
.u
.n
.name
, id
.u
.n
.length
);
312 /* Print a list of resource ID's. */
315 res_ids_print (stream
, cids
, ids
)
318 const struct res_id
*ids
;
322 for (i
= 0; i
< cids
; i
++)
324 res_id_print (stream
, ids
[i
], 1);
326 fprintf (stream
, ": ");
330 /* Convert an ASCII string to a resource ID. */
333 res_string_to_id (res_id
, string
)
334 struct res_id
*res_id
;
338 unicode_from_ascii (&res_id
->u
.n
.length
, &res_id
->u
.n
.name
, string
);
341 /* Define a resource. The arguments are the resource tree, RESOURCES,
342 and the location at which to put it in the tree, CIDS and IDS.
343 This returns a newly allocated res_resource structure, which the
344 caller is expected to initialize. If DUPOK is non-zero, then if a
345 resource with this ID exists, it is returned. Otherwise, a warning
346 is issued, and a new resource is created replacing the existing
349 struct res_resource
*
350 define_resource (resources
, cids
, ids
, dupok
)
351 struct res_directory
**resources
;
353 const struct res_id
*ids
;
356 struct res_entry
*re
= NULL
;
360 for (i
= 0; i
< cids
; i
++)
362 struct res_entry
**pp
;
364 if (*resources
== NULL
)
366 static unsigned long timeval
;
368 /* Use the same timestamp for every resource created in a
371 timeval
= time (NULL
);
373 *resources
= ((struct res_directory
*)
374 res_alloc (sizeof **resources
));
375 (*resources
)->characteristics
= 0;
376 (*resources
)->time
= timeval
;
377 (*resources
)->major
= 0;
378 (*resources
)->minor
= 0;
379 (*resources
)->entries
= NULL
;
382 for (pp
= &(*resources
)->entries
; *pp
!= NULL
; pp
= &(*pp
)->next
)
383 if (res_id_cmp ((*pp
)->id
, ids
[i
]) == 0)
390 re
= (struct res_entry
*) res_alloc (sizeof *re
);
411 fprintf (stderr
, "%s: ", program_name
);
412 res_ids_print (stderr
, i
, ids
);
413 fprintf (stderr
, _(": expected to be a directory\n"));
417 resources
= &re
->u
.dir
;
423 fprintf (stderr
, "%s: ", program_name
);
424 res_ids_print (stderr
, cids
, ids
);
425 fprintf (stderr
, _(": expected to be a leaf\n"));
429 if (re
->u
.res
!= NULL
)
434 fprintf (stderr
, _("%s: warning: "), program_name
);
435 res_ids_print (stderr
, cids
, ids
);
436 fprintf (stderr
, _(": duplicate value\n"));
439 re
->u
.res
= ((struct res_resource
*)
440 res_alloc (sizeof (struct res_resource
)));
441 memset (re
->u
.res
, 0, sizeof (struct res_resource
));
443 re
->u
.res
->type
= RES_TYPE_UNINITIALIZED
;
447 /* Define a standard resource. This is a version of define_resource
448 that just takes type, name, and language arguments. */
450 struct res_resource
*
451 define_standard_resource (resources
, type
, name
, language
, dupok
)
452 struct res_directory
**resources
;
464 a
[2].u
.id
= language
;
465 return define_resource (resources
, 3, a
, dupok
);
468 /* Comparison routine for resource sorting. */
471 cmp_res_entry (p1
, p2
)
475 const struct res_entry
**re1
, **re2
;
477 re1
= (const struct res_entry
**) p1
;
478 re2
= (const struct res_entry
**) p2
;
479 return res_id_cmp ((*re1
)->id
, (*re2
)->id
);
482 /* Sort the resources. */
484 static struct res_directory
*
485 sort_resources (resdir
)
486 struct res_directory
*resdir
;
489 struct res_entry
*re
;
490 struct res_entry
**a
;
492 if (resdir
->entries
== NULL
)
496 for (re
= resdir
->entries
; re
!= NULL
; re
= re
->next
)
499 /* This is a recursive routine, so using xmalloc is probably better
501 a
= (struct res_entry
**) xmalloc (c
* sizeof (struct res_entry
*));
503 for (i
= 0, re
= resdir
->entries
; re
!= NULL
; re
= re
->next
, i
++)
506 qsort (a
, c
, sizeof (struct res_entry
*), cmp_res_entry
);
508 resdir
->entries
= a
[0];
509 for (i
= 0; i
< c
- 1; i
++)
510 a
[i
]->next
= a
[i
+ 1];
515 /* Now sort the subdirectories. */
517 for (re
= resdir
->entries
; re
!= NULL
; re
= re
->next
)
519 re
->u
.dir
= sort_resources (re
->u
.dir
);
524 /* Return whether the dialog resource DIALOG is a DIALOG or a
528 extended_dialog (dialog
)
529 const struct dialog
*dialog
;
531 const struct dialog_control
*c
;
533 if (dialog
->ex
!= NULL
)
536 for (c
= dialog
->controls
; c
!= NULL
; c
= c
->next
)
537 if (c
->data
!= NULL
|| c
->help
!= 0)
543 /* Return whether MENUITEMS are a MENU or a MENUEX. */
547 const struct menu
*menu
;
549 return extended_menuitems (menu
->items
);
553 extended_menuitems (menuitems
)
554 const struct menuitem
*menuitems
;
556 const struct menuitem
*mi
;
558 for (mi
= menuitems
; mi
!= NULL
; mi
= mi
->next
)
560 if (mi
->help
!= 0 || mi
->state
!= 0)
562 if (mi
->popup
!= NULL
&& mi
->id
!= 0)
565 & ~ (MENUITEM_CHECKED
569 | MENUITEM_MENUBARBREAK
570 | MENUITEM_MENUBREAK
))
573 if (mi
->popup
!= NULL
)
575 if (extended_menuitems (mi
->popup
))
583 /* Convert a string to a format type, or exit if it can't be done. */
585 static enum res_format
586 format_from_name (name
)
589 const struct format_map
*m
;
591 for (m
= format_names
; m
->name
!= NULL
; m
++)
592 if (strcasecmp (m
->name
, name
) == 0)
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 -I --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 --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 --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 output_format
;
783 const char *quotedarg
;
785 struct res_directory
*resources
;
788 #if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
789 setlocale (LC_MESSAGES
, "");
791 #if defined (HAVE_SETLOCALE)
792 setlocale (LC_CTYPE
, "");
794 bindtextdomain (PACKAGE
, LOCALEDIR
);
795 textdomain (PACKAGE
);
797 program_name
= argv
[0];
798 xmalloc_set_program_name (program_name
);
801 set_default_bfd_target ();
805 input_filename
= NULL
;
806 output_filename
= NULL
;
807 input_format
= RES_FORMAT_UNKNOWN
;
808 output_format
= RES_FORMAT_UNKNOWN
;
812 language
= 0x409; /* LANG_ENGLISH, SUBLANG_ENGLISH_US. */
815 while ((c
= getopt_long (argc
, argv
, "i:l:o:I:O:F:D:U:rhHvV", long_options
,
821 input_filename
= optarg
;
825 output_filename
= optarg
;
829 input_format
= format_from_name (optarg
);
833 output_format
= format_from_name (optarg
);
840 case OPTION_PREPROCESSOR
:
841 preprocessor
= optarg
;
846 if (preprocargs
== NULL
)
848 quotedarg
= quot (optarg
);
849 preprocargs
= xmalloc (strlen (quotedarg
) + 3);
850 sprintf (preprocargs
, "-%c%s", c
, quotedarg
);
856 quotedarg
= quot (optarg
);
857 n
= xmalloc (strlen (preprocargs
) + strlen (quotedarg
) + 4);
858 sprintf (n
, "%s -%c%s", preprocargs
, c
, quotedarg
);
865 /* Ignored for compatibility with rc. */
872 case OPTION_INCLUDE_DIR
:
873 if (preprocargs
== NULL
)
875 quotedarg
= quot (optarg
);
876 preprocargs
= xmalloc (strlen (quotedarg
) + 3);
877 sprintf (preprocargs
, "-I%s", quotedarg
);
883 quotedarg
= quot (optarg
);
884 n
= xmalloc (strlen (preprocargs
) + strlen (quotedarg
) + 4);
885 sprintf (n
, "%s -I%s", preprocargs
, quotedarg
);
891 struct include_dir
*n
, **pp
;
893 n
= (struct include_dir
*) xmalloc (sizeof *n
);
897 for (pp
= &include_dirs
; *pp
!= NULL
; pp
= &(*pp
)->next
)
905 language
= strtol (optarg
, (char **) NULL
, 16);
908 case OPTION_USE_TEMP_FILE
:
912 case OPTION_NO_USE_TEMP_FILE
:
928 print_version ("windres");
937 if (input_filename
== NULL
&& optind
< argc
)
939 input_filename
= argv
[optind
];
943 if (output_filename
== NULL
&& optind
< argc
)
945 output_filename
= argv
[optind
];
952 if (input_format
== RES_FORMAT_UNKNOWN
)
954 if (input_filename
== NULL
)
955 input_format
= RES_FORMAT_RC
;
957 input_format
= format_from_filename (input_filename
, 1);
960 if (output_format
== RES_FORMAT_UNKNOWN
)
962 if (output_filename
== NULL
)
963 output_format
= RES_FORMAT_RC
;
965 output_format
= format_from_filename (output_filename
, 0);
968 /* Read the input file. */
969 switch (input_format
)
974 resources
= read_rc_file (input_filename
, preprocessor
, preprocargs
,
975 language
, use_temp_file
);
978 resources
= read_res_file (input_filename
);
980 case RES_FORMAT_COFF
:
981 resources
= read_coff_rsrc (input_filename
, target
);
985 if (resources
== NULL
)
986 fatal (_("no resources"));
988 /* Sort the resources. This is required for COFF, convenient for
989 rc, and unimportant for res. */
990 resources
= sort_resources (resources
);
992 /* Write the output file. */
995 switch (output_format
)
1000 write_rc_file (output_filename
, resources
);
1002 case RES_FORMAT_RES
:
1003 write_res_file (output_filename
, resources
);
1005 case RES_FORMAT_COFF
:
1006 write_coff_file (output_filename
, target
, resources
);