resres.c revision 1.3
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
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 (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 */
399static rc_uint_type
400write_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
423static rc_uint_type
424write_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
436static void
437read_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 */
452static void
453read_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 */
464static rc_uint_type
465write_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 */
496static rc_uint_type
497write_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 */
514static void
515read_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 */
542static unichar *
543read_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
574static int
575probe_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 */
605static void
606skip_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
623skip_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 */
630static void
631res_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 */
647static void
648res_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}
732