/* which.c: check to see if a file is executable.
This function was originally written with Maarten Litmaath's which.c as
a template, but was changed in order to accomodate the possibility of
rc's running setuid or the possibility of executing files not in the
primary group. Much of this file has been re-vamped by Paul Haahr.
I re-re-vamped the functions that Paul supplied to correct minor bugs
and to strip out unneeded functionality.
#define X_ALL (X_USR|X_GRP|X_OTH)
extern int stat(const char *, struct stat
*);
static bool initialized
= FALSE
;
static int ngroups
, gidset
[NGROUPS
];
/* determine whether gid lies in gidset */
static int ingidset(int g
) {
for (i
= 0; i
< ngroups
; i
++)
A home-grown access/stat. Does the right thing for group-executable files.
Returns a bool instead of this -1 nonsense.
static bool rc_access(char *path
, bool verbose
) {
if (stat(path
, &st
) != 0) {
if (verbose
) /* verbose flag only set for absolute pathname */
else if (uid
== st
.st_uid
)
else if (gid
== st
.st_gid
|| ingidset(st
.st_gid
))
if (((st
.st_mode
& S_IFMT
) == S_IFREG
) && (st
.st_mode
& mask
))
/* return a full pathname by searching $path, and by checking the status of the file */
extern char *which(char *name
, bool verbose
) {
static char *test
= NULL
;
static size_t testlen
= 0;
if (name
== NULL
) /* no filename? can happen with "> foo" as a command */
ngroups
= getgroups(NGROUPS
, gidset
);
if (isabsolute(name
)) /* absolute pathname? */
return rc_access(name
, verbose
) ? name
: NULL
;
for (path
= varlookup("path"); path
!= NULL
; path
= path
->n
) {
size_t need
= strlen(path
->w
) + len
+ 2; /* one for null terminator, one for the '/' */
test
= ealloc(testlen
= need
);
if (!streq(test
, "/")) /* "//" is special to POSIX */
if (rc_access(test
, FALSE
))
fprint(2, "%s not found\n", name
);