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