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
3    Copyright 1997, 1998 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 <assert.h>
24 #include <stdio.h>
25 #include <errno.h>
26
27 #include "windres.h"
28
29 struct res_hdr
30   {
31     unsigned long data_size;
32     unsigned long header_size;
33   };
34
35 static void write_res_directory
36   PARAMS ((const struct res_directory *,
37            const struct res_id *, const struct res_id *,
38            int *, int));
39 static void write_res_resource
40   PARAMS ((const struct res_id *, const struct res_id *,
41            const struct res_resource *, int *));
42 static void write_res_bin
43   PARAMS ((const struct res_resource *, const struct res_id *,
44            const struct res_id *, const struct res_res_info *));
45
46 static void write_res_id PARAMS ((const struct res_id *));
47 static void write_res_info PARAMS ((const struct res_res_info *));
48 static void write_res_data PARAMS ((const void *, size_t, int));
49 static void write_res_header
50   PARAMS ((unsigned long, const struct res_id *, const struct res_id *,
51            const struct res_res_info *));
52
53 static int read_resource_entry PARAMS ((void));
54 static void read_res_data PARAMS ((void *, size_t, int));
55 static void read_res_id PARAMS ((struct res_id *));
56 static unichar *read_unistring PARAMS ((int *));
57 static void skip_null_resource PARAMS ((void));
58
59 static unsigned long get_id_size PARAMS ((const struct res_id *));
60 static void res_align_file PARAMS ((void));
61
62 static void
63   res_add_resource
64   PARAMS ((struct res_resource *, const struct res_id *,
65            const struct res_id *, int, int));
66
67 void
68   res_append_resource
69   PARAMS ((struct res_directory **, struct res_resource *,
70            int, const struct res_id *, int));
71
72 static struct res_directory *resources = NULL;
73
74 static FILE *fres;
75 static const char *filename;
76
77 extern char *program_name;
78
79 /* Read resource file */
80 struct res_directory *
81 read_res_file (fn)
82      const char *fn;
83 {
84   filename = fn;
85   fres = fopen (filename, "rb");
86   if (fres == NULL)
87     fatal ("can't open `%s' for output: %s", filename, strerror (errno));
88
89   skip_null_resource ();
90
91   while (read_resource_entry ())
92     ;
93
94   fclose (fres);
95
96   return resources;
97 }
98
99 /* Write resource file */
100 void
101 write_res_file (fn, resdir)
102      const char *fn;
103      const struct res_directory *resdir;
104 {
105   int language;
106   static const unsigned char sign[] =
107   {0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
108    0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
109    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
111   long fpos;
112   struct res_entry *e;
113   int i;
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   enum res_type rtype;
147   long version;
148   long n;
149   void *buff;
150
151   struct res_resource *r;
152
153   res_align_file ();
154
155   /* Read header */
156   if (fread (&reshdr, sizeof (reshdr), 1, fres) != 1)
157     return 0;
158
159   /* read resource type */
160   read_res_id (&type);
161   /* read resource id */
162   read_res_id (&name);
163
164   res_align_file ();
165
166   /* Read additional resource header */
167   read_res_data (&resinfo.version, sizeof (resinfo.version), 1);
168   read_res_data (&resinfo.memflags, sizeof (resinfo.memflags), 1);
169   read_res_data (&resinfo.language, sizeof (resinfo.language), 1);
170   read_res_data (&version, sizeof (version), 1);
171   read_res_data (&resinfo.characteristics, sizeof (resinfo.characteristics), 1);
172
173   res_align_file ();
174
175   /* Allocate buffer for data */
176   buff = res_alloc (reshdr.data_size);
177   /* Read data */
178   read_res_data (buff, reshdr.data_size, 1);
179   /* Convert binary data to resource */
180   r = bin_to_res (type, buff, reshdr.data_size, 0);
181   r->res_info = resinfo;
182   /* Add resource to resource directory */
183   res_add_resource (r, &type, &name, resinfo.language, 0);
184
185   return 1;
186 }
187
188 /* write resource directory to binary resource file */
189 static void
190 write_res_directory (rd, type, name, language, level)
191      const struct res_directory *rd;
192      const struct res_id *type;
193      const struct res_id *name;
194      int *language;
195      int level;
196 {
197   const struct res_entry *re;
198
199   for (re = rd->entries; re != NULL; re = re->next)
200     {
201       switch (level)
202         {
203         case 1:
204           /* If we're at level 1, the key of this resource is the
205              type.  This normally duplicates the information we have
206              stored with the resource itself, but we need to remember
207              the type if this is a user define resource type.  */
208           type = &re->id;
209           break;
210
211         case 2:
212           /* If we're at level 2, the key of this resource is the name
213              we are going to use in the rc printout. */
214           name = &re->id;
215           break;
216
217         case 3:
218           /* If we're at level 3, then this key represents a language.
219              Use it to update the current language.  */
220           if (!re->id.named
221               && re->id.u.id != *language
222               && (re->id.u.id & 0xffff) == re->id.u.id)
223             {
224               *language = re->id.u.id;
225             }
226           break;
227
228         default:
229           break;
230         }
231
232       if (re->subdir)
233         write_res_directory (re->u.dir, type, name, language, level + 1);
234       else
235         {
236           if (level == 3)
237             {
238               /* This is the normal case: the three levels are
239                  TYPE/NAME/LANGUAGE.  NAME will have been set at level
240                  2, and represents the name to use.  We probably just
241                  set LANGUAGE, and it will probably match what the
242                  resource itself records if anything.  */
243               write_res_resource (type, name, re->u.res, language);
244             }
245           else
246             {
247               fprintf (stderr, "// Resource at unexpected level %d\n", level);
248               write_res_resource (type, (struct res_id *) NULL, re->u.res,
249                                   language);
250             }
251         }
252     }
253
254 }
255
256 static void
257 write_res_resource (type, name, res, language)
258      const struct res_id *type;
259      const struct res_id *name;
260      const struct res_resource *res;
261      int *language;
262 {
263   int rt;
264
265   switch (res->type)
266     {
267     default:
268       abort ();
269
270     case RES_TYPE_ACCELERATOR:
271       rt = RT_ACCELERATOR;
272       break;
273
274     case RES_TYPE_BITMAP:
275       rt = RT_BITMAP;
276       break;
277
278     case RES_TYPE_CURSOR:
279       rt = RT_CURSOR;
280       break;
281
282     case RES_TYPE_GROUP_CURSOR:
283       rt = RT_GROUP_CURSOR;
284       break;
285
286     case RES_TYPE_DIALOG:
287       rt = RT_DIALOG;
288       break;
289
290     case RES_TYPE_FONT:
291       rt = RT_FONT;
292       break;
293
294     case RES_TYPE_FONTDIR:
295       rt = RT_FONTDIR;
296       break;
297
298     case RES_TYPE_ICON:
299       rt = RT_ICON;
300       break;
301
302     case RES_TYPE_GROUP_ICON:
303       rt = RT_GROUP_ICON;
304       break;
305
306     case RES_TYPE_MENU:
307       rt = RT_MENU;
308       break;
309
310     case RES_TYPE_MESSAGETABLE:
311       rt = RT_MESSAGETABLE;
312       break;
313
314     case RES_TYPE_RCDATA:
315       rt = RT_RCDATA;
316       break;
317
318     case RES_TYPE_STRINGTABLE:
319       rt = RT_STRING;
320       break;
321
322     case RES_TYPE_USERDATA:
323       rt = 0;
324       break;
325
326     case RES_TYPE_VERSIONINFO:
327       rt = RT_VERSION;
328       break;
329     }
330
331   if (rt != 0
332       && type != NULL
333       && (type->named || type->u.id != rt))
334     {
335       fprintf (stderr, "// Unexpected resource type mismatch: ");
336       res_id_print (stderr, *type, 1);
337       fprintf (stderr, " != %d", rt);
338       abort ();
339     }
340
341   write_res_bin (res, type, name, &res->res_info);
342   return;
343 }
344
345 /* Write a resource in binary resource format */
346 static void
347 write_res_bin (res, type, name, resinfo)
348      const struct res_resource *res;
349      const struct res_id *type;
350      const struct res_id *name;
351      const struct res_res_info *resinfo;
352 {
353   unsigned long datasize = 0;
354   const struct bindata *bin_rep, *data;
355
356   bin_rep = res_to_bin (res, 0);
357   for (data = bin_rep; data != NULL; data = data->next)
358     datasize += data->length;
359
360   write_res_header (datasize, type, name, resinfo);
361
362   for (data = bin_rep; data != NULL; data = data->next)
363     write_res_data (data->data, data->length, 1);
364 }
365
366 /* Get number of bytes needed to store an id in binary format */
367 static unsigned long
368 get_id_size (id)
369      const struct res_id *id;
370 {
371   if (id->named)
372     return sizeof (unichar) * (id->u.n.length + 1);
373   else
374     return sizeof (unichar) * 2;
375 }
376
377 /* Write a resource header */
378 static void
379 write_res_header (datasize, type, name, resinfo)
380      unsigned long datasize;
381      const struct res_id *type;
382      const struct res_id *name;
383      const struct res_res_info *resinfo;
384 {
385   struct res_hdr reshdr;
386   reshdr.data_size = datasize;
387   reshdr.header_size = 24 + get_id_size (type) + get_id_size (name);
388
389   res_align_file ();
390   write_res_data (&reshdr, sizeof (reshdr), 1);
391   write_res_id (type);
392   write_res_id (name);
393
394   res_align_file ();
395
396   write_res_info (resinfo);
397   res_align_file ();
398 }
399
400
401 /* Write data to file, abort on failure */
402 static void
403 write_res_data (data, size, count)
404      const void *data;
405      size_t size;
406      int count;
407 {
408   if (fwrite (data, size, count, fres) != count)
409     fatal ("%s: %s: could not write to file", program_name, filename);
410 }
411
412 /* Read data from file, abort on failure */
413 static void
414 read_res_data (data, size, count)
415      void *data;
416      size_t size;
417      int count;
418 {
419   if (fread (data, size, count, fres) != count)
420     fatal ("%s: %s: unexpected end of file", program_name, filename);
421 }
422
423 /* Write a resource id */
424 static void
425 write_res_id (id)
426      const struct res_id *id;
427 {
428   if (id->named)
429     {
430       unsigned long len = id->u.n.length;
431       unichar null_term = 0;
432       write_res_data (id->u.n.name, len * sizeof (unichar), 1);
433       write_res_data (&null_term, sizeof (null_term), 1);
434     }
435   else
436     {
437       unsigned short i = 0xFFFF;
438       write_res_data (&i, sizeof (i), 1);
439       i = id->u.id;
440       write_res_data (&i, sizeof (i), 1);
441     }
442 }
443
444 /* Write resource info */
445 static void
446 write_res_info (info)
447      const struct res_res_info *info;
448 {
449   write_res_data (&info->version, sizeof (info->version), 1);
450   write_res_data (&info->memflags, sizeof (info->memflags), 1);
451   write_res_data (&info->language, sizeof (info->language), 1);
452   write_res_data (&info->version, sizeof (info->version), 1);
453   write_res_data (&info->characteristics, sizeof (info->characteristics), 1);
454 }
455
456 /* read a resource identifier */
457 void 
458 read_res_id (id)
459      struct res_id *id;
460 {
461   unsigned short ord;
462   unichar *id_s = NULL;
463   int len;
464
465   read_res_data (&ord, sizeof (ord), 1);
466   if (ord == 0xFFFF)            /* an ordinal id */
467     {
468       read_res_data (&ord, sizeof (ord), 1);
469       id->named = 0;
470       id->u.id = ord;
471     }
472   else
473     /* named id */
474     {
475       if (fseek (fres, -sizeof (ord), SEEK_CUR) != 0)
476         fatal ("%s: %s: could not seek in file", program_name, filename);
477       id_s = read_unistring (&len);
478       id->named = 1;
479       id->u.n.length = len;
480       id->u.n.name = id_s;
481     }
482 }
483
484 /* Read a null terminated UNICODE string */
485 static unichar *
486 read_unistring (len)
487      int *len;
488 {
489   unichar *s;
490   unichar c;
491   unichar *p;
492   int n;
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 }