xref: /illumos-gate/usr/src/cmd/make/bin/files.cc (revision ae389aa9)
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(register Name target);
65 extern  void		set_target_stat(register Name target, struct stat buf);
66 static	timestruc_t&	vpath_exists(register Name target);
67 static	Name		enter_file_name(wchar_t *name_string, wchar_t *library);
68 static	Boolean		star_match(register char *string, register char *pattern);
69 static	Boolean		amatch(register wchar_t *string, register 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&
88 exists(register Name target)
89 {
90 	struct stat		buf;
91 	register 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
177 set_target_stat(register 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&
212 vpath_exists(register 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
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         register 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
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
616 star_match(register wchar_t *string, register wchar_t *pattern)
617 {
618 	register 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
660 amatch(register wchar_t *string, register wchar_t *pattern)
661 {
662 	register long		lower_bound;
663 	register long		string_ch;
664 	register long		pattern_ch;
665 	register 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