xref: /netbsd-src/external/gpl3/binutils.old/dist/binutils/resres.c (revision a24efa7dea9f1f56c3bdb15a927d3516792ace1c)
1 /* resres.c: read_res_file and write_res_file implementation for windres.
2    Copyright 1998, 1999, 2001, 2002, 2005, 2007, 2008, 2011
3    Free Software Foundation, Inc.
4    Written by Anders Norlander <anorland@hem2.passagen.se>.
5    Rewritten by Kai Tietz, Onevision.
6 
7    This file is part of GNU Binutils.
8 
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
22    02110-1301, USA.  */
23 
24 /* FIXME: This file does not work correctly in a cross configuration.
25    It assumes that it can use fread and fwrite to read and write
26    integers.  It does no swapping.  */
27 
28 #include "sysdep.h"
29 #include "bfd.h"
30 #include "bucomm.h"
31 #include "libiberty.h"
32 #include "windres.h"
33 
34 #include <assert.h>
35 #include <time.h>
36 
37 static rc_uint_type write_res_directory (windres_bfd *, rc_uint_type,
38 				    	 const rc_res_directory *, const rc_res_id *,
39 				    	 const rc_res_id *, rc_uint_type *, int);
40 static rc_uint_type write_res_resource (windres_bfd *, rc_uint_type,const rc_res_id *,
41 				   	const rc_res_id *, const rc_res_resource *,
42 				   	rc_uint_type *);
43 static rc_uint_type write_res_bin (windres_bfd *, rc_uint_type, const rc_res_resource *,
44 				   const rc_res_id *, const rc_res_id *,
45 				   const rc_res_res_info *);
46 
47 static rc_uint_type write_res_id (windres_bfd *, rc_uint_type, const rc_res_id *);
48 static rc_uint_type write_res_info (windres_bfd *, rc_uint_type, const rc_res_res_info *);
49 static rc_uint_type write_res_data_hdr (windres_bfd *, rc_uint_type, res_hdr *);
50 
51 static rc_uint_type write_res_header (windres_bfd *, rc_uint_type, rc_uint_type,
52 				      const rc_res_id *, const rc_res_id *,
53 				      const rc_res_res_info *);
54 
55 static int read_resource_entry (windres_bfd *, rc_uint_type *, rc_uint_type);
56 static void read_res_data (windres_bfd *, rc_uint_type *, rc_uint_type, void *,
57 			   rc_uint_type);
58 static void read_res_data_hdr (windres_bfd *, rc_uint_type *, rc_uint_type, res_hdr *);
59 static void read_res_id (windres_bfd *, rc_uint_type *, rc_uint_type, rc_res_id *);
60 static unichar *read_unistring (windres_bfd *, rc_uint_type *, rc_uint_type, rc_uint_type *);
61 static void skip_null_resource (windres_bfd *, rc_uint_type *, rc_uint_type);
62 static int probe_binary (windres_bfd *wrbfd, rc_uint_type);
63 
64 static unsigned long get_id_size (const rc_res_id *);
65 
66 static void res_add_resource (rc_res_resource *, const rc_res_id *,
67 			      const rc_res_id *, rc_uint_type, int);
68 
69 static void res_append_resource (rc_res_directory **, rc_res_resource *,
70 				 int, const rc_res_id *, int);
71 
72 static rc_res_directory *resources = NULL;
73 
74 static const char *filename;
75 
76 extern char *program_name;
77 
78 /* Read resource file */
79 rc_res_directory *
80 read_res_file (const char *fn)
81 {
82   rc_uint_type off, flen;
83   windres_bfd wrbfd;
84   bfd *abfd;
85   asection *sec;
86   filename = fn;
87 
88   flen = (rc_uint_type) get_file_size (filename);
89   if (! flen)
90     fatal ("can't open '%s' for input.", filename);
91   abfd = windres_open_as_binary (filename, 1);
92   sec = bfd_get_section_by_name (abfd, ".data");
93   if (sec == NULL)
94     bfd_fatal ("bfd_get_section_by_name");
95   set_windres_bfd (&wrbfd, abfd, sec,
96 		   (target_is_bigendian ? WR_KIND_BFD_BIN_B
97 					: WR_KIND_BFD_BIN_L));
98   off = 0;
99 
100   if (! probe_binary (&wrbfd, flen))
101     set_windres_bfd_endianness (&wrbfd, ! target_is_bigendian);
102 
103   skip_null_resource (&wrbfd, &off, flen);
104 
105   while (read_resource_entry (&wrbfd, &off, flen))
106     ;
107 
108   bfd_close (abfd);
109 
110   return resources;
111 }
112 
113 /* Write resource file */
114 void
115 write_res_file (const char *fn,const rc_res_directory *resdir)
116 {
117   asection *sec;
118   rc_uint_type language;
119   bfd *abfd;
120   windres_bfd wrbfd;
121   unsigned long sec_length = 0,sec_length_wrote;
122   static const bfd_byte sign[] =
123   {0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
124    0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
125    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
127 
128   filename = fn;
129 
130   abfd = windres_open_as_binary (filename, 0);
131   sec = bfd_make_section_with_flags (abfd, ".data",
132 				     (SEC_HAS_CONTENTS | SEC_ALLOC
133 				      | SEC_LOAD | SEC_DATA));
134   if (sec == NULL)
135     bfd_fatal ("bfd_make_section");
136   /* Requiring this is probably a bug in BFD.  */
137   sec->output_section = sec;
138 
139   set_windres_bfd (&wrbfd, abfd, sec,
140 		   (target_is_bigendian ? WR_KIND_BFD_BIN_B
141 					: WR_KIND_BFD_BIN_L));
142 
143   language = -1;
144   sec_length = write_res_directory ((windres_bfd *) NULL, 0x20UL, resdir,
145 				    (const rc_res_id *) NULL,
146 				    (const rc_res_id *) NULL, &language, 1);
147   if (! bfd_set_section_size (abfd, sec, (sec_length + 3) & ~3))
148     bfd_fatal ("bfd_set_section_size");
149   if ((sec_length & 3) != 0)
150     set_windres_bfd_content (&wrbfd, sign, sec_length, 4-(sec_length & 3));
151   set_windres_bfd_content (&wrbfd, sign, 0, sizeof (sign));
152   language = -1;
153   sec_length_wrote = write_res_directory (&wrbfd, 0x20UL, resdir,
154 					  (const rc_res_id *) NULL,
155 					  (const rc_res_id *) NULL,
156 					  &language, 1);
157   if (sec_length != sec_length_wrote)
158     fatal ("res write failed with different sizes (%lu/%lu).",
159 	   (unsigned long) sec_length, (unsigned long) sec_length_wrote);
160 
161   bfd_close (abfd);
162   return;
163 }
164 
165 /* Read a resource entry, returns 0 when all resources are read */
166 static int
167 read_resource_entry (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
168 {
169   rc_res_id type;
170   rc_res_id name;
171   rc_res_res_info resinfo;
172   res_hdr reshdr;
173   void *buff;
174 
175   rc_res_resource *r;
176   struct bin_res_info l;
177 
178   off[0] = (off[0] + 3) & ~3;
179 
180   /* Read header */
181   if ((off[0] + 8) > omax)
182     return 0;
183   read_res_data_hdr (wrbfd, off, omax, &reshdr);
184 
185   /* read resource type */
186   read_res_id (wrbfd, off, omax, &type);
187   /* read resource id */
188   read_res_id (wrbfd, off, omax, &name);
189 
190   off[0] = (off[0] + 3) & ~3;
191 
192   /* Read additional resource header */
193   read_res_data (wrbfd, off, omax, &l, BIN_RES_INFO_SIZE);
194   resinfo.version = windres_get_32 (wrbfd, l.version, 4);
195   resinfo.memflags = windres_get_16 (wrbfd, l.memflags, 2);
196   resinfo.language = windres_get_16 (wrbfd, l.language, 2);
197   /* resinfo.version2 = windres_get_32 (wrbfd, l.version2, 4); */
198   resinfo.characteristics = windres_get_32 (wrbfd, l.characteristics, 4);
199 
200   off[0] = (off[0] + 3) & ~3;
201 
202   /* Allocate buffer for data */
203   buff = res_alloc (reshdr.data_size);
204   /* Read data */
205   read_res_data (wrbfd, off, omax, buff, reshdr.data_size);
206   /* Convert binary data to resource */
207   r = bin_to_res (wrbfd, type, buff, reshdr.data_size);
208   r->res_info = resinfo;
209   /* Add resource to resource directory */
210   res_add_resource (r, &type, &name, resinfo.language, 0);
211 
212   return 1;
213 }
214 
215 /* write resource directory to binary resource file */
216 static rc_uint_type
217 write_res_directory (windres_bfd *wrbfd, rc_uint_type off, const rc_res_directory *rd,
218 		     const rc_res_id *type, const rc_res_id *name, rc_uint_type *language,
219 		     int level)
220 {
221   const rc_res_entry *re;
222 
223   for (re = rd->entries; re != NULL; re = re->next)
224     {
225       switch (level)
226 	{
227 	case 1:
228 	  /* If we're at level 1, the key of this resource is the
229 	     type.  This normally duplicates the information we have
230 	     stored with the resource itself, but we need to remember
231 	     the type if this is a user define resource type.  */
232 	  type = &re->id;
233 	  break;
234 
235 	case 2:
236 	  /* If we're at level 2, the key of this resource is the name
237 	     we are going to use in the rc printout.  */
238 	  name = &re->id;
239 	  break;
240 
241 	case 3:
242 	  /* If we're at level 3, then this key represents a language.
243 	     Use it to update the current language.  */
244 	  if (! re->id.named
245 	      && re->id.u.id != (unsigned long) *language
246 	      && (re->id.u.id & 0xffff) == re->id.u.id)
247 	    {
248 	      *language = re->id.u.id;
249 	    }
250 	  break;
251 
252 	default:
253 	  break;
254 	}
255 
256       if (re->subdir)
257 	off = write_res_directory (wrbfd, off, re->u.dir, type, name, language,
258 				   level + 1);
259       else
260 	{
261 	  if (level == 3)
262 	    {
263 	      /* This is the normal case: the three levels are
264 	         TYPE/NAME/LANGUAGE.  NAME will have been set at level
265 	         2, and represents the name to use.  We probably just
266 	         set LANGUAGE, and it will probably match what the
267 	         resource itself records if anything.  */
268 	      off = write_res_resource (wrbfd, off, type, name, re->u.res,
269 	      				language);
270 	    }
271 	  else
272 	    {
273 	      fprintf (stderr, "// Resource at unexpected level %d\n", level);
274 	      off = write_res_resource (wrbfd, off, type, (rc_res_id *) NULL,
275 	      				re->u.res, language);
276 	    }
277 	}
278     }
279 
280   return off;
281 }
282 
283 static rc_uint_type
284 write_res_resource (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *type,
285 		    const rc_res_id *name, const rc_res_resource *res,
286 		    rc_uint_type *language ATTRIBUTE_UNUSED)
287 {
288   int rt;
289 
290   switch (res->type)
291     {
292     default:
293       abort ();
294 
295     case RES_TYPE_ACCELERATOR:
296       rt = RT_ACCELERATOR;
297       break;
298 
299     case RES_TYPE_BITMAP:
300       rt = RT_BITMAP;
301       break;
302 
303     case RES_TYPE_CURSOR:
304       rt = RT_CURSOR;
305       break;
306 
307     case RES_TYPE_GROUP_CURSOR:
308       rt = RT_GROUP_CURSOR;
309       break;
310 
311     case RES_TYPE_DIALOG:
312       rt = RT_DIALOG;
313       break;
314 
315     case RES_TYPE_FONT:
316       rt = RT_FONT;
317       break;
318 
319     case RES_TYPE_FONTDIR:
320       rt = RT_FONTDIR;
321       break;
322 
323     case RES_TYPE_ICON:
324       rt = RT_ICON;
325       break;
326 
327     case RES_TYPE_GROUP_ICON:
328       rt = RT_GROUP_ICON;
329       break;
330 
331     case RES_TYPE_MENU:
332       rt = RT_MENU;
333       break;
334 
335     case RES_TYPE_MESSAGETABLE:
336       rt = RT_MESSAGETABLE;
337       break;
338 
339     case RES_TYPE_RCDATA:
340       rt = RT_RCDATA;
341       break;
342 
343     case RES_TYPE_STRINGTABLE:
344       rt = RT_STRING;
345       break;
346 
347     case RES_TYPE_USERDATA:
348       rt = 0;
349       break;
350 
351     case RES_TYPE_VERSIONINFO:
352       rt = RT_VERSION;
353       break;
354 
355     case RES_TYPE_TOOLBAR:
356       rt = RT_TOOLBAR;
357       break;
358     }
359 
360   if (rt != 0
361       && type != NULL
362       && (type->named || type->u.id != (unsigned long) rt))
363     {
364       fprintf (stderr, "// Unexpected resource type mismatch: ");
365       res_id_print (stderr, *type, 1);
366       fprintf (stderr, " != %d", rt);
367       abort ();
368     }
369 
370   return write_res_bin (wrbfd, off, res, type, name, &res->res_info);
371 }
372 
373 /* Write a resource in binary resource format */
374 static rc_uint_type
375 write_res_bin (windres_bfd *wrbfd, rc_uint_type off, const rc_res_resource *res,
376 	       const rc_res_id *type, const rc_res_id *name,
377 	       const rc_res_res_info *resinfo)
378 {
379   rc_uint_type noff;
380   rc_uint_type datasize = 0;
381 
382   noff = res_to_bin ((windres_bfd *) NULL, off, res);
383   datasize = noff - off;
384 
385   off = write_res_header (wrbfd, off, datasize, type, name, resinfo);
386   return res_to_bin (wrbfd, off, res);
387 }
388 
389 /* Get number of bytes needed to store an id in binary format */
390 static unsigned long
391 get_id_size (id)
392      const rc_res_id *id;
393 {
394   if (id->named)
395     return sizeof (unichar) * (id->u.n.length + 1);
396   else
397     return sizeof (unichar) * 2;
398 }
399 
400 /* Write a resource header */
401 static rc_uint_type
402 write_res_header (windres_bfd *wrbfd, rc_uint_type off, rc_uint_type datasize,
403 		  const rc_res_id *type, const rc_res_id *name,
404 		  const rc_res_res_info *resinfo)
405 {
406   res_hdr reshdr;
407   reshdr.data_size = datasize;
408   reshdr.header_size = 24 + get_id_size (type) + get_id_size (name);
409 
410   reshdr.header_size = (reshdr.header_size + 3) & ~3;
411 
412   off = (off + 3) & ~3;
413 
414   off = write_res_data_hdr (wrbfd, off, &reshdr);
415   off = write_res_id (wrbfd, off, type);
416   off = write_res_id (wrbfd, off, name);
417 
418   off = (off + 3) & ~3;
419 
420   off = write_res_info (wrbfd, off, resinfo);
421   off = (off + 3) & ~3;
422   return off;
423 }
424 
425 static rc_uint_type
426 write_res_data_hdr (windres_bfd *wrbfd, rc_uint_type off, res_hdr *hdr)
427 {
428   if (wrbfd)
429     {
430       struct bin_res_hdr brh;
431       windres_put_32 (wrbfd, brh.data_size, hdr->data_size);
432       windres_put_32 (wrbfd, brh.header_size, hdr->header_size);
433       set_windres_bfd_content (wrbfd, &brh, off, BIN_RES_HDR_SIZE);
434     }
435   return off + BIN_RES_HDR_SIZE;
436 }
437 
438 static void
439 read_res_data_hdr (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
440 		   res_hdr *reshdr)
441 {
442   struct bin_res_hdr brh;
443 
444   if ((off[0] + BIN_RES_HDR_SIZE) > omax)
445     fatal ("%s: unexpected end of file %ld/%ld", filename,(long) off[0], (long) omax);
446 
447   get_windres_bfd_content (wrbfd, &brh, off[0], BIN_RES_HDR_SIZE);
448   reshdr->data_size = windres_get_32 (wrbfd, brh.data_size, 4);
449   reshdr->header_size = windres_get_32 (wrbfd, brh.header_size, 4);
450   off[0] += BIN_RES_HDR_SIZE;
451 }
452 
453 /* Read data from file, abort on failure */
454 static void
455 read_res_data (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, void *data,
456 	       rc_uint_type size)
457 {
458   if ((off[0] + size) > omax)
459     fatal ("%s: unexpected end of file %ld/%ld %ld", filename,(long) off[0],
460     	   (long) omax, (long) size);
461   get_windres_bfd_content (wrbfd, data, off[0], size);
462   off[0] += size;
463 }
464 
465 /* Write a resource id */
466 static rc_uint_type
467 write_res_id (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *id)
468 {
469   if (id->named)
470     {
471       rc_uint_type len = (((bfd_signed_vma) id->u.n.length < 0 ? 0 : id->u.n.length) + 1);
472       if (wrbfd)
473 	{
474 	  rc_uint_type i;
475 	  bfd_byte *d = (bfd_byte *) xmalloc (len * sizeof (unichar));
476 	  for (i = 0; i < (len - 1); i++)
477 	    windres_put_16 (wrbfd, d + (i * sizeof (unichar)), id->u.n.name[i]);
478 	  windres_put_16 (wrbfd, d + (i * sizeof (unichar)), 0);
479 	  set_windres_bfd_content (wrbfd, d, off, (len * sizeof (unichar)));
480 	}
481       off += (len * sizeof (unichar));
482     }
483   else
484     {
485       if (wrbfd)
486 	{
487 	  struct bin_res_id bid;
488 	  windres_put_16 (wrbfd, bid.sig, 0xffff);
489 	  windres_put_16 (wrbfd, bid.id, id->u.id);
490 	  set_windres_bfd_content (wrbfd, &bid, off, BIN_RES_ID);
491 	}
492       off += BIN_RES_ID;
493     }
494   return off;
495 }
496 
497 /* Write resource info */
498 static rc_uint_type
499 write_res_info (windres_bfd *wrbfd, rc_uint_type off, const rc_res_res_info *info)
500 {
501   if (wrbfd)
502     {
503       struct bin_res_info l;
504 
505       windres_put_32 (wrbfd, l.version, info->version);
506       windres_put_16 (wrbfd, l.memflags, info->memflags);
507       windres_put_16 (wrbfd, l.language, info->language);
508       windres_put_32 (wrbfd, l.version2, info->version);
509       windres_put_32 (wrbfd, l.characteristics, info->characteristics);
510       set_windres_bfd_content (wrbfd, &l, off, BIN_RES_INFO_SIZE);
511     }
512   return off + BIN_RES_INFO_SIZE;
513 }
514 
515 /* read a resource identifier */
516 static void
517 read_res_id (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, rc_res_id *id)
518 {
519   struct bin_res_id bid;
520   unsigned short ord;
521   unichar *id_s = NULL;
522   rc_uint_type len;
523 
524   read_res_data (wrbfd, off, omax, &bid, BIN_RES_ID - 2);
525   ord = (unsigned short) windres_get_16 (wrbfd, bid.sig, 2);
526   if (ord == 0xFFFF)		/* an ordinal id */
527     {
528       read_res_data (wrbfd, off, omax, bid.id, BIN_RES_ID - 2);
529       id->named = 0;
530       id->u.id = windres_get_16 (wrbfd, bid.id, 2);
531     }
532   else
533     /* named id */
534     {
535       off[0] -= 2;
536       id_s = read_unistring (wrbfd, off, omax, &len);
537       id->named = 1;
538       id->u.n.length = len;
539       id->u.n.name = id_s;
540     }
541 }
542 
543 /* Read a null terminated UNICODE string */
544 static unichar *
545 read_unistring (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
546 		rc_uint_type *len)
547 {
548   unichar *s;
549   bfd_byte d[2];
550   unichar c;
551   unichar *p;
552   rc_uint_type l;
553   rc_uint_type soff = off[0];
554 
555   do
556     {
557       read_res_data (wrbfd, &soff, omax, d, sizeof (unichar));
558       c = windres_get_16 (wrbfd, d, 2);
559     }
560   while (c != 0);
561   l = ((soff - off[0]) / sizeof (unichar));
562 
563   /* there are hardly any names longer than 256 characters, but anyway. */
564   p = s = (unichar *) xmalloc (sizeof (unichar) * l);
565   do
566     {
567       read_res_data (wrbfd, off, omax, d, sizeof (unichar));
568       c = windres_get_16 (wrbfd, d, 2);
569       *p++ = c;
570     }
571   while (c != 0);
572   *len = l - 1;
573   return s;
574 }
575 
576 static int
577 probe_binary (windres_bfd *wrbfd, rc_uint_type omax)
578 {
579   rc_uint_type off;
580   res_hdr reshdr;
581 
582   off = 0;
583   read_res_data_hdr (wrbfd, &off, omax, &reshdr);
584   if (reshdr.data_size != 0)
585     return 1;
586   if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
587       || (reshdr.header_size != 0x20000000 && target_is_bigendian))
588     return 1;
589 
590   /* Subtract size of HeaderSize. DataSize has to be zero. */
591   off += 0x20 - BIN_RES_HDR_SIZE;
592   if ((off + BIN_RES_HDR_SIZE) >= omax)
593     return 1;
594   read_res_data_hdr (wrbfd, &off, omax, &reshdr);
595   /* off is advanced by BIN_RES_HDR_SIZE in read_res_data_hdr()
596      which is part of reshdr.header_size. We shouldn't take it
597      into account twice.  */
598   if ((off - BIN_RES_HDR_SIZE + reshdr.data_size + reshdr.header_size) > omax)
599     return 0;
600   return 1;
601 }
602 
603 /* Check if file is a win32 binary resource file, if so
604    skip past the null resource. Returns 0 if successful, -1 on
605    error.
606  */
607 static void
608 skip_null_resource (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
609 {
610   res_hdr reshdr;
611   read_res_data_hdr (wrbfd, off, omax, &reshdr);
612   if (reshdr.data_size != 0)
613     goto skip_err;
614   if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
615     || (reshdr.header_size != 0x20000000 && target_is_bigendian))
616     goto skip_err;
617 
618   /* Subtract size of HeaderSize. DataSize has to be zero. */
619   off[0] += 0x20 - BIN_RES_HDR_SIZE;
620   if (off[0] >= omax)
621     goto skip_err;
622 
623   return;
624 
625 skip_err:
626   fprintf (stderr, "%s: %s: Not a valid WIN32 resource file\n", program_name,
627 	   filename);
628   xexit (1);
629 }
630 
631 /* Add a resource to resource directory */
632 static void
633 res_add_resource (rc_res_resource *r, const rc_res_id *type, const rc_res_id *id,
634 		  rc_uint_type language, int dupok)
635 {
636   rc_res_id a[3];
637 
638   a[0] = *type;
639   a[1] = *id;
640   a[2].named = 0;
641   a[2].u.id = language;
642   res_append_resource (&resources, r, 3, a, dupok);
643 }
644 
645 /* Append a resource to resource directory.
646    This is just copied from define_resource
647    and modified to add an existing resource.
648  */
649 static void
650 res_append_resource (rc_res_directory **res_dirs, rc_res_resource *resource,
651 		     int cids, const rc_res_id *ids, int dupok)
652 {
653   rc_res_entry *re = NULL;
654   int i;
655 
656   assert (cids > 0);
657   for (i = 0; i < cids; i++)
658     {
659       rc_res_entry **pp;
660 
661       if (*res_dirs == NULL)
662 	{
663 	  static unsigned long timeval;
664 
665 	  /* Use the same timestamp for every resource created in a
666 	     single run.  */
667 	  if (timeval == 0)
668 	    timeval = time (NULL);
669 
670 	  *res_dirs = ((rc_res_directory *)
671 			res_alloc (sizeof (rc_res_directory)));
672 	  (*res_dirs)->characteristics = 0;
673 	  (*res_dirs)->time = timeval;
674 	  (*res_dirs)->major = 0;
675 	  (*res_dirs)->minor = 0;
676 	  (*res_dirs)->entries = NULL;
677 	}
678 
679       for (pp = &(*res_dirs)->entries; *pp != NULL; pp = &(*pp)->next)
680 	if (res_id_cmp ((*pp)->id, ids[i]) == 0)
681 	  break;
682 
683       if (*pp != NULL)
684 	re = *pp;
685       else
686 	{
687 	  re = (rc_res_entry *) res_alloc (sizeof (rc_res_entry));
688 	  re->next = NULL;
689 	  re->id = ids[i];
690 	  if ((i + 1) < cids)
691 	    {
692 	      re->subdir = 1;
693 	      re->u.dir = NULL;
694 	    }
695 	  else
696 	    {
697 	      re->subdir = 0;
698 	      re->u.res = NULL;
699 	    }
700 
701 	  *pp = re;
702 	}
703 
704       if ((i + 1) < cids)
705 	{
706 	  if (! re->subdir)
707 	    {
708 	      fprintf (stderr, "%s: ", program_name);
709 	      res_ids_print (stderr, i, ids);
710 	      fprintf (stderr, ": expected to be a directory\n");
711 	      xexit (1);
712 	    }
713 
714 	  res_dirs = &re->u.dir;
715 	}
716     }
717 
718   if (re->subdir)
719     {
720       fprintf (stderr, "%s: ", program_name);
721       res_ids_print (stderr, cids, ids);
722       fprintf (stderr, ": expected to be a leaf\n");
723       xexit (1);
724     }
725 
726   if (re->u.res != NULL)
727     {
728       if (dupok)
729 	return;
730 
731       fprintf (stderr, "%s: warning: ", program_name);
732       res_ids_print (stderr, cids, ids);
733       fprintf (stderr, ": duplicate value\n");
734     }
735 
736   re->u.res = resource;
737 }
738