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 | }
|
---|