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