1 /* resres.c: read_res_file and write_res_file implementation for windres. 2 Copyright (C) 1998-2014 Free Software Foundation, Inc. 3 Written by Anders Norlander <anorland (at) 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 } 732