1/* resres.c: read_res_file and write_res_file implementation for windres.
2   Copyright (C) 1998-2017 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
35static 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);
38static 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 *);
41static 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
45static rc_uint_type write_res_id (windres_bfd *, rc_uint_type, const rc_res_id *);
46static rc_uint_type write_res_info (windres_bfd *, rc_uint_type, const rc_res_res_info *);
47static rc_uint_type write_res_data_hdr (windres_bfd *, rc_uint_type, res_hdr *);
48
49static 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
53static int read_resource_entry (windres_bfd *, rc_uint_type *, rc_uint_type);
54static void read_res_data (windres_bfd *, rc_uint_type *, rc_uint_type, void *,
55			   rc_uint_type);
56static void read_res_data_hdr (windres_bfd *, rc_uint_type *, rc_uint_type, res_hdr *);
57static void read_res_id (windres_bfd *, rc_uint_type *, rc_uint_type, rc_res_id *);
58static unichar *read_unistring (windres_bfd *, rc_uint_type *, rc_uint_type, rc_uint_type *);
59static void skip_null_resource (windres_bfd *, rc_uint_type *, rc_uint_type);
60static int probe_binary (windres_bfd *wrbfd, rc_uint_type);
61
62static unsigned long get_id_size (const rc_res_id *);
63
64static void res_add_resource (rc_res_resource *, const rc_res_id *,
65			      const rc_res_id *, rc_uint_type, int);
66
67static void res_append_resource (rc_res_directory **, rc_res_resource *,
68				 int, const rc_res_id *, int);
69
70static rc_res_directory *resources = NULL;
71
72static const char *filename;
73
74extern char *program_name;
75
76/* Read resource file */
77rc_res_directory *
78read_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 */
112void
113write_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 */
164static int
165read_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 */
214static rc_uint_type
215write_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
281static rc_uint_type
282write_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 */
372static rc_uint_type
373write_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 */
388static unsigned long
389get_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 */
398static rc_uint_type
399write_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
422static rc_uint_type
423write_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
435static void
436read_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 */
451static void
452read_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 */
463static rc_uint_type
464write_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 */
495static rc_uint_type
496write_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 */
513static void
514read_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 */
541static unichar *
542read_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
573static int
574probe_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 */
604static void
605skip_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
622skip_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 */
629static void
630res_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 */
646static void
647res_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}
731