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