summaryrefslogtreecommitdiffstats
path: root/src/printf.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/printf.c')
-rw-r--r--src/printf.c567
1 files changed, 304 insertions, 263 deletions
diff --git a/src/printf.c b/src/printf.c
index 8fdde41..f8428f7 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -1,70 +1,68 @@
-/****************************************************************************
- * bfs *
- * Copyright (C) 2017-2022 Tavian Barnes <tavianator@tavianator.com> *
- * *
- * 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. *
- ****************************************************************************/
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+#include "prelude.h"
#include "printf.h"
+#include "alloc.h"
+#include "bfstd.h"
#include "bftw.h"
#include "color.h"
#include "ctx.h"
-#include "darray.h"
#include "diag.h"
#include "dir.h"
#include "dstring.h"
#include "expr.h"
+#include "fsade.h"
#include "mtab.h"
#include "pwcache.h"
#include "stat.h"
-#include "util.h"
-#include "xtime.h"
-#include <assert.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
-#include <stdbool.h>
+#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
+struct bfs_fmt;
+
/**
* A function implementing a printf directive.
*/
-typedef int bfs_printf_fn(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf);
+typedef int bfs_printf_fn(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf);
/**
- * A single printf directive like %f or %#4m. The whole format string is stored
- * as a darray of these.
+ * A single formatting directive like %f or %#4m.
*/
-struct bfs_printf {
+struct bfs_fmt {
/** The printing function to invoke. */
bfs_printf_fn *fn;
/** String data associated with this directive. */
- char *str;
+ dchar *str;
/** The stat field to print. */
enum bfs_stat_field stat_field;
/** Character data associated with this directive. */
char c;
/** Some data used by the directive. */
- const void *ptr;
+ void *ptr;
+};
+
+/**
+ * An entire format string.
+ */
+struct bfs_printf {
+ /** An array of formatting directives. */
+ struct bfs_fmt *fmts;
+ /** The number of directives. */
+ size_t nfmts;
};
/** Print some text as-is. */
-static int bfs_printf_literal(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- size_t len = dstrlen(directive->str);
- if (fwrite(directive->str, 1, len, cfile->file) == len) {
+static int bfs_printf_literal(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
+ size_t len = dstrlen(fmt->str);
+ if (fwrite(fmt->str, 1, len, cfile->file) == len) {
return 0;
} else {
return -1;
@@ -72,26 +70,44 @@ static int bfs_printf_literal(CFILE *cfile, const struct bfs_printf *directive,
}
/** \c: flush */
-static int bfs_printf_flush(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_flush(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
return fflush(cfile->file);
}
/** Check if we can safely colorize this directive. */
-static bool should_color(CFILE *cfile, const struct bfs_printf *directive) {
- return cfile->colors && strcmp(directive->str, "%s") == 0;
+static bool should_color(CFILE *cfile, const struct bfs_fmt *fmt) {
+ return cfile->colors && strcmp(fmt->str, "%s") == 0;
}
/**
* Print a value to a temporary buffer before formatting it.
*/
-#define BFS_PRINTF_BUF(buf, format, ...) \
- char buf[256]; \
- int ret = snprintf(buf, sizeof(buf), format, __VA_ARGS__); \
- assert(ret >= 0 && (size_t)ret < sizeof(buf)); \
+#define BFS_PRINTF_BUF(buf, format, ...) \
+ char buf[256]; \
+ int ret = snprintf(buf, sizeof(buf), format, __VA_ARGS__); \
+ bfs_assert(ret >= 0 && (size_t)ret < sizeof(buf)); \
(void)ret
+/** Return a dynamic format string. */
+attr(format_arg(2))
+static const char *dyn_fmt(const char *str, const char *fake) {
+ bfs_assert(strcmp(str + strlen(str) - strlen(fake) + 1, fake + 1) == 0,
+ "Mismatched format specifiers: '%s' vs. '%s'", str, fake);
+ return str;
+}
+
+/** Wrapper for fprintf(). */
+attr(printf(3, 4))
+static int bfs_fprintf(CFILE *cfile, const struct bfs_fmt *fmt, const char *fake, ...) {
+ va_list args;
+ va_start(args, fake);
+ int ret = vfprintf(cfile->file, dyn_fmt(fmt->str, fake), args);
+ va_end(args);
+ return ret;
+}
+
/** %a, %c, %t: ctime() */
-static int bfs_printf_ctime(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_ctime(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
// Not using ctime() itself because GNU find adds nanoseconds
static const char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
@@ -101,69 +117,69 @@ static int bfs_printf_ctime(CFILE *cfile, const struct bfs_printf *directive, co
return -1;
}
- const struct timespec *ts = bfs_stat_time(statbuf, directive->stat_field);
+ const struct timespec *ts = bfs_stat_time(statbuf, fmt->stat_field);
if (!ts) {
return -1;
}
struct tm tm;
- if (xlocaltime(&ts->tv_sec, &tm) != 0) {
+ if (!localtime_r(&ts->tv_sec, &tm)) {
return -1;
}
BFS_PRINTF_BUF(buf, "%s %s %2d %.2d:%.2d:%.2d.%09ld0 %4d",
- days[tm.tm_wday],
- months[tm.tm_mon],
- tm.tm_mday,
- tm.tm_hour,
- tm.tm_min,
- tm.tm_sec,
- (long)ts->tv_nsec,
- 1900 + tm.tm_year);
+ days[tm.tm_wday],
+ months[tm.tm_mon],
+ tm.tm_mday,
+ tm.tm_hour,
+ tm.tm_min,
+ tm.tm_sec,
+ (long)ts->tv_nsec,
+ 1900 + tm.tm_year);
- return fprintf(cfile->file, directive->str, buf);
+ return bfs_fprintf(cfile, fmt, "%s", buf);
}
/** %A, %B/%W, %C, %T: strftime() */
-static int bfs_printf_strftime(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_strftime(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
if (!statbuf) {
return -1;
}
- const struct timespec *ts = bfs_stat_time(statbuf, directive->stat_field);
+ const struct timespec *ts = bfs_stat_time(statbuf, fmt->stat_field);
if (!ts) {
return -1;
}
struct tm tm;
- if (xlocaltime(&ts->tv_sec, &tm) != 0) {
+ if (!localtime_r(&ts->tv_sec, &tm)) {
return -1;
}
int ret;
char buf[256];
char format[] = "% ";
- switch (directive->c) {
+ switch (fmt->c) {
// Non-POSIX strftime() features
case '@':
ret = snprintf(buf, sizeof(buf), "%lld.%09ld0", (long long)ts->tv_sec, (long)ts->tv_nsec);
break;
case '+':
ret = snprintf(buf, sizeof(buf), "%4d-%.2d-%.2d+%.2d:%.2d:%.2d.%09ld0",
- 1900 + tm.tm_year,
- tm.tm_mon + 1,
- tm.tm_mday,
- tm.tm_hour,
- tm.tm_min,
- tm.tm_sec,
- (long)ts->tv_nsec);
+ 1900 + tm.tm_year,
+ tm.tm_mon + 1,
+ tm.tm_mday,
+ tm.tm_hour,
+ tm.tm_min,
+ tm.tm_sec,
+ (long)ts->tv_nsec);
break;
case 'k':
ret = snprintf(buf, sizeof(buf), "%2d", tm.tm_hour);
break;
case 'l':
- ret = snprintf(buf, sizeof(buf), "%2d", (tm.tm_hour + 11)%12 + 1);
+ ret = snprintf(buf, sizeof(buf), "%2d", (tm.tm_hour + 11) % 12 + 1);
break;
case 's':
ret = snprintf(buf, sizeof(buf), "%lld", (long long)ts->tv_sec);
@@ -173,102 +189,113 @@ static int bfs_printf_strftime(CFILE *cfile, const struct bfs_printf *directive,
break;
case 'T':
ret = snprintf(buf, sizeof(buf), "%.2d:%.2d:%.2d.%09ld0",
- tm.tm_hour,
- tm.tm_min,
- tm.tm_sec,
- (long)ts->tv_nsec);
+ tm.tm_hour,
+ tm.tm_min,
+ tm.tm_sec,
+ (long)ts->tv_nsec);
break;
// POSIX strftime() features
default:
- format[1] = directive->c;
+ format[1] = fmt->c;
+#if __GNUC__
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wformat-nonliteral"
+#endif
ret = strftime(buf, sizeof(buf), format, &tm);
+#if __GNUC__
+# pragma GCC diagnostic pop
+#endif
break;
}
- assert(ret >= 0 && (size_t)ret < sizeof(buf));
+ bfs_assert(ret >= 0 && (size_t)ret < sizeof(buf));
(void)ret;
- return fprintf(cfile->file, directive->str, buf);
+ return bfs_fprintf(cfile, fmt, "%s", buf);
}
/** %b: blocks */
-static int bfs_printf_b(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_b(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
if (!statbuf) {
return -1;
}
- uintmax_t blocks = ((uintmax_t)statbuf->blocks*BFS_STAT_BLKSIZE + 511)/512;
+ uintmax_t blocks = ((uintmax_t)statbuf->blocks * BFS_STAT_BLKSIZE + 511) / 512;
BFS_PRINTF_BUF(buf, "%ju", blocks);
- return fprintf(cfile->file, directive->str, buf);
+ return bfs_fprintf(cfile, fmt, "%s", buf);
}
/** %d: depth */
-static int bfs_printf_d(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- return fprintf(cfile->file, directive->str, (intmax_t)ftwbuf->depth);
+static int bfs_printf_d(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
+ return bfs_fprintf(cfile, fmt, "%jd", (intmax_t)ftwbuf->depth);
}
/** %D: device */
-static int bfs_printf_D(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_D(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
if (!statbuf) {
return -1;
}
BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->dev);
- return fprintf(cfile->file, directive->str, buf);
+ return bfs_fprintf(cfile, fmt, "%s", buf);
}
/** %f: file name */
-static int bfs_printf_f(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- if (should_color(cfile, directive)) {
+static int bfs_printf_f(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
+ if (should_color(cfile, fmt)) {
return cfprintf(cfile, "%pF", ftwbuf);
} else {
- return fprintf(cfile->file, directive->str, ftwbuf->path + ftwbuf->nameoff);
+ return bfs_fprintf(cfile, fmt, "%s", ftwbuf->path + ftwbuf->nameoff);
}
}
/** %F: file system type */
-static int bfs_printf_F(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_F(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
if (!statbuf) {
return -1;
}
- const char *type = bfs_fstype(directive->ptr, statbuf);
- return fprintf(cfile->file, directive->str, type);
+ const char *type = bfs_fstype(fmt->ptr, statbuf);
+ if (!type) {
+ return -1;
+ }
+
+ return bfs_fprintf(cfile, fmt, "%s", type);
}
/** %G: gid */
-static int bfs_printf_G(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_G(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
if (!statbuf) {
return -1;
}
BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->gid);
- return fprintf(cfile->file, directive->str, buf);
+ return bfs_fprintf(cfile, fmt, "%s", buf);
}
/** %g: group name */
-static int bfs_printf_g(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_g(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
if (!statbuf) {
return -1;
}
- const struct bfs_groups *groups = directive->ptr;
- const struct group *grp = groups ? bfs_getgrgid(groups, statbuf->gid) : NULL;
+ struct bfs_groups *groups = fmt->ptr;
+ const struct group *grp = bfs_getgrgid(groups, statbuf->gid);
if (!grp) {
- return bfs_printf_G(cfile, directive, ftwbuf);
+ return bfs_printf_G(cfile, fmt, ftwbuf);
}
- return fprintf(cfile->file, directive->str, grp->gr_name);
+ return bfs_fprintf(cfile, fmt, "%s", grp->gr_name);
}
/** %h: leading directories */
-static int bfs_printf_h(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_h(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
char *copy = NULL;
const char *buf;
@@ -290,10 +317,10 @@ static int bfs_printf_h(CFILE *cfile, const struct bfs_printf *directive, const
}
int ret;
- if (should_color(cfile, directive)) {
- ret = cfprintf(cfile, "${di}%s${rs}", buf);
+ if (should_color(cfile, fmt)) {
+ ret = cfprintf(cfile, "${di}%pQ${rs}", buf);
} else {
- ret = fprintf(cfile->file, directive->str, buf);
+ ret = bfs_fprintf(cfile, fmt, "%s", buf);
}
free(copy);
@@ -301,48 +328,48 @@ static int bfs_printf_h(CFILE *cfile, const struct bfs_printf *directive, const
}
/** %H: current root */
-static int bfs_printf_H(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- if (should_color(cfile, directive)) {
+static int bfs_printf_H(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
+ if (should_color(cfile, fmt)) {
if (ftwbuf->depth == 0) {
return cfprintf(cfile, "%pP", ftwbuf);
} else {
- return cfprintf(cfile, "${di}%s${rs}", ftwbuf->root);
+ return cfprintf(cfile, "${di}%pQ${rs}", ftwbuf->root);
}
} else {
- return fprintf(cfile->file, directive->str, ftwbuf->root);
+ return bfs_fprintf(cfile, fmt, "%s", ftwbuf->root);
}
}
/** %i: inode */
-static int bfs_printf_i(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_i(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
if (!statbuf) {
return -1;
}
BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->ino);
- return fprintf(cfile->file, directive->str, buf);
+ return bfs_fprintf(cfile, fmt, "%s", buf);
}
/** %k: 1K blocks */
-static int bfs_printf_k(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_k(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
if (!statbuf) {
return -1;
}
- uintmax_t blocks = ((uintmax_t)statbuf->blocks*BFS_STAT_BLKSIZE + 1023)/1024;
+ uintmax_t blocks = ((uintmax_t)statbuf->blocks * BFS_STAT_BLKSIZE + 1023) / 1024;
BFS_PRINTF_BUF(buf, "%ju", blocks);
- return fprintf(cfile->file, directive->str, buf);
+ return bfs_fprintf(cfile, fmt, "%s", buf);
}
/** %l: link target */
-static int bfs_printf_l(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_l(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
char *buf = NULL;
const char *target = "";
if (ftwbuf->type == BFS_LNK) {
- if (should_color(cfile, directive)) {
+ if (should_color(cfile, fmt)) {
return cfprintf(cfile, "%pL", ftwbuf);
}
@@ -355,23 +382,23 @@ static int bfs_printf_l(CFILE *cfile, const struct bfs_printf *directive, const
}
}
- int ret = fprintf(cfile->file, directive->str, target);
+ int ret = bfs_fprintf(cfile, fmt, "%s", target);
free(buf);
return ret;
}
/** %m: mode */
-static int bfs_printf_m(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_m(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
if (!statbuf) {
return -1;
}
- return fprintf(cfile->file, directive->str, (unsigned int)(statbuf->mode & 07777));
+ return bfs_fprintf(cfile, fmt, "%o", (unsigned int)(statbuf->mode & 07777));
}
/** %M: symbolic mode */
-static int bfs_printf_M(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_M(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
if (!statbuf) {
return -1;
@@ -379,37 +406,37 @@ static int bfs_printf_M(CFILE *cfile, const struct bfs_printf *directive, const
char buf[11];
xstrmode(statbuf->mode, buf);
- return fprintf(cfile->file, directive->str, buf);
+ return bfs_fprintf(cfile, fmt, "%s", buf);
}
/** %n: link count */
-static int bfs_printf_n(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_n(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
if (!statbuf) {
return -1;
}
BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->nlink);
- return fprintf(cfile->file, directive->str, buf);
+ return bfs_fprintf(cfile, fmt, "%s", buf);
}
/** %p: full path */
-static int bfs_printf_p(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- if (should_color(cfile, directive)) {
+static int bfs_printf_p(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
+ if (should_color(cfile, fmt)) {
return cfprintf(cfile, "%pP", ftwbuf);
} else {
- return fprintf(cfile->file, directive->str, ftwbuf->path);
+ return bfs_fprintf(cfile, fmt, "%s", ftwbuf->path);
}
}
/** %P: path after root */
-static int bfs_printf_P(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_P(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
size_t offset = strlen(ftwbuf->root);
if (ftwbuf->path[offset] == '/') {
++offset;
}
- if (should_color(cfile, directive)) {
+ if (should_color(cfile, fmt)) {
if (ftwbuf->depth == 0) {
return 0;
}
@@ -419,23 +446,23 @@ static int bfs_printf_P(CFILE *cfile, const struct bfs_printf *directive, const
copybuf.nameoff -= offset;
return cfprintf(cfile, "%pP", &copybuf);
} else {
- return fprintf(cfile->file, directive->str, ftwbuf->path + offset);
+ return bfs_fprintf(cfile, fmt, "%s", ftwbuf->path + offset);
}
}
/** %s: size */
-static int bfs_printf_s(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_s(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
if (!statbuf) {
return -1;
}
BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->size);
- return fprintf(cfile->file, directive->str, buf);
+ return bfs_fprintf(cfile, fmt, "%s", buf);
}
/** %S: sparseness */
-static int bfs_printf_S(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_S(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
if (!statbuf) {
return -1;
@@ -445,36 +472,36 @@ static int bfs_printf_S(CFILE *cfile, const struct bfs_printf *directive, const
if (statbuf->size == 0 && statbuf->blocks == 0) {
sparsity = 1.0;
} else {
- sparsity = (double)BFS_STAT_BLKSIZE*statbuf->blocks/statbuf->size;
+ sparsity = (double)BFS_STAT_BLKSIZE * statbuf->blocks / statbuf->size;
}
- return fprintf(cfile->file, directive->str, sparsity);
+ return bfs_fprintf(cfile, fmt, "%g", sparsity);
}
/** %U: uid */
-static int bfs_printf_U(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_U(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
if (!statbuf) {
return -1;
}
BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->uid);
- return fprintf(cfile->file, directive->str, buf);
+ return bfs_fprintf(cfile, fmt, "%s", buf);
}
/** %u: user name */
-static int bfs_printf_u(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_u(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
if (!statbuf) {
return -1;
}
- const struct bfs_users *users = directive->ptr;
- const struct passwd *pwd = users ? bfs_getpwuid(users, statbuf->uid) : NULL;
+ struct bfs_users *users = fmt->ptr;
+ const struct passwd *pwd = bfs_getpwuid(users, statbuf->uid);
if (!pwd) {
- return bfs_printf_U(cfile, directive, ftwbuf);
+ return bfs_printf_U(cfile, fmt, ftwbuf);
}
- return fprintf(cfile->file, directive->str, pwd->pw_name);
+ return bfs_fprintf(cfile, fmt, "%s", pwd->pw_name);
}
static const char *bfs_printf_type(enum bfs_type type) {
@@ -491,51 +518,45 @@ static const char *bfs_printf_type(enum bfs_type type) {
return "p";
case BFS_LNK:
return "l";
+ case BFS_PORT:
+ return "P";
case BFS_REG:
return "f";
case BFS_SOCK:
return "s";
+ case BFS_WHT:
+ return "w";
default:
return "U";
}
}
/** %y: type */
-static int bfs_printf_y(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
+static int bfs_printf_y(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
const char *type = bfs_printf_type(ftwbuf->type);
- return fprintf(cfile->file, directive->str, type);
+ return bfs_fprintf(cfile, fmt, "%s", type);
}
/** %Y: target type */
-static int bfs_printf_Y(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- int error = 0;
-
- if (ftwbuf->type != BFS_LNK) {
- return bfs_printf_y(cfile, directive, ftwbuf);
- }
+static int bfs_printf_Y(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
+ enum bfs_type type = bftw_type(ftwbuf, BFS_STAT_FOLLOW);
+ const char *str;
- const char *type = "U";
-
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, BFS_STAT_FOLLOW);
- if (statbuf) {
- type = bfs_printf_type(bfs_mode_to_type(statbuf->mode));
- } else {
- switch (errno) {
- case ELOOP:
- type = "L";
- break;
- case ENOENT:
- case ENOTDIR:
- type = "N";
- break;
- default:
- type = "?";
+ int error = 0;
+ if (type == BFS_ERROR) {
+ if (errno_is_like(ELOOP)) {
+ str = "L";
+ } else if (errno_is_like(ENOENT)) {
+ str = "N";
+ } else {
+ str = "?";
error = errno;
- break;
}
+ } else {
+ str = bfs_printf_type(type);
}
- int ret = fprintf(cfile->file, directive->str, type);
+ int ret = bfs_fprintf(cfile, fmt, "%s", str);
if (error != 0) {
ret = -1;
errno = error;
@@ -543,24 +564,36 @@ static int bfs_printf_Y(CFILE *cfile, const struct bfs_printf *directive, const
return ret;
}
+/** %Z: SELinux context */
+attr(maybe_unused)
+static int bfs_printf_Z(CFILE *cfile, const struct bfs_fmt *fmt, const struct BFTW *ftwbuf) {
+ char *con = bfs_getfilecon(ftwbuf);
+ if (!con) {
+ return -1;
+ }
+
+ int ret = bfs_fprintf(cfile, fmt, "%s", con);
+ bfs_freecon(con);
+ return ret;
+}
+
/**
* Append a literal string to the chain.
*/
-static int append_literal(const struct bfs_ctx *ctx, struct bfs_printf **format, char **literal) {
+static int append_literal(const struct bfs_ctx *ctx, struct bfs_printf *format, dchar **literal) {
if (dstrlen(*literal) == 0) {
return 0;
}
- struct bfs_printf directive = {
- .fn = bfs_printf_literal,
- .str = *literal,
- };
-
- if (DARRAY_PUSH(format, &directive) != 0) {
- bfs_perror(ctx, "DARRAY_PUSH()");
+ struct bfs_fmt *fmt = RESERVE(struct bfs_fmt, &format->fmts, &format->nfmts);
+ if (!fmt) {
+ bfs_perror(ctx, "RESERVE()");
return -1;
}
+ fmt->fn = bfs_printf_literal;
+ fmt->str = *literal;
+
*literal = dstralloc(0);
if (!*literal) {
bfs_perror(ctx, "dstralloc()");
@@ -573,23 +606,29 @@ static int append_literal(const struct bfs_ctx *ctx, struct bfs_printf **format,
/**
* Append a printf directive to the chain.
*/
-static int append_directive(const struct bfs_ctx *ctx, struct bfs_printf **format, char **literal, struct bfs_printf *directive) {
+static int append_directive(const struct bfs_ctx *ctx, struct bfs_printf *format, dchar **literal, struct bfs_fmt *fmt) {
if (append_literal(ctx, format, literal) != 0) {
return -1;
}
- if (DARRAY_PUSH(format, directive) != 0) {
- bfs_perror(ctx, "DARRAY_PUSH()");
+ struct bfs_fmt *dest = RESERVE(struct bfs_fmt, &format->fmts, &format->nfmts);
+ if (!dest) {
+ bfs_perror(ctx, "RESERVE()");
return -1;
}
+ *dest = *fmt;
return 0;
}
int bfs_printf_parse(const struct bfs_ctx *ctx, struct bfs_expr *expr, const char *format) {
- expr->printf = NULL;
+ expr->printf = ZALLOC(struct bfs_printf);
+ if (!expr->printf) {
+ bfs_perror(ctx, "zalloc()");
+ return -1;
+ }
- char *literal = dstralloc(0);
+ dchar *literal = dstralloc(0);
if (!literal) {
bfs_perror(ctx, "dstralloc()");
goto error;
@@ -623,10 +662,10 @@ int bfs_printf_parse(const struct bfs_ctx *ctx, struct bfs_expr *expr, const cha
case 'c':
{
- struct bfs_printf directive = {
+ struct bfs_fmt fmt = {
.fn = bfs_printf_flush,
};
- if (append_directive(ctx, &expr->printf, &literal, &directive) != 0) {
+ if (append_directive(ctx, expr->printf, &literal, &fmt) != 0) {
goto error;
}
goto done;
@@ -648,15 +687,15 @@ int bfs_printf_parse(const struct bfs_ctx *ctx, struct bfs_expr *expr, const cha
goto one_char;
}
- struct bfs_printf directive = {
+ struct bfs_fmt fmt = {
.str = dstralloc(2),
};
- if (!directive.str) {
- goto directive_error;
+ if (!fmt.str) {
+ goto fmt_error;
}
- if (dstrapp(&directive.str, c) != 0) {
+ if (dstrapp(&fmt.str, c) != 0) {
bfs_perror(ctx, "dstrapp()");
- goto directive_error;
+ goto fmt_error;
}
const char *specifier = "s";
@@ -671,17 +710,17 @@ int bfs_printf_parse(const struct bfs_ctx *ctx, struct bfs_expr *expr, const cha
case '0':
case '+':
must_be_numeric = true;
- BFS_FALLTHROUGH;
+ fallthru;
case ' ':
case '-':
- if (strchr(directive.str, c)) {
+ if (strchr(fmt.str, c)) {
bfs_expr_error(ctx, expr);
bfs_error(ctx, "Duplicate flag '%c'.\n", c);
- goto directive_error;
+ goto fmt_error;
}
- if (dstrapp(&directive.str, c) != 0) {
+ if (dstrapp(&fmt.str, c) != 0) {
bfs_perror(ctx, "dstrapp()");
- goto directive_error;
+ goto fmt_error;
}
continue;
}
@@ -691,9 +730,9 @@ int bfs_printf_parse(const struct bfs_ctx *ctx, struct bfs_expr *expr, const cha
// Parse the field width
while (c >= '0' && c <= '9') {
- if (dstrapp(&directive.str, c) != 0) {
+ if (dstrapp(&fmt.str, c) != 0) {
bfs_perror(ctx, "dstrapp()");
- goto directive_error;
+ goto fmt_error;
}
c = *++i;
}
@@ -701,9 +740,9 @@ int bfs_printf_parse(const struct bfs_ctx *ctx, struct bfs_expr *expr, const cha
// Parse the precision
if (c == '.') {
do {
- if (dstrapp(&directive.str, c) != 0) {
+ if (dstrapp(&fmt.str, c) != 0) {
bfs_perror(ctx, "dstrapp()");
- goto directive_error;
+ goto fmt_error;
}
c = *++i;
} while (c >= '0' && c <= '9');
@@ -711,175 +750,172 @@ int bfs_printf_parse(const struct bfs_ctx *ctx, struct bfs_expr *expr, const cha
switch (c) {
case 'a':
- directive.fn = bfs_printf_ctime;
- directive.stat_field = BFS_STAT_ATIME;
+ fmt.fn = bfs_printf_ctime;
+ fmt.stat_field = BFS_STAT_ATIME;
break;
case 'b':
- directive.fn = bfs_printf_b;
+ fmt.fn = bfs_printf_b;
break;
case 'c':
- directive.fn = bfs_printf_ctime;
- directive.stat_field = BFS_STAT_CTIME;
+ fmt.fn = bfs_printf_ctime;
+ fmt.stat_field = BFS_STAT_CTIME;
break;
case 'd':
- directive.fn = bfs_printf_d;
+ fmt.fn = bfs_printf_d;
specifier = "jd";
break;
case 'D':
- directive.fn = bfs_printf_D;
+ fmt.fn = bfs_printf_D;
break;
case 'f':
- directive.fn = bfs_printf_f;
+ fmt.fn = bfs_printf_f;
break;
case 'F':
- directive.ptr = bfs_ctx_mtab(ctx);
- if (!directive.ptr) {
+ fmt.fn = bfs_printf_F;
+ fmt.ptr = (void *)bfs_ctx_mtab(ctx);
+ if (!fmt.ptr) {
int error = errno;
bfs_expr_error(ctx, expr);
- bfs_error(ctx, "Couldn't parse the mount table: %s.\n", strerror(error));
- goto directive_error;
+ bfs_error(ctx, "Couldn't parse the mount table: %s.\n", xstrerror(error));
+ goto fmt_error;
}
- directive.fn = bfs_printf_F;
break;
case 'g':
- directive.ptr = bfs_ctx_groups(ctx);
- if (!directive.ptr) {
- int error = errno;
- bfs_expr_error(ctx, expr);
- bfs_error(ctx, "Couldn't parse the group table: %s.\n", strerror(error));
- goto directive_error;
- }
- directive.fn = bfs_printf_g;
+ fmt.fn = bfs_printf_g;
+ fmt.ptr = ctx->groups;
break;
case 'G':
- directive.fn = bfs_printf_G;
+ fmt.fn = bfs_printf_G;
break;
case 'h':
- directive.fn = bfs_printf_h;
+ fmt.fn = bfs_printf_h;
break;
case 'H':
- directive.fn = bfs_printf_H;
+ fmt.fn = bfs_printf_H;
break;
case 'i':
- directive.fn = bfs_printf_i;
+ fmt.fn = bfs_printf_i;
break;
case 'k':
- directive.fn = bfs_printf_k;
+ fmt.fn = bfs_printf_k;
break;
case 'l':
- directive.fn = bfs_printf_l;
+ fmt.fn = bfs_printf_l;
break;
case 'm':
- directive.fn = bfs_printf_m;
+ fmt.fn = bfs_printf_m;
specifier = "o";
break;
case 'M':
- directive.fn = bfs_printf_M;
+ fmt.fn = bfs_printf_M;
break;
case 'n':
- directive.fn = bfs_printf_n;
+ fmt.fn = bfs_printf_n;
break;
case 'p':
- directive.fn = bfs_printf_p;
+ fmt.fn = bfs_printf_p;
break;
case 'P':
- directive.fn = bfs_printf_P;
+ fmt.fn = bfs_printf_P;
break;
case 's':
- directive.fn = bfs_printf_s;
+ fmt.fn = bfs_printf_s;
break;
case 'S':
- directive.fn = bfs_printf_S;
+ fmt.fn = bfs_printf_S;
specifier = "g";
break;
case 't':
- directive.fn = bfs_printf_ctime;
- directive.stat_field = BFS_STAT_MTIME;
+ fmt.fn = bfs_printf_ctime;
+ fmt.stat_field = BFS_STAT_MTIME;
break;
case 'u':
- directive.ptr = bfs_ctx_users(ctx);
- if (!directive.ptr) {
- int error = errno;
- bfs_expr_error(ctx, expr);
- bfs_error(ctx, "Couldn't parse the user table: %s.\n", strerror(error));
- goto directive_error;
- }
- directive.fn = bfs_printf_u;
+ fmt.fn = bfs_printf_u;
+ fmt.ptr = ctx->users;
break;
case 'U':
- directive.fn = bfs_printf_U;
+ fmt.fn = bfs_printf_U;
break;
case 'w':
- directive.fn = bfs_printf_ctime;
- directive.stat_field = BFS_STAT_BTIME;
+ fmt.fn = bfs_printf_ctime;
+ fmt.stat_field = BFS_STAT_BTIME;
break;
case 'y':
- directive.fn = bfs_printf_y;
+ fmt.fn = bfs_printf_y;
break;
case 'Y':
- directive.fn = bfs_printf_Y;
+ fmt.fn = bfs_printf_Y;
+ break;
+ case 'Z':
+#if BFS_CAN_CHECK_CONTEXT
+ fmt.fn = bfs_printf_Z;
break;
+#else
+ bfs_expr_error(ctx, expr);
+ bfs_error(ctx, "Missing platform support for '%%%c'.\n", c);
+ goto fmt_error;
+#endif
case 'A':
- directive.stat_field = BFS_STAT_ATIME;
- goto directive_strftime;
+ fmt.stat_field = BFS_STAT_ATIME;
+ goto fmt_strftime;
case 'B':
case 'W':
- directive.stat_field = BFS_STAT_BTIME;
- goto directive_strftime;
+ fmt.stat_field = BFS_STAT_BTIME;
+ goto fmt_strftime;
case 'C':
- directive.stat_field = BFS_STAT_CTIME;
- goto directive_strftime;
+ fmt.stat_field = BFS_STAT_CTIME;
+ goto fmt_strftime;
case 'T':
- directive.stat_field = BFS_STAT_MTIME;
- goto directive_strftime;
+ fmt.stat_field = BFS_STAT_MTIME;
+ goto fmt_strftime;
- directive_strftime:
- directive.fn = bfs_printf_strftime;
+ fmt_strftime:
+ fmt.fn = bfs_printf_strftime;
c = *++i;
if (!c) {
bfs_expr_error(ctx, expr);
- bfs_error(ctx, "Incomplete time specifier '%s%c'.\n", directive.str, i[-1]);
- goto directive_error;
+ bfs_error(ctx, "Incomplete time specifier '%s%c'.\n", fmt.str, i[-1]);
+ goto fmt_error;
} else if (strchr("%+@aAbBcCdDeFgGhHIjklmMnprRsStTuUVwWxXyYzZ", c)) {
- directive.c = c;
+ fmt.c = c;
} else {
bfs_expr_error(ctx, expr);
bfs_error(ctx, "Unrecognized time specifier '%%%c%c'.\n", i[-1], c);
- goto directive_error;
+ goto fmt_error;
}
break;
case '\0':
bfs_expr_error(ctx, expr);
- bfs_error(ctx, "Incomplete format specifier '%s'.\n", directive.str);
- goto directive_error;
+ bfs_error(ctx, "Incomplete format specifier '%s'.\n", fmt.str);
+ goto fmt_error;
default:
bfs_expr_error(ctx, expr);
bfs_error(ctx, "Unrecognized format specifier '%%%c'.\n", c);
- goto directive_error;
+ goto fmt_error;
}
if (must_be_numeric && strcmp(specifier, "s") == 0) {
bfs_expr_error(ctx, expr);
- bfs_error(ctx, "Invalid flags '%s' for string format '%%%c'.\n", directive.str + 1, c);
- goto directive_error;
+ bfs_error(ctx, "Invalid flags '%s' for string format '%%%c'.\n", fmt.str + 1, c);
+ goto fmt_error;
}
- if (dstrcat(&directive.str, specifier) != 0) {
+ if (dstrcat(&fmt.str, specifier) != 0) {
bfs_perror(ctx, "dstrcat()");
- goto directive_error;
+ goto fmt_error;
}
- if (append_directive(ctx, &expr->printf, &literal, &directive) != 0) {
- goto directive_error;
+ if (append_directive(ctx, expr->printf, &literal, &fmt) != 0) {
+ goto fmt_error;
}
continue;
- directive_error:
- dstrfree(directive.str);
+ fmt_error:
+ dstrfree(fmt.str);
goto error;
}
@@ -891,7 +927,7 @@ int bfs_printf_parse(const struct bfs_ctx *ctx, struct bfs_expr *expr, const cha
}
done:
- if (append_literal(ctx, &expr->printf, &literal) != 0) {
+ if (append_literal(ctx, expr->printf, &literal) != 0) {
goto error;
}
dstrfree(literal);
@@ -907,9 +943,9 @@ error:
int bfs_printf(CFILE *cfile, const struct bfs_printf *format, const struct BFTW *ftwbuf) {
int ret = 0, error = 0;
- for (size_t i = 0; i < darray_length(format); ++i) {
- const struct bfs_printf *directive = &format[i];
- if (directive->fn(cfile, directive, ftwbuf) < 0) {
+ for (size_t i = 0; i < format->nfmts; ++i) {
+ const struct bfs_fmt *fmt = &format->fmts[i];
+ if (fmt->fn(cfile, fmt, ftwbuf) < 0) {
ret = -1;
error = errno;
}
@@ -920,8 +956,13 @@ int bfs_printf(CFILE *cfile, const struct bfs_printf *format, const struct BFTW
}
void bfs_printf_free(struct bfs_printf *format) {
- for (size_t i = 0; i < darray_length(format); ++i) {
- dstrfree(format[i].str);
+ if (!format) {
+ return;
+ }
+
+ for (size_t i = 0; i < format->nfmts; ++i) {
+ dstrfree(format->fmts[i].str);
}
- darray_free(format);
+ free(format->fmts);
+ free(format);
}