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