1 /* @(#)findinpath.c	1.7 19/07/31 Copyright 2004-2019 J. Schilling */
2 #define	USE_LARGEFILES
3 #include <schily/mconfig.h>
4 #ifndef lint
5 static	UConst char sccsid[] =
6 	"@(#)findinpath.c	1.7 19/07/31 Copyright 2004-2019 J. Schilling";
7 #endif
8 /*
9  * Search a file name in the PATH and return the path name in allocated space.
10  *
11  * Copyright 2004-2019 J. Schilling
12  */
13 /*
14  * The contents of this file are subject to the terms of the
15  * Common Development and Distribution License, Version 1.0 only
16  * (the "License").  You may not use this file except in compliance
17  * with the License.
18  *
19  * See the file CDDL.Schily.txt in this distribution for details.
20  * A copy of the CDDL is also available via the Internet at
21  * http://www.opensource.org/licenses/cddl1.txt
22  *
23  * When distributing Covered Code, include this CDDL HEADER in each
24  * file and include the License file CDDL.Schily.txt from this distribution.
25  */
26 
27 /*
28  * Since we need to call stat() and since this is not a predictable call,
29  * we always compile this module in largefile mode.
30  * See #define USE_LARGEFILES before #include <schily/mconfig.h>
31  */
32 
33 #include <schily/stdlib.h>
34 #include <schily/stat.h>
35 #include <schily/errno.h>
36 #include <schily/string.h>
37 #include <schily/standard.h>
38 #include <schily/schily.h>
39 
40 EXPORT	char	*findinpath	__PR((char *name, int mode, BOOL plain_file,
41 								char *path));
42 
43 #ifdef	JOS
44 #define	enofile(t)			((t) == EMISSDIR || \
45 					(t)  == ENOFILE || \
46 					(t)  == EISADIR || \
47 					(t)  == EIOERR)
48 #else
49 #define	enofile(t)			((t) == ENOENT || \
50 					(t)  == ENOTDIR || \
51 					(t)  == EISDIR || \
52 					(t)  == EIO)
53 #endif
54 
55 EXPORT char *
findinpath(name,mode,plain_file,path)56 findinpath(name, mode, plain_file, path)
57 	char	*name;			/* The name to execute			*/
58 	int	mode;			/* Mode for access() e.g. X_OK		*/
59 	BOOL	plain_file;		/* Whether to check only plain files	*/
60 	char	*path;			/* PATH to use if not NULL		*/
61 {
62 	char	*pathlist;
63 	char	*p1;
64 	char	*p2;
65 	char	*tmp;
66 	int	err = 0;
67 	int	exerr = 0;
68 	struct stat sb;
69 
70 	if (name == NULL)
71 		return (NULL);
72 	if (strchr(name, '/'))
73 		return (strdup(name));
74 
75 	if (path != NULL)
76 		pathlist = path;
77 	else if ((pathlist = getenv("PATH")) == NULL)
78 		pathlist = "/bin";
79 	p2 = pathlist = strdup(pathlist);
80 	if (pathlist == NULL)
81 		return (NULL);
82 
83 	for (;;) {
84 		p1 = p2;
85 		if ((p2 = strchr(p2, PATH_ENV_DELIM)) != NULL)
86 			*p2++ = '\0';
87 		if (*p1 == '\0') {
88 			tmp = strdup(name);
89 			if (tmp == NULL) {
90 				free(pathlist);
91 				return (NULL);
92 			}
93 		} else {
94 			size_t	len = strlen(p1) + strlen(name) + 2;
95 
96 			tmp = malloc(len);
97 			if (tmp == NULL) {
98 				free(pathlist);
99 				return (strdup(name));
100 			}
101 			strcatl(tmp, p1, PATH_DELIM_STR, name, (char *)NULL);
102 		}
103 
104 		seterrno(0);
105 		if (stat(tmp, &sb) >= 0) {
106 			if ((!plain_file || S_ISREG(sb.st_mode)) &&
107 				(eaccess(tmp, mode) >= 0)) {
108 				free(pathlist);
109 				return (tmp);
110 			}
111 			if ((err = geterrno()) == 0)
112 				err = ENOEXEC;
113 		} else {
114 			err = geterrno();
115 		}
116 		free(tmp);
117 		if (exerr == 0 && !enofile(err))
118 			exerr = err;
119 		if ((!enofile(err) && !(err == EACCES)) || p2 == NULL)
120 			break;
121 	}
122 	free(pathlist);
123 	seterrno(exerr);
124 	return (NULL);
125 }
126