/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ ╞══════════════════════════════════════════════════════════════════════════════╡ │ Copyright 2020 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ │ above copyright notice and this permission notice appear in all copies. │ │ │ │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/struct/stat.h" #include "libc/errno.h" #include "libc/intrin/strace.h" #include "libc/paths.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/sysv/consts/ok.h" #include "libc/sysv/consts/s.h" #include "libc/sysv/errfuns.h" /** * Resolves full pathname of executable. * * @return execve()'able path, or NULL w/ errno * @errno ENOENT, EACCES, ENOMEM * @see free(), execvpe() * @asyncsignalsafe * @vforksafe */ char *commandv(const char *name, char *pathbuf, size_t pathbufsz) { // bounce empty names size_t namelen; if (!(namelen = strlen(name))) { enoent(); return 0; } // get system path const char *syspath; if (memchr(name, '/', namelen)) { syspath = ""; } else if (!(syspath = getenv("PATH"))) { syspath = _PATH_DEFPATH; } // iterate through directories int old_errno = errno; bool seen_eacces = false; const char *b, *a = syspath; errno = ENOENT; do { b = strchrnul(a, ':'); size_t dirlen = b - a; if (dirlen + 1 + namelen < pathbufsz) { if (dirlen) { memcpy(pathbuf, a, dirlen); pathbuf[dirlen] = '/'; memcpy(pathbuf + dirlen + 1, name, namelen + 1); } else { memcpy(pathbuf, name, namelen + 1); } if (!access(pathbuf, X_OK)) { struct stat st; if (!stat(pathbuf, &st) && S_ISREG(st.st_mode)) { errno = old_errno; return pathbuf; } } else if (errno == EACCES) { seen_eacces = true; } } else { enametoolong(); } a = b + 1; } while (*b); // return error if not found if (seen_eacces) { errno = EACCES; } return 0; }