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