diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 131ad369..9e5d8019 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,8 +30,9 @@ jobs: # see: https://github.com/actions/setup-python/issues/577 brew update || true brew install llvm || true - brew link --overwrite python@3.11 + brew unlink python@3.11 && brew link python@3.11 export PATH="/usr/local/opt/llvm/bin:$PATH" + export PATH="/opt/homebrew/opt/llvm/bin:$PATH" export CFLAGS="$CFLAGS -Werror" make clean make diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index b220cfba..dc7d8ac5 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -3,12 +3,21 @@ name: 'Lock threads' on: schedule: - cron: '0 0 * * *' + workflow_dispatch: + +permissions: + issues: write + pull-requests: write + discussions: write + +concurrency: + group: lock-threads jobs: lock: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v2 + - uses: dessant/lock-threads@v5 with: github-token: ${{ github.token }} issue-lock-inactive-days: '30' diff --git a/Makefile b/Makefile index 372893a2..5edac1dc 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,8 @@ O_GITSTATUS := 1 # add git status to detail view O_NAMEFIRST := 0 # print file name first, add uid and guid to detail view O_RESTOREPREVIEW := 0 # add preview pipe to close and restore preview pane +T_ICONS := 0 # test if multiple icons options are set and fail + # convert targets to flags for backwards compatibility ifneq ($(filter debug,$(MAKECMDGOALS)),) O_DEBUG := 1 @@ -97,16 +99,28 @@ endif ifeq ($(strip $(O_ICONS)),1) ICONS_INCLUDE = icons-generated-icons-in-term.h CPPFLAGS += -DICONS_IN_TERM -DICONS_INCLUDE=\"$(ICONS_INCLUDE)\" +ifeq ($(strip $(T_ICONS)),1) +$(error Choose only one system for icons (O_ICONS, O_NERD or O_EMOJI)) +endif + T_ICONS := 1 endif ifeq ($(strip $(O_NERD)),1) ICONS_INCLUDE = icons-generated-nerd.h CPPFLAGS += -DNERD -DICONS_INCLUDE=\"$(ICONS_INCLUDE)\" +ifeq ($(strip $(T_ICONS)),1) +$(error Choose only one system for icons (O_ICONS, O_NERD or O_EMOJI)) +endif + T_ICONS := 1 endif ifeq ($(strip $(O_EMOJI)),1) ICONS_INCLUDE = icons-generated-emoji.h CPPFLAGS += -DEMOJI -DICONS_INCLUDE=\"$(ICONS_INCLUDE)\" +ifeq ($(strip $(T_ICONS)),1) +$(error Choose only one system for icons (O_ICONS, O_NERD or O_EMOJI)) +endif + T_ICONS := 1 endif ifeq ($(strip $(O_QSORT)),1) diff --git a/misc/auto-completion/zsh/_nnn b/misc/auto-completion/zsh/_nnn index 007e8f18..4cc4c3fb 100644 --- a/misc/auto-completion/zsh/_nnn +++ b/misc/auto-completion/zsh/_nnn @@ -20,6 +20,7 @@ args=( '(-e)-e[open text files in $VISUAL/$EDITOR/vi]' '(-E)-E[use EDITOR for undetached edits]' '(-f)-f[use readline history file]' + '(-F)-F[fifo notification mode]:mode:(( 0\:"notify as previewer" 1\:"notify as explorer" ))' '(-g)-g[regex filters]' '(-H)-H[show hidden files]' '(-i)-i[show current file info]' @@ -36,7 +37,7 @@ args=( '(-s)-s[load session]:session name' '(-S)-S[persistent session]' '(-t)-t[timeout to lock]:seconds' - '(-T)-T[a d e r s t v]:key' + '(-T)-T[sort order]:key:(( a\:"apparent disk usasge" d\:"disk usage" e\:"extension" r\:"reverse" s\:"size" t\:"time" v\:"version" ))' '(-u)-u[use selection (no prompt)]' '(-U)-U[show user and group]' '(-V)-V[show program version and exit]' diff --git a/misc/quitcd/quitcd.nu b/misc/quitcd/quitcd.nu index c2211b65..fe0d85a7 100644 --- a/misc/quitcd/quitcd.nu +++ b/misc/quitcd/quitcd.nu @@ -28,7 +28,9 @@ export def --env n [ if ($nnn_tmpfile | path exists) { # Remove from the first part of the string and the last single quote <'>. # Fix post-processing of nnn's given path that escapes its single quotes with POSIX syntax. - let path = open $nnn_tmpfile | str substring 4..-1 | str replace --all `'\''` `'` + let path = open $nnn_tmpfile + | str replace --all --regex `^cd '|'$` `` + | str replace --all `'\''` `'` ^rm -- $nnn_tmpfile diff --git a/nnn.1 b/nnn.1 index d4a06044..4a42da0a 100644 --- a/nnn.1 +++ b/nnn.1 @@ -148,7 +148,7 @@ supports the following options: .Pp .Fl "T key" sort order - keys: 'a'u / 'd'u / 'e'xtension / 'r'everse / 's'ize / 't'ime / 'v'ersion + keys: 'a'pparent disk usage / 'd'isk usage / 'e'xtension / 'r'everse / 's'ize / 't'ime / 'v'ersion capitalize to reverse (except 'r') .Pp .Fl u diff --git a/patches/colemak/mainline.diff b/patches/colemak/mainline.diff index 7dcc4597..127262ee 100644 --- a/patches/colemak/mainline.diff +++ b/patches/colemak/mainline.diff @@ -1,8 +1,8 @@ diff --git a/src/nnn.c b/src/nnn.c -index 6792d503..0a59e8e3 100644 +index d7c53166..bb7ff3e8 100644 --- a/src/nnn.c +++ b/src/nnn.c -@@ -5148,12 +5148,12 @@ static void show_help(const char *path) +@@ -5149,12 +5149,12 @@ static void show_help(const char *path) "2(___n))\n" "0\n" "1NAVIGATION\n" @@ -20,12 +20,12 @@ index 6792d503..0a59e8e3 100644 "8B (,) Book(mark)%11b ^/ Select bookmark\n" "a1-4 Context%11(Sh)Tab Cycle/new context\n" "62Esc ^Q Quit%19^y Next young\n" -@@ -5161,20 +5161,20 @@ static void show_help(const char *path) +@@ -5162,27 +5162,27 @@ static void show_help(const char *path) "cq Quit context\n" "0\n" "1FILTER & PROMPT\n" - "c/ Filter%17^N Toggle type-to-nav\n" -+ "c/ Filter%17^M Toggle type-to-nav\n" ++ "c/ Filter%17^K Toggle type-to-nav\n" "aEsc Exit prompt%12^L Toggle last filter\n" "c. Toggle hidden%05Alt+Esc Unfilter, quit context\n" "0\n" @@ -45,8 +45,20 @@ index 6792d503..0a59e8e3 100644 "9x ^X Delete or trash%09S Listed sel size\n" "cX Delete (rm -rf)%07Esc Send to FIFO\n" "0\n" + "1MISC\n" + "8Alt ; Select plugin%11= Launch app\n" + "9! ^] Shell%19] Cmd prompt\n" +- "cc Connect remote%10u Unmount remote/archive\n" ++ "cC Connect remote%10u Unmount remote/archive\n" + "9t ^T Sort toggles%12s Manage session\n" + "cT Set time type%110 Lock\n" + "b^L Redraw%18? Help, conf\n" diff --git a/src/nnn.h b/src/nnn.h +<<<<<<< HEAD index bd500244..b12df5c0 100644 +======= +index bd500244..43b7fa22 100644 +>>>>>>> c/master --- a/src/nnn.h +++ b/src/nnn.h @@ -139,12 +139,12 @@ static struct key bindings[] = { @@ -79,12 +91,21 @@ index bd500244..b12df5c0 100644 /* Go to first file */ { '\'', SEL_FIRST }, /* Jump to an entry number/offset */ +@@ -179,7 +179,7 @@ static struct key bindings[] = { + { 'b', SEL_BMOPEN }, + { CONTROL('_'), SEL_BMOPEN }, + /* Connect to server over SSHFS */ +- { 'c', SEL_REMOTE }, ++ { 'C', SEL_REMOTE }, + /* Cycle contexts in forward direction */ + { '\t', SEL_CYCLE }, + /* Cycle contexts in reverse direction */ @@ -202,7 +202,7 @@ static struct key bindings[] = { /* Filter */ { '/', SEL_FLTR }, /* Toggle filter mode */ - { CONTROL('N'), SEL_MFLTR }, -+ { CONTROL('M'), SEL_MFLTR }, ++ { CONTROL('K'), SEL_MFLTR }, /* Toggle hide .dot files */ { '.', SEL_HIDDEN }, /* Detailed listing */ diff --git a/plugins/preview-tabbed b/plugins/preview-tabbed index 68d8d1ab..ab5b2c1f 100755 --- a/plugins/preview-tabbed +++ b/plugins/preview-tabbed @@ -69,6 +69,9 @@ else echo "No xembed term found" >&2 fi +if type xdg-user-dir >/dev/null 2>&1 ; then + PICTURES_DIR=$(xdg-user-dir PICTURES) +fi term_nuke () { # $1 -> $XID, $2 -> $FILE @@ -177,7 +180,17 @@ previewer_loop () { fi ;; inode/directory) - $TERMINAL "$XID" -e nnn "$FILE" & + if [[ -n $PICTURES_DIR && "$FILE" == "$PICTURES_DIR"* ]] ; then + if type sxiv >/dev/null 2>&1 ; then + sxiv -te "$XID" "$FILE" & + elif type nsxiv >/dev/null 2>&1 ; then + nsxiv -te "$XID" "$FILE" & + else + $TERMINAL "$XID" -e nnn "$FILE" & + fi + else + $TERMINAL "$XID" -e nnn "$FILE" & + fi ;; text/*) if [ -x "$NUKE" ] ; then diff --git a/plugins/preview-tui b/plugins/preview-tui index d25d934e..654a5c8d 100755 --- a/plugins/preview-tui +++ b/plugins/preview-tui @@ -44,6 +44,7 @@ # - optional: pistol file viewer (https://github.com/doronbehar/pistol). # 1. install pistol # 2. set/export $NNN_PISTOL as 1 +# - optional: librsvg for rsvg-convert # # Usage: # You need to set a NNN_FIFO path and a key for the plugin with NNN_PLUG, @@ -94,7 +95,7 @@ # 'no_focus [title="preview-tui"]' to your i3 config file. # # Shell: Bash (for environment manipulation through arrays) -# Authors: Todd Yamakawa, Léo Villeveygoux, @Recidiviste, Mario Ortiz Manero, Luuk van Baal, @WanderLanz +# Authors: Todd Yamakawa, Léo Villeveygoux, @Recidiviste, Mario Ortiz Manero, Luuk van Baal, @WanderLanz, @flipflop133 #SPLIT="$SPLIT" # you can set a permanent split here #TERMINAL="$TERMINAL" # same goes for the terminal @@ -405,7 +406,13 @@ generate_preview() { else image_preview "$1" "$2" "$3" && return fi ;; - image) image_preview "$1" "$2" "$3" && return ;; + image) if exists rsvg-convert && [[ "${3##*.}" == "svg" ]]; then + rsvg-convert -a -w "$NNN_PREVIEWWIDTH" -h "$NNN_PREVIEWHEIGHT" -f png -o "$NNN_PREVIEWDIR/$3.png" "$3" + elif exists convert; then + convert "$3" -flatten -resize "$NNN_PREVIEWWIDTH"x"$NNN_PREVIEWHEIGHT"\> "$NNN_PREVIEWDIR/$3.jpg" + else + image_preview "$1" "$2" "$3" && return + fi ;; office) libreoffice --convert-to jpg "$3" --outdir "$NNN_PREVIEWDIR/${3%/*}" filename="$(printf "%s" "${3##*/}" | cut -d. -f1)" mv -- "$NNN_PREVIEWDIR/${3%/*}/$filename.jpg" "$NNN_PREVIEWDIR/$3.jpg" ;; @@ -416,6 +423,8 @@ generate_preview() { fi if [ -f "$NNN_PREVIEWDIR/$3.jpg" ]; then image_preview "$1" "$2" "$NNN_PREVIEWDIR/$3.jpg" + elif [[ "${3##*.}" == "svg" ]] && [ -f "$NNN_PREVIEWDIR/$3.png" ]; then + image_preview "$1" "$2" "$NNN_PREVIEWDIR/$3.png" else fifo_pager print_bin_info "$3" fi diff --git a/plugins/upload b/plugins/upload index 4948587b..e225d207 100755 --- a/plugins/upload +++ b/plugins/upload @@ -1,12 +1,11 @@ #!/usr/bin/env sh -# Description: Selections are uploaded using Firefox Send +# Description: Selections are archived into a tar file (uncompressed) and uploaded to file.io # For single files: -# Upload to Firefox Send if ffsend is found, else -# Paste contents of a text a file http://ix.io +# Paste contents of a text file to http://ix.io # Upload a binary file to file.io # -# Dependencies: ffsend (https://github.com/timvisee/ffsend), curl, jq, tr +# Dependencies: curl, jq, tar, file with `--mime-type` support # # Note: Binary file set to expire after a week # @@ -15,30 +14,24 @@ selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection} if [ -s "$selection" ]; then - if type ffsend >/dev/null 2>&1; then - # File name will be randomized foo.tar - xargs -0 < "$selection" ffsend u - else - printf "ffsend is required to upload selection." - fi + xargs -0 tar -c < "$selection" | \ + curl -s -F "file=@/dev/stdin;filename=selection.tar" 'https://file.io/?expires=1w' | \ + jq '.link' | tr -d '"' # Clear selection printf "-" > "$NNN_PIPE" else if [ -n "$1" ] && [ -s "$1" ]; then - if type ffsend >/dev/null 2>&1; then - ffsend -fiq u "$1" - elif [ "$(mimetype --output-format %m "$1" | awk -F '/' '{print $1}')" = "text" ]; then - curl -F "f:1=@$1" ix.io - else - # Upload the file, show the download link and wait till user presses any key - curl -s -F "file=@$1" https://file.io/?expires=1w | jq '.link' | tr -d '"' - - # To write download link to "$1".loc and exit - # curl -s -F "file=@$1" https://file.io/?expires=1w -o `basename "$1"`.loc - fi + if file --mime-type "$1" | grep -q -F "text/"; then + curl -F "file=@$1" https://0x0.st + else + # Upload the file, show the download link and wait till user presses any key + curl -s -F "file=@$1" https://file.io/?expires=1w | jq '.link' | tr -d '"' + # To write download link to "$1".loc and exit + # curl -s -F "file=@$1" https://file.io/?expires=1w -o `basename "$1"`.loc + fi else - printf "empty file!" + printf "empty file!" fi fi diff --git a/src/.clang-tidy b/src/.clang-tidy index 6772ddbc..bad19684 100644 --- a/src/.clang-tidy +++ b/src/.clang-tidy @@ -22,10 +22,13 @@ Checks: > -readability-identifier-length, -readability-isolate-declaration, -readability-suspicious-call-argument, + -readability-avoid-nested-conditional-operator, -bugprone-easily-swappable-parameters, -bugprone-narrowing-conversions, -bugprone-reserved-identifier, -bugprone-switch-missing-default-case, + -bugprone-inc-dec-in-conditions, + -bugprone-multi-level-implicit-pointer-conversion, WarningsAsErrors: '*' HeaderFilterRegex: '.*(?> 6); + ullong_t *m = ihashbmp + (nr >> 6); if (*m & (1 << (nr & 63))) { pthread_mutex_unlock(&hardlink_mutex); @@ -1454,7 +1452,7 @@ static int create_tmp_file(void) static void msg(const char *message) { - dprintf(STDERR_FILENO, "%s\n", message); + fprintf(stderr, "%s\n", message); } #ifdef KEY_RESIZE @@ -1584,19 +1582,24 @@ static void xdelay(useconds_t delay) static char confirm_force(bool selection, bool use_trash) { - char str[64]; + char str[300]; /* Note: ideally we should use utils[UTIL_RM_RF] instead of the "rm -rf" string */ - snprintf(str, 64, messages[MSG_FORCE_RM], - use_trash ? utils[UTIL_GIO_TRASH] + 4 : "rm -rf", - (selection ? "selected" : "hovered")); + int r = snprintf(str, 20, "%s", use_trash ? utils[UTIL_GIO_TRASH] + 4 : "rm -rf"); - int r = get_input(str); + if (selection) + snprintf(str + r, 280, " %d files?", nselected); + else + snprintf(str + r, 280, " '%s'?", pdents[cur].name); + + r = get_input(str); if (r == ESC) return '\0'; /* cancel */ if (r == 'y' || r == 'Y') return 'f'; /* forceful for rm */ + if (r == 'n' || r == 'N') + return '\0'; /* cancel */ return (use_trash ? '\0' : 'i'); /* interactive for rm */ } @@ -2315,8 +2318,10 @@ static bool initcurses(void *oldmask) msg(env_cfg[NNN_COLORS]); return FALSE; } - } else + } else { *pcode = (*colors < '0' || *colors > '7') ? 4 : *colors - '0'; + fcolors[i + 1] = *pcode; + } ++colors; } else *pcode = 4; @@ -2587,7 +2592,7 @@ static bool rmmulstr(char *buf, bool use_trash) return FALSE; if (!use_trash) - snprintf(buf, CMD_LEN_MAX, "xargs -0 sh -c 'rm -%cr -- \"$0\" \"$@\" < /dev/tty' < %s", + snprintf(buf, CMD_LEN_MAX, "xargs -0 sh -c 'rm -%cvr -- \"$0\" \"$@\" < /dev/tty' < %s", r, selpath); else snprintf(buf, CMD_LEN_MAX, "xargs -0 %s < %s", @@ -2604,9 +2609,9 @@ static bool xrm(char * const fpath, bool use_trash) return FALSE; if (!use_trash) { - char rm_opts[] = "-ir"; + char rm_opts[5] = "-vr\0"; - rm_opts[1] = r; + rm_opts[3] = r; spawn("rm", rm_opts, "--", fpath, F_NORMAL | F_CHKRTN); } else spawn(utils[(g_state.trash == 1) ? UTIL_TRASH_CLI : UTIL_GIO_TRASH], @@ -2872,7 +2877,11 @@ static void write_lastdir(const char *curpath, const char *outfile) : cfgpath, O_CREAT | O_WRONLY | O_TRUNC, S_IWUSR | S_IRUSR); if (fd != -1 && shell_escape(g_buf, sizeof(g_buf), curpath)) { - dprintf(fd, "cd %s", g_buf); + if (write(fd, "cd ", 3) == 3) { + if (write(fd, g_buf, strlen(g_buf)) != (ssize_t)strlen(g_buf)) { + DPRINTF_S("write failed!"); + } + } close(fd); } } @@ -4808,7 +4817,7 @@ next: return FALSE; } } else { - int fd = open(path, O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR); /* Forced create mode for files */ + int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR); /* Forced create mode for files */ if (fd == -1 && errno != EEXIST) { DPRINTF_S("open!"); @@ -5133,12 +5142,12 @@ static void lock_terminal(void) spawn(xgetenv("NNN_LOCKER", utils[UTIL_LOCKER]), NULL, NULL, NULL, F_CLI); } -static void printkv(kv *kvarr, int fd, uchar_t max, uchar_t id) +static void printkv(kv *kvarr, FILE *f, uchar_t max, uchar_t id) { char *val = (id == NNN_BMS) ? bmstr : pluginstr; for (uchar_t i = 0; i < max && kvarr[i].key; ++i) - dprintf(fd, " %c: %s\n", (char)kvarr[i].key, val + kvarr[i].off); + fprintf(f, " %c: %s\n", (char)kvarr[i].key, val + kvarr[i].off); } static void printkeys(kv *kvarr, char *buf, uchar_t max) @@ -5263,66 +5272,71 @@ static void show_help(const char *path) "cT Set time type%110 Lock\n" "b^L Redraw%18? Help, conf\n" }; - char help_buf[1<<11]; // if editing helpstr, ensure this has enough space to decode it int fd = create_tmp_file(); if (fd == -1) return; + FILE *f = fdopen(fd, "wb"); + if (f == NULL) { + close(fd); + unlink(g_tmpfpath); + return; + } char *prog = xgetenv(env_cfg[NNN_HELP], NULL); if (prog) get_output(prog, NULL, NULL, fd, FALSE); bool hex = true; - char *w = help_buf; - const char *end = helpstr + (sizeof helpstr - 1); + const char *end = helpstr + sizeof(helpstr) - 1; + for (const char *s = helpstr; s < end; ++s) { if (hex) { - for (int k = 0, n = xchartohex(*s); k < n; ++k) *w++ = ' '; + for (int k = 0, n = xchartohex(*s); k < n; ++k) + fputc(' ', f); } else if (*s == '%') { int n = ((s[1] - '0') * 10) + (s[2] - '0'); - for (int k = 0; k < n; ++k) *w++ = ' '; + for (int k = 0; k < n; ++k) + fputc(' ', f); s += 2; } else { - *w++ = *s; + fputc(*s, f); } - hex = *s == '\n'; + hex = (*s == '\n'); } - ssize_t res = write(fd, help_buf, w - help_buf); - (void)res; // silence warning - dprintf(fd, "\nLOCATIONS\n"); + fprintf(f, "\nLOCATIONS\n"); for (uchar_t i = 0; i < CTX_MAX; ++i) if (g_ctx[i].c_cfg.ctxactive) - dprintf(fd, " %u: %s\n", i + 1, g_ctx[i].c_path); + fprintf(f, " %u: %s\n", i + 1, g_ctx[i].c_path); - dprintf(fd, "\nVOLUME: avail:%s ", coolsize(get_fs_info(path, VFS_AVAIL))); - dprintf(fd, "used:%s ", coolsize(get_fs_info(path, VFS_USED))); - dprintf(fd, "size:%s\n\n", coolsize(get_fs_info(path, VFS_SIZE))); + fprintf(f, "\nVOLUME: avail:%s ", coolsize(get_fs_info(path, VFS_AVAIL))); + fprintf(f, "used:%s ", coolsize(get_fs_info(path, VFS_USED))); + fprintf(f, "size:%s\n\n", coolsize(get_fs_info(path, VFS_SIZE))); if (bookmark) { - dprintf(fd, "BOOKMARKS\n"); - printkv(bookmark, fd, maxbm, NNN_BMS); - dprintf(fd, "\n"); + fprintf(f, "BOOKMARKS\n"); + printkv(bookmark, f, maxbm, NNN_BMS); + fprintf(f, "\n"); } if (plug) { - dprintf(fd, "PLUGIN KEYS\n"); - printkv(plug, fd, maxplug, NNN_PLUG); - dprintf(fd, "\n"); + fprintf(f, "PLUGIN KEYS\n"); + printkv(plug, f, maxplug, NNN_PLUG); + fprintf(f, "\n"); } for (uchar_t i = NNN_OPENER; i <= NNN_TRASH; ++i) { char *s = getenv(env_cfg[i]); if (s) - dprintf(fd, "%s: %s\n", env_cfg[i], s); + fprintf(f, "%s: %s\n", env_cfg[i], s); } if (selpath) - dprintf(fd, "SELECTION FILE: %s\n", selpath); + fprintf(f, "SELECTION FILE: %s\n", selpath); - dprintf(fd, "\nv%s\n%s\n", VERSION, GENERAL_INFO); - close(fd); + fprintf(f, "\nv%s\n%s\n", VERSION, GENERAL_INFO); + fclose(f); // also closes fd spawn(pager, g_tmpfpath, NULL, NULL, F_CLI | F_TTY); unlink(g_tmpfpath); @@ -8455,7 +8469,7 @@ static void check_key_collision(void) key = bindings[i].sym; if (bitmap[key]) - dprintf(STDERR_FILENO, "key collision! [%s]\n", keyname(key)); + fprintf(stderr, "key collision! [%s]\n", keyname(key)); else bitmap[key] = TRUE; } @@ -8463,7 +8477,7 @@ static void check_key_collision(void) static void usage(void) { - dprintf(STDOUT_FILENO, + fprintf(stdout, "%s: nnn [OPTIONS] [PATH]\n\n" "The unorthodox terminal file manager.\n\n" "positional args:\n" @@ -8819,7 +8833,7 @@ int main(int argc, char *argv[]) g_state.uidgid = 1; break; case 'V': - dprintf(STDOUT_FILENO, "%s\n", VERSION); + fprintf(stdout, "%s\n", VERSION); return EXIT_SUCCESS; case 'x': cfg.x11 = 1;