]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - binutils/resres.c
* config/sh/tm-sh.h (BELIEVE_PCC_PROMOTION): Define, so that
[thirdparty/binutils-gdb.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 }