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 #include <libintl.h>
46
47 /*
48 * Defined macros
49 */
50
51 /*
52 * typedefs & structs
53 */
54
55 /*
56 * Static variables
57 */
58
59 /*
60 * File table of contents
61 */
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);
68
69 /*
70 * exists(target)
71 *
72 * Figure out the timestamp for one target.
73 *
74 * Return value:
75 * The time the target was created
76 *
77 * Parameters:
78 * target The target to check
79 *
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?
84 */
85 timestruc_t&
exists(register Name target)86 exists(register Name target)
87 {
88 struct stat buf;
89 register int result;
90
91 /* We cache stat information. */
92 if (target->stat.time != file_no_time) {
93 return target->stat.time;
94 }
95
96 /*
97 * If the target is a member, we have to extract the time
98 * from the archive.
99 */
100 if (target->is_member &&
101 (get_prop(target->prop, member_prop) != NULL)) {
102 return read_archive(target);
103 }
104
105 if (debug_level > 1) {
106 (void) printf("%*sstat(%s)\n",
107 recursion_level,
108 "",
109 target->string_mb);
110 }
111
112 result = lstat_vroot(target->string_mb, &buf, NULL, VROOT_DEFAULT);
113 if ((result != -1) && ((buf.st_mode & S_IFMT) == S_IFLNK)) {
114 /*
115 * If the file is a symbolic link, we remember that
116 * and then we get the status for the refd file.
117 */
118 target->stat.is_sym_link = true;
119 result = stat_vroot(target->string_mb, &buf, NULL, VROOT_DEFAULT);
120 } else {
121 target->stat.is_sym_link = false;
122 }
123
124 if (result < 0) {
125 target->stat.time = file_doesnt_exist;
126 target->stat.stat_errno = errno;
127 if ((errno == ENOENT) &&
128 vpath_defined &&
129 /* azv, fixing bug 1262942, VPATH works with a leaf name
130 * but not a directory name.
131 */
132 (target->string_mb[0] != (int) slash_char) ) {
133 /* BID_1214655 */
134 /* azv */
135 vpath_exists(target);
136 // return vpath_exists(target);
137 }
138 } else {
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;
148 } else {
149 /* target->stat.time = buf.st_mtime; */
150 /* BID_1129806 */
151 /* vis@nbsp.nsk.su */
152 target->stat.time = MAX(buf.st_mtim, file_min_time);
153 }
154 }
155 if ((target->colon_splits > 0) &&
156 (get_prop(target->prop, time_prop) == NULL)) {
157 append_prop(target, time_prop)->body.time.time =
158 target->stat.time;
159 }
160 return target->stat.time;
161 }
162
163 /*
164 * set_target_stat( target, buf)
165 *
166 * Called by exists() to set some stat fields in the Name structure
167 * to those read by the stat_vroot() call (from disk).
168 *
169 * Parameters:
170 * target The target whose stat field is set
171 * buf stat values (on disk) of the file
172 * represented by target.
173 */
174 void
set_target_stat(register Name target,struct stat buf)175 set_target_stat(register Name target, struct stat buf)
176 {
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;
185 } else {
186 /* target->stat.time = buf.st_mtime; */
187 /* BID_1129806 */
188 /* vis@nbsp.nsk.su */
189 target->stat.time = MAX(buf.st_mtim, file_min_time);
190 }
191 }
192
193
194 /*
195 * vpath_exists(target)
196 *
197 * Called if exists() discovers that there is a VPATH defined.
198 * This function stats the VPATH translation of the target.
199 *
200 * Return value:
201 * The time the target was created
202 *
203 * Parameters:
204 * target The target to check
205 *
206 * Global variables used:
207 * vpath_name The Name "VPATH", used to get macro value
208 */
209 static timestruc_t&
vpath_exists(register Name target)210 vpath_exists(register Name target)
211 {
212 wchar_t *vpath;
213 wchar_t file_name[MAXPATHLEN];
214 wchar_t *name_p;
215 Name alias;
216
217 /*
218 * To avoid recursive search through VPATH when exists(alias) is called
219 */
220 vpath_defined = false;
221
222 Wstring wcb(getvar(vpath_name));
223 Wstring wcb1(target);
224
225 vpath = wcb.get_string();
226
227 while (*vpath != (int) nul_char) {
228 name_p = file_name;
229 while ((*vpath != (int) colon_char) &&
230 (*vpath != (int) nul_char)) {
231 *name_p++ = *vpath++;
232 }
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;
247 }
248 while ((*vpath != (int) nul_char) &&
249 ((*vpath == (int) colon_char) || iswspace(*vpath))) {
250 vpath++;
251 }
252 }
253 /*
254 * Restore vpath_defined
255 */
256 vpath_defined = true;
257 return target->stat.time;
258 }
259
260 /*
261 * read_dir(dir, pattern, line, library)
262 *
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.
266 *
267 * Return value:
268 * Non-0 if we found files to match the pattern
269 *
270 * Parameters:
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>)"
276 *
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
283 */
284 int
read_dir(Name dir,wchar_t * pattern,Property line,wchar_t * library)285 read_dir(Name dir, wchar_t *pattern, Property line, wchar_t *library)
286 {
287 wchar_t file_name[MAXPATHLEN];
288 wchar_t *file_name_p = file_name;
289 Name file;
290 wchar_t plain_file_name[MAXPATHLEN];
291 wchar_t *plain_file_name_p;
292 Name plain_file;
293 wchar_t tmp_wcs_buffer[MAXPATHLEN];
294 DIR *dir_fd;
295 int m_local_dependency=0;
296 #define d_fileno d_ino
297 register struct dirent *dp;
298 wchar_t *vpath = NULL;
299 wchar_t *p;
300 int result = 0;
301
302 if(dir->hash.length >= MAXPATHLEN) {
303 return 0;
304 }
305
306 Wstring wcb(dir);
307 Wstring vps;
308
309 /* A directory is only read once unless we need to expand wildcards. */
310 if (pattern == NULL) {
311 if (dir->has_read_dir) {
312 return 0;
313 }
314 dir->has_read_dir = true;
315 }
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();
320 }
321
322 /*
323 * Prepare the string where we build the full name of the
324 * files in the directory.
325 */
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);
331 }
332
333 /* Open the directory. */
334 vpath_loop:
335 dir_fd = opendir(dir->string_mb);
336 if (dir_fd == NULL) {
337 return 0;
338 }
339
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))))) {
348 continue;
349 }
350 /*
351 * Build the full name of the file using whatever
352 * path supplied to the function.
353 */
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)) {
358 /*
359 * If we are expanding a wildcard pattern, we
360 * enter the file as a dependency for the target.
361 */
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,
366 file->string_mb,
367 mbs_buffer);
368 }
369 enter_dependency(line, file, false);
370 result++;
371 } else {
372 /*
373 * If the file has an SCCS/s. file,
374 * we will detect that later on.
375 */
376 file->stat.has_sccs = NO_SCCS;
377 /*
378 * If this is an s. file, we also enter it as if it
379 * existed in the plain directory.
380 */
381 if ((dp->d_name[0] == 's') &&
382 (dp->d_name[1] == (int) period_char)) {
383
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;
390 /*
391 * Enter the s. file as a dependency for the
392 * plain file.
393 */
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->
403 string_mb,
404 plain_file->string_mb,
405 mbs_buffer);
406 }
407 enter_dependency(line, plain_file, false);
408 result++;
409 }
410 }
411 }
412 }
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))) {
417 vpath++;
418 }
419 p = vpath;
420 while ((*vpath != (int) colon_char) &&
421 (*vpath != (int) nul_char)) {
422 vpath++;
423 }
424 if (vpath > p) {
425 dir = GETNAME(p, vpath - p);
426 goto vpath_loop;
427 }
428 }
429 /*
430 * look into SCCS directory only if it's not svr4. For svr4 dont do that.
431 */
432
433 /*
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.
438 */
439 (void) wcsncpy(plain_file_name,
440 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);
444
445 if(!svr4) {
446
447 if (sccs_dir_path != NULL) {
448 wchar_t tmp_wchar;
449 wchar_t path[MAXPATHLEN];
450 char mb_path[MAXPATHLEN];
451
452 if (file_name_p - file_name > 0) {
453 tmp_wchar = *file_name_p;
454 *file_name_p = 0;
455 WCSTOMBS(mbs_buffer, file_name);
456 (void) sprintf(mb_path, "%s/%s/SCCS",
457 sccs_dir_path,
458 mbs_buffer);
459 *file_name_p = tmp_wchar;
460 } else {
461 (void) sprintf(mb_path, "%s/SCCS", sccs_dir_path);
462 }
463 MBSTOWCS(path, mb_path);
464 (void) wcscpy(file_name, path);
465 } else {
466 MBSTOWCS(wcs_buffer, "SCCS");
467 (void) wcscpy(file_name_p, wcs_buffer);
468 }
469 } else {
470 MBSTOWCS(wcs_buffer, ".");
471 (void) wcscpy(file_name_p, wcs_buffer);
472 }
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) {
477 return result;
478 }
479 /* Open the directory. */
480 dir_fd = opendir(dir->string_mb);
481 if (dir_fd == NULL) {
482 return result;
483 }
484 MBSTOWCS(wcs_buffer, "/");
485 (void) wcscat(file_name, wcs_buffer);
486 file_name_p = file_name + wcslen(file_name);
487
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))))) {
494 continue;
495 }
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;
502 /*
503 * If this is an s. file, we also enter it as if it
504 * existed in the plain directory.
505 */
506 if ((dp->d_name[0] == 's') &&
507 (dp->d_name[1] == (int) period_char)) {
508
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);
517 if(sprop != NULL) {
518 if (sprop->body.sccs.file) {
519 goto try_pattern;
520 }
521 }
522 }
523
524 /*
525 * Enter the s. file as a dependency for the
526 * plain file.
527 */
528 maybe_append_prop(plain_file, sccs_prop)->
529 body.sccs.file = file;
530 try_pattern:
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->
538 string_mb,
539 plain_file->string_mb,
540 mbs_buffer);
541 }
542 enter_dependency(line, plain_file, false);
543 result++;
544 }
545 }
546 }
547 (void) closedir(dir_fd);
548
549 return result;
550 }
551
552 /*
553 * enter_file_name(name_string, library)
554 *
555 * Helper function for read_dir().
556 *
557 * Return value:
558 * The Name that was entered
559 *
560 * Parameters:
561 * name_string Name of the file we want to enter
562 * library The library it is a member of, if any
563 *
564 * Global variables used:
565 */
566 static Name
enter_file_name(wchar_t * name_string,wchar_t * library)567 enter_file_name(wchar_t *name_string, wchar_t *library)
568 {
569 wchar_t buffer[STRING_BUFFER_LENGTH];
570 String_rec lib_name;
571 Name name;
572 Property prop;
573
574 if (library == NULL) {
575 name = GETNAME(name_string, FIND_LENGTH);
576 name->stat.is_file = true;
577 return name;
578 }
579
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);
585
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;
595 return name;
596 }
597
598 /*
599 * star_match(string, pattern)
600 *
601 * This is a regular shell type wildcard pattern matcher
602 * It is used when xpanding wildcards in dependency lists
603 *
604 * Return value:
605 * Indication if the string matched the pattern
606 *
607 * Parameters:
608 * string String to match
609 * pattern Pattern to match it against
610 *
611 * Global variables used:
612 */
613 static Boolean
star_match(register wchar_t * string,register wchar_t * pattern)614 star_match(register wchar_t *string, register wchar_t *pattern)
615 {
616 register int pattern_ch;
617
618 switch (*pattern) {
619 case 0:
620 return succeeded;
621 case bracketleft_char:
622 case question_char:
623 case asterisk_char:
624 while (*string) {
625 if (amatch(string++, pattern)) {
626 return succeeded;
627 }
628 }
629 break;
630 default:
631 pattern_ch = (int) *pattern++;
632 while (*string) {
633 if ((*string++ == pattern_ch) &&
634 amatch(string, pattern)) {
635 return succeeded;
636 }
637 }
638 break;
639 }
640 return failed;
641 }
642
643 /*
644 * amatch(string, pattern)
645 *
646 * Helper function for shell pattern matching
647 *
648 * Return value:
649 * Indication if the string matched the pattern
650 *
651 * Parameters:
652 * string String to match
653 * pattern Pattern to match it against
654 *
655 * Global variables used:
656 */
657 static Boolean
amatch(register wchar_t * string,register wchar_t * pattern)658 amatch(register wchar_t *string, register wchar_t *pattern)
659 {
660 register long lower_bound;
661 register long string_ch;
662 register long pattern_ch;
663 register int k;
664
665 top:
666 for (; 1; pattern++, string++) {
667 lower_bound = 017777777777;
668 string_ch = *string;
669 switch (pattern_ch = *pattern) {
670 case bracketleft_char:
671 k = 0;
672 while ((pattern_ch = *++pattern) != 0) {
673 switch (pattern_ch) {
674 case bracketright_char:
675 if (!k) {
676 return failed;
677 }
678 string++;
679 pattern++;
680 goto top;
681 case hyphen_char:
682 k |= (lower_bound <= string_ch) &&
683 (string_ch <=
684 (pattern_ch = pattern[1]));
685 default:
686 if (string_ch ==
687 (lower_bound = pattern_ch)) {
688 k++;
689 }
690 }
691 }
692 return failed;
693 case asterisk_char:
694 return star_match(string, ++pattern);
695 case 0:
696 return BOOLEAN(!string_ch);
697 case question_char:
698 if (string_ch == 0) {
699 return failed;
700 }
701 break;
702 default:
703 if (pattern_ch != string_ch) {
704 return failed;
705 }
706 break;
707 }
708 }
709 /* NOTREACHED */
710 }
711
712