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.
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.
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]
22 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
29 * Various file related routines:
30 * Figure out if file exists
31 * Wildcard resolution for directory reader
39 #include <dirent.h> /* opendir() */
40 #include <errno.h> /* errno */
42 #include <mksh/macro.h> /* getvar() */
43 #include <mksh/misc.h> /* get_prop(), append_prop() */
44 #include <sys/stat.h> /* lstat() */
60 * File table of contents
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
);
72 * Figure out the timestamp for one target.
75 * The time the target was created
78 * target The target to check
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?
86 exists(register Name target
)
91 /* We cache stat information. */
92 if (target
->stat
.time
!= file_no_time
) {
93 return target
->stat
.time
;
97 * If the target is a member, we have to extract the time
100 if (target
->is_member
&&
101 (get_prop(target
->prop
, member_prop
) != NULL
)) {
102 return read_archive(target
);
105 if (debug_level
> 1) {
106 (void) printf("%*sstat(%s)\n",
112 result
= lstat_vroot(target
->string_mb
, &buf
, NULL
, VROOT_DEFAULT
);
113 if ((result
!= -1) && ((buf
.st_mode
& S_IFMT
) == S_IFLNK
)) {
115 * If the file is a symbolic link, we remember that
116 * and then we get the status for the refd file.
118 target
->stat
.is_sym_link
= true;
119 result
= stat_vroot(target
->string_mb
, &buf
, NULL
, VROOT_DEFAULT
);
121 target
->stat
.is_sym_link
= false;
125 target
->stat
.time
= file_doesnt_exist
;
126 target
->stat
.stat_errno
= errno
;
127 if ((errno
== ENOENT
) &&
129 /* azv, fixing bug 1262942, VPATH works with a leaf name
130 * but not a directory name.
132 (target
->string_mb
[0] != (int) slash_char
) ) {
135 vpath_exists(target
);
136 // return vpath_exists(target);
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
;
149 /* target->stat.time = buf.st_mtime; */
151 /* vis@nbsp.nsk.su */
152 target
->stat
.time
= MAX(buf
.st_mtim
, file_min_time
);
155 if ((target
->colon_splits
> 0) &&
156 (get_prop(target
->prop
, time_prop
) == NULL
)) {
157 append_prop(target
, time_prop
)->body
.time
.time
=
160 return target
->stat
.time
;
164 * set_target_stat( target, buf)
166 * Called by exists() to set some stat fields in the Name structure
167 * to those read by the stat_vroot() call (from disk).
170 * target The target whose stat field is set
171 * buf stat values (on disk) of the file
172 * represented by target.
175 set_target_stat(register Name target
, struct stat buf
)
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
;
186 /* target->stat.time = buf.st_mtime; */
188 /* vis@nbsp.nsk.su */
189 target
->stat
.time
= MAX(buf
.st_mtim
, file_min_time
);
195 * vpath_exists(target)
197 * Called if exists() discovers that there is a VPATH defined.
198 * This function stats the VPATH translation of the target.
201 * The time the target was created
204 * target The target to check
206 * Global variables used:
207 * vpath_name The Name "VPATH", used to get macro value
210 vpath_exists(register Name target
)
213 wchar_t file_name
[MAXPATHLEN
];
218 * To avoid recursive search through VPATH when exists(alias) is called
220 vpath_defined
= false;
222 Wstring
wcb(getvar(vpath_name
));
223 Wstring
wcb1(target
);
225 vpath
= wcb
.get_string();
227 while (*vpath
!= (int) nul_char
) {
229 while ((*vpath
!= (int) colon_char
) &&
230 (*vpath
!= (int) nul_char
)) {
231 *name_p
++ = *vpath
++;
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
;
248 while ((*vpath
!= (int) nul_char
) &&
249 ((*vpath
== (int) colon_char
) || iswspace(*vpath
))) {
254 * Restore vpath_defined
256 vpath_defined
= true;
257 return target
->stat
.time
;
261 * read_dir(dir, pattern, line, library)
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.
268 * Non-0 if we found files to match the pattern
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>)"
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
285 read_dir(Name dir
, wchar_t *pattern
, Property line
, wchar_t *library
)
287 wchar_t file_name
[MAXPATHLEN
];
288 wchar_t *file_name_p
= file_name
;
290 wchar_t plain_file_name
[MAXPATHLEN
];
291 wchar_t *plain_file_name_p
;
293 wchar_t tmp_wcs_buffer
[MAXPATHLEN
];
295 int m_local_dependency
=0;
296 #define d_fileno d_ino
297 register struct dirent
*dp
;
298 wchar_t *vpath
= NULL
;
302 if(dir
->hash
.length
>= MAXPATHLEN
) {
309 /* A directory is only read once unless we need to expand wildcards. */
310 if (pattern
== NULL
) {
311 if (dir
->has_read_dir
) {
314 dir
->has_read_dir
= true;
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();
323 * Prepare the string where we build the full name of the
324 * files in the directory.
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
);
333 /* Open the directory. */
335 dir_fd
= opendir(dir
->string_mb
);
336 if (dir_fd
== NULL
) {
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))))) {
351 * Build the full name of the file using whatever
352 * path supplied to the function.
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
)) {
359 * If we are expanding a wildcard pattern, we
360 * enter the file as a dependency for the target.
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
,
369 enter_dependency(line
, file
, false);
373 * If the file has an SCCS/s. file,
374 * we will detect that later on.
376 file
->stat
.has_sccs
= NO_SCCS
;
378 * If this is an s. file, we also enter it as if it
379 * existed in the plain directory.
381 if ((dp
->d_name
[0] == 's') &&
382 (dp
->d_name
[1] == (int) period_char
)) {
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
;
391 * Enter the s. file as a dependency for the
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
->
404 plain_file
->string_mb
,
407 enter_dependency(line
, plain_file
, false);
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
))) {
420 while ((*vpath
!= (int) colon_char
) &&
421 (*vpath
!= (int) nul_char
)) {
425 dir
= GETNAME(p
, vpath
- p
);
430 * look into SCCS directory only if it's not svr4. For svr4 dont do that.
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.
439 (void) wcsncpy(plain_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
);
447 if (sccs_dir_path
!= NULL
) {
449 wchar_t path
[MAXPATHLEN
];
450 char mb_path
[MAXPATHLEN
];
452 if (file_name_p
- file_name
> 0) {
453 tmp_wchar
= *file_name_p
;
455 WCSTOMBS(mbs_buffer
, file_name
);
456 (void) sprintf(mb_path
, "%s/%s/SCCS",
459 *file_name_p
= tmp_wchar
;
461 (void) sprintf(mb_path
, "%s/SCCS", sccs_dir_path
);
463 MBSTOWCS(path
, mb_path
);
464 (void) wcscpy(file_name
, path
);
466 MBSTOWCS(wcs_buffer
, "SCCS");
467 (void) wcscpy(file_name_p
, wcs_buffer
);
470 MBSTOWCS(wcs_buffer
, ".");
471 (void) wcscpy(file_name_p
, wcs_buffer
);
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
) {
479 /* Open the directory. */
480 dir_fd
= opendir(dir
->string_mb
);
481 if (dir_fd
== NULL
) {
484 MBSTOWCS(wcs_buffer
, "/");
485 (void) wcscat(file_name
, wcs_buffer
);
486 file_name_p
= file_name
+ wcslen(file_name
);
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))))) {
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
;
503 * If this is an s. file, we also enter it as if it
504 * existed in the plain directory.
506 if ((dp
->d_name
[0] == 's') &&
507 (dp
->d_name
[1] == (int) period_char
)) {
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
);
518 if (sprop
->body
.sccs
.file
) {
525 * Enter the s. file as a dependency for the
528 maybe_append_prop(plain_file
, sccs_prop
)->
529 body
.sccs
.file
= file
;
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
->
539 plain_file
->string_mb
,
542 enter_dependency(line
, plain_file
, false);
547 (void) closedir(dir_fd
);
553 * enter_file_name(name_string, library)
555 * Helper function for read_dir().
558 * The Name that was entered
561 * name_string Name of the file we want to enter
562 * library The library it is a member of, if any
564 * Global variables used:
567 enter_file_name(wchar_t *name_string
, wchar_t *library
)
569 wchar_t buffer
[STRING_BUFFER_LENGTH
];
574 if (library
== NULL
) {
575 name
= GETNAME(name_string
, FIND_LENGTH
);
576 name
->stat
.is_file
= true;
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
);
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;
599 * star_match(string, pattern)
601 * This is a regular shell type wildcard pattern matcher
602 * It is used when xpanding wildcards in dependency lists
605 * Indication if the string matched the pattern
608 * string String to match
609 * pattern Pattern to match it against
611 * Global variables used:
614 star_match(register wchar_t *string
, register wchar_t *pattern
)
616 register int pattern_ch
;
621 case bracketleft_char
:
625 if (amatch(string
++, pattern
)) {
631 pattern_ch
= (int) *pattern
++;
633 if ((*string
++ == pattern_ch
) &&
634 amatch(string
, pattern
)) {
644 * amatch(string, pattern)
646 * Helper function for shell pattern matching
649 * Indication if the string matched the pattern
652 * string String to match
653 * pattern Pattern to match it against
655 * Global variables used:
658 amatch(register wchar_t *string
, register wchar_t *pattern
)
660 register long lower_bound
;
661 register long string_ch
;
662 register long pattern_ch
;
666 for (; 1; pattern
++, string
++) {
667 lower_bound
= 017777777777;
669 switch (pattern_ch
= *pattern
) {
670 case bracketleft_char
:
672 while ((pattern_ch
= *++pattern
) != 0) {
673 switch (pattern_ch
) {
674 case bracketright_char
:
682 k
|= (lower_bound
<= string_ch
) &&
684 (pattern_ch
= pattern
[1]));
687 (lower_bound
= pattern_ch
)) {
694 return star_match(string
, ++pattern
);
696 return BOOLEAN(!string_ch
);
698 if (string_ch
== 0) {
703 if (pattern_ch
!= string_ch
) {