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"
47 /* An enumeration of format types. */
53 /* Textual RC file. */
55 /* Binary RES file. */
61 /* A structure used to map between format types and strings. */
66 enum res_format format
;
69 /* A mapping between names and format types. */
71 static const struct format_map format_names
[] =
73 { "rc", RES_FORMAT_RC
},
74 { "res", RES_FORMAT_RES
},
75 { "coff", RES_FORMAT_COFF
},
76 { NULL
, RES_FORMAT_UNKNOWN
}
79 /* A mapping from file extensions to format types. */
81 static const struct format_map format_fileexts
[] =
83 { "rc", RES_FORMAT_RC
},
84 { "res", RES_FORMAT_RES
},
85 { "exe", RES_FORMAT_COFF
},
86 { "obj", RES_FORMAT_COFF
},
87 { "o", RES_FORMAT_COFF
},
88 { NULL
, RES_FORMAT_UNKNOWN
}
91 /* A list of include directories. */
95 struct include_dir
*next
;
99 static struct include_dir
*include_dirs
;
103 /* 150 isn't special; it's just an arbitrary non-ASCII char value. */
105 #define OPTION_DEFINE 150
106 #define OPTION_HELP (OPTION_DEFINE + 1)
107 #define OPTION_INCLUDE_DIR (OPTION_HELP + 1)
108 #define OPTION_LANGUAGE (OPTION_INCLUDE_DIR + 1)
109 #define OPTION_PREPROCESSOR (OPTION_LANGUAGE + 1)
110 #define OPTION_VERSION (OPTION_PREPROCESSOR + 1)
111 #define OPTION_YYDEBUG (OPTION_VERSION + 1)
113 static const struct option long_options
[] =
115 {"define", required_argument
, 0, OPTION_DEFINE
},
116 {"help", no_argument
, 0, OPTION_HELP
},
117 {"include-dir", required_argument
, 0, OPTION_INCLUDE_DIR
},
118 {"input-format", required_argument
, 0, 'I'},
119 {"language", required_argument
, 0, OPTION_LANGUAGE
},
120 {"output-format", required_argument
, 0, 'O'},
121 {"preprocessor", required_argument
, 0, OPTION_PREPROCESSOR
},
122 {"target", required_argument
, 0, 'F'},
123 {"version", no_argument
, 0, OPTION_VERSION
},
124 {"yydebug", no_argument
, 0, OPTION_YYDEBUG
},
125 {0, no_argument
, 0, 0}
128 /* Static functions. */
130 static enum res_format format_from_name
PARAMS ((const char *));
131 static enum res_format format_from_filename
PARAMS ((const char *, int));
132 static void usage
PARAMS ((FILE *, int));
134 /* Open a file using the include directory search list. */
137 open_file_search (filename
, mode
, errmsg
, real_filename
)
138 const char *filename
;
141 char **real_filename
;
144 struct include_dir
*d
;
146 e
= fopen (filename
, mode
);
149 *real_filename
= xstrdup (filename
);
155 for (d
= include_dirs
; d
!= NULL
; d
= d
->next
)
159 n
= (char *) xmalloc (strlen (d
->dir
) + strlen (filename
) + 2);
160 sprintf (n
, "%s/%s", d
->dir
, filename
);
173 fatal ("can't open %s `%s': %s", errmsg
, filename
, strerror (errno
));
175 /* Return a value to avoid a compiler warning. */
179 /* Unicode support. */
181 /* Convert an ASCII string to a unicode string. We just copy it,
182 expanding chars to shorts, rather than doing something intelligent. */
185 unicode_from_ascii (length
, unicode
, ascii
)
186 unsigned short *length
;
187 unsigned short **unicode
;
194 len
= strlen (ascii
);
199 fatal ("string too long (%d chars > 0xffff)", len
);
203 *unicode
= (unsigned short *) xmalloc ((len
+ 1) * sizeof (unsigned short));
205 for (s
= ascii
, w
= *unicode
; *s
!= '\0'; s
++, w
++)
210 /* Print the unicode string UNICODE to the file E. LENGTH is the
211 number of characters to print, or -1 if we should print until the
212 end of the string. */
215 unicode_print (e
, unicode
, length
)
217 const unsigned short *unicode
;
236 if ((ch
& 0x7f) == ch
&& isprint (ch
))
238 else if ((ch
& 0xff) == ch
)
239 fprintf (e
, "\\%03o", (unsigned int) ch
);
241 fprintf (e
, "\\x%x", (unsigned int) ch
);
245 /* Compare two resource ID's. We consider name entries to come before
246 numeric entries, because that is how they appear in the COFF .rsrc
260 else if (a
.u
.id
< b
.u
.id
)
267 unsigned short *as
, *ase
, *bs
, *bse
;
273 ase
= as
+ a
.u
.n
.length
;
275 bse
= bs
+ b
.u
.n
.length
;
283 i
= (int) *as
- (int) *bs
;
297 /* Print a resource ID. */
300 res_id_print (stream
, id
, quote
)
306 fprintf (stream
, "%lu", id
.u
.id
);
311 unicode_print (stream
, id
.u
.n
.name
, id
.u
.n
.length
);
317 /* Print a list of resource ID's. */
320 res_ids_print (stream
, cids
, ids
)
323 const struct res_id
*ids
;
327 for (i
= 0; i
< cids
; i
++)
329 res_id_print (stream
, ids
[i
], 1);
331 fprintf (stream
, ": ");
335 /* Convert an ASCII string to a resource ID. */
338 res_string_to_id (res_id
, string
)
339 struct res_id
*res_id
;
343 unicode_from_ascii (&res_id
->u
.n
.length
, &res_id
->u
.n
.name
, string
);
346 /* Define a resource. The arguments are the resource tree, RESOURCES,
347 and the location at which to put it in the tree, CIDS and IDS.
348 This returns a newly allocated res_resource structure, which the
349 caller is expected to initialize. If DUPOK is non-zero, then if a
350 resource with this ID exists, it is returned. Otherwise, a warning
351 is issued, and a new resource is created replacing the existing
354 struct res_resource
*
355 define_resource (resources
, cids
, ids
, dupok
)
356 struct res_directory
**resources
;
358 const struct res_id
*ids
;
361 struct res_entry
*re
= NULL
;
365 for (i
= 0; i
< cids
; i
++)
367 struct res_entry
**pp
;
369 if (*resources
== NULL
)
371 *resources
= (struct res_directory
*) xmalloc (sizeof **resources
);
372 (*resources
)->characteristics
= 0;
373 (*resources
)->time
= 0;
374 (*resources
)->major
= 0;
375 (*resources
)->minor
= 0;
376 (*resources
)->entries
= NULL
;
379 for (pp
= &(*resources
)->entries
; *pp
!= NULL
; pp
= &(*pp
)->next
)
380 if (res_id_cmp ((*pp
)->id
, ids
[i
]) == 0)
387 re
= (struct res_entry
*) xmalloc (sizeof *re
);
408 fprintf (stderr
, "%s: ", program_name
);
409 res_ids_print (stderr
, i
, ids
);
410 fprintf (stderr
, ": expected to be a directory\n");
414 resources
= &re
->u
.dir
;
420 fprintf (stderr
, "%s: ", program_name
);
421 res_ids_print (stderr
, cids
, ids
);
422 fprintf (stderr
, ": expected to be a leaf\n");
426 if (re
->u
.res
!= NULL
)
431 fprintf (stderr
, "%s: warning: ", program_name
);
432 res_ids_print (stderr
, cids
, ids
);
433 fprintf (stderr
, ": duplicate value\n");
436 re
->u
.res
= (struct res_resource
*) xmalloc (sizeof (struct res_resource
));
438 re
->u
.res
->type
= RES_TYPE_UNINITIALIZED
;
439 memset (&re
->u
.res
->res_info
, 0, sizeof (struct res_res_info
));
440 memset (&re
->u
.res
->coff_info
, 0, sizeof (struct res_coff_info
));
445 /* Define a standard resource. This is a version of define_resource
446 that just takes type, name, and language arguments. */
448 struct res_resource
*
449 define_standard_resource (resources
, type
, name
, language
, dupok
)
450 struct res_directory
**resources
;
462 a
[2].u
.id
= language
;
463 return define_resource (resources
, 3, a
, dupok
);
466 /* Return whether the dialog resource DIALOG is a DIALOG or a
470 extended_dialog (dialog
)
471 const struct dialog
*dialog
;
473 const struct dialog_control
*c
;
475 if (dialog
->ex
!= NULL
)
478 for (c
= dialog
->controls
; c
!= NULL
; c
= c
->next
)
479 if (c
->data
!= NULL
|| c
->help
!= 0)
485 /* Return whether MENUITEMS are a MENU or a MENUEX. */
488 extended_menu (menuitems
)
489 const struct menuitem
*menuitems
;
491 const struct menuitem
*mi
;
493 for (mi
= menuitems
; mi
!= NULL
; mi
= mi
->next
)
495 if (mi
->help
!= 0 || mi
->state
!= 0)
497 if (mi
->popup
!= NULL
&& mi
->id
!= 0)
500 & ~ (MENUITEM_CHECKED
504 | MENUITEM_MENUBARBREAK
505 | MENUITEM_MENUBREAK
))
508 if (mi
->popup
!= NULL
)
510 if (extended_menu (mi
->popup
))
518 /* Convert a string to a format type, or exit if it can't be done. */
520 static enum res_format
521 format_from_name (name
)
524 const struct format_map
*m
;
526 for (m
= format_names
; m
->name
!= NULL
; m
++)
527 if (strcasecmp (m
->name
, name
) == 0)
532 fprintf (stderr
, "%s: unknown format type `%s'\n", program_name
, name
);
533 fprintf (stderr
, "%s: supported formats:", program_name
);
534 for (m
= format_names
; m
->name
!= NULL
; m
++)
535 fprintf (stderr
, " %s", m
->name
);
536 fprintf (stderr
, "\n");
543 /* Work out a format type given a file name. If INPUT is non-zero,
544 it's OK to look at the file itself. */
546 static enum res_format
547 format_from_filename (filename
, input
)
548 const char *filename
;
553 unsigned char b1
, b2
, b3
, b4
, b5
;
556 /* If we have an extension, see if we recognize it as implying a
557 particular format. */
558 ext
= strrchr (filename
, '.');
561 const struct format_map
*m
;
564 for (m
= format_fileexts
; m
->name
!= NULL
; m
++)
565 if (strcasecmp (m
->name
, ext
) == 0)
569 /* If we don't recognize the name of an output file, assume it's a
573 return RES_FORMAT_COFF
;
575 /* Read the first few bytes of the file to see if we can guess what
578 e
= fopen (filename
, FOPEN_RB
);
580 fatal ("%s: %s", filename
, strerror (errno
));
590 /* A PE executable starts with 0x4d 0x5a. */
591 if (b1
== 0x4d && b2
== 0x5a)
592 return RES_FORMAT_COFF
;
594 /* A COFF .o file starts with a COFF magic number. */
595 magic
= (b2
<< 8) | b1
;
598 case 0x14c: /* i386 */
599 case 0x166: /* MIPS */
600 case 0x184: /* Alpha */
601 case 0x268: /* 68k */
602 case 0x1f0: /* PowerPC */
604 return RES_FORMAT_COFF
;
607 /* A RES file starts with 0x0 0x0 0x0 0x0 0x20 0x0 0x0 0x0. */
608 if (b1
== 0 && b2
== 0 && b3
== 0 && b4
== 0 && b5
== 0x20)
609 return RES_FORMAT_RES
;
611 /* If every character is printable or space, assume it's an RC file. */
612 if ((isprint (b1
) || isspace (b1
))
613 && (isprint (b2
) || isspace (b2
))
614 && (isprint (b3
) || isspace (b3
))
615 && (isprint (b4
) || isspace (b4
))
616 && (isprint (b5
) || isspace (b5
)))
617 return RES_FORMAT_RC
;
619 /* Otherwise, we give up. */
620 fatal ("can not determine type of file `%s'; use the -I option",
623 /* Return something to silence the compiler warning. */
624 return RES_FORMAT_UNKNOWN
;
627 /* Print a usage message and exit. */
630 usage (stream
, status
)
634 fprintf (stream
, "Usage: %s [options] [input-file] [output-file]\n",
638 -i FILE, --input FILE Name input file\n\
639 -o FILE, --output FILE Name output file\n\
640 -I FORMAT, --input-format FORMAT\n\
641 Specify input format\n\
642 -O FORMAT, --output-format FORMAT\n\
643 Specify output format\n\
644 -F TARGET, --target TARGET Specify COFF target\n\
645 --preprocessor PROGRAM Program to use to preprocess rc file\n\
646 --include-dir DIR Include directory when preprocessing rc file\n\
647 --define SYM[=VAL] Define SYM when preprocessing rc file\n\
648 --language VAL Set language when reading rc file\n\
650 --yydebug Turn on parser debugging\n\
652 --help Print this help message\n\
653 --version Print version information\n");
655 FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
656 extension if not specified. A single file name is an input file.\n\
657 No input-file is stdin, default rc. No output-file is stdout, default rc.\n");
658 list_supported_targets (program_name
, stream
);
660 fprintf (stream
, "Report bugs to bug-gnu-utils@prep.ai.mit.edu\n");
664 /* The main function. */
672 char *input_filename
;
673 char *output_filename
;
674 enum res_format input_format
;
675 enum res_format output_format
;
680 struct res_directory
*resources
;
682 program_name
= argv
[0];
683 xmalloc_set_program_name (program_name
);
686 set_default_bfd_target ();
688 input_filename
= NULL
;
689 output_filename
= NULL
;
690 input_format
= RES_FORMAT_UNKNOWN
;
691 output_format
= RES_FORMAT_UNKNOWN
;
697 while ((c
= getopt_long (argc
, argv
, "i:o:I:O:F:", long_options
,
703 input_filename
= optarg
;
707 output_filename
= optarg
;
711 input_format
= format_from_name (optarg
);
715 output_format
= format_from_name (optarg
);
722 case OPTION_PREPROCESSOR
:
723 preprocessor
= optarg
;
727 if (preprocargs
== NULL
)
729 preprocargs
= xmalloc (strlen (optarg
) + 3);
730 sprintf (preprocargs
, "-D%s", optarg
);
736 n
= xmalloc (strlen (preprocargs
) + strlen (optarg
) + 4);
737 sprintf (n
, "%s -D%s", preprocargs
, optarg
);
743 case OPTION_INCLUDE_DIR
:
744 if (preprocargs
== NULL
)
746 preprocargs
= xmalloc (strlen (optarg
) + 3);
747 sprintf (preprocargs
, "-I%s", optarg
);
753 n
= xmalloc (strlen (preprocargs
) + strlen (optarg
) + 4);
754 sprintf (n
, "%s -I%s", preprocargs
, optarg
);
760 struct include_dir
*n
, **pp
;
762 n
= (struct include_dir
*) xmalloc (sizeof *n
);
766 for (pp
= &include_dirs
; *pp
!= NULL
; pp
= &(*pp
)->next
)
773 case OPTION_LANGUAGE
:
774 language
= strtol (optarg
, (char **) NULL
, 16);
788 print_version ("windres");
797 if (input_filename
== NULL
&& optind
< argc
)
799 input_filename
= argv
[optind
];
803 if (output_filename
== NULL
&& optind
< argc
)
805 output_filename
= argv
[optind
];
812 if (input_format
== RES_FORMAT_UNKNOWN
)
814 if (input_filename
== NULL
)
815 input_format
= RES_FORMAT_RC
;
817 input_format
= format_from_filename (input_filename
, 1);
820 if (output_format
== RES_FORMAT_UNKNOWN
)
822 if (output_filename
== NULL
)
823 output_format
= RES_FORMAT_RC
;
825 output_format
= format_from_filename (output_filename
, 0);
828 /* Read the input file. */
830 switch (input_format
)
835 resources
= read_rc_file (input_filename
, preprocessor
, preprocargs
,
839 resources
= read_res_file (input_filename
);
841 case RES_FORMAT_COFF
:
842 resources
= read_coff_rsrc (input_filename
, target
);
846 /* Write the output file. */
848 switch (output_format
)
853 write_rc_file (output_filename
, resources
);
856 write_res_file (output_filename
, resources
);
858 case RES_FORMAT_COFF
:
859 write_coff_file (output_filename
, target
, resources
);
867 struct res_directory
*
868 read_res_file (filename
)
869 const char *filename
;
871 fatal ("read_res_file unimplemented");
876 write_res_file (filename
, resources
)
877 const char *filename
;
878 const struct res_directory
*resources
;
880 fatal ("write_res_file unimplemented");
884 write_coff_file (filename
, target
, resources
)
885 const char *filename
;
887 const struct res_directory
*resources
;
889 fatal ("write_coff_file unimplemented");