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