* resbin.c: New file.
[deliverable/binutils-gdb.git] / binutils / windres.c
1 /* windres.c -- a program to manipulate Windows resources
2 Copyright 1997 Free Software Foundation, Inc.
3 Written by Ian Lance Taylor, Cygnus Support.
4
5 This file is part of GNU Binutils.
6
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.
11
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.
16
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. */
21
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.
26
27 It is based on information taken from the following sources:
28
29 * Microsoft documentation.
30
31 * The rcl program, written by Gunther Ebert
32 <gunther.ebert@ixos-leipzig.de>.
33
34 * The res2coff program, written by Pedro A. Aranda <paag@tid.es>.
35
36 */
37
38 #include "bfd.h"
39 #include "getopt.h"
40 #include "bucomm.h"
41 #include "libiberty.h"
42 #include "obstack.h"
43 #include "windres.h"
44
45 #include <assert.h>
46 #include <ctype.h>
47
48 /* An enumeration of format types. */
49
50 enum res_format
51 {
52 /* Unknown format. */
53 RES_FORMAT_UNKNOWN,
54 /* Textual RC file. */
55 RES_FORMAT_RC,
56 /* Binary RES file. */
57 RES_FORMAT_RES,
58 /* COFF file. */
59 RES_FORMAT_COFF
60 };
61
62 /* A structure used to map between format types and strings. */
63
64 struct format_map
65 {
66 const char *name;
67 enum res_format format;
68 };
69
70 /* A mapping between names and format types. */
71
72 static const struct format_map format_names[] =
73 {
74 { "rc", RES_FORMAT_RC },
75 { "res", RES_FORMAT_RES },
76 { "coff", RES_FORMAT_COFF },
77 { NULL, RES_FORMAT_UNKNOWN }
78 };
79
80 /* A mapping from file extensions to format types. */
81
82 static const struct format_map format_fileexts[] =
83 {
84 { "rc", RES_FORMAT_RC },
85 { "res", RES_FORMAT_RES },
86 { "exe", RES_FORMAT_COFF },
87 { "obj", RES_FORMAT_COFF },
88 { "o", RES_FORMAT_COFF },
89 { NULL, RES_FORMAT_UNKNOWN }
90 };
91
92 /* A list of include directories. */
93
94 struct include_dir
95 {
96 struct include_dir *next;
97 char *dir;
98 };
99
100 static struct include_dir *include_dirs;
101
102 /* Long options. */
103
104 /* 150 isn't special; it's just an arbitrary non-ASCII char value. */
105
106 #define OPTION_DEFINE 150
107 #define OPTION_HELP (OPTION_DEFINE + 1)
108 #define OPTION_INCLUDE_DIR (OPTION_HELP + 1)
109 #define OPTION_LANGUAGE (OPTION_INCLUDE_DIR + 1)
110 #define OPTION_PREPROCESSOR (OPTION_LANGUAGE + 1)
111 #define OPTION_VERSION (OPTION_PREPROCESSOR + 1)
112 #define OPTION_YYDEBUG (OPTION_VERSION + 1)
113
114 static const struct option long_options[] =
115 {
116 {"define", required_argument, 0, OPTION_DEFINE},
117 {"help", no_argument, 0, OPTION_HELP},
118 {"include-dir", required_argument, 0, OPTION_INCLUDE_DIR},
119 {"input-format", required_argument, 0, 'I'},
120 {"language", required_argument, 0, OPTION_LANGUAGE},
121 {"output-format", required_argument, 0, 'O'},
122 {"preprocessor", required_argument, 0, OPTION_PREPROCESSOR},
123 {"target", required_argument, 0, 'F'},
124 {"version", no_argument, 0, OPTION_VERSION},
125 {"yydebug", no_argument, 0, OPTION_YYDEBUG},
126 {0, no_argument, 0, 0}
127 };
128
129 /* Static functions. */
130
131 static void res_init PARAMS ((void));
132 static int extended_menuitems PARAMS ((const struct menuitem *));
133 static enum res_format format_from_name PARAMS ((const char *));
134 static enum res_format format_from_filename PARAMS ((const char *, int));
135 static void usage PARAMS ((FILE *, int));
136 static int cmp_res_entry PARAMS ((const PTR, const PTR));
137 static struct res_directory *sort_resources PARAMS ((struct res_directory *));
138 \f
139 /* When we are building a resource tree, we allocate everything onto
140 an obstack, so that we can free it all at once if we want. */
141
142 #define obstack_chunk_alloc xmalloc
143 #define obstack_chunk_free free
144
145 /* The resource building obstack. */
146
147 static struct obstack res_obstack;
148
149 /* Initialize the resource building obstack. */
150
151 static void
152 res_init ()
153 {
154 obstack_init (&res_obstack);
155 }
156
157 /* Allocate space on the resource building obstack. */
158
159 PTR
160 res_alloc (bytes)
161 size_t bytes;
162 {
163 return (PTR) obstack_alloc (&res_obstack, bytes);
164 }
165
166 /* We also use an obstack to save memory used while writing out a set
167 of resources. */
168
169 static struct obstack reswr_obstack;
170
171 /* Initialize the resource writing obstack. */
172
173 static void
174 reswr_init ()
175 {
176 obstack_init (&reswr_obstack);
177 }
178
179 /* Allocate space on the resource writing obstack. */
180
181 PTR
182 reswr_alloc (bytes)
183 size_t bytes;
184 {
185 return (PTR) obstack_alloc (&reswr_obstack, bytes);
186 }
187 \f
188 /* Open a file using the include directory search list. */
189
190 FILE *
191 open_file_search (filename, mode, errmsg, real_filename)
192 const char *filename;
193 const char *mode;
194 const char *errmsg;
195 char **real_filename;
196 {
197 FILE *e;
198 struct include_dir *d;
199
200 e = fopen (filename, mode);
201 if (e != NULL)
202 {
203 *real_filename = xstrdup (filename);
204 return e;
205 }
206
207 if (errno == ENOENT)
208 {
209 for (d = include_dirs; d != NULL; d = d->next)
210 {
211 char *n;
212
213 n = (char *) xmalloc (strlen (d->dir) + strlen (filename) + 2);
214 sprintf (n, "%s/%s", d->dir, filename);
215 e = fopen (n, mode);
216 if (e != NULL)
217 {
218 *real_filename = n;
219 return e;
220 }
221
222 if (errno != ENOENT)
223 break;
224 }
225 }
226
227 fatal ("can't open %s `%s': %s", errmsg, filename, strerror (errno));
228
229 /* Return a value to avoid a compiler warning. */
230 return NULL;
231 }
232 \f
233 /* Unicode support. */
234
235 /* Convert an ASCII string to a unicode string. We just copy it,
236 expanding chars to shorts, rather than doing something intelligent. */
237
238 void
239 unicode_from_ascii (length, unicode, ascii)
240 int *length;
241 unichar **unicode;
242 const char *ascii;
243 {
244 int len;
245 const char *s;
246 unsigned short *w;
247
248 len = strlen (ascii);
249
250 if (length != NULL)
251 *length = len;
252
253 *unicode = ((unichar *) res_alloc ((len + 1) * sizeof (unichar)));
254
255 for (s = ascii, w = *unicode; *s != '\0'; s++, w++)
256 *w = *s & 0xff;
257 *w = 0;
258 }
259
260 /* Print the unicode string UNICODE to the file E. LENGTH is the
261 number of characters to print, or -1 if we should print until the
262 end of the string. */
263
264 void
265 unicode_print (e, unicode, length)
266 FILE *e;
267 const unichar *unicode;
268 int length;
269 {
270 while (1)
271 {
272 unichar ch;
273
274 if (length == 0)
275 return;
276 if (length > 0)
277 --length;
278
279 ch = *unicode;
280
281 if (ch == 0 && length < 0)
282 return;
283
284 ++unicode;
285
286 if ((ch & 0x7f) == ch && isprint (ch))
287 putc (ch, e);
288 else if ((ch & 0xff) == ch)
289 fprintf (e, "\\%03o", (unsigned int) ch);
290 else
291 fprintf (e, "\\x%x", (unsigned int) ch);
292 }
293 }
294 \f
295 /* Compare two resource ID's. We consider name entries to come before
296 numeric entries, because that is how they appear in the COFF .rsrc
297 section. */
298
299 int
300 res_id_cmp (a, b)
301 struct res_id a;
302 struct res_id b;
303 {
304 if (! a.named)
305 {
306 if (b.named)
307 return 1;
308 if (a.u.id > b.u.id)
309 return 1;
310 else if (a.u.id < b.u.id)
311 return -1;
312 else
313 return 0;
314 }
315 else
316 {
317 unichar *as, *ase, *bs, *bse;
318
319 if (! b.named)
320 return -1;
321
322 as = a.u.n.name;
323 ase = as + a.u.n.length;
324 bs = b.u.n.name;
325 bse = bs + b.u.n.length;
326
327 while (as < ase)
328 {
329 int i;
330
331 if (bs >= bse)
332 return 1;
333 i = (int) *as - (int) *bs;
334 if (i != 0)
335 return i;
336 ++as;
337 ++bs;
338 }
339
340 if (bs < bse)
341 return -1;
342
343 return 0;
344 }
345 }
346
347 /* Print a resource ID. */
348
349 void
350 res_id_print (stream, id, quote)
351 FILE *stream;
352 struct res_id id;
353 int quote;
354 {
355 if (! id.named)
356 fprintf (stream, "%lu", id.u.id);
357 else
358 {
359 if (quote)
360 putc ('"', stream);
361 unicode_print (stream, id.u.n.name, id.u.n.length);
362 if (quote)
363 putc ('"', stream);
364 }
365 }
366
367 /* Print a list of resource ID's. */
368
369 void
370 res_ids_print (stream, cids, ids)
371 FILE *stream;
372 int cids;
373 const struct res_id *ids;
374 {
375 int i;
376
377 for (i = 0; i < cids; i++)
378 {
379 res_id_print (stream, ids[i], 1);
380 if (i + 1 < cids)
381 fprintf (stream, ": ");
382 }
383 }
384
385 /* Convert an ASCII string to a resource ID. */
386
387 void
388 res_string_to_id (res_id, string)
389 struct res_id *res_id;
390 const char *string;
391 {
392 res_id->named = 1;
393 unicode_from_ascii (&res_id->u.n.length, &res_id->u.n.name, string);
394 }
395
396 /* Define a resource. The arguments are the resource tree, RESOURCES,
397 and the location at which to put it in the tree, CIDS and IDS.
398 This returns a newly allocated res_resource structure, which the
399 caller is expected to initialize. If DUPOK is non-zero, then if a
400 resource with this ID exists, it is returned. Otherwise, a warning
401 is issued, and a new resource is created replacing the existing
402 one. */
403
404 struct res_resource *
405 define_resource (resources, cids, ids, dupok)
406 struct res_directory **resources;
407 int cids;
408 const struct res_id *ids;
409 int dupok;
410 {
411 struct res_entry *re = NULL;
412 int i;
413
414 assert (cids > 0);
415 for (i = 0; i < cids; i++)
416 {
417 struct res_entry **pp;
418
419 if (*resources == NULL)
420 {
421 *resources = ((struct res_directory *)
422 res_alloc (sizeof **resources));
423 (*resources)->characteristics = 0;
424 (*resources)->time = 0;
425 (*resources)->major = 0;
426 (*resources)->minor = 0;
427 (*resources)->entries = NULL;
428 }
429
430 for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
431 if (res_id_cmp ((*pp)->id, ids[i]) == 0)
432 break;
433
434 if (*pp != NULL)
435 re = *pp;
436 else
437 {
438 re = (struct res_entry *) res_alloc (sizeof *re);
439 re->next = NULL;
440 re->id = ids[i];
441 if ((i + 1) < cids)
442 {
443 re->subdir = 1;
444 re->u.dir = NULL;
445 }
446 else
447 {
448 re->subdir = 0;
449 re->u.res = NULL;
450 }
451
452 *pp = re;
453 }
454
455 if ((i + 1) < cids)
456 {
457 if (! re->subdir)
458 {
459 fprintf (stderr, "%s: ", program_name);
460 res_ids_print (stderr, i, ids);
461 fprintf (stderr, ": expected to be a directory\n");
462 xexit (1);
463 }
464
465 resources = &re->u.dir;
466 }
467 }
468
469 if (re->subdir)
470 {
471 fprintf (stderr, "%s: ", program_name);
472 res_ids_print (stderr, cids, ids);
473 fprintf (stderr, ": expected to be a leaf\n");
474 xexit (1);
475 }
476
477 if (re->u.res != NULL)
478 {
479 if (dupok)
480 return re->u.res;
481
482 fprintf (stderr, "%s: warning: ", program_name);
483 res_ids_print (stderr, cids, ids);
484 fprintf (stderr, ": duplicate value\n");
485 }
486
487 re->u.res = ((struct res_resource *)
488 res_alloc (sizeof (struct res_resource)));
489
490 re->u.res->type = RES_TYPE_UNINITIALIZED;
491 memset (&re->u.res->res_info, 0, sizeof (struct res_res_info));
492 memset (&re->u.res->coff_info, 0, sizeof (struct res_coff_info));
493
494 return re->u.res;
495 }
496
497 /* Define a standard resource. This is a version of define_resource
498 that just takes type, name, and language arguments. */
499
500 struct res_resource *
501 define_standard_resource (resources, type, name, language, dupok)
502 struct res_directory **resources;
503 int type;
504 struct res_id name;
505 int language;
506 int dupok;
507 {
508 struct res_id a[3];
509
510 a[0].named = 0;
511 a[0].u.id = type;
512 a[1] = name;
513 a[2].named = 0;
514 a[2].u.id = language;
515 return define_resource (resources, 3, a, dupok);
516 }
517
518 /* Comparison routine for resource sorting. */
519
520 static int
521 cmp_res_entry (p1, p2)
522 const PTR p1;
523 const PTR p2;
524 {
525 const struct res_entry **re1, **re2;
526
527 re1 = (const struct res_entry **) p1;
528 re2 = (const struct res_entry **) p2;
529 return res_id_cmp ((*re1)->id, (*re2)->id);
530 }
531
532 /* Sort the resources. */
533
534 static struct res_directory *
535 sort_resources (resdir)
536 struct res_directory *resdir;
537 {
538 int c, i;
539 struct res_entry *re;
540 struct res_entry **a;
541
542 if (resdir->entries == NULL)
543 return resdir;
544
545 c = 0;
546 for (re = resdir->entries; re != NULL; re = re->next)
547 ++c;
548
549 /* This is a recursive routine, so using xmalloc is probably better
550 than alloca. */
551 a = (struct res_entry **) xmalloc (c * sizeof (struct res_entry *));
552
553 for (i = 0, re = resdir->entries; re != NULL; re = re->next, i++)
554 a[i] = re;
555
556 qsort (a, c, sizeof (struct res_entry *), cmp_res_entry);
557
558 resdir->entries = a[0];
559 for (i = 0; i < c - 1; i++)
560 a[i]->next = a[i + 1];
561 a[i]->next = NULL;
562
563 free (a);
564
565 /* Now sort the subdirectories. */
566
567 for (re = resdir->entries; re != NULL; re = re->next)
568 if (re->subdir)
569 re->u.dir = sort_resources (re->u.dir);
570
571 return resdir;
572 }
573 \f
574 /* Return whether the dialog resource DIALOG is a DIALOG or a
575 DIALOGEX. */
576
577 int
578 extended_dialog (dialog)
579 const struct dialog *dialog;
580 {
581 const struct dialog_control *c;
582
583 if (dialog->ex != NULL)
584 return 1;
585
586 for (c = dialog->controls; c != NULL; c = c->next)
587 if (c->data != NULL || c->help != 0)
588 return 1;
589
590 return 0;
591 }
592
593 /* Return whether MENUITEMS are a MENU or a MENUEX. */
594
595 int
596 extended_menu (menu)
597 const struct menu *menu;
598 {
599 return extended_menuitems (menu->items);
600 }
601
602 static int
603 extended_menuitems (menuitems)
604 const struct menuitem *menuitems;
605 {
606 const struct menuitem *mi;
607
608 for (mi = menuitems; mi != NULL; mi = mi->next)
609 {
610 if (mi->help != 0 || mi->state != 0)
611 return 1;
612 if (mi->popup != NULL && mi->id != 0)
613 return 1;
614 if ((mi->type
615 & ~ (MENUITEM_CHECKED
616 | MENUITEM_GRAYED
617 | MENUITEM_HELP
618 | MENUITEM_INACTIVE
619 | MENUITEM_MENUBARBREAK
620 | MENUITEM_MENUBREAK))
621 != 0)
622 return 1;
623 if (mi->popup != NULL)
624 {
625 if (extended_menuitems (mi->popup))
626 return 1;
627 }
628 }
629
630 return 0;
631 }
632 \f
633 /* Convert a string to a format type, or exit if it can't be done. */
634
635 static enum res_format
636 format_from_name (name)
637 const char *name;
638 {
639 const struct format_map *m;
640
641 for (m = format_names; m->name != NULL; m++)
642 if (strcasecmp (m->name, name) == 0)
643 break;
644
645 if (m->name == NULL)
646 {
647 fprintf (stderr, "%s: unknown format type `%s'\n", program_name, name);
648 fprintf (stderr, "%s: supported formats:", program_name);
649 for (m = format_names; m->name != NULL; m++)
650 fprintf (stderr, " %s", m->name);
651 fprintf (stderr, "\n");
652 xexit (1);
653 }
654
655 return m->format;
656 }
657
658 /* Work out a format type given a file name. If INPUT is non-zero,
659 it's OK to look at the file itself. */
660
661 static enum res_format
662 format_from_filename (filename, input)
663 const char *filename;
664 int input;
665 {
666 const char *ext;
667 FILE *e;
668 unsigned char b1, b2, b3, b4, b5;
669 int magic;
670
671 /* If we have an extension, see if we recognize it as implying a
672 particular format. */
673 ext = strrchr (filename, '.');
674 if (ext != NULL)
675 {
676 const struct format_map *m;
677
678 ++ext;
679 for (m = format_fileexts; m->name != NULL; m++)
680 if (strcasecmp (m->name, ext) == 0)
681 return m->format;
682 }
683
684 /* If we don't recognize the name of an output file, assume it's a
685 COFF file. */
686
687 if (! input)
688 return RES_FORMAT_COFF;
689
690 /* Read the first few bytes of the file to see if we can guess what
691 it is. */
692
693 e = fopen (filename, FOPEN_RB);
694 if (e == NULL)
695 fatal ("%s: %s", filename, strerror (errno));
696
697 b1 = getc (e);
698 b2 = getc (e);
699 b3 = getc (e);
700 b4 = getc (e);
701 b5 = getc (e);
702
703 fclose (e);
704
705 /* A PE executable starts with 0x4d 0x5a. */
706 if (b1 == 0x4d && b2 == 0x5a)
707 return RES_FORMAT_COFF;
708
709 /* A COFF .o file starts with a COFF magic number. */
710 magic = (b2 << 8) | b1;
711 switch (magic)
712 {
713 case 0x14c: /* i386 */
714 case 0x166: /* MIPS */
715 case 0x184: /* Alpha */
716 case 0x268: /* 68k */
717 case 0x1f0: /* PowerPC */
718 case 0x290: /* PA */
719 return RES_FORMAT_COFF;
720 }
721
722 /* A RES file starts with 0x0 0x0 0x0 0x0 0x20 0x0 0x0 0x0. */
723 if (b1 == 0 && b2 == 0 && b3 == 0 && b4 == 0 && b5 == 0x20)
724 return RES_FORMAT_RES;
725
726 /* If every character is printable or space, assume it's an RC file. */
727 if ((isprint (b1) || isspace (b1))
728 && (isprint (b2) || isspace (b2))
729 && (isprint (b3) || isspace (b3))
730 && (isprint (b4) || isspace (b4))
731 && (isprint (b5) || isspace (b5)))
732 return RES_FORMAT_RC;
733
734 /* Otherwise, we give up. */
735 fatal ("can not determine type of file `%s'; use the -I option",
736 filename);
737
738 /* Return something to silence the compiler warning. */
739 return RES_FORMAT_UNKNOWN;
740 }
741
742 /* Print a usage message and exit. */
743
744 static void
745 usage (stream, status)
746 FILE *stream;
747 int status;
748 {
749 fprintf (stream, "Usage: %s [options] [input-file] [output-file]\n",
750 program_name);
751 fprintf (stream, "\
752 Options:\n\
753 -i FILE, --input FILE Name input file\n\
754 -o FILE, --output FILE Name output file\n\
755 -I FORMAT, --input-format FORMAT\n\
756 Specify input format\n\
757 -O FORMAT, --output-format FORMAT\n\
758 Specify output format\n\
759 -F TARGET, --target TARGET Specify COFF target\n\
760 --preprocessor PROGRAM Program to use to preprocess rc file\n\
761 --include-dir DIR Include directory when preprocessing rc file\n\
762 --define SYM[=VAL] Define SYM when preprocessing rc file\n\
763 --language VAL Set language when reading rc file\n\
764 #ifdef YYDEBUG
765 --yydebug Turn on parser debugging\n\
766 #endif
767 --help Print this help message\n\
768 --version Print version information\n");
769 fprintf (stream, "\
770 FORMAT is one of rc, res, or coff, and is deduced from the file name\n\
771 extension if not specified. A single file name is an input file.\n\
772 No input-file is stdin, default rc. No output-file is stdout, default rc.\n");
773 list_supported_targets (program_name, stream);
774 if (status == 0)
775 fprintf (stream, "Report bugs to bug-gnu-utils@prep.ai.mit.edu\n");
776 exit (status);
777 }
778
779 /* The main function. */
780
781 int
782 main (argc, argv)
783 int argc;
784 char **argv;
785 {
786 int c;
787 char *input_filename;
788 char *output_filename;
789 enum res_format input_format;
790 enum res_format output_format;
791 char *target;
792 char *preprocessor;
793 char *preprocargs;
794 int language;
795 struct res_directory *resources;
796
797 program_name = argv[0];
798 xmalloc_set_program_name (program_name);
799
800 bfd_init ();
801 set_default_bfd_target ();
802
803 res_init ();
804
805 input_filename = NULL;
806 output_filename = NULL;
807 input_format = RES_FORMAT_UNKNOWN;
808 output_format = RES_FORMAT_UNKNOWN;
809 target = NULL;
810 preprocessor = NULL;
811 preprocargs = NULL;
812 language = -1;
813
814 while ((c = getopt_long (argc, argv, "i:o:I:O:F:", long_options,
815 (int *) 0)) != EOF)
816 {
817 switch (c)
818 {
819 case 'i':
820 input_filename = optarg;
821 break;
822
823 case 'o':
824 output_filename = optarg;
825 break;
826
827 case 'I':
828 input_format = format_from_name (optarg);
829 break;
830
831 case 'O':
832 output_format = format_from_name (optarg);
833 break;
834
835 case 'F':
836 target = optarg;
837 break;
838
839 case OPTION_PREPROCESSOR:
840 preprocessor = optarg;
841 break;
842
843 case OPTION_DEFINE:
844 if (preprocargs == NULL)
845 {
846 preprocargs = xmalloc (strlen (optarg) + 3);
847 sprintf (preprocargs, "-D%s", optarg);
848 }
849 else
850 {
851 char *n;
852
853 n = xmalloc (strlen (preprocargs) + strlen (optarg) + 4);
854 sprintf (n, "%s -D%s", preprocargs, optarg);
855 free (preprocargs);
856 preprocargs = n;
857 }
858 break;
859
860 case OPTION_INCLUDE_DIR:
861 if (preprocargs == NULL)
862 {
863 preprocargs = xmalloc (strlen (optarg) + 3);
864 sprintf (preprocargs, "-I%s", optarg);
865 }
866 else
867 {
868 char *n;
869
870 n = xmalloc (strlen (preprocargs) + strlen (optarg) + 4);
871 sprintf (n, "%s -I%s", preprocargs, optarg);
872 free (preprocargs);
873 preprocargs = n;
874 }
875
876 {
877 struct include_dir *n, **pp;
878
879 n = (struct include_dir *) xmalloc (sizeof *n);
880 n->next = NULL;
881 n->dir = optarg;
882
883 for (pp = &include_dirs; *pp != NULL; pp = &(*pp)->next)
884 ;
885 *pp = n;
886 }
887
888 break;
889
890 case OPTION_LANGUAGE:
891 language = strtol (optarg, (char **) NULL, 16);
892 break;
893
894 #ifdef YYDEBUG
895 case OPTION_YYDEBUG:
896 yydebug = 1;
897 break;
898 #endif
899
900 case OPTION_HELP:
901 usage (stdout, 0);
902 break;
903
904 case OPTION_VERSION:
905 print_version ("windres");
906 break;
907
908 default:
909 usage (stderr, 1);
910 break;
911 }
912 }
913
914 if (input_filename == NULL && optind < argc)
915 {
916 input_filename = argv[optind];
917 ++optind;
918 }
919
920 if (output_filename == NULL && optind < argc)
921 {
922 output_filename = argv[optind];
923 ++optind;
924 }
925
926 if (argc != optind)
927 usage (stderr, 1);
928
929 if (input_format == RES_FORMAT_UNKNOWN)
930 {
931 if (input_filename == NULL)
932 input_format = RES_FORMAT_RC;
933 else
934 input_format = format_from_filename (input_filename, 1);
935 }
936
937 if (output_format == RES_FORMAT_UNKNOWN)
938 {
939 if (output_filename == NULL)
940 output_format = RES_FORMAT_RC;
941 else
942 output_format = format_from_filename (output_filename, 0);
943 }
944
945 /* Read the input file. */
946
947 switch (input_format)
948 {
949 default:
950 abort ();
951 case RES_FORMAT_RC:
952 resources = read_rc_file (input_filename, preprocessor, preprocargs,
953 language);
954 break;
955 case RES_FORMAT_RES:
956 resources = read_res_file (input_filename);
957 break;
958 case RES_FORMAT_COFF:
959 resources = read_coff_rsrc (input_filename, target);
960 break;
961 }
962
963 /* Sort the resources. This is required for COFF, convenient for
964 rc, and unimportant for res. */
965
966 resources = sort_resources (resources);
967
968 /* Write the output file. */
969
970 reswr_init ();
971
972 switch (output_format)
973 {
974 default:
975 abort ();
976 case RES_FORMAT_RC:
977 write_rc_file (output_filename, resources);
978 break;
979 case RES_FORMAT_RES:
980 write_res_file (output_filename, resources);
981 break;
982 case RES_FORMAT_COFF:
983 write_coff_file (output_filename, target, resources);
984 break;
985 }
986
987 xexit (0);
988 return 0;
989 }
990
991 struct res_directory *
992 read_res_file (filename)
993 const char *filename;
994 {
995 fatal ("read_res_file unimplemented");
996 return NULL;
997 }
998
999 void
1000 write_res_file (filename, resources)
1001 const char *filename;
1002 const struct res_directory *resources;
1003 {
1004 fatal ("write_res_file unimplemented");
1005 }
This page took 0.106013 seconds and 5 git commands to generate.