diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2024-05-07 15:42:46 -0400 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2024-05-07 15:42:46 -0400 |
commit | 452d6697e0f92326ab139eed4eadd9c2fd8b55ca (patch) | |
tree | 0feeb3722dcf6debb6c33c5175342bf1d70a1dba /src/printf.c | |
parent | a4299f9bc1d3e60a7e628561e8d650c2a241e1c2 (diff) | |
parent | c5cf2cf90834f2f56b2940d2a499a1a614ebfd21 (diff) | |
download | bfs-find2fd.tar.xz |
Merge branch 'main' into find2fdfind2fd
Diffstat (limited to 'src/printf.c')
-rw-r--r-- | src/printf.c | 548 |
1 files changed, 300 insertions, 248 deletions
diff --git a/src/printf.c b/src/printf.c index 6a7b9a9..f8428f7 100644 --- a/src/printf.c +++ b/src/printf.c @@ -1,59 +1,46 @@ -/**************************************************************************** - * 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 "config.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 "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. */ @@ -62,10 +49,20 @@ struct bfs_printf { 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; @@ -73,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"}; @@ -102,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); @@ -174,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; } - struct bfs_groups *groups = directive->ptr; + 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; @@ -291,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); @@ -302,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); } @@ -356,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; @@ -380,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; } @@ -420,23 +446,23 @@ static int bfs_printf_P(CFILE *cfile, const struct bfs_printf *directive, const copybuf.nameoff -= offset; return cfprintf(cfile, "%pP", ©buf); } 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; @@ -446,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; } - struct bfs_users *users = directive->ptr; + 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) { @@ -492,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; +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; - if (ftwbuf->type != BFS_LNK) { - return bfs_printf_y(cfile, directive, ftwbuf); - } - - 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; @@ -544,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()"); @@ -574,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; @@ -624,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; @@ -649,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"; @@ -672,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; } @@ -692,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; } @@ -702,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'); @@ -712,163 +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.fn = bfs_printf_F; - directive.ptr = (void *)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; } break; case 'g': - directive.fn = bfs_printf_g; - directive.ptr = ctx->groups; + 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.fn = bfs_printf_u; - directive.ptr = ctx->users; + 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; } @@ -880,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); @@ -896,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; } @@ -909,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); } |