Initial revision
[binutils.git] / binutils / windres.c
blob7de28c259e6ab413b46499079d5ec2c6061edeb1
1 /* windres.c -- a program to manipulate Windows resources
2 Copyright 1997, 1998, 1999 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
20 02111-1307, USA. */
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
25 program.
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>.
38 #include "bfd.h"
39 #include "getopt.h"
40 #include "bucomm.h"
41 #include "libiberty.h"
42 #include "obstack.h"
43 #include "windres.h"
45 #include <assert.h>
46 #include <ctype.h>
47 #include <time.h>
49 /* An enumeration of format types. */
51 enum res_format
53 /* Unknown format. */
54 RES_FORMAT_UNKNOWN,
55 /* Textual RC file. */
56 RES_FORMAT_RC,
57 /* Binary RES file. */
58 RES_FORMAT_RES,
59 /* COFF file. */
60 RES_FORMAT_COFF
63 /* A structure used to map between format types and strings. */
65 struct format_map
67 const char *name;
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. */
95 struct include_dir
97 struct include_dir *next;
98 char *dir;
101 static struct include_dir *include_dirs;
103 /* Long options. */
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. */
152 static void
153 res_init ()
155 obstack_init (&res_obstack);
158 /* Allocate space on the resource building obstack. */
161 res_alloc (bytes)
162 size_t bytes;
164 return (PTR) obstack_alloc (&res_obstack, bytes);
167 /* We also use an obstack to save memory used while writing out a set
168 of resources. */
170 static struct obstack reswr_obstack;
172 /* Initialize the resource writing obstack. */
174 static void
175 reswr_init ()
177 obstack_init (&reswr_obstack);
180 /* Allocate space on the resource writing obstack. */
183 reswr_alloc (bytes)
184 size_t bytes;
186 return (PTR) obstack_alloc (&reswr_obstack, bytes);
189 /* Open a file using the include directory search list. */
191 FILE *
192 open_file_search (filename, mode, errmsg, real_filename)
193 const char *filename;
194 const char *mode;
195 const char *errmsg;
196 char **real_filename;
198 FILE *e;
199 struct include_dir *d;
201 e = fopen (filename, mode);
202 if (e != NULL)
204 *real_filename = xstrdup (filename);
205 return e;
208 if (errno == ENOENT)
210 for (d = include_dirs; d != NULL; d = d->next)
212 char *n;
214 n = (char *) xmalloc (strlen (d->dir) + strlen (filename) + 2);
215 sprintf (n, "%s/%s", d->dir, filename);
216 e = fopen (n, mode);
217 if (e != NULL)
219 *real_filename = n;
220 return e;
223 if (errno != ENOENT)
224 break;
228 fatal (_("can't open %s `%s': %s"), errmsg, filename, strerror (errno));
230 /* Return a value to avoid a compiler warning. */
231 return NULL;
234 /* Compare two resource ID's. We consider name entries to come before
235 numeric entries, because that is how they appear in the COFF .rsrc
236 section. */
239 res_id_cmp (a, b)
240 struct res_id a;
241 struct res_id b;
243 if (! a.named)
245 if (b.named)
246 return 1;
247 if (a.u.id > b.u.id)
248 return 1;
249 else if (a.u.id < b.u.id)
250 return -1;
251 else
252 return 0;
254 else
256 unichar *as, *ase, *bs, *bse;
258 if (! b.named)
259 return -1;
261 as = a.u.n.name;
262 ase = as + a.u.n.length;
263 bs = b.u.n.name;
264 bse = bs + b.u.n.length;
266 while (as < ase)
268 int i;
270 if (bs >= bse)
271 return 1;
272 i = (int) *as - (int) *bs;
273 if (i != 0)
274 return i;
275 ++as;
276 ++bs;
279 if (bs < bse)
280 return -1;
282 return 0;
286 /* Print a resource ID. */
288 void
289 res_id_print (stream, id, quote)
290 FILE *stream;
291 struct res_id id;
292 int quote;
294 if (! id.named)
295 fprintf (stream, "%lu", id.u.id);
296 else
298 if (quote)
299 putc ('"', stream);
300 unicode_print (stream, id.u.n.name, id.u.n.length);
301 if (quote)
302 putc ('"', stream);
306 /* Print a list of resource ID's. */
308 void
309 res_ids_print (stream, cids, ids)
310 FILE *stream;
311 int cids;
312 const struct res_id *ids;
314 int i;
316 for (i = 0; i < cids; i++)
318 res_id_print (stream, ids[i], 1);
319 if (i + 1 < cids)
320 fprintf (stream, ": ");
324 /* Convert an ASCII string to a resource ID. */
326 void
327 res_string_to_id (res_id, string)
328 struct res_id *res_id;
329 const char *string;
331 res_id->named = 1;
332 unicode_from_ascii (&res_id->u.n.length, &res_id->u.n.name, string);
335 /* Define a resource. The arguments are the resource tree, RESOURCES,
336 and the location at which to put it in the tree, CIDS and IDS.
337 This returns a newly allocated res_resource structure, which the
338 caller is expected to initialize. If DUPOK is non-zero, then if a
339 resource with this ID exists, it is returned. Otherwise, a warning
340 is issued, and a new resource is created replacing the existing
341 one. */
343 struct res_resource *
344 define_resource (resources, cids, ids, dupok)
345 struct res_directory **resources;
346 int cids;
347 const struct res_id *ids;
348 int dupok;
350 struct res_entry *re = NULL;
351 int i;
353 assert (cids > 0);
354 for (i = 0; i < cids; i++)
356 struct res_entry **pp;
358 if (*resources == NULL)
360 static unsigned long timeval;
362 /* Use the same timestamp for every resource created in a
363 single run. */
364 if (timeval == 0)
365 timeval = time (NULL);
367 *resources = ((struct res_directory *)
368 res_alloc (sizeof **resources));
369 (*resources)->characteristics = 0;
370 (*resources)->time = timeval;
371 (*resources)->major = 0;
372 (*resources)->minor = 0;
373 (*resources)->entries = NULL;
376 for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
377 if (res_id_cmp ((*pp)->id, ids[i]) == 0)
378 break;
380 if (*pp != NULL)
381 re = *pp;
382 else
384 re = (struct res_entry *) res_alloc (sizeof *re);
385 re->next = NULL;
386 re->id = ids[i];
387 if ((i + 1) < cids)
389 re->subdir = 1;
390 re->u.dir = NULL;
392 else
394 re->subdir = 0;
395 re->u.res = NULL;
398 *pp = re;
401 if ((i + 1) < cids)
403 if (! re->subdir)
405 fprintf (stderr, "%s: ", program_name);
406 res_ids_print (stderr, i, ids);
407 fprintf (stderr, _(": expected to be a directory\n"));
408 xexit (1);
411 resources = &re->u.dir;
415 if (re->subdir)
417 fprintf (stderr, "%s: ", program_name);
418 res_ids_print (stderr, cids, ids);
419 fprintf (stderr, _(": expected to be a leaf\n"));
420 xexit (1);
423 if (re->u.res != NULL)
425 if (dupok)
426 return re->u.res;
428 fprintf (stderr, _("%s: warning: "), program_name);
429 res_ids_print (stderr, cids, ids);
430 fprintf (stderr, _(": duplicate value\n"));
433 re->u.res = ((struct res_resource *)
434 res_alloc (sizeof (struct res_resource)));
436 re->u.res->type = RES_TYPE_UNINITIALIZED;
437 memset (&re->u.res->res_info, 0, sizeof (struct res_res_info));
438 memset (&re->u.res->coff_info, 0, sizeof (struct res_coff_info));
440 return re->u.res;
443 /* Define a standard resource. This is a version of define_resource
444 that just takes type, name, and language arguments. */
446 struct res_resource *
447 define_standard_resource (resources, type, name, language, dupok)
448 struct res_directory **resources;
449 int type;
450 struct res_id name;
451 int language;
452 int dupok;
454 struct res_id a[3];
456 a[0].named = 0;
457 a[0].u.id = type;
458 a[1] = name;
459 a[2].named = 0;
460 a[2].u.id = language;
461 return define_resource (resources, 3, a, dupok);
464 /* Comparison routine for resource sorting. */
466 static int
467 cmp_res_entry (p1, p2)
468 const PTR p1;
469 const PTR p2;
471 const struct res_entry **re1, **re2;
473 re1 = (const struct res_entry **) p1;
474 re2 = (const struct res_entry **) p2;
475 return res_id_cmp ((*re1)->id, (*re2)->id);
478 /* Sort the resources. */
480 static struct res_directory *
481 sort_resources (resdir)
482 struct res_directory *resdir;
484 int c, i;
485 struct res_entry *re;
486 struct res_entry **a;
488 if (resdir->entries == NULL)
489 return resdir;
491 c = 0;
492 for (re = resdir->entries; re != NULL; re = re->next)
493 ++c;
495 /* This is a recursive routine, so using xmalloc is probably better
496 than alloca. */
497 a = (struct res_entry **) xmalloc (c * sizeof (struct res_entry *));
499 for (i = 0, re = resdir->entries; re != NULL; re = re->next, i++)
500 a[i] = re;
502 qsort (a, c, sizeof (struct res_entry *), cmp_res_entry);
504 resdir->entries = a[0];
505 for (i = 0; i < c - 1; i++)
506 a[i]->next = a[i + 1];
507 a[i]->next = NULL;
509 free (a);
511 /* Now sort the subdirectories. */
513 for (re = resdir->entries; re != NULL; re = re->next)
514 if (re->subdir)
515 re->u.dir = sort_resources (re->u.dir);
517 return resdir;
520 /* Return whether the dialog resource DIALOG is a DIALOG or a
521 DIALOGEX. */
524 extended_dialog (dialog)
525 const struct dialog *dialog;
527 const struct dialog_control *c;
529 if (dialog->ex != NULL)
530 return 1;
532 for (c = dialog->controls; c != NULL; c = c->next)
533 if (c->data != NULL || c->help != 0)
534 return 1;
536 return 0;
539 /* Return whether MENUITEMS are a MENU or a MENUEX. */
542 extended_menu (menu)
543 const struct menu *menu;
545 return extended_menuitems (menu->items);
548 static int
549 extended_menuitems (menuitems)
550 const struct menuitem *menuitems;
552 const struct menuitem *mi;
554 for (mi = menuitems; mi != NULL; mi = mi->next)
556 if (mi->help != 0 || mi->state != 0)
557 return 1;
558 if (mi->popup != NULL && mi->id != 0)
559 return 1;
560 if ((mi->type
561 & ~ (MENUITEM_CHECKED
562 | MENUITEM_GRAYED
563 | MENUITEM_HELP
564 | MENUITEM_INACTIVE
565 | MENUITEM_MENUBARBREAK
566 | MENUITEM_MENUBREAK))
567 != 0)
568 return 1;
569 if (mi->popup != NULL)
571 if (extended_menuitems (mi->popup))
572 return 1;
576 return 0;
579 /* Convert a string to a format type, or exit if it can't be done. */
581 static enum res_format
582 format_from_name (name)
583 const char *name;
585 const struct format_map *m;
587 for (m = format_names; m->name != NULL; m++)
588 if (strcasecmp (m->name, name) == 0)
589 break;
591 if (m->name == NULL)
593 fprintf (stderr, _("%s: unknown format type `%s'\n"), program_name, name);
594 fprintf (stderr, _("%s: supported formats:"), program_name);
595 for (m = format_names; m->name != NULL; m++)
596 fprintf (stderr, " %s", m->name);
597 fprintf (stderr, "\n");
598 xexit (1);
601 return m->format;
604 /* Work out a format type given a file name. If INPUT is non-zero,
605 it's OK to look at the file itself. */
607 static enum res_format
608 format_from_filename (filename, input)
609 const char *filename;
610 int input;
612 const char *ext;
613 FILE *e;
614 unsigned char b1, b2, b3, b4, b5;
615 int magic;
617 /* If we have an extension, see if we recognize it as implying a
618 particular format. */
619 ext = strrchr (filename, '.');
620 if (ext != NULL)
622 const struct format_map *m;
624 ++ext;
625 for (m = format_fileexts; m->name != NULL; m++)
626 if (strcasecmp (m->name, ext) == 0)
627 return m->format;
630 /* If we don't recognize the name of an output file, assume it's a
631 COFF file. */
633 if (! input)
634 return RES_FORMAT_COFF;
636 /* Read the first few bytes of the file to see if we can guess what
637 it is. */
639 e = fopen (filename, FOPEN_RB);
640 if (e == NULL)
641 fatal ("%s: %s", filename, strerror (errno));
643 b1 = getc (e);
644 b2 = getc (e);
645 b3 = getc (e);
646 b4 = getc (e);
647 b5 = getc (e);
649 fclose (e);
651 /* A PE executable starts with 0x4d 0x5a. */
652 if (b1 == 0x4d && b2 == 0x5a)
653 return RES_FORMAT_COFF;
655 /* A COFF .o file starts with a COFF magic number. */
656 magic = (b2 << 8) | b1;
657 switch (magic)
659 case 0x14c: /* i386 */
660 case 0x166: /* MIPS */
661 case 0x184: /* Alpha */
662 case 0x268: /* 68k */
663 case 0x1f0: /* PowerPC */
664 case 0x290: /* PA */
665 return RES_FORMAT_COFF;
668 /* A RES file starts with 0x0 0x0 0x0 0x0 0x20 0x0 0x0 0x0. */
669 if (b1 == 0 && b2 == 0 && b3 == 0 && b4 == 0 && b5 == 0x20)
670 return RES_FORMAT_RES;
672 /* If every character is printable or space, assume it's an RC file. */
673 if ((isprint (b1) || isspace (b1))
674 && (isprint (b2) || isspace (b2))
675 && (isprint (b3) || isspace (b3))
676 && (isprint (b4) || isspace (b4))
677 && (isprint (b5) || isspace (b5)))
678 return RES_FORMAT_RC;
680 /* Otherwise, we give up. */
681 fatal (_("can not determine type of file `%s'; use the -I option"),
682 filename);
684 /* Return something to silence the compiler warning. */
685 return RES_FORMAT_UNKNOWN;
688 /* Print a usage message and exit. */
690 static void
691 usage (stream, status)
692 FILE *stream;
693 int status;
695 fprintf (stream, _("Usage: %s [options] [input-file] [output-file]\n"),
696 program_name);
697 fprintf (stream, _("\
698 Options:\n\
699 -i FILE, --input FILE Name input file\n\
700 -o FILE, --output FILE Name output file\n\
701 -I FORMAT, --input-format FORMAT\n\
702 Specify input format\n\
703 -O FORMAT, --output-format FORMAT\n\
704 Specify output format\n\
705 -F TARGET, --target TARGET Specify COFF target\n\
706 --preprocessor PROGRAM Program to use to preprocess rc file\n\
707 --include-dir DIR Include directory when preprocessing rc file\n\
708 --define SYM[=VAL] Define SYM when preprocessing rc file\n\
709 --language VAL Set language when reading rc file\n"));
710 #ifdef YYDEBUG
711 fprintf (stream, _("\
712 --yydebug Turn on parser debugging\n"));
713 #endif
714 fprintf (stream, _("\
715 --help Print this help message\n\
716 --version Print version information\n"));
717 fprintf (stream, _("\
718 FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
719 extension if not specified. A single file name is an input file.\n\
720 No input-file is stdin, default rc. No output-file is stdout, default rc.\n"));
721 list_supported_targets (program_name, stream);
722 if (status == 0)
723 fprintf (stream, _("Report bugs to bug-gnu-utils@gnu.org\n"));
724 exit (status);
727 /* The main function. */
730 main (argc, argv)
731 int argc;
732 char **argv;
734 int c;
735 char *input_filename;
736 char *output_filename;
737 enum res_format input_format;
738 enum res_format output_format;
739 char *target;
740 char *preprocessor;
741 char *preprocargs;
742 int language;
743 struct res_directory *resources;
745 #if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
746 setlocale (LC_MESSAGES, "");
747 #endif
748 bindtextdomain (PACKAGE, LOCALEDIR);
749 textdomain (PACKAGE);
751 program_name = argv[0];
752 xmalloc_set_program_name (program_name);
754 bfd_init ();
755 set_default_bfd_target ();
757 res_init ();
759 input_filename = NULL;
760 output_filename = NULL;
761 input_format = RES_FORMAT_UNKNOWN;
762 output_format = RES_FORMAT_UNKNOWN;
763 target = NULL;
764 preprocessor = NULL;
765 preprocargs = NULL;
766 language = -1;
768 while ((c = getopt_long (argc, argv, "i:o:I:O:F:", long_options,
769 (int *) 0)) != EOF)
771 switch (c)
773 case 'i':
774 input_filename = optarg;
775 break;
777 case 'o':
778 output_filename = optarg;
779 break;
781 case 'I':
782 input_format = format_from_name (optarg);
783 break;
785 case 'O':
786 output_format = format_from_name (optarg);
787 break;
789 case 'F':
790 target = optarg;
791 break;
793 case OPTION_PREPROCESSOR:
794 preprocessor = optarg;
795 break;
797 case OPTION_DEFINE:
798 if (preprocargs == NULL)
800 preprocargs = xmalloc (strlen (optarg) + 3);
801 sprintf (preprocargs, "-D%s", optarg);
803 else
805 char *n;
807 n = xmalloc (strlen (preprocargs) + strlen (optarg) + 4);
808 sprintf (n, "%s -D%s", preprocargs, optarg);
809 free (preprocargs);
810 preprocargs = n;
812 break;
814 case OPTION_INCLUDE_DIR:
815 if (preprocargs == NULL)
817 preprocargs = xmalloc (strlen (optarg) + 3);
818 sprintf (preprocargs, "-I%s", optarg);
820 else
822 char *n;
824 n = xmalloc (strlen (preprocargs) + strlen (optarg) + 4);
825 sprintf (n, "%s -I%s", preprocargs, optarg);
826 free (preprocargs);
827 preprocargs = n;
831 struct include_dir *n, **pp;
833 n = (struct include_dir *) xmalloc (sizeof *n);
834 n->next = NULL;
835 n->dir = optarg;
837 for (pp = &include_dirs; *pp != NULL; pp = &(*pp)->next)
839 *pp = n;
842 break;
844 case OPTION_LANGUAGE:
845 language = strtol (optarg, (char **) NULL, 16);
846 break;
848 #ifdef YYDEBUG
849 case OPTION_YYDEBUG:
850 yydebug = 1;
851 break;
852 #endif
854 case OPTION_HELP:
855 usage (stdout, 0);
856 break;
858 case OPTION_VERSION:
859 print_version ("windres");
860 break;
862 default:
863 usage (stderr, 1);
864 break;
868 if (input_filename == NULL && optind < argc)
870 input_filename = argv[optind];
871 ++optind;
874 if (output_filename == NULL && optind < argc)
876 output_filename = argv[optind];
877 ++optind;
880 if (argc != optind)
881 usage (stderr, 1);
883 if (input_format == RES_FORMAT_UNKNOWN)
885 if (input_filename == NULL)
886 input_format = RES_FORMAT_RC;
887 else
888 input_format = format_from_filename (input_filename, 1);
891 if (output_format == RES_FORMAT_UNKNOWN)
893 if (output_filename == NULL)
894 output_format = RES_FORMAT_RC;
895 else
896 output_format = format_from_filename (output_filename, 0);
899 /* Read the input file. */
901 switch (input_format)
903 default:
904 abort ();
905 case RES_FORMAT_RC:
906 resources = read_rc_file (input_filename, preprocessor, preprocargs,
907 language);
908 break;
909 case RES_FORMAT_RES:
910 resources = read_res_file (input_filename);
911 break;
912 case RES_FORMAT_COFF:
913 resources = read_coff_rsrc (input_filename, target);
914 break;
917 if (resources == NULL)
918 fatal (_("no resources"));
920 /* Sort the resources. This is required for COFF, convenient for
921 rc, and unimportant for res. */
923 resources = sort_resources (resources);
925 /* Write the output file. */
927 reswr_init ();
929 switch (output_format)
931 default:
932 abort ();
933 case RES_FORMAT_RC:
934 write_rc_file (output_filename, resources);
935 break;
936 case RES_FORMAT_RES:
937 write_res_file (output_filename, resources);
938 break;
939 case RES_FORMAT_COFF:
940 write_coff_file (output_filename, target, resources);
941 break;
944 xexit (0);
945 return 0;