1 /*
2 * CDDL HEADER START
3 *
4 * This file and its contents are supplied under the terms of the
5 * Common Development and Distribution License ("CDDL"), version 1.0.
6 * You may use this file only in accordance with the terms of version
7 * 1.0 of the CDDL.
8 *
9 * A full copy of the text of the CDDL should have accompanied this
10 * source. A copy of the CDDL is also available via the Internet at
11 * http://www.opensource.org/licenses/cddl1.txt
12 * See the License for the specific language governing permissions
13 * and limitations under the License.
14 *
15 * When distributing Covered Code, include this CDDL HEADER in each
16 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17 * If applicable, add the following below this CDDL HEADER, with the
18 * fields enclosed by brackets "[]" replaced with your own identifying
19 * information: Portions Copyright [yyyy] [name of copyright owner]
20 *
21 * CDDL HEADER END
22 */
23 /*
24 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 */
27 /*
28 * @(#)files.cc 1.37 06/12/12
29 */
30
31 #pragma ident "@(#)files.cc 1.37 06/12/12"
32
33 /*
34 * Copyright 2017-2020 J. Schilling
35 *
36 * @(#)files.cc 1.11 20/11/19 2017-2020 J. Schilling
37 */
38 #include <schily/mconfig.h>
39 #ifndef lint
40 static UConst char sccsid[] =
41 "@(#)files.cc 1.11 20/11/19 2017-2020 J. Schilling";
42 #endif
43
44 /*
45 * files.c
46 *
47 * Various file related routines:
48 * Figure out if file exists
49 * Wildcard resolution for directory reader
50 * Directory reader
51 */
52
53
54 /*
55 * Included files
56 */
57 #include <mk/defs.h>
58 #include <mksh/macro.h> /* getvar() */
59 #include <mksh/misc.h> /* get_prop(), append_prop() */
60
61 /*
62 * Defined macros
63 */
64
65 /*
66 * typedefs & structs
67 */
68
69 /*
70 * Static variables
71 */
72
73 /*
74 * File table of contents
75 */
76 extern timestruc_t& exists(register Name target);
77 extern void set_target_stat(register Name target, struct stat buf);
78 static timestruc_t& vpath_exists(register Name target);
79 static Name enter_file_name(wchar_t *name_string, wchar_t *library);
80 static Boolean star_match(register char *string, register char *pattern);
81 static Boolean amatch(register wchar_t *string, register wchar_t *pattern);
82
83 /*
84 * exists(target)
85 *
86 * Figure out the timestamp for one target.
87 *
88 * Return value:
89 * The time the target was created
90 *
91 * Parameters:
92 * target The target to check
93 *
94 * Global variables used:
95 * debug_level Should we trace the stat call?
96 * recursion_level Used for tracing
97 * vpath_defined Was the variable VPATH defined in environment?
98 */
99 timestruc_t&
exists(register Name target)100 exists(register Name target)
101 {
102 struct stat buf;
103 register int result;
104
105 /* We cache stat information. */
106 if (target->stat.time != file_no_time) {
107 return target->stat.time;
108 }
109
110 if (target->stat.is_phony && target->stat.time == file_no_time) {
111 target->stat.time = file_phony_time;
112 return target->stat.time;
113 }
114
115 /*
116 * If the target is a member, we have to extract the time
117 * from the archive.
118 */
119 if (target->is_member &&
120 (get_prop(target->prop, member_prop) != NULL)) {
121 return read_archive(target);
122 }
123
124 if (debug_level > 1) {
125 (void) printf(NOCATGETS("%*sstat(%s)\n"),
126 recursion_level,
127 "",
128 target->string_mb);
129 }
130
131 result = lstat_vroot(target->string_mb, &buf, NULL, VROOT_DEFAULT);
132 if ((result != -1) && ((buf.st_mode & S_IFMT) == S_IFLNK)) {
133 /*
134 * If the file is a symbolic link, we remember that
135 * and then we get the status for the refd file.
136 */
137 target->stat.is_sym_link = true;
138 result = stat_vroot(target->string_mb, &buf, NULL, VROOT_DEFAULT);
139 } else {
140 target->stat.is_sym_link = false;
141 }
142
143 if (result < 0) {
144 target->stat.time = file_doesnt_exist;
145 target->stat.stat_errno = errno;
146 if ((errno == ENOENT) &&
147 vpath_defined &&
148 /* azv, fixing bug 1262942, VPATH works with a leaf name
149 * but not a directory name.
150 */
151 (target->string_mb[0] != (int) slash_char) ) {
152 /* BID_1214655 */
153 /* azv */
154 vpath_exists(target);
155 // return vpath_exists(target);
156 }
157 } else {
158 /* Save all the information we need about the file */
159 target->stat.stat_errno = 0;
160 target->stat.is_file = true;
161 target->stat.mode = buf.st_mode & 0777;
162 target->stat.size = buf.st_size;
163 target->stat.is_dir =
164 BOOLEAN((buf.st_mode & S_IFMT) == S_IFDIR);
165 if (target->stat.is_dir) {
166 target->stat.time = file_is_dir;
167 } else {
168 /* target->stat.time = buf.st_mtime; */
169
170 #ifdef stat_mnsecs
171 timestruc_t ttime = { buf.st_mtime, stat_mnsecs(&buf) };
172 target->stat.time = MAX(ttime, file_min_time);
173
174 /* BID_1129806 */
175 /* vis@nbsp.nsk.su */
176 #elif defined(linux)
177 timestruc_t ttime = { buf.st_mtime, 0 };
178 target->stat.time = MAX(ttime, file_min_time);
179 #else
180 target->stat.time = MAX(buf.st_mtim, file_min_time);
181 #endif
182 }
183 }
184 if ((target->colon_splits > 0) &&
185 (get_prop(target->prop, time_prop) == NULL)) {
186 append_prop(target, time_prop)->body.time.time =
187 target->stat.time;
188 }
189 return target->stat.time;
190 }
191
192 /*
193 * set_target_stat( target, buf)
194 *
195 * Called by exists() to set some stat fields in the Name structure
196 * to those read by the stat_vroot() call (from disk).
197 *
198 * Parameters:
199 * target The target whose stat field is set
200 * buf stat values (on disk) of the file
201 * represented by target.
202 */
203 void
set_target_stat(register Name target,struct stat buf)204 set_target_stat(register Name target, struct stat buf)
205 {
206 target->stat.stat_errno = 0;
207 target->stat.is_file = true;
208 target->stat.mode = buf.st_mode & 0777;
209 target->stat.size = buf.st_size;
210 target->stat.is_dir =
211 BOOLEAN((buf.st_mode & S_IFMT) == S_IFDIR);
212 if (target->stat.is_dir) {
213 target->stat.time = file_is_dir;
214 } else {
215 /* target->stat.time = buf.st_mtime; */
216
217 #ifdef stat_mnsecs
218 timestruc_t ttime = { buf.st_mtime, stat_mnsecs(&buf) };
219 target->stat.time = MAX(ttime, file_min_time);
220 /* BID_1129806 */
221 /* vis@nbsp.nsk.su */
222 #elif defined(linux)
223 timestruc_t ttime = { buf.st_mtime, 0 };
224 target->stat.time = ttime;
225 #else
226 target->stat.time = MAX(buf.st_mtim, file_min_time);
227 #endif
228 }
229 }
230
231
232 /*
233 * vpath_exists(target)
234 *
235 * Called if exists() discovers that there is a VPATH defined.
236 * This function stats the VPATH translation of the target.
237 *
238 * Return value:
239 * The time the target was created
240 *
241 * Parameters:
242 * target The target to check
243 *
244 * Global variables used:
245 * vpath_name The Name "VPATH", used to get macro value
246 */
247 static timestruc_t&
vpath_exists(register Name target)248 vpath_exists(register Name target)
249 {
250 wchar_t *vpath;
251 wchar_t file_name[MAXPATHLEN];
252 wchar_t *name_p;
253 Name alias;
254
255 /*
256 * To avoid recursive search through VPATH when exists(alias) is called
257 */
258 vpath_defined = false;
259
260 Wstring wcb(getvar(vpath_name));
261 Wstring wcb1(target);
262
263 vpath = wcb.get_string();
264
265 while (*vpath != (int) nul_char) {
266 name_p = file_name;
267 while ((*vpath != (int) colon_char) &&
268 (*vpath != (int) nul_char)) {
269 *name_p++ = *vpath++;
270 }
271 *name_p++ = (int) slash_char;
272 (void) wcscpy(name_p, wcb1.get_string());
273 alias = GETNAME(file_name, FIND_LENGTH);
274 if (exists(alias) != file_doesnt_exist) {
275 target->stat.is_file = true;
276 target->stat.mode = alias->stat.mode;
277 target->stat.size = alias->stat.size;
278 target->stat.is_dir = alias->stat.is_dir;
279 target->stat.time = alias->stat.time;
280 maybe_append_prop(target, vpath_alias_prop)->
281 body.vpath_alias.alias = alias;
282 target->has_vpath_alias_prop = true;
283 vpath_defined = true;
284 return alias->stat.time;
285 }
286 while ((*vpath != (int) nul_char) &&
287 ((*vpath == (int) colon_char) || iswspace(*vpath))) {
288 vpath++;
289 }
290 }
291 /*
292 * Restore vpath_defined
293 */
294 vpath_defined = true;
295 return target->stat.time;
296 }
297
298 /*
299 * read_dir(dir, pattern, line, library)
300 *
301 * Used to enter the contents of directories into makes namespace.
302 * Presence of a file is important when scanning for implicit rules.
303 * read_dir() is also used to expand wildcards in dependency lists.
304 *
305 * Return value:
306 * Non-0 if we found files to match the pattern
307 *
308 * Parameters:
309 * dir Path to the directory to read
310 * pattern Pattern for that files should match or NULL
311 * line When we scan using a pattern we enter files
312 * we find as dependencies for this line
313 * library If we scan for "lib.a(<wildcard-member>)"
314 *
315 * Global variables used:
316 * debug_level Should we trace the dir reading?
317 * dot The Name ".", compared against
318 * sccs_dir_path The path to the SCCS dir (from PROJECTDIR)
319 * vpath_defined Was the variable VPATH defined in environment?
320 * vpath_name The Name "VPATH", use to get macro value
321 */
322 int
read_dir(Name dir,wchar_t * pattern,Property line,wchar_t * library)323 read_dir(Name dir, wchar_t *pattern, Property line, wchar_t *library)
324 {
325 wchar_t file_name[MAXPATHLEN];
326 wchar_t *file_name_p = file_name;
327 Name file;
328 wchar_t plain_file_name[MAXPATHLEN];
329 wchar_t *plain_file_name_p;
330 Name plain_file;
331 wchar_t tmp_wcs_buffer[MAXPATHLEN];
332 DIR *dir_fd;
333 int m_local_dependency=0;
334 #if defined(SUN5_0) || defined(HP_UX)
335 #define d_fileno d_ino
336 register struct dirent *dp;
337 #else
338 register struct direct *dp;
339 #endif
340 wchar_t *vpath = NULL;
341 wchar_t *p;
342 int result = 0;
343
344 if(dir->hash.length >= MAXPATHLEN) {
345 return 0;
346 }
347
348 Wstring wcb(dir);
349 Wstring vps;
350
351 /* A directory is only read once unless we need to expand wildcards. */
352 if (pattern == NULL) {
353 if (dir->has_read_dir) {
354 return 0;
355 }
356 dir->has_read_dir = true;
357 }
358 /* Check if VPATH is active and setup list if it is. */
359 if (vpath_defined && (dir == dot)) {
360 vps.init(getvar(vpath_name));
361 vpath = vps.get_string();
362 }
363
364 /*
365 * Prepare the string where we build the full name of the
366 * files in the directory.
367 */
368 if ((dir->hash.length > 1) || (wcb.get_string()[0] != (int) period_char)) {
369 (void) wcscpy(file_name, wcb.get_string());
370 MBSTOWCS(wcs_buffer, "/");
371 (void) wcscat(file_name, wcs_buffer);
372 file_name_p = file_name + wcslen(file_name);
373 }
374
375 /* Open the directory. */
376 vpath_loop:
377 dir_fd = opendir(dir->string_mb);
378 if (dir_fd == NULL) {
379 return 0;
380 }
381
382 /* Read all the directory entries. */
383 while ((dp = readdir(dir_fd)) != NULL) {
384 /* We ignore "." and ".." */
385 if ((dp->d_fileno == 0) ||
386 ((dp->d_name[0] == (int) period_char) &&
387 ((dp->d_name[1] == 0) ||
388 ((dp->d_name[1] == (int) period_char) &&
389 (dp->d_name[2] == 0))))) {
390 continue;
391 }
392 /*
393 * Build the full name of the file using whatever
394 * path supplied to the function.
395 */
396 MBSTOWCS(tmp_wcs_buffer, dp->d_name);
397 (void) wcscpy(file_name_p, tmp_wcs_buffer);
398 file = enter_file_name(file_name, library);
399 if ((pattern != NULL) && amatch(tmp_wcs_buffer, pattern)) {
400 /*
401 * If we are expanding a wildcard pattern, we
402 * enter the file as a dependency for the target.
403 */
404 if (debug_level > 0){
405 WCSTOMBS(mbs_buffer, pattern);
406 (void) printf(gettext("'%s: %s' due to %s expansion\n"),
407 line->body.line.target->string_mb,
408 file->string_mb,
409 mbs_buffer);
410 }
411 enter_dependency(line, file, false);
412 result++;
413 } else {
414 /*
415 * If the file has an SCCS/s. file,
416 * we will detect that later on.
417 */
418 file->stat.has_sccs = NO_SCCS;
419 /*
420 * If this is an s. file, we also enter it as if it
421 * existed in the plain directory.
422 */
423 if ((dp->d_name[0] == 's') &&
424 (dp->d_name[1] == (int) period_char)) {
425
426 MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2);
427 plain_file_name_p = plain_file_name;
428 (void) wcscpy(plain_file_name_p, tmp_wcs_buffer);
429 plain_file = GETNAME(plain_file_name, FIND_LENGTH);
430 plain_file->stat.is_file = true;
431 plain_file->stat.has_sccs = HAS_SCCS;
432 /*
433 * Enter the s. file as a dependency for the
434 * plain file.
435 */
436 maybe_append_prop(plain_file, sccs_prop)->
437 body.sccs.file = file;
438 MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2);
439 if ((pattern != NULL) &&
440 amatch(tmp_wcs_buffer, pattern)) {
441 if (debug_level > 0) {
442 WCSTOMBS(mbs_buffer, pattern);
443 (void) printf(gettext("'%s: %s' due to %s expansion\n"),
444 line->body.line.target->
445 string_mb,
446 plain_file->string_mb,
447 mbs_buffer);
448 }
449 enter_dependency(line, plain_file, false);
450 result++;
451 }
452 }
453 }
454 }
455 (void) closedir(dir_fd);
456 if ((vpath != NULL) && (*vpath != (int) nul_char)) {
457 while ((*vpath != (int) nul_char) &&
458 (iswspace(*vpath) || (*vpath == (int) colon_char))) {
459 vpath++;
460 }
461 p = vpath;
462 while ((*vpath != (int) colon_char) &&
463 (*vpath != (int) nul_char)) {
464 vpath++;
465 }
466 if (vpath > p) {
467 dir = GETNAME(p, vpath - p);
468 goto vpath_loop;
469 }
470 }
471 /*
472 * look into SCCS directory only if it's not svr4. For svr4 dont do that.
473 */
474
475 /*
476 * Now read the SCCS directory.
477 * Files in the SCSC directory are considered to be part of the set of
478 * files in the plain directory. They are also entered in their own right.
479 * Prepare the string where we build the true name of the SCCS files.
480 */
481 (void) wcsncpy(plain_file_name,
482 file_name,
483 file_name_p - file_name);
484 plain_file_name[file_name_p - file_name] = 0;
485 plain_file_name_p = plain_file_name + wcslen(plain_file_name);
486
487 if(!svr4) {
488
489 if (sccs_dir_path != NULL) {
490 wchar_t tmp_wchar;
491 wchar_t path[MAXPATHLEN];
492 char mb_path[MAXPATHLEN];
493
494 if (file_name_p - file_name > 0) {
495 tmp_wchar = *file_name_p;
496 *file_name_p = 0;
497 WCSTOMBS(mbs_buffer, file_name);
498 (void) sprintf(mb_path, NOCATGETS("%s/%s/SCCS"),
499 sccs_dir_path,
500 mbs_buffer);
501 *file_name_p = tmp_wchar;
502 } else {
503 (void) sprintf(mb_path, NOCATGETS("%s/SCCS"), sccs_dir_path);
504 }
505 MBSTOWCS(path, mb_path);
506 (void) wcscpy(file_name, path);
507 } else {
508 MBSTOWCS(wcs_buffer, NOCATGETS("SCCS"));
509 (void) wcscpy(file_name_p, wcs_buffer);
510 }
511 } else {
512 MBSTOWCS(wcs_buffer, NOCATGETS("."));
513 (void) wcscpy(file_name_p, wcs_buffer);
514 }
515 /* Internalize the constructed SCCS dir name. */
516 (void) exists(dir = GETNAME(file_name, FIND_LENGTH));
517 /* Just give up if the directory file doesnt exist. */
518 if (!dir->stat.is_file) {
519 return result;
520 }
521 /* Open the directory. */
522 dir_fd = opendir(dir->string_mb);
523 if (dir_fd == NULL) {
524 return result;
525 }
526 MBSTOWCS(wcs_buffer, "/");
527 (void) wcscat(file_name, wcs_buffer);
528 file_name_p = file_name + wcslen(file_name);
529
530 while ((dp = readdir(dir_fd)) != NULL) {
531 if ((dp->d_fileno == 0) ||
532 ((dp->d_name[0] == (int) period_char) &&
533 ((dp->d_name[1] == 0) ||
534 ((dp->d_name[1] == (int) period_char) &&
535 (dp->d_name[2] == 0))))) {
536 continue;
537 }
538 /* Construct and internalize the true name of the SCCS file. */
539 MBSTOWCS(wcs_buffer, dp->d_name);
540 (void) wcscpy(file_name_p, wcs_buffer);
541 file = GETNAME(file_name, FIND_LENGTH);
542 file->stat.is_file = true;
543 file->stat.has_sccs = NO_SCCS;
544 /*
545 * If this is an s. file, we also enter it as if it
546 * existed in the plain directory.
547 */
548 if ((dp->d_name[0] == 's') &&
549 (dp->d_name[1] == (int) period_char)) {
550
551 MBSTOWCS(wcs_buffer, dp->d_name + 2);
552 (void) wcscpy(plain_file_name_p, wcs_buffer);
553 plain_file = GETNAME(plain_file_name, FIND_LENGTH);
554 plain_file->stat.is_file = true;
555 plain_file->stat.has_sccs = HAS_SCCS;
556 /* if sccs dependency is already set,skip */
557 if(plain_file->prop) {
558 Property sprop = get_prop(plain_file->prop,sccs_prop);
559 if(sprop != NULL) {
560 if (sprop->body.sccs.file) {
561 goto try_pattern;
562 }
563 }
564 }
565
566 /*
567 * Enter the s. file as a dependency for the
568 * plain file.
569 */
570 maybe_append_prop(plain_file, sccs_prop)->
571 body.sccs.file = file;
572 try_pattern:
573 MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2);
574 if ((pattern != NULL) &&
575 amatch(tmp_wcs_buffer, pattern)) {
576 if (debug_level > 0) {
577 WCSTOMBS(mbs_buffer, pattern);
578 (void) printf(gettext("'%s: %s' due to %s expansion\n"),
579 line->body.line.target->
580 string_mb,
581 plain_file->string_mb,
582 mbs_buffer);
583 }
584 enter_dependency(line, plain_file, false);
585 result++;
586 }
587 }
588 }
589 (void) closedir(dir_fd);
590
591 return result;
592 }
593
594 /*
595 * enter_file_name(name_string, library)
596 *
597 * Helper function for read_dir().
598 *
599 * Return value:
600 * The Name that was entered
601 *
602 * Parameters:
603 * name_string Name of the file we want to enter
604 * library The library it is a member of, if any
605 *
606 * Global variables used:
607 */
608 static Name
enter_file_name(wchar_t * name_string,wchar_t * library)609 enter_file_name(wchar_t *name_string, wchar_t *library)
610 {
611 wchar_t buffer[STRING_BUFFER_LENGTH];
612 String_rec lib_name;
613 Name name;
614 Property prop;
615
616 if (library == NULL) {
617 name = GETNAME(name_string, FIND_LENGTH);
618 name->stat.is_file = true;
619 return name;
620 }
621
622 INIT_STRING_FROM_STACK(lib_name, buffer);
623 append_string(library, &lib_name, FIND_LENGTH);
624 append_char((int) parenleft_char, &lib_name);
625 append_string(name_string, &lib_name, FIND_LENGTH);
626 append_char((int) parenright_char, &lib_name);
627
628 name = GETNAME(lib_name.buffer.start, FIND_LENGTH);
629 name->stat.is_file = true;
630 name->is_member = true;
631 prop = maybe_append_prop(name, member_prop);
632 prop->body.member.library = GETNAME(library, FIND_LENGTH);
633 prop->body.member.library->stat.is_file = true;
634 prop->body.member.entry = NULL;
635 prop->body.member.member = GETNAME(name_string, FIND_LENGTH);
636 prop->body.member.member->stat.is_file = true;
637 return name;
638 }
639
640 /*
641 * star_match(string, pattern)
642 *
643 * This is a regular shell type wildcard pattern matcher
644 * It is used when xpanding wildcards in dependency lists
645 *
646 * Return value:
647 * Indication if the string matched the pattern
648 *
649 * Parameters:
650 * string String to match
651 * pattern Pattern to match it against
652 *
653 * Global variables used:
654 */
655 static Boolean
star_match(register wchar_t * string,register wchar_t * pattern)656 star_match(register wchar_t *string, register wchar_t *pattern)
657 {
658 register int pattern_ch;
659
660 switch (*pattern) {
661 case 0:
662 return succeeded;
663 case bracketleft_char:
664 case question_char:
665 case asterisk_char:
666 while (*string) {
667 if (amatch(string++, pattern)) {
668 return succeeded;
669 }
670 }
671 break;
672 default:
673 pattern_ch = (int) *pattern++;
674 while (*string) {
675 if ((*string++ == pattern_ch) &&
676 amatch(string, pattern)) {
677 return succeeded;
678 }
679 }
680 break;
681 }
682 return failed;
683 }
684
685 /*
686 * amatch(string, pattern)
687 *
688 * Helper function for shell pattern matching
689 *
690 * Return value:
691 * Indication if the string matched the pattern
692 *
693 * Parameters:
694 * string String to match
695 * pattern Pattern to match it against
696 *
697 * Global variables used:
698 */
699 static Boolean
amatch(register wchar_t * string,register wchar_t * pattern)700 amatch(register wchar_t *string, register wchar_t *pattern)
701 {
702 register long lower_bound;
703 register long string_ch;
704 register long pattern_ch;
705 register int k;
706
707 top:
708 for (; 1; pattern++, string++) {
709 lower_bound = 017777777777;
710 string_ch = *string;
711 switch (pattern_ch = *pattern) {
712 case bracketleft_char:
713 k = 0;
714 while ((pattern_ch = *++pattern) != 0) {
715 switch (pattern_ch) {
716 case bracketright_char:
717 if (!k) {
718 return failed;
719 }
720 string++;
721 pattern++;
722 goto top;
723 case hyphen_char:
724 k |= (lower_bound <= string_ch) &&
725 (string_ch <=
726 (pattern_ch = pattern[1]));
727 /* FALLTHRU */
728 default:
729 if (string_ch ==
730 (lower_bound = pattern_ch)) {
731 k++;
732 }
733 }
734 }
735 return failed;
736 case asterisk_char:
737 return star_match(string, ++pattern);
738 case 0:
739 return BOOLEAN(!string_ch);
740 case question_char:
741 if (string_ch == 0) {
742 return failed;
743 }
744 break;
745 default:
746 if (pattern_ch != string_ch) {
747 return failed;
748 }
749 break;
750 }
751 }
752 /* NOTREACHED */
753 }
754
755