1999-09-19 Alexandre Oliva <oliva@lsd.ic.unicamp.br>
[deliverable/binutils-gdb.git] / binutils / resres.c
CommitLineData
252b5132
RH
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
31struct res_hdr
32 {
33 unsigned long data_size;
34 unsigned long header_size;
35 };
36
37static void write_res_directory
38 PARAMS ((const struct res_directory *,
39 const struct res_id *, const struct res_id *,
40 int *, int));
41static void write_res_resource
42 PARAMS ((const struct res_id *, const struct res_id *,
43 const struct res_resource *, int *));
44static 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
48static void write_res_id PARAMS ((const struct res_id *));
49static void write_res_info PARAMS ((const struct res_res_info *));
50static void write_res_data PARAMS ((const void *, size_t, int));
51static void write_res_header
52 PARAMS ((unsigned long, const struct res_id *, const struct res_id *,
53 const struct res_res_info *));
54
55static int read_resource_entry PARAMS ((void));
56static void read_res_data PARAMS ((void *, size_t, int));
57static void read_res_id PARAMS ((struct res_id *));
58static unichar *read_unistring PARAMS ((int *));
59static void skip_null_resource PARAMS ((void));
60
61static unsigned long get_id_size PARAMS ((const struct res_id *));
62static void res_align_file PARAMS ((void));
63
64static void
65 res_add_resource
66 PARAMS ((struct res_resource *, const struct res_id *,
67 const struct res_id *, int, int));
68
69void
70 res_append_resource
71 PARAMS ((struct res_directory **, struct res_resource *,
72 int, const struct res_id *, int));
73
74static struct res_directory *resources = NULL;
75
76static FILE *fres;
77static const char *filename;
78
79extern char *program_name;
80
81/* Read resource file */
82struct res_directory *
83read_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 */
102void
103write_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 */
139static int
140read_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 */
187static void
188write_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
254static void
255write_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 */
344static void
345write_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 */
365static unsigned long
366get_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 */
376static void
377write_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 */
400static void
401write_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 */
411static void
412read_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 */
422static void
423write_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 */
443static void
444write_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 */
455void
456read_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 */
483static unichar *
484read_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 */
510static void
511res_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 */
521static void
522skip_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
536skip_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 */
543void
544res_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 */
564void
565res_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}
This page took 0.069903 seconds and 4 git commands to generate.