xref: /netbsd-src/external/gpl3/binutils.old/dist/binutils/windres.c (revision e992f068c547fd6e84b3f104dc2340adcc955732)
1 /* windres.c -- a program to manipulate Windows resources
2    Copyright (C) 1997-2022 Free Software Foundation, Inc.
3    Written by Ian Lance Taylor, Cygnus Support.
4    Rewritten by Kai Tietz, Onevision.
5 
6    This file is part of GNU Binutils.
7 
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 3 of the License, or
11    (at your option) any later version.
12 
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.
17 
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., 51 Franklin Street - Fifth Floor, Boston, MA
21    02110-1301, USA.  */
22 
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
26    program.
27 
28    It is based on information taken from the following sources:
29 
30    * Microsoft documentation.
31 
32    * The rcl program, written by Gunther Ebert
33      <gunther.ebert@ixos-leipzig.de>.
34 
35    * The res2coff program, written by Pedro A. Aranda <paag@tid.es>.  */
36 
37 #include "sysdep.h"
38 #include <assert.h>
39 #include "bfd.h"
40 #include "getopt.h"
41 #include "bucomm.h"
42 #include "libiberty.h"
43 #include "safe-ctype.h"
44 #include "obstack.h"
45 #include "windres.h"
46 
47 /* Used by resrc.c at least.  */
48 
49 int verbose = 0;
50 
51 bool target_is_bigendian = 0;
52 const char *def_target_arch;
53 
54 static void set_endianness (bfd *, const char *);
55 
56 /* An enumeration of format types.  */
57 
58 enum res_format
59 {
60   /* Unknown format.  */
61   RES_FORMAT_UNKNOWN,
62   /* Textual RC file.  */
63   RES_FORMAT_RC,
64   /* Binary RES file.  */
65   RES_FORMAT_RES,
66   /* COFF file.  */
67   RES_FORMAT_COFF
68 };
69 
70 /* A structure used to map between format types and strings.  */
71 
72 struct format_map
73 {
74   const char *name;
75   enum res_format format;
76 };
77 
78 /* A mapping between names and format types.  */
79 
80 static const struct format_map format_names[] =
81 {
82   { "rc", RES_FORMAT_RC },
83   { "res", RES_FORMAT_RES },
84   { "coff", RES_FORMAT_COFF },
85   { NULL, RES_FORMAT_UNKNOWN }
86 };
87 
88 /* A mapping from file extensions to format types.  */
89 
90 static const struct format_map format_fileexts[] =
91 {
92   { "rc", RES_FORMAT_RC },
93   { "res", RES_FORMAT_RES },
94   { "exe", RES_FORMAT_COFF },
95   { "obj", RES_FORMAT_COFF },
96   { "o", RES_FORMAT_COFF },
97   { NULL, RES_FORMAT_UNKNOWN }
98 };
99 
100 /* A list of include directories.  */
101 
102 struct include_dir
103 {
104   struct include_dir *next;
105   char *dir;
106 };
107 
108 static struct include_dir *include_dirs;
109 
110 /* Static functions.  */
111 
112 static void res_init (void);
113 static int extended_menuitems (const rc_menuitem *);
114 static enum res_format format_from_name (const char *, int);
115 static enum res_format format_from_filename (const char *, int);
116 static void usage (FILE *, int);
117 static int cmp_res_entry (const void *, const void *);
118 static rc_res_directory *sort_resources (rc_res_directory *);
119 static void reswr_init (void);
120 static const char * quot (const char *);
121 
122 static rc_uint_type target_get_8 (const void *, rc_uint_type);
123 static void target_put_8 (void *, rc_uint_type);
124 static rc_uint_type target_get_16 (const void *, rc_uint_type);
125 static void target_put_16 (void *, rc_uint_type);
126 static rc_uint_type target_get_32 (const void *, rc_uint_type);
127 static void target_put_32 (void *, rc_uint_type);
128 
129 
130 /* When we are building a resource tree, we allocate everything onto
131    an obstack, so that we can free it all at once if we want.  */
132 
133 #define obstack_chunk_alloc xmalloc
134 #define obstack_chunk_free free
135 
136 /* The resource building obstack.  */
137 
138 static struct obstack res_obstack;
139 
140 /* Initialize the resource building obstack.  */
141 
142 static void
res_init(void)143 res_init (void)
144 {
145   obstack_init (&res_obstack);
146 }
147 
148 /* Allocate space on the resource building obstack.  */
149 
150 void *
res_alloc(rc_uint_type bytes)151 res_alloc (rc_uint_type bytes)
152 {
153   return obstack_alloc (&res_obstack, (size_t) bytes);
154 }
155 
156 /* We also use an obstack to save memory used while writing out a set
157    of resources.  */
158 
159 static struct obstack reswr_obstack;
160 
161 /* Initialize the resource writing obstack.  */
162 
163 static void
reswr_init(void)164 reswr_init (void)
165 {
166   obstack_init (&reswr_obstack);
167 }
168 
169 /* Allocate space on the resource writing obstack.  */
170 
171 void *
reswr_alloc(rc_uint_type bytes)172 reswr_alloc (rc_uint_type bytes)
173 {
174   return obstack_alloc (&reswr_obstack, (size_t) bytes);
175 }
176 
177 /* Open a file using the include directory search list.  */
178 
179 FILE *
open_file_search(const char * filename,const char * mode,const char * errmsg,char ** real_filename)180 open_file_search (const char *filename, const char *mode, const char *errmsg,
181 		  char **real_filename)
182 {
183   FILE *e;
184   struct include_dir *d;
185 
186   e = fopen (filename, mode);
187   if (e != NULL)
188     {
189       *real_filename = xstrdup (filename);
190       return e;
191     }
192 
193   if (errno == ENOENT)
194     {
195       for (d = include_dirs; d != NULL; d = d->next)
196 	{
197 	  char *n;
198 
199 	  n = (char *) xmalloc (strlen (d->dir) + strlen (filename) + 2);
200 	  sprintf (n, "%s/%s", d->dir, filename);
201 	  e = fopen (n, mode);
202 	  if (e != NULL)
203 	    {
204 	      *real_filename = n;
205 	      return e;
206 	    }
207 	  free (n);
208 
209 	  if (errno != ENOENT)
210 	    break;
211 	}
212     }
213 
214   fatal (_("can't open %s `%s': %s"), errmsg, filename, strerror (errno));
215 
216   /* Return a value to avoid a compiler warning.  */
217   return NULL;
218 }
219 
220 /* Compare two resource ID's.  We consider name entries to come before
221    numeric entries, because that is how they appear in the COFF .rsrc
222    section.  */
223 
224 int
res_id_cmp(rc_res_id a,rc_res_id b)225 res_id_cmp (rc_res_id a, rc_res_id b)
226 {
227   if (! a.named)
228     {
229       if (b.named)
230 	return 1;
231       if (a.u.id > b.u.id)
232 	return 1;
233       else if (a.u.id < b.u.id)
234 	return -1;
235       else
236 	return 0;
237     }
238   else
239     {
240       unichar *as, *ase, *bs, *bse;
241 
242       if (! b.named)
243 	return -1;
244 
245       as = a.u.n.name;
246       ase = as + a.u.n.length;
247       bs = b.u.n.name;
248       bse = bs + b.u.n.length;
249 
250       while (as < ase)
251 	{
252 	  int i;
253 
254 	  if (bs >= bse)
255 	    return 1;
256 	  i = (int) *as - (int) *bs;
257 	  if (i != 0)
258 	    return i;
259 	  ++as;
260 	  ++bs;
261 	}
262 
263       if (bs < bse)
264 	return -1;
265 
266       return 0;
267     }
268 }
269 
270 /* Print a resource ID.  */
271 
272 void
res_id_print(FILE * stream,rc_res_id id,int quote)273 res_id_print (FILE *stream, rc_res_id id, int quote)
274 {
275   if (! id.named)
276     fprintf (stream, "%u", (int) id.u.id);
277   else
278     {
279       if (quote)
280 	unicode_print_quoted (stream, id.u.n.name, id.u.n.length);
281       else
282       unicode_print (stream, id.u.n.name, id.u.n.length);
283     }
284 }
285 
286 /* Print a list of resource ID's.  */
287 
288 void
res_ids_print(FILE * stream,int cids,const rc_res_id * ids)289 res_ids_print (FILE *stream, int cids, const rc_res_id *ids)
290 {
291   int i;
292 
293   for (i = 0; i < cids; i++)
294     {
295       res_id_print (stream, ids[i], 1);
296       if (i + 1 < cids)
297 	fprintf (stream, ": ");
298     }
299 }
300 
301 /* Convert an ASCII string to a resource ID.  */
302 
303 void
res_string_to_id(rc_res_id * res_id,const char * string)304 res_string_to_id (rc_res_id *res_id, const char *string)
305 {
306   res_id->named = 1;
307   unicode_from_ascii (&res_id->u.n.length, &res_id->u.n.name, string);
308 }
309 
310 /* Convert an unicode string to a resource ID.  */
311 void
res_unistring_to_id(rc_res_id * res_id,const unichar * u)312 res_unistring_to_id (rc_res_id *res_id, const unichar *u)
313 {
314   res_id->named = 1;
315   res_id->u.n.length = unichar_len (u);
316   res_id->u.n.name = unichar_dup_uppercase (u);
317 }
318 
319 /* Define a resource.  The arguments are the resource tree, RESOURCES,
320    and the location at which to put it in the tree, CIDS and IDS.
321    This returns a newly allocated rc_res_resource structure, which the
322    caller is expected to initialize.  If DUPOK is non-zero, then if a
323    resource with this ID exists, it is returned.  Otherwise, a warning
324    is issued, and a new resource is created replacing the existing
325    one.  */
326 
327 rc_res_resource *
define_resource(rc_res_directory ** resources,int cids,const rc_res_id * ids,int dupok)328 define_resource (rc_res_directory **resources, int cids,
329 		 const rc_res_id *ids, int dupok)
330 {
331   rc_res_entry *re = NULL;
332   int i;
333 
334   assert (cids > 0);
335   for (i = 0; i < cids; i++)
336     {
337       rc_res_entry **pp;
338 
339       if (*resources == NULL)
340 	{
341 	  *resources = ((rc_res_directory *)
342 			res_alloc (sizeof (rc_res_directory)));
343 	  (*resources)->characteristics = 0;
344 	  /* Using a real timestamp only serves to create non-deterministic
345 	     results.  Use zero instead.  */
346 	  (*resources)->time = 0;
347 	  (*resources)->major = 0;
348 	  (*resources)->minor = 0;
349 	  (*resources)->entries = NULL;
350 	}
351 
352       for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
353 	if (res_id_cmp ((*pp)->id, ids[i]) == 0)
354 	  break;
355 
356       if (*pp != NULL)
357 	re = *pp;
358       else
359 	{
360 	  re = (rc_res_entry *) res_alloc (sizeof (rc_res_entry));
361 	  re->next = NULL;
362 	  re->id = ids[i];
363 	  if ((i + 1) < cids)
364 	    {
365 	      re->subdir = 1;
366 	      re->u.dir = NULL;
367 	    }
368 	  else
369 	    {
370 	      re->subdir = 0;
371 	      re->u.res = NULL;
372 	    }
373 
374 	  *pp = re;
375 	}
376 
377       if ((i + 1) < cids)
378 	{
379 	  if (! re->subdir)
380 	    {
381 	      fprintf (stderr, "%s: ", program_name);
382 	      res_ids_print (stderr, i, ids);
383 	      fprintf (stderr, _(": expected to be a directory\n"));
384 	      xexit (1);
385 	    }
386 
387 	  resources = &re->u.dir;
388 	}
389     }
390 
391   if (re->subdir)
392     {
393       fprintf (stderr, "%s: ", program_name);
394       res_ids_print (stderr, cids, ids);
395       fprintf (stderr, _(": expected to be a leaf\n"));
396       xexit (1);
397     }
398 
399   if (re->u.res != NULL)
400     {
401       if (dupok)
402 	return re->u.res;
403 
404       fprintf (stderr, _("%s: warning: "), program_name);
405       res_ids_print (stderr, cids, ids);
406       fprintf (stderr, _(": duplicate value\n"));
407     }
408 
409   re->u.res = ((rc_res_resource *)
410 	       res_alloc (sizeof (rc_res_resource)));
411   memset (re->u.res, 0, sizeof (rc_res_resource));
412 
413   re->u.res->type = RES_TYPE_UNINITIALIZED;
414   return re->u.res;
415 }
416 
417 /* Define a standard resource.  This is a version of define_resource
418    that just takes type, name, and language arguments.  */
419 
420 rc_res_resource *
define_standard_resource(rc_res_directory ** resources,int type,rc_res_id name,rc_uint_type language,int dupok)421 define_standard_resource (rc_res_directory **resources, int type,
422 			  rc_res_id name, rc_uint_type language, int dupok)
423 {
424   rc_res_id a[3];
425 
426   a[0].named = 0;
427   a[0].u.id = type;
428   a[1] = name;
429   a[2].named = 0;
430   a[2].u.id = language;
431   return define_resource (resources, 3, a, dupok);
432 }
433 
434 /* Comparison routine for resource sorting.  */
435 
436 static int
cmp_res_entry(const void * p1,const void * p2)437 cmp_res_entry (const void *p1, const void *p2)
438 {
439   const rc_res_entry **re1, **re2;
440 
441   re1 = (const rc_res_entry **) p1;
442   re2 = (const rc_res_entry **) p2;
443   return res_id_cmp ((*re1)->id, (*re2)->id);
444 }
445 
446 /* Sort the resources.  */
447 
448 static rc_res_directory *
sort_resources(rc_res_directory * resdir)449 sort_resources (rc_res_directory *resdir)
450 {
451   int c, i;
452   rc_res_entry *re;
453   rc_res_entry **a;
454 
455   if (resdir->entries == NULL)
456     return resdir;
457 
458   c = 0;
459   for (re = resdir->entries; re != NULL; re = re->next)
460     ++c;
461 
462   /* This is a recursive routine, so using xmalloc is probably better
463      than alloca.  */
464   a = (rc_res_entry **) xmalloc (c * sizeof (rc_res_entry *));
465 
466   for (i = 0, re = resdir->entries; re != NULL; re = re->next, i++)
467     a[i] = re;
468 
469   qsort (a, c, sizeof (rc_res_entry *), cmp_res_entry);
470 
471   resdir->entries = a[0];
472   for (i = 0; i < c - 1; i++)
473     a[i]->next = a[i + 1];
474   a[i]->next = NULL;
475 
476   free (a);
477 
478   /* Now sort the subdirectories.  */
479 
480   for (re = resdir->entries; re != NULL; re = re->next)
481     if (re->subdir)
482       re->u.dir = sort_resources (re->u.dir);
483 
484   return resdir;
485 }
486 
487 /* Return whether the dialog resource DIALOG is a DIALOG or a
488    DIALOGEX.  */
489 
490 int
extended_dialog(const rc_dialog * dialog)491 extended_dialog (const rc_dialog *dialog)
492 {
493   const rc_dialog_control *c;
494 
495   if (dialog->ex != NULL)
496     return 1;
497 
498   for (c = dialog->controls; c != NULL; c = c->next)
499     if (c->data != NULL || c->help != 0)
500       return 1;
501 
502   return 0;
503 }
504 
505 /* Return whether MENUITEMS are a MENU or a MENUEX.  */
506 
507 int
extended_menu(const rc_menu * menu)508 extended_menu (const rc_menu *menu)
509 {
510   return extended_menuitems (menu->items);
511 }
512 
513 static int
extended_menuitems(const rc_menuitem * menuitems)514 extended_menuitems (const rc_menuitem *menuitems)
515 {
516   const rc_menuitem *mi;
517 
518   for (mi = menuitems; mi != NULL; mi = mi->next)
519     {
520       if (mi->help != 0 || mi->state != 0)
521 	return 1;
522       if (mi->popup != NULL && mi->id != 0)
523 	return 1;
524       if ((mi->type
525 	   & ~ (MENUITEM_CHECKED
526 		| MENUITEM_GRAYED
527 		| MENUITEM_HELP
528 		| MENUITEM_INACTIVE
529 		| MENUITEM_MENUBARBREAK
530 		| MENUITEM_BITMAP
531 		| MENUITEM_OWNERDRAW
532 		| MENUITEM_MENUBREAK))
533 	  != 0)
534 	return 1;
535       if (mi->popup != NULL)
536 	{
537 	  if (extended_menuitems (mi->popup))
538 	    return 1;
539 	}
540     }
541 
542   return 0;
543 }
544 
545 /* Convert a string to a format type, or exit if it can't be done.  */
546 
547 static enum res_format
format_from_name(const char * name,int exit_on_error)548 format_from_name (const char *name, int exit_on_error)
549 {
550   const struct format_map *m;
551 
552   for (m = format_names; m->name != NULL; m++)
553     if (strcasecmp (m->name, name) == 0)
554       break;
555 
556   if (m->name == NULL && exit_on_error)
557     {
558       non_fatal (_("unknown format type `%s'"), name);
559       fprintf (stderr, _("%s: supported formats:"), program_name);
560       for (m = format_names; m->name != NULL; m++)
561 	fprintf (stderr, " %s", m->name);
562       fprintf (stderr, "\n");
563       xexit (1);
564     }
565 
566   return m->format;
567 }
568 
569 /* Work out a format type given a file name.  If INPUT is non-zero,
570    it's OK to look at the file itself.  */
571 
572 static enum res_format
format_from_filename(const char * filename,int input)573 format_from_filename (const char *filename, int input)
574 {
575   const char *ext;
576   FILE *e;
577   bfd_byte b1, b2, b3, b4, b5;
578   int magic;
579 
580   /* If we have an extension, see if we recognize it as implying a
581      particular format.  */
582   ext = strrchr (filename, '.');
583   if (ext != NULL)
584     {
585       const struct format_map *m;
586 
587       ++ext;
588       for (m = format_fileexts; m->name != NULL; m++)
589 	if (strcasecmp (m->name, ext) == 0)
590 	  return m->format;
591     }
592 
593   /* If we don't recognize the name of an output file, assume it's a
594      COFF file.  */
595   if (! input)
596     return RES_FORMAT_COFF;
597 
598   /* Read the first few bytes of the file to see if we can guess what
599      it is.  */
600   e = fopen (filename, FOPEN_RB);
601   if (e == NULL)
602     fatal ("%s: %s", filename, strerror (errno));
603 
604   b1 = getc (e);
605   b2 = getc (e);
606   b3 = getc (e);
607   b4 = getc (e);
608   b5 = getc (e);
609 
610   fclose (e);
611 
612   /* A PE executable starts with 0x4d 0x5a.  */
613   if (b1 == 0x4d && b2 == 0x5a)
614     return RES_FORMAT_COFF;
615 
616   /* A COFF .o file starts with a COFF magic number.  */
617   magic = (b2 << 8) | b1;
618   switch (magic)
619     {
620     case 0x14c: /* i386 */
621     case 0x166: /* MIPS */
622     case 0x184: /* Alpha */
623     case 0x268: /* 68k */
624     case 0x1f0: /* PowerPC */
625     case 0x290: /* PA */
626       return RES_FORMAT_COFF;
627     }
628 
629   /* A RES file starts with 0x0 0x0 0x0 0x0 0x20 0x0 0x0 0x0.  */
630   if (b1 == 0 && b2 == 0 && b3 == 0 && b4 == 0 && b5 == 0x20)
631     return RES_FORMAT_RES;
632 
633   /* If every character is printable or space, assume it's an RC file.  */
634   if ((ISPRINT (b1) || ISSPACE (b1))
635       && (ISPRINT (b2) || ISSPACE (b2))
636       && (ISPRINT (b3) || ISSPACE (b3))
637       && (ISPRINT (b4) || ISSPACE (b4))
638       && (ISPRINT (b5) || ISSPACE (b5)))
639     return RES_FORMAT_RC;
640 
641   /* Otherwise, we give up.  */
642   fatal (_("can not determine type of file `%s'; use the -J option"),
643 	 filename);
644 
645   /* Return something to silence the compiler warning.  */
646   return RES_FORMAT_UNKNOWN;
647 }
648 
649 /* Print a usage message and exit.  */
650 
651 static void
usage(FILE * stream,int status)652 usage (FILE *stream, int status)
653 {
654   fprintf (stream, _("Usage: %s [option(s)] [input-file] [output-file]\n"),
655 	   program_name);
656   fprintf (stream, _(" The options are:\n\
657   -i --input=<file>            Name input file\n\
658   -o --output=<file>           Name output file\n\
659   -J --input-format=<format>   Specify input format\n\
660   -O --output-format=<format>  Specify output format\n\
661   -F --target=<target>         Specify COFF target\n\
662      --preprocessor=<program>  Program to use to preprocess rc file\n\
663      --preprocessor-arg=<arg>  Additional preprocessor argument\n\
664   -I --include-dir=<dir>       Include directory when preprocessing rc file\n\
665   -D --define <sym>[=<val>]    Define SYM when preprocessing rc file\n\
666   -U --undefine <sym>          Undefine SYM when preprocessing rc file\n\
667   -v --verbose                 Verbose - tells you what it's doing\n\
668   -c --codepage=<codepage>     Specify default codepage\n\
669   -l --language=<val>          Set language when reading rc file\n\
670      --use-temp-file           Use a temporary file instead of popen to read\n\
671                                the preprocessor output\n\
672      --no-use-temp-file        Use popen (default)\n"));
673 #ifdef YYDEBUG
674   fprintf (stream, _("\
675      --yydebug                 Turn on parser debugging\n"));
676 #endif
677   fprintf (stream, _("\
678   -r                           Ignored for compatibility with rc\n\
679   @<file>                      Read options from <file>\n\
680   -h --help                    Print this help message\n\
681   -V --version                 Print version information\n"));
682   fprintf (stream, _("\
683 FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
684 extension if not specified.  A single file name is an input file.\n\
685 No input-file is stdin, default rc.  No output-file is stdout, default rc.\n"));
686 
687   list_supported_targets (program_name, stream);
688 
689   if (REPORT_BUGS_TO[0] && status == 0)
690     fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO);
691 
692   exit (status);
693 }
694 
695 /* Quote characters that will confuse the shell when we run the preprocessor.  */
696 
697 static const char *
quot(const char * string)698 quot (const char *string)
699 {
700   static char *buf = 0;
701   static int buflen = 0;
702   int slen = strlen (string);
703   const char *src;
704   char *dest;
705 
706   if ((buflen < slen * 2 + 3) || ! buf)
707     {
708       buflen = slen * 2 + 3;
709       free (buf);
710       buf = (char *) xmalloc (buflen);
711     }
712 
713   for (src = string, dest = buf; *src; src++, dest++)
714     {
715       if (*src == '(' || *src == ')' || *src == ' ')
716 	*dest++ = '\\';
717       *dest = *src;
718     }
719 
720   *dest = 0;
721   return buf;
722 }
723 
724 /* Long options.  */
725 
726 enum option_values
727 {
728   /* 150 isn't special; it's just an arbitrary non-ASCII char value.  */
729   OPTION_PREPROCESSOR	= 150,
730   OPTION_USE_TEMP_FILE,
731   OPTION_NO_USE_TEMP_FILE,
732   OPTION_YYDEBUG,
733   OPTION_INCLUDE_DIR,
734   OPTION_PREPROCESSOR_ARG
735 };
736 
737 static const struct option long_options[] =
738 {
739   {"input", required_argument, 0, 'i'},
740   {"output", required_argument, 0, 'o'},
741   {"input-format", required_argument, 0, 'J'},
742   {"output-format", required_argument, 0, 'O'},
743   {"target", required_argument, 0, 'F'},
744   {"preprocessor", required_argument, 0, OPTION_PREPROCESSOR},
745   {"preprocessor-arg", required_argument, 0, OPTION_PREPROCESSOR_ARG},
746   {"include-dir", required_argument, 0, OPTION_INCLUDE_DIR},
747   {"define", required_argument, 0, 'D'},
748   {"undefine", required_argument, 0, 'U'},
749   {"verbose", no_argument, 0, 'v'},
750   {"codepage", required_argument, 0, 'c'},
751   {"language", required_argument, 0, 'l'},
752   {"use-temp-file", no_argument, 0, OPTION_USE_TEMP_FILE},
753   {"no-use-temp-file", no_argument, 0, OPTION_NO_USE_TEMP_FILE},
754   {"yydebug", no_argument, 0, OPTION_YYDEBUG},
755   {"version", no_argument, 0, 'V'},
756   {"help", no_argument, 0, 'h'},
757   {0, no_argument, 0, 0}
758 };
759 
760 void
windres_add_include_dir(const char * p)761 windres_add_include_dir (const char *p)
762 {
763   struct include_dir *n, **pp;
764 
765   /* Computing paths is often complicated and error prone.
766      The easiest way to check for mistakes is at the time
767      we add them to include_dirs.  */
768   assert (p != NULL);
769   assert (*p != '\0');
770 
771   n = xmalloc (sizeof *n);
772   n->next = NULL;
773   n->dir = (char * ) p;
774 
775   for (pp = &include_dirs; *pp != NULL; pp = &(*pp)->next)
776     ;
777   *pp = n;
778 }
779 
780 /* This keeps gcc happy when using -Wmissing-prototypes -Wstrict-prototypes.  */
781 int main (int, char **);
782 
783 /* The main function.  */
784 
785 int
main(int argc,char ** argv)786 main (int argc, char **argv)
787 {
788   int c;
789   char *input_filename;
790   char *output_filename;
791   enum res_format input_format;
792   enum res_format input_format_tmp;
793   enum res_format output_format;
794   char *target;
795   char *preprocessor;
796   char *preprocargs;
797   const char *quotedarg;
798   int language;
799   rc_res_directory *resources;
800   int use_temp_file;
801 
802 #ifdef HAVE_LC_MESSAGES
803   setlocale (LC_MESSAGES, "");
804 #endif
805   setlocale (LC_CTYPE, "");
806   bindtextdomain (PACKAGE, LOCALEDIR);
807   textdomain (PACKAGE);
808 
809   program_name = argv[0];
810   xmalloc_set_program_name (program_name);
811   bfd_set_error_program_name (program_name);
812 
813   expandargv (&argc, &argv);
814 
815   if (bfd_init () != BFD_INIT_MAGIC)
816     fatal (_("fatal error: libbfd ABI mismatch"));
817   set_default_bfd_target ();
818 
819   res_init ();
820 
821   input_filename = NULL;
822   output_filename = NULL;
823   input_format = RES_FORMAT_UNKNOWN;
824   output_format = RES_FORMAT_UNKNOWN;
825   target = NULL;
826   preprocessor = NULL;
827   preprocargs = NULL;
828   language = 0x409;   /* LANG_ENGLISH, SUBLANG_ENGLISH_US.  */
829   use_temp_file = 0;
830 
831   while ((c = getopt_long (argc, argv, "c:f:i:l:o:I:J:O:F:D:U:rhHvV", long_options,
832 			   (int *) 0)) != EOF)
833     {
834       switch (c)
835 	{
836 	case 'c':
837 	  {
838 	    rc_uint_type ncp;
839 
840 	    if (optarg[0] == '0' && (optarg[1] == 'x' || optarg[1] == 'X'))
841 	      ncp = (rc_uint_type) strtol (optarg + 2, NULL, 16);
842 	    else
843 	      ncp = (rc_uint_type) strtol (optarg, NULL, 10);
844 	    if (ncp == CP_UTF16 || ! unicode_is_valid_codepage (ncp))
845 	      fatal (_("invalid codepage specified.\n"));
846 	    wind_default_codepage = wind_current_codepage = ncp;
847 	  }
848 	  break;
849 
850 	case 'i':
851 	  input_filename = optarg;
852 	  break;
853 
854 	case 'f':
855 	  /* For compatibility with rc we accept "-fo <name>" as being the
856 	     equivalent of "-o <name>".  We do not advertise this fact
857 	     though, as we do not want users to use non-GNU like command
858 	     line switches.  */
859 	  if (*optarg != 'o')
860 	    fatal (_("invalid option -f\n"));
861 	  optarg++;
862 	  if (* optarg == 0)
863 	    {
864 	      if (optind == argc)
865 		fatal (_("No filename following the -fo option.\n"));
866 	      optarg = argv [optind++];
867 	    }
868 	  /* Fall through.  */
869 
870 	case 'o':
871 	  output_filename = optarg;
872 	  break;
873 
874 	case 'J':
875 	  input_format = format_from_name (optarg, 1);
876 	  break;
877 
878 	case 'O':
879 	  output_format = format_from_name (optarg, 1);
880 	  break;
881 
882 	case 'F':
883 	  target = optarg;
884 	  break;
885 
886 	case OPTION_PREPROCESSOR:
887 	  if (strchr (optarg, ' '))
888 	    {
889 	      if (asprintf (& preprocessor, "\"%s\"", optarg) == -1)
890 		preprocessor = optarg;
891 	    }
892 	  else
893 	    preprocessor = optarg;
894 	  break;
895 
896 	case OPTION_PREPROCESSOR_ARG:
897 	  if (preprocargs == NULL)
898 	    {
899 	      quotedarg = quot (optarg);
900 	      preprocargs = xstrdup (quotedarg);
901 	    }
902 	  else
903 	    {
904 	      char *n;
905 
906 	      quotedarg = quot (optarg);
907 	      n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 2);
908 	      sprintf (n, "%s %s", preprocargs, quotedarg);
909 	      free (preprocargs);
910 	      preprocargs = n;
911 	    }
912 	  break;
913 
914 	case 'D':
915 	case 'U':
916 	  if (preprocargs == NULL)
917 	    {
918 	      quotedarg = quot (optarg);
919 	      preprocargs = xmalloc (strlen (quotedarg) + 3);
920 	      sprintf (preprocargs, "-%c%s", c, quotedarg);
921 	    }
922 	  else
923 	    {
924 	      char *n;
925 
926 	      quotedarg = quot (optarg);
927 	      n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
928 	      sprintf (n, "%s -%c%s", preprocargs, c, quotedarg);
929 	      free (preprocargs);
930 	      preprocargs = n;
931 	    }
932 	  break;
933 
934 	case 'r':
935 	  /* Ignored for compatibility with rc.  */
936 	  break;
937 
938 	case 'v':
939 	  verbose ++;
940 	  break;
941 
942 	case 'I':
943 	  /* For backward compatibility, should be removed in the future.  */
944 	  input_format_tmp = format_from_name (optarg, 0);
945 	  if (input_format_tmp != RES_FORMAT_UNKNOWN)
946 	    {
947 	      struct stat statbuf;
948 	      char modebuf[11];
949 
950 	      if (stat (optarg, & statbuf) == 0
951 		  /* Coded this way to avoid importing knowledge of S_ISDIR into this file.  */
952 		  && (mode_string (statbuf.st_mode, modebuf), modebuf[0] == 'd'))
953 		/* We have a -I option with a directory name that just happens
954 		   to match a format name as well.  eg: -I res  Assume that the
955 		   user knows what they are doing and do not complain.  */
956 		;
957 	      else
958 		{
959 		  fprintf (stderr,
960 			   _("Option -I is deprecated for setting the input format, please use -J instead.\n"));
961 		  input_format = input_format_tmp;
962 		  break;
963 		}
964 	    }
965 	  /* Fall through.  */
966 
967 	case OPTION_INCLUDE_DIR:
968 	  if (preprocargs == NULL)
969 	    {
970 	      quotedarg = quot (optarg);
971 	      preprocargs = xmalloc (strlen (quotedarg) + 3);
972 	      sprintf (preprocargs, "-I%s", quotedarg);
973 	    }
974 	  else
975 	    {
976 	      char *n;
977 
978 	      quotedarg = quot (optarg);
979 	      n = xmalloc (strlen (preprocargs) + strlen (quotedarg) + 4);
980 	      sprintf (n, "%s -I%s", preprocargs, quotedarg);
981 	      free (preprocargs);
982 	      preprocargs = n;
983 	    }
984 
985 	  windres_add_include_dir (optarg);
986 
987 	  break;
988 
989 	case 'l':
990 	  language = strtol (optarg, (char **) NULL, 16);
991 	  break;
992 
993 	case OPTION_USE_TEMP_FILE:
994 	  use_temp_file = 1;
995 	  break;
996 
997 	case OPTION_NO_USE_TEMP_FILE:
998 	  use_temp_file = 0;
999 	  break;
1000 
1001 #ifdef YYDEBUG
1002 	case OPTION_YYDEBUG:
1003 	  yydebug = 1;
1004 	  break;
1005 #endif
1006 
1007 	case 'h':
1008 	case 'H':
1009 	  usage (stdout, 0);
1010 	  break;
1011 
1012 	case 'V':
1013 	  print_version ("windres");
1014 	  break;
1015 
1016 	default:
1017 	  usage (stderr, 1);
1018 	  break;
1019 	}
1020     }
1021 
1022   if (input_filename == NULL && optind < argc)
1023     {
1024       input_filename = argv[optind];
1025       ++optind;
1026     }
1027 
1028   if (output_filename == NULL && optind < argc)
1029     {
1030       output_filename = argv[optind];
1031       ++optind;
1032     }
1033 
1034   if (argc != optind)
1035     usage (stderr, 1);
1036 
1037   if (input_format == RES_FORMAT_UNKNOWN)
1038     {
1039       if (input_filename == NULL)
1040 	input_format = RES_FORMAT_RC;
1041       else
1042 	input_format = format_from_filename (input_filename, 1);
1043     }
1044 
1045   if (output_format == RES_FORMAT_UNKNOWN)
1046     {
1047       if (output_filename == NULL)
1048 	output_format = RES_FORMAT_RC;
1049       else
1050 	output_format = format_from_filename (output_filename, 0);
1051     }
1052 
1053   set_endianness (NULL, target);
1054 
1055   /* Read the input file.  */
1056   switch (input_format)
1057     {
1058     default:
1059       abort ();
1060     case RES_FORMAT_RC:
1061       resources = read_rc_file (input_filename, preprocessor, preprocargs,
1062 				language, use_temp_file);
1063       break;
1064     case RES_FORMAT_RES:
1065       resources = read_res_file (input_filename);
1066       break;
1067     case RES_FORMAT_COFF:
1068       resources = read_coff_rsrc (input_filename, target);
1069       break;
1070     }
1071 
1072   if (resources == NULL)
1073     fatal (_("no resources"));
1074 
1075   /* Sort the resources.  This is required for COFF, convenient for
1076      rc, and unimportant for res.  */
1077   resources = sort_resources (resources);
1078 
1079   /* Write the output file.  */
1080   reswr_init ();
1081 
1082   switch (output_format)
1083     {
1084     default:
1085       abort ();
1086     case RES_FORMAT_RC:
1087       write_rc_file (output_filename, resources);
1088       break;
1089     case RES_FORMAT_RES:
1090       write_res_file (output_filename, resources);
1091       break;
1092     case RES_FORMAT_COFF:
1093       write_coff_file (output_filename, target, resources);
1094       break;
1095     }
1096 
1097   xexit (0);
1098   return 0;
1099 }
1100 
1101 static void
set_endianness(bfd * abfd,const char * target)1102 set_endianness (bfd *abfd, const char *target)
1103 {
1104   const bfd_target *target_vec;
1105 
1106   def_target_arch = NULL;
1107   target_vec = bfd_get_target_info (target, abfd, &target_is_bigendian, NULL,
1108                                    &def_target_arch);
1109   if (! target_vec)
1110     fatal ("Can't detect target endianness and architecture.");
1111   if (! def_target_arch)
1112     fatal ("Can't detect architecture.");
1113 }
1114 
1115 bfd *
windres_open_as_binary(const char * filename,int rdmode)1116 windres_open_as_binary (const char *filename, int rdmode)
1117 {
1118   bfd *abfd;
1119 
1120   abfd = (rdmode ? bfd_openr (filename, "binary") : bfd_openw (filename, "binary"));
1121   if (! abfd)
1122     fatal ("can't open `%s' for %s", filename, (rdmode ? "input" : "output"));
1123 
1124   if (rdmode && ! bfd_check_format (abfd, bfd_object))
1125     fatal ("can't open `%s' for input.", filename);
1126 
1127   return abfd;
1128 }
1129 
1130 void
set_windres_bfd_endianness(windres_bfd * wrbfd,int is_bigendian)1131 set_windres_bfd_endianness (windres_bfd *wrbfd, int is_bigendian)
1132 {
1133   assert (!! wrbfd);
1134   switch (WR_KIND(wrbfd))
1135   {
1136   case WR_KIND_BFD_BIN_L:
1137     if (is_bigendian)
1138       WR_KIND(wrbfd) = WR_KIND_BFD_BIN_B;
1139     break;
1140   case WR_KIND_BFD_BIN_B:
1141     if (! is_bigendian)
1142       WR_KIND(wrbfd) = WR_KIND_BFD_BIN_L;
1143     break;
1144   default:
1145     /* only binary bfd can be overriden. */
1146     abort ();
1147   }
1148 }
1149 
1150 void
set_windres_bfd(windres_bfd * wrbfd,bfd * abfd,asection * sec,rc_uint_type kind)1151 set_windres_bfd (windres_bfd *wrbfd, bfd *abfd, asection *sec, rc_uint_type kind)
1152 {
1153   assert (!! wrbfd);
1154   switch (kind)
1155   {
1156   case WR_KIND_TARGET:
1157     abfd = NULL;
1158     sec = NULL;
1159     break;
1160   case WR_KIND_BFD:
1161   case WR_KIND_BFD_BIN_L:
1162   case WR_KIND_BFD_BIN_B:
1163     assert (!! abfd);
1164     assert (!!sec);
1165     break;
1166   default:
1167     abort ();
1168   }
1169   WR_KIND(wrbfd) = kind;
1170   WR_BFD(wrbfd) = abfd;
1171   WR_SECTION(wrbfd) = sec;
1172 }
1173 
1174 void
set_windres_bfd_content(windres_bfd * wrbfd,const void * data,rc_uint_type off,rc_uint_type length)1175 set_windres_bfd_content (windres_bfd *wrbfd, const void *data, rc_uint_type off,
1176 			 rc_uint_type length)
1177 {
1178   if (WR_KIND(wrbfd) != WR_KIND_TARGET)
1179     {
1180       if (! bfd_set_section_contents (WR_BFD(wrbfd), WR_SECTION(wrbfd), data, off, length))
1181 	bfd_fatal ("bfd_set_section_contents");
1182     }
1183   else
1184     abort ();
1185 }
1186 
1187 void
get_windres_bfd_content(windres_bfd * wrbfd,void * data,rc_uint_type off,rc_uint_type length)1188 get_windres_bfd_content (windres_bfd *wrbfd, void *data, rc_uint_type off,
1189 			 rc_uint_type length)
1190 {
1191   if (WR_KIND(wrbfd) != WR_KIND_TARGET)
1192     {
1193       if (! bfd_get_section_contents (WR_BFD(wrbfd), WR_SECTION(wrbfd), data, off, length))
1194 	bfd_fatal ("bfd_get_section_contents");
1195     }
1196   else
1197     abort ();
1198 }
1199 
1200 void
windres_put_8(windres_bfd * wrbfd,void * p,rc_uint_type value)1201 windres_put_8 (windres_bfd *wrbfd, void *p, rc_uint_type value)
1202 {
1203   switch (WR_KIND(wrbfd))
1204     {
1205     case WR_KIND_TARGET:
1206       target_put_8 (p, value);
1207       break;
1208     case WR_KIND_BFD:
1209     case WR_KIND_BFD_BIN_L:
1210     case WR_KIND_BFD_BIN_B:
1211       bfd_put_8 (WR_BFD(wrbfd), value, p);
1212       break;
1213     default:
1214       abort ();
1215     }
1216 }
1217 
1218 void
windres_put_16(windres_bfd * wrbfd,void * data,rc_uint_type value)1219 windres_put_16 (windres_bfd *wrbfd, void *data, rc_uint_type value)
1220 {
1221   switch (WR_KIND(wrbfd))
1222     {
1223     case WR_KIND_TARGET:
1224       target_put_16 (data, value);
1225       break;
1226     case WR_KIND_BFD:
1227     case WR_KIND_BFD_BIN_B:
1228       bfd_put_16 (WR_BFD(wrbfd), value, data);
1229       break;
1230     case WR_KIND_BFD_BIN_L:
1231       bfd_putl16 (value, data);
1232       break;
1233     default:
1234       abort ();
1235     }
1236 }
1237 
1238 void
windres_put_32(windres_bfd * wrbfd,void * data,rc_uint_type value)1239 windres_put_32 (windres_bfd *wrbfd, void *data, rc_uint_type value)
1240 {
1241   switch (WR_KIND(wrbfd))
1242     {
1243     case WR_KIND_TARGET:
1244       target_put_32 (data, value);
1245       break;
1246     case WR_KIND_BFD:
1247     case WR_KIND_BFD_BIN_B:
1248       bfd_put_32 (WR_BFD(wrbfd), value, data);
1249       break;
1250     case WR_KIND_BFD_BIN_L:
1251       bfd_putl32 (value, data);
1252       break;
1253     default:
1254       abort ();
1255     }
1256 }
1257 
1258 rc_uint_type
windres_get_8(windres_bfd * wrbfd,const void * data,rc_uint_type length)1259 windres_get_8 (windres_bfd *wrbfd, const void *data, rc_uint_type length)
1260 {
1261   if (length < 1)
1262     fatal ("windres_get_8: unexpected eob.");
1263   switch (WR_KIND(wrbfd))
1264     {
1265     case WR_KIND_TARGET:
1266       return target_get_8 (data, length);
1267     case WR_KIND_BFD:
1268     case WR_KIND_BFD_BIN_B:
1269     case WR_KIND_BFD_BIN_L:
1270       return bfd_get_8 (WR_BFD(wrbfd), data);
1271     default:
1272       abort ();
1273     }
1274   return 0;
1275 }
1276 
1277 rc_uint_type
windres_get_16(windres_bfd * wrbfd,const void * data,rc_uint_type length)1278 windres_get_16 (windres_bfd *wrbfd, const void *data, rc_uint_type length)
1279 {
1280   if (length < 2)
1281     fatal ("windres_get_16: unexpected eob.");
1282   switch (WR_KIND(wrbfd))
1283     {
1284     case WR_KIND_TARGET:
1285       return target_get_16 (data, length);
1286     case WR_KIND_BFD:
1287     case WR_KIND_BFD_BIN_B:
1288       return bfd_get_16 (WR_BFD(wrbfd), data);
1289     case WR_KIND_BFD_BIN_L:
1290       return bfd_getl16 (data);
1291     default:
1292       abort ();
1293     }
1294   return 0;
1295 }
1296 
1297 rc_uint_type
windres_get_32(windres_bfd * wrbfd,const void * data,rc_uint_type length)1298 windres_get_32 (windres_bfd *wrbfd, const void *data, rc_uint_type length)
1299 {
1300   if (length < 4)
1301     fatal ("windres_get_32: unexpected eob.");
1302   switch (WR_KIND(wrbfd))
1303     {
1304     case WR_KIND_TARGET:
1305       return target_get_32 (data, length);
1306     case WR_KIND_BFD:
1307     case WR_KIND_BFD_BIN_B:
1308       return bfd_get_32 (WR_BFD(wrbfd), data);
1309     case WR_KIND_BFD_BIN_L:
1310       return bfd_getl32 (data);
1311     default:
1312       abort ();
1313     }
1314   return 0;
1315 }
1316 
1317 static rc_uint_type
target_get_8(const void * p,rc_uint_type length)1318 target_get_8 (const void *p, rc_uint_type length)
1319 {
1320   rc_uint_type ret;
1321 
1322   if (length < 1)
1323     fatal ("Resource too small for getting 8-bit value.");
1324 
1325   ret = (rc_uint_type) *((const bfd_byte *) p);
1326   return ret & 0xff;
1327 }
1328 
1329 static rc_uint_type
target_get_16(const void * p,rc_uint_type length)1330 target_get_16 (const void *p, rc_uint_type length)
1331 {
1332   if (length < 2)
1333     fatal ("Resource too small for getting 16-bit value.");
1334 
1335   if (target_is_bigendian)
1336     return bfd_getb16 (p);
1337   else
1338     return bfd_getl16 (p);
1339 }
1340 
1341 static rc_uint_type
target_get_32(const void * p,rc_uint_type length)1342 target_get_32 (const void *p, rc_uint_type length)
1343 {
1344   if (length < 4)
1345     fatal ("Resource too small for getting 32-bit value.");
1346 
1347   if (target_is_bigendian)
1348     return bfd_getb32 (p);
1349   else
1350     return bfd_getl32 (p);
1351 }
1352 
1353 static void
target_put_8(void * p,rc_uint_type value)1354 target_put_8 (void *p, rc_uint_type value)
1355 {
1356   assert (!! p);
1357   *((bfd_byte *) p)=(bfd_byte) value;
1358 }
1359 
1360 static void
target_put_16(void * p,rc_uint_type value)1361 target_put_16 (void *p, rc_uint_type value)
1362 {
1363   assert (!! p);
1364 
1365   if (target_is_bigendian)
1366     bfd_putb16 (value, p);
1367   else
1368     bfd_putl16 (value, p);
1369 }
1370 
1371 static void
target_put_32(void * p,rc_uint_type value)1372 target_put_32 (void *p, rc_uint_type value)
1373 {
1374   assert (!! p);
1375 
1376   if (target_is_bigendian)
1377     bfd_putb32 (value, p);
1378   else
1379     bfd_putl32 (value, p);
1380 }
1381 
1382 static int isInComment = 0;
1383 
wr_printcomment(FILE * e,const char * fmt,...)1384 int wr_printcomment (FILE *e, const char *fmt, ...)
1385 {
1386   va_list arg;
1387   int r = 0;
1388 
1389   if (isInComment)
1390     r += fprintf (e, "\n   ");
1391   else
1392     fprintf (e, "/* ");
1393   isInComment = 1;
1394   if (fmt == NULL)
1395     return r;
1396   va_start (arg, fmt);
1397   r += vfprintf (e, fmt, arg);
1398   va_end (arg);
1399   return r;
1400 }
1401 
wr_print(FILE * e,const char * fmt,...)1402 int wr_print (FILE *e, const char *fmt, ...)
1403 {
1404   va_list arg;
1405   int r = 0;
1406   if (isInComment)
1407     r += fprintf (e, ".  */\n");
1408   isInComment = 0;
1409   if (! fmt)
1410     return r;
1411   va_start (arg, fmt);
1412   r += vfprintf (e, fmt, arg);
1413   va_end (arg);
1414   return r;
1415 }
1416