// Copyright © Tavian Barnes <tavianator@tavianator.com>
// SPDX-License-Identifier: 0BSD
#include "bfstd.h"
#include "bit.h"
#include "config.h"
#include "diag.h"
#include "xregex.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <langinfo.h>
#include <nl_types.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <wchar.h>
#include <wctype.h>
#if BFS_USE_SYS_SYSMACROS_H
# include <sys/sysmacros.h>
#elif BFS_USE_SYS_MKDEV_H
# include <sys/mkdev.h>
#endif
#if BFS_USE_UTIL_H
# include <util.h>
#endif
bool is_nonexistence_error(int error) {
return error == ENOENT || errno == ENOTDIR;
}
char *xdirname(const char *path) {
size_t i = xbaseoff(path);
// Skip trailing slashes
while (i > 0 && path[i - 1] == '/') {
--i;
}
if (i > 0) {
return strndup(path, i);
} else if (path[i] == '/') {
return strdup("/");
} else {
return strdup(".");
}
}
char *xbasename(const char *path) {
size_t i = xbaseoff(path);
size_t len = strcspn(path + i, "/");
if (len > 0) {
return strndup(path + i, len);
} else if (path[i] == '/') {
return strdup("/");
} else {
return strdup(".");
}
}
size_t xbaseoff(const char *path) {
size_t i = strlen(path);
// Skip trailing slashes
while (i > 0 && path[i - 1] == '/') {
--i;
}
// Find the beginning of the name
while (i > 0 && path[i - 1] != '/') {
--i;
}
// Skip leading slashes
while (path[i] == '/' && path[i + 1]) {
++i;
}
return i;
}
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:
bfs_bug("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;
}
}
const char *xgetprogname(void) {
const char *cmd = NULL;
#if __GLIBC__
cmd = program_invocation_short_name;
#elif BSD
cmd = getprogname();
#endif
if (!cmd) {
cmd = BFS_COMMAND;
}
return cmd;
}
/** 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