1 /* windres.c -- a program to manipulate Windows resources
2 Copyright 1997 Free Software Foundation, Inc.
3 Written by Ian Lance Taylor, Cygnus Support.
5 This file is part of GNU Binutils.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
22 /* This program can read and write Windows resources in various
23 formats. In particular, it can act like the rc resource compiler
24 program, and it can act like the cvtres res to COFF conversion
27 It is based on information taken from the following sources:
29 * Microsoft documentation.
31 * The rcl program, written by Gunther Ebert
32 <gunther.ebert@ixos-leipzig.de>.
34 * The res2coff program, written by Pedro A. Aranda <paag@tid.es>.
41 #include "libiberty.h"
48 /* An enumeration of format types. */
54 /* Textual RC file. */
56 /* Binary RES file. */
62 /* A structure used to map between format types and strings. */
67 enum res_format format
;
70 /* A mapping between names and format types. */
72 static const struct format_map format_names
[] =
74 { "rc", RES_FORMAT_RC
},
75 { "res", RES_FORMAT_RES
},
76 { "coff", RES_FORMAT_COFF
},
77 { NULL
, RES_FORMAT_UNKNOWN
}
80 /* A mapping from file extensions to format types. */
82 static const struct format_map format_fileexts
[] =
84 { "rc", RES_FORMAT_RC
},
85 { "res", RES_FORMAT_RES
},
86 { "exe", RES_FORMAT_COFF
},
87 { "obj", RES_FORMAT_COFF
},
88 { "o", RES_FORMAT_COFF
},
89 { NULL
, RES_FORMAT_UNKNOWN
}
92 /* A list of include directories. */
96 struct include_dir
*next
;
100 static struct include_dir
*include_dirs
;
104 /* 150 isn't special; it's just an arbitrary non-ASCII char value. */
106 #define OPTION_DEFINE 150
107 #define OPTION_HELP (OPTION_DEFINE + 1)
108 #define OPTION_INCLUDE_DIR (OPTION_HELP + 1)
109 #define OPTION_LANGUAGE (OPTION_INCLUDE_DIR + 1)
110 #define OPTION_PREPROCESSOR (OPTION_LANGUAGE + 1)
111 #define OPTION_VERSION (OPTION_PREPROCESSOR + 1)
112 #define OPTION_YYDEBUG (OPTION_VERSION + 1)
114 static const struct option long_options
[] =
116 {"define", required_argument
, 0, OPTION_DEFINE
},
117 {"help", no_argument
, 0, OPTION_HELP
},
118 {"include-dir", required_argument
, 0, OPTION_INCLUDE_DIR
},
119 {"input-format", required_argument
, 0, 'I'},
120 {"language", required_argument
, 0, OPTION_LANGUAGE
},
121 {"output-format", required_argument
, 0, 'O'},
122 {"preprocessor", required_argument
, 0, OPTION_PREPROCESSOR
},
123 {"target", required_argument
, 0, 'F'},
124 {"version", no_argument
, 0, OPTION_VERSION
},
125 {"yydebug", no_argument
, 0, OPTION_YYDEBUG
},
126 {0, no_argument
, 0, 0}
129 /* Static functions. */
131 static void res_init
PARAMS ((void));
132 static int extended_menuitems
PARAMS ((const struct menuitem
*));
133 static enum res_format format_from_name
PARAMS ((const char *));
134 static enum res_format format_from_filename
PARAMS ((const char *, int));
135 static void usage
PARAMS ((FILE *, int));
136 static int cmp_res_entry
PARAMS ((const PTR
, const PTR
));
137 static struct res_directory
*sort_resources
PARAMS ((struct res_directory
*));
139 /* When we are building a resource tree, we allocate everything onto
140 an obstack, so that we can free it all at once if we want. */
142 #define obstack_chunk_alloc xmalloc
143 #define obstack_chunk_free free
145 /* The resource building obstack. */
147 static struct obstack res_obstack
;
149 /* Initialize the resource building obstack. */
154 obstack_init (&res_obstack
);
157 /* Allocate space on the resource building obstack. */
163 return (PTR
) obstack_alloc (&res_obstack
, bytes
);
166 /* We also use an obstack to save memory used while writing out a set
169 static struct obstack reswr_obstack
;
171 /* Initialize the resource writing obstack. */
176 obstack_init (&reswr_obstack
);
179 /* Allocate space on the resource writing obstack. */
185 return (PTR
) obstack_alloc (&reswr_obstack
, bytes
);
188 /* Open a file using the include directory search list. */
191 open_file_search (filename
, mode
, errmsg
, real_filename
)
192 const char *filename
;
195 char **real_filename
;
198 struct include_dir
*d
;
200 e
= fopen (filename
, mode
);
203 *real_filename
= xstrdup (filename
);
209 for (d
= include_dirs
; d
!= NULL
; d
= d
->next
)
213 n
= (char *) xmalloc (strlen (d
->dir
) + strlen (filename
) + 2);
214 sprintf (n
, "%s/%s", d
->dir
, filename
);
227 fatal ("can't open %s `%s': %s", errmsg
, filename
, strerror (errno
));
229 /* Return a value to avoid a compiler warning. */
233 /* Unicode support. */
235 /* Convert an ASCII string to a unicode string. We just copy it,
236 expanding chars to shorts, rather than doing something intelligent. */
239 unicode_from_ascii (length
, unicode
, ascii
)
248 len
= strlen (ascii
);
253 *unicode
= ((unichar
*) res_alloc ((len
+ 1) * sizeof (unichar
)));
255 for (s
= ascii
, w
= *unicode
; *s
!= '\0'; s
++, w
++)
260 /* Print the unicode string UNICODE to the file E. LENGTH is the
261 number of characters to print, or -1 if we should print until the
262 end of the string. */
265 unicode_print (e
, unicode
, length
)
267 const unichar
*unicode
;
281 if (ch
== 0 && length
< 0)
286 if ((ch
& 0x7f) == ch
&& isprint (ch
))
288 else if ((ch
& 0xff) == ch
)
289 fprintf (e
, "\\%03o", (unsigned int) ch
);
291 fprintf (e
, "\\x%x", (unsigned int) ch
);
295 /* Compare two resource ID's. We consider name entries to come before
296 numeric entries, because that is how they appear in the COFF .rsrc
310 else if (a
.u
.id
< b
.u
.id
)
317 unichar
*as
, *ase
, *bs
, *bse
;
323 ase
= as
+ a
.u
.n
.length
;
325 bse
= bs
+ b
.u
.n
.length
;
333 i
= (int) *as
- (int) *bs
;
347 /* Print a resource ID. */
350 res_id_print (stream
, id
, quote
)
356 fprintf (stream
, "%lu", id
.u
.id
);
361 unicode_print (stream
, id
.u
.n
.name
, id
.u
.n
.length
);
367 /* Print a list of resource ID's. */
370 res_ids_print (stream
, cids
, ids
)
373 const struct res_id
*ids
;
377 for (i
= 0; i
< cids
; i
++)
379 res_id_print (stream
, ids
[i
], 1);
381 fprintf (stream
, ": ");
385 /* Convert an ASCII string to a resource ID. */
388 res_string_to_id (res_id
, string
)
389 struct res_id
*res_id
;
393 unicode_from_ascii (&res_id
->u
.n
.length
, &res_id
->u
.n
.name
, string
);
396 /* Define a resource. The arguments are the resource tree, RESOURCES,
397 and the location at which to put it in the tree, CIDS and IDS.
398 This returns a newly allocated res_resource structure, which the
399 caller is expected to initialize. If DUPOK is non-zero, then if a
400 resource with this ID exists, it is returned. Otherwise, a warning
401 is issued, and a new resource is created replacing the existing
404 struct res_resource
*
405 define_resource (resources
, cids
, ids
, dupok
)
406 struct res_directory
**resources
;
408 const struct res_id
*ids
;
411 struct res_entry
*re
= NULL
;
415 for (i
= 0; i
< cids
; i
++)
417 struct res_entry
**pp
;
419 if (*resources
== NULL
)
421 *resources
= ((struct res_directory
*)
422 res_alloc (sizeof **resources
));
423 (*resources
)->characteristics
= 0;
424 (*resources
)->time
= 0;
425 (*resources
)->major
= 0;
426 (*resources
)->minor
= 0;
427 (*resources
)->entries
= NULL
;
430 for (pp
= &(*resources
)->entries
; *pp
!= NULL
; pp
= &(*pp
)->next
)
431 if (res_id_cmp ((*pp
)->id
, ids
[i
]) == 0)
438 re
= (struct res_entry
*) res_alloc (sizeof *re
);
459 fprintf (stderr
, "%s: ", program_name
);
460 res_ids_print (stderr
, i
, ids
);
461 fprintf (stderr
, ": expected to be a directory\n");
465 resources
= &re
->u
.dir
;
471 fprintf (stderr
, "%s: ", program_name
);
472 res_ids_print (stderr
, cids
, ids
);
473 fprintf (stderr
, ": expected to be a leaf\n");
477 if (re
->u
.res
!= NULL
)
482 fprintf (stderr
, "%s: warning: ", program_name
);
483 res_ids_print (stderr
, cids
, ids
);
484 fprintf (stderr
, ": duplicate value\n");
487 re
->u
.res
= ((struct res_resource
*)
488 res_alloc (sizeof (struct res_resource
)));
490 re
->u
.res
->type
= RES_TYPE_UNINITIALIZED
;
491 memset (&re
->u
.res
->res_info
, 0, sizeof (struct res_res_info
));
492 memset (&re
->u
.res
->coff_info
, 0, sizeof (struct res_coff_info
));
497 /* Define a standard resource. This is a version of define_resource
498 that just takes type, name, and language arguments. */
500 struct res_resource
*
501 define_standard_resource (resources
, type
, name
, language
, dupok
)
502 struct res_directory
**resources
;
514 a
[2].u
.id
= language
;
515 return define_resource (resources
, 3, a
, dupok
);
518 /* Comparison routine for resource sorting. */
521 cmp_res_entry (p1
, p2
)
525 const struct res_entry
**re1
, **re2
;
527 re1
= (const struct res_entry
**) p1
;
528 re2
= (const struct res_entry
**) p2
;
529 return res_id_cmp ((*re1
)->id
, (*re2
)->id
);
532 /* Sort the resources. */
534 static struct res_directory
*
535 sort_resources (resdir
)
536 struct res_directory
*resdir
;
539 struct res_entry
*re
;
540 struct res_entry
**a
;
542 if (resdir
->entries
== NULL
)
546 for (re
= resdir
->entries
; re
!= NULL
; re
= re
->next
)
549 /* This is a recursive routine, so using xmalloc is probably better
551 a
= (struct res_entry
**) xmalloc (c
* sizeof (struct res_entry
*));
553 for (i
= 0, re
= resdir
->entries
; re
!= NULL
; re
= re
->next
, i
++)
556 qsort (a
, c
, sizeof (struct res_entry
*), cmp_res_entry
);
558 resdir
->entries
= a
[0];
559 for (i
= 0; i
< c
- 1; i
++)
560 a
[i
]->next
= a
[i
+ 1];
565 /* Now sort the subdirectories. */
567 for (re
= resdir
->entries
; re
!= NULL
; re
= re
->next
)
569 re
->u
.dir
= sort_resources (re
->u
.dir
);
574 /* Return whether the dialog resource DIALOG is a DIALOG or a
578 extended_dialog (dialog
)
579 const struct dialog
*dialog
;
581 const struct dialog_control
*c
;
583 if (dialog
->ex
!= NULL
)
586 for (c
= dialog
->controls
; c
!= NULL
; c
= c
->next
)
587 if (c
->data
!= NULL
|| c
->help
!= 0)
593 /* Return whether MENUITEMS are a MENU or a MENUEX. */
597 const struct menu
*menu
;
599 return extended_menuitems (menu
->items
);
603 extended_menuitems (menuitems
)
604 const struct menuitem
*menuitems
;
606 const struct menuitem
*mi
;
608 for (mi
= menuitems
; mi
!= NULL
; mi
= mi
->next
)
610 if (mi
->help
!= 0 || mi
->state
!= 0)
612 if (mi
->popup
!= NULL
&& mi
->id
!= 0)
615 & ~ (MENUITEM_CHECKED
619 | MENUITEM_MENUBARBREAK
620 | MENUITEM_MENUBREAK
))
623 if (mi
->popup
!= NULL
)
625 if (extended_menuitems (mi
->popup
))
633 /* Convert a string to a format type, or exit if it can't be done. */
635 static enum res_format
636 format_from_name (name
)
639 const struct format_map
*m
;
641 for (m
= format_names
; m
->name
!= NULL
; m
++)
642 if (strcasecmp (m
->name
, name
) == 0)
647 fprintf (stderr
, "%s: unknown format type `%s'\n", program_name
, name
);
648 fprintf (stderr
, "%s: supported formats:", program_name
);
649 for (m
= format_names
; m
->name
!= NULL
; m
++)
650 fprintf (stderr
, " %s", m
->name
);
651 fprintf (stderr
, "\n");
658 /* Work out a format type given a file name. If INPUT is non-zero,
659 it's OK to look at the file itself. */
661 static enum res_format
662 format_from_filename (filename
, input
)
663 const char *filename
;
668 unsigned char b1
, b2
, b3
, b4
, b5
;
671 /* If we have an extension, see if we recognize it as implying a
672 particular format. */
673 ext
= strrchr (filename
, '.');
676 const struct format_map
*m
;
679 for (m
= format_fileexts
; m
->name
!= NULL
; m
++)
680 if (strcasecmp (m
->name
, ext
) == 0)
684 /* If we don't recognize the name of an output file, assume it's a
688 return RES_FORMAT_COFF
;
690 /* Read the first few bytes of the file to see if we can guess what
693 e
= fopen (filename
, FOPEN_RB
);
695 fatal ("%s: %s", filename
, strerror (errno
));
705 /* A PE executable starts with 0x4d 0x5a. */
706 if (b1
== 0x4d && b2
== 0x5a)
707 return RES_FORMAT_COFF
;
709 /* A COFF .o file starts with a COFF magic number. */
710 magic
= (b2
<< 8) | b1
;
713 case 0x14c: /* i386 */
714 case 0x166: /* MIPS */
715 case 0x184: /* Alpha */
716 case 0x268: /* 68k */
717 case 0x1f0: /* PowerPC */
719 return RES_FORMAT_COFF
;
722 /* A RES file starts with 0x0 0x0 0x0 0x0 0x20 0x0 0x0 0x0. */
723 if (b1
== 0 && b2
== 0 && b3
== 0 && b4
== 0 && b5
== 0x20)
724 return RES_FORMAT_RES
;
726 /* If every character is printable or space, assume it's an RC file. */
727 if ((isprint (b1
) || isspace (b1
))
728 && (isprint (b2
) || isspace (b2
))
729 && (isprint (b3
) || isspace (b3
))
730 && (isprint (b4
) || isspace (b4
))
731 && (isprint (b5
) || isspace (b5
)))
732 return RES_FORMAT_RC
;
734 /* Otherwise, we give up. */
735 fatal ("can not determine type of file `%s'; use the -I option",
738 /* Return something to silence the compiler warning. */
739 return RES_FORMAT_UNKNOWN
;
742 /* Print a usage message and exit. */
745 usage (stream
, status
)
749 fprintf (stream
, "Usage: %s [options] [input-file] [output-file]\n",
753 -i FILE, --input FILE Name input file\n\
754 -o FILE, --output FILE Name output file\n\
755 -I FORMAT, --input-format FORMAT\n\
756 Specify input format\n\
757 -O FORMAT, --output-format FORMAT\n\
758 Specify output format\n\
759 -F TARGET, --target TARGET Specify COFF target\n\
760 --preprocessor PROGRAM Program to use to preprocess rc file\n\
761 --include-dir DIR Include directory when preprocessing rc file\n\
762 --define SYM[=VAL] Define SYM when preprocessing rc file\n\
763 --language VAL Set language when reading rc file\n\
765 --yydebug Turn on parser debugging\n\
767 --help Print this help message\n\
768 --version Print version information\n");
770 FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
771 extension if not specified. A single file name is an input file.\n\
772 No input-file is stdin, default rc. No output-file is stdout, default rc.\n");
773 list_supported_targets (program_name
, stream
);
775 fprintf (stream
, "Report bugs to bug-gnu-utils@prep.ai.mit.edu\n");
779 /* The main function. */
787 char *input_filename
;
788 char *output_filename
;
789 enum res_format input_format
;
790 enum res_format output_format
;
795 struct res_directory
*resources
;
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
;
814 while ((c
= getopt_long (argc
, argv
, "i:o:I:O:F:", long_options
,
820 input_filename
= optarg
;
824 output_filename
= optarg
;
828 input_format
= format_from_name (optarg
);
832 output_format
= format_from_name (optarg
);
839 case OPTION_PREPROCESSOR
:
840 preprocessor
= optarg
;
844 if (preprocargs
== NULL
)
846 preprocargs
= xmalloc (strlen (optarg
) + 3);
847 sprintf (preprocargs
, "-D%s", optarg
);
853 n
= xmalloc (strlen (preprocargs
) + strlen (optarg
) + 4);
854 sprintf (n
, "%s -D%s", preprocargs
, optarg
);
860 case OPTION_INCLUDE_DIR
:
861 if (preprocargs
== NULL
)
863 preprocargs
= xmalloc (strlen (optarg
) + 3);
864 sprintf (preprocargs
, "-I%s", optarg
);
870 n
= xmalloc (strlen (preprocargs
) + strlen (optarg
) + 4);
871 sprintf (n
, "%s -I%s", preprocargs
, optarg
);
877 struct include_dir
*n
, **pp
;
879 n
= (struct include_dir
*) xmalloc (sizeof *n
);
883 for (pp
= &include_dirs
; *pp
!= NULL
; pp
= &(*pp
)->next
)
890 case OPTION_LANGUAGE
:
891 language
= strtol (optarg
, (char **) NULL
, 16);
905 print_version ("windres");
914 if (input_filename
== NULL
&& optind
< argc
)
916 input_filename
= argv
[optind
];
920 if (output_filename
== NULL
&& optind
< argc
)
922 output_filename
= argv
[optind
];
929 if (input_format
== RES_FORMAT_UNKNOWN
)
931 if (input_filename
== NULL
)
932 input_format
= RES_FORMAT_RC
;
934 input_format
= format_from_filename (input_filename
, 1);
937 if (output_format
== RES_FORMAT_UNKNOWN
)
939 if (output_filename
== NULL
)
940 output_format
= RES_FORMAT_RC
;
942 output_format
= format_from_filename (output_filename
, 0);
945 /* Read the input file. */
947 switch (input_format
)
952 resources
= read_rc_file (input_filename
, preprocessor
, preprocargs
,
956 resources
= read_res_file (input_filename
);
958 case RES_FORMAT_COFF
:
959 resources
= read_coff_rsrc (input_filename
, target
);
963 /* Sort the resources. This is required for COFF, convenient for
964 rc, and unimportant for res. */
966 resources
= sort_resources (resources
);
968 /* Write the output file. */
972 switch (output_format
)
977 write_rc_file (output_filename
, resources
);
980 write_res_file (output_filename
, resources
);
982 case RES_FORMAT_COFF
:
983 write_coff_file (output_filename
, target
, resources
);
991 struct res_directory
*
992 read_res_file (filename
)
993 const char *filename
;
995 fatal ("read_res_file unimplemented");
1000 write_res_file (filename
, resources
)
1001 const char *filename
;
1002 const struct res_directory
*resources
;
1004 fatal ("write_res_file unimplemented");