This commit was generated by cvs2svn to track changes on a CVS vendor
[deliverable/binutils-gdb.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 }
This page took 0.049469 seconds and 5 git commands to generate.