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