qutil.c (193 lines of code) (raw):
// SPDX-License-Identifier: Apache-2.0
/* Copyright (c) 2024 Elastic NV */
#include <ctype.h> /* is_digit(3) */
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "quark.h"
ssize_t
qread(int fd, void *buf, size_t count)
{
ssize_t n;
again:
n = read(fd, buf, count);
if (n == -1) {
if (errno == EINTR)
goto again;
return (-1);
}
return (n);
}
int
qwrite(int fd, const void *buf, size_t count)
{
ssize_t n;
const char *p;
for (p = buf; count != 0; p += n, count -= n) {
again:
n = write(fd, p, count);
if (n == -1) {
if (errno == EINTR)
goto again;
return (-1);
} else if (n == 0)
return (errno = EPIPE, -1);
}
return (0);
}
/*
* Safer readlinkat(2), guarantees termination and returns strlen(pathname) so
* caller can check truncation, like strlcpy(). Compare to >= 0 if truncation is
* acceptable.
*/
ssize_t
qreadlinkat(int dfd, const char *pathname, char *buf, size_t bufsiz)
{
ssize_t n;
if (bufsiz < 2)
return (errno = EINVAL, -1);
if ((n = readlinkat(dfd, pathname, buf, bufsiz - 1)) == -1)
return (-1);
buf[n] = 0;
return (strlen(pathname));
}
int
isnumber(const char *s)
{
for (; *s != 0; s++) {
if (!isdigit(*s))
return (0);
}
return (1);
}
/*
* Reads a "single-lined" file. Returns size of the line excluding NUL and
* excluding \n, guarantees termination on >= 0, truncates silently.
*/
ssize_t
readlineat(int dfd, const char *pathname, char *buf, size_t bufsiz)
{
int fd;
ssize_t n;
fd = openat(dfd, pathname, O_RDONLY);
if (fd == -1)
return (-1);
n = qread(fd, buf, bufsiz);
close(fd);
if (n == -1)
return (-1);
else if (n == 0) {
buf[0] = 0;
return (0);
}
buf[n - 1] = 0;
return (n - 1);
}
/*
* Like a strtoull but with proper detection.
*/
int
strtou64(u64 *dst, const char *v, int base)
{
char *p;
u64 u;
errno = 0;
u = strtoull(v, &p, base);
if (*p != 0 || (u == ULLONG_MAX && errno != 0))
return (-1);
*dst = u;
return (0);
}
char *
find_line(FILE *f, const char *needle)
{
char *line, *found;
size_t line_len;
ssize_t n;
long old_pos;
old_pos = ftell(f);
if (old_pos == -1)
return (NULL);
rewind(f);
line = NULL;
line_len = 0;
found = NULL;
while ((n = getline(&line, &line_len, f)) != -1) {
if (line[n - 1] == '\n')
line[n - 1] = 0;
if (strncmp(line, needle, strlen(needle)))
continue;
found = strdup(line);
break;
}
free(line);
(void)fseek(f, old_pos, SEEK_SET);
return (found);
}
char *
find_line_p(const char *path, const char *needle)
{
FILE *f;
char *line;
if ((f = fopen(path, "r")) == NULL)
return (NULL);
line = find_line(f, needle);
fclose(f);
return (line);
}
char *
load_file_nostat(int fd, size_t *total)
{
ssize_t n, bufsize, copied;
char *buf, *nbuf;
buf = NULL;
bufsize = 0;
copied = 0;
for (; ;) {
if (bufsize - copied == 0) {
bufsize = bufsize == 0 ? 4096 : bufsize * 2;
/* Grow with one extra for NUL */
nbuf = realloc(buf, bufsize + 1);
if (nbuf == NULL) {
free(buf);
return (NULL);
}
buf = nbuf;
}
n = qread(fd, buf + copied, bufsize - copied);
if (n == -1) {
free(buf);
return (NULL);
} else if (n == 0) {
/* We allocate an extra byte to guarantee NUL space */
buf[copied] = 0;
break;
}
copied += n;
}
/* Signal an empty file with NULL */
if (copied == 0) {
free(buf);
buf = NULL;
}
if (total != NULL)
*total = copied;
return (buf);
}
void
qlog_func(int pri, int do_errno, const char *func, int lineno,
const char *fmt, ...)
{
va_list ap;
char *nfmt;
int saved_errno;
if (pri > quark_verbose)
return;
if (do_errno)
saved_errno = errno;
va_start(ap, fmt);
/* best effort in out of mem situations */
if (asprintf(&nfmt, "%s:%d: %s", func, lineno, fmt) == -1) {
fprintf(stderr, "%s: %s:%d: ",
program_invocation_short_name, func, lineno);
vfprintf(stderr, fmt, ap);
if (do_errno)
fprintf(stderr, ": %s", strerror(saved_errno));
fprintf(stderr, "\n");
} else {
if (do_errno) {
errno = saved_errno;
vwarn(nfmt, ap);
} else
vwarnx(nfmt, ap);
free(nfmt);
}
va_end(ap);
}