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