First stab at Windows resource compiler:
[external/binutils.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 }