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