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