update copyright dates
[external/binutils.git] / binutils / resres.c
1 /* resres.c: read_res_file and write_res_file implementation for windres.
2    Copyright 1998, 1999, 2001, 2002, 2005, 2007, 2008
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_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_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 **resources, 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 (*resources == 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           *resources = ((rc_res_directory *)
671                         res_alloc (sizeof (rc_res_directory)));
672           (*resources)->characteristics = 0;
673           (*resources)->time = timeval;
674           (*resources)->major = 0;
675           (*resources)->minor = 0;
676           (*resources)->entries = NULL;
677         }
678
679       for (pp = &(*resources)->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           resources = &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 }