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
37static 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);
40static 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 *);
43static 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
47static rc_uint_type write_res_id (windres_bfd *, rc_uint_type, const rc_res_id *);
48static rc_uint_type write_res_info (windres_bfd *, rc_uint_type, const rc_res_res_info *);
49static rc_uint_type write_res_data_hdr (windres_bfd *, rc_uint_type, res_hdr *);
50
51static 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
55static int read_resource_entry (windres_bfd *, rc_uint_type *, rc_uint_type);
56static void read_res_data (windres_bfd *, rc_uint_type *, rc_uint_type, void *,
57			   rc_uint_type);
58static void read_res_data_hdr (windres_bfd *, rc_uint_type *, rc_uint_type, res_hdr *);
59static void read_res_id (windres_bfd *, rc_uint_type *, rc_uint_type, rc_res_id *);
60static unichar *read_unistring (windres_bfd *, rc_uint_type *, rc_uint_type, rc_uint_type *);
61static void skip_null_resource (windres_bfd *, rc_uint_type *, rc_uint_type);
62static int probe_binary (windres_bfd *wrbfd, rc_uint_type);
63
64static unsigned long get_id_size (const rc_res_id *);
65
66static void res_add_resource (rc_res_resource *, const rc_res_id *,
67			      const rc_res_id *, rc_uint_type, int);
68
69static void res_append_resource (rc_res_directory **, rc_res_resource *,
70				 int, const rc_res_id *, int);
71
72static rc_res_directory *resources = NULL;
73
74static const char *filename;
75
76extern char *program_name;
77
78/* Read resource file */
79rc_res_directory *
80read_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 */
114void
115write_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 */
168static int
169read_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 */
218static rc_uint_type
219write_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
285static rc_uint_type
286write_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 */
376static rc_uint_type
377write_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 */
392static unsigned long
393get_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 */
403static rc_uint_type
404write_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
427static rc_uint_type
428write_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
440static void
441read_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 */
456static void
457read_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 */
468static rc_uint_type
469write_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 */
500static rc_uint_type
501write_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 */
518static void
519read_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 */
546static unichar *
547read_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    {
559      read_res_data (wrbfd, &soff, omax, d, sizeof (unichar));
560      c = windres_get_16 (wrbfd, d, 2);
561    }
562  while (c != 0);
563  l = ((soff - off[0]) / sizeof (unichar));
564
565  /* there are hardly any names longer than 256 characters, but anyway. */
566  p = s = (unichar *) xmalloc (sizeof (unichar) * l);
567  do
568    {
569      read_res_data (wrbfd, off, omax, d, sizeof (unichar));
570      c = windres_get_16 (wrbfd, d, 2);
571      *p++ = c;
572    }
573  while (c != 0);
574  *len = l - 1;
575  return s;
576}
577
578static int
579probe_binary (windres_bfd *wrbfd, rc_uint_type omax)
580{
581  rc_uint_type off;
582  res_hdr reshdr;
583
584  off = 0;
585  read_res_data_hdr (wrbfd, &off, omax, &reshdr);
586  if (reshdr.data_size != 0)
587    return 1;
588  if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
589      || (reshdr.header_size != 0x20000000 && target_is_bigendian))
590    return 1;
591
592  /* Subtract size of HeaderSize. DataSize has to be zero. */
593  off += 0x20 - BIN_RES_HDR_SIZE;
594  if ((off + BIN_RES_HDR_SIZE) >= omax)
595    return 1;
596  read_res_data_hdr (wrbfd, &off, omax, &reshdr);
597  /* off is advanced by BIN_RES_HDR_SIZE in read_res_data_hdr()
598     which is part of reshdr.header_size. We shouldn't take it
599     into account twice.  */
600  if ((off - BIN_RES_HDR_SIZE + reshdr.data_size + reshdr.header_size) > omax)
601    return 0;
602  return 1;
603}
604
605/* Check if file is a win32 binary resource file, if so
606   skip past the null resource. Returns 0 if successful, -1 on
607   error.
608 */
609static void
610skip_null_resource (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax)
611{
612  res_hdr reshdr;
613  read_res_data_hdr (wrbfd, off, omax, &reshdr);
614  if (reshdr.data_size != 0)
615    goto skip_err;
616  if ((reshdr.header_size != 0x20 && ! target_is_bigendian)
617    || (reshdr.header_size != 0x20000000 && target_is_bigendian))
618    goto skip_err;
619
620  /* Subtract size of HeaderSize. DataSize has to be zero. */
621  off[0] += 0x20 - BIN_RES_HDR_SIZE;
622  if (off[0] >= omax)
623    goto skip_err;
624
625  return;
626
627skip_err:
628  fprintf (stderr, "%s: %s: Not a valid WIN32 resource file\n", program_name,
629	   filename);
630  xexit (1);
631}
632
633/* Add a resource to resource directory */
634static void
635res_add_resource (rc_res_resource *r, const rc_res_id *type, const rc_res_id *id,
636		  rc_uint_type language, int dupok)
637{
638  rc_res_id a[3];
639
640  a[0] = *type;
641  a[1] = *id;
642  a[2].named = 0;
643  a[2].u.id = language;
644  res_append_resource (&resources, r, 3, a, dupok);
645}
646
647/* Append a resource to resource directory.
648   This is just copied from define_resource
649   and modified to add an existing resource.
650 */
651static void
652res_append_resource (rc_res_directory **resources, rc_res_resource *resource,
653		     int cids, const rc_res_id *ids, int dupok)
654{
655  rc_res_entry *re = NULL;
656  int i;
657
658  assert (cids > 0);
659  for (i = 0; i < cids; i++)
660    {
661      rc_res_entry **pp;
662
663      if (*resources == NULL)
664	{
665	  static unsigned long timeval;
666
667	  /* Use the same timestamp for every resource created in a
668	     single run.  */
669	  if (timeval == 0)
670	    timeval = time (NULL);
671
672	  *resources = ((rc_res_directory *)
673			res_alloc (sizeof (rc_res_directory)));
674	  (*resources)->characteristics = 0;
675	  (*resources)->time = timeval;
676	  (*resources)->major = 0;
677	  (*resources)->minor = 0;
678	  (*resources)->entries = NULL;
679	}
680
681      for (pp = &(*resources)->entries; *pp != NULL; pp = &(*pp)->next)
682	if (res_id_cmp ((*pp)->id, ids[i]) == 0)
683	  break;
684
685      if (*pp != NULL)
686	re = *pp;
687      else
688	{
689	  re = (rc_res_entry *) res_alloc (sizeof (rc_res_entry));
690	  re->next = NULL;
691	  re->id = ids[i];
692	  if ((i + 1) < cids)
693	    {
694	      re->subdir = 1;
695	      re->u.dir = NULL;
696	    }
697	  else
698	    {
699	      re->subdir = 0;
700	      re->u.res = NULL;
701	    }
702
703	  *pp = re;
704	}
705
706      if ((i + 1) < cids)
707	{
708	  if (! re->subdir)
709	    {
710	      fprintf (stderr, "%s: ", program_name);
711	      res_ids_print (stderr, i, ids);
712	      fprintf (stderr, ": expected to be a directory\n");
713	      xexit (1);
714	    }
715
716	  resources = &re->u.dir;
717	}
718    }
719
720  if (re->subdir)
721    {
722      fprintf (stderr, "%s: ", program_name);
723      res_ids_print (stderr, cids, ids);
724      fprintf (stderr, ": expected to be a leaf\n");
725      xexit (1);
726    }
727
728  if (re->u.res != NULL)
729    {
730      if (dupok)
731	return;
732
733      fprintf (stderr, "%s: warning: ", program_name);
734      res_ids_print (stderr, cids, ids);
735      fprintf (stderr, ": duplicate value\n");
736    }
737
738  re->u.res = resource;
739}
740