bfd/
[platform/upstream/binutils.git] / binutils / resres.c
1 /* resres.c: read_res_file and write_res_file implementation for windres.
2    Copyright 1998, 1999, 2001, 2002, 2007
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 2 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_endianess (&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 (abfd, ".data");
132   if (sec == NULL)
133     bfd_fatal ("bfd_make_section");
134   if (! bfd_set_section_flags (abfd, sec,
135                                (SEC_HAS_CONTENTS | SEC_ALLOC
136                                 | SEC_LOAD | SEC_DATA)))
137     bfd_fatal ("bfd_set_section_flags");
138   /* Requiring this is probably a bug in BFD.  */
139   sec->output_section = sec;
140
141   set_windres_bfd (&wrbfd, abfd, sec,
142                    (target_is_bigendian ? WR_KIND_BFD_BIN_B
143                                         : WR_KIND_BFD_BIN_L));
144
145   language = -1;
146   sec_length = write_res_directory ((windres_bfd *) NULL, 0x20UL, resdir,
147                                     (const rc_res_id *) NULL,
148                                     (const rc_res_id *) NULL, &language, 1);
149   if (! bfd_set_section_size (abfd, sec, (sec_length + 3) & ~3))
150     bfd_fatal ("bfd_set_section_size");
151   if ((sec_length & 3) != 0)
152     set_windres_bfd_content (&wrbfd, sign, sec_length, 4-(sec_length & 3));
153   set_windres_bfd_content (&wrbfd, sign, 0, sizeof (sign));
154   language = -1;
155   sec_length_wrote = write_res_directory (&wrbfd, 0x20UL, resdir,
156                                           (const rc_res_id *) NULL,
157                                           (const rc_res_id *) NULL,
158                                           &language, 1);
159   if (sec_length != sec_length_wrote)
160     fatal ("res write failed with different sizes (%lu/%lu).", (long) sec_length,
161            (long) sec_length_wrote);
162
163   bfd_close (abfd);
164   return;
165 }
166
167 /* Read a resource entry, returns 0 when all resources are read */
168 static int
169 read_resource_entry (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
170 {
171   rc_res_id type;
172   rc_res_id name;
173   rc_res_res_info resinfo;
174   res_hdr reshdr;
175   void *buff;
176
177   rc_res_resource *r;
178   struct bin_res_info l;
179
180   off[0] = (off[0] + 3) & ~3;
181
182   /* Read header */
183   if ((off[0] + 8) > omax)
184     return 0;
185   read_res_data_hdr (wrbfd, off, omax, &reshdr);
186
187   /* read resource type */
188   read_res_id (wrbfd, off, omax, &type);
189   /* read resource id */
190   read_res_id (wrbfd, off, omax, &name);
191
192   off[0] = (off[0] + 3) & ~3;
193
194   /* Read additional resource header */
195   read_res_data (wrbfd, off, omax, &l, BIN_RES_INFO_SIZE);
196   resinfo.version = windres_get_32 (wrbfd, l.version, 4);
197   resinfo.memflags = windres_get_16 (wrbfd, l.memflags, 2);
198   resinfo.language = windres_get_16 (wrbfd, l.language, 2);
199   /* resinfo.version2 = windres_get_32 (wrbfd, l.version2, 4); */
200   resinfo.characteristics = windres_get_32 (wrbfd, l.characteristics, 4);
201
202   off[0] = (off[0] + 3) & ~3;
203
204   /* Allocate buffer for data */
205   buff = res_alloc (reshdr.data_size);
206   /* Read data */
207   read_res_data (wrbfd, off, omax, buff, reshdr.data_size);
208   /* Convert binary data to resource */
209   r = bin_to_res (wrbfd, type, buff, reshdr.data_size);
210   r->res_info = resinfo;
211   /* Add resource to resource directory */
212   res_add_resource (r, &type, &name, resinfo.language, 0);
213
214   return 1;
215 }
216
217 /* write resource directory to binary resource file */
218 static rc_uint_type
219 write_res_directory (windres_bfd *wrbfd, rc_uint_type off, const rc_res_directory *rd,
220                      const rc_res_id *type, const rc_res_id *name, rc_uint_type *language,
221                      int level)
222 {
223   const rc_res_entry *re;
224
225   for (re = rd->entries; re != NULL; re = re->next)
226     {
227       switch (level)
228         {
229         case 1:
230           /* If we're at level 1, the key of this resource is the
231              type.  This normally duplicates the information we have
232              stored with the resource itself, but we need to remember
233              the type if this is a user define resource type.  */
234           type = &re->id;
235           break;
236
237         case 2:
238           /* If we're at level 2, the key of this resource is the name
239              we are going to use in the rc printout.  */
240           name = &re->id;
241           break;
242
243         case 3:
244           /* If we're at level 3, then this key represents a language.
245              Use it to update the current language.  */
246           if (! re->id.named
247               && re->id.u.id != (unsigned long) *language
248               && (re->id.u.id & 0xffff) == re->id.u.id)
249             {
250               *language = re->id.u.id;
251             }
252           break;
253
254         default:
255           break;
256         }
257
258       if (re->subdir)
259         off = write_res_directory (wrbfd, off, re->u.dir, type, name, language,
260                                    level + 1);
261       else
262         {
263           if (level == 3)
264             {
265               /* This is the normal case: the three levels are
266                  TYPE/NAME/LANGUAGE.  NAME will have been set at level
267                  2, and represents the name to use.  We probably just
268                  set LANGUAGE, and it will probably match what the
269                  resource itself records if anything.  */
270               off = write_res_resource (wrbfd, off, type, name, re->u.res,
271                                         language);
272             }
273           else
274             {
275               fprintf (stderr, "// Resource at unexpected level %d\n", level);
276               off = write_res_resource (wrbfd, off, type, (rc_res_id *) NULL,
277                                         re->u.res, language);
278             }
279         }
280     }
281
282   return off;
283 }
284
285 static rc_uint_type
286 write_res_resource (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *type,
287                     const rc_res_id *name, const rc_res_resource *res,
288                     rc_uint_type *language ATTRIBUTE_UNUSED)
289 {
290   int rt;
291
292   switch (res->type)
293     {
294     default:
295       abort ();
296
297     case RES_TYPE_ACCELERATOR:
298       rt = RT_ACCELERATOR;
299       break;
300
301     case RES_TYPE_BITMAP:
302       rt = RT_BITMAP;
303       break;
304
305     case RES_TYPE_CURSOR:
306       rt = RT_CURSOR;
307       break;
308
309     case RES_TYPE_GROUP_CURSOR:
310       rt = RT_GROUP_CURSOR;
311       break;
312
313     case RES_TYPE_DIALOG:
314       rt = RT_DIALOG;
315       break;
316
317     case RES_TYPE_FONT:
318       rt = RT_FONT;
319       break;
320
321     case RES_TYPE_FONTDIR:
322       rt = RT_FONTDIR;
323       break;
324
325     case RES_TYPE_ICON:
326       rt = RT_ICON;
327       break;
328
329     case RES_TYPE_GROUP_ICON:
330       rt = RT_GROUP_ICON;
331       break;
332
333     case RES_TYPE_MENU:
334       rt = RT_MENU;
335       break;
336
337     case RES_TYPE_MESSAGETABLE:
338       rt = RT_MESSAGETABLE;
339       break;
340
341     case RES_TYPE_RCDATA:
342       rt = RT_RCDATA;
343       break;
344
345     case RES_TYPE_STRINGTABLE:
346       rt = RT_STRING;
347       break;
348
349     case RES_TYPE_USERDATA:
350       rt = 0;
351       break;
352
353     case RES_TYPE_VERSIONINFO:
354       rt = RT_VERSION;
355       break;
356
357     case RES_TYPE_TOOLBAR:
358       rt = RT_TOOLBAR;
359       break;
360     }
361
362   if (rt != 0
363       && type != NULL
364       && (type->named || type->u.id != (unsigned long) rt))
365     {
366       fprintf (stderr, "// Unexpected resource type mismatch: ");
367       res_id_print (stderr, *type, 1);
368       fprintf (stderr, " != %d", rt);
369       abort ();
370     }
371
372   return write_res_bin (wrbfd, off, res, type, name, &res->res_info);
373 }
374
375 /* Write a resource in binary resource format */
376 static rc_uint_type
377 write_res_bin (windres_bfd *wrbfd, rc_uint_type off, const rc_res_resource *res,
378                const rc_res_id *type, const rc_res_id *name,
379                const rc_res_res_info *resinfo)
380 {
381   rc_uint_type noff;
382   rc_uint_type datasize = 0;
383
384   noff = res_to_bin ((windres_bfd *) NULL, off, res);
385   datasize = noff - off;
386
387   off = write_res_header (wrbfd, off, datasize, type, name, resinfo);
388   return res_to_bin (wrbfd, off, res);
389 }
390
391 /* Get number of bytes needed to store an id in binary format */
392 static unsigned long
393 get_id_size (id)
394      const rc_res_id *id;
395 {
396   if (id->named)
397     return sizeof (unichar) * (id->u.n.length + 1);
398   else
399     return sizeof (unichar) * 2;
400 }
401
402 /* Write a resource header */
403 static rc_uint_type
404 write_res_header (windres_bfd *wrbfd, rc_uint_type off, rc_uint_type datasize,
405                   const rc_res_id *type, const rc_res_id *name,
406                   const rc_res_res_info *resinfo)
407 {
408   res_hdr reshdr;
409   reshdr.data_size = datasize;
410   reshdr.header_size = 24 + get_id_size (type) + get_id_size (name);
411
412   reshdr.header_size = (reshdr.header_size + 3) & ~3;
413
414   off = (off + 3) & ~3;
415
416   off = write_res_data_hdr (wrbfd, off, &reshdr);
417   off = write_res_id (wrbfd, off, type);
418   off = write_res_id (wrbfd, off, name);
419
420   off = (off + 3) & ~3;
421
422   off = write_res_info (wrbfd, off, resinfo);
423   off = (off + 3) & ~3;
424   return off;
425 }
426
427 static rc_uint_type
428 write_res_data_hdr (windres_bfd *wrbfd, rc_uint_type off, res_hdr *hdr)
429 {
430   if (wrbfd)
431     {
432       struct bin_res_hdr brh;
433       windres_put_32 (wrbfd, brh.data_size, hdr->data_size);
434       windres_put_32 (wrbfd, brh.header_size, hdr->header_size);
435       set_windres_bfd_content (wrbfd, &brh, off, BIN_RES_HDR_SIZE);
436     }
437   return off + BIN_RES_HDR_SIZE;
438 }
439
440 static void
441 read_res_data_hdr (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
442                    res_hdr *reshdr)
443 {
444   struct bin_res_hdr brh;
445
446   if ((off[0] + BIN_RES_HDR_SIZE) > omax)
447     fatal ("%s: unexpected end of file %ld/%ld", filename,(long) off[0], (long) omax);
448
449   get_windres_bfd_content (wrbfd, &brh, off[0], BIN_RES_HDR_SIZE);
450   reshdr->data_size = windres_get_32 (wrbfd, brh.data_size, 4);
451   reshdr->header_size = windres_get_32 (wrbfd, brh.header_size, 4);
452   off[0] += BIN_RES_HDR_SIZE;
453 }
454
455 /* Read data from file, abort on failure */
456 static void
457 read_res_data (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, void *data,
458                rc_uint_type size)
459 {
460   if ((off[0] + size) > omax)
461     fatal ("%s: unexpected end of file %ld/%ld %ld", filename,(long) off[0],
462            (long) omax, (long) size);
463   get_windres_bfd_content (wrbfd, data, off[0], size);
464   off[0] += size;
465 }
466
467 /* Write a resource id */
468 static rc_uint_type
469 write_res_id (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *id)
470 {
471   if (id->named)
472     {
473       rc_uint_type len = (((bfd_signed_vma) id->u.n.length < 0 ? 0 : id->u.n.length) + 1);
474       if (wrbfd)
475         {
476           rc_uint_type i;
477           bfd_byte *d = (bfd_byte *) xmalloc (len * sizeof (unichar));
478           for (i = 0; i < (len - 1); i++)
479             windres_put_16 (wrbfd, d + (i * sizeof (unichar)), id->u.n.name[i]);
480           windres_put_16 (wrbfd, d + (i * sizeof (unichar)), 0);
481           set_windres_bfd_content (wrbfd, d, off, (len * sizeof (unichar)));
482         }
483       off += (len * sizeof (unichar));
484     }
485   else
486     {
487       if (wrbfd)
488         {
489           struct bin_res_id bid;
490           windres_put_16 (wrbfd, bid.sig, 0xffff);
491           windres_put_16 (wrbfd, bid.id, id->u.id);
492           set_windres_bfd_content (wrbfd, &bid, off, BIN_RES_ID);
493         }
494       off += BIN_RES_ID;
495     }
496   return off;
497 }
498
499 /* Write resource info */
500 static rc_uint_type
501 write_res_info (windres_bfd *wrbfd, rc_uint_type off, const rc_res_res_info *info)
502 {
503   if (wrbfd)
504     {
505       struct bin_res_info l;
506       
507       windres_put_32 (wrbfd, l.version, info->version);
508       windres_put_16 (wrbfd, l.memflags, info->memflags);
509       windres_put_16 (wrbfd, l.language, info->language);
510       windres_put_32 (wrbfd, l.version2, info->version);
511       windres_put_32 (wrbfd, l.characteristics, info->characteristics);
512       set_windres_bfd_content (wrbfd, &l, off, BIN_RES_INFO_SIZE);
513     }
514   return off + BIN_RES_INFO_SIZE;
515 }
516
517 /* read a resource identifier */
518 static void
519 read_res_id (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, rc_res_id *id)
520 {
521   struct bin_res_id bid;
522   unsigned short ord;
523   unichar *id_s = NULL;
524   rc_uint_type len;
525
526   read_res_data (wrbfd, off, omax, &bid, BIN_RES_ID - 2);
527   ord = (unsigned short) windres_get_16 (wrbfd, bid.sig, 2);
528   if (ord == 0xFFFF)            /* an ordinal id */
529     {
530       read_res_data (wrbfd, off, omax, bid.id, BIN_RES_ID - 2);
531       id->named = 0;
532       id->u.id = windres_get_16 (wrbfd, bid.id, 2);
533     }
534   else
535     /* named id */
536     {
537       off[0] -= 2;
538       id_s = read_unistring (wrbfd, off, omax, &len);
539       id->named = 1;
540       id->u.n.length = len;
541       id->u.n.name = id_s;
542     }
543 }
544
545 /* Read a null terminated UNICODE string */
546 static unichar *
547 read_unistring (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
548                 rc_uint_type *len)
549 {
550   unichar *s;
551   bfd_byte d[2];
552   unichar c;
553   unichar *p;
554   rc_uint_type l;
555   rc_uint_type soff = off[0];
556
557   do
558     {
559       read_res_data (wrbfd, &soff, omax, d, sizeof (unichar));
560       c = windres_get_16 (wrbfd, d, 2);
561     }
562   while (c != 0);
563   l = ((soff - off[0]) / sizeof (unichar));
564
565   /* there are hardly any names longer than 256 characters, but anyway. */
566   p = s = (unichar *) xmalloc (sizeof (unichar) * l);
567   do
568     {
569       read_res_data (wrbfd, off, omax, d, sizeof (unichar));
570       c = windres_get_16 (wrbfd, d, 2);
571       *p++ = c;
572     }
573   while (c != 0);
574   *len = l - 1;
575   return s;
576 }
577
578 static int
579 probe_binary (windres_bfd *wrbfd, rc_uint_type omax)
580 {
581   rc_uint_type off;
582   res_hdr reshdr;
583
584   off = 0;
585   read_res_data_hdr (wrbfd, &off, omax, &reshdr);
586   if (reshdr.data_size != 0)
587     return 1;
588   if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
589       || (reshdr.header_size != 0x20000000 && target_is_bigendian))
590     return 1;
591
592   /* Subtract size of HeaderSize. DataSize has to be zero. */
593   off += 0x20 - BIN_RES_HDR_SIZE;
594   if ((off + BIN_RES_HDR_SIZE) >= omax)
595     return 1;
596   read_res_data_hdr (wrbfd, &off, omax, &reshdr);
597   /* off is advanced by BIN_RES_HDR_SIZE in read_res_data_hdr()
598      which is part of reshdr.header_size. We shouldn't take it
599      into account twice.  */
600   if ((off - BIN_RES_HDR_SIZE + reshdr.data_size + reshdr.header_size) > omax)
601     return 0;
602   return 1;
603 }
604
605 /* Check if file is a win32 binary resource file, if so
606    skip past the null resource. Returns 0 if successful, -1 on
607    error.
608  */
609 static void
610 skip_null_resource (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
611 {
612   res_hdr reshdr;
613   read_res_data_hdr (wrbfd, off, omax, &reshdr);
614   if (reshdr.data_size != 0)
615     goto skip_err;
616   if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
617     || (reshdr.header_size != 0x20000000 && target_is_bigendian))
618     goto skip_err;
619
620   /* Subtract size of HeaderSize. DataSize has to be zero. */
621   off[0] += 0x20 - BIN_RES_HDR_SIZE;
622   if (off[0] >= omax)
623     goto skip_err;
624
625   return;
626
627 skip_err:
628   fprintf (stderr, "%s: %s: Not a valid WIN32 resource file\n", program_name,
629            filename);
630   xexit (1);
631 }
632
633 /* Add a resource to resource directory */
634 static void
635 res_add_resource (rc_res_resource *r, const rc_res_id *type, const rc_res_id *id,
636                   rc_uint_type language, int dupok)
637 {
638   rc_res_id a[3];
639
640   a[0] = *type;
641   a[1] = *id;
642   a[2].named = 0;
643   a[2].u.id = language;
644   res_append_resource (&resources, r, 3, a, dupok);
645 }
646
647 /* Append a resource to resource directory.
648    This is just copied from define_resource
649    and modified to add an existing resource.
650  */
651 static void
652 res_append_resource (rc_res_directory **resources, rc_res_resource *resource,
653                      int cids, const rc_res_id *ids, int dupok)
654 {
655   rc_res_entry *re = NULL;
656   int i;
657
658   assert (cids > 0);
659   for (i = 0; i < cids; i++)
660     {
661       rc_res_entry **pp;
662
663       if (*resources == NULL)
664         {
665           static unsigned long timeval;
666
667           /* Use the same timestamp for every resource created in a
668              single run.  */
669           if (timeval == 0)
670             timeval = time (NULL);
671
672           *resources = ((rc_res_directory *)
673                         res_alloc (sizeof (rc_res_directory)));
674           (*resources)->characteristics = 0;
675           (*resources)->time = timeval;
676           (*resources)->major = 0;
677           (*resources)->minor = 0;
678           (*resources)->entries = NULL;
679         }
680
681       for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
682         if (res_id_cmp ((*pp)->id, ids[i]) == 0)
683           break;
684
685       if (*pp != NULL)
686         re = *pp;
687       else
688         {
689           re = (rc_res_entry *) res_alloc (sizeof (rc_res_entry));
690           re->next = NULL;
691           re->id = ids[i];
692           if ((i + 1) < cids)
693             {
694               re->subdir = 1;
695               re->u.dir = NULL;
696             }
697           else
698             {
699               re->subdir = 0;
700               re->u.res = NULL;
701             }
702
703           *pp = re;
704         }
705
706       if ((i + 1) < cids)
707         {
708           if (! re->subdir)
709             {
710               fprintf (stderr, "%s: ", program_name);
711               res_ids_print (stderr, i, ids);
712               fprintf (stderr, ": expected to be a directory\n");
713               xexit (1);
714             }
715
716           resources = &re->u.dir;
717         }
718     }
719
720   if (re->subdir)
721     {
722       fprintf (stderr, "%s: ", program_name);
723       res_ids_print (stderr, cids, ids);
724       fprintf (stderr, ": expected to be a leaf\n");
725       xexit (1);
726     }
727
728   if (re->u.res != NULL)
729     {
730       if (dupok)
731         return;
732
733       fprintf (stderr, "%s: warning: ", program_name);
734       res_ids_print (stderr, cids, ids);
735       fprintf (stderr, ": duplicate value\n");
736     }
737
738   re->u.res = resource;
739 }