1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* 26 * @(#)files.cc 1.37 06/12/12 27 */ 28 29 #pragma ident "@(#)files.cc 1.37 06/12/12" 30 31 /* 32 * files.c 33 * 34 * Various file related routines: 35 * Figure out if file exists 36 * Wildcard resolution for directory reader 37 * Directory reader 38 */ 39 40 41 /* 42 * Included files 43 */ 44 #if defined(SUN5_0) || defined(HP_UX) 45 #include <dirent.h> /* opendir() */ 46 #else 47 #include <sys/dir.h> /* opendir() */ 48 #endif 49 #include <errno.h> /* errno */ 50 #include <mk/defs.h> 51 #include <mksh/macro.h> /* getvar() */ 52 #include <mksh/misc.h> /* get_prop(), append_prop() */ 53 #include <sys/stat.h> /* lstat() */ 54 55 /* 56 * Defined macros 57 */ 58 59 /* 60 * typedefs & structs 61 */ 62 63 /* 64 * Static variables 65 */ 66 67 /* 68 * File table of contents 69 */ 70 extern timestruc_t& exists(register Name target); 71 extern void set_target_stat(register Name target, struct stat buf); 72 static timestruc_t& vpath_exists(register Name target); 73 static Name enter_file_name(wchar_t *name_string, wchar_t *library); 74 static Boolean star_match(register char *string, register char *pattern); 75 static Boolean amatch(register wchar_t *string, register wchar_t *pattern); 76 77 /* 78 * exists(target) 79 * 80 * Figure out the timestamp for one target. 81 * 82 * Return value: 83 * The time the target was created 84 * 85 * Parameters: 86 * target The target to check 87 * 88 * Global variables used: 89 * debug_level Should we trace the stat call? 90 * recursion_level Used for tracing 91 * vpath_defined Was the variable VPATH defined in environment? 92 */ 93 timestruc_t& 94 exists(register Name target) 95 { 96 struct stat buf; 97 register int result; 98 99 /* We cache stat information. */ 100 if (target->stat.time != file_no_time) { 101 return target->stat.time; 102 } 103 104 /* 105 * If the target is a member, we have to extract the time 106 * from the archive. 107 */ 108 if (target->is_member && 109 (get_prop(target->prop, member_prop) != NULL)) { 110 return read_archive(target); 111 } 112 113 if (debug_level > 1) { 114 (void) printf(NOCATGETS("%*sstat(%s)\n"), 115 recursion_level, 116 "", 117 target->string_mb); 118 } 119 120 result = lstat_vroot(target->string_mb, &buf, NULL, VROOT_DEFAULT); 121 if ((result != -1) && ((buf.st_mode & S_IFMT) == S_IFLNK)) { 122 /* 123 * If the file is a symbolic link, we remember that 124 * and then we get the status for the refd file. 125 */ 126 target->stat.is_sym_link = true; 127 result = stat_vroot(target->string_mb, &buf, NULL, VROOT_DEFAULT); 128 } else { 129 target->stat.is_sym_link = false; 130 } 131 132 if (result < 0) { 133 target->stat.time = file_doesnt_exist; 134 target->stat.stat_errno = errno; 135 if ((errno == ENOENT) && 136 vpath_defined && 137 /* azv, fixing bug 1262942, VPATH works with a leaf name 138 * but not a directory name. 139 */ 140 (target->string_mb[0] != (int) slash_char) ) { 141 /* BID_1214655 */ 142 /* azv */ 143 vpath_exists(target); 144 // return vpath_exists(target); 145 } 146 } else { 147 /* Save all the information we need about the file */ 148 target->stat.stat_errno = 0; 149 target->stat.is_file = true; 150 target->stat.mode = buf.st_mode & 0777; 151 target->stat.size = buf.st_size; 152 target->stat.is_dir = 153 BOOLEAN((buf.st_mode & S_IFMT) == S_IFDIR); 154 if (target->stat.is_dir) { 155 target->stat.time = file_is_dir; 156 } else { 157 /* target->stat.time = buf.st_mtime; */ 158 /* BID_1129806 */ 159 /* vis@nbsp.nsk.su */ 160 #if defined(linux) 161 timestruc_t ttime = { buf.st_mtime, 0 }; 162 target->stat.time = MAX(ttime, file_min_time); 163 #else 164 target->stat.time = MAX(buf.st_mtim, file_min_time); 165 #endif 166 } 167 } 168 if ((target->colon_splits > 0) && 169 (get_prop(target->prop, time_prop) == NULL)) { 170 append_prop(target, time_prop)->body.time.time = 171 target->stat.time; 172 } 173 return target->stat.time; 174 } 175 176 /* 177 * set_target_stat( target, buf) 178 * 179 * Called by exists() to set some stat fields in the Name structure 180 * to those read by the stat_vroot() call (from disk). 181 * 182 * Parameters: 183 * target The target whose stat field is set 184 * buf stat values (on disk) of the file 185 * represented by target. 186 */ 187 void 188 set_target_stat(register Name target, struct stat buf) 189 { 190 target->stat.stat_errno = 0; 191 target->stat.is_file = true; 192 target->stat.mode = buf.st_mode & 0777; 193 target->stat.size = buf.st_size; 194 target->stat.is_dir = 195 BOOLEAN((buf.st_mode & S_IFMT) == S_IFDIR); 196 if (target->stat.is_dir) { 197 target->stat.time = file_is_dir; 198 } else { 199 /* target->stat.time = buf.st_mtime; */ 200 /* BID_1129806 */ 201 /* vis@nbsp.nsk.su */ 202 #if defined(linux) 203 timestruc_t ttime = { buf.st_mtime, 0 }; 204 target->stat.time = ttime; 205 #else 206 target->stat.time = MAX(buf.st_mtim, file_min_time); 207 #endif 208 } 209 } 210 211 212 /* 213 * vpath_exists(target) 214 * 215 * Called if exists() discovers that there is a VPATH defined. 216 * This function stats the VPATH translation of the target. 217 * 218 * Return value: 219 * The time the target was created 220 * 221 * Parameters: 222 * target The target to check 223 * 224 * Global variables used: 225 * vpath_name The Name "VPATH", used to get macro value 226 */ 227 static timestruc_t& 228 vpath_exists(register Name target) 229 { 230 wchar_t *vpath; 231 wchar_t file_name[MAXPATHLEN]; 232 wchar_t *name_p; 233 Name alias; 234 235 /* 236 * To avoid recursive search through VPATH when exists(alias) is called 237 */ 238 vpath_defined = false; 239 240 Wstring wcb(getvar(vpath_name)); 241 Wstring wcb1(target); 242 243 vpath = wcb.get_string(); 244 245 while (*vpath != (int) nul_char) { 246 name_p = file_name; 247 while ((*vpath != (int) colon_char) && 248 (*vpath != (int) nul_char)) { 249 *name_p++ = *vpath++; 250 } 251 *name_p++ = (int) slash_char; 252 (void) wscpy(name_p, wcb1.get_string()); 253 alias = GETNAME(file_name, FIND_LENGTH); 254 if (exists(alias) != file_doesnt_exist) { 255 target->stat.is_file = true; 256 target->stat.mode = alias->stat.mode; 257 target->stat.size = alias->stat.size; 258 target->stat.is_dir = alias->stat.is_dir; 259 target->stat.time = alias->stat.time; 260 maybe_append_prop(target, vpath_alias_prop)-> 261 body.vpath_alias.alias = alias; 262 target->has_vpath_alias_prop = true; 263 vpath_defined = true; 264 return alias->stat.time; 265 } 266 while ((*vpath != (int) nul_char) && 267 ((*vpath == (int) colon_char) || iswspace(*vpath))) { 268 vpath++; 269 } 270 } 271 /* 272 * Restore vpath_defined 273 */ 274 vpath_defined = true; 275 return target->stat.time; 276 } 277 278 /* 279 * read_dir(dir, pattern, line, library) 280 * 281 * Used to enter the contents of directories into makes namespace. 282 * Presence of a file is important when scanning for implicit rules. 283 * read_dir() is also used to expand wildcards in dependency lists. 284 * 285 * Return value: 286 * Non-0 if we found files to match the pattern 287 * 288 * Parameters: 289 * dir Path to the directory to read 290 * pattern Pattern for that files should match or NULL 291 * line When we scan using a pattern we enter files 292 * we find as dependencies for this line 293 * library If we scan for "lib.a(<wildcard-member>)" 294 * 295 * Global variables used: 296 * debug_level Should we trace the dir reading? 297 * dot The Name ".", compared against 298 * sccs_dir_path The path to the SCCS dir (from PROJECTDIR) 299 * vpath_defined Was the variable VPATH defined in environment? 300 * vpath_name The Name "VPATH", use to get macro value 301 */ 302 int 303 read_dir(Name dir, wchar_t *pattern, Property line, wchar_t *library) 304 { 305 wchar_t file_name[MAXPATHLEN]; 306 wchar_t *file_name_p = file_name; 307 Name file; 308 wchar_t plain_file_name[MAXPATHLEN]; 309 wchar_t *plain_file_name_p; 310 Name plain_file; 311 wchar_t tmp_wcs_buffer[MAXPATHLEN]; 312 DIR *dir_fd; 313 int m_local_dependency=0; 314 #if defined(SUN5_0) || defined(HP_UX) 315 #define d_fileno d_ino 316 register struct dirent *dp; 317 #else 318 register struct direct *dp; 319 #endif 320 wchar_t *vpath = NULL; 321 wchar_t *p; 322 int result = 0; 323 324 if(dir->hash.length >= MAXPATHLEN) { 325 return 0; 326 } 327 328 Wstring wcb(dir); 329 Wstring vps; 330 331 /* A directory is only read once unless we need to expand wildcards. */ 332 if (pattern == NULL) { 333 if (dir->has_read_dir) { 334 return 0; 335 } 336 dir->has_read_dir = true; 337 } 338 /* Check if VPATH is active and setup list if it is. */ 339 if (vpath_defined && (dir == dot)) { 340 vps.init(getvar(vpath_name)); 341 vpath = vps.get_string(); 342 } 343 344 /* 345 * Prepare the string where we build the full name of the 346 * files in the directory. 347 */ 348 if ((dir->hash.length > 1) || (wcb.get_string()[0] != (int) period_char)) { 349 (void) wscpy(file_name, wcb.get_string()); 350 MBSTOWCS(wcs_buffer, "/"); 351 (void) wscat(file_name, wcs_buffer); 352 file_name_p = file_name + wslen(file_name); 353 } 354 355 /* Open the directory. */ 356 vpath_loop: 357 dir_fd = opendir(dir->string_mb); 358 if (dir_fd == NULL) { 359 return 0; 360 } 361 362 /* Read all the directory entries. */ 363 while ((dp = readdir(dir_fd)) != NULL) { 364 /* We ignore "." and ".." */ 365 if ((dp->d_fileno == 0) || 366 ((dp->d_name[0] == (int) period_char) && 367 ((dp->d_name[1] == 0) || 368 ((dp->d_name[1] == (int) period_char) && 369 (dp->d_name[2] == 0))))) { 370 continue; 371 } 372 /* 373 * Build the full name of the file using whatever 374 * path supplied to the function. 375 */ 376 MBSTOWCS(tmp_wcs_buffer, dp->d_name); 377 (void) wscpy(file_name_p, tmp_wcs_buffer); 378 file = enter_file_name(file_name, library); 379 if ((pattern != NULL) && amatch(tmp_wcs_buffer, pattern)) { 380 /* 381 * If we are expanding a wildcard pattern, we 382 * enter the file as a dependency for the target. 383 */ 384 if (debug_level > 0){ 385 WCSTOMBS(mbs_buffer, pattern); 386 (void) printf(catgets(catd, 1, 231, "'%s: %s' due to %s expansion\n"), 387 line->body.line.target->string_mb, 388 file->string_mb, 389 mbs_buffer); 390 } 391 enter_dependency(line, file, false); 392 result++; 393 } else { 394 /* 395 * If the file has an SCCS/s. file, 396 * we will detect that later on. 397 */ 398 file->stat.has_sccs = NO_SCCS; 399 /* 400 * If this is an s. file, we also enter it as if it 401 * existed in the plain directory. 402 */ 403 if ((dp->d_name[0] == 's') && 404 (dp->d_name[1] == (int) period_char)) { 405 406 MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2); 407 plain_file_name_p = plain_file_name; 408 (void) wscpy(plain_file_name_p, tmp_wcs_buffer); 409 plain_file = GETNAME(plain_file_name, FIND_LENGTH); 410 plain_file->stat.is_file = true; 411 plain_file->stat.has_sccs = HAS_SCCS; 412 /* 413 * Enter the s. file as a dependency for the 414 * plain file. 415 */ 416 maybe_append_prop(plain_file, sccs_prop)-> 417 body.sccs.file = file; 418 MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2); 419 if ((pattern != NULL) && 420 amatch(tmp_wcs_buffer, pattern)) { 421 if (debug_level > 0) { 422 WCSTOMBS(mbs_buffer, pattern); 423 (void) printf(catgets(catd, 1, 232, "'%s: %s' due to %s expansion\n"), 424 line->body.line.target-> 425 string_mb, 426 plain_file->string_mb, 427 mbs_buffer); 428 } 429 enter_dependency(line, plain_file, false); 430 result++; 431 } 432 } 433 } 434 } 435 (void) closedir(dir_fd); 436 if ((vpath != NULL) && (*vpath != (int) nul_char)) { 437 while ((*vpath != (int) nul_char) && 438 (iswspace(*vpath) || (*vpath == (int) colon_char))) { 439 vpath++; 440 } 441 p = vpath; 442 while ((*vpath != (int) colon_char) && 443 (*vpath != (int) nul_char)) { 444 vpath++; 445 } 446 if (vpath > p) { 447 dir = GETNAME(p, vpath - p); 448 goto vpath_loop; 449 } 450 } 451 /* 452 * look into SCCS directory only if it's not svr4. For svr4 dont do that. 453 */ 454 455 /* 456 * Now read the SCCS directory. 457 * Files in the SCSC directory are considered to be part of the set of 458 * files in the plain directory. They are also entered in their own right. 459 * Prepare the string where we build the true name of the SCCS files. 460 */ 461 (void) wsncpy(plain_file_name, 462 file_name, 463 file_name_p - file_name); 464 plain_file_name[file_name_p - file_name] = 0; 465 plain_file_name_p = plain_file_name + wslen(plain_file_name); 466 467 if(!svr4) { 468 469 if (sccs_dir_path != NULL) { 470 wchar_t tmp_wchar; 471 wchar_t path[MAXPATHLEN]; 472 char mb_path[MAXPATHLEN]; 473 474 if (file_name_p - file_name > 0) { 475 tmp_wchar = *file_name_p; 476 *file_name_p = 0; 477 WCSTOMBS(mbs_buffer, file_name); 478 (void) sprintf(mb_path, NOCATGETS("%s/%s/SCCS"), 479 sccs_dir_path, 480 mbs_buffer); 481 *file_name_p = tmp_wchar; 482 } else { 483 (void) sprintf(mb_path, NOCATGETS("%s/SCCS"), sccs_dir_path); 484 } 485 MBSTOWCS(path, mb_path); 486 (void) wscpy(file_name, path); 487 } else { 488 MBSTOWCS(wcs_buffer, NOCATGETS("SCCS")); 489 (void) wscpy(file_name_p, wcs_buffer); 490 } 491 } else { 492 MBSTOWCS(wcs_buffer, NOCATGETS(".")); 493 (void) wscpy(file_name_p, wcs_buffer); 494 } 495 /* Internalize the constructed SCCS dir name. */ 496 (void) exists(dir = GETNAME(file_name, FIND_LENGTH)); 497 /* Just give up if the directory file doesnt exist. */ 498 if (!dir->stat.is_file) { 499 return result; 500 } 501 /* Open the directory. */ 502 dir_fd = opendir(dir->string_mb); 503 if (dir_fd == NULL) { 504 return result; 505 } 506 MBSTOWCS(wcs_buffer, "/"); 507 (void) wscat(file_name, wcs_buffer); 508 file_name_p = file_name + wslen(file_name); 509 510 while ((dp = readdir(dir_fd)) != NULL) { 511 if ((dp->d_fileno == 0) || 512 ((dp->d_name[0] == (int) period_char) && 513 ((dp->d_name[1] == 0) || 514 ((dp->d_name[1] == (int) period_char) && 515 (dp->d_name[2] == 0))))) { 516 continue; 517 } 518 /* Construct and internalize the true name of the SCCS file. */ 519 MBSTOWCS(wcs_buffer, dp->d_name); 520 (void) wscpy(file_name_p, wcs_buffer); 521 file = GETNAME(file_name, FIND_LENGTH); 522 file->stat.is_file = true; 523 file->stat.has_sccs = NO_SCCS; 524 /* 525 * If this is an s. file, we also enter it as if it 526 * existed in the plain directory. 527 */ 528 if ((dp->d_name[0] == 's') && 529 (dp->d_name[1] == (int) period_char)) { 530 531 MBSTOWCS(wcs_buffer, dp->d_name + 2); 532 (void) wscpy(plain_file_name_p, wcs_buffer); 533 plain_file = GETNAME(plain_file_name, FIND_LENGTH); 534 plain_file->stat.is_file = true; 535 plain_file->stat.has_sccs = HAS_SCCS; 536 /* if sccs dependency is already set,skip */ 537 if(plain_file->prop) { 538 Property sprop = get_prop(plain_file->prop,sccs_prop); 539 if(sprop != NULL) { 540 if (sprop->body.sccs.file) { 541 goto try_pattern; 542 } 543 } 544 } 545 546 /* 547 * Enter the s. file as a dependency for the 548 * plain file. 549 */ 550 maybe_append_prop(plain_file, sccs_prop)-> 551 body.sccs.file = file; 552 try_pattern: 553 MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2); 554 if ((pattern != NULL) && 555 amatch(tmp_wcs_buffer, pattern)) { 556 if (debug_level > 0) { 557 WCSTOMBS(mbs_buffer, pattern); 558 (void) printf(catgets(catd, 1, 233, "'%s: %s' due to %s expansion\n"), 559 line->body.line.target-> 560 string_mb, 561 plain_file->string_mb, 562 mbs_buffer); 563 } 564 enter_dependency(line, plain_file, false); 565 result++; 566 } 567 } 568 } 569 (void) closedir(dir_fd); 570 571 return result; 572 } 573 574 /* 575 * enter_file_name(name_string, library) 576 * 577 * Helper function for read_dir(). 578 * 579 * Return value: 580 * The Name that was entered 581 * 582 * Parameters: 583 * name_string Name of the file we want to enter 584 * library The library it is a member of, if any 585 * 586 * Global variables used: 587 */ 588 static Name 589 enter_file_name(wchar_t *name_string, wchar_t *library) 590 { 591 wchar_t buffer[STRING_BUFFER_LENGTH]; 592 String_rec lib_name; 593 Name name; 594 Property prop; 595 596 if (library == NULL) { 597 name = GETNAME(name_string, FIND_LENGTH); 598 name->stat.is_file = true; 599 return name; 600 } 601 602 INIT_STRING_FROM_STACK(lib_name, buffer); 603 append_string(library, &lib_name, FIND_LENGTH); 604 append_char((int) parenleft_char, &lib_name); 605 append_string(name_string, &lib_name, FIND_LENGTH); 606 append_char((int) parenright_char, &lib_name); 607 608 name = GETNAME(lib_name.buffer.start, FIND_LENGTH); 609 name->stat.is_file = true; 610 name->is_member = true; 611 prop = maybe_append_prop(name, member_prop); 612 prop->body.member.library = GETNAME(library, FIND_LENGTH); 613 prop->body.member.library->stat.is_file = true; 614 prop->body.member.entry = NULL; 615 prop->body.member.member = GETNAME(name_string, FIND_LENGTH); 616 prop->body.member.member->stat.is_file = true; 617 return name; 618 } 619 620 /* 621 * star_match(string, pattern) 622 * 623 * This is a regular shell type wildcard pattern matcher 624 * It is used when xpanding wildcards in dependency lists 625 * 626 * Return value: 627 * Indication if the string matched the pattern 628 * 629 * Parameters: 630 * string String to match 631 * pattern Pattern to match it against 632 * 633 * Global variables used: 634 */ 635 static Boolean 636 star_match(register wchar_t *string, register wchar_t *pattern) 637 { 638 register int pattern_ch; 639 640 switch (*pattern) { 641 case 0: 642 return succeeded; 643 case bracketleft_char: 644 case question_char: 645 case asterisk_char: 646 while (*string) { 647 if (amatch(string++, pattern)) { 648 return succeeded; 649 } 650 } 651 break; 652 default: 653 pattern_ch = (int) *pattern++; 654 while (*string) { 655 if ((*string++ == pattern_ch) && 656 amatch(string, pattern)) { 657 return succeeded; 658 } 659 } 660 break; 661 } 662 return failed; 663 } 664 665 /* 666 * amatch(string, pattern) 667 * 668 * Helper function for shell pattern matching 669 * 670 * Return value: 671 * Indication if the string matched the pattern 672 * 673 * Parameters: 674 * string String to match 675 * pattern Pattern to match it against 676 * 677 * Global variables used: 678 */ 679 static Boolean 680 amatch(register wchar_t *string, register wchar_t *pattern) 681 { 682 register long lower_bound; 683 register long string_ch; 684 register long pattern_ch; 685 register int k; 686 687 top: 688 for (; 1; pattern++, string++) { 689 lower_bound = 017777777777; 690 string_ch = *string; 691 switch (pattern_ch = *pattern) { 692 case bracketleft_char: 693 k = 0; 694 while ((pattern_ch = *++pattern) != 0) { 695 switch (pattern_ch) { 696 case bracketright_char: 697 if (!k) { 698 return failed; 699 } 700 string++; 701 pattern++; 702 goto top; 703 case hyphen_char: 704 k |= (lower_bound <= string_ch) && 705 (string_ch <= 706 (pattern_ch = pattern[1])); 707 default: 708 if (string_ch == 709 (lower_bound = pattern_ch)) { 710 k++; 711 } 712 } 713 } 714 return failed; 715 case asterisk_char: 716 return star_match(string, ++pattern); 717 case 0: 718 return BOOLEAN(!string_ch); 719 case question_char: 720 if (string_ch == 0) { 721 return failed; 722 } 723 break; 724 default: 725 if (pattern_ch != string_ch) { 726 return failed; 727 } 728 break; 729 } 730 } 731 /* NOTREACHED */ 732 } 733