master xplshn/aruu / cmd / pseudo / which.c
  1/* See LICENSE file for copyright and license details. */
  2#include "arg.h"
  3#include "util.h"
  4#include "wexec.h"
  5
  6#include <fcntl.h>
  7#include <limits.h>
  8#include <stdio.h>
  9#include <stdlib.h>
 10#include <string.h>
 11#include <sys/stat.h>
 12#include <sys/types.h>
 13#include <unistd.h>
 14
 15static int aflag;
 16
 17static int
 18canexec(int fd, const char *name)
 19{
 20  struct stat st;
 21
 22  if (fstatat(fd, name, &st, 0) < 0 || !S_ISREG(st.st_mode))
 23    return 0;
 24  return faccessat(fd, name, X_OK, AT_EACCESS) == 0;
 25}
 26
 27static int
 28which(const char *path, const char *name)
 29{
 30  char  *ptr, *p;
 31  size_t i, len;
 32  int    dirfd, found = 0;
 33
 34  if (strchr(name, '/')) {
 35    found = canexec(AT_FDCWD, name);
 36    if (found)
 37      puts(name);
 38    return found;
 39  }
 40
 41  ptr = p = enstrdup(3, path);
 42  len     = strlen(p);
 43  for (i = 0; i < len + 1; i++) {
 44    if (ptr[i] != ':' && ptr[i] != '\0')
 45      continue;
 46    ptr[i] = '\0';
 47    if ((dirfd = open(p, O_RDONLY)) >= 0) {
 48      if (canexec(dirfd, name)) {
 49        found = 1;
 50        fputs(p, stdout);
 51        if (i && ptr[i - 1] != '/')
 52          fputc('/', stdout);
 53        puts(name);
 54      }
 55      close(dirfd);
 56      if (!aflag && found)
 57        break;
 58    }
 59    p = ptr + i + 1;
 60  }
 61  free(ptr);
 62
 63  return found;
 64}
 65
 66static void
 67usage(void)
 68{
 69  eprintf("usage: %s [-a] name ...\n", argv0);
 70}
 71
 72// ?man which: locate a command
 73// ?man arguments: name ...
 74// ?man find the path of executable files in PATH
 75int
 76main(int argc, char *argv[])
 77{
 78  char *path;
 79  int   found = 0, foundall = 1;
 80
 81  ARGBEGIN
 82  {
 83    // ?man -a: print or show all entries
 84    case 'a':
 85      aflag = 1;
 86      break;
 87    default:
 88      usage();
 89  }
 90  ARGEND
 91
 92  if (!argc)
 93    usage();
 94
 95  if (!(path = getenv("PATH")))
 96    enprintf(3, "$PATH is not set\n");
 97
 98  for (; *argv; argc--, argv++) {
 99#if FEATURE_NOEXEC
100    /* builtins are function pointers with no filesystem path */
101    if (wexec_get_noexec() && wexec_is_builtin(*argv)) {
102      printf("%s: builtin applet\n", *argv);
103      found = 1;
104      if (!aflag)
105        continue;
106    }
107#endif
108    if (which(path, *argv)) {
109      found = 1;
110    } else {
111#if FEATURE_NOEXEC
112      /* already reported above when builtin was active */
113      if (!wexec_get_noexec() || !wexec_is_builtin(*argv))
114#endif
115      {
116        weprintf("%s: not an external command\n", *argv);
117        foundall = 0;
118      }
119    }
120  }
121
122  if (fshut(stdout, "<stdout>"))
123    return 2;
124  return found ? foundall ? 0 : 1 : 2;
125}