1 /* windres.c -- a program to manipulate Windows resources
2 Copyright 1997, 1998 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"
49 /* An enumeration of format types. */
55 /* Textual RC file. */
57 /* Binary RES file. */
63 /* A structure used to map between format types and strings. */
68 enum res_format format;
71 /* A mapping between names and format types. */
73 static const struct format_map format_names[] =
75 { "rc", RES_FORMAT_RC },
76 { "res", RES_FORMAT_RES },
77 { "coff", RES_FORMAT_COFF },
78 { NULL, RES_FORMAT_UNKNOWN }
81 /* A mapping from file extensions to format types. */
83 static const struct format_map format_fileexts[] =
85 { "rc", RES_FORMAT_RC },
86 { "res", RES_FORMAT_RES },
87 { "exe", RES_FORMAT_COFF },
88 { "obj", RES_FORMAT_COFF },
89 { "o", RES_FORMAT_COFF },
90 { NULL, RES_FORMAT_UNKNOWN }
93 /* A list of include directories. */
97 struct include_dir *next;
101 static struct include_dir *include_dirs;
105 /* 150 isn't special; it's just an arbitrary non-ASCII char value. */
107 #define OPTION_DEFINE 150
108 #define OPTION_HELP (OPTION_DEFINE + 1)
109 #define OPTION_INCLUDE_DIR (OPTION_HELP + 1)
110 #define OPTION_LANGUAGE (OPTION_INCLUDE_DIR + 1)
111 #define OPTION_PREPROCESSOR (OPTION_LANGUAGE + 1)
112 #define OPTION_VERSION (OPTION_PREPROCESSOR + 1)
113 #define OPTION_YYDEBUG (OPTION_VERSION + 1)
115 static const struct option long_options[] =
117 {"define", required_argument, 0, OPTION_DEFINE},
118 {"help", no_argument, 0, OPTION_HELP},
119 {"include-dir", required_argument, 0, OPTION_INCLUDE_DIR},
120 {"input-format", required_argument, 0, 'I'},
121 {"language", required_argument, 0, OPTION_LANGUAGE},
122 {"output-format", required_argument, 0, 'O'},
123 {"preprocessor", required_argument, 0, OPTION_PREPROCESSOR},
124 {"target", required_argument, 0, 'F'},
125 {"version", no_argument, 0, OPTION_VERSION},
126 {"yydebug", no_argument, 0, OPTION_YYDEBUG},
127 {0, no_argument, 0, 0}
130 /* Static functions. */
132 static void res_init PARAMS ((void));
133 static int extended_menuitems PARAMS ((const struct menuitem *));
134 static enum res_format format_from_name PARAMS ((const char *));
135 static enum res_format format_from_filename PARAMS ((const char *, int));
136 static void usage PARAMS ((FILE *, int));
137 static int cmp_res_entry PARAMS ((const PTR, const PTR));
138 static struct res_directory *sort_resources PARAMS ((struct res_directory *));
140 /* When we are building a resource tree, we allocate everything onto
141 an obstack, so that we can free it all at once if we want. */
143 #define obstack_chunk_alloc xmalloc
144 #define obstack_chunk_free free
146 /* The resource building obstack. */
148 static struct obstack res_obstack;
150 /* Initialize the resource building obstack. */
155 obstack_init (&res_obstack);
158 /* Allocate space on the resource building obstack. */
164 return (PTR) obstack_alloc (&res_obstack, bytes);
167 /* We also use an obstack to save memory used while writing out a set
170 static struct obstack reswr_obstack;
172 /* Initialize the resource writing obstack. */
177 obstack_init (&reswr_obstack);
180 /* Allocate space on the resource writing obstack. */
186 return (PTR) obstack_alloc (&reswr_obstack, bytes);
189 /* Open a file using the include directory search list. */
192 open_file_search (filename, mode, errmsg, real_filename)
193 const char *filename;
196 char **real_filename;
199 struct include_dir *d;
201 e = fopen (filename, mode);
204 *real_filename = xstrdup (filename);
210 for (d = include_dirs; d != NULL; d = d->next)
214 n = (char *) xmalloc (strlen (d->dir) + strlen (filename) + 2);
215 sprintf (n, "%s/%s", d->dir, filename);
228 fatal (_("can't open %s `%s': %s"), errmsg, filename, strerror (errno));
230 /* Return a value to avoid a compiler warning. */
234 /* Unicode support. */
236 /* Convert an ASCII string to a unicode string. We just copy it,
237 expanding chars to shorts, rather than doing something intelligent. */
240 unicode_from_ascii (length, unicode, ascii)
249 len = strlen (ascii);
254 *unicode = ((unichar *) res_alloc ((len + 1) * sizeof (unichar)));
256 for (s = ascii, w = *unicode; *s != '\0'; s++, w++)
261 /* Print the unicode string UNICODE to the file E. LENGTH is the
262 number of characters to print, or -1 if we should print until the
263 end of the string. */
266 unicode_print (e, unicode, length)
268 const unichar *unicode;
282 if (ch == 0 && length < 0)
287 if ((ch & 0x7f) == ch)
291 else if (isprint (ch))
326 fprintf (e, "\\%03o", (unsigned int) ch);
331 else if ((ch & 0xff) == ch)
332 fprintf (e, "\\%03o", (unsigned int) ch);
334 fprintf (e, "\\x%x", (unsigned int) ch);
338 /* Compare two resource ID's. We consider name entries to come before
339 numeric entries, because that is how they appear in the COFF .rsrc
353 else if (a.u.id < b.u.id)
360 unichar *as, *ase, *bs, *bse;
366 ase = as + a.u.n.length;
368 bse = bs + b.u.n.length;
376 i = (int) *as - (int) *bs;
390 /* Print a resource ID. */
393 res_id_print (stream, id, quote)
399 fprintf (stream, "%lu", id.u.id);
404 unicode_print (stream, id.u.n.name, id.u.n.length);
410 /* Print a list of resource ID's. */
413 res_ids_print (stream, cids, ids)
416 const struct res_id *ids;
420 for (i = 0; i < cids; i++)
422 res_id_print (stream, ids[i], 1);
424 fprintf (stream, ": ");
428 /* Convert an ASCII string to a resource ID. */
431 res_string_to_id (res_id, string)
432 struct res_id *res_id;
436 unicode_from_ascii (&res_id->u.n.length, &res_id->u.n.name, string);
439 /* Define a resource. The arguments are the resource tree, RESOURCES,
440 and the location at which to put it in the tree, CIDS and IDS.
441 This returns a newly allocated res_resource structure, which the
442 caller is expected to initialize. If DUPOK is non-zero, then if a
443 resource with this ID exists, it is returned. Otherwise, a warning
444 is issued, and a new resource is created replacing the existing
447 struct res_resource *
448 define_resource (resources, cids, ids, dupok)
449 struct res_directory **resources;
451 const struct res_id *ids;
454 struct res_entry *re = NULL;
458 for (i = 0; i < cids; i++)
460 struct res_entry **pp;
462 if (*resources == NULL)
464 static unsigned long timeval;
466 /* Use the same timestamp for every resource created in a
469 timeval = time (NULL);
471 *resources = ((struct res_directory *)
472 res_alloc (sizeof **resources));
473 (*resources)->characteristics = 0;
474 (*resources)->time = timeval;
475 (*resources)->major = 0;
476 (*resources)->minor = 0;
477 (*resources)->entries = NULL;
480 for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
481 if (res_id_cmp ((*pp)->id, ids[i]) == 0)
488 re = (struct res_entry *) res_alloc (sizeof *re);
509 fprintf (stderr, "%s: ", program_name);
510 res_ids_print (stderr, i, ids);
511 fprintf (stderr, _(": expected to be a directory\n"));
515 resources = &re->u.dir;
521 fprintf (stderr, "%s: ", program_name);
522 res_ids_print (stderr, cids, ids);
523 fprintf (stderr, _(": expected to be a leaf\n"));
527 if (re->u.res != NULL)
532 fprintf (stderr, _("%s: warning: "), program_name);
533 res_ids_print (stderr, cids, ids);
534 fprintf (stderr, _(": duplicate value\n"));
537 re->u.res = ((struct res_resource *)
538 res_alloc (sizeof (struct res_resource)));
540 re->u.res->type = RES_TYPE_UNINITIALIZED;
541 memset (&re->u.res->res_info, 0, sizeof (struct res_res_info));
542 memset (&re->u.res->coff_info, 0, sizeof (struct res_coff_info));
547 /* Define a standard resource. This is a version of define_resource
548 that just takes type, name, and language arguments. */
550 struct res_resource *
551 define_standard_resource (resources, type, name, language, dupok)
552 struct res_directory **resources;
564 a[2].u.id = language;
565 return define_resource (resources, 3, a, dupok);
568 /* Comparison routine for resource sorting. */
571 cmp_res_entry (p1, p2)
575 const struct res_entry **re1, **re2;
577 re1 = (const struct res_entry **) p1;
578 re2 = (const struct res_entry **) p2;
579 return res_id_cmp ((*re1)->id, (*re2)->id);
582 /* Sort the resources. */
584 static struct res_directory *
585 sort_resources (resdir)
586 struct res_directory *resdir;
589 struct res_entry *re;
590 struct res_entry **a;
592 if (resdir->entries == NULL)
596 for (re = resdir->entries; re != NULL; re = re->next)
599 /* This is a recursive routine, so using xmalloc is probably better
601 a = (struct res_entry **) xmalloc (c * sizeof (struct res_entry *));
603 for (i = 0, re = resdir->entries; re != NULL; re = re->next, i++)
606 qsort (a, c, sizeof (struct res_entry *), cmp_res_entry);
608 resdir->entries = a[0];
609 for (i = 0; i < c - 1; i++)
610 a[i]->next = a[i + 1];
615 /* Now sort the subdirectories. */
617 for (re = resdir->entries; re != NULL; re = re->next)
619 re->u.dir = sort_resources (re->u.dir);
624 /* Return whether the dialog resource DIALOG is a DIALOG or a
628 extended_dialog (dialog)
629 const struct dialog *dialog;
631 const struct dialog_control *c;
633 if (dialog->ex != NULL)
636 for (c = dialog->controls; c != NULL; c = c->next)
637 if (c->data != NULL || c->help != 0)
643 /* Return whether MENUITEMS are a MENU or a MENUEX. */
647 const struct menu *menu;
649 return extended_menuitems (menu->items);
653 extended_menuitems (menuitems)
654 const struct menuitem *menuitems;
656 const struct menuitem *mi;
658 for (mi = menuitems; mi != NULL; mi = mi->next)
660 if (mi->help != 0 || mi->state != 0)
662 if (mi->popup != NULL && mi->id != 0)
665 & ~ (MENUITEM_CHECKED
669 | MENUITEM_MENUBARBREAK
670 | MENUITEM_MENUBREAK))
673 if (mi->popup != NULL)
675 if (extended_menuitems (mi->popup))
683 /* Convert a string to a format type, or exit if it can't be done. */
685 static enum res_format
686 format_from_name (name)
689 const struct format_map *m;
691 for (m = format_names; m->name != NULL; m++)
692 if (strcasecmp (m->name, name) == 0)
697 fprintf (stderr, _("%s: unknown format type `%s'\n"), program_name, name);
698 fprintf (stderr, _("%s: supported formats:"), program_name);
699 for (m = format_names; m->name != NULL; m++)
700 fprintf (stderr, " %s", m->name);
701 fprintf (stderr, "\n");
708 /* Work out a format type given a file name. If INPUT is non-zero,
709 it's OK to look at the file itself. */
711 static enum res_format
712 format_from_filename (filename, input)
713 const char *filename;
718 unsigned char b1, b2, b3, b4, b5;
721 /* If we have an extension, see if we recognize it as implying a
722 particular format. */
723 ext = strrchr (filename, '.');
726 const struct format_map *m;
729 for (m = format_fileexts; m->name != NULL; m++)
730 if (strcasecmp (m->name, ext) == 0)
734 /* If we don't recognize the name of an output file, assume it's a
738 return RES_FORMAT_COFF;
740 /* Read the first few bytes of the file to see if we can guess what
743 e = fopen (filename, FOPEN_RB);
745 fatal ("%s: %s", filename, strerror (errno));
755 /* A PE executable starts with 0x4d 0x5a. */
756 if (b1 == 0x4d && b2 == 0x5a)
757 return RES_FORMAT_COFF;
759 /* A COFF .o file starts with a COFF magic number. */
760 magic = (b2 << 8) | b1;
763 case 0x14c: /* i386 */
764 case 0x166: /* MIPS */
765 case 0x184: /* Alpha */
766 case 0x268: /* 68k */
767 case 0x1f0: /* PowerPC */
769 return RES_FORMAT_COFF;
772 /* A RES file starts with 0x0 0x0 0x0 0x0 0x20 0x0 0x0 0x0. */
773 if (b1 == 0 && b2 == 0 && b3 == 0 && b4 == 0 && b5 == 0x20)
774 return RES_FORMAT_RES;
776 /* If every character is printable or space, assume it's an RC file. */
777 if ((isprint (b1) || isspace (b1))
778 && (isprint (b2) || isspace (b2))
779 && (isprint (b3) || isspace (b3))
780 && (isprint (b4) || isspace (b4))
781 && (isprint (b5) || isspace (b5)))
782 return RES_FORMAT_RC;
784 /* Otherwise, we give up. */
785 fatal (_("can not determine type of file `%s'; use the -I option"),
788 /* Return something to silence the compiler warning. */
789 return RES_FORMAT_UNKNOWN;
792 /* Print a usage message and exit. */
795 usage (stream, status)
799 fprintf (stream, _("Usage: %s [options] [input-file] [output-file]\n"),
801 fprintf (stream, _("\
803 -i FILE, --input FILE Name input file\n\
804 -o FILE, --output FILE Name output file\n\
805 -I FORMAT, --input-format FORMAT\n\
806 Specify input format\n\
807 -O FORMAT, --output-format FORMAT\n\
808 Specify output format\n\
809 -F TARGET, --target TARGET Specify COFF target\n\
810 --preprocessor PROGRAM Program to use to preprocess rc file\n\
811 --include-dir DIR Include directory when preprocessing rc file\n\
812 --define SYM[=VAL] Define SYM when preprocessing rc file\n\
813 --language VAL Set language when reading rc file\n"));
815 fprintf (stream, _("\
816 --yydebug Turn on parser debugging\n"));
818 fprintf (stream, _("\
819 --help Print this help message\n\
820 --version Print version information\n"));
821 fprintf (stream, _("\
822 FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
823 extension if not specified. A single file name is an input file.\n\
824 No input-file is stdin, default rc. No output-file is stdout, default rc.\n"));
825 list_supported_targets (program_name, stream);
827 fprintf (stream, _("Report bugs to bug-gnu-utils@gnu.org\n"));
831 /* The main function. */
839 char *input_filename;
840 char *output_filename;
841 enum res_format input_format;
842 enum res_format output_format;
847 struct res_directory *resources;
849 setlocale (LC_MESSAGES, "");
850 bindtextdomain (PACKAGE, LOCALEDIR);
851 textdomain (PACKAGE);
853 program_name = argv[0];
854 xmalloc_set_program_name (program_name);
857 set_default_bfd_target ();
861 input_filename = NULL;
862 output_filename = NULL;
863 input_format = RES_FORMAT_UNKNOWN;
864 output_format = RES_FORMAT_UNKNOWN;
870 while ((c = getopt_long (argc, argv, "i:o:I:O:F:", long_options,
876 input_filename = optarg;
880 output_filename = optarg;
884 input_format = format_from_name (optarg);
888 output_format = format_from_name (optarg);
895 case OPTION_PREPROCESSOR:
896 preprocessor = optarg;
900 if (preprocargs == NULL)
902 preprocargs = xmalloc (strlen (optarg) + 3);
903 sprintf (preprocargs, "-D%s", optarg);
909 n = xmalloc (strlen (preprocargs) + strlen (optarg) + 4);
910 sprintf (n, "%s -D%s", preprocargs, optarg);
916 case OPTION_INCLUDE_DIR:
917 if (preprocargs == NULL)
919 preprocargs = xmalloc (strlen (optarg) + 3);
920 sprintf (preprocargs, "-I%s", optarg);
926 n = xmalloc (strlen (preprocargs) + strlen (optarg) + 4);
927 sprintf (n, "%s -I%s", preprocargs, optarg);
933 struct include_dir *n, **pp;
935 n = (struct include_dir *) xmalloc (sizeof *n);
939 for (pp = &include_dirs; *pp != NULL; pp = &(*pp)->next)
946 case OPTION_LANGUAGE:
947 language = strtol (optarg, (char **) NULL, 16);
961 print_version ("windres");
970 if (input_filename == NULL && optind < argc)
972 input_filename = argv[optind];
976 if (output_filename == NULL && optind < argc)
978 output_filename = argv[optind];
985 if (input_format == RES_FORMAT_UNKNOWN)
987 if (input_filename == NULL)
988 input_format = RES_FORMAT_RC;
990 input_format = format_from_filename (input_filename, 1);
993 if (output_format == RES_FORMAT_UNKNOWN)
995 if (output_filename == NULL)
996 output_format = RES_FORMAT_RC;
998 output_format = format_from_filename (output_filename, 0);
1001 /* Read the input file. */
1003 switch (input_format)
1008 resources = read_rc_file (input_filename, preprocessor, preprocargs,
1011 case RES_FORMAT_RES:
1012 resources = read_res_file (input_filename);
1014 case RES_FORMAT_COFF:
1015 resources = read_coff_rsrc (input_filename, target);
1019 if (resources == NULL)
1020 fatal (_("no resources"));
1022 /* Sort the resources. This is required for COFF, convenient for
1023 rc, and unimportant for res. */
1025 resources = sort_resources (resources);
1027 /* Write the output file. */
1031 switch (output_format)
1036 write_rc_file (output_filename, resources);
1038 case RES_FORMAT_RES:
1039 write_res_file (output_filename, resources);
1041 case RES_FORMAT_COFF:
1042 write_coff_file (output_filename, target, resources);
1050 struct res_directory *
1051 read_res_file (filename)
1052 const char *filename;
1054 fatal (_("read_res_file unimplemented"));
1059 write_res_file (filename, resources)
1060 const char *filename;
1061 const struct res_directory *resources;
1063 fatal (_("write_res_file unimplemented"));