This commit was generated by cvs2svn to track changes on a CVS vendor
[external/binutils.git] / binutils / resres.c
1 /* resres.c: read_res_file and write_res_file implementation for windres.
2    Copyright 1998, 1999 Free Software Foundation, Inc.
3    Written by Anders Norlander <anorland@hem2.passagen.se>.
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 /* FIXME: This file does not work correctly in a cross configuration.
23    It assumes that it can use fread and fwrite to read and write
24    integers.  It does no swapping.  */
25
26 #include "bfd.h"
27 #include "bucomm.h"
28 #include "libiberty.h"
29 #include "windres.h"
30
31 #include <assert.h>
32 #include <time.h>
33
34 struct res_hdr
35   {
36     unsigned long data_size;
37     unsigned long header_size;
38   };
39
40 static void write_res_directory
41   PARAMS ((const struct res_directory *,
42            const struct res_id *, const struct res_id *,
43            int *, int));
44 static void write_res_resource
45   PARAMS ((const struct res_id *, const struct res_id *,
46            const struct res_resource *, int *));
47 static void write_res_bin
48   PARAMS ((const struct res_resource *, const struct res_id *,
49            const struct res_id *, const struct res_res_info *));
50
51 static void write_res_id PARAMS ((const struct res_id *));
52 static void write_res_info PARAMS ((const struct res_res_info *));
53 static void write_res_data PARAMS ((const void *, size_t, int));
54 static void write_res_header
55   PARAMS ((unsigned long, const struct res_id *, const struct res_id *,
56            const struct res_res_info *));
57
58 static int read_resource_entry PARAMS ((void));
59 static void read_res_data PARAMS ((void *, size_t, int));
60 static void read_res_id PARAMS ((struct res_id *));
61 static unichar *read_unistring PARAMS ((int *));
62 static void skip_null_resource PARAMS ((void));
63
64 static unsigned long get_id_size PARAMS ((const struct res_id *));
65 static void res_align_file PARAMS ((void));
66
67 static void
68   res_add_resource
69   PARAMS ((struct res_resource *, const struct res_id *,
70            const struct res_id *, int, int));
71
72 void
73   res_append_resource
74   PARAMS ((struct res_directory **, struct res_resource *,
75            int, const struct res_id *, int));
76
77 static struct res_directory *resources = NULL;
78
79 static FILE *fres;
80 static const char *filename;
81
82 extern char *program_name;
83
84 /* Read resource file */
85 struct res_directory *
86 read_res_file (fn)
87      const char *fn;
88 {
89   filename = fn;
90   fres = fopen (filename, "rb");
91   if (fres == NULL)
92     fatal ("can't open `%s' for output: %s", filename, strerror (errno));
93
94   skip_null_resource ();
95
96   while (read_resource_entry ())
97     ;
98
99   fclose (fres);
100
101   return resources;
102 }
103
104 /* Write resource file */
105 void
106 write_res_file (fn, resdir)
107      const char *fn;
108      const struct res_directory *resdir;
109 {
110   int language;
111   static const unsigned char sign[] =
112   {0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
113    0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
114    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
116   long fpos;
117
118   filename = fn;
119
120   fres = fopen (filename, "wb");
121   if (fres == NULL)
122     fatal ("can't open `%s' for output: %s", filename, strerror (errno));
123
124   /* Write 32 bit resource signature */
125   write_res_data (sign, sizeof (sign), 1);
126
127   /* write resources */
128
129   language = -1;
130   write_res_directory (resdir, (const struct res_id *) NULL,
131                        (const struct res_id *) NULL, &language, 1);
132
133   /* end file on DWORD boundary */
134   fpos = ftell (fres);
135   if (fpos % 4)
136     write_res_data (sign, fpos % 4, 1);
137
138   fclose (fres);
139 }
140
141 /* Read a resource entry, returns 0 when all resources are read */
142 static int
143 read_resource_entry (void)
144 {
145   struct res_id type;
146   struct res_id name;
147   struct res_res_info resinfo;
148   struct res_hdr reshdr;
149   long version;
150   void *buff;
151
152   struct res_resource *r;
153
154   res_align_file ();
155
156   /* Read header */
157   if (fread (&reshdr, sizeof (reshdr), 1, fres) != 1)
158     return 0;
159
160   /* read resource type */
161   read_res_id (&type);
162   /* read resource id */
163   read_res_id (&name);
164
165   res_align_file ();
166
167   /* Read additional resource header */
168   read_res_data (&resinfo.version, sizeof (resinfo.version), 1);
169   read_res_data (&resinfo.memflags, sizeof (resinfo.memflags), 1);
170   read_res_data (&resinfo.language, sizeof (resinfo.language), 1);
171   read_res_data (&version, sizeof (version), 1);
172   read_res_data (&resinfo.characteristics, sizeof (resinfo.characteristics), 1);
173
174   res_align_file ();
175
176   /* Allocate buffer for data */
177   buff = res_alloc (reshdr.data_size);
178   /* Read data */
179   read_res_data (buff, reshdr.data_size, 1);
180   /* Convert binary data to resource */
181   r = bin_to_res (type, buff, reshdr.data_size, 0);
182   r->res_info = resinfo;
183   /* Add resource to resource directory */
184   res_add_resource (r, &type, &name, resinfo.language, 0);
185
186   return 1;
187 }
188
189 /* write resource directory to binary resource file */
190 static void
191 write_res_directory (rd, type, name, language, level)
192      const struct res_directory *rd;
193      const struct res_id *type;
194      const struct res_id *name;
195      int *language;
196      int level;
197 {
198   const struct res_entry *re;
199
200   for (re = rd->entries; re != NULL; re = re->next)
201     {
202       switch (level)
203         {
204         case 1:
205           /* If we're at level 1, the key of this resource is the
206              type.  This normally duplicates the information we have
207              stored with the resource itself, but we need to remember
208              the type if this is a user define resource type.  */
209           type = &re->id;
210           break;
211
212         case 2:
213           /* If we're at level 2, the key of this resource is the name
214              we are going to use in the rc printout. */
215           name = &re->id;
216           break;
217
218         case 3:
219           /* If we're at level 3, then this key represents a language.
220              Use it to update the current language.  */
221           if (!re->id.named
222               && re->id.u.id != (unsigned long) *language
223               && (re->id.u.id & 0xffff) == re->id.u.id)
224             {
225               *language = re->id.u.id;
226             }
227           break;
228
229         default:
230           break;
231         }
232
233       if (re->subdir)
234         write_res_directory (re->u.dir, type, name, language, level + 1);
235       else
236         {
237           if (level == 3)
238             {
239               /* This is the normal case: the three levels are
240                  TYPE/NAME/LANGUAGE.  NAME will have been set at level
241                  2, and represents the name to use.  We probably just
242                  set LANGUAGE, and it will probably match what the
243                  resource itself records if anything.  */
244               write_res_resource (type, name, re->u.res, language);
245             }
246           else
247             {
248               fprintf (stderr, "// Resource at unexpected level %d\n", level);
249               write_res_resource (type, (struct res_id *) NULL, re->u.res,
250                                   language);
251             }
252         }
253     }
254
255 }
256
257 static void
258 write_res_resource (type, name, res, language)
259      const struct res_id *type;
260      const struct res_id *name;
261      const struct res_resource *res;
262      int *language ATTRIBUTE_UNUSED;
263 {
264   int rt;
265
266   switch (res->type)
267     {
268     default:
269       abort ();
270
271     case RES_TYPE_ACCELERATOR:
272       rt = RT_ACCELERATOR;
273       break;
274
275     case RES_TYPE_BITMAP:
276       rt = RT_BITMAP;
277       break;
278
279     case RES_TYPE_CURSOR:
280       rt = RT_CURSOR;
281       break;
282
283     case RES_TYPE_GROUP_CURSOR:
284       rt = RT_GROUP_CURSOR;
285       break;
286
287     case RES_TYPE_DIALOG:
288       rt = RT_DIALOG;
289       break;
290
291     case RES_TYPE_FONT:
292       rt = RT_FONT;
293       break;
294
295     case RES_TYPE_FONTDIR:
296       rt = RT_FONTDIR;
297       break;
298
299     case RES_TYPE_ICON:
300       rt = RT_ICON;
301       break;
302
303     case RES_TYPE_GROUP_ICON:
304       rt = RT_GROUP_ICON;
305       break;
306
307     case RES_TYPE_MENU:
308       rt = RT_MENU;
309       break;
310
311     case RES_TYPE_MESSAGETABLE:
312       rt = RT_MESSAGETABLE;
313       break;
314
315     case RES_TYPE_RCDATA:
316       rt = RT_RCDATA;
317       break;
318
319     case RES_TYPE_STRINGTABLE:
320       rt = RT_STRING;
321       break;
322
323     case RES_TYPE_USERDATA:
324       rt = 0;
325       break;
326
327     case RES_TYPE_VERSIONINFO:
328       rt = RT_VERSION;
329       break;
330     }
331
332   if (rt != 0
333       && type != NULL
334       && (type->named || type->u.id != (unsigned long) rt))
335     {
336       fprintf (stderr, "// Unexpected resource type mismatch: ");
337       res_id_print (stderr, *type, 1);
338       fprintf (stderr, " != %d", rt);
339       abort ();
340     }
341
342   write_res_bin (res, type, name, &res->res_info);
343   return;
344 }
345
346 /* Write a resource in binary resource format */
347 static void
348 write_res_bin (res, type, name, resinfo)
349      const struct res_resource *res;
350      const struct res_id *type;
351      const struct res_id *name;
352      const struct res_res_info *resinfo;
353 {
354   unsigned long datasize = 0;
355   const struct bindata *bin_rep, *data;
356
357   bin_rep = res_to_bin (res, 0);
358   for (data = bin_rep; data != NULL; data = data->next)
359     datasize += data->length;
360
361   write_res_header (datasize, type, name, resinfo);
362
363   for (data = bin_rep; data != NULL; data = data->next)
364     write_res_data (data->data, data->length, 1);
365 }
366
367 /* Get number of bytes needed to store an id in binary format */
368 static unsigned long
369 get_id_size (id)
370      const struct res_id *id;
371 {
372   if (id->named)
373     return sizeof (unichar) * (id->u.n.length + 1);
374   else
375     return sizeof (unichar) * 2;
376 }
377
378 /* Write a resource header */
379 static void
380 write_res_header (datasize, type, name, resinfo)
381      unsigned long datasize;
382      const struct res_id *type;
383      const struct res_id *name;
384      const struct res_res_info *resinfo;
385 {
386   struct res_hdr reshdr;
387   reshdr.data_size = datasize;
388   reshdr.header_size = 24 + get_id_size (type) + get_id_size (name);
389
390   res_align_file ();
391   write_res_data (&reshdr, sizeof (reshdr), 1);
392   write_res_id (type);
393   write_res_id (name);
394
395   res_align_file ();
396
397   write_res_info (resinfo);
398   res_align_file ();
399 }
400
401
402 /* Write data to file, abort on failure */
403 static void
404 write_res_data (data, size, count)
405      const void *data;
406      size_t size;
407      int count;
408 {
409   if (fwrite (data, size, count, fres) != (size_t) count)
410     fatal ("%s: could not write to file", filename);
411 }
412
413 /* Read data from file, abort on failure */
414 static void
415 read_res_data (data, size, count)
416      void *data;
417      size_t size;
418      int count;
419 {
420   if (fread (data, size, count, fres) != (size_t) count)
421     fatal ("%s: unexpected end of file", filename);
422 }
423
424 /* Write a resource id */
425 static void
426 write_res_id (id)
427      const struct res_id *id;
428 {
429   if (id->named)
430     {
431       unsigned long len = id->u.n.length;
432       unichar null_term = 0;
433       write_res_data (id->u.n.name, len * sizeof (unichar), 1);
434       write_res_data (&null_term, sizeof (null_term), 1);
435     }
436   else
437     {
438       unsigned short i = 0xFFFF;
439       write_res_data (&i, sizeof (i), 1);
440       i = id->u.id;
441       write_res_data (&i, sizeof (i), 1);
442     }
443 }
444
445 /* Write resource info */
446 static void
447 write_res_info (info)
448      const struct res_res_info *info;
449 {
450   write_res_data (&info->version, sizeof (info->version), 1);
451   write_res_data (&info->memflags, sizeof (info->memflags), 1);
452   write_res_data (&info->language, sizeof (info->language), 1);
453   write_res_data (&info->version, sizeof (info->version), 1);
454   write_res_data (&info->characteristics, sizeof (info->characteristics), 1);
455 }
456
457 /* read a resource identifier */
458 void 
459 read_res_id (id)
460      struct res_id *id;
461 {
462   unsigned short ord;
463   unichar *id_s = NULL;
464   int len;
465
466   read_res_data (&ord, sizeof (ord), 1);
467   if (ord == 0xFFFF)            /* an ordinal id */
468     {
469       read_res_data (&ord, sizeof (ord), 1);
470       id->named = 0;
471       id->u.id = ord;
472     }
473   else
474     /* named id */
475     {
476       if (fseek (fres, -sizeof (ord), SEEK_CUR) != 0)
477         fatal ("%s: %s: could not seek in file", program_name, filename);
478       id_s = read_unistring (&len);
479       id->named = 1;
480       id->u.n.length = len;
481       id->u.n.name = id_s;
482     }
483 }
484
485 /* Read a null terminated UNICODE string */
486 static unichar *
487 read_unistring (len)
488      int *len;
489 {
490   unichar *s;
491   unichar c;
492   unichar *p;
493   int l;
494
495   *len = 0;
496   l = 0;
497
498   /* there are hardly any names longer than 256 characters */
499   p = s = (unichar *) xmalloc (sizeof (unichar) * 256);
500   do
501     {
502       read_res_data (&c, sizeof (c), 1);
503       *p++ = c;
504       if (c != 0)
505         l++;
506     }
507   while (c != 0);
508   *len = l;
509   return s;
510 }
511
512 /* align file on DWORD boundary */
513 static void
514 res_align_file (void)
515 {
516   if (fseek (fres, ftell (fres) % 4, SEEK_CUR) != 0)
517     fatal ("%s: %s: unable to align file", program_name, filename);
518 }
519
520 /* Check if file is a win32 binary resource file, if so
521    skip past the null resource. Returns 0 if successful, -1 on
522    error.
523  */
524 static void
525 skip_null_resource (void)
526 {
527   struct res_hdr reshdr =
528   {0, 0};
529   read_res_data (&reshdr, sizeof (reshdr), 1);
530   if ((reshdr.data_size != 0) || (reshdr.header_size != 0x20))
531     goto skip_err;
532
533   /* Subtract size of HeaderSize and DataSize */
534   if (fseek (fres, reshdr.header_size - 8, SEEK_CUR) != 0)
535     goto skip_err;
536
537   return;
538
539 skip_err:
540   fprintf (stderr, "%s: %s: Not a valid WIN32 resource file\n", program_name,
541            filename);
542   xexit (1);
543 }
544
545 /* Add a resource to resource directory */
546 void
547 res_add_resource (r, type, id, language, dupok)
548      struct res_resource *r;
549      const struct res_id *type;
550      const struct res_id *id;
551      int language;
552      int dupok;
553 {
554   struct res_id a[3];
555
556   a[0] = *type;
557   a[1] = *id;
558   a[2].named = 0;
559   a[2].u.id = language;
560   res_append_resource (&resources, r, 3, a, dupok);
561 }
562
563 /* Append a resource to resource directory.
564    This is just copied from define_resource
565    and modified to add an existing resource.
566  */
567 void
568 res_append_resource (resources, resource, cids, ids, dupok)
569      struct res_directory **resources;
570      struct res_resource *resource;
571      int cids;
572      const struct res_id *ids;
573      int dupok;
574 {
575   struct res_entry *re = NULL;
576   int i;
577
578   assert (cids > 0);
579   for (i = 0; i < cids; i++)
580     {
581       struct res_entry **pp;
582
583       if (*resources == NULL)
584         {
585           static unsigned long timeval;
586
587           /* Use the same timestamp for every resource created in a
588              single run.  */
589           if (timeval == 0)
590             timeval = time (NULL);
591
592           *resources = ((struct res_directory *)
593                         res_alloc (sizeof **resources));
594           (*resources)->characteristics = 0;
595           (*resources)->time = timeval;
596           (*resources)->major = 0;
597           (*resources)->minor = 0;
598           (*resources)->entries = NULL;
599         }
600
601       for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
602         if (res_id_cmp ((*pp)->id, ids[i]) == 0)
603           break;
604
605       if (*pp != NULL)
606         re = *pp;
607       else
608         {
609           re = (struct res_entry *) res_alloc (sizeof *re);
610           re->next = NULL;
611           re->id = ids[i];
612           if ((i + 1) < cids)
613             {
614               re->subdir = 1;
615               re->u.dir = NULL;
616             }
617           else
618             {
619               re->subdir = 0;
620               re->u.res = NULL;
621             }
622
623           *pp = re;
624         }
625
626       if ((i + 1) < cids)
627         {
628           if (!re->subdir)
629             {
630               fprintf (stderr, "%s: ", program_name);
631               res_ids_print (stderr, i, ids);
632               fprintf (stderr, ": expected to be a directory\n");
633               xexit (1);
634             }
635
636           resources = &re->u.dir;
637         }
638     }
639
640   if (re->subdir)
641     {
642       fprintf (stderr, "%s: ", program_name);
643       res_ids_print (stderr, cids, ids);
644       fprintf (stderr, ": expected to be a leaf\n");
645       xexit (1);
646     }
647
648   if (re->u.res != NULL)
649     {
650       if (dupok)
651         return;
652
653       fprintf (stderr, "%s: warning: ", program_name);
654       res_ids_print (stderr, cids, ids);
655       fprintf (stderr, ": duplicate value\n");
656     }
657
658   re->u.res = resource;
659 }