2007-05-07 H.J. Lu <hongjiu.lu@intel.com>
[deliverable/binutils-gdb.git] / binutils / resres.c
CommitLineData
252b5132 1/* resres.c: read_res_file and write_res_file implementation for windres.
3db64b00 2 Copyright 1998, 1999, 2001, 2002, 2007 Free Software Foundation, Inc.
252b5132
RH
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
b43b5d5f
NC
19 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
20 02110-1301, USA. */
252b5132 21
34ca6cf8
ILT
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
3db64b00 26#include "sysdep.h"
252b5132 27#include "bfd.h"
252b5132 28#include "libiberty.h"
3db64b00 29#include "bucomm.h"
252b5132
RH
30#include "windres.h"
31
32#include <assert.h>
33#include <time.h>
34
35struct res_hdr
36 {
37 unsigned long data_size;
38 unsigned long header_size;
39 };
40
41static void write_res_directory
42 PARAMS ((const struct res_directory *,
43 const struct res_id *, const struct res_id *,
44 int *, int));
45static void write_res_resource
46 PARAMS ((const struct res_id *, const struct res_id *,
47 const struct res_resource *, int *));
48static 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
52static void write_res_id PARAMS ((const struct res_id *));
53static void write_res_info PARAMS ((const struct res_res_info *));
54static void write_res_data PARAMS ((const void *, size_t, int));
55static void write_res_header
56 PARAMS ((unsigned long, const struct res_id *, const struct res_id *,
57 const struct res_res_info *));
58
59static int read_resource_entry PARAMS ((void));
60static void read_res_data PARAMS ((void *, size_t, int));
61static void read_res_id PARAMS ((struct res_id *));
62static unichar *read_unistring PARAMS ((int *));
63static void skip_null_resource PARAMS ((void));
64
65static unsigned long get_id_size PARAMS ((const struct res_id *));
66static void res_align_file PARAMS ((void));
67
68static void
69 res_add_resource
70 PARAMS ((struct res_resource *, const struct res_id *,
71 const struct res_id *, int, int));
72
73void
74 res_append_resource
75 PARAMS ((struct res_directory **, struct res_resource *,
76 int, const struct res_id *, int));
77
78static struct res_directory *resources = NULL;
79
80static FILE *fres;
81static const char *filename;
82
83extern char *program_name;
84
85/* Read resource file */
86struct res_directory *
87read_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 */
106void
107write_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 */
143static int
144read_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 */
191static void
192write_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
53c7db4b 215 we are going to use in the rc printout. */
252b5132
RH
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
34ca6cf8 223 && re->id.u.id != (unsigned long) *language
252b5132
RH
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
258static void
259write_res_resource (type, name, res, language)
260 const struct res_id *type;
261 const struct res_id *name;
262 const struct res_resource *res;
34ca6cf8 263 int *language ATTRIBUTE_UNUSED;
252b5132
RH
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
34ca6cf8 335 && (type->named || type->u.id != (unsigned long) rt))
252b5132
RH
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 */
348static void
349write_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 */
369static unsigned long
370get_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 */
380static void
381write_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
5f16d855
DD
391 reshdr.header_size = (reshdr.header_size + 3) & ~3;
392
252b5132
RH
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 */
406static void
407write_res_data (data, size, count)
408 const void *data;
409 size_t size;
410 int count;
411{
cbee2975 412 if ((size_t) fwrite (data, size, count, fres) != (size_t) count)
34ca6cf8 413 fatal ("%s: could not write to file", filename);
252b5132
RH
414}
415
416/* Read data from file, abort on failure */
417static void
418read_res_data (data, size, count)
419 void *data;
420 size_t size;
421 int count;
422{
34ca6cf8
ILT
423 if (fread (data, size, count, fres) != (size_t) count)
424 fatal ("%s: unexpected end of file", filename);
252b5132
RH
425}
426
427/* Write a resource id */
428static void
429write_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 */
449static void
450write_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 */
53c7db4b 461void
252b5132
RH
462read_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 */
489static unichar *
490read_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 */
516static void
517res_align_file (void)
518{
5f16d855
DD
519 int pos = ftell (fres);
520 int skip = ((pos + 3) & ~3) - pos;
521 if (fseek (fres, skip, SEEK_CUR) != 0)
252b5132
RH
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 */
529static void
530skip_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
544skip_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 */
551void
552res_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 */
572void
573res_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}
This page took 0.356245 seconds and 4 git commands to generate.