1 /* resres.c: read_res_file and write_res_file implementation for windres. 2 Copyright 1998, 1999, 2001, 2002, 2005, 2007, 2008, 2011 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 3 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 37 static 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); 40 static 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 *); 43 static 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 47 static rc_uint_type write_res_id (windres_bfd *, rc_uint_type, const rc_res_id *); 48 static rc_uint_type write_res_info (windres_bfd *, rc_uint_type, const rc_res_res_info *); 49 static rc_uint_type write_res_data_hdr (windres_bfd *, rc_uint_type, res_hdr *); 50 51 static 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 55 static int read_resource_entry (windres_bfd *, rc_uint_type *, rc_uint_type); 56 static void read_res_data (windres_bfd *, rc_uint_type *, rc_uint_type, void *, 57 rc_uint_type); 58 static void read_res_data_hdr (windres_bfd *, rc_uint_type *, rc_uint_type, res_hdr *); 59 static void read_res_id (windres_bfd *, rc_uint_type *, rc_uint_type, rc_res_id *); 60 static unichar *read_unistring (windres_bfd *, rc_uint_type *, rc_uint_type, rc_uint_type *); 61 static void skip_null_resource (windres_bfd *, rc_uint_type *, rc_uint_type); 62 static int probe_binary (windres_bfd *wrbfd, rc_uint_type); 63 64 static unsigned long get_id_size (const rc_res_id *); 65 66 static void res_add_resource (rc_res_resource *, const rc_res_id *, 67 const rc_res_id *, rc_uint_type, int); 68 69 static void res_append_resource (rc_res_directory **, rc_res_resource *, 70 int, const rc_res_id *, int); 71 72 static rc_res_directory *resources = NULL; 73 74 static const char *filename; 75 76 extern char *program_name; 77 78 /* Read resource file */ 79 rc_res_directory * 80 read_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_endianness (&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 */ 114 void 115 write_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_with_flags (abfd, ".data", 132 (SEC_HAS_CONTENTS | SEC_ALLOC 133 | SEC_LOAD | SEC_DATA)); 134 if (sec == NULL) 135 bfd_fatal ("bfd_make_section"); 136 /* Requiring this is probably a bug in BFD. */ 137 sec->output_section = sec; 138 139 set_windres_bfd (&wrbfd, abfd, sec, 140 (target_is_bigendian ? WR_KIND_BFD_BIN_B 141 : WR_KIND_BFD_BIN_L)); 142 143 language = -1; 144 sec_length = write_res_directory ((windres_bfd *) NULL, 0x20UL, resdir, 145 (const rc_res_id *) NULL, 146 (const rc_res_id *) NULL, &language, 1); 147 if (! bfd_set_section_size (abfd, sec, (sec_length + 3) & ~3)) 148 bfd_fatal ("bfd_set_section_size"); 149 if ((sec_length & 3) != 0) 150 set_windres_bfd_content (&wrbfd, sign, sec_length, 4-(sec_length & 3)); 151 set_windres_bfd_content (&wrbfd, sign, 0, sizeof (sign)); 152 language = -1; 153 sec_length_wrote = write_res_directory (&wrbfd, 0x20UL, resdir, 154 (const rc_res_id *) NULL, 155 (const rc_res_id *) NULL, 156 &language, 1); 157 if (sec_length != sec_length_wrote) 158 fatal ("res write failed with different sizes (%lu/%lu).", 159 (unsigned long) sec_length, (unsigned long) sec_length_wrote); 160 161 bfd_close (abfd); 162 return; 163 } 164 165 /* Read a resource entry, returns 0 when all resources are read */ 166 static int 167 read_resource_entry (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax) 168 { 169 rc_res_id type; 170 rc_res_id name; 171 rc_res_res_info resinfo; 172 res_hdr reshdr; 173 void *buff; 174 175 rc_res_resource *r; 176 struct bin_res_info l; 177 178 off[0] = (off[0] + 3) & ~3; 179 180 /* Read header */ 181 if ((off[0] + 8) > omax) 182 return 0; 183 read_res_data_hdr (wrbfd, off, omax, &reshdr); 184 185 /* read resource type */ 186 read_res_id (wrbfd, off, omax, &type); 187 /* read resource id */ 188 read_res_id (wrbfd, off, omax, &name); 189 190 off[0] = (off[0] + 3) & ~3; 191 192 /* Read additional resource header */ 193 read_res_data (wrbfd, off, omax, &l, BIN_RES_INFO_SIZE); 194 resinfo.version = windres_get_32 (wrbfd, l.version, 4); 195 resinfo.memflags = windres_get_16 (wrbfd, l.memflags, 2); 196 resinfo.language = windres_get_16 (wrbfd, l.language, 2); 197 /* resinfo.version2 = windres_get_32 (wrbfd, l.version2, 4); */ 198 resinfo.characteristics = windres_get_32 (wrbfd, l.characteristics, 4); 199 200 off[0] = (off[0] + 3) & ~3; 201 202 /* Allocate buffer for data */ 203 buff = res_alloc (reshdr.data_size); 204 /* Read data */ 205 read_res_data (wrbfd, off, omax, buff, reshdr.data_size); 206 /* Convert binary data to resource */ 207 r = bin_to_res (wrbfd, type, buff, reshdr.data_size); 208 r->res_info = resinfo; 209 /* Add resource to resource directory */ 210 res_add_resource (r, &type, &name, resinfo.language, 0); 211 212 return 1; 213 } 214 215 /* write resource directory to binary resource file */ 216 static rc_uint_type 217 write_res_directory (windres_bfd *wrbfd, rc_uint_type off, const rc_res_directory *rd, 218 const rc_res_id *type, const rc_res_id *name, rc_uint_type *language, 219 int level) 220 { 221 const rc_res_entry *re; 222 223 for (re = rd->entries; re != NULL; re = re->next) 224 { 225 switch (level) 226 { 227 case 1: 228 /* If we're at level 1, the key of this resource is the 229 type. This normally duplicates the information we have 230 stored with the resource itself, but we need to remember 231 the type if this is a user define resource type. */ 232 type = &re->id; 233 break; 234 235 case 2: 236 /* If we're at level 2, the key of this resource is the name 237 we are going to use in the rc printout. */ 238 name = &re->id; 239 break; 240 241 case 3: 242 /* If we're at level 3, then this key represents a language. 243 Use it to update the current language. */ 244 if (! re->id.named 245 && re->id.u.id != (unsigned long) *language 246 && (re->id.u.id & 0xffff) == re->id.u.id) 247 { 248 *language = re->id.u.id; 249 } 250 break; 251 252 default: 253 break; 254 } 255 256 if (re->subdir) 257 off = write_res_directory (wrbfd, off, re->u.dir, type, name, language, 258 level + 1); 259 else 260 { 261 if (level == 3) 262 { 263 /* This is the normal case: the three levels are 264 TYPE/NAME/LANGUAGE. NAME will have been set at level 265 2, and represents the name to use. We probably just 266 set LANGUAGE, and it will probably match what the 267 resource itself records if anything. */ 268 off = write_res_resource (wrbfd, off, type, name, re->u.res, 269 language); 270 } 271 else 272 { 273 fprintf (stderr, "// Resource at unexpected level %d\n", level); 274 off = write_res_resource (wrbfd, off, type, (rc_res_id *) NULL, 275 re->u.res, language); 276 } 277 } 278 } 279 280 return off; 281 } 282 283 static rc_uint_type 284 write_res_resource (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *type, 285 const rc_res_id *name, const rc_res_resource *res, 286 rc_uint_type *language ATTRIBUTE_UNUSED) 287 { 288 int rt; 289 290 switch (res->type) 291 { 292 default: 293 abort (); 294 295 case RES_TYPE_ACCELERATOR: 296 rt = RT_ACCELERATOR; 297 break; 298 299 case RES_TYPE_BITMAP: 300 rt = RT_BITMAP; 301 break; 302 303 case RES_TYPE_CURSOR: 304 rt = RT_CURSOR; 305 break; 306 307 case RES_TYPE_GROUP_CURSOR: 308 rt = RT_GROUP_CURSOR; 309 break; 310 311 case RES_TYPE_DIALOG: 312 rt = RT_DIALOG; 313 break; 314 315 case RES_TYPE_FONT: 316 rt = RT_FONT; 317 break; 318 319 case RES_TYPE_FONTDIR: 320 rt = RT_FONTDIR; 321 break; 322 323 case RES_TYPE_ICON: 324 rt = RT_ICON; 325 break; 326 327 case RES_TYPE_GROUP_ICON: 328 rt = RT_GROUP_ICON; 329 break; 330 331 case RES_TYPE_MENU: 332 rt = RT_MENU; 333 break; 334 335 case RES_TYPE_MESSAGETABLE: 336 rt = RT_MESSAGETABLE; 337 break; 338 339 case RES_TYPE_RCDATA: 340 rt = RT_RCDATA; 341 break; 342 343 case RES_TYPE_STRINGTABLE: 344 rt = RT_STRING; 345 break; 346 347 case RES_TYPE_USERDATA: 348 rt = 0; 349 break; 350 351 case RES_TYPE_VERSIONINFO: 352 rt = RT_VERSION; 353 break; 354 355 case RES_TYPE_TOOLBAR: 356 rt = RT_TOOLBAR; 357 break; 358 } 359 360 if (rt != 0 361 && type != NULL 362 && (type->named || type->u.id != (unsigned long) rt)) 363 { 364 fprintf (stderr, "// Unexpected resource type mismatch: "); 365 res_id_print (stderr, *type, 1); 366 fprintf (stderr, " != %d", rt); 367 abort (); 368 } 369 370 return write_res_bin (wrbfd, off, res, type, name, &res->res_info); 371 } 372 373 /* Write a resource in binary resource format */ 374 static rc_uint_type 375 write_res_bin (windres_bfd *wrbfd, rc_uint_type off, const rc_res_resource *res, 376 const rc_res_id *type, const rc_res_id *name, 377 const rc_res_res_info *resinfo) 378 { 379 rc_uint_type noff; 380 rc_uint_type datasize = 0; 381 382 noff = res_to_bin ((windres_bfd *) NULL, off, res); 383 datasize = noff - off; 384 385 off = write_res_header (wrbfd, off, datasize, type, name, resinfo); 386 return res_to_bin (wrbfd, off, res); 387 } 388 389 /* Get number of bytes needed to store an id in binary format */ 390 static unsigned long 391 get_id_size (id) 392 const rc_res_id *id; 393 { 394 if (id->named) 395 return sizeof (unichar) * (id->u.n.length + 1); 396 else 397 return sizeof (unichar) * 2; 398 } 399 400 /* Write a resource header */ 401 static rc_uint_type 402 write_res_header (windres_bfd *wrbfd, rc_uint_type off, rc_uint_type datasize, 403 const rc_res_id *type, const rc_res_id *name, 404 const rc_res_res_info *resinfo) 405 { 406 res_hdr reshdr; 407 reshdr.data_size = datasize; 408 reshdr.header_size = 24 + get_id_size (type) + get_id_size (name); 409 410 reshdr.header_size = (reshdr.header_size + 3) & ~3; 411 412 off = (off + 3) & ~3; 413 414 off = write_res_data_hdr (wrbfd, off, &reshdr); 415 off = write_res_id (wrbfd, off, type); 416 off = write_res_id (wrbfd, off, name); 417 418 off = (off + 3) & ~3; 419 420 off = write_res_info (wrbfd, off, resinfo); 421 off = (off + 3) & ~3; 422 return off; 423 } 424 425 static rc_uint_type 426 write_res_data_hdr (windres_bfd *wrbfd, rc_uint_type off, res_hdr *hdr) 427 { 428 if (wrbfd) 429 { 430 struct bin_res_hdr brh; 431 windres_put_32 (wrbfd, brh.data_size, hdr->data_size); 432 windres_put_32 (wrbfd, brh.header_size, hdr->header_size); 433 set_windres_bfd_content (wrbfd, &brh, off, BIN_RES_HDR_SIZE); 434 } 435 return off + BIN_RES_HDR_SIZE; 436 } 437 438 static void 439 read_res_data_hdr (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, 440 res_hdr *reshdr) 441 { 442 struct bin_res_hdr brh; 443 444 if ((off[0] + BIN_RES_HDR_SIZE) > omax) 445 fatal ("%s: unexpected end of file %ld/%ld", filename,(long) off[0], (long) omax); 446 447 get_windres_bfd_content (wrbfd, &brh, off[0], BIN_RES_HDR_SIZE); 448 reshdr->data_size = windres_get_32 (wrbfd, brh.data_size, 4); 449 reshdr->header_size = windres_get_32 (wrbfd, brh.header_size, 4); 450 off[0] += BIN_RES_HDR_SIZE; 451 } 452 453 /* Read data from file, abort on failure */ 454 static void 455 read_res_data (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, void *data, 456 rc_uint_type size) 457 { 458 if ((off[0] + size) > omax) 459 fatal ("%s: unexpected end of file %ld/%ld %ld", filename,(long) off[0], 460 (long) omax, (long) size); 461 get_windres_bfd_content (wrbfd, data, off[0], size); 462 off[0] += size; 463 } 464 465 /* Write a resource id */ 466 static rc_uint_type 467 write_res_id (windres_bfd *wrbfd, rc_uint_type off, const rc_res_id *id) 468 { 469 if (id->named) 470 { 471 rc_uint_type len = (((bfd_signed_vma) id->u.n.length < 0 ? 0 : id->u.n.length) + 1); 472 if (wrbfd) 473 { 474 rc_uint_type i; 475 bfd_byte *d = (bfd_byte *) xmalloc (len * sizeof (unichar)); 476 for (i = 0; i < (len - 1); i++) 477 windres_put_16 (wrbfd, d + (i * sizeof (unichar)), id->u.n.name[i]); 478 windres_put_16 (wrbfd, d + (i * sizeof (unichar)), 0); 479 set_windres_bfd_content (wrbfd, d, off, (len * sizeof (unichar))); 480 } 481 off += (len * sizeof (unichar)); 482 } 483 else 484 { 485 if (wrbfd) 486 { 487 struct bin_res_id bid; 488 windres_put_16 (wrbfd, bid.sig, 0xffff); 489 windres_put_16 (wrbfd, bid.id, id->u.id); 490 set_windres_bfd_content (wrbfd, &bid, off, BIN_RES_ID); 491 } 492 off += BIN_RES_ID; 493 } 494 return off; 495 } 496 497 /* Write resource info */ 498 static rc_uint_type 499 write_res_info (windres_bfd *wrbfd, rc_uint_type off, const rc_res_res_info *info) 500 { 501 if (wrbfd) 502 { 503 struct bin_res_info l; 504 505 windres_put_32 (wrbfd, l.version, info->version); 506 windres_put_16 (wrbfd, l.memflags, info->memflags); 507 windres_put_16 (wrbfd, l.language, info->language); 508 windres_put_32 (wrbfd, l.version2, info->version); 509 windres_put_32 (wrbfd, l.characteristics, info->characteristics); 510 set_windres_bfd_content (wrbfd, &l, off, BIN_RES_INFO_SIZE); 511 } 512 return off + BIN_RES_INFO_SIZE; 513 } 514 515 /* read a resource identifier */ 516 static void 517 read_res_id (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, rc_res_id *id) 518 { 519 struct bin_res_id bid; 520 unsigned short ord; 521 unichar *id_s = NULL; 522 rc_uint_type len; 523 524 read_res_data (wrbfd, off, omax, &bid, BIN_RES_ID - 2); 525 ord = (unsigned short) windres_get_16 (wrbfd, bid.sig, 2); 526 if (ord == 0xFFFF) /* an ordinal id */ 527 { 528 read_res_data (wrbfd, off, omax, bid.id, BIN_RES_ID - 2); 529 id->named = 0; 530 id->u.id = windres_get_16 (wrbfd, bid.id, 2); 531 } 532 else 533 /* named id */ 534 { 535 off[0] -= 2; 536 id_s = read_unistring (wrbfd, off, omax, &len); 537 id->named = 1; 538 id->u.n.length = len; 539 id->u.n.name = id_s; 540 } 541 } 542 543 /* Read a null terminated UNICODE string */ 544 static unichar * 545 read_unistring (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax, 546 rc_uint_type *len) 547 { 548 unichar *s; 549 bfd_byte d[2]; 550 unichar c; 551 unichar *p; 552 rc_uint_type l; 553 rc_uint_type soff = off[0]; 554 555 do 556 { 557 read_res_data (wrbfd, &soff, omax, d, sizeof (unichar)); 558 c = windres_get_16 (wrbfd, d, 2); 559 } 560 while (c != 0); 561 l = ((soff - off[0]) / sizeof (unichar)); 562 563 /* there are hardly any names longer than 256 characters, but anyway. */ 564 p = s = (unichar *) xmalloc (sizeof (unichar) * l); 565 do 566 { 567 read_res_data (wrbfd, off, omax, d, sizeof (unichar)); 568 c = windres_get_16 (wrbfd, d, 2); 569 *p++ = c; 570 } 571 while (c != 0); 572 *len = l - 1; 573 return s; 574 } 575 576 static int 577 probe_binary (windres_bfd *wrbfd, rc_uint_type omax) 578 { 579 rc_uint_type off; 580 res_hdr reshdr; 581 582 off = 0; 583 read_res_data_hdr (wrbfd, &off, omax, &reshdr); 584 if (reshdr.data_size != 0) 585 return 1; 586 if ((reshdr.header_size != 0x20 && ! target_is_bigendian) 587 || (reshdr.header_size != 0x20000000 && target_is_bigendian)) 588 return 1; 589 590 /* Subtract size of HeaderSize. DataSize has to be zero. */ 591 off += 0x20 - BIN_RES_HDR_SIZE; 592 if ((off + BIN_RES_HDR_SIZE) >= omax) 593 return 1; 594 read_res_data_hdr (wrbfd, &off, omax, &reshdr); 595 /* off is advanced by BIN_RES_HDR_SIZE in read_res_data_hdr() 596 which is part of reshdr.header_size. We shouldn't take it 597 into account twice. */ 598 if ((off - BIN_RES_HDR_SIZE + reshdr.data_size + reshdr.header_size) > omax) 599 return 0; 600 return 1; 601 } 602 603 /* Check if file is a win32 binary resource file, if so 604 skip past the null resource. Returns 0 if successful, -1 on 605 error. 606 */ 607 static void 608 skip_null_resource (windres_bfd *wrbfd, rc_uint_type *off, rc_uint_type omax) 609 { 610 res_hdr reshdr; 611 read_res_data_hdr (wrbfd, off, omax, &reshdr); 612 if (reshdr.data_size != 0) 613 goto skip_err; 614 if ((reshdr.header_size != 0x20 && ! target_is_bigendian) 615 || (reshdr.header_size != 0x20000000 && target_is_bigendian)) 616 goto skip_err; 617 618 /* Subtract size of HeaderSize. DataSize has to be zero. */ 619 off[0] += 0x20 - BIN_RES_HDR_SIZE; 620 if (off[0] >= omax) 621 goto skip_err; 622 623 return; 624 625 skip_err: 626 fprintf (stderr, "%s: %s: Not a valid WIN32 resource file\n", program_name, 627 filename); 628 xexit (1); 629 } 630 631 /* Add a resource to resource directory */ 632 static void 633 res_add_resource (rc_res_resource *r, const rc_res_id *type, const rc_res_id *id, 634 rc_uint_type language, int dupok) 635 { 636 rc_res_id a[3]; 637 638 a[0] = *type; 639 a[1] = *id; 640 a[2].named = 0; 641 a[2].u.id = language; 642 res_append_resource (&resources, r, 3, a, dupok); 643 } 644 645 /* Append a resource to resource directory. 646 This is just copied from define_resource 647 and modified to add an existing resource. 648 */ 649 static void 650 res_append_resource (rc_res_directory **res_dirs, rc_res_resource *resource, 651 int cids, const rc_res_id *ids, int dupok) 652 { 653 rc_res_entry *re = NULL; 654 int i; 655 656 assert (cids > 0); 657 for (i = 0; i < cids; i++) 658 { 659 rc_res_entry **pp; 660 661 if (*res_dirs == NULL) 662 { 663 static unsigned long timeval; 664 665 /* Use the same timestamp for every resource created in a 666 single run. */ 667 if (timeval == 0) 668 timeval = time (NULL); 669 670 *res_dirs = ((rc_res_directory *) 671 res_alloc (sizeof (rc_res_directory))); 672 (*res_dirs)->characteristics = 0; 673 (*res_dirs)->time = timeval; 674 (*res_dirs)->major = 0; 675 (*res_dirs)->minor = 0; 676 (*res_dirs)->entries = NULL; 677 } 678 679 for (pp = &(*res_dirs)->entries; *pp != NULL; pp = &(*pp)->next) 680 if (res_id_cmp ((*pp)->id, ids[i]) == 0) 681 break; 682 683 if (*pp != NULL) 684 re = *pp; 685 else 686 { 687 re = (rc_res_entry *) res_alloc (sizeof (rc_res_entry)); 688 re->next = NULL; 689 re->id = ids[i]; 690 if ((i + 1) < cids) 691 { 692 re->subdir = 1; 693 re->u.dir = NULL; 694 } 695 else 696 { 697 re->subdir = 0; 698 re->u.res = NULL; 699 } 700 701 *pp = re; 702 } 703 704 if ((i + 1) < cids) 705 { 706 if (! re->subdir) 707 { 708 fprintf (stderr, "%s: ", program_name); 709 res_ids_print (stderr, i, ids); 710 fprintf (stderr, ": expected to be a directory\n"); 711 xexit (1); 712 } 713 714 res_dirs = &re->u.dir; 715 } 716 } 717 718 if (re->subdir) 719 { 720 fprintf (stderr, "%s: ", program_name); 721 res_ids_print (stderr, cids, ids); 722 fprintf (stderr, ": expected to be a leaf\n"); 723 xexit (1); 724 } 725 726 if (re->u.res != NULL) 727 { 728 if (dupok) 729 return; 730 731 fprintf (stderr, "%s: warning: ", program_name); 732 res_ids_print (stderr, cids, ids); 733 fprintf (stderr, ": duplicate value\n"); 734 } 735 736 re->u.res = resource; 737 } 738