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