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