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