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