From b4c7112bafe9b85f3a039a8b34fef2c1f2fa3b58 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sun, 6 Nov 2022 17:47:23 -0500 Subject: bfstd: Rename from util and reorganize it --- src/bar.c | 2 +- src/bfstd.c | 512 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/bfstd.h | 234 +++++++++++++++++++++++++++ src/bftw.c | 2 +- src/color.c | 2 +- src/diag.c | 2 +- src/diag.h | 2 +- src/dir.c | 2 +- src/eval.c | 6 +- src/exec.c | 2 +- src/fsade.c | 3 +- src/main.c | 4 +- src/mtab.c | 2 +- src/opt.c | 1 - src/parse.c | 2 +- src/printf.c | 2 +- src/stat.c | 6 +- src/util.c | 512 ----------------------------------------------------------- src/util.h | 218 ------------------------- src/xregex.c | 3 +- src/xspawn.c | 2 +- 21 files changed, 769 insertions(+), 752 deletions(-) create mode 100644 src/bfstd.c create mode 100644 src/bfstd.h delete mode 100644 src/util.c delete mode 100644 src/util.h (limited to 'src') diff --git a/src/bar.c b/src/bar.c index b0e595e..c656b14 100644 --- a/src/bar.c +++ b/src/bar.c @@ -15,8 +15,8 @@ ****************************************************************************/ #include "bar.h" +#include "bfstd.h" #include "dstring.h" -#include "util.h" #include #include #include diff --git a/src/bfstd.c b/src/bfstd.c new file mode 100644 index 0000000..043e8e5 --- /dev/null +++ b/src/bfstd.c @@ -0,0 +1,512 @@ +/**************************************************************************** + * bfs * + * Copyright (C) 2016-2022 Tavian Barnes * + * * + * Permission to use, copy, modify, and/or distribute this software for any * + * purpose with or without fee is hereby granted. * + * * + * 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 "bfstd.h" +#include "config.h" +#include "dstring.h" +#include "xregex.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if BFS_USE_SYS_SYSMACROS_H +# include +#elif BFS_USE_SYS_MKDEV_H +# include +#endif + +#if BFS_USE_UTIL_H +# include +#endif + +bool is_nonexistence_error(int error) { + return error == ENOENT || errno == ENOTDIR; +} + +const char *xbasename(const char *path) { + const char *i; + + // Skip trailing slashes + for (i = path + strlen(path); i > path && i[-1] == '/'; --i); + + // Find the beginning of the name + for (; i > path && i[-1] != '/'; --i); + + // Skip leading slashes + for (; i[0] == '/' && i[1]; ++i); + + return i; +} + +void close_quietly(int fd) { + int error = errno; + xclose(fd); + errno = error; +} + +int xclose(int fd) { + int ret = close(fd); + if (ret != 0) { + assert(errno != EBADF); + } + return ret; +} + +FILE *xfopen(const char *path, int flags) { + char mode[4]; + + switch (flags & O_ACCMODE) { + case O_RDONLY: + strcpy(mode, "rb"); + break; + case O_WRONLY: + strcpy(mode, "wb"); + break; + case O_RDWR: + strcpy(mode, "r+b"); + break; + default: + assert(!"Invalid access mode"); + errno = EINVAL; + return NULL; + } + + if (flags & O_APPEND) { + mode[0] = 'a'; + } + + int fd; + if (flags & O_CREAT) { + fd = open(path, flags, 0666); + } else { + fd = open(path, flags); + } + + if (fd < 0) { + return NULL; + } + + FILE *ret = fdopen(fd, mode); + if (!ret) { + close_quietly(fd); + return NULL; + } + + return ret; +} + +char *xgetdelim(FILE *file, char delim) { + char *chunk = NULL; + size_t n = 0; + ssize_t len = getdelim(&chunk, &n, delim, file); + if (len >= 0) { + if (chunk[len] == delim) { + chunk[len] = '\0'; + } + return chunk; + } else { + free(chunk); + if (!ferror(file)) { + errno = 0; + } + return NULL; + } +} + +size_t xread(int fd, void *buf, size_t nbytes) { + size_t count = 0; + + while (count < nbytes) { + ssize_t ret = read(fd, (char *)buf + count, nbytes - count); + if (ret < 0) { + if (errno == EINTR) { + continue; + } else { + break; + } + } else if (ret == 0) { + // EOF + errno = 0; + break; + } else { + count += ret; + } + } + + return count; +} + +size_t xwrite(int fd, const void *buf, size_t nbytes) { + size_t count = 0; + + while (count < nbytes) { + ssize_t ret = write(fd, (const char *)buf + count, nbytes - count); + if (ret < 0) { + if (errno == EINTR) { + continue; + } else { + break; + } + } else if (ret == 0) { + // EOF? + errno = 0; + break; + } else { + count += ret; + } + } + + return count; +} + +/** Compile and execute a regular expression for xrpmatch(). */ +static int xrpregex(nl_item item, const char *response) { + const char *pattern = nl_langinfo(item); + if (!pattern) { + return -1; + } + + struct bfs_regex *regex; + int ret = bfs_regcomp(®ex, pattern, BFS_REGEX_POSIX_EXTENDED, 0); + if (ret == 0) { + ret = bfs_regexec(regex, response, 0); + } + + bfs_regfree(regex); + return ret; +} + +/** Check if a response is affirmative or negative. */ +static int xrpmatch(const char *response) { + int ret = xrpregex(NOEXPR, response); + if (ret > 0) { + return 0; + } else if (ret < 0) { + return -1; + } + + ret = xrpregex(YESEXPR, response); + if (ret > 0) { + return 1; + } else if (ret < 0) { + return -1; + } + + // Failsafe: always handle y/n + char c = response[0]; + if (c == 'n' || c == 'N') { + return 0; + } else if (c == 'y' || c == 'Y') { + return 1; + } else { + return -1; + } +} + +int ynprompt(void) { + fflush(stderr); + + char *line = xgetdelim(stdin, '\n'); + int ret = line ? xrpmatch(line) : -1; + free(line); + return ret; +} + +/** Get the single character describing the given file type. */ +static char type_char(mode_t mode) { + switch (mode & S_IFMT) { + case S_IFREG: + return '-'; + case S_IFBLK: + return 'b'; + case S_IFCHR: + return 'c'; + case S_IFDIR: + return 'd'; + case S_IFLNK: + return 'l'; + case S_IFIFO: + return 'p'; + case S_IFSOCK: + return 's'; +#ifdef S_IFDOOR + case S_IFDOOR: + return 'D'; +#endif +#ifdef S_IFPORT + case S_IFPORT: + return 'P'; +#endif +#ifdef S_IFWHT + case S_IFWHT: + return 'w'; +#endif + } + + return '?'; +} + +void xstrmode(mode_t mode, char str[11]) { + strcpy(str, "----------"); + + str[0] = type_char(mode); + + if (mode & 00400) { + str[1] = 'r'; + } + if (mode & 00200) { + str[2] = 'w'; + } + if ((mode & 04100) == 04000) { + str[3] = 'S'; + } else if (mode & 04000) { + str[3] = 's'; + } else if (mode & 00100) { + str[3] = 'x'; + } + + if (mode & 00040) { + str[4] = 'r'; + } + if (mode & 00020) { + str[5] = 'w'; + } + if ((mode & 02010) == 02000) { + str[6] = 'S'; + } else if (mode & 02000) { + str[6] = 's'; + } else if (mode & 00010) { + str[6] = 'x'; + } + + if (mode & 00004) { + str[7] = 'r'; + } + if (mode & 00002) { + str[8] = 'w'; + } + if ((mode & 01001) == 01000) { + str[9] = 'T'; + } else if (mode & 01000) { + str[9] = 't'; + } else if (mode & 00001) { + str[9] = 'x'; + } +} + +dev_t xmakedev(int ma, int mi) { +#ifdef makedev + return makedev(ma, mi); +#else + return (ma << 8) | mi; +#endif +} + +int xmajor(dev_t dev) { +#ifdef major + return major(dev); +#else + return dev >> 8; +#endif +} + +int xminor(dev_t dev) { +#ifdef minor + return minor(dev); +#else + return dev & 0xFF; +#endif +} + +int dup_cloexec(int fd) { +#ifdef F_DUPFD_CLOEXEC + return fcntl(fd, F_DUPFD_CLOEXEC, 0); +#else + int ret = dup(fd); + if (ret < 0) { + return -1; + } + + if (fcntl(ret, F_SETFD, FD_CLOEXEC) == -1) { + close_quietly(ret); + return -1; + } + + return ret; +#endif +} + +int pipe_cloexec(int pipefd[2]) { +#if __linux__ || (BSD && !__APPLE__) + return pipe2(pipefd, O_CLOEXEC); +#else + if (pipe(pipefd) != 0) { + return -1; + } + + if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) == -1 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) == -1) { + close_quietly(pipefd[1]); + close_quietly(pipefd[0]); + return -1; + } + + return 0; +#endif +} + +char *xconfstr(int name) { +#if __ANDROID__ + errno = ENOTSUP; + return NULL; +#else + size_t len = confstr(name, NULL, 0); + if (len == 0) { + return NULL; + } + + char *str = malloc(len); + if (!str) { + return NULL; + } + + if (confstr(name, str, len) != len) { + free(str); + return NULL; + } + + return str; +#endif // !__ANDROID__ +} + +int xfaccessat(int fd, const char *path, int amode) { + int ret = faccessat(fd, path, amode, 0); + +#ifdef AT_EACCESS + // Some platforms, like Hurd, only support AT_EACCESS. Other platforms, + // like Android, don't support AT_EACCESS at all. + if (ret != 0 && (errno == EINVAL || errno == ENOTSUP)) { + ret = faccessat(fd, path, amode, AT_EACCESS); + } +#endif + + return ret; +} + +char *xreadlinkat(int fd, const char *path, size_t size) { + ssize_t len; + char *name = NULL; + + if (size == 0) { + size = 64; + } else { + ++size; // NUL terminator + } + + while (true) { + char *new_name = realloc(name, size); + if (!new_name) { + goto error; + } + name = new_name; + + len = readlinkat(fd, path, name, size); + if (len < 0) { + goto error; + } else if ((size_t)len >= size) { + size *= 2; + } else { + break; + } + } + + name[len] = '\0'; + return name; + +error: + free(name); + return NULL; +} + +int xstrtofflags(const char **str, unsigned long long *set, unsigned long long *clear) { +#if BSD && !__GNU__ + char *str_arg = (char *)*str; + unsigned long set_arg = 0; + unsigned long clear_arg = 0; + +#if __NetBSD__ + int ret = string_to_flags(&str_arg, &set_arg, &clear_arg); +#else + int ret = strtofflags(&str_arg, &set_arg, &clear_arg); +#endif + + *str = str_arg; + *set = set_arg; + *clear = clear_arg; + + if (ret != 0) { + errno = EINVAL; + } + return ret; +#else // !BSD + errno = ENOTSUP; + return -1; +#endif +} + +size_t xstrwidth(const char *str) { + size_t len = strlen(str); + size_t ret = 0; + + mbstate_t mb; + memset(&mb, 0, sizeof(mb)); + + while (len > 0) { + wchar_t wc; + size_t mblen = mbrtowc(&wc, str, len, &mb); + int cwidth; + if (mblen == (size_t)-1) { + // Invalid byte sequence, assume a single-width '?' + mblen = 1; + cwidth = 1; + memset(&mb, 0, sizeof(mb)); + } else if (mblen == (size_t)-2) { + // Incomplete byte sequence, assume a single-width '?' + mblen = len; + cwidth = 1; + } else { + cwidth = wcwidth(wc); + if (cwidth < 0) { + cwidth = 0; + } + } + + str += mblen; + len -= mblen; + ret += cwidth; + } + + return ret; +} diff --git a/src/bfstd.h b/src/bfstd.h new file mode 100644 index 0000000..3ad2116 --- /dev/null +++ b/src/bfstd.h @@ -0,0 +1,234 @@ +/**************************************************************************** + * bfs * + * Copyright (C) 2016-2022 Tavian Barnes * + * * + * Permission to use, copy, modify, and/or distribute this software for any * + * purpose with or without fee is hereby granted. * + * * + * 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. * + ****************************************************************************/ + +/** + * Standard library wrappers and polyfills. + */ + +#ifndef BFS_BFSTD_H +#define BFS_BFSTD_H + +#include + +// #include + +/** + * Return whether an error code is due to a path not existing. + */ +bool is_nonexistence_error(int error); + +#include + +#ifndef O_DIRECTORY +# define O_DIRECTORY 0 +#endif + +#include + +#if !defined(FNM_CASEFOLD) && defined(FNM_IGNORECASE) +# define FNM_CASEFOLD FNM_IGNORECASE +#endif + +// #include + +/** + * basename() variant that doesn't modify the input. + * + * @param path + * The path in question. + * @return A pointer into path at the base name offset. + */ +const char *xbasename(const char *path); + +#include + +/** + * close() variant that preserves errno. + * + * @param fd + * The file descriptor to close. + */ +void close_quietly(int fd); + +/** + * close() wrapper that asserts the file descriptor is valid. + * + * @param fd + * The file descriptor to close. + * @return + * 0 on success, or -1 on error. + */ +int xclose(int fd); + +/** + * fopen() variant that takes open() style flags. + * + * @param path + * The path to open. + * @param flags + * Flags to pass to open(). + */ +FILE *xfopen(const char *path, int flags); + +/** + * Convenience wrapper for getdelim(). + * + * @param file + * The file to read. + * @param delim + * The delimiter character to split on. + * @return + * The read chunk (without the delimiter), allocated with malloc(). + * NULL is returned on error (errno != 0) or end of file (errno == 0). + */ +char *xgetdelim(FILE *file, char delim); + +/** + * A safe version of read() that handles interrupted system calls and partial + * reads. + * + * @return + * The number of bytes read. A value != nbytes indicates an error + * (errno != 0) or end of file (errno == 0). + */ +size_t xread(int fd, void *buf, size_t nbytes); + +/** + * A safe version of write() that handles interrupted system calls and partial + * writes. + * + * @return + The number of bytes written. A value != nbytes indicates an error. + */ +size_t xwrite(int fd, const void *buf, size_t nbytes); + +// #include + +/** + * Process a yes/no prompt. + * + * @return 1 for yes, 0 for no, and -1 for unknown. + */ +int ynprompt(void); + +// #include + +/** + * Format a mode like ls -l (e.g. -rw-r--r--). + * + * @param mode + * The mode to format. + * @param str + * The string to hold the formatted mode. + */ +void xstrmode(mode_t mode, char str[11]); + +#include + +/** + * Portable version of makedev(). + */ +dev_t xmakedev(int ma, int mi); + +/** + * Portable version of major(). + */ +int xmajor(dev_t dev); + +/** + * Portable version of minor(). + */ +int xminor(dev_t dev); + +#include + +/** + * Like dup(), but set the FD_CLOEXEC flag. + * + * @param fd + * The file descriptor to duplicate. + * @return + * A duplicated file descriptor, or -1 on failure. + */ +int dup_cloexec(int fd); + +/** + * Like pipe(), but set the FD_CLOEXEC flag. + * + * @param pipefd + * The array to hold the two file descriptors. + * @return + * 0 on success, -1 on failure. + */ +int pipe_cloexec(int pipefd[2]); + +/** + * Wrapper for confstr() that allocates with malloc(). + * + * @param name + * The ID of the confstr to look up. + * @return + * The value of the confstr, or NULL on failure. + */ +char *xconfstr(int name); + +/** + * Wrapper for faccessat() that handles some portability issues. + */ +int xfaccessat(int fd, const char *path, int amode); + +/** + * readlinkat() wrapper that dynamically allocates the result. + * + * @param fd + * The base directory descriptor. + * @param path + * The path to the link, relative to fd. + * @param size + * An estimate for the size of the link name (pass 0 if unknown). + * @return + * The target of the link, allocated with malloc(), or NULL on failure. + */ +char *xreadlinkat(int fd, const char *path, size_t size); + +/** + * Portability wrapper for strtofflags(). + * + * @param str + * The string to parse. The pointee will be advanced to the first + * invalid position on error. + * @param set + * The flags that are set in the string. + * @param clear + * The flags that are cleared in the string. + * @return + * 0 on success, -1 on failure. + */ +int xstrtofflags(const char **str, unsigned long long *set, unsigned long long *clear); + +// #include + +/** + * wcswidth() variant that works on narrow strings. + * + * @param str + * The string to measure. + * @return + * The likely width of that string in a terminal. + */ +size_t xstrwidth(const char *str); + +#endif // BFS_BFSTD_H diff --git a/src/bftw.c b/src/bftw.c index 6f97bf6..378a225 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -31,13 +31,13 @@ */ #include "bftw.h" +#include "bfstd.h" #include "dir.h" #include "darray.h" #include "dstring.h" #include "mtab.h" #include "stat.h" #include "trie.h" -#include "util.h" #include #include #include diff --git a/src/color.c b/src/color.c index 6240b9c..98fe9f2 100644 --- a/src/color.c +++ b/src/color.c @@ -15,6 +15,7 @@ ****************************************************************************/ #include "color.h" +#include "bfstd.h" #include "bftw.h" #include "dir.h" #include "dstring.h" @@ -22,7 +23,6 @@ #include "fsade.h" #include "stat.h" #include "trie.h" -#include "util.h" #include #include #include diff --git a/src/diag.c b/src/diag.c index 27848f1..a9be5b4 100644 --- a/src/diag.c +++ b/src/diag.c @@ -15,10 +15,10 @@ ****************************************************************************/ #include "diag.h" +#include "bfstd.h" #include "ctx.h" #include "color.h" #include "expr.h" -#include "util.h" #include #include #include diff --git a/src/diag.h b/src/diag.h index 39129cc..56ad39b 100644 --- a/src/diag.h +++ b/src/diag.h @@ -22,7 +22,7 @@ #define BFS_DIAG_H #include "ctx.h" -#include "util.h" +#include "config.h" #include #include diff --git a/src/dir.c b/src/dir.c index 1cd2d2c..2081cc5 100644 --- a/src/dir.c +++ b/src/dir.c @@ -15,8 +15,8 @@ ****************************************************************************/ #include "dir.h" +#include "bfstd.h" #include "config.h" -#include "util.h" #include #include #include diff --git a/src/eval.c b/src/eval.c index dc79188..5bf3120 100644 --- a/src/eval.c +++ b/src/eval.c @@ -20,6 +20,7 @@ #include "eval.h" #include "bar.h" +#include "bfstd.h" #include "bftw.h" #include "color.h" #include "config.h" @@ -36,7 +37,6 @@ #include "pwcache.h" #include "stat.h" #include "trie.h" -#include "util.h" #include "xregex.h" #include "xtime.h" #include @@ -686,8 +686,8 @@ bool eval_fls(const struct bfs_expr *expr, struct bfs_eval *state) { } if (ftwbuf->type == BFS_BLK || ftwbuf->type == BFS_CHR) { - int ma = bfs_major(statbuf->rdev); - int mi = bfs_minor(statbuf->rdev); + int ma = xmajor(statbuf->rdev); + int mi = xminor(statbuf->rdev); if (fprintf(file, " %3d, %3d", ma, mi) < 0) { goto error; } diff --git a/src/exec.c b/src/exec.c index 0130317..afbdcb7 100644 --- a/src/exec.c +++ b/src/exec.c @@ -15,12 +15,12 @@ ****************************************************************************/ #include "exec.h" +#include "bfstd.h" #include "bftw.h" #include "ctx.h" #include "color.h" #include "diag.h" #include "dstring.h" -#include "util.h" #include "xspawn.h" #include #include diff --git a/src/fsade.c b/src/fsade.c index 7a7201e..a609b97 100644 --- a/src/fsade.c +++ b/src/fsade.c @@ -15,10 +15,11 @@ ****************************************************************************/ #include "fsade.h" +#include "config.h" +#include "bfstd.h" #include "bftw.h" #include "dir.h" #include "dstring.h" -#include "util.h" #include #include #include diff --git a/src/main.c b/src/main.c index 6f82976..fbddbe5 100644 --- a/src/main.c +++ b/src/main.c @@ -34,6 +34,7 @@ * * - Utilities: * - bar.[ch] (a terminal status bar) + * - bfstd.[ch] (standard library wrappers/polyfills) * - color.[ch] (for pretty terminal colors) * - config.h (configuration and feature/platform detection) * - darray.[ch] (a dynamic array library) @@ -49,13 +50,12 @@ * - xregex.[ch] (regular expression support) * - xspawn.[ch] (spawns processes) * - xtime.[ch] (date/time handling utilities) - * - util.[ch] (everything else) */ +#include "bfstd.h" #include "ctx.h" #include "eval.h" #include "parse.h" -#include "util.h" #include #include #include diff --git a/src/mtab.c b/src/mtab.c index e6a92e1..316ec6e 100644 --- a/src/mtab.c +++ b/src/mtab.c @@ -15,11 +15,11 @@ ****************************************************************************/ #include "mtab.h" +#include "bfstd.h" #include "config.h" #include "darray.h" #include "stat.h" #include "trie.h" -#include "util.h" #include #include #include diff --git a/src/opt.c b/src/opt.c index f8c0ba3..80fdd43 100644 --- a/src/opt.c +++ b/src/opt.c @@ -46,7 +46,6 @@ #include "eval.h" #include "expr.h" #include "pwcache.h" -#include "util.h" #include #include #include diff --git a/src/parse.c b/src/parse.c index 2fe3473..1be7cb7 100644 --- a/src/parse.c +++ b/src/parse.c @@ -22,6 +22,7 @@ */ #include "parse.h" +#include "bfstd.h" #include "bftw.h" #include "color.h" #include "config.h" @@ -38,7 +39,6 @@ #include "pwcache.h" #include "stat.h" #include "typo.h" -#include "util.h" #include "xregex.h" #include "xspawn.h" #include "xtime.h" diff --git a/src/printf.c b/src/printf.c index 8fdde41..97c203f 100644 --- a/src/printf.c +++ b/src/printf.c @@ -15,6 +15,7 @@ ****************************************************************************/ #include "printf.h" +#include "bfstd.h" #include "bftw.h" #include "color.h" #include "ctx.h" @@ -26,7 +27,6 @@ #include "mtab.h" #include "pwcache.h" #include "stat.h" -#include "util.h" #include "xtime.h" #include #include diff --git a/src/stat.c b/src/stat.c index 3134b6a..b59e9b0 100644 --- a/src/stat.c +++ b/src/stat.c @@ -15,8 +15,8 @@ ****************************************************************************/ #include "stat.h" +#include "bfstd.h" #include "config.h" -#include "util.h" #include #include #include @@ -186,7 +186,7 @@ static int bfs_statx_impl(int at_fd, const char *at_path, int at_flags, struct b buf->mask = 0; - buf->dev = bfs_makedev(xbuf.stx_dev_major, xbuf.stx_dev_minor); + buf->dev = xmakedev(xbuf.stx_dev_major, xbuf.stx_dev_minor); buf->mask |= BFS_STAT_DEV; if (xbuf.stx_mask & STATX_INO) { @@ -227,7 +227,7 @@ static int bfs_statx_impl(int at_fd, const char *at_path, int at_flags, struct b buf->mask |= BFS_STAT_BLOCKS; } - buf->rdev = bfs_makedev(xbuf.stx_rdev_major, xbuf.stx_rdev_minor); + buf->rdev = xmakedev(xbuf.stx_rdev_major, xbuf.stx_rdev_minor); buf->mask |= BFS_STAT_RDEV; buf->attrs = xbuf.stx_attributes; diff --git a/src/util.c b/src/util.c deleted file mode 100644 index da54040..0000000 --- a/src/util.c +++ /dev/null @@ -1,512 +0,0 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * 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 "util.h" -#include "config.h" -#include "dstring.h" -#include "xregex.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if BFS_USE_SYS_SYSMACROS_H -# include -#elif BFS_USE_SYS_MKDEV_H -# include -#endif - -#if BFS_USE_UTIL_H -# include -#endif - -char *xreadlinkat(int fd, const char *path, size_t size) { - ssize_t len; - char *name = NULL; - - if (size == 0) { - size = 64; - } else { - ++size; // NUL terminator - } - - while (true) { - char *new_name = realloc(name, size); - if (!new_name) { - goto error; - } - name = new_name; - - len = readlinkat(fd, path, name, size); - if (len < 0) { - goto error; - } else if ((size_t)len >= size) { - size *= 2; - } else { - break; - } - } - - name[len] = '\0'; - return name; - -error: - free(name); - return NULL; -} - -int dup_cloexec(int fd) { -#ifdef F_DUPFD_CLOEXEC - return fcntl(fd, F_DUPFD_CLOEXEC, 0); -#else - int ret = dup(fd); - if (ret < 0) { - return -1; - } - - if (fcntl(ret, F_SETFD, FD_CLOEXEC) == -1) { - close_quietly(ret); - return -1; - } - - return ret; -#endif -} - -int pipe_cloexec(int pipefd[2]) { -#if __linux__ || (BSD && !__APPLE__) - return pipe2(pipefd, O_CLOEXEC); -#else - if (pipe(pipefd) != 0) { - return -1; - } - - if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) == -1 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) == -1) { - close_quietly(pipefd[1]); - close_quietly(pipefd[0]); - return -1; - } - - return 0; -#endif -} - -/** Get the single character describing the given file type. */ -static char type_char(mode_t mode) { - switch (mode & S_IFMT) { - case S_IFREG: - return '-'; - case S_IFBLK: - return 'b'; - case S_IFCHR: - return 'c'; - case S_IFDIR: - return 'd'; - case S_IFLNK: - return 'l'; - case S_IFIFO: - return 'p'; - case S_IFSOCK: - return 's'; -#ifdef S_IFDOOR - case S_IFDOOR: - return 'D'; -#endif -#ifdef S_IFPORT - case S_IFPORT: - return 'P'; -#endif -#ifdef S_IFWHT - case S_IFWHT: - return 'w'; -#endif - } - - return '?'; -} - -void xstrmode(mode_t mode, char str[11]) { - strcpy(str, "----------"); - - str[0] = type_char(mode); - - if (mode & 00400) { - str[1] = 'r'; - } - if (mode & 00200) { - str[2] = 'w'; - } - if ((mode & 04100) == 04000) { - str[3] = 'S'; - } else if (mode & 04000) { - str[3] = 's'; - } else if (mode & 00100) { - str[3] = 'x'; - } - - if (mode & 00040) { - str[4] = 'r'; - } - if (mode & 00020) { - str[5] = 'w'; - } - if ((mode & 02010) == 02000) { - str[6] = 'S'; - } else if (mode & 02000) { - str[6] = 's'; - } else if (mode & 00010) { - str[6] = 'x'; - } - - if (mode & 00004) { - str[7] = 'r'; - } - if (mode & 00002) { - str[8] = 'w'; - } - if ((mode & 01001) == 01000) { - str[9] = 'T'; - } else if (mode & 01000) { - str[9] = 't'; - } else if (mode & 00001) { - str[9] = 'x'; - } -} - -const char *xbasename(const char *path) { - const char *i; - - // Skip trailing slashes - for (i = path + strlen(path); i > path && i[-1] == '/'; --i); - - // Find the beginning of the name - for (; i > path && i[-1] != '/'; --i); - - // Skip leading slashes - for (; i[0] == '/' && i[1]; ++i); - - return i; -} - -int xfaccessat(int fd, const char *path, int amode) { - int ret = faccessat(fd, path, amode, 0); - -#ifdef AT_EACCESS - // Some platforms, like Hurd, only support AT_EACCESS. Other platforms, - // like Android, don't support AT_EACCESS at all. - if (ret != 0 && (errno == EINVAL || errno == ENOTSUP)) { - ret = faccessat(fd, path, amode, AT_EACCESS); - } -#endif - - return ret; -} - -int xstrtofflags(const char **str, unsigned long long *set, unsigned long long *clear) { -#if BSD && !__GNU__ - char *str_arg = (char *)*str; - unsigned long set_arg = 0; - unsigned long clear_arg = 0; - -#if __NetBSD__ - int ret = string_to_flags(&str_arg, &set_arg, &clear_arg); -#else - int ret = strtofflags(&str_arg, &set_arg, &clear_arg); -#endif - - *str = str_arg; - *set = set_arg; - *clear = clear_arg; - - if (ret != 0) { - errno = EINVAL; - } - return ret; -#else // !BSD - errno = ENOTSUP; - return -1; -#endif -} - -size_t xstrwidth(const char *str) { - size_t len = strlen(str); - size_t ret = 0; - - mbstate_t mb; - memset(&mb, 0, sizeof(mb)); - - while (len > 0) { - wchar_t wc; - size_t mblen = mbrtowc(&wc, str, len, &mb); - int cwidth; - if (mblen == (size_t)-1) { - // Invalid byte sequence, assume a single-width '?' - mblen = 1; - cwidth = 1; - memset(&mb, 0, sizeof(mb)); - } else if (mblen == (size_t)-2) { - // Incomplete byte sequence, assume a single-width '?' - mblen = len; - cwidth = 1; - } else { - cwidth = wcwidth(wc); - if (cwidth < 0) { - cwidth = 0; - } - } - - str += mblen; - len -= mblen; - ret += cwidth; - } - - return ret; -} - -bool is_nonexistence_error(int error) { - return error == ENOENT || errno == ENOTDIR; -} - -/** Compile and execute a regular expression for xrpmatch(). */ -static int xrpregex(nl_item item, const char *response) { - const char *pattern = nl_langinfo(item); - if (!pattern) { - return -1; - } - - struct bfs_regex *regex; - int ret = bfs_regcomp(®ex, pattern, BFS_REGEX_POSIX_EXTENDED, 0); - if (ret == 0) { - ret = bfs_regexec(regex, response, 0); - } - - bfs_regfree(regex); - return ret; -} - -/** Check if a response is affirmative or negative. */ -static int xrpmatch(const char *response) { - int ret = xrpregex(NOEXPR, response); - if (ret > 0) { - return 0; - } else if (ret < 0) { - return -1; - } - - ret = xrpregex(YESEXPR, response); - if (ret > 0) { - return 1; - } else if (ret < 0) { - return -1; - } - - // Failsafe: always handle y/n - char c = response[0]; - if (c == 'n' || c == 'N') { - return 0; - } else if (c == 'y' || c == 'Y') { - return 1; - } else { - return -1; - } -} - -int ynprompt(void) { - fflush(stderr); - - char *line = xgetdelim(stdin, '\n'); - int ret = line ? xrpmatch(line) : -1; - free(line); - return ret; -} - -dev_t bfs_makedev(int ma, int mi) { -#ifdef makedev - return makedev(ma, mi); -#else - return (ma << 8) | mi; -#endif -} - -int bfs_major(dev_t dev) { -#ifdef major - return major(dev); -#else - return dev >> 8; -#endif -} - -int bfs_minor(dev_t dev) { -#ifdef minor - return minor(dev); -#else - return dev & 0xFF; -#endif -} - -size_t xread(int fd, void *buf, size_t nbytes) { - size_t count = 0; - - while (count < nbytes) { - ssize_t ret = read(fd, (char *)buf + count, nbytes - count); - if (ret < 0) { - if (errno == EINTR) { - continue; - } else { - break; - } - } else if (ret == 0) { - // EOF - errno = 0; - break; - } else { - count += ret; - } - } - - return count; -} - -size_t xwrite(int fd, const void *buf, size_t nbytes) { - size_t count = 0; - - while (count < nbytes) { - ssize_t ret = write(fd, (const char *)buf + count, nbytes - count); - if (ret < 0) { - if (errno == EINTR) { - continue; - } else { - break; - } - } else if (ret == 0) { - // EOF? - errno = 0; - break; - } else { - count += ret; - } - } - - return count; -} - -char *xconfstr(int name) { -#if __ANDROID__ - errno = ENOTSUP; - return NULL; -#else - size_t len = confstr(name, NULL, 0); - if (len == 0) { - return NULL; - } - - char *str = malloc(len); - if (!str) { - return NULL; - } - - if (confstr(name, str, len) != len) { - free(str); - return NULL; - } - - return str; -#endif // !__ANDROID__ -} - -char *xgetdelim(FILE *file, char delim) { - char *chunk = NULL; - size_t n = 0; - ssize_t len = getdelim(&chunk, &n, delim, file); - if (len >= 0) { - if (chunk[len] == delim) { - chunk[len] = '\0'; - } - return chunk; - } else { - free(chunk); - if (!ferror(file)) { - errno = 0; - } - return NULL; - } -} - -FILE *xfopen(const char *path, int flags) { - char mode[4]; - - switch (flags & O_ACCMODE) { - case O_RDONLY: - strcpy(mode, "rb"); - break; - case O_WRONLY: - strcpy(mode, "wb"); - break; - case O_RDWR: - strcpy(mode, "r+b"); - break; - default: - assert(!"Invalid access mode"); - errno = EINVAL; - return NULL; - } - - if (flags & O_APPEND) { - mode[0] = 'a'; - } - - int fd; - if (flags & O_CREAT) { - fd = open(path, flags, 0666); - } else { - fd = open(path, flags); - } - - if (fd < 0) { - return NULL; - } - - FILE *ret = fdopen(fd, mode); - if (!ret) { - close_quietly(fd); - return NULL; - } - - return ret; -} - -int xclose(int fd) { - int ret = close(fd); - if (ret != 0) { - assert(errno != EBADF); - } - return ret; -} - -void close_quietly(int fd) { - int error = errno; - xclose(fd); - errno = error; -} diff --git a/src/util.h b/src/util.h deleted file mode 100644 index 2d99152..0000000 --- a/src/util.h +++ /dev/null @@ -1,218 +0,0 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * 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. * - ****************************************************************************/ - -/** - * Assorted utilities that don't belong anywhere else. - */ - -#ifndef BFS_UTIL_H -#define BFS_UTIL_H - -#include -#include -#include -#include -#include -#include - -// Some portability concerns - -#if !defined(FNM_CASEFOLD) && defined(FNM_IGNORECASE) -# define FNM_CASEFOLD FNM_IGNORECASE -#endif - -#ifndef O_DIRECTORY -# define O_DIRECTORY 0 -#endif - -/** - * readlinkat() wrapper that dynamically allocates the result. - * - * @param fd - * The base directory descriptor. - * @param path - * The path to the link, relative to fd. - * @param size - * An estimate for the size of the link name (pass 0 if unknown). - * @return The target of the link, allocated with malloc(), or NULL on failure. - */ -char *xreadlinkat(int fd, const char *path, size_t size); - -/** - * Like dup(), but set the FD_CLOEXEC flag. - * - * @param fd - * The file descriptor to duplicate. - * @return A duplicated file descriptor, or -1 on failure. - */ -int dup_cloexec(int fd); - -/** - * Like pipe(), but set the FD_CLOEXEC flag. - * - * @param pipefd - * The array to hold the two file descriptors. - * @return 0 on success, -1 on failure. - */ -int pipe_cloexec(int pipefd[2]); - -/** - * Format a mode like ls -l (e.g. -rw-r--r--). - * - * @param mode - * The mode to format. - * @param str - * The string to hold the formatted mode. - */ -void xstrmode(mode_t mode, char str[11]); - -/** - * basename() variant that doesn't modify the input. - * - * @param path - * The path in question. - * @return A pointer into path at the base name offset. - */ -const char *xbasename(const char *path); - -/** - * Wrapper for faccessat() that handles some portability issues. - */ -int xfaccessat(int fd, const char *path, int amode); - -/** - * Portability wrapper for strtofflags(). - * - * @param str - * The string to parse. The pointee will be advanced to the first - * invalid position on error. - * @param set - * The flags that are set in the string. - * @param clear - * The flags that are cleared in the string. - * @return - * 0 on success, -1 on failure. - */ -int xstrtofflags(const char **str, unsigned long long *set, unsigned long long *clear); - -/** - * wcswidth() variant that works on narrow strings. - * - * @param str - * The string to measure. - * @return - * The likely width of that string in a terminal. - */ -size_t xstrwidth(const char *str); - -/** - * Return whether an error code is due to a path not existing. - */ -bool is_nonexistence_error(int error); - -/** - * Process a yes/no prompt. - * - * @return 1 for yes, 0 for no, and -1 for unknown. - */ -int ynprompt(void); - -/** - * Portable version of makedev(). - */ -dev_t bfs_makedev(int ma, int mi); - -/** - * Portable version of major(). - */ -int bfs_major(dev_t dev); - -/** - * Portable version of minor(). - */ -int bfs_minor(dev_t dev); - -/** - * A safe version of read() that handles interrupted system calls and partial - * reads. - * - * @return - * The number of bytes read. A value != nbytes indicates an error - * (errno != 0) or end of file (errno == 0). - */ -size_t xread(int fd, void *buf, size_t nbytes); - -/** - * A safe version of write() that handles interrupted system calls and partial - * writes. - * - * @return - The number of bytes written. A value != nbytes indicates an error. - */ -size_t xwrite(int fd, const void *buf, size_t nbytes); - -/** - * Wrapper for confstr() that allocates with malloc(). - * - * @param name - * The ID of the confstr to look up. - * @return - * The value of the confstr, or NULL on failure. - */ -char *xconfstr(int name); - -/** - * Convenience wrapper for getdelim(). - * - * @param file - * The file to read. - * @param delim - * The delimiter character to split on. - * @return - * The read chunk (without the delimiter), allocated with malloc(). - * NULL is returned on error (errno != 0) or end of file (errno == 0). - */ -char *xgetdelim(FILE *file, char delim); - -/** - * fopen() variant that takes open() style flags. - * - * @param path - * The path to open. - * @param flags - * Flags to pass to open(). - */ -FILE *xfopen(const char *path, int flags); - -/** - * close() wrapper that asserts the file descriptor is valid. - * - * @param fd - * The file descriptor to close. - * @return - * 0 on success, or -1 on error. - */ -int xclose(int fd); - -/** - * close() variant that preserves errno. - * - * @param fd - * The file descriptor to close. - */ -void close_quietly(int fd); - -#endif // BFS_UTIL_H diff --git a/src/xregex.c b/src/xregex.c index 4fa098d..e8d6b23 100644 --- a/src/xregex.c +++ b/src/xregex.c @@ -15,7 +15,8 @@ ****************************************************************************/ #include "xregex.h" -#include "util.h" +#include "bfstd.h" +#include "config.h" #include #include #include diff --git a/src/xspawn.c b/src/xspawn.c index f9d52b0..5d2c320 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -15,8 +15,8 @@ ****************************************************************************/ #include "xspawn.h" +#include "bfstd.h" #include "config.h" -#include "util.h" #include #include #include -- cgit v1.2.3