Updated windres tool
[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, 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     read_res_data (wrbfd, &soff, omax, d, sizeof (unichar));
559     c = windres_get_16 (wrbfd, d, 2);
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   if ((off + 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 **resources, 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 (*resources == NULL)
659         {
660           static unsigned long timeval;
661
662           /* Use the same timestamp for every resource created in a
663              single run.  */
664           if (timeval == 0)
665             timeval = time (NULL);
666
667           *resources = ((rc_res_directory *)
668                         res_alloc (sizeof (rc_res_directory)));
669           (*resources)->characteristics = 0;
670           (*resources)->time = timeval;
671           (*resources)->major = 0;
672           (*resources)->minor = 0;
673           (*resources)->entries = NULL;
674         }
675
676       for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
677         if (res_id_cmp ((*pp)->id, ids[i]) == 0)
678           break;
679
680       if (*pp != NULL)
681         re = *pp;
682       else
683         {
684           re = (rc_res_entry *) res_alloc (sizeof (rc_res_entry));
685           re->next = NULL;
686           re->id = ids[i];
687           if ((i + 1) < cids)
688             {
689               re->subdir = 1;
690               re->u.dir = NULL;
691             }
692           else
693             {
694               re->subdir = 0;
695               re->u.res = NULL;
696             }
697
698           *pp = re;
699         }
700
701       if ((i + 1) < cids)
702         {
703           if (! re->subdir)
704             {
705               fprintf (stderr, "%s: ", program_name);
706               res_ids_print (stderr, i, ids);
707               fprintf (stderr, ": expected to be a directory\n");
708               xexit (1);
709             }
710
711           resources = &re->u.dir;
712         }
713     }
714
715   if (re->subdir)
716     {
717       fprintf (stderr, "%s: ", program_name);
718       res_ids_print (stderr, cids, ids);
719       fprintf (stderr, ": expected to be a leaf\n");
720       xexit (1);
721     }
722
723   if (re->u.res != NULL)
724     {
725       if (dupok)
726         return;
727
728       fprintf (stderr, "%s: warning: ", program_name);
729       res_ids_print (stderr, cids, ids);
730       fprintf (stderr, ": duplicate value\n");
731     }
732
733   re->u.res = resource;
734 }