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