ChangeLog rotatation and copyright year update
[external/binutils.git] / binutils / resres.c
1 /* resres.c: read_res_file and write_res_file implementation for windres.
2    Copyright (C) 1998-2015 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 (id)
390      const rc_res_id *id;
391 {
392   if (id->named)
393     return sizeof (unichar) * (id->u.n.length + 1);
394   else
395     return sizeof (unichar) * 2;
396 }
397
398 /* Write a resource header */
399 static rc_uint_type
400 write_res_header (windres_bfd *wrbfd, rc_uint_type off, rc_uint_type datasize,
401                   const rc_res_id *type, const rc_res_id *name,
402                   const rc_res_res_info *resinfo)
403 {
404   res_hdr reshdr;
405   reshdr.data_size = datasize;
406   reshdr.header_size = 24 + get_id_size (type) + get_id_size (name);
407
408   reshdr.header_size = (reshdr.header_size + 3) & ~3;
409
410   off = (off + 3) & ~3;
411
412   off = write_res_data_hdr (wrbfd, off, &reshdr);
413   off = write_res_id (wrbfd, off, type);
414   off = write_res_id (wrbfd, off, name);
415
416   off = (off + 3) & ~3;
417
418   off = write_res_info (wrbfd, off, resinfo);
419   off = (off + 3) & ~3;
420   return off;
421 }
422
423 static rc_uint_type
424 write_res_data_hdr (windres_bfd *wrbfd, rc_uint_type off, res_hdr *hdr)
425 {
426   if (wrbfd)
427     {
428       struct bin_res_hdr brh;
429       windres_put_32 (wrbfd, brh.data_size, hdr->data_size);
430       windres_put_32 (wrbfd, brh.header_size, hdr->header_size);
431       set_windres_bfd_content (wrbfd, &brh, off, BIN_RES_HDR_SIZE);
432     }
433   return off + BIN_RES_HDR_SIZE;
434 }
435
436 static void
437 read_res_data_hdr (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
438                    res_hdr *reshdr)
439 {
440   struct bin_res_hdr brh;
441
442   if ((off[0] + BIN_RES_HDR_SIZE) > omax)
443     fatal ("%s: unexpected end of file %ld/%ld", filename,(long) off[0], (long) omax);
444
445   get_windres_bfd_content (wrbfd, &brh, off[0], BIN_RES_HDR_SIZE);
446   reshdr->data_size = windres_get_32 (wrbfd, brh.data_size, 4);
447   reshdr->header_size = windres_get_32 (wrbfd, brh.header_size, 4);
448   off[0] += BIN_RES_HDR_SIZE;
449 }
450
451 /* Read data from file, abort on failure */
452 static void
453 read_res_data (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, void *data,
454                rc_uint_type size)
455 {
456   if ((off[0] + size) > omax)
457     fatal ("%s: unexpected end of file %ld/%ld %ld", filename,(long) off[0],
458            (long) omax, (long) size);
459   get_windres_bfd_content (wrbfd, data, off[0], size);
460   off[0] += size;
461 }
462
463 /* Write a resource id */
464 static rc_uint_type
465 write_res_id (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *id)
466 {
467   if (id->named)
468     {
469       rc_uint_type len = (((bfd_signed_vma) id->u.n.length < 0 ? 0 : id->u.n.length) + 1);
470       if (wrbfd)
471         {
472           rc_uint_type i;
473           bfd_byte *d = (bfd_byte *) xmalloc (len * sizeof (unichar));
474           for (i = 0; i < (len - 1); i++)
475             windres_put_16 (wrbfd, d + (i * sizeof (unichar)), id->u.n.name[i]);
476           windres_put_16 (wrbfd, d + (i * sizeof (unichar)), 0);
477           set_windres_bfd_content (wrbfd, d, off, (len * sizeof (unichar)));
478         }
479       off += (len * sizeof (unichar));
480     }
481   else
482     {
483       if (wrbfd)
484         {
485           struct bin_res_id bid;
486           windres_put_16 (wrbfd, bid.sig, 0xffff);
487           windres_put_16 (wrbfd, bid.id, id->u.id);
488           set_windres_bfd_content (wrbfd, &bid, off, BIN_RES_ID);
489         }
490       off += BIN_RES_ID;
491     }
492   return off;
493 }
494
495 /* Write resource info */
496 static rc_uint_type
497 write_res_info (windres_bfd *wrbfd, rc_uint_type off, const rc_res_res_info *info)
498 {
499   if (wrbfd)
500     {
501       struct bin_res_info l;
502       
503       windres_put_32 (wrbfd, l.version, info->version);
504       windres_put_16 (wrbfd, l.memflags, info->memflags);
505       windres_put_16 (wrbfd, l.language, info->language);
506       windres_put_32 (wrbfd, l.version2, info->version);
507       windres_put_32 (wrbfd, l.characteristics, info->characteristics);
508       set_windres_bfd_content (wrbfd, &l, off, BIN_RES_INFO_SIZE);
509     }
510   return off + BIN_RES_INFO_SIZE;
511 }
512
513 /* read a resource identifier */
514 static void
515 read_res_id (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, rc_res_id *id)
516 {
517   struct bin_res_id bid;
518   unsigned short ord;
519   unichar *id_s = NULL;
520   rc_uint_type len;
521
522   read_res_data (wrbfd, off, omax, &bid, BIN_RES_ID - 2);
523   ord = (unsigned short) windres_get_16 (wrbfd, bid.sig, 2);
524   if (ord == 0xFFFF)            /* an ordinal id */
525     {
526       read_res_data (wrbfd, off, omax, bid.id, BIN_RES_ID - 2);
527       id->named = 0;
528       id->u.id = windres_get_16 (wrbfd, bid.id, 2);
529     }
530   else
531     /* named id */
532     {
533       off[0] -= 2;
534       id_s = read_unistring (wrbfd, off, omax, &len);
535       id->named = 1;
536       id->u.n.length = len;
537       id->u.n.name = id_s;
538     }
539 }
540
541 /* Read a null terminated UNICODE string */
542 static unichar *
543 read_unistring (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax,
544                 rc_uint_type *len)
545 {
546   unichar *s;
547   bfd_byte d[2];
548   unichar c;
549   unichar *p;
550   rc_uint_type l;
551   rc_uint_type soff = off[0];
552
553   do
554     {
555       read_res_data (wrbfd, &soff, omax, d, sizeof (unichar));
556       c = windres_get_16 (wrbfd, d, 2);
557     }
558   while (c != 0);
559   l = ((soff - off[0]) / sizeof (unichar));
560
561   /* there are hardly any names longer than 256 characters, but anyway. */
562   p = s = (unichar *) xmalloc (sizeof (unichar) * l);
563   do
564     {
565       read_res_data (wrbfd, off, omax, d, sizeof (unichar));
566       c = windres_get_16 (wrbfd, d, 2);
567       *p++ = c;
568     }
569   while (c != 0);
570   *len = l - 1;
571   return s;
572 }
573
574 static int
575 probe_binary (windres_bfd *wrbfd, rc_uint_type omax)
576 {
577   rc_uint_type off;
578   res_hdr reshdr;
579
580   off = 0;
581   read_res_data_hdr (wrbfd, &off, omax, &reshdr);
582   if (reshdr.data_size != 0)
583     return 1;
584   if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
585       || (reshdr.header_size != 0x20000000 && target_is_bigendian))
586     return 1;
587
588   /* Subtract size of HeaderSize. DataSize has to be zero. */
589   off += 0x20 - BIN_RES_HDR_SIZE;
590   if ((off + BIN_RES_HDR_SIZE) >= omax)
591     return 1;
592   read_res_data_hdr (wrbfd, &off, omax, &reshdr);
593   /* off is advanced by BIN_RES_HDR_SIZE in read_res_data_hdr()
594      which is part of reshdr.header_size. We shouldn't take it
595      into account twice.  */
596   if ((off - BIN_RES_HDR_SIZE + reshdr.data_size + reshdr.header_size) > omax)
597     return 0;
598   return 1;
599 }
600
601 /* Check if file is a win32 binary resource file, if so
602    skip past the null resource. Returns 0 if successful, -1 on
603    error.
604  */
605 static void
606 skip_null_resource (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
607 {
608   res_hdr reshdr;
609   read_res_data_hdr (wrbfd, off, omax, &reshdr);
610   if (reshdr.data_size != 0)
611     goto skip_err;
612   if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
613     || (reshdr.header_size != 0x20000000 && target_is_bigendian))
614     goto skip_err;
615
616   /* Subtract size of HeaderSize. DataSize has to be zero. */
617   off[0] += 0x20 - BIN_RES_HDR_SIZE;
618   if (off[0] >= omax)
619     goto skip_err;
620
621   return;
622
623 skip_err:
624   fprintf (stderr, "%s: %s: Not a valid WIN32 resource file\n", program_name,
625            filename);
626   xexit (1);
627 }
628
629 /* Add a resource to resource directory */
630 static void
631 res_add_resource (rc_res_resource *r, const rc_res_id *type, const rc_res_id *id,
632                   rc_uint_type language, int dupok)
633 {
634   rc_res_id a[3];
635
636   a[0] = *type;
637   a[1] = *id;
638   a[2].named = 0;
639   a[2].u.id = language;
640   res_append_resource (&resources, r, 3, a, dupok);
641 }
642
643 /* Append a resource to resource directory.
644    This is just copied from define_resource
645    and modified to add an existing resource.
646  */
647 static void
648 res_append_resource (rc_res_directory **res_dirs, rc_res_resource *resource,
649                      int cids, const rc_res_id *ids, int dupok)
650 {
651   rc_res_entry *re = NULL;
652   int i;
653
654   assert (cids > 0);
655   for (i = 0; i < cids; i++)
656     {
657       rc_res_entry **pp;
658
659       if (*res_dirs == NULL)
660         {
661           *res_dirs = ((rc_res_directory *)
662                         res_alloc (sizeof (rc_res_directory)));
663
664           (*res_dirs)->characteristics = 0;
665           /* Using a real timestamp only serves to create non-deterministic
666              results.  Use zero instead.  */
667           (*res_dirs)->time = 0;
668           (*res_dirs)->major = 0;
669           (*res_dirs)->minor = 0;
670           (*res_dirs)->entries = NULL;
671         }
672
673       for (pp = &(*res_dirs)->entries; *pp != NULL; pp = &(*pp)->next)
674         if (res_id_cmp ((*pp)->id, ids[i]) == 0)
675           break;
676
677       if (*pp != NULL)
678         re = *pp;
679       else
680         {
681           re = (rc_res_entry *) res_alloc (sizeof (rc_res_entry));
682           re->next = NULL;
683           re->id = ids[i];
684           if ((i + 1) < cids)
685             {
686               re->subdir = 1;
687               re->u.dir = NULL;
688             }
689           else
690             {
691               re->subdir = 0;
692               re->u.res = NULL;
693             }
694
695           *pp = re;
696         }
697
698       if ((i + 1) < cids)
699         {
700           if (! re->subdir)
701             {
702               fprintf (stderr, "%s: ", program_name);
703               res_ids_print (stderr, i, ids);
704               fprintf (stderr, ": expected to be a directory\n");
705               xexit (1);
706             }
707
708           res_dirs = &re->u.dir;
709         }
710     }
711
712   if (re->subdir)
713     {
714       fprintf (stderr, "%s: ", program_name);
715       res_ids_print (stderr, cids, ids);
716       fprintf (stderr, ": expected to be a leaf\n");
717       xexit (1);
718     }
719
720   if (re->u.res != NULL)
721     {
722       if (dupok)
723         return;
724
725       fprintf (stderr, "%s: warning: ", program_name);
726       res_ids_print (stderr, cids, ids);
727       fprintf (stderr, ": duplicate value\n");
728     }
729
730   re->u.res = resource;
731 }