summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE.txt64
-rw-r--r--.github/PULL_REQUEST_TEMPLATE.txt68
-rw-r--r--.gitignore1
-rw-r--r--.mailmap5
-rw-r--r--.prev-version2
-rw-r--r--.vg-suppressions2
-rw-r--r--HACKING2
-rw-r--r--Makefile.am7
-rw-r--r--NEWS285
-rw-r--r--README2
-rw-r--r--README-hacking2
-rw-r--r--README-valgrind2
-rw-r--r--THANKS.in2
-rw-r--r--TODO2
-rwxr-xr-xbootstrap9
-rw-r--r--bootstrap.conf26
-rwxr-xr-xbuild-aux/gen-lists-of-programs.sh23
-rwxr-xr-xbuild-aux/gen-single-binary.sh6
-rwxr-xr-xbuild-aux/makeinfo-wrapper.sh.in41
-rw-r--r--cfg.mk51
-rw-r--r--configure.ac141
-rw-r--r--doc/coreutils.texi3881
-rw-r--r--doc/local.mk10
-rw-r--r--doc/perm.texi2
-rw-r--r--doc/sort-version.texi2
-rw-r--r--gl/lib/buffer-lcm.c2
-rw-r--r--gl/lib/cl-strtod.c2
-rw-r--r--gl/lib/dtimespec-bound.h2
-rw-r--r--gl/lib/fadvise.c2
-rw-r--r--gl/lib/fadvise.h2
-rw-r--r--gl/lib/fd-reopen.c4
-rw-r--r--gl/lib/fd-reopen.h2
-rw-r--r--gl/lib/heap.c6
-rw-r--r--gl/lib/heap.h2
-rw-r--r--gl/lib/mbbuf.c2
-rw-r--r--gl/lib/mbbuf.h93
-rw-r--r--gl/lib/rand-isaac.c2
-rw-r--r--gl/lib/rand-isaac.h2
-rw-r--r--gl/lib/randint.c12
-rw-r--r--gl/lib/randint.h2
-rw-r--r--gl/lib/randperm.c12
-rw-r--r--gl/lib/randread.c14
-rw-r--r--gl/lib/randread.h2
-rw-r--r--gl/lib/root-dev-ino.c6
-rw-r--r--gl/lib/root-dev-ino.h2
-rw-r--r--gl/lib/skipchars.h2
-rw-r--r--gl/lib/smack.h4
-rw-r--r--gl/lib/strintcmp.c2
-rw-r--r--gl/lib/strnumcmp-in.h2
-rw-r--r--gl/lib/strnumcmp.c2
-rw-r--r--gl/lib/targetdir.c2
-rw-r--r--gl/lib/targetdir.h2
-rw-r--r--gl/lib/xdectoint.c4
-rw-r--r--gl/lib/xdectoint.h2
-rw-r--r--gl/lib/xfts.c4
-rw-r--r--gl/local.mk2
-rw-r--r--gl/tests/test-fadvise.c4
-rw-r--r--gl/tests/test-rand-isaac.c4
m---------gnulib0
-rw-r--r--init.cfg57
-rwxr-xr-xlib/t-chdir-long2
-rw-r--r--m4/check-decl.m42
-rw-r--r--m4/include-exclude-prog.m42
-rw-r--r--m4/jm-macros.m43
-rw-r--r--m4/prereq.m42
-rw-r--r--m4/stat-prog.m42
-rw-r--r--man/basenc.x2
-rw-r--r--man/chmod.x6
-rw-r--r--man/chown.x2
-rw-r--r--man/date.x12
-rw-r--r--man/df.x2
-rw-r--r--man/dir.x2
-rw-r--r--man/du.x2
-rw-r--r--man/env.x2
-rwxr-xr-xman/help2man142
-rw-r--r--man/local.mk9
-rw-r--r--man/md5sum.x5
-rw-r--r--man/rm.x2
-rw-r--r--man/sha1sum.x7
-rw-r--r--man/stdbuf.x2
-rw-r--r--man/test.x2
-rw-r--r--man/touch.x2
-rw-r--r--man/vdir.x2
-rwxr-xr-xman/viewman32
-rw-r--r--po/POTFILES.in4
-rw-r--r--scripts/build-older-versions/README.older-versions2
-rwxr-xr-xscripts/build-older-versions/build-older-versions.sh2
-rwxr-xr-xscripts/git-hooks/commit-msg4
-rw-r--r--src/.gitignore1
-rw-r--r--src/basename.c84
-rw-r--r--src/basenc.c141
-rw-r--r--src/blake2/b2sum.c6
-rw-r--r--src/cat.c267
-rw-r--r--src/chcon.c144
-rw-r--r--src/chmod.c109
-rw-r--r--src/chown-chgrp.c2
-rw-r--r--src/chown-chown.c2
-rw-r--r--src/chown-core.c58
-rw-r--r--src/chown-core.h4
-rw-r--r--src/chown.c127
-rw-r--r--src/chown.h13
-rw-r--r--src/chroot.c71
-rw-r--r--src/cksum.c2042
-rw-r--r--src/cksum.h69
-rw-r--r--src/cksum_avx2.c12
-rw-r--r--src/cksum_avx512.c12
-rw-r--r--src/cksum_crc.c377
-rw-r--r--src/cksum_crc.h50
-rw-r--r--src/cksum_pclmul.c12
-rw-r--r--src/cksum_vmull.c10
-rw-r--r--src/comm.c100
-rw-r--r--src/copy-file-data.c71
-rw-r--r--src/copy.c61
-rw-r--r--src/copy.h4
-rw-r--r--src/coreutils-arch.c2
-rw-r--r--src/coreutils-chgrp.c2
-rw-r--r--src/coreutils-dir.c2
-rw-r--r--src/coreutils-md5sum.c31
-rw-r--r--src/coreutils-sha1sum.c31
-rw-r--r--src/coreutils-sha224sum.c31
-rw-r--r--src/coreutils-sha256sum.c31
-rw-r--r--src/coreutils-sha384sum.c31
-rw-r--r--src/coreutils-sha512sum.c31
-rw-r--r--src/coreutils-vdir.c2
-rw-r--r--src/coreutils.c36
-rw-r--r--src/cp-hash.c22
-rw-r--r--src/cp.c395
-rw-r--r--src/csplit.c229
-rw-r--r--src/cut.c1299
-rw-r--r--src/date.c360
-rwxr-xr-xsrc/dcgen2
-rw-r--r--src/dd.c227
-rw-r--r--src/df.c365
-rw-r--r--src/digest.c1805
-rw-r--r--src/dircolors.c70
-rw-r--r--src/dircolors.hin2
-rw-r--r--src/dirname.c27
-rwxr-xr-xsrc/du-tests2
-rw-r--r--src/du.c280
-rw-r--r--src/echo.c32
-rw-r--r--src/env.c189
-rw-r--r--src/expand-common.c36
-rw-r--r--src/expand-common.h6
-rw-r--r--src/expand.c70
-rw-r--r--src/expr.c153
-rw-r--r--src/extract-magic6
-rw-r--r--src/factor.c32
-rw-r--r--src/find-mount-point.c21
-rw-r--r--src/find-mount-point.h2
-rw-r--r--src/fmt.c118
-rw-r--r--src/fold.c50
-rw-r--r--src/force-link.c4
-rw-r--r--src/getlimits.c48
-rw-r--r--src/group-list.c10
-rw-r--r--src/group-list.h2
-rw-r--r--src/groups.c38
-rw-r--r--src/head.c83
-rw-r--r--src/hostid.c14
-rw-r--r--src/hostname.c10
-rw-r--r--src/id.c86
-rw-r--r--src/install.c313
-rw-r--r--src/ioblksize.h2
-rw-r--r--src/iopoll.c58
-rw-r--r--src/iopoll.h4
-rw-r--r--src/join.c164
-rw-r--r--src/kill.c47
-rw-r--r--src/libstdbuf.c8
-rw-r--r--src/link.c8
-rw-r--r--src/ln.c143
-rw-r--r--src/local.mk89
-rw-r--r--src/logname.c12
-rw-r--r--src/ls-dir.c2
-rw-r--r--src/ls-ls.c2
-rw-r--r--src/ls-vdir.c2
-rw-r--r--src/ls.c860
-rw-r--r--src/ls.h17
-rw-r--r--src/make-prime-list.c2
-rw-r--r--src/mkdir.c65
-rw-r--r--src/mkfifo.c53
-rw-r--r--src/mknod.c73
-rw-r--r--src/mktemp.c82
-rw-r--r--src/mv.c179
-rw-r--r--src/nice.c57
-rw-r--r--src/nl.c113
-rw-r--r--src/nohup.c34
-rw-r--r--src/nproc.c38
-rw-r--r--src/numfmt.c283
-rw-r--r--src/od.c145
-rw-r--r--src/operand2sig.c5
-rw-r--r--src/operand2sig.h2
-rw-r--r--src/paste.c226
-rw-r--r--src/pathchk.c36
-rw-r--r--src/pinky.c181
-rw-r--r--src/pr.c272
-rw-r--r--src/printenv.c45
-rw-r--r--src/printf.c16
-rw-r--r--src/prog-fprintf.c2
-rw-r--r--src/prog-fprintf.h2
-rw-r--r--src/ptx.c189
-rw-r--r--src/pwd.c96
-rw-r--r--src/readlink.c90
-rw-r--r--src/realpath.c65
-rw-r--r--src/relpath.c2
-rw-r--r--src/relpath.h2
-rw-r--r--src/remove.c12
-rw-r--r--src/remove.h2
-rw-r--r--src/rm.c106
-rw-r--r--src/rmdir.c53
-rw-r--r--src/runcon.c52
-rw-r--r--src/selinux.c26
-rw-r--r--src/selinux.h2
-rw-r--r--src/seq.c103
-rw-r--r--src/set-fields.c5
-rw-r--r--src/set-fields.h2
-rw-r--r--src/shred.c138
-rw-r--r--src/shuf.c93
-rw-r--r--src/sleep.c8
-rw-r--r--src/sort.c750
-rw-r--r--src/splice.h46
-rw-r--r--src/split.c173
-rw-r--r--src/stat.c123
-rw-r--r--src/statx.h2
-rw-r--r--src/stdbuf.c35
-rw-r--r--src/stty.c67
-rw-r--r--src/sum.c54
-rw-r--r--src/sum.h10
-rw-r--r--src/sync.c37
-rw-r--r--src/system.h261
-rw-r--r--src/tac-pipe.c8
-rw-r--r--src/tac.c53
-rw-r--r--src/tail.c197
-rw-r--r--src/tee.c92
-rw-r--r--src/temp-stream.c12
-rw-r--r--src/term-sig.h50
-rw-r--r--src/termios.c2
-rw-r--r--src/test.c93
-rw-r--r--src/timeout.c157
-rw-r--r--src/touch.c107
-rw-r--r--src/tr.c83
-rw-r--r--src/true.c8
-rw-r--r--src/truncate.c36
-rw-r--r--src/tsort.c44
-rw-r--r--src/tty.c21
-rw-r--r--src/uname-arch.c2
-rw-r--r--src/uname-uname.c2
-rw-r--r--src/uname.c60
-rw-r--r--src/uname.h13
-rw-r--r--src/unexpand.c87
-rw-r--r--src/uniq.c132
-rw-r--r--src/unlink.c8
-rw-r--r--src/uptime.c16
-rw-r--r--src/users.c13
-rw-r--r--src/wc.c130
-rw-r--r--src/wc.h1
-rw-r--r--src/wc_avx2.c2
-rw-r--r--src/wc_avx512.c2
-rw-r--r--src/wc_neon.c109
-rw-r--r--src/who.c151
-rw-r--r--src/whoami.c14
-rw-r--r--src/yes.c162
-rw-r--r--tests/Coreutils.pm12
-rw-r--r--tests/CuSkip.pm2
-rw-r--r--tests/CuTmpdir.pm2
-rwxr-xr-xtests/basenc/base64.pl2
-rwxr-xr-xtests/basenc/basenc.pl2
-rwxr-xr-xtests/basenc/bounded-memory.sh10
-rwxr-xr-xtests/basenc/large-input.sh2
-rwxr-xr-xtests/cat/cat-E.sh2
-rwxr-xr-xtests/cat/cat-buf.sh2
-rwxr-xr-xtests/cat/cat-proc.sh2
-rwxr-xr-xtests/cat/cat-self.sh2
-rwxr-xr-xtests/cat/splice.sh73
-rwxr-xr-xtests/chcon/chcon-fail.sh2
-rwxr-xr-xtests/chcon/chcon.sh2
-rwxr-xr-xtests/chgrp/basic.sh10
-rwxr-xr-xtests/chgrp/default-no-deref.sh2
-rwxr-xr-xtests/chgrp/deref.sh2
-rwxr-xr-xtests/chgrp/from.sh4
-rwxr-xr-xtests/chgrp/no-x.sh5
-rwxr-xr-xtests/chgrp/posix-H.sh2
-rwxr-xr-xtests/chgrp/recurse.sh2
-rwxr-xr-xtests/chmod/c-option.sh2
-rwxr-xr-xtests/chmod/equal-x.sh2
-rwxr-xr-xtests/chmod/equals.sh2
-rwxr-xr-xtests/chmod/ignore-symlink.sh2
-rwxr-xr-xtests/chmod/inaccessible.sh2
-rwxr-xr-xtests/chmod/no-x.sh5
-rwxr-xr-xtests/chmod/octal.sh2
-rwxr-xr-xtests/chmod/only-op.sh31
-rwxr-xr-xtests/chmod/partial-fail.sh31
-rwxr-xr-xtests/chmod/setgid.sh2
-rwxr-xr-xtests/chmod/silent.sh2
-rwxr-xr-xtests/chmod/symlinks.sh2
-rwxr-xr-xtests/chmod/thru-dangling.sh2
-rwxr-xr-xtests/chmod/umask-x.sh2
-rwxr-xr-xtests/chmod/usage.sh2
-rwxr-xr-xtests/chown/basic.sh2
-rwxr-xr-xtests/chown/deref.sh2
-rwxr-xr-xtests/chown/preserve-root.sh2
-rwxr-xr-xtests/chown/separator.sh6
-rwxr-xr-xtests/chroot/chroot-credentials.sh2
-rwxr-xr-xtests/chroot/chroot-fail.sh2
-rwxr-xr-xtests/cksum/b2sum.sh2
-rwxr-xr-xtests/cksum/cksum-a.sh11
-rwxr-xr-xtests/cksum/cksum-base64-untagged.sh2
-rwxr-xr-xtests/cksum/cksum-base64.pl2
-rwxr-xr-xtests/cksum/cksum-c.sh31
-rwxr-xr-xtests/cksum/cksum-raw.sh2
-rwxr-xr-xtests/cksum/cksum-sha3.sh2
-rwxr-xr-xtests/cksum/cksum.sh2
-rwxr-xr-xtests/cksum/md5sum-bsd.sh6
-rwxr-xr-xtests/cksum/md5sum-newline.pl2
-rwxr-xr-xtests/cksum/md5sum-parallel.sh2
-rwxr-xr-xtests/cksum/md5sum.pl2
-rwxr-xr-xtests/cksum/sha1sum-vec.pl2
-rwxr-xr-xtests/cksum/sha1sum.pl2
-rwxr-xr-xtests/cksum/sha224sum.pl2
-rwxr-xr-xtests/cksum/sha256sum.pl2
-rwxr-xr-xtests/cksum/sha384sum.pl2
-rwxr-xr-xtests/cksum/sha512sum.pl2
-rwxr-xr-xtests/cksum/sm3sum.pl2
-rwxr-xr-xtests/cksum/sum-sysv.sh2
-rwxr-xr-xtests/cksum/sum.pl2
-rwxr-xr-xtests/comm/comm.pl (renamed from tests/misc/comm.pl)2
-rwxr-xr-xtests/comm/dash-dash.sh31
-rwxr-xr-xtests/cp/abuse.sh2
-rwxr-xr-xtests/cp/acl.sh2
-rwxr-xr-xtests/cp/attr-existing.sh2
-rwxr-xr-xtests/cp/backup-1.sh2
-rwxr-xr-xtests/cp/backup-dir.sh2
-rwxr-xr-xtests/cp/backup-is-src.sh2
-rwxr-xr-xtests/cp/capability.sh2
-rwxr-xr-xtests/cp/copy-FMR.sh2
-rwxr-xr-xtests/cp/cp-HL.sh2
-rwxr-xr-xtests/cp/cp-a-selinux.sh7
-rwxr-xr-xtests/cp/cp-deref.sh2
-rwxr-xr-xtests/cp/cp-i.sh2
-rwxr-xr-xtests/cp/cp-mv-backup.sh2
-rwxr-xr-xtests/cp/cp-mv-enotsup-xattr.sh45
-rwxr-xr-xtests/cp/cp-parents.sh2
-rwxr-xr-xtests/cp/cross-dev-symlink.sh2
-rwxr-xr-xtests/cp/debug.sh7
-rwxr-xr-xtests/cp/deref-slink.sh2
-rwxr-xr-xtests/cp/dir-rm-dest.sh2
-rwxr-xr-xtests/cp/dir-slash.sh2
-rwxr-xr-xtests/cp/dir-vs-file.sh2
-rwxr-xr-xtests/cp/existing-perm-dir.sh2
-rwxr-xr-xtests/cp/existing-perm-race.sh2
-rwxr-xr-xtests/cp/fail-perm.sh18
-rwxr-xr-xtests/cp/file-perm-race.sh2
-rwxr-xr-xtests/cp/into-self.sh2
-rwxr-xr-xtests/cp/keep-directory-symlink.sh2
-rwxr-xr-xtests/cp/link-deref.sh2
-rwxr-xr-xtests/cp/link-heap.sh2
-rwxr-xr-xtests/cp/link-no-deref.sh2
-rwxr-xr-xtests/cp/link-preserve.sh2
-rwxr-xr-xtests/cp/link-symlink.sh2
-rwxr-xr-xtests/cp/link.sh2
-rwxr-xr-xtests/cp/nfs-removal-race.sh66
-rwxr-xr-xtests/cp/no-ctx.sh2
-rwxr-xr-xtests/cp/no-deref-link1.sh2
-rwxr-xr-xtests/cp/no-deref-link2.sh2
-rwxr-xr-xtests/cp/no-deref-link3.sh2
-rwxr-xr-xtests/cp/non-utf8-name.sh47
-rwxr-xr-xtests/cp/parent-perm-race.sh2
-rwxr-xr-xtests/cp/parent-perm.sh2
-rwxr-xr-xtests/cp/perm.sh2
-rwxr-xr-xtests/cp/preserve-2.sh2
-rwxr-xr-xtests/cp/preserve-gid.sh2
-rwxr-xr-xtests/cp/preserve-link.sh2
-rwxr-xr-xtests/cp/preserve-mode.sh2
-rwxr-xr-xtests/cp/preserve-slink-time.sh2
-rwxr-xr-xtests/cp/proc-short-read.sh2
-rwxr-xr-xtests/cp/proc-zero-len.sh2
-rwxr-xr-xtests/cp/r-vs-symlink.sh2
-rwxr-xr-xtests/cp/readonly-dir.sh65
-rwxr-xr-xtests/cp/reflink-auto.sh2
-rwxr-xr-xtests/cp/reflink-perm.sh2
-rwxr-xr-xtests/cp/same-file.sh2
-rwxr-xr-xtests/cp/slink-2-slink.sh2
-rwxr-xr-xtests/cp/sparse-2.sh2
-rwxr-xr-xtests/cp/sparse-extents-2.sh2
-rwxr-xr-xtests/cp/sparse-extents.sh2
-rwxr-xr-xtests/cp/sparse-perf.sh8
-rwxr-xr-xtests/cp/sparse-to-pipe.sh2
-rwxr-xr-xtests/cp/sparse.sh2
-rwxr-xr-xtests/cp/special-bits.sh2
-rwxr-xr-xtests/cp/special-f.sh2
-rwxr-xr-xtests/cp/src-base-dot.sh2
-rwxr-xr-xtests/cp/symlink-slash.sh2
-rwxr-xr-xtests/cp/thru-dangling.sh2
-rwxr-xr-xtests/csplit/csplit-1000.sh2
-rwxr-xr-xtests/csplit/csplit-heap.sh2
-rwxr-xr-xtests/csplit/csplit-io-err.sh79
-rwxr-xr-xtests/csplit/csplit-suppress-matched.pl2
-rwxr-xr-xtests/csplit/csplit.sh2
-rwxr-xr-xtests/cut/bounded-memory.sh38
-rwxr-xr-xtests/cut/cut-huge-range.sh5
-rwxr-xr-xtests/cut/cut.pl210
-rwxr-xr-xtests/cut/mb-non-utf8.sh80
-rwxr-xr-xtests/date/date-debug.sh2
-rwxr-xr-xtests/date/date-ethiopia.sh2
-rwxr-xr-xtests/date/date-iran.sh2
-rwxr-xr-xtests/date/date-locale-hour.sh35
-rwxr-xr-xtests/date/date-next-dow.pl2
-rwxr-xr-xtests/date/date-sec.sh2
-rwxr-xr-xtests/date/date-thailand.sh2
-rwxr-xr-xtests/date/date-tz.sh2
-rwxr-xr-xtests/date/date.pl96
-rwxr-xr-xtests/date/percent-percent.sh36
-rwxr-xr-xtests/date/reference.sh4
-rwxr-xr-xtests/date/resolution.sh2
-rwxr-xr-xtests/dd/ascii.sh2
-rwxr-xr-xtests/dd/bytes.sh11
-rwxr-xr-xtests/dd/conv-case.sh57
-rwxr-xr-xtests/dd/direct.sh2
-rwxr-xr-xtests/dd/fail-ftruncate-fstat.sh77
-rwxr-xr-xtests/dd/misc.sh8
-rwxr-xr-xtests/dd/no-allocate.sh13
-rwxr-xr-xtests/dd/nocache.sh2
-rwxr-xr-xtests/dd/nocache_eof.sh2
-rwxr-xr-xtests/dd/nocache_fail.sh2
-rwxr-xr-xtests/dd/not-rewound.sh2
-rwxr-xr-xtests/dd/partial-write.sh38
-rwxr-xr-xtests/dd/reblock.sh2
-rwxr-xr-xtests/dd/skip-seek-past-dev.sh2
-rwxr-xr-xtests/dd/skip-seek-past-file.sh2
-rwxr-xr-xtests/dd/skip-seek.pl2
-rwxr-xr-xtests/dd/skip-seek2.sh2
-rwxr-xr-xtests/dd/sparse.sh2
-rwxr-xr-xtests/dd/stats.sh2
-rwxr-xr-xtests/dd/stderr.sh2
-rwxr-xr-xtests/dd/unblock-sync.sh2
-rwxr-xr-xtests/dd/unblock.pl2
-rwxr-xr-xtests/df/df-P.sh2
-rwxr-xr-xtests/df/df-output.sh4
-rwxr-xr-xtests/df/df-symlink.sh2
-rwxr-xr-xtests/df/header.sh2
-rwxr-xr-xtests/df/no-mtab-status-masked-proc.sh70
-rwxr-xr-xtests/df/no-mtab-status.sh16
-rwxr-xr-xtests/df/over-mount-device.sh2
-rwxr-xr-xtests/df/problematic-chars.sh2
-rwxr-xr-xtests/df/skip-duplicates.sh16
-rwxr-xr-xtests/df/skip-rootfs.sh2
-rwxr-xr-xtests/df/sync.sh55
-rwxr-xr-xtests/df/total-unprocessed.sh2
-rwxr-xr-xtests/df/total-verify.sh2
-rwxr-xr-xtests/df/unreadable.sh2
-rwxr-xr-xtests/du/2g.sh2
-rwxr-xr-xtests/du/8gb.sh2
-rwxr-xr-xtests/du/apparent.sh15
-rwxr-xr-xtests/du/basic.sh2
-rwxr-xr-xtests/du/bigtime.sh2
-rwxr-xr-xtests/du/bind-mount-dir-cycle-v2.sh2
-rwxr-xr-xtests/du/bind-mount-dir-cycle.sh2
-rwxr-xr-xtests/du/deref-args.sh2
-rwxr-xr-xtests/du/deref.sh2
-rwxr-xr-xtests/du/exclude.sh2
-rwxr-xr-xtests/du/fd-leak.sh2
-rwxr-xr-xtests/du/files0-from-dir.sh2
-rwxr-xr-xtests/du/files0-from.pl20
-rwxr-xr-xtests/du/hard-link.sh14
-rwxr-xr-xtests/du/inacc-dest.sh7
-rwxr-xr-xtests/du/inacc-dir.sh2
-rwxr-xr-xtests/du/inaccessible-cwd.sh2
-rwxr-xr-xtests/du/inodes.sh4
-rwxr-xr-xtests/du/long-from-unreadable.sh2
-rwxr-xr-xtests/du/long-sloop.sh14
-rwxr-xr-xtests/du/max-depth.sh2
-rwxr-xr-xtests/du/move-dir-while-traversing.sh2
-rwxr-xr-xtests/du/no-deref.sh2
-rwxr-xr-xtests/du/no-x.sh5
-rwxr-xr-xtests/du/one-file-system.sh2
-rwxr-xr-xtests/du/restore-wd.sh2
-rwxr-xr-xtests/du/slash.sh2
-rwxr-xr-xtests/du/threshold.sh2
-rwxr-xr-xtests/du/trailing-slash.sh2
-rwxr-xr-xtests/du/two-args.sh2
-rwxr-xr-xtests/env/env-S-script.sh2
-rwxr-xr-xtests/env/env-S.pl2
-rwxr-xr-xtests/env/env-null.sh2
-rwxr-xr-xtests/env/env-signal-handler.sh65
-rwxr-xr-xtests/env/env.sh31
-rw-r--r--tests/envvar-check2
-rwxr-xr-xtests/expand/bounded-memory.sh34
-rwxr-xr-xtests/expand/expand.pl (renamed from tests/misc/expand.pl)2
-rwxr-xr-xtests/expand/mb.sh172
-rwxr-xr-xtests/expr/expr-multibyte.pl2
-rwxr-xr-xtests/expr/expr.pl8
-rwxr-xr-xtests/factor/create-test.sh2
-rwxr-xr-xtests/factor/factor-parallel.sh2
-rwxr-xr-xtests/factor/factor.pl27
-rwxr-xr-xtests/factor/run.sh2
-rwxr-xr-xtests/fmt/base.pl12
-rwxr-xr-xtests/fmt/goal-option.sh2
-rwxr-xr-xtests/fmt/long-line.sh2
-rwxr-xr-xtests/fmt/non-space.sh2
-rwxr-xr-xtests/fmt/width.sh40
-rwxr-xr-xtests/fold/fold-characters.sh15
-rwxr-xr-xtests/fold/fold-nbsp.sh2
-rwxr-xr-xtests/fold/fold-spaces.sh2
-rwxr-xr-xtests/fold/fold-zero-width.sh18
-rwxr-xr-xtests/fold/fold.pl2
-rwxr-xr-xtests/fold/multiple-files.sh36
-rwxr-xr-xtests/groups/groups-dash.sh2
-rwxr-xr-xtests/groups/groups-process-all.sh2
-rwxr-xr-xtests/groups/groups-version.sh2
-rwxr-xr-xtests/head/head-c.sh2
-rwxr-xr-xtests/head/head-elide-tail.pl2
-rwxr-xr-xtests/head/head-n0.sh81
-rwxr-xr-xtests/head/head-pos.sh2
-rwxr-xr-xtests/head/head-write-error.sh12
-rwxr-xr-xtests/head/head.pl2
-rwxr-xr-xtests/help/help-version-getopt.sh2
-rwxr-xr-xtests/help/help-version.sh2
-rwxr-xr-xtests/id/context.sh2
-rwxr-xr-xtests/id/gnu-zero-uids.sh2
-rwxr-xr-xtests/id/no-context.sh2
-rwxr-xr-xtests/id/setgid.sh2
-rwxr-xr-xtests/id/smack.sh2
-rwxr-xr-xtests/id/uid.sh2
-rwxr-xr-xtests/id/zero.sh2
-rwxr-xr-xtests/install/basic-1.sh23
-rwxr-xr-xtests/install/create-leading.sh2
-rwxr-xr-xtests/install/d-slashdot.sh2
-rwxr-xr-xtests/install/install-C-root.sh2
-rwxr-xr-xtests/install/install-C-selinux.sh2
-rwxr-xr-xtests/install/install-C.sh30
-rwxr-xr-xtests/install/install-Z-selinux.sh2
-rwxr-xr-xtests/install/strip-program.sh2
-rwxr-xr-xtests/install/trap.sh2
-rwxr-xr-xtests/join/join-utf8.sh2
-rwxr-xr-xtests/join/join.pl2
-rwxr-xr-xtests/ln/backup-1.sh2
-rwxr-xr-xtests/ln/backup-suffix-traversal.sh65
-rwxr-xr-xtests/ln/hard-backup.sh2
-rwxr-xr-xtests/ln/hard-to-sym.sh2
-rwxr-xr-xtests/ln/misc.sh23
-rwxr-xr-xtests/ln/non-utf8-src.sh40
-rwxr-xr-xtests/ln/relative.sh2
-rwxr-xr-xtests/ln/sf-1.sh2
-rwxr-xr-xtests/ln/slash-decorated-nonexistent-dest.sh2
-rwxr-xr-xtests/ln/target-1.sh2
-rw-r--r--tests/local.mk80
-rwxr-xr-xtests/ls/a-option.sh2
-rwxr-xr-xtests/ls/abmon-align.sh2
-rwxr-xr-xtests/ls/acl.sh17
-rwxr-xr-xtests/ls/birthtime.sh2
-rwxr-xr-xtests/ls/block-size.sh2
-rwxr-xr-xtests/ls/capability.sh2
-rwxr-xr-xtests/ls/classify.sh2
-rwxr-xr-xtests/ls/color-clear-to-eol.sh2
-rwxr-xr-xtests/ls/color-dtype-dir.sh2
-rwxr-xr-xtests/ls/color-ext.sh2
-rwxr-xr-xtests/ls/color-norm.sh2
-rwxr-xr-xtests/ls/color-term.sh2
-rwxr-xr-xtests/ls/dangle.sh2
-rwxr-xr-xtests/ls/dired.sh2
-rwxr-xr-xtests/ls/file-type.sh2
-rwxr-xr-xtests/ls/follow-slink.sh2
-rwxr-xr-xtests/ls/getxattr-speedup.sh2
-rwxr-xr-xtests/ls/group-dirs.sh2
-rwxr-xr-xtests/ls/hex-option.sh2
-rwxr-xr-xtests/ls/hyperlink.sh10
-rwxr-xr-xtests/ls/infloop.sh2
-rwxr-xr-xtests/ls/inode.sh2
-rwxr-xr-xtests/ls/ls-misc.pl2
-rwxr-xr-xtests/ls/ls-time.sh2
-rwxr-xr-xtests/ls/m-option.sh15
-rwxr-xr-xtests/ls/multihardlink.sh2
-rwxr-xr-xtests/ls/nameless-uid.sh2
-rwxr-xr-xtests/ls/no-arg.sh2
-rwxr-xr-xtests/ls/no-cap.sh2
-rwxr-xr-xtests/ls/non-utf8-hidden.sh60
-rwxr-xr-xtests/ls/quote-align.sh2
-rwxr-xr-xtests/ls/quoting-utf8.sh70
-rwxr-xr-xtests/ls/readdir-mountpoint-inode.sh2
-rwxr-xr-xtests/ls/recursive.sh9
-rwxr-xr-xtests/ls/removed-directory.sh2
-rwxr-xr-xtests/ls/root-rel-symlink-color.sh2
-rwxr-xr-xtests/ls/rt-1.sh2
-rwxr-xr-xtests/ls/selinux-segfault.sh2
-rwxr-xr-xtests/ls/selinux.sh2
-rwxr-xr-xtests/ls/size-align.sh2
-rwxr-xr-xtests/ls/slink-acl.sh2
-rwxr-xr-xtests/ls/sort-width-option.sh2
-rwxr-xr-xtests/ls/stat-dtype.sh2
-rwxr-xr-xtests/ls/stat-failed.sh2
-rwxr-xr-xtests/ls/stat-free-color.sh2
-rwxr-xr-xtests/ls/stat-free-symlinks.sh2
-rwxr-xr-xtests/ls/stat-vs-dirent.sh2
-rwxr-xr-xtests/ls/symlink-loop.sh2
-rwxr-xr-xtests/ls/symlink-quote.sh2
-rwxr-xr-xtests/ls/symlink-slash.sh2
-rwxr-xr-xtests/ls/time-style-diag.sh2
-rwxr-xr-xtests/ls/w-option.sh32
-rwxr-xr-xtests/ls/x-option.sh2
-rwxr-xr-xtests/ls/zero-option.sh2
-rwxr-xr-xtests/misc/arch.sh4
-rwxr-xr-xtests/misc/basename.pl2
-rwxr-xr-xtests/misc/close-stdin.sh86
-rwxr-xr-xtests/misc/close-stdout.sh2
-rwxr-xr-xtests/misc/coreutils.sh22
-rwxr-xr-xtests/misc/dircolors.pl2
-rwxr-xr-xtests/misc/dirname.pl2
-rwxr-xr-xtests/misc/echo.sh2
-rwxr-xr-xtests/misc/false-status.sh2
-rwxr-xr-xtests/misc/getopt_vs_usage.sh59
-rwxr-xr-xtests/misc/invalid-opt.pl2
-rwxr-xr-xtests/misc/io-errors.sh133
-rwxr-xr-xtests/misc/kill.sh15
-rwxr-xr-xtests/misc/mknod.sh16
-rwxr-xr-xtests/misc/nohup.sh8
-rwxr-xr-xtests/misc/option-aliases.sh2
-rwxr-xr-xtests/misc/pathchk.sh2
-rwxr-xr-xtests/misc/printenv.sh2
-rwxr-xr-xtests/misc/read-errors.sh115
-rwxr-xr-xtests/misc/realpath.sh2
-rwxr-xr-xtests/misc/responsive.sh85
-rwxr-xr-xtests/misc/selinux.sh2
-rwxr-xr-xtests/misc/sleep.sh2
-rwxr-xr-xtests/misc/stdbuf.sh2
-rwxr-xr-xtests/misc/sync.sh24
-rwxr-xr-xtests/misc/time-style.sh2
-rwxr-xr-xtests/misc/tsort.pl2
-rwxr-xr-xtests/misc/tty-eof.pl179
-rwxr-xr-xtests/misc/usage_vs_getopt.sh4
-rwxr-xr-xtests/misc/usage_vs_refs.sh68
-rwxr-xr-xtests/misc/user.sh33
-rwxr-xr-xtests/misc/warning-errors.sh65
-rwxr-xr-xtests/misc/write-errors.sh35
-rwxr-xr-xtests/misc/xattr.sh2
-rwxr-xr-xtests/misc/xstrtol.pl2
-rwxr-xr-xtests/misc/yes.sh37
-rwxr-xr-xtests/mkdir/p-1.sh2
-rwxr-xr-xtests/mkdir/p-2.sh2
-rwxr-xr-xtests/mkdir/p-3.sh2
-rwxr-xr-xtests/mkdir/p-acl.sh2
-rwxr-xr-xtests/mkdir/p-slashdot.sh2
-rwxr-xr-xtests/mkdir/p-thru-slink.sh2
-rwxr-xr-xtests/mkdir/p-v.sh2
-rwxr-xr-xtests/mkdir/parents.sh2
-rwxr-xr-xtests/mkdir/perm.sh2
-rwxr-xr-xtests/mkdir/restorecon.sh2
-rwxr-xr-xtests/mkdir/selinux.sh2
-rwxr-xr-xtests/mkdir/smack-no-root.sh8
-rwxr-xr-xtests/mkdir/smack-root.sh2
-rwxr-xr-xtests/mkdir/special-1.sh2
-rwxr-xr-xtests/mkdir/t-slash.sh2
-rwxr-xr-xtests/mkdir/writable-under-readonly.sh33
-rwxr-xr-xtests/mktemp/bad-unicode.sh51
-rwxr-xr-xtests/mktemp/mktemp-misc.sh33
-rwxr-xr-xtests/mktemp/mktemp.pl (renamed from tests/misc/mktemp.pl)36
-rwxr-xr-xtests/mktemp/write-error.sh36
-rwxr-xr-xtests/mv/acl.sh2
-rwxr-xr-xtests/mv/atomic.sh2
-rwxr-xr-xtests/mv/atomic2.sh2
-rwxr-xr-xtests/mv/backup-dir.sh2
-rwxr-xr-xtests/mv/backup-is-src.sh2
-rwxr-xr-xtests/mv/childproof.sh2
-rwxr-xr-xtests/mv/diag.sh2
-rwxr-xr-xtests/mv/dir-file.sh2
-rwxr-xr-xtests/mv/dir2dir.sh12
-rwxr-xr-xtests/mv/dup-source.sh2
-rwxr-xr-xtests/mv/force.sh2
-rwxr-xr-xtests/mv/hard-2.sh2
-rwxr-xr-xtests/mv/hard-3.sh2
-rwxr-xr-xtests/mv/hard-4.sh2
-rwxr-xr-xtests/mv/hard-link-1.sh2
-rwxr-xr-xtests/mv/hardlink-case.sh22
-rwxr-xr-xtests/mv/i-1.pl2
-rwxr-xr-xtests/mv/i-2.sh2
-rwxr-xr-xtests/mv/i-3.sh2
-rwxr-xr-xtests/mv/i-4.sh2
-rwxr-xr-xtests/mv/i-5.sh2
-rwxr-xr-xtests/mv/i-link-no.sh2
-rwxr-xr-xtests/mv/into-self-2.sh2
-rwxr-xr-xtests/mv/into-self-3.sh2
-rwxr-xr-xtests/mv/into-self-4.sh2
-rwxr-xr-xtests/mv/into-self.sh2
-rwxr-xr-xtests/mv/leak-fd.sh2
-rwxr-xr-xtests/mv/meta-to-xpart.sh40
-rwxr-xr-xtests/mv/mv-exchange.sh10
-rwxr-xr-xtests/mv/mv-n.sh2
-rwxr-xr-xtests/mv/mv-special-1.sh12
-rwxr-xr-xtests/mv/mv-special-2.sh75
-rwxr-xr-xtests/mv/no-copy.sh2
-rwxr-xr-xtests/mv/no-target-dir.sh2
-rwxr-xr-xtests/mv/part-fail.sh10
-rwxr-xr-xtests/mv/part-hardlink.sh2
-rwxr-xr-xtests/mv/part-rename.sh2
-rwxr-xr-xtests/mv/part-symlink.sh2
-rwxr-xr-xtests/mv/partition-perm.sh2
-rwxr-xr-xtests/mv/perm-1.sh7
-rwxr-xr-xtests/mv/sticky-to-xpart.sh13
-rwxr-xr-xtests/mv/symlink-onto-hardlink-to-self.sh2
-rwxr-xr-xtests/mv/symlink-onto-hardlink.sh2
-rwxr-xr-xtests/mv/to-symlink.sh2
-rwxr-xr-xtests/mv/trailing-slash.sh2
-rwxr-xr-xtests/mv/update.sh2
-rwxr-xr-xtests/nice/nice-fail.sh3
-rwxr-xr-xtests/nice/nice.sh55
-rwxr-xr-xtests/nl/multibyte.sh56
-rwxr-xr-xtests/nl/multiple-files.sh33
-rwxr-xr-xtests/nl/nl.sh (renamed from tests/misc/nl.sh)16
-rwxr-xr-xtests/nproc/nproc-avail.sh2
-rwxr-xr-xtests/nproc/nproc-override.sh2
-rwxr-xr-xtests/nproc/nproc-positive.sh2
-rwxr-xr-xtests/nproc/nproc-quota-systemd.sh55
-rwxr-xr-xtests/nproc/nproc-quota.sh4
-rwxr-xr-xtests/numfmt/mb-non-utf8.sh2
-rwxr-xr-xtests/numfmt/numfmt.pl48
-rwxr-xr-xtests/od/big-w.sh2
-rwxr-xr-xtests/od/od-N.sh9
-rwxr-xr-xtests/od/od-endian.sh2
-rwxr-xr-xtests/od/od-float.sh17
-rwxr-xr-xtests/od/od-j.sh4
-rwxr-xr-xtests/od/od-multiple-t.sh2
-rwxr-xr-xtests/od/od-x8.sh2
-rwxr-xr-xtests/od/od.pl13
-rw-r--r--tests/other-fs-tmpdir2
-rwxr-xr-xtests/paste/multi-byte.sh103
-rwxr-xr-xtests/paste/paste.pl (renamed from tests/misc/paste.pl)17
-rwxr-xr-xtests/pr/bounded-memory.sh34
-rwxr-xr-xtests/pr/pr-tests.pl2
-rwxr-xr-xtests/printf/printf-cov.pl2
-rwxr-xr-xtests/printf/printf-hex.sh2
-rwxr-xr-xtests/printf/printf-indexed.sh2
-rwxr-xr-xtests/printf/printf-mb.sh2
-rwxr-xr-xtests/printf/printf-quote.sh6
-rwxr-xr-xtests/printf/printf-surprise.sh2
-rwxr-xr-xtests/printf/printf.sh2
-rwxr-xr-xtests/ptx/ptx-overrun.sh6
-rwxr-xr-xtests/ptx/ptx.pl9
-rwxr-xr-xtests/pwd/argument.sh33
-rwxr-xr-xtests/pwd/pwd-long.sh11
-rwxr-xr-xtests/pwd/pwd-option.sh2
-rwxr-xr-xtests/readlink/can-e.sh2
-rwxr-xr-xtests/readlink/can-f.sh2
-rwxr-xr-xtests/readlink/can-m.sh2
-rwxr-xr-xtests/readlink/multi.sh2
-rwxr-xr-xtests/readlink/readlink-fp-loop.sh2
-rwxr-xr-xtests/readlink/readlink-posix.sh6
-rwxr-xr-xtests/readlink/readlink-root.sh2
-rwxr-xr-xtests/readlink/rl-1.sh2
-rwxr-xr-xtests/rm/cycle.sh2
-rwxr-xr-xtests/rm/d-1.sh2
-rwxr-xr-xtests/rm/d-2.sh8
-rwxr-xr-xtests/rm/d-3.sh2
-rwxr-xr-xtests/rm/dangling-symlink.sh2
-rwxr-xr-xtests/rm/dash-hint.sh53
-rwxr-xr-xtests/rm/deep-1.sh2
-rwxr-xr-xtests/rm/deep-2.sh2
-rwxr-xr-xtests/rm/dir-no-w.sh2
-rwxr-xr-xtests/rm/dir-nonrecur.sh2
-rwxr-xr-xtests/rm/dot-rel.sh2
-rwxr-xr-xtests/rm/empty-immutable-skip.sh2
-rwxr-xr-xtests/rm/empty-inacc.sh2
-rwxr-xr-xtests/rm/empty-name.pl2
-rwxr-xr-xtests/rm/ext3-perf.sh2
-rwxr-xr-xtests/rm/f-1.sh2
-rwxr-xr-xtests/rm/fail-2eperm.sh29
-rwxr-xr-xtests/rm/fail-eacces.sh11
-rwxr-xr-xtests/rm/fail-eperm.xpl150
-rwxr-xr-xtests/rm/hash.sh2
-rwxr-xr-xtests/rm/i-1.sh2
-rwxr-xr-xtests/rm/i-never.sh2
-rwxr-xr-xtests/rm/i-no-r.sh2
-rwxr-xr-xtests/rm/ignorable.sh2
-rwxr-xr-xtests/rm/inaccessible.sh15
-rwxr-xr-xtests/rm/interactive-always.sh2
-rwxr-xr-xtests/rm/interactive-once.sh2
-rwxr-xr-xtests/rm/ir-1.sh2
-rwxr-xr-xtests/rm/isatty.sh2
-rwxr-xr-xtests/rm/many-dir-entries-vs-OOM.sh2
-rwxr-xr-xtests/rm/no-give-up.sh2
-rwxr-xr-xtests/rm/one-file-system.sh2
-rwxr-xr-xtests/rm/one-file-system2.sh2
-rwxr-xr-xtests/rm/r-1.sh2
-rwxr-xr-xtests/rm/r-2.sh2
-rwxr-xr-xtests/rm/r-3.sh2
-rwxr-xr-xtests/rm/r-4.sh2
-rwxr-xr-xtests/rm/r-root.sh6
-rwxr-xr-xtests/rm/read-only.sh2
-rwxr-xr-xtests/rm/readdir-bug.sh2
-rwxr-xr-xtests/rm/rm-readdir-fail.sh4
-rwxr-xr-xtests/rm/rm1.sh11
-rwxr-xr-xtests/rm/rm2.sh15
-rwxr-xr-xtests/rm/rm3.sh2
-rwxr-xr-xtests/rm/rm4.sh2
-rwxr-xr-xtests/rm/rm5.sh2
-rwxr-xr-xtests/rm/sunos-1.sh2
-rwxr-xr-xtests/rm/unread2.sh7
-rwxr-xr-xtests/rm/unread3.sh2
-rwxr-xr-xtests/rm/unreadable.pl6
-rwxr-xr-xtests/rm/v-slash.sh2
-rwxr-xr-xtests/rmdir/fail-perm.sh2
-rwxr-xr-xtests/rmdir/ignore.sh2
-rwxr-xr-xtests/rmdir/symlink-errors.sh2
-rwxr-xr-xtests/rmdir/t-slash.sh2
-rwxr-xr-xtests/runcon/runcon-compute.sh4
-rwxr-xr-xtests/runcon/runcon-no-reorder.sh2
-rw-r--r--tests/sample-test2
-rwxr-xr-xtests/seq/seq-epipe.sh2
-rwxr-xr-xtests/seq/seq-extra-number.sh2
-rwxr-xr-xtests/seq/seq-io-errors.sh2
-rwxr-xr-xtests/seq/seq-locale.sh2
-rwxr-xr-xtests/seq/seq-long-double.sh2
-rwxr-xr-xtests/seq/seq-precision.sh2
-rwxr-xr-xtests/seq/seq.pl2
-rwxr-xr-xtests/shred/fifo.sh43
-rwxr-xr-xtests/shred/shred-exact.sh2
-rwxr-xr-xtests/shred/shred-passes.sh2
-rwxr-xr-xtests/shred/shred-remove.sh13
-rwxr-xr-xtests/shred/shred-size.sh2
-rwxr-xr-xtests/shuf/shuf-reservoir.sh2
-rwxr-xr-xtests/shuf/shuf.sh31
-rwxr-xr-xtests/sort/sort-NaN-infloop.sh2
-rwxr-xr-xtests/sort/sort-benchmark-random.sh2
-rwxr-xr-xtests/sort/sort-buffer-size.sh52
-rwxr-xr-xtests/sort/sort-compress-hang.sh2
-rwxr-xr-xtests/sort/sort-compress-proc.sh6
-rwxr-xr-xtests/sort/sort-compress.sh2
-rwxr-xr-xtests/sort/sort-continue.sh2
-rwxr-xr-xtests/sort/sort-debug-keys.sh2
-rwxr-xr-xtests/sort/sort-debug-warn.sh2
-rwxr-xr-xtests/sort/sort-discrim.sh2
-rwxr-xr-xtests/sort/sort-exit-early.sh2
-rwxr-xr-xtests/sort/sort-field-limit.sh2
-rwxr-xr-xtests/sort/sort-files0-from.pl2
-rwxr-xr-xtests/sort/sort-float.sh2
-rwxr-xr-xtests/sort/sort-h-thousands-sep.sh2
-rwxr-xr-xtests/sort/sort-locale.sh51
-rwxr-xr-xtests/sort/sort-merge-fdlimit.sh9
-rwxr-xr-xtests/sort/sort-merge.pl2
-rwxr-xr-xtests/sort/sort-month.sh2
-rwxr-xr-xtests/sort/sort-rand.sh2
-rwxr-xr-xtests/sort/sort-spinlock-abuse.sh2
-rwxr-xr-xtests/sort/sort-stale-thread-mem.sh2
-rwxr-xr-xtests/sort/sort-u-FMR.sh2
-rwxr-xr-xtests/sort/sort-unique-segv.sh2
-rwxr-xr-xtests/sort/sort-unique.sh2
-rwxr-xr-xtests/sort/sort-version.sh2
-rwxr-xr-xtests/sort/sort.pl2
-rwxr-xr-xtests/split/additional-suffix.sh2
-rwxr-xr-xtests/split/b-chunk.sh2
-rwxr-xr-xtests/split/fail.sh2
-rwxr-xr-xtests/split/filter.sh2
-rwxr-xr-xtests/split/guard-input.sh2
-rwxr-xr-xtests/split/l-chunk-root.sh2
-rwxr-xr-xtests/split/l-chunk.sh2
-rwxr-xr-xtests/split/line-bytes.sh6
-rwxr-xr-xtests/split/lines.sh2
-rwxr-xr-xtests/split/non-utf8.sh38
-rwxr-xr-xtests/split/numeric.sh2
-rwxr-xr-xtests/split/r-chunk.sh4
-rwxr-xr-xtests/split/record-sep.sh2
-rwxr-xr-xtests/split/split-io-err.sh44
-rwxr-xr-xtests/split/suffix-auto-length.sh2
-rwxr-xr-xtests/split/suffix-length.sh2
-rwxr-xr-xtests/stat/stat-birthtime.sh2
-rwxr-xr-xtests/stat/stat-fmt.sh32
-rwxr-xr-xtests/stat/stat-hyphen.sh2
-rwxr-xr-xtests/stat/stat-mount.sh15
-rwxr-xr-xtests/stat/stat-nanoseconds.sh2
-rwxr-xr-xtests/stat/stat-printf.pl2
-rwxr-xr-xtests/stat/stat-slash.sh2
-rwxr-xr-xtests/stty/bad-speed.sh3
-rwxr-xr-xtests/stty/stty-invalid.sh2
-rwxr-xr-xtests/stty/stty-pairs.sh2
-rwxr-xr-xtests/stty/stty-row-col.sh17
-rwxr-xr-xtests/stty/stty.sh2
-rwxr-xr-xtests/tac/tac-2-nonseekable.sh2
-rwxr-xr-xtests/tac/tac-continue.sh58
-rwxr-xr-xtests/tac/tac-locale.sh44
-rwxr-xr-xtests/tac/tac.pl6
-rwxr-xr-xtests/tail/F-headers.sh2
-rwxr-xr-xtests/tail/F-vs-missing.sh2
-rwxr-xr-xtests/tail/F-vs-rename.sh2
-rwxr-xr-xtests/tail/append-only.sh2
-rwxr-xr-xtests/tail/assert-2.sh2
-rwxr-xr-xtests/tail/assert.sh2
-rwxr-xr-xtests/tail/basic-seek.sh2
-rwxr-xr-xtests/tail/big-4gb.sh2
-rwxr-xr-xtests/tail/debug.sh71
-rwxr-xr-xtests/tail/descriptor-vs-rename.sh2
-rwxr-xr-xtests/tail/end-of-device.sh2
-rwxr-xr-xtests/tail/flush-initial.sh2
-rwxr-xr-xtests/tail/follow-name.sh2
-rwxr-xr-xtests/tail/follow-stdin.sh2
-rwxr-xr-xtests/tail/inotify-dir-recreate.sh32
-rwxr-xr-xtests/tail/inotify-hash-abuse.sh2
-rwxr-xr-xtests/tail/inotify-hash-abuse2.sh2
-rwxr-xr-xtests/tail/inotify-only-regular.sh5
-rwxr-xr-xtests/tail/inotify-race.sh6
-rwxr-xr-xtests/tail/inotify-race2.sh6
-rwxr-xr-xtests/tail/inotify-rotate-resources.sh2
-rwxr-xr-xtests/tail/inotify-rotate.sh2
-rwxr-xr-xtests/tail/overlay-headers.sh33
-rwxr-xr-xtests/tail/pid-pipe.sh45
-rwxr-xr-xtests/tail/pid.sh2
-rwxr-xr-xtests/tail/pipe-f.sh6
-rwxr-xr-xtests/tail/pipe-f2.sh2
-rwxr-xr-xtests/tail/proc-ksyms.sh2
-rwxr-xr-xtests/tail/retry.sh2
-rwxr-xr-xtests/tail/start-middle.sh2
-rwxr-xr-xtests/tail/symlink.sh2
-rwxr-xr-xtests/tail/tail-c.sh9
-rwxr-xr-xtests/tail/tail-n0f.sh2
-rwxr-xr-xtests/tail/tail-sysfs.sh2
-rwxr-xr-xtests/tail/tail.pl2
-rwxr-xr-xtests/tail/truncate.sh2
-rwxr-xr-xtests/tail/wait.sh2
-rwxr-xr-xtests/tee/append.sh47
-rwxr-xr-xtests/tee/tee.sh (renamed from tests/misc/tee.sh)19
-rwxr-xr-xtests/test/test-N.sh2
-rwxr-xr-xtests/test/test-diag.pl2
-rwxr-xr-xtests/test/test-file.sh56
-rwxr-xr-xtests/test/test.pl3
-rwxr-xr-xtests/timeout/init-parent.sh32
-rwxr-xr-xtests/timeout/timeout-blocked.pl2
-rwxr-xr-xtests/timeout/timeout-group.sh67
-rwxr-xr-xtests/timeout/timeout-large-parameters.sh2
-rwxr-xr-xtests/timeout/timeout-parameters.sh2
-rwxr-xr-xtests/timeout/timeout.sh26
-rwxr-xr-xtests/touch/60-seconds.sh2
-rwxr-xr-xtests/touch/dangling-symlink.sh6
-rwxr-xr-xtests/touch/empty-file.sh2
-rwxr-xr-xtests/touch/fail-diag.sh2
-rwxr-xr-xtests/touch/fifo.sh2
-rwxr-xr-xtests/touch/no-create-missing.sh2
-rwxr-xr-xtests/touch/no-dereference.sh2
-rwxr-xr-xtests/touch/no-rights.sh2
-rwxr-xr-xtests/touch/not-owner.sh8
-rwxr-xr-xtests/touch/now-owned-by-other.sh2
-rwxr-xr-xtests/touch/obsolescent.sh2
-rwxr-xr-xtests/touch/read-only.sh2
-rwxr-xr-xtests/touch/relative.sh2
-rwxr-xr-xtests/touch/trailing-slash.sh2
-rwxr-xr-xtests/tr/tr-case-class.sh2
-rwxr-xr-xtests/tr/tr.pl6
-rwxr-xr-xtests/truncate/multiple-files.sh29
-rwxr-xr-xtests/truncate/truncate-dangling-symlink.sh2
-rwxr-xr-xtests/truncate/truncate-dir-fail.sh2
-rwxr-xr-xtests/truncate/truncate-fail-diag.sh2
-rwxr-xr-xtests/truncate/truncate-fifo.sh2
-rwxr-xr-xtests/truncate/truncate-no-create-missing.sh2
-rwxr-xr-xtests/truncate/truncate-overflow.sh2
-rwxr-xr-xtests/truncate/truncate-owned-by-other.sh2
-rwxr-xr-xtests/truncate/truncate-parameters.sh2
-rwxr-xr-xtests/truncate/truncate-relative.sh2
-rwxr-xr-xtests/tty/tty-eof.pl118
-rwxr-xr-xtests/tty/tty.sh2
-rwxr-xr-xtests/unexpand/bounded-memory.sh34
-rwxr-xr-xtests/unexpand/mb.sh172
-rwxr-xr-xtests/unexpand/unexpand.pl (renamed from tests/misc/unexpand.pl)4
-rwxr-xr-xtests/uniq/uniq-collate.sh2
-rwxr-xr-xtests/uniq/uniq-perf.sh2
-rwxr-xr-xtests/uniq/uniq.pl5
-rwxr-xr-xtests/wc/wc-cpu.sh6
-rwxr-xr-xtests/wc/wc-files0-from.pl17
-rwxr-xr-xtests/wc/wc-files0.sh2
-rwxr-xr-xtests/wc/wc-nbsp.sh2
-rwxr-xr-xtests/wc/wc-parallel.sh2
-rwxr-xr-xtests/wc/wc-proc.sh2
-rwxr-xr-xtests/wc/wc-total.sh2
-rwxr-xr-xtests/wc/wc.pl2
967 files changed, 18952 insertions, 11703 deletions
diff --git a/.github/ISSUE_TEMPLATE.txt b/.github/ISSUE_TEMPLATE.txt
index 9add36812..783c26769 100644
--- a/.github/ISSUE_TEMPLATE.txt
+++ b/.github/ISSUE_TEMPLATE.txt
@@ -1,12 +1,5 @@
-Please **do not** send pull-requests or open new issues on Github.
-
-Github is a downstream mirror and is not frequently monitored,
-all development is coordinated upstream on GNU resources.
-
-* Send general questions or suggestions to: coreutils@gnu.org .
-* Send bugs reports to: bug-coreutils@gnu.org .
-
-## Bug reports
+The preferred process for sending suggestions or bugs
+is to email coreutils@gnu.org or bug-coreutils@gnu.org respectively.
Before reporting a new bug, please check the following resources:
@@ -16,59 +9,8 @@ Before reporting a new bug, please check the following resources:
contains a list of some quirks and unexpected behavior (which are often
mistaken for bugs).
-* Online Manual:
- https://www.gnu.org/software/coreutils/manual/html_node/index.html
-
-* Search the archives for previous questions and answers:
-
- * Coreutils Mailing list (General usage and advice):
- https://lists.gnu.org/archive/html/coreutils/
-
- * Bug reports Mailing List:
- https://lists.gnu.org/archive/html/bug-coreutils/
-
-* Open Bugs:
- https://debbugs.gnu.org/cgi/pkgreport.cgi?which=pkg&data=coreutils
-
-* Translation related issues:
- https://translationproject.org/domain/coreutils.html
-
-
-## Effective bug reports
-
-* Include a descriptive subject line (e.g. the program with which
- you experience a problem, and what the problem is).
-* Include the version of the program (e.g. the output of `PROG --version`).
-* Include the operating system and the type of hardware you are using
- (e.g. the output of `uname -a`).
-* Include the exact command and parameters you have used.
-* Clearly explain what is the output you expected to get, and what is
- the actual result you encountered.
-* Include as much information as possible to reproduce the problem.
- If the problem happens on a very large input file, try to provide
- a minimal example (a subset of the input file) that still causes the problem.
- *Do not* include attachments over 40kB.
-* List policy is reply-to-all, and non-subscribers may post.
-* There may be a moderation delay for a first-time post, whether or not
- you subscribe.
-
-
-## Mailing List Etiquette
-
-When sending messages to coreutils@gnu.org or bug-coreutils@gnu.org :
-
-* Send messages as plain text.
-* Do not send messages encoded as HTML nor encoded as base64 MIME nor
- included as multiple formats.
-* Avoid sending large messages, such as log files, system call trace
- output, and other content resulting in messages over about 40 kB.
-* Avoid sending screenshots (e.g. PNG files). When reporting errors
- you encounter on the terminal, copy and paste the text to your message.
-
-
-
<!--
-Copyright (C) 2017-2025 Free Software Foundation, Inc.
+Copyright (C) 2017-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/.github/PULL_REQUEST_TEMPLATE.txt b/.github/PULL_REQUEST_TEMPLATE.txt
index 970e4ede4..715fe5bd8 100644
--- a/.github/PULL_REQUEST_TEMPLATE.txt
+++ b/.github/PULL_REQUEST_TEMPLATE.txt
@@ -1,67 +1,9 @@
-Please *do not* send pull-requests or open new issues on Github.
-See "hacking resources" below for recommended alternatives.
+The preferred process for sending changes
+is to email coreutils@gnu.org or bug-coreutils@gnu.org respectively.
-Github is a downstream mirror and is not frequently monitored,
-all development is coordinated upstream on GNU resources.
+## Developer resources
-* Send general questions or suggestions to: coreutils@gnu.org .
-* Send bugs reports to: <bug-coreutils@gnu.org>
-
-Before sending the bug, please consult the FAQ and Mailing list
-archives (see below). Often these perceived bugs are simply due to
-wrong program usage.
-
-Please remember that development of Coreutils is a volunteer effort,
-and you can also contribute to its development. For information about
-contributing to the GNU Project, please read
-[How to help GNU](https://www.gnu.org/help/].
-
-
-## Getting Help
-
-* Coreutils FAQ: https://www.gnu.org/software/coreutils/faq/coreutils-faq.html
-
-* Coreutils Gotchas: https://www.pixelbeat.org/docs/coreutils-gotchas.html
- contains a list of some quirks and unexpected behavior (which are often
- mistaken for bugs).
-
-* Online Manual:
- https://www.gnu.org/software/coreutils/manual/html_node/index.html
-
-* Search the archives for previous questions and answers:
-
- * Coreutils Mailing list (General usage and advice):
- https://lists.gnu.org/archive/html/coreutils/
-
- * Bug reports Mailing List:
- https://lists.gnu.org/archive/html/bug-coreutils/
-
-* Open Bugs: https://debbugs.gnu.org/cgi/pkgreport.cgi?which=pkg&data=coreutils
-
-* Translation related issues:
- https://translationproject.org/domain/coreutils.html
-
-
-## Mailing List Etiquette
-
-When sending messages to coreutils@gnu.org or bug-coreutils@gnu.org :
-
-* Send messages as plain text.
-* Do not send messages encoded as HTML nor encoded as base64 MIME nor
- included as multiple formats.
-* Include a descriptive subject line.
-* Avoid sending large messages, such as log files, system call trace
- output, and other content resulting in messages over about 40 kB.
-* Avoid sending screenshots (e.g. PNG files). When reporting errors
- you encounter on the terminal, copy and paste the text to your message.
-* List policy is reply-to-all, and non-subscribers may post.
-* There may be a moderation delay for a first-time post, whether or not
- you subscribe.
-
-
-## Hacking resources
-
-files contain information about hacking and contributing to GNU coreutils:
+files containing information about contributing to GNU coreutils:
https://git.savannah.gnu.org/cgit/coreutils.git/tree/HACKING
https://git.savannah.gnu.org/cgit/coreutils.git/tree/README-hacking
Please read them first.
@@ -88,7 +30,7 @@ in this matter.
<!--
-Copyright (C) 2017-2025 Free Software Foundation, Inc.
+Copyright (C) 2017-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/.gitignore b/.gitignore
index 756f2d874..b6a576d56 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,7 @@
/build-aux/config.sub
/build-aux/depcomp
/build-aux/install-sh
+/build-aux/makeinfo-wrapper.sh
/build-aux/mdate-sh
/build-aux/missing
/build-aux/snippet/
diff --git a/.mailmap b/.mailmap
index 0f777b9a3..2ab0b7f7f 100644
--- a/.mailmap
+++ b/.mailmap
@@ -13,7 +13,12 @@ Paul Eggert <eggert@cs.ucla.edu> <eggert@CS.UCLA.EDU>
Evan Hunt <ethanol@armory.com> Evan Hunt <jim@meyering.net>
<P@draigBrady.com> <P@draigBrady.com (trivial change)>
+<P@draigBrady.com> <pbrady@fb.com>
Pádraig Brady <P@draigBrady.com>
+<adilger@whamcloud.com> <adilger@sun.com>
+<bensberg@telfort.nl> <bensberg@justemail.net>
+<rasmus.villemoes@prevas.dk> <rv@rasmusvillemoes.dk>
+<stephane@chazelas.org> <stephane.chazelas@gmail.com>
<chen.guo.0625@gmail.com> <chenguo4@yahoo.com>
<chen.guo.0625@gmail.com> <chenguo4@ucla.edu>
<schwab@linux-m68k.org> <schwab@suse.de>
diff --git a/.prev-version b/.prev-version
index 021debdfd..d4ce17d7d 100644
--- a/.prev-version
+++ b/.prev-version
@@ -1 +1 @@
-9.8
+9.11
diff --git a/.vg-suppressions b/.vg-suppressions
index a0c91e755..dd33720e0 100644
--- a/.vg-suppressions
+++ b/.vg-suppressions
@@ -1,6 +1,6 @@
# Suppress valgrind diagnostics we don't care about.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/HACKING b/HACKING
index f07903844..90d1544b2 100644
--- a/HACKING
+++ b/HACKING
@@ -616,7 +616,7 @@ and root only tests, is to follow these steps (requires lcov to be installed):
xdg-open doc/coverage/index.html
========================================================================
-Copyright (C) 2009-2025 Free Software Foundation, Inc.
+Copyright (C) 2009-2026 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
diff --git a/Makefile.am b/Makefile.am
index 71f093683..2e9c4ed55 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,6 +1,6 @@
# Make coreutils. -*-Makefile-*-
-# Copyright (C) 1990-2025 Free Software Foundation, Inc.
+# Copyright (C) 1990-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -188,11 +188,14 @@ install-exec-hook:
test $$p = x && continue; \
ptrans=$$(printf '%s' "$$p" | sed -e "$(transform)"); \
rm -f $(DESTDIR)$(bindir)/$$ptrans$(EXEEXT) || exit $$?; \
- if test "x$(single_binary_install_type)" = xshebangs; then \
+ if test "$(single_binary_install_type)" = shebangs; then \
printf '#!%s --coreutils-prog-shebang=%s\n' \
$(bindir)/$$ctrans$(EXEEXT) $$p \
>$(DESTDIR)$(bindir)/$$ptrans$(EXEEXT) || exit $$?; \
chmod a+x,a-w $(DESTDIR)$(bindir)/$$ptrans$(EXEEXT) || exit $$?;\
+ elif test "$(single_binary_install_type)" = hardlinks; then \
+ ln $(DESTDIR)$(bindir)/$$ctrans$(EXEEXT) \
+ $(DESTDIR)$(bindir)/$$ptrans$(EXEEXT) || exit $$?; \
else \
$(LN_S) -s $$ctrans$(EXEEXT) \
$(DESTDIR)$(bindir)/$$ptrans$(EXEEXT) || exit $$?; \
diff --git a/NEWS b/NEWS
index 7e7dc6376..f8c43d5ed 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,283 @@ GNU coreutils NEWS -*- outline -*-
** Bug fixes
+ 'comm - -' no longer closes standard input twice. Previously it would
+ mistakenly exit with a nonzero status.
+ [This bug was present in "the beginning".]
+
+ 'pinky -l' no longer no longer prints output in the incorrect order when
+ standard output is fully buffered, e.g., when redirected to a file.
+ [bug introduced in coreutils-9.10]
+
+ 'shred' no longer blocks when opening a FIFO that has no readers.
+ [This bug was present in "the beginning".]
+
+ 'unexpand -t' no longer overflows a heap buffer, for tab values > SIZE_MAX/16.
+ [bug introduced in coreutils-9.11]
+
+ 'uniq -w' no longer overruns the read buffer in multibyte locales.
+ [bug introduced in coreutils-9.5]
+
+** Changes in behavior
+
+ 'ls' -w,--width no longer includes '\n' in the width of a line.
+ I.e., the width or $COLUMNS is interpreted to be an _inclusive_ maximum.
+
+** Improvements
+
+ 'sort' will now better use available memory and parallel operation
+ when reading from unknown sized inputs like pipes.
+
+
+* Noteworthy changes in release 9.11 (2026-04-20) [stable]
+
+** Bug fixes
+
+ 'dd' now always diagnoses partial writes correctly upon write failure.
+ Previously it may have indicated that only full writes were performed.
+ [This bug was present in "the beginning".]
+
+ 'fold' will no longer truncate output when encountering 0xFF bytes.
+ [bug introduced in coreutils-9.8]
+
+ 'fold' is again responsive to its input. Previously it would have delayed
+ processing until 256KiB was read from the input.
+ [bug introduced in coreutils-9.8]
+
+ 'kill --help' now has links to valid anchors in the html manual.
+ [bug introduced in coreutils-9.10]
+
+ When configured with --enable-systemd, the commands 'pinky',
+ 'uptime', 'users', and 'who' no longer consider the systemd session
+ classes 'greeter', 'lock-screen', 'background', 'background-light',
+ and 'none' to be users.
+ [bug introduced in coreutils-9.4]
+
+ 'pwd' on ancient systems will no longer overflow a buffer
+ when operating in deep paths longer than twice the system PATH_MAX.
+ [bug introduced in coreutils-9.6]
+
+ 'stat --printf=%%N' no longer performs unnecessary checks of the QUOTING_STYLE
+ environment variable.
+ [bug introduced in coreutils-8.26]
+
+ 'timeout' no longer exits abruptly when its parent is the init process, e.g.,
+ when started by the entrypoint of a container.
+ [bug introduced in coreutils-9.10]
+
+** New Features
+
+ 'cut' now supports multi-byte input and delimiters. Consequently
+ the -c option is now honored, and no longer an alias for -b, and
+ the -n option is now honored, and no longer ignored.
+ Also the -d option supports multi-byte delimiters.
+
+ 'cut' adds new options for better compatibility:
+ The -w,--whitespace-delimited option was added to support blank aligned fields
+ and for better compatibility with FreeBSD/macOS.
+ The -O option was added as an alias for the --output-delimiter option,
+ for better compatibility with busybox/toybox.
+ The -F option was added as an alias for -w -O ' '
+ for better compatibility with busybox/toybox.
+
+ 'date --date' now parses dot delimited dd.mm.yy format common in Europe.
+ This is in addition to the already supported mm/dd/yy and yy-mm-dd formats.
+
+** Changes in behavior
+
+ 'cksum --check' now uses shell quoting when required, to more robustly
+ escape file names output in diagnostics.
+ This also affects md5sum, sha*sum, and b2sum.
+
+** Improvements
+
+ 'cat' now uses zero-copy I/O on Linux when appropriate, to improve throughput.
+ E.g., throughput improved 6x from 12.9GiB/s to 81.8GiB/s on a Power10 system.
+
+ 'df --local' recognises more file system types as remote.
+ Specifically: autofs, ncpfs, smb, smb2, gfs, gfs2, userlandfs.
+
+ 'df' improves duplicate mount suppression, by checking each mount against
+ all previously kept entries for the same device, not just the latest one.
+
+ 'expand' and 'unexpand' now support multi-byte characters.
+
+ 'groups' and 'id' will now exit sooner after a write error,
+ which is significant when listing information for many users.
+
+ 'install' now allows the combination of the --compare and
+ --preserve-timestamps options.
+
+ 'fold', 'join', 'numfmt', 'uniq' now use more consistent blank character
+ determination on non GLIBC platforms. For example \u3000 (ideographic space)
+ will be considered a blank character on all platforms.
+
+ 'nl' now supports multi-byte --section-delimiter characters.
+
+ 'shuf -i' now operates up to two times faster on systems with unlocked stdio
+ functions.
+
+ 'tac' will now exit sooner after a write error, which is significant when
+ operating on a file with many lines.
+
+ 'timeout' now properly detects when it is reparented by a subreaper process on
+ Linux instead of init, e.g., the 'systemd --user' process.
+
+ 'wc -l' now operates up to four and a half times faster on hosts that support
+ Neon instructions.
+
+ 'wc -m' now operates up to 2.6 times faster on GLIBC when processing
+ non-ASCII UTF-8 characters.
+
+ 'yes' now uses zero-copy I/O on Linux to significantly increase throughput.
+ E.g., throughput improved 15x from 11.6GiB/s to 175GiB/s on a Power10 system.
+
+** Build-related
+
+ ./configure --enable-single-binary=hardlinks is now supported on systems
+ with dash as the system shell at /bin/sh.
+ [issue introduced in coreutils-9.10]
+
+ The test suite may have failed with a "Hangup" error if run non-interactively.
+ [issue introduced in coreutils-9.10]
+
+
+* Noteworthy changes in release 9.10 (2026-02-04) [stable]
+
+** Bug fixes
+
+ cp, install, and mv no longer enter an infinite loop copying sparse files
+ with SEEK_HOLE. E.g., this was seen on ext4 when copying sparse files with
+ extents that are being actively updated, and copy offload is not being used.
+ [bug introduced in coreutils-9.9]
+
+ 'date' no longer fails with format directives that return an empty string.
+ [bug introduced in coreutils-9.9]
+
+ 'dd seek=N of=FILE' no longer continues copying, overwriting FILE if it
+ exists, if ftruncate fails.
+ [bug introduced in coreutils-9.1]
+
+ du and ls no longer modify strings returned by getenv.
+ POSIX says this is not portable.
+ [bug introduced in fileutils-4.1.6]
+
+ 'fmt' now correctly diagnoses read errors.
+ Previously fmt generated a generic error for any read error.
+ [bug introduced in coreutils-9.0]
+
+ md5sum --text correctly translates CRLF line endings with the MSYS2 runtime.
+ This also applies to the sha*sum and b2sum utilities.
+ [This bug was present in "the beginning".]
+
+ 'numfmt' no longer drops custom suffixes from numbers it cannot fully parse.
+ [bug introduced with numfmt in coreutils-8.21]
+
+ 'tail -f --pid' can no longer exit upon receiving a non terminating signal.
+ On older Linux systems it may have failed with "Interrupted system call".
+ [bug introduced in coreutils-7.5]
+
+ 'timeout' will now propagate all terminating signals to the monitored command.
+ Previously 'timeout' could have exited and left the monitored command running.
+ [bug introduced with timeout in coreutils-7.0]
+
+ wc now documents its --debug option, currently used to
+ indicate the line count acceleration being used.
+ [bug introduced in coreutils-9.0]
+
+ When built with `clang -fno-inline`, memory allocation issues are again
+ handled in a defined manner. Previously programs may have crashed etc.
+ after a failure to allocate memory.
+ [bug introduced in coreutils-9.0]
+
+** New Features
+
+ configure accepts a new --enable-single-binary=hardlinks mode to build the
+ selected programs as hard links to a multi-call binary called "coreutils".
+ This augments the existing "symlinks" and "shebangs" modes already
+ supported by the --enable-single-binary option.
+
+ 'stat' and 'tail' now know about the "guest-memfd" file system type.
+ stat -f -c%T now reports the file system type,
+ and tail -f uses polling for this file system.
+
+ 'tail' now accepts the --debug option, which is currently used to
+ detail the --follow implementation being used.
+
+ 'du' now supports the short option -A corresponding to the existing long
+ option --apparent-size, for compatibility with FreeBSD.
+
+** Changes in behavior
+
+ All commands now markup option names in --help and man pages,
+ with bold attributes, and hyperlinks into the online manual on gnu.org.
+ The links can be configured with the --enable-manual-url configure option,
+ and the bold highlighting with --disable-bold-man-page-references.
+ At runtime all markup can be disabled with the TERM=dumb env var value.
+
+ 'fmt' -w,--width no longer includes '\n' in the width of a line.
+ I.e., the specified width is interpreted to be an _inclusive_ maximum.
+
+ 'ls --hyperlink' now uses more standard format hyperlinks.
+ 'ESC\' (ST) is now used as a delimiter, instead of '\a' (BEL).
+
+ 'ptx' -t is no longer a no-op, and now sets the default width to 100 columns.
+
+ 'timeout' now honors ignored signals and will not propagate them. E.g.,
+ timeout(1) in a shell backgrounded job, will not terminate upon receiving
+ SIGINT or SIGQUIT, as these are ignored by default in shell background jobs.
+
+ 'timeout -v -s 0' now prints the signal number 0 instead of EXIT.
+
+ The multi-call binary now only processes --help or --version options
+ if it is installed with a name ending with "coreutils". This allows
+ for more consistent handling of these options with unsupported commands.
+
+** Improvements
+
+ The multi-call binary built with configure --enable-single-binary
+ is reduced in size by 3.2% through the more efficient reuse of the cksum
+ utility by the md5sum and sha*sum utilities.
+
+ 'cksum' now validates its options more consistently.
+ E.g., `cksum --text --tag` now fails like `cksum --tag --text` already did.
+
+ 'cksum', 'du', and 'wc' now exit promptly upon receiving a write
+ error, which is significant when processing many input files.
+
+ csplit, ls, and sort, now handle a more complete set of terminating signals.
+
+ 'du' now processes directories with 10,000 or more entries up to 9 times
+ faster on the Lustre file system.
+
+ 'paste' now supports multi-byte --delimiters characters.
+
+ 'pinky' will now exit immediately upon receiving a write error, which is
+ significant when reading large plan or project files.
+
+ 'readlink' and 'realpath' will now exit promptly upon receiving a write error,
+ which is significant when canonicalizing multiple file names longer than
+ PATH_MAX.
+
+ 'timeout' on Linux will always terminate the child in the case where the
+ timeout process itself dies, like when it receives a KILL signal for example.
+
+** Build-related
+
+ Programs now port to C23 platforms that strictly check types when
+ qualifier-generic functions like strchr are used.
+
+ 'chcon' and 'runcon' stub binaries will be built on systems without
+ libselinux, when configured using --with-selinux.
+
+ 'kill' and 'uptime' are no longer built by default. These programs can be
+ built with the --enable-install-program=kill,uptime configure option.
+
+
+* Noteworthy changes in release 9.9 (2025-11-10) [stable]
+
+** Bug fixes
+
`basenc --base58` would not operate correctly with input > 15561475 bytes.
[bug introduced with --base58 in coreutils-9.8]
@@ -26,6 +303,10 @@ GNU coreutils NEWS -*- outline -*-
will no longer always set a __CF_USER_TEXT_ENCODING environment variable.
[bug introduced in coreutils-9.8]
+ 'nice' now limits the adjusted niceness value to its supported range on
+ GNU/Hurd.
+ [This bug was present in "the beginning".]
+
'numfmt' no longer reads out-of-bounds memory with trailing blanks in input.
[bug introduced with numfmt in coreutils-8.21]
@@ -6308,15 +6589,13 @@ point at which the packages merged to form the coreutils:
* who once again prints whatever host information it has, even without --lookup
========================================================================
-For older NEWS entries for the fileutils, textutils, and sh-utils
-packages, see ./old/*/NEWS.
This package began as the union of the following:
textutils-2.1, fileutils-4.1.11, sh-utils-2.0.15.
========================================================================
-Copyright (C) 2001-2025 Free Software Foundation, Inc.
+Copyright (C) 2001-2026 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
diff --git a/README b/README
index c0d549ff3..424e3f064 100644
--- a/README
+++ b/README
@@ -131,7 +131,7 @@ Please see the file COPYING for copying conditions.
========================================================================
-Copyright (C) 1998-2025 Free Software Foundation, Inc.
+Copyright (C) 1998-2026 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
diff --git a/README-hacking b/README-hacking
index c6529ac2c..6fa0aa6b1 100644
--- a/README-hacking
+++ b/README-hacking
@@ -102,7 +102,7 @@ each program. One way to do this is to use vc-dwim
-----
-Copyright (C) 2002-2025 Free Software Foundation, Inc.
+Copyright (C) 2002-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/README-valgrind b/README-valgrind
index 8b4abecfb..975238d63 100644
--- a/README-valgrind
+++ b/README-valgrind
@@ -1,7 +1,7 @@
#! /bin/bash
# Convert this package for use with valgrind.
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/THANKS.in b/THANKS.in
index 8f6af1b61..5a2fd3501 100644
--- a/THANKS.in
+++ b/THANKS.in
@@ -185,7 +185,6 @@ Dragos Harabor dharabor@us.oracle.com
Duncan Roe duncanr@optimation.com.au
Edward Schwartz edmcman@cmu.edu
Edzer Pebesma Edzer.Pebesma@rivm.nl
-Egmont Koblinger egmont@uhulinux.hu
Eirik Fuller eirik@hackrat.com
Eivind eivindt@multinet.no
Elbert Pol elbert.pol@gmail.com
@@ -460,6 +459,7 @@ Michail Litvak mci@owl.openwall.com
Michal Politowski mpol@charybda.icm.edu.pl
Michal Svec msvec@suse.cz
Michal Trunecka mtruneck@redhat.com
+Michał Majchrowicz mmajchrowicz@afine.com
Michel Robitaille robitail@IRO.UMontreal.CA
Michiel Bacchiani bacchian@raven.bu.edu
Mike Castle dalgoda@ix.netcom.com
diff --git a/TODO b/TODO
index 880c6fc51..07e95f7ec 100644
--- a/TODO
+++ b/TODO
@@ -139,7 +139,7 @@ pr's use of nstrftime can make it malloc a very large (up to SIZE_MAX) buffer
-----
-Copyright (C) 2002-2025 Free Software Foundation, Inc.
+Copyright (C) 2002-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/bootstrap b/bootstrap
index a15c4f574..19f9424bb 100755
--- a/bootstrap
+++ b/bootstrap
@@ -5,7 +5,7 @@
scriptversion=2025-06-10.02; # UTC
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -37,9 +37,9 @@ medir=`dirname "$me"`
# A library of shell functions for autopull.sh, autogen.sh, and bootstrap.
-scriptlibversion=2025-09-07.23; # UTC
+scriptlibversion=2025-12-04.19; # UTC
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -861,7 +861,8 @@ autopull()
elif check_exists git-merge-changelog; then
echo "$0: initializing git-merge-changelog driver"
git config merge.merge-changelog.name 'GNU-style ChangeLog merge driver'
- git config merge.merge-changelog.driver 'git-merge-changelog %O %A %B'
+ git config merge.merge-changelog.driver \
+ 'git-merge-changelog %O %A %B "%Y"'
else
echo "$0: consider installing git-merge-changelog from gnulib"
fi
diff --git a/bootstrap.conf b/bootstrap.conf
index ec68ac8bf..aee0c4f33 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -1,6 +1,6 @@
# Bootstrap configuration. -*- sh -*-
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,6 +20,8 @@
avoided_gnulib_modules='
--avoid=canonicalize-lgpl
--avoid=dummy
+ --avoid=mbiter
+ --avoid=mbiterf
--avoid=mbuiter
--avoid=mbuiterf
'
@@ -69,8 +71,8 @@ gnulib_modules="
crc-x86_64
crypto/md5
crypto/sha1
- crypto/sha3
crypto/sha256
+ crypto/sha3
crypto/sha512
crypto/sm3
cycle-check
@@ -85,11 +87,11 @@ gnulib_modules="
dup2
endian
environ
+ errno-iter
error
euidaccess
exclude
exitfail
- explicit_bzero
faccessat
fadvise
fchdir
@@ -117,6 +119,7 @@ gnulib_modules="
freopen
freopen-safer
fseeko
+ fseterr
fstatat
fsusage
fsync
@@ -129,7 +132,6 @@ gnulib_modules="
getline
getloadavg
getlogin
- getndelim2
getopt-gnu
getpagesize
gettext-h
@@ -183,9 +185,11 @@ gnulib_modules="
mcel-prefer
memcasecmp
memchr
+ memchr2
memcmp2
mempcpy
memrchr
+ memset_explicit
minmax
mkancesdirs
mkdir
@@ -202,7 +206,6 @@ gnulib_modules="
mpsort
nproc
nstrftime
- nullptr
obstack
open
openat-safer
@@ -213,16 +216,17 @@ gnulib_modules="
physmem
pipe-posix
pipe2
+ pipe2-safer
posix-shell
posix_spawn
- posix_spawnattr_destroy
- posix_spawnattr_init
- posix_spawnattr_setflags
- posix_spawnattr_setsigdefault
posix_spawn_file_actions_addclose
posix_spawn_file_actions_adddup2
posix_spawn_file_actions_destroy
posix_spawn_file_actions_init
+ posix_spawnattr_destroy
+ posix_spawnattr_init
+ posix_spawnattr_setflags
+ posix_spawnattr_setsigdefault
posix_spawnp
posixtm
posixver
@@ -244,7 +248,6 @@ gnulib_modules="
readtokens0
readutmp
regex
- remove
renameat
renameatu
rmdir
@@ -275,6 +278,7 @@ gnulib_modules="
stpcpy
str_endswith
strdup-posix
+ strerrorname_np
stringeq
strnlen
strnumcmp
@@ -286,6 +290,7 @@ gnulib_modules="
sys_resource-h
sys_stat-h
sys_types-h
+ sys_uio-h
sys_wait-h
targetdir
tempname
@@ -325,6 +330,7 @@ gnulib_modules="
xgetgroups
xgethostname
xmemcoll
+ xmemdup0
xnanosleep
xprintf
xprintf-posix
diff --git a/build-aux/gen-lists-of-programs.sh b/build-aux/gen-lists-of-programs.sh
index 454eba0aa..5d307219a 100755
--- a/build-aux/gen-lists-of-programs.sh
+++ b/build-aux/gen-lists-of-programs.sh
@@ -19,25 +19,33 @@ disabled_by_default_progs='
arch
coreutils
hostname
+ kill
+ uptime
'
# Programs that can be built only when certain requisite system
# features are detected at configure time.
build_if_possible_progs='
- chcon
chroot
df
hostid
libstdbuf.so
nice
pinky
- runcon
stdbuf
stty
timeout
users
who
'
+# Programs that are built only when certain requisite system
+# features are detected at configure time. I.e., they can
+# be built on all systems, but are only effective on some.
+# These will be built to create man pages for distribution.
+build_if_appropriate_progs='
+ chcon
+ runcon
+'
# All the other programs, to be built by default, and that should
# be buildable without problems on any target system.
@@ -76,7 +84,6 @@ normal_progs='
head
id
join
- kill
link
ln
logname
@@ -131,7 +138,6 @@ normal_progs='
unexpand
uniq
unlink
- uptime
vdir
wc
whoami
@@ -149,7 +155,8 @@ case $#,$1 in
echo "gl_ADD_PROG([optional_bin_progs], [$p])"
done
# Extra 'echo' to normalize whitespace.
- echo "no_install_progs_default='`echo $disabled_by_default_progs`'"
+ echo "no_install_progs_default='`echo $disabled_by_default_progs \
+ $build_if_appropriate_progs`'"
sed 's/^ *//' <<END
# Given the name of a variable containing a space-separated
# list of install-by-default programs and the actual list of
@@ -177,6 +184,10 @@ END
for p in $build_if_possible_progs; do
echo build_if_possible__progs += $progsdir/$p
done
+ echo build_if_appropriate__progs =
+ for p in $build_if_appropriate_progs; do
+ echo build_if_appropriate__progs += $progsdir/$p
+ done
echo default__progs =
for p in $normal_progs; do
echo default__progs += $progsdir/$p
@@ -184,7 +195,7 @@ END
;;
1,--list-progs)
for p in $disabled_by_default_progs $build_if_possible_progs \
- $normal_progs; do
+ $build_if_appropriate_progs $normal_progs; do
echo $p
done
;;
diff --git a/build-aux/gen-single-binary.sh b/build-aux/gen-single-binary.sh
index 7d15b9bfc..a89a6bc83 100755
--- a/build-aux/gen-single-binary.sh
+++ b/build-aux/gen-single-binary.sh
@@ -72,6 +72,12 @@ override_single dir ls
override_single vdir ls
override_single arch uname
override_single chgrp chown
+override_single md5sum cksum
+override_single sha1sum cksum
+override_single sha224sum cksum
+override_single sha256sum cksum
+override_single sha384sum cksum
+override_single sha512sum cksum
for cmd in $ALL_PROGRAMS; do
echo "# Command $cmd"
diff --git a/build-aux/makeinfo-wrapper.sh.in b/build-aux/makeinfo-wrapper.sh.in
new file mode 100755
index 000000000..6acea2f19
--- /dev/null
+++ b/build-aux/makeinfo-wrapper.sh.in
@@ -0,0 +1,41 @@
+#!/bin/sh
+# makeinfo wrapper that post-processes HTML output to replace _002d with -,
+# only on lines containing "option", corresponding to our @optAnchor macro.
+# Note texi uses "-" in anchors for spaces, hence why it escapes - with _002d.
+
+@MAKEINFO@ "$@" || exit
+
+process_html()
+{
+ sed_anchor_cleanup=\
+'/id=.*_002doption/{ s/id="\([^"]*\)_002doption/id="\1/g; s/_002d/-/g; }'
+
+ sed -e "$sed_anchor_cleanup" "$1" > "$1.t" &&
+ mv "$1.t" "$1"
+}
+
+case " $* " in
+ *" --html"*)
+ # Find the output file/directory
+ output=""
+ next_is_output=false
+ for arg in "$@"; do
+ if [ "$next_is_output" = true ]; then
+ output="$arg"
+ break
+ fi
+ case "$arg" in
+ -o) next_is_output=true ;;
+ --output=*) output="${arg#--output=}" ;;
+ esac
+ done
+
+ # Process the output file/directory
+ if test -n "$output"; then
+ test -f "$output" && NAMES='*' || NAMES='*.html'
+ find "$output" -name "$NAMES" -type f |
+ # dash doesn't support read -d '' yet.
+ while IFS= read -r htmlfile; do process_html "$htmlfile"; done
+ fi
+ ;;
+esac
diff --git a/cfg.mk b/cfg.mk
index 03fc61560..d5e8f9025 100644
--- a/cfg.mk
+++ b/cfg.mk
@@ -1,5 +1,5 @@
# Customize maint.mk -*- makefile -*-
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -17,6 +17,9 @@
# Used in maint.mk's web-manual rule
manual_title = Core GNU utilities
+# Don't create node redirection files for each anchor
+gendocs_options_ = --common --no-node-files
+
# Use the direct link. This is guaranteed to work immediately, while
# it can take a while for the faster mirror links to become usable.
url_dir_list = https://ftp.gnu.org/gnu/$(PACKAGE)
@@ -48,7 +51,7 @@ export VERBOSE = yes
# 4914152 9e
export XZ_OPT = -8e
-old_NEWS_hash = 6651d3c6c61d6b5d0f95b969e5c0c2af
+old_NEWS_hash = 6be180da274a8719bdb5bf771502ef5b
# Add an exemption for sc_makefile_at_at_check.
_makefile_at_at_check_exceptions = \
@@ -372,6 +375,11 @@ sc_texi_long_option_escaped: doc/coreutils.info
@grep ' –[^ ]' '$<' \
&& { echo 1>&2 '$@: found unquoted --long-option'; exit 1; } || :
+# pdf (and dvi) generation requires explicit empty args (trailing comma)
+sc_texi_ensure_empty_option_args:
+ @grep '^@optItem[^,]*,[^,]*$$' doc/*.texi && { echo 1>&2 \
+ '$@: Use explicit empty argument to @optItem[x]'; exit 1; } || :
+
# Ensure all man/*.[1x] files are present.
sc_man_file_correlation: check-x-vs-1 check-programs-vs-x
@@ -516,12 +524,6 @@ sc_prohibit_exit_write_error:
halt='Use write_error() instead' \
$(_sc_search_regexp)
-sc_prohibit_NULL:
- @prohibit='$(begword)NULL$(endword)' \
- in_vc_files='\.[ch]$$' \
- halt='use nullptr instead' \
- $(_sc_search_regexp)
-
sc_prohibit_bare_set:
@prohibit='^ *set [`$$]' \
in_vc_files='\.sh$$' \
@@ -601,6 +603,19 @@ sc_require_stdlib_safer:
else :; \
fi
+# Ensure that "unistd--.h" is used where appropriate.
+sc_require_unistd_safer:
+ @if $(VC_LIST_EXCEPT) | grep -l '\.[ch]$$' > /dev/null; then \
+ files=$$(grep -El '$(begword)(pipe2?|dup[23]?) ?\(' \
+ $$($(VC_LIST_EXCEPT) \
+ | grep '\.[ch]$$')); \
+ test -n "$$files" && grep -LE 'include "unistd--.h"' $$files \
+ | grep . && \
+ { echo '$(ME): the above files should use "unistd--.h"' \
+ 1>&2; exit 1; } || :; \
+ else :; \
+ fi
+
sc_prohibit_perl_hash_quotes:
@prohibit="\{'[A-Z_]+' *[=}]" \
halt="in Perl code, write \$$hash{KEY}, not \$$hash{'K''EY'}" \
@@ -632,6 +647,12 @@ sc_env_test_dependencies:
|| echo $$test should call: print_ver_ $$prog; \
done | grep . && exit 1 || :
+# Enforce using our printf if using \u or \x
+sc_env_printf:
+ @cd $(top_srcdir) && GIT_PAGER= git grep 'printf.*[^\\]\\[ux]' tests \
+ | grep -v -- '--printf' | grep -v 'env printf' \
+ && { echo 'use "env printf" with \x or \u'; exit 1; } || :
+
# Use framework_failure_, not the old name without the trailing underscore.
sc_prohibit_framework_failure:
@prohibit='$(begword)framework_''failure$(endword)' \
@@ -646,6 +667,7 @@ sc_prohibit_test_backticks:
# Ensure that compare is used to check empty files
# so that the unexpected contents are displayed
+# Use `returns_ 1 test -s ... || fail=1` to avoid this check.
sc_prohibit_test_empty:
@prohibit='test -s.*&&' in_vc_files='^tests/' \
halt='use `compare /dev/null ...`, not `test -s ...` in tests/' \
@@ -884,6 +906,10 @@ sc_prohibit-long-form-bug-urls:
announcement_Cc_ = $(translation_project_), \
coreutils@gnu.org, coreutils-announce@gnu.org
+# Write cksum supported checksums into the announcement.
+# I.e., base64 to reduce space, and possibly tagged to ease usage.
+announce_gen_args = --cksum-checksums
+
-include $(srcdir)/dist-check.mk
update-copyright-env = \
@@ -893,9 +919,9 @@ update-copyright-env = \
# List syntax-check exemptions.
exclude_file_name_regexp--sc_space_tab = \
- ^(tests/pr/|tests/misc/nl\.sh$$|gl/.*\.diff$$|man/help2man$$)
+ ^(tests/pr/|tests/nl/nl\.sh$$|gl/.*\.diff$$|man/help2man$$)
exclude_file_name_regexp--sc_bindtextdomain = \
- ^(gl/.*|lib/euidaccess-stat|src/make-prime-list|src/cksum)\.c$$
+ ^(gl/.*|lib/euidaccess-stat|src/make-prime-list|src/cksum_crc)\.c$$
exclude_file_name_regexp--sc_trailing_blank = \
^(tests/pr/|gl/.*\.diff$$|man/help2man)
_x_system_h := (system|copy|chown-core|find-mount-point)\.h
@@ -931,7 +957,7 @@ _ll = ^src/longlong\.h$$
exclude_file_name_regexp--sc_useless_cpp_parens = $(_ll)
exclude_file_name_regexp--sc_space_before_open_paren = $(_ll)
-tbi_1 = ^tests/pr/|(\.mk|^man/help2man)$$
+tbi_1 = ^tests/pr/|(\.mk|^gl/.*\.diff|^man/help2man)$$
tbi_2 = ^scripts/git-hooks/(pre-commit|pre-applypatch|applypatch-msg)$$
tbi_3 = (GNU)?[Mm]akefile(\.am)?$$|$(_ll)
exclude_file_name_regexp--sc_prohibit_tab_based_indentation = \
@@ -946,8 +972,9 @@ exclude_file_name_regexp--sc_prohibit_continued_string_alpha_in_column_1 = \
^src/(system\.h|od\.c|printf\.c|getlimits\.c)$$
_cksum = ^tests/cksum/cksum-base64\.pl$$
+_tb_misc = misc/(stdbuf|responsive)
exclude_file_name_regexp--sc_prohibit_test_backticks = \
- ^tests/(local\.mk|(init|misc/stdbuf|factor/create-test)\.sh)$$|$(_cksum)
+ ^tests/(local\.mk|(init|$(_tb_misc)|factor/create-test)\.sh)$$|$(_cksum)
# Exempt test.c, since it's nominally shared, and relatively static.
exclude_file_name_regexp--sc_prohibit_operator_at_end_of_line = \
diff --git a/configure.ac b/configure.ac
index 5e99ef386..25fabe49d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,7 @@
# -*- autoconf -*-
# Process this file with autoconf to produce a configure script.
-# Copyright (C) 1991-2025 Free Software Foundation, Inc.
+# Copyright (C) 1991-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -64,7 +64,7 @@ gl_INIT
coreutils_MACROS
# These are safe, since 'sort', coreutils's only multithreaded app,
-# does not use the relevant modules.
+# does not use the relevant modules, or calls their code from one thread.
AC_DEFINE([GNULIB_EXCLUDE_SINGLE_THREAD], [1],
[Define to 1 if apps call 'exclude' functions from a single thread.])
AC_DEFINE([GNULIB_REGEX_SINGLE_THREAD], [1],
@@ -105,16 +105,16 @@ AC_DEFUN([gl_GCC_VERSION_IFELSE],
)
AC_ARG_ENABLE([single-binary],
- [AS_HELP_STRING([--enable-single-binary=[shebangs|symlinks]],
+ [AS_HELP_STRING([--enable-single-binary@<:@=shebangs|symlinks|hardlinks@:>@],
[Compile all the tools in a single binary, reducing the overall size.
- When compiled this way, shebangs (default when enabled) or symlinks are
- installed for each tool that points to the single binary.])],
+ When compiled this way, shebangs (default when enabled), symlinks, or
+ hardlinks are installed for each tool, which point to a single binary])],
[gl_single_binary=no ;
case $enableval in
yes) gl_single_binary=shebangs ;;
- no|shebangs|symlinks) gl_single_binary=$enableval ;;
+ no|shebangs|symlinks|hardlinks) gl_single_binary=$enableval ;;
*) AC_MSG_ERROR([bad value $enableval for single-binary option.
- Options are: symlinks, shebangs, no.]) ;;
+ Options are: hardlinks, symlinks, shebangs, no.]) ;;
esac],
[gl_single_binary=no]
)
@@ -126,18 +126,20 @@ AC_ARG_ENABLE([single-binary-exceptions],
[gl_single_binary_exceptions=$enableval],
[gl_single_binary_exceptions=]
)
-if test "$gl_single_binary" = 'symlinks'; then
+if test "$gl_single_binary" = 'symlinks' ||
+ test "$gl_single_binary" = 'hardlinks'; then
if ! test "`echo ls | sed \"$program_transform_name\"`" = 'ls'; then
AC_MSG_ERROR([program name transformations are not currently supported
- with --enable-single-binary=symlinks.])
+ with --enable-single-binary=$gl_single_binary.])
fi
fi
AM_CONDITIONAL([SINGLE_BINARY], [test "$gl_single_binary" != no])
+AM_CONDITIONAL([SINGLE_BINARY_HARD], [test "$gl_single_binary" = hardlinks])
AC_ARG_ENABLE([bold-man-page-references],
[AS_HELP_STRING([--disable-bold-man-page-references],
- [When generating man pages, do not apply bold style around any
- references like name(1) etc.])],
+ [When generating man pages, or displaying --help, do not apply bold style
+ around any references like name(1) or --option references etc.])],
[gl_bold_manpages=yes ;
case $enableval in
no|yes) gl_bold_manpages=$enableval ;;
@@ -146,8 +148,35 @@ AC_ARG_ENABLE([bold-man-page-references],
esac],
[gl_bold_manpages=yes]
)
+if test "$gl_bold_manpages" != no; then
+ AC_DEFINE([BOLD_MAN_REFS], [1], [Whether to markup references as bold])
+fi
AM_CONDITIONAL([BOLD_MAN_REFS], [test "$gl_bold_manpages" != no])
+# When generating manual pages, include a "Report any translation bugs" line.
+AC_DEFINE([PACKAGE_L10N_BUGREPORT], ["https://translationproject.org/team/"],
+ [Define to the URL of your translation project.])
+
+AC_ARG_ENABLE([manual-url],
+ [AS_HELP_STRING([--enable-manual-url@<:@=<url>@:>@],
+ [When generating man pages, or displaying --help, generate hyperlinks
+ from each "--option" to <url>@%:@command-option. A local file is
+ supported through a file:// URL. The default URL used is:
+ $PACKAGE_URL/manual/$PACKAGE.html.])],
+ [case $enableval in
+ yes) gl_manual_url=default ;;
+ no) gl_manual_url='' ;;
+ *) gl_manual_url="$enableval" ;;
+ esac],
+ [gl_manual_url=default]
+)
+if test x"$gl_manual_url" != x; then
+ if test "$gl_manual_url" = default; then
+ gl_manual_url=${PACKAGE_URL}manual/$PACKAGE.html
+ fi
+ AC_DEFINE_UNQUOTED([MANUAL_URL], ["$gl_manual_url"], [URL for full manual])
+fi
+
AC_ARG_ENABLE([gcc-warnings],
[AS_HELP_STRING([--enable-gcc-warnings@<:@=TYPE@:>@],
[control generation of GCC warnings. The TYPE 'no' disables
@@ -264,54 +293,6 @@ AC_CHECK_FUNCS([sigsuspend],
gl_WINSIZE_IN_PTEM
-AC_MSG_CHECKING([whether localtime caches TZ])
-AC_CACHE_VAL([utils_cv_localtime_cache],
-[if test x$ac_cv_func_tzset = xyes; then
-AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <time.h>
-#if STDC_HEADERS
-# include <stdlib.h>
-#endif
-extern char **environ;
-void unset_TZ (void)
-{
- char **from, **to;
- for (to = from = environ; (*to = *from); from++)
- if (! (to[0][0] == 'T' && to[0][1] == 'Z' && to[0][2] == '='))
- to++;
-}
-int
-main ()
-{
- time_t now = time ((time_t *) 0);
- int hour_GMT0, hour_unset;
- if (putenv ("TZ=GMT0") != 0)
- return 1;
- hour_GMT0 = localtime (&now)->tm_hour;
- unset_TZ ();
- hour_unset = localtime (&now)->tm_hour;
- if (putenv ("TZ=PST8") != 0)
- return 1;
- if (localtime (&now)->tm_hour == hour_GMT0)
- return 1;
- unset_TZ ();
- if (localtime (&now)->tm_hour != hour_unset)
- return 1;
- return 0;
-}]])],
-[utils_cv_localtime_cache=no],
-[utils_cv_localtime_cache=yes],
-[# If we have tzset, assume the worst when cross-compiling.
-utils_cv_localtime_cache=yes])
-else
- # If we lack tzset, report that localtime does not cache TZ,
- # since we can't invalidate the cache if we don't have tzset.
- utils_cv_localtime_cache=no
-fi])dnl
-AC_MSG_RESULT([$utils_cv_localtime_cache])
-if test $utils_cv_localtime_cache = yes; then
- AC_DEFINE([LOCALTIME_CACHE], [1], [FIXME])
-fi
-
# Find the library for dynamic loading of shared libraries.
AC_SEARCH_LIBS([dlopen], [dl])
AS_CASE([$ac_cv_search_dlopen],
@@ -783,6 +764,45 @@ AM_CONDITIONAL([USE_AVX512_WC_LINECOUNT],
[test $utils_cv_avx512_intrinsic_exists = yes])
CFLAGS=$ac_save_CFLAGS
+
+CFLAGS="-march=armv8-a+simd $CFLAGS"
+AC_MSG_CHECKING([for neon intrinsics])
+AC_CACHE_VAL([utils_cv_neon_intrinsic_exists],[
+AC_LINK_IFELSE(
+ [AC_LANG_SOURCE([[
+ #include <sys/auxv.h>
+ #include <asm/hwcap.h>
+ #include <arm_neon.h>
+
+ int
+ main (void)
+ {
+ unsigned char buffer[128] = {0};
+ const uint8x16_t endlines = vdupq_n_u8 ('\n');
+ int8x16_t acc0 = vdupq_n_s8 (0);
+ uint8x16_t v0 = vld1q_u8 (buffer);
+ int8x16_t c0 = vreinterpretq_s8_u8 (vceqq_u8 (v0, endlines));
+ acc0 = vaddq_s8 (acc0, c0);
+ int16x8_t a0 = vpaddlq_s8 (acc0);
+ int32x4_t b0 = vpaddlq_s16 (a0);
+ int64x2_t c1 = vpaddlq_s32 (b0);
+ int lines = vgetq_lane_s64 (c1, 0) + vgetq_lane_s64 (c1, 1);
+ return lines && 0 < (getauxval (AT_HWCAP) & HWCAP_ASIMD);
+ }
+ ]])
+ ],[
+ utils_cv_neon_intrinsic_exists=yes
+ ],[
+ utils_cv_neon_intrinsic_exists=no
+ ])])
+AC_MSG_RESULT([$utils_cv_neon_intrinsic_exists])
+if test $utils_cv_neon_intrinsic_exists = yes; then
+ AC_DEFINE([USE_NEON_WC_LINECOUNT], [1], [Counting lines with Neon enabled])
+fi
+AM_CONDITIONAL([USE_NEON_WC_LINECOUNT],
+ [test $utils_cv_neon_intrinsic_exists = yes])
+
+CFLAGS=$ac_save_CFLAGS
############################################################################
dnl Autogenerated by the 'gen-lists-of-programs.sh' auxiliary script.
@@ -892,7 +912,6 @@ AM_CONDITIONAL([CROSS_COMPILING], [test "$cross_compiling" = yes])
# translatable strings, we must use need-formatstring-macros here.
AM_GNU_GETTEXT([external], [need-formatstring-macros])
AM_GNU_GETTEXT_VERSION([0.19.2])
-AM_CONDITIONAL([USE_NLS], [test "$USE_NLS" = yes])
# For a test of uniq: it uses the $LOCALE_FR envvar.
gt_LOCALE_FR
@@ -902,4 +921,6 @@ AC_CONFIG_FILES(
po/Makefile.in
gnulib-tests/Makefile
)
+AC_CONFIG_FILES([build-aux/makeinfo-wrapper.sh],
+ [chmod +x build-aux/makeinfo-wrapper.sh])
AC_OUTPUT
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index b4206069d..fb12856da 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -144,7 +144,7 @@
This manual documents version @value{VERSION} of the GNU core
utilities, including the standard programs for text and file manipulation.
-Copyright @copyright{} 1994--2025 Free Software Foundation, Inc.
+Copyright @copyright{} 1994--2026 Free Software Foundation, Inc.
@quotation
Permission is granted to copy, distribute and/or modify this document
@@ -560,7 +560,7 @@ This manual was originally derived from the Unix man pages in the
distributions, which were written by David MacKenzie and updated by Jim
Meyering. What you are reading now is the authoritative documentation
for these utilities; the man pages are no longer being maintained. The
-original @command{fmt} man page was written by Ross Paterson. Fran@,{c}ois
+original @command{fmt} man page was written by Ross Paterson. François
Pinard did the initial conversion to Texinfo format. Karl Berry did the
indexing, some reorganization, and editing of the results. Brian
Youmans of the Free Software Foundation office staff combined the
@@ -568,45 +568,53 @@ manuals for textutils, fileutils, and sh-utils to produce the present
omnibus manual. Richard Stallman contributed his usual invaluable
insights to the overall process.
+@macro optAnchor{command,option}
+@anchor{\command\-option\option\}
+@end macro
+
+@macro optItem{command,option,param}
+@optAnchor{\command\,\option\}
+@item \option\\param\
+@opindex \option\
+@end macro
+
+@macro optItemx{command,option,param}
+@optAnchor{\command\,\option\}
+@itemx \option\\param\
+@opindex \option\
+@end macro
+
@node Common options
@chapter Common options
-@macro optBackup
-@item -b
-@itemx --backup[=@var{method}]
-@opindex -b
-@opindex --backup
+@macro optBackup{cmd}
+@optItem{\cmd\,-b,}
+@optItemx{\cmd\,--backup,[=@var{method}]}
@vindex VERSION_CONTROL
@cindex backups, making
@xref{Backup options}.
Make a backup of each file that would otherwise be overwritten or removed.
@end macro
-@macro optBackupSuffix
-@item -S @var{suffix}
-@itemx --suffix=@var{suffix}
-@opindex -S
-@opindex --suffix
+@macro optBackupSuffix{cmd}
+@optItem{\cmd\,-S,@w{ }@var{suffix}}
+@optItemx{\cmd\,--suffix,=@var{suffix}}
Append @var{suffix} to each backup file made with @option{-b}.
@xref{Backup options}.
@end macro
-@macro optTargetDirectory
-@item -t @var{directory}
-@itemx --target-directory=@var{directory}
-@opindex -t
-@opindex --target-directory
+@macro optTargetDirectory{cmd}
+@optItem{\cmd\,-t,@w{ }@var{directory}}
+@optItemx{\cmd\,--target-directory,=@var{directory}}
@cindex target directory
@cindex destination directory
Specify the destination @var{directory}.
@xref{Target directory}.
@end macro
-@macro optNoTargetDirectory
-@item -T
-@itemx --no-target-directory
-@opindex -T
-@opindex --no-target-directory
+@macro optNoTargetDirectory{cmd}
+@optItem{\cmd\,-T,}
+@optItemx{\cmd\,--no-target-directory,}
@cindex target directory
@cindex destination directory
Do not treat the last operand specially when it is a directory or a
@@ -620,27 +628,21 @@ rather than a newline. This option enables other programs to parse the
output even when that output would contain data with embedded newlines.
@end macro
-@macro optNull
-@item -0
-@itemx --null
-@opindex -0
-@opindex --null
+@macro optNull{cmd}
+@optItem{\cmd\,-0,}
+@optItemx{\cmd\,--null,}
@outputNUL
@end macro
-@macro optZero
-@item -z
-@itemx --zero
-@opindex -z
-@opindex --zero
+@macro optZero{cmd}
+@optItem{\cmd\,-z,}
+@optItemx{\cmd\,--zero,}
@outputNUL
@end macro
-@macro optZeroTerminated
-@item -z
-@itemx --zero-terminated
-@opindex -z
-@opindex --zero-terminated
+@macro optZeroTerminated{cmd}
+@optItem{\cmd\,-z,}
+@optItemx{\cmd\,--zero-terminated,}
@cindex process zero-terminated items
Delimit items with a zero byte rather than a newline (ASCII LF).
I.e., treat input as items separated by ASCII NUL
@@ -651,9 +653,8 @@ reliably handle arbitrary file names (even those containing blanks
or other special characters).
@end macro
-@macro optSi
-@item --si
-@opindex --si
+@macro optSi{cmd}
+@optItem{\cmd\,--si,}
@cindex SI output
Append an SI-style abbreviation to each size, such as @samp{M} for
megabytes. Powers of 1000 are used, not 1024; @samp{M} stands for
@@ -663,11 +664,9 @@ megabytes. Powers of 1000 are used, not 1024; @samp{M} stands for
you prefer powers of 1024.
@end macro
-@macro optHumanReadable
-@item -h
-@itemx --human-readable
-@opindex -h
-@opindex --human-readable
+@macro optHumanReadable{cmd}
+@optItem{\cmd\,-h,}
+@optItemx{\cmd\,--human-readable,}
@cindex human-readable output
Append a size letter to each size, such as @samp{M} for mebibytes.
Powers of 1024 are used, not 1000; @samp{M} stands for 1,048,576 bytes.
@@ -675,9 +674,8 @@ This option is equivalent to @option{--block-size=human-readable}.
Use the @option{--si} option if you prefer powers of 1000.
@end macro
-@macro optStripTrailingSlashes
-@item --strip-trailing-slashes
-@opindex --strip-trailing-slashes
+@macro optStripTrailingSlashes{cmd}
+@optItem{\cmd\,--strip-trailing-slashes,}
@cindex stripping trailing slashes
Remove any trailing slashes from each @var{source} argument.
@xref{Trailing slashes}.
@@ -766,8 +764,12 @@ these programs, abbreviations of the long options are not always recognized.
@item --help
@opindex --help
+@vindex TERM
@cindex help, online
Print a usage message listing all available options, then exit successfully.
+Help output may be marked up with terminal codes for formatting or
+hyperlinks, which can be disabled by unsetting the @env{TERM} environment
+variable, or setting it to the value @samp{dumb}.
@item --version
@opindex --version
@@ -1407,18 +1409,16 @@ a symlink or its referent.
@table @samp
-@macro choptH
-@item -H
-@opindex -H
+@macro choptH{cmd}
+@optItem{\cmd\,-H,}
@cindex symbolic link to directory, traverse if on the command line
If @option{--recursive} (@option{-R}) is specified and
a command line argument is a symbolic link to a directory, traverse it.
@end macro
-@choptH
+@choptH{}
-@macro choptL
-@item -L
-@opindex -L
+@macro choptL{cmd}
+@optItem{\cmd\,-L,}
@cindex symbolic link to directory, traverse each that is encountered
In a recursive traversal, traverse every symbolic link to a directory
that is encountered.
@@ -1435,16 +1435,14 @@ the operation will be performed on the target of that symlink,
possibly allowing the attacker to escalate privileges.
@end macro
+@choptL{}
-@choptL
-
-@macro choptP
-@item -P
-@opindex -P
+@macro choptP{cmd}
+@optItem{\cmd\,-P,}
@cindex symbolic link to directory, never traverse
Do not traverse any symbolic links.
@end macro
-@choptP
+@choptP{}
@macro choptDefault
This is the default if none of @option{-H}, @option{-L},
@@ -1601,6 +1599,7 @@ time using various configure variables, which currently include:
@item utils_cv_avx2_intrinsic_exists
@item utils_cv_avx2_pclmul_intrinsic_exists
@item utils_cv_avx512_pclmul_intrinsic_exists
+@item utils_cv_neon_intrinsic_exists
@item utils_cv_pclmul_intrinsic_exists
@item utils_cv_vmull_intrinsic_exists
@end table
@@ -1625,7 +1624,7 @@ ARM, and AArch64 platforms:
@example
export OPENSSL_ia32cap='0x0'
export OPENSSL_armcap='0x0'
-export GLIBC_TUNABLES='glibc.cpu.hwcaps=-AVX512F,-AVX2,-AVX,-PMULL'
+export GLIBC_TUNABLES='glibc.cpu.hwcaps=-ASIMD,-AVX512F,-AVX2,-AVX,-PMULL'
@end example
The @option{--debug} option is available on all utilities supporting
@@ -1734,63 +1733,46 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -A
-@itemx --show-all
-@opindex -A
-@opindex --show-all
+@optItem{cat,-A,}
+@optItemx{cat,--show-all,}
Equivalent to @option{-vET}.
-@item -b
-@itemx --number-nonblank
-@opindex -b
-@opindex --number-nonblank
+@optItem{cat,-b,}
+@optItemx{cat,--number-nonblank,}
Number all nonempty output lines, starting with 1.
-@item -e
-@opindex -e
+@optItem{cat,-e,}
Equivalent to @option{-vE}.
-@item -E
-@itemx --show-ends
-@opindex -E
-@opindex --show-ends
+@optItem{cat,-E,}
+@optItemx{cat,--show-ends,}
Display a @samp{$} after the end of each line.
The @code{\r\n} combination is shown as @samp{^M$}.
-@item -n
-@itemx --number
-@opindex -n
-@opindex --number
+@optItem{cat,-n,}
+@optItemx{cat,--number,}
Number all output lines, starting with 1. This option is ignored
if @option{-b} is in effect.
-@item -s
-@itemx --squeeze-blank
-@opindex -s
-@opindex --squeeze-blank
+@optItem{cat,-s,}
+@optItemx{cat,--squeeze-blank,}
@cindex squeezing empty lines
@cindex squeezing blank lines
Suppress repeated adjacent blank lines; output just one empty line
instead of several.
-@item -t
-@opindex -t
+@optItem{cat,-t,}
Equivalent to @option{-vT}.
-@item -T
-@itemx --show-tabs
-@opindex -T
-@opindex --show-tabs
+@optItem{cat,-T,}
+@optItemx{cat,--show-tabs,}
Display TAB characters as @samp{^I}.
-@item -u
-@opindex -u
+@optItem{cat,-u,}
Ignored; for POSIX compatibility.
-@item -v
-@itemx --show-nonprinting
-@opindex -v
-@opindex --show-nonprinting
+@optItem{cat,-v,}
+@optItemx{cat,--show-nonprinting,}
Display control characters except for LFD and TAB using
@samp{^} notation and precede characters that have the high bit set with
@samp{M-}.
@@ -1840,23 +1822,17 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -b
-@itemx --before
-@opindex -b
-@opindex --before
+@optItem{tac,-b,}
+@optItemx{tac,--before,}
The separator is attached to the beginning of the record that it
precedes in the file.
-@item -r
-@itemx --regex
-@opindex -r
-@opindex --regex
+@optItem{tac,-r,}
+@optItemx{tac,--regex,}
Treat the separator string as a regular expression.
-@item -s @var{separator}
-@itemx --separator=@var{separator}
-@opindex -s
-@opindex --separator
+@optItem{tac,-s,@w{ }@var{separator}}
+@optItemx{tac,--separator,=@var{separator}}
Use @var{separator} as the record separator, instead of newline.
Note an empty @var{separator} is treated as a zero byte.
I.e., input and output items are delimited with ASCII NUL.
@@ -1866,6 +1842,11 @@ I.e., input and output items are delimited with ASCII NUL.
On systems like MS-DOS that distinguish between text and binary files,
@command{tac} reads and writes in binary mode.
+@vindex TMPDIR
+Non-seekable input is buffered to @env{$TMPDIR}, defaulting to @file{/tmp},
+if the @env{TMPDIR} environment variable is not set
+or the location is not available.
+
@exitstatus
Example:
@@ -1929,10 +1910,8 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -b @var{style}
-@itemx --body-numbering=@var{style}
-@opindex -b
-@opindex --body-numbering
+@optItem{nl,-b,@w{ }@var{style}}
+@optItemx{nl,--body-numbering,=@var{style}}
Select the numbering style for lines in the body section of each
logical page. When a line is not numbered, the current line number
is not incremented, but the line number separator character is still
@@ -1951,10 +1930,8 @@ expression @var{bre}.
@xref{Regular Expressions, , Regular Expressions, grep, The GNU Grep Manual}.
@end table
-@item -d @var{cd}
-@itemx --section-delimiter=@var{cd}
-@opindex -d
-@opindex --section-delimiter
+@optItem{nl,-d,@w{ }@var{cd}}
+@optItemx{nl,--section-delimiter,=@var{cd}}
@cindex section delimiters of pages
Set the section delimiter characters to @var{cd}; default is
@samp{\:}. If only @var{c} is given, the second remains @samp{:}.
@@ -1964,29 +1941,21 @@ matching is disabled.
(Remember to protect @samp{\} or other metacharacters from shell
expansion with quotes or extra backslashes.)
-@item -f @var{style}
-@itemx --footer-numbering=@var{style}
-@opindex -f
-@opindex --footer-numbering
+@optItem{nl,-f,@w{ }@var{style}}
+@optItemx{nl,--footer-numbering,=@var{style}}
Analogous to @option{--body-numbering}.
-@item -h @var{style}
-@itemx --header-numbering=@var{style}
-@opindex -h
-@opindex --header-numbering
+@optItem{nl,-h,@w{ }@var{style}}
+@optItemx{nl,--header-numbering,=@var{style}}
Analogous to @option{--body-numbering}.
-@item -i @var{number}
-@itemx --line-increment=@var{number}
-@opindex -i
-@opindex --line-increment
+@optItem{nl,-i,@w{ }@var{number}}
+@optItemx{nl,--line-increment,=@var{number}}
Increment line numbers by @var{number} (default 1).
@var{number} can be negative to decrement.
-@item -l @var{number}
-@itemx --join-blank-lines=@var{number}
-@opindex -l
-@opindex --join-blank-lines
+@optItem{nl,-l,@w{ }@var{number}}
+@optItemx{nl,--join-blank-lines,=@var{number}}
@cindex empty lines, numbering
@cindex blank lines, numbering
Consider @var{number} (default 1) consecutive empty lines to be one
@@ -1995,10 +1964,8 @@ than @var{number} consecutive empty lines occur, do not number them.
An empty line is one that contains no characters, not even spaces
or tabs.
-@item -n @var{format}
-@itemx --number-format=@var{format}
-@opindex -n
-@opindex --number-format
+@optItem{nl,-n,@w{ }@var{format}}
+@optItemx{nl,--number-format,=@var{format}}
Select the line numbering format (default is @code{rn}):
@table @samp
@@ -2013,30 +1980,22 @@ right justified, no leading zeros;
right justified, leading zeros.
@end table
-@item -p
-@itemx --no-renumber
-@opindex -p
-@opindex --no-renumber
+@optItem{nl,-p,}
+@optItemx{nl,--no-renumber,}
Do not reset the line number at the start of a logical page.
-@item -s @var{string}
-@itemx --number-separator=@var{string}
-@opindex -s
-@opindex --number-separator
+@optItem{nl,-s,@w{ }@var{string}}
+@optItemx{nl,--number-separator,=@var{string}}
Separate the line number from the text line in the output with
@var{string} (default is the TAB character).
-@item -v @var{number}
-@itemx --starting-line-number=@var{number}
-@opindex -v
-@opindex --starting-line-number
+@optItem{nl,-v,@w{ }@var{number}}
+@optItemx{nl,--starting-line-number,=@var{number}}
Set the initial line number on each logical page to @var{number} (default 1).
The starting @var{number} can be negative.
-@item -w @var{number}
-@itemx --number-width=@var{number}
-@opindex -w
-@opindex --number-width
+@optItem{nl,-w,@w{ }@var{number}}
+@optItemx{nl,--number-width,=@var{number}}
Use @var{number} characters for line numbers (default 6).
@end table
@@ -2087,10 +2046,8 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -A @var{radix}
-@itemx --address-radix=@var{radix}
-@opindex -A
-@opindex --address-radix
+@optItem{od,-A,@w{ }@var{radix}}
+@optItemx{od,--address-radix,=@var{radix}}
@cindex radix for file offsets
@cindex file offset radix
Select the base in which file offsets are printed. @var{radix} can
@@ -2109,8 +2066,7 @@ none (do not print offsets).
The default is octal.
-@item --endian=@var{order}
-@opindex --endian
+@optItem{od,--endian,=@var{order}}
@cindex byte-swapping
@cindex endianness
Reorder input bytes, to handle inputs with differing byte orders,
@@ -2119,27 +2075,21 @@ of the current system. Swapping is performed according to the
specified @option{--type} size and endian @var{order}, which can be
@samp{little} or @samp{big}.
-@item -j @var{bytes}
-@itemx --skip-bytes=@var{bytes}
-@opindex -j
-@opindex --skip-bytes
+@optItem{od,-j,@w{ }@var{bytes}}
+@optItemx{od,--skip-bytes,=@var{bytes}}
Skip @var{bytes} input bytes before formatting and writing. If
@var{bytes} begins with @samp{0x} or @samp{0X}, it is interpreted in
hexadecimal; otherwise, if it begins with @samp{0}, in octal; otherwise,
in decimal.
@multiplierSuffixes{bytes}
-@item -N @var{bytes}
-@itemx --read-bytes=@var{bytes}
-@opindex -N
-@opindex --read-bytes
+@optItem{od,-N,@w{ }@var{bytes}}
+@optItemx{od,--read-bytes,=@var{bytes}}
Output at most @var{bytes} bytes of the input. Prefixes and suffixes on
@code{bytes} are interpreted as for the @option{-j} option.
-@item -S @var{bytes}
-@itemx --strings[=@var{bytes}]
-@opindex -S
-@opindex --strings
+@optItem{od,-S,@w{ }@var{bytes}}
+@optItemx{od,--strings,=@var{bytes}}
@cindex string constants, outputting
Instead of the normal output, output only @dfn{string constants}: at
least @var{bytes} consecutive printable characters,
@@ -2151,10 +2101,8 @@ are considered ASCII NUL terminated.
If @var{bytes} is omitted with @option{--strings}, the default is 3.
-@item -t @var{type}
-@itemx --format=@var{type}
-@opindex -t
-@opindex --format
+@optItem{od,-t,@w{ }@var{type}}
+@optItemx{od,--format,=@var{type}}
Select the format in which to output the file data. @var{type} is a
string of one or more of the below type indicator characters. If you
include more than one type indicator character in a single @var{type}
@@ -2227,19 +2175,15 @@ double
long double
@end table
-@item -v
-@itemx --output-duplicates
-@opindex -v
-@opindex --output-duplicates
+@optItem{od,-v,}
+@optItemx{od,--output-duplicates,}
Output consecutive lines that are identical. By default, when two or
more consecutive output lines would be identical, @command{od} outputs only
the first line, and puts just an asterisk on the following line to
indicate the elision.
-@item -w[@var{n}]
-@itemx --width[=@var{n}]
-@opindex -w
-@opindex --width
+@optItem{od,-w,@w{ }@var{n}}
+@optItemx{od,--width,=@var{n}}
Dump @code{n} input bytes per output line. This must be a multiple of
the least common multiple of the sizes associated with the specified
output types.
@@ -2255,49 +2199,38 @@ specification options. These options accumulate.
@table @samp
-@item -a
-@opindex -a
+@optItem{od,-a,}
Output as named characters. Equivalent to @samp{-t a}.
-@item -b
-@opindex -b
+@optItem{od,-b,}
Output as octal bytes. Equivalent to @samp{-t o1}.
-@item -c
-@opindex -c
+@optItem{od,-c,}
Output as printable single byte characters, C backslash escapes
or 3 digit octal sequences. Equivalent to @samp{-t c}.
-@item -d
-@opindex -d
+@optItem{od,-d,}
Output as unsigned decimal two-byte units. Equivalent to @samp{-t u2}.
-@item -f
-@opindex -f
+@optItem{od,-f,}
Output as floats. Equivalent to @samp{-t fF}.
-@item -i
-@opindex -i
+@optItem{od,-i,}
Output as decimal ints. Equivalent to @samp{-t dI}.
-@item -l
-@opindex -l
+@optItem{od,-l,}
Output as decimal long ints. Equivalent to @samp{-t dL}.
-@item -o
-@opindex -o
+@optItem{od,-o,}
Output as octal two-byte units. Equivalent to @option{-t o2}.
-@item -s
-@opindex -s
+@optItem{od,-s,}
Output as decimal two-byte units. Equivalent to @option{-t d2}.
-@item -x
-@opindex -x
+@optItem{od,-x,}
Output as hexadecimal two-byte units. Equivalent to @samp{-t x2}.
-@item --traditional
-@opindex --traditional
+@optItem{od,--traditional,}
Recognize the non-option label argument that traditional @command{od}
accepted. The following syntax:
@@ -2359,10 +2292,12 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -w @var{cols}
-@itemx --wrap=@var{cols}
-@opindex -w
-@opindex --wrap
+@optAnchor{base32,-w}
+@optAnchor{base32,--wrap}
+@optAnchor{basenc,-w}
+@optAnchor{basenc,--wrap}
+@optItem{base64,-w,@w{ }@var{cols}}
+@optItemx{base64,--wrap,=@var{cols}}
@cindex wrap data
@cindex column to wrap data after
During encoding, wrap lines after @var{cols} characters. This must be
@@ -2371,20 +2306,24 @@ a positive number.
The default is to wrap after 76 characters. Use the value 0 to
disable line wrapping altogether.
-@item -d
-@itemx --decode
-@opindex -d
-@opindex --decode
+@optAnchor{base32,-d}
+@optAnchor{base32,--decode}
+@optAnchor{basenc,-d}
+@optAnchor{basenc,--decode}
+@optItem{base64,-d,}
+@optItemx{base64,--decode,}
@cindex Decode base64 data
@cindex Base64 decoding
Change the mode of operation, from the default of encoding data, to
decoding data. Input is expected to be base64 encoded data, and the
output will be the original data.
-@item -i
-@itemx --ignore-garbage
-@opindex -i
-@opindex --ignore-garbage
+@optAnchor{base32,-i}
+@optAnchor{base32,--ignore-garbage}
+@optAnchor{basenc,-i}
+@optAnchor{basenc,--ignore-garbage}
+@optItem{base64,-i,}
+@optItemx{base64,--ignore-garbage,}
@cindex Ignore garbage in base64 stream
When decoding, newlines are always accepted.
During decoding, ignore unrecognized bytes,
@@ -2422,22 +2361,19 @@ Supported @var{encoding}s are:
@table @samp
-@item --base64
-@opindex --base64
+@optItem{basenc,--base64,}
Encode into (or decode from with @option{-d/--decode}) base64 form.
The format conforms to
@uref{https://datatracker.ietf.org/doc/html/rfc4648#section-4, RFC 4648#4}.
Equivalent to the @command{base64} command.
-@item --base64url
-@opindex --base64url
+@optItem{basenc,--base64url,}
Encode into (or decode from with @option{-d/--decode}) file-and-url-safe
base64 form (using @samp{_} and @samp{-} instead of @samp{+} and @samp{/}).
The format conforms to
@uref{https://datatracker.ietf.org/doc/html/rfc4648#section-5, RFC 4648#5}.
-@item --base58
-@opindex --base58
+@optItem{basenc,--base58,}
Encode into (or decode from with @option{-d/--decode}) base58 form.
The format conforms to
@uref{https://datatracker.ietf.org/doc/html/draft-msporny-base58-03,
@@ -2449,42 +2385,36 @@ For example this generates a unique 128 bit ID in 22 bytes:
uuidgen | basenc --base16 -di | basenc --base58
@end example
-@item --base32
-@opindex --base32
+@optItem{basenc,--base32,}
Encode into (or decode from with @option{-d/--decode}) base32 form.
The encoded data uses the @samp{ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=} characters.
The format conforms to
@uref{https://datatracker.ietf.org/doc/html/rfc4648#section-6, RFC 4648#6}.
Equivalent to the @command{base32} command.
-@item --base32hex
-@opindex --base32hex
+@optItem{basenc,--base32hex,}
Encode into (or decode from with @option{-d/--decode}) Extended Hex Alphabet
base32 form. The encoded data uses the
@samp{0123456789ABCDEFGHIJKLMNOPQRSTUV=} characters. The format conforms to
@uref{https://datatracker.ietf.org/doc/html/rfc4648#section-7, RFC 4648#7}.
-@item --base16
-@opindex --base16
+@optItem{basenc,--base16,}
Encode into (or decode from with @option{-d/--decode}) base16 (hexadecimal)
form. The encoded data uses the @samp{0123456789ABCDEF} characters. The format
conforms to
@uref{https://datatracker.ietf.org/doc/html/rfc4648#section-8, RFC 4648#8}.
-@item --base2lsbf
-@opindex --base2lsbf
+@optItem{basenc,--base2lsbf,}
Encode into (or decode from with @option{-d/--decode}) binary string form
(@samp{0} and @samp{1}) with the @emph{least} significant bit of every byte
first.
-@item --base2msbf
-@opindex --base2msbf
+@optItem{basenc,--base2msbf,}
Encode into (or decode from with @option{-d/--decode}) binary string form
(@samp{0} and @samp{1}) with the @emph{most} significant bit of every byte
first.
-@item --z85
-@opindex --z85
+@optItem{basenc,--z85,}
Encode into (or decode from with @option{-d/--decode}) Z85 form
(a modified Ascii85 form). The encoded data uses the
@samp{0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTU@
@@ -2591,58 +2521,45 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -c
-@itemx --crown-margin
-@opindex -c
-@opindex --crown-margin
+@optItem{fmt,-c,}
+@optItemx{fmt,--crown-margin,}
@cindex crown margin
@dfn{Crown margin} mode: preserve the indentation of the first two
lines within a paragraph, and align the left margin of each subsequent
line with that of the second line.
-@item -t
-@itemx --tagged-paragraph
-@opindex -t
-@opindex --tagged-paragraph
+@optItem{fmt,-t,}
+@optItemx{fmt,--tagged-paragraph,}
@cindex tagged paragraphs
@dfn{Tagged paragraph} mode: like crown margin mode, except that if
indentation of the first line of a paragraph is the same as the
indentation of the second, the first line is treated as a one-line
paragraph.
-@item -s
-@itemx --split-only
-@opindex -s
-@opindex --split-only
+@optItem{fmt,-s,}
+@optItemx{fmt,--split-only,}
Split lines only. Do not join short lines to form longer ones. This
prevents sample lines of code, and other such ``formatted'' text from
being unduly combined.
-@item -u
-@itemx --uniform-spacing
-@opindex -u
-@opindex --uniform-spacing
+@optItem{fmt,-u,}
+@optItemx{fmt,--uniform-spacing,}
Uniform spacing. Reduce spacing between words to one space, and spacing
between sentences to two spaces.
-@item -@var{width}
-@itemx -w @var{width}
-@itemx --width=@var{width}
-@opindex -@var{width}
-@opindex -w
-@opindex --width
+@optItem{fmt,-@var{width},}
+@optItemx{fmt,-w,@w{ }@var{width}}
+@optItemx{fmt,--width,=@var{width}}
Fill output lines up to @var{width} characters (default 75 or @var{goal}
plus 10, if @var{goal} is provided).
-@item -g @var{goal}
-@itemx --goal=@var{goal}
-@opindex -g
-@opindex --goal
+@optItem{fmt,-g,@w{ }@var{goal}}
+@optItemx{fmt,--goal,=@var{goal}}
@command{fmt} initially tries to make lines @var{goal} characters wide.
By default, this is 7% shorter than @var{width}.
-@item -p @var{prefix}
-@itemx --prefix=@var{prefix}
+@optItem{fmt,-p,@w{ }@var{prefix}}
+@optItemx{fmt,--prefix,=@var{prefix}}
Only lines beginning with @var{prefix} (possibly preceded by whitespace)
are subject to formatting. The prefix and any preceding whitespace are
stripped for the formatting and then re-attached to each formatted output
@@ -2700,6 +2617,7 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
+@optAnchor{pr,--pages}
@item +@var{first_page}[:@var{last_page}]
@itemx --pages=@var{first_page}[:@var{last_page}]
@c The two following @opindex lines evoke warnings because they contain ':'
@@ -2717,10 +2635,9 @@ is identical. By default, counting starts with the first page of input
file (not first page printed). Line numbering may be altered by @option{-N}
option.
-@item -@var{column}
-@itemx --columns=@var{column}
-@opindex -@var{column}
-@opindex --columns
+@optAnchor{pr,-COLS}
+@optItem{pr,-@var{column},}
+@optItemx{pr,--columns,=@var{column}}
@cindex down columns
With each single @var{file}, produce @var{column} columns of output
(default is 1) and print columns down, unless @option{-a} is used. The
@@ -2736,32 +2653,26 @@ Lines of full length are joined in a free field format and @option{-S}
option may set field separators. @option{-@var{column}} may not be used
with the @option{-m} option.
-@item -a
-@itemx --across
-@opindex -a
-@opindex --across
+@optItem{pr,-a,}
+@optItemx{pr,--across,}
@cindex across columns
With each single @var{file}, print columns across rather than down. The
@option{-@var{column}} option must be given with @var{column} greater than one.
If a line is too long to fit in a column, it is truncated.
-@item -c
-@itemx --show-control-chars
-@opindex -c
-@opindex --show-control-chars
+@optItem{pr,-c,}
+@optItemx{pr,--show-control-chars,}
Print control characters using hat notation (e.g., @samp{^G}); print
other nonprinting characters in octal backslash notation. By default,
nonprinting characters are not changed.
-@item -d
-@itemx --double-space
-@opindex -d
-@opindex --double-space
+@optItem{pr,-d,}
+@optItemx{pr,--double-space,}
@cindex double spacing
Double space the output.
-@item -D @var{format}
-@itemx --date-format=@var{format}
+@optItem{pr,-D,@w{ }@var{format}}
+@optItemx{pr,--date-format,=@var{format}}
@cindex time formats
@cindex formatting times
Format header dates using @var{format}, using the same conventions as
@@ -2786,47 +2697,36 @@ the @env{TZ} environment variable, or by the system default rules if
@env{TZ} is not set. @xref{TZ Variable,, Specifying the Time Zone
with @env{TZ}, libc, The GNU C Library Reference Manual}.
-@item -e[@var{in-tabchar}[@var{in-tabwidth}]]
-@itemx --expand-tabs[=@var{in-tabchar}[@var{in-tabwidth}]]
-@opindex -e
-@opindex --expand-tabs
+@optItem{pr,-e,[@var{in-tabchar}[@var{in-tabwidth}]]}
+@optItemx{pr,--expand-tabs,[=@var{in-tabchar}[@var{in-tabwidth}]]}
@cindex input tabs
Expand @var{tab}s to spaces on input. Optional argument @var{in-tabchar} is
the input tab character (default is the TAB character). Second optional
argument @var{in-tabwidth} is the input tab character's width (default
is 8).
-@item -f
-@itemx -F
-@itemx --form-feed
-@opindex -F
-@opindex -f
-@opindex --form-feed
+@optItem{pr,-f,}
+@optItemx{pr,-F,}
+@optItemx{pr,--form-feed,}
Use a form feed instead of newlines to separate output pages. This does
not alter the default page length of 66 lines.
-@item -h @var{header}
-@itemx --header=@var{header}
-@opindex -h
-@opindex --header
+@optItem{pr,-h,@w{ }@var{header}}
+@optItemx{pr,--header,=@var{header}}
Replace the file name in the header with the centered string @var{header}.
When using the shell, @var{header} should be quoted and should be
separated from @option{-h} by a space.
-@item -i[@var{out-tabchar}[@var{out-tabwidth}]]
-@itemx --output-tabs[=@var{out-tabchar}[@var{out-tabwidth}]]
-@opindex -i
-@opindex --output-tabs
+@optItem{pr,-i,@w{ }[@var{out-tabchar}[@var{out-tabwidth}]]}
+@optItemx{pr,--output-tabs,=[@var{out-tabchar}[@var{out-tabwidth}]]}
@cindex output tabs
Replace spaces with @var{tab}s on output. Optional argument @var{out-tabchar}
is the output tab character (default is the TAB character). Second optional
argument @var{out-tabwidth} is the output tab character's width (default
is 8).
-@item -J
-@itemx --join-lines
-@opindex -J
-@opindex --join-lines
+@optItem{pr,-J,}
+@optItemx{pr,--join-lines,}
Merge lines of full length. Used together with the column options
@option{-@var{column}}, @option{-a -@var{column}} or @option{-m}. Turns off
@option{-W/-w} line truncation;
@@ -2837,19 +2737,15 @@ to disentangle the old (POSIX-compliant) options @option{-w} and
@option{-s} along with the three column options.
-@item -l @var{page_length}
-@itemx --length=@var{page_length}
-@opindex -l
-@opindex --length
+@optItem{pr,-l,@w{ }@var{page_length}}
+@optItemx{pr,--length,=@var{page_length}}
Set the page length to @var{page_length} (default 66) lines, including
the lines of the header [and the footer]. If @var{page_length} is less
than or equal to 10, the header and footer are omitted, as if the
@option{-t} option had been given.
-@item -m
-@itemx --merge
-@opindex -m
-@opindex --merge
+@optItem{pr,-m,}
+@optItemx{pr,--merge,}
Merge and print all @var{file}s in parallel, one in each column. If a
line is too long to fit in a column, it is truncated, unless the @option{-J}
option is used. @option{--sep-string[=@var{string}]} may be used.
@@ -2862,10 +2758,8 @@ show no separators or line numbers. The default header becomes
may be used with the @option{-h} or @option{--header} option to fill up
the middle blank part.
-@item -n[@var{number-separator}[@var{digits}]]
-@itemx --number-lines[=@var{number-separator}[@var{digits}]]
-@opindex -n
-@opindex --number-lines
+@optItem{pr,-n,[@var{number-separator}[@var{digits}]]}
+@optItemx{pr,--number-lines,[=@var{number-separator}[@var{digits}]]}
Provide @var{digits} digit line numbering (default for @var{digits} is
5). With multicolumn output the number occupies the first @var{digits}
column positions of each text column or only each line of @option{-m}
@@ -2886,17 +2780,13 @@ fixed number of spaces is always printed in the place of the
@var{number-separator} TAB@. The tabification depends upon the output
position.
-@item -N @var{line_number}
-@itemx --first-line-number=@var{line_number}
-@opindex -N
-@opindex --first-line-number
+@optItem{pr,-N,@w{ }@var{line_number}}
+@optItemx{pr,--first-line-number,=@var{line_number}}
Start line counting with the number @var{line_number} at first line of
first page printed (in most cases not the first line of the input file).
-@item -o @var{margin}
-@itemx --indent=@var{margin}
-@opindex -o
-@opindex --indent
+@optItem{pr,-o,@w{ }@var{margin}}
+@optItemx{pr,--indent,=@var{margin}}
@cindex indenting lines
@cindex left margin
Indent each line with a margin @var{margin} spaces wide (default is zero).
@@ -2904,17 +2794,13 @@ The total page width is the size of the margin plus the @var{page_width}
set with the @option{-W/-w} option. A limited overflow may occur with
numbered single column output (compare @option{-n} option).
-@item -r
-@itemx --no-file-warnings
-@opindex -r
-@opindex --no-file-warnings
+@optItem{pr,-r,}
+@optItemx{pr,--no-file-warnings,}
Do not print a warning message when an argument @var{file} cannot be
opened. (The exit status will still be nonzero, however.)
-@item -s[@var{char}]
-@itemx --separator[=@var{char}]
-@opindex -s
-@opindex --separator
+@optItem{pr,-s,@w{ }@var{char}}
+@optItemx{pr,--separator,=@var{char}}
Separate columns by a single character @var{char}. The default for
@var{char} is the TAB character without @option{-w} and @samp{no
character} with @option{-w}. Without @option{-s} the default separator
@@ -2922,11 +2808,8 @@ character} with @option{-w}. Without @option{-s} the default separator
three column options (@option{-COLUMN}|@option{-a -COLUMN}|@option{-m}) unless
@option{-w} is set. This is a POSIX-compliant formulation.
-
-@item -S[@var{string}]
-@itemx --sep-string[=@var{string}]
-@opindex -S
-@opindex --sep-string
+@optItem{pr,-S,[@var{string}]}
+@optItemx{pr,--sep-string,[=@var{string}]}
Use @var{string} to separate output columns. The @option{-S} option doesn't
affect the @option{-W/-w} option, unlike the @option{-s} option which does. It
does not affect line truncation or column alignment.
@@ -2936,10 +2819,8 @@ Without @option{-S} or @option{-J}, @command{pr} uses a @samp{space}
(same as @option{-S"@w{ }"}).
If no @samp{@var{string}} argument is specified, @samp{""} is assumed.
-@item -t
-@itemx --omit-header
-@opindex -t
-@opindex --omit-header
+@optItem{pr,-t,}
+@optItemx{pr,--omit-header,}
Do not print the usual header [and footer] on each page, and do not fill
out the bottom of pages (with blank lines or a form feed). No page
structure is produced, but form feeds set in the input files are retained.
@@ -2948,23 +2829,17 @@ useful together with other options; e.g.: @option{-t -e4}, expand TAB characters
in the input file to 4 spaces but don't make any other changes. Use of
@option{-t} overrides @option{-h}.
-@item -T
-@itemx --omit-pagination
-@opindex -T
-@opindex --omit-pagination
+@optItem{pr,-T,}
+@optItemx{pr,--omit-pagination,}
Do not print header [and footer]. In addition eliminate all form feeds
set in the input files.
-@item -v
-@itemx --show-nonprinting
-@opindex -v
-@opindex --show-nonprinting
+@optItem{pr,-v,}
+@optItemx{pr,--show-nonprinting,}
Print nonprinting characters in octal backslash notation.
-@item -w @var{page_width}
-@itemx --width=@var{page_width}
-@opindex -w
-@opindex --width
+@optItem{pr,-w,@w{ }@var{page_width}}
+@optItemx{pr,--width,=@var{page_width}}
Set page width to @var{page_width} characters for multiple text-column
output only (default for @var{page_width} is 72). The specified
@var{page_width} is rounded down so that columns have equal width.
@@ -2974,10 +2849,8 @@ Lines of full length are merged, regardless of the column options
set. No @var{page_width} setting is possible with single column output.
A POSIX-compliant formulation.
-@item -W @var{page_width}
-@itemx --page_width=@var{page_width}
-@opindex -W
-@opindex --page_width
+@optItem{pr,-W,@w{ }@var{page_width}}
+@optItemx{pr,--page-width,=@var{page_width}}
Set the page width to @var{page_width} characters, honored with and
without a column option. With a column option, the specified @var{page_width}
is rounded down so that columns have equal width. Text lines are truncated,
@@ -3022,33 +2895,25 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -b
-@itemx --bytes
-@opindex -b
-@opindex --bytes
+@optItem{fold,-b,}
+@optItemx{fold,--bytes,}
Count bytes rather than columns, so that tabs, backspaces, and carriage
returns are each counted as taking up one column, just like other
characters.
-@item -c
-@itemx --characters
-@opindex -c
-@opindex --characters
+@optItem{fold,-c,}
+@optItemx{fold,--characters,}
Count characters rather than columns, meaning that lines containing
characters wider than one column will be visually longer.
-@item -s
-@itemx --spaces
-@opindex -s
-@opindex --spaces
+@optItem{fold,-s,}
+@optItemx{fold,--spaces,}
Break at word boundaries: the line is broken after the last blank before
the maximum line length. If the line contains no such blanks, the line
is broken at the maximum line length as usual.
-@item -w @var{width}
-@itemx --width=@var{width}
-@opindex -w
-@opindex --width
+@optItem{fold,-w,@w{ }@var{width}}
+@optItemx{fold,--width,=@var{width}}
Use a maximum line length of @var{width} columns instead of 80.
For compatibility @command{fold} supports an obsolete option syntax
@@ -3104,39 +2969,30 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -c [-]@var{num}
-@itemx --bytes=[-]@var{num}
-@opindex -c
-@opindex --bytes
+@optItem{head,-c,@w{ }[-]@var{num}}
+@optItemx{head,--bytes,=[-]@var{num}}
Print the first @var{num} bytes, instead of initial lines.
However, if @var{num} is prefixed with a @samp{-},
print all but the last @var{num} bytes of each file.
@multiplierSuffixes{num}
-@item -n [-]@var{num}
-@itemx --lines=[-]@var{num}
-@opindex -n
-@opindex --lines
+@optItem{head,-n,@w{ }[-]@var{num}}
+@optItemx{head,--lines,=[-]@var{num}}
Output the first @var{num} lines.
However, if @var{num} is prefixed with a @samp{-},
print all but the last @var{num} lines of each file.
Size multiplier suffixes are the same as with the @option{-c} option.
-@item -q
-@itemx --quiet
-@itemx --silent
-@opindex -q
-@opindex --quiet
-@opindex --silent
+@optItem{head,-q,}
+@optItemx{head,--quiet,}
+@optItemx{head,--silent,}
Never print file name headers.
-@item -v
-@itemx --verbose
-@opindex -v
-@opindex --verbose
+@optItem{head,-v,}
+@optItemx{head,--verbose,}
Always print file name headers.
-@optZeroTerminated
+@optZeroTerminated{head}
@end table
@@ -3199,20 +3055,20 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -c [+]@var{num}
-@itemx --bytes=[+]@var{num}
-@opindex -c
-@opindex --bytes
+@optItem{tail,-c,@w{ }[+]@var{num}}
+@optItemx{tail,--bytes,=[+]@var{num}}
Output the last @var{num} bytes, instead of final lines.
If @var{num} is prefixed with a @samp{+}, start printing with
byte @var{num} from the start of each file. For example to skip the first byte
use @code{tail -c +2}, while to skip all but the last byte use @code{tail -c 1}.
@multiplierSuffixes{num}
-@item -f
-@itemx --follow[=@var{how}]
-@opindex -f
-@opindex --follow
+@optItem{tail,--debug,}
+Output extra information to standard error,
+like the --follow implementation being used.
+
+@optItem{tail,-f,}
+@optItemx{tail,--follow,[=@var{how}]}
@cindex growing files
@vindex name @r{follow option}
@vindex descriptor @r{follow option}
@@ -3269,14 +3125,12 @@ by using a sub-second sleep interval, e.g., via an alias like this:
alias tail='tail -s.1'
@end example
-@item -F
-@opindex -F
+@optItem{tail,-F,}
This option is the same as @option{--follow=name --retry}. That is, tail
will attempt to reopen a file when it is removed. Should this fail, tail
will keep trying until it becomes accessible again.
-@item --max-unchanged-stats=@var{n}
-@opindex --max-unchanged-stats
+@optItem{tail,--max-unchanged-stats,=@var{n}}
When tailing a file by name, if there have been @var{n} (default
n=@value{DEFAULT_MAX_N_UNCHANGED_STATS_BETWEEN_OPENS}) consecutive
iterations for which the file has not changed, then
@@ -3288,18 +3142,15 @@ and when it prints the lines that have accumulated in the new log file.
This option is meaningful only when polling (i.e., without inotify)
and when following by name.
-@item -n [+]@var{num}
-@itemx --lines=[+]@var{}
-@opindex -n
-@opindex --lines
+@optItem{tail,-n,@w{ }[+]@var{num}}
+@optItemx{tail,--lines,=[+]@var{num}}
Output the last @var{num} lines.
If @var{num} is prefixed with a @samp{+}, start printing with
line @var{num} from the start of each file. For example to skip the first line
use @code{tail -n +2}, while to skip all but the last line use @code{tail -n 1}.
Size multiplier suffixes are the same as with the @option{-c} option.
-@item --pid=@var{pid}
-@opindex --pid
+@optItem{tail,--pid,=@var{pid}}
When following by name or by descriptor, you may specify the process ID,
@var{pid}, of one or more (by repeating @option{--pid}) writers of the
@var{file} arguments. Then, @command{tail} will exit shortly
@@ -3322,16 +3173,12 @@ terminate until long after the real writer has terminated.
On some systems, @option{--pid} is not supported and @command{tail}
outputs a warning.
-@item -q
-@itemx --quiet
-@itemx --silent
-@opindex -q
-@opindex --quiet
-@opindex --silent
+@optItem{tail,-q,}
+@optItemx{tail,--quiet,}
+@optItemx{tail,--silent,}
Never print file name headers.
-@item --retry
-@opindex --retry
+@optItem{tail,--retry,}
Indefinitely try to open the specified file.
This option is useful mainly when following (and otherwise issues a warning).
@@ -3346,10 +3193,8 @@ Without this option, when @command{tail} encounters a file that doesn't
exist or is otherwise inaccessible, it reports that fact and
never checks it again.
-@item -s @var{number}
-@itemx --sleep-interval=@var{number}
-@opindex -s
-@opindex --sleep-interval
+@optItem{tail,-s,@w{ }@var{number}}
+@optItemx{tail,--sleep-interval,=@var{number}}
Change the number of seconds to wait between iterations (the default is 1.0).
During one iteration, every specified file is checked to see if it has
changed size.
@@ -3360,13 +3205,11 @@ every @var{number} seconds.
The @var{number} must be non-negative and can be a floating-point number
in either the current or the C locale. @xref{Floating point}.
-@item -v
-@itemx --verbose
-@opindex -v
-@opindex --verbose
+@optItem{tail,-v,}
+@optItemx{tail,--verbose,}
Always print file name headers.
-@optZeroTerminated
+@optZeroTerminated{tail}
@end table
@@ -3445,10 +3288,8 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -l @var{lines}
-@itemx --lines=@var{lines}
-@opindex -l
-@opindex --lines
+@optItem{split,-l,@w{ }@var{lines}}
+@optItemx{split,--lines,=@var{lines}}
Put @var{lines} lines of @var{input} into each output file.
If @option{--separator} is specified, then @var{lines} determines
the number of records.
@@ -3457,17 +3298,13 @@ For compatibility @command{split} also supports an obsolete
option syntax @option{-@var{lines}}. New scripts should use
@option{-l @var{lines}} instead.
-@item -b @var{size}
-@itemx --bytes=@var{size}
-@opindex -b
-@opindex --bytes
+@optItem{split,-b,@w{ }@var{size}}
+@optItemx{split,--bytes,=@var{size}}
Put @var{size} bytes of @var{input} into each output file.
@multiplierSuffixes{size}
-@item -C @var{size}
-@itemx --line-bytes=@var{size}
-@opindex -C
-@opindex --line-bytes
+@optItem{split,-C,@w{ }@var{size}}
+@optItemx{split,--line-bytes,=@var{size}}
Put into each output file as many complete lines of @var{input} as
possible without exceeding @var{size} bytes. Individual lines or records
longer than @var{size} bytes are broken into multiple files.
@@ -3475,8 +3312,7 @@ longer than @var{size} bytes are broken into multiple files.
If @option{--separator} is specified, then @var{lines} determines
the number of records.
-@item --filter=@var{command}
-@opindex --filter
+@optItem{split,--filter,=@var{command}}
With this option, rather than simply writing to each output file,
write through a pipe to the specified shell @var{command} for each output file.
@var{command} should use the $FILE environment variable, which is set
@@ -3494,11 +3330,8 @@ xz -dc BIG.xz | split -b200G --filter='xz > $FILE.xz' - big-
Assuming a 10:1 compression ratio, that would create about fifty 20GiB files
with names @file{big-aa.xz}, @file{big-ab.xz}, @file{big-ac.xz}, etc.
-@item -n @var{chunks}
-@itemx --number=@var{chunks}
-@opindex -n
-@opindex --number
-
+@optItem{split,-n,@w{ }@var{chunks}}
+@optItemx{split,--number,=@var{chunks}}
Split @var{input} to @var{chunks} output files where @var{chunks} may be:
@macro Stdout
@@ -3530,25 +3363,24 @@ are not split even if they overlap a partition, the files written
can be larger or smaller than the partition size, and even empty
if a line/record is so long as to completely overlap the partition.
+@vindex TMPDIR
When the input is a pipe or some other special file where the size
cannot easily be determined, there is no trouble for @samp{r} mode
-because the size of the input is irrelevant. For other modes, such an
-input is first copied to a temporary to determine its size.
+because the size of the input is irrelevant. For other modes,
+such an input's size is determined by first copying to @env{$TMPDIR},
+or @file{/tmp} if the @env{TMPDIR} environment variable is not set
+or the location is not available.
-@item -a @var{length}
-@itemx --suffix-length=@var{length}
-@opindex -a
-@opindex --suffix-length
+@optItem{split,-a,@w{ }@var{length}}
+@optItemx{split,--suffix-length,=@var{length}}
Use suffixes of length @var{length}. If a @var{length} of 0 is specified,
this is the same as if (any previous) @option{-a} was not specified, and
thus enables the default behavior, which starts the suffix length at 2,
and unless @option{-n} or @option{--numeric-suffixes=@var{from}} is
specified, will auto increase the length by 2 as required.
-@item -d
-@itemx --numeric-suffixes[=@var{from}]
-@opindex -d
-@opindex --numeric-suffixes
+@optItem{split,-d,}
+@optItemx{split,--numeric-suffixes,[=@var{from}]}
Use digits in suffixes rather than lower-case letters. The numerical
suffix counts from @var{from} if specified, 0 otherwise.
@@ -3560,31 +3392,24 @@ suffixes beyond @samp{99}. If option @option{--number} is specified and
the number of files is less than @var{from}, a single run is assumed and the
minimum suffix length required is automatically determined.
-@item -x
-@itemx --hex-suffixes[=@var{from}]
-@opindex -x
-@opindex --hex-suffixes
+@optItem{split,-x,}
+@optItemx{split,--hex-suffixes,[=@var{from}]}
Like @option{--numeric-suffixes}, but use hexadecimal numbers (in lower case).
-@item --additional-suffix=@var{suffix}
-@opindex --additional-suffix
+@optItem{split,--additional-suffix,=@var{suffix}}
Append an additional @var{suffix} to output file names. @var{suffix}
must not contain slash.
-@item -e
-@itemx --elide-empty-files
-@opindex -e
-@opindex --elide-empty-files
+@optItem{split,-e,}
+@optItemx{split,--elide-empty-files,}
Suppress the generation of zero-length output files. This can happen
with the @option{--number} option if a file is (truncated to be) shorter
than the number requested, or if a line is so long as to completely
span a chunk. The output file sequence numbers, always run consecutively
even when this option is specified.
-@item -t @var{separator}
-@itemx --separator=@var{separator}
-@opindex -t
-@opindex --separator
+@optItem{split,-t,@w{ }@var{separator}}
+@optItemx{split,--separator,=@var{separator}}
@cindex line separator character
@cindex record separator character
Use character @var{separator} as the record separator instead of the default
@@ -3592,15 +3417,12 @@ newline character (ASCII LF).
To specify ASCII NUL as the separator, use the two-character string @samp{\0},
e.g., @samp{split -t '\0'}.
-@item -u
-@itemx --unbuffered
-@opindex -u
-@opindex --unbuffered
+@optItem{split,-u,}
+@optItemx{split,--unbuffered,}
Immediately copy input to output in @option{--number r/@dots{}} mode,
which is a much slower mode of operation.
-@item --verbose
-@opindex --verbose
+@optItem{split,--verbose,}
Write a diagnostic just before each output file is opened.
@end table
@@ -3741,17 +3563,13 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -f @var{prefix}
-@itemx --prefix=@var{prefix}
-@opindex -f
-@opindex --prefix
+@optItem{csplit,-f,@w{ }@var{prefix}}
+@optItemx{csplit,--prefix,=@var{prefix}}
@cindex output file name prefix
Use @var{prefix} as the output file name prefix.
-@item -b @var{format}
-@itemx --suffix-format=@var{format}
-@opindex -b
-@opindex --suffix-format
+@optItem{csplit,-b,@w{ }@var{format}}
+@optItemx{csplit,--suffix-format,=@var{format}}
@cindex output file name suffix
Use @var{format} as the output file name suffix. When this option is
specified, the suffix string must include exactly one
@@ -3766,29 +3584,22 @@ entire @var{format} is given (with the current output file number) to
individual output files in turn. If this option is used, the
@option{--digits} option is ignored.
-@item -n @var{digits}
-@itemx --digits=@var{digits}
-@opindex -n
-@opindex --digits
+@optItem{csplit,-n,@w{ }@var{digits}}
+@optItemx{csplit,--digits,=@var{digits}}
Use output file names containing numbers that are @var{digits} digits
long instead of the default 2.
-@item -k
-@itemx --keep-files
-@opindex -k
-@opindex --keep-files
+@optItem{csplit,-k,}
+@optItemx{csplit,--keep-files,}
Do not remove output files when errors are encountered.
-@item --suppress-matched
-@opindex --suppress-matched
+@optItem{csplit,--suppress-matched,}
Do not output lines matching the specified @var{pattern}.
I.e., suppress the boundary line from the start of the second
and subsequent splits.
-@item -z
-@itemx --elide-empty-files
-@opindex -z
-@opindex --elide-empty-files
+@optItem{csplit,-z,}
+@optItemx{csplit,--elide-empty-files,}
Suppress the generation of zero-length output files. (In cases where
the section delimiters of the input file are supposed to mark the first
lines of each of the sections, the first output file will generally be a
@@ -3796,14 +3607,10 @@ zero-length file unless you use this option.) The output file sequence
numbers always run consecutively starting from 0, even when this option
is specified.
-@item -s
-@itemx -q
-@itemx --silent
-@itemx --quiet
-@opindex -s
-@opindex -q
-@opindex --silent
-@opindex --quiet
+@optItem{csplit,-s,}
+@optItemx{csplit,-q,}
+@optItemx{csplit,--silent,}
+@optItemx{csplit,--quiet,}
Do not print counts of output file sizes.
@end table
@@ -3955,23 +3762,17 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -c
-@itemx --bytes
-@opindex -c
-@opindex --bytes
+@optItem{wc,-c,}
+@optItemx{wc,--bytes,}
Print only the byte counts.
-@item -m
-@itemx --chars
-@opindex -m
-@opindex --chars
+@optItem{wc,-m,}
+@optItemx{wc,--chars,}
Print only the character counts, as per the current locale.
Encoding errors are not counted.
-@item -w
-@itemx --words
-@opindex -w
-@opindex --words
+@optItem{wc,-w,}
+@optItemx{wc,--words,}
Print only the word counts. A word is a nonempty sequence of non white
space delimited by white space characters or by start or end of input.
The current locale determines which characters are white space.
@@ -3984,25 +3785,24 @@ space even if the current locale does not: U+00A0 NO-BREAK SPACE,
U+2007 FIGURE SPACE, U+202F NARROW NO-BREAK SPACE, and U+2060 WORD
JOINER.
-@item -l
-@itemx --lines
-@opindex -l
-@opindex --lines
+@optItem{wc,-l,}
+@optItemx{wc,--lines,}
Print only the newline character counts.
If a file ends in a non-newline character,
its trailing partial line is not counted.
-@item -L
-@itemx --max-line-length
-@opindex -L
-@opindex --max-line-length
+@optItem{wc,--debug,}
+Output extra information to standard error.
+Currently; print the line count acceleration implementation being used.
+
+@optItem{wc,-L,}
+@optItemx{wc,--max-line-length,}
Print only the maximum display widths.
Tabs are set at every 8th column.
Display widths of wide characters are considered.
Non-printable characters are given 0 width.
-@item --total=@var{when}
-@opindex --total=@var{when}
+@optItem{wc,--total,}
Control when and how the final line with cumulative counts is printed.
@var{when} is one of:
@itemize @bullet
@@ -4025,8 +3825,7 @@ to simplify subsequent processing.
@end itemize
@macro filesZeroFromOption{cmd,withTotalOption,subListOutput}
-@item --files0-from=@var{file}
-@opindex --files0-from=@var{file}
+@optItem{\cmd\,--files0-from,=@var{file}}
@c This is commented out to avoid a texi2dvi failure.
@c texi2dvi (GNU Texinfo 4.11) 1.104
@c @cindex including files from @command{\cmd\}
@@ -4086,17 +3885,14 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -r
-@opindex -r
+@optItem{sum,-r,}
@cindex BSD @command{sum}
Use the default (BSD compatible) algorithm. This option is included for
compatibility with the System V @command{sum}. Unless @option{-s} was also
given, it has no effect.
-@item -s
-@itemx --sysv
-@opindex -s
-@opindex --sysv
+@optItem{sum,-s,}
+@optItemx{sum,--sysv,}
@cindex System V @command{sum}
Compute checksums using an algorithm compatible with System V
@command{sum}'s default, and print file sizes in units of 512-byte blocks.
@@ -4194,10 +3990,8 @@ escape sequences are reserved for future use.
@table @samp
-@item -a
-@itemx --algorithm
-@opindex -a
-@opindex --algorithm
+@optItem{cksum,-a,@w{ }@var{type}}
+@optItemx{cksum,--algorithm,=@var{type}}
@cindex digest algorithm
Compute checksums using the specified digest algorithm.
@@ -4219,8 +4013,16 @@ Supported more modern digest algorithms are:
@samp{sm3} only available through @command{cksum}
@end example
-@item --base64
-@opindex --base64
+The following algorithms are currently considered secure against
+malicious tampering, i.e., there is no known way to modify a file to
+produce the same checksum:
+@example
+@samp{sha2} equivalent to @command{sha@{224,256,384,512@}sum}
+@samp{sha3} only available through @command{cksum}
+@samp{blake2b} equivalent to @command{b2sum}
+@end example
+
+@optItem{cksum,--base64,}
@cindex base64 checksum encoding
Print base64-encoded digests not hexadecimal.
This option is ignored with @option{--check}.
@@ -4233,16 +4035,14 @@ modulo 3, and the @option{--check} parser requires precisely the same
input digest string as what is output. I.e., removing or adding any
@samp{=} padding renders a digest non-matching.
-@item --debug
-@opindex --debug
+@optItem{cksum,--debug,}
Output extra information to standard error,
like the checksum implementation being used.
-@item -l
-@itemx --length
-@opindex -l
-@opindex --length
+@optItem{cksum,-l,@w{ }@var{bits}}
+@optItemx{cksum,--length,=@var{bits}}
@cindex BLAKE2 hash length
+@cindex SHA-2 hash length
@cindex SHA-3 hash length
Specify the digest size used with @option{-a sha2, sha3, or blake2b}.
For @samp{blake2b} this is optional, with 512 being the default. If the
@@ -4252,8 +4052,7 @@ option is required, and the @var{length} must be one of 224, 256, 384, or 512.
This option is ignored when @option{--check} is specified,
as the length is automatically determined when checking.
-@item --raw
-@opindex --raw
+@optItem{cksum,--raw,}
@cindex raw binary checksum
Print only the unencoded raw binary digest for a single input.
Do not output the file name or anything else.
@@ -4263,8 +4062,7 @@ This option works only with a single input.
Unlike other output formats, @command{cksum} provides no way to
@option{--check} a @option{--raw} checksum.
-@item --untagged
-@opindex --untagged
+@optItem{cksum,--untagged,}
Output using the original Coreutils format used by the other
standalone checksum utilities like @command{md5sum} for example.
This format has the checksum at the start of the line, and may be
@@ -4279,10 +4077,8 @@ This does not identify the digest algorithm used for the checksum.
@table @samp
-@item -b
-@itemx --binary
-@opindex -b
-@opindex --binary
+@optItem{cksum,-b,}
+@optItemx{cksum,--binary,}
@cindex binary input files
Treat each input file as binary, by reading it in binary mode and
outputting a @samp{*} flag. This is the inverse of @option{--text}.
@@ -4298,8 +4094,8 @@ of the legacy standalone checksumming utilities.
@end macro
@cksumTextMode
-@item -c
-@itemx --check
+@optItem{cksum,-c,}
+@optItemx{cksum,--check,}
Read file names and checksum information (not data) from each
@var{file} (or from standard input if no @var{file} was specified) and report
whether the checksums match the contents of the named files.
@@ -4334,22 +4130,24 @@ By default, for each valid line, one line is written to standard
output indicating whether the named file passed the test.
After all checks have been performed, if there were any failures,
a warning is issued to standard error.
-Use the @option{--status} option to inhibit that output.
+If file names contain problematic characters,
+they will be quoted in a way suitable for POSIX-compatible shells,
+so that any file name is represented unambiguously and safely.
+
+Use the @option{--status} option to inhibit the output described above.
If any listed file cannot be opened or read, if any valid line has
a checksum inconsistent with the associated file, or if no valid
line is found, @command{cksum} exits with nonzero status. Otherwise,
it exits successfully.
-@item --ignore-missing
-@opindex --ignore-missing
+@optItem{cksum,--ignore-missing,}
@cindex verifying checksums
This option is useful only when verifying checksums.
When verifying checksums, don't fail or report any status
for missing files. This is useful when verifying a subset
of downloaded files given a larger list of checksums.
-@item --quiet
-@opindex --quiet
+@optItem{cksum,--quiet,}
@cindex verifying checksums
This option is useful only when verifying checksums.
When verifying checksums, don't generate an 'OK' message per successfully
@@ -4357,8 +4155,7 @@ checked file. Files that fail the verification are reported in the
default one-line-per-file format. If there is any checksum mismatch,
print a warning summarizing the failures to standard error.
-@item --status
-@opindex --status
+@optItem{cksum,--status,}
@cindex verifying checksums
This option is useful only when verifying checksums.
When verifying checksums, don't generate the default one-line-per-file
@@ -4369,8 +4166,7 @@ If all listed files are readable and are consistent with the associated
checksums, exit successfully. Otherwise exit with a status code
indicating there was a failure.
-@item --tag
-@opindex --tag
+@optItem{cksum,--tag,}
@cindex BSD output
Output BSD style checksums, which indicate the checksum algorithm used.
As a GNU extension, if @option{--zero} is not used, file names with problematic
@@ -4382,10 +4178,8 @@ the output format, while providing little benefit.
@xref{cksum output modes} for details of this format.
The @command{cksum} command, uses @option{--tag} as its default output format.
-@item -t
-@itemx --text
-@opindex -t
-@opindex --text
+@optItem{cksum,-t,}
+@optItemx{cksum,--text,}
@cindex text input files
Treat each input file as text, by reading it in text mode and
outputting a @samp{ } flag. This is the inverse of @option{--binary}.
@@ -4395,23 +4189,20 @@ the default for reading standard input when standard input is a
terminal. This mode is never defaulted to if @option{--tag} is used.
@cksumTextMode
-@item -w
-@itemx --warn
-@opindex -w
-@opindex --warn
+@optItem{cksum,-w,}
+@optItemx{cksum,--warn,}
@cindex verifying checksums
When verifying checksums, warn about improperly formatted checksum lines.
This option is useful only if all but a few lines in the checked input
are valid.
-@item --strict
-@opindex --strict
+@optItem{cksum,--strict,}
@cindex verifying checksums
When verifying checksums,
if one or more input line is invalid,
exit nonzero after all warnings have been issued.
-@optZero
+@optZero{cksum}
Also file name escaping is not used.
@end table
@@ -4428,6 +4219,12 @@ Also file name escaping is not used.
@command{md5sum} computes a 128-bit checksum (or @dfn{fingerprint} or
@dfn{message-digest}) for each specified @var{file}.
+@macro legacyDigest
+This is a legacy interface to the more modern @command{cksum} utility.
+@xref{cksum invocation}.
+@end macro
+@legacyDigest
+
@macro weakHash{hash}
The \hash\ digest is more reliable than a simple CRC (provided by
the @command{cksum} command) for detecting accidental file corruption,
@@ -4439,7 +4236,6 @@ to modify certain files, including digital certificates, so that they
appear valid when signed with an \hash\ digest. For more secure hashes,
consider using @samp{sha2}, @samp{sha3}, or @samp{blake2b},
available through the @command{cksum} @option{--algorithm} option.
-@xref{cksum invocation}.
@end macro
@weakHash{MD5}
@@ -4476,16 +4272,16 @@ The program accepts @ref{cksum common options}. Also see @ref{Common options}.
@command{b2sum} computes a 512-bit checksum for each specified
@var{file}.
+@legacyDigest
+
@checksumUsage{b2sum}
In addition @command{b2sum} supports the following options.
@table @samp
-@item -l
-@itemx --length
-@opindex -l
-@opindex --length
+@optItem{b2sum,-l,@w{ }@var{bits}}
+@optItemx{b2sum,--length,=@var{bits}}
@cindex BLAKE2 hash length
Specify the digest size used by the algorithm. This option is optional.
By default a 512 bit digest will be used. If the option is given it
@@ -4509,6 +4305,8 @@ as the length is automatically determined when checking.
@command{sha1sum} computes a 160-bit checksum for each specified @var{file}.
+@legacyDigest
+
@weakHash{SHA-1}
@checksumUsage{sha1sum}
@@ -4516,6 +4314,8 @@ as the length is automatically determined when checking.
@node sha2 utilities
@section sha2 utilities: Print or check SHA-2 digests
+@legacyDigest
+
@pindex sha224sum
@pindex sha256sum
@pindex sha384sum
@@ -4621,11 +4421,9 @@ mode:
@table @samp
-@item -c
-@itemx --check
+@optItem{sort,-c,}
+@optItemx{sort,--check,}
@itemx --check=diagnose-first
-@opindex -c
-@opindex --check
@cindex checking whether a file is sorted
Check whether the given file is already sorted: if it is not all
sorted, print a diagnostic containing the first out-of-order line and
@@ -4633,10 +4431,9 @@ exit with a status of 1.
Otherwise, exit successfully.
At most one input file can be given.
-@item -C
+@optItem{sort,-C,}
@itemx --check=quiet
@itemx --check=silent
-@opindex -c
@opindex --check
@cindex checking whether a file is sorted
Exit successfully if the given file is already sorted, and
@@ -4644,10 +4441,8 @@ exit with status 1 otherwise.
At most one input file can be given.
This is like @option{-c}, except it does not print a diagnostic.
-@item -m
-@itemx --merge
-@opindex -m
-@opindex --merge
+@optItem{sort,-m,}
+@optItemx{sort,--merge,}
@cindex merging sorted files
Merge the given files by sorting them as a group. Each input file must
always be individually sorted. It always works to sort instead of
@@ -4681,10 +4476,8 @@ so portable shell scripts should specify global options first.
@table @samp
-@item -b
-@itemx --ignore-leading-blanks
-@opindex -b
-@opindex --ignore-leading-blanks
+@optItem{sort,-b,}
+@optItemx{sort,--ignore-leading-blanks,}
@cindex blanks, ignoring leading
@vindex LC_CTYPE
Ignore leading blanks when finding sort keys in each line.
@@ -4693,10 +4486,8 @@ can change this. Blanks may be ignored by your locale's collating
rules, but without this option they will be significant for character
positions specified in keys with the @option{-k} option.
-@item -d
-@itemx --dictionary-order
-@opindex -d
-@opindex --dictionary-order
+@optItem{sort,-d,}
+@optItemx{sort,--dictionary-order,}
@cindex dictionary order
@cindex phone directory order
@cindex telephone directory order
@@ -4706,10 +4497,8 @@ letters, digits and blanks when sorting.
By default letters and digits are those of ASCII and a blank
is a space or a tab, but the @env{LC_CTYPE} locale can change this.
-@item -f
-@itemx --ignore-case
-@opindex -f
-@opindex --ignore-case
+@optItem{sort,-f,}
+@optItemx{sort,--ignore-case,}
@cindex ignoring case
@cindex case folding
@vindex LC_CTYPE
@@ -4721,12 +4510,9 @@ thrown away. (There is currently no way to throw away the upper case
equivalent instead. (Any @option{--reverse} given would only affect
the final result, after the throwing away.))
-@item -g
-@itemx --general-numeric-sort
-@itemx --sort=general-numeric
-@opindex -g
-@opindex --general-numeric-sort
-@opindex --sort
+@optItem{sort,-g,}
+@optItemx{sort,--general-numeric-sort,}
+@optItemx{sort,--sort,=general-numeric}
@cindex general numeric sort
@vindex LC_NUMERIC
Sort numerically, converting a prefix of each line to a long
@@ -4758,11 +4544,9 @@ or of varying case. However for hex numbers of consistent case,
and left padded with @samp{0} to a consistent width, a standard
lexicographic sort will be faster.
-@item -h
-@itemx --human-numeric-sort
+@optItem{sort,-h,}
+@optItemx{sort,--human-numeric-sort,}
@itemx --sort=human-numeric
-@opindex -h
-@opindex --human-numeric-sort
@opindex --sort
@cindex human numeric sort
@vindex LC_NUMERIC
@@ -4781,10 +4565,8 @@ option; the SI suffix must immediately follow the number.
To sort more accurately, you can use the @command{numfmt} command
to reformat numbers to human format @emph{after} the sort.
-@item -i
-@itemx --ignore-nonprinting
-@opindex -i
-@opindex --ignore-nonprinting
+@optItem{sort,-i,}
+@optItemx{sort,--ignore-nonprinting,}
@cindex nonprinting characters, ignoring
@cindex unprintable characters, ignoring
@vindex LC_CTYPE
@@ -4793,11 +4575,9 @@ The @env{LC_CTYPE} locale determines character types.
This option has no effect if the stronger @option{--dictionary-order}
(@option{-d}) option is also given.
-@item -M
-@itemx --month-sort
+@optItem{sort,-M,}
+@optItemx{sort,--month-sort,}
@itemx --sort=month
-@opindex -M
-@opindex --month-sort
@opindex --sort
@cindex months, sorting by
@vindex LC_TIME
@@ -4809,11 +4589,9 @@ category determines the month spellings.
By default a blank is a space or a tab, but the @env{LC_CTYPE} locale
can change this.
-@item -n
-@itemx --numeric-sort
+@optItem{sort,-n,}
+@optItemx{sort,--numeric-sort,}
@itemx --sort=numeric
-@opindex -n
-@opindex --numeric-sort
@opindex --sort
@cindex numeric sort
@vindex LC_CTYPE
@@ -4836,28 +4614,24 @@ Neither a leading @samp{+} nor exponential notation is recognized.
To compare such strings numerically, use the
@option{--general-numeric-sort} (@option{-g}) option.
-@item -V
-@itemx --version-sort
-@opindex -V
-@opindex --version-sort
+@optItem{sort,-V,}
+@optItemx{sort,--version-sort,}
+@itemx --sort=version
+@opindex --sort
@cindex version number sort
Sort by version name and number. It behaves like a standard sort,
except that each sequence of decimal digits is treated numerically
as an index/version number. (@xref{Version sort ordering}.)
-@item -r
-@itemx --reverse
-@opindex -r
-@opindex --reverse
+@optItem{sort,-r,}
+@optItemx{sort,--reverse,}
@cindex reverse sorting
Reverse the result of comparison, so that lines with greater key values
appear earlier in the output instead of later.
-@item -R
-@itemx --random-sort
+@optItem{sort,-R,}
+@optItemx{sort,--random-sort,}
@itemx --sort=random
-@opindex -R
-@opindex --random-sort
@opindex --sort
@cindex random sort
Sort by hashing the input keys and then sorting the hash values.
@@ -4880,7 +4654,7 @@ Other options are:
@table @samp
-@item --compress-program=@var{prog}
+@optItem{sort,--compress-program,@w{ }@var{prog}}
Compress any temporary files with the program @var{prog}.
With no arguments, @var{prog} must compress standard input to standard
@@ -4898,10 +4672,8 @@ White space and the backslash character should not appear in
@filesZeroFromOption{sort,,sorted output}
-@item -k @var{pos1}[,@var{pos2}]
-@itemx --key=@var{pos1}[,@var{pos2}]
-@opindex -k
-@opindex --key
+@optItem{sort,-k,@w{ }@var{pos1}[@comma{}@var{pos2}]}
+@optItemx{sort,--key,=@var{pos1}[@comma{}@var{pos2}]}
@cindex sort field
Specify a sort field that consists of the part of the line between
@var{pos1} and @var{pos2} (or the end of the line, if @var{pos2} is
@@ -4930,12 +4702,11 @@ Example: To sort on the second field, use @option{--key=2,2}
See also the @option{--debug} option to help determine the part
of the line being used in the sort.
-@item --debug
+@optItem{sort,--debug,}
Highlight the portion of each line used for sorting.
Also issue warnings about questionable usage to standard error.
-@item --batch-size=@var{nmerge}
-@opindex --batch-size
+@optItem{sort,--batch-size,=@var{nmerge}}
@cindex number of inputs to merge, nmerge
Merge at most @var{nmerge} inputs at once.
@@ -4961,10 +4732,8 @@ the operating system has other limits on the number of open files. If
the value of @var{nmerge} exceeds the resource limit, @command{sort}
silently uses a smaller value.
-@item -o @var{output-file}
-@itemx --output=@var{output-file}
-@opindex -o
-@opindex --output
+@optItem{sort,-o,@w{ }@var{output-file}}
+@optItemx{sort,--output,=@var{output-file}}
@cindex overwriting of input, allowed
Write output to @var{output-file} instead of standard output.
Normally, @command{sort} reads all input before opening
@@ -4984,17 +4753,14 @@ On newer systems, @option{-o} cannot appear after an input file if
scripts should specify @option{-o @var{output-file}} before any input
files.
-@item --random-source=@var{file}
-@opindex --random-source
+@optItem{sort,--random-source,=@var{file}}
@cindex random source for sorting
Use @var{file} as a source of random data used to determine which
random hash function to use with the @option{-R} option. @xref{Random
sources}.
-@item -s
-@itemx --stable
-@opindex -s
-@opindex --stable
+@optItem{sort,-s,}
+@optItemx{sort,--stable,}
@cindex sort stability
@cindex sort's last-resort comparison
@@ -5002,10 +4768,8 @@ Make @command{sort} stable by disabling its last-resort comparison.
This option has no effect if no fields or global ordering options
other than @option{--reverse} (@option{-r}) are specified.
-@item -S @var{size}
-@itemx --buffer-size=@var{size}
-@opindex -S
-@opindex --buffer-size
+@optItem{sort,-S,@w{ }@var{size}}
+@optItemx{sort,--buffer-size,=@var{size}}
@cindex size for main memory sorting
Use a main-memory sort buffer of the given @var{size}. By default,
@var{size} is in units of 1024 bytes. Appending @samp{%} causes
@@ -5023,10 +4787,8 @@ However, this option affects only the initial buffer size. The buffer
grows beyond @var{size} if @command{sort} encounters input lines larger
than @var{size}.
-@item -t @var{separator}
-@itemx --field-separator=@var{separator}
-@opindex -t
-@opindex --field-separator
+@optItem{sort,-t,@w{ }@var{separator}}
+@optItemx{sort,--field-separator,=@var{separator}}
@cindex field separator character
Use character @var{separator} as the field separator when finding the
sort keys in each line. By default, fields are separated by the empty
@@ -5046,10 +4808,8 @@ retain the field separators present between the endpoints of the range.
To specify ASCII NUL as the field separator,
use the two-character string @samp{\0}, e.g., @samp{sort -t '\0'}.
-@item -T @var{tempdir}
-@itemx --temporary-directory=@var{tempdir}
-@opindex -T
-@opindex --temporary-directory
+@optItem{sort,-T,@w{ }@var{tempdir}}
+@optItemx{sort,--temporary-directory,=@var{tempdir}}
@cindex temporary directory
@vindex TMPDIR
Use directory @var{tempdir} to store temporary files, overriding the
@@ -5059,8 +4819,7 @@ have a large sort or merge that is I/O-bound, you can often improve
performance by using this option to specify directories on different
file systems.
-@item --parallel=@var{n}
-@opindex --parallel
+@optItem{sort,--parallel,=@var{n}}
@cindex multithreaded sort
Set the number of sorts run in parallel to @var{n}. By default,
@var{n} is set to the number of available processors, but limited
@@ -5068,10 +4827,8 @@ to 8, as performance gains diminish after that.
Using @var{n} threads increases the memory usage by
a factor of log @var{n}. Also see @ref{nproc invocation}.
-@item -u
-@itemx --unique
-@opindex -u
-@opindex --unique
+@optItem{sort,-u,}
+@optItemx{sort,--unique,}
@cindex uniquifying output
Normally, output only the first of a sequence of lines that compare
@@ -5086,7 +4843,7 @@ For example, @code{sort -n -u} inspects only the value of the initial
numeric string when checking for uniqueness, whereas @code{sort -n |
uniq} inspects the entire line. @xref{uniq invocation}.
-@optZeroTerminated
+@optZeroTerminated{sort}
@macro newlineFieldSeparator
With @option{-z} the newline character is treated as a field separator.
@end macro
@@ -5332,17 +5089,13 @@ input. The following options change the operation mode:
@table @samp
-@item -e
-@itemx --echo
-@opindex -c
-@opindex --echo
+@optItem{shuf,-e,}
+@optItemx{shuf,--echo,}
@cindex command-line operands to shuffle
Treat each command-line operand as an input line.
-@item -i @var{lo}-@var{hi}
-@itemx --input-range=@var{lo}-@var{hi}
-@opindex -i
-@opindex --input-range
+@optItem{shuf,-i,@w{ }@var{lo}-@var{hi}}
+@optItemx{shuf,--input-range,=@var{lo}-@var{hi}}
@cindex input range to shuffle
Act as if input came from a file containing the range of unsigned
decimal integers @var{lo}@dots{}@var{hi}, one per line.
@@ -5354,34 +5107,27 @@ operation modes:
@table @samp
-@item -n @var{count}
-@itemx --head-count=@var{count}
-@opindex -n
-@opindex --head-count
+@optItem{shuf,-n,@w{ }@var{count}}
+@optItemx{shuf,--head-count,=@var{count}}
@cindex head of output
Output at most @var{count} lines. By default, all input lines are
output.
-@item -o @var{output-file}
-@itemx --output=@var{output-file}
-@opindex -o
-@opindex --output
+@optItem{shuf,-o,@w{ }@var{output-file}}
+@optItemx{shuf,--output,=@var{output-file}}
@cindex overwriting of input, allowed
Write output to @var{output-file} instead of standard output.
@command{shuf} reads all input before opening
@var{output-file}, so you can safely shuffle a file in place by using
commands like @code{shuf -o F <F} and @code{cat F | shuf -o F}.
-@item --random-source=@var{file}
-@opindex --random-source
+@optItem{shuf,--random-source,=@var{file}}
@cindex random source for shuffling
Use @var{file} as a source of random data used to determine which
permutation to generate. @xref{Random sources}.
-@item -r
-@itemx --repeat
-@opindex -r
-@opindex --repeat
+@optItem{shuf,-r,}
+@optItemx{shuf,--repeat,}
@cindex repeat output values
Repeat output values, that is, select with replacement. With this
option the output is not a permutation of the input; instead, each
@@ -5390,7 +5136,7 @@ typically combined with @option{--head-count}; if
@option{--head-count} is not given, @command{shuf} repeats
indefinitely.
-@optZeroTerminated
+@optZeroTerminated{shuf}
@end table
@@ -5501,10 +5247,8 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -f @var{n}
-@itemx --skip-fields=@var{n}
-@opindex -f
-@opindex --skip-fields
+@optItem{uniq,-f,@w{ }@var{n}}
+@optItemx{uniq,--skip-fields,=@var{n}}
Skip @var{n} fields on each line before checking for uniqueness. Use
a null string for comparison if a line has fewer than @var{n} fields.
Fields are a sequence of blank characters followed by non-blank characters.
@@ -5514,10 +5258,8 @@ field (which may optionally have leading blanks).
For compatibility @command{uniq} supports a traditional option syntax
@option{-@var{n}}. New scripts should use @option{-f @var{n}} instead.
-@item -s @var{n}
-@itemx --skip-chars=@var{n}
-@opindex -s
-@opindex --skip-chars
+@optItem{uniq,-s,@w{ }@var{n}}
+@optItemx{uniq,--skip-chars,=@var{n}}
Skip @var{n} characters before checking for uniqueness. Use a null string
for comparison if a line has fewer than @var{n} characters. If you use both
the field and character skipping options, fields are skipped over first.
@@ -5533,31 +5275,23 @@ behavior depends on this variable.
For example, use @samp{uniq ./+10} or @samp{uniq -s 10} rather than
the ambiguous @samp{uniq +10}.
-@item -c
-@itemx --count
-@opindex -c
-@opindex --count
+@optItem{uniq,-c,}
+@optItemx{uniq,--count,}
Print the number of times each line occurred along with the line.
-@item -i
-@itemx --ignore-case
-@opindex -i
-@opindex --ignore-case
+@optItem{uniq,-i,}
+@optItemx{uniq,--ignore-case,}
Ignore differences in case when comparing lines.
-@item -d
-@itemx --repeated
-@opindex -d
-@opindex --repeated
+@optItem{uniq,-d,}
+@optItemx{uniq,--repeated,}
@cindex repeated lines, outputting
Discard lines that are not repeated. When used by itself, this option
causes @command{uniq} to print the first copy of each repeated line,
and nothing else.
-@item -D
-@itemx --all-repeated[=@var{delimit-method}]
-@opindex -D
-@opindex --all-repeated
+@optItem{uniq,-D,}
+@optItemx{uniq,--all-repeated,[=@var{delimit-method}]}
@cindex all repeated lines, outputting
Do not discard the second and subsequent repeated input lines,
but discard lines that are not repeated.
@@ -5600,8 +5334,7 @@ remove blank lines.
This is a GNU extension.
@c FIXME: give an example showing *how* it's useful
-@item --group[=@var{delimit-method}]
-@opindex --group
+@optItem{uniq,--group,[=@var{delimit-method}]}
@cindex all lines, grouping
Output all lines, and delimit each unique group.
@nulOutputNote
@@ -5629,24 +5362,20 @@ Output a delimiter around each group of unique items.
This is a GNU extension.
-@item -u
-@itemx --unique
-@opindex -u
-@opindex --unique
+@optItem{uniq,-u,}
+@optItemx{uniq,--unique,}
@cindex unique lines, outputting
Discard the last line that would be output for a repeated input group.
When used by itself, this option causes @command{uniq} to print unique
lines, and nothing else.
-@item -w @var{n}
-@itemx --check-chars=@var{n}
-@opindex -w
-@opindex --check-chars
+@optItem{uniq,-w,@w{ }@var{n}}
+@optItemx{uniq,--check-chars,=@var{n}}
Compare at most @var{n} characters on each line (after skipping any specified
fields and characters). By default the entire rest of the lines are
compared.
-@optZeroTerminated
+@optZeroTerminated{uniq}
@newlineFieldSeparator
@end table
@@ -5685,6 +5414,9 @@ Columns are separated by a single TAB character.
@c FIXME: when there's an option to supply an alternative separator
@c string, append "by default" to the above sentence.
+@optAnchor{comm,-1}
+@optAnchor{comm,-2}
+@optAnchor{comm,-3}
@opindex -1
@opindex -2
@opindex -3
@@ -5720,22 +5452,22 @@ probably not correspond with whatever you hoped it would be.
@table @samp
-@item --check-order
+@optItem{comm,--check-order,}
Fail with an error message if either input file is wrongly ordered.
-@item --nocheck-order
+@optItem{comm,--nocheck-order,}
Do not check that both input files are in sorted order.
Other options are:
-@item --output-delimiter=@var{str}
+@optItem{comm,--output-delimiter,=@var{str}}
Print @var{str} between adjacent output columns,
rather than the default of a single TAB character.
The delimiter @var{str} may be empty, in which case
the ASCII NUL character is used to delimit output columns.
-@item --total
+@optItem{comm,--total,}
Output a summary at the end.
Similar to the regular output,
@@ -5766,10 +5498,16 @@ $ comm -12 file1 file2 | wc -l # number of lines common to both files
4
@end example
-@optZeroTerminated
+@optZeroTerminated{comm}
@end table
+POSIX leaves the behavior undefined when @var{file1} and @var{file2}
+both refer to standard input, i.e., when both are @samp{-}. GNU
+@command{comm} reads the first line as if it came from @var{file1} and
+the second as if it were from @var{file2}, and continues alternating in
+this manner until the end of file is reached.
+
@node ptx invocation
@section @command{ptx}: Produce permuted indexes
@@ -5834,8 +5572,8 @@ convention more than once per program invocation.
@table @samp
-@item -G
-@itemx --traditional
+@optItem{ptx,-G,}
+@optItemx{ptx,--traditional,}
As already explained, this option disables all GNU extensions to
@command{ptx} and switches to traditional mode.
@@ -5870,10 +5608,8 @@ correctly.
@table @samp
-@item -f
-@itemx --ignore-case
-@opindex -f
-@opindex --ignore-case
+@optItem{ptx,-f,}
+@optItemx{ptx,--ignore-case,}
Fold lower case letters to upper case for sorting.
@end table
@@ -5884,14 +5620,11 @@ Fold lower case letters to upper case for sorting.
@table @samp
-@item -b @var{file}
-@itemx --break-file=@var{file}
-@opindex -b
-@opindex --break-file
-
+@optItem{ptx,-b,@w{ }@var{file}}
+@optItemx{ptx,--break-file,=@var{file}}
This option provides an alternative (to @option{-W}) method of describing
which characters make up words. It introduces the name of a
-file which contains a list of characters which can@emph{not} be part of
+@var{file} which contains a list of characters which can@emph{not} be part of
one word; this file is called the @dfn{Break file}. Any character which
is not part of the Break file is a word constituent. If both options
@option{-b} and @option{-W} are specified, then @option{-W} has precedence and
@@ -5903,23 +5636,17 @@ newline at all, not even at the end of the file. When GNU extensions
are disabled, spaces, tabs and newlines are always considered as break
characters even if not included in the Break file.
-@item -i @var{file}
-@itemx --ignore-file=@var{file}
-@opindex -i
-@opindex --ignore-file
-
-The file associated with this option contains a list of words which will
+@optItem{ptx,-i,@w{ }@var{file}}
+@optItemx{ptx,--ignore-file,=@var{file}}
+The @var{file} associated with this option contains a list of words which will
never be taken as keywords in concordance output. It is called the
@dfn{Ignore file}. The file contains exactly one word in each line; the
end of line separation of words is not subject to the value of the
@option{-S} option.
-@item -o @var{file}
-@itemx --only-file=@var{file}
-@opindex -o
-@opindex --only-file
-
-The file associated with this option contains a list of words which will
+@optItem{ptx,-o,@w{ }@var{file}}
+@optItemx{ptx,--only-file,=@var{file}}
+The @var{file} associated with this option contains a list of words which will
be retained in concordance output; any word not mentioned in this file
is ignored. The file is called the @dfn{Only file}. The file contains
exactly one word in each line; the end of line separation of words is
@@ -5929,11 +5656,8 @@ There is no default for the Only file. When both an Only file and an
Ignore file are specified, a word is considered a keyword only
if it is listed in the Only file and not in the Ignore file.
-@item -r
-@itemx --references
-@opindex -r
-@opindex --references
-
+@optItem{ptx,-r,}
+@optItemx{ptx,--references,}
On each input line, the leading sequence of non-white space characters will be
taken to be a reference that has the purpose of identifying this input
line in the resulting permuted index.
@@ -5948,17 +5672,14 @@ references from contexts in output, but it succeeds in doing so
are disabled, this condition is always met and references are completely
excluded from the output contexts.
-@item -S @var{regexp}
-@itemx --sentence-regexp=@var{regexp}
-@opindex -S
-@opindex --sentence-regexp
-
+@optItem{ptx,-S,@w{ }@var{regexp}}
+@optItemx{ptx,--sentence-regexp,=@var{regexp}}
This option selects which regular expression will describe the end of a
line or the end of a sentence. In fact, this regular expression is not
the only distinction between end of lines or end of sentences, and input
line boundaries have no special significance outside this option. By
default, when GNU extensions are enabled and if @option{-r} option is not
-used, end of sentences are used. In this case, this @var{regex} is
+used, end of sentences are used. In this case, this @var{regexp} is
imported from GNU Emacs:
@example
@@ -5993,11 +5714,8 @@ As a matter of convenience to the user, many usual backslashed escape
sequences from the C language are recognized and converted to the
corresponding characters by @command{ptx} itself.
-@item -W @var{regexp}
-@itemx --word-regexp=@var{regexp}
-@opindex -W
-@opindex --word-regexp
-
+@optItem{ptx,-W,@w{ }@var{regexp}}
+@optItemx{ptx,--word-regexp,=@var{regexp}}
This option selects which regular expression will describe each keyword.
By default, if GNU extensions are enabled, a word is a sequence of
letters; the @var{regexp} used is @samp{\w+}. When GNU extensions are
@@ -6039,19 +5757,19 @@ Output format is further controlled by the following options.
@table @samp
-@item -g @var{number}
-@itemx --gap-size=@var{number}
-@opindex -g
-@opindex --gap-size
-
+@optItem{ptx,-g,@w{ }@var{number}}
+@optItemx{ptx,--gap-size,=@var{number}}
Select the size of the minimum white space gap between the fields on the
output line.
-@item -w @var{number}
-@itemx --width=@var{number}
-@opindex -w
-@opindex --width
+@optItem{ptx,-t,}
+@optItemx{ptx,--typeset-mode,}
+Prepare the output for a phototypesetter.
+I.e., change the default output width from 72 to 100 columns.
+This is equivalent to @option{--width=100}.
+@optItem{ptx,-w,@w{ }@var{number}}
+@optItemx{ptx,--width,=@var{number}}
Select the maximum output width of each final line. If references are
used, they are included or excluded from the maximum output width
depending on the value of option @option{-R}@. If this option is not
@@ -6062,11 +5780,8 @@ output after the right context, the maximum output width does not take
into account the space taken by references, nor the gap that precedes
them.
-@item -A
-@itemx --auto-reference
-@opindex -A
-@opindex --auto-reference
-
+@optItem{ptx,-A,}
+@optItemx{ptx,--auto-reference,}
Select automatic references. Each input line will have an automatic
reference made up of the file name and the line ordinal, with a single
colon between them. However, the file name will be empty when standard
@@ -6074,11 +5789,8 @@ input is being read. If both @option{-A} and @option{-r} are selected, then
the input reference is still read and skipped, but the automatic
reference is used at output time, overriding the input reference.
-@item -R
-@itemx --right-side-refs
-@opindex -R
-@opindex --right-side-refs
-
+@optItem{ptx,-R,}
+@optItemx{ptx,--right-side-refs,}
In the default output format, when option @option{-R} is not used, any
references produced by the effect of options @option{-r} or @option{-A} are
placed to the far right of output lines, after the right context. With
@@ -6091,11 +5803,8 @@ is @emph{not} taken into account in total output width given by @option{-w}.
This option is automatically selected whenever GNU extensions are
disabled.
-@item -F @var{string}
-@itemx --flag-truncation=@var{string}
-@opindex -F
-@opindex --flag-truncation
-
+@optItem{ptx,-F,@w{ }@var{string}}
+@optItemx{ptx,--flag-truncation,=@var{string}}
This option will request that any truncation in the output be reported
using the string @var{string}. Most output fields theoretically extend
towards the beginning or the end of the current line, or current
@@ -6115,19 +5824,13 @@ As a matter of convenience to the user, many usual backslashed escape
sequences, as found in the C language, are recognized and converted to
the corresponding characters by @command{ptx} itself.
-@item -M @var{string}
-@itemx --macro-name=@var{string}
-@opindex -M
-@opindex --macro-name
-
+@optItem{ptx,-M,@w{ }@var{string}}
+@optItemx{ptx,--macro-name,=@var{string}}
Select another @var{string} to be used instead of @samp{xx}, while
generating output suitable for @command{nroff}, @command{troff} or @TeX{}.
-@item -O
-@itemx --format=roff
-@opindex -O
-@opindex --format=roff
-
+@optItem{ptx,-O,}
+@optItemx{ptx,--format,=roff}
Choose an output format suitable for @command{nroff} or @command{troff}
processing. Each output line will look like:
@@ -6146,9 +5849,8 @@ tab, is merely changed to exactly one space, with no special attempt to
compress consecutive spaces. Each quote character @samp{"} is doubled
so it will be correctly processed by @command{nroff} or @command{troff}.
-@item -T
+@optItem{ptx,-T,}
@itemx --format=tex
-@opindex -T
@opindex --format=tex
Choose an output format suitable for @TeX{} processing. Each output
@@ -6456,32 +6158,45 @@ options}.
@table @samp
-@item -b @var{byte-list}
-@itemx --bytes=@var{byte-list}
-@opindex -b
-@opindex --bytes
+@optItem{cut,-b,@w{ }@var{byte-list}}
+@optItemx{cut,--bytes,=@var{byte-list}}
Select for printing only the bytes in positions listed in
@var{byte-list}. Tabs and backspaces are treated like any other
character; they take up 1 byte. If an output delimiter is specified,
(see the description of @option{--output-delimiter}), then output that
string between ranges of selected bytes.
-@item -c @var{character-list}
-@itemx --characters=@var{character-list}
-@opindex -c
-@opindex --characters
+@optItem{cut,-c,@w{ }@var{character-list}}
+@optItemx{cut,--characters,=@var{character-list}}
Select for printing only the characters in positions listed in
-@var{character-list}. The same as @option{-b} for now, but
-internationalization will change that. Tabs and backspaces are
-treated like any other character; they take up 1 character. If an
-output delimiter is specified, (see the description of
+@var{character-list}. Tabs and backspaces are
+treated like any other character; they take up 1 character.
+Combining characters are considered as separate characters.
+If an output delimiter is specified, (see the description of
@option{--output-delimiter}), then output that string between ranges
-of selected bytes.
+of selected characters.
-@item -f @var{field-list}
-@itemx --fields=@var{field-list}
-@opindex -f
-@opindex --fields
+@optItem{cut,--complement,}
+This option is a GNU extension.
+Select for printing the complement of the bytes, characters or fields
+selected with the @option{-b}, @option{-c} or @option{-f} options.
+In other words, do @emph{not} print the bytes, characters or fields
+specified via those options. This option is useful when you have
+many fields and want to print all but a few of them.
+
+@optItem{cut,-d,@w{ }@var{character}}
+@optItemx{cut,--delimiter,=@var{character}}
+With @option{-f}, use @var{character} as
+the input field separator (default is TAB).
+If an empty delimiter @var{character} is specified then the ASCII NUL
+character is used to delimit the fields.
+Note the delimiter @var{character} can be the same as the
+line terminator character, which can be used to select ranges of lines.
+In this case the last line terminator in the input is not treated
+as a field separator, which is significant with the @option{-s} option.
+
+@optItem{cut,-f,@w{ }@var{field-list}}
+@optItemx{cut,--fields,=@var{field-list}}
Select for printing only the fields listed in @var{field-list}.
Fields are separated by a TAB character by default. Also print any
line that contains no delimiter character, unless the
@@ -6511,43 +6226,43 @@ join -a1 -o 1.2,1.1 - /dev/null # reorder the first two fields
@end verbatim
@end example
-@item -d @var{input_delim_byte}
-@itemx --delimiter=@var{input_delim_byte}
-@opindex -d
-@opindex --delimiter
-With @option{-f}, use the first byte of @var{input_delim_byte} as
-the input fields separator (default is TAB).
+@optItem{cut,-F,}
+Like the @option{-f,--fields} option, but also implies
+@option{-w} and @option{--output-delimitor=' '}.
+I.e., fields are separated by runs of blank characters,
+and output fields are separated by a single ASCII space.
-@item -n
-@opindex -n
-Do not split multi-byte characters (no-op for now).
+@optItem{cut,-n,}
+@optItemx{cut,--no-partial,}
+With @option{--bytes}, do not split multi-byte characters.
+A byte range must encompass the end of a multi-byte character
+for it to be selected.
-@item -s
-@itemx --only-delimited
-@opindex -s
-@opindex --only-delimited
-For @option{-f}, do not print lines that do not contain the field separator
-character. Normally, any line without a field separator is printed verbatim.
-
-@item --output-delimiter=@var{output_delim_string}
-@opindex --output-delimiter
-With @option{-f}, output fields are separated by @var{output_delim_string}.
-The default with @option{-f} is to use the input delimiter.
+@optItem{cut,-O,@w{ }@var{string}}
+@optItemx{cut,--output-delimiter,=@var{string}}
+With @option{-f}, output fields are separated by @var{string}.
+The default with @option{-f} is to use the input delimiter,
+or with @option{-w} the TAB character.
When using @option{-b} or @option{-c} to select ranges of byte or
character offsets (as opposed to ranges of fields),
output @var{output_delim_string} between non-overlapping
ranges of selected bytes.
-@item --complement
-@opindex --complement
-This option is a GNU extension.
-Select for printing the complement of the bytes, characters or fields
-selected with the @option{-b}, @option{-c} or @option{-f} options.
-In other words, do @emph{not} print the bytes, characters or fields
-specified via those options. This option is useful when you have
-many fields and want to print all but a few of them.
+@optItem{cut,-s,}
+@optItemx{cut,--only-delimited,}
+For @option{-f}, do not print lines that do not contain field separators.
+Normally, any line without a field separator is printed verbatim.
+With @option{--whitespace-delimited=trimmed}, lines that contain
+only leading or trailing blanks will be suppressed.
+
+@optItem{cut,-w,}
+@optItemx{cut,--whitespace-delimited,[=trimmed]}
+With @option{-f}, separate fields with a run of blank characters
+(usually space or TAB). With @samp{trimmed} do not consider
+leading and trailing blanks as field separators.
+@option{-w} is implied with the @option{-F} option.
-@optZeroTerminated
+@optZeroTerminated{cut}
@end table
@@ -6561,7 +6276,10 @@ many fields and want to print all but a few of them.
@cindex merging files
@command{paste} writes to standard output lines consisting of sequentially
-corresponding lines of each given file, separated by a TAB character.
+corresponding lines of each given file, separated by a delimiter character
+(default is the TAB character).
+The newline of every line except the line from the last input file
+is replaced with a delimiter character.
Standard input is used for a file name of @samp{-} or if no input files
are given.
@@ -6613,16 +6331,22 @@ $ seq 4 | paste -d ' ' - -
3 4
@end example
+Comma separate data:
+@example
+$ seq 4 | paste -s -d ','
+1,2,3,4
+@end example
+
The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -s
-@itemx --serial
-@opindex -s
-@opindex --serial
-Paste the lines of one file at a time rather than one line from each
-file. Using the above example data:
+@optItem{paste,-s,}
+@optItemx{paste,--serial,}
+Paste the lines of one file at a time rather than one line from each file.
+The newline of every line except the last line in each input file
+is replaced with a delimiter character.
+Using the above example data:
@example
$ paste -s num2 let3
@@ -6630,14 +6354,31 @@ $ paste -s num2 let3
a b c
@end example
-@item -d @var{delim-list}
-@itemx --delimiters=@var{delim-list}
-@opindex -d
-@opindex --delimiters
+@optItem{paste,-d,@w{ }@var{delim-list}}
+@optItemx{paste,--delimiters,=@var{delim-list}}
Consecutively use the characters in @var{delim-list} instead of
TAB to separate merged lines. When @var{delim-list} is
-exhausted, start again at its beginning. Using the above example data:
+exhausted, start again at its beginning.
+
+The following backslash escape sequences are recognized
+in @var{delim-list}:
+@table @samp
+@item \0 The empty string (not the NUL character)
+@item \n newline
+@item \t TAB
+@item \\ A backslash
+@item \b backspace (GNU extension)
+@item \f form feed (GNU extension)
+@item \r carriage return (GNU extension)
+@item \v vertical tab (GNU extension)
+@end table
+
+It is an error if no character follows an unescaped backslash.
+As a GNU extension, a backslash followed by a character not listed
+above is interpreted as that character.
+
+Using the above example data:
@example
$ paste -d '%_' num2 let3 num2
1%a_1
@@ -6645,7 +6386,7 @@ $ paste -d '%_' num2 let3 num2
%c_
@end example
-@optZeroTerminated
+@optZeroTerminated{paste}
@end table
@@ -6715,24 +6456,21 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -a @var{file-number}
-@opindex -a
+@optItem{join,-a,@w{ }@var{file-number}}
Print a line for each unpairable line in file @var{file-number} (either
@samp{1} or @samp{2}), in addition to the normal output.
-@item --check-order
+@optItem{join,--check-order,}
Fail with an error message if either input file is wrongly ordered.
-@item --nocheck-order
+@optItem{join,--nocheck-order,}
Do not check that both input files are in sorted order. This is the default.
-@item -e @var{string}
-@opindex -e
+@optItem{join,-e,@w{ }@var{string}}
Replace those output fields that are missing in the input with @var{string}.
I.e., missing fields specified with the @option{-12jo} options.
-@item --header
-@opindex --header
+@optItem{join,--header,}
Treat the first line of each input file as a header line. The header lines
will be joined and printed as the first output line. If @option{-o} is used to
specify output format, the header line will be printed according to the
@@ -6740,26 +6478,22 @@ specified format. The header lines will not be checked for ordering even if
@option{--check-order} is specified. Also if the header lines from each file
do not match, the heading fields from the first file will be used.
-@item -i
-@itemx --ignore-case
-@opindex -i
-@opindex --ignore-case
+@optItem{join,-i,}
+@optItemx{join,--ignore-case,}
Ignore differences in case when comparing keys.
With this option, the lines of the input files must be ordered in the same way.
Use @samp{sort -f} to produce this ordering.
-@item -1 @var{field}
-@opindex -1
+@optItem{join,-1,@w{ }@var{field}}
Join on field @var{field} (a positive integer) of file 1.
-@item -2 @var{field}
-@opindex -2
+@optItem{join,-2,@w{ }@var{field}}
Join on field @var{field} (a positive integer) of file 2.
-@item -j @var{field}
+@optItem{join,-j,@w{ }@var{field}}
Equivalent to @option{-1 @var{field} -2 @var{field}}.
-@item -o @var{field-list}
+@optItem{join,-o,@w{ }@var{field-list}}
@itemx -o auto
If the keyword @samp{auto} is specified, infer the output format from
the first line in each file. This is the same as the default output format
@@ -6791,7 +6525,7 @@ example, the commands @samp{join -o 1.2,2.2} and @samp{join -o '1.2
All output lines -- including those printed because of any @option{-a}
or @option{-v} option -- are subject to the specified @var{field-list}.
-@item -t @var{char}
+@optItem{join,-t,@w{ }@var{char}}
Use character @var{char} as the input and output field separator.
Treat as significant each occurrence of @var{char} in the input file.
Use @samp{sort -t @var{char}}, without the @option{-b} option of
@@ -6800,11 +6534,11 @@ the whole line is considered, matching the default operation of sort.
If @samp{-t '\0'} is specified then the ASCII NUL
character is used to delimit the fields.
-@item -v @var{file-number}
+@optItem{join,-v,@w{ }@var{file-number}}
Print a line for each unpairable line in file @var{file-number}
(either @samp{1} or @samp{2}), instead of the normal output.
-@optZeroTerminated
+@optZeroTerminated{join}
@newlineFieldSeparator
@end table
@@ -7226,34 +6960,25 @@ Options must precede operands.
@table @samp
-@item -c
-@itemx -C
-@itemx --complement
-@opindex -c
-@opindex -C
-@opindex --complement
+@optItem{tr,-c,}
+@optItemx{tr,-C,}
+@optItemx{tr,--complement,}
Instead of @var{array1}, use its complement (all characters not
specified by @var{string1}), in ascending order. Use this option with
caution in multibyte locales where its meaning is not always clear
or portable; see @ref{Character arrays}.
-@item -d
-@itemx --delete
-@opindex -d
-@opindex --delete
+@optItem{tr,-d,}
+@optItemx{tr,--delete,}
Delete characters in @var{array1}; do not translate.
-@item -s
-@itemx --squeeze-repeats
-@opindex -s
-@opindex --squeeze-repeats
+@optItem{tr,-s,}
+@optItemx{tr,--squeeze-repeats,}
Replace each sequence of a repeated character that is listed in
the last specified @var{array}, with a single occurrence of that character.
-@item -t
-@itemx --truncate-set1
-@opindex -t
-@opindex --truncate-set1
+@optItem{tr,-t,}
+@optItemx{tr,--truncate-set1,}
Truncate @var{array1} to the length of @var{array2}.
@end table
@@ -7282,7 +7007,7 @@ The interpretation of @var{string1} and @var{string2} depends on locale.
GNU @command{tr} fully supports only safe single-byte locales,
where each possible input byte represents a single character.
Unfortunately, this means GNU @command{tr} will not handle commands
-like @samp{tr @"o @L{}} the way you might expect,
+like @samp{tr ö Ł} the way you might expect,
since (assuming a UTF-8 encoding) this is equivalent to
@samp{tr '\303\266' '\305\201'} and GNU @command{tr} will
simply transliterate all @samp{\303} bytes to @samp{\305} bytes, etc.
@@ -7441,6 +7166,9 @@ which is of no particular use.
@end table
+Since @samp{[:@var{class}:]} and @samp{[=@var{c}=]} syntax
+are similar to shell globbing syntax, all such class arguments should
+be quoted appropriately, when running @command{tr} from the shell.
@node Translating
@subsection Translating
@@ -7638,10 +7366,8 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -t @var{tab1}[,@var{tab2}]@dots{}
-@itemx --tabs=@var{tab1}[,@var{tab2}]@dots{}
-@opindex -t
-@opindex --tabs
+@optItem{expand,-t,@w{ }@var{tab1}[@comma{}@var{tab2}]@dots{}}
+@optItemx{expand,--tabs,=@var{tab1}[@comma{}@var{tab2}]@dots{}}
@cindex tab stops, setting
If only one tab stop is given, set the tabs @var{tab1} spaces apart
(default is 8). Otherwise, set the tabs at columns @var{tab1},
@@ -7669,10 +7395,8 @@ For compatibility, GNU @command{expand} also accepts the obsolete
option syntax, @option{-@var{t1}[,@var{t2}]@dots{}}. New scripts
should use @option{-t @var{t1}[,@var{t2}]@dots{}} instead.
-@item -i
-@itemx --initial
-@opindex -i
-@opindex --initial
+@optItem{expand,-i,}
+@optItemx{expand,--initial,}
@cindex initial tabs, converting
Only convert initial tabs (those that precede all non-space or non-tab
characters) on each line to spaces.
@@ -7708,10 +7432,8 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -t @var{tab1}[,@var{tab2}]@dots{}
-@itemx --tabs=@var{tab1}[,@var{tab2}]@dots{}
-@opindex -t
-@opindex --tabs
+@optItem{unexpand,-t,@w{ }@var{tab1}[@comma{}@var{tab2}]@dots{}}
+@optItemx{unexpand,--tabs,=@var{tab1}[@comma{}@var{tab2}]@dots{}}
If only one tab stop is given, set the tabs @var{tab1} columns apart
instead of the default 8. Otherwise, set the tabs at columns
@var{tab1}, @var{tab2}, @dots{} (numbered from 0), and leave blanks
@@ -7726,13 +7448,13 @@ separated by commas. (Unlike @option{-t}, this obsolete option does
not imply @option{-a}.) New scripts should use @option{--first-only -t
@var{tab1}[,@var{tab2}]@dots{}} instead.
-@item -a
-@itemx --all
-@opindex -a
-@opindex --all
+@optItem{unexpand,-a,}
+@optItemx{unexpand,--all,}
Also convert all sequences of two or more blanks just before a tab stop,
even if they occur after non-blank characters in a line.
+@optItem{unexpand,--first-only,}
+Convert only leading sequences of blanks (overrides @option{-a}).
@end table
@exitstatus
@@ -7823,32 +7545,24 @@ files whose names start with @samp{.}.
@table @samp
-@item -a
-@itemx --all
-@opindex -a
-@opindex --all
+@optItem{ls,-a,}
+@optItemx{ls,--all,}
In directories, do not ignore file names that start with @samp{.}.
-@item -A
-@itemx --almost-all
-@opindex -A
-@opindex --almost-all
+@optItem{ls,-A,}
+@optItemx{ls,--almost-all,}
In directories, do not ignore all file names that start with @samp{.};
ignore only @file{.} and @file{..}. The @option{--all} (@option{-a})
option overrides this option.
-@item -B
-@itemx --ignore-backups
-@opindex -B
-@opindex --ignore-backups
+@optItem{ls,-B,}
+@optItemx{ls,--ignore-backups,}
@cindex backup files, ignoring
In directories, ignore files that end with @samp{~}. This option is
equivalent to @samp{--ignore='*~' --ignore='.*~'}.
-@item -d
-@itemx --directory
-@opindex -d
-@opindex --directory
+@optItem{ls,-d,}
+@optItemx{ls,--directory,}
List just the names of directories, as with other types of files, rather
than listing their contents.
@c The following sentence is the same as the one for -F.
@@ -7857,16 +7571,13 @@ command line unless the @option{--dereference-command-line} (@option{-H}),
@option{--dereference} (@option{-L}), or
@option{--dereference-command-line-symlink-to-dir} options are specified.
-@item -H
-@itemx --dereference-command-line
-@opindex -H
-@opindex --dereference-command-line
+@optItem{ls,-H,}
+@optItemx{ls,--dereference-command-line,}
@cindex symbolic links, dereferencing
If a command line argument specifies a symbolic link, show information
for the file the link references rather than for the link itself.
-@item --dereference-command-line-symlink-to-dir
-@opindex --dereference-command-line-symlink-to-dir
+@optItem{ls,--dereference-command-line-symlink-to-dir,}
@cindex symbolic links, dereferencing
Do not dereference symbolic links, with one exception:
if a command line argument specifies a symbolic link that refers to
@@ -7879,8 +7590,7 @@ or any of the following options is in effect:
@option{--dereference} (@option{-L}), or
@option{--dereference-command-line} (@option{-H})).
-@item --hide=PATTERN
-@opindex --hide=@var{pattern}
+@optItem{ls,--hide,=@var{pattern}}
In directories, ignore files whose names match the shell pattern
@var{pattern}, unless the @option{--all} (@option{-a}) or
@option{--almost-all} (@option{-A}) is also given. This
@@ -7893,10 +7603,8 @@ This option can be useful in shell aliases. For example, if
an alias for @samp{ls --ignore='*~'}, then the command @samp{lx -A}
lists the file @file{README~} even though @samp{ly -A} would not.
-@item -I @var{pattern}
-@itemx --ignore=@var{pattern}
-@opindex -I
-@opindex --ignore=@var{pattern}
+@optItem{ls,-I,@w{ }@var{pattern}}
+@optItemx{ls,--ignore,=@var{pattern}}
In directories, ignore files whose names match the shell pattern
(not regular expression) @var{pattern}. As
in the shell, an initial @samp{.} in a file name does not match a
@@ -7911,20 +7619,16 @@ The first option ignores names of length 3 or more that start with @samp{.},
the second ignores all two-character names that start with @samp{.}
except @samp{..}, and the third ignores names that start with @samp{#}.
-@item -L
-@itemx --dereference
-@opindex -L
-@opindex --dereference
+@optItem{ls,-L,}
+@optItemx{ls,--dereference,}
@cindex symbolic links, dereferencing
When showing file information for a symbolic link, show information
for the file the link references rather than the link itself.
However, even with this option, @command{ls} still prints the name
of the link itself, not the name of the file that the link points to.
-@item -R
-@itemx --recursive
-@opindex -R
-@opindex --recursive
+@optItem{ls,-R,}
+@optItemx{ls,--recursive,}
@cindex recursive directory listing
@cindex directory listing, recursive
List the contents of all directories recursively.
@@ -7940,17 +7644,14 @@ default, only file names are shown.
@table @samp
-@item --author
-@opindex --author
+@optItem{ls,--author,}
@cindex hurd, author, printing
In long format, list each file's author.
In GNU/Hurd, file authors can differ from their owners, but in other
operating systems the two are the same.
-@item -D
-@itemx --dired
-@opindex -D
-@opindex --dired
+@optItem{ls,-D,}
+@optItemx{ls,--dired,}
@cindex dired Emacs mode support
Print an additional line after the main output:
@@ -8053,40 +7754,38 @@ The @option{--dired} (@option{-D}) option implies long format output
with hyperlinks disabled, and takes precedence over previously specified
output formats or hyperlink mode.
-@item --full-time
-@opindex --full-time
+@optItem{ls,--full-time,}
Produce long format, and list times in full. It is
equivalent to using @option{--format=long} (@option{-l}) with
@option{--time-style=full-iso} (@pxref{Formatting file timestamps}).
-@item -g
-@opindex -g
+@optItem{ls,-g,}
Produce long format, but omit owner information.
-@item -G
-@itemx --no-group
-@opindex -G
-@opindex --no-group
+@optItem{ls,-G,}
+@optItemx{ls,--no-group,}
Inhibit display of group information in long format.
(This is the default in some non-GNU versions of @command{ls}, so we
provide this option for compatibility.)
-@optHumanReadable
+@optItem{ls,--block-size,=@var{size}}
+@cindex file sizes
+With @option{-l}, scale sizes by @var{size} before printing them
+(@pxref{Block size}). For example,
+@option{--block-size=G} prints sizes in units of 1,073,741,824 bytes.
+
+@optHumanReadable{ls}
-@item -i
-@itemx --inode
-@opindex -i
-@opindex --inode
+@optItem{ls,-i,}
+@optItemx{ls,--inode,}
@cindex inode number, printing
Print the inode number (also called the file serial number and index
number) of each file to the left of the file name. (This number
uniquely identifies each file within a particular file system.)
-@item -l
-@itemx --format=long
+@optItem{ls,-l,}
+@optItemx{ls,--format,=long}
@itemx --format=verbose
-@opindex -l
-@opindex --format
@opindex long ls @r{format}
@opindex verbose ls @r{format}
Produce long format.
@@ -8209,26 +7908,21 @@ is marked with a @samp{+} character.
whether alternate access methods apply to the file, which may happen for
example with some NFS setups with files without read permission.
-@item -n
-@itemx --numeric-uid-gid
-@opindex -n
-@opindex --numeric-uid-gid
+@optItem{ls,-n,}
+@optItemx{ls,--numeric-uid-gid,}
@cindex numeric uid and gid
@cindex numeric user and group IDs
Produce long format, but
display right-justified numeric user and group IDs
instead of left-justified owner and group names.
-@item -o
-@opindex -o
+@optItem{ls,-o,}
Produce long format, but omit group information.
It is equivalent to using @option{--format=long} (@option{-l})
with @option{--no-group} (@option{-G}).
-@item -s
-@itemx --size
-@opindex -s
-@opindex --size
+@optItem{ls,-s,}
+@optItemx{ls,--size,}
@cindex file system allocation
@cindex size of files, reporting
Print the file system allocation of each file to the left of the file name.
@@ -8245,12 +7939,10 @@ systems, it reports sizes that are twice the correct values for files
that are NFS-mounted from BSD systems. This is due to a flaw in HP-UX;
it also affects the HP-UX @command{ls} program.
-@optSi
+@optSi{ls}
-@item -Z
-@itemx --context
-@opindex -Z
-@opindex --context
+@optItem{ls,-Z,}
+@optItemx{ls,--context,}
@cindex SELinux
@cindex security context
Display the SELinux security context or @samp{?} if none is found.
@@ -8272,10 +7964,9 @@ character collating sequence specified by the @env{LC_COLLATE} locale.
@table @samp
-@item -c
+@optItem{ls,-c,}
@itemx --time=ctime
@itemx --time=status
-@opindex -c
@opindex --time
@opindex ctime@r{, printing or sorting by}
@opindex status time@r{, printing or sorting by}
@@ -8285,16 +7976,14 @@ print the status change timestamp (the ctime) instead of the mtime.
When sorting by time or when not using long format,
sort according to the ctime. @xref{File timestamps}.
-@item -f
-@opindex -f
+@optItem{ls,-f,}
@cindex unsorted directory listing
@cindex directory order, listing by
Do not sort, and list all files.
This is like @option{--sort=none} (@option{-U}) combined
with @option{--all} (@option{-a}).
-@item --group-directories-first
-@opindex --group-directories-first
+@optItem{ls,--group-directories-first,}
When listing a directory's files,
group all subdirectories before non-directories
and then sort the subdirectories and the non-directories separately.
@@ -8303,38 +7992,31 @@ and the other sorting options specify a secondary key.
However, any use of @option{--sort=none}
(@option{-U}) disables this option altogether.
-@item -r
-@itemx --reverse
-@opindex -r
-@opindex --reverse
+@optItem{ls,-r,}
+@optItemx{ls,--reverse,}
@cindex reverse sorting
Reverse whatever the sorting method is -- e.g., list files in reverse
alphabetical order, youngest first, smallest first, or whatever.
This option has no effect when @option{--sort=none} (@option{-U})
is in effect.
-@item -S
-@itemx --sort=size
-@opindex -S
-@opindex --sort
+@optItem{ls,-S,}
+@optItemx{ls,--sort,=size}
@opindex size of files@r{, sorting files by}
Sort by file size, largest first.
-@item -t
+@optItem{ls,-t,}
@itemx --sort=time
-@opindex -t
@opindex --sort
@opindex modification timestamp@r{, sorting files by}
Sort by modification timestamp (mtime) by default, newest first.
The timestamp to order by can be changed with the @option{--time} option.
@xref{File timestamps}.
-@item -u
-@itemx --time=atime
+@optItem{ls,-u,}
+@optItem{ls,--time,=atime}
@itemx --time=access
@itemx --time=use
-@opindex -u
-@opindex --time
@opindex use time@r{, printing or sorting files by}
@opindex atime@r{, printing or sorting files by}
@opindex access timestamp@r{, printing or sorting files by}
@@ -8365,9 +8047,8 @@ When sorting by time or when not using long format,
sort according to the birth time.
@xref{File timestamps}.
-@item -U
+@optItem{ls,-U,}
@itemx --sort=none
-@opindex -U
@opindex --sort
@opindex none@r{, sorting option for @command{ls}}
Do not sort; list the files in whatever order they are
@@ -8377,9 +8058,8 @@ directories, where sorting can take some time.
Unlike @option{-f}, this option does not imply @option{--all}
(@option{-a}).
-@item -v
+@optItem{ls,-v,}
@itemx --sort=version
-@opindex -v
@opindex --sort
@opindex version@r{, sorting option for @command{ls}}
Sort by version name and number, lowest first. It behaves like a default
@@ -8400,9 +8080,8 @@ Sort by printed width of file names.
This can be useful with the @option{--format=vertical} (@option{-C})
output format, to most densely display the listed files.
-@item -X
+@optItem{ls,-X,}
@itemx --sort=extension
-@opindex -X
@opindex --sort
@opindex extension@r{, sorting files by}
Sort directory contents alphabetically by file extension (characters
@@ -8427,14 +8106,12 @@ output is not a terminal. See also the @option{--escape} (@option{-b}),
@option{--hide-control-chars} (@option{-q}), and @option{--zero} options
to disambiguate output of file names containing newline characters.
-@item -1
-@opindex -1
+@optItem{ls,-1,}
List one file per line. This is like @option{--format=single-column}
except that it has no effect if long format is also in effect.
-@item -C
+@optItem{ls,-C,}
@itemx --format=vertical
-@opindex -C
@opindex --format
@opindex vertical @r{sorted files in columns}
List files in columns, sorted vertically, with no other information.
@@ -8443,8 +8120,7 @@ It is always the default for the @command{dir} program.
GNU @command{ls} uses variable width columns to display as many files as
possible in the fewest lines.
-@item --color [=@var{when}]
-@opindex --color
+@optItem{ls,--color,[=@var{when}]}
@cindex color, distinguishing file types with
Specify whether to use color for distinguishing file types; @var{when}
may be omitted, or one of:
@@ -8483,11 +8159,9 @@ eval $(dircolors -p | perl -pe \
and on a @code{dirent.d_type}-capable file system, @command{ls}
will perform only one @code{stat} call per command line argument.
-@item -F
-@itemx --classify [=@var{when}]
+@optItem{ls,-F,}
+@optItemx{ls,--classify,[=@var{when}]}
@itemx --indicator-style=classify
-@opindex -F
-@opindex --classify
@opindex --indicator-style
@cindex file type and executables, marking
@cindex executables and file type, marking
@@ -8517,16 +8191,14 @@ command line unless the @option{--dereference-command-line} (@option{-H}),
@option{--dereference} (@option{-L}), or
@option{--dereference-command-line-symlink-to-dir} options are specified.
-@item --file-type
+@optItem{ls,--file-type,}
@itemx --indicator-style=file-type
-@opindex --file-type
@opindex --indicator-style
@cindex file type, marking
Append a character to each file name indicating the file type. This is
like @option{--classify} (@option{-F}, except that executables are not marked.
-@item --hyperlink [=@var{when}]
-@opindex --hyperlink
+@optItem{ls,--hyperlink,[=@var{when}]}
@cindex hyperlink, linking to files
Output codes recognized by some terminals to link
to files using the @samp{file://} URI format.
@@ -8546,8 +8218,7 @@ to files using the @samp{file://} URI format.
Specifying @option{--hyperlink} and no @var{when} is equivalent to
@option{--hyperlink=always}.
-@item --indicator-style=@var{word}
-@opindex --indicator-style
+@optItem{ls,--indicator-style,=@var{word}}
Append a character indicator with style @var{word} to file names,
as follows:
@@ -8567,10 +8238,8 @@ Append @samp{*} for executable regular files, otherwise behave as for
(@option{-F}) option.
@end table
-@item -k
-@itemx --kibibytes
-@opindex -k
-@opindex --kibibytes
+@optItem{ls,-k,}
+@optItemx{ls,--kibibytes,}
Set the default block size to its normal value of 1024 bytes,
overriding any contrary specification in environment variables
(@pxref{Block size}). If @option{--block-size},
@@ -8584,35 +8253,30 @@ and the file system allocation written by the @option{--size} (@option{-s})
option. It does not affect the file size in bytes that is written in
long format.
-@item -m
+@optItem{ls,-m,}
@itemx --format=commas
-@opindex -m
@opindex --format
@opindex commas@r{, outputting between files}
List files horizontally, with as many as will fit on each line,
separated by @samp{, } (a comma and a space),
and with no other information.
-@item -p
+@optItem{ls,-p,}
@itemx --indicator-style=slash
-@opindex -p
@opindex --indicator-style
@cindex file type, marking
Append a @samp{/} to directory names.
-@item -x
+@optItem{ls,-x,}
@itemx --format=across
@itemx --format=horizontal
-@opindex -x
@opindex --format
@opindex across@r{, listing files}
@opindex horizontal@r{, listing files}
List the files in columns, sorted horizontally.
-@item -T @var{cols}
-@itemx --tabsize=@var{cols}
-@opindex -T
-@opindex --tabsize
+@optItem{ls,-T,@w{ }@var{cols}}
+@optItemx{ls,--tabsize,=@var{cols}}
Assume that each tab stop is @var{cols} columns wide. The default is 8.
@command{ls} uses tabs where possible in the output, for efficiency. If
@var{cols} is zero, do not use tabs at all.
@@ -8631,10 +8295,8 @@ to set hardware tabs to every four columns, you should also run
@samp{export TABSIZE=4} or @samp{export TABSIZE=0}, or use the
corresponding @option{--tabsize} options.
-@item -w @var{cols}
-@itemx --width=@var{cols}
-@opindex -w
-@opindex --width
+@optItem{ls,-w,@w{ }@var{cols}}
+@optItemx{ls,--width,=@var{cols}}
@vindex COLUMNS
Assume the screen is @var{cols} columns wide. The default is taken
from the terminal settings if possible; otherwise the environment
@@ -8643,8 +8305,7 @@ is 80. With a @var{cols} value of @samp{0}, there is no limit on
the length of the output line, and that single output line will
be delimited with spaces, not tabs.
-@item --zero
-@opindex --zero
+@optItem{ls,--zero,}
@outputNUL
This option is incompatible with the @option{--dired} (@option{-D}) option.
This option also implies the options @option{--show-control-chars},
@@ -8679,8 +8340,7 @@ with @env{TZ}, libc, The GNU C Library Reference Manual}.
The following option changes how file timestamps are printed.
@table @samp
-@item --time-style=@var{style}
-@opindex --time-style
+@optItem{ls,--time-style,=@var{style}}
@cindex time style
List timestamps in style @var{style}. The @var{style} should
be one of the following:
@@ -8757,8 +8417,8 @@ ls -l --time-style="locale"
Other locales behave differently. For example, in a German locale,
@option{--time-style="locale"} might be equivalent to
@option{--time-style="+%e. %b %Y $newline%e. %b %H:%M"}
-and might generate timestamps like @samp{30. M@"ar 2020@ } and
-@samp{30. M@"ar 23:45}.
+and might generate timestamps like @samp{30. Mär 2020@ } and
+@samp{30. Mär 23:45}.
@item posix-@var{style}
@vindex LC_TIME
@@ -8788,48 +8448,44 @@ longer than 1000 bytes may be treated as errors.
These options change how file names themselves are printed.
+You can specify the default value of the @option{--quoting-style} option
+with the environment variable @env{QUOTING_STYLE}@. If that environment
+variable is not set, the default value is @samp{shell-escape} when the
+output is a terminal, and @samp{literal} otherwise.
+
@table @samp
-@item -b
-@itemx --escape
+@optItem{ls,-b,}
+@optItemx{ls,--escape,}
@itemx --quoting-style=escape
-@opindex -b
-@opindex --escape
@opindex --quoting-style
@cindex backslash sequences for file names
Quote nongraphic characters in file names using alphabetic and octal
backslash sequences like those used in C.
-@item -N
-@itemx --literal
+@optItem{ls,-N,}
+@optItemx{ls,--literal,}
@itemx --quoting-style=literal
-@opindex -N
-@opindex --literal
@opindex --quoting-style
Do not quote file names. However, with @command{ls} nongraphic
characters are still printed as question marks if the output is a
terminal and you do not specify the @option{--show-control-chars}
option.
-@item -q
-@itemx --hide-control-chars
-@opindex -q
-@opindex --hide-control-chars
+@optItem{ls,-q,}
+@optItemx{ls,--hide-control-chars,}
Print question marks instead of nongraphic characters in file names.
This is the default if the output is a terminal and the program is
@command{ls}.
-@item -Q
-@itemx --quote-name
+@optItem{ls,-Q,}
+@optItemx{ls,--quote-name,}
@itemx --quoting-style=c
-@opindex -Q
-@opindex --quote-name
@opindex --quoting-style
Enclose file names in double quotes and quote nongraphic characters as
in C.
-@item --quoting-style=@var{word}
-@opindex --quoting-style
+@optItem{ls,--quoting-style,=@var{word}}
@cindex quoting style
Use style @var{word} to quote file names and other strings that may
contain arbitrary characters. The @var{word} should
@@ -8849,8 +8505,17 @@ like @command{csh}.
@item shell-always
Quote strings for the shell, even if they would normally not require quoting.
@item shell-escape
-Like @samp{shell}, but also quoting non-printable characters using the POSIX
-@samp{$''} syntax suitable for most shells.
+Like @samp{shell}, but also quote non-printable characters using the POSIX
+@samp{$''} syntax suitable for most shells. This is the most general format
+as any file name is represented unambiguously and safely. I.e., the full
+quoted string can be pasted back to the shell to refer to any file.
+Consequently, this is the default format used when @command{ls}
+is outputting to a tty.
+Note also that specifying the C locale with @code{LC_ALL=C} can be useful
+with this output format, to avoid character set conversion issues
+with some terminals; e.g., xterm always converting to unicode composed form.
+See also
+@uref{https://www.gnu.org/software/coreutils/quotes.html, Quoting file names}.
@item shell-escape-always
Like @samp{shell-escape}, but quote strings even if they would
normally not require quoting.
@@ -8879,13 +8544,7 @@ this"} in the default C locale. This looks nicer on many displays.
@end macro
@quotingStyles
-You can specify the default value of the @option{--quoting-style} option
-with the environment variable @env{QUOTING_STYLE}@. If that environment
-variable is not set, the default value is @samp{shell-escape} when the
-output is a terminal, and @samp{literal} otherwise.
-
-@item --show-control-chars
-@opindex --show-control-chars
+@optItem{ls,--show-control-chars,}
Print nongraphic characters as-is in file names.
This is the default unless the output is a terminal and the program is
@command{ls}.
@@ -8957,33 +8616,25 @@ environment variable.
The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -b
-@itemx --sh
-@itemx --bourne-shell
-@opindex -b
-@opindex --sh
-@opindex --bourne-shell
+@optItem{dircolors,-b,}
+@optItemx{dircolors,--sh,}
+@optItemx{dircolors,--bourne-shell,}
@cindex Bourne shell syntax for color setup
@cindex @command{sh} syntax for color setup
Output Bourne shell commands. This is the default if the @env{SHELL}
environment variable is set and does not end with @samp{csh} or
@samp{tcsh}.
-@item -c
-@itemx --csh
-@itemx --c-shell
-@opindex -c
-@opindex --csh
-@opindex --c-shell
+@optItem{dircolors,-c,}
+@optItemx{dircolors,--csh,}
+@optItemx{dircolors,--c-shell,}
@cindex C shell syntax for color setup
@cindex @command{csh} syntax for color setup
Output C shell commands. This is the default if @code{SHELL} ends with
@command{csh} or @command{tcsh}.
-@item -p
-@itemx --print-database
-@opindex -p
-@opindex --print-database
+@optItem{dircolors,-p,}
+@optItemx{dircolors,--print-database,}
@cindex color database, printing
@cindex database for color setup, printing
@cindex printing color database
@@ -8991,8 +8642,7 @@ Print the (compiled-in) default color configuration database. This
output is itself a valid configuration file, and is fairly descriptive
of the possibilities.
-@item --print-ls-colors
-@opindex --print-ls-colors
+@optItem{dircolors,--print-ls-colors,}
@cindex printing ls colors
Print the LS_COLORS entries on separate lines,
each colored as per the color they represent.
@@ -9097,10 +8747,8 @@ you simply want to make a backup of an existing file before changing it.
The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -a
-@itemx --archive
-@opindex -a
-@opindex --archive
+@optItem{cp,-a,}
+@optItemx{cp,--archive,}
Preserve as much as possible of the structure and attributes of the
original files in the copy (but do not attempt to preserve internal
directory structure; i.e., @samp{ls -U} may list the entries in a copied
@@ -9109,24 +8757,16 @@ Try to preserve SELinux security context and extended attributes (xattr),
but ignore any failure to do that and print no corresponding diagnostic.
Equivalent to @option{-dR --preserve=all} with the reduced diagnostics.
-@item --attributes-only
-@opindex --attributes-only
+@optItem{cp,--attributes-only,}
Copy only the specified attributes of the source file to the destination.
If the destination already exists, do not alter its contents.
See the @option{--preserve} option for controlling which attributes to copy.
-@item -b
-@itemx --backup[=@var{method}]
-@opindex -b
-@opindex --backup
-@vindex VERSION_CONTROL
-@cindex backups, making
-@xref{Backup options}.
-Make a backup of each file that would otherwise be overwritten or removed.
-As a special case, @command{cp} makes a backup of @var{source} when the force
-and backup options are given and @var{source} and @var{dest} are the same
-name for an existing, regular file. One useful application of this
-combination of options is this tiny Bourne shell script:
+@optBackup{cp}
+As a special case, @command{cp} makes a backup of @var{source} when the
+@option{force} and @option{backup} options are given and @var{source} and
+@var{dest} are the same name for an existing, regular file. One useful
+application of this combination of options is this tiny Bourne shell script:
@example
#!/bin/sh
@@ -9139,7 +8779,7 @@ done
exit $fail
@end example
-@item --copy-contents
+@optItem{cp,--copy-contents,}
@cindex directories, copying recursively
@cindex copying directories recursively
@cindex recursively copying directories
@@ -9156,27 +8796,23 @@ fill up your destination file system if you use it to copy @file{/dev/zero}.
This option has no effect unless copying recursively, and it does not
affect the copying of symbolic links.
-@item -d
-@opindex -d
+@optItem{cp,-d,}
@cindex symbolic links, copying
@cindex hard links, preserving
Copy symbolic links as symbolic links rather than copying the files that
they point to, and preserve hard links between source files in the copies.
Equivalent to @option{--no-dereference --preserve=links}.
-@macro optDebugCopy
-@item --debug
-@opindex --debug
+@macro optDebugCopy{cmd}
+@optItem{\cmd\,--debug,}
@cindex debugging, copying
Print extra information to standard output, explaining how files are copied.
This option implies the @option{--verbose} option.
@end macro
-@optDebugCopy
+@optDebugCopy{cp}
-@item -f
-@itemx --force
-@opindex -f
-@opindex --force
+@optItem{cp,-f,}
+@optItemx{cp,--force,}
When copying without this option and an existing destination file cannot
be opened for writing, the copy fails. However, with @option{--force},
when a destination file cannot be opened, @command{cp} then
@@ -9195,41 +8831,45 @@ This option is independent of the @option{--interactive} or
This option is ignored when the @option{--no-clobber} or @option{-n} option
is also used.
-@item -H
-@opindex -H
-If a command line argument specifies a symbolic link, then copy the
-file it points to rather than the symbolic link itself. However,
-copy (preserving its nature) any symbolic link that is encountered
-via recursive traversal.
-
-@item -i
-@itemx --interactive
-@opindex -i
-@opindex --interactive
+@optItem{cp,-i,}
+@optItemx{cp,--interactive,}
When copying a file other than a directory, prompt whether to
overwrite an existing destination file, and fail if the response
is not affirmative. The @option{-i} option overrides
a previous @option{-n} option.
-@item -l
-@itemx --link
-@opindex -l
-@opindex --link
-Make hard links instead of copies of non-directories.
+@optItem{cp,-H,}
+If a command line argument specifies a symbolic link, then copy the
+file it points to rather than the symbolic link itself. However,
+copy (preserving its nature) any symbolic link that is encountered
+via recursive traversal.
-@item -L
-@itemx --dereference
-@opindex -L
-@opindex --dereference
+@optItem{cp,-L,}
+@optItemx{cp,--dereference,}
Follow symbolic links when copying from them.
With this option, @command{cp} cannot create a symbolic link.
For example, a symlink (to regular file) in the source tree will be copied to
a regular file in the destination tree.
-@item -n
-@itemx --no-clobber
-@opindex -n
-@opindex --no-clobber
+@optItem{cp,-P,}
+@optItemx{cp,--no-dereference,}
+@cindex symbolic links, copying
+Copy symbolic links as symbolic links rather than copying the files that
+they point to. This option affects only symbolic links in the source;
+symbolic links in the destination are always followed if possible.
+
+@optItem{cp,--keep-directory-symlink,}
+Follow existing symlinks to directories when copying.
+Use this option only when the destination directory's contents are trusted,
+as an attacker can place symlinks in the destination
+to cause @command{cp} write to arbitrary target directories.
+
+@optItem{cp,-l,}
+@optItemx{cp,--link,}
+Make hard links instead of copies of non-directories.
+
+@optItem{cp,-n,}
+@optItemx{cp,--no-clobber,}
Do not overwrite an existing file; silently skip instead.
This option overrides a previous @option{-i} option.
This option is mutually exclusive with @option{-b} or @option{--backup} option.
@@ -9238,19 +8878,8 @@ other platforms. See also the @option{--update} option which will
give more control over how to deal with existing files in the destination,
and over the exit status in particular.
-@item -P
-@itemx --no-dereference
-@opindex -P
-@opindex --no-dereference
-@cindex symbolic links, copying
-Copy symbolic links as symbolic links rather than copying the files that
-they point to. This option affects only symbolic links in the source;
-symbolic links in the destination are always followed if possible.
-
-@item -p
-@itemx --preserve[=@var{attribute_list}]
-@opindex -p
-@opindex --preserve
+@optItem{cp,-p,}
+@optItemx{cp,--preserve,[=@var{attribute_list}]}
@cindex file information, preserving, extended attributes, xattr
Preserve the specified attributes of the original files.
If specified, the @var{attribute_list} must be a comma-separated list
@@ -9331,13 +8960,12 @@ the umask or a default ACL, possibly resulting in a more restrictive
file mode.
@xref{File permissions}.
-@item --no-preserve=@var{attribute_list}
+@optItem{cp,--no-preserve,=@var{attribute_list}}
@cindex file information, preserving
Do not preserve the specified attributes. The @var{attribute_list}
has the same form as for @option{--preserve}.
-@item --parents
-@opindex --parents
+@optItem{cp,--parents,}
@cindex parent directories and @command{cp}
Form the name of each destination file by appending to the target
directory a slash and the specified name of the source file. The last
@@ -9352,12 +8980,9 @@ cp --parents a/b/c existing_dir
copies the file @file{a/b/c} to @file{existing_dir/a/b/c}, creating
any missing intermediate directories.
-@item -R
-@itemx -r
-@itemx --recursive
-@opindex -R
-@opindex -r
-@opindex --recursive
+@optItem{cp,-r,}
+@optItemx{cp,-R,}
+@optItemx{cp,--recursive,}
@cindex directories, copying recursively
@cindex copying directories recursively
@cindex recursively copying directories
@@ -9376,8 +9001,7 @@ Also, it is not portable to use @option{-R} to copy symbolic links
unless you also specify @option{-P}, as POSIX allows
implementations that dereference symbolic links by default.
-@item --reflink[=@var{when}]
-@opindex --reflink[=@var{when}]
+@optItem{cp,--reflink,[=@var{when}]}
@cindex COW
@cindex clone
@cindex copy on write
@@ -9408,13 +9032,11 @@ This option is overridden by the @option{--link}, @option{--symbolic-link}
and @option{--attributes-only} options, thus allowing it to be used
to configure the default data copying behavior for @command{cp}.
-@item --remove-destination
-@opindex --remove-destination
+@optItem{cp,--remove-destination,}
Remove each existing destination file before attempting to open it
(contrast with @option{-f} above).
-@item --sparse=@var{when}
-@opindex --sparse=@var{when}
+@optItem{cp,--sparse,=@var{when}}
@cindex sparse files, copying
@cindex holes, copying files with
@findex read @r{system call, and holes}
@@ -9460,28 +9082,24 @@ minimum amount of space supported by the file system.
alias cp='cp --sparse=always'
@end example
-@optStripTrailingSlashes
+@optStripTrailingSlashes{cp}
-@item -s
-@itemx --symbolic-link
-@opindex -s
-@opindex --symbolic-link
+@optItem{cp,-s,}
+@optItemx{cp,--symbolic-link,}
@cindex symbolic links, copying with
Make symbolic links instead of copies of non-directories. All source
file names must be absolute (starting with @samp{/}) unless the
destination files are in the current directory. This option merely
results in an error message on systems that do not support symbolic links.
-@optBackupSuffix
+@optBackupSuffix{cp}
-@optTargetDirectory
+@optTargetDirectory{cp}
-@optNoTargetDirectory
+@optNoTargetDirectory{cp}
-@item -u
-@itemx --update[=@var{which}]
-@opindex -u
-@opindex --update[=@var{which}]
+@optItem{cp,-u,}
+@optItemx{cp,--update,[=@var{which}]}
@cindex newer files, copying only
Do not copy a non-directory that has an existing destination with the
same or newer modification timestamp; instead, silently skip the file
@@ -9521,26 +9139,20 @@ in files being replaced if they're older than the corresponding source file.
@end macro
@whichUpdate
-@item -v
-@itemx --verbose
-@opindex -v
-@opindex --verbose
+@optItem{cp,-v,}
+@optItemx{cp,--verbose,}
Print the name of each file before copying it.
-@item -x
-@itemx --one-file-system
-@opindex -x
-@opindex --one-file-system
+@optItem{cp,-x,}
+@optItemx{cp,--one-file-system,}
@cindex file systems, omitting copying to different
Skip subdirectories that are on different file systems from the one that
the copy started on.
However, mount point directories @emph{are} copied.
-@macro optContext
-@item -Z
-@itemx --context[=@var{context}]
-@opindex -Z
-@opindex --context
+@macro optContext{cmd}
+@optItem{\cmd\,-Z,}
+@optItemx{\cmd\,--context,[=@var{context}]}
@cindex SELinux, setting/restoring security context
@cindex security context
Without a specified @var{context}, adjust the SELinux security context according
@@ -9551,7 +9163,7 @@ will set the context for newly created files only.
With a specified context, if both SELinux and SMACK are disabled, a warning is
issued.
@end macro
-@optContext
+@optContext{cp}
This option is mutually exclusive with the @option{--preserve=context}
option, and overrides the @option{--preserve=all} and @option{-a} options.
@@ -9617,33 +9229,28 @@ OS/360 JCL.
@table @samp
-@item if=@var{file}
-@opindex if
+@optItem{dd,if,=@var{file}}
Read from @var{file} instead of standard input.
-@item of=@var{file}
-@opindex of
+@optItem{dd,of,=@var{file}}
Write to @var{file} instead of standard output. Unless
@samp{conv=notrunc} is given, truncate @var{file} before writing it.
-@item ibs=@var{bytes}
-@opindex ibs
+@optItem{dd,ibs,=@var{bytes}}
@cindex block size of input
@cindex input block size
Set the input block size to @var{bytes}.
This makes @command{dd} read @var{bytes} per block.
The default is 512 bytes.
-@item obs=@var{bytes}
-@opindex obs
+@optItem{dd,obs,=@var{bytes}}
@cindex block size of output
@cindex output block size
Set the output block size to @var{bytes}.
This makes @command{dd} write @var{bytes} per block.
The default is 512 bytes.
-@item bs=@var{bytes}
-@opindex bs
+@optItem{dd,bs,=@var{bytes}}
@cindex block size
Set both input and output block sizes to @var{bytes}.
This makes @command{dd} read and write @var{bytes} per block,
@@ -9652,8 +9259,7 @@ In addition, if no data-transforming @option{conv} operand is specified,
input is copied to the output as soon as it's read,
even if it is smaller than the block size.
-@item cbs=@var{bytes}
-@opindex cbs
+@optItem{dd,cbs,=@var{bytes}}
@cindex block size of conversion
@cindex conversion block size
@cindex fixed-length records, converting to variable-length
@@ -9663,27 +9269,22 @@ When converting variable-length records to fixed-length ones
(@option{conv=block}) or the reverse (@option{conv=unblock}),
use @var{bytes} as the fixed record length.
-@item skip=@var{n}
-@itemx iseek=@var{n}
-@opindex skip
-@opindex iseek
+@optItem{dd,skip,=@var{n}}
+@optItemx{dd,iseek,=@var{n}}
Skip @var{n} @samp{ibs}-byte blocks in the input file before copying.
If @var{n} ends in the letter @samp{B}, interpret @var{n}
as a byte count rather than a block count.
(@samp{B} and the @samp{iseek=} spelling are GNU extensions to POSIX.)
-@item seek=@var{n}
-@itemx oseek=@var{n}
-@opindex seek
-@opindex oseek
+@optItem{dd,seek,=@var{n}}
+@optItemx{dd,oseek,=@var{n}}
Skip @var{n} @samp{obs}-byte blocks in the output file before
truncating or copying.
If @var{n} ends in the letter @samp{B}, interpret @var{n}
as a byte count rather than a block count.
(@samp{B} and the @samp{oseek=} spelling are GNU extensions to POSIX.)
-@item count=@var{n}
-@opindex count
+@optItem{dd,count,=@var{n}}
Copy @var{n} @samp{ibs}-byte blocks from the input file, instead
of everything until the end of the file.
If @var{n} ends in the letter @samp{B},
@@ -9696,8 +9297,7 @@ rather than input read operations.
As an extension to POSIX, @samp{count=0} copies zero blocks
instead of copying all blocks.
-@item status=@var{level}
-@opindex status
+@optItem{dd,status,=@var{level}}
Specify the amount of information printed.
If this operand is given multiple times, the last one takes precedence.
The @var{level} value can be one of the following:
@@ -9742,8 +9342,7 @@ truncated records} is output after the @samp{records out} line if
The @samp{status=} operand is a GNU extension to POSIX.
-@item conv=@var{conversion}[,@var{conversion}]@dots{}
-@opindex conv
+@optItem{dd,conv,=@var{conversion}[@comma{}@var{conversion}]@dots{}}
Convert the file as specified by the @var{conversion} argument(s).
(No spaces around any comma(s).)
@@ -9802,6 +9401,17 @@ Change lowercase letters to uppercase.
The @samp{lcase} and @samp{ucase} conversions are mutually exclusive.
+@c https://austingroupbugs.net/view.php?id=1959
+POSIX leaves the behavior of @samp{lcase} and @samp{ucase} unspecified
+on multibyte characters. GNU @command{dd} supports only unibyte
+conversion, because multibyte characters may cross block boundaries and
+case conversion may change the length of characters.
+
+POSIX also leaves the behavior of @samp{lcase} and @samp{ucase}
+unspecified if used with @samp{ascii}, @samp{ebcdic}, or @samp{ibm}.
+GNU @command{dd} will perform the case conversion and then perform the
+character set conversion.
+
@item sparse
@opindex sparse
Try to seek rather than write NUL output blocks.
@@ -9886,13 +9496,11 @@ This conversion is a GNU extension to POSIX.
@end table
-@item iflag=@var{flag}[,@var{flag}]@dots{}
-@opindex iflag
+@optItem{dd,iflag,=@var{flag}[@comma{}@var{flag}]@dots{}}
Access the input file using the flags specified by the @var{flag}
argument(s). (No spaces around any comma(s).)
-@item oflag=@var{flag}[,@var{flag}]@dots{}
-@opindex oflag
+@optItem{dd,oflag,=@var{flag}[@comma{}@var{flag}]@dots{}}
Access the output file using the flags specified by the @var{flag}
argument(s). (No spaces around any comma(s).)
@@ -10199,12 +9807,10 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@optBackup
+@optBackup{install}
-@item -C
-@itemx --compare
-@opindex -C
-@opindex --compare
+@optItem{install,-C,}
+@optItemx{install,--compare,}
Compare content of source and destination files, and if there would be no
change to the destination content, owner, group, permissions, and possibly
SELinux context, then do not modify the destination at all.
@@ -10215,21 +9821,17 @@ incorrectly determines the default attributes that installed files would have
This could result in redundant copies or attributes that are not reset to the
correct defaults.
-@item -c
-@opindex -c
+@optItem{install,-c,}
Ignored; for compatibility with old Unix versions of @command{install}.
-@item -D
-@opindex -D
+@optItem{install,-D,}
Create any missing parent directories of @var{dest},
then copy @var{source} to @var{dest}.
Explicitly specifying the @option{--target-directory=@var{dir}} will similarly
ensure the presence of that hierarchy before copying @var{source} arguments.
-@item -d
-@itemx --directory
-@opindex -d
-@opindex --directory
+@optItem{install,-d,}
+@optItemx{install,--directory,}
@cindex directories, creating with given attributes
@cindex parent directories, creating missing
@cindex leading directories, creating missing
@@ -10237,21 +9839,17 @@ Create any missing parent directories, giving them the default
attributes. Then create each given directory, setting their owner,
group and mode as given on the command line or to the defaults.
-@optDebugCopy
+@optDebugCopy{install}
-@item -g @var{group}
-@itemx --group=@var{group}
-@opindex -g
-@opindex --group
+@optItem{install,-g,@w{ }@var{group}}
+@optItemx{install,--group,=@var{group}}
@cindex group ownership of installed files, setting
Set the group ownership of installed files or directories to
@var{group}. The default is the process's current group. @var{group}
may be either a group name or a numeric group ID.
-@item -m @var{mode}
-@itemx --mode=@var{mode}
-@opindex -m
-@opindex --mode
+@optItem{install,-m,@w{ }@var{mode}}
+@optItemx{install,--mode,=@var{mode}}
@cindex permissions of installed files, setting
Set the file mode bits for the installed file or directory to @var{mode},
which can be either an octal number, or a symbolic mode as in
@@ -10264,10 +9862,8 @@ This default is not quite the same as @samp{755}, since it disables
instead of preserving set-user-ID and set-group-ID on directories.
@xref{Directory Setuid and Setgid}.
-@item -o @var{owner}
-@itemx --owner=@var{owner}
-@opindex -o
-@opindex --owner
+@optItem{install,-o,@w{ }@var{owner}}
+@optItemx{install,--owner,=@var{owner}}
@cindex ownership of installed files, setting
@cindex appropriate privileges
@vindex root @r{as default owner}
@@ -10276,8 +9872,7 @@ ownership of installed files or directories to @var{owner}. The default
is @code{root}. @var{owner} may be either a user name or a numeric user
ID.
-@item --preserve-context
-@opindex --preserve-context
+@optItem{install,--preserve-context,}
@cindex SELinux
@cindex security context
Preserve the SELinux security context of files and directories.
@@ -10285,10 +9880,8 @@ Failure to preserve the context in all of the files or directories
will result in an exit status of 1. If SELinux is disabled then
print a warning and ignore the option.
-@item -p
-@itemx --preserve-timestamps
-@opindex -p
-@opindex --preserve-timestamps
+@optItem{install,-p,}
+@optItemx{install,--preserve-timestamps,}
@cindex timestamps of installed files, preserving
Set the time of last access and the time of last modification of each
installed file to match those of each corresponding original file.
@@ -10298,33 +9891,28 @@ This option is useful if you want to use the last modification timestamps
of installed files to keep track of when they were last built as opposed
to when they were last installed.
-@item -s
-@itemx --strip
-@opindex -s
-@opindex --strip
+@optItem{install,-s,}
+@optItemx{install,--strip,}
@cindex symbol table information, stripping
@cindex stripping symbol table information
Strip the symbol tables from installed binary executables.
-@item --strip-program=@var{program}
-@opindex --strip-program
+@optItem{install,--strip-program,=@var{program}}
@cindex symbol table information, stripping, program
Program used to strip binaries.
-@optBackupSuffix
+@optBackupSuffix{install}
-@optTargetDirectory
+@optTargetDirectory{install}
Also specifying the @option{-D} option will ensure the directory is present.
-@optNoTargetDirectory
+@optNoTargetDirectory{install}
-@item -v
-@itemx --verbose
-@opindex -v
-@opindex --verbose
+@optItem{install,-v,}
+@optItemx{install,--verbose,}
Print the name of each file before copying it.
-@optContext
+@optContext{install}
This option is mutually exclusive with the @option{--preserve-context} option.
@@ -10399,14 +9987,12 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@optBackup
+@optBackup{mv}
-@optDebugCopy
+@optDebugCopy{mv}
-@item -f
-@itemx --force
-@opindex -f
-@opindex --force
+@optItem{mv,-f,}
+@optItemx{mv,--force,}
@cindex prompts, omitting
Do not prompt the user before replacing a destination file.
@macro mvOptsIfn
@@ -10415,19 +10001,15 @@ options, only the final one takes effect.
@end macro
@mvOptsIfn
-@item -i
-@itemx --interactive
-@opindex -i
-@opindex --interactive
+@optItem{mv,-i,}
+@optItemx{mv,--interactive,}
@cindex prompts, forcing
Prompt whether to overwrite each existing destination file, regardless
of its permissions, and fail if the response is not affirmative.
@mvOptsIfn
-@item -n
-@itemx --no-clobber
-@opindex -n
-@opindex --no-clobber
+@optItem{mv,-n,}
+@optItemx{mv,--no-clobber,}
@cindex prompts, omitting
Do not overwrite an existing file; silently fail instead.
@mvOptsIfn
@@ -10435,14 +10017,12 @@ This option is mutually exclusive with @option{-b} or @option{--backup} option.
See also the @option{--update=none} option which will
skip existing files but not fail.
-@item --no-copy
-@opindex --no-copy
+@optItem{mv,--no-copy,}
@cindex renaming files without copying them
If a file cannot be renamed because the destination file system differs,
fail with a diagnostic instead of copying and then removing the file.
-@item --exchange
-@opindex --exchange
+@optItem{mv,--exchange,}
Exchange source and destination instead of renaming source to destination.
Both files must exist; they need not be the same type.
This exchanges all data and metadata.
@@ -10462,10 +10042,8 @@ If the source and destination might not be on the same file system,
using @code{--no-copy} will prevent future versions of @command{mv}
from implementing the exchange by copying.
-@item -u
-@itemx --update
-@opindex -u
-@opindex --update
+@optItem{mv,-u,}
+@optItemx{mv,--update,}
@cindex newer files, moving only
Do not move a non-directory that has an existing destination with the
same or newer modification timestamp;
@@ -10480,31 +10058,20 @@ option is also specified.
@whichUpdate
-@item -v
-@itemx --verbose
-@opindex -v
-@opindex --verbose
+@optItem{mv,-v,}
+@optItemx{mv,--verbose,}
Print the name of each file before moving it.
-@item --keep-directory-symlink
-@opindex --keep-directory-symlink
-Follow existing symlinks to directories when copying.
-Use this option only when the destination directory's contents are trusted,
-as an attacker can place symlinks in the destination
-to cause @command{cp} write to arbitrary target directories.
+@optStripTrailingSlashes{mv}
-@optStripTrailingSlashes
+@optBackupSuffix{mv}
-@optBackupSuffix
+@optTargetDirectory{mv}
-@optTargetDirectory
+@optNoTargetDirectory{mv}
-@optNoTargetDirectory
-
-@item -Z
-@itemx --context
-@opindex -Z
-@opindex --context
+@optItem{mv,-Z,}
+@optItemx{mv,--context,}
@cindex SELinux, restoring security context
@cindex security context
This option functions similarly to the @command{restorecon} command,
@@ -10554,36 +10121,29 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -d
-@itemx --dir
-@opindex -d
-@opindex --dir
+@optItem{rm,-d,}
+@optItemx{rm,--dir,}
@cindex directories, removing
Remove the listed directories if they are empty.
-@item -f
-@itemx --force
-@opindex -f
-@opindex --force
+@optItem{rm,-f,}
+@optItemx{rm,--force,}
Ignore nonexistent files and missing operands, and never prompt the user.
Ignore any previous @option{--interactive} (@option{-i}) option.
-@item -i
-@opindex -i
+@optItem{rm,-i,}
Prompt whether to remove each file.
If the response is not affirmative, silently skip the file without failing.
Ignore any previous @option{--force} (@option{-f}) option.
Equivalent to @option{--interactive=always}.
-@item -I
-@opindex -I
+@optItem{rm,-I,}
Prompt once whether to proceed with the command, if more than three
files are named or if a recursive removal is requested. Ignore any
previous @option{--force} (@option{-f}) option. Equivalent to
@option{--interactive=once}.
-@item --interactive [=@var{when}]
-@opindex --interactive
+@optItem{rm,--interactive,[=@var{when}]}
Specify when to issue an interactive prompt. @var{when} may be
omitted, or one of:
@itemize @bullet
@@ -10601,8 +10161,7 @@ removal is requested. Equivalent to @option{-I}.
@option{--interactive} with no @var{when} is equivalent to
@option{--interactive=always}.
-@item --one-file-system
-@opindex --one-file-system
+@optItem{rm,--one-file-system,}
@cindex one file system, restricting @command{rm} to
When removing a hierarchy recursively, do not remove any directory that is on a
file system different from that of the corresponding command line argument.
@@ -10621,8 +10180,7 @@ chroot happen to be on the same file system.
See also @option{--preserve-root=all} to protect command line arguments
themselves.
-@item --preserve-root [=all]
-@opindex --preserve-root
+@optItem{rm,--preserve-root,[=all]}
@cindex root directory, disallow recursive destruction
Fail upon any attempt to remove the root directory, @file{/},
when used with the @option{--recursive} option.
@@ -10631,27 +10189,21 @@ This is the default behavior.
When @samp{all} is specified, reject any command line argument
that is not on the same file system as its parent.
-@item --no-preserve-root
-@opindex --no-preserve-root
+@optItem{rm,--no-preserve-root,}
@cindex root directory, allow recursive destruction
Do not treat @file{/} specially when removing recursively.
This option is not recommended unless you really want to
remove all the files on your computer.
@xref{Treating / specially}.
-@item -r
-@itemx -R
-@itemx --recursive
-@opindex -r
-@opindex -R
-@opindex --recursive
+@optItem{rm,-r,}
+@optItemx{rm,-R,}
+@optItemx{rm,--recursive,}
@cindex directories, removing (recursively)
Remove the listed directories and their contents recursively.
-@item -v
-@itemx --verbose
-@opindex -v
-@opindex --verbose
+@optItem{rm,-v,}
+@optItemx{rm,--verbose,}
Print the name of each file before removing it.
@end table
@@ -10820,49 +10372,41 @@ to destroy using @command{shred}, be sure that it is not backed up or mirrored.
shred [@var{option}]@dots{} @var{file}[@dots{}]
@end example
+If @var{file} is a FIFO, socket, or terminal, @command{shred} skips it
+and emit a diagnostic message because the contents of these file types
+do not reside on disk.
+
The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -f
-@itemx --force
-@opindex -f
-@opindex --force
+@optItem{shred,-f,}
+@optItemx{shred,--force,}
@cindex force deletion
Override file permissions if necessary to allow overwriting.
-@item -n @var{number}
-@itemx --iterations=@var{number}
-@opindex -n @var{number}
-@opindex --iterations=@var{number}
+@optItem{shred,-n,@w{ }@var{number}}
+@optItemx{shred,--iterations,=@var{number}}
@cindex iterations, selecting the number of
By default, @command{shred} uses @value{SHRED_DEFAULT_PASSES} passes of
overwrite. You can reduce this to save time, or increase it if you think it's
appropriate. After 25 passes all of the internal overwrite patterns will have
been used at least once.
-@item --random-source=@var{file}
-@opindex --random-source
+@optItem{shred,--random-source,=@var{file}}
@cindex random source for shredding
Use @var{file} as a source of random data used to overwrite and to
choose pass ordering. @xref{Random sources}.
-@item -s @var{bytes}
-@itemx --size=@var{bytes}
-@opindex -s @var{bytes}
-@opindex --size=@var{bytes}
+@optItem{shred,-s,@w{ }@var{bytes}}
+@optItemx{shred,--size,=@var{bytes}}
@cindex size of file to shred
Shred the first @var{bytes} bytes of the file. The default is to shred
the whole file. @var{bytes} can be followed by a size specification like
@samp{K}, @samp{M}, or @samp{G} to specify a multiple. @xref{Block size}.
-@item -u
-@itemx --remove[=@var{how}]
-@opindex -u
-@opindex --remove
-@opindex --remove=unlink
-@opindex --remove=wipe
-@opindex --remove=wipesync
+@optItem{shred,-u,}
+@optItemx{shred,--remove,[=@var{how}]}
@cindex removing files after shredding
After shredding a file, deallocate it (if possible) and then remove it.
If a file has multiple links, only the named links will be removed.
@@ -10878,16 +10422,13 @@ requiring a sync for every character in every file. This can become
significant with many files, or is redundant if your file system provides
synchronous metadata updates.
-@item -v
-@itemx --verbose
-@opindex -v
-@opindex --verbose
+@optItem{shred,-v,}
+@optItemx{shred,--verbose,}
Display to standard error all status updates as sterilization proceeds.
+This details each data and metadata operation as it is performed.
-@item -x
-@itemx --exact
-@opindex -x
-@opindex --exact
+@optItem{shred,-x,}
+@optItemx{shred,--exact,}
By default, @command{shred} rounds the size of a regular file up to the next
multiple of the file system block size to fully erase the slack space in
the last block of the file. This space may contain portions of the current
@@ -10897,10 +10438,8 @@ Thus, by default if you shred a 10-byte regular file on a system with 512-byte
blocks, the resulting file will be 512 bytes long. With this option,
shred does not increase the apparent size of the file.
-@item -z
-@itemx --zero
-@opindex -z
-@opindex --zero
+@optItem{shred,-z,}
+@optItemx{shred,--zero,}
Normally, the last pass that @command{shred} writes is made up of
random data. If this would be conspicuous on your storage device (for
example, because it looks like encrypted data), or you just think
@@ -11132,46 +10671,35 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@optBackup
+@optBackup{ln}
-@item -d
-@itemx -F
-@itemx --directory
-@opindex -d
-@opindex -F
-@opindex --directory
+@optItem{ln,-d,}
+@optItemx{ln,-F,}
+@optItemx{ln,--directory,}
@cindex hard links to directories
Allow users with appropriate privileges to attempt to make hard links
to directories.
However, this will probably fail due to
system restrictions, even for the super-user.
-@item -f
-@itemx --force
-@opindex -f
-@opindex --force
+@optItem{ln,-f,}
+@optItemx{ln,--force,}
Remove existing destination files.
-@item -i
-@itemx --interactive
-@opindex -i
-@opindex --interactive
+@optItem{ln,-i,}
+@optItemx{ln,--interactive,}
@cindex prompting, and @command{ln}
Prompt whether to remove existing destination files,
and fail if the response is not affirmative.
-@item -L
-@itemx --logical
-@opindex -L
-@opindex --logical
+@optItem{ln,-L,}
+@optItemx{ln,--logical,}
If @option{-s} is not in effect, and the source file is a symbolic
link, create the hard link to the file referred to by the symbolic
link, rather than the symbolic link itself.
-@item -n
-@itemx --no-dereference
-@opindex -n
-@opindex --no-dereference
+@optItem{ln,-n,}
+@optItemx{ln,--no-dereference,}
Do not treat the last operand specially when it is a symbolic link to
a directory. Instead, treat it as if it were a normal file.
@@ -11189,10 +10717,8 @@ just like a directory.
This option is weaker than the @option{--no-target-directory}
(@option{-T}) option, so it has no effect if both options are given.
-@item -P
-@itemx --physical
-@opindex -P
-@opindex --physical
+@optItem{ln,-P,}
+@optItemx{ln,--physical,}
If @option{-s} is not in effect, and the source file is a symbolic
link, create the hard link to the symbolic link itself. On platforms
where this is not supported by the kernel, this option creates a
@@ -11200,10 +10726,8 @@ symbolic link with identical contents; since symbolic link contents
cannot be edited, any file name resolution performed through either
link will be the same as if a hard link had been created.
-@item -r
-@itemx --relative
-@opindex -r
-@opindex --relative
+@optItem{ln,-r,}
+@optItemx{ln,--relative,}
Make symbolic links relative to the link location.
This option is only valid with the @option{--symbolic} option.
@@ -11233,23 +10757,19 @@ ln--relative() {
@end verbatim
@end example
-@item -s
-@itemx --symbolic
-@opindex -s
-@opindex --symbolic
+@optItem{ln,-s,}
+@optItemx{ln,--symbolic,}
Make symbolic links instead of hard links. This option merely produces
an error message on systems that do not support symbolic links.
-@optBackupSuffix
+@optBackupSuffix{ln}
-@optTargetDirectory
+@optTargetDirectory{ln}
-@optNoTargetDirectory
+@optNoTargetDirectory{ln}
-@item -v
-@itemx --verbose
-@opindex -v
-@opindex --verbose
+@optItem{ln,-v,}
+@optItemx{ln,--verbose,}
Print the name of each file after linking it successfully.
@end table
@@ -11315,10 +10835,8 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -m @var{mode}
-@itemx --mode=@var{mode}
-@opindex -m
-@opindex --mode
+@optItem{mkdir,-m,@w{ }@var{mode}}
+@optItemx{mkdir,--mode,=@var{mode}}
@cindex modes of created directories, setting
Set the file permission bits of created directories to @var{mode},
which uses the same syntax as
@@ -11335,10 +10853,8 @@ incorrect. @xref{Directory Setuid and Setgid}, for how the
set-user-ID and set-group-ID bits of directories are inherited unless
overridden in this way.
-@item -p
-@itemx --parents
-@opindex -p
-@opindex --parents
+@optItem{mkdir,-p,}
+@optItemx{mkdir,--parents,}
@cindex parent directories, creating
Make any missing parent directories for each argument, setting their
file permission bits to @samp{=rwx,u+wx},
@@ -11358,14 +10874,12 @@ To set a parent's special mode bits as well, you can invoke
Setgid}, for how the set-user-ID and set-group-ID bits of
newly-created parent directories are inherited.
-@item -v
-@itemx --verbose
-@opindex -v
-@opindex --verbose
+@optItem{mkdir,-v,}
+@optItemx{mkdir,--verbose,}
Print a message for each created directory. This is most useful with
@option{--parents}.
-@optContext
+@optContext{mkdir}
@end table
@@ -11396,17 +10910,15 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -m @var{mode}
-@itemx --mode=@var{mode}
-@opindex -m
-@opindex --mode
+@optItem{mkfifo,-m,@w{ }@var{mode}}
+@optItemx{mkfifo,--mode,=@var{mode}}
@cindex modes of created FIFOs, setting
Set the mode of created FIFOs to @var{mode}, which is symbolic as in
@command{chmod} and uses @samp{a=rw} (read and write allowed for everyone)
for the point of departure. @var{mode} should specify only file
permission bits. @xref{File permissions}.
-@optContext
+@optContext{mkfifo}
@end table
@@ -11474,16 +10986,14 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -m @var{mode}
-@itemx --mode=@var{mode}
-@opindex -m
-@opindex --mode
+@optItem{mknod,-m,@w{ }@var{mode}}
+@optItemx{mknod,--mode,=@var{mode}}
Set the mode of created files to @var{mode}, which is symbolic as in
@command{chmod} and uses @samp{a=rw} as the point of departure.
@var{mode} should specify only file permission bits.
@xref{File permissions}.
-@optContext
+@optContext{mknod}
@end table
@@ -11528,64 +11038,50 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -f
-@itemx --canonicalize
-@opindex -f
-@opindex --canonicalize
+@optItem{readlink,-f,}
+@optItemx{readlink,--canonicalize,}
Activate canonicalize mode.
If any component of the file name except the last one is missing or unavailable,
@command{readlink} produces no output and exits with a nonzero exit
code. A trailing slash is ignored.
-@item -e
-@itemx --canonicalize-existing
-@opindex -e
-@opindex --canonicalize-existing
+@optItem{readlink,-e,}
+@optItemx{readlink,--canonicalize-existing,}
Activate canonicalize mode.
If any component is missing or unavailable, @command{readlink} produces
no output and exits with a nonzero exit code. A trailing slash
requires that the name resolve to a directory.
-@item -m
-@itemx --canonicalize-missing
-@opindex -m
-@opindex --canonicalize-missing
+@optItem{readlink,-m,}
+@optItemx{readlink,--canonicalize-missing,}
Activate canonicalize mode.
If any component is missing or unavailable, @command{readlink} treats it
as a directory.
-@item -n
-@itemx --no-newline
-@opindex -n
-@opindex --no-newline
+@optItem{readlink,-n,}
+@optItemx{readlink,--no-newline,}
Do not print the output delimiter, when a single @var{file} is specified.
Print a warning if specified along with multiple @var{file}s.
-@item -s
-@itemx -q
-@itemx --silent
-@itemx --quiet
-@opindex -s
-@opindex -q
-@opindex --silent
-@opindex --quiet
+@optItem{readlink,-q,}
+@optItemx{readlink,-s,}
+@optItemx{readlink,--quiet,}
+@optItemx{readlink,--silent,}
Suppress most error messages.
@vindex POSIXLY_CORRECT
This option is on by default if the @env{POSIXLY_CORRECT} environment
variable is not set.
-@item -v
-@itemx --verbose
-@opindex -v
-@opindex --verbose
+@optItem{readlink,-v,}
+@optItemx{readlink,--verbose,}
Report error messages.
@vindex POSIXLY_CORRECT
This option is on by default if the @env{POSIXLY_CORRECT} environment
variable is set.
-@optZero
+@optZero{readlink}
@end table
@@ -11617,15 +11113,12 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item --ignore-fail-on-non-empty
-@opindex --ignore-fail-on-non-empty
+@optItem{rmdir,--ignore-fail-on-non-empty,}
@cindex directory deletion, ignoring failures
Ignore each failure to remove a directory that is non-empty.
-@item -p
-@itemx --parents
-@opindex -p
-@opindex --parents
+@optItem{rmdir,-p,}
+@optItemx{rmdir,--parents,}
@cindex parent directories, removing
Remove @var{directory}, then try to remove each component of @var{directory}.
So, for example, @samp{rmdir -p a/b/c} is similar to @samp{rmdir a/b/c a/b a}.
@@ -11634,10 +11127,8 @@ Use the @option{--ignore-fail-on-non-empty} option to make it so such
a failure does not evoke a diagnostic and does not cause @command{rmdir} to
exit unsuccessfully.
-@item -v
-@itemx --verbose
-@opindex -v
-@opindex --verbose
+@optItem{rmdir,-v,}
+@optItemx{rmdir,--verbose,}
@cindex directory deletion, reporting
Give a diagnostic for each successful removal.
@var{directory} is removed.
@@ -11803,27 +11294,21 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -c
-@itemx --changes
-@opindex -c
-@opindex --changes
+@optItem{chown,-c,}
+@optItemx{chown,--changes,}
@cindex changed owners, verbosely describing
Verbosely describe the action for each @var{file} whose ownership
actually changes.
-@item -f
-@itemx --silent
-@itemx --quiet
-@opindex -f
-@opindex --silent
-@opindex --quiet
+@optItem{chown,-f,}
+@optItemx{chown,--silent,}
+@optItemx{chown,--quiet,}
@cindex error messages, omitting
Do not print error messages about files whose ownership cannot be
changed.
@macro chownFromOption{cmd}
-@item --from=@var{old-owner}
-@opindex --from
+@optItem{\cmd\,--from,=@var{old-owner}}
@cindex symbolic links, changing owner
Change a @var{file}'s ownership only if it has current attributes specified
by @var{old-owner}. @var{old-owner} has the same form as @var{new-owner}
@@ -11857,19 +11342,16 @@ though still not perfect:
@end macro
@chownFromOption{chown}
-@macro symlinkRefOpts
-@item --dereference
-@opindex --dereference
+@macro symlinkRefOpts{cmd}
+@optItem{\cmd\,--dereference,}
@cindex symbolic links, changing owner, group
@findex lchown
Do not act on symbolic links themselves but rather on what they point to.
This is the default when not operating recursively.
@warnOptDerefWithRec
-@item -h
-@itemx --no-dereference
-@opindex -h
-@opindex --no-dereference
+@optItem{\cmd\,-h,}
+@optItemx{\cmd\,--no-dereference,}
@cindex symbolic links, changing owner
@findex lchown
Act on symbolic links themselves instead of what they point to.
@@ -11877,53 +11359,46 @@ This mode relies on the @code{lchown} system call.
On systems that do not provide the @code{lchown} system call,
no diagnostic is issued, but see @option{--verbose}.
@end macro
-@symlinkRefOpts
+@symlinkRefOpts{chown}
-@item --preserve-root
-@opindex --preserve-root
+@optItem{chown,--preserve-root,}
@cindex root directory, disallow recursive modification
Fail upon any attempt to recursively change the root directory, @file{/}.
Without @option{--recursive}, this option has no effect.
@xref{Treating / specially}.
-@item --no-preserve-root
-@opindex --no-preserve-root
+@optItem{chown,--no-preserve-root,}
@cindex root directory, allow recursive modification
Cancel the effect of any preceding @option{--preserve-root} option.
@xref{Treating / specially}.
-@item --reference=@var{ref_file}
-@opindex --reference
+@optItem{chown,--reference,=@var{ref_file}}
Change the user and group of each @var{file} to be the same as those of
@var{ref_file}. If @var{ref_file} is a symbolic link, do not use the
user and group of the symbolic link, but rather those of the file it
refers to.
-@item -v
-@itemx --verbose
-@opindex -v
-@opindex --verbose
+@optItem{chown,-v,}
+@optItemx{chown,--verbose,}
Output a diagnostic for every file processed.
If a symbolic link is encountered during a recursive traversal
on a system without the @code{lchown} system call, and @option{--no-dereference}
is in effect, then issue a diagnostic saying neither the symbolic link nor
its referent is being changed.
-@item -R
-@itemx --recursive
-@opindex -R
-@opindex --recursive
+@optItem{chown,-R,}
+@optItemx{chown,--recursive,}
@cindex recursively changing file ownership
Recursively change ownership of directories and their contents.
-@choptH
+@choptH{chown}
@xref{Traversing symlinks}.
-@choptL
+@choptL{chown}
@warnOptDerefWithRec
@xref{Traversing symlinks}.
-@choptP
+@choptP{chown}
@choptDefault
@xref{Traversing symlinks}.
@@ -11972,72 +11447,60 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -c
-@itemx --changes
-@opindex -c
-@opindex --changes
+@optItem{chgrp,-c,}
+@optItemx{chgrp,--changes,}
@cindex changed files, verbosely describing
Verbosely describe the action for each @var{file} whose group actually
changes.
-@item -f
-@itemx --silent
-@itemx --quiet
-@opindex -f
-@opindex --silent
-@opindex --quiet
+@optItem{chgrp,-f,}
+@optItemx{chgrp,--silent,}
+@optItemx{chgrp,--quiet,}
@cindex error messages, omitting
Do not print error messages about files whose group cannot be
changed.
@chownFromOption{chgrp}
-@symlinkRefOpts
+@symlinkRefOpts{chgrp}
-@item --preserve-root
-@opindex --preserve-root
+@optItem{chgrp,--preserve-root,}
@cindex root directory, disallow recursive modification
Fail upon any attempt to recursively change the root directory, @file{/}.
Without @option{--recursive}, this option has no effect.
@xref{Treating / specially}.
-@item --no-preserve-root
-@opindex --no-preserve-root
+@optItem{chgrp,--no-preserve-root,}
@cindex root directory, allow recursive modification
Cancel the effect of any preceding @option{--preserve-root} option.
@xref{Treating / specially}.
-@item --reference=@var{ref_file}
-@opindex --reference
+@optItem{chgrp,--reference,=@var{ref_file}}
Change the group of each @var{file} to be the same as that of
@var{ref_file}. If @var{ref_file} is a symbolic link, do not use the
group of the symbolic link, but rather that of the file it refers to.
-@item -v
-@itemx --verbose
-@opindex -v
-@opindex --verbose
+@optItem{chgrp,-v,}
+@optItemx{chgrp,--verbose,}
Output a diagnostic for every file processed.
If a symbolic link is encountered during a recursive traversal
on a system without the @code{lchown} system call, and @option{--no-dereference}
is in effect, then issue a diagnostic saying neither the symbolic link nor
its referent is being changed.
-@item -R
-@itemx --recursive
-@opindex -R
-@opindex --recursive
+@optItem{chgrp,-R,}
+@optItemx{chgrp,--recursive,}
@cindex recursively changing group ownership
Recursively change the group ownership of directories and their contents.
-@choptH
+@choptH{chgrp}
@xref{Traversing symlinks}.
-@choptL
+@choptL{chgrp}
@warnOptDerefWithRec
@xref{Traversing symlinks}.
-@choptP
+@choptP{chgrp}
@choptDefault
@xref{Traversing symlinks}.
@@ -12106,82 +11569,67 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -c
-@itemx --changes
-@opindex -c
-@opindex --changes
+@optItem{chmod,-c,}
+@optItemx{chmod,--changes,}
Verbosely describe the action for each @var{file} whose permissions
actually change.
-@item --dereference
-@opindex --dereference
+@optItem{chmod,--dereference,}
@cindex symbolic links, changing mode
Do not act on symbolic links themselves but rather on what they point to.
This is the default for command line arguments, but not for
symbolic links encountered when recursing.
@warnOptDerefWithRec
-@item -h
-@itemx --no-dereference
-@opindex -h
-@opindex --no-dereference
+@optItem{chmod,-h,}
+@optItemx{chmod,--no-dereference,}
@cindex symbolic links, changing mode
Act on symbolic links themselves instead of what they point to.
On systems that do not support this, no diagnostic is issued,
but see @option{--verbose}.
-@item -f
-@itemx --silent
-@itemx --quiet
-@opindex -f
-@opindex --silent
-@opindex --quiet
+@optItem{chmod,-f,}
+@optItemx{chmod,--silent,}
+@optItemx{chmod,--quiet,}
@cindex error messages, omitting
Do not print error messages about files whose permissions cannot be
changed.
-@item --preserve-root
-@opindex --preserve-root
+@optItem{chmod,--preserve-root,}
@cindex root directory, disallow recursive modification
Fail upon any attempt to recursively change the root directory, @file{/}.
Without @option{--recursive}, this option has no effect.
@xref{Treating / specially}.
-@item --no-preserve-root
-@opindex --no-preserve-root
+@optItem{chmod,--no-preserve-root,}
@cindex root directory, allow recursive modification
Cancel the effect of any preceding @option{--preserve-root} option.
@xref{Treating / specially}.
-@item -v
-@itemx --verbose
-@opindex -v
-@opindex --verbose
+@optItem{chmod,-v,}
+@optItemx{chmod,--verbose,}
Verbosely describe the action or non-action taken for every @var{file}.
-@item --reference=@var{ref_file}
-@opindex --reference
+@optItem{chmod,--reference,=@var{ref_file}}
Change the mode of each @var{file} to be the same as that of @var{ref_file}.
@xref{File permissions}.
If @var{ref_file} is a symbolic link, do not use the mode
of the symbolic link, but rather that of the file it refers to.
-@item -R
-@itemx --recursive
-@opindex -R
-@opindex --recursive
+@optItem{chmod,-R,}
+@optItemx{chmod,--recursive,}
@cindex recursively changing access permissions
Recursively change permissions of directories and their contents.
-@choptH
+@choptH{chmod}
@choptDefault
@xref{Traversing symlinks}.
-@choptL
+@choptL{chmod}
@warnOptDerefWithRec
@xref{Traversing symlinks}.
-@choptP
+@choptP{chmod}
@xref{Traversing symlinks}.
@end table
@@ -12197,8 +11645,8 @@ chmod 644 foo
chmod a=r,u+w foo
# Add user and group execute permissions to FOO.
-chmod +110 file
-chmod ug+x file
+chmod +110 foo
+chmod ug+x foo
# Set file permissions of DIR and subsidiary files to
# be the umask default, assuming execute permissions for
@@ -12262,27 +11710,21 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -a
-@itemx --time=atime
+@optItem{touch,-a,}
+@optItemx{touch,--time,=atime}
@itemx --time=access
@itemx --time=use
-@opindex -a
-@opindex --time
@opindex atime@r{, changing}
@opindex access @r{time, changing}
@opindex use @r{time, changing}
Change the access timestamp only. @xref{File timestamps}.
-@item -c
-@itemx --no-create
-@opindex -c
-@opindex --no-create
+@optItem{touch,-c,}
+@optItemx{touch,--no-create,}
Do not warn about or create files that do not exist.
-@item -d @var{time}
-@itemx --date=@var{time}
-@opindex -d
-@opindex --date
+@optItem{touch,-d,@w{ }@var{time}}
+@optItemx{touch,--date,=@var{time}}
@opindex time
Use @var{time} instead of the current time. It can contain month names,
time zones, @samp{am} and @samp{pm}, @samp{yesterday}, etc. For
@@ -12293,15 +11735,12 @@ minutes east of UTC@. @xref{Date input formats}.
File systems that do not support high-resolution timestamps
silently ignore any excess precision here.
-@item -f
-@opindex -f
+@optItem{touch,-f,}
@cindex BSD @command{touch} compatibility
Ignored; for compatibility with BSD versions of @command{touch}.
-@item -h
-@itemx --no-dereference
-@opindex -h
-@opindex --no-dereference
+@optItem{touch,-h,}
+@optItemx{touch,--no-dereference,}
@cindex symbolic links, changing time
@findex lutimes
Attempt to change the timestamps of a symbolic link, rather than what
@@ -12316,19 +11755,16 @@ long enough to be observable. When coupled with option @option{-r}, a
reference timestamp is taken from a symbolic link rather than the file
it refers to.
-@item -m
+@optItem{touch,-m,}
@itemx --time=mtime
@itemx --time=modify
-@opindex -m
@opindex --time
@opindex mtime@r{, changing}
@opindex modify @r{time, changing}
Change the modification timestamp only.
-@item -r @var{file}
-@itemx --reference=@var{file}
-@opindex -r
-@opindex --reference
+@optItem{touch,-r,@w{ }@var{file}}
+@optItemx{touch,--reference,=@var{file}}
Use the times of the reference @var{file} instead of the current time.
If this option is combined with the @option{--date=@var{time}}
(@option{-d @var{time}}) option, the reference @var{file}'s time is
@@ -12338,7 +11774,7 @@ equal to five seconds before the corresponding timestamp for @file{foo}.
If @var{file} is a symbolic link, the reference timestamp is taken
from the target of the symlink, unless @option{-h} was also in effect.
-@item -t [[@var{cc}]@var{yy}]@var{mmddhhmm}[.@var{ss}]
+@optItem{touch,-t,@w{ }[[@var{cc}]@var{yy}]@var{mmddhhmm}[.@var{ss}]}
@cindex leap seconds
Use the argument (optional four-digit or two-digit years, months,
days, hours, minutes, optional seconds) instead of the current time.
@@ -12435,10 +11871,8 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -a
-@itemx --all
-@opindex -a
-@opindex --all
+@optItem{df,-a,}
+@optItemx{df,--all,}
@cindex ignore file systems
Include in the listing dummy, duplicate, or inaccessible file systems, which
are omitted by default. Dummy file systems are typically special purpose
@@ -12449,55 +11883,44 @@ Inaccessible file systems are those which are mounted but subsequently
over-mounted by another file system at that point, or otherwise inaccessible
due to permissions of the mount point etc.
-@item -B @var{size}
-@itemx --block-size=@var{size}
-@opindex -B
-@opindex --block-size
+@optItem{df,-B,@w{ }@var{size}}
+@optItemx{df,--block-size,=@var{size}}
@cindex file system sizes
Scale sizes by @var{size} before printing them (@pxref{Block size}).
For example, @option{-BG} prints sizes in units of 1,073,741,824 bytes.
-@optHumanReadable
+@optHumanReadable{df}
-@item -H
-@opindex -H
+@optItem{df,-H,}
Equivalent to @option{--si}.
-@item -i
-@itemx --inodes
-@opindex -i
-@opindex --inodes
+@optItem{df,-i,}
+@optItemx{df,--inodes,}
@cindex inode usage
List inode usage information instead of block usage. An inode (short
for index node) contains information about a file such as its owner,
permissions, timestamps, and location on the file system.
-@item -k
-@opindex -k
+@optItem{df,-k,}
@cindex kibibytes for file system sizes
Print sizes in 1024-byte blocks, overriding the default block size
(@pxref{Block size}).
This option is equivalent to @option{--block-size=1K}.
-@item -l
-@itemx --local
-@opindex -l
-@opindex --local
+@optItem{df,-l,}
+@optItemx{df,--local,}
@cindex file system types, limiting output to certain
Limit the listing to local file systems. By default, remote file systems
are also listed.
-@item --no-sync
-@opindex --no-sync
+@optItem{df,--no-sync,}
@cindex file system space, retrieving old data more quickly
Do not invoke the @code{sync} system call before getting any usage data.
This may make @command{df} run significantly faster on systems with many
file systems, but on some systems (notably Solaris) the results may be slightly
out of date. This is the default.
-@item --output
-@itemx --output[=@var{field_list}]
-@opindex --output
+@optItem{df,--output,[=@var{field_list}]}
Use the output format defined by @var{field_list}, or print all fields if
@var{field_list} is omitted. In the latter case, the order of the columns
conforms to the order of the field descriptions below.
@@ -12558,10 +11981,8 @@ df --o
@end example
-@item -P
-@itemx --portability
-@opindex -P
-@opindex --portability
+@optItem{df,-P,}
+@optItemx{df,--portability,}
@cindex one-line output format
@cindex POSIX output format
@cindex portable output format
@@ -12587,18 +12008,16 @@ variables. However, the default block size is still affected by
otherwise. @xref{Block size}.
@end enumerate
-@optSi
+@optSi{df}
-@item --sync
-@opindex --sync
+@optItem{df,--sync,}
@cindex file system space, retrieving current data more slowly
Invoke the @code{sync} system call before getting any usage data. On
some systems (notably Solaris), doing this yields more up to date results,
but in general this option makes @command{df} much slower, especially when
there are many or very busy file systems.
-@item --total
-@opindex --total
+@optItem{df,--total,}
@cindex grand total of file system size, usage and available space
Print a grand total of all arguments after all arguments have
been processed. This can be used to find out the total size, usage
@@ -12612,19 +12031,15 @@ If there is no @var{source} column (see @option{--output}), then
@command{df} prints @samp{total} into the @var{target} column,
if present.
-@item -t @var{fstype}
-@itemx --type=@var{fstype}
-@opindex -t
-@opindex --type
+@optItem{df,-t,@w{ }@var{fstype}}
+@optItemx{df,--type,=@var{fstype}}
@cindex file system types, limiting output to certain
Limit the listing to file systems of type @var{fstype}. Multiple
file system types can be specified by giving multiple @option{-t} options.
By default, nothing is omitted.
-@item -T
-@itemx --print-type
-@opindex -T
-@opindex --print-type
+@optItem{df,-T,}
+@optItemx{df,--print-type,}
@cindex file system types, printing
Print each file system's type. The types printed here are the same ones
you can include or exclude with @option{-t} and @option{-x}. The particular
@@ -12670,15 +12085,13 @@ File systems used by MS-Windows / MS-DOS.
@end table
-@item -x @var{fstype}
-@itemx --exclude-type=@var{fstype}
-@opindex -x
-@opindex --exclude-type
+@optItem{df,-x,@w{ }@var{fstype}}
+@optItemx{df,--exclude-type,=@var{fstype}}
Limit the listing to file systems not of type @var{fstype}.
Multiple file system types can be eliminated by giving multiple
@option{-x} options. By default, no file system types are omitted.
-@item -v
+@optItem{df,-v,}
Ignored; for compatibility with System V versions of @command{df}.
@end table
@@ -12727,16 +12140,14 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@optNull
+@optNull{du}
-@item -a
-@itemx --all
-@opindex -a
-@opindex --all
+@optItem{du,-a,}
+@optItemx{du,--all,}
Show counts for all files, not just directories.
-@item --apparent-size
-@opindex --apparent-size
+@optItem{du,-A,}
+@optItemx{du,--apparent-size,}
Print apparent sizes, rather than file system usage. The apparent size of a
file is the number of bytes reported by @code{wc -c} on regular files,
or more generally, @code{ls -l --block-size=1} or @code{stat --format=%s}.
@@ -12757,42 +12168,32 @@ file systems, it actually uses almost no space.
Apparent sizes are meaningful only for regular files and symbolic links.
Other file types do not contribute to apparent size.
-@item -B @var{size}
-@itemx --block-size=@var{size}
-@opindex -B
-@opindex --block-size
+@optItem{du,-B,@w{ }@var{size}}
+@optItemx{du,--block-size,=@var{size}}
@cindex file sizes
Scale sizes by @var{size} before printing them (@pxref{Block size}).
For example, @option{-BG} prints sizes in units of 1,073,741,824 bytes.
-@item -b
-@itemx --bytes
-@opindex -b
-@opindex --bytes
+@optItem{du,-b,}
+@optItemx{du,--bytes,}
Equivalent to @code{--apparent-size --block-size=1}.
-@item -c
-@itemx --total
-@opindex -c
-@opindex --total
+@optItem{du,-c,}
+@optItemx{du,--total,}
@cindex grand total of file system space
Print a grand total of all arguments after all arguments have
been processed. This can be used to find out the total file system usage of
a given set of files or directories.
-@item -D
-@itemx --dereference-args
-@opindex -D
-@opindex --dereference-args
+@optItem{du,-D,}
+@optItemx{du,--dereference-args,}
Dereference symbolic links that are command line arguments.
Does not affect other symbolic links. This is helpful for finding
out the file system usage of directories, such as @file{/usr/tmp}, which
are often symbolic links.
-@item -d @var{depth}
-@itemx --max-depth=@var{depth}
-@opindex -d @var{depth}
-@opindex --max-depth=@var{depth}
+@optItem{du,-d,@w{ }@var{depth}}
+@optItemx{du,--max-depth,=@var{depth}}
@cindex limiting output of @command{du}
Show the total for each directory (and file if @option{--all}) that is at
most MAX_DEPTH levels down from the root of the hierarchy. The root
@@ -12801,14 +12202,12 @@ is at level 0, so @code{du --max-depth=0} is equivalent to @code{du -s}.
@c --files0-from=FILE
@filesZeroFromOption{du,, with the @option{--total} (@option{-c}) option}
-@item -H
-@opindex -H
+@optItem{du,-H,}
Equivalent to @option{--dereference-args} (@option{-D}).
-@optHumanReadable
+@optHumanReadable{du}
-@item --inodes
-@opindex --inodes
+@optItem{du,--inodes,}
@cindex inode usage, dereferencing in @command{du}
List inode usage information instead of block usage.
This option is useful for finding directories which contain many files, and
@@ -12819,67 +12218,53 @@ It can well be combined with the options @option{-a}, @option{-c},
@option{-x}; however, passing other options regarding the block size, for
example @option{-b}, @option{-m} and @option{--apparent-size}, is ignored.
-@item -k
-@opindex -k
+@optItem{du,-k,}
@cindex kibibytes for file sizes
Print sizes in 1024-byte blocks, overriding the default block size
(@pxref{Block size}).
This option is equivalent to @option{--block-size=1K}.
-@item -L
-@itemx --dereference
-@opindex -L
-@opindex --dereference
+@optItem{du,-L,}
+@optItemx{du,--dereference,}
@cindex symbolic links, dereferencing in @command{du}
Dereference symbolic links (show the file system space used by the file
or directory that the link points to instead of the space used by
the link).
-@item -l
-@itemx --count-links
-@opindex -l
-@opindex --count-links
+@optItem{du,-l,}
+@optItemx{du,--count-links,}
@cindex hard links, counting in @command{du}
Count the size of all files, even if they have appeared already (as a
hard link).
-@item -m
-@opindex -m
+@optItem{du,-m,}
@cindex mebibytes for file sizes
Print sizes in 1,048,576-byte blocks, overriding the default block size
(@pxref{Block size}).
This option is equivalent to @option{--block-size=1M}.
-@item -P
-@itemx --no-dereference
-@opindex -P
-@opindex --no-dereference
+@optItem{du,-P,}
+@optItemx{du,--no-dereference,}
@cindex symbolic links, dereferencing in @command{du}
For each symbolic link encountered by @command{du},
consider the file system space used by the symbolic link itself.
-@item -S
-@itemx --separate-dirs
-@opindex -S
-@opindex --separate-dirs
+@optItem{du,-S,}
+@optItemx{du,--separate-dirs,}
Normally, in the output of @command{du} (when not using @option{--summarize}),
the size listed next to a directory name, @var{d}, represents the sum
of sizes of all entries beneath @var{d} as well as the size of @var{d} itself.
With @option{--separate-dirs}, the size reported for a directory name,
@var{d}, will exclude the size of any subdirectories.
-@optSi
+@optSi{du}
-@item -s
-@itemx --summarize
-@opindex -s
-@opindex --summarize
+@optItem{du,-s,}
+@optItemx{du,--summarize,}
Display only a total for each argument.
-@item -t @var{size}
-@itemx --threshold=@var{size}
-@opindex -t
-@opindex --threshold
+@optItem{du,-t,@w{ }@var{size}}
+@optItemx{du,--threshold,=@var{size}}
Exclude entries based on a given @var{size}. The @var{size} refers to used
blocks in normal mode (@pxref{Block size}), or inodes count in conjunction
with the @option{--inodes} option.
@@ -12899,6 +12284,9 @@ When combined with the @option{--apparent-size} option, the
When combined with the @option{--inodes} option, it elides entries
based on inode counts.
+When combined with the @option{--total} option, the @option{--threshold}
+option does not prevent entries from being added to the grand total.
+
Here's how you would use @option{--threshold} to find directories with a size
greater than or equal to 200 megabytes:
@@ -12922,8 +12310,7 @@ du --inodes -x --threshold=20000 /
@end example
-@item --time
-@opindex --time
+@optItem{du,--time,}
@cindex last modified dates, displaying in @command{du}
Show the most recent modification timestamp (mtime) of any file in the
directory, or any of its subdirectories. @xref{File timestamps}.
@@ -12946,8 +12333,7 @@ the directory, or any of its subdirectories. @xref{File timestamps}.
Show the most recent access timestamp (atime) of any file in the
directory, or any of its subdirectories. @xref{File timestamps}.
-@item --time-style=@var{style}
-@opindex --time-style
+@optItem{du,--time-style,=@var{style}}
@cindex time style
List timestamps in style @var{style}. This option has an effect only if
the @option{--time} option is also specified. The @var{style} should
@@ -12989,26 +12375,21 @@ the newline and any later characters are ignored; if @env{TIME_STYLE}
begins with @samp{posix-} the @samp{posix-} is ignored; and if
@env{TIME_STYLE} is @samp{locale} it is ignored.
-@item -X @var{file}
-@itemx --exclude-from=@var{file}
-@opindex -X @var{file}
-@opindex --exclude-from=@var{file}
+@optItem{du,-X,@w{ }@var{file}}
+@optItemx{du,--exclude-from,=@var{file}}
@cindex excluding files from @command{du}
Like @option{--exclude}, except take the patterns to exclude from @var{file},
one per line. If @var{file} is @samp{-}, take the patterns from standard
input.
-@item --exclude=@var{pattern}
-@opindex --exclude=@var{pattern}
+@optItem{du,--exclude,=@var{pattern}}
@cindex excluding files from @command{du}
When recursing, skip subdirectories or files matching @var{pattern}.
For example, @code{du --exclude='*.o'} excludes files whose names
end in @samp{.o}.
-@item -x
-@itemx --one-file-system
-@opindex -x
-@opindex --one-file-system
+@optItem{du,-x,}
+@optItemx{du,--one-file-system,}
@cindex one file system, restricting @command{du} to
Skip directories that are on different file systems from the one that
the argument being processed is on.
@@ -13075,27 +12456,22 @@ also give information about the files the links point to.
@table @samp
-@item -L
-@itemx --dereference
-@opindex -L
-@opindex --dereference
+@optItem{stat,-L,}
+@optItemx{stat,--dereference,}
@cindex symbolic links, dereferencing in @command{stat}
Change how @command{stat} treats symbolic links.
With this option, @command{stat} acts on the file referenced
by each symbolic link argument.
Without it, @command{stat} acts on any symbolic link argument directly.
-@item -f
-@itemx --file-system
-@opindex -f
-@opindex --file-system
+@optItem{stat,-f,}
+@optItemx{stat,--file-system,}
@cindex file systems
Report information about the file systems where the given files are located
instead of information about the files themselves.
This option implies the @option{-L} option.
-@item --cached=@var{mode}
-@opindex --cached=@var{mode}
+@optItem{stat,--cached,=@var{mode}}
@cindex attribute caching
Control how attributes are read from the file system;
if supported by the system. This allows one to
@@ -13116,10 +12492,8 @@ Leave the caching behavior to the underlying file system.
@end table
-@item -c
-@itemx --format=@var{format}
-@opindex -c
-@opindex --format=@var{format}
+@optItem{stat,-c,}
+@optItemx{stat,--format,=@var{format}}
@cindex output format
Use @var{format} rather than the default format.
@var{format} is automatically newline-terminated, so
@@ -13131,8 +12505,7 @@ $ stat --format=%d:%i / /usr
2057:2
@end example
-@item --printf=@var{format}
-@opindex --printf=@var{format}
+@optItem{stat,--printf,=@var{format}}
@cindex output format
Use @var{format} rather than the default format.
Like @option{--format}, but interpret backslash escapes,
@@ -13146,10 +12519,8 @@ $ stat --printf='%d:%i\n' / /usr
2057:2
@end example
-@item -t
-@itemx --terse
-@opindex -t
-@opindex --terse
+@optItem{stat,-t,}
+@optItemx{stat,--terse,}
@cindex terse output
Print the information in terse form, suitable for parsing by other programs.
@@ -13333,15 +12704,13 @@ synchronization method with the following options. Also see
@ref{Common options}.
@table @samp
-@item -d
-@itemx --data
-@opindex --data
+@optItem{sync,-d,}
+@optItemx{sync,--data,}
Use fdatasync(2) to sync only the data for the file,
and any metadata required to maintain file system consistency.
-@item -f
-@itemx --file-system
-@opindex --file-system
+@optItem{sync,-f,}
+@optItemx{sync,--file-system,}
Synchronize all the I/O waiting for the file systems that contain the file,
using the syscall syncfs(2). You would usually @emph{not} specify
this option if passing a device node like @samp{/dev/sda} for example,
@@ -13381,28 +12750,20 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -c
-@itemx --no-create
-@opindex -c
-@opindex --no-create
+@optItem{truncate,-c,}
+@optItemx{truncate,--no-create,}
Do not create files that do not exist.
-@item -o
-@itemx --io-blocks
-@opindex -o
-@opindex --io-blocks
+@optItem{truncate,-o,}
+@optItemx{truncate,--io-blocks,}
Treat @var{size} as number of I/O blocks of the @var{file} rather than bytes.
-@item -r @var{rfile}
-@itemx --reference=@var{rfile}
-@opindex -r
-@opindex --reference
+@optItem{truncate,-r,@w{ }@var{rfile}}
+@optItemx{truncate,--reference,=@var{rfile}}
Base the size of each @var{file} on the size of @var{rfile}.
-@item -s @var{size}
-@itemx --size=@var{size}
-@opindex -s
-@opindex --size
+@optItem{truncate,-s,@w{ }@var{size}}
+@optItemx{truncate,--size,=@var{size}}
Set or adjust the size of each @var{file} according to @var{size}.
@var{size} is in bytes unless @option{--io-blocks} is specified.
@multiplierSuffixesNoBlocks{size}
@@ -13469,12 +12830,10 @@ Options must precede operands, and the normally-special argument
@var{string}.
@table @samp
-@item -n
-@opindex -n
+@optItem{echo,-n,}
Do not output the trailing newline.
-@item -e
-@opindex -e
+@optItem{echo,-e,}
@cindex backslash escapes
Enable interpretation of the following backslash-escaped characters in
each @var{string}:
@@ -13513,8 +12872,7 @@ the eight-bit value that is the hexadecimal number @var{hh}
(one or two hexadecimal digits)
@end table
-@item -E
-@opindex -E
+@optItem{echo,-E,}
@cindex backslash escapes
Disable interpretation of backslash escapes in each @var{string}.
This is the default. If @option{-e} and @option{-E} are both
@@ -13707,8 +13065,14 @@ $ recode BIG5..JAVA < sample.txt \
> sample.sh
@end example
-The only options are a lone @option{--help} or
-@option{--version}. @xref{Common options}.
+@macro printDash{command}
+To output an argument that begins with @samp{-}, precede it with
+@option{--}, e.g., @samp{\command\ -- --help}.
+@end macro
+
+The only options are a lone @option{--help} or @option{--version}.
+@printDash{printf}
+@xref{Common options}.
Options must precede operands.
@exitstatus
@@ -13727,8 +13091,7 @@ given, it prints @samp{y} followed by a newline forever until killed.
Upon a write error, @command{yes} exits with status @samp{1}.
The only options are a lone @option{--help} or @option{--version}.
-To output an argument that begins with
-@samp{-}, precede it with @option{--}, e.g., @samp{yes -- --help}.
+@printDash{yes}
@xref{Common options}.
@@ -13893,47 +13256,38 @@ but not all files are the same!)
@table @samp
-@item -b @var{file}
-@opindex -b
+@optItem{test,-b,@w{ }@var{file}}
@cindex block special check
True if @var{file} exists and is a block special device.
-@item -c @var{file}
-@opindex -c
+@optItem{test,-c,@w{ }@var{file}}
@cindex character special check
True if @var{file} exists and is a character special device.
-@item -d @var{file}
-@opindex -d
+@optItem{test,-d,@w{ }@var{file}}
@cindex directory check
True if @var{file} exists and is a directory.
-@item -f @var{file}
-@opindex -f
+@optItem{test,-f,@w{ }@var{file}}
@cindex regular file check
True if @var{file} exists and is a regular file.
-@item -h @var{file}
-@itemx -L @var{file}
-@opindex -L
-@opindex -h
+@optItem{test,-h,@w{ }@var{file}}
+@optItemx{test,-L,@w{ }@var{file}}
@cindex symbolic link check
True if @var{file} exists and is a symbolic link.
Unlike all other file-related tests, this test does not dereference
@var{file} if it is a symbolic link.
-@item -p @var{file}
-@opindex -p
+@optItem{test,-p,@w{ }@var{file}}
@cindex named pipe check
True if @var{file} exists and is a named pipe.
-@item -S @var{file}
-@opindex -S
+@optItem{test,-S,@w{ }@var{file}}
@cindex socket check
True if @var{file} exists and is a socket.
-@item -t @var{fd}
-@opindex -t
+@optItem{test,-t,@w{ }@var{fd}}
@cindex terminal check
True if @var{fd} is a file descriptor that is associated with a
terminal.
@@ -13951,44 +13305,36 @@ These options test for particular access permissions.
@table @samp
-@item -g @var{file}
-@opindex -g
+@optItem{test,-g,@w{ }@var{file}}
@cindex set-group-ID check
True if @var{file} exists and has its set-group-ID bit set.
-@item -k @var{file}
-@opindex -k
+@optItem{test,-k,@w{ }@var{file}}
@cindex sticky bit check
True if @var{file} exists and has its @dfn{sticky} bit set.
-@item -r @var{file}
-@opindex -r
+@optItem{test,-r,@w{ }@var{file}}
@cindex readable file check
True if @var{file} exists and the user has read access.
-@item -u @var{file}
-@opindex -u
+@optItem{test,-u,@w{ }@var{file}}
@cindex set-user-ID check
True if @var{file} exists and has its set-user-ID bit set.
-@item -w @var{file}
-@opindex -w
+@optItem{test,-w,@w{ }@var{file}}
@cindex writable file check
True if @var{file} exists and the user has write access.
-@item -x @var{file}
-@opindex -x
+@optItem{test,-x,@w{ }@var{file}}
@cindex executable file check
True if @var{file} exists and the user has execute access
(or search permission, if it is a directory).
-@item -O @var{file}
-@opindex -O
+@optItem{test,-O,@w{ }@var{file}}
@cindex owned by effective user ID check
True if @var{file} exists and is owned by the current effective user ID.
-@item -G @var{file}
-@opindex -G
+@optItem{test,-G,@w{ }@var{file}}
@cindex owned by effective group ID check
True if @var{file} exists and is owned by the current effective group ID.
@@ -14003,28 +13349,29 @@ These options test other file characteristics.
@table @samp
-@item -e @var{file}
-@opindex -e
+@optItem{test,-e,@w{ }@var{file}}
@cindex existence-of-file check
True if @var{file} exists.
-@item -s @var{file}
-@opindex -s
+@optItem{test,-s,@w{ }@var{file}}
@cindex nonempty file check
True if @var{file} exists and has a size greater than zero.
+@optAnchor{test,-nt}
@item @var{file1} -nt @var{file2}
@opindex -nt
@cindex newer-than file check
True if @var{file1} is newer (according to modification date) than
@var{file2}, or if @var{file1} exists and @var{file2} does not.
+@optAnchor{test,-ot}
@item @var{file1} -ot @var{file2}
@opindex -ot
@cindex older-than file check
True if @var{file1} is older (according to modification date) than
@var{file2}, or if @var{file2} exists and @var{file1} does not.
+@optAnchor{test,-ef}
@item @var{file1} -ef @var{file2}
@opindex -ef
@cindex same file check
@@ -14032,8 +13379,7 @@ True if @var{file1} is older (according to modification date) than
True if @var{file1} and @var{file2} have the same device and inode
numbers, i.e., if they are hard links to each other.
-@item -N @var{file}
-@opindex -N
+@optItem{test,-N,@w{ }@var{file}}
@cindex mtime-greater-atime file check
True if @var{file} exists and has been modified (mtime) since it was
last read (atime).
@@ -14058,14 +13404,12 @@ The quotes here prevent the wrong arguments from being passed to
@table @samp
-@item -z @var{string}
-@opindex -z
+@optItem{test,-z,@w{ }@var{string}}
@cindex zero-length string check
True if the length of @var{string} is zero.
-@item -n @var{string}
+@optItem{test,-n,@w{ }@var{string}}
@itemx @var{string}
-@opindex -n
@cindex nonzero-length string check
True if the length of @var{string} is nonzero.
@@ -14111,6 +13455,12 @@ which evaluates to the length of @var{string}.
@table @samp
+@optAnchor{test,-eq}
+@optAnchor{test,-ne}
+@optAnchor{test,-lt}
+@optAnchor{test,-le}
+@optAnchor{test,-gt}
+@optAnchor{test,-ge}
@item @var{arg1} -eq @var{arg2}
@itemx @var{arg1} -ne @var{arg2}
@itemx @var{arg1} -lt @var{arg2}
@@ -14179,6 +13529,7 @@ The @samp{!} should be specified to the left
of a binary expression, I.e., @samp{! 1 -gt 2}
rather than @samp{1 ! -gt 2}.
+@optAnchor{test,-a}
@item @var{expr1} -a @var{expr2}
@opindex -a
@cindex logical and operator
@@ -14187,6 +13538,7 @@ True if both @var{expr1} and @var{expr2} are true.
@samp{-a} is left associative,
and has a higher precedence than @samp{-o}.
+@optAnchor{test,-o}
@item @var{expr1} -o @var{expr2}
@opindex -o
@cindex logical or operator
@@ -14507,23 +13859,17 @@ conforms to POSIX and treats @samp{-} as a file name.
The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -a
-@itemx --append
-@opindex -a
-@opindex --append
+@optItem{tee,-a,}
+@optItemx{tee,--append,}
Append standard input to the given files rather than overwriting
them.
-@item -i
-@itemx --ignore-interrupts
-@opindex -i
-@opindex --ignore-interrupts
+@optItem{tee,-i,}
+@optItemx{tee,--ignore-interrupts,}
Ignore interrupt signals.
-@item -p
-@itemx --output-error[=@var{mode}]
-@opindex -p
-@opindex --output-error
+@optItem{tee,-p,}
+@optItemx{tee,--output-error,[=@var{mode}]}
Adjust the behavior with errors on the outputs.
In summary @option{-p} allows @command{tee} to operate in a more
appropriate manner with pipes, and to continue to process data
@@ -14565,27 +13911,27 @@ you often want to verify its signature or checksum right away.
The inefficient way to do it is simply:
@example
-wget https://example.com/some.iso && sha1sum some.iso
+wget https://example.com/some.iso && cksum -a sha2 -l 256 some.iso
@end example
One problem with the above is that it makes you wait for the
-download to complete before starting the time-consuming SHA1 computation.
-Perhaps even more importantly, the above requires reading
+download to complete before starting the time-consuming SHA-256
+computation. Perhaps even more importantly, the above requires reading
the DVD image a second time (the first was from the network).
The efficient way to do it is to interleave the download
-and SHA1 computation. Then, you'll get the checksum for
+and SHA-256 computation. Then, you'll get the checksum for
free, because the entire process parallelizes so well:
@example
# slightly contrived, to demonstrate process substitution
wget -O - https://example.com/dvd.iso \
- | tee >(sha1sum > dvd.sha1) > dvd.iso
+ | tee >(cksum -a sha2 -l 256 > dvd.sha256) > dvd.iso
@end example
That makes @command{tee} write not just to the expected output file,
-but also to a pipe running @command{sha1sum} and saving the final
-checksum in a file named @file{dvd.sha1}.
+but also to a pipe running @command{cksum} and saving the final
+checksum in a file named @file{dvd.sha256}.
However, this example relies on a feature of modern shells
called @dfn{process substitution}
@@ -14606,17 +13952,17 @@ a more conventional and portable use of @command{tee} is even better:
@example
wget -O - https://example.com/dvd.iso \
- | tee dvd.iso | sha1sum > dvd.sha1
+ | tee dvd.iso | cksum -a sha2 -l 256 > dvd.sha256
@end example
You can extend this example to make @command{tee} write to two processes,
-computing MD5 and SHA1 checksums in parallel. In this case,
+computing SHA-256 and SHA3-256 checksums in parallel. In this case,
process substitution is required:
@example
wget -O - https://example.com/dvd.iso \
- | tee >(sha1sum > dvd.sha1) \
- >(md5sum > dvd.md5) \
+ | tee >(cksum -a sha2 -l 256 > dvd.sha256) \
+ >(cksum -a sha3 -l 256 > dvd.sha3) \
> dvd.iso
@end example
@@ -14677,8 +14023,9 @@ PIPE_BUF size at a time), that's possible with a construct like:
@example
tardir=your-pkg-M.N
tar chof - "$tardir" \
- | tee >(md5sum --tag) > >(sha256sum --tag) \
- | sort | gpg --clearsign > your-pkg-M.N.tar.sig
+ | tee >(cksum -a sha2 -l 256) > >(cksum -a sha3 -l 256) \
+ | sort | tee your-pkg-M.N.checksums \
+ | gpg --detach-sign --armor -o your-pkg-M.N.checksums.sig
@end example
@exitstatus
@@ -14746,22 +14093,18 @@ Options must precede operands.
@table @samp
-@item -a
-@itemx --multiple
-@opindex -a
-@opindex --multiple
+@optItem{basename,-a,}
+@optItemx{basename,--multiple,}
Support more than one argument. Treat every argument as a @var{name}.
With this, an optional @var{suffix} must be specified using the
@option{-s} option.
-@item -s @var{suffix}
-@itemx --suffix=@var{suffix}
-@opindex -s
-@opindex --suffix
+@optItem{basename,-s,@w{ }@var{suffix}}
+@optItemx{basename,--suffix,=@var{suffix}}
Remove a trailing @var{suffix}.
This option implies the @option{-a} option.
-@optZero
+@optZero{basename}
@end table
@@ -14816,7 +14159,7 @@ The program accepts the following option. Also see @ref{Common options}.
@table @samp
-@optZero
+@optZero{dirname}
@end table
@@ -14874,8 +14217,7 @@ Options must precede operands.
@table @samp
-@item -p
-@opindex -p
+@optItem{pathchk,-p,}
Instead of performing checks based on the underlying file system,
print an error message if any of these conditions is true:
@@ -14893,13 +14235,11 @@ The length of a file name or one of its components exceeds the
POSIX minimum limits for portability.
@end enumerate
-@item -P
-@opindex -P
+@optItem{pathchk,-P,}
Print an error message if a file name is empty, or if it contains a component
that begins with @samp{-}.
-@item --portability
-@opindex --portability
+@optItem{pathchk,--portability,}
Print an error message if a file name is not portable to all POSIX
hosts. This option is equivalent to @samp{-p -P}.
@@ -15019,36 +14359,28 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -d
-@itemx --directory
-@opindex -d
-@opindex --directory
+@optItem{mktemp,-d,}
+@optItemx{mktemp,--directory,}
Create a directory rather than a file. The directory will have read,
write, and search permissions for the current user, but no permissions
for the group or others; these permissions are reduced if the current
umask is more restrictive.
-@item -q
-@itemx --quiet
-@opindex -q
-@opindex --quiet
+@optItem{mktemp,-q,}
+@optItemx{mktemp,--quiet,}
Suppress diagnostics about failure to create a file or directory. The
exit status will still reflect whether a file was created.
-@item -u
-@itemx --dry-run
-@opindex -u
-@opindex --dry-run
+@optItem{mktemp,-u,}
+@optItemx{mktemp,--dry-run,}
Generate a temporary name that does not name an existing file, without
changing the file system contents. Using the output of this command
to create a new file is inherently unsafe, as there is a window of
time between generating the name and using it where another process
can create an object by the same name.
-@item -p @var{dir}
-@itemx --tmpdir[=@var{dir}]
-@opindex -p
-@opindex --tmpdir
+@optItem{mktemp,-p,@w{ }@var{dir}}
+@optItemx{mktemp,--tmpdir,[=@var{dir}]}
Treat @var{template} relative to the directory @var{dir}. If
@var{dir} is not specified (only possible with the long option
@option{--tmpdir}) or is the empty string, use the value of
@@ -15057,8 +14389,7 @@ specified, @var{template} must not be absolute. However,
@var{template} can still contain slashes, although intermediate
directories must already exist.
-@item --suffix=@var{suffix}
-@opindex --suffix
+@optItem{mktemp,--suffix,=@var{suffix}}
Append @var{suffix} to the @var{template}. @var{suffix} must not
contain slash. If @option{--suffix} is specified, @var{template} must
end in @samp{X}; if it is not specified, then an appropriate
@@ -15067,8 +14398,7 @@ end in @samp{X}; if it is not specified, then an appropriate
@var{template} and for the creation of a @var{suffix} that starts with
@samp{X}.
-@item -t
-@opindex -t
+@optItem{mktemp,-t,}
Treat @var{template} as a single file relative to the value of
@env{TMPDIR} if available, or to the directory specified by
@option{-p}, otherwise to @samp{/tmp}. @var{template} must not
@@ -15122,60 +14452,45 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -E
-@itemx --canonicalize
-@opindex -E
-@opindex --canonicalize
-
+@optItem{realpath,-E,}
+@optItemx{realpath,--canonicalize,}
Do not fail merely because the named file does not exist.
Although this is the default behavior of GNU @code{realpath},
POSIX does not require this default, so portable applications should
use an explicit @option{-E}.
-@item -e
-@itemx --canonicalize-existing
-@opindex -e
-@opindex --canonicalize-existing
+@optItem{realpath,-e,}
+@optItemx{realpath,--canonicalize-existing,}
Fail if the named file does not exist.
A trailing slash requires that the name resolve to a directory.
-@item -m
-@itemx --canonicalize-missing
-@opindex -m
-@opindex --canonicalize-missing
+@optItem{realpath,-m,}
+@optItemx{realpath,--canonicalize-missing,}
If any component of a specified file name is missing or unavailable,
treat it as a directory.
-@item -L
-@itemx --logical
-@opindex -L
-@opindex --logical
+@optItem{realpath,-L,}
+@optItemx{realpath,--logical,}
Symbolic links are resolved in the specified file names,
but they are resolved after any subsequent @samp{..} components are processed.
-@item -P
-@itemx --physical
-@opindex -P
-@opindex --physical
+@optItem{realpath,-P,}
+@optItemx{realpath,--physical,}
Symbolic links are resolved in the specified file names,
and they are resolved before any subsequent @samp{..} components are processed.
This is the default mode of operation.
-@item -q
-@itemx --quiet
-@opindex -q
-@opindex --quiet
+@optItem{realpath,-q,}
+@optItemx{realpath,--quiet,}
Suppress diagnostic messages for specified file names.
-@item --relative-to=@var{dir}
-@opindex --relative-to
+@optItem{realpath,--relative-to,=@var{dir}}
@cindex relpath
Print the resolved file names relative to the specified directory.
This option honors the @option{-m} and @option{-e} options
pertaining to file existence.
-@item --relative-base=@var{dir}
-@opindex --relative-base
+@optItem{realpath,--relative-base,=@var{dir}}
Print the resolved file names as relative @emph{if} the files
are descendants of @var{dir}.
Otherwise, print the resolved file names as absolute.
@@ -15184,18 +14499,15 @@ pertaining to file existence.
For details about combining @option{--relative-to} and @option{--relative-base},
@pxref{Realpath usage examples}.
-@item -s
-@itemx --strip
-@itemx --no-symlinks
-@opindex -s
-@opindex --strip
-@opindex --no-symlinks
+@optItem{realpath,-s,}
+@optItemx{realpath,--strip,}
+@optItemx{realpath,--no-symlinks,}
Do not resolve symbolic links. Only resolve references to
@samp{/./}, @samp{/../} and remove extra @samp{/} characters.
When combined with the @option{-m} option, realpath operates
only on the file name, and does not touch any actual file.
-@optZero
+@optZero{realpath}
@end table
@@ -15318,19 +14630,15 @@ pwd [@var{option}]@dots{}
The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -L
-@itemx --logical
-@opindex -L
-@opindex --logical
+@optItem{pwd,-L,}
+@optItemx{pwd,--logical,}
If the contents of the environment variable @env{PWD} provide an
absolute name of the current directory with no @samp{.} or @samp{..}
components, but possibly with symbolic links, then output those
contents. Otherwise, fall back to default @option{-P} handling.
-@item -P
-@itemx --physical
-@opindex -P
-@opindex --physical
+@optItem{pwd,-P,}
+@optItemx{pwd,--physical,}
Print a fully resolved name for the current directory. That is, all
components of the printed name will be actual directory names -- none
will be symbolic links.
@@ -15376,17 +14684,13 @@ the terminal line operation, as described below.
The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -a
-@itemx --all
-@opindex -a
-@opindex --all
+@optItem{stty,-a,}
+@optItemx{stty,--all,}
Print all current settings in human-readable form. This option may not
be used in combination with any line settings.
-@item -F @var{device}
-@itemx --file=@var{device}
-@opindex -F
-@opindex --file
+@optItem{stty,-F,@w{ }@var{device}}
+@optItemx{stty,--file,=@var{device}}
Set the line opened by the file name specified in @var{device} instead of
the tty line connected to standard input. This option is necessary
because opening a POSIX tty requires use of the
@@ -15395,15 +14699,17 @@ until the carrier detect line is high if
the @code{clocal} flag is not set. Hence, it is not always possible
to allow the shell to open the device in the traditional manner.
-@item -g
-@itemx --save
-@opindex -g
-@opindex --save
+@optItem{stty,-g,}
+@optItemx{stty,--save,}
@cindex machine-readable @command{stty} output
Print all current settings in a form that can be used as an argument to
another @command{stty} command to restore the current settings. This option
may not be used in combination with any line settings.
+POSIX-1.2024 allows implementations of the @code{-g} option to exclude
+the terminal window size from the output. This is the behavior of GNU
+@command{stty}.
+
@end table
Many settings can be turned off by preceding them with a @samp{-}.
@@ -16095,7 +15401,7 @@ The program accepts the following option. Also see @ref{Common options}.
@table @samp
-@optNull
+@optNull{printenv}
@end table
@@ -16128,14 +15434,13 @@ The program accepts the following option. Also see @ref{Common options}.
@table @samp
-@item -s
-@itemx --silent
-@itemx --quiet
-@opindex -s
-@opindex --silent
-@opindex --quiet
+@optItem{tty,-s,}
+@optItemx{tty,--silent,}
+@optItemx{tty,--quiet,}
Print nothing; only return an exit status.
+POSIX.1-2001 removed the @option{-s} option. Portable shell scripts
+should redirect standard output to @file{/dev/null} instead.
@end table
@cindex exit status of @command{tty}
@@ -16204,42 +15509,33 @@ The options cause @command{id} to print only part of the above information.
Also see @ref{Common options}.
@table @samp
-@item -g
-@itemx --group
-@opindex -g
-@opindex --group
+@optItem{id,-a,}
+Ignored; for compatibility with other versions of @command{id}.
+
+@optItem{id,-g,}
+@optItemx{id,--group,}
Print only the group ID.
-@item -G
-@itemx --groups
-@opindex -G
-@opindex --groups
+@optItem{id,-G,}
+@optItemx{id,--groups,}
Print only the group ID and the supplementary groups.
-@item -n
-@itemx --name
-@opindex -n
-@opindex --name
+@optItem{id,-n,}
+@optItemx{id,--name,}
Print the user or group name instead of the ID number. Requires
@option{-u}, @option{-g}, or @option{-G}.
-@item -r
-@itemx --real
-@opindex -r
-@opindex --real
+@optItem{id,-r,}
+@optItemx{id,--real,}
Print the real, instead of effective, user or group ID@. Requires
@option{-u}, @option{-g}, or @option{-G}.
-@item -u
-@itemx --user
-@opindex -u
-@opindex --user
+@optItem{id,-u,}
+@optItemx{id,--user,}
Print only the user ID.
-@item -Z
-@itemx --context
-@opindex -Z
-@opindex --context
+@optItem{id,-Z,}
+@optItemx{id,--context,}
@cindex SELinux
@cindex security context
Print only the security context of the process, which is generally
@@ -16247,10 +15543,8 @@ the user's security context inherited from the parent process.
If neither SELinux or SMACK is enabled then print a warning and
set the exit status to 1.
-@item -z
-@itemx --zero
-@opindex -z
-@opindex --zero
+@optItem{id,-z,}
+@optItemx{id,--zero,}
Delimit output items with ASCII NUL characters.
This option is not permitted when using the default format.
When multiple users are specified, and the @option{--groups} option
@@ -16422,95 +15716,70 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -a
-@itemx --all
-@opindex -a
-@opindex --all
+@optItem{who,-a,}
+@optItemx{who,--all,}
Same as @samp{-b -d --login -p -r -t -T -u}.
-@item -b
-@itemx --boot
-@opindex -b
-@opindex --boot
+@optItem{who,-b,}
+@optItemx{who,--boot,}
Print the date and time of last system boot.
-@item -d
-@itemx --dead
-@opindex -d
-@opindex --dead
+@optItem{who,-d,}
+@optItemx{who,--dead,}
Print information corresponding to dead processes.
-@item -H
-@itemx --heading
-@opindex -H
-@opindex --heading
+@optItem{who,-H,}
+@optItemx{who,--heading,}
Print a line of column headings.
-@item -l
-@itemx --login
-@opindex -l
-@opindex --login
+@optItem{who,-l,}
+@optItemx{who,--login,}
List only the entries that correspond to processes via which the
system is waiting for a user to login. The user name is always @samp{LOGIN}.
-@macro lookupOption
-@item --lookup
-@opindex --lookup
+@macro lookupOption{cmd}
+@optItem{\cmd\,--lookup,}
Attempt to canonicalize hostnames found in utmp through a DNS lookup.
This is not the default because of potential delays.
@end macro
-@lookupOption
+@lookupOption{who}
-@item -m
-@opindex -m
+@optItem{who,-m,}
Same as @samp{who am i}.
-@item -p
-@itemx --process
-@opindex -p
-@opindex --process
+@optItem{who,-p,}
+@optItemx{who,--process,}
List active processes spawned by init.
-@item -q
-@itemx --count
-@opindex -q
-@opindex --count
+@optItem{who,-q,}
+@optItemx{who,--count,}
Print only the login names and the number of users logged on.
Overrides all other options.
-@item -r
-@itemx --runlevel
-@opindex -r
-@opindex --runlevel
+@optItem{who,-r,}
+@optItemx{who,--runlevel,}
Print the current (and maybe previous) run-level of the init process.
-@item -s
-@opindex -s
+@optItem{who,-s,}
+@optItemx{who,--short,}
Ignored; for compatibility with other versions of @command{who}.
-@item -t
-@itemx --time
-@opindex -t
-@opindex --time
+@optItem{who,-t,}
+@optItemx{who,--time,}
Print last system clock change.
-@item -u
-@opindex -u
+@optItem{who,-u,}
+@optItemx{who,--users,}
@cindex idle time
After the login time, print the number of hours and minutes that the
user has been idle. @samp{.} means the user was active in the last minute.
@samp{old} means the user has been idle for more than 24 hours.
-@item -w
-@itemx -T
-@itemx --mesg
-@itemx --message
-@itemx --writable
-@opindex -w
-@opindex -T
-@opindex --mesg
-@opindex --message
-@opindex --writable
+@optItem{who,-w,}
+@optItemx{who,-T,}
+@optItemx{who,--mesg,}
+@optItemx{who,--message,}
+@optItemx{who,--writable,}
@cindex message status
@pindex write@r{, allowed}
After each login name print a character indicating the user's message status:
@@ -16544,50 +15813,41 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -l
-@opindex -l
+@optItem{pinky,-l,}
Produce long format output.
When producing long output at least one @var{username} must be given.
If @var{username} cannot be found, the real name is printed as
@samp{???} and the home directory and shell are omitted.
-@item -b
-@opindex -b
+@optItem{pinky,-b,}
Omit the user's home directory and shell when printing in long format.
-@item -h
-@opindex -h
+@optItem{pinky,-h,}
Omit the user's project file when printing in long format.
-@item -p
-@opindex -p
+@optItem{pinky,-p,}
Omit the user's plan file when printing in long format.
-@item -s
-@opindex -s
+@optItem{pinky,-s,}
Produce short format output. This is the default behavior when no
options are given.
-@item -f
-@opindex -f
+@optItem{pinky,-f,}
Omit the column headings when printing in short format.
-@item -w
-@opindex -w
+@optItem{pinky,-w,}
Omit the user's full name when printing in short format.
-@item -i
-@opindex -i
+@optItem{pinky,-i,}
Omit the user's full name and remote host when printing in short
format.
-@item -q
-@opindex -q
+@optItem{pinky,-q,}
Omit the user's full name, remote host, and idle time when printing in
short format.
-@lookupOption
+@lookupOption{pinky}
@end table
@@ -16625,20 +15885,20 @@ Synopses:
@example
date [@var{option}]@dots{} [+@var{format}]
-date [-u|--utc|--universal] @c this avoids a newline in the output
-[ MMDDhhmm[[CC]YY][.ss] ]
+date [@var{option}]@dots{} @c Avoid a newline in the output.
+@var{mm}@var{dd}@var{hh}@var{mm}[[@var{cc}]@var{yy}][.@var{ss}]
@end example
The @command{date} command displays the date and time.
With the @option{--set} (@option{-s}) option, or with
-@samp{MMDDhhmm[[CC]YY][.ss]},
-it sets the date and time.
+@samp{@var{mm}@var{dd}@var{hh}@var{mm}[[@var{cc}]@var{yy}][.@var{ss}]},
+it sets the date and time before displaying it.
@vindex LC_TIME
-Invoking @command{date} with no @var{format} argument is equivalent to invoking
+Invoking @command{date} with no operands is equivalent to invoking
it with a default format that depends on the @env{LC_TIME} locale category.
-In the default C locale, this format is @samp{'+%a %b %e %H:%M:%S %Z %Y'},
-so the output looks like @samp{Thu Jul @ 9 17:00:00 EDT 2020}.
+In the default C locale, this format is @samp{+%a %b %e %H:%M:%S %Z %Y},
+so the output looks like @samp{Thu Jul @ 9 17:00:00 EDT 2026}.
@vindex TZ
Normally, @command{date} uses the time zone rules indicated by the
@@ -16666,8 +15926,7 @@ is not set. @xref{TZ Variable,, Specifying the Time Zone with
@cindex time formats
@cindex formatting times
If given an argument that starts with a @samp{+}, @command{date} prints the
-current date and time (or the date and time specified by the
-@option{--date} option, see below) in the format defined by that argument,
+date and time in the format defined by that argument,
which is similar to that of the @code{strftime} function. Except for
conversion specifiers, which start with @samp{%}, characters in the
format string are printed unchanged. The conversion specifiers are
@@ -17071,26 +16330,26 @@ hardware clock may need to be updated from the system clock, which
might not happen automatically on your system.
To set the clock, you can use the @option{--set} (@option{-s}) option
-(@pxref{Options for date}). To set the clock without using GNU
-extensions, you can give @command{date} an argument of the form
-@samp{MMDDhhmm[[CC]YY][.ss]} where each two-letter
+(@pxref{Options for date}) or a operand of the form
+@samp{@var{mm}@var{dd}@var{hh}@var{mm}[[@var{cc}]@var{yy}][.@var{ss}]}
+where each two-letter
component stands for two digits with the following meanings:
@table @var
-@item MM
-month
-@item DD
+@item mm
+month (this is the first @var{mm})
+@item dd
day within month
@item hh
hour
@item mm
-minute
-@item CC
+minute (this is the second @var{mm})
+@item cc
first two digits of year (optional)
-@item YY
+@item yy
last two digits of year (optional)
@item ss
-second (optional)
+second (optional; this is a GNU extension)
@end table
The @option{--date} and @option{--set} options may not be used with an
@@ -17114,10 +16373,8 @@ I.e.: @option{--date}, @option{--file}, @option{--reference},
@table @samp
-@item -d @var{datestr}
-@itemx --date=@var{datestr}
-@opindex -d
-@opindex --date
+@optItem{date,-d,@w{ }@var{datestr}}
+@optItemx{date,--date,=@var{datestr}}
@cindex parsing date strings
@cindex date strings, parsing
@cindex arbitrary date strings, parsing
@@ -17139,28 +16396,23 @@ date -d "$(LC_TIME=C date)"
@end example
@xref{Date input formats}.
-@item --debug
-@opindex --debug
+@optItem{date,--debug,}
@cindex debugging date strings
@cindex date strings, debugging
@cindex arbitrary date strings, debugging
Annotate the parsed date, display the effective time zone, and warn about
potential misuse.
-@item -f @var{datefile}
-@itemx --file=@var{datefile}
-@opindex -f
-@opindex --file
+@optItem{date,-f,@w{ }@var{datefile}}
+@optItemx{date,--file,=@var{datefile}}
Parse each line in @var{datefile} as with @option{-d} and display the
resulting date and time. If @var{datefile} is @samp{-}, use standard
input. This is useful when you have many dates to process, because the
system overhead of starting up the @command{date} executable many times can
be considerable.
-@item -I[@var{timespec}]
-@itemx --iso-8601[=@var{timespec}]
-@opindex -I[@var{timespec}]
-@opindex --iso-8601[=@var{timespec}]
+@optItem{date,-I,[@var{timespec}]}
+@optItemx{date,--iso-8601,[=@var{timespec}]}
Display the date using an ISO 8601 format, @samp{%Y-%m-%d}.
The argument @var{timespec} specifies the number of additional
@@ -17199,15 +16451,12 @@ for the @option{--date} (@option{-d}) and @option{--file}
@end macro
@dateParseNote
-@item -r @var{file}
-@itemx --reference=@var{file}
-@opindex -r
-@opindex --reference
+@optItem{date,-r,@w{ }@var{file}}
+@optItemx{date,--reference,=@var{file}}
Display the date and time of the last modification of @var{file},
instead of the current date and time.
-@item --resolution
-@opindex --resolution
+@optItem{date,--resolution,}
Display the timestamp resolution instead of the time.
Current clock timestamps that are output by @command{date}
are integer multiples of the timestamp resolution.
@@ -17219,10 +16468,8 @@ the output is:
0.001000000
@end example
-@item -R
-@itemx --rfc-email
-@opindex -R
-@opindex --rfc-email
+@optItem{date,-R,}
+@optItemx{date,--rfc-email,}
Display the date and time using the format @samp{%a, %d %b %Y %H:%M:%S
%z}, evaluated in the C locale so abbreviations are always in English.
For example:
@@ -17242,8 +16489,7 @@ For compatibility with older versions of @command{date},
@option{--rfc-2822} and @option{--rfc-822} are aliases for
@option{--rfc-email}.
-@item --rfc-3339=@var{timespec}
-@opindex --rfc-3339=@var{timespec}
+@optItem{date,--rfc-3339,=@var{timespec}}
Display the date using a format specified by
@uref{https://datatracker.ietf.org/doc/rfc3339/, Internet
RFC 3339}. This is like @option{--iso-8601}, except that a space rather
@@ -17273,19 +16519,14 @@ This is like the format @samp{%Y-%m-%d %H:%M:%S.%N%:z}.
@end table
-@item -s @var{datestr}
-@itemx --set=@var{datestr}
-@opindex -s
-@opindex --set
+@optItem{date,-s,@w{ }@var{datestr}}
+@optItemx{date,--set,=@var{datestr}}
Set the date and time to @var{datestr}. See @option{-d} above.
See also @ref{Setting the time}.
-@item -u
-@itemx --utc
-@itemx --universal
-@opindex -u
-@opindex --utc
-@opindex --universal
+@optItem{date,-u,}
+@optItemx{date,--utc,}
+@optItemx{date,--universal,}
@cindex Coordinated Universal Time
@cindex UTC
@cindex Greenwich Mean Time
@@ -17531,15 +16772,13 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item --all
-@opindex --all
+@optItem{nproc,--all,}
Print the number of installed processors on the system, which may
be greater than the number online or available to the current process.
The @env{OMP_NUM_THREADS} or @env{OMP_THREAD_LIMIT} environment variables,
or cgroup CPU quotas, are not honored in this case.
-@item --ignore=@var{number}
-@opindex --ignore
+@optItem{nproc,--ignore,=@var{number}}
If possible, exclude this @var{number} of processing units.
@end table
@@ -17585,17 +16824,13 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -a
-@itemx --all
-@opindex -a
-@opindex --all
+@optItem{uname,-a,}
+@optItemx{uname,--all,}
Print all of the below information, except omit the processor type
and the hardware platform name if they are unknown.
-@item -i
-@itemx --hardware-platform
-@opindex -i
-@opindex --hardware-platform
+@optItem{uname,-i,}
+@optItemx{uname,--hardware-platform,}
@cindex implementation, hardware
@cindex hardware platform
@cindex platform, hardware
@@ -17604,54 +16839,42 @@ Print the hardware platform name
Print @samp{unknown} if this information is not available.
This is non-portable, even across GNU/Linux distributions.
-@item -m
-@itemx --machine
-@opindex -m
-@opindex --machine
+@optItem{uname,-m,}
+@optItemx{uname,--machine,}
@cindex machine type
@cindex hardware class
@cindex hardware type
Print the machine hardware name (sometimes called the hardware class
or hardware type).
-@item -n
-@itemx --nodename
-@opindex -n
-@opindex --nodename
+@optItem{uname,-n,}
+@optItemx{uname,--nodename,}
@cindex hostname
@cindex node name
@cindex network node name
Print the network node hostname.
-@item -p
-@itemx --processor
-@opindex -p
-@opindex --processor
+@optItem{uname,-p,}
+@optItemx{uname,--processor,}
@cindex host processor type
Print the processor type (sometimes called the instruction set
architecture or ISA).
Print @samp{unknown} if this information is not available.
This is non-portable, even across GNU/Linux distributions.
-@item -o
-@itemx --operating-system
-@opindex -o
-@opindex --operating-system
+@optItem{uname,-o,}
+@optItemx{uname,--operating-system,}
@cindex operating system name
Print the name of the operating system.
-@item -r
-@itemx --kernel-release
-@opindex -r
-@opindex --kernel-release
+@optItem{uname,-r,}
+@optItemx{uname,--kernel-release,}
@cindex kernel release
@cindex release of kernel
Print the kernel release.
-@item -s
-@itemx --kernel-name
-@opindex -s
-@opindex --kernel-name
+@optItem{uname,-s,}
+@optItemx{uname,--kernel-name,}
@cindex kernel name
@cindex name of kernel
Print the kernel name.
@@ -17664,10 +16887,8 @@ differ. Some operating systems (e.g., FreeBSD, HP-UX) have the same
name as their underlying kernels; others (e.g., GNU/Linux, Solaris)
do not.
-@item -v
-@itemx --kernel-version
-@opindex -v
-@opindex --kernel-version
+@optItem{uname,-v,}
+@optItemx{uname,--kernel-version,}
@cindex kernel version
@cindex version of kernel
Print the kernel version.
@@ -17811,78 +17032,60 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item --dereference
-@opindex --dereference
+@optItem{chcon,--dereference,}
Do not affect symbolic links but what they refer to; this is the default.
-@item -h
-@itemx --no-dereference
-@opindex -h
-@opindex --no-dereference
+@optItem{chcon,-h,}
+@optItemx{chcon,--no-dereference,}
@cindex no dereference
Affect the symbolic links themselves instead of any referenced file.
-@item --reference=@var{rfile}
-@opindex --reference
+@optItem{chcon,--reference,=@var{rfile}}
@cindex reference file
Use @var{rfile}'s security context rather than specifying a @var{context} value.
-@item -R
-@itemx --recursive
-@opindex -R
-@opindex --recursive
+@optItem{chcon,-R,}
+@optItemx{chcon,--recursive,}
Operate on files and directories recursively.
-@item --preserve-root
-@opindex --preserve-root
+@optItem{chcon,--preserve-root,}
Refuse to operate recursively on the root directory, @file{/},
when used together with the @option{--recursive} option.
@xref{Treating / specially}.
-@item --no-preserve-root
-@opindex --no-preserve-root
+@optItem{chcon,--no-preserve-root,}
Do not treat the root directory, @file{/}, specially when operating
recursively; this is the default.
@xref{Treating / specially}.
-@choptH
+@choptH{chcon}
@xref{Traversing symlinks}.
-@choptL
+@choptL{chcon}
@xref{Traversing symlinks}.
-@choptP
+@choptP{chcon}
@xref{Traversing symlinks}.
-@item -v
-@itemx --verbose
-@opindex -v
-@opindex --verbose
+@optItem{chcon,-v,}
+@optItemx{chcon,--verbose,}
@cindex diagnostic
Output a diagnostic for every file processed.
-@item -u @var{user}
-@itemx --user=@var{user}
-@opindex -u
-@opindex --user
+@optItem{chcon,-u,@w{ }@var{user}}
+@optItemx{chcon,--user,=@var{user}}
Set user @var{user} in the target security context.
-@item -r @var{role}
-@itemx --role=@var{role}
-@opindex -r
-@opindex --role
+@optItem{chcon,-r,@w{ }@var{role}}
+@optItemx{chcon,--role,=@var{role}}
Set role @var{role} in the target security context.
-@item -t @var{type}
-@itemx --type=@var{type}
-@opindex -t
-@opindex --type
+@optItem{chcon,-t,@w{ }@var{type}}
+@optItemx{chcon,--type,=@var{type}}
Set type @var{type} in the target security context.
-@item -l @var{range}
-@itemx --range=@var{range}
-@opindex -l
-@opindex --range
+@optItem{chcon,-l,@w{ }@var{range}}
+@optItemx{chcon,--range,=@var{range}}
Set range @var{range} in the target security context.
@end table
@@ -17900,9 +17103,9 @@ Set range @var{range} in the target security context.
Synopses:
@example
-runcon @var{context} @var{command} [@var{args}]
-runcon [ -c ] [-u @var{user}] [-r @var{role}] [-t @var{type}]@c
- [-l @var{range}] @var{command} [@var{args}]
+runcon [@var{context} @var{command} [@var{arg}]@dots{}]
+runcon [-c] [-u @var{user}] [-r @var{role}] [-t @var{type}]@c
+ [-l @var{range}] @var{command} [@var{arg}@dots{}]
@end example
Run @var{command} with completely-specified @var{context}, or with
@@ -17928,34 +17131,24 @@ than the process would normally have.
@table @samp
-@item -c
-@itemx --compute
-@opindex -c
-@opindex --compute
+@optItem{runcon,-c,}
+@optItemx{runcon,--compute,}
Compute process transition context before modifying.
-@item -u @var{user}
-@itemx --user=@var{user}
-@opindex -u
-@opindex --user
+@optItem{runcon,-u,@w{ }@var{user}}
+@optItemx{runcon,--user,=@var{user}}
Set user @var{user} in the target security context.
-@item -r @var{role}
-@itemx --role=@var{role}
-@opindex -r
-@opindex --role
+@optItem{runcon,-r,@w{ }@var{role}}
+@optItemx{runcon,--role,=@var{role}}
Set role @var{role} in the target security context.
-@item -t @var{type}
-@itemx --type=@var{type}
-@opindex -t
-@opindex --type
+@optItem{runcon,-t,@w{ }@var{type}}
+@optItemx{runcon,--type,=@var{type}}
Set type @var{type} in the target security context.
-@item -l @var{range}
-@itemx --range=@var{range}
-@opindex -l
-@opindex --range
+@optItem{runcon,-l,@w{ }@var{range}}
+@optItemx{runcon,--range,=@var{range}}
Set range @var{range} in the target security context.
@end table
@@ -18028,16 +17221,14 @@ Options must precede operands.
@table @samp
-@item --groups=@var{groups}
-@opindex --groups
+@optItem{chroot,--groups,=@var{groups}}
Use this option to override the supplementary @var{groups} to be
used by the new process.
The items in the list (names or numeric IDs) must be separated by commas.
Use @samp{--groups=''} to disable the supplementary group look-up
implicit in the @option{--userspec} option.
-@item --userspec=@var{user}[:@var{group}]
-@opindex --userspec
+@optItem{chroot,--userspec,=@var{user}[:@var{group}]}
By default, @var{command} is run with the same credentials
as the invoking process.
Use this option to run it as a different @var{user} and/or with a
@@ -18046,8 +17237,7 @@ If a @var{user} is specified then the supplementary groups
are set according to the system defined list for that user,
unless overridden with the @option{--groups} option.
-@item --skip-chdir
-@opindex --skip-chdir
+@optItem{chroot,--skip-chdir,}
Use this option to not change the working directory to @file{/} after changing
the root directory to @var{newroot}, i.e., inside the chroot.
This option is only permitted when @var{newroot} is the old @file{/} directory,
@@ -18236,34 +17426,25 @@ Options must precede operands.
@table @samp
-@optNull
+@optNull{env}
-@item -a @var{arg}
-@itemx --argv0=@var{arg}
-@opindex -a
-@opindex --argv0
+@optItem{env,-a,@w{ }@var{arg}}
+@optItemx{env,--argv0,=@var{arg}}
Override the zeroth argument passed to the command being executed.
Without this option a default value of @var{command} is used.
-@item -u @var{name}
-@itemx --unset=@var{name}
-@opindex -u
-@opindex --unset
+@optItem{env,-u,@w{ }@var{name}}
+@optItemx{env,--unset,=@var{name}}
Remove variable @var{name} from the environment, if it was in the
environment.
-@item -
-@itemx -i
-@itemx --ignore-environment
-@opindex -
-@opindex -i
-@opindex --ignore-environment
+@optItem{env,-,}
+@optItemx{env,-i,}
+@optItemx{env,--ignore-environment,}
Start with an empty environment, ignoring the inherited environment.
-@item -C @var{dir}
-@itemx --chdir=@var{dir}
-@opindex -C
-@opindex --chdir
+@optItem{env,-C,@w{ }@var{dir}}
+@optItemx{env,--chdir,=@var{dir}}
Change the working directory to @var{dir} before invoking @var{command}.
This differs from the shell built-in @command{cd} in that it starts
@var{command} as a subprocess rather than altering the shell's own working
@@ -18279,7 +17460,7 @@ chroot /chroot env --chdir=/srv true
env --chdir=/build FOO=bar timeout 5 true
@end example
-@item --default-signal[=@var{sig}]
+@optItem{env,--default-signal,[=@var{sig}]}
Unblock and reset signal @var{sig} to its default signal handler.
Without @var{sig} all known signals are unblocked and reset to their defaults.
Multiple signals can be comma-separated. An empty @var{sig} argument is a no-op.
@@ -18309,7 +17490,7 @@ trap '' PIPE && sh -c 'env --default-signal=PIPE seq inf | head -n1'
@end example
-@item --ignore-signal[=@var{sig}]
+@optItem{env,--ignore-signal,[=@var{sig}]}
Ignore signal @var{sig} when running a program. Without @var{sig} all
known signals are set to ignore. Multiple signals can be comma-separated.
An empty @var{sig} argument is a no-op. The following command runs @command{seq}
@@ -18334,18 +17515,16 @@ set to default while @samp{SIGINT} is ignored:
env --default-signal=INT,PIPE --ignore-signal=INT
@end example
-@item --block-signal[=@var{sig}]
+@optItem{env,--block-signal,[=@var{sig}]}
Block signal(s) @var{sig} from being delivered. Without @var{sig} all
known signals are set to blocked. Multiple signals can be comma-separated.
An empty @var{sig} argument is a no-op.
-@item --list-signal-handling
+@optItem{env,--list-signal-handling,}
List blocked or ignored signals to standard error, before executing a command.
-@item -v
-@itemx --debug
-@opindex -v
-@opindex --debug
+@optItem{env,-v,}
+@optItemx{env,--debug,}
Show verbose information for each processing step.
@example
@@ -18361,10 +17540,8 @@ Linux
When combined with @option{-S} it is recommended to list @option{-v}
first, e.g. @command{env -vS'string'}.
-@item -S @var{string}
-@itemx --split-string=@var{string}
-@opindex -S
-@opindex --split-string
+@optItem{env,-S,@w{ }@var{string}}
+@optItemx{env,--split-string,=@var{string}}
@cindex shebang arguments
@cindex scripts arguments
@cindex env in scripts
@@ -18787,10 +17964,8 @@ The program accepts the following option. Also see @ref{Common options}.
Options must precede operands.
@table @samp
-@item -n @var{adjustment}
-@itemx --adjustment=@var{adjustment}
-@opindex -n
-@opindex --adjustment
+@optItem{nice,-n,@w{ }@var{adjustment}}
+@optItemx{nice,--adjustment,=@var{adjustment}}
Add @var{adjustment} instead of 10 to the command's niceness. If
@var{adjustment} is negative and you lack appropriate privileges,
@command{nice} issues a warning but otherwise acts as if you specified
@@ -18974,22 +18149,16 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -i @var{mode}
-@itemx --input=@var{mode}
-@opindex -i
-@opindex --input
+@optItem{stdbuf,-i,@w{ }@var{mode}}
+@optItemx{stdbuf,--input,=@var{mode}}
Adjust the standard input stream buffering.
-@item -o @var{mode}
-@itemx --output=@var{mode}
-@opindex -o
-@opindex --output
+@optItem{stdbuf,-o,@w{ }@var{mode}}
+@optItemx{stdbuf,--output,=@var{mode}}
Adjust the standard output stream buffering.
-@item -e @var{mode}
-@itemx --error=@var{mode}
-@opindex -e
-@opindex --error
+@optItem{stdbuf,-e,@w{ }@var{mode}}
+@optItemx{stdbuf,--error,=@var{mode}}
Adjust the standard error stream buffering.
@end table
@@ -19057,10 +18226,8 @@ The program accepts the following options. Also see @ref{Common options}.
Options must precede operands.
@table @samp
-@item -f
-@itemx --foreground
-@opindex -f
-@opindex --foreground
+@optItem{timeout,-f,}
+@optItemx{timeout,--foreground,}
Don't create a separate background program group, so that
the managed @var{command} can use the foreground TTY normally.
This is needed to support two situations when timing out commands,
@@ -19079,10 +18246,8 @@ as it's generally not needed with foreground processes, and can
cause intermittent signal delivery issues with programs that are monitors
themselves (like GDB for example).
-@item -k @var{duration}
-@itemx --kill-after=@var{duration}
-@opindex -k
-@opindex --kill-after
+@optItem{timeout,-k,@w{ }@var{duration}}
+@optItemx{timeout,--kill-after,=@var{duration}}
Ensure the monitored @var{command} is killed by also sending a @samp{KILL}
signal.
@@ -19099,26 +18264,20 @@ either because the signal was blocked or ignored, or if the @var{command} takes
too long (e.g. for cleanup work) to terminate itself within a certain amount
of time.
-@item -p
-@itemx --preserve-status
-@opindex -p
-@opindex --preserve-status
+@optItem{timeout,-p,}
+@optItemx{timeout,--preserve-status,}
Return the exit status of the managed @var{command} on timeout, rather than
a specific exit status indicating a timeout. This is useful if the
managed @var{command} supports running for an indeterminate amount of time.
-@item -s @var{signal}
-@itemx --signal=@var{signal}
-@opindex -s
-@opindex --signal
+@optItem{timeout,-s,@w{ }@var{signal}}
+@optItemx{timeout,--signal,=@var{signal}}
Send this @var{signal} to @var{command} on timeout, rather than the
default @samp{TERM} signal. @var{signal} may be a name like @samp{HUP}
or a number. @xref{Signal specifications}.
-@item -v
-@itemx --verbose
-@opindex -v
-@opindex --verbose
+@optItem{timeout,-v,}
+@optItemx{timeout,--verbose,}
Diagnose to standard error, any signal sent upon timeout.
@end table
@@ -19243,18 +18402,30 @@ The first form of the @command{kill} command succeeds if every @var{pid}
argument specifies at least one process that the signal was sent to.
The second form of the @command{kill} command lists signal information.
-Either the @option{-l} or @option{--list} option, or the @option{-t}
-or @option{--table} option must be specified. Without any
-@var{signal} argument, all supported signals are listed. The output
-of @option{-l} or @option{--list} is a list of the signal names, one
-per line; if @var{signal} is already a name, the signal number is
-printed instead. The output of @option{-t} or @option{--table} is a
-table of signal numbers, names, and descriptions. This form of the
-@command{kill} command succeeds if all @var{signal} arguments are valid
-and if there is no output error.
-
-The @command{kill} command also supports the @option{--help} and
-@option{--version} options. @xref{Common options}.
+This form of the @command{kill} command succeeds if all @var{signal}
+arguments are valid and if there is no output error.
+
+The program accepts the following options. Also see @ref{Common options}.
+
+@table @samp
+
+@optAnchor{kill,-SIGNAL}
+@optItem{kill,-s,}
+@optItemx{kill,--signal,}
+Specify the name or number of the signal to be sent.
+
+@optItem{kill,-l,}
+@optItemx{kill,--list,}
+List the supported signal names one per line if the @var{signal} option
+is omitted. If one or more @var{signal} options are provided, they are
+converted between signal names and signal numbers.
+
+@optItem{kill,-t,}
+@optItemx{kill,--table,}
+This option behaves the same way as @option{--list} except it prints a
+table of signal numbers, names, and descriptions.
+
+@end table
A @var{signal} may be a signal name like @samp{HUP}, or a signal
number like @samp{1}, or an exit status of a process terminated by the
@@ -19367,10 +18538,8 @@ numbers from standard input, delimited by newlines, tabs, or spaces.
The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item -h
-@itemx --exponents
-@opindex -h
-@opindex --exponents
+@optItem{factor,-h,}
+@optItemx{factor,--exponents,}
print factors in the form @math{p^e}, rather than repeating
the prime @samp{p}, @samp{e} times. If the exponent @samp{e} is 1,
then it is omitted.
@@ -19452,19 +18621,15 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
-@item --debug
-@opindex --debug
+@optItem{numfmt,--debug,}
Print (to standard error) warning messages about possible erroneous usage.
-@item -d @var{d}
-@itemx --delimiter=@var{d}
-@opindex -d
-@opindex --delimiter
+@optItem{numfmt,-d,}
+@optItemx{numfmt,--delimiter,=@var{d}}
Use the character @var{d} as input field separator (default: newline or blank).
Using non-default delimiter turns off automatic padding.
-@item --field=@var{fields}
-@opindex --field
+@optItem{numfmt,--field,=@var{fields}}
Convert the number in input field @var{fields} (default: 1).
@var{fields} supports @command{cut} style field ranges:
@@ -19477,8 +18642,7 @@ N-M from N'th to M'th field (inclusive)
@end example
-@item --format=@var{format}
-@opindex --format
+@optItem{numfmt,--format,=@var{format}}
Use printf-style floating FORMAT string. The @var{format} string must contain
one @samp{%f} directive, optionally with @samp{'}, @samp{-}, @samp{0}, width
or precision modifiers. The @samp{'} modifier will enable @option{--grouping},
@@ -19489,32 +18653,26 @@ number, up to the specified width. A precision specification like @samp{%.1f}
will override the precision determined from the input data or set due to
@option{--to} option auto scaling.
-@item --from=@var{unit}
-@opindex --from
+@optItem{numfmt,--from,=@var{unit}}
Auto-scales input numbers according to @var{unit}. See UNITS below.
The default is no scaling, meaning suffixes (e.g. @samp{M}, @samp{G}) will
trigger an error.
-@item --from-unit=@var{n}
-@opindex --from-unit
+@optItem{numfmt,--from-unit,=@var{n}}
Specify the input unit size (instead of the default 1). Use this option when
the input numbers represent other units (e.g. if the input number @samp{10}
represents 10 units of 512 bytes, use @samp{--from-unit=512}).
Suffixes are handled as with @samp{--from=auto}.
-@item --grouping
-@opindex --grouping
+@optItem{numfmt,--grouping,}
Group digits in output numbers according to the current locale's grouping rules
(e.g @emph{Thousands Separator} character, commonly @samp{.} (dot) or @samp{,}
comma). This option has no effect in @samp{POSIX/C} locale.
-@item --header[=@var{n}]
-@opindex --header
-@opindex --header=N
+@optItem{numfmt,--header,[=@var{n}]}
Print the first @var{n} (default: 1) lines without any conversion.
-@item --invalid=@var{mode}
-@opindex --invalid
+@optItem{numfmt,--invalid,=@var{mode}}
The default action on input errors is to exit immediately with status code 2.
@option{--invalid=@samp{abort}} explicitly specifies this default mode.
With a @var{mode} of @samp{fail}, print a warning for @emph{each} conversion
@@ -19522,14 +18680,13 @@ error, and exit with status 2. With a @var{mode} of @samp{warn}, exit with
status 0, even in the presence of conversion errors, and with a @var{mode} of
@samp{ignore} do not even print diagnostics.
-@item --padding=@var{n}
-@opindex --padding
+@optItem{numfmt,--padding,=@var{n}}
Pad the output numbers to @var{n} characters, by adding spaces. If @var{n} is
a positive number, numbers will be right-aligned. If @var{n} is a negative
number, numbers will be left-aligned. By default, numbers are automatically
aligned based on the input line's width (only with the default delimiter).
-@item --round=@var{method}
+@optItem{numfmt,--round,=@var{method}}
@opindex --round
@opindex --round=up
@opindex --round=down
@@ -19540,25 +18697,21 @@ When converting number representations, round the number according to
@var{method}, which can be @samp{up}, @samp{down},
@samp{from-zero} (the default), @samp{towards-zero}, @samp{nearest}.
-@item --suffix=@var{suffix}
-@opindex --suffix
+@optItem{numfmt,--suffix,=@var{suffix}}
Add @samp{SUFFIX} to the output numbers, and accept optional @samp{SUFFIX} in
input numbers.
-@item --to=@var{unit}
-@opindex --to
+@optItem{numfmt,--to,=@var{unit}}
Auto-scales output numbers according to @var{unit}. See @emph{Units} below.
The default is no scaling, meaning all the digits of the number are printed.
-@item --to-unit=@var{n}
-@opindex --to-unit
+@optItem{numfmt,--to-unit,=@var{n}}
Specify the output unit size (instead of the default 1). Use this option when
the output numbers represent other units (e.g. to represent @samp{4,000,000}
bytes in blocks of 1kB, use @samp{--to=si --to-unit=1000}).
Suffixes are handled as with @samp{--from=auto}.
-@item --unit-separator=@var{sep}
-@opindex --unit-separator
+@optItem{numfmt,--unit-separator,=@var{sep}}
Support a separator @var{sep} between the number and unit,
with @option{--from} or @option{--to} auto-scaled units.
By default a blank or non-breaking space character is accepted on input,
@@ -19574,7 +18727,7 @@ Support blanks on input: @option{--delimiter=''}
Ditto and output non-breaking space: @option{-d '' --unit-separator=$'\u00A0'}
@end example
-@optZeroTerminated
+@optZeroTerminated{numfmt}
@newlineFieldSeparator
@end table
@@ -19832,10 +18985,8 @@ The program accepts the following options. Also see @ref{Common options}.
Options must precede operands.
@table @samp
-@item -f @var{format}
-@itemx --format=@var{format}
-@opindex -f
-@opindex --format
+@optItem{seq,-f,@w{ }@var{format}}
+@optItemx{seq,--format,=@var{format}}
@cindex formatting of numbers in @command{seq}
Print all numbers using @var{format}.
@var{format} must contain exactly one of the @samp{printf}-style
@@ -19854,18 +19005,14 @@ the default format is @samp{%.@var{p}f}, where @var{p} is the minimum
precision that can represent the output numbers exactly. Otherwise,
the default format is @samp{%g}.
-@item -s @var{string}
-@itemx --separator=@var{string}
-@opindex -s
-@opindex --separator
+@optItem{seq,-s,@w{ }@var{string}}
+@optItemx{seq,--separator,=@var{string}}
@cindex separator for numbers in @command{seq}
Separate numbers with @var{string}; default is a newline.
The output always terminates with a newline.
-@item -w
-@itemx --equal-width
-@opindex -w
-@opindex --equal-width
+@optItem{seq,-w,}
+@optItemx{seq,--equal-width,}
Print all numbers with the same width, by padding with leading zeros.
@var{first}, @var{step}, and @var{last} should all use a fixed point
decimal representation.
@@ -20021,7 +19168,7 @@ to set a file's timestamp to an arbitrary value.
@chapter Opening the Software Toolbox
An earlier version of this chapter appeared in
-@uref{https://www.linuxjournal.com/article.php?sid=2762, the
+@uref{https://www.linuxjournal.com/article/2762, the
@cite{What's GNU@?} column of the June 1994 @cite{Linux Journal}}.
It was written by Arnold Robbins.
diff --git a/doc/local.mk b/doc/local.mk
index 3a46e0820..313a7648a 100644
--- a/doc/local.mk
+++ b/doc/local.mk
@@ -1,7 +1,7 @@
# Make coreutils documentation. -*-Makefile-*-
# This is included by the top-level Makefile.am.
-# Copyright (C) 1995-2025 Free Software Foundation, Inc.
+# Copyright (C) 1995-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -25,9 +25,11 @@ doc_coreutils_TEXINFOS = \
doc/fdl.texi \
doc/sort-version.texi
+MAKEINFO = SHELL='$(SHELL)' $(abs_top_builddir)/build-aux/makeinfo-wrapper.sh
+
# The customization variable CHECK_NORMAL_MENU_STRUCTURE is necessary with
# makeinfo versions ≥ 6.8.
-MAKEINFO = @MAKEINFO@ -c CHECK_NORMAL_MENU_STRUCTURE=1
+AM_MAKEINFOFLAGS = -c CHECK_NORMAL_MENU_STRUCTURE=1
# The following is necessary if the package name is 8 characters or longer.
# If the info documentation would be split into 10 or more separate files,
@@ -37,7 +39,7 @@ MAKEINFO = @MAKEINFO@ -c CHECK_NORMAL_MENU_STRUCTURE=1
# Otherwise, it would also generate files with names like <package>.info-[123],
# and those names all map to one 14-byte name (<package>.info-) on some crufty
# old systems.
-AM_MAKEINFOFLAGS = --no-split
+AM_MAKEINFOFLAGS += --no-split
doc/constants.texi: $(top_srcdir)/src/tail.c $(top_srcdir)/src/shred.c
$(AM_V_GEN)LC_ALL=C; export LC_ALL; \
@@ -131,3 +133,5 @@ sc-lower-case-var:
$(PERL) -lne $(find_upper_case_var) $(texi_files)
check-local: check-texinfo
+
+.PHONY: html-local
diff --git a/doc/perm.texi b/doc/perm.texi
index 8128c9360..4eaf23165 100644
--- a/doc/perm.texi
+++ b/doc/perm.texi
@@ -1,6 +1,6 @@
@c File mode bits
-@c Copyright (C) 1994--2025 Free Software Foundation, Inc.
+@c Copyright (C) 1994--2026 Free Software Foundation, Inc.
@c Permission is granted to copy, distribute and/or modify this document
@c under the terms of the GNU Free Documentation License, Version 1.3 or
diff --git a/doc/sort-version.texi b/doc/sort-version.texi
index 8a6ee54b6..fc582f996 100644
--- a/doc/sort-version.texi
+++ b/doc/sort-version.texi
@@ -1,6 +1,6 @@
@c GNU Version-sort ordering documentation
-@c Copyright (C) 2019--2025 Free Software Foundation, Inc.
+@c Copyright (C) 2019--2026 Free Software Foundation, Inc.
@c Permission is granted to copy, distribute and/or modify this document
@c under the terms of the GNU Free Documentation License, Version 1.3 or
diff --git a/gl/lib/buffer-lcm.c b/gl/lib/buffer-lcm.c
index 9b2e186ec..a6aa62816 100644
--- a/gl/lib/buffer-lcm.c
+++ b/gl/lib/buffer-lcm.c
@@ -1,6 +1,6 @@
/* buffer-lcm.c - compute a good buffer size for dealing with two files
- Copyright (C) 2002-2025 Free Software Foundation, Inc.
+ Copyright (C) 2002-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gl/lib/cl-strtod.c b/gl/lib/cl-strtod.c
index e1a3d132a..1b0e5af62 100644
--- a/gl/lib/cl-strtod.c
+++ b/gl/lib/cl-strtod.c
@@ -1,6 +1,6 @@
/* Convert string to double in the current locale, falling back on the C locale.
- Copyright 2019-2025 Free Software Foundation, Inc.
+ Copyright 2019-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gl/lib/dtimespec-bound.h b/gl/lib/dtimespec-bound.h
index efeb5b1ab..42261f171 100644
--- a/gl/lib/dtimespec-bound.h
+++ b/gl/lib/dtimespec-bound.h
@@ -1,6 +1,6 @@
/* Compute a timespec-related bound for floating point.
- Copyright 2025 Free Software Foundation, Inc.
+ Copyright 2025-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gl/lib/fadvise.c b/gl/lib/fadvise.c
index 3d76b5996..09f1c5cb8 100644
--- a/gl/lib/fadvise.c
+++ b/gl/lib/fadvise.c
@@ -1,5 +1,5 @@
/* Declare an access pattern hint for files.
- Copyright (C) 2010-2025 Free Software Foundation, Inc.
+ Copyright (C) 2010-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gl/lib/fadvise.h b/gl/lib/fadvise.h
index c71c2a1a9..3d99486a2 100644
--- a/gl/lib/fadvise.h
+++ b/gl/lib/fadvise.h
@@ -1,5 +1,5 @@
/* Declare an access pattern hint for files.
- Copyright (C) 2010-2025 Free Software Foundation, Inc.
+ Copyright (C) 2010-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gl/lib/fd-reopen.c b/gl/lib/fd-reopen.c
index 0f3b5809f..607235feb 100644
--- a/gl/lib/fd-reopen.c
+++ b/gl/lib/fd-reopen.c
@@ -1,6 +1,6 @@
/* Invoke open, but return either a desired file descriptor or -1.
- Copyright (C) 2005-2025 Free Software Foundation, Inc.
+ Copyright (C) 2005-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,7 +23,7 @@
#include <errno.h>
#include <fcntl.h>
-#include <unistd.h>
+#include "unistd--.h"
/* Open a file to a particular file descriptor. This is like standard
'open', except it always returns DESIRED_FD if successful. */
diff --git a/gl/lib/fd-reopen.h b/gl/lib/fd-reopen.h
index 743657a0d..e9d5e19ab 100644
--- a/gl/lib/fd-reopen.h
+++ b/gl/lib/fd-reopen.h
@@ -1,6 +1,6 @@
/* Invoke open, but return either a desired file descriptor or -1.
- Copyright (C) 2005-2025 Free Software Foundation, Inc.
+ Copyright (C) 2005-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gl/lib/heap.c b/gl/lib/heap.c
index 0b69904f9..567e02190 100644
--- a/gl/lib/heap.c
+++ b/gl/lib/heap.c
@@ -1,6 +1,6 @@
/* Barebones heap implementation supporting only insert and pop.
- Copyright (C) 2010-2025 Free Software Foundation, Inc.
+ Copyright (C) 2010-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -50,7 +50,7 @@ heap_alloc (int (*compare) (void const *, void const *), idx_t n_reserve)
heap->array = xnmalloc (n_reserve, sizeof *(heap->array));
- heap->array[0] = nullptr;
+ heap->array[0] = NULL;
heap->capacity = n_reserve;
heap->count = 0;
heap->compare = compare ? compare : heap_default_compare;
@@ -96,7 +96,7 @@ heap_remove_top (struct heap *heap)
void *top;
if (heap->count == 0)
- return nullptr;
+ return NULL;
top = heap->array[1];
heap->array[1] = heap->array[heap->count--];
diff --git a/gl/lib/heap.h b/gl/lib/heap.h
index 6894a6d02..f8f4ebe70 100644
--- a/gl/lib/heap.h
+++ b/gl/lib/heap.h
@@ -1,6 +1,6 @@
/* Barebones heap implementation supporting only insert and pop.
- Copyright (C) 2010-2025 Free Software Foundation, Inc.
+ Copyright (C) 2010-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gl/lib/mbbuf.c b/gl/lib/mbbuf.c
index 551a9ced3..3b9d3cc42 100644
--- a/gl/lib/mbbuf.c
+++ b/gl/lib/mbbuf.c
@@ -1,5 +1,5 @@
/* Buffering for multi-byte characters.
- Copyright (C) 2025 Free Software Foundation, Inc.
+ Copyright (C) 2025-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gl/lib/mbbuf.h b/gl/lib/mbbuf.h
index 135b82f80..a6289a8ce 100644
--- a/gl/lib/mbbuf.h
+++ b/gl/lib/mbbuf.h
@@ -1,5 +1,5 @@
/* Buffering for multi-byte characters.
- Copyright (C) 2025 Free Software Foundation, Inc.
+ Copyright (C) 2025-2026 Free Software Foundation, Inc.
This file is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
@@ -25,7 +25,10 @@
#include <stdio.h>
#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include "fseterr.h"
#include "mcel.h"
#include "idx.h"
@@ -35,7 +38,7 @@ _GL_INLINE_HEADER_BEGIN
#endif
/* End of file. */
-#define MBBUF_EOF UINT32_MAX
+#define MBBUF_EOF (MCEL_CHAR_MAX+1)
/* MBBUF_EOF should not be a valid character. */
static_assert (MCEL_CHAR_MAX < MBBUF_EOF);
@@ -47,8 +50,15 @@ typedef struct
idx_t size; /* Number of bytes allocated for BUFFER. */
idx_t length; /* Number of bytes with data in BUFFER. */
idx_t offset; /* Current position in BUFFER. */
+ bool eof; /* Whether at End Of File. */
} mbbuf_t;
+MBBUF_INLINE idx_t
+mbbuf_avail (mbbuf_t const *mbbuf)
+{
+ return mbbuf->length - mbbuf->offset;
+}
+
/* Initialize MBBUF with an allocated BUFFER of SIZE bytes and a file stream
FP open for reading. SIZE must be greater than or equal to MCEL_LEN_MAX.
*/
@@ -62,17 +72,19 @@ mbbuf_init (mbbuf_t *mbbuf, char *buffer, idx_t size, FILE *fp)
mbbuf->size = size;
mbbuf->length = 0;
mbbuf->offset = 0;
+ mbbuf->eof = false;
}
-/* Get the next character in the buffer, filling it from FP if necessary.
- If an invalid multi-byte character is seen, we assume the program wants to
- fall back to the read byte. */
-MBBUF_INLINE mcel_t
-mbbuf_get_char (mbbuf_t *mbbuf)
+/* Fill the input buffer with at least MCEL_LEN_MAX bytes if possible.
+ Return the number of bytes available from the current offset.
+ At end of file, MBBUF.EOF is set, and zero will eventually be returned.
+ Note feof() will _NOT_ be set on the MBBUF.FP. */
+MBBUF_INLINE idx_t
+mbbuf_topup (mbbuf_t *mbbuf)
{
- idx_t available = mbbuf->length - mbbuf->offset;
- /* Check if we need to fill the input buffer. */
- if (available < MCEL_LEN_MAX && ! feof (mbbuf->fp))
+ idx_t available = mbbuf_avail (mbbuf);
+
+ if (available < MCEL_LEN_MAX && ! mbbuf->eof)
{
idx_t start;
if (!(0 < available))
@@ -82,11 +94,64 @@ mbbuf_get_char (mbbuf_t *mbbuf)
memmove (mbbuf->buffer, mbbuf->buffer + mbbuf->offset, available);
start = available;
}
- mbbuf->length = fread (mbbuf->buffer + start, 1, mbbuf->size - start,
- mbbuf->fp) + start;
+ ssize_t read_ret = read (fileno (mbbuf->fp), mbbuf->buffer + start,
+ mbbuf->size - start);
+ if (read_ret < 0)
+ {
+ fseterr (mbbuf->fp);
+ mbbuf->eof = true; /* Avoid any more reads(). */
+ mbbuf->length = start;
+ }
+ else
+ {
+ mbbuf->eof = read_ret == 0;
+ mbbuf->length = read_ret + start;
+ }
+
mbbuf->offset = 0;
- available = mbbuf->length - mbbuf->offset;
+ available = mbbuf_avail (mbbuf);
}
+
+ return available;
+}
+
+/* Fill the input buffer enough to scan the next character if possible.
+ Return the number of bytes available from the current offset. */
+MBBUF_INLINE idx_t
+mbbuf_fill (mbbuf_t *mbbuf)
+{
+ idx_t available = mbbuf_avail (mbbuf);
+
+ if (available == 0)
+ return mbbuf_topup (mbbuf);
+
+ if (available < MCEL_LEN_MAX && ! mbbuf->eof)
+ {
+ mcel_t g = mcel_scan (mbbuf->buffer + mbbuf->offset,
+ mbbuf->buffer + mbbuf->length);
+ if (g.err)
+ return mbbuf_topup (mbbuf);
+ }
+
+ return available;
+}
+
+/* Consume N bytes from the current buffer. */
+MBBUF_INLINE void
+mbbuf_advance (mbbuf_t *mbbuf, idx_t n)
+{
+ if (mbbuf_avail (mbbuf) < n)
+ unreachable ();
+ mbbuf->offset += n;
+}
+
+/* Get the next character in the buffer, filling it from FP if necessary.
+ If an invalid multi-byte character is seen, we assume the program wants to
+ fall back to the read byte. */
+MBBUF_INLINE mcel_t
+mbbuf_get_char (mbbuf_t *mbbuf)
+{
+ idx_t available = mbbuf_fill (mbbuf);
if (available <= 0)
return (mcel_t) { .ch = MBBUF_EOF };
mcel_t g = mcel_scan (mbbuf->buffer + mbbuf->offset,
@@ -96,7 +161,7 @@ mbbuf_get_char (mbbuf_t *mbbuf)
else
{
/* Assume the program will emit the byte, but keep the error flag. */
- g.ch = mbbuf->buffer[mbbuf->offset++];
+ g.ch = (unsigned char) mbbuf->buffer[mbbuf->offset++];
}
return g;
}
diff --git a/gl/lib/rand-isaac.c b/gl/lib/rand-isaac.c
index 5b4f32c29..ed83088a8 100644
--- a/gl/lib/rand-isaac.c
+++ b/gl/lib/rand-isaac.c
@@ -1,6 +1,6 @@
/* Bob Jenkins's cryptographic random number generators, ISAAC and ISAAC64.
- Copyright (C) 1999-2025 Free Software Foundation, Inc.
+ Copyright (C) 1999-2026 Free Software Foundation, Inc.
Copyright (C) 1997, 1998, 1999 Colin Plumb.
This program is free software: you can redistribute it and/or modify
diff --git a/gl/lib/rand-isaac.h b/gl/lib/rand-isaac.h
index 0916719b6..2e8e0ef92 100644
--- a/gl/lib/rand-isaac.h
+++ b/gl/lib/rand-isaac.h
@@ -1,6 +1,6 @@
/* Bob Jenkins's cryptographic random number generators, ISAAC and ISAAC64.
- Copyright (C) 1999-2025 Free Software Foundation, Inc.
+ Copyright (C) 1999-2026 Free Software Foundation, Inc.
Copyright (C) 1997, 1998, 1999 Colin Plumb.
This program is free software: you can redistribute it and/or modify
diff --git a/gl/lib/randint.c b/gl/lib/randint.c
index 9c2317bc4..97fe0bf90 100644
--- a/gl/lib/randint.c
+++ b/gl/lib/randint.c
@@ -1,6 +1,6 @@
/* Generate random integers.
- Copyright (C) 2006-2025 Free Software Foundation, Inc.
+ Copyright (C) 2006-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -35,8 +35,8 @@ int
main (int argc, char **argv)
{
randint i;
- randint n = strtoumax (argv[1], nullptr, 10);
- randint choices = strtoumax (argv[2], nullptr, 10);
+ randint n = strtoumax (argv[1], NULL, 10);
+ randint choices = strtoumax (argv[2], NULL, 10);
char const *name = argv[3];
struct randint_source *ints = randint_all_new (name, SIZE_MAX);
@@ -77,14 +77,14 @@ randint_new (struct randread_source *source)
}
/* Create a new randint_source by creating a randread_source from
- NAME and ESTIMATED_BYTES. Return nullptr (setting errno) if
+ NAME and ESTIMATED_BYTES. Return NULL (setting errno) if
unsuccessful. */
struct randint_source *
randint_all_new (char const *name, size_t bytes_bound)
{
struct randread_source *source = randread_new (name, bytes_bound);
- return (source ? randint_new (source) : nullptr);
+ return (source ? randint_new (source) : NULL);
}
/* Return the random data source of *S. */
@@ -198,7 +198,7 @@ randint_genmax (struct randint_source *s, randint genmax)
void
randint_free (struct randint_source *s)
{
- explicit_bzero (s, sizeof *s);
+ memset_explicit (s, 0, sizeof *s);
free (s);
}
diff --git a/gl/lib/randint.h b/gl/lib/randint.h
index 084746984..e738d6b4f 100644
--- a/gl/lib/randint.h
+++ b/gl/lib/randint.h
@@ -1,6 +1,6 @@
/* Generate random integers.
- Copyright (C) 2006-2025 Free Software Foundation, Inc.
+ Copyright (C) 2006-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gl/lib/randperm.c b/gl/lib/randperm.c
index 7f0820bf1..7c77f4d74 100644
--- a/gl/lib/randperm.c
+++ b/gl/lib/randperm.c
@@ -1,6 +1,6 @@
/* Generate random permutations.
- Copyright (C) 2006-2025 Free Software Foundation, Inc.
+ Copyright (C) 2006-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -98,7 +98,7 @@ typedef Hash_table sparse_map;
static sparse_map *
sparse_new (size_t size_hint)
{
- return hash_initialize (size_hint, nullptr, sparse_hash_, sparse_cmp_, free);
+ return hash_initialize (size_hint, NULL, sparse_hash_, sparse_cmp_, free);
}
/* Swap the values for I and J. If a value is not already present
@@ -143,7 +143,7 @@ sparse_free (sparse_map *sv)
/* From R, allocate and return a malloc'd array of the first H elements
of a random permutation of N elements. H must not exceed N.
- Return nullptr if H is zero. */
+ Return NULL if H is zero. */
size_t *
randperm_new (struct randint_source *r, size_t h, size_t n)
@@ -153,7 +153,7 @@ randperm_new (struct randint_source *r, size_t h, size_t n)
switch (h)
{
case 0:
- v = nullptr;
+ v = NULL;
break;
case 1:
@@ -198,13 +198,13 @@ randperm_new (struct randint_source *r, size_t h, size_t n)
if (sparse)
{
sv = sparse_new (h * 2);
- if (sv == nullptr)
+ if (sv == NULL)
xalloc_die ();
v = xnmalloc (h, sizeof *v);
}
else
{
- sv = nullptr; /* To placate GCC's -Wuninitialized. */
+ sv = NULL; /* To placate GCC's -Wuninitialized. */
v = xnmalloc (n, sizeof *v);
for (i = 0; i < n; i++)
v[i] = i;
diff --git a/gl/lib/randread.c b/gl/lib/randread.c
index 59f8950b8..249d400c9 100644
--- a/gl/lib/randread.c
+++ b/gl/lib/randread.c
@@ -1,6 +1,6 @@
/* Generate buffers of random data.
- Copyright (C) 2006-2025 Free Software Foundation, Inc.
+ Copyright (C) 2006-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -153,7 +153,7 @@ static int
randread_free_body (struct randread_source *s)
{
FILE *source = s->source;
- explicit_bzero (s, sizeof *s);
+ memset_explicit (s, 0, sizeof *s);
free (s);
return source ? fclose (source) : 0;
}
@@ -167,21 +167,21 @@ randread_free_body (struct randread_source *s)
default handler. Unless a non-default handler is used, NAME's
lifetime should be at least that of the returned value.
- Return nullptr (setting errno) on failure. */
+ Return NULL (setting errno) on failure. */
struct randread_source *
randread_new (char const *name, size_t bytes_bound)
{
if (bytes_bound == 0)
- return simple_new (nullptr, nullptr);
+ return simple_new (NULL, NULL);
else
{
- FILE *source = nullptr;
+ FILE *source = NULL;
struct randread_source *s;
if (name)
if (! (source = fopen_safer (name, "rb")))
- return nullptr;
+ return NULL;
s = simple_new (source, name);
@@ -206,7 +206,7 @@ randread_new (char const *name, size_t bytes_bound)
int e = errno;
randread_free_body (s);
errno = e;
- return nullptr;
+ return NULL;
}
isaac_seed (&s->buf.isaac.state);
}
diff --git a/gl/lib/randread.h b/gl/lib/randread.h
index 6248f3a87..cf75dcbcb 100644
--- a/gl/lib/randread.h
+++ b/gl/lib/randread.h
@@ -1,6 +1,6 @@
/* Generate buffers of random data.
- Copyright (C) 2006-2025 Free Software Foundation, Inc.
+ Copyright (C) 2006-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gl/lib/root-dev-ino.c b/gl/lib/root-dev-ino.c
index ee22b4c6b..87f404e43 100644
--- a/gl/lib/root-dev-ino.c
+++ b/gl/lib/root-dev-ino.c
@@ -1,5 +1,5 @@
/* root-dev-ino.c -- get the device and inode numbers for '/'.
- Copyright (C) 2003-2025 Free Software Foundation, Inc.
+ Copyright (C) 2003-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,14 +23,14 @@
#include <stdlib.h>
/* Call lstat to get the device and inode numbers for '/'.
- Upon failure, return nullptr. Otherwise, set the members of
+ Upon failure, return NULL. Otherwise, set the members of
*ROOT_D_I accordingly and return ROOT_D_I. */
struct dev_ino *
get_root_dev_ino (struct dev_ino *root_d_i)
{
struct stat statbuf;
if (lstat ("/", &statbuf))
- return nullptr;
+ return NULL;
root_d_i->st_ino = statbuf.st_ino;
root_d_i->st_dev = statbuf.st_dev;
return root_d_i;
diff --git a/gl/lib/root-dev-ino.h b/gl/lib/root-dev-ino.h
index a4d909f82..0bfa8f71f 100644
--- a/gl/lib/root-dev-ino.h
+++ b/gl/lib/root-dev-ino.h
@@ -1,6 +1,6 @@
/* Root device and inode number checking.
- Copyright (C) 2003-2025 Free Software Foundation, Inc.
+ Copyright (C) 2003-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gl/lib/skipchars.h b/gl/lib/skipchars.h
index d623b0437..99229baec 100644
--- a/gl/lib/skipchars.h
+++ b/gl/lib/skipchars.h
@@ -1,6 +1,6 @@
/* Skipping sequences of characters satisfying a predicate
- Copyright 2023-2025 Free Software Foundation, Inc.
+ Copyright 2023-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gl/lib/smack.h b/gl/lib/smack.h
index c503d0b79..e0ed63d4f 100644
--- a/gl/lib/smack.h
+++ b/gl/lib/smack.h
@@ -1,5 +1,5 @@
/* Include and determine availability of smack routines
- Copyright (C) 2013-2025 Free Software Foundation, Inc.
+ Copyright (C) 2013-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -37,7 +37,7 @@ static inline bool
is_smack_enabled (void)
{
#ifdef HAVE_SMACK
- return smack_smackfs_path () != nullptr;
+ return smack_smackfs_path () != NULL;
#else
return false;
#endif
diff --git a/gl/lib/strintcmp.c b/gl/lib/strintcmp.c
index d05b92711..c963a5e55 100644
--- a/gl/lib/strintcmp.c
+++ b/gl/lib/strintcmp.c
@@ -1,6 +1,6 @@
/* Compare integer strings.
- Copyright (C) 2005-2025 Free Software Foundation, Inc.
+ Copyright (C) 2005-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gl/lib/strnumcmp-in.h b/gl/lib/strnumcmp-in.h
index 660abeb40..5af6b3ed9 100644
--- a/gl/lib/strnumcmp-in.h
+++ b/gl/lib/strnumcmp-in.h
@@ -1,6 +1,6 @@
/* Compare numeric strings. This is an internal include file.
- Copyright (C) 1988-2025 Free Software Foundation, Inc.
+ Copyright (C) 1988-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gl/lib/strnumcmp.c b/gl/lib/strnumcmp.c
index b7150968c..b14f52795 100644
--- a/gl/lib/strnumcmp.c
+++ b/gl/lib/strnumcmp.c
@@ -1,6 +1,6 @@
/* Compare numeric strings.
- Copyright (C) 2005-2025 Free Software Foundation, Inc.
+ Copyright (C) 2005-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gl/lib/targetdir.c b/gl/lib/targetdir.c
index 9ea2a7087..24cff48c3 100644
--- a/gl/lib/targetdir.c
+++ b/gl/lib/targetdir.c
@@ -1,6 +1,6 @@
/* Target directory operands for coreutils
- Copyright 2004-2025 Free Software Foundation, Inc.
+ Copyright 2004-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gl/lib/targetdir.h b/gl/lib/targetdir.h
index bc67bf31a..4cceaac85 100644
--- a/gl/lib/targetdir.h
+++ b/gl/lib/targetdir.h
@@ -1,6 +1,6 @@
/* Target directory operands for coreutils
- Copyright 2022-2025 Free Software Foundation, Inc.
+ Copyright 2022-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gl/lib/xdectoint.c b/gl/lib/xdectoint.c
index ecd70c1f6..c367be9b1 100644
--- a/gl/lib/xdectoint.c
+++ b/gl/lib/xdectoint.c
@@ -1,6 +1,6 @@
/* Convert decimal strings with bounds checking and exit on error.
- Copyright (C) 2014-2025 Free Software Foundation, Inc.
+ Copyright (C) 2014-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -44,7 +44,7 @@ __xnumtoint (char const *n_str, int base, __xdectoint_t min, __xdectoint_t max,
int flags)
{
__xdectoint_t tnum, r;
- strtol_error s_err = __xstrtol (n_str, nullptr, base, &tnum, suffixes);
+ strtol_error s_err = __xstrtol (n_str, NULL, base, &tnum, suffixes);
/* Errno value to report if there is an overflow. */
int overflow_errno;
diff --git a/gl/lib/xdectoint.h b/gl/lib/xdectoint.h
index c4386a10c..5bdcb64de 100644
--- a/gl/lib/xdectoint.h
+++ b/gl/lib/xdectoint.h
@@ -1,6 +1,6 @@
/* Convert decimal strings with bounds checking and exit on error.
- Copyright (C) 2014-2025 Free Software Foundation, Inc.
+ Copyright (C) 2014-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/gl/lib/xfts.c b/gl/lib/xfts.c
index e7f9f0732..9c5f10d6b 100644
--- a/gl/lib/xfts.c
+++ b/gl/lib/xfts.c
@@ -1,6 +1,6 @@
/* xfts.c -- a wrapper for fts_open
- Copyright (C) 2003-2025 Free Software Foundation, Inc.
+ Copyright (C) 2003-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -33,7 +33,7 @@ xfts_open (char * const *argv, int options,
int (*compar) (const FTSENT **, const FTSENT **))
{
FTS *fts = fts_open (argv, options | FTS_CWDFD, compar);
- if (fts == nullptr)
+ if (fts == NULL)
{
/* This can fail in two ways: out of memory or with errno==EINVAL,
which indicates it was called with invalid bit_flags. */
diff --git a/gl/local.mk b/gl/local.mk
index 7dfa80d40..8befebe0a 100644
--- a/gl/local.mk
+++ b/gl/local.mk
@@ -1,7 +1,7 @@
# Make coreutils programs. -*-Makefile-*-
# This is included by the top-level Makefile.am.
-## Copyright (C) 2024-2025 Free Software Foundation, Inc.
+## Copyright (C) 2024-2026 Free Software Foundation, Inc.
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
diff --git a/gl/tests/test-fadvise.c b/gl/tests/test-fadvise.c
index cc98fefd0..ad561f55b 100644
--- a/gl/tests/test-fadvise.c
+++ b/gl/tests/test-fadvise.c
@@ -1,5 +1,5 @@
/* Test that fadvise works as advertised.
- Copyright (C) 2010-2025 Free Software Foundation, Inc.
+ Copyright (C) 2010-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -36,7 +36,7 @@ main (void)
fdadvise (fileno (stdin), 0, 0, FADVISE_RANDOM);
/* Ignored. */
- fadvise (nullptr, FADVISE_RANDOM);
+ fadvise (NULL, FADVISE_RANDOM);
/* Invalid. */
fdadvise (42, 0, 0, FADVISE_RANDOM);
diff --git a/gl/tests/test-rand-isaac.c b/gl/tests/test-rand-isaac.c
index 5457d0f3c..f53fffc42 100644
--- a/gl/tests/test-rand-isaac.c
+++ b/gl/tests/test-rand-isaac.c
@@ -1,6 +1,6 @@
/* Test the ISAAC or ISAAC64 pseudorandom number generator.
- Copyright (C) 2010-2025 Free Software Foundation, Inc.
+ Copyright (C) 2010-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -581,7 +581,7 @@ main (int argc, char **argv)
/* If invoked with a positive argument, run a benchmark;
if with a negative, run a do-nothing benchmark. */
- for (iterations = argc <= 1 ? 0 : strtol (argv[1], nullptr, 10);
+ for (iterations = argc <= 1 ? 0 : strtol (argv[1], NULL, 10);
iterations != 0;
iterations += (iterations < 0 ? 1 : -1))
if (0 <= iterations)
diff --git a/gnulib b/gnulib
-Subproject 862a81c0e15448adde6a6e7473ec47e3a4bd91a
+Subproject e2fb3d721a7dc65bee6ddc887ccb0d1bdd7291e
diff --git a/init.cfg b/init.cfg
index 982418900..0896d8dda 100644
--- a/init.cfg
+++ b/init.cfg
@@ -1,6 +1,6 @@
# This file is sourced by init.sh, *before* its initialization.
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -112,6 +112,7 @@ require_selinux_()
# When in a chroot of an SELinux-enabled system, but with a mock-simulated
# SELinux-*disabled* system, recognize that SELinux is disabled system wide:
grep 'selinuxfs$' /proc/filesystems > /dev/null \
+ || { mount | grep ' type selinuxfs ' > /dev/null; } \
|| skip_ "this system lacks SELinux support"
# Independent of whether SELinux is enabled system-wide,
@@ -246,6 +247,8 @@ require_strace_()
{
test $# = 1 || framework_failure_
+ uses_strace_
+
strace -V < /dev/null > /dev/null 2>&1 ||
skip_ 'no strace program'
@@ -765,9 +768,6 @@ working_umask_or_skip_()
# This example will call the dd_reblock_1 function with
# an initial delay of .1 second and call it at most 6 times
# with a max delay of 3.2s (doubled each time), or a total of 6.3s
-# Note ensure you do _not_ quote the parameter to GNU sleep in
-# your function, as it may contain separate values that sleep
-# needs to accumulate.
# Further function arguments will be forwarded to the test function.
retry_delay_()
{
@@ -789,6 +789,28 @@ retry_delay_()
test "$time_fail" = 0
}
+detect_tail_mode_()
+{
+ touch tail.debug || framework_failure_
+ returns_ 124 timeout "$1" tail -n0 -F --debug tail.debug 2>tail.mode ||
+ framework_failure_
+ grep 'using .* mode' tail.mode >/dev/null
+}
+
+require_inotify_supported_()
+{
+ require_built_ tail timeout
+
+ retry_delay_ detect_tail_mode_ .1 4
+
+ # This may skip because:
+ # inotify is not available in this build
+ # inotify is not supported on this file system
+ # tail didn't print the debug info within 1.5s (unlikely)
+ grep 'using notification mode' tail.mode >/dev/null ||
+ skip_ 'inotify not detected'
+}
+
# Call this with a list of programs under test immediately after
# sourcing init.sh.
print_ver_()
@@ -809,4 +831,31 @@ require_gnu_()
|| skip_ 'not running on GNU/Hurd'
}
+# Sequence derived from <https://datatracker.ietf.org/doc/rfc9839>.
+bad_unicode ()
+{
+ require_built_ printf
+
+ # invalid UTF8|unpaired surrogate|C1 control|noncharacter
+ env printf '\xFF|\xED\xBA\xAD|\u0089|\xED\xA6\xBF\xED\xBF\xBF\n'
+}
+
+# AIX has a 'strace' program unrelated to the one we care about. Avoid
+# executing it since it will run forever until manually killed when given
+# three to seven arguments.
+uses_strace_ ()
+{
+ strace true > /dev/null 2>&1
+ if test $? -ne 0; then
+ strace () { false; }
+ fi
+}
+
+# Return true if passed program is built with ASAN, or UBSAN etc.
+sanitizer_build_()
+{
+ env $(printf '%sSAN_OPTIONS=help=1 ' A UB L M T) "$1" --version 2>&1 |
+ grep '[Ss]anitizer' >/dev/null
+}
+
sanitize_path_
diff --git a/lib/t-chdir-long b/lib/t-chdir-long
index da2f45dbf..c7abd1e11 100755
--- a/lib/t-chdir-long
+++ b/lib/t-chdir-long
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise chdir-long's sample main program.
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/m4/check-decl.m4 b/m4/check-decl.m4
index 010263118..8859f3c0f 100644
--- a/m4/check-decl.m4
+++ b/m4/check-decl.m4
@@ -1,7 +1,7 @@
#serial 27
# Check declarations for this package.
-dnl Copyright (C) 1997-2025 Free Software Foundation, Inc.
+dnl Copyright (C) 1997-2026 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
diff --git a/m4/include-exclude-prog.m4 b/m4/include-exclude-prog.m4
index b65a8898d..d0e5aebd8 100644
--- a/m4/include-exclude-prog.m4
+++ b/m4/include-exclude-prog.m4
@@ -1,5 +1,5 @@
#serial 2
-dnl Copyright (C) 2007-2025 Free Software Foundation, Inc.
+dnl Copyright (C) 2007-2026 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/jm-macros.m4 b/m4/jm-macros.m4
index 598c2476d..fb00f9faf 100644
--- a/m4/jm-macros.m4
+++ b/m4/jm-macros.m4
@@ -2,7 +2,7 @@
dnl Misc type-related macros for coreutils.
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -70,6 +70,7 @@ AC_DEFUN([coreutils_MACROS],
setgroups
sethostname
siginterrupt
+ vmsplice
sync
sysinfo
tcgetpgrp
diff --git a/m4/prereq.m4 b/m4/prereq.m4
index 0d82e9960..887a8a161 100644
--- a/m4/prereq.m4
+++ b/m4/prereq.m4
@@ -7,7 +7,7 @@ m4_pattern_forbid([^gl_[ABCDEFGHIJKLMNOPQRSTUVXYZ]])dnl
# directory of the coreutils package.
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/m4/stat-prog.m4 b/m4/stat-prog.m4
index ef050ddd0..bd0ba2c2e 100644
--- a/m4/stat-prog.m4
+++ b/m4/stat-prog.m4
@@ -1,7 +1,7 @@
# stat-prog.m4 serial 7
# Record the prerequisites of src/stat.c from the coreutils package.
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/man/basenc.x b/man/basenc.x
index 87b99ec10..8a2315e95 100644
--- a/man/basenc.x
+++ b/man/basenc.x
@@ -1,4 +1,4 @@
-'\" Copyright (C) 2018-2025 Free Software Foundation, Inc.
+'\" Copyright (C) 2018-2026 Free Software Foundation, Inc.
'\"
'\" This is free software. You may redistribute copies of it under the terms
'\" of the GNU General Public License <https://www.gnu.org/licenses/gpl.html>.
diff --git a/man/chmod.x b/man/chmod.x
index 50c9e6a73..4a20c5b43 100644
--- a/man/chmod.x
+++ b/man/chmod.x
@@ -1,4 +1,4 @@
-'\" Copyright (C) 1998-2025 Free Software Foundation, Inc.
+'\" Copyright (C) 1998-2026 Free Software Foundation, Inc.
'\"
'\" This is free software. You may redistribute copies of it under the terms
'\" of the GNU General Public License <https://www.gnu.org/licenses/gpl.html>.
@@ -49,7 +49,9 @@ users who are members of the file's group (\fBg\fP),
and the permissions granted to users that are in neither of the two preceding
categories (\fBo\fP).
.PP
-A numeric mode is from one to four octal digits (0\-7), derived by
+.ds cU https://www.gnu.org/software/coreutils/manual/html_node
+A \X'tty: link \*(cU/Numeric-Modes.html'\fBnumeric mode\fP\X'tty: link'
+is from one to four octal digits (0\-7), derived by
adding up the bits with values 4, 2, and 1. Omitted digits are
assumed to be leading zeros.
The first digit selects the set user ID (4) and set group ID (2) and
diff --git a/man/chown.x b/man/chown.x
index 0766f5ee5..9aa7261d2 100644
--- a/man/chown.x
+++ b/man/chown.x
@@ -1,4 +1,4 @@
-'\" Copyright (C) 1998-2025 Free Software Foundation, Inc.
+'\" Copyright (C) 1998-2026 Free Software Foundation, Inc.
'\"
'\" This is free software. You may redistribute copies of it under the terms
'\" of the GNU General Public License <https://www.gnu.org/licenses/gpl.html>.
diff --git a/man/date.x b/man/date.x
index bbcbafcb6..c0bdc41c6 100644
--- a/man/date.x
+++ b/man/date.x
@@ -1,5 +1,17 @@
+'\" Copyright (C) 1998-2026 Free Software Foundation, Inc.
+'\"
+'\" This is free software. You may redistribute copies of it under the terms
+'\" of the GNU General Public License <https://www.gnu.org/licenses/gpl.html>.
+'\" There is NO WARRANTY, to the extent permitted by law.
[NAME]
date \- print or set the system date and time
+[SYNOPSIS]
+.B date
+[\fI\,OPTION\/\fR]... [\fB+\fR\fI\,FORMAT\/\fR]
+.br
+.B date
+[\fI\,OPTION\/\fR]... \
+\fIMMDDhhmm\/\fR[[\fI\,CC\/\fR]\fI\,YY\/\fR][\fB.\fI\,ss\/\fR]
[DESCRIPTION]
.\" Add any additional description here
[DATE STRING]
diff --git a/man/df.x b/man/df.x
index aca9ee776..ce4e84198 100644
--- a/man/df.x
+++ b/man/df.x
@@ -1,4 +1,4 @@
-'\" Copyright (C) 1998-2025 Free Software Foundation, Inc.
+'\" Copyright (C) 1998-2026 Free Software Foundation, Inc.
'\"
'\" This is free software. You may redistribute copies of it under the terms
'\" of the GNU General Public License <https://www.gnu.org/licenses/gpl.html>.
diff --git a/man/dir.x b/man/dir.x
index 9ba5e5997..47cb9992f 100644
--- a/man/dir.x
+++ b/man/dir.x
@@ -2,3 +2,5 @@
dir \- list directory contents
[DESCRIPTION]
.\" Add any additional description here
+[SEE ALSO]
+dircolors(1)
diff --git a/man/du.x b/man/du.x
index 9f4fe223b..fc303474d 100644
--- a/man/du.x
+++ b/man/du.x
@@ -1,4 +1,4 @@
-'\" Copyright (C) 1998-2025 Free Software Foundation, Inc.
+'\" Copyright (C) 1998-2026 Free Software Foundation, Inc.
'\"
'\" This is free software. You may redistribute copies of it under the terms
'\" of the GNU General Public License <https://www.gnu.org/licenses/gpl.html>.
diff --git a/man/env.x b/man/env.x
index 348dfb098..c6d622579 100644
--- a/man/env.x
+++ b/man/env.x
@@ -1,4 +1,4 @@
-'\" Copyright (C) 1998-2025 Free Software Foundation, Inc.
+'\" Copyright (C) 1998-2026 Free Software Foundation, Inc.
'\"
'\" This is free software. You may redistribute copies of it under the terms
'\" of the GNU General Public License <https://www.gnu.org/licenses/gpl.html>.
diff --git a/man/help2man b/man/help2man
index e021ff8dc..9c45d3e39 100755
--- a/man/help2man
+++ b/man/help2man
@@ -49,7 +49,10 @@ sub program_basename;
sub get_option_value;
sub convert_option;
sub fix_italic_spacing;
+sub generic_indented_tag_line;
+sub indented_tag_line;
sub set_indent;
+sub visual_length;
my $version_info = enc_user sprintf _(<<'EOT'), $this_program, $this_version;
GNU %s %s
@@ -276,6 +279,10 @@ my $program = program_basename $ARGV[0];
my $package = $program;
my $version;
+# Normalize help text that embeds argv[0], so examples are formatted
+# using the displayed program name rather than a temporary build path.
+$help_text =~ s/\Q$ARGV[0]\E/$program/g if $ARGV[0] ne $program;
+
if ($opt_output)
{
unlink $opt_output or kark N_("%s: can't unlink %s (%s)"),
@@ -406,7 +413,7 @@ if ($help_text =~ s/^($PAT_USAGE):( +(\S+))(.*)((?:\n(?: {6}\1| *($PAT_USAGE_CON
my $sect = _('DESCRIPTION');
$_ = "$help_text\n\n$version_text";
-# Normalise paragraph breaks.
+# Normalize paragraph breaks.
s/^\n+//;
s/\n*$/\n/;
s/\n\n+/\n\n/g;
@@ -447,27 +454,33 @@ s/([^\n])\n($PAT_BUGS|$PAT_AUTHOR|$PAT_SEE_ALSO) /$1\n\n$2 /og;
s/^Copyright +(?:\xa9|\([Cc]\))/Copyright \\(co/mg;
my $require_mono = 0;
+my $compact_indented_tags = 0;
+my $blank_before_paragraph = 0;
while (length)
{
# Convert some standard paragraph names.
if (s/^($PAT_OPTIONS): *\n+//o)
{
$sect = _('OPTIONS');
+ $blank_before_paragraph = 0;
next;
}
if (s/^($PAT_ENVIRONMENT): *\n+//o)
{
$sect = _('ENVIRONMENT');
+ $blank_before_paragraph = 0;
next;
}
if (s/^($PAT_FILES): *\n+//o)
{
$sect = _('FILES');
+ $blank_before_paragraph = 0;
next;
}
elsif (s/^($PAT_EXAMPLES): *\n+//o)
{
$sect = _('EXAMPLES');
+ $blank_before_paragraph = 0;
next;
}
@@ -477,6 +490,7 @@ while (length)
$sect = uc $1;
$sect =~ tr/*/ /; # also accept *Section*Name*
push @sections, $sect;
+ $blank_before_paragraph = 0;
next;
}
@@ -532,17 +546,19 @@ while (length)
my $indent = 0;
my $content = '';
+ my $tag_line_indent;
+ my $tag_line_width;
# Option with description.
- if (s/^( {1,10}([+-]\S.*?))(?:( +(?!-))|\n( {20,}))(\S.*)\n//)
+ if (s/^( {1,10}([+-]\S.*?))(?:( +(?!-))|\n( {7,}))(\S.*)\n//)
{
$matched .= $& if %append_match;
- $indent = set_indent length ($4 || "$1$3");
+ $indent = set_indent visual_length ($4 || "$1$3");
$content = ".TP\n\x84$2\n\x84$5\n";
unless ($4)
{
# Indent may be different on second line.
- $indent = set_indent length $& if /^ {20,}/;
+ $indent = set_indent visual_length $& if /^ {20,}/;
}
}
@@ -555,18 +571,21 @@ while (length)
}
# Indented paragraph with tag.
- elsif (s/^( +(\S.*?) +)(\S.*)\n//)
+ elsif (s/^(( +)(\S.*?) +)(\S.*)\n//)
{
$matched .= $& if %append_match;
- $indent = set_indent length $1;
- $content = ".TP\n\x84$2\n\x84$3\n";
+ $tag_line_width = (visual_length $1) - (visual_length $2);
+ $tag_line_width = 1 if $tag_line_width < 1;
+ $indent = set_indent $tag_line_width;
+ $content = ".TP\n\x84$3\n\x84$4\n";
+ $tag_line_indent = $2;
}
# Indented paragraph.
elsif (s/^( +)(\S.*)\n//)
{
$matched .= $& if %append_match;
- $indent = set_indent length $1;
+ $indent = set_indent visual_length $1;
$content = ".IP\n\x84$2\n";
}
@@ -586,8 +605,42 @@ while (length)
$content .= "\x84$1\n";
}
- # Move to next paragraph.
- s/^\n+//;
+ # Consecutive generic tagged rows are dense tables. Keep .TP's
+ # hanging indentation, but suppress inter-paragraph distance.
+ my $is_indented_tag = $content =~ /^\.TP\n/ && defined $tag_line_indent;
+ my $next_indented_tag = $is_indented_tag
+ && indented_tag_line ($_, $tag_line_indent);
+ my $compact_this_tag = $is_indented_tag
+ && ($compact_indented_tags || $next_indented_tag);
+ my $preserve_blank_after = 0;
+
+ if ($compact_this_tag)
+ {
+ $content =~ s/^\.TP\n/.TP $tag_line_width\n/;
+ unless ($compact_indented_tags)
+ {
+ $content = ".PD 0\n$content";
+ $content = ".PP\n$content" if $blank_before_paragraph;
+ $compact_indented_tags = 1;
+ }
+ unless ($next_indented_tag)
+ {
+ $content .= ".PD\n";
+ (my $next_paragraph = $_) =~ s/^\n+//;
+ $preserve_blank_after = /^\n/
+ && generic_indented_tag_line $next_paragraph;
+ $content .= ".PP\n" if $preserve_blank_after;
+ $compact_indented_tags = 0;
+ }
+ }
+ elsif ($compact_indented_tags)
+ {
+ $content = ".PD\n$content";
+ $compact_indented_tags = 0;
+ }
+
+ $blank_before_paragraph = s/^\n+//;
+ $blank_before_paragraph = 0 if $preserve_blank_after;
for ($content)
{
@@ -607,7 +660,7 @@ while (length)
)
/$1 . convert_option $2/xmge;
- # Italicise filenames: /a/b, $VAR/c/d, ~/e/f
+ # Italicize filenames: /a/b, $VAR/c/d, ~/e/f
s!
(^|[ (]) # space/punctuation before
(
@@ -724,6 +777,19 @@ my @post = (_('ENVIRONMENT'), _('FILES'), _('AUTHOR'),
_('REPORTING BUGS'), _('COPYRIGHT'), _('SEE ALSO'));
my %filter = map { $_ => 1 } @pre, @post;
+# Global storage for OSC 8 hyperlink URLs
+our @hyperlink_urls;
+
+# Helper to convert hyperlink markers to roff \X escapes
+sub convert_hyperlink {
+ my ($dashes, $idx, $text) = @_;
+ my $url = $hyperlink_urls[$idx];
+ my $full = $dashes . $text;
+ $full =~ s/\\f[BIRP]//g;
+ $full = "\\fB$full\\fP" if $opt_bold_refs;
+ return "\\X'tty: link $url'$full\\X'tty: link'";
+}
+
# Output content.
my %done;
for my $sect (@pre, (grep !$filter{$_}, @sections), @post)
@@ -751,6 +817,10 @@ for my $sect (@pre, (grep !$filter{$_}, @sections), @post)
s/\x82/\\e/g;
s/\x83/\\-/g;
+ # Convert hyperlink markers to roff \X escape sequences
+ s{((?:\+|(?:(?:\\f.)?\\-(?:\\-)?(?:\\f.)?))?)\x01(\d+)\x02(.*?)\x03}
+ {convert_hyperlink($1, $2, $3)}gse;
+
# Convert some latin1 chars to troff equivalents.
s/\xa0/\\ /g; # non-breaking space
@@ -793,11 +863,28 @@ sub get_option_value
kark $err, $this_program, $opt, $prog, $extra;
}
+ # Strip ANSI SGR formatting codes (colors, bold, etc.)
+ $value =~ s/\x1b\[[0-9;]*m//g;
+
+ # Convert OSC 8 hyperlinks to markers placed after any leading dashes/plus
+ # This preserves help2man's option detection (which looks for /^ +[-+]/)
+ # Support both BEL (\x07) and ST (\x1b\) terminators
+ $value =~ s/
+ \x1b\]8;;([^\x07\x1b]*)(?:\x07|\x1b\\) # opening OSC 8 with URL
+ (\+|--?)?(.*?) # optional +, -, or -- and link text
+ \x1b\]8;;(?:\x07|\x1b\\) # closing OSC 8
+ /{
+ my $idx = scalar @hyperlink_urls;
+ push @hyperlink_urls, $1;
+ my $dashes = $2 || "";
+ "$dashes\x01$idx\x02$3\x03";
+ }/gsex;
+
$value;
}
# Convert option dashes to \- to stop nroff from hyphenating 'em, and
-# embolden. Option arguments get italicised.
+# embolden. Option arguments get italicized.
sub convert_option
{
local $_ = '\fB' . shift;
@@ -824,6 +911,27 @@ sub fix_italic_spacing
return $_;
}
+# Return true if text starts with a generic indented tag row using
+# the same leading indentation. Option rows are handled separately.
+sub generic_indented_tag_line
+{
+ my $text = shift;
+
+ return 0 unless $text =~ /^ +\S.*? +\S.*\n/;
+ return 0 if $text
+ =~ /^( {1,10}[+-]\S.*?)(?:( +(?!-))|\n( {7,}))(\S.*)\n/;
+ return 0 if $text =~ /^ {1,10}[+-]\S.*\n/;
+ return 1;
+}
+
+sub indented_tag_line
+{
+ my ($text, $leading_indent) = @_;
+
+ return $text =~ /^\Q$leading_indent\E/
+ && generic_indented_tag_line $text;
+}
+
# Return indent to use: either the value passed in, or $v,$v+4 if
# loose index matching is used. The resulting string is used in a
# regex as " {$indent}", so will match either the exact number of
@@ -835,3 +943,13 @@ sub set_indent
$i .= ',' . ($_[0] + 4) if $loose_indent;
return $i;
}
+
+# Return visual length of a string, ignoring hyperlink markers.
+# Markers are \x01<digits>\x02 (opening) and \x03 (closing).
+sub visual_length
+{
+ local $_ = shift;
+ s/\x01\d+\x02//g;
+ s/\x03//g;
+ return length;
+}
diff --git a/man/local.mk b/man/local.mk
index 9759eabce..156b0e18c 100644
--- a/man/local.mk
+++ b/man/local.mk
@@ -1,7 +1,7 @@
# Make coreutils man pages. -*-Makefile-*-
# This is included by the top-level Makefile.am.
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -27,7 +27,8 @@ if HAVE_PERL
if BOLD_MAN_REFS
help2man_OPTS=--bold-refs
endif
-run_help2man = $(PERL) -- $(srcdir)/man/help2man --loose-indent $(help2man_OPTS)
+run_help2man = env IN_HELP2MAN=1 TERM=not_dumb $(PERL) -- \
+ $(srcdir)/man/help2man --loose-indent $(help2man_OPTS)
else
run_help2man = $(SHELL) $(srcdir)/man/dummy-man
endif
@@ -61,7 +62,11 @@ mandeps += $(top_srcdir)/src/system.h
$(ALL_MANS): $(mandeps)
if SINGLE_BINARY
+if SINGLE_BINARY_HARD
+mandeps += src/coreutils_hardlinks
+else
mandeps += src/coreutils$(EXEEXT)
+endif
else
# Most prog.1 man pages depend on src/prog. List the exceptions:
man/install.1: src/ginstall$(EXEEXT)
diff --git a/man/md5sum.x b/man/md5sum.x
index 3f4cc6409..c8fd8c0ed 100644
--- a/man/md5sum.x
+++ b/man/md5sum.x
@@ -4,8 +4,7 @@ md5sum \- compute and check MD5 message digest
.\" Add any additional description here
[BUGS]
Do not use the MD5 algorithm for security related purposes.
-Instead, use an SHA\-2 algorithm, implemented in the programs
-sha224sum(1), sha256sum(1), sha384sum(1), sha512sum(1),
-or the BLAKE2 algorithm, implemented in b2sum(1)
+Instead, use an SHA\-2, SHA\-3, or BLAKE2 algorithm,
+implemented in the cksum(1) program.
[SEE ALSO]
cksum(1)
diff --git a/man/rm.x b/man/rm.x
index 5a4ef75dc..4198b7895 100644
--- a/man/rm.x
+++ b/man/rm.x
@@ -1,4 +1,4 @@
-'\" Copyright (C) 1998-2025 Free Software Foundation, Inc.
+'\" Copyright (C) 1998-2026 Free Software Foundation, Inc.
'\"
'\" This is free software. You may redistribute copies of it under the terms
'\" of the GNU General Public License <https://www.gnu.org/licenses/gpl.html>.
diff --git a/man/sha1sum.x b/man/sha1sum.x
index 8fd824f10..fb2851390 100644
--- a/man/sha1sum.x
+++ b/man/sha1sum.x
@@ -3,9 +3,8 @@ sha1sum \- compute and check SHA1 message digest
[DESCRIPTION]
.\" Add any additional description here
[BUGS]
-Do not use the SHA-1 algorithm for security related purposes.
-Instead, use an SHA\-2 algorithm, implemented in the programs
-sha224sum(1), sha256sum(1), sha384sum(1), sha512sum(1),
-or the BLAKE2 algorithm, implemented in b2sum(1)
+Do not use the MD5 algorithm for security related purposes.
+Instead, use an SHA\-2, SHA\-3, or BLAKE2 algorithm,
+implemented in the cksum(1) program.
[SEE ALSO]
cksum(1)
diff --git a/man/stdbuf.x b/man/stdbuf.x
index 2446d713a..f95aea5e0 100644
--- a/man/stdbuf.x
+++ b/man/stdbuf.x
@@ -1,4 +1,4 @@
-'\" Copyright (C) 2009-2025 Free Software Foundation, Inc.
+'\" Copyright (C) 2009-2026 Free Software Foundation, Inc.
'\"
'\" This is free software. You may redistribute copies of it under the terms
'\" of the GNU General Public License <https://www.gnu.org/licenses/gpl.html>.
diff --git a/man/test.x b/man/test.x
index b293da976..2f951900b 100644
--- a/man/test.x
+++ b/man/test.x
@@ -1,4 +1,4 @@
-'\" Copyright (C) 1998-2025 Free Software Foundation, Inc.
+'\" Copyright (C) 1998-2026 Free Software Foundation, Inc.
'\"
'\" This is free software. You may redistribute copies of it under the terms
'\" of the GNU General Public License <https://www.gnu.org/licenses/gpl.html>.
diff --git a/man/touch.x b/man/touch.x
index 6c3aac299..739f0bbd7 100644
--- a/man/touch.x
+++ b/man/touch.x
@@ -1,5 +1,5 @@
[NAME]
-touch \- change file timestamps
+touch \- create file, or change file timestamps
[DESCRIPTION]
.\" Add any additional description here
[DATE STRING]
diff --git a/man/vdir.x b/man/vdir.x
index 60c02b7d1..aeaddb755 100644
--- a/man/vdir.x
+++ b/man/vdir.x
@@ -2,3 +2,5 @@
vdir \- list directory contents
[DESCRIPTION]
.\" Add any additional description here
+[SEE ALSO]
+dircolors(1)
diff --git a/man/viewman b/man/viewman
new file mode 100755
index 000000000..ee9f313b9
--- /dev/null
+++ b/man/viewman
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# Generate and view man page with less, for passed command name
+
+# Note to get man to behave like this you can also:
+# export MANROFFOPT=-P-i
+# export LESS=-R
+# export MANPAGER=less
+
+unset GROFF_NO_SGR
+
+hdir=$(dirname "$0")
+
+CONFIG_HEADER="$hdir"/../lib/config.h
+
+grep '^#define BOLD_MAN_REFS 1' $CONFIG_HEADER > /dev/null &&
+ BOLD_REFS=--bold-refs
+
+man="$1"; cmd="$1"
+test "$1" = 'test' && cmd='['
+test "$1" = '[' && man='test'
+test "$1" = 'install' && cmd='ginstall'
+test "$1" = 'ginstall' && man='install'
+
+"$hdir"/help2man \
+ --include="$hdir"/../man/$man.x \
+ $BOLD_REFS \
+ --loose-indent \
+ "$hdir"/../src/$cmd |
+preconv | # convert utf-8 chars like in Author names to groff compat
+groff -Tutf8 -man -P-i -rLL=${MANWIDTH:-$COLUMNS}n |
+less -R
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 444202840..0c9dfe810 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,5 +1,5 @@
# List of files which contain translatable strings.
-# Copyright (C) 1996-2025 Free Software Foundation, Inc.
+# Copyright (C) 1996-2026 Free Software Foundation, Inc.
# These are nominally temporary...
lib/argmatch.c
@@ -43,6 +43,7 @@ src/chown-core.c
src/chown.c
src/chroot.c
src/cksum.c
+src/cksum_crc.c
src/comm.c
src/copy.c
src/copy-file-data.c
@@ -81,7 +82,6 @@ src/link.c
src/ln.c
src/logname.c
src/ls.c
-src/digest.c
src/mkdir.c
src/mkfifo.c
src/mknod.c
diff --git a/scripts/build-older-versions/README.older-versions b/scripts/build-older-versions/README.older-versions
index 8e7f2109c..8e6320f8c 100644
--- a/scripts/build-older-versions/README.older-versions
+++ b/scripts/build-older-versions/README.older-versions
@@ -164,7 +164,7 @@ By adding the directory to your $PATH, older versions can be easily used:
========================================================================
-Copyright (C) 2019-2025 Free Software Foundation, Inc.
+Copyright (C) 2019-2026 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
diff --git a/scripts/build-older-versions/build-older-versions.sh b/scripts/build-older-versions/build-older-versions.sh
index 47171b88b..9aec72b10 100755
--- a/scripts/build-older-versions/build-older-versions.sh
+++ b/scripts/build-older-versions/build-older-versions.sh
@@ -1,6 +1,6 @@
#!/bin/sh
#
-# Copyright (C) 2019-2025 Free Software Foundation, Inc.
+# Copyright (C) 2019-2026 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
diff --git a/scripts/git-hooks/commit-msg b/scripts/git-hooks/commit-msg
index bdea8c38d..769cd1a50 100755
--- a/scripts/git-hooks/commit-msg
+++ b/scripts/git-hooks/commit-msg
@@ -24,7 +24,7 @@ my @valid = qw(
sum sync tac tail tee test timeout touch tr true truncate tsort
tty uname unexpand uniq unlink uptime users vdir wc who whoami yes
- all copy gnulib tests maint doc build scripts sha\*sum digest
+ all copy getlimits gnulib tests maint doc build scripts sha\*sum
);
my $v_or = join '|', @valid;
my $valid_regex = qr/^(?:$v_or)$/;
@@ -49,7 +49,7 @@ sub re_edit($)
warn "Interrupt (Ctrl-C) to abort...\n";
- system 'sh', '-c', "$editor $log_file";
+ system 'sh', '-c', "$editor $log_file </dev/tty >/dev/tty 2>&1";
($? & 127) || ($? >> 8)
and die "$ME: $log_file: the editor ($editor) failed, aborting\n";
}
diff --git a/src/.gitignore b/src/.gitignore
index 2fffa349b..50af90108 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -16,6 +16,7 @@ chroot
cksum
comm
coreutils
+coreutils_hardlinks
cp
csplit
cut
diff --git a/src/basename.c b/src/basename.c
index 13eaf64dd..96bbb71c5 100644
--- a/src/basename.c
+++ b/src/basename.c
@@ -1,5 +1,5 @@
/* basename -- strip directory and suffix from file names
- Copyright (C) 1990-2025 Free Software Foundation, Inc.
+ Copyright (C) 1990-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -29,12 +29,12 @@
static struct option const longopts[] =
{
- {"multiple", no_argument, nullptr, 'a'},
- {"suffix", required_argument, nullptr, 's'},
- {"zero", no_argument, nullptr, 'z'},
+ {"multiple", no_argument, NULL, 'a'},
+ {"suffix", required_argument, NULL, 's'},
+ {"zero", no_argument, NULL, 'z'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -56,13 +56,20 @@ If specified, also remove a trailing SUFFIX.\n\
emit_mandatory_arg_note ();
- fputs (_("\
- -a, --multiple support multiple arguments and treat each as a NAME\n\
- -s, --suffix=SUFFIX remove a trailing SUFFIX; implies -a\n\
- -z, --zero end each output line with NUL, not newline\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -a, --multiple\n\
+ support multiple arguments and treat each as a NAME\n\
+"));
+ oputs (_("\
+ -s, --suffix=SUFFIX\n\
+ remove a trailing SUFFIX; implies -a\n\
+"));
+ oputs (_("\
+ -z, --zero\n\
+ end each output line with NUL, not newline\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
printf (_("\
\n\
Examples:\n\
@@ -81,13 +88,10 @@ Examples:\n\
consists entirely of SUFFIX. */
static void
-remove_suffix (char *name, char const *suffix)
+remove_suffix (char *name, char const *suffix, idx_t suffix_len)
{
- char *np;
- char const *sp;
-
- np = name + strlen (name);
- sp = suffix + strlen (suffix);
+ char *np = name + strlen (name);
+ char const *sp = suffix + suffix_len;
while (np > name && sp > suffix)
if (*--np != *--sp)
@@ -96,11 +100,13 @@ remove_suffix (char *name, char const *suffix)
*np = '\0';
}
-/* Perform the basename operation on STRING. If SUFFIX is non-null, remove
- the trailing SUFFIX. Finally, output the result string. */
+/* Perform the basename operation on STRING. If SUFFIX has a positive
+ length of SUFFIX_LEN bytes, remove the trailing SUFFIX.
+ Finally, output the result string. */
static void
-perform_basename (char const *string, char const *suffix, bool use_nuls)
+perform_basename (char const *string, char const *suffix, idx_t suffix_len,
+ bool use_nuls)
{
char *name = base_name (string);
strip_trailing_slashes (name);
@@ -111,8 +117,9 @@ perform_basename (char const *string, char const *suffix, bool use_nuls)
skipping suffix stripping if base_name returned an absolute path
or a drive letter (only possible if name is a file-system
root). */
- if (suffix && IS_RELATIVE_FILE_NAME (name) && ! FILE_SYSTEM_PREFIX_LEN (name))
- remove_suffix (name, suffix);
+ if (0 < suffix_len && IS_RELATIVE_FILE_NAME (name)
+ && ! FILE_SYSTEM_PREFIX_LEN (name))
+ remove_suffix (name, suffix, suffix_len);
fputs (name, stdout);
putchar (use_nuls ? '\0' : '\n');
@@ -124,7 +131,7 @@ main (int argc, char **argv)
{
bool multiple_names = false;
bool use_nuls = false;
- char const *suffix = nullptr;
+ char const *suffix = NULL;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -136,7 +143,7 @@ main (int argc, char **argv)
while (true)
{
- int c = getopt_long (argc, argv, "+as:z", longopts, nullptr);
+ int c = getopt_long (argc, argv, "+as:z", longopts, NULL);
if (c == -1)
break;
@@ -170,21 +177,24 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
- if (!multiple_names && optind + 2 < argc)
+ if (! multiple_names)
{
- error (0, 0, _("extra operand %s"), quote (argv[optind + 2]));
- usage (EXIT_FAILURE);
+ if (optind + 2 == argc)
+ suffix = argv[optind + 1];
+ else if (optind + 2 < argc)
+ {
+ error (0, 0, _("extra operand %s"), quote (argv[optind + 2]));
+ usage (EXIT_FAILURE);
+ }
}
- if (multiple_names)
- {
- for (; optind < argc; optind++)
- perform_basename (argv[optind], suffix, use_nuls);
- }
- else
- perform_basename (argv[optind],
- optind + 2 == argc ? argv[optind + 1] : nullptr,
- use_nuls);
+ idx_t suffix_len = suffix ? strlen (suffix) : 0;
+
+ char **file = argv + optind;
+ int n_files = multiple_names ? argc - optind : 1;
+
+ for (int i = 0; i < n_files; ++i)
+ perform_basename (file[i], suffix, suffix_len, use_nuls);
return EXIT_SUCCESS;
}
diff --git a/src/basenc.c b/src/basenc.c
index ae55f8e32..8e8f61c3a 100644
--- a/src/basenc.c
+++ b/src/basenc.c
@@ -1,5 +1,5 @@
/* Base64, base32, and similar encoding/decoding strings or files.
- Copyright (C) 2004-2025 Free Software Foundation, Inc.
+ Copyright (C) 2004-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -29,7 +29,6 @@
#include "fadvise.h"
#include "quote.h"
#include "xstrtol.h"
-#include "xdectoint.h"
#include "xbinary-io.h"
#if BASE_TYPE == 42
@@ -74,23 +73,23 @@ enum
static struct option const long_options[] =
{
- {"decode", no_argument, 0, 'd'},
- {"wrap", required_argument, 0, 'w'},
- {"ignore-garbage", no_argument, 0, 'i'},
+ {"decode", no_argument, NULL, 'd'},
+ {"wrap", required_argument, NULL, 'w'},
+ {"ignore-garbage", no_argument, NULL, 'i'},
#if BASE_TYPE == 42
- {"base64", no_argument, 0, BASE64_OPTION},
- {"base64url", no_argument, 0, BASE64URL_OPTION},
- {"base58", no_argument, 0, BASE58_OPTION},
- {"base32", no_argument, 0, BASE32_OPTION},
- {"base32hex", no_argument, 0, BASE32HEX_OPTION},
- {"base16", no_argument, 0, BASE16_OPTION},
- {"base2msbf", no_argument, 0, BASE2MSBF_OPTION},
- {"base2lsbf", no_argument, 0, BASE2LSBF_OPTION},
- {"z85", no_argument, 0, Z85_OPTION},
+ {"base64", no_argument, NULL, BASE64_OPTION},
+ {"base64url", no_argument, NULL, BASE64URL_OPTION},
+ {"base58", no_argument, NULL, BASE58_OPTION},
+ {"base32", no_argument, NULL, BASE32_OPTION},
+ {"base32hex", no_argument, NULL, BASE32HEX_OPTION},
+ {"base16", no_argument, NULL, BASE16_OPTION},
+ {"base2msbf", no_argument, NULL, BASE2MSBF_OPTION},
+ {"base2lsbf", no_argument, NULL, BASE2LSBF_OPTION},
+ {"z85", no_argument, NULL, Z85_OPTION},
#endif
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -117,46 +116,62 @@ Base%d encode or decode FILE, or standard input, to standard output.\n\
emit_stdin_note ();
emit_mandatory_arg_note ();
#if BASE_TYPE == 42
- fputs (_("\
- --base64 same as 'base64' program (RFC4648 section 4)\n\
-"), stdout);
- fputs (_("\
- --base64url file- and url-safe base64 (RFC4648 section 5)\n\
-"), stdout);
- fputs (_("\
- --base58 visually unambiguous base58 encoding\n\
-"), stdout);
- fputs (_("\
- --base32 same as 'base32' program (RFC4648 section 6)\n\
-"), stdout);
- fputs (_("\
- --base32hex extended hex alphabet base32 (RFC4648 section 7)\n\
-"), stdout);
- fputs (_("\
- --base16 hex encoding (RFC4648 section 8)\n\
-"), stdout);
- fputs (_("\
- --base2msbf bit string with most significant bit (msb) first\n\
-"), stdout);
- fputs (_("\
- --base2lsbf bit string with least significant bit (lsb) first\n\
-"), stdout);
+ oputs (_("\
+ --base64\n\
+ same as base64(1) program (RFC4648 section 4)\n\
+"));
+ oputs (_("\
+ --base64url\n\
+ file- and url-safe base64 (RFC4648 section 5)\n\
+"));
+ oputs (_("\
+ --base58\n\
+ visually unambiguous base58 encoding\n\
+"));
+ oputs (_("\
+ --base32\n\
+ same as base32(1) program (RFC4648 section 6)\n\
+"));
+ oputs (_("\
+ --base32hex\n\
+ extended hex alphabet base32 (RFC4648 section 7)\n\
+"));
+ oputs (_("\
+ --base16\n\
+ hex encoding (RFC4648 section 8)\n\
+"));
+ oputs (_("\
+ --base2msbf\n\
+ bit string with most significant bit (msb) first\n\
+"));
+ oputs (_("\
+ --base2lsbf\n\
+ bit string with least significant bit (lsb) first\n\
+"));
#endif
- fputs (_("\
- -d, --decode decode data\n\
- -i, --ignore-garbage when decoding, ignore non-alphabet characters\n\
- -w, --wrap=COLS wrap encoded lines after COLS character (default 76).\n\
- Use 0 to disable line wrapping\n\
-"), stdout);
+ oputs (_("\
+ -d, --decode\n\
+ decode data\n\
+"));
+ oputs (_("\
+ -i, --ignore-garbage\n\
+ when decoding, ignore non-alphabet characters\n\
+"));
+ oputs (_("\
+ -w, --wrap=COLS\n\
+ wrap encoded lines after COLS character (default 76).\n\
+ Use 0 to disable line wrapping\n\
+"));
#if BASE_TYPE == 42
- fputs (_("\
- --z85 ascii85-like encoding (ZeroMQ spec:32/Z85);\n\
- when encoding, input length must be a multiple of 4;\n\
- when decoding, input length must be a multiple of 5\n\
-"), stdout);
+ oputs (_("\
+ --z85\n\
+ ascii85-like encoding (ZeroMQ spec:32/Z85);\n\
+ when encoding, input length must be a multiple of 4;\n\
+ when decoding, input length must be a multiple of 5\n\
+"));
#endif
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
#if BASE_TYPE == 42
fputs (_("\
\n\
@@ -833,7 +848,7 @@ z85_length (idx_t len)
static bool
isuz85 (unsigned char ch)
{
- return c_isalnum (ch) || strchr (".-:+=^!/*?&<>()[]{}@%$#", ch) != nullptr;
+ return c_isalnum (ch) || strchr (".-:+=^!/*?&<>()[]{}@%$#", ch) != NULL;
}
static char const z85_encoding[85] ATTRIBUTE_NONSTRING =
@@ -1224,7 +1239,7 @@ base58_length (idx_t len)
static void
base58_encode_ctx_init (struct base_encode_context *ctx)
{
- ctx->ctx.base58.buf = nullptr;
+ ctx->ctx.base58.buf = NULL;
ctx->ctx.base58.size = 0;
ctx->ctx.base58.capacity = 0;
}
@@ -1256,7 +1271,7 @@ base58_encode_ctx (struct base_encode_context *ctx,
}
static void
-base58_encode (char const* data, size_t data_len,
+base58_encode (char const *data, size_t data_len,
char *out, idx_t *outlen)
{
affirm (base_length (data_len) <= *outlen);
@@ -1300,7 +1315,7 @@ base58_encode_ctx_finalize (struct base_encode_context *ctx,
*out, outlen);
free (ctx->ctx.base58.buf);
- ctx->ctx.base58.buf = nullptr;
+ ctx->ctx.base58.buf = NULL;
return true;
}
@@ -1311,7 +1326,7 @@ base58_decode_ctx_init (struct base_decode_context *ctx)
{
ctx->ctx.base58.size = 0;
ctx->ctx.base58.capacity = 0;
- ctx->ctx.base58.buf = nullptr;
+ ctx->ctx.base58.buf = NULL;
}
static bool
@@ -1414,7 +1429,7 @@ base58_decode_ctx_finalize (struct base_decode_context *ctx,
*out, outlen);
free (ctx->ctx.base58.buf);
- ctx->ctx.base58.buf = nullptr;
+ ctx->ctx.base58.buf = NULL;
return ret;
}
@@ -1481,7 +1496,7 @@ do_encode (FILE *in, char const *infile, FILE *out, idx_t wrap_column)
#if BASE_TYPE == 42
/* Initialize encoding context if needed (for base58) */
struct base_encode_context encode_ctx;
- bool use_ctx = (base_encode_ctx_init != nullptr);
+ bool use_ctx = (base_encode_ctx_init != NULL);
if (use_ctx)
base_encode_ctx_init (&encode_ctx);
#endif
@@ -1553,7 +1568,7 @@ do_decode (FILE *in, char const *infile, FILE *out, bool ignore_garbage)
outbuf = xmalloc (DEC_BLOCKSIZE);
#if BASE_TYPE == 42
- ctx.inbuf = nullptr;
+ ctx.inbuf = NULL;
#endif
base_decode_ctx_init (&ctx);
@@ -1636,7 +1651,7 @@ main (int argc, char **argv)
atexit (close_stdout);
- while ((opt = getopt_long (argc, argv, "diw:", long_options, nullptr)) != -1)
+ while ((opt = getopt_long (argc, argv, "diw:", long_options, NULL)) != -1)
switch (opt)
{
case 'd':
@@ -1646,7 +1661,7 @@ main (int argc, char **argv)
case 'w':
{
intmax_t w;
- strtol_error s_err = xstrtoimax (optarg, nullptr, 10, &w, "");
+ strtol_error s_err = xstrtoimax (optarg, NULL, 10, &w, "");
if (LONGINT_OVERFLOW < s_err || w < 0)
error (EXIT_FAILURE, 0, "%s: %s",
_("invalid wrap size"), quote (optarg));
@@ -1805,7 +1820,7 @@ main (int argc, char **argv)
else
{
input_fh = fopen (infile, "rb");
- if (input_fh == nullptr)
+ if (input_fh == NULL)
error (EXIT_FAILURE, errno, "%s", quotef (infile));
}
diff --git a/src/blake2/b2sum.c b/src/blake2/b2sum.c
index 5d69ff8d4..2d96b93a5 100644
--- a/src/blake2/b2sum.c
+++ b/src/blake2/b2sum.c
@@ -267,12 +267,12 @@ int main( int argc, char **argv )
while( 1 )
{
int option_index = 0;
- char *end = nullptr;
+ char *end = NULL;
unsigned long outbits;
static struct option long_options[] = {
{ "help", no_argument, 0, 0 },
{ "tag", no_argument, 0, 0 },
- { nullptr, 0, nullptr, 0 }
+ { NULL, 0, NULL, 0 }
};
c = getopt_long( argc, argv, "a:l:", long_options, &option_index );
@@ -349,7 +349,7 @@ int main( int argc, char **argv )
for( i = optind; i < argc; ++i )
{
- FILE *f = nullptr;
+ FILE *f = NULL;
if( argv[i][0] == '-' && argv[i][1] == '\0' )
f = stdin;
else
diff --git a/src/cat.c b/src/cat.c
index 2a3c1d18a..43063fb9b 100644
--- a/src/cat.c
+++ b/src/cat.c
@@ -1,5 +1,5 @@
/* cat -- concatenate files and print on the standard output.
- Copyright (C) 1988-2025 Free Software Foundation, Inc.
+ Copyright (C) 1988-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -34,9 +34,13 @@
#include "system.h"
#include "alignalloc.h"
+#include "assure.h"
#include "ioblksize.h"
#include "fadvise.h"
#include "full-write.h"
+#include "isapipe.h"
+#include "splice.h"
+#include "unistd--.h"
#include "xbinary-io.h"
/* The official name of this program (e.g., no 'g' prefix). */
@@ -96,23 +100,38 @@ Concatenate FILE(s) to standard output.\n\
emit_stdin_note ();
- fputs (_("\
-\n\
+ oputs (_("\
-A, --show-all equivalent to -vET\n\
+"));
+ oputs (_("\
-b, --number-nonblank number nonempty output lines, overrides -n\n\
+"));
+ oputs (_("\
-e equivalent to -vE\n\
- -E, --show-ends display $ at end of each line\n\
+"));
+ oputs (_("\
+ -E, --show-ends display $ or ^M$ at end of each line\n\
+"));
+ oputs (_("\
-n, --number number all output lines\n\
+"));
+ oputs (_("\
-s, --squeeze-blank suppress repeated empty output lines\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-t equivalent to -vT\n\
+"));
+ oputs (_("\
-T, --show-tabs display TAB characters as ^I\n\
+"));
+ oputs (_("\
-u (ignored)\n\
+"));
+ oputs (_("\
-v, --show-nonprinting use ^ and M- notation, except for LFD and TAB\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
printf (_("\
\n\
Examples:\n\
@@ -514,7 +533,7 @@ copy_cat (void)
unsupported or the input file seems empty. */
for (bool some_copied = false; ; some_copied = true)
- switch (copy_file_range (input_desc, nullptr, STDOUT_FILENO, nullptr,
+ switch (copy_file_range (input_desc, NULL, STDOUT_FILENO, NULL,
copy_max, 0))
{
case 0:
@@ -523,13 +542,141 @@ copy_cat (void)
case -1:
if (errno == ENOSYS || is_ENOTSUP (errno) || errno == EINVAL
|| errno == EBADF || errno == EXDEV || errno == ETXTBSY
- || errno == EPERM)
+ || errno == EPERM || errno == EFBIG)
return 0;
error (0, errno, "%s", quotef (infile));
return -1;
}
}
+/* Copy data from input to output using splice if possible.
+ Return 1 if successful, 0 if ordinary read+write should be tried,
+ -1 if a serious problem has been diagnosed. */
+
+static int
+splice_cat (void)
+{
+ bool some_copied = false;
+ bool in_ok = true;
+ bool out_ok = true;
+
+#if HAVE_SPLICE
+
+ /* If PIPEFD[0] is a non-negative value, we have an open pipe from a
+ previous call to this function. At the start of this function the
+ pipe will always be empty. */
+ static int pipefd[2] = { -1, -1 };
+
+ /* The size of the pipe referred to by PIPEFD. */
+ static idx_t pipefd_pipe_size = 0;
+
+ /* Create an intermediate pipe if it is not already open.
+ Even if both input and output are pipes,
+ so that read and write errors can be distinguished. */
+ if (pipefd[0] < 0)
+ {
+ if (pipe (pipefd) < 0)
+ return false;
+ pipefd_pipe_size = increase_pipe_size (pipefd[1]);
+ }
+
+ /* Increase the size of the pipe referred to by standard output. */
+ static int stdout_is_pipe = -1;
+ static idx_t stdout_pipe_size = 0;
+ if (stdout_is_pipe == -1)
+ {
+ stdout_is_pipe = 0 < isapipe (STDOUT_FILENO);
+ if (stdout_is_pipe)
+ stdout_pipe_size = increase_pipe_size (STDOUT_FILENO);
+ }
+
+ idx_t pipe_size = MAX (pipefd_pipe_size, stdout_pipe_size);
+
+ while (true)
+ {
+ ssize_t bytes_read = splice (input_desc, NULL, pipefd[1], NULL,
+ pipe_size, 0);
+ /* If we successfully splice'd input previously, assume that any
+ subsequent error is fatal. If not, then fall back to read
+ and write. */
+ in_ok = 0 <= bytes_read || ! some_copied;
+ if (bytes_read == 0)
+ some_copied = true; /* Indicate splice complete. */
+ if (bytes_read <= 0)
+ goto done;
+ /* We need to drain the intermediate pipe to standard output. */
+ while (0 < bytes_read)
+ {
+ ssize_t bytes_written = splice (pipefd[0], NULL, STDOUT_FILENO, NULL,
+ pipe_size, 0);
+ /* If we successfully splice'd output, assume any subsequent
+ error is fatal. If not, than drain the intermediate pipe and
+ continue using read and write. */
+ if (bytes_written < 0)
+ {
+ if (some_copied)
+ out_ok = false;
+ else
+ {
+ char buf[BUFSIZ];
+ while (0 < bytes_read)
+ {
+ ssize_t count = MIN (bytes_read, sizeof buf);
+ ssize_t n_read = read (pipefd[0], buf, count);
+ /* Failure not associated with in or out. */
+ in_ok = out_ok = 0 <= n_read;
+ if (n_read <= 0)
+ goto done;
+ if (full_write (STDOUT_FILENO, buf, n_read) != n_read)
+ write_error ();
+ bytes_read -= n_read;
+ }
+ }
+ }
+ if (bytes_written <= 0)
+ goto done;
+ some_copied = true;
+ bytes_read -= bytes_written;
+ }
+ }
+
+ done:
+ if (! in_ok && ! out_ok)
+ {
+ /* Recreate the pipe on internal error. */
+ int saved_errno = errno;
+ close (pipefd[0]);
+ close (pipefd[1]);
+ errno = saved_errno;
+ pipefd[0] = pipefd[1] = -1;
+ pipefd_pipe_size = 0;
+ error (0, errno, "%s", _("splice error"));
+ }
+ else if (! in_ok)
+ error (0, errno, "%s", quotef (infile));
+ else if (! out_ok)
+ write_error ();
+#endif
+
+ return (in_ok && out_ok) ? some_copied : -1;
+}
+
+/* Reuse an aligned buffer across inputs, growing it only as needed. */
+
+static char *
+ensure_buf_size (char *buf, idx_t *buf_alloc, idx_t alignment, idx_t size)
+{
+ affirm (buf != NULL || *buf_alloc < size);
+
+ if (*buf_alloc < size)
+ {
+ alignfree (buf);
+ buf = xalignalloc (alignment, size);
+ *buf_alloc = size;
+ }
+
+ return buf;
+}
int
main (int argc, char **argv)
@@ -537,7 +684,7 @@ main (int argc, char **argv)
/* Nonzero if we have ever read standard input. */
bool have_read_stdin = false;
- struct stat stat_buf;
+ struct stat ostat_buf;
/* Variables that are set according to the specified options. */
bool number = false;
@@ -550,16 +697,16 @@ main (int argc, char **argv)
static struct option const long_options[] =
{
- {"number-nonblank", no_argument, nullptr, 'b'},
- {"number", no_argument, nullptr, 'n'},
- {"squeeze-blank", no_argument, nullptr, 's'},
- {"show-nonprinting", no_argument, nullptr, 'v'},
- {"show-ends", no_argument, nullptr, 'E'},
- {"show-tabs", no_argument, nullptr, 'T'},
- {"show-all", no_argument, nullptr, 'A'},
+ {"number-nonblank", no_argument, NULL, 'b'},
+ {"number", no_argument, NULL, 'n'},
+ {"squeeze-blank", no_argument, NULL, 's'},
+ {"show-nonprinting", no_argument, NULL, 'v'},
+ {"show-ends", no_argument, NULL, 'E'},
+ {"show-tabs", no_argument, NULL, 'T'},
+ {"show-all", no_argument, NULL, 'A'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
initialize_main (&argc, &argv);
@@ -577,7 +724,7 @@ main (int argc, char **argv)
/* Parse command line options. */
int c;
- while ((c = getopt_long (argc, argv, "benstuvAET", long_options, nullptr))
+ while ((c = getopt_long (argc, argv, "benstuvAET", long_options, NULL))
!= -1)
{
switch (c)
@@ -638,28 +785,17 @@ main (int argc, char **argv)
/* Get device, i-node number, and optimal blocksize of output. */
- if (fstat (STDOUT_FILENO, &stat_buf) < 0)
+ if (fstat (STDOUT_FILENO, &ostat_buf) < 0)
error (EXIT_FAILURE, errno, _("standard output"));
/* Optimal size of i/o operations of output. */
- idx_t outsize = io_blksize (&stat_buf);
+ idx_t outsize = io_blksize (&ostat_buf);
/* Device, I-node number and lazily-acquired flags of the output. */
- struct
- {
- dev_t st_dev;
- ino_t st_ino;
- } out_id;
int out_flags = -2;
- bool have_out_dev = ! (S_TYPEISSHM (&stat_buf) || S_TYPEISTMO (&stat_buf));
- if (have_out_dev)
- {
- out_id.st_dev = stat_buf.st_dev;
- out_id.st_ino = stat_buf.st_ino;
- }
/* True if the output is a regular file. */
- bool out_isreg = S_ISREG (stat_buf.st_mode) != 0;
+ bool out_isreg = S_ISREG (ostat_buf.st_mode) != 0;
if (! (number || show_ends || squeeze_blank))
{
@@ -673,6 +809,10 @@ main (int argc, char **argv)
int argind = optind;
bool ok = true;
idx_t page_size = getpagesize ();
+ char *inbuf = NULL;
+ char *outbuf = NULL;
+ idx_t inbuf_alloc = 0;
+ idx_t outbuf_alloc = 0;
do
{
@@ -698,7 +838,8 @@ main (int argc, char **argv)
}
}
- if (fstat (input_desc, &stat_buf) < 0)
+ struct stat istat_buf;
+ if (fstat (input_desc, &istat_buf) < 0)
{
error (0, errno, "%s", quotef (infile));
ok = false;
@@ -706,7 +847,7 @@ main (int argc, char **argv)
}
/* Optimal size of i/o operations of input. */
- idx_t insize = io_blksize (&stat_buf);
+ idx_t insize = io_blksize (&istat_buf);
fdadvise (input_desc, 0, 0, FADVISE_SEQUENTIAL);
@@ -714,10 +855,10 @@ main (int argc, char **argv)
output device. It's better to catch this error earlier
rather than later. */
- if (! (S_ISFIFO (stat_buf.st_mode) || S_ISSOCK (stat_buf.st_mode)
- || S_TYPEISSHM (&stat_buf) || S_TYPEISTMO (&stat_buf))
- && have_out_dev
- && SAME_INODE (stat_buf, out_id))
+ if (! (S_ISFIFO (istat_buf.st_mode) || S_ISSOCK (istat_buf.st_mode)
+ || S_TYPEISSHM (&istat_buf) || S_TYPEISTMO (&istat_buf))
+ && ! (S_TYPEISSHM (&ostat_buf) || S_TYPEISTMO (&ostat_buf))
+ && SAME_INODE (istat_buf, ostat_buf))
{
off_t in_pos = lseek (input_desc, 0, SEEK_CUR);
if (0 <= in_pos)
@@ -736,9 +877,6 @@ main (int argc, char **argv)
}
}
- /* Pointer to the input buffer. */
- char *inbuf;
-
/* Select which version of 'cat' to use. If any format-oriented
options were given use 'cat'; if not, use 'copy_cat' if it
works, 'simple_cat' otherwise. */
@@ -747,23 +885,34 @@ main (int argc, char **argv)
|| show_tabs || squeeze_blank))
{
int copy_cat_status =
- out_isreg && S_ISREG (stat_buf.st_mode) ? copy_cat () : 0;
+ out_isreg && S_ISREG (istat_buf.st_mode) ? copy_cat () : 0;
if (copy_cat_status != 0)
- {
- inbuf = nullptr;
- ok &= 0 < copy_cat_status;
- }
+ ok &= 0 < copy_cat_status;
else
{
- insize = MAX (insize, outsize);
- inbuf = xalignalloc (page_size, insize);
- ok &= simple_cat (inbuf, insize);
+ /* Note 32768 was determined as the limit when splice
+ starts to have a performance advantage.
+ It also excludes zero length files which may not
+ be compatible with splice in some edge cases. */
+ int splice_cat_status = ((usable_st_size (&istat_buf)
+ && istat_buf.st_size <= 32768)
+ ? 0 : splice_cat ());
+ if (splice_cat_status != 0)
+ ok &= 0 < splice_cat_status;
+ else
+ {
+ insize = MAX (insize, outsize);
+ inbuf = ensure_buf_size (inbuf, &inbuf_alloc,
+ page_size, insize);
+ ok &= simple_cat (inbuf, insize);
+ }
}
}
else
{
/* Allocate, with an extra byte for a newline sentinel. */
- inbuf = xalignalloc (page_size, insize + 1);
+ inbuf = ensure_buf_size (inbuf, &inbuf_alloc,
+ page_size, insize + 1);
/* Why are
(OUTSIZE - 1 + INSIZE * 4 + LINE_COUNTER_BUF_LEN)
@@ -791,17 +940,14 @@ main (int argc, char **argv)
|| ckd_add (&bufsize, bufsize, outsize)
|| ckd_add (&bufsize, bufsize, LINE_COUNTER_BUF_LEN - 1))
xalloc_die ();
- char *outbuf = xalignalloc (page_size, bufsize);
+ outbuf = ensure_buf_size (outbuf, &outbuf_alloc,
+ page_size, bufsize);
ok &= cat (inbuf, insize, outbuf, outsize, show_nonprinting,
show_tabs, number, number_nonblank, show_ends,
squeeze_blank);
-
- alignfree (outbuf);
}
- alignfree (inbuf);
-
contin:
if (!reading_stdin && close (input_desc) < 0)
{
@@ -817,6 +963,11 @@ main (int argc, char **argv)
write_error ();
}
+#ifdef lint
+ alignfree (outbuf);
+ alignfree (inbuf);
+#endif
+
if (have_read_stdin && close (STDIN_FILENO) < 0)
error (EXIT_FAILURE, errno, _("closing standard input"));
diff --git a/src/chcon.c b/src/chcon.c
index 5f13c7d75..91b78148d 100644
--- a/src/chcon.c
+++ b/src/chcon.c
@@ -1,5 +1,5 @@
/* chcon -- change security context of files
- Copyright (C) 2005-2025 Free Software Foundation, Inc.
+ Copyright (C) 2005-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -46,7 +46,7 @@ static bool recurse;
static bool verbose;
/* Pointer to the device and inode numbers of '/', when --recursive.
- Otherwise nullptr. */
+ Otherwise NULL. */
static struct dev_ino *root_dev_ino;
/* The name of the context file is being given. */
@@ -70,20 +70,20 @@ enum
static struct option const long_options[] =
{
- {"recursive", no_argument, nullptr, 'R'},
- {"dereference", no_argument, nullptr, DEREFERENCE_OPTION},
- {"no-dereference", no_argument, nullptr, 'h'},
- {"no-preserve-root", no_argument, nullptr, NO_PRESERVE_ROOT},
- {"preserve-root", no_argument, nullptr, PRESERVE_ROOT},
- {"reference", required_argument, nullptr, REFERENCE_FILE_OPTION},
- {"user", required_argument, nullptr, 'u'},
- {"role", required_argument, nullptr, 'r'},
- {"type", required_argument, nullptr, 't'},
- {"range", required_argument, nullptr, 'l'},
- {"verbose", no_argument, nullptr, 'v'},
+ {"recursive", no_argument, NULL, 'R'},
+ {"dereference", no_argument, NULL, DEREFERENCE_OPTION},
+ {"no-dereference", no_argument, NULL, 'h'},
+ {"no-preserve-root", no_argument, NULL, NO_PRESERVE_ROOT},
+ {"preserve-root", no_argument, NULL, PRESERVE_ROOT},
+ {"reference", required_argument, NULL, REFERENCE_FILE_OPTION},
+ {"user", required_argument, NULL, 'u'},
+ {"role", required_argument, NULL, 'r'},
+ {"type", required_argument, NULL, 't'},
+ {"range", required_argument, NULL, 'l'},
+ {"verbose", no_argument, NULL, 'v'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* Given a security context, CONTEXT, derive a context_t (*RET),
@@ -139,12 +139,12 @@ compute_context_from_mask (char const *context, context_t *ret)
static int
change_file_context (int fd, char const *file)
{
- char *file_context = nullptr;
- context_t context IF_LINT (= 0);
+ char *file_context = NULL;
+ context_t context IF_LINT (= NULL);
char const * context_string;
int errors = 0;
- if (specified_context == nullptr)
+ if (specified_context == NULL)
{
int status = (affect_symlink_referent
? getfileconat (fd, file, &file_context)
@@ -160,7 +160,7 @@ change_file_context (int fd, char const *file)
/* If the file doesn't have a context, and we're not setting all of
the context components, there isn't really an obvious default.
Thus, we just give up. */
- if (file_context == nullptr)
+ if (file_context == NULL)
{
error (0, 0, _("can't apply partial context to unlabeled file %s"),
quoteaf (file));
@@ -180,7 +180,7 @@ change_file_context (int fd, char const *file)
context_string = specified_context;
}
- if (file_context == nullptr || ! streq (context_string, file_context))
+ if (file_context == NULL || ! streq (context_string, file_context))
{
int fail = (affect_symlink_referent
? setfileconat (fd, file, context_string)
@@ -194,7 +194,7 @@ change_file_context (int fd, char const *file)
}
}
- if (specified_context == nullptr)
+ if (specified_context == NULL)
{
context_free (context);
freecon (file_context);
@@ -314,14 +314,14 @@ process_files (char **files, int bit_flags)
{
bool ok = true;
- FTS *fts = xfts_open (files, bit_flags, nullptr);
+ FTS *fts = xfts_open (files, bit_flags, NULL);
while (true)
{
FTSENT *ent;
ent = fts_read (fts);
- if (ent == nullptr)
+ if (ent == NULL)
{
if (errno != 0)
{
@@ -364,46 +364,54 @@ With --reference, change the security context of each FILE to that of RFILE.\n\
emit_mandatory_arg_note ();
- fputs (_("\
- --dereference affect the referent of each symbolic link (this is\n\
- the default), rather than the symbolic link itself\n\
- -h, --no-dereference affect symbolic links instead of any referenced file\n\
-"), stdout);
- fputs (_("\
- -u, --user=USER set user USER in the target security context\n\
- -r, --role=ROLE set role ROLE in the target security context\n\
- -t, --type=TYPE set type TYPE in the target security context\n\
- -l, --range=RANGE set range RANGE in the target security context\n\
-"), stdout);
- fputs (_("\
- --no-preserve-root do not treat '/' specially (the default)\n\
- --preserve-root fail to operate recursively on '/'\n\
-"), stdout);
- fputs (_("\
- --reference=RFILE use RFILE's security context rather than specifying\n\
- a CONTEXT value\n\
-"), stdout);
- fputs (_("\
- -R, --recursive operate on files and directories recursively\n\
-"), stdout);
- fputs (_("\
- -v, --verbose output a diagnostic for every file processed\n\
-"), stdout);
- fputs (_("\
-\n\
-The following options modify how a hierarchy is traversed when the -R\n\
-option is also specified. If more than one is specified, only the final\n\
-one takes effect.\n\
-\n\
- -H if a command line argument is a symbolic link\n\
- to a directory, traverse it\n\
- -L traverse every symbolic link to a directory\n\
- encountered\n\
- -P do not traverse any symbolic links (default)\n\
-\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ --dereference\n\
+ affect the referent of each symbolic link (this is\n\
+ the default), rather than the symbolic link itself\n\
+"));
+ oputs (_("\
+ -h, --no-dereference\n\
+ affect symbolic links instead of any referenced file\n\
+"));
+ oputs (_("\
+ -u, --user=USER\n\
+ set user USER in the target security context\n\
+"));
+ oputs (_("\
+ -r, --role=ROLE\n\
+ set role ROLE in the target security context\n\
+"));
+ oputs (_("\
+ -t, --type=TYPE\n\
+ set type TYPE in the target security context\n\
+"));
+ oputs (_("\
+ -l, --range=RANGE\n\
+ set range RANGE in the target security context\n\
+"));
+ oputs (_("\
+ --no-preserve-root\n\
+ do not treat '/' specially (the default)\n\
+"));
+ oputs (_("\
+ --preserve-root\n\
+ fail to operate recursively on '/'\n\
+"));
+ oputs (_("\
+ --reference=RFILE\n\
+ use RFILE's security context rather than specifying a CONTEXT value\n\
+"));
+ oputs (_("\
+ -R, --recursive\n\
+ operate on files and directories recursively\n\
+"));
+ oputs (_("\
+ -v, --verbose\n\
+ output a diagnostic for every file processed\n\
+"));
+ emit_symlink_recurse_options ("-P");
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -422,7 +430,7 @@ main (int argc, char **argv)
bool ok;
bool preserve_root = false;
bool component_specified = false;
- char *reference_file = nullptr;
+ char *reference_file = NULL;
int optc;
initialize_main (&argc, &argv);
@@ -434,7 +442,7 @@ main (int argc, char **argv)
atexit (close_stdout);
while ((optc = getopt_long (argc, argv, "HLPRhvu:r:t:l:",
- long_options, nullptr))
+ long_options, NULL))
!= -1)
{
switch (optc)
@@ -544,7 +552,7 @@ main (int argc, char **argv)
if (reference_file)
{
- char *ref_context = nullptr;
+ char *ref_context = NULL;
if (getfilecon (reference_file, &ref_context) < 0)
error (EXIT_FAILURE, errno, _("failed to get security context of %s"),
@@ -555,7 +563,7 @@ main (int argc, char **argv)
else if (component_specified)
{
/* FIXME: it's already null, so this is a no-op. */
- specified_context = nullptr;
+ specified_context = NULL;
}
else
{
@@ -576,13 +584,13 @@ main (int argc, char **argv)
{
static struct dev_ino dev_ino_buf;
root_dev_ino = get_root_dev_ino (&dev_ino_buf);
- if (root_dev_ino == nullptr)
+ if (root_dev_ino == NULL)
error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
quoteaf ("/"));
}
else
{
- root_dev_ino = nullptr;
+ root_dev_ino = NULL;
}
ok = process_files (argv + optind, bit_flags | FTS_NOSTAT);
diff --git a/src/chmod.c b/src/chmod.c
index e38fdbd2b..8b5717270 100644
--- a/src/chmod.c
+++ b/src/chmod.c
@@ -1,5 +1,5 @@
/* chmod -- change permission modes of files
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -90,7 +90,7 @@ static bool diagnose_surprises;
static enum Verbosity verbosity = V_off;
/* Pointer to the device and inode numbers of '/', when --recursive.
- Otherwise nullptr. */
+ Otherwise NULL. */
static struct dev_ino *root_dev_ino;
/* For long options that have no equivalent short option, use a
@@ -105,19 +105,19 @@ enum
static struct option const long_options[] =
{
- {"changes", no_argument, nullptr, 'c'},
- {"dereference", no_argument, nullptr, DEREFERENCE_OPTION},
- {"recursive", no_argument, nullptr, 'R'},
- {"no-dereference", no_argument, nullptr, 'h'},
- {"no-preserve-root", no_argument, nullptr, NO_PRESERVE_ROOT},
- {"preserve-root", no_argument, nullptr, PRESERVE_ROOT},
- {"quiet", no_argument, nullptr, 'f'},
- {"reference", required_argument, nullptr, REFERENCE_FILE_OPTION},
- {"silent", no_argument, nullptr, 'f'},
- {"verbose", no_argument, nullptr, 'v'},
+ {"changes", no_argument, NULL, 'c'},
+ {"dereference", no_argument, NULL, DEREFERENCE_OPTION},
+ {"recursive", no_argument, NULL, 'R'},
+ {"no-dereference", no_argument, NULL, 'h'},
+ {"no-preserve-root", no_argument, NULL, NO_PRESERVE_ROOT},
+ {"preserve-root", no_argument, NULL, PRESERVE_ROOT},
+ {"quiet", no_argument, NULL, 'f'},
+ {"reference", required_argument, NULL, REFERENCE_FILE_OPTION},
+ {"silent", no_argument, NULL, 'f'},
+ {"verbose", no_argument, NULL, 'v'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* Return true if the chmodable permission bits of FILE changed.
@@ -305,7 +305,7 @@ process_file (FTS *fts, FTSENT *ent)
{
ch.old_mode = file_stats->st_mode;
ch.new_mode = mode_adjust (ch.old_mode, S_ISDIR (ch.old_mode) != 0,
- umask_value, change, nullptr);
+ umask_value, change, NULL);
bool follow_symlink = !!dereference;
if (dereference == -1) /* -H with/without -R, -P without -R. */
follow_symlink = ent->fts_level == FTS_ROOTLEVEL;
@@ -341,7 +341,7 @@ process_file (FTS *fts, FTSENT *ent)
{
mode_t naively_expected_mode =
mode_adjust (ch.old_mode, S_ISDIR (ch.old_mode) != 0,
- 0, change, nullptr);
+ 0, change, NULL);
if (ch.new_mode & ~naively_expected_mode)
{
char new_perms[12];
@@ -372,14 +372,14 @@ process_files (char **files, int bit_flags)
{
bool ok = true;
- FTS *fts = xfts_open (files, bit_flags, nullptr);
+ FTS *fts = xfts_open (files, bit_flags, NULL);
while (true)
{
FTSENT *ent;
ent = fts_read (fts);
- if (ent == nullptr)
+ if (ent == NULL)
{
if (errno != 0)
{
@@ -421,30 +421,47 @@ Change the mode of each FILE to MODE.\n\
With --reference, change the mode of each FILE to that of RFILE.\n\
\n\
"), stdout);
- fputs (_("\
- -c, --changes like verbose but report only when a change is made\n\
- -f, --silent, --quiet suppress most error messages\n\
- -v, --verbose output a diagnostic for every file processed\n\
-"), stdout);
- fputs (_("\
- --dereference affect the referent of each symbolic link,\n\
- rather than the symbolic link itself\n\
- -h, --no-dereference affect each symbolic link, rather than the referent\n\
-"), stdout);
- fputs (_("\
- --no-preserve-root do not treat '/' specially (the default)\n\
- --preserve-root fail to operate recursively on '/'\n\
-"), stdout);
- fputs (_("\
- --reference=RFILE use RFILE's mode instead of specifying MODE values.\n\
- RFILE is always dereferenced if a symbolic link.\n\
-"), stdout);
- fputs (_("\
- -R, --recursive change files and directories recursively\n\
-"), stdout);
+ oputs (_("\
+ -c, --changes\n\
+ like verbose but report only when a change is made\n\
+"));
+ oputs (_("\
+ -f, --silent, --quiet\n\
+ suppress most error messages\n\
+"));
+ oputs (_("\
+ -v, --verbose\n\
+ output a diagnostic for every file processed\n\
+"));
+ oputs (_("\
+ --dereference\n\
+ affect the referent of each symbolic link,\n\
+ rather than the symbolic link itself\n\
+"));
+ oputs (_("\
+ -h, --no-dereference\n\
+ affect each symbolic link, rather than the referent\n\
+"));
+ oputs (_("\
+ --no-preserve-root\n\
+ do not treat '/' specially (the default)\n\
+"));
+ oputs (_("\
+ --preserve-root\n\
+ fail to operate recursively on '/'\n\
+"));
+ oputs (_("\
+ --reference=RFILE\n\
+ use RFILE's mode instead of specifying MODE values.\n\
+ RFILE is always dereferenced if a symbolic link.\n\
+"));
+ oputs (_("\
+ -R, --recursive\n\
+ change files and directories recursively\n\
+"));
emit_symlink_recurse_options ("-H");
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=][0-7]+'.\n\
@@ -460,12 +477,12 @@ Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=][0-7]+'.\n\
int
main (int argc, char **argv)
{
- char *mode = nullptr;
+ char *mode = NULL;
idx_t mode_len = 0;
idx_t mode_alloc = 0;
bool ok;
bool preserve_root = false;
- char const *reference_file = nullptr;
+ char const *reference_file = NULL;
int c;
int bit_flags = FTS_COMFOLLOW | FTS_PHYSICAL;
@@ -477,12 +494,10 @@ main (int argc, char **argv)
atexit (close_stdout);
- recurse = force_silent = diagnose_surprises = false;
-
while ((c = getopt_long (argc, argv,
("HLPRcfhvr::w::x::X::s::t::u::g::o::a::,::+::=::"
"0::1::2::3::4::5::6::7::"),
- long_options, nullptr))
+ long_options, NULL))
!= -1)
{
switch (c)
@@ -636,13 +651,13 @@ main (int argc, char **argv)
{
static struct dev_ino dev_ino_buf;
root_dev_ino = get_root_dev_ino (&dev_ino_buf);
- if (root_dev_ino == nullptr)
+ if (root_dev_ino == NULL)
error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
quoteaf ("/"));
}
else
{
- root_dev_ino = nullptr;
+ root_dev_ino = NULL;
}
bit_flags |= FTS_DEFER_STAT;
diff --git a/src/chown-chgrp.c b/src/chown-chgrp.c
index aa0b18007..fb076d136 100644
--- a/src/chown-chgrp.c
+++ b/src/chown-chgrp.c
@@ -1,2 +1,2 @@
#include "chown.h"
-int chown_mode = CHOWN_CHGRP;
+enum chown_modes chown_mode = CHOWN_CHGRP;
diff --git a/src/chown-chown.c b/src/chown-chown.c
index 9531fa260..b11873a4b 100644
--- a/src/chown-chown.c
+++ b/src/chown-chown.c
@@ -1,2 +1,2 @@
#include "chown.h"
-int chown_mode = CHOWN_CHOWN;
+enum chown_modes chown_mode = CHOWN_CHOWN;
diff --git a/src/chown-core.c b/src/chown-core.c
index 55d1cf718..e5e355fdb 100644
--- a/src/chown-core.c
+++ b/src/chown-core.c
@@ -1,5 +1,5 @@
/* chown-core.c -- core functions for changing ownership.
- Copyright (C) 2000-2025 Free Software Foundation, Inc.
+ Copyright (C) 2000-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -58,12 +58,12 @@ extern void
chopt_init (struct Chown_option *chopt)
{
chopt->verbosity = V_off;
- chopt->root_dev_ino = nullptr;
+ chopt->root_dev_ino = NULL;
chopt->affect_symlink_referent = true;
chopt->recurse = false;
chopt->force_silent = false;
- chopt->user_name = nullptr;
- chopt->group_name = nullptr;
+ chopt->user_name = NULL;
+ chopt->group_name = NULL;
}
extern void
@@ -122,7 +122,7 @@ uid_to_name (uid_t uid)
static char *
user_group_str (char const *user, char const *group)
{
- char *spec = nullptr;
+ char *spec = NULL;
if (user)
{
@@ -153,10 +153,6 @@ describe_change (char const *file, enum Change_status changed,
char const *old_user, char const *old_group,
char const *user, char const *group)
{
- char const *fmt;
- char *old_spec;
- char *spec;
-
if (changed == CH_NOT_APPLIED)
{
printf (_("neither symbolic link %s nor referent has been changed\n"),
@@ -164,10 +160,11 @@ describe_change (char const *file, enum Change_status changed,
return;
}
- spec = user_group_str (user, group);
- old_spec = user_group_str (user ? old_user : nullptr,
- group ? old_group : nullptr);
+ char *spec = user_group_str (user, group);
+ char *old_spec = user_group_str (user ? old_user : NULL,
+ group ? old_group : NULL);
+ char const *fmt;
switch (changed)
{
case CH_SUCCEEDED:
@@ -189,7 +186,7 @@ describe_change (char const *file, enum Change_status changed,
: _("failed to change ownership of %s\n"));
free (old_spec);
old_spec = spec;
- spec = nullptr;
+ spec = NULL;
}
break;
case CH_NO_CHANGE_REQUESTED:
@@ -232,14 +229,10 @@ restricted_chown (int cwd_fd, char const *file,
uid_t uid, gid_t gid,
uid_t required_uid, gid_t required_gid)
{
- enum RCH_status status = RC_ok;
- struct stat st;
- int open_flags = O_NONBLOCK | O_NOCTTY;
- int fd;
-
if (required_uid == (uid_t) -1 && required_gid == (gid_t) -1)
return RC_do_ordinary_chown;
+ int open_flags = O_NONBLOCK | O_NOCTTY;
if (! S_ISREG (orig_st->st_mode))
{
if (S_ISDIR (orig_st->st_mode))
@@ -248,12 +241,14 @@ restricted_chown (int cwd_fd, char const *file,
return RC_do_ordinary_chown;
}
- fd = openat (cwd_fd, file, O_RDONLY | open_flags);
+ int fd = openat (cwd_fd, file, O_RDONLY | open_flags);
if (! (0 <= fd
|| (errno == EACCES && S_ISREG (orig_st->st_mode)
&& 0 <= (fd = openat (cwd_fd, file, O_WRONLY | open_flags)))))
return (errno == EACCES ? RC_do_ordinary_chown : RC_error);
+ enum RCH_status status = RC_ok;
+ struct stat st;
if (fstat (fd, &st) != 0)
status = RC_error;
else if (! psame_inode (orig_st, &st))
@@ -288,11 +283,7 @@ change_file_owner (FTS *fts, FTSENT *ent,
{
char const *file_full_name = ent->fts_path;
char const *file = ent->fts_accpath;
- struct stat const *file_stats;
- struct stat stat_buf;
bool ok = true;
- bool do_chown;
- bool symlink_changed = true;
switch (ent->fts_info)
{
@@ -364,10 +355,13 @@ change_file_owner (FTS *fts, FTSENT *ent,
break;
}
+ bool do_chown;
+ struct stat stat_buf;
+ struct stat const *file_stats;
if (!ok)
{
do_chown = false;
- file_stats = nullptr;
+ file_stats = NULL;
}
else if (required_uid == (uid_t) -1 && required_gid == (gid_t) -1
&& chopt->verbosity == V_off
@@ -412,6 +406,7 @@ change_file_owner (FTS *fts, FTSENT *ent,
return false;
}
+ bool symlink_changed = true;
if (do_chown)
{
if ( ! chopt->affect_symlink_referent)
@@ -496,15 +491,15 @@ change_file_owner (FTS *fts, FTSENT *ent,
: !changed ? CH_NO_CHANGE_REQUESTED
: CH_SUCCEEDED);
char *old_usr = (file_stats
- ? uid_to_name (file_stats->st_uid) : nullptr);
+ ? uid_to_name (file_stats->st_uid) : NULL);
char *old_grp = (file_stats
- ? gid_to_name (file_stats->st_gid) : nullptr);
+ ? gid_to_name (file_stats->st_gid) : NULL);
char *new_usr = chopt->user_name
? chopt->user_name : uid != -1
- ? uid_to_str (uid) : nullptr;
+ ? uid_to_str (uid) : NULL;
char *new_grp = chopt->group_name
? chopt->group_name : gid != -1
- ? gid_to_str (gid) : nullptr;
+ ? gid_to_str (gid) : NULL;
describe_change (file_full_name, ch_status,
old_usr, old_grp,
new_usr, new_grp);
@@ -547,14 +542,13 @@ chown_files (char **files, int bit_flags,
? 0
: FTS_NOSTAT);
- FTS *fts = xfts_open (files, bit_flags | stat_flags, nullptr);
+ FTS *fts = xfts_open (files, bit_flags | stat_flags, NULL);
while (true)
{
- FTSENT *ent;
+ FTSENT *ent = fts_read (fts);
- ent = fts_read (fts);
- if (ent == nullptr)
+ if (ent == NULL)
{
if (errno != 0)
{
diff --git a/src/chown-core.h b/src/chown-core.h
index 9fc50a005..8927f68e0 100644
--- a/src/chown-core.h
+++ b/src/chown-core.h
@@ -1,6 +1,6 @@
/* chown-core.h -- types and prototypes shared by chown and chgrp.
- Copyright (C) 2000-2025 Free Software Foundation, Inc.
+ Copyright (C) 2000-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -50,7 +50,7 @@ struct Chown_option
bool recurse;
/* Pointer to the device and inode numbers of '/', when --recursive.
- Need not be freed. Otherwise nullptr. */
+ Need not be freed. Otherwise NULL. */
struct dev_ino *root_dev_ino;
/* This corresponds to the --dereference (opposite of -h) option. */
diff --git a/src/chown.c b/src/chown.c
index 5b497a1fe..82343cfc6 100644
--- a/src/chown.c
+++ b/src/chown.c
@@ -1,5 +1,5 @@
/* chown, chgrp -- change user and group ownership of files
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -53,20 +53,20 @@ enum
static struct option const long_options[] =
{
- {"recursive", no_argument, nullptr, 'R'},
- {"changes", no_argument, nullptr, 'c'},
- {"dereference", no_argument, nullptr, DEREFERENCE_OPTION},
- {"from", required_argument, nullptr, FROM_OPTION},
- {"no-dereference", no_argument, nullptr, 'h'},
- {"no-preserve-root", no_argument, nullptr, NO_PRESERVE_ROOT},
- {"preserve-root", no_argument, nullptr, PRESERVE_ROOT},
- {"quiet", no_argument, nullptr, 'f'},
- {"silent", no_argument, nullptr, 'f'},
- {"reference", required_argument, nullptr, REFERENCE_FILE_OPTION},
- {"verbose", no_argument, nullptr, 'v'},
+ {"recursive", no_argument, NULL, 'R'},
+ {"changes", no_argument, NULL, 'c'},
+ {"dereference", no_argument, NULL, DEREFERENCE_OPTION},
+ {"from", required_argument, NULL, FROM_OPTION},
+ {"no-dereference", no_argument, NULL, 'h'},
+ {"no-preserve-root", no_argument, NULL, NO_PRESERVE_ROOT},
+ {"preserve-root", no_argument, NULL, PRESERVE_ROOT},
+ {"quiet", no_argument, NULL, 'f'},
+ {"silent", no_argument, NULL, 'f'},
+ {"reference", required_argument, NULL, REFERENCE_FILE_OPTION},
+ {"verbose", no_argument, NULL, 'v'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -95,41 +95,55 @@ Change the group of each FILE to GROUP.\n\
With --reference, change the group of each FILE to that of RFILE.\n\
\n\
"), stdout);
- fputs (_("\
- -c, --changes like verbose but report only when a change is made\n\
- -f, --silent, --quiet suppress most error messages\n\
- -v, --verbose output a diagnostic for every file processed\n\
-"), stdout);
- fputs (_("\
- --dereference affect the referent of each symbolic link (this is\n\
- the default), rather than the symbolic link itself\n\
- -h, --no-dereference affect symbolic links instead of any referenced file\n\
-"), stdout);
- fputs (_("\
- (useful only on systems that can change the\n\
- ownership of a symlink)\n\
-"), stdout);
- fputs (_("\
+ oputs (_("\
+ -c, --changes\n\
+ like verbose but report only when a change is made\n\
+"));
+ oputs (_("\
+ -f, --silent, --quiet\n\
+ suppress most error messages\n\
+"));
+ oputs (_("\
+ -v, --verbose\n\
+ output a diagnostic for every file processed\n\
+"));
+ oputs (_("\
+ --dereference\n\
+ affect the referent of each symbolic link (this is\n\
+ the default), rather than the symbolic link itself\n\
+"));
+ oputs (_("\
+ -h, --no-dereference\n\
+ affect symbolic links instead of any referenced file;\n\
+ useful only on systems that can change the ownership of a symlink\n\
+"));
+ oputs (_("\
--from=CURRENT_OWNER:CURRENT_GROUP\n\
- change the ownership of each file only if\n\
- its current owner and/or group match those specified\n\
- here. Either may be omitted, in which case a match\n\
- is not required for the omitted attribute\n\
-"), stdout);
- fputs (_("\
- --no-preserve-root do not treat '/' specially (the default)\n\
- --preserve-root fail to operate recursively on '/'\n\
-"), stdout);
- fputs (_("\
- --reference=RFILE use RFILE's ownership rather than specifying values.\n\
- RFILE is always dereferenced if a symbolic link.\n\
-"), stdout);
- fputs (_("\
- -R, --recursive operate on files and directories recursively\n\
-"), stdout);
+ change the ownership of each file only if its\n\
+ current owner and/or group match those specified here.\n\
+ Either may be omitted, in which case a match\n\
+ is not required for the omitted attribute\n\
+"));
+ oputs (_("\
+ --no-preserve-root\n\
+ do not treat '/' specially (the default)\n\
+"));
+ oputs (_("\
+ --preserve-root\n\
+ fail to operate recursively on '/'\n\
+"));
+ oputs (_("\
+ --reference=RFILE\n\
+ use RFILE's ownership rather than specifying values.\n\
+ RFILE is always dereferenced if a symbolic link.\n\
+"));
+ oputs (_("\
+ -R, --recursive\n\
+ operate on files and directories recursively\n\
+"));
emit_symlink_recurse_options ("-P");
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
if (chown_mode == CHOWN_CHOWN)
fputs (_("\
\n\
@@ -165,9 +179,6 @@ main (int argc, char **argv)
{
bool preserve_root = false;
- uid_t uid = -1; /* Specified uid; -1 if not to be changed. */
- gid_t gid = -1; /* Specified gid; -1 if not to be changed. */
-
/* Change the owner (group) of a file only if it has this uid (gid).
-1 means there's no restriction. */
uid_t required_uid = -1;
@@ -181,8 +192,6 @@ main (int argc, char **argv)
int dereference = -1;
struct Chown_option chopt;
- bool ok;
- int optc;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -194,7 +203,8 @@ main (int argc, char **argv)
chopt_init (&chopt);
- while ((optc = getopt_long (argc, argv, "HLPRcfhv", long_options, nullptr))
+ int optc;
+ while ((optc = getopt_long (argc, argv, "HLPRcfhv", long_options, NULL))
!= -1)
{
switch (optc)
@@ -237,7 +247,7 @@ main (int argc, char **argv)
bool warn;
char const *e = parse_user_spec_warn (optarg,
&required_uid, &required_gid,
- nullptr, nullptr, &warn);
+ NULL, NULL, &warn);
if (e)
error (warn ? 0 : EXIT_FAILURE, 0, "%s: %s", e, quote (optarg));
break;
@@ -291,6 +301,9 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
+ uid_t uid = -1; /* Specified uid; -1 if not to be changed. */
+ gid_t gid = -1; /* Specified gid; -1 if not to be changed. */
+
if (reference_file)
{
struct stat ref_stats;
@@ -339,15 +352,15 @@ main (int argc, char **argv)
{
static struct dev_ino dev_ino_buf;
chopt.root_dev_ino = get_root_dev_ino (&dev_ino_buf);
- if (chopt.root_dev_ino == nullptr)
+ if (chopt.root_dev_ino == NULL)
error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
quoteaf ("/"));
}
bit_flags |= FTS_DEFER_STAT;
- ok = chown_files (argv + optind, bit_flags,
- uid, gid,
- required_uid, required_gid, &chopt);
+ bool ok = chown_files (argv + optind, bit_flags,
+ uid, gid,
+ required_uid, required_gid, &chopt);
main_exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
}
diff --git a/src/chown.h b/src/chown.h
index 21f01c405..0f87a8832 100644
--- a/src/chown.h
+++ b/src/chown.h
@@ -1,7 +1,10 @@
-/* This is for the 'chown' program. */
-#define CHOWN_CHOWN 1
+enum chown_modes
+{
+ /* This is for the 'chown' program. */
+ CHOWN_CHOWN,
-/* This is for the 'chgrp' program. */
-#define CHOWN_CHGRP 2
+ /* This is for the 'chgrp' program. */
+ CHOWN_CHGRP,
+};
-extern int chown_mode;
+extern enum chown_modes chown_mode;
diff --git a/src/chroot.c b/src/chroot.c
index 27cb15e72..8ee41e4e9 100644
--- a/src/chroot.c
+++ b/src/chroot.c
@@ -1,5 +1,5 @@
/* chroot -- run command or shell with special root directory
- Copyright (C) 1995-2025 Free Software Foundation, Inc.
+ Copyright (C) 1995-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,7 +17,6 @@
/* Written by Roland McGrath. */
#include <config.h>
-#include <ctype.h>
#include <getopt.h>
#include <stdio.h>
#include <sys/types.h>
@@ -28,7 +27,6 @@
#include "ignore-value.h"
#include "mgetgroups.h"
#include "quote.h"
-#include "root-dev-ino.h"
#include "userspec.h"
#include "xstrtol.h"
@@ -55,12 +53,12 @@ enum
static struct option const long_opts[] =
{
- {"groups", required_argument, nullptr, GROUPS},
- {"userspec", required_argument, nullptr, USERSPEC},
- {"skip-chdir", no_argument, nullptr, SKIP_CHDIR},
+ {"groups", required_argument, NULL, GROUPS},
+ {"userspec", required_argument, NULL, USERSPEC},
+ {"skip-chdir", no_argument, NULL, SKIP_CHDIR},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
#if ! HAVE_SETGROUPS
@@ -95,19 +93,19 @@ static int
parse_additional_groups (char const *groups, GETGROUPS_T **pgids,
idx_t *pn_gids, bool show_errors)
{
- GETGROUPS_T *gids = nullptr;
+ GETGROUPS_T *gids = NULL;
idx_t n_gids_allocated = 0;
idx_t n_gids = 0;
char *buffer = xstrdup (groups);
- char const *tmp;
int ret = 0;
- for (tmp = strtok (buffer, ","); tmp; tmp = strtok (nullptr, ","))
+ for (char const *tmp = strtok (buffer, ","); tmp;
+ tmp = strtok (NULL, ","))
{
struct group *g;
uintmax_t value;
- if (xstrtoumax (tmp, nullptr, 10, &value, "") == LONGINT_OK
+ if (xstrtoumax (tmp, NULL, 10, &value, "") == LONGINT_OK
&& value <= MAXGID)
{
while (isspace (to_uchar (*tmp)))
@@ -116,20 +114,20 @@ parse_additional_groups (char const *groups, GETGROUPS_T **pgids,
{
/* Handle the case where the name is numeric. */
g = getgrnam (tmp);
- if (g != nullptr)
+ if (g != NULL)
value = g->gr_gid;
}
/* Flag that we've got a group from the number. */
- g = (struct group *) (intptr_t) ! nullptr;
+ g = (struct group *) (intptr_t) ! NULL;
}
else
{
g = getgrnam (tmp);
- if (g != nullptr)
+ if (g != NULL)
value = g->gr_gid;
}
- if (g == nullptr)
+ if (g == NULL)
{
ret = -1;
@@ -191,18 +189,21 @@ Run COMMAND with root directory set to NEWROOT.\n\
\n\
"), stdout);
- fputs (_("\
- --groups=G_LIST specify supplementary groups as g1,g2,..,gN\n\
-"), stdout);
- fputs (_("\
- --userspec=USER:GROUP specify user and group (ID or name) to use\n\
-"), stdout);
- printf (_("\
- --skip-chdir do not change working directory to %s\n\
+ oputs (_("\
+ --groups=G_LIST\n\
+ specify supplementary groups as g1,g2,..,gN\n\
+"));
+ oputs (_("\
+ --userspec=USER:GROUP\n\
+ specify user and group (ID or name) to use\n\
+"));
+ oprintf (_("\
+ --skip-chdir\n\
+ do not change working directory to %s\n\
"), quoteaf ("/"));
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
If no command is given, run '\"$SHELL\" -i' (default: '/bin/sh -i').\n\
@@ -219,15 +220,15 @@ main (int argc, char **argv)
int c;
/* Input user and groups spec. */
- char *userspec = nullptr;
- char const *username = nullptr;
- char const *groups = nullptr;
+ char *userspec = NULL;
+ char const *username = NULL;
+ char const *groups = NULL;
bool skip_chdir = false;
/* Parsed user and group IDs. */
uid_t uid = -1;
gid_t gid = -1;
- GETGROUPS_T *out_gids = nullptr;
+ GETGROUPS_T *out_gids = NULL;
idx_t n_gids = 0;
initialize_main (&argc, &argv);
@@ -239,7 +240,7 @@ main (int argc, char **argv)
initialize_exit_failure (EXIT_CANCELED);
atexit (close_stdout);
- while ((c = getopt_long (argc, argv, "+", long_opts, nullptr)) != -1)
+ while ((c = getopt_long (argc, argv, "+", long_opts, NULL)) != -1)
{
switch (c)
{
@@ -297,7 +298,7 @@ main (int argc, char **argv)
Within chroot lookup is the main justification for having
the --user option supported by the chroot command itself. */
if (userspec)
- ignore_value (parse_user_spec (userspec, &uid, &gid, nullptr, nullptr));
+ ignore_value (parse_user_spec (userspec, &uid, &gid, NULL, NULL));
/* If no gid is supplied or looked up, do so now.
Also lookup the username for use with getgroups. */
@@ -336,11 +337,11 @@ main (int argc, char **argv)
{
/* No command. Run an interactive shell. */
char *shell = getenv ("SHELL");
- if (shell == nullptr)
+ if (shell == NULL)
shell = bad_cast ("/bin/sh");
argv[0] = shell;
argv[1] = bad_cast ("-i");
- argv[2] = nullptr;
+ argv[2] = NULL;
}
else
{
@@ -354,7 +355,7 @@ main (int argc, char **argv)
{
bool warn;
char const *err = parse_user_spec_warn (userspec, &uid, &gid,
- nullptr, nullptr, &warn);
+ NULL, NULL, &warn);
if (err)
error (warn ? 0 : EXIT_CANCELED, 0, "%s", (err));
}
@@ -379,7 +380,7 @@ main (int argc, char **argv)
}
GETGROUPS_T *gids = out_gids;
- GETGROUPS_T *in_gids = nullptr;
+ GETGROUPS_T *in_gids = NULL;
if (groups && *groups)
{
if (parse_additional_groups (groups, &in_gids, &n_gids, !n_gids) != 0)
diff --git a/src/cksum.c b/src/cksum.c
index a74af07a0..186ba3185 100644
--- a/src/cksum.c
+++ b/src/cksum.c
@@ -1,5 +1,5 @@
-/* cksum -- calculate and print POSIX checksums and sizes of files
- Copyright (C) 1992-2025 Free Software Foundation, Inc.
+/* Compute checksums of files or strings.
+ Copyright (C) 1995-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -14,366 +14,1882 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
-/* Written by Q. Frank Xia, qx@math.columbia.edu.
- Cosmetic changes and reorganization by David MacKenzie, djm@gnu.ai.mit.edu.
+/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>. */
- Usage: cksum [file...]
+#include <config.h>
- The code segment between "#ifdef CRCTAB" and "#else" is the code
- which generates crctab.c
+#include <getopt.h>
+#include <sys/types.h>
- This software is compatible with neither the System V nor the BSD
- 'sum' program. It is supposed to conform to POSIX, except perhaps
- for foreign language support. Any inconsistency with the standard
- (other than foreign language support) is a bug. */
+#include "assure.h"
+#include "system.h"
+#include "argmatch.h"
+#include "c-ctype.h"
+#include "quote.h"
+#include "xdectoint.h"
+#include "xstrtol.h"
-/* This include must be at the top of the file to satisfy
- sc_require_config_h_first. */
-#ifndef CRCTAB
-# include <config.h>
+#ifndef HASH_ALGO_CKSUM
+# define HASH_ALGO_CKSUM 0
#endif
-#ifdef CRCTAB
+#if HASH_ALGO_SUM || HASH_ALGO_CKSUM
+# include "sum.h"
+#endif
+#if HASH_ALGO_CKSUM
+# include "cksum.h"
+# include "cksum_crc.h"
+# include "base64.h"
+#endif
+#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+# include "blake2/b2sum.h"
+#endif
+#if HASH_ALGO_MD5 || HASH_ALGO_CKSUM
+# include "md5.h"
+#endif
+#if HASH_ALGO_SHA1 || HASH_ALGO_CKSUM
+# include "sha1.h"
+#endif
+#if HASH_ALGO_SHA256 || HASH_ALGO_SHA224 || HASH_ALGO_CKSUM
+# include "sha256.h"
+#endif
+#if HASH_ALGO_SHA512 || HASH_ALGO_SHA384 || HASH_ALGO_CKSUM
+# include "sha512.h"
+#endif
+#if HASH_ALGO_CKSUM
+# include "sha3.h"
+#endif
+#if HASH_ALGO_CKSUM
+# include "sm3.h"
+#endif
+#include "fadvise.h"
+#include "stdio--.h"
+#include "xbinary-io.h"
+
+/* The official name of this program (e.g., no 'g' prefix). */
+#if HASH_ALGO_SUM
+# define PROGRAM_NAME "sum"
+# define DIGEST_TYPE_STRING "BSD"
+# define DIGEST_STREAM sumfns[sum_algorithm]
+# define DIGEST_OUT sum_output_fns[sum_algorithm]
+# define DIGEST_BITS 16
+# define DIGEST_ALIGN 4
+#elif HASH_ALGO_CKSUM
+# define MAX_DIGEST_BITS 512
+# define MAX_DIGEST_ALIGN 8
+# define DIGEST_TYPE_STRING algorithm_tags[cksum_algorithm]
+# define DIGEST_STREAM cksumfns[cksum_algorithm]
+# define DIGEST_OUT cksum_output_fns[cksum_algorithm]
+# define DIGEST_BITS MAX_DIGEST_BITS
+# define DIGEST_ALIGN MAX_DIGEST_ALIGN
+#elif HASH_ALGO_MD5
+# define PROGRAM_NAME "md5sum"
+# define DIGEST_TYPE_STRING "MD5"
+# define DIGEST_STREAM md5_stream
+# define DIGEST_BITS 128
+# define DIGEST_REFERENCE "RFC 1321"
+# define DIGEST_ALIGN 4
+#elif HASH_ALGO_BLAKE2
+# define PROGRAM_NAME "b2sum"
+# define DIGEST_TYPE_STRING "BLAKE2b"
+# define DIGEST_STREAM blake2b_stream
+# define DIGEST_BITS 512
+# define DIGEST_REFERENCE "RFC 7693"
+# define DIGEST_ALIGN 8
+#elif HASH_ALGO_SHA1
+# define PROGRAM_NAME "sha1sum"
+# define DIGEST_TYPE_STRING "SHA1"
+# define DIGEST_STREAM sha1_stream
+# define DIGEST_BITS 160
+# define DIGEST_REFERENCE "FIPS-180-1"
+# define DIGEST_ALIGN 4
+#elif HASH_ALGO_SHA256
+# define PROGRAM_NAME "sha256sum"
+# define DIGEST_TYPE_STRING "SHA256"
+# define DIGEST_STREAM sha256_stream
+# define DIGEST_BITS 256
+# define DIGEST_REFERENCE "FIPS-180-2"
+# define DIGEST_ALIGN 4
+#elif HASH_ALGO_SHA224
+# define PROGRAM_NAME "sha224sum"
+# define DIGEST_TYPE_STRING "SHA224"
+# define DIGEST_STREAM sha224_stream
+# define DIGEST_BITS 224
+# define DIGEST_REFERENCE "RFC 3874"
+# define DIGEST_ALIGN 4
+#elif HASH_ALGO_SHA512
+# define PROGRAM_NAME "sha512sum"
+# define DIGEST_TYPE_STRING "SHA512"
+# define DIGEST_STREAM sha512_stream
+# define DIGEST_BITS 512
+# define DIGEST_REFERENCE "FIPS-180-2"
+# define DIGEST_ALIGN 8
+#elif HASH_ALGO_SHA384
+# define PROGRAM_NAME "sha384sum"
+# define DIGEST_TYPE_STRING "SHA384"
+# define DIGEST_STREAM sha384_stream
+# define DIGEST_BITS 384
+# define DIGEST_REFERENCE "FIPS-180-2"
+# define DIGEST_ALIGN 8
+#else
+# error "Can't decide which hash algorithm to compile."
+#endif
+#if !HASH_ALGO_SUM && !HASH_ALGO_CKSUM
+# define DIGEST_OUT output_file
+#endif
-# include <stdio.h>
+#if HASH_ALGO_CKSUM
+# define PROGRAM_NAME (!legacy_mode ? "cksum" \
+ : cksum_algorithm == md5 ? "md5sum" \
+ : cksum_algorithm == sha1 ? "sha1sum" \
+ : cksum_algorithm == sha224 ? "sha224sum" \
+ : cksum_algorithm == sha256 ? "sha256sum" \
+ : cksum_algorithm == sha384 ? "sha384sum" \
+ : cksum_algorithm == sha512 ? "sha512sum" \
+ : "cksum")
+#endif
+
+#if HASH_ALGO_SUM
+# define AUTHORS \
+ proper_name ("Kayvan Aghaiepour"), \
+ proper_name ("David MacKenzie")
+#elif HASH_ALGO_CKSUM
+# define AUTHORS \
+ proper_name_lite ("Padraig Brady", "P\303\241draig Brady"), \
+ proper_name ("Q. Frank Xia")
+#elif HASH_ALGO_BLAKE2
+# define AUTHORS \
+ proper_name_lite ("Padraig Brady", "P\303\241draig Brady"), \
+ proper_name ("Samuel Neves")
+#else
+# define AUTHORS \
+ proper_name ("Ulrich Drepper"), \
+ proper_name ("Scott Miller"), \
+ proper_name ("David Madore")
+#endif
+#if !HASH_ALGO_BLAKE2 && !HASH_ALGO_CKSUM
+# define DIGEST_HEX_BYTES (DIGEST_BITS / 4)
+#endif
+#define DIGEST_BIN_BYTES (DIGEST_BITS / 8)
+
+/* The minimum length of a valid digest line. This length does
+ not include any newline character at the end of a line. */
+#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+# define MIN_DIGEST_LINE_LENGTH 3 /* With -l 8. */
+#else
+# define MIN_DIGEST_LINE_LENGTH \
+ (DIGEST_HEX_BYTES /* length of hexadecimal message digest */ \
+ + 1 /* blank */ \
+ + 1 /* minimum filename length */ )
+#endif
+
+#if !HASH_ALGO_SUM
+static void
+output_file (char const *file, int binary_file, void const *digest,
+ bool raw, bool tagged, unsigned char delim, bool args,
+ intmax_t length);
+#endif
-# define BIT(x) (1u << (x))
-# define SBIT BIT (31)
+/* True if any of the files read were the standard input. */
+static bool have_read_stdin;
-/* The generating polynomial is
+/* The minimum length of a valid checksum line for the selected algorithm. */
+static idx_t min_digest_line_length;
- 32 26 23 22 16 12 11 10 8 7 5 4 2 1
- G(X)=X + X + X + X + X + X + X + X + X + X + X + X + X + X + 1
+/* Set to the length of a digest hex string for the selected algorithm. */
+static idx_t digest_hex_bytes;
- The i bit in GEN is set if X^i is a summand of G(X) except X^32. */
+/* With --check, don't generate any output.
+ The exit code indicates success or failure. */
+static bool status_only = false;
-# define GEN (BIT (26) | BIT (23) | BIT (22) | BIT (16) | BIT (12) \
- | BIT (11) | BIT (10) | BIT (8) | BIT (7) | BIT (5) \
- | BIT (4) | BIT (2) | BIT (1) | BIT (0))
+/* With --check, print a message to standard error warning about each
+ improperly formatted checksum line. */
+static bool warn = false;
-static unsigned int r[8];
+/* With --check, ignore missing files. */
+static bool ignore_missing = false;
-static void
-fill_r (void)
+/* With --check, suppress the "OK" printed for each verified file. */
+static bool quiet = false;
+
+/* With --check, exit with a non-zero return code if any line is
+ improperly formatted. */
+static bool strict = false;
+
+/* Whether a BSD reversed format checksum is detected. */
+static int bsd_reversed = -1;
+
+/* line delimiter. */
+static unsigned char digest_delim = '\n';
+
+#if HASH_ALGO_CKSUM
+/* If true, print base64-encoded digests, not hex. */
+static bool base64_digest = false;
+#endif
+
+/* If true, print binary digests, not hex. */
+static bool raw_digest = false;
+
+/* blake2b and sha3 allow the -l option. Luckily they both have the same
+ maximum digest size. */
+#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+# if HASH_ALGO_CKSUM
+static_assert (BLAKE2B_OUTBYTES == SHA3_512_DIGEST_SIZE);
+# endif
+# define DIGEST_MAX_LEN BLAKE2B_OUTBYTES
+static idx_t digest_length;
+#endif /* HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM */
+
+typedef void (*digest_output_fn)(char const *, int, void const *, bool,
+ bool, unsigned char, bool, intmax_t);
+#if HASH_ALGO_SUM
+enum Algorithm
+{
+ bsd,
+ sysv,
+};
+
+static enum Algorithm sum_algorithm;
+static sumfn sumfns[]=
+{
+ bsd_sum_stream,
+ sysv_sum_stream,
+};
+static digest_output_fn sum_output_fns[]=
+{
+ output_bsd,
+ output_sysv,
+};
+#endif
+
+#if HASH_ALGO_CKSUM
+static int
+md5_sum_stream (FILE *stream, void *resstream, MAYBE_UNUSED intmax_t *length)
+{
+ return md5_stream (stream, resstream);
+}
+static int
+sha1_sum_stream (FILE *stream, void *resstream, MAYBE_UNUSED intmax_t *length)
+{
+ return sha1_stream (stream, resstream);
+}
+static int
+sha224_sum_stream (FILE *stream, void *resstream,
+ MAYBE_UNUSED intmax_t *length)
+{
+ return sha224_stream (stream, resstream);
+}
+static int
+sha256_sum_stream (FILE *stream, void *resstream,
+ MAYBE_UNUSED intmax_t *length)
+{
+ return sha256_stream (stream, resstream);
+}
+static int
+sha384_sum_stream (FILE *stream, void *resstream,
+ MAYBE_UNUSED intmax_t *length)
+{
+ return sha384_stream (stream, resstream);
+}
+static int
+sha512_sum_stream (FILE *stream, void *resstream,
+ MAYBE_UNUSED intmax_t *length)
{
- r[0] = GEN;
- for (int i = 1; i < 8; i++)
- r[i] = (r[i - 1] << 1) ^ ((r[i - 1] & SBIT) ? GEN : 0);
+ return sha512_stream (stream, resstream);
}
+static int
+sha2_sum_stream (FILE *stream, void *resstream, intmax_t *length)
+{
+ switch (*length)
+ {
+ case SHA224_DIGEST_SIZE:
+ return sha224_stream (stream, resstream);
+ case SHA256_DIGEST_SIZE:
+ return sha256_stream (stream, resstream);
+ case SHA384_DIGEST_SIZE:
+ return sha384_stream (stream, resstream);
+ case SHA512_DIGEST_SIZE:
+ return sha512_stream (stream, resstream);
+ default:
+ affirm (false);
+ }
+}
+static int
+sha3_sum_stream (FILE *stream, void *resstream, intmax_t *length)
+{
+ switch (*length)
+ {
+ case SHA3_224_DIGEST_SIZE:
+ return sha3_224_stream (stream, resstream);
+ case SHA3_256_DIGEST_SIZE:
+ return sha3_256_stream (stream, resstream);
+ case SHA3_384_DIGEST_SIZE:
+ return sha3_384_stream (stream, resstream);
+ case SHA3_512_DIGEST_SIZE:
+ return sha3_512_stream (stream, resstream);
+ default:
+ affirm (false);
+ }
+}
+static int
+blake2b_sum_stream (FILE *stream, void *resstream, intmax_t *length)
+{
+ return blake2b_stream (stream, resstream, *length);
+}
+static int
+sm3_sum_stream (FILE *stream, void *resstream, MAYBE_UNUSED intmax_t *length)
+{
+ return sm3_stream (stream, resstream);
+}
+
+static char const *const algorithm_args[] =
+{
+ "bsd", "sysv", "crc", "crc32b", "md5", "sha1",
+ "sha224", "sha256", "sha384", "sha512", /* Legacy naming */
+ "sha2", "sha3", "blake2b", "sm3", NULL
+};
+static enum Algorithm const algorithm_types[] =
+{
+ bsd, sysv, crc, crc32b, md5, sha1,
+ sha224, sha256, sha384, sha512,
+ sha2, sha3, blake2b, sm3,
+};
+ARGMATCH_VERIFY (algorithm_args, algorithm_types);
-static unsigned int
-crc_remainder (int m)
+static char const *const algorithm_tags[] =
{
- unsigned int rem = 0;
+ "BSD", "SYSV", "CRC", "CRC32B", "MD5", "SHA1",
+ "SHA224", "SHA256", "SHA384", "SHA512",
+ "SHA2", "SHA3", "BLAKE2b", "SM3", NULL
+};
+static int const algorithm_bits[] =
+{
+ 16, 16, 32, 32, 128, 160,
+ 224, 256, 384, 512,
+ 512, 512, 512, 256, 0
+};
- for (int i = 0; i < 8; i++)
- if (BIT (i) & m)
- rem ^= r[i];
+static_assert (countof (algorithm_bits) == countof (algorithm_args));
- return rem & 0xFFFFFFFF; /* Make it run on 64-bit machine. */
-}
+static bool algorithm_specified = false;
+static bool legacy_mode = false;
+enum Algorithm cksum_algorithm = crc;
-int
-main (void)
+static sumfn cksumfns[]=
{
- static unsigned int crctab[8][256];
+ bsd_sum_stream,
+ sysv_sum_stream,
+ crc_sum_stream,
+ crc32b_sum_stream,
+ md5_sum_stream,
+ sha1_sum_stream,
+ sha224_sum_stream,
+ sha256_sum_stream,
+ sha384_sum_stream,
+ sha512_sum_stream,
+ sha2_sum_stream,
+ sha3_sum_stream,
+ blake2b_sum_stream,
+ sm3_sum_stream,
+};
+static digest_output_fn cksum_output_fns[]=
+{
+ output_bsd,
+ output_sysv,
+ output_crc,
+ output_crc,
+ output_file,
+ output_file,
+ output_file,
+ output_file,
+ output_file,
+ output_file,
+ output_file,
+ output_file,
+ output_file,
+ output_file,
+};
+bool cksum_debug;
+#endif
+
+/* For long options that have no equivalent short option, use a
+ non-character as a pseudo short option, starting with CHAR_MAX + 1. */
- fill_r ();
+enum
+{
+ IGNORE_MISSING_OPTION = CHAR_MAX + 1,
+ STATUS_OPTION,
+ QUIET_OPTION,
+ STRICT_OPTION,
+ TAG_OPTION,
+ UNTAG_OPTION,
+ DEBUG_PROGRAM_OPTION,
+ RAW_OPTION,
+ BASE64_OPTION,
+};
+
+#if HASH_ALGO_CKSUM
+static struct option const legacy_long_options[] =
+{
+ { "check", no_argument, NULL, 'c' },
+ { "ignore-missing", no_argument, NULL, IGNORE_MISSING_OPTION },
+ { "quiet", no_argument, NULL, QUIET_OPTION },
+ { "status", no_argument, NULL, STATUS_OPTION },
+ { "warn", no_argument, NULL, 'w' },
+ { "strict", no_argument, NULL, STRICT_OPTION },
+ { "tag", no_argument, NULL, TAG_OPTION },
+ { "zero", no_argument, NULL, 'z' },
+
+ { "binary", no_argument, NULL, 'b' }, /* Deprecated. */
+ { "text", no_argument, NULL, 't' }, /* Deprecated. */
+
+ { GETOPT_HELP_OPTION_DECL },
+ { GETOPT_VERSION_OPTION_DECL },
+ { NULL, 0, NULL, 0 }
+};
+#endif
- for (int i = 0; i < 256; i++)
- crctab[0][i] = crc_remainder (i);
+static struct option const long_options[] =
+{
+#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+ { "length", required_argument, NULL, 'l'},
+#endif
- /* CRC(0x11 0x22 0x33 0x44)
- is equal to
- CRC(0x11 0x00 0x00 0x00) XOR CRC(0x22 0x00 0x00) XOR
- CRC(0x33 0x00) XOR CRC(0x44)
- We precompute the CRC values for the offset values into
- separate CRC tables. We can then use them to speed up
- CRC calculation by processing multiple bytes at the time. */
- for (int i = 0; i < 256; i++)
+#if !HASH_ALGO_SUM
+ { "check", no_argument, NULL, 'c' },
+ { "ignore-missing", no_argument, NULL, IGNORE_MISSING_OPTION},
+ { "quiet", no_argument, NULL, QUIET_OPTION },
+ { "status", no_argument, NULL, STATUS_OPTION },
+ { "warn", no_argument, NULL, 'w' },
+ { "strict", no_argument, NULL, STRICT_OPTION },
+ { "tag", no_argument, NULL, TAG_OPTION },
+ { "zero", no_argument, NULL, 'z' },
+
+# if HASH_ALGO_CKSUM
+ { "algorithm", required_argument, NULL, 'a'},
+ { "base64", no_argument, NULL, BASE64_OPTION },
+ { "debug", no_argument, NULL, DEBUG_PROGRAM_OPTION},
+ { "raw", no_argument, NULL, RAW_OPTION},
+ { "untagged", no_argument, NULL, UNTAG_OPTION },
+# endif
+ { "binary", no_argument, NULL, 'b' }, /* Deprecated. */
+ { "text", no_argument, NULL, 't' }, /* Deprecated. */
+
+#else
+ {"sysv", no_argument, NULL, 's'}, /* Not in cksum. */
+#endif
+
+ { GETOPT_HELP_OPTION_DECL },
+ { GETOPT_VERSION_OPTION_DECL },
+ { NULL, 0, NULL, 0 }
+};
+
+void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ emit_try_help ();
+ else
{
- unsigned int crc = 0;
+ printf (_("\
+Usage: %s [OPTION]... [FILE]...\n\
+"), program_name);
+#if HASH_ALGO_CKSUM
+ if (legacy_mode)
+ {
+ printf (_("\
+Print or check %s checksums.\n\
+"), DIGEST_TYPE_STRING);
+ fputs (_("\
+Legacy interface to the cksum(1) utility.\n\
+"), stdout);
+ }
+ else
+ fputs (_("\
+Print or verify checksums.\n\
+By default use the 32 bit CRC algorithm.\n\
+"), stdout);
+#else
+ printf (_("\
+Print or check %s (%d-bit) checksums.\n\
+"),
+ DIGEST_TYPE_STRING,
+ DIGEST_BITS);
+ fputs (_("\
+Legacy interface to the cksum(1) utility.\n\
+"), stdout);
+#endif
- crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ i) & 0xFF];
- for (int offset = 1; offset < 8; offset++)
+ emit_stdin_note ();
+#if HASH_ALGO_SUM
+ oputs (_("\
+ -r\n\
+ use BSD sum algorithm (the default), use 1K blocks\n\
+"));
+ oputs (_("\
+ -s, --sysv\n\
+ use System V sum algorithm, use 512 bytes blocks\n\
+"));
+#endif
+#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+ emit_mandatory_arg_note ();
+#endif
+#if HASH_ALGO_CKSUM
+ if (! legacy_mode)
+ oputs (_("\
+ -a, --algorithm=TYPE\n\
+ select the digest type to use. See DIGEST below\n\
+"));
+ if (! legacy_mode)
+ oputs (_("\
+ --base64\n\
+ emit base64-encoded digests, not hexadecimal\n\
+"));
+#endif
+#if !HASH_ALGO_SUM
+# if HASH_ALGO_CKSUM
+ if (legacy_mode)
+ {
+# endif
+ if (O_BINARY)
+ oputs (_("\
+ -b, --binary\n\
+ read in binary mode (default unless reading tty stdin)\n\
+"));
+ else
+ oputs (_("\
+ -b, --binary\n\
+ read in binary mode\n\
+"));
+# if HASH_ALGO_CKSUM
+ }
+# endif
+ oputs (_("\
+ -c, --check\n\
+ read checksums from the FILEs and check them\n\
+"));
+# if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+# if HASH_ALGO_CKSUM
+ if (! legacy_mode)
+# endif
+ oputs (_("\
+ -l, --length=BITS\n\
+ digest length in bits; must not exceed the max size\n\
+ and must be a multiple of 8 for blake2b;\n\
+ must be 224, 256, 384, or 512 for sha2 or sha3\n\
+"));
+# endif
+# if HASH_ALGO_CKSUM
+ if (! legacy_mode)
+ oputs (_("\
+ --raw\n\
+ emit a raw binary digest, not hexadecimal\n\
+"));
+ if (legacy_mode)
+ oputs (_("\
+ --tag\n\
+ create a BSD-style checksum\n\
+"));
+ else
+ oputs (_("\
+ --tag\n\
+ create a BSD-style checksum (the default)\n\
+"));
+ if (! legacy_mode)
+ oputs (_("\
+ --untagged\n\
+ create a reversed style checksum, without digest type\n\
+"));
+# else
+ oputs (_("\
+ --tag\n\
+ create a BSD-style checksum\n\
+"));
+# endif
+# if HASH_ALGO_CKSUM
+ if (legacy_mode)
+ {
+# endif
+ if (O_BINARY)
+ oputs (_("\
+ -t, --text\n\
+ read in text mode (default if reading tty stdin)\n\
+"));
+ else
+ oputs (_("\
+ -t, --text\n\
+ read in text mode (default)\n\
+"));
+# if HASH_ALGO_CKSUM
+ }
+# endif
+ oputs (_("\
+ -z, --zero\n\
+ end each output line with NUL, not newline,\n\
+ and disable file name escaping\n\
+"));
+ fputs (_("\
+\n\
+The following five options are useful only when verifying checksums:\n\
+"), stdout);
+ oputs (_("\
+ --ignore-missing\n\
+ don't fail or report status for missing files\n\
+"));
+ oputs (_("\
+ --quiet\n\
+ don't print OK for each successfully verified file\n\
+"));
+ oputs (_("\
+ --status\n\
+ don't output anything, status code shows success\n\
+"));
+ oputs (_("\
+ --strict\n\
+ exit non-zero for improperly formatted checksum lines\n\
+"));
+ oputs (_("\
+ -w, --warn\n\
+ warn about improperly formatted checksum lines\n\
+"));
+#endif
+#if HASH_ALGO_CKSUM
+ if (! legacy_mode)
+ oputs (_("\
+ --debug\n\
+ indicate which implementation used\n\
+"));
+#endif
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
+#if HASH_ALGO_CKSUM
+ if (! legacy_mode)
+ fputs (_("\
+\n\
+DIGEST determines the digest algorithm and default output format:\n\
+ sysv (equivalent to sum -s)\n\
+ bsd (equivalent to sum -r)\n\
+ crc (equivalent to cksum)\n\
+ crc32b (only available through cksum)\n\
+ md5 (equivalent to md5sum)\n\
+ sha1 (equivalent to sha1sum)\n\
+ sha2 (equivalent to sha{224,256,384,512}sum)\n\
+ sha3 (only available through cksum)\n\
+ blake2b (equivalent to b2sum)\n\
+ sm3 (only available through cksum)\n\
+\n"), stdout);
+#endif
+#if !HASH_ALGO_SUM && !HASH_ALGO_CKSUM
+ printf (_("\
+\n\
+The sums are computed as described in %s.\n"), DIGEST_REFERENCE);
+ fputs (_("\
+When checking, the input should be a former output of this program.\n\
+The default mode is to print a line with: checksum, a space,\n\
+a character indicating input mode ('*' for binary, ' ' for text\n\
+or where binary is insignificant), and name for each FILE.\n\
+\n\
+There is no difference between binary mode and text mode on GNU systems.\
+\n"), stdout);
+#endif
+#if HASH_ALGO_CKSUM
+ fputs (_("\
+When checking, the input should be a former output of this program,\n\
+or equivalent standalone program.\
+\n"), stdout);
+#endif
+ emit_ancillary_info (PROGRAM_NAME);
+ }
+
+ exit (status);
+}
+
+#if !HASH_ALGO_SUM
+/* Given a string S, return TRUE if it contains problematic characters
+ that need escaping. Note we escape '\' itself to provide some forward
+ compat to introduce escaping of other characters. */
+
+ATTRIBUTE_PURE
+static bool
+problematic_chars (char const *s)
+{
+ idx_t length = strcspn (s, "\\\n\r");
+ return s[length] != '\0';
+}
+#endif
+
+/* Given a file name, S of length S_LEN, that is not NUL-terminated,
+ modify it in place, performing the equivalent of this sed substitution:
+ 's/\\n/\n/g;s/\\r/\r/g;s/\\\\/\\/g' i.e., replacing each "\\n" string
+ with a newline, each "\\r" string with a carriage return,
+ and each "\\\\" with a single backslash, NUL-terminate it and return S.
+ If S is not a valid escaped file name, i.e., if it ends with an odd number
+ of backslashes or if it contains a backslash followed by anything other
+ than "n" or another backslash, return NULL. */
+
+static char *
+filename_unescape (char *s, idx_t s_len)
+{
+ char *dst = s;
+
+ for (idx_t i = 0; i < s_len; i++)
+ {
+ switch (s[i])
{
- crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ 0) & 0xFF];
- crctab[offset][i] = crc & 0xFFFFFFFF;
+ case '\\':
+ if (i == s_len - 1)
+ {
+ /* File name ends with an unescaped backslash: invalid. */
+ return NULL;
+ }
+ ++i;
+ switch (s[i])
+ {
+ case 'n':
+ *dst++ = '\n';
+ break;
+ case 'r':
+ *dst++ = '\r';
+ break;
+ case '\\':
+ *dst++ = '\\';
+ break;
+ default:
+ /* Only '\', 'n' or 'r' may follow a backslash. */
+ return NULL;
+ }
+ break;
+
+ case '\0':
+ /* The file name may not contain a NUL. */
+ return NULL;
+
+ default:
+ *dst++ = s[i];
+ break;
}
}
+ if (dst < s + s_len)
+ *dst = '\0';
- printf ("#include <config.h>\n");
- printf ("#include \"cksum.h\"\n");
- printf ("\n");
- printf ("uint_fast32_t const crctab[8][256] = {\n");
- for (int y = 0; y < 8; y++)
+ return s;
+}
+
+/* Return true if S is a LEN-byte NUL-terminated string of hex or base64
+ digits and has the expected length. Otherwise, return false. */
+ATTRIBUTE_PURE
+static bool
+valid_digits (unsigned char const *s, idx_t len)
+{
+#if HASH_ALGO_CKSUM
+ if (len == BASE64_LENGTH (digest_length >> 3))
+ {
+ idx_t i;
+ for (i = 0; i < len - digest_length % 3; i++)
+ {
+ if (!isbase64 (*s))
+ return false;
+ ++s;
+ }
+ for ( ; i < len; i++)
+ {
+ if (*s != '=')
+ return false;
+ ++s;
+ }
+ }
+ else
+#endif
+ if (len == digest_hex_bytes)
{
- printf ("{\n 0x%08x", crctab[y][0]);
- for (int i = 0; i < 51; i++)
+ for (idx_t i = 0; i < digest_hex_bytes; i++)
{
- printf (",\n 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x",
- crctab[y][i * 5 + 1], crctab[y][i * 5 + 2],
- crctab[y][i * 5 + 3], crctab[y][i * 5 + 4],
- crctab[y][i * 5 + 5]);
+ if (!c_isxdigit (*s))
+ return false;
+ ++s;
}
- printf ("\n},\n");
}
- printf ("};\n");
+ else
+ return false;
+
+ return *s == '\0';
}
-#else /* !CRCTAB */
+/* Split the checksum string S (of length S_LEN) from a BSD 'md5' or
+ 'sha1' command into two parts: a hexadecimal digest, and the file
+ name. S is modified. Set *D_LEN to the length of the digest string.
+ Return true if successful. */
-# include "cksum.h"
-# include <sys/types.h>
-# include <endian.h>
-# include "system.h"
+static bool
+bsd_split_3 (char *s, idx_t s_len,
+ unsigned char **digest, idx_t *d_len,
+ char **file_name, bool escaped_filename)
+{
+ if (s_len == 0)
+ return false;
-# ifdef USE_VMULL_CRC32
-# include <sys/auxv.h>
-# include <asm/hwcap.h>
-# endif
+ /* Find end of filename. */
+ idx_t i = s_len - 1;
+ while (i && s[i] != ')')
+ i--;
-# include "crc.h"
-# include "cpu-supports.h"
+ if (s[i] != ')')
+ return false;
+
+ *file_name = s;
+
+ if (escaped_filename && filename_unescape (s, i) == NULL)
+ return false;
+
+ s[i++] = '\0';
-/* Number of bytes to read at once. */
-# define BUFLEN (1 << 16)
+ while (c_isblank (s[i]))
+ i++;
-typedef bool (*cksum_fp_t) (FILE *, uint_fast32_t *, uintmax_t *);
+ if (s[i] != '=')
+ return false;
-static cksum_fp_t
-pclmul_supported (void)
+ i++;
+
+ while (c_isblank (s[i]))
+ i++;
+
+ *digest = (unsigned char *) &s[i];
+
+ *d_len = s_len - i;
+ return valid_digits (*digest, *d_len);
+}
+
+#if HASH_ALGO_CKSUM
+/* Return the corresponding Algorithm for the string S,
+ or -1 for no match. */
+
+static ptrdiff_t
+algorithm_from_tag (char *s)
{
-# if USE_PCLMUL_CRC32 || GL_CRC_X86_64_PCLMUL
- bool pclmul_enabled = (cpu_supports ("avx")
- && cpu_supports ("pclmul"));
- if (cksum_debug)
- error (0, 0, "%s",
- (pclmul_enabled
- ? _("using pclmul hardware support")
- : _("pclmul support not detected")));
-# if USE_PCLMUL_CRC32
- if (pclmul_enabled)
- return cksum_pclmul;
-# endif
-# endif
+ /* Limit check size to this length for perf reasons. */
+ static idx_t max_tag_len;
+ if (! max_tag_len)
+ {
+ char const * const * tag = algorithm_tags;
+ while (*tag)
+ {
+ idx_t tag_len = strlen (*tag++);
+ max_tag_len = MAX (tag_len, max_tag_len);
+ }
+ }
+
+ idx_t i = 0;
+
+ /* Find end of tag */
+ while (i <= max_tag_len && s[i] && ! c_isblank (s[i])
+ && s[i] != '-' && s[i] != '(')
+ ++i;
+
+ if (i > max_tag_len)
+ return -1;
+
+ /* Terminate tag, and lookup. */
+ char sep = s[i];
+ s[i] = '\0';
+ ptrdiff_t algo = argmatch_exact (s, algorithm_tags);
+ s[i] = sep;
- return nullptr;
+ return algo;
}
+#endif
+
+/* Split the string S (of length S_LEN) into three parts:
+ a hexadecimal digest, binary flag, and the file name.
+ S is modified. Set *D_LEN to the length of the digest string.
+ Return true if successful. */
+
+static bool
+split_3 (char *s, idx_t s_len,
+ unsigned char **digest, idx_t *d_len, int *binary, char **file_name)
+{
+ bool escaped_filename = false;
+ idx_t algo_name_len;
+
+ idx_t i = 0;
+ while (c_isblank (s[i]))
+ ++i;
+
+ if (s[i] == '\\')
+ {
+ ++i;
+ escaped_filename = true;
+ }
+
+ /* Check for BSD-style checksum line. */
+
+#if HASH_ALGO_CKSUM
+ if (! algorithm_specified || cksum_algorithm == sha2)
+ {
+ ptrdiff_t algo_tag = algorithm_from_tag (s + i);
+ if (! algorithm_specified)
+ {
+ if (algo_tag >= 0)
+ {
+ if (algo_tag <= crc32b)
+ return false; /* We don't support checking these formats. */
+ cksum_algorithm = algo_tag;
+ }
+ else
+ return false; /* We only support tagged format without -a. */
+ }
+ else
+ {
+ if (cksum_algorithm == sha2 && (algo_tag == sha2
+ || algo_tag == sha224 || algo_tag == sha256
+ || algo_tag == sha384 || algo_tag == sha512))
+ cksum_algorithm = algo_tag;
+ }
+ }
+#endif
-static cksum_fp_t
-avx2_supported (void)
-{
- /* AVX512 processors will not set vpclmulqdq unless they support
- the avx512 version, but it implies that the avx2 version
- is supported */
-# if USE_AVX2_CRC32
- bool avx2_enabled = (cpu_supports ("avx2")
- && cpu_supports ("vpclmulqdq"));
- if (cksum_debug)
- error (0, 0, "%s",
- (avx2_enabled
- ? _("using avx2 hardware support")
- : _("avx2 support not detected")));
- if (avx2_enabled)
- return cksum_avx2;
+ /* Try to parse BSD or OpenSSL tagged format. I.e.:
+ openssl: MD5(f)= d41d8cd98f00b204e9800998ecf8427e
+ bsd: MD5 (f) = d41d8cd98f00b204e9800998ecf8427e */
+
+ idx_t parse_offset = i;
+ algo_name_len = strlen (DIGEST_TYPE_STRING);
+ if (STREQ_LEN (s + i, DIGEST_TYPE_STRING, algo_name_len))
+ {
+ i += algo_name_len;
+#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+
+# if HASH_ALGO_BLAKE2
+ digest_length = DIGEST_MAX_LEN * 8;
+# else
+ digest_length = algorithm_bits[cksum_algorithm];
# endif
+ if (s[i] == '-') /* length specified. Not base64 */
+ {
+ ++i;
+ intmax_t length;
+ char *siend;
+ if (!c_isdigit (s[i])
+ || xstrtoimax (s + i, &siend, 0, &length, NULL) != LONGINT_OK)
+ return false;
+# if HASH_ALGO_CKSUM
+ else if (cksum_algorithm == sha2 || cksum_algorithm == sha3)
+ {
+ if (length != SHA224_DIGEST_SIZE * 8
+ && length != SHA256_DIGEST_SIZE * 8
+ && length != SHA384_DIGEST_SIZE * 8
+ && length != SHA512_DIGEST_SIZE * 8)
+ return false;
+ }
+# endif
+ else if (!(0 < length && length <= digest_length && length % 8 == 0))
+ return false;
- return nullptr;
-}
+ i = siend - s;
+ digest_length = length;
+ }
+ digest_hex_bytes = digest_length >> 2;
+#endif
+ if (s[i] == ' ')
+ ++i;
+ if (s[i] == '(') /* not base64 */
+ {
+ ++i;
+ *binary = 0;
+ return bsd_split_3 (s + i, s_len - i,
+ digest, d_len, file_name, escaped_filename);
+ }
+
+ /* Note with --base64 --untagged format, we may have matched a "tag".
+ Even very short digests with: cksum -a blake2b -l24 --untagged --base64
+ So fallback to checking untagged format if issues detecting tags. */
+ i = parse_offset;
+ }
-static cksum_fp_t
-avx512_supported (void)
-{
- /* vpclmulqdq for multiplication
- mavx512f for most of the avx512 functions we're using
- mavx512bw for byte swapping */
-# if USE_AVX512_CRC32
- bool avx512_enabled = (cpu_supports ("avx512f")
- && cpu_supports ("avx512bw")
- && cpu_supports ("vpclmulqdq"));
-
- if (cksum_debug)
- error (0, 0, "%s",
- (avx512_enabled
- ? _("using avx512 hardware support")
- : _("avx512 support not detected")));
- if (avx512_enabled)
- return cksum_avx512;
+ /* Ignore this line if it is too short.
+ Each line must have at least 'min_digest_line_length - 1' (or one more, if
+ the first is a backslash) more characters to contain correct message digest
+ information. */
+ if (s_len - i < min_digest_line_length + (s[i] == '\\'))
+ return false;
+
+ *digest = (unsigned char *) &s[i];
+
+#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+ /* Auto determine length. */
+# if HASH_ALGO_CKSUM
+ if (cksum_algorithm == blake2b
+ || cksum_algorithm == sha2 || cksum_algorithm == sha3) {
# endif
+ unsigned char const *hp = *digest;
+ digest_hex_bytes = 0;
+ for (; c_isxdigit (*hp); ++hp, ++digest_hex_bytes)
+ ;
+# if HASH_ALGO_CKSUM
+ /* Check the number of base64 characters. This works because the hexadecimal
+ character set is a subset of the base64 character set.
+ Note there is the ambiguity that all characters are hex when they
+ are actually base64 encoded, which could be ambiguous with:
+ cksum -a sha2 -l 384 --base64 --untagged
+ cksum -a sha2 -l 256 --untagged
+ Similarly for sha3 and blake2b.
+ However at this length the chances are exceedingly rare (1 in 480R),
+ and smaller blake2b lengths aren't practical for verification anyway. */
+ idx_t digest_base64_bytes = digest_hex_bytes;
+ idx_t trailing_equals = 0;
+ for (; isubase64 (*hp); ++hp, ++digest_base64_bytes)
+ ;
+ for (; *hp == '='; ++hp, ++trailing_equals)
+ ;
+ if ((cksum_algorithm == sha2 || cksum_algorithm == sha3)
+ && digest_hex_bytes >> 1 != SHA224_DIGEST_SIZE
+ && digest_hex_bytes >> 1 != SHA256_DIGEST_SIZE
+ && digest_hex_bytes >> 1 != SHA384_DIGEST_SIZE
+ && digest_hex_bytes >> 1 != SHA512_DIGEST_SIZE)
+ {
+ if (digest_base64_bytes + trailing_equals
+ == BASE64_LENGTH (SHA224_DIGEST_SIZE))
+ digest_hex_bytes = SHA224_DIGEST_SIZE * 2;
+ else if (digest_base64_bytes + trailing_equals
+ == BASE64_LENGTH (SHA256_DIGEST_SIZE))
+ digest_hex_bytes = SHA256_DIGEST_SIZE * 2;
+ else if (digest_base64_bytes + trailing_equals
+ == BASE64_LENGTH (SHA384_DIGEST_SIZE))
+ digest_hex_bytes = SHA384_DIGEST_SIZE * 2;
+ else if (digest_base64_bytes + trailing_equals
+ == BASE64_LENGTH (SHA512_DIGEST_SIZE))
+ digest_hex_bytes = SHA512_DIGEST_SIZE * 2;
+ else
+ return false;
+ }
+ else if (cksum_algorithm == blake2b
+ && digest_hex_bytes < digest_base64_bytes)
+ {
+ for (int j = 8; j <= DIGEST_MAX_LEN * 8; j += 8)
+ {
+ if (BASE64_LENGTH (j / 8) == digest_base64_bytes + trailing_equals
+ && j % 3 == trailing_equals)
+ {
+ digest_hex_bytes = j / 4;
+ break;
+ }
+ }
+ }
+# endif
+ if (digest_hex_bytes < 2 || digest_hex_bytes % 2
+ || DIGEST_MAX_LEN * 2 < digest_hex_bytes)
+ return false;
+ digest_length = digest_hex_bytes * 4;
+# if HASH_ALGO_CKSUM
+ }
+# endif
+#endif
+
+ /* This field must be the hexadecimal or base64 representation
+ of the message digest. */
+ while (s[i] && !c_isblank (s[i]))
+ i++;
+
+ /* The digest must be followed by at least one whitespace character. */
+ if (i == s_len)
+ return false;
+
+ *d_len = &s[i] - (char *) *digest;
+ s[i++] = '\0';
+
+ if (! valid_digits (*digest, *d_len))
+ return false;
+
+ /* If "bsd reversed" format detected. */
+ if ((s_len - i == 1) || (s[i] != ' ' && s[i] != '*'))
+ {
+ /* Don't allow mixing bsd and standard formats,
+ to minimize security issues with attackers
+ renaming files with leading spaces.
+ This assumes that with bsd format checksums
+ that the first file name does not have
+ a leading ' ' or '*'. */
+ if (bsd_reversed == 0)
+ return false;
+ bsd_reversed = 1;
+ }
+ else if (bsd_reversed != 1)
+ {
+ bsd_reversed = 0;
+ *binary = (s[i++] == '*');
+ }
+
+ /* All characters between the type indicator and end of line are
+ significant -- that includes leading and trailing white space. */
+ *file_name = &s[i];
+
+ if (escaped_filename)
+ return filename_unescape (&s[i], s_len - i) != NULL;
- return nullptr;
+ return true;
}
-static cksum_fp_t
-vmull_supported (void)
-{
- /* vmull for multiplication */
-# if USE_VMULL_CRC32
- bool vmull_enabled = (cpu_may_support ("pmull")
- && (getauxval (AT_HWCAP) & HWCAP_PMULL) > 0);
- if (cksum_debug)
- error (0, 0, "%s",
- (vmull_enabled
- ? _("using vmull hardware support")
- : _("vmull support not detected")));
- if (vmull_enabled)
- return cksum_vmull;
-# endif
+#if !HASH_ALGO_SUM
+/* If ESCAPE is true, then translate each:
+ NEWLINE byte to the string, "\\n",
+ CARRIAGE RETURN byte to the string, "\\r",
+ and each backslash to "\\\\". */
+static void
+print_filename (char const *file, bool escape)
+{
+ if (! escape)
+ {
+ fputs (file, stdout);
+ return;
+ }
+
+ while (*file)
+ {
+ switch (*file)
+ {
+ case '\n':
+ fputs ("\\n", stdout);
+ break;
- return nullptr;
+ case '\r':
+ fputs ("\\r", stdout);
+ break;
+
+ case '\\':
+ fputs ("\\\\", stdout);
+ break;
+
+ default:
+ putchar (*file);
+ break;
+ }
+ file++;
+ }
}
+#endif
+
+/* An interface to the function, DIGEST_STREAM.
+ Operate on FILENAME (it may be "-").
+
+ *BINARY indicates whether the file is binary. BINARY < 0 means it
+ depends on whether binary mode makes any difference and the file is
+ a terminal; in that case, clear *BINARY if the file was treated as
+ text because it was a terminal.
+
+ Put the checksum in *BIN_RESULT, which must be properly aligned.
+ Put true in *MISSING if the file can't be opened due to ENOENT.
+ Return true if successful. */
static bool
-cksum_slice8 (FILE *fp, uint_fast32_t *crc_out, uintmax_t *length_out)
+digest_file (char const *filename, int *binary, unsigned char *bin_result,
+ bool *missing, MAYBE_UNUSED intmax_t *length)
{
- uint32_t buf[BUFLEN / sizeof (uint32_t)];
- uint_fast32_t crc = 0;
- uintmax_t length = 0;
- size_t bytes_read;
+ FILE *fp;
+ int err;
+ bool is_stdin = streq (filename, "-");
- if (!fp || !crc_out || !length_out)
- return false;
+ *missing = false;
- while ((bytes_read = fread (buf, 1, BUFLEN, fp)) > 0)
+ if (is_stdin)
{
- uint32_t *datap;
-
- if (ckd_add (&length, length, bytes_read))
+ have_read_stdin = true;
+ fp = stdin;
+ if (O_BINARY && *binary)
{
- errno = EOVERFLOW;
+ if (*binary < 0)
+ *binary = ! isatty (STDIN_FILENO);
+ if (*binary)
+ xset_binary_mode (STDIN_FILENO, O_BINARY);
+ }
+ }
+ else
+ {
+ fp = fopen (filename, O_BINARY ? (*binary ? "rb" : "rt") : "r");
+ if (fp == NULL)
+ {
+ if (ignore_missing && errno == ENOENT)
+ {
+ *missing = true;
+ return true;
+ }
+ error (0, errno, "%s", quotef (filename));
return false;
}
+ }
+
+ fadvise (fp, FADVISE_SEQUENTIAL);
+
+#if HASH_ALGO_CKSUM
+ if (cksum_algorithm == blake2b
+ || cksum_algorithm == sha2 || cksum_algorithm == sha3)
+ *length = digest_length >> 3;
+ err = DIGEST_STREAM (fp, bin_result, length);
+#elif HASH_ALGO_SUM
+ err = DIGEST_STREAM (fp, bin_result, length);
+#elif HASH_ALGO_BLAKE2
+ err = DIGEST_STREAM (fp, bin_result, digest_length >> 3);
+#else
+ err = DIGEST_STREAM (fp, bin_result);
+#endif
+ err = err ? errno : 0;
+ if (is_stdin)
+ clearerr (fp);
+ else if (fclose (fp) != 0 && !err)
+ err = errno;
+
+ if (err)
+ {
+ error (0, err, "%s", quotef (filename));
+ return false;
+ }
+
+ return true;
+}
+
+#if !HASH_ALGO_SUM
+static void
+output_file (char const *file, int binary_file, void const *digest,
+ MAYBE_UNUSED bool raw, bool tagged, unsigned char delim,
+ MAYBE_UNUSED bool args, MAYBE_UNUSED intmax_t length)
+{
+# if HASH_ALGO_CKSUM
+ if (raw)
+ {
+ fwrite (digest, 1, digest_length >> 3, stdout);
+ return;
+ }
+# endif
+
+ unsigned char const *bin_buffer = digest;
+
+ /* Output a leading backslash if the file name contains problematic chars. */
+ bool needs_escape = delim == '\n' && problematic_chars (file);
- /* Process multiples of 8 bytes */
- datap = (uint32_t *)buf;
- while (bytes_read >= 8)
+ if (needs_escape)
+ putchar ('\\');
+
+ if (tagged)
+ {
+# if HASH_ALGO_CKSUM
+ if (cksum_algorithm == sha2)
+ printf ("SHA%u", (unsigned int) digest_length);
+ else
+# endif
+ fputs (DIGEST_TYPE_STRING, stdout);
+# if HASH_ALGO_BLAKE2
+ if (digest_length < DIGEST_MAX_LEN * 8)
+ printf ("-%u", (unsigned int) digest_length);
+# elif HASH_ALGO_CKSUM
+ if (cksum_algorithm == sha3)
+ printf ("-%u", (unsigned int) digest_length);
+ if (cksum_algorithm == blake2b)
{
- uint32_t first = *datap++, second = *datap++;
- crc ^= htobe32 (first);
- second = htobe32 (second);
- crc = (crctab[7][(crc >> 24) & 0xFF]
- ^ crctab[6][(crc >> 16) & 0xFF]
- ^ crctab[5][(crc >> 8) & 0xFF]
- ^ crctab[4][(crc) & 0xFF]
- ^ crctab[3][(second >> 24) & 0xFF]
- ^ crctab[2][(second >> 16) & 0xFF]
- ^ crctab[1][(second >> 8) & 0xFF]
- ^ crctab[0][(second) & 0xFF]);
- bytes_read -= 8;
+ if (digest_length < DIGEST_MAX_LEN * 8)
+ printf ("-%u", (unsigned int) digest_length);
}
+# endif
+ fputs (" (", stdout);
+ print_filename (file, needs_escape);
+ fputs (") = ", stdout);
+ }
- /* And finish up last 0-7 bytes in a byte by byte fashion */
- unsigned char *cp = (unsigned char *)datap;
- while (bytes_read--)
- crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ *cp++) & 0xFF];
- if (feof (fp))
- break;
+# if HASH_ALGO_CKSUM
+ if (base64_digest)
+ {
+ char b64[BASE64_LENGTH (DIGEST_BIN_BYTES) + 1];
+ base64_encode ((char const *) bin_buffer, digest_length >> 3,
+ b64, sizeof b64);
+ fputs (b64, stdout);
+ }
+ else
+# endif
+ {
+ for (idx_t i = 0; i < (digest_hex_bytes >> 1); ++i)
+ printf ("%02x", bin_buffer[i]);
}
- *crc_out = crc;
- *length_out = length;
+ if (!tagged)
+ {
+ putchar (' ');
+ putchar (binary_file ? '*' : ' ');
+ print_filename (file, needs_escape);
+ }
- return !ferror (fp);
+ putchar (delim);
}
+#endif
-/* Calculate the checksum and length in bytes of stream STREAM.
- Return -1 on error, 0 on success. */
+#if HASH_ALGO_CKSUM
+/* Return true if B64_DIGEST is the same as the base64 digest of the
+ DIGEST_LENGTH/8 bytes at BIN_BUFFER. */
+static bool
+b64_equal (unsigned char const *b64_digest, unsigned char const *bin_buffer)
+{
+ idx_t b64_n_bytes = BASE64_LENGTH (digest_length >> 3);
+ char b64[BASE64_LENGTH (DIGEST_BIN_BYTES) + 1];
+ base64_encode ((char const *) bin_buffer, digest_length >> 3,
+ b64, sizeof b64);
+ return memeq (b64_digest, b64, b64_n_bytes + 1);
+}
+#endif
-int
-crc_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
-{
- uintmax_t total_bytes = 0;
- uint_fast32_t crc = 0;
-
- static cksum_fp_t cksum_fp;
- if (! cksum_fp)
- cksum_fp = avx512_supported ();
- if (! cksum_fp)
- cksum_fp = avx2_supported ();
- if (! cksum_fp)
- cksum_fp = pclmul_supported ();
- if (! cksum_fp)
- cksum_fp = vmull_supported ();
- if (! cksum_fp)
- cksum_fp = cksum_slice8;
-
- if (! cksum_fp (stream, &crc, &total_bytes))
- return -1;
+/* Return true if HEX_DIGEST is the same as the hex-encoded digest of the
+ DIGEST_LENGTH/8 bytes at BIN_BUFFER. */
+static bool
+hex_equal (unsigned char const *hex_digest, unsigned char const *bin_buffer)
+{
+ static const char bin2hex[] = { '0', '1', '2', '3',
+ '4', '5', '6', '7',
+ '8', '9', 'a', 'b',
+ 'c', 'd', 'e', 'f' };
+ idx_t digest_bin_bytes = digest_hex_bytes >> 1;
+
+ /* Compare generated binary number with text representation
+ in check file. Ignore case of hex digits. */
+ idx_t cnt;
+ for (cnt = 0; cnt < digest_bin_bytes; ++cnt)
+ {
+ if (c_tolower (hex_digest[2 * cnt])
+ != bin2hex[bin_buffer[cnt] >> 4]
+ || (c_tolower (hex_digest[2 * cnt + 1])
+ != (bin2hex[bin_buffer[cnt] & 0xf])))
+ break;
+ }
+ return cnt == digest_bin_bytes;
+}
- *length = total_bytes;
+static bool
+digest_check (char const *checkfile_name)
+{
+ FILE *checkfile_stream;
+ intmax_t n_misformatted_lines = 0;
+ intmax_t n_mismatched_checksums = 0;
+ intmax_t n_open_or_read_failures = 0;
+ bool properly_formatted_lines = false;
+ bool matched_checksums = false;
+ unsigned char bin_buffer_unaligned[DIGEST_BIN_BYTES + DIGEST_ALIGN];
+ /* Make sure bin_buffer is properly aligned. */
+ unsigned char *bin_buffer = ptr_align (bin_buffer_unaligned, DIGEST_ALIGN);
+ intmax_t line_number;
+ char *line;
+ size_t line_chars_allocated;
+ bool is_stdin = streq (checkfile_name, "-");
+
+ if (is_stdin)
+ {
+ have_read_stdin = true;
+ checkfile_name = _("standard input");
+ checkfile_stream = stdin;
+ }
+ else
+ {
+ checkfile_stream = fopen (checkfile_name, "r");
+ if (checkfile_stream == NULL)
+ {
+ error (0, errno, "%s", quotef (checkfile_name));
+ return false;
+ }
+ }
- for (; total_bytes; total_bytes >>= 8)
- crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ total_bytes) & 0xFF];
- crc = ~crc & 0xFFFFFFFF;
+ line_number = 0;
+ line = NULL;
+ line_chars_allocated = 0;
+ do
+ {
+ char *filename;
+ int binary;
+ unsigned char *digest;
+ ssize_t line_length;
+
+ ++line_number;
+ if (line_number == 0)
+ error (EXIT_FAILURE, 0, _("%s: too many checksum lines"),
+ quotef (checkfile_name));
+
+ line_length = getline (&line, &line_chars_allocated, checkfile_stream);
+ if (line_length <= 0)
+ break;
- unsigned int crc_out = crc;
- memcpy (resstream, &crc_out, sizeof crc_out);
+ /* Ignore comment lines, which begin with a '#' character. */
+ if (line[0] == '#')
+ continue;
- return 0;
-}
+ /* Remove any trailing newline. */
+ line_length -= line[line_length - 1] == '\n';
+ /* Remove any trailing carriage return. */
+ line_length -= line[line_length - (0 < line_length)] == '\r';
-/* Calculate the crc32b checksum and length in bytes of stream STREAM.
- Return -1 on error, 0 on success. */
+ /* Ignore empty lines. */
+ if (line_length == 0)
+ continue;
+
+ line[line_length] = '\0';
+
+ idx_t d_len;
+ if (! (split_3 (line, line_length, &digest, &d_len, &binary, &filename)
+ && ! (is_stdin && streq (filename, "-"))))
+ {
+ ++n_misformatted_lines;
+
+ if (warn)
+ {
+ error (0, 0,
+ _("%s: %jd"
+ ": improperly formatted %s checksum line"),
+ quotef (checkfile_name), line_number,
+ DIGEST_TYPE_STRING);
+ }
+ }
+ else
+ {
+ bool ok;
+ bool missing;
+
+ properly_formatted_lines = true;
+
+ intmax_t length;
+ ok = digest_file (filename, &binary, bin_buffer, &missing, &length);
+
+ if (!ok)
+ {
+ ++n_open_or_read_failures;
+ if (!status_only)
+ printf ("%s: %s\n", quotef (filename),_("FAILED open or read"));
+ }
+ else if (ignore_missing && missing)
+ {
+ /* Ignore missing files with --ignore-missing. */
+ ;
+ }
+ else
+ {
+ bool match = false;
+#if HASH_ALGO_CKSUM
+ if (d_len == BASE64_LENGTH (digest_length >> 3))
+ match = b64_equal (digest, bin_buffer);
+ else
+#endif
+ if (d_len == digest_hex_bytes)
+ match = hex_equal (digest, bin_buffer);
+
+ if (match)
+ matched_checksums = true;
+ else
+ ++n_mismatched_checksums;
+
+ if (!status_only)
+ {
+ if (! match || ! quiet)
+ fputs (quotef (filename), stdout);
+
+ if (! match)
+ printf (": %s\n", _("FAILED"));
+ else if (!quiet)
+ printf (": %s\n", _("OK"));
+ }
+ }
+
+ if (ferror (stdout))
+ write_error ();
+ }
+ }
+ while (!feof (checkfile_stream) && !ferror (checkfile_stream));
+
+ free (line);
+
+ int err = ferror (checkfile_stream) ? 0 : -1;
+ if (is_stdin)
+ clearerr (checkfile_stream);
+ else if (fclose (checkfile_stream) != 0 && err < 0)
+ err = errno;
+
+ if (0 <= err)
+ {
+ error (0, err, err ? "%s" : _("%s: read error"),
+ quotef (checkfile_name));
+ return false;
+ }
+
+ if (! properly_formatted_lines)
+ {
+ /* Warn if no tests are found. */
+ error (0, 0, _("%s: no properly formatted checksum lines found"),
+ quotef (checkfile_name));
+ }
+ else
+ {
+ if (!status_only)
+ {
+ if (n_misformatted_lines != 0)
+ error (0, 0,
+ (ngettext
+ ("WARNING: %jd line is improperly formatted",
+ "WARNING: %jd lines are improperly formatted",
+ select_plural (n_misformatted_lines))),
+ n_misformatted_lines);
+
+ if (n_open_or_read_failures != 0)
+ error (0, 0,
+ (ngettext
+ ("WARNING: %jd listed file could not be read",
+ "WARNING: %jd listed files could not be read",
+ select_plural (n_open_or_read_failures))),
+ n_open_or_read_failures);
+
+ if (n_mismatched_checksums != 0)
+ error (0, 0,
+ (ngettext
+ ("WARNING: %jd computed checksum did NOT match",
+ "WARNING: %jd computed checksums did NOT match",
+ select_plural (n_mismatched_checksums))),
+ n_mismatched_checksums);
+
+ if (ignore_missing && ! matched_checksums)
+ error (0, 0, _("%s: no file was verified"),
+ quotef (checkfile_name));
+ }
+ }
+
+ return (properly_formatted_lines
+ && matched_checksums
+ && n_mismatched_checksums == 0
+ && n_open_or_read_failures == 0
+ && (!strict || n_misformatted_lines == 0));
+}
int
-crc32b_sum_stream (FILE *stream, void *resstream, uintmax_t *reslen)
+main (int argc, char **argv)
{
- uint32_t buf[BUFLEN / sizeof (uint32_t)];
- uint32_t crc = 0;
- uintmax_t len = 0;
- size_t bytes_read;
+ unsigned char bin_buffer_unaligned[DIGEST_BIN_BYTES + DIGEST_ALIGN];
+ /* Make sure bin_buffer is properly aligned. */
+ unsigned char *bin_buffer = ptr_align (bin_buffer_unaligned, DIGEST_ALIGN);
+ bool do_check = false;
+ int opt;
+ bool ok = true;
+ int binary = -1;
+ int prefix_tag = -1;
+ struct option const *long_opts = long_options;
+
+#if HASH_ALGO_CKSUM
+ if (cksum_algorithm != crc)
+ {
+ legacy_mode = true;
+ prefix_tag = 0;
+ algorithm_specified = true;
+ long_opts = legacy_long_options;
+ }
+#endif
- if (!stream || !resstream || !reslen)
- return -1;
+ /* Setting values of global variables. */
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ atexit (close_stdout);
+
+ /* Line buffer stdout to ensure lines are written atomically and immediately
+ so that processes running in parallel do not intersperse their output. */
+ setvbuf (stdout, NULL, _IOLBF, 0);
+
+#if HASH_ALGO_SUM
+ char const *short_opts = "rs";
+#elif HASH_ALGO_CKSUM
+ char const *short_opts = legacy_mode ? "bctwz" : "a:l:bctwz";
+ char const *digest_length_str = "";
+#elif HASH_ALGO_BLAKE2
+ char const *short_opts = "l:bctwz";
+ char const *digest_length_str = "";
+#else
+ char const *short_opts = "bctwz";
+#endif
-# if GL_CRC_X86_64_PCLMUL
- if (cksum_debug)
- (void) pclmul_supported ();
+ while ((opt = getopt_long (argc, argv, short_opts, long_opts, NULL))
+ != -1)
+ switch (opt)
+ {
+#if HASH_ALGO_CKSUM
+ case 'a':
+ cksum_algorithm = XARGMATCH_EXACT ("--algorithm", optarg,
+ algorithm_args, algorithm_types);
+ algorithm_specified = true;
+ break;
+
+ case DEBUG_PROGRAM_OPTION:
+ cksum_debug = true;
+ break;
+#endif
+#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+ case 'l':
+ digest_length = xnumtoimax (optarg, 10, 0, IDX_MAX, "",
+ _("invalid length"), 0,
+ XTOINT_MAX_QUIET);
+ digest_length_str = optarg;
+ break;
+#endif
+#if !HASH_ALGO_SUM
+ case 'c':
+ do_check = true;
+ break;
+ case STATUS_OPTION:
+ status_only = true;
+ warn = false;
+ quiet = false;
+ break;
+ case 'b':
+ binary = 1;
+ break;
+ case 't':
+ binary = 0;
+ break;
+ case 'w':
+ status_only = false;
+ warn = true;
+ quiet = false;
+ break;
+ case IGNORE_MISSING_OPTION:
+ ignore_missing = true;
+ break;
+ case QUIET_OPTION:
+ status_only = false;
+ warn = false;
+ quiet = true;
+ break;
+ case STRICT_OPTION:
+ strict = true;
+ break;
+# if HASH_ALGO_CKSUM
+ case BASE64_OPTION:
+ base64_digest = true;
+ break;
+ case RAW_OPTION:
+ raw_digest = true;
+ break;
+ case UNTAG_OPTION:
+ prefix_tag = 0;
+ break;
# endif
+ case TAG_OPTION:
+ prefix_tag = 1;
+ break;
+ case 'z':
+ digest_delim = '\0';
+ break;
+#endif
+#if HASH_ALGO_SUM
+ case 'r': /* For SysV compatibility. */
+ sum_algorithm = bsd;
+ break;
- while ((bytes_read = fread (buf, 1, BUFLEN, stream)) > 0)
+ case 's':
+ sum_algorithm = sysv;
+ break;
+#endif
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
+
+ min_digest_line_length = MIN_DIGEST_LINE_LENGTH;
+#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
+# if HASH_ALGO_CKSUM
+ if (digest_length && (cksum_algorithm != blake2b
+ && cksum_algorithm != sha2
+ && cksum_algorithm != sha3))
+ error (EXIT_FAILURE, 0,
+ _("--length is only supported with --algorithm "
+ "blake2b, sha2, or sha3"));
+ if (cksum_algorithm == sha2 || cksum_algorithm == sha3)
{
- if (ckd_add (&len, len, bytes_read))
+ /* Do not require --length with --check. */
+ if (digest_length == 0 && *digest_length_str == '\0' && ! do_check)
+ error (EXIT_FAILURE, 0, _("--algorithm=%s requires specifying "
+ "--length 224, 256, 384, or 512"),
+ algorithm_args[cksum_algorithm]);
+ /* If --check and --length are used we verify the digest length. */
+ if ((! do_check || *digest_length_str != '\0')
+ && digest_length != SHA224_DIGEST_SIZE * 8
+ && digest_length != SHA256_DIGEST_SIZE * 8
+ && digest_length != SHA384_DIGEST_SIZE * 8
+ && digest_length != SHA512_DIGEST_SIZE * 8)
{
- errno = EOVERFLOW;
- return -1;
+ error (0, 0, _("invalid length: %s"), quote (digest_length_str));
+ error (EXIT_FAILURE, 0, _("digest length for %s must be "
+ "224, 256, 384, or 512"),
+ quote (DIGEST_TYPE_STRING));
}
+ }
+ else
+ {
+ /* If the digest length checks for SHA-3 are satisfied, the less strict
+ checks for BLAKE2 will also be. */
+# else
+ {
+# endif
+ if (digest_length > DIGEST_MAX_LEN * 8)
+ {
+ error (0, 0, _("invalid length: %s"), quote (digest_length_str));
+ error (EXIT_FAILURE, 0,
+ _("maximum digest length for %s is %d bits"),
+ quote (DIGEST_TYPE_STRING),
+ DIGEST_MAX_LEN * 8);
+ }
+ if (digest_length % 8 != 0)
+ {
+ error (0, 0, _("invalid length: %s"), quote (digest_length_str));
+ error (EXIT_FAILURE, 0, _("length is not a multiple of 8"));
+ }
+ }
+ if (digest_length == 0)
+ {
+# if HASH_ALGO_BLAKE2
+ digest_length = DIGEST_MAX_LEN * 8;
+# else
+ digest_length = algorithm_bits[cksum_algorithm];
+# endif
+ }
+ digest_hex_bytes = digest_length >> 2;
+#else
+ digest_hex_bytes = DIGEST_HEX_BYTES;
+#endif
- crc = crc32_update (crc, (char const *)buf, bytes_read);
-
- if (feof (stream))
+#if HASH_ALGO_CKSUM
+ switch (+cksum_algorithm)
+ {
+ case bsd:
+ case sysv:
+ case crc:
+ case crc32b:
+ if (do_check && algorithm_specified)
+ error (EXIT_FAILURE, 0,
+ _("--check is not supported with "
+ "--algorithm={bsd,sysv,crc,crc32b}"));
break;
}
- unsigned int crc_out = crc;
- memcpy (resstream, &crc_out, sizeof crc_out);
+ if (base64_digest && raw_digest)
+ {
+ error (0, 0, _("--base64 and --raw are mutually exclusive"));
+ usage (EXIT_FAILURE);
+ }
+#endif
- *reslen = len;
+ if (digest_delim != '\n' && do_check)
+ {
+ error (0, 0, _("the --zero option is not supported when "
+ "verifying checksums"));
+ usage (EXIT_FAILURE);
+ }
+ if (1 <= prefix_tag && do_check)
+ {
+ /* Note we allow --untagged with --check to more
+ seamlessly support --untagged in an emulation wrapper. */
+ error (0, 0, _("the --tag option is meaningless when "
+ "verifying checksums"));
+ usage (EXIT_FAILURE);
+ }
- return ferror (stream) ? -1 : 0;
-}
+ if (0 <= binary && do_check)
+ {
+ error (0, 0, _("the --binary and --text options are meaningless when "
+ "verifying checksums"));
+ usage (EXIT_FAILURE);
+ }
-/* Print the checksum and size to stdout.
- If ARGS is true, also print the FILE name. */
+ if (ignore_missing && !do_check)
+ {
+ error (0, 0,
+ _("the --ignore-missing option is meaningful only when "
+ "verifying checksums"));
+ usage (EXIT_FAILURE);
+ }
-void
-output_crc (char const *file, MAYBE_UNUSED int binary_file,
- void const *digest, bool raw, MAYBE_UNUSED bool tagged,
- unsigned char delim, bool args, uintmax_t length)
-{
- if (raw)
+ if (status_only && !do_check)
{
- /* Output in network byte order (big endian). */
- uint32_t out_int = htobe32 (*(uint32_t *)digest);
- fwrite (&out_int, 1, 32/8, stdout);
- return;
+ error (0, 0,
+ _("the --status option is meaningful only when verifying checksums"));
+ usage (EXIT_FAILURE);
}
- printf ("%u %ju", *(unsigned int *)digest, length);
- if (args)
- printf (" %s", file);
- putchar (delim);
-}
+ if (warn && !do_check)
+ {
+ error (0, 0,
+ _("the --warn option is meaningful only when verifying checksums"));
+ usage (EXIT_FAILURE);
+ }
-#endif /* !CRCTAB */
+ if (quiet && !do_check)
+ {
+ error (0, 0,
+ _("the --quiet option is meaningful only when verifying checksums"));
+ usage (EXIT_FAILURE);
+ }
+
+ if (strict & !do_check)
+ {
+ error (0, 0,
+ _("the --strict option is meaningful only when verifying checksums"));
+ usage (EXIT_FAILURE);
+ }
+
+ if (prefix_tag == -1)
+ prefix_tag = HASH_ALGO_CKSUM;
+
+ if (prefix_tag && !binary)
+ {
+ /* This could be supported in a backwards compatible way
+ by prefixing the output line with a space in text mode.
+ However that's invasive enough that it was agreed to
+ not support this mode with --tag, as --text use cases
+ are adequately supported by the default output format. */
+#if !HASH_ALGO_CKSUM
+ error (0, 0, _("--tag does not support --text mode"));
+#else
+ error (0, 0, _("--text mode is only supported with --untagged"));
+#endif
+ usage (EXIT_FAILURE);
+ }
+
+ if (!O_BINARY && binary < 0)
+ binary = 0;
+ else if (prefix_tag)
+ binary = 1;
+
+ char **operand_lim = argv + argc;
+ if (optind == argc)
+ *operand_lim++ = bad_cast ("-");
+ else if (1 < argc - optind && raw_digest)
+ error (EXIT_FAILURE, 0,
+ _("the --raw option is not supported with multiple files"));
+
+ for (char **operandp = argv + optind; operandp < operand_lim; operandp++)
+ {
+ char *file = *operandp;
+ if (do_check)
+ ok &= digest_check (file);
+ else
+ {
+ int binary_file = binary;
+ bool missing;
+ intmax_t length;
+
+ if (! digest_file (file, &binary_file, bin_buffer, &missing, &length))
+ ok = false;
+ else
+ {
+ DIGEST_OUT (file, binary_file, bin_buffer, raw_digest, prefix_tag,
+ digest_delim, optind != argc, length);
+ if (ferror (stdout))
+ write_error ();
+ }
+ }
+ }
+
+ if (have_read_stdin && fclose (stdin) == EOF)
+ error (EXIT_FAILURE, errno, _("standard input"));
+
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/cksum.h b/src/cksum.h
index 1c202ef76..588911e11 100644
--- a/src/cksum.h
+++ b/src/cksum.h
@@ -1,50 +1,19 @@
-/* Calculate checksums and sizes of files
- Copyright 2025 Free Software Foundation, Inc.
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>. */
-
-#ifndef __CKSUM_H__
-# define __CKSUM_H__
-
-# include <stdint.h>
-# include <stdio.h>
-
-extern bool cksum_debug;
-
-extern int
-crc_sum_stream (FILE *stream, void *resstream, uintmax_t *length);
-
-extern int
-crc32b_sum_stream (FILE *stream, void *resstream, uintmax_t *length);
-
-extern void
-output_crc (char const *file, int binary_file, void const *digest, bool raw,
- bool tagged, unsigned char delim, bool args, uintmax_t length)
- _GL_ATTRIBUTE_NONNULL ((3));
-
-extern bool
-cksum_vmull (FILE *fp, uint_fast32_t *crc_out, uintmax_t *length_out);
-
-extern bool
-cksum_pclmul (FILE *fp, uint_fast32_t *crc_out, uintmax_t *length_out);
-
-extern bool
-cksum_avx2 (FILE *fp, uint_fast32_t *crc_out, uintmax_t *length_out);
-
-extern bool
-cksum_avx512 (FILE *fp, uint_fast32_t *crc_out, uintmax_t *length_out);
-
-extern uint_fast32_t const crctab[8][256];
-
-#endif
+enum Algorithm
+{
+ bsd,
+ sysv,
+ crc,
+ crc32b,
+ md5,
+ sha1,
+ sha224,
+ sha256,
+ sha384,
+ sha512,
+ sha2,
+ sha3,
+ blake2b,
+ sm3,
+};
+
+extern enum Algorithm cksum_algorithm;
diff --git a/src/cksum_avx2.c b/src/cksum_avx2.c
index 23ecc66e5..eea8e073a 100644
--- a/src/cksum_avx2.c
+++ b/src/cksum_avx2.c
@@ -1,5 +1,5 @@
-/* cksum -- calculate and print POSIX checksums and sizes of files
- Copyright (C) 2024-2025 Free Software Foundation, Inc.
+/* cksum_crc -- calculate and print POSIX checksums and sizes of files
+ Copyright (C) 2024-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
#include <config.h>
-#include "cksum.h"
+#include "cksum_crc.h"
#include <stdio.h>
#include <sys/types.h>
@@ -27,11 +27,11 @@
#define BUFLEN (1 << 16)
bool
-cksum_avx2 (FILE *fp, uint_fast32_t *crc_out, uintmax_t *length_out)
+cksum_avx2 (FILE *fp, uint_fast32_t *crc_out, intmax_t *length_out)
{
__m256i buf[BUFLEN / sizeof (__m256i)];
uint_fast32_t crc = 0;
- uintmax_t length = 0;
+ intmax_t length = 0;
size_t bytes_read;
__m256i single_mult_constant;
__m256i four_mult_constant;
@@ -74,7 +74,7 @@ cksum_avx2 (FILE *fp, uint_fast32_t *crc_out, uintmax_t *length_out)
return false;
}
- datap = (__m256i *)buf;
+ datap = buf;
/* Fold in parallel 16x 16-byte blocks into 8x 16-byte blocks */
if (bytes_read >= 16 * 8 * 2)
diff --git a/src/cksum_avx512.c b/src/cksum_avx512.c
index df6abf569..cc1302321 100644
--- a/src/cksum_avx512.c
+++ b/src/cksum_avx512.c
@@ -1,5 +1,5 @@
-/* cksum -- calculate and print POSIX checksums and sizes of files
- Copyright (C) 2024-2025 Free Software Foundation, Inc.
+/* cksum_crc -- calculate and print POSIX checksums and sizes of files
+ Copyright (C) 2024-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
#include <config.h>
-#include "cksum.h"
+#include "cksum_crc.h"
#include <sys/types.h>
#include <x86intrin.h>
@@ -26,11 +26,11 @@
#define BUFLEN (1 << 16)
bool
-cksum_avx512 (FILE *fp, uint_fast32_t *crc_out, uintmax_t *length_out)
+cksum_avx512 (FILE *fp, uint_fast32_t *crc_out, intmax_t *length_out)
{
__m512i buf[BUFLEN / sizeof (__m512i)];
uint_fast32_t crc = 0;
- uintmax_t length = 0;
+ intmax_t length = 0;
size_t bytes_read;
__m512i single_mult_constant;
__m512i four_mult_constant;
@@ -81,7 +81,7 @@ cksum_avx512 (FILE *fp, uint_fast32_t *crc_out, uintmax_t *length_out)
return false;
}
- datap = (__m512i *)buf;
+ datap = buf;
/* Fold in parallel 32x 16-byte blocks into 16x 16-byte blocks */
if (bytes_read >= 16 * 8 * 4)
diff --git a/src/cksum_crc.c b/src/cksum_crc.c
new file mode 100644
index 000000000..dbe2b994f
--- /dev/null
+++ b/src/cksum_crc.c
@@ -0,0 +1,377 @@
+/* cksum_crc -- calculate and print POSIX checksums and sizes of files
+ Copyright (C) 1992-2026 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* Written by Q. Frank Xia, qx@math.columbia.edu.
+ Cosmetic changes and reorganization by David MacKenzie, djm@gnu.ai.mit.edu.
+
+ The code segment between "#ifdef CRCTAB" and "#else" is the code
+ which generates crctab.c
+
+ This software is compatible with neither the System V nor the BSD
+ 'sum' program. It is supposed to conform to POSIX, except perhaps
+ for foreign language support. Any inconsistency with the standard
+ (other than foreign language support) is a bug. */
+
+/* This include must be at the top of the file to satisfy
+ sc_require_config_h_first. */
+#ifndef CRCTAB
+# include <config.h>
+#endif
+
+#ifdef CRCTAB
+
+# include <stdio.h>
+
+# define BIT(x) (1u << (x))
+# define SBIT BIT (31)
+
+/* The generating polynomial is
+
+ 32 26 23 22 16 12 11 10 8 7 5 4 2 1
+ G(X)=X + X + X + X + X + X + X + X + X + X + X + X + X + X + 1
+
+ The i bit in GEN is set if X^i is a summand of G(X) except X^32. */
+
+# define GEN (BIT (26) | BIT (23) | BIT (22) | BIT (16) | BIT (12) \
+ | BIT (11) | BIT (10) | BIT (8) | BIT (7) | BIT (5) \
+ | BIT (4) | BIT (2) | BIT (1) | BIT (0))
+
+static unsigned int r[8];
+
+static void
+fill_r (void)
+{
+ r[0] = GEN;
+ for (int i = 1; i < 8; i++)
+ r[i] = (r[i - 1] << 1) ^ ((r[i - 1] & SBIT) ? GEN : 0);
+}
+
+static unsigned int
+crc_remainder (int m)
+{
+ unsigned int rem = 0;
+
+ for (int i = 0; i < 8; i++)
+ if (BIT (i) & m)
+ rem ^= r[i];
+
+ return rem & 0xFFFFFFFF; /* Make it run on 64-bit machine. */
+}
+
+int
+main (void)
+{
+ static unsigned int crctab[8][256];
+
+ fill_r ();
+
+ for (int i = 0; i < 256; i++)
+ crctab[0][i] = crc_remainder (i);
+
+ /* CRC(0x11 0x22 0x33 0x44)
+ is equal to
+ CRC(0x11 0x00 0x00 0x00) XOR CRC(0x22 0x00 0x00) XOR
+ CRC(0x33 0x00) XOR CRC(0x44)
+ We precompute the CRC values for the offset values into
+ separate CRC tables. We can then use them to speed up
+ CRC calculation by processing multiple bytes at the time. */
+ for (int i = 0; i < 256; i++)
+ {
+ unsigned int crc = 0;
+
+ crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ i) & 0xFF];
+ for (int offset = 1; offset < 8; offset++)
+ {
+ crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ 0) & 0xFF];
+ crctab[offset][i] = crc & 0xFFFFFFFF;
+ }
+ }
+
+ printf ("#include <config.h>\n");
+ printf ("#include \"cksum_crc.h\"\n");
+ printf ("\n");
+ printf ("uint_fast32_t const crctab[8][256] = {\n");
+ for (int y = 0; y < 8; y++)
+ {
+ printf ("{\n 0x%08x", crctab[y][0]);
+ for (int i = 0; i < 51; i++)
+ {
+ printf (",\n 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x",
+ crctab[y][i * 5 + 1], crctab[y][i * 5 + 2],
+ crctab[y][i * 5 + 3], crctab[y][i * 5 + 4],
+ crctab[y][i * 5 + 5]);
+ }
+ printf ("\n},\n");
+ }
+ printf ("};\n");
+}
+
+#else /* !CRCTAB */
+
+# include "cksum_crc.h"
+# include <sys/types.h>
+# include <endian.h>
+# include "system.h"
+
+# ifdef USE_VMULL_CRC32
+# include <sys/auxv.h>
+# include <asm/hwcap.h>
+# endif
+
+# include "crc.h"
+# include "cpu-supports.h"
+
+/* Number of bytes to read at once. */
+# define BUFLEN (1 << 16)
+
+typedef bool (*cksum_fp_t) (FILE *, uint_fast32_t *, intmax_t *);
+
+static cksum_fp_t
+pclmul_supported (void)
+{
+# if USE_PCLMUL_CRC32 || GL_CRC_X86_64_PCLMUL
+ bool pclmul_enabled = (cpu_supports ("avx")
+ && cpu_supports ("pclmul"));
+ if (cksum_debug)
+ error (0, 0, "%s",
+ (pclmul_enabled
+ ? _("using pclmul hardware support")
+ : _("pclmul support not detected")));
+# if USE_PCLMUL_CRC32
+ if (pclmul_enabled)
+ return cksum_pclmul;
+# endif
+# endif
+
+ return NULL;
+}
+
+static cksum_fp_t
+avx2_supported (void)
+{
+ /* AVX512 processors will not set vpclmulqdq unless they support
+ the avx512 version, but it implies that the avx2 version
+ is supported */
+# if USE_AVX2_CRC32
+ bool avx2_enabled = (cpu_supports ("avx2")
+ && cpu_supports ("vpclmulqdq"));
+ if (cksum_debug)
+ error (0, 0, "%s",
+ (avx2_enabled
+ ? _("using avx2 hardware support")
+ : _("avx2 support not detected")));
+ if (avx2_enabled)
+ return cksum_avx2;
+# endif
+
+ return NULL;
+}
+
+static cksum_fp_t
+avx512_supported (void)
+{
+ /* vpclmulqdq for multiplication
+ mavx512f for most of the avx512 functions we're using
+ mavx512bw for byte swapping */
+# if USE_AVX512_CRC32
+ bool avx512_enabled = (cpu_supports ("avx512f")
+ && cpu_supports ("avx512bw")
+ && cpu_supports ("vpclmulqdq"));
+
+ if (cksum_debug)
+ error (0, 0, "%s",
+ (avx512_enabled
+ ? _("using avx512 hardware support")
+ : _("avx512 support not detected")));
+ if (avx512_enabled)
+ return cksum_avx512;
+# endif
+
+ return NULL;
+}
+
+static cksum_fp_t
+vmull_supported (void)
+{
+ /* vmull for multiplication */
+# if USE_VMULL_CRC32
+ bool vmull_enabled = (cpu_may_support ("pmull")
+ && (getauxval (AT_HWCAP) & HWCAP_PMULL) > 0);
+ if (cksum_debug)
+ error (0, 0, "%s",
+ (vmull_enabled
+ ? _("using vmull hardware support")
+ : _("vmull support not detected")));
+ if (vmull_enabled)
+ return cksum_vmull;
+# endif
+
+ return NULL;
+}
+
+static bool
+cksum_slice8 (FILE *fp, uint_fast32_t *crc_out, intmax_t *length_out)
+{
+ uint32_t buf[BUFLEN / sizeof (uint32_t)];
+ uint_fast32_t crc = 0;
+ intmax_t length = 0;
+ size_t bytes_read;
+
+ if (!fp || !crc_out || !length_out)
+ return false;
+
+ while ((bytes_read = fread (buf, 1, BUFLEN, fp)) > 0)
+ {
+ uint32_t *datap;
+
+ if (ckd_add (&length, length, bytes_read))
+ {
+ errno = EOVERFLOW;
+ return false;
+ }
+
+ /* Process multiples of 8 bytes */
+ datap = buf;
+ while (bytes_read >= 8)
+ {
+ uint32_t first = *datap++, second = *datap++;
+ crc ^= htobe32 (first);
+ second = htobe32 (second);
+ crc = (crctab[7][(crc >> 24) & 0xFF]
+ ^ crctab[6][(crc >> 16) & 0xFF]
+ ^ crctab[5][(crc >> 8) & 0xFF]
+ ^ crctab[4][(crc) & 0xFF]
+ ^ crctab[3][(second >> 24) & 0xFF]
+ ^ crctab[2][(second >> 16) & 0xFF]
+ ^ crctab[1][(second >> 8) & 0xFF]
+ ^ crctab[0][(second) & 0xFF]);
+ bytes_read -= 8;
+ }
+
+ /* And finish up last 0-7 bytes in a byte by byte fashion */
+ unsigned char *cp = (unsigned char *)datap;
+ while (bytes_read--)
+ crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ *cp++) & 0xFF];
+ if (feof (fp))
+ break;
+ }
+
+ *crc_out = crc;
+ *length_out = length;
+
+ return !ferror (fp);
+}
+
+/* Calculate the checksum and length in bytes of stream STREAM.
+ Return -1 on error, 0 on success. */
+
+int
+crc_sum_stream (FILE *stream, void *resstream, intmax_t *length)
+{
+ intmax_t total_bytes = 0;
+ uint_fast32_t crc = 0;
+
+ static cksum_fp_t cksum_fp;
+ if (! cksum_fp)
+ cksum_fp = avx512_supported ();
+ if (! cksum_fp)
+ cksum_fp = avx2_supported ();
+ if (! cksum_fp)
+ cksum_fp = pclmul_supported ();
+ if (! cksum_fp)
+ cksum_fp = vmull_supported ();
+ if (! cksum_fp)
+ cksum_fp = cksum_slice8;
+
+ if (! cksum_fp (stream, &crc, &total_bytes))
+ return -1;
+
+ *length = total_bytes;
+
+ for (; total_bytes; total_bytes >>= 8)
+ crc = (crc << 8) ^ crctab[0][((crc >> 24) ^ total_bytes) & 0xFF];
+ crc = ~crc & 0xFFFFFFFF;
+
+ unsigned int crc_out = crc;
+ memcpy (resstream, &crc_out, sizeof crc_out);
+
+ return 0;
+}
+
+/* Calculate the crc32b checksum and length in bytes of stream STREAM.
+ Return -1 on error, 0 on success. */
+
+int
+crc32b_sum_stream (FILE *stream, void *resstream, intmax_t *reslen)
+{
+ uint32_t buf[BUFLEN / sizeof (uint32_t)];
+ uint32_t crc = 0;
+ intmax_t len = 0;
+ size_t bytes_read;
+
+ if (!stream || !resstream || !reslen)
+ return -1;
+
+# if GL_CRC_X86_64_PCLMUL
+ if (cksum_debug)
+ (void) pclmul_supported ();
+# endif
+
+ while ((bytes_read = fread (buf, 1, BUFLEN, stream)) > 0)
+ {
+ if (ckd_add (&len, len, bytes_read))
+ {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ crc = crc32_update (crc, (char const *)buf, bytes_read);
+
+ if (feof (stream))
+ break;
+ }
+
+ unsigned int crc_out = crc;
+ memcpy (resstream, &crc_out, sizeof crc_out);
+
+ *reslen = len;
+
+ return ferror (stream) ? -1 : 0;
+}
+
+/* Print the checksum and size to stdout.
+ If ARGS is true, also print the FILE name. */
+
+void
+output_crc (char const *file, MAYBE_UNUSED int binary_file,
+ void const *digest, bool raw, MAYBE_UNUSED bool tagged,
+ unsigned char delim, bool args, intmax_t length)
+{
+ if (raw)
+ {
+ /* Output in network byte order (big endian). */
+ uint32_t out_int = htobe32 (*(uint32_t *)digest);
+ fwrite (&out_int, 1, 32/8, stdout);
+ return;
+ }
+
+ printf ("%u %jd", *(unsigned int *)digest, length);
+ if (args)
+ printf (" %s", file);
+ putchar (delim);
+}
+
+#endif /* !CRCTAB */
diff --git a/src/cksum_crc.h b/src/cksum_crc.h
new file mode 100644
index 000000000..2e1ef725d
--- /dev/null
+++ b/src/cksum_crc.h
@@ -0,0 +1,50 @@
+/* Calculate checksums and sizes of files
+ Copyright 2025-2026 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+#ifndef __CKSUM_CRC_H__
+# define __CKSUM_CRC_H__
+
+# include <stdint.h>
+# include <stdio.h>
+
+extern bool cksum_debug;
+
+extern int
+crc_sum_stream (FILE *stream, void *resstream, intmax_t *length);
+
+extern int
+crc32b_sum_stream (FILE *stream, void *resstream, intmax_t *length);
+
+extern void
+output_crc (char const *file, int binary_file, void const *digest, bool raw,
+ bool tagged, unsigned char delim, bool args, intmax_t length)
+ _GL_ATTRIBUTE_NONNULL ((3));
+
+extern bool
+cksum_vmull (FILE *fp, uint_fast32_t *crc_out, intmax_t *length_out);
+
+extern bool
+cksum_pclmul (FILE *fp, uint_fast32_t *crc_out, intmax_t *length_out);
+
+extern bool
+cksum_avx2 (FILE *fp, uint_fast32_t *crc_out, intmax_t *length_out);
+
+extern bool
+cksum_avx512 (FILE *fp, uint_fast32_t *crc_out, intmax_t *length_out);
+
+extern uint_fast32_t const crctab[8][256];
+
+#endif
diff --git a/src/cksum_pclmul.c b/src/cksum_pclmul.c
index 240d475fc..a3a83b282 100644
--- a/src/cksum_pclmul.c
+++ b/src/cksum_pclmul.c
@@ -1,5 +1,5 @@
-/* cksum -- calculate and print POSIX checksums and sizes of files
- Copyright (C) 2021-2025 Free Software Foundation, Inc.
+/* cksum_crc -- calculate and print POSIX checksums and sizes of files
+ Copyright (C) 2021-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
#include <config.h>
-#include "cksum.h"
+#include "cksum_crc.h"
#include <stdio.h>
#include <sys/types.h>
@@ -29,11 +29,11 @@
/* Calculate CRC32 using PCLMULQDQ CPU instruction found in x86/x64 CPUs */
bool
-cksum_pclmul (FILE *fp, uint_fast32_t *crc_out, uintmax_t *length_out)
+cksum_pclmul (FILE *fp, uint_fast32_t *crc_out, intmax_t *length_out)
{
__m128i buf[BUFLEN / sizeof (__m128i)];
uint_fast32_t crc = 0;
- uintmax_t length = 0;
+ intmax_t length = 0;
size_t bytes_read;
__m128i single_mult_constant;
__m128i four_mult_constant;
@@ -72,7 +72,7 @@ cksum_pclmul (FILE *fp, uint_fast32_t *crc_out, uintmax_t *length_out)
return false;
}
- datap = (__m128i *)buf;
+ datap = buf;
/* Fold in parallel eight 16-byte blocks into four 16-byte blocks */
if (bytes_read >= 16 * 8)
diff --git a/src/cksum_vmull.c b/src/cksum_vmull.c
index fddfaa47b..1d18ba898 100644
--- a/src/cksum_vmull.c
+++ b/src/cksum_vmull.c
@@ -1,5 +1,5 @@
-/* cksum -- calculate and print POSIX checksums and sizes of files
- Copyright (C) 2024-2025 Free Software Foundation, Inc.
+/* cksum_crc -- calculate and print POSIX checksums and sizes of files
+ Copyright (C) 2024-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
#include <config.h>
-#include "cksum.h"
+#include "cksum_crc.h"
#include <stdio.h>
#include <sys/types.h>
@@ -38,11 +38,11 @@ bswap_neon (uint64x2_t in)
/* Calculate CRC32 using VMULL CPU instruction found in ARMv8 CPUs */
bool
-cksum_vmull (FILE *fp, uint_fast32_t *crc_out, uintmax_t *length_out)
+cksum_vmull (FILE *fp, uint_fast32_t *crc_out, intmax_t *length_out)
{
uint64x2_t buf[BUFLEN / sizeof (uint64x2_t)];
uint_fast32_t crc = 0;
- uintmax_t length = 0;
+ intmax_t length = 0;
size_t bytes_read;
poly64x2_t single_mult_constant;
poly64x2_t four_mult_constant;
diff --git a/src/comm.c b/src/comm.c
index 355f72b69..b2ceca382 100644
--- a/src/comm.c
+++ b/src/comm.c
@@ -1,5 +1,5 @@
/* comm -- compare two sorted files line by line.
- Copyright (C) 1986-2025 Free Software Foundation, Inc.
+ Copyright (C) 1986-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -40,13 +40,13 @@
static bool hard_LC_COLLATE;
/* If true, print lines that are found only in file 1. */
-static bool only_file_1;
+static bool only_file_1 = true;
/* If true, print lines that are found only in file 2. */
-static bool only_file_2;
+static bool only_file_2 = true;
/* If true, print lines that are found in both files. */
-static bool both;
+static bool both = true;
/* If nonzero, we have seen at least one unpairable line. */
static bool seen_unpairable;
@@ -85,14 +85,14 @@ enum
static struct option const long_options[] =
{
- {"check-order", no_argument, nullptr, CHECK_ORDER_OPTION},
- {"nocheck-order", no_argument, nullptr, NOCHECK_ORDER_OPTION},
- {"output-delimiter", required_argument, nullptr, OUTPUT_DELIMITER_OPTION},
- {"total", no_argument, nullptr, TOTAL_OPTION},
- {"zero-terminated", no_argument, nullptr, 'z'},
+ {"check-order", no_argument, NULL, CHECK_ORDER_OPTION},
+ {"nocheck-order", no_argument, NULL, NOCHECK_ORDER_OPTION},
+ {"output-delimiter", required_argument, NULL, OUTPUT_DELIMITER_OPTION},
+ {"total", no_argument, NULL, TOTAL_OPTION},
+ {"zero-terminated", no_argument, NULL, 'z'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
@@ -112,7 +112,7 @@ Compare sorted files FILE1 and FILE2 line by line.\n\
"), stdout);
fputs (_("\
\n\
-When FILE1 or FILE2 (not both) is -, read standard input.\n\
+When FILE1 or FILE2 is -, read standard input.\n\
"), stdout);
fputs (_("\
\n\
@@ -120,29 +120,38 @@ With no options, produce three-column output. Column one contains\n\
lines unique to FILE1, column two contains lines unique to FILE2,\n\
and column three contains lines common to both files.\n\
"), stdout);
- fputs (_("\
-\n\
- -1 suppress column 1 (lines unique to FILE1)\n\
- -2 suppress column 2 (lines unique to FILE2)\n\
- -3 suppress column 3 (lines that appear in both files)\n\
-"), stdout);
- fputs (_("\
-\n\
- --check-order check that the input is correctly sorted, even\n\
- if all input lines are pairable\n\
- --nocheck-order do not check that the input is correctly sorted\n\
-"), stdout);
- fputs (_("\
- --output-delimiter=STR separate columns with STR\n\
-"), stdout);
- fputs (_("\
- --total output a summary\n\
-"), stdout);
- fputs (_("\
- -z, --zero-terminated line delimiter is NUL, not newline\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -1 suppress column 1 (lines unique to FILE1)\n\
+"));
+ oputs (_("\
+ -2 suppress column 2 (lines unique to FILE2)\n\
+"));
+ oputs (_("\
+ -3 suppress column 3 (lines that appear in both files)\n\
+"));
+ oputs (_("\
+ --check-order\n\
+ check that the input is correctly sorted,\n\
+ even if all input lines are pairable\n\
+"));
+ oputs (_("\
+ --nocheck-order\n\
+ do not check that the input is correctly sorted\n\
+"));
+ oputs (_("\
+ --output-delimiter=STR\n\
+ separate columns with STR\n\
+"));
+ oputs (_("\
+ --total\n\
+ output a summary\n\
+"));
+ oputs (_("\
+ -z, --zero-terminated\n\
+ line delimiter is NUL, not newline\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
Comparisons honor the rules specified by 'LC_COLLATE'.\n\
@@ -272,12 +281,10 @@ compare_files (char **infiles)
/* Counters for the summary. */
uintmax_t total[] = {0, 0, 0};
- int i, j;
-
/* Initialize the storage. */
- for (i = 0; i < 2; i++)
+ for (int i = 0; i < 2; i++)
{
- for (j = 0; j < 4; j++)
+ for (int j = 0; j < 4; j++)
{
initbuffer (&lba[i][j]);
all_line[i][j] = &lba[i][j];
@@ -353,7 +360,7 @@ compare_files (char **infiles)
if (order <= 0)
fill_up[0] = true;
- for (i = 0; i < 2; i++)
+ for (int i = 0; i < 2; i++)
if (fill_up[i])
{
/* Rotate the buffers for this file. */
@@ -381,7 +388,9 @@ compare_files (char **infiles)
}
}
- for (i = 0; i < 2; i++)
+ /* Avoid closing standard input twice when invoking 'comm - -'. */
+ const int n_streams = 2 - (streams[0] == streams[1]);
+ for (int i = 0; i < n_streams; i++)
if (fclose (streams[i]) != 0)
error (EXIT_FAILURE, errno, "%s", quotef (infiles[i]));
@@ -427,16 +436,7 @@ main (int argc, char **argv)
atexit (close_stdout);
- only_file_1 = true;
- only_file_2 = true;
- both = true;
-
- seen_unpairable = false;
- issued_disorder_warning[0] = issued_disorder_warning[1] = false;
- check_input_order = CHECK_ORDER_DEFAULT;
- total_option = false;
-
- while ((c = getopt_long (argc, argv, "123z", long_options, nullptr)) != -1)
+ while ((c = getopt_long (argc, argv, "123z", long_options, NULL)) != -1)
switch (c)
{
case '1':
diff --git a/src/copy-file-data.c b/src/copy-file-data.c
index c88b10cb3..d76ccc0db 100644
--- a/src/copy-file-data.c
+++ b/src/copy-file-data.c
@@ -1,5 +1,5 @@
/* Copy data from one file to another.
- Copyright 1989-2025 Free Software Foundation, Inc.
+ Copyright 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -85,9 +85,8 @@ create_hole (int fd, char const *name, off_t size)
return file_end;
}
-/* Similarly, whether ERR indicates that the copying operation is not
- supported or allowed for this file or process, even though the
- operation was invoked correctly. */
+/* Does ERR mean the copying operation is not supported or allowed for
+ this file or process, even though the operation was invoked correctly? */
static bool
is_CLONENOTSUP (int err)
{
@@ -97,10 +96,11 @@ is_CLONENOTSUP (int err)
|| err == EPERM || err == EACCES;
}
-/* Copy the regular file open on SRC_FD/SRC_NAME to DST_FD/DST_NAME,
- honoring the MAKE_HOLES setting and using the BUF_SIZE-byte buffer
- *ABUF for temporary storage, allocating it lazily if *ABUF is null.
+/* Copy SRC_FD to DEST_FD, using the buffer *ABUF of size BUF_SIZE
+ for temporary storage, allocating the buffer lazily if *ABUF is null.
For best results, *ABUF should be well-aligned.
+ If ALLOW_REFLINK, possibly use copy_file_range to copy.
+ SRC_NAME and DST_NAME are the source and destination's name.
Copy no more than MAX_N_READ bytes.
If HOLE_SIZE, look for holes in the input; *HOLE_SIZE contains
the size of the current hole so far, and update *HOLE_SIZE
@@ -131,7 +131,7 @@ sparse_copy (int src_fd, int dest_fd, char **abuf, idx_t buf_size,
(SSIZE_MAX, SIZE_MAX) truncated to a value that is
surely aligned well. */
ssize_t copy_max = MIN (SSIZE_MAX, SIZE_MAX) >> 30 << 30;
- ssize_t n_copied = copy_file_range (src_fd, nullptr, dest_fd, nullptr,
+ ssize_t n_copied = copy_file_range (src_fd, NULL, dest_fd, NULL,
MIN (max_n_read, copy_max), 0);
if (n_copied == 0)
{
@@ -146,6 +146,14 @@ sparse_copy (int src_fd, int dest_fd, char **abuf, idx_t buf_size,
}
if (n_copied < 0)
{
+ /* Don’t treat EFBIG as a reportable error from copy_file_range.
+ If the input is at EOF and the output position is 2**63 - 1,
+ copy_file_range (ifd, NULL, ofd, NULL, 2146435072, 0)
+ incorrectly fails with EFBIG. Problem observed on Ubuntu 25.10
+ x86-64 with Linux kernel 6.17.0-8-generic #8-Ubuntu. */
+ if (errno == EFBIG)
+ break;
+
debug->offload = COPY_DEBUG_UNSUPPORTED;
/* Consider operation unsupported only if no data copied.
@@ -267,12 +275,14 @@ sparse_copy (int src_fd, int dest_fd, char **abuf, idx_t buf_size,
return total_n_read;
}
-/* Write N_BYTES zero bytes to file descriptor FD. Return true if successful.
- Upon write failure, set errno and return false. */
+/* Write to FD. Write data consisting of N_BYTES' worth of '\0's.
+ Use the buffer *ABUF of size BUF_SIZE for temporary storage,
+ allocating the buffer lazily if *ABUF is null.
+ Return true if successful, false (setting errno) otherwise. */
static bool
write_zeros (int fd, off_t n_bytes, char **abuf, idx_t buf_size)
{
- char *zeros = nullptr;
+ char *zeros = NULL;
while (n_bytes)
{
idx_t n = MIN (buf_size, n_bytes);
@@ -296,19 +306,19 @@ write_zeros (int fd, off_t n_bytes, char **abuf, idx_t buf_size)
copy, and thus makes copying sparse files much more efficient.
Copy from SRC_FD to DEST_FD, using *ABUF (of size BUF_SIZE) for a buffer.
Allocate *ABUF lazily if *ABUF is null.
- The input file was originally positioned at SRC_POS
- and the output file is positioned at OPOS.
+ The input file was originally positioned at SRC_POS.
Copy at most IBYTES.
- If EXT_START is nonnegative the file is now positioned at EXT_START,
- the start of its first data extent on or after SRC_POS;
+ SCAN_INFERENCE points to the result of an input file scan inference.
+ If SCAN_INFERENCE->ext_start is nonnegative, SRC_FD now positioned there;
otherwise the file has no data extents and the file is now
positioned at SRC_POS.
+ SCAN_INFERENCE->hole_start is the position of the first hole.
The file is of size SRC_TOTAL_SIZE.
Use SPARSE_MODE to determine whether to create holes in the output.
+ If ALLOW_REFLINK, possibly use copy_file_range to copy.
SRC_NAME and DST_NAME are the input and output file names.
- Set *HOLE_SIZE to be the size of the hole at the end of the input.
- Set *TOTAL_N_READ to the number of bytes read; this counts
- the trailing hole, which has not yet been output.
+ Set *HOLE_SIZE to be the size of the hole at the end of the input;
+ do not output the zeros in that hole.
Read and update *DEBUG as needed.
If successful, return the number of bytes copied,
otherwise diagnose the failure and return -1. */
@@ -335,12 +345,19 @@ lseek_copy (int src_fd, int dest_fd, char **abuf, idx_t buf_size,
debug->sparse_detection = COPY_DEBUG_EXTERNAL;
+ bool used_scan_inference = false;
+
for (off_t ext_start = scan_inference->ext_start;
0 <= ext_start && ext_start < max_ipos; )
{
- off_t ext_end = (ext_start == ipos
- ? scan_inference->hole_start
- : lseek (src_fd, ext_start, SEEK_HOLE));
+ off_t ext_end;
+ if (ext_start == src_pos && ! used_scan_inference)
+ {
+ ext_end = scan_inference->hole_start;
+ used_scan_inference = true;
+ }
+ else
+ ext_end = lseek (src_fd, ext_start, SEEK_HOLE);
if (0 <= ext_end)
ext_end = MIN (ext_end, max_ipos);
else
@@ -407,7 +424,7 @@ lseek_copy (int src_fd, int dest_fd, char **abuf, idx_t buf_size,
= sparse_copy (src_fd, dest_fd, abuf, buf_size,
allow_reflink, src_name, dst_name,
ext_len,
- sparse_mode == SPARSE_ALWAYS ? hole_size : nullptr,
+ sparse_mode == SPARSE_ALWAYS ? hole_size : NULL,
debug);
if (n_read < 0)
return -1;
@@ -491,7 +508,7 @@ infer_scantype (int fd, struct stat const *sb, off_t pos,
/* we prefer to return PLAIN_SCANTYPE here so that copy offload
continues to be used. Falling through to ZERO_SCANTYPE would be
- less performant in the compressed file case. */
+ less efficient in the compressed file case. */
return PLAIN_SCANTYPE;
}
}
@@ -511,12 +528,14 @@ infer_scantype (int fd, struct stat const *sb, off_t pos,
offset IPOS, and name INAME) to output file (OFD, OST, OPOS, ONAME).
If IPOS and OPOS are negative, their values are not known, perhaps
because the files are not seekable so their positions are irrelevant.
+ Set the input and output file positions to unspecified values.
Copy until IBYTES have been copied or until end of file;
if IBYTES is COUNT_MAX that suffices to copy to end of file.
Respect copy options X's sparse_mode and reflink_mode settings.
Read and update *DEBUG as needed.
If successful, return the number of bytes copied;
- otherwise, diagnose the error and return -1. */
+ otherwise, diagnose the error and return -1.
+ If unsuccessful, the destination area's contents are unspecified. */
extern intmax_t
copy_file_data (int ifd, struct stat const *ist, off_t ipos, char const *iname,
@@ -579,7 +598,7 @@ copy_file_data (int ifd, struct stat const *ist, off_t ipos, char const *iname,
buf_size -= buf_size % blcm;
}
- char *buf = nullptr;
+ char *buf = NULL;
intmax_t result;
off_t hole_size = 0;
@@ -599,7 +618,7 @@ copy_file_data (int ifd, struct stat const *ist, off_t ipos, char const *iname,
result = sparse_copy (ifd, ofd, &buf, buf_size,
x->reflink_mode != REFLINK_NEVER,
iname, oname, ibytes,
- make_holes ? &hole_size : nullptr,
+ make_holes ? &hole_size : NULL,
debug);
if (0 <= result && 0 < hole_size)
diff --git a/src/copy.c b/src/copy.c
index 4af66c882..a53fb8f8c 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -1,5 +1,5 @@
/* copy.c -- core functions for copying files and directories
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -44,7 +44,7 @@
#include "hash.h"
#include "hashcode-file.h"
#include "ignore-value.h"
-#include "issymlink.h"
+#include "issymlinkat.h"
#include "quote.h"
#include "renameatu.h"
#include "root-uid.h"
@@ -131,7 +131,7 @@ static char const *top_level_dst_name;
/* debug info about the last file copy. */
static struct copy_debug copy_debug;
-static const char*
+static char const *
copy_debug_string (enum copy_debug_val debug_val)
{
switch (debug_val)
@@ -148,7 +148,7 @@ copy_debug_string (enum copy_debug_val debug_val)
}
}
-static const char*
+static char const *
copy_debug_sparse_string (enum copy_debug_val debug_val)
{
switch (debug_val)
@@ -226,13 +226,11 @@ is_terminal_error (int err)
/* Perform the O(1) btrfs clone operation, if possible.
Upon success, return 0. Otherwise, return -1 and set errno. */
static inline int
-clone_file (int dest_fd, int src_fd)
+clone_file (MAYBE_UNUSED int dest_fd, MAYBE_UNUSED int src_fd)
{
#ifdef FICLONE
return ioctl (dest_fd, FICLONE, src_fd);
#else
- (void) dest_fd;
- (void) src_fd;
errno = ENOTSUP;
return -1;
#endif
@@ -247,7 +245,7 @@ ATTRIBUTE_PURE
static bool
is_ancestor (const struct stat *sb, const struct dir_list *ancestors)
{
- while (ancestors != 0)
+ while (ancestors)
{
if (PSAME_INODE (ancestors, sb))
return true;
@@ -329,7 +327,7 @@ copy_attr (char const *src_path, int src_fd,
bool some_errors = (!all_errors && !x->reduce_diagnostics);
int (*check) (char const *, struct error_context *)
= (x->preserve_security_context || x->set_security_context
- ? check_selinux_attr : nullptr);
+ ? check_selinux_attr : NULL);
# if 4 < __GNUC__ + (8 <= __GNUC_MINOR__)
/* Pacify gcc -Wsuggest-attribute=format through at least GCC 13.2.1. */
@@ -343,7 +341,7 @@ copy_attr (char const *src_path, int src_fd,
.quote = copy_attr_quote,
.quote_free = copy_attr_free
})
- : nullptr);
+ : NULL);
# if 4 < __GNUC__ + (8 <= __GNUC_MINOR__)
# pragma GCC diagnostic pop
# endif
@@ -392,7 +390,7 @@ copy_dir (char const *src_name_in, char const *dst_name_in,
bool ok = true;
name_space = savedir (src_name_in, SAVEDIR_SORT_FASTREAD);
- if (name_space == nullptr)
+ if (name_space == NULL)
{
/* This diagnostic is a bit vague because savedir can fail in
several different ways. */
@@ -410,8 +408,8 @@ copy_dir (char const *src_name_in, char const *dst_name_in,
while (*namep != '\0')
{
bool local_copy_into_self;
- char *src_name = file_name_concat (src_name_in, namep, nullptr);
- char *dst_name = file_name_concat (dst_name_in, namep, nullptr);
+ char *src_name = file_name_concat (src_name_in, namep, NULL);
+ char *dst_name = file_name_concat (dst_name_in, namep, NULL);
bool first_dir_created = *first_dir_created_per_command_line_arg;
bool rename_succeeded;
@@ -541,7 +539,8 @@ set_owner (const struct cp_options *x, char const *dst_name,
DST_NAME if defined. */
static void
-set_author (char const *dst_name, int dest_desc, const struct stat *src_sb)
+set_author (MAYBE_UNUSED char const *dst_name, MAYBE_UNUSED int dest_desc,
+ MAYBE_UNUSED const struct stat *src_sb)
{
#if HAVE_STRUCT_STAT_ST_AUTHOR
/* FIXME: Modify the following code so that it does not
@@ -561,10 +560,6 @@ set_author (char const *dst_name, int dest_desc, const struct stat *src_sb)
quoteaf (dst_name));
mach_port_deallocate (mach_task_self (), file);
}
-#else
- (void) dst_name;
- (void) dest_desc;
- (void) src_sb;
#endif
}
@@ -1449,7 +1444,7 @@ dest_info_init (struct cp_options *x)
{
x->dest_info
= hash_initialize (DEST_INFO_INITIAL_CAPACITY,
- nullptr,
+ NULL,
triple_hash,
triple_compare,
triple_free);
@@ -1473,7 +1468,7 @@ src_info_init (struct cp_options *x)
*/
x->src_info
= hash_initialize (DEST_INFO_INITIAL_CAPACITY,
- nullptr,
+ NULL,
triple_hash_no_name,
triple_compare,
triple_free);
@@ -1518,11 +1513,11 @@ emit_verbose (char const *format, char const *src, char const *dst,
putchar ('\n');
}
-/* A wrapper around "setfscreatecon (nullptr)" that exits upon failure. */
+/* A wrapper around "setfscreatecon (NULL)" that exits upon failure. */
static void
restore_default_fscreatecon_or_die (void)
{
- if (setfscreatecon (nullptr) != 0)
+ if (setfscreatecon (NULL) != 0)
error (EXIT_FAILURE, errno,
_("failed to restore the default file creation context"));
}
@@ -1560,7 +1555,7 @@ create_hard_link (char const *src_name, int src_dirfd, char const *src_relname,
if (0 < err)
{
- char *a_src_name = nullptr;
+ char *a_src_name = NULL;
if (!src_name)
src_name = a_src_name = subst_suffix (dst_name, dst_relname,
src_relname);
@@ -1643,8 +1638,8 @@ copy_internal (char const *src_name, char const *dst_name,
mode_t dst_mode_bits;
mode_t omitted_permissions;
bool restore_dst_mode = false;
- char *earlier_file = nullptr;
- char *dst_backup = nullptr;
+ char *earlier_file = NULL;
+ char *dst_backup = NULL;
char const *drelname = *dst_relname ? dst_relname : ".";
bool delayed_ok;
bool copied_as_regular = false;
@@ -1820,7 +1815,7 @@ copy_internal (char const *src_name, char const *dst_name,
{
/* Note we currently replace DST_NAME unconditionally,
even if it was a newer separate file. */
- if (! create_hard_link (nullptr, dst_dirfd, earlier_file,
+ if (! create_hard_link (NULL, dst_dirfd, earlier_file,
dst_name, dst_dirfd, dst_relname,
true,
x->verbose, dereference))
@@ -2003,7 +1998,7 @@ skip:
struct stat *dst_lstat_sb
= (have_dst_lstat ? &dst_sb
: fstatat (dst_dirfd, drelname, &tmp_buf, AT_SYMLINK_NOFOLLOW) < 0
- ? nullptr : &tmp_buf);
+ ? NULL : &tmp_buf);
/* Never copy through a symlink we've just created. */
if (dst_lstat_sb
@@ -2055,7 +2050,7 @@ skip:
We'll use that info to detect this problem: cp -R dir dir. */
if (rename_errno == 0 || x->exchange)
- earlier_file = nullptr;
+ earlier_file = NULL;
else if (x->recursive && S_ISDIR (src_mode))
{
if (command_line_arg)
@@ -2138,7 +2133,7 @@ skip:
}
else
{
- if (! create_hard_link (nullptr, dst_dirfd, earlier_file,
+ if (! create_hard_link (NULL, dst_dirfd, earlier_file,
dst_name, dst_dirfd, dst_relname,
true, x->verbose, dereference))
goto un_backup;
@@ -2386,7 +2381,7 @@ skip:
if (x->move_mode)
printf (_("created directory %s\n"), quoteaf (dst_name));
else
- emit_verbose ("%s -> %s", src_name, dst_name, nullptr);
+ emit_verbose ("%s -> %s", src_name, dst_name, NULL);
}
}
else
@@ -2534,7 +2529,7 @@ skip:
{
char *src_link_val = areadlink_with_size (src_name, src_sb.st_size);
dest_is_symlink = true;
- if (src_link_val == nullptr)
+ if (src_link_val == NULL)
{
error (0, errno, _("cannot read symbolic link %s"),
quoteaf (src_name));
@@ -2760,7 +2755,7 @@ un_backup:
remove the entry associating the source dev/ino with the
destination file name, so we don't try to 'preserve' a link
to a file we didn't create. */
- if (earlier_file == nullptr)
+ if (earlier_file == NULL)
forget_created (src_sb.st_ino, src_sb.st_dev);
if (dst_backup)
@@ -2821,7 +2816,7 @@ copy (char const *src_name, char const *dst_name,
bool first_dir_created_per_command_line_arg = false;
return copy_internal (src_name, dst_name, dst_dirfd, dst_relname,
- nonexistent_dst, nullptr, nullptr,
+ nonexistent_dst, NULL, NULL,
options, true,
&first_dir_created_per_command_line_arg,
copy_into_self, rename_succeeded);
diff --git a/src/copy.h b/src/copy.h
index 1f2b2bb4d..0f3616c21 100644
--- a/src/copy.h
+++ b/src/copy.h
@@ -1,5 +1,5 @@
/* core functions for copying files and directories
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -294,7 +294,7 @@ struct cp_options
rm -rf a b c; mkdir a b c; touch a/f b/f; mv a/f b/f c
For now, it protects only regular files when copying (i.e., not renaming).
When renaming, it protects all non-directories.
- Use dest_info_init to initialize it, or set it to nullptr to disable
+ Use dest_info_init to initialize it, or set it to NULL to disable
this feature. */
Hash_table *dest_info;
diff --git a/src/coreutils-arch.c b/src/coreutils-arch.c
index 855e53fd4..e053e285f 100644
--- a/src/coreutils-arch.c
+++ b/src/coreutils-arch.c
@@ -1,5 +1,5 @@
/* arch -- wrapper to uname with the right uname_mode.
- Copyright (C) 2014-2025 Free Software Foundation, Inc.
+ Copyright (C) 2014-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/coreutils-chgrp.c b/src/coreutils-chgrp.c
index b6f010190..041ac8156 100644
--- a/src/coreutils-chgrp.c
+++ b/src/coreutils-chgrp.c
@@ -1,5 +1,5 @@
/* chgrp -- wrapper to uname with the right chown_mode.
- Copyright (C) 2023-2025 Free Software Foundation, Inc.
+ Copyright (C) 2023-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/coreutils-dir.c b/src/coreutils-dir.c
index 7df2f5488..eda00d412 100644
--- a/src/coreutils-dir.c
+++ b/src/coreutils-dir.c
@@ -1,5 +1,5 @@
/* dir -- wrapper to ls with the right ls_mode.
- Copyright (C) 2014-2025 Free Software Foundation, Inc.
+ Copyright (C) 2014-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/coreutils-md5sum.c b/src/coreutils-md5sum.c
new file mode 100644
index 000000000..12acc5e6b
--- /dev/null
+++ b/src/coreutils-md5sum.c
@@ -0,0 +1,31 @@
+/* md5sum -- wrapper to cksum with the right algorithm.
+ Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include "system.h"
+#include "cksum.h"
+
+/* Ensure that the main for cksum is declared even if the tool is not built
+ in this single-binary. */
+int single_binary_main_cksum (int argc, char **argv);
+int single_binary_main_md5sum (int argc, char **argv);
+
+int
+single_binary_main_md5sum (int argc, char **argv)
+{
+ cksum_algorithm = md5;
+ return single_binary_main_cksum (argc, argv);
+}
diff --git a/src/coreutils-sha1sum.c b/src/coreutils-sha1sum.c
new file mode 100644
index 000000000..3066cd8ed
--- /dev/null
+++ b/src/coreutils-sha1sum.c
@@ -0,0 +1,31 @@
+/* sha1sum -- wrapper to cksum with the right algorithm.
+ Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include "system.h"
+#include "cksum.h"
+
+/* Ensure that the main for cksum is declared even if the tool is not built
+ in this single-binary. */
+int single_binary_main_cksum (int argc, char **argv);
+int single_binary_main_sha1sum (int argc, char **argv);
+
+int
+single_binary_main_sha1sum (int argc, char **argv)
+{
+ cksum_algorithm = sha1;
+ return single_binary_main_cksum (argc, argv);
+}
diff --git a/src/coreutils-sha224sum.c b/src/coreutils-sha224sum.c
new file mode 100644
index 000000000..3d59308de
--- /dev/null
+++ b/src/coreutils-sha224sum.c
@@ -0,0 +1,31 @@
+/* sha224sum -- wrapper to cksum with the right algorithm.
+ Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include "system.h"
+#include "cksum.h"
+
+/* Ensure that the main for cksum is declared even if the tool is not built
+ in this single-binary. */
+int single_binary_main_cksum (int argc, char **argv);
+int single_binary_main_sha224sum (int argc, char **argv);
+
+int
+single_binary_main_sha224sum (int argc, char **argv)
+{
+ cksum_algorithm = sha224;
+ return single_binary_main_cksum (argc, argv);
+}
diff --git a/src/coreutils-sha256sum.c b/src/coreutils-sha256sum.c
new file mode 100644
index 000000000..03a2dd86f
--- /dev/null
+++ b/src/coreutils-sha256sum.c
@@ -0,0 +1,31 @@
+/* sha256sum -- wrapper to cksum with the right algorithm.
+ Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include "system.h"
+#include "cksum.h"
+
+/* Ensure that the main for cksum is declared even if the tool is not built
+ in this single-binary. */
+int single_binary_main_cksum (int argc, char **argv);
+int single_binary_main_sha256sum (int argc, char **argv);
+
+int
+single_binary_main_sha256sum (int argc, char **argv)
+{
+ cksum_algorithm = sha256;
+ return single_binary_main_cksum (argc, argv);
+}
diff --git a/src/coreutils-sha384sum.c b/src/coreutils-sha384sum.c
new file mode 100644
index 000000000..2ad79f237
--- /dev/null
+++ b/src/coreutils-sha384sum.c
@@ -0,0 +1,31 @@
+/* sha384sum -- wrapper to cksum with the right algorithm.
+ Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include "system.h"
+#include "cksum.h"
+
+/* Ensure that the main for cksum is declared even if the tool is not built
+ in this single-binary. */
+int single_binary_main_cksum (int argc, char **argv);
+int single_binary_main_sha384sum (int argc, char **argv);
+
+int
+single_binary_main_sha384sum (int argc, char **argv)
+{
+ cksum_algorithm = sha384;
+ return single_binary_main_cksum (argc, argv);
+}
diff --git a/src/coreutils-sha512sum.c b/src/coreutils-sha512sum.c
new file mode 100644
index 000000000..8bca6ceda
--- /dev/null
+++ b/src/coreutils-sha512sum.c
@@ -0,0 +1,31 @@
+/* sha512sum -- wrapper to cksum with the right algorithm.
+ Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include "system.h"
+#include "cksum.h"
+
+/* Ensure that the main for cksum is declared even if the tool is not built
+ in this single-binary. */
+int single_binary_main_cksum (int argc, char **argv);
+int single_binary_main_sha512sum (int argc, char **argv);
+
+int
+single_binary_main_sha512sum (int argc, char **argv)
+{
+ cksum_algorithm = sha512;
+ return single_binary_main_cksum (argc, argv);
+}
diff --git a/src/coreutils-vdir.c b/src/coreutils-vdir.c
index a472d7cc1..c9be132f5 100644
--- a/src/coreutils-vdir.c
+++ b/src/coreutils-vdir.c
@@ -1,5 +1,5 @@
/* vdir -- wrapper to ls with the right ls_mode.
- Copyright (C) 2014-2025 Free Software Foundation, Inc.
+ Copyright (C) 2014-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/coreutils.c b/src/coreutils.c
index 5f357cd68..180bfd9ce 100644
--- a/src/coreutils.c
+++ b/src/coreutils.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2014-2025 Free Software Foundation, Inc.
+/* Copyright (C) 2014-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -49,7 +49,7 @@ static struct option const long_options[] =
{
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
@@ -66,8 +66,8 @@ Usage: %s --coreutils-prog=PROGRAM_NAME [PARAMETERS]... \n"),
fputs (_("\
Execute the PROGRAM_NAME built-in program with the given PARAMETERS.\n\
\n"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
#ifdef SINGLE_BINARY
/* XXX: Ideally we'd like to present "install" here, not "ginstall". */
@@ -93,7 +93,7 @@ Use: '%s --coreutils-prog=PROGRAM_NAME --help' for individual program help.\n"),
static void
launch_program (char const *prog_name, int prog_argc, char **prog_argv)
{
- int (*prog_main) (int, char **) = nullptr;
+ int (*prog_main) (int, char **) = NULL;
/* Ensure that at least one parameter was passed. */
if (!prog_argc || !prog_argv || !prog_argv[0] || !prog_name)
@@ -148,10 +148,10 @@ main (int argc, char **argv)
path/to/coreutils --coreutils-prog=someprog someprog ...
The third argument is what the program will see as argv[0]. */
+ size_t nskip = 0;
if (argc >= 2)
{
- size_t nskip = 0;
- char *arg_name = nullptr;
+ char *arg_name = NULL;
/* If calling coreutils directly, the "script" name isn't passed.
Distinguish the two cases with a -shebang suffix. */
@@ -174,13 +174,19 @@ main (int argc, char **argv)
{
argv[nskip] = arg_name; /* XXX: Discards any specified path. */
launch_program (prog_name, argc - nskip, argv + nskip);
- error (EXIT_FAILURE, 0, _("unknown program %s"),
- quote (prog_name));
}
}
- /* No known program was selected. From here on, we behave like any other
- coreutils program. */
+ /* Only process options if calling multi-call binary directly,
+ otherwise `foo --version` would succeed. */
+ if (nskip || (prog_name && !str_endswith (prog_name, "coreutils")))
+ {
+ fprintf (stderr, _("%s: unknown program %s\n"),
+ PROGRAM_NAME, quote (prog_name));
+ exit (EXIT_FAILURE);
+ }
+
+ /* From here on, we behave like any other coreutils program. */
initialize_main (&argc, &argv);
set_program_name (argv[0]);
setlocale (LC_ALL, "");
@@ -188,7 +194,7 @@ main (int argc, char **argv)
textdomain (PACKAGE);
atexit (close_stdout);
- if ((optc = getopt_long (argc, argv, "", long_options, nullptr)) != -1)
+ if ((optc = getopt_long (argc, argv, "", long_options, NULL)) != -1)
switch (optc)
{
case_GETOPT_HELP_CHAR;
@@ -196,11 +202,5 @@ main (int argc, char **argv)
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
}
- /* Only print the error message when no options have been passed
- to coreutils. */
- if (optind == 1 && prog_name && !streq (prog_name, "coreutils"))
- error (0, 0, _("unknown program %s"),
- quote (prog_name));
-
usage (EXIT_FAILURE);
}
diff --git a/src/cp-hash.c b/src/cp-hash.c
index e42bd7e30..80d4b00b4 100644
--- a/src/cp-hash.c
+++ b/src/cp-hash.c
@@ -1,5 +1,5 @@
/* cp-hash.c -- file copying (hash search routines)
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -55,7 +55,7 @@ src_to_dest_hash (void const *x, size_t table_size)
/* Ignoring the device number here should be fine. */
/* The cast to uintmax_t prevents negative remainders
if st_ino is negative. */
- return (uintmax_t) p->st_ino % table_size;
+ return (uintmax_t) {p->st_ino} % table_size;
}
/* Compare two Src_to_dest entries.
@@ -86,7 +86,7 @@ forget_created (ino_t ino, dev_t dev)
probe.st_ino = ino;
probe.st_dev = dev;
- probe.name = nullptr;
+ probe.name = NULL;
ent = hash_remove (src_to_dest, &probe);
if (ent)
@@ -94,7 +94,7 @@ forget_created (ino_t ino, dev_t dev)
}
/* If INO/DEV correspond to an already-copied source file, return the
- name of the corresponding destination file. Otherwise, return nullptr. */
+ name of the corresponding destination file. Otherwise, return NULL. */
extern char *
src_to_dest_lookup (ino_t ino, dev_t dev)
@@ -104,12 +104,12 @@ src_to_dest_lookup (ino_t ino, dev_t dev)
ent.st_ino = ino;
ent.st_dev = dev;
e = hash_lookup (src_to_dest, &ent);
- return e ? e->name : nullptr;
+ return e ? e->name : NULL;
}
/* Add file NAME, copied from inode number INO and device number DEV,
to the list of files we have copied.
- Return nullptr if inserted, otherwise a non-null pointer. */
+ Return NULL if inserted, otherwise a non-null pointer. */
extern char *
remember_copied (char const *name, ino_t ino, dev_t dev)
@@ -123,7 +123,7 @@ remember_copied (char const *name, ino_t ino, dev_t dev)
ent->st_dev = dev;
ent_from_table = hash_insert (src_to_dest, ent);
- if (ent_from_table == nullptr)
+ if (ent_from_table == NULL)
{
/* Insertion failed due to lack of memory. */
xalloc_die ();
@@ -135,21 +135,21 @@ remember_copied (char const *name, ino_t ino, dev_t dev)
if (ent_from_table != ent)
{
src_to_dest_free (ent);
- return (char *) ent_from_table->name;
+ return ent_from_table->name;
}
/* New key; insertion succeeded. */
- return nullptr;
+ return NULL;
}
/* Initialize the hash table. */
extern void
hash_init (void)
{
- src_to_dest = hash_initialize (INITIAL_TABLE_SIZE, nullptr,
+ src_to_dest = hash_initialize (INITIAL_TABLE_SIZE, NULL,
src_to_dest_hash,
src_to_dest_compare,
src_to_dest_free);
- if (src_to_dest == nullptr)
+ if (src_to_dest == NULL)
xalloc_die ();
}
diff --git a/src/cp.c b/src/cp.c
index 317d667ce..e17484b5d 100644
--- a/src/cp.c
+++ b/src/cp.c
@@ -1,5 +1,5 @@
/* cp.c -- file copying (main routines)
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -84,7 +84,7 @@ static bool remove_trailing_slashes;
static char const *const sparse_type_string[] =
{
- "never", "auto", "always", nullptr
+ "never", "auto", "always", NULL
};
static enum Sparse_type const sparse_type[] =
{
@@ -94,7 +94,7 @@ ARGMATCH_VERIFY (sparse_type_string, sparse_type);
static char const *const reflink_type_string[] =
{
- "auto", "always", "never", nullptr
+ "auto", "always", "never", NULL
};
static enum Reflink_type const reflink_type[] =
{
@@ -104,7 +104,7 @@ ARGMATCH_VERIFY (reflink_type_string, reflink_type);
static char const *const update_type_string[] =
{
- "all", "none", "none-fail", "older", nullptr
+ "all", "none", "none-fail", "older", NULL
};
static enum Update_type const update_type[] =
{
@@ -114,40 +114,40 @@ ARGMATCH_VERIFY (update_type_string, update_type);
static struct option const long_opts[] =
{
- {"archive", no_argument, nullptr, 'a'},
- {"attributes-only", no_argument, nullptr, ATTRIBUTES_ONLY_OPTION},
- {"backup", optional_argument, nullptr, 'b'},
- {"copy-contents", no_argument, nullptr, COPY_CONTENTS_OPTION},
- {"debug", no_argument, nullptr, DEBUG_OPTION},
- {"dereference", no_argument, nullptr, 'L'},
- {"force", no_argument, nullptr, 'f'},
- {"interactive", no_argument, nullptr, 'i'},
- {"link", no_argument, nullptr, 'l'},
- {"no-clobber", no_argument, nullptr, 'n'}, /* Deprecated. */
- {"no-dereference", no_argument, nullptr, 'P'},
- {"no-preserve", required_argument, nullptr, NO_PRESERVE_ATTRIBUTES_OPTION},
- {"no-target-directory", no_argument, nullptr, 'T'},
- {"one-file-system", no_argument, nullptr, 'x'},
- {"parents", no_argument, nullptr, PARENTS_OPTION},
- {"path", no_argument, nullptr, PARENTS_OPTION}, /* Deprecated. */
- {"preserve", optional_argument, nullptr, PRESERVE_ATTRIBUTES_OPTION},
- {"recursive", no_argument, nullptr, 'R'},
- {"remove-destination", no_argument, nullptr, UNLINK_DEST_BEFORE_OPENING},
- {"sparse", required_argument, nullptr, SPARSE_OPTION},
- {"reflink", optional_argument, nullptr, REFLINK_OPTION},
- {"strip-trailing-slashes", no_argument, nullptr,
+ {"archive", no_argument, NULL, 'a'},
+ {"attributes-only", no_argument, NULL, ATTRIBUTES_ONLY_OPTION},
+ {"backup", optional_argument, NULL, 'b'},
+ {"copy-contents", no_argument, NULL, COPY_CONTENTS_OPTION},
+ {"debug", no_argument, NULL, DEBUG_OPTION},
+ {"dereference", no_argument, NULL, 'L'},
+ {"force", no_argument, NULL, 'f'},
+ {"interactive", no_argument, NULL, 'i'},
+ {"link", no_argument, NULL, 'l'},
+ {"no-clobber", no_argument, NULL, 'n'}, /* Deprecated. */
+ {"no-dereference", no_argument, NULL, 'P'},
+ {"no-preserve", required_argument, NULL, NO_PRESERVE_ATTRIBUTES_OPTION},
+ {"no-target-directory", no_argument, NULL, 'T'},
+ {"one-file-system", no_argument, NULL, 'x'},
+ {"parents", no_argument, NULL, PARENTS_OPTION},
+ {"path", no_argument, NULL, PARENTS_OPTION}, /* Deprecated. */
+ {"preserve", optional_argument, NULL, PRESERVE_ATTRIBUTES_OPTION},
+ {"recursive", no_argument, NULL, 'R'},
+ {"remove-destination", no_argument, NULL, UNLINK_DEST_BEFORE_OPENING},
+ {"sparse", required_argument, NULL, SPARSE_OPTION},
+ {"reflink", optional_argument, NULL, REFLINK_OPTION},
+ {"strip-trailing-slashes", no_argument, NULL,
STRIP_TRAILING_SLASHES_OPTION},
- {"suffix", required_argument, nullptr, 'S'},
- {"symbolic-link", no_argument, nullptr, 's'},
- {"target-directory", required_argument, nullptr, 't'},
- {"update", optional_argument, nullptr, 'u'},
- {"verbose", no_argument, nullptr, 'v'},
- {"keep-directory-symlink", no_argument, nullptr,
+ {"suffix", required_argument, NULL, 'S'},
+ {"symbolic-link", no_argument, NULL, 's'},
+ {"target-directory", required_argument, NULL, 't'},
+ {"update", optional_argument, NULL, 'u'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"keep-directory-symlink", no_argument, NULL,
KEEP_DIRECTORY_SYMLINK_OPTION},
{GETOPT_SELINUX_CONTEXT_OPTION_DECL},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -169,85 +169,148 @@ Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n\
emit_mandatory_arg_note ();
- fputs (_("\
- -a, --archive same as -dR --preserve=all\n\
- --attributes-only don't copy the file data, just the attributes\n\
- --backup[=CONTROL] make a backup of each existing destination file\
-\n\
- -b like --backup but does not accept an argument\n\
- --copy-contents copy contents of special files when recursive\n\
- -d same as --no-dereference --preserve=links\n\
-"), stdout);
- fputs (_("\
- --debug explain how a file is copied. Implies -v\n\
-"), stdout);
- fputs (_("\
- -f, --force if an existing destination file cannot be\n\
- opened, remove it and try again (this option\n\
- is ignored when the -n option is also used)\n\
- -i, --interactive prompt before overwrite (overrides a previous -n\
-\n\
- option)\n\
- -H follow command-line symbolic links in SOURCE\n\
-"), stdout);
- fputs (_("\
- -l, --link hard link files instead of copying\n\
- -L, --dereference always follow symbolic links in SOURCE\n\
-"), stdout);
- fputs (_("\
- -n, --no-clobber (deprecated) silently skip existing files.\n\
- See also --update\n\
-"), stdout);
- fputs (_("\
- -P, --no-dereference never follow symbolic links in SOURCE\n\
-"), stdout);
- fputs (_("\
- -p same as --preserve=mode,ownership,timestamps\n\
- --preserve[=ATTR_LIST] preserve the specified attributes\n\
-"), stdout);
- fputs (_("\
- --no-preserve=ATTR_LIST don't preserve the specified attributes\n\
- --parents use full source file name under DIRECTORY\n\
-"), stdout);
- fputs (_("\
- -R, -r, --recursive copy directories recursively\n\
- --reflink[=WHEN] control clone/CoW copies. See below\n\
- --remove-destination remove each existing destination file before\n\
- attempting to open it (contrast with --force)\
-\n"), stdout);
- fputs (_("\
- --sparse=WHEN control creation of sparse files. See below\n\
- --strip-trailing-slashes remove any trailing slashes from each SOURCE\n\
- argument\n\
-"), stdout);
- fputs (_("\
- -s, --symbolic-link make symbolic links instead of copying\n\
- -S, --suffix=SUFFIX override the usual backup suffix\n\
- -t, --target-directory=DIRECTORY copy all SOURCE arguments into DIRECTORY\n\
- -T, --no-target-directory treat DEST as a normal file\n\
-"), stdout);
- fputs (_("\
- --update[=UPDATE] control which existing files are updated;\n\
- UPDATE={all,none,none-fail,older(default)}\n\
- -u equivalent to --update[=older]. See below\n\
-"), stdout);
- fputs (_("\
- -v, --verbose explain what is being done\n\
-"), stdout);
- fputs (_("\
- --keep-directory-symlink follow existing symlinks to directories\n\
-"), stdout);
- fputs (_("\
- -x, --one-file-system stay on this file system\n\
-"), stdout);
- fputs (_("\
- -Z set SELinux security context of destination\n\
- file to default type\n\
- --context[=CTX] like -Z, or if CTX is specified then set the\n\
- SELinux or SMACK security context to CTX\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -a, --archive\n\
+ same as -dR --preserve=all\n\
+"));
+ oputs (_("\
+ --attributes-only\n\
+ don't copy the file data, just the attributes\n\
+"));
+ oputs (_("\
+ --backup[=CONTROL]\n\
+ make a backup of each existing destination file\n\
+"));
+ oputs (_("\
+ -b\n\
+ like --backup but does not accept an argument\n\
+"));
+ oputs (_("\
+ --copy-contents\n\
+ copy contents of special files when recursive\n\
+"));
+ oputs (_("\
+ -d\n\
+ same as --no-dereference --preserve=links\n\
+"));
+ oputs (_("\
+ --debug\n\
+ explain how a file is copied. Implies -v\n\
+"));
+ oputs (_("\
+ -f, --force\n\
+ if an existing destination file cannot be opened, remove it and try\n\
+ again (this option is ignored when the -n option is also used)\n\
+"));
+ oputs (_("\
+ -i, --interactive\n\
+ prompt before overwrite (overrides a previous -n option)\n\
+"));
+ oputs (_("\
+ -H\n\
+ follow command-line symbolic links in SOURCE\n\
+"));
+ oputs (_("\
+ -L, --dereference\n\
+ always follow symbolic links in SOURCE\n\
+"));
+ oputs (_("\
+ -P, --no-dereference\n\
+ never follow symbolic links in SOURCE\n\
+"));
+ oputs (_("\
+ --keep-directory-symlink\n\
+ follow existing symlinks to directories\n\
+"));
+ oputs (_("\
+ -l, --link\n\
+ hard link files instead of copying\n\
+"));
+ oputs (_("\
+ -n, --no-clobber\n\
+ (deprecated) silently skip existing files. See also --update\n\
+"));
+ oputs (_("\
+ -p\n\
+ same as --preserve=mode,ownership,timestamps\n\
+"));
+ oputs (_("\
+ --preserve[=ATTR_LIST]\n\
+ preserve the specified attributes\n\
+"));
+ oputs (_("\
+ --no-preserve=ATTR_LIST\n\
+ don't preserve the specified attributes\n\
+"));
+ oputs (_("\
+ --parents\n\
+ use full source file name under DIRECTORY\n\
+"));
+ oputs (_("\
+ -R, -r, --recursive\n\
+ copy directories recursively\n\
+"));
+ oputs (_("\
+ --reflink[=WHEN]\n\
+ control clone/CoW copies. See below\n\
+"));
+ oputs (_("\
+ --remove-destination\n\
+ remove each existing destination file before attempting to open it\n\
+ (contrast with --force)\n\
+"));
+ oputs (_("\
+ --sparse=WHEN\n\
+ control creation of sparse files. See below\n\
+"));
+ oputs (_("\
+ --strip-trailing-slashes\n\
+ remove any trailing slashes from each SOURCE argument\n\
+"));
+ oputs (_("\
+ -s, --symbolic-link\n\
+ make symbolic links instead of copying\n\
+"));
+ oputs (_("\
+ -S, --suffix=SUFFIX\n\
+ override the usual backup suffix\n\
+"));
+ oputs (_("\
+ -t, --target-directory=DIRECTORY\n\
+ copy all SOURCE arguments into DIRECTORY\n\
+"));
+ oputs (_("\
+ -T, --no-target-directory\n\
+ treat DEST as a normal file\n\
+"));
+ oputs (_("\
+ --update[=UPDATE]\n\
+ control which existing files are updated;\n\
+ UPDATE={all,none,none-fail,older(default)}\n\
+"));
+ oputs (_("\
+ -u\n\
+ equivalent to --update[=older]. See below\n\
+"));
+ oputs (_("\
+ -v, --verbose\n\
+ explain what is being done\n\
+"));
+ oputs (_("\
+ -x, --one-file-system\n\
+ stay on this file system\n\
+"));
+ oputs (_("\
+ -Z\n\
+ set SELinux security context of destination file to default type\n\
+"));
+ oputs (_("\
+ --context[=CTX]\n\
+ like -Z, or if CTX is specified then set the\n\
+ SELinux or SMACK security context to CTX\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
ATTR_LIST is a comma-separated list of attributes. Attributes are 'mode' for\n\
@@ -309,7 +372,6 @@ re_protect (char const *const_dst_name, char const *dst_src_name,
int dst_dirfd, char const *dst_relname,
struct dir_attr *attr_list, const struct cp_options *x)
{
- struct dir_attr *p;
char *dst_name; /* A copy of CONST_DST_NAME we can change. */
ASSIGN_STRDUPA (dst_name, const_dst_name);
@@ -321,7 +383,7 @@ re_protect (char const *const_dst_name, char const *dst_src_name,
/* Likewise, but with any leading '/'s skipped. */
char const *relname = dst_name + (dst_relname - const_dst_name);
- for (p = attr_list; p; p = p->next)
+ for (struct dir_attr *p = attr_list; p; p = p->next)
{
dst_name[p->slash_offset] = '\0';
@@ -331,10 +393,8 @@ re_protect (char const *const_dst_name, char const *dst_src_name,
if (x->preserve_timestamps)
{
- struct timespec timespec[2];
-
- timespec[0] = get_stat_atime (&p->st);
- timespec[1] = get_stat_mtime (&p->st);
+ struct timespec timespec[2] = { get_stat_atime (&p->st),
+ get_stat_mtime (&p->st) };
if (utimensat (dst_dirfd, relname, timespec, 0))
{
@@ -408,24 +468,24 @@ make_dir_parents_private (char const *const_dir, size_t src_offset,
struct dir_attr **attr_list, bool *new_dst,
const struct cp_options *x)
{
- struct stat stats;
- char *dir; /* A copy of CONST_DIR we can change. */
- char *src; /* Source name in DIR. */
- char *dst_dir; /* Leading directory of DIR. */
idx_t dirlen = dir_len (const_dir);
- *attr_list = nullptr;
+ *attr_list = NULL;
/* Succeed immediately if the parent of CONST_DIR must already exist,
as the target directory has already been checked. */
if (dirlen <= src_offset)
return true;
+ /* A copy of CONST_DIR we can change. */
+ char *dir;
ASSIGN_STRDUPA (dir, const_dir);
- src = dir + src_offset;
+ /* Source name in DIR. */
+ char *src = dir + src_offset;
- dst_dir = alloca (dirlen + 1);
+ /* Leading directory of DIR. */
+ char *dst_dir = alloca (dirlen + 1);
memcpy (dst_dir, dir, dirlen);
dst_dir[dirlen] = '\0';
char const *dst_reldir = dst_dir + src_offset;
@@ -434,13 +494,13 @@ make_dir_parents_private (char const *const_dir, size_t src_offset,
/* XXX: If all dirs are present at the destination,
no permissions or security contexts will be updated. */
+ struct stat stats;
if (fstatat (dst_dirfd, dst_reldir, &stats, 0) != 0)
{
/* A parent of CONST_DIR does not exist.
Make all missing intermediate directories. */
- char *slash;
+ char *slash = src;
- slash = src;
while (*slash == '/')
slash++;
dst_reldir = slash;
@@ -448,10 +508,9 @@ make_dir_parents_private (char const *const_dir, size_t src_offset,
while ((slash = strchr (slash, '/')))
{
struct dir_attr *new;
- bool missing_dir;
*slash = '\0';
- missing_dir = fstatat (dst_dirfd, dst_reldir, &stats, 0) != 0;
+ bool missing_dir = fstatat (dst_dirfd, dst_reldir, &stats, 0) != 0;
if (missing_dir || x->preserve_ownership || x->preserve_mode
|| x->preserve_timestamps)
@@ -487,33 +546,30 @@ make_dir_parents_private (char const *const_dir, size_t src_offset,
if (missing_dir)
{
- mode_t src_mode;
- mode_t omitted_permissions;
- mode_t mkdir_mode;
-
/* This component does not exist. We must set
*new_dst and new->st.st_mode inside this loop because,
for example, in the command 'cp --parents ../a/../b/c e_dir',
make_dir_parents_private creates only e_dir/../a if
./b already exists. */
*new_dst = true;
- src_mode = new->st.st_mode;
+ mode_t src_mode = new->st.st_mode;
/* If the ownership or special mode bits might change,
omit some permissions at first, so unauthorized users
cannot nip in before the file is ready. */
- omitted_permissions = (src_mode
- & (x->preserve_ownership
- ? S_IRWXG | S_IRWXO
- : x->preserve_mode
- ? S_IWGRP | S_IWOTH
- : 0));
+ mode_t omitted_permissions = (src_mode
+ & (x->preserve_ownership
+ ? S_IRWXG | S_IRWXO
+ : x->preserve_mode
+ ? S_IWGRP | S_IWOTH
+ : 0));
/* POSIX says mkdir's behavior is implementation-defined when
(src_mode & ~S_IRWXUGO) != 0. However, common practice is
to ask mkdir to copy all the CHMOD_MODE_BITS, letting mkdir
decide what to do with S_ISUID | S_ISGID | S_ISVTX. */
- mkdir_mode = x->explicit_no_preserve_mode ? S_IRWXUGO : src_mode;
+ mode_t mkdir_mode = (x->explicit_no_preserve_mode
+ ? S_IRWXUGO : src_mode);
mkdir_mode &= CHMOD_MODE_BITS & ~omitted_permissions;
if (mkdirat (dst_dirfd, dst_reldir, mkdir_mode) != 0)
{
@@ -523,7 +579,7 @@ make_dir_parents_private (char const *const_dir, size_t src_offset,
}
else
{
- if (verbose_fmt_string != nullptr)
+ if (verbose_fmt_string != NULL)
printf (verbose_fmt_string, src, dir);
}
@@ -614,10 +670,6 @@ static bool
do_copy (int n_files, char **file, char const *target_directory,
bool no_target_directory, struct cp_options *x)
{
- struct stat sb;
- bool new_dst = false;
- bool ok = true;
-
if (n_files <= !target_directory)
{
if (n_files <= 0)
@@ -628,8 +680,11 @@ do_copy (int n_files, char **file, char const *target_directory,
usage (EXIT_FAILURE);
}
+ struct stat sb;
sb.st_mode = 0;
int target_dirfd = AT_FDCWD;
+ bool new_dst = false;
+ bool ok = true;
if (no_target_directory)
{
if (target_directory)
@@ -732,7 +787,7 @@ do_copy (int n_files, char **file, char const *target_directory,
parent_exists =
(make_dir_parents_private
(dst_name, arg_in_concat - dst_name, target_dirfd,
- (x->verbose ? "%s -> %s\n" : nullptr),
+ (x->verbose ? "%s -> %s\n" : NULL),
&attr_list, &new_dst, x));
}
else
@@ -761,7 +816,7 @@ do_copy (int n_files, char **file, char const *target_directory,
bool copy_into_self;
ok &= copy (arg, dst_name, target_dirfd, dst_relname,
- new_dst, x, &copy_into_self, nullptr);
+ new_dst, x, &copy_into_self, NULL);
if (parents_option)
ok &= re_protect (dst_name, arg_in_concat, target_dirfd,
@@ -783,10 +838,6 @@ do_copy (int n_files, char **file, char const *target_directory,
}
else /* !target_directory */
{
- char const *source = file[0];
- char const *dest = file[1];
- bool unused;
-
if (parents_option)
{
error (0, 0,
@@ -800,6 +851,8 @@ do_copy (int n_files, char **file, char const *target_directory,
'cp --force --backup foo foo' to 'cp --force foo fooSUFFIX'
where SUFFIX is determined by any version control options used. */
+ char const *source = file[0];
+ char const *dest = file[1];
if (x->unlink_dest_after_failed_open
&& x->backup_type != no_backups
&& streq (source, dest)
@@ -819,7 +872,8 @@ do_copy (int n_files, char **file, char const *target_directory,
x = &x_tmp;
}
- ok = copy (source, dest, AT_FDCWD, dest, -new_dst, x, &unused, nullptr);
+ bool unused;
+ ok = copy (source, dest, AT_FDCWD, dest, -new_dst, x, &unused, NULL);
}
return ok;
@@ -847,7 +901,7 @@ cp_option_init (struct cp_options *x)
x->explicit_no_preserve_mode = false;
x->preserve_security_context = false; /* -a or --preserve=context. */
x->require_preserve_context = false; /* --preserve=context. */
- x->set_security_context = nullptr; /* -Z, set sys default context. */
+ x->set_security_context = NULL; /* -Z, set sys default context. */
x->preserve_xattr = false;
x->reduce_diagnostics = false;
x->require_preserve_xattr = false;
@@ -871,10 +925,10 @@ cp_option_init (struct cp_options *x)
in general one cannot do that safely, give the current semantics of
open's O_EXCL flag, (which POSIX doesn't even allow cp to use, btw).
But POSIX requires it. */
- x->open_dangling_dest_symlink = getenv ("POSIXLY_CORRECT") != nullptr;
+ x->open_dangling_dest_symlink = getenv ("POSIXLY_CORRECT") != NULL;
- x->dest_info = nullptr;
- x->src_info = nullptr;
+ x->dest_info = NULL;
+ x->src_info = NULL;
}
/* Given a string, ARG, containing a comma-separated list of arguments
@@ -902,7 +956,7 @@ decode_preserve_arg (char const *arg, struct cp_options *x, bool on_off)
static char const *const preserve_args[] =
{
"mode", "timestamps",
- "ownership", "links", "context", "xattr", "all", nullptr
+ "ownership", "links", "context", "xattr", "all", NULL
};
ARGMATCH_VERIFY (preserve_args, preserve_vals);
@@ -974,16 +1028,14 @@ decode_preserve_arg (char const *arg, struct cp_options *x, bool on_off)
int
main (int argc, char **argv)
{
- int c;
- bool ok;
bool make_backups = false;
- char const *backup_suffix = nullptr;
- char *version_control_string = nullptr;
+ char const *backup_suffix = NULL;
+ char *version_control_string = NULL;
struct cp_options x;
bool copy_contents = false;
- char *target_directory = nullptr;
+ char *target_directory = NULL;
bool no_target_directory = false;
- char const *scontext = nullptr;
+ char const *scontext = NULL;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -996,8 +1048,9 @@ main (int argc, char **argv)
selinux_enabled = (0 < is_selinux_enabled ());
cp_option_init (&x);
+ int c;
while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ",
- long_opts, nullptr))
+ long_opts, NULL))
!= -1)
{
switch (c)
@@ -1008,7 +1061,7 @@ main (int argc, char **argv)
break;
case REFLINK_OPTION:
- if (optarg == nullptr)
+ if (optarg == NULL)
x.reflink_mode = REFLINK_ALWAYS;
else
x.reflink_mode = XARGMATCH ("--reflink", optarg,
@@ -1086,7 +1139,7 @@ main (int argc, char **argv)
break;
case PRESERVE_ATTRIBUTES_OPTION:
- if (optarg == nullptr)
+ if (optarg == NULL)
{
/* Fall through to the case for 'p' below. */
}
@@ -1165,7 +1218,7 @@ main (int argc, char **argv)
else
{
x.set_security_context = selabel_open (SELABEL_CTX_FILE,
- nullptr, 0);
+ NULL, 0);
if (! x.set_security_context)
error (0, errno, _("warning: ignoring --context"));
}
@@ -1258,7 +1311,7 @@ main (int argc, char **argv)
/* FIXME: This handles new files. But what about existing files?
I.e., if updating a tree, new files would have the specified context,
but shouldn't existing files be updated for consistency like this?
- if (scontext && !restorecon (nullptr, dst_path, 0))
+ if (scontext && !restorecon (NULL, dst_path, 0))
error (...);
*/
if (scontext && setfscreatecon (scontext) < 0)
@@ -1276,8 +1329,8 @@ main (int argc, char **argv)
hash_init ();
- ok = do_copy (argc - optind, argv + optind,
- target_directory, no_target_directory, &x);
+ bool ok = do_copy (argc - optind, argv + optind,
+ target_directory, no_target_directory, &x);
main_exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
}
diff --git a/src/csplit.c b/src/csplit.c
index f3fccfcd5..d453afbbf 100644
--- a/src/csplit.c
+++ b/src/csplit.c
@@ -1,5 +1,5 @@
/* csplit - split a file into sections determined by context lines
- Copyright (C) 1991-2025 Free Software Foundation, Inc.
+ Copyright (C) 1991-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,7 +19,6 @@
#include <config.h>
-#include <ctype.h>
#include <getopt.h>
#include <sys/types.h>
#include <signal.h>
@@ -33,6 +32,7 @@
#include "quote.h"
#include "safe-read.h"
#include "stdio--.h"
+#include "term-sig.h"
#include "xdectoint.h"
#include "xstrtol.h"
@@ -111,10 +111,10 @@ static void delete_all_files (bool);
static void save_line_to_file (const struct cstring *line);
/* Start of buffer list. */
-static struct buffer_record *head = nullptr;
+static struct buffer_record *head = NULL;
/* Partially read line. */
-static char *hold_area = nullptr;
+static char *hold_area = NULL;
/* Number of bytes in 'hold_area'. */
static idx_t hold_count = 0;
@@ -129,13 +129,13 @@ static intmax_t current_line = 0;
static bool have_read_eof = false;
/* Name of output files. */
-static char *volatile filename_space = nullptr;
+static char *volatile filename_space = NULL;
/* Prefix part of output file names. */
-static char const *volatile prefix = nullptr;
+static char const *volatile prefix = DEFAULT_PREFIX;
/* Suffix part of output file names. */
-static char *volatile suffix = nullptr;
+static char *volatile suffix = NULL;
/* Number of digits to use in output file names. */
static int volatile digits = 2;
@@ -147,10 +147,10 @@ static int volatile files_created = 0;
static intmax_t bytes_written;
/* Output file pointer. */
-static FILE *output_stream = nullptr;
+static FILE *output_stream = NULL;
/* Output file name. */
-static char *output_filename = nullptr;
+static char *output_filename = NULL;
/* Perhaps it would be cleaner to pass arg values instead of indexes. */
static char **global_argv;
@@ -159,7 +159,7 @@ static char **global_argv;
static bool suppress_count;
/* If true, remove output files on error. */
-static bool volatile remove_files;
+static bool volatile remove_files = true;
/* If true, remove all output files which have a zero length. */
static bool elide_empty_files;
@@ -186,17 +186,17 @@ enum
static struct option const longopts[] =
{
- {"digits", required_argument, nullptr, 'n'},
- {"quiet", no_argument, nullptr, 'q'},
- {"silent", no_argument, nullptr, 's'},
- {"keep-files", no_argument, nullptr, 'k'},
- {"elide-empty-files", no_argument, nullptr, 'z'},
- {"prefix", required_argument, nullptr, 'f'},
- {"suffix-format", required_argument, nullptr, 'b'},
- {"suppress-matched", no_argument, nullptr, SUPPRESS_MATCHED_OPTION},
+ {"digits", required_argument, NULL, 'n'},
+ {"quiet", no_argument, NULL, 'q'}, /* Deprecated. */
+ {"silent", no_argument, NULL, 's'},
+ {"keep-files", no_argument, NULL, 'k'},
+ {"elide-empty-files", no_argument, NULL, 'z'},
+ {"prefix", required_argument, NULL, 'f'},
+ {"suffix-format", required_argument, NULL, 'b'},
+ {"suppress-matched", no_argument, NULL, SUPPRESS_MATCHED_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* Optionally remove files created so far; then exit.
@@ -211,7 +211,7 @@ cleanup (void)
sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
delete_all_files (false);
- sigprocmask (SIG_SETMASK, &oldset, nullptr);
+ sigprocmask (SIG_SETMASK, &oldset, NULL);
}
static _Noreturn void
@@ -290,7 +290,7 @@ new_line_control (void)
{
struct line *p = xmalloc (sizeof *p);
- p->next = nullptr;
+ p->next = NULL;
clear_line_control (p);
return p;
@@ -305,7 +305,7 @@ keep_new_line (struct buffer_record *b, char *line_start, idx_t line_len)
struct line *l;
/* If there is no existing area to keep line info, get some. */
- if (b->line_start == nullptr)
+ if (b->line_start == NULL)
b->line_start = b->curr_line = new_line_control ();
/* If existing area for lines is full, get more. */
@@ -406,13 +406,13 @@ get_new_buffer (idx_t min_size)
{
struct buffer_record *new_buffer = xmalloc (sizeof *new_buffer);
new_buffer->bytes_alloc = 0;
- new_buffer->buffer = xpalloc (nullptr, &new_buffer->bytes_alloc, min_size,
+ new_buffer->buffer = xpalloc (NULL, &new_buffer->bytes_alloc, min_size,
-1, 1);
new_buffer->bytes_used = 0;
new_buffer->start_line = new_buffer->first_available = last_line_number + 1;
new_buffer->num_lines = 0;
- new_buffer->line_start = new_buffer->curr_line = nullptr;
- new_buffer->next = nullptr;
+ new_buffer->line_start = new_buffer->curr_line = NULL;
+ new_buffer->next = NULL;
return new_buffer;
}
@@ -423,15 +423,14 @@ get_new_buffer (idx_t min_size)
static void
save_buffer (struct buffer_record *buf)
{
- struct buffer_record *p;
-
- buf->next = nullptr;
+ buf->next = NULL;
buf->curr_line = buf->line_start;
- if (head == nullptr)
+ if (head == NULL)
head = buf;
else
{
+ struct buffer_record *p;
for (p = head; p->next; p = p->next)
/* Do nothing. */ ;
p->next = buf;
@@ -500,7 +499,7 @@ load_buffer (void)
static intmax_t
get_first_line_in_buffer (void)
{
- if (head == nullptr && !load_buffer ())
+ if (head == NULL && !load_buffer ())
return 0;
return head->first_available;
@@ -508,7 +507,7 @@ get_first_line_in_buffer (void)
/* Return a pointer to the logical first line in the buffer and make the
next line the logical first line.
- Return nullptr if there is no more input. */
+ Return NULL if there is no more input. */
static struct cstring *
remove_line (void)
@@ -516,7 +515,7 @@ remove_line (void)
/* If non-null, this is the buffer for which the previous call
returned the final line. So now, presuming that line has been
processed, we can free the buffer and reset this pointer. */
- static struct buffer_record *prev_buf = nullptr;
+ static struct buffer_record *prev_buf = NULL;
struct cstring *line; /* Return value. */
struct line *l; /* For convenience. */
@@ -524,11 +523,11 @@ remove_line (void)
if (prev_buf)
{
free_buffer (prev_buf);
- prev_buf = nullptr;
+ prev_buf = NULL;
}
- if (head == nullptr && !load_buffer ())
- return nullptr;
+ if (head == NULL && !load_buffer ())
+ return NULL;
if (current_line < head->first_available)
current_line = head->first_available;
@@ -544,7 +543,7 @@ remove_line (void)
{
/* Go on to the next line record. */
head->curr_line = l->next;
- if (head->curr_line == nullptr || head->curr_line->used == 0)
+ if (head->curr_line == NULL || head->curr_line->used == 0)
{
/* Go on to the next data block.
but first record the current one so we can free it
@@ -558,20 +557,18 @@ remove_line (void)
}
/* Search the buffers for line LINENUM, reading more input if necessary.
- Return a pointer to the line, or nullptr if it is not found in the file. */
+ Return a pointer to the line, or NULL if it is not found in the file. */
static struct cstring *
find_line (intmax_t linenum)
{
- struct buffer_record *b;
-
- if (head == nullptr && !load_buffer ())
- return nullptr;
+ if (head == NULL && !load_buffer ())
+ return NULL;
if (linenum < head->start_line)
- return nullptr;
+ return NULL;
- for (b = head;;)
+ for (struct buffer_record *b = head;;)
{
if (linenum < b->start_line + b->num_lines)
{
@@ -589,8 +586,8 @@ find_line (intmax_t linenum)
}
return &l->starts[offset];
}
- if (b->next == nullptr && !load_buffer ())
- return nullptr;
+ if (b->next == NULL && !load_buffer ())
+ return NULL;
b = b->next; /* Try the next data block. */
}
}
@@ -600,7 +597,7 @@ find_line (intmax_t linenum)
static bool
no_more_lines (void)
{
- return find_line (current_line + 1) == nullptr;
+ return find_line (current_line + 1) == NULL;
}
/* Open NAME as standard input. */
@@ -624,7 +621,6 @@ write_to_file (intmax_t last_line, bool ignore, int argnum)
struct cstring *line;
intmax_t first_line; /* First available input line. */
intmax_t lines; /* Number of lines to output. */
- intmax_t i;
first_line = get_first_line_in_buffer ();
@@ -637,10 +633,10 @@ write_to_file (intmax_t last_line, bool ignore, int argnum)
lines = last_line - first_line;
- for (i = 0; i < lines; i++)
+ for (intmax_t i = 0; i < lines; i++)
{
line = remove_line ();
- if (line == nullptr)
+ if (line == NULL)
{
error (0, 0, _("%s: line number out of range"),
quote (global_argv[argnum]));
@@ -658,7 +654,7 @@ dump_rest_of_file (void)
{
struct cstring *line;
- while ((line = remove_line ()) != nullptr)
+ while ((line = remove_line ()) != NULL)
save_line_to_file (line);
}
@@ -705,7 +701,7 @@ process_line_count (const struct control *p, intmax_t repetition)
while (linenum++ < last_line_to_save)
{
struct cstring *line = remove_line ();
- if (line == nullptr)
+ if (line == NULL)
handle_line_error (p, repetition);
save_line_to_file (line);
}
@@ -764,7 +760,7 @@ process_regexp (struct control *p, intmax_t repetition)
while (true)
{
line = find_line (++current_line);
- if (line == nullptr)
+ if (line == NULL)
{
if (p->repeat_forever)
{
@@ -782,7 +778,7 @@ process_regexp (struct control *p, intmax_t repetition)
if (line->str[line_len - 1] == '\n')
line_len--;
ret = re_search (&p->re_compiled, line->str, line_len,
- 0, line_len, nullptr);
+ 0, line_len, NULL);
if (ret == -2)
{
error (0, 0, _("error in regular expression search"));
@@ -804,7 +800,7 @@ process_regexp (struct control *p, intmax_t repetition)
while (true)
{
line = find_line (++current_line);
- if (line == nullptr)
+ if (line == NULL)
{
if (p->repeat_forever)
{
@@ -822,7 +818,7 @@ process_regexp (struct control *p, intmax_t repetition)
if (line->str[line_len - 1] == '\n')
line_len--;
ret = re_search (&p->re_compiled, line->str, line_len,
- 0, line_len, nullptr);
+ 0, line_len, NULL);
if (ret == -2)
{
error (0, 0, _("error in regular expression search"));
@@ -855,16 +851,15 @@ split_file (void)
{
for (idx_t i = 0; i < control_used; i++)
{
- intmax_t j;
if (controls[i].regexpr)
{
- for (j = 0; (controls[i].repeat_forever
+ for (intmax_t j = 0; (controls[i].repeat_forever
|| j <= controls[i].repeat); j++)
process_regexp (&controls[i], j);
}
else
{
- for (j = 0; (controls[i].repeat_forever
+ for (intmax_t j = 0; (controls[i].repeat_forever
|| j <= controls[i].repeat); j++)
process_line_count (&controls[i], j);
}
@@ -915,10 +910,10 @@ create_output_file (void)
sigset_t oldset;
sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
output_stream = fopen (output_filename, "w");
- fopen_ok = (output_stream != nullptr);
+ fopen_ok = (output_stream != NULL);
fopen_errno = errno;
files_created = nfiles + fopen_ok;
- sigprocmask (SIG_SETMASK, &oldset, nullptr);
+ sigprocmask (SIG_SETMASK, &oldset, NULL);
}
if (! fopen_ok)
@@ -959,13 +954,13 @@ close_output_file (void)
if (ferror (output_stream))
{
error (0, 0, _("write error for %s"), quoteaf (output_filename));
- output_stream = nullptr;
+ output_stream = NULL;
cleanup_fatal ();
}
if (fclose (output_stream) != 0)
{
error (0, errno, "%s", quotef (output_filename));
- output_stream = nullptr;
+ output_stream = NULL;
cleanup_fatal ();
}
if (bytes_written == 0 && elide_empty_files)
@@ -979,7 +974,7 @@ close_output_file (void)
unlink_ok = (unlink (output_filename) == 0);
unlink_errno = errno;
files_created--;
- sigprocmask (SIG_SETMASK, &oldset, nullptr);
+ sigprocmask (SIG_SETMASK, &oldset, NULL);
if (! unlink_ok && unlink_errno != ENOENT)
error (0, unlink_errno, "%s", quotef (output_filename));
@@ -989,7 +984,7 @@ close_output_file (void)
if (!suppress_count)
fprintf (stdout, "%jd\n", bytes_written);
}
- output_stream = nullptr;
+ output_stream = NULL;
}
}
@@ -1003,7 +998,7 @@ save_line_to_file (const struct cstring *line)
if (l != line->len)
{
error (0, errno, _("write error for %s"), quoteaf (output_filename));
- output_stream = nullptr;
+ output_stream = NULL;
cleanup_fatal ();
}
bytes_written += line->len;
@@ -1036,7 +1031,7 @@ new_control_record (void)
static void
check_for_offset (struct control *p, char const *str, char const *num)
{
- if (xstrtoimax (num, nullptr, 10, &p->offset, "") != LONGINT_OK)
+ if (xstrtoimax (num, NULL, 10, &p->offset, "") != LONGINT_OK)
error (EXIT_FAILURE, 0, _("%s: integer expected after delimiter"),
quote (str));
}
@@ -1062,7 +1057,7 @@ parse_repeat_count (int argnum, struct control *p, char *str)
else
{
uintmax_t val;
- if (xstrtoumax (str + 1, nullptr, 10, &val, "") != LONGINT_OK
+ if (xstrtoumax (str + 1, NULL, 10, &val, "") != LONGINT_OK
|| ckd_add (&p->repeat, val, 0))
{
error (EXIT_FAILURE, 0,
@@ -1090,7 +1085,7 @@ extract_regexp (int argnum, bool ignore, char const *str)
char const *err;
closing_delim = strrchr (str + 1, delim);
- if (closing_delim == nullptr)
+ if (closing_delim == NULL)
error (EXIT_FAILURE, 0,
_("%s: closing delimiter '%c' missing"), str, delim);
@@ -1100,10 +1095,10 @@ extract_regexp (int argnum, bool ignore, char const *str)
p->ignore = ignore;
p->regexpr = true;
- p->re_compiled.buffer = nullptr;
+ p->re_compiled.buffer = NULL;
p->re_compiled.allocated = 0;
p->re_compiled.fastmap = xmalloc (UCHAR_MAX + 1);
- p->re_compiled.translate = nullptr;
+ p->re_compiled.translate = NULL;
re_syntax_options =
RE_SYNTAX_POSIX_BASIC & ~RE_CONTEXT_INVALID_DUP & ~RE_NO_EMPTY_RANGES;
err = re_compile_pattern (str + 1, len, &p->re_compiled);
@@ -1140,7 +1135,7 @@ parse_patterns (int argc, int start, char **argv)
p->argnum = i;
uintmax_t val;
- if (xstrtoumax (argv[i], nullptr, 10, &val, "") != LONGINT_OK
+ if (xstrtoumax (argv[i], NULL, 10, &val, "") != LONGINT_OK
|| INTMAX_MAX < val)
error (EXIT_FAILURE, 0, _("%s: invalid pattern"), quote (argv[i]));
if (val == 0)
@@ -1277,7 +1272,7 @@ max_out (char *format)
error (EXIT_FAILURE, 0,
_("missing %% conversion specification in suffix"));
- int maxlen = snprintf (nullptr, 0, format, INT_MAX);
+ int maxlen = snprintf (NULL, 0, format, INT_MAX);
if (! (0 <= maxlen && maxlen <= IDX_MAX))
xalloc_die ();
return maxlen;
@@ -1297,14 +1292,8 @@ main (int argc, char **argv)
atexit (close_stdout);
global_argv = argv;
- controls = nullptr;
- control_used = 0;
- suppress_count = false;
- remove_files = true;
- suppress_matched = false;
- prefix = DEFAULT_PREFIX;
-
- while ((optc = getopt_long (argc, argv, "f:b:kn:sqz", longopts, nullptr))
+
+ while ((optc = getopt_long (argc, argv, "f:b:kn:sqz", longopts, NULL))
!= -1)
switch (optc)
{
@@ -1370,46 +1359,25 @@ main (int argc, char **argv)
parse_patterns (argc, optind, argv);
{
- int i;
- static int const sig[] =
- {
- /* The usual suspects. */
- SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM,
-#ifdef SIGPOLL
- SIGPOLL,
-#endif
-#ifdef SIGPROF
- SIGPROF,
-#endif
-#ifdef SIGVTALRM
- SIGVTALRM,
-#endif
-#ifdef SIGXCPU
- SIGXCPU,
-#endif
-#ifdef SIGXFSZ
- SIGXFSZ,
-#endif
- };
- enum { nsigs = countof (sig) };
+ enum { nsigs = countof (term_sig) };
struct sigaction act;
sigemptyset (&caught_signals);
- for (i = 0; i < nsigs; i++)
+ for (int i = 0; i < nsigs; i++)
{
- sigaction (sig[i], nullptr, &act);
+ sigaction (term_sig[i], NULL, &act);
if (act.sa_handler != SIG_IGN)
- sigaddset (&caught_signals, sig[i]);
+ sigaddset (&caught_signals, term_sig[i]);
}
act.sa_handler = interrupt_handler;
act.sa_mask = caught_signals;
act.sa_flags = 0;
- for (i = 0; i < nsigs; i++)
- if (sigismember (&caught_signals, sig[i]))
- sigaction (sig[i], &act, nullptr);
+ for (int i = 0; i < nsigs; i++)
+ if (sigismember (&caught_signals, term_sig[i]))
+ sigaction (term_sig[i], &act, NULL);
}
split_file ();
@@ -1445,21 +1413,36 @@ Read standard input if FILE is -\n\
emit_mandatory_arg_note ();
- fputs (_("\
- -b, --suffix-format=FORMAT use sprintf FORMAT instead of %02d\n\
- -f, --prefix=PREFIX use PREFIX instead of 'xx'\n\
- -k, --keep-files do not remove output files on errors\n\
-"), stdout);
- fputs (_("\
- --suppress-matched suppress the lines matching PATTERN\n\
-"), stdout);
- fputs (_("\
- -n, --digits=DIGITS use specified number of digits instead of 2\n\
- -s, --quiet, --silent do not print counts of output file sizes\n\
- -z, --elide-empty-files suppress empty output files\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -b, --suffix-format=FORMAT\n\
+ use sprintf FORMAT instead of %02d\n\
+"));
+ oputs (_("\
+ -f, --prefix=PREFIX\n\
+ use PREFIX instead of 'xx'\n\
+"));
+ oputs (_("\
+ -k, --keep-files\n\
+ do not remove output files on errors\n\
+"));
+ oputs (_("\
+ --suppress-matched\n\
+ suppress the lines matching PATTERN\n\
+"));
+ oputs (_("\
+ -n, --digits=DIGITS\n\
+ use specified number of digits instead of 2\n\
+"));
+ oputs (_("\
+ -s, --quiet, --silent\n\
+ do not print counts of output file sizes\n\
+"));
+ oputs (_("\
+ -z, --elide-empty-files\n\
+ suppress empty output files\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
Each PATTERN may be:\n\
diff --git a/src/cut.c b/src/cut.c
index f0effb9eb..3ec406586 100644
--- a/src/cut.c
+++ b/src/cut.c
@@ -1,5 +1,5 @@
/* cut - remove parts of lines of files
- Copyright (C) 1997-2025 Free Software Foundation, Inc.
+ Copyright (C) 1997-2026 Free Software Foundation, Inc.
Copyright (C) 1984 David M. Ihnat
This program is free software: you can redistribute it and/or modify
@@ -29,9 +29,13 @@
#include <sys/types.h>
#include "system.h"
+#include "argmatch.h"
#include "assure.h"
+#include "c-ctype.h"
#include "fadvise.h"
-#include "getndelim2.h"
+#include "ioblksize.h"
+#include "mbbuf.h"
+#include "memchr2.h"
#include "set-fields.h"
@@ -51,10 +55,13 @@
} \
while (0)
+/* Above this function call overhead becomes less of a concern.
+ At this and below we avoid fwrite(), memchr() etc. */
+#define SMALL_BYTE_THRESHOLD 3
-/* Pointer inside RP. When checking if a byte or field is selected
+/* Pointer inside RP. When checking if a -b,-c,-f is selected
by a finite range, we check if it is between CURRENT_RP.LO
- and CURRENT_RP.HI. If the byte or field index is greater than
+ and CURRENT_RP.HI. If the index is greater than
CURRENT_RP.HI then we make CURRENT_RP to point to the next range pair. */
static struct field_range_pair *current_rp;
@@ -68,7 +75,7 @@ static struct field_range_pair *current_rp;
static char *field_1_buffer;
/* The number of bytes allocated for FIELD_1_BUFFER. */
-static size_t field_1_bufsize;
+static idx_t field_1_bufsize;
/* If true, do not output lines containing no delimiter characters.
Otherwise, all such lines are printed. This option is valid only
@@ -79,8 +86,11 @@ static bool suppress_non_delimited;
those that were specified. */
static bool complement;
-/* The delimiter character for field mode. */
-static unsigned char delim;
+/* The delimiter character for multibyte field mode. */
+static mcel_t delim_mcel;
+
+/* The delimiter bytes. */
+static char delim_bytes[MCEL_LEN_MAX];
/* The delimiter for each line/record. */
static unsigned char line_delim = '\n';
@@ -93,32 +103,71 @@ static size_t output_delimiter_length;
static char *output_delimiter_string;
/* The output delimiter string contents, if the default. */
-static char output_delimiter_default[1];
+static char output_delimiter_default[MCEL_LEN_MAX];
/* True if we have ever read standard input. */
static bool have_read_stdin;
+/* If true, don't split multibyte characters in byte mode. */
+static bool no_split;
+
+/* If true, interpret each run of whitespace as one field delimiter. */
+static bool whitespace_delimited;
+
+/* If true, ignore leading and trailing whitespace in -w mode. */
+static bool trim_outer_whitespace;
+
+/* If true, default the output delimiter to a single space. */
+static bool space_output_delimiter_default;
+
+enum whitespace_option
+{
+ WHITESPACE_OPTION_TRIMMED
+};
+
+static char const *const whitespace_option_args[] =
+{
+ "trimmed", NULL
+};
+
+static enum whitespace_option const whitespace_option_types[] =
+{
+ WHITESPACE_OPTION_TRIMMED
+};
+
+ARGMATCH_VERIFY (whitespace_option_args, whitespace_option_types);
+
+/* Whether to cut bytes, characters, or fields. */
+static enum
+{
+ CUT_MODE_NONE,
+ CUT_MODE_BYTES,
+ CUT_MODE_CHARACTERS,
+ CUT_MODE_FIELDS
+} cut_mode = CUT_MODE_NONE;
+
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
{
- OUTPUT_DELIMITER_OPTION = CHAR_MAX + 1,
- COMPLEMENT_OPTION
+ COMPLEMENT_OPTION = CHAR_MAX + 1
};
static struct option const longopts[] =
{
- {"bytes", required_argument, nullptr, 'b'},
- {"characters", required_argument, nullptr, 'c'},
- {"fields", required_argument, nullptr, 'f'},
- {"delimiter", required_argument, nullptr, 'd'},
- {"only-delimited", no_argument, nullptr, 's'},
- {"output-delimiter", required_argument, nullptr, OUTPUT_DELIMITER_OPTION},
- {"complement", no_argument, nullptr, COMPLEMENT_OPTION},
- {"zero-terminated", no_argument, nullptr, 'z'},
+ {"bytes", required_argument, NULL, 'b'},
+ {"characters", required_argument, NULL, 'c'},
+ {"fields", required_argument, NULL, 'f'},
+ {"delimiter", required_argument, NULL, 'd'},
+ {"no-partial", no_argument, NULL, 'n'},
+ {"whitespace-delimited", optional_argument, NULL, 'w'},
+ {"only-delimited", no_argument, NULL, 's'},
+ {"output-delimiter", required_argument, NULL, 'O'},
+ {"complement", no_argument, NULL, COMPLEMENT_OPTION},
+ {"zero-terminated", no_argument, NULL, 'z'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -139,34 +188,58 @@ Print selected parts of lines from each FILE to standard output.\n\
emit_stdin_note ();
emit_mandatory_arg_note ();
- fputs (_("\
- -b, --bytes=LIST select only these bytes\n\
- -c, --characters=LIST select only these characters\n\
- -d, --delimiter=DELIM use DELIM instead of TAB for field delimiter\n\
-"), stdout);
- fputs (_("\
- -f, --fields=LIST select only these fields; also print any line\n\
- that contains no delimiter character, unless\n\
- the -s option is specified\n\
- -n (ignored)\n\
-"), stdout);
- fputs (_("\
- --complement complement the set of selected bytes, characters\n\
- or fields\n\
-"), stdout);
- fputs (_("\
- -s, --only-delimited do not print lines not containing delimiters\n\
- --output-delimiter=STRING use STRING as the output delimiter\n\
- the default is to use the input delimiter\n\
-"), stdout);
- fputs (_("\
- -z, --zero-terminated line delimiter is NUL, not newline\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -b, --bytes=LIST\n\
+ select only these byte positions\n\
+"));
+ oputs (_("\
+ -c, --characters=LIST\n\
+ select only these character positions\n\
+"));
+ oputs (_("\
+ --complement\n\
+ complement the set of selected bytes, characters or fields\n\
+"));
+ oputs (_("\
+ -d, --delimiter=DELIM\n\
+ use DELIM instead of TAB for field delimiter\n\
+"));
+ oputs (_("\
+ -f, --fields=LIST\n\
+ select only these fields; also print any line that contains\n\
+ no delimiter character, unless the -s option is specified\n\
+"));
+ oputs (_("\
+ -F LIST\n\
+ like -f, but also implies -w and -O ' '\n\
+"));
+ oputs (_("\
+ -n, --no-partial\n\
+ with -b, don't output partial multi-byte characters\n\
+"));
+ oputs (_("\
+ -O, --output-delimiter=STRING\n\
+ use STRING as the output delimiter;\n\
+ the default is to use the input delimiter\n\
+"));
+ oputs (_("\
+ -s, --only-delimited\n\
+ do not print lines not containing delimiters\n\
+"));
+ oputs (_("\
+ -w, --whitespace-delimited[=trimmed]\n\
+ use a run of blank characters as the field delimiter;\n\
+ with 'trimmed', ignore leading and trailing blanks\n\
+"));
+ oputs (_("\
+ -z, --zero-terminated\n\
+ line delimiter is NUL, not newline\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
-Use one, and only one of -b, -c or -f. Each LIST is made up of one\n\
+Use one, and only one of -b, -c, -f or -F. Each LIST is made up of one\n\
range, or many ranges separated by commas. Selected input is written\n\
in the same order that it is read, and is written exactly once.\n\
"), stdout);
@@ -211,226 +284,950 @@ is_range_start_index (uintmax_t k)
return k == current_rp->lo;
}
-/* Read from stream STREAM, printing to standard output any selected bytes. */
+static inline bool
+field_delim_is_line_delim (void)
+{
+ return delim_mcel.len == 1 && delim_bytes[0] == line_delim;
+}
+
+/* This is equivalent to but faster than calling c32issep directly.
+ It assumes all unibyte locales match c_isblank. */
+
+static bool
+mcel_isblank (mcel_t g)
+{
+ return (g.len == 1 && c_isblank (g.ch)) || (g.len > 1 && c32issep (g.ch));
+}
+
+/* Return TRUE if it's valid to do a simple byte search
+ for the delimiter bytes. */
+
+static inline bool
+bytesearch_field_delim_ok (void)
+{
+ unsigned char delim_0 = delim_bytes[0];
+
+ return (delim_mcel.len == 1
+ ? (MB_CUR_MAX <= 1
+ || (is_utf8_charset ()
+ ? (delim_0 < 0x80 || delim_0 > 0xF4) : delim_0 < 0x30))
+ : is_utf8_charset () && ! delim_mcel.err);
+}
+
+
+enum field_terminator
+{
+ FIELD_DATA,
+ FIELD_DELIMITER,
+ FIELD_LINE_DELIMITER,
+ FIELD_EOF
+};
+
+enum bytesearch_mode
+{
+ BYTESEARCH_FIELDS,
+ BYTESEARCH_LINE_ONLY
+};
+
+struct bytesearch_context
+{
+ enum bytesearch_mode mode;
+ bool blank_delimited;
+ bool at_eof;
+ char *line_end;
+ bool line_end_known;
+};
+
+static inline void
+bytesearch_context_reset (struct bytesearch_context *ctx)
+{
+ ctx->mode = BYTESEARCH_FIELDS;
+ ctx->line_end = NULL;
+ ctx->line_end_known = false;
+}
+
+struct mbfield_parser
+{
+ bool whitespace_delimited;
+ bool trim_outer_whitespace;
+ bool at_line_start;
+ bool have_saved;
+ mcel_t saved_g;
+};
+
+static inline mcel_t
+mbbuf_get_saved_char (mbbuf_t *mbbuf, bool *have_saved, mcel_t *saved_g)
+{
+ if (*have_saved)
+ {
+ *have_saved = false;
+ return *saved_g;
+ }
+ return mbbuf_get_char (mbbuf);
+}
+
+static inline enum field_terminator
+skip_whitespace_run (mbbuf_t *mbuf, struct mbfield_parser *parser,
+ bool *have_pending_line,
+ bool have_initial_whitespace)
+{
+ mcel_t g;
+
+ do
+ {
+ g = mbbuf_get_char (mbuf);
+ if (g.ch != MBBUF_EOF)
+ *have_pending_line = true;
+ }
+ while (g.ch != MBBUF_EOF && g.ch != line_delim && mcel_isblank (g));
+
+ bool trim_start = parser->trim_outer_whitespace && parser->at_line_start;
+
+ if (parser->trim_outer_whitespace
+ && (g.ch == MBBUF_EOF || g.ch == line_delim))
+ {
+ return g.ch == MBBUF_EOF ? FIELD_EOF : FIELD_LINE_DELIMITER;
+ }
+
+ parser->saved_g = g;
+ parser->have_saved = true;
+ return trim_start && !have_initial_whitespace ? FIELD_DATA : FIELD_DELIMITER;
+}
+
+/* Like fwrite, but avoid a function call for smaller amounts,
+ and exit immediately upon error. */
static void
-cut_bytes (FILE *stream)
+write_bytes (char const *buf, size_t n_bytes)
+{
+ if (n_bytes <= SMALL_BYTE_THRESHOLD)
+ {
+ for (size_t i = 0; i < n_bytes; i++)
+ if (putchar (buf[i]) < 0)
+ write_error ();
+ return;
+ }
+
+ if (fwrite (buf, sizeof (char), n_bytes, stdout) != n_bytes)
+ write_error ();
+}
+
+/* Like memcpy, but avoid a function call for smaller amounts. */
+
+static inline void
+copy_bytes (char *dst, char const *src, size_t n_bytes)
+{
+ if (n_bytes <= SMALL_BYTE_THRESHOLD)
+ {
+ for (size_t i = 0; i < n_bytes; i++)
+ dst[i] = src[i];
+ return;
+ }
+
+ memcpy (dst, src, n_bytes);
+}
+
+/* Like memchr, but avoid a function call for smaller amounts. */
+
+static inline void*
+search_bytes (char const *buf, int c, size_t n_bytes)
{
- uintmax_t byte_idx; /* Number of bytes in the line so far. */
- /* Whether to begin printing delimiters between ranges for the current line.
- Set after we've begun printing data corresponding to the first range. */
- bool print_delimiter;
+ if (n_bytes != 0 && to_uchar (buf[0]) == c)
+ return (char *) buf;
+ if (1 < n_bytes && to_uchar (buf[1]) == c)
+ return (char *) buf + 1;
- byte_idx = 0;
- print_delimiter = false;
+ return 2 < n_bytes ? (char *) memchr (buf + 2, c, n_bytes - 2) : NULL;
+}
+
+static inline void
+write_line_delim (void)
+{
+ if (putchar (line_delim) < 0)
+ write_error ();
+}
+
+static inline void
+reset_item_line (uintmax_t *item_idx, bool *print_delimiter)
+{
+ write_line_delim ();
+ *item_idx = 0;
+ *print_delimiter = false;
current_rp = frp;
+}
+
+static inline void
+write_pending_line_delim (uintmax_t item_idx)
+{
+ if (item_idx > 0)
+ write_line_delim ();
+}
+
+static inline void
+write_selected_item (bool *print_delimiter, bool range_start,
+ char const *buf, size_t n_bytes)
+{
+ if (output_delimiter_string != output_delimiter_default)
+ {
+ if (*print_delimiter && range_start)
+ write_bytes (output_delimiter_string, output_delimiter_length);
+ *print_delimiter = true;
+ }
+
+ write_bytes (buf, n_bytes);
+}
+
+static inline void
+append_field_1_chunk (char const *buf, idx_t len, idx_t *n_bytes)
+{
+ if (field_1_bufsize - *n_bytes < len)
+ {
+ field_1_buffer = xpalloc (field_1_buffer, &field_1_bufsize,
+ len, -1, sizeof *field_1_buffer);
+ }
+
+ copy_bytes (field_1_buffer + *n_bytes, buf, len);
+ *n_bytes += len;
+}
+
+static inline void
+append_field_1_bytes (mbbuf_t *mbbuf, mcel_t g, idx_t *n_bytes)
+{
+ append_field_1_chunk (mbbuf_char_offset (mbbuf, g), g.len, n_bytes);
+}
+
+static enum field_terminator
+scan_mb_blank_field (mbbuf_t *mbbuf, struct mbfield_parser *parser,
+ bool *have_pending_line, bool write_field,
+ idx_t *n_bytes)
+{
+ if (parser->trim_outer_whitespace && parser->at_line_start)
+ {
+ enum field_terminator terminator
+ = skip_whitespace_run (mbbuf, parser, have_pending_line, false);
+ if (terminator != FIELD_DATA)
+ return terminator;
+ }
+
+ parser->at_line_start = false;
+
+ while (true)
+ {
+ mcel_t g = mbbuf_get_saved_char (mbbuf, &parser->have_saved,
+ &parser->saved_g);
+ if (g.ch == MBBUF_EOF)
+ return FIELD_EOF;
+
+ *have_pending_line = true;
+
+ if (g.ch == line_delim)
+ return FIELD_LINE_DELIMITER;
+
+ if (mcel_isblank (g))
+ return skip_whitespace_run (mbbuf, parser, have_pending_line, true);
+
+ if (n_bytes)
+ append_field_1_bytes (mbbuf, g, n_bytes);
+ else if (write_field)
+ write_bytes (mbbuf_char_offset (mbbuf, g), g.len);
+ }
+}
+
+static enum field_terminator
+scan_mb_delim_field (mbbuf_t *mbbuf, bool *have_pending_line,
+ bool write_field, idx_t *n_bytes)
+{
while (true)
{
- int c; /* Each character from the file. */
+ mcel_t g = mbbuf_get_char (mbbuf);
+ if (g.ch == MBBUF_EOF)
+ return FIELD_EOF;
+
+ *have_pending_line = true;
+
+ if (g.ch == line_delim)
+ return FIELD_LINE_DELIMITER;
+
+ if (delim_mcel.err ? g.err == delim_mcel.err : mcel_eq (g, delim_mcel))
+ return FIELD_DELIMITER;
+
+ if (n_bytes)
+ append_field_1_bytes (mbbuf, g, n_bytes);
+ else if (write_field)
+ write_bytes (mbbuf_char_offset (mbbuf, g), g.len);
+ }
+}
+
+static inline enum field_terminator
+scan_mb_field (mbbuf_t *mbbuf, struct mbfield_parser *parser,
+ bool *have_pending_line, bool write_field, idx_t *n_bytes)
+{
+ return (parser->whitespace_delimited
+ ? scan_mb_blank_field (mbbuf, parser, have_pending_line,
+ write_field, n_bytes)
+ : scan_mb_delim_field (mbbuf, have_pending_line, write_field,
+ n_bytes));
+}
+
+/* Return a pointer to the next field delimiter in BUF, searching LEN bytes.
+ Return NULL if none is found. DELIM_BYTES must be a single byte or
+ represent a valid UTF-8 character. BUF can contain invalid/NUL bytes,
+ and must have room for a trailing NUL byte at BUF[LEN]. */
+
+ATTRIBUTE_PURE
+static char *
+find_field_delim (char *buf, size_t len)
+{
+ if (len < delim_mcel.len)
+ return NULL;
- c = getc (stream);
+#if ! __GLIBC__ /* Only S390 has optimized memmem on glibc-2.42 */
+ return memmem (buf, len, delim_bytes, delim_mcel.len);
+#else
+ if (delim_mcel.len == 1)
+ return memchr (buf, delim_bytes[0], len);
- if (c == line_delim)
+ char const *p = buf;
+ char const *end = buf + len;
+
+ char saved = buf[len];
+ buf[len] = '\0'; /* for strstr. */
+
+ while (p < end)
+ {
+ char const *nul = memchr (p, '\0', end - p);
+ if (!nul)
{
- if (putchar (c) < 0)
- write_error ();
- byte_idx = 0;
- print_delimiter = false;
- current_rp = frp;
+ char const *match = strstr (p, delim_bytes);
+ buf[len] = saved;
+ return (char *) match;
}
- else if (c == EOF)
- {
- if (byte_idx > 0)
- {
- if (putchar (line_delim) < 0)
- write_error ();
- }
- break;
- }
- else
+
+ if (p < nul)
{
- next_item (&byte_idx);
- if (print_kth (byte_idx))
+ char const *match = strstr (p, delim_bytes);
+ if (match)
{
- if (output_delimiter_string != output_delimiter_default)
- {
- if (print_delimiter && is_range_start_index (byte_idx))
- {
- if (fwrite (output_delimiter_string, sizeof (char),
- output_delimiter_length, stdout)
- != output_delimiter_length)
- write_error ();
- }
- print_delimiter = true;
- }
-
- if (putchar (c) < 0)
- write_error ();
+ buf[len] = saved;
+ return (char *) match;
}
}
+
+ p = nul + 1;
}
+
+ buf[len] = saved;
+ return NULL;
+#endif
}
-/* Read from stream STREAM, printing to standard output any selected fields. */
+/* Return the number of trailing bytes in BUF that could be the initial
+ bytes of a delimiter split across buffers. */
-static void
-cut_fields (FILE *stream)
+ATTRIBUTE_PURE
+static idx_t
+field_delim_overlap (char const *buf, idx_t len)
{
- int c; /* Each character from the file. */
- uintmax_t field_idx = 1;
- bool found_any_selected_field = false;
- bool buffer_first_field;
+ idx_t overlap = MIN (len, delim_mcel.len - 1);
+
+ while (0 < overlap)
+ {
+ if (memcmp (buf + len - overlap, delim_bytes, overlap) == 0)
+ return overlap;
+ overlap--;
+ }
+
+ return 0;
+}
+
+/* Byte search for line end or delimiter in BUF,
+ returning results in CTX. */
+
+static inline enum field_terminator
+find_field_terminator (char *buf, idx_t len,
+ struct bytesearch_context *ctx, char **terminator)
+{
+ if (ctx->mode == BYTESEARCH_LINE_ONLY)
+ {
+ if (!ctx->line_end_known)
+ {
+ ctx->line_end = memchr (buf, line_delim, len);
+ ctx->line_end_known = true;
+ }
+
+ *terminator = ctx->line_end;
+ return *terminator ? FIELD_LINE_DELIMITER : FIELD_DATA;
+ }
+
+ if (field_delim_is_line_delim ())
+ {
+ *terminator = memchr (buf, line_delim, len);
+ return *terminator ? FIELD_DELIMITER : FIELD_DATA;
+ }
+
+ if (!ctx->line_end_known)
+ {
+ ctx->line_end = memchr (buf, line_delim, len);
+ ctx->line_end_known = true;
+ }
+
+ idx_t field_len = ctx->line_end ? ctx->line_end - buf : len;
+
+ char *field_end = (ctx->blank_delimited
+ ? memchr2 (buf, ' ', '\t', field_len)
+ : find_field_delim (buf, field_len));
+
+ if (field_end)
+ {
+ *terminator = field_end;
+ return FIELD_DELIMITER;
+ }
+
+ *terminator = ctx->line_end;
+ return ctx->line_end ? FIELD_LINE_DELIMITER : FIELD_DATA;
+}
+
+/* Write the end-of-line delimiter if appropriate for the current line. */
+
+static inline void
+maybe_write_line_delim (bool found_any_selected_field, uintmax_t field_idx)
+{
+ if (found_any_selected_field
+ || !(suppress_non_delimited && field_idx == 1))
+ write_line_delim ();
+}
+
+/* Return TRUE if FIELD_IDX is selected,
+ and write the output delimiter if appropriate. */
+
+static inline bool
+begin_field_output (uintmax_t field_idx, bool buffer_first_field,
+ bool *found_any_selected_field)
+{
+ bool write_field = print_kth (field_idx);
+
+ if (write_field && ! (field_idx == 1 && buffer_first_field))
+ {
+ if (*found_any_selected_field)
+ write_bytes (output_delimiter_string, output_delimiter_length);
+ *found_any_selected_field = true;
+ }
+
+ return write_field;
+}
+
+static inline bool
+handle_field_1 (uintmax_t field_idx, bool buffer_first_field,
+ idx_t *field_1_n_bytes)
+{
+ bool field_1_selected = false;
+
+ if (field_idx == 1 && buffer_first_field)
+ {
+ if ((field_1_selected = print_kth (1)))
+ write_bytes (field_1_buffer, *field_1_n_bytes);
+ *field_1_n_bytes = 0;
+ }
+
+ return field_1_selected;
+}
+
+static inline void
+handle_field_delimiter (uintmax_t *field_idx, bool buffer_first_field,
+ idx_t *field_1_n_bytes,
+ bool *found_any_selected_field, bool *write_field,
+ bool blank_delimited, bool *skip_blank_run)
+{
+ if (handle_field_1 (*field_idx, buffer_first_field, field_1_n_bytes))
+ *found_any_selected_field = true;
+
+ next_item (field_idx);
+ *write_field = begin_field_output (*field_idx, buffer_first_field,
+ found_any_selected_field);
+ if (blank_delimited)
+ *skip_blank_run = true;
+}
+
+static inline bool
+field_selection_exhausted (uintmax_t field_idx)
+{
+ return !print_kth (field_idx) && current_rp->lo == UINTMAX_MAX;
+}
+
+static inline void
+finish_current_line (uintmax_t field_idx, bool buffer_first_field,
+ idx_t *field_1_n_bytes,
+ bool found_any_selected_field, bool line_terminated)
+{
+ if (field_idx == 1 && buffer_first_field)
+ {
+ if (!suppress_non_delimited
+ && (line_terminated || *field_1_n_bytes != 0))
+ {
+ write_bytes (field_1_buffer, *field_1_n_bytes);
+ write_line_delim ();
+ }
+ *field_1_n_bytes = 0;
+ }
+ else if (field_idx != 1 || found_any_selected_field)
+ maybe_write_line_delim (found_any_selected_field, field_idx);
+}
+
+static inline void
+sync_byte_selection (uintmax_t byte_idx)
+{
+ while (current_rp->hi <= byte_idx)
+ current_rp++;
+}
+static inline void
+reset_field_line (uintmax_t *field_idx, bool *found_any_selected_field,
+ bool *have_pending_line, struct mbfield_parser *parser)
+{
+ *field_idx = 1;
current_rp = frp;
+ *found_any_selected_field = false;
+ *have_pending_line = false;
+ parser->have_saved = false;
+ parser->at_line_start = true;
+}
- c = getc (stream);
- if (c == EOF)
- return;
+/* Read from STREAM using buffered block reads, printing selected bytes.
+ BYTE_IDX and PRINT_DELIMITER track the current line state and allow
+ callers to hand off mid-line. */
- ungetc (c, stream);
- c = 0;
+static void
+cut_bytes (FILE *stream)
+{
+ static char bytes_in[IO_BUFSIZE];
+ uintmax_t byte_idx = 0;
+ bool print_delimiter = false;
+ int fd = fileno (stream);
- /* To support the semantics of the -s flag, we may have to buffer
- all of the first field to determine whether it is 'delimited.'
- But that is unnecessary if all non-delimited lines must be printed
- and the first field has been selected, or if non-delimited lines
- must be suppressed and the first field has *not* been selected.
- That is because a non-delimited line has exactly one field. */
- buffer_first_field = (suppress_non_delimited ^ !print_kth (1));
+ current_rp = frp;
while (true)
{
- if (field_idx == 1 && buffer_first_field)
+ idx_t available = read (fd, bytes_in, sizeof bytes_in);
+ if (available <= 0)
{
- ssize_t len;
- size_t n_bytes;
-
- len = getndelim2 (&field_1_buffer, &field_1_bufsize, 0,
- GETNLINE_NO_LIMIT, delim, line_delim, stream);
- if (len < 0)
- {
- free (field_1_buffer);
- field_1_buffer = nullptr;
- if (ferror (stream) || feof (stream))
- break;
- xalloc_die ();
- }
+ if (available < 0)
+ fseterr (stream);
+ write_pending_line_delim (byte_idx);
+ break;
+ }
- n_bytes = len;
- affirm (n_bytes != 0);
+ idx_t processed = 0;
- c = 0;
+ while (processed < available)
+ {
+ char *line = bytes_in + processed;
+ idx_t to_process = available - processed;
+ char *line_end = search_bytes (line, line_delim, to_process);
+ char *end = line + (line_end ? line_end - line : to_process);
+ char *p = line;
- /* If the first field extends to the end of line (it is not
- delimited) and we are printing all non-delimited lines,
- print this one. */
- if (to_uchar (field_1_buffer[n_bytes - 1]) != delim)
+ while (0 < end - p)
{
- if (suppress_non_delimited)
+ sync_byte_selection (byte_idx);
+
+ if (byte_idx + 1 < current_rp->lo)
{
- /* Empty. */
+ idx_t skip = MIN (end - p, current_rp->lo - (byte_idx + 1));
+ p += skip;
+ byte_idx += skip;
}
else
{
- if (fwrite (field_1_buffer, sizeof (char), n_bytes, stdout)
- != n_bytes)
- write_error ();
- /* Make sure the output line is newline terminated. */
- if (field_1_buffer[n_bytes - 1] != line_delim)
- {
- if (putchar (line_delim) < 0)
- write_error ();
- }
- c = line_delim;
+ idx_t n = MIN (end - p, current_rp->hi - byte_idx);
+ write_selected_item (&print_delimiter,
+ is_range_start_index (byte_idx + 1),
+ p, n);
+ p += n;
+ byte_idx += n;
}
- continue;
}
- if (print_kth (1))
+ processed += end - line;
+ if (line_end)
{
- /* Print the field, but not the trailing delimiter. */
- if (fwrite (field_1_buffer, sizeof (char), n_bytes - 1, stdout)
- != n_bytes - 1)
- write_error ();
+ processed++;
+ reset_item_line (&byte_idx, &print_delimiter);
+ }
+ }
+ }
+}
+
+/* Read from STREAM, printing selected bytes without splitting
+ multibyte characters. */
+
+static void
+cut_characters_mode (FILE *stream, bool byte_mode)
+{
+ uintmax_t idx = 0;
+ bool print_delimiter = false;
+ static char bytes_in[IO_BUFSIZE];
+ mbbuf_t mbbuf;
+
+ current_rp = frp;
+ mbbuf_init (&mbbuf, bytes_in, sizeof bytes_in, stream);
- /* With -d$'\n' don't treat the last '\n' as a delimiter. */
- if (delim == line_delim)
+ while (true)
+ {
+ mcel_t g = mbbuf_get_char (&mbbuf);
+
+ if (g.ch == line_delim)
+ reset_item_line (&idx, &print_delimiter);
+ else if (g.ch == MBBUF_EOF)
+ {
+ write_pending_line_delim (idx);
+ break;
+ }
+ else if (byte_mode)
+ {
+ bool first_selected_is_range_start = false;
+ bool seen_selected = false;
+ bool suffix_selected = true;
+
+ for (idx_t i = 0; i < g.len; i++)
+ {
+ next_item (&idx);
+ if (print_kth (idx))
{
- int last_c = getc (stream);
- if (last_c != EOF)
+ if (!seen_selected)
{
- ungetc (last_c, stream);
- found_any_selected_field = true;
+ seen_selected = true;
+ first_selected_is_range_start
+ = is_range_start_index (idx);
}
}
- else
- {
- found_any_selected_field = true;
- }
+ else if (seen_selected)
+ suffix_selected = false;
}
- next_item (&field_idx);
+
+ if (seen_selected && suffix_selected)
+ write_selected_item (&print_delimiter,first_selected_is_range_start,
+ mbbuf_char_offset (&mbbuf, g), g.len);
+ }
+ else
+ {
+ next_item (&idx);
+ if (print_kth (idx))
+ write_selected_item (&print_delimiter,
+ is_range_start_index (idx),
+ mbbuf_char_offset (&mbbuf, g), g.len);
}
+ }
+}
+
+static void
+cut_bytes_no_split (FILE *stream)
+{
+ cut_characters_mode (stream, true);
+}
+
+static void
+cut_characters (FILE *stream)
+{
+ cut_characters_mode (stream, false);
+}
- int prev_c = c;
+/* Read from STREAM, printing to standard output any selected fields,
+ using a multibyte-aware field delimiter parser. */
- if (print_kth (field_idx))
+static void
+cut_fields_mb_any (FILE *stream, bool whitespace_mode)
+{
+ static char bytes_in[IO_BUFSIZE];
+ mbbuf_t mbbuf;
+ struct mbfield_parser parser =
+ {
+ .whitespace_delimited = whitespace_mode,
+ .trim_outer_whitespace = trim_outer_whitespace,
+ .at_line_start = true,
+ .saved_g = { .ch = MBBUF_EOF }
+ };
+ uintmax_t field_idx = 1;
+ bool found_any_selected_field = false;
+ bool buffer_first_field;
+ bool have_pending_line = false;
+
+ current_rp = frp;
+ mbbuf_init (&mbbuf, bytes_in, sizeof bytes_in, stream);
+
+ buffer_first_field = (suppress_non_delimited ^ !print_kth (1));
+
+ while (true)
+ {
+ if (field_idx == 1 && buffer_first_field)
{
- if (found_any_selected_field)
+ idx_t n_bytes = 0;
+ enum field_terminator terminator
+ = scan_mb_field (&mbbuf, &parser, &have_pending_line, false,
+ &n_bytes);
+ if (terminator == FIELD_EOF && n_bytes == 0)
+ return;
+
+ if (terminator != FIELD_DELIMITER)
{
- if (fwrite (output_delimiter_string, sizeof (char),
- output_delimiter_length, stdout)
- != output_delimiter_length)
- write_error ();
+ if (!suppress_non_delimited)
+ {
+ write_bytes (field_1_buffer, n_bytes);
+ write_line_delim ();
+ }
+
+ if (terminator == FIELD_EOF)
+ break;
+
+ reset_field_line (&field_idx, &found_any_selected_field,
+ &have_pending_line, &parser);
+ continue;
}
- found_any_selected_field = true;
- while ((c = getc (stream)) != delim && c != line_delim && c != EOF)
+ if (print_kth (1))
{
- if (putchar (c) < 0)
- write_error ();
- prev_c = c;
+ write_bytes (field_1_buffer, n_bytes);
+ found_any_selected_field = true;
}
+ next_item (&field_idx);
}
+
+ enum field_terminator terminator;
+ bool write_field = begin_field_output (field_idx, buffer_first_field,
+ &found_any_selected_field);
+
+ terminator = scan_mb_field (&mbbuf, &parser, &have_pending_line,
+ write_field, NULL);
+
+ if (terminator == FIELD_DELIMITER)
+ next_item (&field_idx);
else
{
- while ((c = getc (stream)) != delim && c != line_delim && c != EOF)
- prev_c = c;
+ if (terminator == FIELD_EOF && !have_pending_line)
+ break;
+ if (found_any_selected_field
+ || !(suppress_non_delimited && field_idx == 1))
+ write_line_delim ();
+ if (terminator == FIELD_EOF)
+ break;
+
+ reset_field_line (&field_idx, &found_any_selected_field,
+ &have_pending_line, &parser);
}
+ }
+}
+
+/* Read from STREAM, printing to standard output any selected fields,
+ using a multibyte field delimiter. */
+
+static void
+cut_fields_mb (FILE *stream)
+{
+ cut_fields_mb_any (stream, false);
+}
- /* With -d$'\n' don't treat the last '\n' as a delimiter. */
- if (delim == line_delim && c == delim)
+/* Read from STREAM, printing to standard output any selected fields,
+ using byte searches for a single-byte or valid UTF-8 field delimiter. */
+
+static void
+cut_fields_bytesearch (FILE *stream)
+{
+ /* Leave 1 byte unused so space to NUL terminate (for strstr). */
+ static char bytes_in[IO_BUFSIZE+1];
+ mbbuf_t mbbuf;
+ uintmax_t field_idx = 1;
+ bool found_any_selected_field = false;
+ bool have_pending_line = false;
+ bool skip_blank_run = false;
+ bool pending_line_delim_field = false;
+ bool write_field;
+ idx_t field_1_n_bytes = 0;
+
+ current_rp = frp;
+ bool buffer_first_field = suppress_non_delimited ^ !print_kth (1);
+ mbbuf_init (&mbbuf, bytes_in, sizeof bytes_in - 1, stream);
+ write_field = begin_field_output (field_idx, buffer_first_field,
+ &found_any_selected_field);
+
+ struct bytesearch_context search = { .blank_delimited = whitespace_delimited};
+ bytesearch_context_reset (&search);
+
+ while (true)
+ {
+ idx_t n_avail = mbbuf_topup (&mbbuf);
+ search.at_eof = mbbuf.eof;
+ search.line_end_known = false;
+
+ if (pending_line_delim_field)
{
- int last_c = getc (stream);
- if (last_c != EOF)
- ungetc (last_c, stream);
- else
- c = last_c;
+ pending_line_delim_field = false;
+
+ if (n_avail == 0)
+ {
+ if (handle_field_1 (field_idx, buffer_first_field,
+ &field_1_n_bytes))
+ next_item (&field_idx);
+ maybe_write_line_delim (found_any_selected_field, field_idx);
+ return;
+ }
+
+ handle_field_delimiter (&field_idx, buffer_first_field,
+ &field_1_n_bytes,
+ &found_any_selected_field, &write_field,
+ whitespace_delimited, &skip_blank_run);
}
- if (c == delim)
- next_item (&field_idx);
- else if (c == line_delim || c == EOF)
+ if (n_avail == 0)
+ break;
+
+ char *chunk = mbbuf.buffer + mbbuf.offset;
+ idx_t processed = 0;
+
+ /* Fast path: no field delimiter in chunk — output or skip lines. */
+ if (field_idx == 1
+ && !whitespace_delimited
+ && !field_delim_is_line_delim ()
+ && !find_field_delim (chunk, n_avail))
{
- if (found_any_selected_field
- || !(suppress_non_delimited && field_idx == 1))
+ char *last_line_delim = search.at_eof
+ ? chunk + n_avail - 1
+ : memrchr (chunk, line_delim, n_avail);
+ if (last_line_delim)
{
- /* Make sure the output line is newline terminated. */
- if (c == line_delim || prev_c != line_delim
- || delim == line_delim)
+ idx_t n = last_line_delim - chunk + 1;
+ if (! suppress_non_delimited)
{
- if (putchar (line_delim) < 0)
- write_error ();
+ write_bytes (field_1_buffer, field_1_n_bytes);
+ write_bytes (chunk, n);
+ if (search.at_eof && chunk[n - 1] != line_delim)
+ write_line_delim ();
}
+ field_1_n_bytes = 0;
+ mbbuf_advance (&mbbuf, n);
+ have_pending_line = false;
+ if (search.at_eof)
+ return;
+ continue;
}
- if (c == EOF)
+ }
+
+ while (processed < n_avail)
+ {
+ char *terminator = NULL;
+
+ if (skip_blank_run)
+ {
+ while (processed < n_avail && c_isblank (chunk[processed]))
+ processed++;
+ if (processed == n_avail)
+ break;
+ skip_blank_run = false;
+ }
+
+ if (field_selection_exhausted (field_idx))
+ {
+ if (field_delim_is_line_delim ())
+ {
+ maybe_write_line_delim (found_any_selected_field,
+ field_idx);
+ mbbuf_advance (&mbbuf, processed);
+ return;
+ }
+ search.mode = BYTESEARCH_LINE_ONLY;
+ enum field_terminator terminator_kind
+ = find_field_terminator (chunk + processed, n_avail - processed,
+ &search, &terminator);
+ if (terminator_kind == FIELD_LINE_DELIMITER)
+ {
+ processed = terminator - chunk + 1;
+ maybe_write_line_delim (found_any_selected_field,
+ field_idx);
+ goto reset_line;
+ }
+ processed = n_avail;
+ break;
+ }
+
+ enum field_terminator terminator_kind
+ = find_field_terminator (chunk + processed, n_avail - processed,
+ &search, &terminator);
+ idx_t field_len = terminator ? terminator - (chunk + processed)
+ : n_avail - processed;
+
+ if (terminator_kind == FIELD_DATA
+ && !search.at_eof
+ && !whitespace_delimited
+ && !field_delim_is_line_delim ())
+ field_len -= field_delim_overlap (chunk + processed, field_len);
+
+ if (field_len || terminator)
+ have_pending_line = true;
+
+ /* Output field content. */
+ if (field_idx == 1 && buffer_first_field)
+ append_field_1_chunk (chunk + processed, field_len,
+ &field_1_n_bytes);
+ else if (write_field)
+ write_bytes (chunk + processed, field_len);
+ processed += field_len;
+
+ if (terminator_kind == FIELD_DATA)
break;
- /* Start processing the next input line. */
- field_idx = 1;
- current_rp = frp;
- found_any_selected_field = false;
+ if (terminator_kind == FIELD_DELIMITER)
+ {
+ if (field_delim_is_line_delim () && processed + 1 == n_avail)
+ {
+ processed++;
+ pending_line_delim_field = true;
+ break;
+ }
+
+ processed += whitespace_delimited ? 1 : delim_mcel.len;
+ handle_field_delimiter (&field_idx, buffer_first_field,
+ &field_1_n_bytes,
+ &found_any_selected_field, &write_field,
+ whitespace_delimited, &skip_blank_run);
+ }
+ else
+ {
+ affirm (terminator_kind == FIELD_LINE_DELIMITER);
+ processed++;
+ finish_current_line (field_idx, buffer_first_field,
+ &field_1_n_bytes,
+ found_any_selected_field, true);
+
+ reset_line:
+ field_idx = 1;
+ current_rp = frp;
+ found_any_selected_field = false;
+ have_pending_line = false;
+ bytesearch_context_reset (&search);
+ write_field = begin_field_output (field_idx, buffer_first_field,
+ &found_any_selected_field);
+ }
}
+
+ mbbuf_advance (&mbbuf, processed);
}
+
+ if (!have_pending_line)
+ return;
+
+ finish_current_line (field_idx, buffer_first_field, &field_1_n_bytes,
+ found_any_selected_field, false);
+}
+
+/* Read from STREAM, printing to standard output any selected fields,
+ using runs of whitespace as the field delimiter. */
+
+static void
+cut_fields_ws (FILE *stream)
+{
+ if (MB_CUR_MAX <= 1 && !trim_outer_whitespace)
+ cut_fields_bytesearch (stream);
+ else
+ cut_fields_mb_any (stream, true);
}
/* Process file FILE to standard output, using CUT_STREAM.
@@ -450,7 +1247,7 @@ cut_file (char const *file, void (*cut_stream) (FILE *))
else
{
stream = fopen (file, "r");
- if (stream == nullptr)
+ if (stream == NULL)
{
error (0, errno, "%s", quotef (file));
return false;
@@ -482,8 +1279,8 @@ main (int argc, char **argv)
int optc;
bool ok;
bool delim_specified = false;
- bool byte_mode = false;
- char *spec_list_string = nullptr;
+ bool whitespace_delimited_specified = false;
+ char *spec_list_string = NULL;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -493,39 +1290,56 @@ main (int argc, char **argv)
atexit (close_stdout);
- /* By default, all non-delimited lines are printed. */
- suppress_non_delimited = false;
-
- delim = '\0';
- have_read_stdin = false;
-
- while ((optc = getopt_long (argc, argv, "b:c:d:f:nsz", longopts, nullptr))
+ while ((optc = getopt_long (argc, argv, "b:c:d:f:F:nO:szw", longopts, NULL))
!= -1)
{
switch (optc)
{
case 'b':
+ cut_mode = CUT_MODE_BYTES;
+ FALLTHROUGH;
case 'c':
- /* Build the byte list. */
- byte_mode = true;
+ if (optc == 'c')
+ cut_mode = CUT_MODE_CHARACTERS;
FALLTHROUGH;
case 'f':
- /* Build the field list. */
+ case 'F':
+ if (optc == 'f' || optc == 'F')
+ cut_mode = CUT_MODE_FIELDS;
+ if (optc == 'F')
+ {
+ whitespace_delimited = true;
+ space_output_delimiter_default = true;
+ }
if (spec_list_string)
FATAL_ERROR (_("only one list may be specified"));
spec_list_string = optarg;
break;
case 'd':
- /* New delimiter. */
- /* Interpret -d '' to mean 'use the NUL byte as the delimiter.' */
- if (optarg[0] != '\0' && optarg[1] != '\0')
- FATAL_ERROR (_("the delimiter must be a single character"));
- delim = optarg[0];
- delim_specified = true;
+ {
+ /* New delimiter. */
+ /* Interpret -d '' to mean 'use the NUL byte as the delimiter.' */
+ mcel_t g = delim_mcel = mcel_scanz (optarg);
+ if (optarg[0] && optarg[g.len])
+ FATAL_ERROR (_("the delimiter must be a single character"));
+ copy_bytes (delim_bytes, optarg, g.len);
+ delim_specified = true;
+ break;
+ }
+
+ case 'w':
+ whitespace_delimited = true;
+ whitespace_delimited_specified = true;
+ trim_outer_whitespace
+ = (optarg
+ && XARGMATCH ("--whitespace-delimited", optarg,
+ whitespace_option_args,
+ whitespace_option_types)
+ == WHITESPACE_OPTION_TRIMMED);
break;
- case OUTPUT_DELIMITER_OPTION:
+ case 'O':
/* Interpret --output-delimiter='' to mean
'use the NUL byte as the delimiter.' */
output_delimiter_length = (optarg[0] == '\0'
@@ -534,6 +1348,7 @@ main (int argc, char **argv)
break;
case 'n':
+ no_split = true;
break;
case 's':
@@ -558,32 +1373,72 @@ main (int argc, char **argv)
if (!spec_list_string)
FATAL_ERROR (_("you must specify a list of bytes, characters, or fields"));
- if (byte_mode)
+ if (cut_mode == CUT_MODE_BYTES || cut_mode == CUT_MODE_CHARACTERS)
{
- if (delim_specified)
- FATAL_ERROR (_("an input delimiter may be specified only\
- when operating on fields"));
+ if (delim_specified || whitespace_delimited)
+ FATAL_ERROR (_("an input delimiter makes sense\n\
+\tonly when operating on fields"));
if (suppress_non_delimited)
FATAL_ERROR (_("suppressing non-delimited lines makes sense\n\
\tonly when operating on fields"));
}
+ if (delim_specified && whitespace_delimited_specified)
+ FATAL_ERROR (_("-d and -w are mutually exclusive"));
+
+ if (delim_specified && !whitespace_delimited_specified)
+ whitespace_delimited = false;
+
set_fields (spec_list_string,
- ((byte_mode ? SETFLD_ERRMSG_USE_POS : 0)
+ (((cut_mode == CUT_MODE_BYTES
+ || cut_mode == CUT_MODE_CHARACTERS)
+ ? SETFLD_ERRMSG_USE_POS : 0)
| (complement ? SETFLD_COMPLEMENT : 0)));
if (!delim_specified)
- delim = '\t';
+ {
+ delim_bytes[0] = '\t';
+ delim_mcel = mcel_ch ('\t', 1);
+ }
- if (output_delimiter_string == nullptr)
+ if (output_delimiter_string == NULL)
{
- output_delimiter_default[0] = delim;
output_delimiter_string = output_delimiter_default;
- output_delimiter_length = 1;
+ if (space_output_delimiter_default)
+ {
+ output_delimiter_default[0] = ' ';
+ output_delimiter_length = 1;
+ }
+ else
+ {
+ copy_bytes (output_delimiter_default, delim_bytes, delim_mcel.len);
+ output_delimiter_length = delim_mcel.len;
+ }
}
- void (*cut_stream) (FILE *) = byte_mode ? cut_bytes : cut_fields;
+ void (*cut_stream) (FILE *) = NULL;
+ switch (cut_mode)
+ {
+ case CUT_MODE_NONE:
+ unreachable ();
+
+ case CUT_MODE_BYTES:
+ cut_stream = MB_CUR_MAX <= 1 || !no_split
+ ? cut_bytes : cut_bytes_no_split;
+ break;
+
+ case CUT_MODE_CHARACTERS:
+ cut_stream = MB_CUR_MAX <= 1 ? cut_bytes : cut_characters;
+ break;
+
+ case CUT_MODE_FIELDS:
+ cut_stream = whitespace_delimited ? cut_fields_ws
+ : bytesearch_field_delim_ok () ? cut_fields_bytesearch
+ : cut_fields_mb;
+ break;
+ }
+ affirm (cut_stream);
if (optind == argc)
ok = cut_file ("-", cut_stream);
else
diff --git a/src/date.c b/src/date.c
index 91651e794..88ea1279a 100644
--- a/src/date.c
+++ b/src/date.c
@@ -1,5 +1,5 @@
/* date - print or set the system date and time
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -60,7 +60,7 @@ static char const *const time_spec_string[] =
/* Put "hours" and "minutes" first, since they aren't valid for
--rfc-3339. */
"hours", "minutes",
- "date", "seconds", "ns", nullptr
+ "date", "seconds", "ns", NULL
};
static enum Time_spec const time_spec[] =
{
@@ -85,34 +85,28 @@ static char const short_options[] = "d:f:I::r:Rs:u";
static struct option const long_options[] =
{
- {"date", required_argument, nullptr, 'd'},
- {"debug", no_argument, nullptr, DEBUG_DATE_PARSING_OPTION},
- {"file", required_argument, nullptr, 'f'},
- {"iso-8601", optional_argument, nullptr, 'I'},
- {"reference", required_argument, nullptr, 'r'},
- {"resolution", no_argument, nullptr, RESOLUTION_OPTION},
- {"rfc-email", no_argument, nullptr, 'R'},
- {"rfc-822", no_argument, nullptr, 'R'},
- {"rfc-2822", no_argument, nullptr, 'R'},
- {"rfc-3339", required_argument, nullptr, RFC_3339_OPTION},
- {"set", required_argument, nullptr, 's'},
- {"uct", no_argument, nullptr, 'u'},
- {"utc", no_argument, nullptr, 'u'},
- {"universal", no_argument, nullptr, 'u'},
+ {"date", required_argument, NULL, 'd'},
+ {"debug", no_argument, NULL, DEBUG_DATE_PARSING_OPTION},
+ {"file", required_argument, NULL, 'f'},
+ {"iso-8601", optional_argument, NULL, 'I'},
+ {"reference", required_argument, NULL, 'r'},
+ {"resolution", no_argument, NULL, RESOLUTION_OPTION},
+ {"rfc-email", no_argument, NULL, 'R'},
+ {"rfc-822", no_argument, NULL, 'R'}, /* Deprecated. */
+ {"rfc-2822", no_argument, NULL, 'R'}, /* Deprecated. */
+ {"rfc-3339", required_argument, NULL, RFC_3339_OPTION},
+ {"set", required_argument, NULL, 's'},
+ {"uct", no_argument, NULL, 'u'}, /* Deprecated. */
+ {"utc", no_argument, NULL, 'u'},
+ {"universal", no_argument, NULL, 'u'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* flags for parse_datetime2 */
static unsigned int parse_datetime_flags;
-#if LOCALTIME_CACHE
-# define TZSET tzset ()
-#else
-# define TZSET /* empty */
-#endif
-
#ifdef _DATE_FMT
# define DATE_FMT_LANGINFO() nl_langinfo (_DATE_FMT)
#else
@@ -128,131 +122,236 @@ usage (int status)
{
printf (_("\
Usage: %s [OPTION]... [+FORMAT]\n\
- or: %s [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]\n\
+ or: %s [OPTION]... MMDDhhmm[[CC]YY][.ss]\n\
"),
program_name, program_name);
fputs (_("\
Display date and time in the given FORMAT.\n\
-With -s, or with [MMDDhhmm[[CC]YY][.ss]], set the date and time.\n\
+With -s, or with MMDDhhmm[[CC]YY][.ss], set the date and time first.\n\
"), stdout);
emit_mandatory_arg_note ();
+ oputs (_("\
+ -d, --date=STRING\n\
+ display time described by STRING, not 'now'\n\
+"));
+ oputs (_("\
+ --debug\n\
+ annotate the parsed date,\n\
+ and warn about questionable usage to standard error\n\
+"));
+ oputs (_("\
+ -f, --file=DATEFILE\n\
+ like --date; once for each line of DATEFILE;\n\
+ if DATEFILE is -, read names from standard input\n\
+"));
+ oputs (_("\
+ -I[FMT], --iso-8601[=FMT]\n\
+ output date/time in ISO 8601 format.\n\
+ FMT='date' (default), 'hours', 'minutes', 'seconds', or 'ns'\n\
+ for date and time to the indicated precision.\n\
+ Example: 2006-08-14T02:34:56-06:00\n\
+"));
+ oputs (_("\
+ --resolution\n\
+ output the available resolution of timestamps.\n\
+ Example: 0.000000001\n\
+"));
+ oputs (_("\
+ -R, --rfc-email\n\
+ output date and time in RFC 5322 format.\n\
+ Example: Mon, 14 Aug 2006 02:34:56 +0000\n\
+"));
+ oputs (_("\
+ --rfc-3339=FMT\n\
+ output date/time in RFC 3339 format.\n\
+ FMT='date', 'seconds', or 'ns'\n\
+ for date and time to the indicated precision.\n\
+ Example: 2006-08-14 02:34:56-06:00\n\
+"));
+ oputs (_("\
+ -r, --reference=FILE\n\
+ display the last modification time of FILE\n\
+"));
+ oputs (_("\
+ -s, --set=STRING\n\
+ set time described by STRING\n\
+"));
+ oputs (_("\
+ -u, --utc, --universal\n\
+ print or set Coordinated Universal Time (UTC)\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
- -d, --date=STRING display time described by STRING, not 'now'\n\
+\n\
+All options that specify the date to display are mutually exclusive.\n\
+I.e.: --date, --file, --reference, --resolution.\n\
+"), stdout);
+ fputs (_("\
+\n\
+FORMAT controls the output. Interpreted sequences are:\n\
+\n\
"), stdout);
fputs (_("\
- --debug annotate the parsed date, and\n\
- warn about questionable usage to standard error\n\
+FORMAT Example Description\n\
+\n\
"), stdout);
fputs (_("\
- -f, --file=DATEFILE like --date; once for each line of DATEFILE\n\
+ %% % a literal %\n\
"), stdout);
fputs (_("\
- -I[FMT], --iso-8601[=FMT] output date/time in ISO 8601 format.\n\
- FMT='date' for date only (the default),\n\
- 'hours', 'minutes', 'seconds', or 'ns'\n\
- for date and time to the indicated precision.\n\
- Example: 2006-08-14T02:34:56-06:00\n\
+ %n \\n a newline\n\
"), stdout);
fputs (_("\
- --resolution output the available resolution of timestamps\n\
- Example: 0.000000001\n\
+ %t \\t a tab\n\
"), stdout);
+
+ fputs ("\n", stdout);
+
fputs (_("\
- -R, --rfc-email output date and time in RFC 5322 format.\n\
- Example: Mon, 14 Aug 2006 02:34:56 -0600\n\
+ %C 20 century; like %Y, except omit last two digits\n\
"), stdout);
fputs (_("\
- --rfc-3339=FMT output date/time in RFC 3339 format.\n\
- FMT='date', 'seconds', or 'ns'\n\
- for date and time to the indicated precision.\n\
- Example: 2006-08-14 02:34:56-06:00\n\
+ %y 99 year (last two digits; 00..99)\n\
"), stdout);
fputs (_("\
- -r, --reference=FILE display the last modification time of FILE\n\
+ %Y 1999 year\n\
"), stdout);
fputs (_("\
- -s, --set=STRING set time described by STRING\n\
- -u, --utc, --universal print or set Coordinated Universal Time (UTC)\n\
+ %g 99 year of ISO week number (last 2 digits; 00-99); see %G\n\
"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
-\n\
-All options that specify the date to display are mutually exclusive.\n\
-I.e.: --date, --file, --reference, --resolution.\n\
+ %G 1999 year of ISO week number; normally useful only with %V\n\
"), stdout);
+
+ fputs ("\n", stdout);
+
fputs (_("\
-\n\
-FORMAT controls the output. Interpreted sequences are:\n\
-\n\
- %% a literal %\n\
- %a locale's abbreviated weekday name (e.g., Sun)\n\
+ %a Sun locale's abbreviated weekday name\n\
+"), stdout);
+ fputs (_("\
+ %A Sunday locale's full weekday name\n\
+"), stdout);
+ fputs (_("\
+ %b Mar locale's abbreviated month name\n\
+"), stdout);
+ fputs (_("\
+ %h Mar same as %b\n\
+"), stdout);
+ fputs (_("\
+ %B March locale's full month name\n\
+"), stdout);
+ fputs (_("\
+ %m 12 month (01..12)\n\
+"), stdout);
+ fputs (_("\
+ %d 31 day of month (01..31)\n\
+"), stdout);
+ fputs (_("\
+ %e 1 day of month, space padded; same as %_d\n\
+"), stdout);
+ fputs (_("\
+ %D 12/31/99 date (ambiguous); same as %m/%d/%y\n\
+"), stdout);
+ fputs (_("\
+ %F 1999-12-31 full date; like %+4Y-%m-%d\n\
+"), stdout);
+ fputs (_("\
+ %x 12/31/99 locale's date (can be ambiguous)\n\
+"), stdout);
+ fputs (_("\
+ %c ... locale's date and time (e.g., Thu Mar 3 23:05 2005)\n\
+"), stdout);
+
+ fputs ("\n", stdout);
+
+ fputs (_("\
+ %H 23 hour (00..23)\n\
+"), stdout);
+ fputs (_("\
+ %I 01 hour (01..12)\n\
+"), stdout);
+ fputs (_("\
+ %k 8 hour, space padded ( 0..23); same as %_H\n\
+"), stdout);
+ fputs (_("\
+ %l 9 hour, space padded ( 1..12); same as %_I\n\
+"), stdout);
+ fputs (_("\
+ %M 59 minute (00..59)\n\
+"), stdout);
+ fputs (_("\
+ %S 60 second (00..60)\n\
+"), stdout);
+ fputs (_("\
+ %N 123456789 nanoseconds (000000000..999999999)\n\
+"), stdout);
+ fputs (_("\
+ %s 1970010100 seconds since the Epoch (1970-01-01 00:00 UTC)\n\
+"), stdout);
+ fputs (_("\
+ %p PM locale's equivalent of AM or PM; blank if not known\n\
+"), stdout);
+ fputs (_("\
+ %P am like %p, but lower case\n\
"), stdout);
fputs (_("\
- %A locale's full weekday name (e.g., Sunday)\n\
- %b locale's abbreviated month name (e.g., Jan)\n\
- %B locale's full month name (e.g., January)\n\
- %c locale's date and time (e.g., Thu Mar 3 23:05:25 2005)\n\
+ %r 1:11:04 PM locale's 12-hour clock time\n\
"), stdout);
fputs (_("\
- %C century; like %Y, except omit last two digits (e.g., 20)\n\
- %d day of month (e.g., 01)\n\
- %D date (ambiguous); same as %m/%d/%y\n\
- %e day of month, space padded; same as %_d\n\
+ %R 23:59 24-hour hour and minute; same as %H:%M\n\
"), stdout);
fputs (_("\
- %F full date; like %+4Y-%m-%d\n\
- %g last two digits of year of ISO week number (ambiguous; 00-99); see %G\n\
- %G year of ISO week number; normally useful only with %V\n\
+ %T 23:59:59 time; same as %H:%M:%S\n\
"), stdout);
fputs (_("\
- %h same as %b\n\
- %H hour (00..23)\n\
- %I hour (01..12)\n\
- %j day of year (001..366)\n\
+ %X 23:59:59 locale's time representation\n\
+"), stdout);
+
+ fputs ("\n", stdout);
+
+ fputs (_("\
+ %j 365 day of year (001..366)\n\
+"), stdout);
+ fputs (_("\
+ %q 4 quarter of year (1..4)\n\
+"), stdout);
+ fputs (_("\
+ %u 7 day of week (1..7); 1 is Monday\n\
+"), stdout);
+ fputs (_("\
+ %w 6 day of week (0..6); 0 is Sunday\n\
+"), stdout);
+ fputs (_("\
+ %U 52 week of year; Sunday as first day of week (00..53)\n\
+"), stdout);
+ fputs (_("\
+ %V 52 ISO week number; Monday as first day of week (01..53)\n\
"), stdout);
fputs (_("\
- %k hour, space padded ( 0..23); same as %_H\n\
- %l hour, space padded ( 1..12); same as %_I\n\
- %m month (01..12)\n\
- %M minute (00..59)\n\
+ %W 52 week of year; Monday as first day of week (00..53)\n\
"), stdout);
+
+ fputs ("\n", stdout);
+
fputs (_("\
- %n a newline\n\
- %N nanoseconds (000000000..999999999)\n\
- %p locale's equivalent of either AM or PM; blank if not known\n\
- %P like %p, but lower case\n\
- %q quarter of year (1..4)\n\
- %r locale's 12-hour clock time (e.g., 11:11:04 PM)\n\
- %R 24-hour hour and minute; same as %H:%M\n\
- %s seconds since the Epoch (1970-01-01 00:00 UTC)\n\
+ %z +0400 +hhmm numeric time zone\n\
"), stdout);
fputs (_("\
- %S second (00..60)\n\
- %t a tab\n\
- %T time; same as %H:%M:%S\n\
- %u day of week (1..7); 1 is Monday\n\
+ %:z +04:00 +hh:mm numeric time zone\n\
"), stdout);
fputs (_("\
- %U week number of year, with Sunday as first day of week (00..53)\n\
- %V ISO week number, with Monday as first day of week (01..53)\n\
- %w day of week (0..6); 0 is Sunday\n\
- %W week number of year, with Monday as first day of week (00..53)\n\
+ %::z +04:00:00 +hh:mm:ss numeric time zone\n\
"), stdout);
fputs (_("\
- %x locale's date (can be ambiguous; e.g., 12/31/99)\n\
- %X locale's time representation (e.g., 23:13:48)\n\
- %y last two digits of year (ambiguous; 00..99)\n\
- %Y year\n\
+ %:::z +04 numeric time zone to necessary precision; with :\n\
+"), stdout);
+ fputs (_("\
+ %Z EDT alphabetic time zone abbreviation\n\
"), stdout);
fputs (_("\
- %z +hhmm numeric time zone (e.g., -0400)\n\
- %:z +hh:mm numeric time zone (e.g., -04:00)\n\
- %::z +hh:mm:ss numeric time zone (e.g., -04:00:00)\n\
- %:::z numeric time zone with : to necessary precision (e.g., -04, +05:30)\n\
- %Z alphabetic time zone abbreviation (e.g., EDT)\n\
\n\
By default, date pads numeric fields with zeroes.\n\
"), stdout);
@@ -305,12 +404,12 @@ res_width (long int res)
/* Return a newly allocated copy of FORMAT with each "%-N" adjusted to
be "%9N", "%6N", or whatever other resolution is appropriate for
- the current platform. If no "%-N" appears, return nullptr. */
+ the current platform. If no "%-N" appears, return NULL. */
static char *
adjust_resolution (char const *format)
{
- char *copy = nullptr;
+ char *copy = NULL;
for (char const *f = format; *f; f++)
if (f[0] == '%')
@@ -343,7 +442,7 @@ set_LC_TIME (char const *locale)
(in order to distinguish the default locale from the C locale on platforms
like macOS). */
char const *all = getenv ("LC_ALL");
- if (all != nullptr && *all != '\0')
+ if (all != NULL && *all != '\0')
{
/* Setting LC_TIME when LC_ALL is set would have no effect. Therefore we
have to unset LC_ALL and sets its value to all locale categories that
@@ -359,8 +458,8 @@ set_LC_TIME (char const *locale)
/* Set LC_TIME as an environment variable. */
char const *value = getenv ("LC_TIME");
- char *ret = (value == nullptr || *value == '\0' ? nullptr : xstrdup (value));
- if (locale != nullptr)
+ char *ret = (value == NULL || *value == '\0' ? NULL : xstrdup (value));
+ if (locale != NULL)
xsetenv ("LC_TIME", locale, 1);
else
unsetenv ("LC_TIME");
@@ -404,11 +503,7 @@ batch_convert (char const *input_filename,
char const *format, bool format_in_c_locale,
timezone_t tz, char const *tzstring)
{
- bool ok;
FILE *in_stream;
- char *line;
- size_t buflen;
- struct timespec when;
if (streq (input_filename, "-"))
{
@@ -418,13 +513,13 @@ batch_convert (char const *input_filename,
else
{
in_stream = fopen (input_filename, "r");
- if (in_stream == nullptr)
+ if (in_stream == NULL)
error (EXIT_FAILURE, errno, "%s", quotef (input_filename));
}
- line = nullptr;
- buflen = 0;
- ok = true;
+ char *line = NULL;
+ size_t buflen = 0;
+ bool ok = true;
while (true)
{
ssize_t line_length = getline (&line, &buflen, in_stream);
@@ -436,7 +531,8 @@ batch_convert (char const *input_filename,
break;
}
- if (! parse_datetime2 (&when, line, nullptr,
+ struct timespec when;
+ if (! parse_datetime2 (&when, line, NULL,
parse_datetime_flags, tz, tzstring))
{
if (line[line_length - 1] == '\n')
@@ -464,20 +560,17 @@ batch_convert (char const *input_filename,
int
main (int argc, char **argv)
{
- int optc;
- char const *datestr = nullptr;
- char const *set_datestr = nullptr;
- struct timespec when;
+ char const *datestr = NULL;
+ char const *set_datestr = NULL;
bool set_date = false;
- char const *format = nullptr;
+ char const *format = NULL;
bool format_in_c_locale = false;
bool get_resolution = false;
- char *batch_file = nullptr;
- char *reference = nullptr;
- struct stat refstats;
- bool ok;
+ char *batch_file = NULL;
+ char *reference = NULL;
bool discarded_datestr = false;
bool discarded_set_datestr = false;
+ char const *tzstring = NULL;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -487,7 +580,8 @@ main (int argc, char **argv)
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, short_options, long_options, nullptr))
+ int optc;
+ while ((optc = getopt_long (argc, argv, short_options, long_options, NULL))
!= -1)
{
switch (optc)
@@ -553,12 +647,7 @@ main (int argc, char **argv)
set_date = true;
break;
case 'u':
- /* POSIX says that 'date -u' is equivalent to setting the TZ
- environment variable, so this option should do nothing other
- than setting TZ. */
- if (putenv (bad_cast ("TZ=UTC0")) != 0)
- xalloc_die ();
- TZSET;
+ tzstring = "UTC0";
break;
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
@@ -637,16 +726,18 @@ main (int argc, char **argv)
char *format_copy = adjust_resolution (format);
char const *format_res = format_copy ? format_copy : format;
- char const *tzstring = getenv ("TZ");
+ if (!tzstring)
+ tzstring = getenv ("TZ");
timezone_t tz = tzalloc (tzstring);
- if (batch_file != nullptr)
+ bool ok = true;
+ if (batch_file != NULL)
ok = batch_convert (batch_file, format_res, format_in_c_locale,
tz, tzstring);
else
{
bool valid_date = true;
- ok = true;
+ struct timespec when;
if (!option_specified_date && !set_date)
{
@@ -671,8 +762,9 @@ main (int argc, char **argv)
else
{
/* (option_specified_date || set_date) */
- if (reference != nullptr)
+ if (reference != NULL)
{
+ struct stat refstats;
if (stat (reference, &refstats) != 0)
error (EXIT_FAILURE, errno, "%s", quotef (reference));
when = get_stat_mtime (&refstats);
@@ -687,7 +779,7 @@ main (int argc, char **argv)
{
if (set_datestr)
datestr = set_datestr;
- valid_date = parse_datetime2 (&when, datestr, nullptr,
+ valid_date = parse_datetime2 (&when, datestr, NULL,
parse_datetime_flags,
tz, tzstring);
}
diff --git a/src/dcgen b/src/dcgen
index 74694d2d7..af7447811 100755
--- a/src/dcgen
+++ b/src/dcgen
@@ -1,7 +1,7 @@
#!/usr/bin/perl -w
# dcgen -- convert dircolors.hin to dircolors.h.
-# Copyright (C) 1996-2025 Free Software Foundation, Inc.
+# Copyright (C) 1996-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/src/dd.c b/src/dd.c
index 98f360089..26382a233 100644
--- a/src/dd.c
+++ b/src/dd.c
@@ -1,5 +1,5 @@
/* dd -- convert a file while copying it.
- Copyright (C) 1985-2025 Free Software Foundation, Inc.
+ Copyright (C) 1985-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
#include <config.h>
-#include <ctype.h>
#include <sys/types.h>
#include <signal.h>
@@ -42,17 +41,6 @@
proper_name ("David MacKenzie"), \
proper_name ("Stuart Kemp")
-/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
- present. */
-#ifndef SA_NOCLDSTOP
-# define SA_NOCLDSTOP 0
-# define sigprocmask(How, Set, Oset) /* empty */
-# define sigset_t int
-# if ! HAVE_SIGINTERRUPT
-# define siginterrupt(sig, flag) /* empty */
-# endif
-#endif
-
/* NonStop circa 2011 lacks SA_RESETHAND; see Bug#9076. */
#ifndef SA_RESETHAND
# define SA_RESETHAND 0
@@ -121,11 +109,11 @@ enum
STATUS_PROGRESS = 4
};
-/* The name of the input file, or nullptr for the standard input. */
-static char const *input_file = nullptr;
+/* The name of the input file, or NULL for the standard input. */
+static char const *input_file = NULL;
-/* The name of the output file, or nullptr for the standard output. */
-static char const *output_file = nullptr;
+/* The name of the output file, or NULL for the standard output. */
+static char const *output_file = NULL;
/* The page size on this host. */
static idx_t page_size;
@@ -543,26 +531,50 @@ Usage: %s [OPERAND]...\n\
fputs (_("\
Copy a file, converting and formatting according to the operands.\n\
\n\
+"), stdout);
+ oputs (_("\
bs=BYTES read and write up to BYTES bytes at a time (default: 512);\n\
overrides ibs and obs\n\
+"));
+ oputs (_("\
cbs=BYTES convert BYTES bytes at a time\n\
+"));
+ oputs (_("\
conv=CONVS convert the file as per the comma separated symbol list\n\
+"));
+ oputs (_("\
count=N copy only N input blocks\n\
+"));
+ oputs (_("\
ibs=BYTES read up to BYTES bytes at a time (default: 512)\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
if=FILE read from FILE instead of standard input\n\
+"));
+ oputs (_("\
iflag=FLAGS read as per the comma separated symbol list\n\
+"));
+ oputs (_("\
obs=BYTES write BYTES bytes at a time (default: 512)\n\
+"));
+ oputs (_("\
of=FILE write to FILE instead of standard output\n\
+"));
+ oputs (_("\
oflag=FLAGS write as per the comma separated symbol list\n\
- seek=N (or oseek=N) skip N obs-sized output blocks\n\
- skip=N (or iseek=N) skip N ibs-sized input blocks\n\
+"));
+ oputs (_("\
+ seek=N (or oseek=N) skip N obs sized output blocks\n\
+"));
+ oputs (_("\
+ skip=N (or iseek=N) skip N ibs sized input blocks\n\
+"));
+ oputs (_("\
status=LEVEL The LEVEL of information to print to standard error;\n\
'none' suppresses everything but error messages,\n\
'noxfer' suppresses the final transfer statistics,\n\
'progress' shows periodic transfer statistics\n\
-"), stdout);
+"));
fputs (_("\
\n\
N and BYTES may be followed by the following multiplicative suffixes:\n\
@@ -644,8 +656,8 @@ Options are:\n\
"), SIGINFO == SIGUSR1 ? "USR1" : "INFO");
}
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -850,10 +862,8 @@ interrupt_handler (int sig)
/* An info signal was received; arrange for the program to print status. */
static void
-siginfo_handler (int sig)
+siginfo_handler (MAYBE_UNUSED int sig)
{
- if (! SA_NOCLDSTOP)
- signal (sig, siginfo_handler);
info_signal_count++;
}
@@ -864,13 +874,11 @@ install_signal_handlers (void)
{
bool catch_siginfo = ! (SIGINFO == SIGUSR1 && getenv ("POSIXLY_CORRECT"));
-#if SA_NOCLDSTOP
-
struct sigaction act;
sigemptyset (&caught_signals);
if (catch_siginfo)
sigaddset (&caught_signals, SIGINFO);
- sigaction (SIGINT, nullptr, &act);
+ sigaction (SIGINT, NULL, &act);
if (act.sa_handler != SIG_IGN)
sigaddset (&caught_signals, SIGINT);
act.sa_mask = caught_signals;
@@ -882,29 +890,15 @@ install_signal_handlers (void)
handle EINTR explicitly in iftruncate etc.
to avoid blocking on uncommitted read/write calls. */
act.sa_flags = 0;
- sigaction (SIGINFO, &act, nullptr);
+ sigaction (SIGINFO, &act, NULL);
}
if (sigismember (&caught_signals, SIGINT))
{
act.sa_handler = interrupt_handler;
act.sa_flags = SA_NODEFER | SA_RESETHAND;
- sigaction (SIGINT, &act, nullptr);
+ sigaction (SIGINT, &act, NULL);
}
-
-#else
-
- if (catch_siginfo)
- {
- signal (SIGINFO, siginfo_handler);
- siginterrupt (SIGINFO, 1);
- }
- if (signal (SIGINT, SIG_IGN) != SIG_IGN)
- {
- signal (SIGINT, interrupt_handler);
- siginterrupt (SIGINT, 1);
- }
-#endif
}
/* Close FD. Return 0 if successful, -1 (setting errno) otherwise.
@@ -971,7 +965,7 @@ process_signals (void)
if (infos)
info_signal_count = infos - 1;
- sigprocmask (SIG_SETMASK, &oldset, nullptr);
+ sigprocmask (SIG_SETMASK, &oldset, NULL);
if (interrupt)
cleanup ();
@@ -1194,7 +1188,7 @@ iwrite (int fd, char const *buf, idx_t size)
o_nocache_eof = true;
invalidate_cache (STDOUT_FILENO, 0);
- /* Attempt to ensure that that final block is committed
+ /* Attempt to ensure that the final block is committed
to stable storage as quickly as possible. */
conversions_mask |= C_FSYNC;
@@ -1419,48 +1413,72 @@ parse_integer (char const *str, strtol_error *invalid)
calling code should not rely on this function returning 0
when *INVALID represents a non-overflow error. */
int indeterminate = 0;
- uintmax_t n = indeterminate;
- char *suffix;
static char const suffixes[] = "bcEGkKMPQRTwYZ0";
- strtol_error e = xstrtoumax (str, &suffix, 10, &n, suffixes);
- intmax_t result;
+ intmax_t result = 1;
+ bool overflow = false;
+ bool warn_zero_multiplier = false;
+ strtol_error e;
- if ((e & ~LONGINT_OVERFLOW) == LONGINT_INVALID_SUFFIX_CHAR
- && *suffix == 'B' && str < suffix && suffix[-1] != 'B')
+ while (true)
{
- suffix++;
- if (!*suffix)
- e &= ~LONGINT_INVALID_SUFFIX_CHAR;
- }
+ uintmax_t n = indeterminate;
+ char *suffix;
+ e = xstrtoumax (str, &suffix, 10, &n, suffixes);
- if ((e & ~LONGINT_OVERFLOW) == LONGINT_INVALID_SUFFIX_CHAR
- && *suffix == 'x')
- {
- strtol_error f = LONGINT_OK;
- intmax_t o = parse_integer (suffix + 1, &f);
- if ((f & ~LONGINT_OVERFLOW) != LONGINT_OK)
+ if ((e & ~LONGINT_OVERFLOW) == LONGINT_INVALID_SUFFIX_CHAR
+ && *suffix == 'B' && str < suffix && suffix[-1] != 'B')
{
- e = f;
- result = indeterminate;
+ suffix++;
+ if (!*suffix)
+ e &= ~LONGINT_INVALID_SUFFIX_CHAR;
}
- else if (ckd_mul (&result, n, o)
- || (result != 0 && ((e | f) & LONGINT_OVERFLOW)))
+
+ bool multiply
+ = ((e & ~LONGINT_OVERFLOW) == LONGINT_INVALID_SUFFIX_CHAR
+ && *suffix == 'x');
+ if (multiply)
+ e &= ~LONGINT_INVALID_SUFFIX_CHAR;
+
+ if ((e & ~LONGINT_OVERFLOW) != LONGINT_OK)
{
+ if (! (e & LONGINT_OVERFLOW) && n <= INTMAX_MAX)
+ {
+ *invalid = e;
+ return indeterminate;
+ }
e = LONGINT_OVERFLOW;
- result = INTMAX_MAX;
}
- else
+
+ if (n == 0)
+ result = 0;
+ else if (result != 0)
{
- if (result == 0 && STRPREFIX (str, "0x"))
- diagnose (0, _("warning: %s is a zero multiplier; "
- "use %s if that is intended"),
- quote_n (0, "0x"), quote_n (1, "00x"));
- e = LONGINT_OK;
+ intmax_t product;
+ if ((e & LONGINT_OVERFLOW)
+ || overflow
+ || ckd_mul (&product, result, n))
+ overflow = true;
+ else
+ result = product;
}
+
+ if (multiply && STRPREFIX (str, "0x"))
+ warn_zero_multiplier = true;
+
+ if (! multiply)
+ break;
+ str = suffix + 1;
}
- else if (n <= INTMAX_MAX)
- result = n;
- else
+
+ if (result == 0)
+ {
+ if (warn_zero_multiplier)
+ diagnose (0, _("warning: %s is a zero multiplier; "
+ "use %s if that is intended"),
+ quote_n (0, "0x"), quote_n (1, "00x"));
+ e = LONGINT_OK;
+ }
+ else if (overflow)
{
e = LONGINT_OVERFLOW;
result = INTMAX_MAX;
@@ -1493,7 +1511,7 @@ scanargs (int argc, char *const *argv)
char const *name = argv[i];
char const *val = strchr (name, '=');
- if (val == nullptr)
+ if (val == NULL)
{
diagnose (0, _("unrecognized operand %s"), quoteaf (name));
usage (EXIT_FAILURE);
@@ -1523,7 +1541,7 @@ scanargs (int argc, char *const *argv)
bool has_B = !!strchr (val, 'B');
intmax_t n_min = 0;
intmax_t n_max = INTMAX_MAX;
- idx_t *converted_idx = nullptr;
+ idx_t *converted_idx = NULL;
/* Maximum blocksize. Keep it smaller than IDX_MAX, so that
it fits into blocksize vars even if 1 is added for conv=swab.
@@ -1691,20 +1709,18 @@ scanargs (int argc, char *const *argv)
static void
apply_translations (void)
{
- int i;
-
if (conversions_mask & C_ASCII)
translate_charset (ebcdic_to_ascii);
if (conversions_mask & C_UCASE)
{
- for (i = 0; i < 256; i++)
+ for (int i = 0; i < 256; i++)
trans_table[i] = toupper (trans_table[i]);
translation_needed = true;
}
else if (conversions_mask & C_LCASE)
{
- for (i = 0; i < 256; i++)
+ for (int i = 0; i < 256; i++)
trans_table[i] = tolower (trans_table[i]);
translation_needed = true;
}
@@ -1729,9 +1745,8 @@ apply_translations (void)
static void
translate_buffer (char *buf, idx_t nread)
{
- idx_t i;
- char *cp;
- for (i = nread, cp = buf; i; i--, cp++)
+ char *cp = buf;
+ for (idx_t i = nread; i; i--, cp++)
*cp = trans_table[to_uchar (*cp)];
}
@@ -1990,8 +2005,7 @@ copy_with_block (char const *buf, idx_t nread)
{
if (col < conversion_blocksize)
{
- idx_t j;
- for (j = col; j < conversion_blocksize; j++)
+ for (idx_t j = col; j < conversion_blocksize; j++)
output_char (space_character);
}
col = 0;
@@ -2274,6 +2288,8 @@ dd_copy (void)
if (nwritten != n_bytes_read)
{
diagnose (errno, _("error writing %s"), quoteaf (output_file));
+ if (nwritten != 0)
+ w_partial++;
return EXIT_FAILURE;
}
else if (n_bytes_read == input_blocksize)
@@ -2360,7 +2376,7 @@ dd_copy (void)
{
diagnose (errno, _("failed to truncate to %jd bytes"
" in output file %s"),
- (intmax_t) output_offset, quoteaf (output_file));
+ (intmax_t) {output_offset}, quoteaf (output_file));
return EXIT_FAILURE;
}
}
@@ -2412,8 +2428,6 @@ synchronize_output (void)
int
main (int argc, char **argv)
{
- int i;
- int exit_status;
off_t offset;
install_signal_handlers ();
@@ -2431,11 +2445,11 @@ main (int argc, char **argv)
parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE, Version,
true, usage, AUTHORS,
- (char const *) nullptr);
+ (char const *) NULL);
close_stdout_required = false;
/* Initialize translation table to identity translation. */
- for (i = 0; i < 256; i++)
+ for (int i = 0; i < 256; i++)
trans_table[i] = i;
/* Decode arguments. */
@@ -2443,7 +2457,7 @@ main (int argc, char **argv)
apply_translations ();
- if (input_file == nullptr)
+ if (input_file == NULL)
{
input_file = _("standard input");
set_fd_flags (STDIN_FILENO, input_flags, input_file);
@@ -2460,7 +2474,7 @@ main (int argc, char **argv)
input_offset = MAX (0, offset);
input_seek_errno = errno;
- if (output_file == nullptr)
+ if (output_file == NULL)
{
output_file = _("standard output");
set_fd_flags (STDOUT_FILENO, output_flags, output_file);
@@ -2506,20 +2520,17 @@ main (int argc, char **argv)
int ftruncate_errno = errno;
struct stat stdout_stat;
if (ifstat (STDOUT_FILENO, &stdout_stat) != 0)
- {
- diagnose (errno, _("cannot fstat %s"), quoteaf (output_file));
- exit_status = EXIT_FAILURE;
- }
+ error (EXIT_FAILURE, errno, _("cannot fstat %s"),
+ quoteaf (output_file));
else if (S_ISREG (stdout_stat.st_mode)
|| S_ISDIR (stdout_stat.st_mode)
|| S_TYPEISSHM (&stdout_stat))
{
intmax_t isize = size;
- diagnose (ftruncate_errno,
- _("failed to truncate to %jd bytes"
- " in output file %s"),
- isize, quoteaf (output_file));
- exit_status = EXIT_FAILURE;
+ error (EXIT_FAILURE, ftruncate_errno,
+ _("failed to truncate to %jd bytes"
+ " in output file %s"),
+ isize, quoteaf (output_file));
}
}
}
@@ -2528,11 +2539,9 @@ main (int argc, char **argv)
start_time = gethrxtime ();
next_time = start_time + XTIME_PRECISION;
- exit_status = dd_copy ();
-
+ int copy_status = dd_copy ();
int sync_status = synchronize_output ();
- if (sync_status)
- exit_status = sync_status;
+ int exit_status = copy_status | sync_status;
if (max_records == 0 && max_bytes == 0)
{
diff --git a/src/df.c b/src/df.c
index 75e638c5e..b3d83f505 100644
--- a/src/df.c
+++ b/src/df.c
@@ -1,5 +1,5 @@
/* df - summarize free file system space
- Copyright (C) 1991-2025 Free Software Foundation, Inc.
+ Copyright (C) 1991-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -50,6 +50,7 @@ struct devlist
dev_t dev_num;
struct mount_entry *me;
struct devlist *next;
+ struct devlist *next_same_dev;
struct devlist *seen_last; /* valid for hashed devlist entries only */
};
@@ -69,7 +70,7 @@ static bool show_local_fs;
static bool show_listed_fs;
/* Human-readable options for output. */
-static int human_output_opts;
+static int human_output_opts = -1;
/* The units to use when printing sizes. */
static uintmax_t output_block_size;
@@ -122,7 +123,7 @@ static bool print_type;
static bool print_grand_total;
/* Grand total data. */
-static struct fs_usage grand_fsu;
+static struct fs_usage grand_fsu = { .fsu_blocksize = 1 };
/* Display modes. */
static enum
@@ -166,7 +167,7 @@ struct field_data_t
display_field_t field;
char const *arg;
field_type_t field_type;
- char const *caption;/* nullptr means use default header of this field. */
+ char const *caption;/* NULL means use default header of this field. */
int width; /* Auto adjusted (up) widths used to align columns. */
bool align_right; /* Whether to right-align columns, not left-align. */
bool used;
@@ -253,23 +254,23 @@ enum
static struct option const long_options[] =
{
- {"all", no_argument, nullptr, 'a'},
- {"block-size", required_argument, nullptr, 'B'},
- {"inodes", no_argument, nullptr, 'i'},
- {"human-readable", no_argument, nullptr, 'h'},
- {"si", no_argument, nullptr, 'H'},
- {"local", no_argument, nullptr, 'l'},
- {"output", optional_argument, nullptr, OUTPUT_OPTION},
- {"portability", no_argument, nullptr, 'P'},
- {"print-type", no_argument, nullptr, 'T'},
- {"sync", no_argument, nullptr, SYNC_OPTION},
- {"no-sync", no_argument, nullptr, NO_SYNC_OPTION},
- {"total", no_argument, nullptr, TOTAL_OPTION},
- {"type", required_argument, nullptr, 't'},
- {"exclude-type", required_argument, nullptr, 'x'},
+ {"all", no_argument, NULL, 'a'},
+ {"block-size", required_argument, NULL, 'B'},
+ {"inodes", no_argument, NULL, 'i'},
+ {"human-readable", no_argument, NULL, 'h'},
+ {"si", no_argument, NULL, 'H'},
+ {"local", no_argument, NULL, 'l'},
+ {"output", optional_argument, NULL, OUTPUT_OPTION},
+ {"portability", no_argument, NULL, 'P'},
+ {"print-type", no_argument, NULL, 'T'},
+ {"sync", no_argument, NULL, SYNC_OPTION},
+ {"no-sync", no_argument, NULL, NO_SYNC_OPTION},
+ {"total", no_argument, NULL, TOTAL_OPTION},
+ {"type", required_argument, NULL, 't'},
+ {"exclude-type", required_argument, NULL, 'x'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* Stat FILE and put the results into *ST. Return 0 if successful, an
@@ -413,7 +414,7 @@ alloc_field (int f, char const *c)
if (ncolumns == ncolumns_alloc)
columns = xpalloc (columns, &ncolumns_alloc, 1, -1, sizeof *columns);
columns[ncolumns++] = &field_data[f];
- if (c != nullptr)
+ if (c != NULL)
field_data[f].caption = c;
affirm (!field_data[f].used);
@@ -475,7 +476,7 @@ decode_output_arg (char const *arg)
case IPCENT_FIELD:
case TARGET_FIELD:
case FILE_FIELD:
- alloc_field (field, nullptr);
+ alloc_field (field, NULL);
break;
case SIZE_FIELD:
@@ -504,48 +505,48 @@ get_field_list (void)
switch (header_mode)
{
case DEFAULT_MODE:
- alloc_field (SOURCE_FIELD, nullptr);
+ alloc_field (SOURCE_FIELD, NULL);
if (print_type)
- alloc_field (FSTYPE_FIELD, nullptr);
- alloc_field (SIZE_FIELD, nullptr);
- alloc_field (USED_FIELD, nullptr);
- alloc_field (AVAIL_FIELD, nullptr);
- alloc_field (PCENT_FIELD, nullptr);
- alloc_field (TARGET_FIELD, nullptr);
+ alloc_field (FSTYPE_FIELD, NULL);
+ alloc_field (SIZE_FIELD, NULL);
+ alloc_field (USED_FIELD, NULL);
+ alloc_field (AVAIL_FIELD, NULL);
+ alloc_field (PCENT_FIELD, NULL);
+ alloc_field (TARGET_FIELD, NULL);
break;
case HUMAN_MODE:
- alloc_field (SOURCE_FIELD, nullptr);
+ alloc_field (SOURCE_FIELD, NULL);
if (print_type)
- alloc_field (FSTYPE_FIELD, nullptr);
+ alloc_field (FSTYPE_FIELD, NULL);
alloc_field (SIZE_FIELD, N_("Size"));
- alloc_field (USED_FIELD, nullptr);
+ alloc_field (USED_FIELD, NULL);
alloc_field (AVAIL_FIELD, N_("Avail"));
- alloc_field (PCENT_FIELD, nullptr);
- alloc_field (TARGET_FIELD, nullptr);
+ alloc_field (PCENT_FIELD, NULL);
+ alloc_field (TARGET_FIELD, NULL);
break;
case INODES_MODE:
- alloc_field (SOURCE_FIELD, nullptr);
+ alloc_field (SOURCE_FIELD, NULL);
if (print_type)
- alloc_field (FSTYPE_FIELD, nullptr);
- alloc_field (ITOTAL_FIELD, nullptr);
- alloc_field (IUSED_FIELD, nullptr);
- alloc_field (IAVAIL_FIELD, nullptr);
- alloc_field (IPCENT_FIELD, nullptr);
- alloc_field (TARGET_FIELD, nullptr);
+ alloc_field (FSTYPE_FIELD, NULL);
+ alloc_field (ITOTAL_FIELD, NULL);
+ alloc_field (IUSED_FIELD, NULL);
+ alloc_field (IAVAIL_FIELD, NULL);
+ alloc_field (IPCENT_FIELD, NULL);
+ alloc_field (TARGET_FIELD, NULL);
break;
case POSIX_MODE:
- alloc_field (SOURCE_FIELD, nullptr);
+ alloc_field (SOURCE_FIELD, NULL);
if (print_type)
- alloc_field (FSTYPE_FIELD, nullptr);
- alloc_field (SIZE_FIELD, nullptr);
- alloc_field (USED_FIELD, nullptr);
- alloc_field (AVAIL_FIELD, nullptr);
+ alloc_field (FSTYPE_FIELD, NULL);
+ alloc_field (SIZE_FIELD, NULL);
+ alloc_field (USED_FIELD, NULL);
+ alloc_field (AVAIL_FIELD, NULL);
alloc_field (PCENT_FIELD, N_("Capacity"));
- alloc_field (TARGET_FIELD, nullptr);
+ alloc_field (TARGET_FIELD, NULL);
break;
case OUTPUT_MODE:
@@ -638,11 +639,10 @@ ATTRIBUTE_PURE
static bool
selected_fstype (char const *fstype)
{
- const struct fs_type_list *fsp;
-
- if (fs_select_list == nullptr || fstype == nullptr)
+ if (fs_select_list == NULL || fstype == NULL)
return true;
- for (fsp = fs_select_list; fsp; fsp = fsp->fs_next)
+ for (const struct fs_type_list *fsp = fs_select_list; fsp;
+ fsp = fsp->fs_next)
if (streq (fstype, fsp->fs_name))
return true;
return false;
@@ -654,11 +654,10 @@ ATTRIBUTE_PURE
static bool
excluded_fstype (char const *fstype)
{
- const struct fs_type_list *fsp;
-
- if (fs_exclude_list == nullptr || fstype == nullptr)
+ if (fs_exclude_list == NULL || fstype == NULL)
return false;
- for (fsp = fs_exclude_list; fsp; fsp = fsp->fs_next)
+ for (const struct fs_type_list *fsp = fs_exclude_list; fsp;
+ fsp = fsp->fs_next)
if (streq (fstype, fsp->fs_name))
return true;
return false;
@@ -668,7 +667,7 @@ static size_t
devlist_hash (void const *x, size_t table_size)
{
struct devlist const *p = x;
- return (uintmax_t) p->dev_num % table_size;
+ return (uintmax_t) {p->dev_num} % table_size;
}
static bool
@@ -682,14 +681,14 @@ devlist_compare (void const *x, void const *y)
static struct devlist *
devlist_for_dev (dev_t dev)
{
- if (devlist_table == nullptr)
- return nullptr;
+ if (devlist_table == NULL)
+ return NULL;
struct devlist dev_entry;
dev_entry.dev_num = dev;
struct devlist *found = hash_lookup (devlist_table, &dev_entry);
- if (found == nullptr)
- return nullptr;
+ if (found == NULL)
+ return NULL;
/* Return the last devlist entry we have seen with this dev_num */
return found->seen_last;
@@ -699,31 +698,30 @@ devlist_for_dev (dev_t dev)
In the case of duplicates - based on the device number - the mount entry
with a '/' in its me_devname (i.e., not pseudo name like tmpfs) wins.
If both have a real devname (e.g. bind mounts), then that with the shorter
- me_mountdir wins. With DEVICES_ONLY == true (set with df -a), only update
+ me_mountdir wins. If DEVICES_ONLY is true (set with df -a), only update
the global devlist_table, rather than filtering the global mount_list. */
static void
filter_mount_list (bool devices_only)
{
- struct mount_entry *me;
-
/* Temporary list to keep entries ordered. */
- struct devlist *device_list = nullptr;
+ struct devlist *device_list = NULL;
int mount_list_size = 0;
- for (me = mount_list; me; me = me->me_next)
+ for (struct mount_entry *me = mount_list; me; me = me->me_next)
mount_list_size++;
- devlist_table = hash_initialize (mount_list_size, nullptr,
- devlist_hash, devlist_compare, nullptr);
- if (devlist_table == nullptr)
+ devlist_table = hash_initialize (mount_list_size, NULL,
+ devlist_hash, devlist_compare, NULL);
+ if (devlist_table == NULL)
xalloc_die ();
/* Sort all 'wanted' entries into the list device_list. */
- for (me = mount_list; me;)
+ for (struct mount_entry *me = mount_list; me;)
{
struct stat buf;
- struct mount_entry *discard_me = nullptr;
+ struct mount_entry *discard_me = NULL;
+ struct devlist *last_seen_dev = NULL, *seen_dev = NULL;
/* Avoid stating remote file systems as that may hang.
On Linux we probably have me_dev populated from /proc/self/mountinfo,
@@ -741,15 +739,15 @@ filter_mount_list (bool devices_only)
else
{
/* If we've already seen this device... */
- struct devlist *seen_dev = devlist_for_dev (buf.st_dev);
+ last_seen_dev = seen_dev = devlist_for_dev (buf.st_dev);
- if (seen_dev)
+ for (; seen_dev && ! discard_me; seen_dev = seen_dev->next_same_dev)
{
bool target_nearer_root = strlen (seen_dev->me->me_mountdir)
> strlen (me->me_mountdir);
/* With bind mounts, prefer items nearer the root of the source */
- bool source_below_root = seen_dev->me->me_mntroot != nullptr
- && me->me_mntroot != nullptr
+ bool source_below_root = seen_dev->me->me_mntroot != NULL
+ && me->me_mntroot != NULL
&& (strlen (seen_dev->me->me_mntroot)
< strlen (me->me_mntroot));
if (! print_grand_total
@@ -800,11 +798,12 @@ filter_mount_list (bool devices_only)
struct devlist *devlist = xmalloc (sizeof *devlist);
devlist->me = me;
devlist->dev_num = buf.st_dev;
+ devlist->next_same_dev = last_seen_dev;
devlist->next = device_list;
device_list = devlist;
struct devlist *hash_entry = hash_insert (devlist_table, devlist);
- if (hash_entry == nullptr)
+ if (hash_entry == NULL)
xalloc_die ();
/* Ensure lookups use this latest devlist. */
hash_entry->seen_last = devlist;
@@ -814,27 +813,27 @@ filter_mount_list (bool devices_only)
}
/* Finally rebuild the mount_list from the devlist. */
- if (! devices_only) {
- mount_list = nullptr;
- while (device_list)
- {
- /* Add the mount entry. */
- me = device_list->me;
- me->me_next = mount_list;
- mount_list = me;
- struct devlist *next = device_list->next;
- free (device_list);
- device_list = next;
- }
-
+ if (! devices_only)
+ {
+ mount_list = NULL;
+ while (device_list)
+ {
+ /* Add the mount entry. */
+ struct mount_entry *me = device_list->me;
+ me->me_next = mount_list;
+ mount_list = me;
+ struct devlist *next = device_list->next;
+ free (device_list);
+ device_list = next;
+ }
hash_free (devlist_table);
- devlist_table = nullptr;
- }
+ devlist_table = NULL;
+ }
}
/* Search a mount entry list for device id DEV.
- Return the corresponding mount entry if found or nullptr if not. */
+ Return the corresponding mount entry if found or NULL if not. */
ATTRIBUTE_PURE
static struct mount_entry const *
@@ -844,7 +843,7 @@ me_for_dev (dev_t dev)
if (dl)
return dl->me;
- return nullptr;
+ return NULL;
}
/* Return true if N is a known integer value. On many file systems,
@@ -1125,7 +1124,7 @@ get_dev (char const *device, char const *mount_point, char const *file,
v = &inode_values;
break;
case OTHER_FLD:
- v = nullptr;
+ v = NULL;
break;
default:
affirm (!"bad field_type");
@@ -1235,14 +1234,13 @@ get_dev (char const *device, char const *mount_point, char const *file,
}
/* Scan the mount list returning the _last_ device found for MOUNT.
- nullptr is returned if MOUNT not found. The result is malloced. */
+ NULL is returned if MOUNT not found. The result is malloced. */
static char *
last_device_for_mount (char const *mount)
{
- struct mount_entry const *me;
- struct mount_entry const *le = nullptr;
+ struct mount_entry const *le = NULL;
- for (me = mount_list; me; me = me->me_next)
+ for (struct mount_entry const *me = mount_list; me; me = me->me_next)
{
if (streq (me->me_mountdir, mount))
le = me;
@@ -1258,7 +1256,7 @@ last_device_for_mount (char const *mount)
return xstrdup (le->me_devname);
}
else
- return nullptr;
+ return NULL;
}
/* If DEVICE corresponds to a mount point, show its usage
@@ -1266,8 +1264,7 @@ last_device_for_mount (char const *mount)
static bool
get_device (char const *device)
{
- struct mount_entry const *me;
- struct mount_entry const *best_match = nullptr;
+ struct mount_entry const *best_match = NULL;
bool best_match_accessible = false;
bool eclipsed_device = false;
char const *file = device;
@@ -1277,7 +1274,7 @@ get_device (char const *device)
device = resolved;
size_t best_match_len = SIZE_MAX;
- for (me = mount_list; me; me = me->me_next)
+ for (struct mount_entry const *me = mount_list; me; me = me->me_next)
{
/* TODO: Should cache canon_dev in the mount_entry struct. */
char *devname = me->me_devname;
@@ -1325,9 +1322,9 @@ get_device (char const *device)
if (best_match)
{
- get_dev (best_match->me_devname, best_match->me_mountdir, file, nullptr,
+ get_dev (best_match->me_devname, best_match->me_mountdir, file, NULL,
best_match->me_type, best_match->me_dummy,
- best_match->me_remote, nullptr, false);
+ best_match->me_remote, NULL, false);
return true;
}
else if (eclipsed_device)
@@ -1348,8 +1345,7 @@ static void
get_point (char const *point, const struct stat *statp)
{
struct stat device_stats;
- struct mount_entry *me;
- struct mount_entry const *best_match = nullptr;
+ struct mount_entry const *best_match = NULL;
/* Calculate the real absolute file name for POINT, and use that to find
the mount point. This avoids statting unavailable mount points,
@@ -1360,7 +1356,7 @@ get_point (char const *point, const struct stat *statp)
size_t resolved_len = strlen (resolved);
size_t best_match_len = 0;
- for (me = mount_list; me; me = me->me_next)
+ for (struct mount_entry *me = mount_list; me; me = me->me_next)
{
if (!streq (me->me_type, "lofs")
&& (!best_match || best_match->me_dummy || !me->me_dummy))
@@ -1381,10 +1377,10 @@ get_point (char const *point, const struct stat *statp)
if (best_match
&& (stat (best_match->me_mountdir, &device_stats) != 0
|| device_stats.st_dev != statp->st_dev))
- best_match = nullptr;
+ best_match = NULL;
if (! best_match)
- for (me = mount_list; me; me = me->me_next)
+ for (struct mount_entry *me = mount_list; me; me = me->me_next)
{
if (me->me_dev == (dev_t) -1)
{
@@ -1422,7 +1418,7 @@ get_point (char const *point, const struct stat *statp)
if (best_match)
get_dev (best_match->me_devname, best_match->me_mountdir, point, point,
best_match->me_type, best_match->me_dummy, best_match->me_remote,
- nullptr, false);
+ NULL, false);
else
{
/* We couldn't find the mount entry corresponding to POINT. Go ahead and
@@ -1433,8 +1429,8 @@ get_point (char const *point, const struct stat *statp)
char *mp = find_mount_point (point, statp);
if (mp)
{
- get_dev (nullptr, mp, point, nullptr, nullptr,
- false, false, nullptr, false);
+ get_dev (NULL, mp, point, NULL, NULL,
+ false, false, NULL, false);
free (mp);
}
}
@@ -1459,13 +1455,11 @@ get_entry (char const *name, struct stat const *statp)
static void
get_all_entries (void)
{
- struct mount_entry *me;
-
filter_mount_list (show_all_fs);
- for (me = mount_list; me; me = me->me_next)
- get_dev (me->me_devname, me->me_mountdir, nullptr, nullptr, me->me_type,
- me->me_dummy, me->me_remote, nullptr, true);
+ for (struct mount_entry *me = mount_list; me; me = me->me_next)
+ get_dev (me->me_devname, me->me_mountdir, NULL, NULL, me->me_type,
+ me->me_dummy, me->me_remote, NULL, true);
}
/* Add FSTYPE to the list of file system types to display. */
@@ -1511,43 +1505,75 @@ or all file systems by default.\n\
/* TRANSLATORS: The thousands and decimal separators are best
adjusted to an appropriate default for your locale. */
- fputs (_("\
- -a, --all include pseudo, duplicate, inaccessible file systems\n\
- -B, --block-size=SIZE scale sizes by SIZE before printing them; e.g.,\n\
- '-BM' prints sizes in units of 1,048,576 bytes;\n\
- see SIZE format below\n\
- -h, --human-readable print sizes in powers of 1024 (e.g., 1023M)\n\
- -H, --si print sizes in powers of 1000 (e.g., 1.1G)\n\
-"), stdout);
- fputs (_("\
- -i, --inodes list inode information instead of block usage\n\
- -k like --block-size=1K\n\
- -l, --local limit listing to local file systems\n\
- --no-sync do not invoke sync before getting usage info (default)\
-\n\
-"), stdout);
- fputs (_("\
- --output[=FIELD_LIST] use the output format defined by FIELD_LIST,\n\
- or print all fields if FIELD_LIST is omitted\n\
-"), stdout);
- fputs (_("\
- -P, --portability use the POSIX output format\n\
-"), stdout);
- fputs (_("\
- --sync invoke sync before getting usage info\n\
-"), stdout);
- fputs (_("\
- --total elide all entries insignificant to available space,\n\
- and produce a grand total\n\
-"), stdout);
- fputs (_("\
- -t, --type=TYPE limit listing to file systems of type TYPE\n\
- -T, --print-type print file system type\n\
- -x, --exclude-type=TYPE limit listing to file systems not of type TYPE\n\
- -v (ignored)\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -a, --all\n\
+ include pseudo, duplicate, inaccessible file systems\n\
+"));
+ oputs (_("\
+ -B, --block-size=SIZE\n\
+ scale sizes by SIZE before printing them; see SIZE format below;\n\
+ E.g., '-BM' prints sizes in units of 1,048,576 bytes\n\
+"));
+ oputs (_("\
+ -h, --human-readable\n\
+ print sizes in powers of 1024 (e.g., 1023M)\n\
+"));
+ oputs (_("\
+ -H, --si\n\
+ print sizes in powers of 1000 (e.g., 1.1G)\n\
+"));
+ oputs (_("\
+ -i, --inodes\n\
+ list inode information instead of block usage\n\
+"));
+ oputs (_("\
+ -k\n\
+ like --block-size=1K\n\
+"));
+ oputs (_("\
+ -l, --local\n\
+ limit listing to local file systems\n\
+"));
+ oputs (_("\
+ --no-sync\n\
+ do not invoke sync before getting usage info (default)\n\
+"));
+ oputs (_("\
+ --output[=FIELD_LIST]\n\
+ use the output format defined by FIELD_LIST,\n\
+ or print all fields if FIELD_LIST is omitted\n\
+"));
+ oputs (_("\
+ -P, --portability\n\
+ use the POSIX output format\n\
+"));
+ oputs (_("\
+ --sync\n\
+ invoke sync before getting usage info\n\
+"));
+ oputs (_("\
+ --total\n\
+ elide all entries insignificant to available space,\n\
+ and produce a grand total\n\
+"));
+ oputs (_("\
+ -t, --type=TYPE\n\
+ limit listing to file systems of type TYPE\n\
+"));
+ oputs (_("\
+ -T, --print-type\n\
+ print file system type\n\
+"));
+ oputs (_("\
+ -x, --exclude-type=TYPE\n\
+ limit listing to file systems not of type TYPE\n\
+"));
+ oputs (_("\
+ -v\n\
+ (ignored)\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_blocksize_note ("DF");
emit_size_note ();
fputs (_("\n\
@@ -1563,7 +1589,7 @@ field names are: 'source', 'fstype', 'itotal', 'iused', 'iavail', 'ipcent',\n\
int
main (int argc, char **argv)
{
- struct stat *stats = nullptr;
+ struct stat *stats = NULL;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -1573,17 +1599,6 @@ main (int argc, char **argv)
atexit (close_stdout);
- fs_select_list = nullptr;
- fs_exclude_list = nullptr;
- show_all_fs = false;
- show_listed_fs = false;
- human_output_opts = -1;
- print_type = false;
- file_systems_processed = false;
- exit_status = EXIT_SUCCESS;
- print_grand_total = false;
- grand_fsu.fsu_blocksize = 1;
-
/* If true, use the POSIX output format. */
bool posix_format = false;
@@ -1728,11 +1743,11 @@ main (int argc, char **argv)
/* Fail if the same file system type was both selected and excluded. */
{
bool match = false;
- struct fs_type_list *fs_incl;
- for (fs_incl = fs_select_list; fs_incl; fs_incl = fs_incl->fs_next)
+ for (struct fs_type_list *fs_incl = fs_select_list; fs_incl;
+ fs_incl = fs_incl->fs_next)
{
- struct fs_type_list *fs_excl;
- for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next)
+ for (struct fs_type_list *fs_excl = fs_exclude_list; fs_excl;
+ fs_excl = fs_excl->fs_next)
{
if (streq (fs_incl->fs_name, fs_excl->fs_name))
{
@@ -1761,19 +1776,19 @@ main (int argc, char **argv)
{
error (0, err, "%s", quotef (argv[i]));
exit_status = EXIT_FAILURE;
- argv[i] = nullptr;
+ argv[i] = NULL;
}
}
}
mount_list =
- read_file_system_list ((fs_select_list != nullptr
- || fs_exclude_list != nullptr
+ read_file_system_list ((fs_select_list != NULL
+ || fs_exclude_list != NULL
|| print_type
|| field_data[FSTYPE_FIELD].used
|| show_local_fs));
- if (mount_list == nullptr)
+ if (mount_list == NULL)
{
/* Couldn't read the table of mounted file systems.
Fail if df was invoked with no file name arguments,
@@ -1783,8 +1798,8 @@ main (int argc, char **argv)
if ( ! (optind < argc)
|| (show_all_fs
|| show_local_fs
- || fs_select_list != nullptr
- || fs_exclude_list != nullptr))
+ || fs_select_list != NULL
+ || fs_exclude_list != NULL))
{
status = EXIT_FAILURE;
}
@@ -1816,7 +1831,7 @@ main (int argc, char **argv)
if (print_grand_total)
get_dev ("total",
(field_data[SOURCE_FIELD].used ? "-" : "total"),
- nullptr, nullptr, nullptr, false, false, &grand_fsu, false);
+ NULL, NULL, NULL, false, false, &grand_fsu, false);
print_table ();
}
diff --git a/src/digest.c b/src/digest.c
deleted file mode 100644
index 58b9c9ec1..000000000
--- a/src/digest.c
+++ /dev/null
@@ -1,1805 +0,0 @@
-/* Compute checksums of files or strings.
- Copyright (C) 1995-2025 Free Software Foundation, Inc.
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>. */
-
-/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>. */
-
-#include <config.h>
-
-#include <getopt.h>
-#include <sys/types.h>
-
-#include "assure.h"
-#include "system.h"
-#include "argmatch.h"
-#include "c-ctype.h"
-#include "quote.h"
-#include "xdectoint.h"
-#include "xstrtol.h"
-
-#ifndef HASH_ALGO_CKSUM
-# define HASH_ALGO_CKSUM 0
-#endif
-
-#if HASH_ALGO_SUM || HASH_ALGO_CKSUM
-# include "sum.h"
-#endif
-#if HASH_ALGO_CKSUM
-# include "cksum.h"
-# include "base64.h"
-#endif
-#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
-# include "blake2/b2sum.h"
-#endif
-#if HASH_ALGO_MD5 || HASH_ALGO_CKSUM
-# include "md5.h"
-#endif
-#if HASH_ALGO_SHA1 || HASH_ALGO_CKSUM
-# include "sha1.h"
-#endif
-#if HASH_ALGO_SHA256 || HASH_ALGO_SHA224 || HASH_ALGO_CKSUM
-# include "sha256.h"
-#endif
-#if HASH_ALGO_SHA512 || HASH_ALGO_SHA384 || HASH_ALGO_CKSUM
-# include "sha512.h"
-#endif
-#if HASH_ALGO_CKSUM
-# include "sha3.h"
-#endif
-#if HASH_ALGO_CKSUM
-# include "sm3.h"
-#endif
-#include "fadvise.h"
-#include "stdio--.h"
-#include "xbinary-io.h"
-
-/* The official name of this program (e.g., no 'g' prefix). */
-#if HASH_ALGO_SUM
-# define PROGRAM_NAME "sum"
-# define DIGEST_TYPE_STRING "BSD"
-# define DIGEST_STREAM sumfns[sum_algorithm]
-# define DIGEST_OUT sum_output_fns[sum_algorithm]
-# define DIGEST_BITS 16
-# define DIGEST_ALIGN 4
-#elif HASH_ALGO_CKSUM
-# define MAX_DIGEST_BITS 512
-# define MAX_DIGEST_ALIGN 8
-# define PROGRAM_NAME "cksum"
-# define DIGEST_TYPE_STRING algorithm_tags[cksum_algorithm]
-# define DIGEST_STREAM cksumfns[cksum_algorithm]
-# define DIGEST_OUT cksum_output_fns[cksum_algorithm]
-# define DIGEST_BITS MAX_DIGEST_BITS
-# define DIGEST_ALIGN MAX_DIGEST_ALIGN
-#elif HASH_ALGO_MD5
-# define PROGRAM_NAME "md5sum"
-# define DIGEST_TYPE_STRING "MD5"
-# define DIGEST_STREAM md5_stream
-# define DIGEST_BITS 128
-# define DIGEST_REFERENCE "RFC 1321"
-# define DIGEST_ALIGN 4
-#elif HASH_ALGO_BLAKE2
-# define PROGRAM_NAME "b2sum"
-# define DIGEST_TYPE_STRING "BLAKE2b"
-# define DIGEST_STREAM blake2b_stream
-# define DIGEST_BITS 512
-# define DIGEST_REFERENCE "RFC 7693"
-# define DIGEST_ALIGN 8
-#elif HASH_ALGO_SHA1
-# define PROGRAM_NAME "sha1sum"
-# define DIGEST_TYPE_STRING "SHA1"
-# define DIGEST_STREAM sha1_stream
-# define DIGEST_BITS 160
-# define DIGEST_REFERENCE "FIPS-180-1"
-# define DIGEST_ALIGN 4
-#elif HASH_ALGO_SHA256
-# define PROGRAM_NAME "sha256sum"
-# define DIGEST_TYPE_STRING "SHA256"
-# define DIGEST_STREAM sha256_stream
-# define DIGEST_BITS 256
-# define DIGEST_REFERENCE "FIPS-180-2"
-# define DIGEST_ALIGN 4
-#elif HASH_ALGO_SHA224
-# define PROGRAM_NAME "sha224sum"
-# define DIGEST_TYPE_STRING "SHA224"
-# define DIGEST_STREAM sha224_stream
-# define DIGEST_BITS 224
-# define DIGEST_REFERENCE "RFC 3874"
-# define DIGEST_ALIGN 4
-#elif HASH_ALGO_SHA512
-# define PROGRAM_NAME "sha512sum"
-# define DIGEST_TYPE_STRING "SHA512"
-# define DIGEST_STREAM sha512_stream
-# define DIGEST_BITS 512
-# define DIGEST_REFERENCE "FIPS-180-2"
-# define DIGEST_ALIGN 8
-#elif HASH_ALGO_SHA384
-# define PROGRAM_NAME "sha384sum"
-# define DIGEST_TYPE_STRING "SHA384"
-# define DIGEST_STREAM sha384_stream
-# define DIGEST_BITS 384
-# define DIGEST_REFERENCE "FIPS-180-2"
-# define DIGEST_ALIGN 8
-#else
-# error "Can't decide which hash algorithm to compile."
-#endif
-#if !HASH_ALGO_SUM && !HASH_ALGO_CKSUM
-# define DIGEST_OUT output_file
-#endif
-
-#if HASH_ALGO_SUM
-# define AUTHORS \
- proper_name ("Kayvan Aghaiepour"), \
- proper_name ("David MacKenzie")
-#elif HASH_ALGO_CKSUM
-# define AUTHORS \
- proper_name_lite ("Padraig Brady", "P\303\241draig Brady"), \
- proper_name ("Q. Frank Xia")
-#elif HASH_ALGO_BLAKE2
-# define AUTHORS \
- proper_name_lite ("Padraig Brady", "P\303\241draig Brady"), \
- proper_name ("Samuel Neves")
-#else
-# define AUTHORS \
- proper_name ("Ulrich Drepper"), \
- proper_name ("Scott Miller"), \
- proper_name ("David Madore")
-#endif
-#if !HASH_ALGO_BLAKE2 && !HASH_ALGO_CKSUM
-# define DIGEST_HEX_BYTES (DIGEST_BITS / 4)
-#endif
-#define DIGEST_BIN_BYTES (DIGEST_BITS / 8)
-
-/* The minimum length of a valid digest line. This length does
- not include any newline character at the end of a line. */
-#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
-# define MIN_DIGEST_LINE_LENGTH 3 /* With -l 8. */
-#else
-# define MIN_DIGEST_LINE_LENGTH \
- (DIGEST_HEX_BYTES /* length of hexadecimal message digest */ \
- + 1 /* blank */ \
- + 1 /* minimum filename length */ )
-#endif
-
-#if !HASH_ALGO_SUM
-static void
-output_file (char const *file, int binary_file, void const *digest,
- bool raw, bool tagged, unsigned char delim, bool args,
- uintmax_t length);
-#endif
-
-/* True if any of the files read were the standard input. */
-static bool have_read_stdin;
-
-/* The minimum length of a valid checksum line for the selected algorithm. */
-static size_t min_digest_line_length;
-
-/* Set to the length of a digest hex string for the selected algorithm. */
-static size_t digest_hex_bytes;
-
-/* With --check, don't generate any output.
- The exit code indicates success or failure. */
-static bool status_only = false;
-
-/* With --check, print a message to standard error warning about each
- improperly formatted checksum line. */
-static bool warn = false;
-
-/* With --check, ignore missing files. */
-static bool ignore_missing = false;
-
-/* With --check, suppress the "OK" printed for each verified file. */
-static bool quiet = false;
-
-/* With --check, exit with a non-zero return code if any line is
- improperly formatted. */
-static bool strict = false;
-
-/* Whether a BSD reversed format checksum is detected. */
-static int bsd_reversed = -1;
-
-/* line delimiter. */
-static unsigned char digest_delim = '\n';
-
-#if HASH_ALGO_CKSUM
-/* If true, print base64-encoded digests, not hex. */
-static bool base64_digest = false;
-#endif
-
-/* If true, print binary digests, not hex. */
-static bool raw_digest = false;
-
-/* blake2b and sha3 allow the -l option. Luckily they both have the same
- maximum digest size. */
-#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
-# if HASH_ALGO_CKSUM
-static_assert (BLAKE2B_OUTBYTES == SHA3_512_DIGEST_SIZE);
-# endif
-# define DIGEST_MAX_LEN BLAKE2B_OUTBYTES
-static uintmax_t digest_length;
-#endif /* HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM */
-
-typedef void (*digest_output_fn)(char const *, int, void const *, bool,
- bool, unsigned char, bool, uintmax_t);
-#if HASH_ALGO_SUM
-enum Algorithm
-{
- bsd,
- sysv,
-};
-
-static enum Algorithm sum_algorithm;
-static sumfn sumfns[]=
-{
- bsd_sum_stream,
- sysv_sum_stream,
-};
-static digest_output_fn sum_output_fns[]=
-{
- output_bsd,
- output_sysv,
-};
-#endif
-
-#if HASH_ALGO_CKSUM
-static int
-md5_sum_stream (FILE *stream, void *resstream, MAYBE_UNUSED uintmax_t *length)
-{
- return md5_stream (stream, resstream);
-}
-static int
-sha1_sum_stream (FILE *stream, void *resstream, MAYBE_UNUSED uintmax_t *length)
-{
- return sha1_stream (stream, resstream);
-}
-static int
-sha224_sum_stream (FILE *stream, void *resstream,
- MAYBE_UNUSED uintmax_t *length)
-{
- return sha224_stream (stream, resstream);
-}
-static int
-sha256_sum_stream (FILE *stream, void *resstream,
- MAYBE_UNUSED uintmax_t *length)
-{
- return sha256_stream (stream, resstream);
-}
-static int
-sha384_sum_stream (FILE *stream, void *resstream,
- MAYBE_UNUSED uintmax_t *length)
-{
- return sha384_stream (stream, resstream);
-}
-static int
-sha512_sum_stream (FILE *stream, void *resstream,
- MAYBE_UNUSED uintmax_t *length)
-{
- return sha512_stream (stream, resstream);
-}
-static int
-sha2_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
-{
- switch (*length)
- {
- case SHA224_DIGEST_SIZE:
- return sha224_stream (stream, resstream);
- case SHA256_DIGEST_SIZE:
- return sha256_stream (stream, resstream);
- case SHA384_DIGEST_SIZE:
- return sha384_stream (stream, resstream);
- case SHA512_DIGEST_SIZE:
- return sha512_stream (stream, resstream);
- default:
- affirm (false);
- }
-}
-static int
-sha3_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
-{
- switch (*length)
- {
- case SHA3_224_DIGEST_SIZE:
- return sha3_224_stream (stream, resstream);
- case SHA3_256_DIGEST_SIZE:
- return sha3_256_stream (stream, resstream);
- case SHA3_384_DIGEST_SIZE:
- return sha3_384_stream (stream, resstream);
- case SHA3_512_DIGEST_SIZE:
- return sha3_512_stream (stream, resstream);
- default:
- affirm (false);
- }
-}
-static int
-blake2b_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
-{
- return blake2b_stream (stream, resstream, *length);
-}
-static int
-sm3_sum_stream (FILE *stream, void *resstream, MAYBE_UNUSED uintmax_t *length)
-{
- return sm3_stream (stream, resstream);
-}
-
-enum Algorithm
-{
- bsd,
- sysv,
- crc,
- crc32b,
- md5,
- sha1,
- sha224,
- sha256,
- sha384,
- sha512,
- sha2,
- sha3,
- blake2b,
- sm3,
-};
-
-static char const *const algorithm_args[] =
-{
- "bsd", "sysv", "crc", "crc32b", "md5", "sha1",
- "sha224", "sha256", "sha384", "sha512", /* Legacy naming */
- "sha2", "sha3", "blake2b", "sm3", nullptr
-};
-static enum Algorithm const algorithm_types[] =
-{
- bsd, sysv, crc, crc32b, md5, sha1,
- sha224, sha256, sha384, sha512,
- sha2, sha3, blake2b, sm3,
-};
-ARGMATCH_VERIFY (algorithm_args, algorithm_types);
-
-static char const *const algorithm_tags[] =
-{
- "BSD", "SYSV", "CRC", "CRC32B", "MD5", "SHA1",
- "SHA224", "SHA256", "SHA384", "SHA512",
- "SHA2", "SHA3", "BLAKE2b", "SM3", nullptr
-};
-static int const algorithm_bits[] =
-{
- 16, 16, 32, 32, 128, 160,
- 224, 256, 384, 512,
- 512, 512, 512, 256, 0
-};
-
-static_assert (countof (algorithm_bits) == countof (algorithm_args));
-
-static bool algorithm_specified = false;
-static enum Algorithm cksum_algorithm = crc;
-static sumfn cksumfns[]=
-{
- bsd_sum_stream,
- sysv_sum_stream,
- crc_sum_stream,
- crc32b_sum_stream,
- md5_sum_stream,
- sha1_sum_stream,
- sha224_sum_stream,
- sha256_sum_stream,
- sha384_sum_stream,
- sha512_sum_stream,
- sha2_sum_stream,
- sha3_sum_stream,
- blake2b_sum_stream,
- sm3_sum_stream,
-};
-static digest_output_fn cksum_output_fns[]=
-{
- output_bsd,
- output_sysv,
- output_crc,
- output_crc,
- output_file,
- output_file,
- output_file,
- output_file,
- output_file,
- output_file,
- output_file,
- output_file,
- output_file,
- output_file,
-};
-bool cksum_debug;
-#endif
-
-/* For long options that have no equivalent short option, use a
- non-character as a pseudo short option, starting with CHAR_MAX + 1. */
-
-enum
-{
- IGNORE_MISSING_OPTION = CHAR_MAX + 1,
- STATUS_OPTION,
- QUIET_OPTION,
- STRICT_OPTION,
- TAG_OPTION,
- UNTAG_OPTION,
- DEBUG_PROGRAM_OPTION,
- RAW_OPTION,
- BASE64_OPTION,
-};
-
-static struct option const long_options[] =
-{
-#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
- { "length", required_argument, nullptr, 'l'},
-#endif
-
-#if !HASH_ALGO_SUM
- { "check", no_argument, nullptr, 'c' },
- { "ignore-missing", no_argument, nullptr, IGNORE_MISSING_OPTION},
- { "quiet", no_argument, nullptr, QUIET_OPTION },
- { "status", no_argument, nullptr, STATUS_OPTION },
- { "warn", no_argument, nullptr, 'w' },
- { "strict", no_argument, nullptr, STRICT_OPTION },
- { "tag", no_argument, nullptr, TAG_OPTION },
- { "zero", no_argument, nullptr, 'z' },
-
-# if HASH_ALGO_CKSUM
- { "algorithm", required_argument, nullptr, 'a'},
- { "base64", no_argument, nullptr, BASE64_OPTION },
- { "debug", no_argument, nullptr, DEBUG_PROGRAM_OPTION},
- { "raw", no_argument, nullptr, RAW_OPTION},
- { "untagged", no_argument, nullptr, UNTAG_OPTION },
-# endif
- { "binary", no_argument, nullptr, 'b' },
- { "text", no_argument, nullptr, 't' },
-
-#else
- {"sysv", no_argument, nullptr, 's'},
-#endif
-
- { GETOPT_HELP_OPTION_DECL },
- { GETOPT_VERSION_OPTION_DECL },
- { nullptr, 0, nullptr, 0 }
-};
-
-void
-usage (int status)
-{
- if (status != EXIT_SUCCESS)
- emit_try_help ();
- else
- {
- printf (_("\
-Usage: %s [OPTION]... [FILE]...\n\
-"), program_name);
-#if HASH_ALGO_CKSUM
- fputs (_("\
-Print or verify checksums.\n\
-By default use the 32 bit CRC algorithm.\n\
-"), stdout);
-#else
- printf (_("\
-Print or check %s (%d-bit) checksums.\n\
-"),
- DIGEST_TYPE_STRING,
- DIGEST_BITS);
-#endif
-
- emit_stdin_note ();
-#if HASH_ALGO_SUM
- fputs (_("\
-\n\
- -r use BSD sum algorithm (the default), use 1K blocks\n\
- -s, --sysv use System V sum algorithm, use 512 bytes blocks\n\
-"), stdout);
-#endif
-#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
- emit_mandatory_arg_note ();
-#endif
-#if HASH_ALGO_CKSUM
- fputs (_("\
- -a, --algorithm=TYPE select the digest type to use. See DIGEST below\
-\n\
-"), stdout);
- fputs (_("\
- --base64 emit base64-encoded digests, not hexadecimal\
-\n\
-"), stdout);
-#endif
-#if !HASH_ALGO_SUM
-# if !HASH_ALGO_CKSUM
- if (O_BINARY)
- fputs (_("\
- -b, --binary read in binary mode (default unless reading tty stdin)\
-\n\
-"), stdout);
- else
- fputs (_("\
- -b, --binary read in binary mode\n\
-"), stdout);
-# endif
- fputs (_("\
- -c, --check read checksums from the FILEs and check them\n\
-"), stdout);
-# if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
- fputs (_("\
- -l, --length=BITS digest length in bits; must not exceed the max size\n\
- and must be a multiple of 8 for blake2b;\n\
- must be 224, 256, 384, or 512 for sha2 or sha3\n\
-"), stdout);
-# endif
-# if HASH_ALGO_CKSUM
- fputs (_("\
- --raw emit a raw binary digest, not hexadecimal\
-\n\
-"), stdout);
- fputs (_("\
- --tag create a BSD-style checksum (the default)\n\
-"), stdout);
- fputs (_("\
- --untagged create a reversed style checksum, without digest type\n\
-"), stdout);
-# else
- fputs (_("\
- --tag create a BSD-style checksum\n\
-"), stdout);
-# endif
-# if !HASH_ALGO_CKSUM
- if (O_BINARY)
- fputs (_("\
- -t, --text read in text mode (default if reading tty stdin)\n\
-"), stdout);
- else
- fputs (_("\
- -t, --text read in text mode (default)\n\
-"), stdout);
-# endif
- fputs (_("\
- -z, --zero end each output line with NUL, not newline,\n\
- and disable file name escaping\n\
-"), stdout);
- fputs (_("\
-\n\
-The following five options are useful only when verifying checksums:\n\
- --ignore-missing don't fail or report status for missing files\n\
- --quiet don't print OK for each successfully verified file\n\
- --status don't output anything, status code shows success\n\
- --strict exit non-zero for improperly formatted checksum lines\n\
- -w, --warn warn about improperly formatted checksum lines\n\
-\n\
-"), stdout);
-#endif
-#if HASH_ALGO_CKSUM
- fputs (_("\
- --debug indicate which implementation used\n\
-"), stdout);
-#endif
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
-#if HASH_ALGO_CKSUM
- fputs (_("\
-\n\
-DIGEST determines the digest algorithm and default output format:\n\
- sysv (equivalent to sum -s)\n\
- bsd (equivalent to sum -r)\n\
- crc (equivalent to cksum)\n\
- crc32b (only available through cksum)\n\
- md5 (equivalent to md5sum)\n\
- sha1 (equivalent to sha1sum)\n\
- sha2 (equivalent to sha{224,256,384,512}sum)\n\
- sha3 (only available through cksum)\n\
- blake2b (equivalent to b2sum)\n\
- sm3 (only available through cksum)\n\
-\n"), stdout);
-#endif
-#if !HASH_ALGO_SUM && !HASH_ALGO_CKSUM
- printf (_("\
-\n\
-The sums are computed as described in %s.\n"), DIGEST_REFERENCE);
- fputs (_("\
-When checking, the input should be a former output of this program.\n\
-The default mode is to print a line with: checksum, a space,\n\
-a character indicating input mode ('*' for binary, ' ' for text\n\
-or where binary is insignificant), and name for each FILE.\n\
-\n\
-There is no difference between binary mode and text mode on GNU systems.\
-\n"), stdout);
-#endif
-#if HASH_ALGO_CKSUM
- fputs (_("\
-When checking, the input should be a former output of this program,\n\
-or equivalent standalone program.\
-\n"), stdout);
-#endif
- emit_ancillary_info (PROGRAM_NAME);
- }
-
- exit (status);
-}
-
-/* Given a string S, return TRUE if it contains problematic characters
- that need escaping. Note we escape '\' itself to provide some forward
- compat to introduce escaping of other characters. */
-
-ATTRIBUTE_PURE
-static bool
-problematic_chars (char const *s)
-{
- size_t length = strcspn (s, "\\\n\r");
- return s[length] != '\0';
-}
-
-#define ISWHITE(c) ((c) == ' ' || (c) == '\t')
-
-/* Given a file name, S of length S_LEN, that is not NUL-terminated,
- modify it in place, performing the equivalent of this sed substitution:
- 's/\\n/\n/g;s/\\r/\r/g;s/\\\\/\\/g' i.e., replacing each "\\n" string
- with a newline, each "\\r" string with a carriage return,
- and each "\\\\" with a single backslash, NUL-terminate it and return S.
- If S is not a valid escaped file name, i.e., if it ends with an odd number
- of backslashes or if it contains a backslash followed by anything other
- than "n" or another backslash, return nullptr. */
-
-static char *
-filename_unescape (char *s, size_t s_len)
-{
- char *dst = s;
-
- for (size_t i = 0; i < s_len; i++)
- {
- switch (s[i])
- {
- case '\\':
- if (i == s_len - 1)
- {
- /* File name ends with an unescaped backslash: invalid. */
- return nullptr;
- }
- ++i;
- switch (s[i])
- {
- case 'n':
- *dst++ = '\n';
- break;
- case 'r':
- *dst++ = '\r';
- break;
- case '\\':
- *dst++ = '\\';
- break;
- default:
- /* Only '\', 'n' or 'r' may follow a backslash. */
- return nullptr;
- }
- break;
-
- case '\0':
- /* The file name may not contain a NUL. */
- return nullptr;
-
- default:
- *dst++ = s[i];
- break;
- }
- }
- if (dst < s + s_len)
- *dst = '\0';
-
- return s;
-}
-
-/* Return true if S is a LEN-byte NUL-terminated string of hex or base64
- digits and has the expected length. Otherwise, return false. */
-ATTRIBUTE_PURE
-static bool
-valid_digits (unsigned char const *s, size_t len)
-{
-#if HASH_ALGO_CKSUM
- if (len == BASE64_LENGTH (digest_length / 8))
- {
- size_t i;
- for (i = 0; i < len - digest_length % 3; i++)
- {
- if (!isbase64 (*s))
- return false;
- ++s;
- }
- for ( ; i < len; i++)
- {
- if (*s != '=')
- return false;
- ++s;
- }
- }
- else
-#endif
- if (len == digest_hex_bytes)
- {
- for (idx_t i = 0; i < digest_hex_bytes; i++)
- {
- if (!c_isxdigit (*s))
- return false;
- ++s;
- }
- }
- else
- return false;
-
- return *s == '\0';
-}
-
-/* Split the checksum string S (of length S_LEN) from a BSD 'md5' or
- 'sha1' command into two parts: a hexadecimal digest, and the file
- name. S is modified. Set *D_LEN to the length of the digest string.
- Return true if successful. */
-
-static bool
-bsd_split_3 (char *s, size_t s_len,
- unsigned char **digest, size_t *d_len,
- char **file_name, bool escaped_filename)
-{
- if (s_len == 0)
- return false;
-
- /* Find end of filename. */
- size_t i = s_len - 1;
- while (i && s[i] != ')')
- i--;
-
- if (s[i] != ')')
- return false;
-
- *file_name = s;
-
- if (escaped_filename && filename_unescape (s, i) == nullptr)
- return false;
-
- s[i++] = '\0';
-
- while (ISWHITE (s[i]))
- i++;
-
- if (s[i] != '=')
- return false;
-
- i++;
-
- while (ISWHITE (s[i]))
- i++;
-
- *digest = (unsigned char *) &s[i];
-
- *d_len = s_len - i;
- return valid_digits (*digest, *d_len);
-}
-
-#if HASH_ALGO_CKSUM
-/* Return the corresponding Algorithm for the string S,
- or -1 for no match. */
-
-static ptrdiff_t
-algorithm_from_tag (char *s)
-{
- /* Limit check size to this length for perf reasons. */
- static size_t max_tag_len;
- if (! max_tag_len)
- {
- char const * const * tag = algorithm_tags;
- while (*tag)
- {
- size_t tag_len = strlen (*tag++);
- max_tag_len = MAX (tag_len, max_tag_len);
- }
- }
-
- size_t i = 0;
-
- /* Find end of tag */
- while (i <= max_tag_len && s[i] && ! ISWHITE (s[i])
- && s[i] != '-' && s[i] != '(')
- ++i;
-
- if (i > max_tag_len)
- return -1;
-
- /* Terminate tag, and lookup. */
- char sep = s[i];
- s[i] = '\0';
- ptrdiff_t algo = argmatch_exact (s, algorithm_tags);
- s[i] = sep;
-
- return algo;
-}
-#endif
-
-/* Split the string S (of length S_LEN) into three parts:
- a hexadecimal digest, binary flag, and the file name.
- S is modified. Set *D_LEN to the length of the digest string.
- Return true if successful. */
-
-static bool
-split_3 (char *s, size_t s_len,
- unsigned char **digest, size_t *d_len, int *binary, char **file_name)
-{
- bool escaped_filename = false;
- size_t algo_name_len;
-
- size_t i = 0;
- while (ISWHITE (s[i]))
- ++i;
-
- if (s[i] == '\\')
- {
- ++i;
- escaped_filename = true;
- }
-
- /* Check for BSD-style checksum line. */
-
-#if HASH_ALGO_CKSUM
- if (! algorithm_specified || cksum_algorithm == sha2)
- {
- ptrdiff_t algo_tag = algorithm_from_tag (s + i);
- if (! algorithm_specified)
- {
- if (algo_tag >= 0)
- {
- if (algo_tag <= crc32b)
- return false; /* We don't support checking these formats. */
- cksum_algorithm = algo_tag;
- }
- else
- return false; /* We only support tagged format without -a. */
- }
- else
- {
- if (cksum_algorithm == sha2 && (algo_tag == sha2
- || algo_tag == sha224 || algo_tag == sha256
- || algo_tag == sha384 || algo_tag == sha512))
- cksum_algorithm = algo_tag;
- }
- }
-#endif
-
- /* Try to parse BSD or OpenSSL tagged format. I.e.:
- openssl: MD5(f)= d41d8cd98f00b204e9800998ecf8427e
- bsd: MD5 (f) = d41d8cd98f00b204e9800998ecf8427e */
-
- size_t parse_offset = i;
- algo_name_len = strlen (DIGEST_TYPE_STRING);
- if (STREQ_LEN (s + i, DIGEST_TYPE_STRING, algo_name_len))
- {
- i += algo_name_len;
-#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
-
-# if HASH_ALGO_BLAKE2
- digest_length = DIGEST_MAX_LEN * 8;
-# else
- digest_length = algorithm_bits[cksum_algorithm];
-# endif
- if (s[i] == '-') /* length specified. Not base64 */
- {
- ++i;
- uintmax_t length;
- char *siend;
- if (xstrtoumax (s + i, &siend, 0, &length, nullptr) != LONGINT_OK)
- return false;
-# if HASH_ALGO_CKSUM
- else if (cksum_algorithm == sha2 || cksum_algorithm == sha3)
- {
- if (length != SHA224_DIGEST_SIZE * 8
- && length != SHA256_DIGEST_SIZE * 8
- && length != SHA384_DIGEST_SIZE * 8
- && length != SHA512_DIGEST_SIZE * 8)
- return false;
- }
-# endif
- else if (!(0 < length && length <= digest_length && length % 8 == 0))
- return false;
-
- i = siend - s;
- digest_length = length;
- }
- digest_hex_bytes = digest_length / 4;
-#endif
- if (s[i] == ' ')
- ++i;
- if (s[i] == '(') /* not base64 */
- {
- ++i;
- *binary = 0;
- return bsd_split_3 (s + i, s_len - i,
- digest, d_len, file_name, escaped_filename);
- }
-
- /* Note with --base64 --untagged format, we may have matched a "tag".
- Even very short digests with: cksum -a blake2b -l24 --untagged --base64
- So fallback to checking untagged format if issues detecting tags. */
- i = parse_offset;
- }
-
- /* Ignore this line if it is too short.
- Each line must have at least 'min_digest_line_length - 1' (or one more, if
- the first is a backslash) more characters to contain correct message digest
- information. */
- if (s_len - i < min_digest_line_length + (s[i] == '\\'))
- return false;
-
- *digest = (unsigned char *) &s[i];
-
-#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
- /* Auto determine length. */
-# if HASH_ALGO_CKSUM
- if (cksum_algorithm == blake2b
- || cksum_algorithm == sha2 || cksum_algorithm == sha3) {
-# endif
- unsigned char const *hp = *digest;
- digest_hex_bytes = 0;
- for (; c_isxdigit (*hp); ++hp, ++digest_hex_bytes)
- ;
-# if HASH_ALGO_CKSUM
- /* Check the number of base64 characters. This works because the hexadecimal
- character set is a subset of the base64 character set.
- Note there is the ambiguity that all characters are hex when they
- are actually base64 encoded, which could be ambiguous with:
- cksum -a sha2 -l 384 --base64 --untagged
- cksum -a sha2 -l 256 --untagged
- Similarly for sha3 and blake2b.
- However at this length the chances are exceedingly rare (1 in 480R),
- and smaller blake2b lengths aren't practical for verification anyway. */
- size_t digest_base64_bytes = digest_hex_bytes;
- size_t trailing_equals = 0;
- for (; isubase64 (*hp); ++hp, ++digest_base64_bytes)
- ;
- for (; *hp == '='; ++hp, ++trailing_equals)
- ;
- if ((cksum_algorithm == sha2 || cksum_algorithm == sha3)
- && digest_hex_bytes / 2 != SHA224_DIGEST_SIZE
- && digest_hex_bytes / 2 != SHA256_DIGEST_SIZE
- && digest_hex_bytes / 2 != SHA384_DIGEST_SIZE
- && digest_hex_bytes / 2 != SHA512_DIGEST_SIZE)
- {
- if (digest_base64_bytes + trailing_equals
- == BASE64_LENGTH (SHA224_DIGEST_SIZE))
- digest_hex_bytes = SHA224_DIGEST_SIZE * 2;
- else if (digest_base64_bytes + trailing_equals
- == BASE64_LENGTH (SHA256_DIGEST_SIZE))
- digest_hex_bytes = SHA256_DIGEST_SIZE * 2;
- else if (digest_base64_bytes + trailing_equals
- == BASE64_LENGTH (SHA384_DIGEST_SIZE))
- digest_hex_bytes = SHA384_DIGEST_SIZE * 2;
- else if (digest_base64_bytes + trailing_equals
- == BASE64_LENGTH (SHA512_DIGEST_SIZE))
- digest_hex_bytes = SHA512_DIGEST_SIZE * 2;
- else
- return false;
- }
- else if (cksum_algorithm == blake2b
- && digest_hex_bytes < digest_base64_bytes)
- {
- for (int j = 8; j <= DIGEST_MAX_LEN * 8; j += 8)
- {
- if (BASE64_LENGTH (j / 8) == digest_base64_bytes + trailing_equals
- && j % 3 == trailing_equals)
- {
- digest_hex_bytes = j / 4;
- break;
- }
- }
- }
-# endif
- if (digest_hex_bytes < 2 || digest_hex_bytes % 2
- || DIGEST_MAX_LEN * 2 < digest_hex_bytes)
- return false;
- digest_length = digest_hex_bytes * 4;
-# if HASH_ALGO_CKSUM
- }
-# endif
-#endif
-
- /* This field must be the hexadecimal or base64 representation
- of the message digest. */
- while (s[i] && !ISWHITE (s[i]))
- i++;
-
- /* The digest must be followed by at least one whitespace character. */
- if (i == s_len)
- return false;
-
- *d_len = &s[i] - (char *) *digest;
- s[i++] = '\0';
-
- if (! valid_digits (*digest, *d_len))
- return false;
-
- /* If "bsd reversed" format detected. */
- if ((s_len - i == 1) || (s[i] != ' ' && s[i] != '*'))
- {
- /* Don't allow mixing bsd and standard formats,
- to minimize security issues with attackers
- renaming files with leading spaces.
- This assumes that with bsd format checksums
- that the first file name does not have
- a leading ' ' or '*'. */
- if (bsd_reversed == 0)
- return false;
- bsd_reversed = 1;
- }
- else if (bsd_reversed != 1)
- {
- bsd_reversed = 0;
- *binary = (s[i++] == '*');
- }
-
- /* All characters between the type indicator and end of line are
- significant -- that includes leading and trailing white space. */
- *file_name = &s[i];
-
- if (escaped_filename)
- return filename_unescape (&s[i], s_len - i) != nullptr;
-
- return true;
-}
-
-/* If ESCAPE is true, then translate each:
- NEWLINE byte to the string, "\\n",
- CARRIAGE RETURN byte to the string, "\\r",
- and each backslash to "\\\\". */
-static void
-print_filename (char const *file, bool escape)
-{
- if (! escape)
- {
- fputs (file, stdout);
- return;
- }
-
- while (*file)
- {
- switch (*file)
- {
- case '\n':
- fputs ("\\n", stdout);
- break;
-
- case '\r':
- fputs ("\\r", stdout);
- break;
-
- case '\\':
- fputs ("\\\\", stdout);
- break;
-
- default:
- putchar (*file);
- break;
- }
- file++;
- }
-}
-
-/* An interface to the function, DIGEST_STREAM.
- Operate on FILENAME (it may be "-").
-
- *BINARY indicates whether the file is binary. BINARY < 0 means it
- depends on whether binary mode makes any difference and the file is
- a terminal; in that case, clear *BINARY if the file was treated as
- text because it was a terminal.
-
- Put the checksum in *BIN_RESULT, which must be properly aligned.
- Put true in *MISSING if the file can't be opened due to ENOENT.
- Return true if successful. */
-
-static bool
-digest_file (char const *filename, int *binary, unsigned char *bin_result,
- bool *missing, MAYBE_UNUSED uintmax_t *length)
-{
- FILE *fp;
- int err;
- bool is_stdin = streq (filename, "-");
-
- *missing = false;
-
- if (is_stdin)
- {
- have_read_stdin = true;
- fp = stdin;
- if (O_BINARY && *binary)
- {
- if (*binary < 0)
- *binary = ! isatty (STDIN_FILENO);
- if (*binary)
- xset_binary_mode (STDIN_FILENO, O_BINARY);
- }
- }
- else
- {
- fp = fopen (filename, (O_BINARY && *binary ? "rb" : "r"));
- if (fp == nullptr)
- {
- if (ignore_missing && errno == ENOENT)
- {
- *missing = true;
- return true;
- }
- error (0, errno, "%s", quotef (filename));
- return false;
- }
- }
-
- fadvise (fp, FADVISE_SEQUENTIAL);
-
-#if HASH_ALGO_CKSUM
- if (cksum_algorithm == blake2b
- || cksum_algorithm == sha2 || cksum_algorithm == sha3)
- *length = digest_length / 8;
- err = DIGEST_STREAM (fp, bin_result, length);
-#elif HASH_ALGO_SUM
- err = DIGEST_STREAM (fp, bin_result, length);
-#elif HASH_ALGO_BLAKE2
- err = DIGEST_STREAM (fp, bin_result, digest_length / 8);
-#else
- err = DIGEST_STREAM (fp, bin_result);
-#endif
- err = err ? errno : 0;
- if (is_stdin)
- clearerr (fp);
- else if (fclose (fp) != 0 && !err)
- err = errno;
-
- if (err)
- {
- error (0, err, "%s", quotef (filename));
- return false;
- }
-
- return true;
-}
-
-#if !HASH_ALGO_SUM
-static void
-output_file (char const *file, int binary_file, void const *digest,
- MAYBE_UNUSED bool raw, bool tagged, unsigned char delim,
- MAYBE_UNUSED bool args, MAYBE_UNUSED uintmax_t length)
-{
-# if HASH_ALGO_CKSUM
- if (raw)
- {
- fwrite (digest, 1, digest_length / 8, stdout);
- return;
- }
-# endif
-
- unsigned char const *bin_buffer = digest;
-
- /* Output a leading backslash if the file name contains problematic chars. */
- bool needs_escape = delim == '\n' && problematic_chars (file);
-
- if (needs_escape)
- putchar ('\\');
-
- if (tagged)
- {
-# if HASH_ALGO_CKSUM
- if (cksum_algorithm == sha2)
- printf ("SHA%ju", digest_length);
- else
-# endif
- fputs (DIGEST_TYPE_STRING, stdout);
-# if HASH_ALGO_BLAKE2
- if (digest_length < DIGEST_MAX_LEN * 8)
- printf ("-%ju", digest_length);
-# elif HASH_ALGO_CKSUM
- if (cksum_algorithm == sha3)
- printf ("-%ju", digest_length);
- if (cksum_algorithm == blake2b)
- {
- if (digest_length < DIGEST_MAX_LEN * 8)
- printf ("-%ju", digest_length);
- }
-# endif
- fputs (" (", stdout);
- print_filename (file, needs_escape);
- fputs (") = ", stdout);
- }
-
-# if HASH_ALGO_CKSUM
- if (base64_digest)
- {
- char b64[BASE64_LENGTH (DIGEST_BIN_BYTES) + 1];
- base64_encode ((char const *) bin_buffer, digest_length / 8,
- b64, sizeof b64);
- fputs (b64, stdout);
- }
- else
-# endif
- {
- for (size_t i = 0; i < (digest_hex_bytes / 2); ++i)
- printf ("%02x", bin_buffer[i]);
- }
-
- if (!tagged)
- {
- putchar (' ');
- putchar (binary_file ? '*' : ' ');
- print_filename (file, needs_escape);
- }
-
- putchar (delim);
-}
-#endif
-
-#if HASH_ALGO_CKSUM
-/* Return true if B64_DIGEST is the same as the base64 digest of the
- DIGEST_LENGTH/8 bytes at BIN_BUFFER. */
-static bool
-b64_equal (unsigned char const *b64_digest, unsigned char const *bin_buffer)
-{
- size_t b64_n_bytes = BASE64_LENGTH (digest_length / 8);
- char b64[BASE64_LENGTH (DIGEST_BIN_BYTES) + 1];
- base64_encode ((char const *) bin_buffer, digest_length / 8, b64, sizeof b64);
- return memeq (b64_digest, b64, b64_n_bytes + 1);
-}
-#endif
-
-/* Return true if HEX_DIGEST is the same as the hex-encoded digest of the
- DIGEST_LENGTH/8 bytes at BIN_BUFFER. */
-static bool
-hex_equal (unsigned char const *hex_digest, unsigned char const *bin_buffer)
-{
- static const char bin2hex[] = { '0', '1', '2', '3',
- '4', '5', '6', '7',
- '8', '9', 'a', 'b',
- 'c', 'd', 'e', 'f' };
- size_t digest_bin_bytes = digest_hex_bytes / 2;
-
- /* Compare generated binary number with text representation
- in check file. Ignore case of hex digits. */
- size_t cnt;
- for (cnt = 0; cnt < digest_bin_bytes; ++cnt)
- {
- if (c_tolower (hex_digest[2 * cnt])
- != bin2hex[bin_buffer[cnt] >> 4]
- || (c_tolower (hex_digest[2 * cnt + 1])
- != (bin2hex[bin_buffer[cnt] & 0xf])))
- break;
- }
- return cnt == digest_bin_bytes;
-}
-
-static bool
-digest_check (char const *checkfile_name)
-{
- FILE *checkfile_stream;
- uintmax_t n_misformatted_lines = 0;
- uintmax_t n_mismatched_checksums = 0;
- uintmax_t n_open_or_read_failures = 0;
- bool properly_formatted_lines = false;
- bool matched_checksums = false;
- unsigned char bin_buffer_unaligned[DIGEST_BIN_BYTES + DIGEST_ALIGN];
- /* Make sure bin_buffer is properly aligned. */
- unsigned char *bin_buffer = ptr_align (bin_buffer_unaligned, DIGEST_ALIGN);
- uintmax_t line_number;
- char *line;
- size_t line_chars_allocated;
- bool is_stdin = streq (checkfile_name, "-");
-
- if (is_stdin)
- {
- have_read_stdin = true;
- checkfile_name = _("standard input");
- checkfile_stream = stdin;
- }
- else
- {
- checkfile_stream = fopen (checkfile_name, "r");
- if (checkfile_stream == nullptr)
- {
- error (0, errno, "%s", quotef (checkfile_name));
- return false;
- }
- }
-
- line_number = 0;
- line = nullptr;
- line_chars_allocated = 0;
- do
- {
- char *filename;
- int binary;
- unsigned char *digest;
- ssize_t line_length;
-
- ++line_number;
- if (line_number == 0)
- error (EXIT_FAILURE, 0, _("%s: too many checksum lines"),
- quotef (checkfile_name));
-
- line_length = getline (&line, &line_chars_allocated, checkfile_stream);
- if (line_length <= 0)
- break;
-
- /* Ignore comment lines, which begin with a '#' character. */
- if (line[0] == '#')
- continue;
-
- /* Remove any trailing newline. */
- line_length -= line[line_length - 1] == '\n';
- /* Remove any trailing carriage return. */
- line_length -= line[line_length - (0 < line_length)] == '\r';
-
- /* Ignore empty lines. */
- if (line_length == 0)
- continue;
-
- line[line_length] = '\0';
-
- size_t d_len;
- if (! (split_3 (line, line_length, &digest, &d_len, &binary, &filename)
- && ! (is_stdin && streq (filename, "-"))))
- {
- ++n_misformatted_lines;
-
- if (warn)
- {
- error (0, 0,
- _("%s: %ju"
- ": improperly formatted %s checksum line"),
- quotef (checkfile_name), line_number,
- DIGEST_TYPE_STRING);
- }
- }
- else
- {
- bool ok;
- bool missing;
- bool needs_escape = ! status_only && problematic_chars (filename);
-
- properly_formatted_lines = true;
-
- uintmax_t length;
- ok = digest_file (filename, &binary, bin_buffer, &missing, &length);
-
- if (!ok)
- {
- ++n_open_or_read_failures;
- if (!status_only)
- {
- if (needs_escape)
- putchar ('\\');
- print_filename (filename, needs_escape);
- printf (": %s\n", _("FAILED open or read"));
- }
- }
- else if (ignore_missing && missing)
- {
- /* Ignore missing files with --ignore-missing. */
- ;
- }
- else
- {
- bool match = false;
-#if HASH_ALGO_CKSUM
- if (d_len == BASE64_LENGTH (digest_length / 8))
- match = b64_equal (digest, bin_buffer);
- else
-#endif
- if (d_len == digest_hex_bytes)
- match = hex_equal (digest, bin_buffer);
-
- if (match)
- matched_checksums = true;
- else
- ++n_mismatched_checksums;
-
- if (!status_only)
- {
- if (! match || ! quiet)
- {
- if (needs_escape)
- putchar ('\\');
- print_filename (filename, needs_escape);
- }
-
- if (! match)
- printf (": %s\n", _("FAILED"));
- else if (!quiet)
- printf (": %s\n", _("OK"));
- }
- }
- }
- }
- while (!feof (checkfile_stream) && !ferror (checkfile_stream));
-
- free (line);
-
- int err = ferror (checkfile_stream) ? 0 : -1;
- if (is_stdin)
- clearerr (checkfile_stream);
- else if (fclose (checkfile_stream) != 0 && err < 0)
- err = errno;
-
- if (0 <= err)
- {
- error (0, err, err ? "%s" : _("%s: read error"),
- quotef (checkfile_name));
- return false;
- }
-
- if (! properly_formatted_lines)
- {
- /* Warn if no tests are found. */
- error (0, 0, _("%s: no properly formatted checksum lines found"),
- quotef (checkfile_name));
- }
- else
- {
- if (!status_only)
- {
- if (n_misformatted_lines != 0)
- error (0, 0,
- (ngettext
- ("WARNING: %ju line is improperly formatted",
- "WARNING: %ju lines are improperly formatted",
- select_plural (n_misformatted_lines))),
- n_misformatted_lines);
-
- if (n_open_or_read_failures != 0)
- error (0, 0,
- (ngettext
- ("WARNING: %ju listed file could not be read",
- "WARNING: %ju listed files could not be read",
- select_plural (n_open_or_read_failures))),
- n_open_or_read_failures);
-
- if (n_mismatched_checksums != 0)
- error (0, 0,
- (ngettext
- ("WARNING: %ju computed checksum did NOT match",
- "WARNING: %ju computed checksums did NOT match",
- select_plural (n_mismatched_checksums))),
- n_mismatched_checksums);
-
- if (ignore_missing && ! matched_checksums)
- error (0, 0, _("%s: no file was verified"),
- quotef (checkfile_name));
- }
- }
-
- return (properly_formatted_lines
- && matched_checksums
- && n_mismatched_checksums == 0
- && n_open_or_read_failures == 0
- && (!strict || n_misformatted_lines == 0));
-}
-
-int
-main (int argc, char **argv)
-{
- unsigned char bin_buffer_unaligned[DIGEST_BIN_BYTES + DIGEST_ALIGN];
- /* Make sure bin_buffer is properly aligned. */
- unsigned char *bin_buffer = ptr_align (bin_buffer_unaligned, DIGEST_ALIGN);
- bool do_check = false;
- int opt;
- bool ok = true;
- int binary = -1;
- int prefix_tag = -1;
-
- /* Setting values of global variables. */
- initialize_main (&argc, &argv);
- set_program_name (argv[0]);
- setlocale (LC_ALL, "");
- bindtextdomain (PACKAGE, LOCALEDIR);
- textdomain (PACKAGE);
-
- atexit (close_stdout);
-
- /* Line buffer stdout to ensure lines are written atomically and immediately
- so that processes running in parallel do not intersperse their output. */
- setvbuf (stdout, nullptr, _IOLBF, 0);
-
-#if HASH_ALGO_SUM
- char const *short_opts = "rs";
-#elif HASH_ALGO_CKSUM
- char const *short_opts = "a:l:bctwz";
- char const *digest_length_str = "";
-#elif HASH_ALGO_BLAKE2
- char const *short_opts = "l:bctwz";
- char const *digest_length_str = "";
-#else
- char const *short_opts = "bctwz";
-#endif
-
- while ((opt = getopt_long (argc, argv, short_opts, long_options, nullptr))
- != -1)
- switch (opt)
- {
-#if HASH_ALGO_CKSUM
- case 'a':
- cksum_algorithm = XARGMATCH_EXACT ("--algorithm", optarg,
- algorithm_args, algorithm_types);
- algorithm_specified = true;
- break;
-
- case DEBUG_PROGRAM_OPTION:
- cksum_debug = true;
- break;
-#endif
-#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
- case 'l':
- digest_length = xnumtoumax (optarg, 10, 0, UINTMAX_MAX, "",
- _("invalid length"), 0,
- XTOINT_MAX_QUIET);
- digest_length_str = optarg;
- break;
-#endif
-#if !HASH_ALGO_SUM
- case 'c':
- do_check = true;
- break;
- case STATUS_OPTION:
- status_only = true;
- warn = false;
- quiet = false;
- break;
- case 'b':
- binary = 1;
- break;
- case 't':
- binary = 0;
- break;
- case 'w':
- status_only = false;
- warn = true;
- quiet = false;
- break;
- case IGNORE_MISSING_OPTION:
- ignore_missing = true;
- break;
- case QUIET_OPTION:
- status_only = false;
- warn = false;
- quiet = true;
- break;
- case STRICT_OPTION:
- strict = true;
- break;
-# if HASH_ALGO_CKSUM
- case BASE64_OPTION:
- base64_digest = true;
- break;
- case RAW_OPTION:
- raw_digest = true;
- break;
- case UNTAG_OPTION:
- if (prefix_tag == 1)
- binary = -1;
- prefix_tag = 0;
- break;
-# endif
- case TAG_OPTION:
- prefix_tag = 1;
- binary = 1;
- break;
- case 'z':
- digest_delim = '\0';
- break;
-#endif
-#if HASH_ALGO_SUM
- case 'r': /* For SysV compatibility. */
- sum_algorithm = bsd;
- break;
-
- case 's':
- sum_algorithm = sysv;
- break;
-#endif
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- }
-
- min_digest_line_length = MIN_DIGEST_LINE_LENGTH;
-#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
-# if HASH_ALGO_CKSUM
- if (digest_length && (cksum_algorithm != blake2b
- && cksum_algorithm != sha2
- && cksum_algorithm != sha3))
- error (EXIT_FAILURE, 0,
- _("--length is only supported with --algorithm "
- "blake2b, sha2, or sha3"));
- if (cksum_algorithm == sha2 || cksum_algorithm == sha3)
- {
- /* Do not require --length with --check. */
- if (digest_length == 0 && *digest_length_str == '\0' && ! do_check)
- error (EXIT_FAILURE, 0, _("--algorithm=%s requires specifying "
- "--length 224, 256, 384, or 512"),
- algorithm_args[cksum_algorithm]);
- /* If --check and --length are used we verify the digest length. */
- if ((! do_check || *digest_length_str != '\0')
- && digest_length != SHA224_DIGEST_SIZE * 8
- && digest_length != SHA256_DIGEST_SIZE * 8
- && digest_length != SHA384_DIGEST_SIZE * 8
- && digest_length != SHA512_DIGEST_SIZE * 8)
- {
- error (0, 0, _("invalid length: %s"), quote (digest_length_str));
- error (EXIT_FAILURE, 0, _("digest length for %s must be "
- "224, 256, 384, or 512"),
- quote (DIGEST_TYPE_STRING));
- }
- }
- else
- {
- /* If the digest length checks for SHA-3 are satisfied, the less strict
- checks for BLAKE2 will also be. */
-# else
- {
-# endif
- if (digest_length > DIGEST_MAX_LEN * 8)
- {
- error (0, 0, _("invalid length: %s"), quote (digest_length_str));
- error (EXIT_FAILURE, 0,
- _("maximum digest length for %s is %d bits"),
- quote (DIGEST_TYPE_STRING),
- DIGEST_MAX_LEN * 8);
- }
- if (digest_length % 8 != 0)
- {
- error (0, 0, _("invalid length: %s"), quote (digest_length_str));
- error (EXIT_FAILURE, 0, _("length is not a multiple of 8"));
- }
- }
- if (digest_length == 0)
- {
-# if HASH_ALGO_BLAKE2
- digest_length = DIGEST_MAX_LEN * 8;
-# else
- digest_length = algorithm_bits[cksum_algorithm];
-# endif
- }
- digest_hex_bytes = digest_length / 4;
-#else
- digest_hex_bytes = DIGEST_HEX_BYTES;
-#endif
-
-#if HASH_ALGO_CKSUM
- switch (+cksum_algorithm)
- {
- case bsd:
- case sysv:
- case crc:
- case crc32b:
- if (do_check && algorithm_specified)
- error (EXIT_FAILURE, 0,
- _("--check is not supported with "
- "--algorithm={bsd,sysv,crc,crc32b}"));
- break;
- }
-
- if (base64_digest && raw_digest)
- {
- error (0, 0, _("--base64 and --raw are mutually exclusive"));
- usage (EXIT_FAILURE);
- }
-#endif
-
- if (prefix_tag == -1)
- prefix_tag = HASH_ALGO_CKSUM;
-
- if (prefix_tag && !binary)
- {
- /* This could be supported in a backwards compatible way
- by prefixing the output line with a space in text mode.
- However that's invasive enough that it was agreed to
- not support this mode with --tag, as --text use cases
- are adequately supported by the default output format. */
-#if !HASH_ALGO_CKSUM
- error (0, 0, _("--tag does not support --text mode"));
-#else
- error (0, 0, _("--text mode is only supported with --untagged"));
-#endif
- usage (EXIT_FAILURE);
- }
-
- if (digest_delim != '\n' && do_check)
- {
- error (0, 0, _("the --zero option is not supported when "
- "verifying checksums"));
- usage (EXIT_FAILURE);
- }
-#if !HASH_ALGO_CKSUM
- if (prefix_tag && do_check)
- {
- error (0, 0, _("the --tag option is meaningless when "
- "verifying checksums"));
- usage (EXIT_FAILURE);
- }
-#endif
-
- if (0 <= binary && do_check)
- {
- error (0, 0, _("the --binary and --text options are meaningless when "
- "verifying checksums"));
- usage (EXIT_FAILURE);
- }
-
- if (ignore_missing && !do_check)
- {
- error (0, 0,
- _("the --ignore-missing option is meaningful only when "
- "verifying checksums"));
- usage (EXIT_FAILURE);
- }
-
- if (status_only && !do_check)
- {
- error (0, 0,
- _("the --status option is meaningful only when verifying checksums"));
- usage (EXIT_FAILURE);
- }
-
- if (warn && !do_check)
- {
- error (0, 0,
- _("the --warn option is meaningful only when verifying checksums"));
- usage (EXIT_FAILURE);
- }
-
- if (quiet && !do_check)
- {
- error (0, 0,
- _("the --quiet option is meaningful only when verifying checksums"));
- usage (EXIT_FAILURE);
- }
-
- if (strict & !do_check)
- {
- error (0, 0,
- _("the --strict option is meaningful only when verifying checksums"));
- usage (EXIT_FAILURE);
- }
-
- if (!O_BINARY && binary < 0)
- binary = 0;
-
- char **operand_lim = argv + argc;
- if (optind == argc)
- *operand_lim++ = bad_cast ("-");
- else if (1 < argc - optind && raw_digest)
- error (EXIT_FAILURE, 0,
- _("the --raw option is not supported with multiple files"));
-
- for (char **operandp = argv + optind; operandp < operand_lim; operandp++)
- {
- char *file = *operandp;
- if (do_check)
- ok &= digest_check (file);
- else
- {
- int binary_file = binary;
- bool missing;
- uintmax_t length;
-
- if (! digest_file (file, &binary_file, bin_buffer, &missing, &length))
- ok = false;
- else
- {
- DIGEST_OUT (file, binary_file, bin_buffer, raw_digest, prefix_tag,
- digest_delim, optind != argc, length);
- }
- }
- }
-
- if (have_read_stdin && fclose (stdin) == EOF)
- error (EXIT_FAILURE, errno, _("standard input"));
-
- return ok ? EXIT_SUCCESS : EXIT_FAILURE;
-}
diff --git a/src/dircolors.c b/src/dircolors.c
index 19e815953..bd7706f05 100644
--- a/src/dircolors.c
+++ b/src/dircolors.c
@@ -1,5 +1,5 @@
/* dircolors - output commands to set the LS_COLOR environment variable
- Copyright (C) 1996-2025 Free Software Foundation, Inc.
+ Copyright (C) 1996-2026 Free Software Foundation, Inc.
Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000 H. Peter Anvin
This program is free software: you can redistribute it and/or modify
@@ -57,7 +57,7 @@ static char const *const slack_codes[] =
"CHR", "CHAR", "DOOR", "EXEC", "LEFT", "LEFTCODE", "RIGHT", "RIGHTCODE",
"END", "ENDCODE", "SUID", "SETUID", "SGID", "SETGID", "STICKY",
"OTHER_WRITABLE", "OWR", "STICKY_OTHER_WRITABLE", "OWT", "CAPABILITY",
- "MULTIHARDLINK", "CLRTOEOL", nullptr
+ "MULTIHARDLINK", "CLRTOEOL", NULL
};
static char const *const ls_codes[] =
@@ -65,7 +65,7 @@ static char const *const ls_codes[] =
"no", "no", "fi", "rs", "di", "ln", "ln", "ln", "or", "mi", "pi", "pi",
"so", "bd", "bd", "cd", "cd", "do", "ex", "lc", "lc", "rc", "rc", "ec", "ec",
"su", "su", "sg", "sg", "st", "ow", "ow", "tw", "tw", "ca", "mh", "cl",
- nullptr
+ NULL
};
static_assert (countof (slack_codes) == countof (ls_codes));
@@ -81,15 +81,15 @@ enum
static struct option const long_options[] =
{
- {"bourne-shell", no_argument, nullptr, 'b'},
- {"sh", no_argument, nullptr, 'b'},
- {"csh", no_argument, nullptr, 'c'},
- {"c-shell", no_argument, nullptr, 'c'},
- {"print-database", no_argument, nullptr, 'p'},
- {"print-ls-colors", no_argument, nullptr, PRINT_LS_COLORS_OPTION},
+ {"bourne-shell", no_argument, NULL, 'b'},
+ {"sh", no_argument, NULL, 'b'},
+ {"csh", no_argument, NULL, 'c'},
+ {"c-shell", no_argument, NULL, 'c'},
+ {"print-database", no_argument, NULL, 'p'},
+ {"print-ls-colors", no_argument, NULL, PRINT_LS_COLORS_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -104,13 +104,25 @@ usage (int status)
Output commands to set the LS_COLORS environment variable.\n\
\n\
Determine format of output:\n\
- -b, --sh, --bourne-shell output Bourne shell code to set LS_COLORS\n\
- -c, --csh, --c-shell output C shell code to set LS_COLORS\n\
- -p, --print-database output defaults\n\
- --print-ls-colors output fully escaped colors for display\n\
"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -b, --sh, --bourne-shell\n\
+ output Bourne shell code to set LS_COLORS\n\
+"));
+ oputs (_("\
+ -c, --csh, --c-shell\n\
+ output C shell code to set LS_COLORS\n\
+"));
+ oputs (_("\
+ -p, --print-database\n\
+ output defaults\n\
+"));
+ oputs (_("\
+ --print-ls-colors\n\
+ output fully escaped colors for display\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
If FILE is specified, read it to determine which colors to use for which\n\
@@ -132,7 +144,7 @@ guess_shell_syntax (void)
char *shell;
shell = getenv ("SHELL");
- if (shell == nullptr || *shell == '\0')
+ if (shell == NULL || *shell == '\0')
return SHELL_SYNTAX_UNKNOWN;
shell = last_component (shell);
@@ -150,8 +162,8 @@ parse_line (char const *line, char **keyword, char **arg)
char const *keyword_start;
char const *arg_start;
- *keyword = nullptr;
- *arg = nullptr;
+ *keyword = NULL;
+ *arg = NULL;
for (p = line; c_isspace (*p); ++p)
continue;
@@ -267,7 +279,7 @@ dc_parse_stream (FILE *fp, char const *filename)
{
idx_t line_number = 0;
char const *next_G_line = G_line;
- char *input_line = nullptr;
+ char *input_line = NULL;
size_t input_line_size = 0;
char const *line;
char const *term;
@@ -279,12 +291,12 @@ dc_parse_stream (FILE *fp, char const *filename)
/* Get terminal type */
term = getenv ("TERM");
- if (term == nullptr || *term == '\0')
+ if (term == NULL || *term == '\0')
term = "none";
/* Also match $COLORTERM. */
colorterm = getenv ("COLORTERM");
- if (colorterm == nullptr)
+ if (colorterm == NULL)
colorterm = ""; /* Doesn't match default "?*" */
while (true)
@@ -318,10 +330,10 @@ dc_parse_stream (FILE *fp, char const *filename)
parse_line (line, &keywd, &arg);
- if (keywd == nullptr)
+ if (keywd == NULL)
continue;
- if (arg == nullptr)
+ if (arg == NULL)
{
error (0, 0, _("%s:%td: invalid line; missing second token"),
quotef (filename), line_number);
@@ -362,11 +374,11 @@ dc_parse_stream (FILE *fp, char const *filename)
{
int i;
- for (i = 0; slack_codes[i] != nullptr; ++i)
+ for (i = 0; slack_codes[i] != NULL; ++i)
if (c_strcasecmp (keywd, slack_codes[i]) == 0)
break;
- if (slack_codes[i] != nullptr)
+ if (slack_codes[i] != NULL)
append_entry (0, ls_codes[i], arg);
else
unrecognized = true;
@@ -396,7 +408,7 @@ dc_parse_file (char const *filename)
{
bool ok;
- if (! streq (filename, "-") && freopen (filename, "r", stdin) == nullptr)
+ if (! streq (filename, "-") && freopen (filename, "r", stdin) == NULL)
{
error (0, errno, "%s", quotef (filename));
return false;
@@ -429,7 +441,7 @@ main (int argc, char **argv)
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "bcp", long_options, nullptr)) != -1)
+ while ((optc = getopt_long (argc, argv, "bcp", long_options, NULL)) != -1)
switch (optc)
{
case 'b': /* Bourne shell syntax. */
@@ -511,7 +523,7 @@ main (int argc, char **argv)
obstack_init (&lsc_obstack);
if (argc == 0)
- ok = dc_parse_stream (nullptr, nullptr);
+ ok = dc_parse_stream (NULL, NULL);
else
ok = dc_parse_file (argv[0]);
diff --git a/src/dircolors.hin b/src/dircolors.hin
index ac57522bd..a470767b2 100644
--- a/src/dircolors.hin
+++ b/src/dircolors.hin
@@ -1,7 +1,7 @@
# Configuration file for dircolors, a utility to help you set the
# LS_COLORS environment variable used by GNU ls with the --color option.
-# Copyright (C) 1996-2025 Free Software Foundation, Inc.
+# Copyright (C) 1996-2026 Free Software Foundation, Inc.
# Copying and distribution of this file, with or without modification,
# are permitted provided the copyright notice and this notice are preserved.
diff --git a/src/dirname.c b/src/dirname.c
index 5d5b437eb..dbd21d683 100644
--- a/src/dirname.c
+++ b/src/dirname.c
@@ -1,6 +1,6 @@
/* dirname -- strip suffix from file name
- Copyright (C) 1990-2025 Free Software Foundation, Inc.
+ Copyright (C) 1990-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -33,10 +33,10 @@
static struct option const longopts[] =
{
- {"zero", no_argument, nullptr, 'z'},
+ {"zero", no_argument, NULL, 'z'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -55,11 +55,12 @@ Output each NAME with its last non-slash component and trailing slashes\n\
removed; if NAME contains no /'s, output '.' (meaning the current directory).\n\
\n\
"), stdout);
- fputs (_("\
- -z, --zero end each output line with NUL, not newline\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -z, --zero\n\
+ end each output line with NUL, not newline\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
printf (_("\
\n\
Examples:\n\
@@ -76,10 +77,7 @@ Examples:\n\
int
main (int argc, char **argv)
{
- static char const dot = '.';
bool use_nuls = false;
- char const *result;
- size_t len;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -91,7 +89,7 @@ main (int argc, char **argv)
while (true)
{
- int c = getopt_long (argc, argv, "z", longopts, nullptr);
+ int c = getopt_long (argc, argv, "z", longopts, NULL);
if (c == -1)
break;
@@ -118,11 +116,12 @@ main (int argc, char **argv)
for (; optind < argc; optind++)
{
- result = argv[optind];
- len = dir_len (result);
+ char const *result = argv[optind];
+ idx_t len = dir_len (result);
if (! len)
{
+ static char const dot = '.';
result = &dot;
len = 1;
}
diff --git a/src/du-tests b/src/du-tests
index fad2f097b..548c972b2 100755
--- a/src/du-tests
+++ b/src/du-tests
@@ -1,6 +1,6 @@
#!/bin/bash
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/src/du.c b/src/du.c
index f4dbd8c05..bff1b6672 100644
--- a/src/du.c
+++ b/src/du.c
@@ -1,5 +1,5 @@
/* du -- summarize device usage
- Copyright (C) 1988-2025 Free Software Foundation, Inc.
+ Copyright (C) 1988-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -40,6 +40,7 @@
#include "stat-time.h"
#include "stdio--.h"
#include "xfts.h"
+#include "xmemdup0.h"
#include "xstrtol.h"
#include "xstrtol-error.h"
@@ -169,10 +170,10 @@ enum time_type
static enum time_type time_type = time_mtime;
/* User specified date / time style */
-static char const *time_style = nullptr;
+static char const *time_style = NULL;
/* Format used to display date / time. Controlled by --time-style */
-static char const *time_format = nullptr;
+static char const *time_format = NULL;
/* The local time zone rules, as per the TZ environment variable. */
static timezone_t localtz;
@@ -194,8 +195,7 @@ static struct duinfo tot_dui;
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
{
- APPARENT_SIZE_OPTION = CHAR_MAX + 1,
- EXCLUDE_OPTION,
+ EXCLUDE_OPTION = CHAR_MAX + 1,
FILES0_FROM_OPTION,
HUMAN_SI_OPTION,
#if GNULIB_FTS_DEBUG
@@ -208,40 +208,40 @@ enum
static struct option const long_options[] =
{
- {"all", no_argument, nullptr, 'a'},
- {"apparent-size", no_argument, nullptr, APPARENT_SIZE_OPTION},
- {"block-size", required_argument, nullptr, 'B'},
- {"bytes", no_argument, nullptr, 'b'},
- {"count-links", no_argument, nullptr, 'l'},
+ {"all", no_argument, NULL, 'a'},
+ {"apparent-size", no_argument, NULL, 'A'},
+ {"block-size", required_argument, NULL, 'B'},
+ {"bytes", no_argument, NULL, 'b'},
+ {"count-links", no_argument, NULL, 'l'},
#if GNULIB_FTS_DEBUG
- {"-debug", no_argument, nullptr, FTS_DEBUG},
+ {"-debug", no_argument, NULL, FTS_DEBUG},
#endif
- {"dereference", no_argument, nullptr, 'L'},
- {"dereference-args", no_argument, nullptr, 'D'},
- {"exclude", required_argument, nullptr, EXCLUDE_OPTION},
- {"exclude-from", required_argument, nullptr, 'X'},
- {"files0-from", required_argument, nullptr, FILES0_FROM_OPTION},
- {"human-readable", no_argument, nullptr, 'h'},
- {"inodes", no_argument, nullptr, INODES_OPTION},
- {"si", no_argument, nullptr, HUMAN_SI_OPTION},
- {"max-depth", required_argument, nullptr, 'd'},
- {"null", no_argument, nullptr, '0'},
- {"no-dereference", no_argument, nullptr, 'P'},
- {"one-file-system", no_argument, nullptr, 'x'},
- {"separate-dirs", no_argument, nullptr, 'S'},
- {"summarize", no_argument, nullptr, 's'},
- {"total", no_argument, nullptr, 'c'},
- {"threshold", required_argument, nullptr, 't'},
- {"time", optional_argument, nullptr, TIME_OPTION},
- {"time-style", required_argument, nullptr, TIME_STYLE_OPTION},
+ {"dereference", no_argument, NULL, 'L'},
+ {"dereference-args", no_argument, NULL, 'D'},
+ {"exclude", required_argument, NULL, EXCLUDE_OPTION},
+ {"exclude-from", required_argument, NULL, 'X'},
+ {"files0-from", required_argument, NULL, FILES0_FROM_OPTION},
+ {"human-readable", no_argument, NULL, 'h'},
+ {"inodes", no_argument, NULL, INODES_OPTION},
+ {"si", no_argument, NULL, HUMAN_SI_OPTION},
+ {"max-depth", required_argument, NULL, 'd'},
+ {"null", no_argument, NULL, '0'},
+ {"no-dereference", no_argument, NULL, 'P'},
+ {"one-file-system", no_argument, NULL, 'x'},
+ {"separate-dirs", no_argument, NULL, 'S'},
+ {"summarize", no_argument, NULL, 's'},
+ {"total", no_argument, NULL, 'c'},
+ {"threshold", required_argument, NULL, 't'},
+ {"time", optional_argument, NULL, TIME_OPTION},
+ {"time-style", required_argument, NULL, TIME_STYLE_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
static char const *const time_args[] =
{
- "atime", "access", "use", "ctime", "status", nullptr
+ "atime", "access", "use", "ctime", "status", NULL
};
static enum time_type const time_types[] =
{
@@ -261,7 +261,7 @@ enum time_style
static char const *const time_style_args[] =
{
- "full-iso", "long-iso", "iso", nullptr
+ "full-iso", "long-iso", "iso", NULL
};
static enum time_style const time_style_types[] =
{
@@ -286,69 +286,134 @@ Summarize device usage of the set of FILEs, recursively for directories.\n\
emit_mandatory_arg_note ();
+ oputs (_("\
+ -0, --null\n\
+ end each output line with NUL, not newline\n\
+"));
+ oputs (_("\
+ -a, --all\n\
+ write counts for all files, not just directories\n\
+"));
+ oputs (_("\
+ -A, --apparent-size\n\
+ print apparent sizes rather than device usage;\n\
+ although the apparent size is usually smaller, it may be\n\
+ larger due to holes in ('sparse') files,\n\
+ internal fragmentation, indirect blocks, etc.\n\
+"));
+ oputs (_("\
+ -B, --block-size=SIZE\n\
+ scale sizes by SIZE before printing them; See SIZE format below;\n\
+ E.g., '-BM' prints sizes in units of 1,048,576 bytes\n\
+"));
+ oputs (_("\
+ -b, --bytes\n\
+ equivalent to '--apparent-size --block-size=1'\n\
+"));
+ oputs (_("\
+ -c, --total\n\
+ produce a grand total\n\
+"));
+ oputs (_("\
+ -D, --dereference-args\n\
+ dereference only symlinks that are listed on the command line\n\
+"));
+ oputs (_("\
+ -d, --max-depth=N\n\
+ print the total for a directory (or file, with --all)\n\
+ only if it is N or fewer levels below the command\n\
+ line argument; --max-depth=0 is the same as --summarize\n\
+"));
+ oputs (_("\
+ --files0-from=F\n\
+ summarize device usage of the NUL-terminated file names\n\
+ specified in file F; if F is -, read names from standard input\n\
+"));
+ oputs (_("\
+ -H\n\
+ equivalent to --dereference-args (-D)\n\
+"));
+ oputs (_("\
+ -h, --human-readable\n\
+ print sizes in human readable format (e.g., 1K 234M 2G)\n\
+"));
+ oputs (_("\
+ --inodes\n\
+ list inode usage information instead of block usage\n\
+"));
+ oputs (_("\
+ -k\n\
+ like --block-size=1K\n\
+"));
+ oputs (_("\
+ -L, --dereference\n\
+ dereference all symbolic links\n\
+"));
+ oputs (_("\
+ -l, --count-links\n\
+ count sizes many times if hard linked\n\
+"));
+ oputs (_("\
+ -m\n\
+ like --block-size=1M\n\
+"));
+ oputs (_("\
+ -P, --no-dereference\n\
+ don't follow any symbolic links (this is the default)\n\
+"));
+ oputs (_("\
+ -S, --separate-dirs\n\
+ for directories do not include size of subdirectories\n\
+"));
+ oputs (_("\
+ --si\n\
+ like -h, but use powers of 1000 not 1024\n\
+"));
+ oputs (_("\
+ -s, --summarize\n\
+ display only a total for each argument\n\
+"));
+ oputs (_("\
+ -t, --threshold=SIZE\n\
+ exclude entries smaller than SIZE if positive,\n\
+ or entries greater than SIZE if negative\n\
+"));
+ oputs (_("\
+ --time\n\
+ show time of the last modification of any file in the directory,\n\
+ or any of its subdirectories\n\
+"));
+ oputs (_("\
+ --time=WORD\n\
+ show time as WORD instead of modification time:\n\
+ atime, access, use, ctime or status\n\
+"));
+ oputs (_("\
+ --time-style=STYLE\n\
+ time/date format with --time; see TIME_STYLE below\n\
+"));
+ oputs (_("\
+ -X, --exclude-from=FILE\n\
+ exclude files that match any pattern in FILE\n\
+"));
+ oputs (_("\
+ --exclude=PATTERN\n\
+ exclude files that match PATTERN\n\
+"));
+ oputs (_("\
+ -x, --one-file-system\n\
+ skip directories on different file systems\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
+ emit_blocksize_note ("DU");
+ emit_size_note ();
fputs (_("\
- -0, --null end each output line with NUL, not newline\n\
- -a, --all write counts for all files, not just directories\n\
- --apparent-size print apparent sizes rather than device usage; although\
-\n\
- the apparent size is usually smaller, it may be\n\
- larger due to holes in ('sparse') files, internal\n\
- fragmentation, indirect blocks, and the like\n\
-"), stdout);
- fputs (_("\
- -B, --block-size=SIZE scale sizes by SIZE before printing them; e.g.,\n\
- '-BM' prints sizes in units of 1,048,576 bytes;\n\
- see SIZE format below\n\
- -b, --bytes equivalent to '--apparent-size --block-size=1'\n\
- -c, --total produce a grand total\n\
- -D, --dereference-args dereference only symlinks that are listed on the\n\
- command line\n\
- -d, --max-depth=N print the total for a directory (or file, with --all)\n\
- only if it is N or fewer levels below the command\n\
- line argument; --max-depth=0 is the same as\n\
- --summarize\n\
-"), stdout);
- fputs (_("\
- --files0-from=F summarize device usage of the\n\
- NUL-terminated file names specified in file F;\n\
- if F is -, then read names from standard input\n\
- -H equivalent to --dereference-args (-D)\n\
- -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\
\n\
- --inodes list inode usage information instead of block usage\n\
-"), stdout);
- fputs (_("\
- -k like --block-size=1K\n\
- -L, --dereference dereference all symbolic links\n\
- -l, --count-links count sizes many times if hard linked\n\
- -m like --block-size=1M\n\
+The --time-style STYLE argument can be full-iso, long-iso, iso, or +FORMAT.\n\
+FORMAT is interpreted like in date(1).\n\
+Also the TIME_STYLE environment variable sets the default style to use.\n\
"), stdout);
- fputs (_("\
- -P, --no-dereference don't follow any symbolic links (this is the default)\n\
- -S, --separate-dirs for directories do not include size of subdirectories\n\
- --si like -h, but use powers of 1000 not 1024\n\
- -s, --summarize display only a total for each argument\n\
-"), stdout);
- fputs (_("\
- -t, --threshold=SIZE exclude entries smaller than SIZE if positive,\n\
- or entries greater than SIZE if negative\n\
- --time show time of the last modification of any file in the\n\
- directory, or any of its subdirectories\n\
- --time=WORD show time as WORD instead of modification time:\n\
- atime, access, use, ctime or status\n\
- --time-style=STYLE show times using STYLE, which can be:\n\
- full-iso, long-iso, iso, or +FORMAT;\n\
- FORMAT is interpreted like in 'date'\n\
-"), stdout);
- fputs (_("\
- -X, --exclude-from=FILE exclude files that match any pattern in FILE\n\
- --exclude=PATTERN exclude files that match PATTERN\n\
- -x, --one-file-system skip directories on different file systems\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
- emit_blocksize_note ("DU");
- emit_size_note ();
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -399,8 +464,11 @@ print_size (const struct duinfo *pdui, char const *string)
fputs (timetostr (pdui->tmax.tv_sec, buf), stdout);
}
}
- printf ("\t%s%c", string, opt_nul_terminate_output ? '\0' : '\n');
- fflush (stdout);
+ putchar ('\t');
+ fputs (string, stdout);
+ putchar (opt_nul_terminate_output ? '\0' : '\n');
+ if (fflush (stdout) < 0)
+ write_error ();
}
/* Fill the di_mnt set with local mount point dev/ino pairs. */
@@ -660,14 +728,14 @@ du_files (char **files, int bit_flags)
if (*files)
{
- FTS *fts = xfts_open (files, bit_flags, nullptr);
+ FTS *fts = xfts_open (files, bit_flags, NULL);
while (true)
{
FTSENT *ent;
ent = fts_read (fts);
- if (ent == nullptr)
+ if (ent == NULL)
{
if (errno != 0)
{
@@ -707,7 +775,7 @@ main (int argc, char **argv)
char *cwd_only[2];
bool max_depth_specified = false;
bool ok = true;
- char *files_from = nullptr;
+ char *files_from = NULL;
/* Bit flags that control how fts works. */
int bit_flags = FTS_NOSTAT;
@@ -720,7 +788,7 @@ main (int argc, char **argv)
bool opt_summarize_only = false;
cwd_only[0] = bad_cast (".");
- cwd_only[1] = nullptr;
+ cwd_only[1] = NULL;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -738,7 +806,7 @@ main (int argc, char **argv)
while (true)
{
int oi = -1;
- int c = getopt_long (argc, argv, "0abd:chHklmst:xB:DLPSX:",
+ int c = getopt_long (argc, argv, "0aAbd:chHklmst:xB:DLPSX:",
long_options, &oi);
if (c == -1)
break;
@@ -759,7 +827,7 @@ main (int argc, char **argv)
opt_all = true;
break;
- case APPARENT_SIZE_OPTION:
+ case 'A':
apparent_size = true;
break;
@@ -791,7 +859,7 @@ main (int argc, char **argv)
case 'd': /* --max-depth=N */
{
intmax_t tmp;
- if (xstrtoimax (optarg, nullptr, 0, &tmp, "") == LONGINT_OK
+ if (xstrtoimax (optarg, NULL, 0, &tmp, "") == LONGINT_OK
&& tmp <= IDX_MAX)
{
max_depth_specified = true;
@@ -822,7 +890,7 @@ main (int argc, char **argv)
case 't':
{
enum strtol_error e;
- e = xstrtoimax (optarg, nullptr, 0, &opt_threshold,
+ e = xstrtoimax (optarg, NULL, 0, &opt_threshold,
"kKmMGTPEZYRQ0");
if (e != LONGINT_OK)
xstrtol_fatal (e, oi, c, long_options, optarg);
@@ -956,9 +1024,9 @@ main (int argc, char **argv)
{
/* Ignore anything after a newline, for compatibility
with ls. */
- char *p = strchr (time_style, '\n');
+ char const *p = strchr (time_style, '\n');
if (p)
- *p = '\0';
+ time_style = xmemdup0 (time_style, p - time_style);
}
else
{
@@ -1042,7 +1110,7 @@ main (int argc, char **argv)
bit_flags |= FTS_TIGHT_CYCLE_CHECK;
bit_flags |= symlink_deref_bits;
- static char *temp_argv[] = { nullptr, nullptr };
+ static char *temp_argv[] = { NULL, NULL };
while (true)
{
@@ -1086,7 +1154,7 @@ main (int argc, char **argv)
among many, knowing the record number may help.
FIXME: currently print the record number only with
--files0-from=FILE. Maybe do it for argv, too? */
- if (files_from == nullptr)
+ if (files_from == NULL)
error (0, 0, "%s", _("invalid zero-length file name"));
else
{
diff --git a/src/echo.c b/src/echo.c
index c688037b3..d64306d7a 100644
--- a/src/echo.c
+++ b/src/echo.c
@@ -1,5 +1,5 @@
/* echo.c, derived from code echo.c in Bash.
- Copyright (C) 1987-2025 Free Software Foundation, Inc.
+ Copyright (C) 1987-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -47,18 +47,26 @@ Usage: %s [SHORT-OPTION]... [STRING]...\n\
fputs (_("\
Echo the STRING(s) to standard output.\n\
\n\
- -n do not output the trailing newline\n\
"), stdout);
- fputs (_(DEFAULT_ECHO_TO_XPG
+ oputs (_("\
+ -n do not output the trailing newline\n\
+"));
+ oputs (_(DEFAULT_ECHO_TO_XPG
? N_("\
- -e enable interpretation of backslash escapes (default)\n\
- -E disable interpretation of backslash escapes\n")
+ -e enable interpretation of backslash escapes (default)\n\
+")
: N_("\
- -e enable interpretation of backslash escapes\n\
- -E disable interpretation of backslash escapes (default)\n")),
- stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ -e enable interpretation of backslash escapes\n\
+")));
+ oputs (_(DEFAULT_ECHO_TO_XPG
+ ? N_("\
+ -E disable interpretation of backslash escapes\n\
+")
+ : N_("\
+ -E disable interpretation of backslash escapes (default)\n\
+")));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
If -e is in effect, the following sequences are recognized:\n\
@@ -66,7 +74,7 @@ If -e is in effect, the following sequences are recognized:\n\
"), stdout);
fputs (_("\
\\\\ backslash\n\
- \\a alert (BEL)\n\
+ \\a alert (bell)\n\
\\b backspace\n\
\\c produce no further output\n\
\\e escape\n\
@@ -141,7 +149,7 @@ main (int argc, char **argv)
if (streq (argv[1], "--version"))
{
version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version, AUTHORS,
- (char *) nullptr);
+ (char *) NULL);
return EXIT_SUCCESS;
}
}
diff --git a/src/env.c b/src/env.c
index e06ca5568..3e4f9b3fc 100644
--- a/src/env.c
+++ b/src/env.c
@@ -1,5 +1,5 @@
/* env - run a program in a modified environment
- Copyright (C) 1986-2025 Free Software Foundation, Inc.
+ Copyright (C) 1986-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -87,20 +87,20 @@ enum
static struct option const longopts[] =
{
- {"argv0", required_argument, nullptr, 'a'},
- {"ignore-environment", no_argument, nullptr, 'i'},
- {"null", no_argument, nullptr, '0'},
- {"unset", required_argument, nullptr, 'u'},
- {"chdir", required_argument, nullptr, 'C'},
- {"default-signal", optional_argument, nullptr, DEFAULT_SIGNAL_OPTION},
- {"ignore-signal", optional_argument, nullptr, IGNORE_SIGNAL_OPTION},
- {"block-signal", optional_argument, nullptr, BLOCK_SIGNAL_OPTION},
- {"list-signal-handling", no_argument, nullptr, LIST_SIGNAL_HANDLING_OPTION},
- {"debug", no_argument, nullptr, 'v'},
- {"split-string", required_argument, nullptr, 'S'},
+ {"argv0", required_argument, NULL, 'a'},
+ {"ignore-environment", no_argument, NULL, 'i'},
+ {"null", no_argument, NULL, '0'},
+ {"unset", required_argument, NULL, 'u'},
+ {"chdir", required_argument, NULL, 'C'},
+ {"default-signal", optional_argument, NULL, DEFAULT_SIGNAL_OPTION},
+ {"ignore-signal", optional_argument, NULL, IGNORE_SIGNAL_OPTION},
+ {"block-signal", optional_argument, NULL, BLOCK_SIGNAL_OPTION},
+ {"list-signal-handling", no_argument, NULL, LIST_SIGNAL_HANDLING_OPTION},
+ {"debug", no_argument, NULL, 'v'},
+ {"split-string", required_argument, NULL, 'S'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -119,39 +119,53 @@ Set each NAME to VALUE in the environment and run COMMAND.\n\
emit_mandatory_arg_note ();
- fputs (_("\
- -a, --argv0=ARG pass ARG as the zeroth argument of COMMAND\n\
-"), stdout);
- fputs (_("\
- -i, --ignore-environment start with an empty environment\n\
- -0, --null end each output line with NUL, not newline\n\
- -u, --unset=NAME remove variable from the environment\n\
-"), stdout);
- fputs (_("\
- -C, --chdir=DIR change working directory to DIR\n\
-"), stdout);
- fputs (_("\
- -S, --split-string=S process and split S into separate arguments;\n\
- used to pass multiple arguments on shebang lines\n\
-"), stdout);
- fputs (_("\
- --block-signal[=SIG] block delivery of SIG signal(s) to COMMAND\n\
-"), stdout);
- fputs (_("\
- --default-signal[=SIG] reset handling of SIG signal(s) to the default\n\
-"), stdout);
- fputs (_("\
- --ignore-signal[=SIG] set handling of SIG signal(s) to do nothing\n\
-"), stdout);
- fputs (_("\
- --list-signal-handling list non default signal handling to \
-standard error\n\
-"), stdout);
- fputs (_("\
- -v, --debug print verbose information for each processing step\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -a, --argv0=ARG\n\
+ pass ARG as the zeroth argument of COMMAND\n\
+"));
+ oputs (_("\
+ -i, --ignore-environment\n\
+ start with an empty environment\n\
+"));
+ oputs (_("\
+ -0, --null\n\
+ end each output line with NUL, not newline\n\
+"));
+ oputs (_("\
+ -u, --unset=NAME\n\
+ remove variable from the environment\n\
+"));
+ oputs (_("\
+ -C, --chdir=DIR\n\
+ change working directory to DIR\n\
+"));
+ oputs (_("\
+ -S, --split-string=S\n\
+ process and split S into separate arguments;\n\
+ used to pass multiple arguments on shebang lines\n\
+"));
+ oputs (_("\
+ --block-signal[=SIG]\n\
+ block delivery of SIG signal(s) to COMMAND\n\
+"));
+ oputs (_("\
+ --default-signal[=SIG]\n\
+ reset handling of SIG signal(s) to the default\n\
+"));
+ oputs (_("\
+ --ignore-signal[=SIG]\n\
+ set handling of SIG signal(s) to do nothing\n\
+"));
+ oputs (_("\
+ --list-signal-handling\n\
+ list non default signal handling to standard error\n\
+"));
+ oputs (_("\
+ -v, --debug\n\
+ print verbose information for each processing step\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
A mere - implies -i. If no COMMAND, print the resulting environment.\n\
@@ -189,7 +203,7 @@ unset_envvars (void)
}
}
-/* Return a pointer to the end of a valid ${VARNAME} string, or nullptr.
+/* Return a pointer to the end of a valid ${VARNAME} string, or NULL.
'str' should point to the '$' character.
First letter in VARNAME must be alpha or underscore,
rest of letters are alnum or underscore.
@@ -207,31 +221,29 @@ scan_varname (char const *str)
return end;
}
- return nullptr;
+ return NULL;
}
/* Return a pointer to a static buffer containing the VARNAME as
extracted from a '${VARNAME}' string.
The returned string will be NUL terminated.
The returned pointer should not be freed.
- Return nullptr if not a valid ${VARNAME} syntax. */
+ Return NULL if not a valid ${VARNAME} syntax. */
static char *
extract_varname (char const *str)
{
- idx_t i;
- char const *p;
+ char const *p = scan_varname (str);
- p = scan_varname (str);
if (!p)
- return nullptr;
+ return NULL;
/* -2 and +2 (below) account for the '${' prefix. */
- i = p - str - 2;
+ idx_t i = p - str - 2;
if (i >= vnlen)
{
free (varname);
- varname = xpalloc (nullptr, &vnlen, i + 1 - vnlen, -1, sizeof *varname);
+ varname = xpalloc (NULL, &vnlen, i + 1 - vnlen, -1, sizeof *varname);
}
memcpy (varname, str + 2, i);
@@ -292,7 +304,7 @@ splitbuf_append_byte (struct splitbuf *ss, char c)
if (ss->half_alloc * sizeof *ss->argv <= string_bytes)
splitbuf_grow (ss);
((char *) (ss->argv + ss->half_alloc))[string_bytes] = c;
- ss->argv[ss->argc] = (char *) (intptr_t) (string_bytes + 1);
+ ss->argv[ss->argc] = (char *) (intptr_t) {string_bytes + 1};
}
/* If SS's most recent character was a separator, finish off its
@@ -360,7 +372,7 @@ build_argv (char const *str, int extra_argc, int *argc)
ss.half_alloc = extra_argc + 2;
ss.extra_argc = extra_argc;
ss.sep = true;
- ss.argv[ss.argc] = 0;
+ ss.argv[ss.argc] = NULL;
/* In the following loop,
'break' causes the character 'newc' to be added to *dest,
@@ -499,7 +511,7 @@ build_argv (char const *str, int extra_argc, int *argc)
argv[1] = "-S-i -C/tmp A=B"
argv[2] = "foo"
argv[3] = "bar"
- argv[4] = nullptr
+ argv[4] = NULL
This function will modify argv to be:
argv[0] = "env"
argv[1] = "-i"
@@ -507,7 +519,7 @@ build_argv (char const *str, int extra_argc, int *argc)
argv[3] = "A=B"
argv[4] = "foo"
argv[5] = "bar"
- argv[6] = nullptr
+ argv[6] = NULL
argc will be updated from 4 to 6.
optind will be reset to 0 to force getopt_long to rescan all arguments. */
static void
@@ -543,9 +555,6 @@ parse_split_string (char const *str, int *orig_optind,
static void
parse_signal_action_params (char const *arg, bool set_default)
{
- char *opt_sig;
- char *optarg_writable;
-
if (! arg)
{
/* Without an argument, reset all signals.
@@ -556,9 +565,9 @@ parse_signal_action_params (char const *arg, bool set_default)
return;
}
- optarg_writable = xstrdup (arg);
+ char *optarg_writable = xstrdup (arg);
- opt_sig = strtok (optarg_writable, ",");
+ char *opt_sig = strtok (optarg_writable, ",");
while (opt_sig)
{
int signum = operand2sig (opt_sig);
@@ -570,7 +579,7 @@ parse_signal_action_params (char const *arg, bool set_default)
signals[signum] = set_default ? DEFAULT : IGNORE;
- opt_sig = strtok (nullptr, ",");
+ opt_sig = strtok (NULL, ",");
}
free (optarg_writable);
@@ -592,7 +601,7 @@ reset_signal_handlers (void)
bool set_to_default = (signals[i] == DEFAULT
|| signals[i] == DEFAULT_NOERR);
- int sig_err = sigaction (i, nullptr, &act);
+ int sig_err = sigaction (i, NULL, &act);
if (sig_err && !ignore_errors)
error (EXIT_CANCELED, errno,
@@ -601,7 +610,7 @@ reset_signal_handlers (void)
if (! sig_err)
{
act.sa_handler = set_to_default ? SIG_DFL : SIG_IGN;
- sig_err = sigaction (i, &act, nullptr);
+ sig_err = sigaction (i, &act, NULL);
if (sig_err && !ignore_errors)
error (EXIT_CANCELED, errno,
_("failed to set signal action for signal %d"), i);
@@ -624,9 +633,6 @@ reset_signal_handlers (void)
static void
parse_block_signal_params (char const *arg, bool block)
{
- char *opt_sig;
- char *optarg_writable;
-
if (! arg)
{
/* Without an argument, reset all signals. */
@@ -645,9 +651,9 @@ parse_block_signal_params (char const *arg, bool block)
if (! arg)
return;
- optarg_writable = xstrdup (arg);
+ char *optarg_writable = xstrdup (arg);
- opt_sig = strtok (optarg_writable, ",");
+ char *opt_sig = strtok (optarg_writable, ",");
while (opt_sig)
{
int signum = operand2sig (opt_sig);
@@ -667,7 +673,7 @@ parse_block_signal_params (char const *arg, bool block)
else
sigdelset (block ? &unblock_signals : &block_signals, signum);
- opt_sig = strtok (nullptr, ",");
+ opt_sig = strtok (NULL, ",");
}
free (optarg_writable);
@@ -678,15 +684,16 @@ set_signal_proc_mask (void)
{
/* Get the existing signal mask */
sigset_t set;
- char const *debug_act;
sigemptyset (&set);
- if (sigprocmask (0, nullptr, &set))
+ if (sigprocmask (0, NULL, &set))
error (EXIT_CANCELED, errno, _("failed to get signal process mask"));
for (int i = 1; i <= SIGNUM_BOUND; i++)
{
+ char const *debug_act = NULL;
+
if (sigismember (&block_signals, i))
{
sigaddset (&set, i);
@@ -697,10 +704,6 @@ set_signal_proc_mask (void)
sigdelset (&set, i);
debug_act = "UNBLOCK";
}
- else
- {
- debug_act = nullptr;
- }
if (dev_debug && debug_act)
{
@@ -712,7 +715,7 @@ set_signal_proc_mask (void)
}
}
- if (sigprocmask (SIG_SETMASK, &set, nullptr))
+ if (sigprocmask (SIG_SETMASK, &set, NULL))
error (EXIT_CANCELED, errno, _("failed to set signal process mask"));
}
@@ -720,16 +723,15 @@ static void
list_signal_handling (void)
{
sigset_t set;
- char signame[SIG2STR_MAX];
sigemptyset (&set);
- if (sigprocmask (0, nullptr, &set))
+ if (sigprocmask (0, NULL, &set))
error (EXIT_CANCELED, errno, _("failed to get signal process mask"));
for (int i = 1; i <= SIGNUM_BOUND; i++)
{
struct sigaction act;
- if (sigaction (i, nullptr, &act))
+ if (sigaction (i, NULL, &act))
continue;
char const *ignored = act.sa_handler == SIG_IGN ? "IGNORE" : "";
@@ -739,6 +741,7 @@ list_signal_handling (void)
if (! *ignored && ! *blocked)
continue;
+ char signame[SIG2STR_MAX];
if (sig2str (i, signame) != 0)
snprintf (signame, sizeof signame, "SIG%d", i);
fprintf (stderr, "%-10s (%2d): %s%s%s\n", signame, i,
@@ -753,18 +756,15 @@ initialize_signals (void)
for (int i = 0 ; i <= SIGNUM_BOUND; i++)
signals[i] = UNCHANGED;
-
- return;
}
int
main (int argc, char **argv)
{
- int optc;
bool ignore_environment = false;
bool opt_nul_terminate_output = false;
- char const *newdir = nullptr;
- char *argv0 = nullptr;
+ char const *newdir = NULL;
+ char *argv0 = NULL;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -777,7 +777,8 @@ main (int argc, char **argv)
initialize_signals ();
- while ((optc = getopt_long (argc, argv, shortopts, longopts, nullptr)) != -1)
+ int optc;
+ while ((optc = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
{
switch (optc)
{
@@ -840,7 +841,7 @@ main (int argc, char **argv)
if (ignore_environment)
{
devmsg ("cleaning environ\n");
- static char *dummy_environ[] = { nullptr };
+ static char *dummy_environ[] = { NULL };
environ = dummy_environ;
}
else
@@ -883,9 +884,11 @@ main (int argc, char **argv)
if (! program_specified)
{
/* Print the environment and exit. */
- char *const *e = environ;
- while (*e)
- printf ("%s%c", *e++, opt_nul_terminate_output ? '\0' : '\n');
+ for (char *const *e = environ; *e; ++e)
+ {
+ fputs (*e, stdout);
+ putchar (opt_nul_terminate_output ? '\0' : '\n');
+ }
return EXIT_SUCCESS;
}
diff --git a/src/expand-common.c b/src/expand-common.c
index 14dd804f9..f63e6d80b 100644
--- a/src/expand-common.c
+++ b/src/expand-common.c
@@ -1,5 +1,5 @@
/* expand-common - common functionality for expand/unexpand
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
#include <config.h>
-#include <ctype.h>
#include <stdio.h>
#include <sys/types.h>
#include "system.h"
@@ -45,7 +44,7 @@ idx_t max_column_width;
/* Array of the explicit column numbers of the tab stops;
after 'tab_list' is exhausted, each additional tab is replaced
by a space. The first column is column 0. */
-static colno *tab_list = nullptr;
+static colno *tab_list = NULL;
/* The number of allocated entries in 'tab_list'. */
static idx_t n_tabs_allocated = 0;
@@ -55,12 +54,12 @@ static idx_t n_tabs_allocated = 0;
static idx_t first_free_tab = 0;
/* Null-terminated array of input filenames. */
-static char **file_list = nullptr;
+static char **file_list = NULL;
/* Default for 'file_list' if no files are given on the command line. */
static char *stdin_argv[] =
{
- (char *) "-", nullptr
+ (char *) "-", NULL
};
/* True if we have ever read standard input. */
@@ -141,7 +140,7 @@ parse_tab_stops (char const *stops)
colno tabval = 0;
bool extend_tabval = false;
bool increment_tabval = false;
- char const *num_start = nullptr;
+ char const *num_start = NULL;
bool ok = true;
for (; *stops; stops++)
@@ -344,7 +343,7 @@ set_file_list (char **list)
/* Close the old stream pointer FP if it is non-null,
and return a new one opened to read the next input file.
Open a filename of '-' as the standard input.
- Return nullptr if there are no more input files. */
+ Return NULL if there are no more input files. */
extern FILE *
next_file (FILE *fp)
@@ -368,7 +367,7 @@ next_file (FILE *fp)
}
}
- while ((file = *file_list++) != nullptr)
+ while ((file = *file_list++) != NULL)
{
if (streq (file, "-"))
{
@@ -386,7 +385,7 @@ next_file (FILE *fp)
error (0, errno, "%s", quotef (file));
exit_status = EXIT_FAILURE;
}
- return nullptr;
+ return NULL;
}
/* Close standard input if we have read from it. */
@@ -400,17 +399,18 @@ cleanup_file_list_stdin (void)
/* Emit the --help output for --tabs=LIST option accepted by expand and
unexpand. */
extern void
-emit_tab_list_info (void)
+emit_tab_list_info (char const *program)
{
/* suppress syntax check for emit_mandatory_arg_note() */
+ oputs_ (program, _("\
+ -t, --tabs=LIST\n\
+ use comma separated list of tab positions.\n\
+"));
fputs (_("\
- -t, --tabs=LIST use comma separated list of tab positions.\n\
-"), stdout);
- fputs (_("\
- The last specified position can be prefixed with '/'\n\
- to specify a tab size to use after the last\n\
- explicitly specified tab stop. Also a prefix of '+'\n\
- can be used to align remaining tab stops relative to\n\
- the last specified tab stop instead of the first column\n\
+ The last specified position can be prefixed with '/'\n\
+ to specify a tab size to use after the last\n\
+ explicitly specified tab stop. Also a prefix of '+'\n\
+ can be used to align remaining tab stops relative to\n\
+ the last specified tab stop instead of the first column\n\
"), stdout);
}
diff --git a/src/expand-common.h b/src/expand-common.h
index 46ef4e35b..49cae1116 100644
--- a/src/expand-common.h
+++ b/src/expand-common.h
@@ -1,6 +1,6 @@
/* expand-common - common functionality for expand/unexpand
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -62,7 +62,7 @@ set_file_list (char **file_list);
/* Close the old stream pointer FP if it is non-null,
and return a new one opened to read the next input file.
Open a filename of '-' as the standard input.
- Return nullptr if there are no more input files. */
+ Return NULL if there are no more input files. */
extern FILE *
next_file (FILE *fp);
@@ -73,4 +73,4 @@ cleanup_file_list_stdin (void);
/* Emit the --help output for --tabs=LIST option accepted by expand and
unexpand. */
extern void
-emit_tab_list_info (void);
+emit_tab_list_info (char const *program);
diff --git a/src/expand.c b/src/expand.c
index 5ec7ce9b8..1d0759079 100644
--- a/src/expand.c
+++ b/src/expand.c
@@ -1,5 +1,5 @@
/* expand - convert tabs to spaces
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -34,11 +34,14 @@
#include <config.h>
-#include <ctype.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
+
#include "system.h"
+#include "ioblksize.h"
+#include "mcel.h"
+#include "mbbuf.h"
#include "expand-common.h"
/* The official name of this program (e.g., no 'g' prefix). */
@@ -50,11 +53,11 @@ static char const shortopts[] = "it:0::1::2::3::4::5::6::7::8::9::";
static struct option const longopts[] =
{
- {"tabs", required_argument, nullptr, 't'},
- {"initial", no_argument, nullptr, 'i'},
+ {"tabs", required_argument, NULL, 't'},
+ {"initial", no_argument, NULL, 'i'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -75,13 +78,17 @@ Convert tabs in each FILE to spaces, writing to standard output.\n\
emit_stdin_note ();
emit_mandatory_arg_note ();
- fputs (_("\
- -i, --initial do not convert tabs after non blanks\n\
- -t, --tabs=N have tabs N characters apart, not 8\n\
-"), stdout);
- emit_tab_list_info ();
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -i, --initial\n\
+ do not convert tabs after non blanks\n\
+"));
+ oputs (_("\
+ -t, --tabs=N\n\
+ have tabs N characters apart, not 8\n\
+"));
+ emit_tab_list_info (PROGRAM_NAME);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -95,15 +102,19 @@ static void
expand (void)
{
/* Input stream. */
- FILE *fp = next_file (nullptr);
+ FILE *fp = next_file (NULL);
if (!fp)
return;
+ static char line_in[IO_BUFSIZE];
+ mbbuf_t mbbuf;
+ mbbuf_init (&mbbuf, line_in, sizeof line_in, fp);
+
while (true)
{
/* Input character, or EOF. */
- int c;
+ mcel_t g;
/* If true, perform translations. */
bool convert = true;
@@ -123,12 +134,15 @@ expand (void)
do
{
- while ((c = getc (fp)) < 0 && (fp = next_file (fp)))
- continue;
+ while ((g = mbbuf_get_char (&mbbuf)).ch == MBBUF_EOF
+ && (fp = next_file (fp)))
+ mbbuf_init (&mbbuf, line_in, sizeof line_in, fp);
if (convert)
{
- if (c == '\t')
+ convert &= convert_entire_line || c32issep (g.ch);
+
+ if (g.ch == '\t')
{
/* Column the next input tab stop is on. */
bool last_tab;
@@ -139,9 +153,12 @@ expand (void)
if (putchar (' ') < 0)
write_error ();
- c = ' ';
+ if (putchar (' ') < 0)
+ write_error ();
+
+ continue;
}
- else if (c == '\b')
+ else if (g.ch == '\b')
{
/* Go back one column, and force recalculation of the
next tab stop. */
@@ -150,20 +167,21 @@ expand (void)
}
else
{
- if (ckd_add (&column, column, 1))
+ int width = c32width (g.ch);
+ if (ckd_add (&column, column, width < 0 ? 1 : width))
error (EXIT_FAILURE, 0, _("input line is too long"));
}
- convert &= convert_entire_line || !! isblank (c);
}
- if (c < 0)
+ if (g.ch == MBBUF_EOF)
return;
- if (putchar (c) < 0)
+ fwrite (mbbuf_char_offset (&mbbuf, g), sizeof (char), g.len, stdout);
+ if (ferror (stdout))
write_error ();
}
- while (c != '\n');
+ while (g.ch != '\n');
}
}
@@ -181,7 +199,7 @@ main (int argc, char **argv)
atexit (close_stdout);
convert_entire_line = true;
- while ((c = getopt_long (argc, argv, shortopts, longopts, nullptr)) != -1)
+ while ((c = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
{
switch (c)
{
@@ -217,7 +235,7 @@ main (int argc, char **argv)
finalize_tab_stops ();
- set_file_list (optind < argc ? &argv[optind] : nullptr);
+ set_file_list (optind < argc ? &argv[optind] : NULL);
expand ();
diff --git a/src/expr.c b/src/expr.c
index 17a1ddd29..94522b264 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -1,5 +1,5 @@
/* expr -- evaluate expressions.
- Copyright (C) 1986-2025 Free Software Foundation, Inc.
+ Copyright (C) 1986-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -39,7 +39,6 @@
#include "long-options.h"
#include "mcel.h"
#include "strnumcmp.h"
-#include "xstrtol.h"
/* Various parts of this code assume size_t fits into unsigned long
int, the widest unsigned type that GMP supports. */
@@ -115,14 +114,13 @@ static void printv (VALUE *v);
static size_t
mbs_logical_cspn (char const *s, char const *accept)
{
- size_t idx = 0;
-
if (accept[0] == '\0')
return 0;
/* General case. */
if (MB_CUR_MAX > 1)
{
+ size_t idx = 0;
for (char const *p = s; *p; )
{
++idx;
@@ -136,7 +134,7 @@ mbs_logical_cspn (char const *s, char const *accept)
for (char const *a = accept; *a; )
{
mcel_t h = mcel_scanz (a);
- if (mcel_cmp (g, h) == 0)
+ if (mcel_eq (g, h))
return idx;
a += h.len;
}
@@ -237,8 +235,8 @@ Usage: %s EXPRESSION\n\
"),
program_name, program_name);
putchar ('\n');
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
Print the value of EXPRESSION to standard output. A blank line below\n\
@@ -306,8 +304,6 @@ or 0, 2 if EXPRESSION is syntactically invalid, and 3 if an error occurred.\n\
int
main (int argc, char **argv)
{
- VALUE *v;
-
initialize_main (&argc, &argv);
set_program_name (argv[0]);
setlocale (LC_ALL, "");
@@ -318,7 +314,7 @@ main (int argc, char **argv)
atexit (close_stdout);
parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, VERSION,
- usage, AUTHORS, (char const *) nullptr);
+ usage, AUTHORS, (char const *) NULL);
/* The above handles --help and --version.
Since there is no other invocation of getopt, handle '--' here. */
@@ -336,7 +332,7 @@ main (int argc, char **argv)
args = argv + 1;
- v = eval (true);
+ VALUE *v = eval (true);
if (!nomoreargs ())
error (EXPR_INVALID, 0, _("syntax error: unexpected argument %s"),
quotearg_n_style (0, locale_quoting_style, *args));
@@ -456,7 +452,7 @@ tostring (VALUE *v)
{
case integer:
{
- char *s = mpz_get_str (nullptr, 10, v->u.i);
+ char *s = mpz_get_str (NULL, 10, v->u.i);
mpz_clear (v->u.i);
v->u.s = s;
v->type = string;
@@ -518,7 +514,7 @@ getsize (mpz_t i)
static bool
nextarg (char const *str)
{
- if (*args == nullptr)
+ if (*args == NULL)
return false;
else
{
@@ -533,7 +529,7 @@ nextarg (char const *str)
static bool
nomoreargs (void)
{
- return *args == 0;
+ return !*args;
}
/* Report missing operand.
@@ -552,13 +548,10 @@ require_more_args (void)
/* Print evaluation trace and args remaining. */
static void
-trace (fxn)
- char *fxn;
+trace (char const *fxn)
{
- char **a;
-
printf ("%s:", fxn);
- for (a = args; *a; a++)
+ for (char **a = args; *a; a++)
printf (" %s", *a);
putchar ('\n');
}
@@ -571,32 +564,31 @@ trace (fxn)
static VALUE *
docolon (VALUE *sv, VALUE *pv)
{
- VALUE *v;
- char const *errmsg;
- struct re_pattern_buffer re_buffer;
- char fastmap[UCHAR_MAX + 1];
- struct re_registers re_regs;
- regoff_t matchlen;
-
tostring (sv);
tostring (pv);
+ struct re_registers re_regs;
re_regs.num_regs = 0;
- re_regs.start = nullptr;
- re_regs.end = nullptr;
+ re_regs.start = NULL;
+ re_regs.end = NULL;
- re_buffer.buffer = nullptr;
+ struct re_pattern_buffer re_buffer;
+ char fastmap[UCHAR_MAX + 1];
+ re_buffer.buffer = NULL;
re_buffer.allocated = 0;
re_buffer.fastmap = fastmap;
- re_buffer.translate = nullptr;
- re_syntax_options =
- RE_SYNTAX_POSIX_BASIC & ~RE_CONTEXT_INVALID_DUP & ~RE_NO_EMPTY_RANGES;
- errmsg = re_compile_pattern (pv->u.s, strlen (pv->u.s), &re_buffer);
+ re_buffer.translate = NULL;
+ re_syntax_options = (RE_SYNTAX_POSIX_BASIC & ~RE_CONTEXT_INVALID_DUP
+ & ~RE_NO_EMPTY_RANGES);
+ char const *errmsg = re_compile_pattern (pv->u.s, strlen (pv->u.s),
+ &re_buffer);
if (errmsg)
error (EXPR_INVALID, 0, "%s", (errmsg));
re_buffer.newline_anchor = 0;
- matchlen = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
+ VALUE *v;
+ regoff_t matchlen = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0,
+ &re_regs);
if (0 <= matchlen)
{
/* Were \(...\) used? */
@@ -638,7 +630,7 @@ docolon (VALUE *sv, VALUE *pv)
free (re_regs.start);
free (re_regs.end);
}
- re_buffer.fastmap = nullptr;
+ re_buffer.fastmap = NULL;
regfree (&re_buffer);
return v;
}
@@ -648,8 +640,6 @@ docolon (VALUE *sv, VALUE *pv)
static VALUE *
eval7 (bool evaluate)
{
- VALUE *v;
-
#ifdef EVAL_TRACE
trace ("eval7");
#endif
@@ -657,7 +647,7 @@ eval7 (bool evaluate)
if (nextarg ("("))
{
- v = eval (evaluate);
+ VALUE *v = eval (evaluate);
if (nomoreargs ())
error (EXPR_INVALID, 0, _("syntax error: expecting ')' after %s"),
quotearg_n_style (0, locale_quoting_style, *(args - 1)));
@@ -678,12 +668,6 @@ eval7 (bool evaluate)
static VALUE *
eval6 (bool evaluate)
{
- VALUE *l;
- VALUE *r;
- VALUE *v;
- VALUE *i1;
- VALUE *i2;
-
#ifdef EVAL_TRACE
trace ("eval6");
#endif
@@ -694,16 +678,17 @@ eval6 (bool evaluate)
}
else if (nextarg ("length"))
{
- r = eval6 (evaluate);
+ VALUE *r = eval6 (evaluate);
tostring (r);
- v = int_value (mbslen (r->u.s));
+ VALUE *v = int_value (mbslen (r->u.s));
freev (r);
return v;
}
else if (nextarg ("match"))
{
- l = eval6 (evaluate);
- r = eval6 (evaluate);
+ VALUE *l = eval6 (evaluate);
+ VALUE *r = eval6 (evaluate);
+ VALUE *v;
if (evaluate)
{
v = docolon (l, r);
@@ -716,25 +701,24 @@ eval6 (bool evaluate)
}
else if (nextarg ("index"))
{
- size_t pos;
-
- l = eval6 (evaluate);
- r = eval6 (evaluate);
+ VALUE *l = eval6 (evaluate);
+ VALUE *r = eval6 (evaluate);
tostring (l);
tostring (r);
- pos = mbs_logical_cspn (l->u.s, r->u.s);
- v = int_value (pos);
+ size_t pos = mbs_logical_cspn (l->u.s, r->u.s);
+ VALUE *v = int_value (pos);
freev (l);
freev (r);
return v;
}
else if (nextarg ("substr"))
{
- l = eval6 (evaluate);
- i1 = eval6 (evaluate);
- i2 = eval6 (evaluate);
+ VALUE *l = eval6 (evaluate);
+ VALUE *i1 = eval6 (evaluate);
+ VALUE *i2 = eval6 (evaluate);
tostring (l);
+ VALUE *v;
if (!toarith (i1) || !toarith (i2))
v = str_value ("");
else
@@ -761,22 +745,18 @@ eval6 (bool evaluate)
static VALUE *
eval5 (bool evaluate)
{
- VALUE *l;
- VALUE *r;
- VALUE *v;
-
#ifdef EVAL_TRACE
trace ("eval5");
#endif
- l = eval6 (evaluate);
+ VALUE *l = eval6 (evaluate);
while (true)
{
if (nextarg (":"))
{
- r = eval6 (evaluate);
+ VALUE *r = eval6 (evaluate);
if (evaluate)
{
- v = docolon (l, r);
+ VALUE *v = docolon (l, r);
freev (l);
l = v;
}
@@ -792,16 +772,14 @@ eval5 (bool evaluate)
static VALUE *
eval4 (bool evaluate)
{
- VALUE *l;
- VALUE *r;
- enum { multiply, divide, mod } fxn;
-
#ifdef EVAL_TRACE
trace ("eval4");
#endif
- l = eval5 (evaluate);
+ VALUE *l = eval5 (evaluate);
while (true)
{
+ enum { multiply, divide, mod } fxn;
+
if (nextarg ("*"))
fxn = multiply;
else if (nextarg ("/"))
@@ -810,7 +788,7 @@ eval4 (bool evaluate)
fxn = mod;
else
return l;
- r = eval5 (evaluate);
+ VALUE *r = eval5 (evaluate);
if (evaluate)
{
if (!toarith (l) || !toarith (r))
@@ -831,23 +809,21 @@ eval4 (bool evaluate)
static VALUE *
eval3 (bool evaluate)
{
- VALUE *l;
- VALUE *r;
- enum { plus, minus } fxn;
-
#ifdef EVAL_TRACE
trace ("eval3");
#endif
- l = eval4 (evaluate);
+ VALUE *l = eval4 (evaluate);
while (true)
{
+ enum { plus, minus } fxn;
+
if (nextarg ("+"))
fxn = plus;
else if (nextarg ("-"))
fxn = minus;
else
return l;
- r = eval4 (evaluate);
+ VALUE *r = eval4 (evaluate);
if (evaluate)
{
if (!toarith (l) || !toarith (r))
@@ -863,20 +839,16 @@ eval3 (bool evaluate)
static VALUE *
eval2 (bool evaluate)
{
- VALUE *l;
-
#ifdef EVAL_TRACE
trace ("eval2");
#endif
- l = eval3 (evaluate);
+ VALUE *l = eval3 (evaluate);
while (true)
{
- VALUE *r;
enum
{
less_than, less_equal, equal, not_equal, greater_equal, greater_than
} fxn;
- bool val = false;
if (nextarg ("<"))
fxn = less_than;
@@ -892,14 +864,15 @@ eval2 (bool evaluate)
fxn = greater_than;
else
return l;
- r = eval3 (evaluate);
+ VALUE *r = eval3 (evaluate);
+ bool val = false;
if (evaluate)
{
- int cmp;
tostring (l);
tostring (r);
+ int cmp;
if (looks_like_integer (l->u.s) && looks_like_integer (r->u.s))
cmp = strintcmp (l->u.s, r->u.s);
else
@@ -941,18 +914,15 @@ eval2 (bool evaluate)
static VALUE *
eval1 (bool evaluate)
{
- VALUE *l;
- VALUE *r;
-
#ifdef EVAL_TRACE
trace ("eval1");
#endif
- l = eval2 (evaluate);
+ VALUE *l = eval2 (evaluate);
while (true)
{
if (nextarg ("&"))
{
- r = eval2 (evaluate && !null (l));
+ VALUE *r = eval2 (evaluate && !null (l));
if (null (l) || null (r))
{
freev (l);
@@ -972,18 +942,15 @@ eval1 (bool evaluate)
static VALUE *
eval (bool evaluate)
{
- VALUE *l;
- VALUE *r;
-
#ifdef EVAL_TRACE
trace ("eval");
#endif
- l = eval1 (evaluate);
+ VALUE *l = eval1 (evaluate);
while (true)
{
if (nextarg ("|"))
{
- r = eval1 (evaluate && null (l));
+ VALUE *r = eval1 (evaluate && null (l));
if (null (l))
{
freev (l);
diff --git a/src/extract-magic b/src/extract-magic
index 978c88a8c..350ede12f 100644
--- a/src/extract-magic
+++ b/src/extract-magic
@@ -1,7 +1,7 @@
#!/usr/bin/perl -w
# Derive #define directives from specially formatted 'case ...:' statements.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -114,8 +114,8 @@ EOF
my $map_comment = <<EOF;
/* Map each S_MAGIC_* value to 1, 0 or -1.
- 1 if it is known to be a remote file system type,
- 0 if it is known to be a local file system type, or -1 otherwise. */
+ 1 if it is known to be a local file system type,
+ 0 if it is known to be a remote file system type, or -1 otherwise. */
EOF
my $magic_comment = <<EOF;
/* Define the magic numbers as given by statfs(2).
diff --git a/src/factor.c b/src/factor.c
index 02b02a977..e9c373207 100644
--- a/src/factor.c
+++ b/src/factor.c
@@ -1,5 +1,5 @@
/* factor -- print prime factors of n.
- Copyright (C) 1986-2025 Free Software Foundation, Inc.
+ Copyright (C) 1986-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -175,6 +175,11 @@ typedef uint64_t UDItype;
# if defined ASSERT || defined __GMP_DECLSPEC || defined __GMP_GNUC_PREREQ
# endif
+/* longlong.h uses casts even when useless. */
+# if 14 <= __GNUC__
+# pragma GCC diagnostic ignored "-Wuseless-cast"
+# endif
+
# if _ARCH_PPC
# define HAVE_HOST_CPU_FAMILY_powerpc 1
# endif
@@ -199,11 +204,11 @@ enum
static struct option const long_options[] =
{
- {"exponents", no_argument, nullptr, 'h'},
- {"-debug", no_argument, nullptr, DEV_DEBUG_OPTION},
+ {"exponents", no_argument, NULL, 'h'},
+ {"-debug", no_argument, NULL, DEV_DEBUG_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* If true, use p^e output format. */
@@ -645,11 +650,11 @@ mpn_tdiv_qr (mp_limb_t *qp, mp_limb_t *rp, MAYBE_UNUSED mp_size_t qxn,
mp_limb_t const *dp, mp_size_t dn)
{
mpz_t q, r, n, d;
- mpz_inits (q, r, nullptr);
+ mpz_inits (q, r, NULL);
mpz_tdiv_qr (q, r, mpz_roinit_n (n, np, nn), mpz_roinit_n (d, dp, dn));
copy_mpn_from_mpz (qp, nn - dn + 1, q);
copy_mpn_from_mpz (rp, dn, r);
- mpz_clears (q, r, nullptr);
+ mpz_clears (q, r, NULL);
}
#endif
@@ -659,7 +664,7 @@ static struct mp_factors mp_factor (mpz_t);
static struct mp_factors
mp_no_factors (void)
{
- return (struct mp_factors) {0,};
+ return (struct mp_factors) {NULL,};
}
/* Free storage allocated for FACTORS, making it uninitialized. */
@@ -1953,11 +1958,12 @@ Print the prime factors of each specified integer NUMBER. If none\n\
are specified on the command line, read them from standard input.\n\
\n\
"), stdout);
- fputs ("\
- -h, --exponents print repeated factors in form p^e unless e is 1\n\
-", stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -h, --exponents\n\
+ print repeated factors in form p^e unless e is 1\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -2002,7 +2008,7 @@ main (int argc, char **argv)
atexit (close_stdout);
int c;
- while ((c = getopt_long (argc, argv, "h", long_options, nullptr)) != -1)
+ while ((c = getopt_long (argc, argv, "h", long_options, NULL)) != -1)
{
switch (c)
{
diff --git a/src/find-mount-point.c b/src/find-mount-point.c
index 732e3cba7..f1666c93f 100644
--- a/src/find-mount-point.c
+++ b/src/find-mount-point.c
@@ -1,5 +1,5 @@
/* find-mount-point.c -- find the root mount point for a file.
- Copyright (C) 2010-2025 Free Software Foundation, Inc.
+ Copyright (C) 2010-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,19 +24,19 @@
/* Return the root mountpoint of the file system on which FILE exists, in
malloced storage. FILE_STAT should be the result of stating FILE.
- Give a diagnostic and return nullptr if unable to determine the mount point.
+ Give a diagnostic and return NULL if unable to determine the mount point.
Exit if unable to restore current working directory. */
extern char *
find_mount_point (char const *file, struct stat const *file_stat)
{
struct saved_cwd cwd;
struct stat last_stat;
- char *mp = nullptr; /* The malloc'd mount point. */
+ char *mp = NULL; /* The malloc'd mount point. */
if (save_cwd (&cwd) != 0)
{
error (0, errno, _("cannot get current directory"));
- return nullptr;
+ return NULL;
}
if (S_ISDIR (file_stat->st_mode))
@@ -46,29 +46,30 @@ find_mount_point (char const *file, struct stat const *file_stat)
if (chdir (file) < 0)
{
error (0, errno, _("cannot change to directory %s"), quoteaf (file));
- return nullptr;
+ return NULL;
}
}
else
/* FILE is some other kind of file; use its directory. */
{
- char *xdir = dir_name (file);
- char *dir;
- ASSIGN_STRDUPA (dir, xdir);
- free (xdir);
+ char *dir = dir_name (file);
if (chdir (dir) < 0)
{
error (0, errno, _("cannot change to directory %s"), quoteaf (dir));
- return nullptr;
+ free (dir);
+ return NULL;
}
if (stat (".", &last_stat) < 0)
{
error (0, errno, _("cannot stat current directory (now %s)"),
quoteaf (dir));
+ free (dir);
goto done;
}
+
+ free (dir);
}
/* Now walk up FILE's parents until we find another file system or /,
diff --git a/src/find-mount-point.h b/src/find-mount-point.h
index d4e6b9ebf..e6352bbf5 100644
--- a/src/find-mount-point.h
+++ b/src/find-mount-point.h
@@ -1,5 +1,5 @@
/* find-mount-point.h -- find the root mount point for a file.
- Copyright (C) 2010-2025 Free Software Foundation, Inc.
+ Copyright (C) 2010-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/fmt.c b/src/fmt.c
index 7a02205a8..3f76f6ec6 100644
--- a/src/fmt.c
+++ b/src/fmt.c
@@ -1,5 +1,5 @@
/* GNU fmt -- simple text formatter.
- Copyright (C) 1994-2025 Free Software Foundation, Inc.
+ Copyright (C) 1994-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,7 +17,6 @@
/* Written by Ross Paterson <rap@doc.ic.ac.uk>. */
#include <config.h>
-#include <ctype.h>
#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>
@@ -116,9 +115,9 @@ typedef long int COST;
/* Extra ctype(3)-style macros. */
-#define isopen(c) (strchr ("(['`\"", c) != nullptr)
-#define isclose(c) (strchr (")]'\"", c) != nullptr)
-#define isperiod(c) (strchr (".?!", c) != nullptr)
+#define isopen(c) (strchr ("(['`\"", c) != NULL)
+#define isclose(c) (strchr (")]'\"", c) != NULL)
+#define isperiod(c) (strchr (".?!", c) != NULL)
/* Size of a tab stop, for expansion on input and re-introduction on
output. */
@@ -183,11 +182,11 @@ static bool split;
static bool uniform;
/* Prefix minus leading and trailing spaces (default ""). */
-static char const *prefix;
+static char const *prefix = "";
/* User-supplied maximum line width (default WIDTH). The only output
lines longer than this will each comprise a single word. */
-static int max_width;
+static int max_width = WIDTH;
/* Values derived from the option values. */
@@ -275,23 +274,39 @@ The option -WIDTH is an abbreviated form of --width=DIGITS.\n\
emit_stdin_note ();
emit_mandatory_arg_note ();
- fputs (_("\
- -c, --crown-margin preserve indentation of first two lines\n\
- -p, --prefix=STRING reformat only lines beginning with STRING,\n\
- reattaching the prefix to reformatted lines\n\
- -s, --split-only split long lines, but do not refill\n\
-"),
- stdout);
+ oputs (_("\
+ -c, --crown-margin\n\
+ preserve indentation of first two lines\n\
+"));
+ oputs (_("\
+ -p, --prefix=STRING\n\
+ reformat only lines beginning with STRING,\n\
+ reattaching the prefix to reformatted lines\n\
+"));
+ oputs (_("\
+ -s, --split-only\n\
+ split long lines, but do not refill\n\
+"));
+ oputs (_("\
+ -t, --tagged-paragraph\n\
+ indentation of first line different from second\n\
+"));
+ oputs (_("\
+ -u, --uniform-spacing\n\
+ one space between words, two after sentences\n\
+"));
+ oputs (_("\
+ -w, --width=WIDTH\n\
+ maximum line width (default of 75 columns)\n\
+"));
/* Tell xgettext that the "% o" below is not a printf-style
format string: xgettext:no-c-format */
- fputs (_("\
- -t, --tagged-paragraph indentation of first line different from second\n\
- -u, --uniform-spacing one space between words, two after sentences\n\
- -w, --width=WIDTH maximum line width (default of 75 columns)\n\
- -g, --goal=WIDTH goal width (default of 93% of width)\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -g, --goal=WIDTH\n\
+ goal width (default of 93% of width)\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -301,16 +316,16 @@ The option -WIDTH is an abbreviated form of --width=DIGITS.\n\
static struct option const long_options[] =
{
- {"crown-margin", no_argument, nullptr, 'c'},
- {"prefix", required_argument, nullptr, 'p'},
- {"split-only", no_argument, nullptr, 's'},
- {"tagged-paragraph", no_argument, nullptr, 't'},
- {"uniform-spacing", no_argument, nullptr, 'u'},
- {"width", required_argument, nullptr, 'w'},
- {"goal", required_argument, nullptr, 'g'},
+ {"crown-margin", no_argument, NULL, 'c'},
+ {"prefix", required_argument, NULL, 'p'},
+ {"split-only", no_argument, NULL, 's'},
+ {"tagged-paragraph", no_argument, NULL, 't'},
+ {"uniform-spacing", no_argument, NULL, 'u'},
+ {"width", required_argument, NULL, 'w'},
+ {"goal", required_argument, NULL, 'g'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0},
+ {NULL, 0, NULL, 0},
};
int
@@ -318,8 +333,8 @@ main (int argc, char **argv)
{
int optchar;
bool ok = true;
- char const *max_width_option = nullptr;
- char const *goal_width_option = nullptr;
+ char const *max_width_option = NULL;
+ char const *goal_width_option = NULL;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -329,11 +344,6 @@ main (int argc, char **argv)
atexit (close_stdout);
- crown = tagged = split = uniform = false;
- max_width = WIDTH;
- prefix = "";
- prefix_length = prefix_lead_space = prefix_full_length = 0;
-
if (argc > 1 && argv[1][0] == '-' && c_isdigit (argv[1][1]))
{
/* Old option syntax; a dash followed by one or more digits. */
@@ -346,7 +356,7 @@ main (int argc, char **argv)
}
while ((optchar = getopt_long (argc, argv, "0123456789cstuw:p:g:",
- long_options, nullptr))
+ long_options, NULL))
!= -1)
switch (optchar)
{
@@ -404,7 +414,7 @@ main (int argc, char **argv)
/* Limit goal_width to max_width. */
goal_width = xdectoumax (goal_width_option, 0, max_width, "",
_("invalid width"), 0);
- if (max_width_option == nullptr)
+ if (max_width_option == NULL)
max_width = goal_width + 10;
}
else
@@ -433,7 +443,7 @@ main (int argc, char **argv)
{
FILE *in_stream;
in_stream = fopen (file, "r");
- if (in_stream != nullptr)
+ if (in_stream != NULL)
ok &= fmt (in_stream, file);
else
{
@@ -501,9 +511,9 @@ fmt (FILE *f, char const *file)
if (0 <= err)
{
if (f == stdin)
- error (0, err, _("read error"));
+ error (0, errno, _("read error"));
else
- error (0, err, _("error reading %s"), quoteaf (file));
+ error (0, errno, _("error reading %s"), quoteaf (file));
}
return err < 0;
}
@@ -636,13 +646,11 @@ get_paragraph (FILE *f)
static int
copy_rest (FILE *f, int c)
{
- char const *s;
-
out_column = 0;
if (in_column > next_prefix_indent || (c != '\n' && c != EOF))
{
put_space (next_prefix_indent);
- for (s = prefix; out_column != in_column && *s; out_column++)
+ for (char const *s = prefix; out_column != in_column && *s; out_column++)
putchar (*s++);
if (c != EOF && c != '\n')
put_space (in_column - out_column);
@@ -743,9 +751,8 @@ get_prefix (FILE *f)
prefix_lead_space : in_column;
else
{
- char const *p;
next_prefix_indent = in_column;
- for (p = prefix; *p != '\0'; p++)
+ for (char const *p = prefix; *p != '\0'; p++)
{
unsigned char pc = *p;
if (c != pc)
@@ -868,7 +875,7 @@ flush_paragraph (void)
static void
fmt_paragraph (void)
{
- WORD *start, *w;
+ WORD *w;
int len;
COST wcost, best;
int saved_length;
@@ -877,7 +884,7 @@ fmt_paragraph (void)
saved_length = word_limit->length;
word_limit->length = max_width; /* sentinel */
- for (start = word_limit - 1; start >= word; start--)
+ for (WORD *start = word_limit - 1; start >= word; start--)
{
best = MAXCOST;
len = start == word ? first_indent : other_indent;
@@ -911,7 +918,7 @@ fmt_paragraph (void)
len += (w - 1)->space + w->length; /* w > start >= word */
}
- while (len < max_width);
+ while (len <= max_width);
start->best_cost = best + base_cost (start);
}
@@ -983,10 +990,8 @@ line_cost (WORD *next, int len)
static void
put_paragraph (WORD *finish)
{
- WORD *w;
-
put_line (word, first_indent);
- for (w = word->next_break; w != finish; w = w->next_break)
+ for (WORD *w = word->next_break; w != finish; w = w->next_break)
put_line (w, other_indent);
}
@@ -1023,11 +1028,8 @@ put_line (WORD *w, int indent)
static void
put_word (WORD *w)
{
- char const *s;
- int n;
-
- s = w->text;
- for (n = w->length; n != 0; n--)
+ char const *s = w->text;
+ for (int n = w->length; n != 0; n--)
putchar (*s++);
out_column += w->length;
}
diff --git a/src/fold.c b/src/fold.c
index 47ed427db..f49078f01 100644
--- a/src/fold.c
+++ b/src/fold.c
@@ -1,5 +1,5 @@
/* fold -- wrap each input line to fit in specified width.
- Copyright (C) 1991-2025 Free Software Foundation, Inc.
+ Copyright (C) 1991-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
#include <config.h>
-#include <ctype.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
@@ -58,13 +57,13 @@ static char const shortopts[] = "bcsw:0::1::2::3::4::5::6::7::8::9::";
static struct option const longopts[] =
{
- {"bytes", no_argument, nullptr, 'b'},
- {"characters", no_argument, nullptr, 'c'},
- {"spaces", no_argument, nullptr, 's'},
- {"width", required_argument, nullptr, 'w'},
+ {"bytes", no_argument, NULL, 'b'},
+ {"characters", no_argument, NULL, 'c'},
+ {"spaces", no_argument, NULL, 's'},
+ {"width", required_argument, NULL, 'w'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -85,14 +84,24 @@ Wrap input lines in each FILE, writing to standard output.\n\
emit_stdin_note ();
emit_mandatory_arg_note ();
- fputs (_("\
- -b, --bytes count bytes rather than columns\n\
- -c, --characters count characters rather than columns\n\
- -s, --spaces break at spaces\n\
- -w, --width=WIDTH use WIDTH columns instead of 80\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -b, --bytes\n\
+ count bytes rather than columns\n\
+"));
+ oputs (_("\
+ -c, --characters\n\
+ count characters rather than columns\n\
+"));
+ oputs (_("\
+ -s, --spaces\n\
+ break after blanks, or in words greater than WIDTH\n\
+"));
+ oputs (_("\
+ -w, --width=WIDTH\n\
+ use WIDTH columns instead of 80\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -165,7 +174,7 @@ fold_file (char const *filename, size_t width)
else
istream = fopen (filename, "r");
- if (istream == nullptr)
+ if (istream == NULL)
{
error (0, errno, "%s", quotef (filename));
return false;
@@ -201,7 +210,7 @@ fold_file (char const *filename, size_t width)
for (mcel_t g2; logical_p < logical_lim; logical_p += g2.len)
{
g2 = mcel_scan (logical_p, logical_lim);
- if (c32isblank (g2.ch) && ! c32isnbspace (g2.ch))
+ if (c32issep (g2.ch))
{
space_length = g2.len;
logical_end = logical_p - line_out;
@@ -280,7 +289,6 @@ int
main (int argc, char **argv)
{
size_t width = 80;
- int i;
int optc;
bool ok;
@@ -292,9 +300,7 @@ main (int argc, char **argv)
atexit (close_stdout);
- break_spaces = have_read_stdin = false;
-
- while ((optc = getopt_long (argc, argv, shortopts, longopts, nullptr)) != -1)
+ while ((optc = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
{
char optargbuf[2];
@@ -343,7 +349,7 @@ main (int argc, char **argv)
else
{
ok = true;
- for (i = optind; i < argc; i++)
+ for (int i = optind; i < argc; i++)
ok &= fold_file (argv[i], width);
}
diff --git a/src/force-link.c b/src/force-link.c
index f173ebc9f..af2d485be 100644
--- a/src/force-link.c
+++ b/src/force-link.c
@@ -1,6 +1,6 @@
/* Implement ln -f "atomically"
- Copyright 2017-2025 Free Software Foundation, Inc.
+ Copyright 2017-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -44,7 +44,7 @@ enum { x_suffix_len = sizeof "XXXXXX" - 1 };
enum { smallsize = 256 };
/* Return a template for a file in the same directory as DSTNAME.
- Use BUF if the template fits, otherwise use malloc and return nullptr
+ Use BUF if the template fits, otherwise use malloc and return NULL
(setting errno) if unsuccessful. */
static char *
diff --git a/src/getlimits.c b/src/getlimits.c
index db44967e7..514dd3c09 100644
--- a/src/getlimits.c
+++ b/src/getlimits.c
@@ -1,5 +1,5 @@
/* getlimits - print various platform dependent limits.
- Copyright (C) 2008-2025 Free Software Foundation, Inc.
+ Copyright (C) 2008-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,10 +17,12 @@
/* Written by Pádraig Brady */
#include <config.h> /* sets _FILE_OFFSET_BITS=64 etc. */
+#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <float.h>
+#include "errno-iter.h"
#include "ftoastr.h"
#include "system.h"
#include "ioblksize.h"
@@ -54,6 +56,14 @@
# define OFF64_T_MIN TYPE_MINIMUM (off64_t)
#endif
+#ifndef SIGRTMIN
+# define SIGRTMIN 0
+# undef SIGRTMAX
+#endif
+#ifndef SIGRTMAX
+# define SIGRTMAX (SIGRTMIN - 1)
+#endif
+
/* These are not interesting to print.
* Instead of these defines it would be nice to be able to do
* #ifdef (TYPE##_MIN) in function macro below. */
@@ -120,6 +130,16 @@ PRINT_FLOATTYPE (print_FLT, float, ftoastr, FLT_BUFSIZE_BOUND)
PRINT_FLOATTYPE (print_DBL, double, dtoastr, DBL_BUFSIZE_BOUND)
PRINT_FLOATTYPE (print_LDBL, long double, ldtoastr, LDBL_BUFSIZE_BOUND)
+static int
+print_errno (void *name, int e)
+{
+ char const *err_name = name ? name : strerrorname_np (e);
+ if (err_name)
+ printf ("%s=%s\n", err_name,
+ quotearg_style (shell_escape_quoting_style, strerror (e)));
+ return 0;
+}
+
int
main (int argc, char **argv)
{
@@ -136,15 +156,15 @@ main (int argc, char **argv)
parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE_NAME,
VERSION, true, usage, AUTHORS,
- (char const *) nullptr);
+ (char const *) NULL);
#define print_int(TYPE) \
- sprintf (limit + 1, "%ju", (uintmax_t) TYPE##_MAX); \
+ sprintf (limit + 1, "%ju", (uintmax_t) {TYPE##_MAX}); \
printf (#TYPE"_MAX=%s\n", limit + 1); \
printf (#TYPE"_OFLOW=%s\n", decimal_absval_add_one (limit)); \
if (TYPE##_MIN) \
{ \
- sprintf (limit + 1, "%jd", (intmax_t) TYPE##_MIN); \
+ sprintf (limit + 1, "%jd", (intmax_t) {TYPE##_MIN}); \
printf (#TYPE"_MIN=%s\n", limit + 1); \
printf (#TYPE"_UFLOW=%s\n", decimal_absval_add_one (limit)); \
}
@@ -179,7 +199,25 @@ main (int argc, char **argv)
print_float (LDBL);
/* Other useful constants */
- printf ("IO_BUFSIZE=%ju\n", (uintmax_t) IO_BUFSIZE);
+ printf ("SIGRTMIN=%jd\n", (intmax_t) {SIGRTMIN});
+ printf ("SIGRTMAX=%jd\n", (intmax_t) {SIGRTMAX});
+ printf ("IO_BUFSIZE=%ju\n", (uintmax_t) {IO_BUFSIZE});
+
+ /* Errnos */
+ errno_iterate (print_errno, NULL);
+ /* Common errno aliases */
+#if defined ENOTEMPTY && ENOTEMPTY == EEXIST
+ print_errno ((char*)"ENOTEMPTY", EEXIST);
+#endif
+#if defined ENOTSUP && ENOTSUP == EOPNOTSUPP
+ print_errno ((char*)"ENOTSUP", EOPNOTSUPP);
+#endif
+#if defined EWOULDBLOCK && EWOULDBLOCK == EAGAIN
+ print_errno ((char*)"EWOULDBLOCK", EAGAIN);
+#endif
+#if defined EDEADLOCK && EDEADLOCK == EDEADLK
+ print_errno ((char*)"EDEADLOCK", EDEADLK);
+#endif
return EXIT_SUCCESS;
}
diff --git a/src/group-list.c b/src/group-list.c
index 184941117..4222f394d 100644
--- a/src/group-list.c
+++ b/src/group-list.c
@@ -1,5 +1,5 @@
/* group-list.c --Print a list of group IDs or names.
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -37,12 +37,12 @@ print_group_list (char const *username,
bool use_names, char delim)
{
bool ok = true;
- struct passwd *pwd = nullptr;
+ struct passwd *pwd = NULL;
if (username)
{
pwd = getpwuid (ruid);
- if (pwd == nullptr)
+ if (pwd == NULL)
ok = false;
}
@@ -90,13 +90,13 @@ print_group_list (char const *username,
extern bool
print_group (gid_t gid, bool use_name)
{
- struct group *grp = nullptr;
+ struct group *grp = NULL;
bool ok = true;
if (use_name)
{
grp = getgrgid (gid);
- if (grp == nullptr)
+ if (grp == NULL)
{
if (TYPE_SIGNED (gid_t))
{
diff --git a/src/group-list.h b/src/group-list.h
index a360b1202..80b88de66 100644
--- a/src/group-list.h
+++ b/src/group-list.h
@@ -1,6 +1,6 @@
/* group-list.h -- prototypes shared by id and groups.
- Copyright (C) 2008-2025 Free Software Foundation, Inc.
+ Copyright (C) 2008-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/groups.c b/src/groups.c
index f01ed7dcc..57acd376b 100644
--- a/src/groups.c
+++ b/src/groups.c
@@ -1,5 +1,5 @@
/* groups -- print the groups a user is in
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -40,7 +40,7 @@ static struct option const longopts[] =
{
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -56,8 +56,8 @@ Print group memberships for each USERNAME or, if no USERNAME is specified, for\
\n\
the current process (which may differ if the groups database has changed).\n"),
stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -66,11 +66,6 @@ the current process (which may differ if the groups database has changed).\n"),
int
main (int argc, char **argv)
{
- int optc;
- bool ok = true;
- gid_t rgid, egid;
- uid_t ruid;
-
initialize_main (&argc, &argv);
set_program_name (argv[0]);
setlocale (LC_ALL, "");
@@ -80,9 +75,9 @@ main (int argc, char **argv)
atexit (close_stdout);
/* Processing the arguments this way makes groups.c behave differently to
- * groups.sh if one of the arguments is "--".
- */
- while ((optc = getopt_long (argc, argv, "", longopts, nullptr)) != -1)
+ groups.sh if one of the arguments is "--". */
+ int optc;
+ while ((optc = getopt_long (argc, argv, "", longopts, NULL)) != -1)
{
switch (optc)
{
@@ -93,6 +88,7 @@ main (int argc, char **argv)
}
}
+ bool ok = true;
if (optind == argc)
{
/* No arguments. Divulge the details of the current process. */
@@ -100,21 +96,21 @@ main (int argc, char **argv)
gid_t NO_GID = -1;
errno = 0;
- ruid = getuid ();
+ uid_t ruid = getuid ();
if (ruid == NO_UID && errno)
error (EXIT_FAILURE, errno, _("cannot get real UID"));
errno = 0;
- egid = getegid ();
+ gid_t egid = getegid ();
if (egid == NO_GID && errno)
error (EXIT_FAILURE, errno, _("cannot get effective GID"));
errno = 0;
- rgid = getgid ();
+ gid_t rgid = getgid ();
if (rgid == NO_GID && errno)
error (EXIT_FAILURE, errno, _("cannot get real GID"));
- if (!print_group_list (nullptr, ruid, rgid, egid, true, ' '))
+ if (!print_group_list (NULL, ruid, rgid, egid, true, ' '))
ok = false;
putchar ('\n');
}
@@ -124,19 +120,23 @@ main (int argc, char **argv)
for ( ; optind < argc; optind++)
{
struct passwd *pwd = getpwnam (argv[optind]);
- if (pwd == nullptr)
+ if (pwd == NULL)
{
error (0, 0, _("%s: no such user"), quote (argv[optind]));
ok = false;
continue;
}
- ruid = pwd->pw_uid;
- rgid = egid = pwd->pw_gid;
+ uid_t ruid = pwd->pw_uid;
+ gid_t rgid = pwd->pw_gid;
+ gid_t egid = rgid;
printf ("%s : ", argv[optind]);
if (!print_group_list (argv[optind], ruid, rgid, egid, true, ' '))
ok = false;
putchar ('\n');
+
+ if (ferror (stdout))
+ write_error ();
}
}
diff --git a/src/head.c b/src/head.c
index 127099dfc..e6596ecbe 100644
--- a/src/head.c
+++ b/src/head.c
@@ -1,5 +1,5 @@
/* head -- output first part of file(s)
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -58,7 +58,7 @@ static bool presume_input_pipe;
static bool print_headers;
/* Character to split lines by. */
-static char line_end;
+static char line_end = '\n';
/* When to print the filename banners. */
enum header_mode
@@ -85,17 +85,17 @@ enum
static struct option const long_options[] =
{
- {"bytes", required_argument, nullptr, 'c'},
- {"lines", required_argument, nullptr, 'n'},
- {"-presume-input-pipe", no_argument, nullptr,
+ {"bytes", required_argument, NULL, 'c'},
+ {"lines", required_argument, NULL, 'n'},
+ {"-presume-input-pipe", no_argument, NULL,
PRESUME_INPUT_PIPE_OPTION}, /* do not document */
- {"quiet", no_argument, nullptr, 'q'},
- {"silent", no_argument, nullptr, 'q'},
- {"verbose", no_argument, nullptr, 'v'},
- {"zero-terminated", no_argument, nullptr, 'z'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"silent", no_argument, NULL, 'q'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"zero-terminated", no_argument, NULL, 'z'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -117,23 +117,30 @@ With more than one FILE, precede each with a header giving the file name.\n\
emit_stdin_note ();
emit_mandatory_arg_note ();
- printf (_("\
- -c, --bytes=[-]NUM print the first NUM bytes of each file;\n\
- with the leading '-', print all but the last\n\
- NUM bytes of each file\n\
- -n, --lines=[-]NUM print the first NUM lines instead of the first %d;\n\
- with the leading '-', print all but the last\n\
- NUM lines of each file\n\
+ oputs (_("\
+ -c, --bytes=[-]NUM\n\
+ print the first NUM bytes of each file;\n\
+ with the leading '-', print all but the last NUM bytes of each file\n\
+"));
+ oprintf (_("\
+ -n, --lines=[-]NUM\n\
+ print the first NUM lines instead of the first %d;\n\
+ with the leading '-', print all but the last NUM lines of each file\n\
"), DEFAULT_NUMBER);
- fputs (_("\
- -q, --quiet, --silent never print headers giving file names\n\
- -v, --verbose always print headers giving file names\n\
-"), stdout);
- fputs (_("\
- -z, --zero-terminated line delimiter is NUL, not newline\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -q, --quiet, --silent\n\
+ never print headers giving file names\n\
+"));
+ oputs (_("\
+ -v, --verbose\n\
+ always print headers giving file names\n\
+"));
+ oputs (_("\
+ -z, --zero-terminated\n\
+ line delimiter is NUL, not newline\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
NUM may have a multiplier suffix:\n\
@@ -297,12 +304,11 @@ elide_tail_bytes_pipe (char const *filename, int fd, uintmax_t n_elide,
bool first = true;
bool eof = false;
idx_t n_to_read = READ_BUFSIZE + n_elide;
- bool i;
char *b[2];
b[0] = xnmalloc (2, n_to_read);
b[1] = b[0] + n_to_read;
- for (i = false; ! eof ; i = !i)
+ for (bool i = false; ! eof ; i = !i)
{
idx_t n_read = full_read (fd, b[i], n_to_read);
idx_t delta = 0;
@@ -357,7 +363,7 @@ elide_tail_bytes_pipe (char const *filename, int fd, uintmax_t n_elide,
bool eof = false;
idx_t n_read;
- char **b = nullptr;
+ char **b = NULL;
idx_t remainder = n_elide % READ_BUFSIZE;
/* The number of buffers needed to hold n_elide bytes plus one
@@ -511,7 +517,7 @@ elide_tail_lines_pipe (char const *filename, int fd, uintmax_t n_elide,
first = last = xmalloc (sizeof (LBUFFER));
first->nbytes = first->nlines = 0;
- first->next = nullptr;
+ first->next = NULL;
tmp = xmalloc (sizeof (LBUFFER));
/* Always read into a fresh buffer.
@@ -532,7 +538,7 @@ elide_tail_lines_pipe (char const *filename, int fd, uintmax_t n_elide,
tmp->nbytes = n_read;
tmp->nlines = 0;
- tmp->next = nullptr;
+ tmp->next = NULL;
/* Count the number of newlines just read. */
{
@@ -687,7 +693,7 @@ elide_tail_lines_seekable (char const *pretty_filename, int fd,
{
char const *nl;
nl = memrchr (buffer, line_end, n);
- if (nl == nullptr)
+ if (nl == NULL)
break;
n = nl - buffer;
}
@@ -911,7 +917,6 @@ main (int argc, char **argv)
enum header_mode header_mode = multiple_files;
bool ok = true;
int c;
- size_t i;
/* Number of items to output, or to elide from the end.
UINTMAX_MAX stands for an essentially unlimited number. */
@@ -927,7 +932,7 @@ main (int argc, char **argv)
/* Initializer for file_list if no file-arguments
were specified on the command line. */
- static char const *const default_file_list[] = {"-", nullptr};
+ static char const *const default_file_list[] = {"-", NULL};
char const *const *file_list;
initialize_main (&argc, &argv);
@@ -938,12 +943,6 @@ main (int argc, char **argv)
atexit (close_stdout);
- have_read_stdin = false;
-
- print_headers = false;
-
- line_end = '\n';
-
if (1 < argc && argv[1][0] == '-' && c_isdigit (argv[1][1]))
{
char *a = argv[1];
@@ -1013,7 +1012,7 @@ main (int argc, char **argv)
}
while ((c = getopt_long (argc, argv, "c:n:qvz0123456789",
- long_options, nullptr))
+ long_options, NULL))
!= -1)
{
switch (c)
@@ -1071,7 +1070,7 @@ main (int argc, char **argv)
xset_binary_mode (STDOUT_FILENO, O_BINARY);
- for (i = 0; file_list[i]; ++i)
+ for (size_t i = 0; file_list[i]; ++i)
ok &= head_file (file_list[i], n_units, count_lines, elide_from_end);
if (have_read_stdin && close (STDIN_FILENO) < 0)
diff --git a/src/hostid.c b/src/hostid.c
index ff30079a7..e20c686f1 100644
--- a/src/hostid.c
+++ b/src/hostid.c
@@ -1,6 +1,6 @@
/* print the hexadecimal identifier for the current host
- Copyright (C) 1997-2025 Free Software Foundation, Inc.
+ Copyright (C) 1997-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -42,8 +42,8 @@ Usage: %s [OPTION]\n\
Print the numeric identifier (in hexadecimal) for the current host.\n\
\n\
"), program_name);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -52,8 +52,6 @@ Print the numeric identifier (in hexadecimal) for the current host.\n\
int
main (int argc, char **argv)
{
- unsigned int id;
-
initialize_main (&argc, &argv);
set_program_name (argv[0]);
setlocale (LC_ALL, "");
@@ -64,7 +62,7 @@ main (int argc, char **argv)
parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE_NAME,
Version, true, usage, AUTHORS,
- (char const *) nullptr);
+ (char const *) NULL);
if (optind < argc)
{
@@ -72,12 +70,10 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
- id = gethostid ();
-
/* POSIX says gethostid returns a "32-bit identifier" but is silent
whether it's sign-extended. Turn off any sign-extension. This
is a no-op unless unsigned int is wider than 32 bits. */
- id &= 0xffffffff;
+ unsigned int id = gethostid () & 0xffffffff;
printf ("%08x\n", id);
diff --git a/src/hostname.c b/src/hostname.c
index ae7b56adb..cd18ef6d9 100644
--- a/src/hostname.c
+++ b/src/hostname.c
@@ -1,5 +1,5 @@
/* hostname - set or print the name of current host system
- Copyright (C) 1994-2025 Free Software Foundation, Inc.
+ Copyright (C) 1994-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -62,8 +62,8 @@ Print or set the hostname of the current system.\n\
\n\
"),
program_name, program_name);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -84,7 +84,7 @@ main (int argc, char **argv)
parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE_NAME,
Version, true, usage, AUTHORS,
- (char const *) nullptr);
+ (char const *) NULL);
if (optind + 1 < argc)
{
@@ -103,7 +103,7 @@ main (int argc, char **argv)
else
{
hostname = xgethostname ();
- if (hostname == nullptr)
+ if (hostname == NULL)
error (EXIT_FAILURE, errno, _("cannot determine hostname"));
puts (hostname);
}
diff --git a/src/id.c b/src/id.c
index d69e6e323..b5a307721 100644
--- a/src/id.c
+++ b/src/id.c
@@ -1,5 +1,5 @@
/* id -- print real and effective UIDs and GIDs
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -64,7 +64,7 @@ static gid_t rgid, egid;
/* The SELinux context. Start with a known invalid value so print_full_info
knows when 'context' has not been set to a meaningful value. */
-static char *context = nullptr;
+static char *context = NULL;
static void print_user (uid_t uid);
static void print_full_info (char const *username);
@@ -72,16 +72,16 @@ static void print_stuff (char const *pw_name);
static struct option const longopts[] =
{
- {"context", no_argument, nullptr, 'Z'},
- {"group", no_argument, nullptr, 'g'},
- {"groups", no_argument, nullptr, 'G'},
- {"name", no_argument, nullptr, 'n'},
- {"real", no_argument, nullptr, 'r'},
- {"user", no_argument, nullptr, 'u'},
- {"zero", no_argument, nullptr, 'z'},
+ {"context", no_argument, NULL, 'Z'},
+ {"group", no_argument, NULL, 'g'},
+ {"groups", no_argument, NULL, 'G'},
+ {"name", no_argument, NULL, 'n'},
+ {"real", no_argument, NULL, 'r'},
+ {"user", no_argument, NULL, 'u'},
+ {"zero", no_argument, NULL, 'z'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -97,19 +97,41 @@ Print user and group information for each specified USER,\n\
or (when USER omitted) for the current process.\n\
\n"),
stdout);
- fputs (_("\
- -a ignore, for compatibility with other versions\n\
- -Z, --context print only the security context of the process\n\
- -g, --group print only the effective group ID\n\
- -G, --groups print all group IDs\n\
- -n, --name print a name instead of a number, for -u,-g,-G\n\
- -r, --real print the real ID instead of the effective ID, with -u,-g,-G\n\
- -u, --user print only the effective user ID\n\
- -z, --zero delimit entries with NUL characters, not whitespace;\n\
- not permitted in default format\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -a\n\
+ ignore, for compatibility with other versions\n\
+"));
+ oputs (_("\
+ -Z, --context\n\
+ print only the security context of the process\n\
+"));
+ oputs (_("\
+ -g, --group\n\
+ print only the effective group ID\n\
+"));
+ oputs (_("\
+ -G, --groups\n\
+ print all group IDs\n\
+"));
+ oputs (_("\
+ -n, --name\n\
+ print a name instead of a number, for -u, -g, -G\n\
+"));
+ oputs (_("\
+ -r, --real\n\
+ print the real ID instead of the effective ID, with -u, -g, -G\n\
+"));
+ oputs (_("\
+ -u, --user\n\
+ print only the effective user ID\n\
+"));
+ oputs (_("\
+ -z, --zero\n\
+ delimit entries with NUL characters, not whitespace;\n\
+ not permitted in default format\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
Without any OPTION, print some useful set of identified information.\n\
@@ -134,7 +156,7 @@ main (int argc, char **argv)
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "agnruzGZ", longopts, nullptr)) != -1)
+ while ((optc = getopt_long (argc, argv, "agnruzGZ", longopts, NULL)) != -1)
{
switch (optc)
{
@@ -232,18 +254,18 @@ main (int argc, char **argv)
/* For each username/userid to get its pw_name field */
for (; optind < n_ids; optind++)
{
- char *pw_name = nullptr;
- struct passwd *pwd = nullptr;
+ char *pw_name = NULL;
+ struct passwd *pwd = NULL;
char const *spec = argv[optind];
/* Disallow an empty spec here as parse_user_spec() doesn't
give an error for that as it seems it's a valid way to
specify a noop or "reset special bits" depending on the system. */
if (*spec)
{
- if (! parse_user_spec (spec, &euid, nullptr, &pw_name, nullptr))
+ if (! parse_user_spec (spec, &euid, NULL, &pw_name, NULL))
pwd = pw_name ? getpwnam (pw_name) : getpwuid (euid);
}
- if (pwd == nullptr)
+ if (pwd == NULL)
{
error (0, errno, _("%s: no such user"), quote (spec));
ok &= false;
@@ -255,6 +277,8 @@ main (int argc, char **argv)
ruid = euid = pwd->pw_uid;
rgid = egid = pwd->pw_gid;
print_stuff (pw_name);
+ if (ferror (stdout))
+ write_error ();
}
free (pw_name);
}
@@ -297,7 +321,7 @@ main (int argc, char **argv)
if (rgid == NO_GID && errno)
error (EXIT_FAILURE, errno, _("cannot get real GID"));
}
- print_stuff (nullptr);
+ print_stuff (NULL);
}
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
@@ -308,12 +332,12 @@ main (int argc, char **argv)
static void
print_user (uid_t uid)
{
- struct passwd *pwd = nullptr;
+ struct passwd *pwd = NULL;
if (use_name)
{
pwd = getpwuid (uid);
- if (pwd == nullptr)
+ if (pwd == NULL)
{
error (0, 0, _("cannot find name for user ID %ju"), (uintmax_t) uid);
ok &= false;
diff --git a/src/install.c b/src/install.c
index b01253997..bccfe606f 100644
--- a/src/install.c
+++ b/src/install.c
@@ -1,5 +1,5 @@
/* install - copy files and set attributes
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -61,14 +61,14 @@ static bool use_default_selinux_context = true;
# define endpwent() ((void) 0)
#endif
-/* The user name that will own the files, or nullptr to make the owner
+/* The user name that will own the files, or NULL to make the owner
the current user ID. */
static char *owner_name;
/* The user ID corresponding to 'owner_name'. */
static uid_t owner_id;
-/* The group name that will own the files, or nullptr to make the group
+/* The group name that will own the files, or NULL to make the group
the current group ID. */
static char *group_name;
@@ -113,25 +113,25 @@ enum
static struct option const long_options[] =
{
- {"backup", optional_argument, nullptr, 'b'},
- {"compare", no_argument, nullptr, 'C'},
+ {"backup", optional_argument, NULL, 'b'},
+ {"compare", no_argument, NULL, 'C'},
{GETOPT_SELINUX_CONTEXT_OPTION_DECL},
- {"debug", no_argument, nullptr, DEBUG_OPTION},
- {"directory", no_argument, nullptr, 'd'},
- {"group", required_argument, nullptr, 'g'},
- {"mode", required_argument, nullptr, 'm'},
- {"no-target-directory", no_argument, nullptr, 'T'},
- {"owner", required_argument, nullptr, 'o'},
- {"preserve-timestamps", no_argument, nullptr, 'p'},
- {"preserve-context", no_argument, nullptr, PRESERVE_CONTEXT_OPTION},
- {"strip", no_argument, nullptr, 's'},
- {"strip-program", required_argument, nullptr, STRIP_PROGRAM_OPTION},
- {"suffix", required_argument, nullptr, 'S'},
- {"target-directory", required_argument, nullptr, 't'},
- {"verbose", no_argument, nullptr, 'v'},
+ {"debug", no_argument, NULL, DEBUG_OPTION},
+ {"directory", no_argument, NULL, 'd'},
+ {"group", required_argument, NULL, 'g'},
+ {"mode", required_argument, NULL, 'm'},
+ {"no-target-directory", no_argument, NULL, 'T'},
+ {"owner", required_argument, NULL, 'o'},
+ {"preserve-timestamps", no_argument, NULL, 'p'},
+ {"preserve-context", no_argument, NULL, PRESERVE_CONTEXT_OPTION},
+ {"strip", no_argument, NULL, 's'},
+ {"strip-program", required_argument, NULL, STRIP_PROGRAM_OPTION},
+ {"suffix", required_argument, NULL, 'S'},
+ {"target-directory", required_argument, NULL, 't'},
+ {"verbose", no_argument, NULL, 'v'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* Compare content of opened files using file descriptors A_FD and B_FD. Return
@@ -170,17 +170,15 @@ need_copy (char const *src_name, char const *dest_name,
int dest_dirfd, char const *dest_relname,
const struct cp_options *x)
{
- struct stat src_sb, dest_sb;
- int src_fd, dest_fd;
- bool content_match;
-
if (extra_mode (mode))
return true;
/* compare files using stat */
+ struct stat src_sb;
if (stat (src_name, &src_sb) != 0)
return true;
+ struct stat dest_sb;
if (fstatat (dest_dirfd, dest_relname, &dest_sb, AT_SYMLINK_NOFOLLOW) != 0)
return true;
@@ -215,20 +213,18 @@ need_copy (char const *src_name, char const *dest_name,
/* compare SELinux context if preserving */
if (selinux_enabled && x->preserve_security_context)
{
- char *file_scontext_raw = nullptr;
- char *to_scontext_raw = nullptr;
- bool scontext_match;
-
+ char *file_scontext_raw = NULL;
if (getfilecon_raw (src_name, &file_scontext_raw) == -1)
return true;
+ char *to_scontext_raw = NULL;
if (getfilecon_raw (dest_name, &to_scontext_raw) == -1)
{
freecon (file_scontext_raw);
return true;
}
- scontext_match = streq (file_scontext_raw, to_scontext_raw);
+ bool scontext_match = streq (file_scontext_raw, to_scontext_raw);
freecon (file_scontext_raw);
freecon (to_scontext_raw);
@@ -237,18 +233,18 @@ need_copy (char const *src_name, char const *dest_name,
}
/* compare files content */
- src_fd = open (src_name, O_RDONLY | O_BINARY);
+ int src_fd = open (src_name, O_RDONLY | O_BINARY);
if (src_fd < 0)
return true;
- dest_fd = openat (dest_dirfd, dest_relname, O_RDONLY | O_BINARY);
+ int dest_fd = openat (dest_dirfd, dest_relname, O_RDONLY | O_BINARY);
if (dest_fd < 0)
{
close (src_fd);
return true;
}
- content_match = have_same_content (src_fd, dest_fd);
+ bool content_match = have_same_content (src_fd, dest_fd);
close (src_fd);
close (dest_fd);
@@ -294,11 +290,11 @@ cp_option_init (struct cp_options *x)
x->update = UPDATE_ALL;
x->require_preserve_context = false; /* Not used by install currently. */
x->preserve_security_context = false; /* Whether to copy context from src. */
- x->set_security_context = nullptr; /* Whether to set sys default context. */
+ x->set_security_context = NULL; /* Whether to set sys default context. */
x->preserve_xattr = false;
x->verbose = false;
- x->dest_info = nullptr;
- x->src_info = nullptr;
+ x->dest_info = NULL;
+ x->src_info = NULL;
}
static struct selabel_handle *
@@ -309,7 +305,7 @@ get_labeling_handle (void)
if (!initialized)
{
initialized = true;
- hnd = selabel_open (SELABEL_CTX_FILE, nullptr, 0);
+ hnd = selabel_open (SELABEL_CTX_FILE, NULL, 0);
if (!hnd)
error (0, errno, _("warning: security labeling handle failed"));
}
@@ -323,20 +319,21 @@ get_labeling_handle (void)
static void
setdefaultfilecon (char const *file)
{
- struct stat st;
- char *scontext_raw = nullptr;
-
if (selinux_enabled != 1)
{
/* Indicate no context found. */
return;
}
+
+ struct stat st;
if (lstat (file, &st) != 0)
return;
struct selabel_handle *hnd = get_labeling_handle ();
if (!hnd)
return;
+
+ char *scontext_raw = NULL;
if (selabel_lookup_raw (hnd, &scontext_raw, file, st.st_mode) != 0)
{
if (errno != ENOENT && ! ignorable_ctx_err (errno))
@@ -409,17 +406,18 @@ process_dir (char *dir, struct savewd *wd, void *options)
return ret;
}
+enum copy_status { COPY_FAILED, COPY_OK, COPY_SKIPPED };
+
/* Copy file FROM onto file TO aka TO_DIRFD+TO_RELNAME, creating TO if
- necessary. Return true if successful. */
+ necessary. Return COPY_OK if successful or COPY_SKIPPED if the copy
+ was skipped. */
-static bool
+static enum copy_status
copy_file (char const *from, char const *to,
int to_dirfd, char const *to_relname, const struct cp_options *x)
{
- bool copy_into_self;
-
if (copy_only_if_needed && !need_copy (from, to, to_dirfd, to_relname, x))
- return true;
+ return COPY_SKIPPED;
/* Allow installing from non-regular files like /dev/null.
Charles Karney reported that some Sun version of install allows that
@@ -427,7 +425,9 @@ copy_file (char const *from, char const *to,
However, since !x->recursive, the call to "copy" will fail if FROM
is a directory. */
- return copy (from, to, to_dirfd, to_relname, 0, x, &copy_into_self, nullptr);
+ bool copy_into_self;
+ return (copy (from, to, to_dirfd, to_relname, 0, x, &copy_into_self, NULL)
+ ? COPY_OK : COPY_FAILED);
}
/* Set the attributes of file or directory NAME aka DIRFD+RELNAME.
@@ -470,9 +470,8 @@ static bool
change_timestamps (struct stat const *src_sb, char const *dest,
int dirfd, char const *relname)
{
- struct timespec timespec[2];
- timespec[0] = get_stat_atime (src_sb);
- timespec[1] = get_stat_mtime (src_sb);
+ struct timespec timespec[2] = { get_stat_atime (src_sb),
+ get_stat_mtime (src_sb) };
if (utimensat (dirfd, relname, timespec, 0))
{
@@ -492,7 +491,7 @@ static bool
strip (char const *name)
{
posix_spawnattr_t attr;
- posix_spawnattr_t *attrp = nullptr;
+ posix_spawnattr_t *attrp = NULL;
/* Try to use vfork for systems where it matters. */
if (posix_spawnattr_init (&attr) == 0)
@@ -504,15 +503,15 @@ strip (char const *name)
}
/* Construct the arguments to 'strip'. */
- char *concat_name = nullptr;
+ char *concat_name = NULL;
char const *safe_name = name;
if (name && *name == '-')
- safe_name = concat_name = file_name_concat (".", name, nullptr);
- char const *const argv[] = { strip_program, safe_name, nullptr };
+ safe_name = concat_name = file_name_concat (".", name, NULL);
+ char const *const argv[] = { strip_program, safe_name, NULL };
/* Run 'strip'. */
pid_t pid;
- int result = posix_spawnp (&pid, strip_program, nullptr, attrp,
+ int result = posix_spawnp (&pid, strip_program, NULL, attrp,
(char * const *) argv, environ);
bool ok = false;
@@ -548,16 +547,13 @@ strip (char const *name)
static void
get_ids (void)
{
- struct passwd *pw;
- struct group *gr;
-
if (owner_name)
{
- pw = getpwnam (owner_name);
- if (pw == nullptr)
+ struct passwd *pw = getpwnam (owner_name);
+ if (pw == NULL)
{
uintmax_t tmp;
- if (xstrtoumax (owner_name, nullptr, 0, &tmp, "") != LONGINT_OK
+ if (xstrtoumax (owner_name, NULL, 0, &tmp, "") != LONGINT_OK
|| ckd_add (&owner_id, tmp, 0))
error (EXIT_FAILURE, 0, _("invalid user %s"),
quoteaf (owner_name));
@@ -571,11 +567,11 @@ get_ids (void)
if (group_name)
{
- gr = getgrnam (group_name);
- if (gr == nullptr)
+ struct group *gr = getgrnam (group_name);
+ if (gr == NULL)
{
uintmax_t tmp;
- if (xstrtoumax (group_name, nullptr, 0, &tmp, "") != LONGINT_OK
+ if (xstrtoumax (group_name, NULL, 0, &tmp, "") != LONGINT_OK
|| ckd_add (&group_id, tmp, 0))
error (EXIT_FAILURE, 0, _("invalid group %s"),
quoteaf (group_name));
@@ -616,51 +612,97 @@ In the 4th form, create all components of the given DIRECTORY(ies).\n\
emit_mandatory_arg_note ();
- fputs (_("\
- --backup[=CONTROL] make a backup of each existing destination file\n\
- -b like --backup but does not accept an argument\n\
- -c (ignored)\n\
- -C, --compare compare content of source and destination files, and\n\
- if no change to content, ownership, and permissions,\n\
- do not modify the destination at all\n\
- -d, --directory treat all arguments as directory names; create all\n\
- components of the specified directories\n\
-"), stdout);
- fputs (_("\
- -D create all leading components of DEST except the last,\n\
- or all components of --target-directory,\n\
- then copy SOURCE to DEST\n\
-"), stdout);
- fputs (_("\
- --debug explain how a file is copied. Implies -v\n\
-"), stdout);
- fputs (_("\
- -g, --group=GROUP set group ownership, instead of process' current group\n\
- -m, --mode=MODE set permission mode (as in chmod), instead of rwxr-xr-x\n\
- -o, --owner=OWNER set ownership (super-user only)\n\
-"), stdout);
- fputs (_("\
- -p, --preserve-timestamps apply access/modification times of SOURCE files\n\
- to corresponding destination files\n\
- -s, --strip strip symbol tables\n\
- --strip-program=PROGRAM program used to strip binaries\n\
- -S, --suffix=SUFFIX override the usual backup suffix\n\
- -t, --target-directory=DIRECTORY copy all SOURCE arguments into DIRECTORY\n\
- -T, --no-target-directory treat DEST as a normal file\n\
-"), stdout);
- fputs (_("\
- -v, --verbose print the name of each created file or directory\n\
-"), stdout);
- fputs (_("\
- --preserve-context preserve SELinux security context\n\
- -Z set SELinux security context of destination\n\
- file and each created directory to default type\n\
- --context[=CTX] like -Z, or if CTX is specified then set the\n\
- SELinux or SMACK security context to CTX\n\
-"), stdout);
-
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ --backup[=CONTROL]\n\
+ make a backup of each existing destination file\n\
+"));
+ oputs (_("\
+ -b\n\
+ like --backup but does not accept an argument\n\
+"));
+ oputs (_("\
+ -c\n\
+ (ignored)\n\
+"));
+ oputs (_("\
+ -C, --compare\n\
+ compare content of source and destination files,\n\
+ and if no change to content, ownership, and permissions,\n\
+ do not modify the destination at all\n\
+"));
+ oputs (_("\
+ -d, --directory\n\
+ treat all arguments as directory names;\n\
+ create all components of the specified directories\n\
+"));
+ oputs (_("\
+ -D\n\
+ create all leading components of DEST except the last,\n\
+ or all components of --target-directory,\n\
+ then copy SOURCE to DEST\n\
+"));
+ oputs (_("\
+ --debug\n\
+ explain how a file is copied. Implies -v\n\
+"));
+ oputs (_("\
+ -g, --group=GROUP\n\
+ set group ownership, instead of process' current group\n\
+"));
+ oputs (_("\
+ -m, --mode=MODE\n\
+ set permission mode (as in chmod), instead of rwxr-xr-x\n\
+"));
+ oputs (_("\
+ -o, --owner=OWNER\n\
+ set ownership (super-user only)\n\
+"));
+ oputs (_("\
+ -p, --preserve-timestamps\n\
+ apply access/modification times of SOURCE files\n\
+ to corresponding destination files\n\
+"));
+ oputs (_("\
+ -s, --strip\n\
+ strip symbol tables\n\
+"));
+ oputs (_("\
+ --strip-program=PROGRAM\n\
+ program used to strip binaries\n\
+"));
+ oputs (_("\
+ -S, --suffix=SUFFIX\n\
+ override the usual backup suffix\n\
+"));
+ oputs (_("\
+ -t, --target-directory=DIRECTORY\n\
+ copy all SOURCE arguments into DIRECTORY\n\
+"));
+ oputs (_("\
+ -T, --no-target-directory\n\
+ treat DEST as a normal file\n\
+"));
+ oputs (_("\
+ -v, --verbose\n\
+ print the name of each created file or directory\n\
+"));
+ oputs (_("\
+ --preserve-context\n\
+ preserve SELinux security context\n\
+"));
+ oputs (_("\
+ -Z\n\
+ set SELinux security context of destination file\n\
+ and each created directory, to default type\n\
+"));
+ oputs (_("\
+ --context[=CTX]\n\
+ like -Z, or if CTX is specified then set the\n\
+ SELinux or SMACK security context to CTX\n\
+"));
+
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_backup_suffix_note ();
emit_ancillary_info (PROGRAM_NAME);
}
@@ -682,16 +724,17 @@ install_file_in_file (char const *from, char const *to,
error (0, errno, _("cannot stat %s"), quoteaf (from));
return false;
}
- if (! copy_file (from, to, to_dirfd, to_relname, x))
+ enum copy_status copy_status = copy_file (from, to, to_dirfd, to_relname, x);
+ if (copy_status == COPY_FAILED)
return false;
- if (strip_files)
- if (! strip (to))
- {
- if (unlinkat (to_dirfd, to_relname, 0) != 0) /* Cleanup. */
- error (EXIT_FAILURE, errno, _("cannot unlink %s"), quoteaf (to));
- return false;
- }
- if (x->preserve_timestamps && (strip_files || ! S_ISREG (from_sb.st_mode))
+ if (copy_status == COPY_OK && strip_files && ! strip (to))
+ {
+ if (unlinkat (to_dirfd, to_relname, 0) != 0) /* Cleanup. */
+ error (EXIT_FAILURE, errno, _("cannot unlink %s"), quoteaf (to));
+ return false;
+ }
+ if (x->preserve_timestamps && (copy_status == COPY_SKIPPED || strip_files
+ || ! S_ISREG (from_sb.st_mode))
&& ! change_timestamps (&from_sb, to, to_dirfd, to_relname))
return false;
return change_attributes (to, to_dirfd, to_relname);
@@ -708,13 +751,13 @@ mkancesdirs_safe_wd (char const *from, char *to, struct cp_options *x,
bool save_working_directory =
save_always
|| ! (IS_ABSOLUTE_FILE_NAME (from) && IS_ABSOLUTE_FILE_NAME (to));
- int status = EXIT_SUCCESS;
struct savewd wd;
savewd_init (&wd);
if (! save_working_directory)
savewd_finish (&wd);
+ int status = EXIT_SUCCESS;
if (mkancesdirs (to, &wd, make_ancestor, x) == -1)
{
error (0, errno, _("cannot create directory %s"), quoteaf (to));
@@ -795,20 +838,16 @@ install_file_in_dir (char const *from, char const *to_dir,
int
main (int argc, char **argv)
{
- int optc;
- int exit_status = EXIT_SUCCESS;
- char const *specified_mode = nullptr;
+ char const *specified_mode = NULL;
bool make_backups = false;
- char const *backup_suffix = nullptr;
- char *version_control_string = nullptr;
+ char const *backup_suffix = NULL;
+ char *version_control_string = NULL;
bool mkdir_and_install = false;
struct cp_options x;
- char const *target_directory = nullptr;
+ char const *target_directory = NULL;
bool no_target_directory = false;
- int n_files;
- char **file;
bool strip_program_specified = false;
- char const *scontext = nullptr;
+ char const *scontext = NULL;
/* set iff kernel has extra selinux system calls */
selinux_enabled = (0 < is_selinux_enabled ());
@@ -822,14 +861,11 @@ main (int argc, char **argv)
cp_option_init (&x);
- owner_name = nullptr;
- group_name = nullptr;
- strip_files = false;
- dir_arg = false;
umask (0);
+ int optc;
while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pt:TvS:Z", long_options,
- nullptr))
+ NULL))
!= -1)
{
switch (optc)
@@ -956,8 +992,8 @@ main (int argc, char **argv)
_("failed to set default file creation context to %s"),
quote (scontext));
- n_files = argc - optind;
- file = argv + optind;
+ int n_files = argc - optind;
+ char **file = argv + optind;
if (n_files <= ! (dir_arg || target_directory))
{
@@ -1010,7 +1046,7 @@ main (int argc, char **argv)
struct mode_change *change = mode_compile (specified_mode);
if (!change)
error (EXIT_FAILURE, 0, _("invalid mode %s"), quote (specified_mode));
- mode = mode_adjust (0, false, 0, change, nullptr);
+ mode = mode_adjust (0, false, 0, change, NULL);
dir_mode = mode_adjust (0, true, 0, change, &dir_mode_bits);
free (change);
}
@@ -1019,13 +1055,6 @@ main (int argc, char **argv)
error (0, 0, _("WARNING: ignoring --strip-program option as -s option was "
"not specified"));
- if (copy_only_if_needed && x.preserve_timestamps)
- {
- error (0, 0, _("options --compare (-C) and --preserve-timestamps are "
- "mutually exclusive"));
- usage (EXIT_FAILURE);
- }
-
if (copy_only_if_needed && strip_files)
{
error (0, 0, _("options --compare (-C) and --strip are mutually "
@@ -1039,6 +1068,7 @@ main (int argc, char **argv)
get_ids ();
+ int exit_status = EXIT_SUCCESS;
if (dir_arg)
exit_status = savewd_process_files (n_files, file, process_dir, &x);
else
@@ -1057,9 +1087,8 @@ main (int argc, char **argv)
}
else
{
- int i;
dest_info_init (&x);
- for (i = 0; i < n_files; i++)
+ for (int i = 0; i < n_files; i++)
if (! install_file_in_dir (file[i], target_directory, &x,
i == 0 && mkdir_and_install,
&target_dirfd))
diff --git a/src/ioblksize.h b/src/ioblksize.h
index 133ca122b..159862135 100644
--- a/src/ioblksize.h
+++ b/src/ioblksize.h
@@ -1,5 +1,5 @@
/* I/O block size definitions for coreutils
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/iopoll.c b/src/iopoll.c
index 32b086635..de20bc8d9 100644
--- a/src/iopoll.c
+++ b/src/iopoll.c
@@ -1,5 +1,5 @@
/* iopoll.c -- broken pipe detection / non blocking output handling
- Copyright (C) 2022-2025 Free Software Foundation, Inc.
+ Copyright (C) 2022-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -116,9 +116,9 @@ iopoll_internal (int fdin, int fdout, bool block, bool broken_output)
struct timeval delay = {0};
ret = select (nfds,
- broken_output ? &fds : nullptr,
- broken_output ? nullptr : &fds,
- nullptr, block ? nullptr : &delay);
+ broken_output ? &fds : NULL,
+ broken_output ? NULL : &fds,
+ NULL, block ? NULL : &delay);
if (ret < 0)
continue;
@@ -177,62 +177,52 @@ iopoll_output_ok (int fdout)
Return true, if EAGAIN has been successfully handled. */
static bool
-fwait_for_nonblocking_write (FILE *f)
+wait_for_nonblocking_write (int fd)
{
if (! IS_EAGAIN (errno))
/* non-recoverable write error */
return false;
- int fd = fileno (f);
- if (fd == -1)
- goto fail;
-
/* wait for the file descriptor to become writable */
if (iopoll_internal (-1, fd, true, false) != 0)
- goto fail;
+ {
+ errno = EAGAIN;
+ return false;
+ }
/* successfully waited for the descriptor to become writable */
- clearerr (f);
return true;
-
-fail:
- errno = EAGAIN;
- return false;
}
-
-/* wrapper for fclose() that also waits for F if non blocking. */
+/* wrapper for close() that also waits for FD if non blocking. */
extern bool
-fclose_wait (FILE *f)
+close_wait (int fd)
{
- for (;;)
- {
- if (fflush (f) == 0)
- break;
-
- if (! fwait_for_nonblocking_write (f))
- break;
- }
-
- return fclose (f) == 0;
+ while (wait_for_nonblocking_write (fd))
+ ;
+ return close (fd) == 0;
}
-/* wrapper for fwrite() that also waits for F if non blocking. */
+/* wrapper for write() that also waits for FD if non blocking. */
extern bool
-fwrite_wait (char const *buf, ssize_t size, FILE *f)
+write_wait (int fd, void const *buffer, size_t size)
{
- for (;;)
+ unsigned char const *buf = buffer;
+
+ while (true)
{
- const size_t written = fwrite (buf, 1, size, f);
+ ssize_t written = write (fd, buf, size);
+ if (written < 0)
+ written = 0;
+
size -= written;
- affirm (size >= 0);
if (size <= 0) /* everything written */
return true;
- if (! fwait_for_nonblocking_write (f))
+ if (! wait_for_nonblocking_write (fd))
return false;
buf += written;
diff --git a/src/iopoll.h b/src/iopoll.h
index 0177a4d25..1711fdab9 100644
--- a/src/iopoll.h
+++ b/src/iopoll.h
@@ -5,5 +5,5 @@ int iopoll (int fdin, int fdout, bool block);
bool iopoll_input_ok (int fdin);
bool iopoll_output_ok (int fdout);
-bool fclose_wait (FILE *f);
-bool fwrite_wait (char const *buf, ssize_t size, FILE *f);
+bool close_wait (int fd);
+bool write_wait (int fd, void const *buffer, size_t size);
diff --git a/src/join.c b/src/join.c
index e1899a93a..4346758a6 100644
--- a/src/join.c
+++ b/src/join.c
@@ -1,5 +1,5 @@
/* join - join lines of two files on a common field
- Copyright (C) 1991-2025 Free Software Foundation, Inc.
+ Copyright (C) 1991-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -87,7 +87,7 @@ struct seq
};
/* The previous line read from each file. */
-static struct line *prevline[2] = {nullptr, nullptr};
+static struct line *prevline[2] = {NULL, NULL};
/* The number of lines read from each file. */
static uintmax_t line_no[2] = {0, 0};
@@ -98,7 +98,7 @@ static char *g_names[2];
/* This provides an extra line buffer for each file. We need these if we
try to read two consecutive lines into the same buffer, since we don't
want to overwrite the previous buffer before we check order. */
-static struct line *spareline[2] = {nullptr, nullptr};
+static struct line *spareline[2] = {NULL, NULL};
/* True if the LC_COLLATE locale is hard. */
static bool hard_LC_COLLATE;
@@ -107,7 +107,7 @@ static bool hard_LC_COLLATE;
static bool print_unpairables_1, print_unpairables_2;
/* If nonzero, print pairable lines. */
-static bool print_pairables;
+static bool print_pairables = true;
/* If nonzero, we have seen at least one unpairable line. */
static bool seen_unpairable;
@@ -162,14 +162,14 @@ enum
static struct option const longopts[] =
{
- {"ignore-case", no_argument, nullptr, 'i'},
- {"check-order", no_argument, nullptr, CHECK_ORDER_OPTION},
- {"nocheck-order", no_argument, nullptr, NOCHECK_ORDER_OPTION},
- {"zero-terminated", no_argument, nullptr, 'z'},
- {"header", no_argument, nullptr, HEADER_LINE_OPTION},
+ {"ignore-case", no_argument, NULL, 'i'},
+ {"check-order", no_argument, NULL, CHECK_ORDER_OPTION},
+ {"nocheck-order", no_argument, NULL, NOCHECK_ORDER_OPTION},
+ {"zero-terminated", no_argument, NULL, 'z'},
+ {"header", no_argument, NULL, HEADER_LINE_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* Used to print non-joining lines */
@@ -205,37 +205,65 @@ standard output. The default join field is the first, delimited by blanks.\
\n\
When FILE1 or FILE2 (not both) is -, read standard input.\n\
"), stdout);
- fputs (_("\
-\n\
- -a FILENUM also print unpairable lines from file FILENUM, where\n\
- FILENUM is 1 or 2, corresponding to FILE1 or FILE2\n\
-"), stdout);
- fputs (_("\
- -e STRING replace missing (empty) input fields with STRING;\n\
- I.e., missing fields specified with '-12jo' options\
-\n\
-"), stdout);
- fputs (_("\
- -i, --ignore-case ignore differences in case when comparing fields\n\
- -j FIELD equivalent to '-1 FIELD -2 FIELD'\n\
- -o FORMAT obey FORMAT while constructing output line\n\
- -t CHAR use CHAR as input and output field separator\n\
-"), stdout);
- fputs (_("\
- -v FILENUM like -a FILENUM, but suppress joined output lines\n\
- -1 FIELD join on this FIELD of file 1\n\
- -2 FIELD join on this FIELD of file 2\n\
- --check-order check that the input is correctly sorted, even\n\
- if all input lines are pairable\n\
- --nocheck-order do not check that the input is correctly sorted\n\
- --header treat the first line in each file as field headers,\n\
- print them without trying to pair them\n\
-"), stdout);
- fputs (_("\
- -z, --zero-terminated line delimiter is NUL, not newline\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+
+ oputs (_("\
+ -a FILENUM\n\
+ also print unpairable lines from file FILENUM,\n\
+ where FILENUM is 1 or 2, corresponding to FILE1 or FILE2\n\
+"));
+ oputs (_("\
+ -e STRING\n\
+ replace missing (empty) input fields with STRING;\n\
+ I.e., missing fields specified with '-12jo' options\n\
+"));
+ oputs (_("\
+ -i, --ignore-case\n\
+ ignore differences in case when comparing fields\n\
+"));
+ oputs (_("\
+ -j FIELD\n\
+ equivalent to '-1 FIELD -2 FIELD'\n\
+"));
+ oputs (_("\
+ -o FORMAT\n\
+ obey FORMAT while constructing output line\n\
+"));
+ oputs (_("\
+ -t CHAR\n\
+ use CHAR as input and output field separator\n\
+"));
+ oputs (_("\
+ -v FILENUM\n\
+ like -a FILENUM, but suppress joined output lines\n\
+"));
+ oputs (_("\
+ -1 FIELD\n\
+ join on this FIELD of file 1\n\
+"));
+ oputs (_("\
+ -2 FIELD\n\
+ join on this FIELD of file 2\n\
+"));
+ oputs (_("\
+ --check-order\n\
+ check that the input is correctly sorted, even\n\
+ if all input lines are pairable\n\
+"));
+ oputs (_("\
+ --nocheck-order\n\
+ do not check that the input is correctly sorted\n\
+"));
+ oputs (_("\
+ --header\n\
+ treat the first line in each file as field headers,\n\
+ print them without trying to pair them\n\
+"));
+ oputs (_("\
+ -z, --zero-terminated\n\
+ line delimiter is NUL, not newline\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
Unless -t CHAR is given, leading blanks separate fields and are ignored,\n\
@@ -274,13 +302,13 @@ extract_field (struct line *line, char *field, idx_t len)
static bool
eq_tab (mcel_t g)
{
- return mcel_cmp (g, tab) == 0;
+ return mcel_eq (g, tab);
}
static bool
newline_or_blank (mcel_t g)
{
- return g.ch == '\n' || c32isblank (g.ch);
+ return g.ch == '\n' || c32issep (g.ch);
}
/* Fill in the 'fields' structure in LINE. */
@@ -316,12 +344,12 @@ xfields (struct line *line)
static void
freeline (struct line *line)
{
- if (line == nullptr)
+ if (line == NULL)
return;
free (line->fields);
- line->fields = nullptr;
+ line->fields = NULL;
free (line->buf.buffer);
- line->buf.buffer = nullptr;
+ line->buf.buffer = NULL;
}
/* Return <0 if the join field in LINE1 compares less than the one in LINE2;
@@ -348,7 +376,7 @@ keycmp (struct line const *line1, struct line const *line2,
}
else
{
- beg1 = nullptr;
+ beg1 = NULL;
len1 = 0;
}
@@ -359,7 +387,7 @@ keycmp (struct line const *line1, struct line const *line2,
}
else
{
- beg2 = nullptr;
+ beg2 = NULL;
len2 = 0;
}
@@ -502,7 +530,7 @@ initseq (struct seq *seq)
{
seq->count = 0;
seq->alloc = 0;
- seq->lines = nullptr;
+ seq->lines = NULL;
}
/* Read a line from FP and add it to SEQ. Return true if successful. */
@@ -514,7 +542,7 @@ getseq (FILE *fp, struct seq *seq, int whichfile)
{
seq->lines = xpalloc (seq->lines, &seq->alloc, 1, -1, sizeof *seq->lines);
for (idx_t i = seq->count; i < seq->alloc; i++)
- seq->lines[i] = nullptr;
+ seq->lines[i] = NULL;
}
if (get_line (fp, &seq->lines[seq->count], whichfile))
@@ -571,15 +599,14 @@ prfield (idx_t n, struct line const *line)
static void
prfields (struct line const *line, idx_t join_field, idx_t autocount)
{
- idx_t i;
idx_t nfields = autoformat ? autocount : line->nfields;
- for (i = 0; i < join_field && i < nfields; ++i)
+ for (idx_t i = 0; i < join_field && i < nfields; ++i)
{
fwrite (output_separator, 1, output_seplen, stdout);
prfield (i, line);
}
- for (i = join_field + 1; i < nfields; ++i)
+ for (idx_t i = join_field + 1; i < nfields; ++i)
{
fwrite (output_separator, 1, output_seplen, stdout);
prfield (i, line);
@@ -623,7 +650,7 @@ prjoin (struct line const *line1, struct line const *line2)
}
prfield (field, line);
o = o->next;
- if (o == nullptr)
+ if (o == NULL)
break;
fwrite (output_separator, 1, output_seplen, stdout);
}
@@ -685,8 +712,8 @@ join (FILE *fp1, FILE *fp2)
struct line const *hline1 = seq1.count ? seq1.lines[0] : &uni_blank;
struct line const *hline2 = seq2.count ? seq2.lines[0] : &uni_blank;
prjoin (hline1, hline2);
- prevline[0] = nullptr;
- prevline[1] = nullptr;
+ prevline[0] = NULL;
+ prevline[1] = NULL;
if (seq1.count)
advance_seq (fp1, &seq1, true, 1);
if (seq2.count)
@@ -744,8 +771,7 @@ join (FILE *fp1, FILE *fp2)
{
for (idx_t i = 0; i < seq1.count - 1; ++i)
{
- idx_t j;
- for (j = 0; j < seq2.count - 1; ++j)
+ for (idx_t j = 0; j < seq2.count - 1; ++j)
prjoin (seq1.lines[i], seq2.lines[j]);
}
}
@@ -771,7 +797,7 @@ join (FILE *fp1, FILE *fp2)
tail ends of both inputs to verify that they are in order. We
skip the rest of the tail once we have issued a warning for that
file, unless we actually need to print the unpairable lines. */
- struct line *line = nullptr;
+ struct line *line = NULL;
bool checktail = false;
if (check_input_order != CHECK_ORDER_DISABLED
@@ -828,7 +854,7 @@ add_field (int file, idx_t field)
o = xmalloc (sizeof *o);
o->file = file;
o->field = field;
- o->next = nullptr;
+ o->next = NULL;
/* Add to the end of the list so the fields are in the right order. */
outlist_end->next = o;
@@ -846,7 +872,7 @@ string_to_join_field (char const *str)
{
intmax_t val;
- strtol_error s_err = xstrtoimax (str, nullptr, 10, &val, "");
+ strtol_error s_err = xstrtoimax (str, NULL, 10, &val, "");
if (s_err == LONGINT_OVERFLOW || (s_err == LONGINT_OK && PTRDIFF_MAX < val))
val = PTRDIFF_MAX;
else if (s_err != LONGINT_OK || val <= 0)
@@ -892,7 +918,7 @@ decode_field_spec (char const *s, int *file_index, idx_t *field_index)
static bool
comma_or_blank (mcel_t g)
{
- return g.ch == ',' || c32isblank (g.ch);
+ return g.ch == ',' || c32issep (g.ch);
}
/* Add the comma or blank separated field spec(s) in STR to 'outlist'. */
@@ -1009,7 +1035,6 @@ main (int argc, char **argv)
FILE *fp1, *fp2;
int optc;
int nfiles = 0;
- int i;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -1021,13 +1046,8 @@ main (int argc, char **argv)
atexit (close_stdout);
atexit (free_spareline);
- print_pairables = true;
- seen_unpairable = false;
- issued_disorder_warning[0] = issued_disorder_warning[1] = false;
- check_input_order = CHECK_ORDER_DEFAULT;
-
while ((optc = getopt_long (argc, argv, "-a:e:i1:2:j:o:t:v:z",
- longopts, nullptr))
+ longopts, NULL))
!= -1)
{
optc_status = MUST_BE_OPERAND;
@@ -1041,7 +1061,7 @@ main (int argc, char **argv)
case 'a':
{
long int val;
- if (xstrtol (optarg, nullptr, 10, &val, "") != LONGINT_OK
+ if (xstrtol (optarg, NULL, 10, &val, "") != LONGINT_OK
|| (val != 1 && val != 2))
error (EXIT_FAILURE, 0,
_("invalid file number: %s"), quote (optarg));
@@ -1119,7 +1139,7 @@ main (int argc, char **argv)
quote (optarg));
output_separator = optarg;
}
- if (tab.len && mcel_cmp (tab, newtab) != 0)
+ if (tab.len && ! mcel_eq (tab, newtab))
error (EXIT_FAILURE, 0, _("incompatible tabs"));
tab = newtab;
output_seplen = newtab.len;
@@ -1175,7 +1195,7 @@ main (int argc, char **argv)
/* If "-j1" was specified and it turns out not to have had an argument,
treat it as "-j 1". Likewise for -j2. */
- for (i = 0; i < 2; i++)
+ for (int i = 0; i < 2; i++)
if (joption_count[i] != 0)
{
set_join_field (&join_field_1, i);
diff --git a/src/kill.c b/src/kill.c
index 27bf02a58..651f597ce 100644
--- a/src/kill.c
+++ b/src/kill.c
@@ -1,5 +1,5 @@
/* kill -- send a signal to a process
- Copyright (C) 2002-2025 Free Software Foundation, Inc.
+ Copyright (C) 2002-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -41,12 +41,12 @@ static char const short_options[] =
static struct option const long_options[] =
{
- {"list", no_argument, nullptr, 'l'},
- {"signal", required_argument, nullptr, 's'},
- {"table", no_argument, nullptr, 't'},
+ {"list", no_argument, NULL, 'l'},
+ {"signal", required_argument, NULL, 's'},
+ {"table", no_argument, NULL, 't'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -68,14 +68,20 @@ Send signals to processes, or list signals.\n\
emit_mandatory_arg_note ();
- fputs (_("\
+ oputs (_("\
-s, --signal=SIGNAL, -SIGNAL\n\
- specify the name or number of the signal to be sent\n\
- -l, --list list signal names, or convert signal names to/from numbers\n\
- -t, --table print a table of signal information\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ specify the name or number of the signal to be sent\n\
+"));
+ oputs (_("\
+ -l, --list\n\
+ list signal names, or convert signal names to/from numbers\n\
+"));
+ oputs (_("\
+ -t, --table\n\
+ print a table of signal information\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\n\
SIGNAL may be a signal name like 'HUP', or a signal number like '1',\n\
or the exit status of a process terminated by a signal.\n\
@@ -107,7 +113,6 @@ print_table_row (int num_width, int signum,
static int
list_signals (bool table, char *const *argv)
{
- int signum;
int status = EXIT_SUCCESS;
char signame[SIG2STR_MAX];
@@ -117,11 +122,11 @@ list_signals (bool table, char *const *argv)
/* Compute the maximum width of a signal number. */
int num_width = 1;
- for (signum = 1; signum <= SIGNUM_BOUND / 10; signum *= 10)
+ for (int signum = 1; signum <= SIGNUM_BOUND / 10; signum *= 10)
num_width++;
/* Compute the maximum width of a signal name. */
- for (signum = 0; signum <= SIGNUM_BOUND; signum++)
+ for (int signum = 0; signum <= SIGNUM_BOUND; signum++)
if (sig2str (signum, signame) == 0)
{
idx_t len = strlen (signame);
@@ -132,7 +137,7 @@ list_signals (bool table, char *const *argv)
if (argv)
for (; *argv; argv++)
{
- signum = operand2sig (*argv);
+ int signum = operand2sig (*argv);
if (signum < 0)
status = EXIT_FAILURE;
else
@@ -143,7 +148,7 @@ list_signals (bool table, char *const *argv)
}
}
else
- for (signum = 0; signum <= SIGNUM_BOUND; signum++)
+ for (int signum = 0; signum <= SIGNUM_BOUND; signum++)
if (sig2str (signum, signame) == 0)
print_table_row (num_width, signum, name_width, signame);
}
@@ -152,7 +157,7 @@ list_signals (bool table, char *const *argv)
if (argv)
for (; *argv; argv++)
{
- signum = operand2sig (*argv);
+ int signum = operand2sig (*argv);
if (signum < 0)
status = EXIT_FAILURE;
else if (c_isdigit (**argv))
@@ -166,7 +171,7 @@ list_signals (bool table, char *const *argv)
printf ("%d\n", signum);
}
else
- for (signum = 0; signum <= SIGNUM_BOUND; signum++)
+ for (int signum = 0; signum <= SIGNUM_BOUND; signum++)
if (sig2str (signum, signame) == 0)
puts (signame);
}
@@ -225,7 +230,7 @@ main (int argc, char **argv)
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, short_options, long_options, nullptr))
+ while ((optc = getopt_long (argc, argv, short_options, long_options, NULL))
!= -1)
switch (optc)
{
@@ -300,6 +305,6 @@ main (int argc, char **argv)
}
return (list
- ? list_signals (table, optind < argc ? argv + optind : nullptr)
+ ? list_signals (table, optind < argc ? argv + optind : NULL)
: send_signals (signum, argv + optind));
}
diff --git a/src/libstdbuf.c b/src/libstdbuf.c
index 51665bd5a..edcb9916e 100644
--- a/src/libstdbuf.c
+++ b/src/libstdbuf.c
@@ -1,5 +1,5 @@
/* libstdbuf -- a shared lib to preload to setup stdio buffering for a command
- Copyright (C) 2009-2025 Free Software Foundation, Inc.
+ Copyright (C) 2009-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -35,7 +35,7 @@
the buffer size, and more problematically does not give any indication
that the new size request was ignored:
- setvbuf (stdout, nullptr, _IOFBF, 8192);
+ setvbuf (stdout, NULL, _IOFBF, 8192);
The ISO C99 standard section 7.19.5.6 on the setvbuf function says:
@@ -70,7 +70,7 @@
static void
apply_mode (FILE *stream, char const *stream_name, char const *envvar)
{
- char *buf = nullptr;
+ char *buf = NULL;
int setvbuf_mode;
unsigned long int size = 0;
@@ -99,7 +99,7 @@ apply_mode (FILE *stream, char const *stream_name, char const *envvar)
Huge sizes can cause problems with some stdio implementations. */
buf = (size <= ((unsigned long int) -2 < (size_t) -1 / 2
? (unsigned long int) -2 : (size_t) -1 / 2)
- ? malloc (size) : nullptr);
+ ? malloc (size) : NULL);
if (!buf)
{
/* We could defer the allocation to libc, however since
diff --git a/src/link.c b/src/link.c
index 0c0f5de01..59c3628ec 100644
--- a/src/link.c
+++ b/src/link.c
@@ -1,5 +1,5 @@
/* link utility for GNU.
- Copyright (C) 2001-2025 Free Software Foundation, Inc.
+ Copyright (C) 2001-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -46,8 +46,8 @@ Usage: %s FILE1 FILE2\n\
fputs (_("Call the link function to create a link named FILE2\
to an existing FILE1.\n\n"),
stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -66,7 +66,7 @@ main (int argc, char **argv)
parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE_NAME,
Version, true, usage, AUTHORS,
- (char const *) nullptr);
+ (char const *) NULL);
if (argc < optind + 2)
{
diff --git a/src/ln.c b/src/ln.c
index 7a366bc90..335c77150 100644
--- a/src/ln.c
+++ b/src/ln.c
@@ -1,5 +1,5 @@
/* 'ln' program to create links between files.
- Copyright (C) 1986-2025 Free Software Foundation, Inc.
+ Copyright (C) 1986-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -90,22 +90,22 @@ enum { DEST_INFO_INITIAL_CAPACITY = 61 };
static struct option const long_options[] =
{
- {"backup", optional_argument, nullptr, 'b'},
- {"directory", no_argument, nullptr, 'F'},
- {"no-dereference", no_argument, nullptr, 'n'},
- {"no-target-directory", no_argument, nullptr, 'T'},
- {"force", no_argument, nullptr, 'f'},
- {"interactive", no_argument, nullptr, 'i'},
- {"suffix", required_argument, nullptr, 'S'},
- {"target-directory", required_argument, nullptr, 't'},
- {"logical", no_argument, nullptr, 'L'},
- {"physical", no_argument, nullptr, 'P'},
- {"relative", no_argument, nullptr, 'r'},
- {"symbolic", no_argument, nullptr, 's'},
- {"verbose", no_argument, nullptr, 'v'},
+ {"backup", optional_argument, NULL, 'b'},
+ {"directory", no_argument, NULL, 'F'},
+ {"no-dereference", no_argument, NULL, 'n'},
+ {"no-target-directory", no_argument, NULL, 'T'},
+ {"force", no_argument, NULL, 'f'},
+ {"interactive", no_argument, NULL, 'i'},
+ {"suffix", required_argument, NULL, 'S'},
+ {"target-directory", required_argument, NULL, 't'},
+ {"logical", no_argument, NULL, 'L'},
+ {"physical", no_argument, NULL, 'P'},
+ {"relative", no_argument, NULL, 'r'},
+ {"symbolic", no_argument, NULL, 's'},
+ {"verbose", no_argument, NULL, 'v'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* Return an errno value for a system call that returned STATUS.
@@ -130,7 +130,7 @@ convert_abs_rel (char const *from, char const *target)
char *realdest = canonicalize_filename_mode (targetdir, CAN_MISSING);
char *realfrom = canonicalize_filename_mode (from, CAN_MISSING);
- char *relative_from = nullptr;
+ char *relative_from = NULL;
if (realdest && realfrom)
{
/* Write to a PATH_MAX buffer. */
@@ -139,7 +139,7 @@ convert_abs_rel (char const *from, char const *target)
if (!relpath (realfrom, realdest, relative_from, PATH_MAX))
{
free (relative_from);
- relative_from = nullptr;
+ relative_from = NULL;
}
}
@@ -180,8 +180,8 @@ do_link (char const *source, int destdir_fd, char const *dest_base,
{
struct stat source_stats;
int source_status = 1;
- char *backup_base = nullptr;
- char *rel_source = nullptr;
+ char *backup_base = NULL;
+ char *rel_source = NULL;
int nofollow_flag = logical ? 0 : AT_SYMLINK_NOFOLLOW;
if (link_errno < 0)
link_errno = atomic_link (source, destdir_fd, dest_base);
@@ -294,7 +294,7 @@ do_link (char const *source, int destdir_fd, char const *dest_base,
{
int rename_errno = errno;
free (backup_base);
- backup_base = nullptr;
+ backup_base = NULL;
if (rename_errno != ENOENT)
{
error (0, rename_errno, _("cannot backup %s"),
@@ -350,7 +350,7 @@ do_link (char const *source, int destdir_fd, char const *dest_base,
if (backup_base)
{
char *backup = backup_base;
- void *alloc = nullptr;
+ void *alloc = NULL;
ptrdiff_t destdirlen = dest_base - dest;
if (0 < destdirlen)
{
@@ -438,32 +438,66 @@ interpreted in relation to its parent directory.\n\
emit_mandatory_arg_note ();
- fputs (_("\
- --backup[=CONTROL] make a backup of each existing destination file\n\
- -b like --backup but does not accept an argument\n\
- -d, -F, --directory allow the superuser to attempt to hard link\n\
- directories (this will probably fail due to\n\
- system restrictions, even for the superuser)\n\
- -f, --force remove existing destination files\n\
-"), stdout);
- fputs (_("\
- -i, --interactive prompt whether to remove destinations\n\
- -L, --logical dereference TARGETs that are symbolic links\n\
- -n, --no-dereference treat LINK_NAME as a normal file if\n\
- it is a symbolic link to a directory\n\
- -P, --physical make hard links directly to symbolic links\n\
- -r, --relative with -s, create links relative to link location\n\
- -s, --symbolic make symbolic links instead of hard links\n\
-"), stdout);
- fputs (_("\
- -S, --suffix=SUFFIX override the usual backup suffix\n\
- -t, --target-directory=DIRECTORY specify the DIRECTORY in which to create\n\
- the links\n\
- -T, --no-target-directory treat LINK_NAME as a normal file always\n\
- -v, --verbose print name of each linked file\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ --backup[=CONTROL]\n\
+ make a backup of each existing destination file\n\
+"));
+ oputs (_("\
+ -b\n\
+ like --backup but does not accept an argument\n\
+"));
+ oputs (_("\
+ -d, -F, --directory\n\
+ allow the superuser to attempt to hard link directories,\n\
+ if supported by the system\n\
+"));
+ oputs (_("\
+ -f, --force\n\
+ remove existing destination files\n\
+"));
+ oputs (_("\
+ -i, --interactive\n\
+ prompt whether to remove destinations\n\
+"));
+ oputs (_("\
+ -L, --logical\n\
+ dereference TARGETs that are symbolic links\n\
+"));
+ oputs (_("\
+ -n, --no-dereference\n\
+ treat LINK_NAME as a normal file\n\
+ if it is a symbolic link to a directory\n\
+"));
+ oputs (_("\
+ -P, --physical\n\
+ make hard links directly to symbolic links\n\
+"));
+ oputs (_("\
+ -r, --relative\n\
+ with -s, create links relative to link location\n\
+"));
+ oputs (_("\
+ -s, --symbolic\n\
+ make symbolic links instead of hard links\n\
+"));
+ oputs (_("\
+ -S, --suffix=SUFFIX\n\
+ override the usual backup suffix\n\
+"));
+ oputs (_("\
+ -t, --target-directory=DIRECTORY\n\
+ specify the DIRECTORY in which to create the links\n\
+"));
+ oputs (_("\
+ -T, --no-target-directory\n\
+ treat LINK_NAME as a normal file always\n\
+"));
+ oputs (_("\
+ -v, --verbose\n\
+ print name of each linked file\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_backup_suffix_note ();
printf (_("\
\n\
@@ -481,9 +515,9 @@ main (int argc, char **argv)
int c;
bool ok;
bool make_backups = false;
- char const *backup_suffix = nullptr;
- char *version_control_string = nullptr;
- char const *target_directory = nullptr;
+ char const *backup_suffix = NULL;
+ char *version_control_string = NULL;
+ char const *target_directory = NULL;
int destdir_fd;
bool no_target_directory = false;
int n_files;
@@ -498,11 +532,8 @@ main (int argc, char **argv)
atexit (close_stdin);
- symbolic_link = remove_existing_files = interactive = verbose
- = hard_dir_link = false;
-
while ((c = getopt_long (argc, argv, "bdfinrst:vFLPS:T",
- long_options, nullptr))
+ long_options, NULL))
!= -1)
{
switch (c)
@@ -655,11 +686,11 @@ main (int argc, char **argv)
&& backup_type != numbered_backups)
{
dest_set = hash_initialize (DEST_INFO_INITIAL_CAPACITY,
- nullptr,
+ NULL,
triple_hash,
triple_compare,
triple_free);
- if (dest_set == nullptr)
+ if (dest_set == NULL)
xalloc_die ();
}
diff --git a/src/local.mk b/src/local.mk
index a8ad6b43f..9d9c9814b 100644
--- a/src/local.mk
+++ b/src/local.mk
@@ -1,7 +1,7 @@
# Make coreutils programs. -*-Makefile-*-
# This is included by the top-level Makefile.am.
-## Copyright (C) 1990-2025 Free Software Foundation, Inc.
+## Copyright (C) 1990-2026 Free Software Foundation, Inc.
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
@@ -28,6 +28,7 @@ include $(srcdir)/src/cu-progs.mk
EXTRA_PROGRAMS = \
$(no_install__progs) \
$(build_if_possible__progs) \
+ $(build_if_appropriate__progs) \
$(default__progs)
# The user can tweak these lists at configure time.
@@ -39,6 +40,7 @@ noinst_PROGRAMS = \
src/getlimits
noinst_HEADERS = \
+ src/cksum.h \
src/chown.h \
src/chown-core.h \
src/copy.h \
@@ -59,9 +61,11 @@ noinst_HEADERS = \
src/remove.h \
src/set-fields.h \
src/show-date.h \
+ src/splice.h \
src/statx.h \
src/system.h \
src/temp-stream.h \
+ src/term-sig.h \
src/uname.h \
src/wc.h
@@ -153,7 +157,7 @@ src_ln_LDADD = $(LDADD)
src_logname_LDADD = $(LDADD)
src_ls_LDADD = $(LDADD)
-src_md5sum_LDADD = $(LDADD)
+# See src_md5sum_LDADD below
src_mkdir_LDADD = $(LDADD)
src_mkfifo_LDADD = $(LDADD)
src_mknod_LDADD = $(LDADD)
@@ -178,11 +182,12 @@ src_rm_LDADD = $(LDADD)
src_rmdir_LDADD = $(LDADD)
src_runcon_LDADD = $(LDADD)
src_seq_LDADD = $(LDADD)
-src_sha1sum_LDADD = $(LDADD)
-src_sha224sum_LDADD = $(LDADD)
-src_sha256sum_LDADD = $(LDADD)
-src_sha384sum_LDADD = $(LDADD)
-src_sha512sum_LDADD = $(LDADD)
+# See src_md5sum_LDADD below
+# See src_sha1sum_LDADD below
+# See src_sha224sum_LDADD below
+# See src_sha256sum_LDADD below
+# See src_sha384sum_LDADD below
+# See src_sha512sum_LDADD below
src_shred_LDADD = $(LDADD)
src_shuf_LDADD = $(LDADD)
src_sleep_LDADD = $(LDADD)
@@ -221,6 +226,12 @@ src___LDADD = $(src_test_LDADD)
src_dir_LDADD = $(src_ls_LDADD)
src_vdir_LDADD = $(src_ls_LDADD)
src_chgrp_LDADD = $(src_chown_LDADD)
+src_md5sum_LDADD = $(src_cksum_LDADD)
+src_sha1sum_LDADD = $(src_cksum_LDADD)
+src_sha224sum_LDADD = $(src_cksum_LDADD)
+src_sha256sum_LDADD = $(src_cksum_LDADD)
+src_sha384sum_LDADD = $(src_cksum_LDADD)
+src_sha512sum_LDADD = $(src_cksum_LDADD)
src_cp_LDADD += $(copy_ldadd)
src_ginstall_LDADD += $(copy_ldadd)
@@ -272,6 +283,7 @@ src_touch_LDADD += $(CLOCK_TIME_LIB)
# for gethrxtime
src_dd_LDADD += $(GETHRXTIME_LIB)
+src_shred_LDADD += $(GETHRXTIME_LIB)
# for cap_get_file
src_ls_LDADD += $(LIB_CAP)
@@ -337,16 +349,6 @@ src_sort_LDADD += $(LIBPMULTITHREAD)
# for pthread_sigmask
src_sort_LDADD += $(PTHREAD_SIGMASK_LIB)
-if !USE_NLS
-# for CFPreferencesCopyAppValue
-src_date_LDADD += $(INTL_MACOSX_LIBS)
-src_du_LDADD += $(INTL_MACOSX_LIBS)
-src_ls_LDADD += $(INTL_MACOSX_LIBS)
-src_pr_LDADD += $(INTL_MACOSX_LIBS)
-src_stat_LDADD += $(INTL_MACOSX_LIBS)
-src_uptime_LDADD += $(INTL_MACOSX_LIBS)
-endif
-
# Get the release year from lib/version-etc.c.
RELEASE_YEAR = \
`sed -n '/.*COPYRIGHT_YEAR = \([0-9][0-9][0-9][0-9]\) };/s//\1/p' \
@@ -429,55 +431,55 @@ src_tac_SOURCES = src/tac.c src/temp-stream.c
src_tail_SOURCES = src/tail.c src/iopoll.c
src_tee_SOURCES = src/tee.c src/iopoll.c
-src_sum_SOURCES = src/sum.c src/sum.h src/digest.c
+src_sum_SOURCES = src/sum.c src/sum.h src/cksum.c
src_sum_CPPFLAGS = -DHASH_ALGO_SUM=1 $(AM_CPPFLAGS)
-src_md5sum_SOURCES = src/digest.c
+src_md5sum_SOURCES = src/cksum.c
src_md5sum_CPPFLAGS = -DHASH_ALGO_MD5=1 $(AM_CPPFLAGS)
-src_sha1sum_SOURCES = src/digest.c
+src_sha1sum_SOURCES = src/cksum.c
src_sha1sum_CPPFLAGS = -DHASH_ALGO_SHA1=1 $(AM_CPPFLAGS)
-src_sha224sum_SOURCES = src/digest.c
+src_sha224sum_SOURCES = src/cksum.c
src_sha224sum_CPPFLAGS = -DHASH_ALGO_SHA224=1 $(AM_CPPFLAGS)
-src_sha256sum_SOURCES = src/digest.c
+src_sha256sum_SOURCES = src/cksum.c
src_sha256sum_CPPFLAGS = -DHASH_ALGO_SHA256=1 $(AM_CPPFLAGS)
-src_sha384sum_SOURCES = src/digest.c
+src_sha384sum_SOURCES = src/cksum.c
src_sha384sum_CPPFLAGS = -DHASH_ALGO_SHA384=1 $(AM_CPPFLAGS)
-src_sha512sum_SOURCES = src/digest.c
+src_sha512sum_SOURCES = src/cksum.c
src_sha512sum_CPPFLAGS = -DHASH_ALGO_SHA512=1 $(AM_CPPFLAGS)
src_b2sum_CPPFLAGS = -DHASH_ALGO_BLAKE2=1 -DHAVE_CONFIG_H $(AM_CPPFLAGS)
-src_b2sum_SOURCES = src/digest.c \
+src_b2sum_SOURCES = src/cksum.c \
src/blake2/blake2.h src/blake2/blake2-impl.h \
src/blake2/blake2b-ref.c \
src/blake2/b2sum.c src/blake2/b2sum.h
src_cksum_SOURCES = $(src_b2sum_SOURCES) src/sum.c src/sum.h \
- src/cksum.c src/cksum.h src/crctab.c
+ src/cksum_crc.c src/cksum_crc.h src/crctab.c
src_cksum_CPPFLAGS = -DHASH_ALGO_CKSUM=1 -DHAVE_CONFIG_H $(AM_CPPFLAGS)
if USE_AVX512_CRC32
noinst_LIBRARIES += src/libcksum_avx512.a
-src_libcksum_avx512_a_SOURCES = src/cksum_avx512.c src/cksum.h
+src_libcksum_avx512_a_SOURCES = src/cksum_avx512.c src/cksum_crc.h
cksum_avx512_ldadd = src/libcksum_avx512.a
src_cksum_LDADD += $(cksum_avx512_ldadd)
src_libcksum_avx512_a_CFLAGS = -mavx512bw -mavx512f -mvpclmulqdq $(AM_CFLAGS)
endif
if USE_AVX2_CRC32
noinst_LIBRARIES += src/libcksum_avx2.a
-src_libcksum_avx2_a_SOURCES = src/cksum_avx2.c src/cksum.h
+src_libcksum_avx2_a_SOURCES = src/cksum_avx2.c src/cksum_crc.h
cksum_avx2_ldadd = src/libcksum_avx2.a
src_cksum_LDADD += $(cksum_avx2_ldadd)
src_libcksum_avx2_a_CFLAGS = -mpclmul -mavx -mavx2 -mvpclmulqdq $(AM_CFLAGS)
endif
if USE_PCLMUL_CRC32
noinst_LIBRARIES += src/libcksum_pclmul.a
-src_libcksum_pclmul_a_SOURCES = src/cksum_pclmul.c src/cksum.h
+src_libcksum_pclmul_a_SOURCES = src/cksum_pclmul.c src/cksum_crc.h
cksum_pclmul_ldadd = src/libcksum_pclmul.a
src_cksum_LDADD += $(cksum_pclmul_ldadd)
src_libcksum_pclmul_a_CFLAGS = -mavx -mpclmul $(AM_CFLAGS)
endif
if USE_VMULL_CRC32
noinst_LIBRARIES += src/libcksum_vmull.a
-src_libcksum_vmull_a_SOURCES = src/cksum_vmull.c src/cksum.h
+src_libcksum_vmull_a_SOURCES = src/cksum_vmull.c src/cksum_crc.h
cksum_vmull_ldadd = src/libcksum_vmull.a
src_cksum_LDADD += $(cksum_vmull_ldadd)
src_libcksum_vmull_a_CFLAGS = -march=armv8-a+crypto $(AM_CFLAGS)
@@ -508,6 +510,13 @@ wc_avx2_ldadd = src/libwc_avx2.a
src_wc_LDADD += $(wc_avx2_ldadd)
src_libwc_avx2_a_CFLAGS = -mavx2 $(AM_CFLAGS)
endif
+if USE_NEON_WC_LINECOUNT
+noinst_LIBRARIES += src/libwc_neon.a
+src_libwc_neon_a_SOURCES = src/wc_neon.c
+wc_neon_ldadd = src/libwc_neon.a
+src_wc_LDADD += $(wc_neon_ldadd)
+src_libwc_neon_a_CFLAGS = -march=armv8-a+simd $(AM_CFLAGS)
+endif
# Ensure we don't link against libcoreutils.a as that lib is
# not compiled with -fPIC which causes issues on 64 bit at least
@@ -529,11 +538,23 @@ EXTRA_src_coreutils_DEPENDENCIES = $(single_binary_deps)
include $(top_srcdir)/src/single-binary.mk
-# Creates symlinks or shebangs to the installed programs when building
-# coreutils single binary.
+# Creates symlinks, or shebangs to the installed programs
+# _before_ building coreutils single binary.
+if !SINGLE_BINARY_HARD
EXTRA_src_coreutils_DEPENDENCIES += src/coreutils_$(single_binary_install_type)
+endif
endif SINGLE_BINARY
+# Creates hardlinks _after_ building the coreutils single binary.
+CLEANFILES += src/coreutils_hardlinks
+src/coreutils_hardlinks: src/coreutils$(EXEEXT)
+ $(AM_V_GEN)touch $@
+ $(AM_V_at)for i in x $(single_binary_progs); do \
+ test $$i = x && continue; \
+ rm -f src/$$i$(EXEEXT) || exit $$?; \
+ ln src/coreutils$(EXEEXT) src/$$i$(EXEEXT) || exit $$?; \
+ done
+
CLEANFILES += src/coreutils_symlinks
src/coreutils_symlinks: Makefile
$(AM_V_GEN)touch $@
@@ -598,13 +619,13 @@ $(top_srcdir)/src/primes.h: $(top_srcdir)/src/make-prime-list.c
# We build crctab in a similar manner to primes.h.
BUILT_SOURCES += $(top_srcdir)/src/crctab.c
-$(top_srcdir)/src/crctab.c: $(top_srcdir)/src/cksum.c
+$(top_srcdir)/src/crctab.c: $(top_srcdir)/src/cksum_crc.c
$(AM_V_GEN)if test -n '$(BUILD_CC)'; then \
$(MKDIR_P) $(top_srcdir)/src/crctab-tmp \
&& (cd $(top_srcdir)/src/crctab-tmp \
&& $(BUILD_CC) $(BUILD_CPPFLAGS) $(BUILD_CFLAGS) \
$(BUILD_LDFLAGS) -DCRCTAB -o crctab$(EXEEXT) \
- $(abs_top_srcdir)/src/cksum.c) \
+ $(abs_top_srcdir)/src/cksum_crc.c) \
&& rm -f $@ $@-t \
&& $(top_srcdir)/src/crctab-tmp/crctab$(EXEEXT) > $@-t \
&& chmod a-w $@-t \
diff --git a/src/logname.c b/src/logname.c
index 5a725e448..31d5e9bcf 100644
--- a/src/logname.c
+++ b/src/logname.c
@@ -1,5 +1,5 @@
/* logname -- print user's login name
- Copyright (C) 1990-2025 Free Software Foundation, Inc.
+ Copyright (C) 1990-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -39,8 +39,8 @@ usage (int status)
Print the user's login name.\n\
\n\
"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -49,8 +49,6 @@ Print the user's login name.\n\
int
main (int argc, char **argv)
{
- char *cp;
-
initialize_main (&argc, &argv);
set_program_name (argv[0]);
setlocale (LC_ALL, "");
@@ -61,7 +59,7 @@ main (int argc, char **argv)
parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE_NAME,
Version, true, usage, AUTHORS,
- (char const *) nullptr);
+ (char const *) NULL);
if (optind < argc)
{
@@ -71,7 +69,7 @@ main (int argc, char **argv)
/* POSIX requires using getlogin (or equivalent code) and prohibits
using a fallback technique. */
- cp = getlogin ();
+ char const *cp = getlogin ();
if (! cp)
error (EXIT_FAILURE, 0, _("no login name"));
diff --git a/src/ls-dir.c b/src/ls-dir.c
index 85fe242eb..18420d23a 100644
--- a/src/ls-dir.c
+++ b/src/ls-dir.c
@@ -1,2 +1,2 @@
#include "ls.h"
-int ls_mode = LS_MULTI_COL;
+enum ls_modes ls_mode = LS_MULTI_COL;
diff --git a/src/ls-ls.c b/src/ls-ls.c
index f33fbbc06..00e5f7ee5 100644
--- a/src/ls-ls.c
+++ b/src/ls-ls.c
@@ -1,2 +1,2 @@
#include "ls.h"
-int ls_mode = LS_LS;
+enum ls_modes ls_mode = LS_LS;
diff --git a/src/ls-vdir.c b/src/ls-vdir.c
index 36ebf913b..24c890fbb 100644
--- a/src/ls-vdir.c
+++ b/src/ls-vdir.c
@@ -1,2 +1,2 @@
#include "ls.h"
-int ls_mode = LS_LONG_FORMAT;
+enum ls_modes ls_mode = LS_LONG_FORMAT;
diff --git a/src/ls.c b/src/ls.c
index 7144d0d9a..3d583b85f 100644
--- a/src/ls.c
+++ b/src/ls.c
@@ -1,5 +1,5 @@
/* 'dir', 'vdir' and 'ls' directory listing programs for GNU.
- Copyright (C) 1985-2025 Free Software Foundation, Inc.
+ Copyright (C) 1985-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -36,7 +36,6 @@
Greg Lee <lee@uhunix.uhcc.hawaii.edu>. */
#include <config.h>
-#include <ctype.h>
#include <sys/types.h>
#include <termios.h>
@@ -60,17 +59,6 @@
# include <langinfo.h>
#endif
-/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
- present. */
-#ifndef SA_NOCLDSTOP
-# define SA_NOCLDSTOP 0
-# define sigprocmask(How, Set, Oset) /* empty */
-# define sigset_t int
-# if ! HAVE_SIGINTERRUPT
-# define siginterrupt(sig, flag) /* empty */
-# endif
-#endif
-
/* NonStop circa 2011 lacks both SA_RESTART and siginterrupt, so don't
restart syscalls after a signal handler fires. This may cause
colors to get messed up on the screen if 'ls' is interrupted, but
@@ -81,20 +69,22 @@
#include <fnmatch.h>
+/* Gnulib includes. */
#include "acl.h"
+#include "areadlink.h"
#include "argmatch.h"
-#include "system.h"
#include "assure.h"
+#include "c-ctype.h"
#include "c-strcase.h"
+#include "canonicalize.h"
#include "dev-ino.h"
+#include "filemode.h"
#include "filenamecat.h"
+#include "filevercmp.h"
#include "hard-locale.h"
#include "hash.h"
#include "human.h"
-#include "filemode.h"
-#include "filevercmp.h"
#include "idcache.h"
-#include "ls.h"
#include "mbswidth.h"
#include "mpsort.h"
#include "obstack.h"
@@ -103,14 +93,17 @@
#include "stat-time.h"
#include "strftime.h"
#include "xdectoint.h"
-#include "xstrtol.h"
+#include "xgethostname.h"
+#include "xmemdup0.h"
#include "xstrtol-error.h"
-#include "areadlink.h"
+#include "xstrtol.h"
+
+#include "system.h"
+
#include "dircolors.h"
-#include "xgethostname.h"
-#include "c-ctype.h"
-#include "canonicalize.h"
+#include "ls.h"
#include "statx.h"
+#include "term-sig.h"
/* Include <sys/capability.h> last to avoid a clash of <sys/types.h>
include guards with some premature versions of libcap.
@@ -457,7 +450,7 @@ enum time_style
static char const *const time_style_args[] =
{
- "full-iso", "long-iso", "iso", "locale", nullptr
+ "full-iso", "long-iso", "iso", "locale", NULL
};
static enum time_style const time_style_types[] =
{
@@ -564,7 +557,7 @@ static enum indicator_style indicator_style;
/* Names of indicator styles. */
static char const *const indicator_style_args[] =
{
- "none", "slash", "file-type", "classify", nullptr
+ "none", "slash", "file-type", "classify", NULL
};
static enum indicator_style const indicator_style_types[] =
{
@@ -634,10 +627,10 @@ static struct bin_str color_indicator[] =
{
{ 2, (char const []) {'\033','['} },/* lc: Left of color sequence */
{ 1, (char const []) {'m'} }, /* rc: Right of color sequence */
- { 0, nullptr }, /* ec: End color (replaces lc+rs+rc) */
+ { 0, NULL }, /* ec: End color (replaces lc+rs+rc) */
{ 1, (char const []) {'0'} }, /* rs: Reset to ordinary colors */
- { 0, nullptr }, /* no: Normal */
- { 0, nullptr }, /* fi: File: default */
+ { 0, NULL }, /* no: Normal */
+ { 0, NULL }, /* fi: File: default */
{ 5, ((char const [])
{'0','1',';','3','4'}) }, /* di: Directory: bright blue */
{ 5, ((char const [])
@@ -649,8 +642,8 @@ static struct bin_str color_indicator[] =
{'0','1',';','3','3'}) }, /* bd: Block device: bright yellow */
{ 5, ((char const [])
{'0','1',';','3','3'}) }, /* cd: Char device: bright yellow */
- { 0, nullptr }, /* mi: Missing file: undefined */
- { 0, nullptr }, /* or: Orphaned symlink: undefined */
+ { 0, NULL }, /* mi: Missing file: undefined */
+ { 0, NULL }, /* or: Orphaned symlink: undefined */
{ 5, ((char const [])
{'0','1',';','3','2'}) }, /* ex: Executable: bright green */
{ 5, ((char const [])
@@ -665,14 +658,14 @@ static struct bin_str color_indicator[] =
{'3','4',';','4','2'}) }, /* ow: other-writable: blue on green */
{ 5, ((char const [])
{'3','0',';','4','2'}) }, /* tw: ow w/ sticky: black on green */
- { 0, nullptr }, /* ca: disabled by default */
- { 0, nullptr }, /* mh: disabled by default */
+ { 0, NULL }, /* ca: disabled by default */
+ { 0, NULL }, /* mh: disabled by default */
{ 3, ((char const [])
{'\033','[','K'}) }, /* cl: clear to end of line */
};
/* A list mapping file extensions to corresponding display sequence. */
-static struct color_ext_type *color_ext_list = nullptr;
+static struct color_ext_type *color_ext_list = NULL;
/* Buffer for color sequences */
static char *color_buf;
@@ -881,59 +874,59 @@ enum
static struct option const long_options[] =
{
- {"all", no_argument, nullptr, 'a'},
- {"escape", no_argument, nullptr, 'b'},
- {"directory", no_argument, nullptr, 'd'},
- {"dired", no_argument, nullptr, 'D'},
- {"full-time", no_argument, nullptr, FULL_TIME_OPTION},
- {"group-directories-first", no_argument, nullptr,
+ {"all", no_argument, NULL, 'a'},
+ {"escape", no_argument, NULL, 'b'},
+ {"directory", no_argument, NULL, 'd'},
+ {"dired", no_argument, NULL, 'D'},
+ {"full-time", no_argument, NULL, FULL_TIME_OPTION},
+ {"group-directories-first", no_argument, NULL,
GROUP_DIRECTORIES_FIRST_OPTION},
- {"human-readable", no_argument, nullptr, 'h'},
- {"inode", no_argument, nullptr, 'i'},
- {"kibibytes", no_argument, nullptr, 'k'},
- {"numeric-uid-gid", no_argument, nullptr, 'n'},
- {"no-group", no_argument, nullptr, 'G'},
- {"hide-control-chars", no_argument, nullptr, 'q'},
- {"reverse", no_argument, nullptr, 'r'},
- {"size", no_argument, nullptr, 's'},
- {"width", required_argument, nullptr, 'w'},
- {"almost-all", no_argument, nullptr, 'A'},
- {"ignore-backups", no_argument, nullptr, 'B'},
- {"classify", optional_argument, nullptr, 'F'},
- {"file-type", no_argument, nullptr, FILE_TYPE_INDICATOR_OPTION},
- {"si", no_argument, nullptr, SI_OPTION},
- {"dereference-command-line", no_argument, nullptr, 'H'},
- {"dereference-command-line-symlink-to-dir", no_argument, nullptr,
+ {"human-readable", no_argument, NULL, 'h'},
+ {"inode", no_argument, NULL, 'i'},
+ {"kibibytes", no_argument, NULL, 'k'},
+ {"numeric-uid-gid", no_argument, NULL, 'n'},
+ {"no-group", no_argument, NULL, 'G'},
+ {"hide-control-chars", no_argument, NULL, 'q'},
+ {"reverse", no_argument, NULL, 'r'},
+ {"size", no_argument, NULL, 's'},
+ {"width", required_argument, NULL, 'w'},
+ {"almost-all", no_argument, NULL, 'A'},
+ {"ignore-backups", no_argument, NULL, 'B'},
+ {"classify", optional_argument, NULL, 'F'},
+ {"file-type", no_argument, NULL, FILE_TYPE_INDICATOR_OPTION},
+ {"si", no_argument, NULL, SI_OPTION},
+ {"dereference-command-line", no_argument, NULL, 'H'},
+ {"dereference-command-line-symlink-to-dir", no_argument, NULL,
DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR_OPTION},
- {"hide", required_argument, nullptr, HIDE_OPTION},
- {"ignore", required_argument, nullptr, 'I'},
- {"indicator-style", required_argument, nullptr, INDICATOR_STYLE_OPTION},
- {"dereference", no_argument, nullptr, 'L'},
- {"literal", no_argument, nullptr, 'N'},
- {"quote-name", no_argument, nullptr, 'Q'},
- {"quoting-style", required_argument, nullptr, QUOTING_STYLE_OPTION},
- {"recursive", no_argument, nullptr, 'R'},
- {"format", required_argument, nullptr, FORMAT_OPTION},
- {"show-control-chars", no_argument, nullptr, SHOW_CONTROL_CHARS_OPTION},
- {"sort", required_argument, nullptr, SORT_OPTION},
- {"tabsize", required_argument, nullptr, 'T'},
- {"time", required_argument, nullptr, TIME_OPTION},
- {"time-style", required_argument, nullptr, TIME_STYLE_OPTION},
- {"zero", no_argument, nullptr, ZERO_OPTION},
- {"color", optional_argument, nullptr, COLOR_OPTION},
- {"hyperlink", optional_argument, nullptr, HYPERLINK_OPTION},
- {"block-size", required_argument, nullptr, BLOCK_SIZE_OPTION},
- {"context", no_argument, 0, 'Z'},
- {"author", no_argument, nullptr, AUTHOR_OPTION},
+ {"hide", required_argument, NULL, HIDE_OPTION},
+ {"ignore", required_argument, NULL, 'I'},
+ {"indicator-style", required_argument, NULL, INDICATOR_STYLE_OPTION},
+ {"dereference", no_argument, NULL, 'L'},
+ {"literal", no_argument, NULL, 'N'},
+ {"quote-name", no_argument, NULL, 'Q'},
+ {"quoting-style", required_argument, NULL, QUOTING_STYLE_OPTION},
+ {"recursive", no_argument, NULL, 'R'},
+ {"format", required_argument, NULL, FORMAT_OPTION},
+ {"show-control-chars", no_argument, NULL, SHOW_CONTROL_CHARS_OPTION},
+ {"sort", required_argument, NULL, SORT_OPTION},
+ {"tabsize", required_argument, NULL, 'T'},
+ {"time", required_argument, NULL, TIME_OPTION},
+ {"time-style", required_argument, NULL, TIME_STYLE_OPTION},
+ {"zero", no_argument, NULL, ZERO_OPTION},
+ {"color", optional_argument, NULL, COLOR_OPTION},
+ {"hyperlink", optional_argument, NULL, HYPERLINK_OPTION},
+ {"block-size", required_argument, NULL, BLOCK_SIZE_OPTION},
+ {"context", no_argument, NULL, 'Z'},
+ {"author", no_argument, NULL, AUTHOR_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
static char const *const format_args[] =
{
"verbose", "long", "commas", "horizontal", "across",
- "vertical", "single-column", nullptr
+ "vertical", "single-column", NULL
};
static enum format const format_types[] =
{
@@ -945,7 +938,7 @@ ARGMATCH_VERIFY (format_args, format_types);
static char const *const sort_args[] =
{
"none", "size", "time", "version", "extension",
- "name", "width", nullptr
+ "name", "width", NULL
};
static enum sort_type const sort_types[] =
{
@@ -960,7 +953,7 @@ static char const *const time_args[] =
"ctime", "status",
"mtime", "modification",
"birth", "creation",
- nullptr
+ NULL
};
static enum time_type const time_types[] =
{
@@ -976,7 +969,7 @@ static char const *const when_args[] =
/* force and none are for compatibility with another color-ls version */
"always", "yes", "force",
"never", "no", "none",
- "auto", "tty", "if-tty", nullptr
+ "auto", "tty", "if-tty", NULL
};
static enum when_type const when_types[] =
{
@@ -1000,8 +993,8 @@ static struct column_info *column_info;
/* Maximum number of columns ever possible for this display. */
static size_t max_idx;
-/* The minimum width of a column is 3: 1 character for the name and 2
- for the separating white space. */
+/* The minimum width of a non-final column is 3: 1 character for the name
+ and 2 for the separating white space. The final column is not padded. */
enum { MIN_COLUMN_WIDTH = 3 };
@@ -1292,7 +1285,7 @@ fstat_for_ino (int fd, struct stat *st)
}
#endif
-/* Return the address of the first plain %b spec in FMT, or nullptr if
+/* Return the address of the first plain %b spec in FMT, or NULL if
there is no such spec. %5b etc. do not match, so that user
widths/flags are honored. */
@@ -1307,7 +1300,7 @@ first_percent_b (char const *fmt)
case 'b': return fmt;
case '%': fmt++; break;
}
- return nullptr;
+ return NULL;
}
static char RFC3986[256];
@@ -1432,7 +1425,7 @@ static size_t
dev_ino_hash (void const *x, size_t table_size)
{
struct dev_ino const *p = x;
- return (uintmax_t) p->st_ino % table_size;
+ return (uintmax_t) {p->st_ino} % table_size;
}
static bool
@@ -1467,7 +1460,7 @@ visit_dir (dev_t dev, ino_t ino)
/* Attempt to insert this entry into the table. */
ent_from_table = hash_insert (active_dir_set, ent);
- if (ent_from_table == nullptr)
+ if (ent_from_table == NULL)
{
/* Insertion failed due to lack of memory. */
xalloc_die ();
@@ -1528,8 +1521,6 @@ set_normal_color (void)
static void
sighandler (int sig)
{
- if (! SA_NOCLDSTOP)
- signal (sig, SIG_IGN);
if (! interrupt_signal)
interrupt_signal = sig;
}
@@ -1537,10 +1528,8 @@ sighandler (int sig)
/* A SIGTSTP was received; arrange for the program to suspend itself. */
static void
-stophandler (int sig)
+stophandler (MAYBE_UNUSED int sig)
{
- if (! SA_NOCLDSTOP)
- signal (sig, stophandler);
if (! interrupt_signal)
stop_signal_count++;
}
@@ -1584,7 +1573,7 @@ process_signals (void)
/* Exit or suspend the program. */
raise (sig);
- sigprocmask (SIG_SETMASK, &oldset, nullptr);
+ sigprocmask (SIG_SETMASK, &oldset, NULL);
/* If execution reaches here, then the program has been
continued (after being suspended). */
@@ -1598,82 +1587,48 @@ static void
signal_setup (bool init)
{
/* The signals that are trapped, and the number of such signals. */
- static int const sig[] =
+ static int const stop_sig[] =
{
/* This one is handled specially. */
SIGTSTP,
-
- /* The usual suspects. */
- SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM,
-#ifdef SIGPOLL
- SIGPOLL,
-#endif
-#ifdef SIGPROF
- SIGPROF,
-#endif
-#ifdef SIGVTALRM
- SIGVTALRM,
-#endif
-#ifdef SIGXCPU
- SIGXCPU,
-#endif
-#ifdef SIGXFSZ
- SIGXFSZ,
-#endif
};
- enum { nsigs = countof (sig) };
-#if ! SA_NOCLDSTOP
- static bool caught_sig[nsigs];
-#endif
-
- int j;
+ enum { nsigs = countof (term_sig), nstop = countof (stop_sig) };
if (init)
{
-#if SA_NOCLDSTOP
struct sigaction act;
sigemptyset (&caught_signals);
- for (j = 0; j < nsigs; j++)
+ for (int j = 0; j < nsigs + nstop; j++)
{
- sigaction (sig[j], nullptr, &act);
+ int sig = j < nsigs ? term_sig[j]: stop_sig[j - nsigs];
+ sigaction (sig, NULL, &act);
if (act.sa_handler != SIG_IGN)
- sigaddset (&caught_signals, sig[j]);
+ sigaddset (&caught_signals, sig);
}
act.sa_mask = caught_signals;
act.sa_flags = SA_RESTART;
- for (j = 0; j < nsigs; j++)
- if (sigismember (&caught_signals, sig[j]))
- {
- act.sa_handler = sig[j] == SIGTSTP ? stophandler : sighandler;
- sigaction (sig[j], &act, nullptr);
- }
-#else
- for (j = 0; j < nsigs; j++)
+ for (int j = 0; j < nsigs + nstop; j++)
{
- caught_sig[j] = (signal (sig[j], SIG_IGN) != SIG_IGN);
- if (caught_sig[j])
+ int sig = j < nsigs ? term_sig[j]: stop_sig[j - nsigs];
+ if (sigismember (&caught_signals, sig))
{
- signal (sig[j], sig[j] == SIGTSTP ? stophandler : sighandler);
- siginterrupt (sig[j], 0);
+ act.sa_handler = j < nsigs ? sighandler : stophandler;
+ sigaction (sig, &act, NULL);
}
}
-#endif
}
else /* restore. */
{
-#if SA_NOCLDSTOP
- for (j = 0; j < nsigs; j++)
- if (sigismember (&caught_signals, sig[j]))
- signal (sig[j], SIG_DFL);
-#else
- for (j = 0; j < nsigs; j++)
- if (caught_sig[j])
- signal (sig[j], SIG_DFL);
-#endif
+ for (int j = 0; j < nsigs + nstop; j++)
+ {
+ int sig = j < nsigs ? term_sig[j]: stop_sig[j - nsigs];
+ if (sigismember (&caught_signals, sig))
+ signal (sig, SIG_DFL);
+ }
}
}
@@ -1709,7 +1664,7 @@ main (int argc, char **argv)
exit_status = EXIT_SUCCESS;
print_dir_name = true;
- pending_dirs = nullptr;
+ pending_dirs = NULL;
current_time.tv_sec = TYPE_MINIMUM (time_t);
current_time.tv_nsec = -1;
@@ -1752,11 +1707,11 @@ main (int argc, char **argv)
detect any directory cycles. */
if (recursive)
{
- active_dir_set = hash_initialize (INITIAL_TABLE_SIZE, nullptr,
+ active_dir_set = hash_initialize (INITIAL_TABLE_SIZE, NULL,
dev_ino_hash,
dev_ino_compare,
dev_ino_free);
- if (active_dir_set == nullptr)
+ if (active_dir_set == NULL)
xalloc_die ();
obstack_init (&dev_ino_obstack);
@@ -1801,20 +1756,20 @@ main (int argc, char **argv)
if (n_files <= 0)
{
if (immediate_dirs)
- gobble_file (".", directory, NOT_AN_INODE_NUMBER, true, nullptr);
+ gobble_file (".", directory, NOT_AN_INODE_NUMBER, true, NULL);
else
- queue_directory (".", nullptr, true);
+ queue_directory (".", NULL, true);
}
else
do
- gobble_file (argv[i++], unknown, NOT_AN_INODE_NUMBER, true, nullptr);
+ gobble_file (argv[i++], unknown, NOT_AN_INODE_NUMBER, true, NULL);
while (i < argc);
if (cwd_n_used)
{
sort_files ();
if (!immediate_dirs)
- extract_dirs_from_files (nullptr, true);
+ extract_dirs_from_files (NULL, true);
/* 'cwd_n_used' might be zero now. */
}
@@ -1828,7 +1783,7 @@ main (int argc, char **argv)
if (pending_dirs)
dired_outbyte ('\n');
}
- else if (n_files <= 1 && pending_dirs && pending_dirs->next == 0)
+ else if (n_files <= 1 && pending_dirs && !pending_dirs->next)
print_dir_name = false;
while (pending_dirs)
@@ -1838,9 +1793,9 @@ main (int argc, char **argv)
if (LOOP_DETECT)
{
- if (thispend->name == nullptr)
+ if (thispend->name == NULL)
{
- /* thispend->name == nullptr means this is a marker entry
+ /* thispend->name == NULL means this is a marker entry
indicating we've finished processing the directory.
Use its dev/ino numbers to remove the corresponding
entry from the active_dir_set hash table. */
@@ -1864,7 +1819,6 @@ main (int argc, char **argv)
if (print_with_color && used_color)
{
- int j;
/* Skip the restore when it would be a no-op, i.e.,
when left is "\033[" and right is "m". */
@@ -1882,9 +1836,9 @@ main (int argc, char **argv)
This can process signals out of order, but there doesn't seem to
be an easy way to do them in order, and the order isn't that
important anyway. */
- for (j = stop_signal_count; j; j--)
+ for (int j = stop_signal_count; j; j--)
raise (SIGSTOP);
- j = interrupt_signal;
+ int j = interrupt_signal;
if (j)
raise (j);
}
@@ -1917,7 +1871,7 @@ decode_line_length (char const *spec)
/* Treat too-large values as if they were 0, which is
effectively infinity. */
- switch (xstrtoumax (spec, nullptr, 0, &val, ""))
+ switch (xstrtoumax (spec, NULL, 0, &val, ""))
{
case LONGINT_OK:
return val <= MIN (PTRDIFF_MAX, SIZE_MAX) ? val : 0;
@@ -1953,7 +1907,7 @@ stdout_isatty (void)
static int
decode_switches (int argc, char **argv)
{
- char const *time_style_option = nullptr;
+ char *time_style_option = NULL;
/* These variables are false or -1 unless a switch says otherwise. */
bool kibibytes_specified = false;
@@ -2200,7 +2154,7 @@ decode_switches (int argc, char **argv)
case FULL_TIME_OPTION:
format_opt = long_format;
- time_style_option = "full-iso";
+ time_style_option = (char *) "full-iso";
break;
case COLOR_OPTION:
@@ -2365,7 +2319,7 @@ decode_switches (int argc, char **argv)
if (p)
{
uintmax_t tmp;
- if (xstrtoumax (p, nullptr, 0, &tmp, "") == LONGINT_OK
+ if (xstrtoumax (p, NULL, 0, &tmp, "") == LONGINT_OK
&& tmp <= SIZE_MAX)
tabsize = tmp;
else
@@ -2389,25 +2343,24 @@ decode_switches (int argc, char **argv)
? (stdout_isatty () ? shell_escape_quoting_style : -1)
: escape_quoting_style);
if (0 <= qs)
- set_quoting_style (nullptr, qs);
- qs = get_quoting_style (nullptr);
+ set_quoting_style (NULL, qs);
+ qs = get_quoting_style (NULL);
align_variable_outer_quotes
= ((format == long_format
|| ((format == many_per_line || format == horizontal) && line_length))
&& (qs == shell_quoting_style
|| qs == shell_escape_quoting_style
|| qs == c_maybe_quoting_style));
- filename_quoting_options = clone_quoting_options (nullptr);
+ filename_quoting_options = clone_quoting_options (NULL);
if (qs == escape_quoting_style)
set_char_quoting (filename_quoting_options, ' ', 1);
if (file_type <= indicator_style)
{
- char const *p;
- for (p = &"*=>@|"[indicator_style - file_type]; *p; p++)
+ for (char const *p = &"*=>@|"[indicator_style - file_type]; *p; p++)
set_char_quoting (filename_quoting_options, *p, 1);
}
- dirname_quoting_options = clone_quoting_options (nullptr);
+ dirname_quoting_options = clone_quoting_options (NULL);
set_char_quoting (dirname_quoting_options, ':', 1);
/* --dired implies --format=long (-l) and sans --hyperlink.
@@ -2427,35 +2380,39 @@ decode_switches (int argc, char **argv)
if (format == long_format)
{
- char const *style = time_style_option;
- static char const posix_prefix[] = "posix-";
-
+ char *envstyle = NULL;
+ char *style = time_style_option;
if (! style)
- {
- style = getenv ("TIME_STYLE");
- if (! style)
- style = "locale";
- }
+ style = envstyle = getenv ("TIME_STYLE");
- while (STREQ_LEN (style, posix_prefix, sizeof posix_prefix - 1))
+ if (! style)
+ style = (char *) "locale";
+ else
{
- if (! hard_locale (LC_TIME))
- return optind;
- style += sizeof posix_prefix - 1;
+ static char const posix_prefix[] = "posix-";
+ while (STREQ_LEN (style, posix_prefix, sizeof posix_prefix - 1))
+ {
+ if (! hard_locale (LC_TIME))
+ return optind;
+ style += sizeof posix_prefix - 1;
+ }
}
if (*style == '+')
{
- char const *p0 = style + 1;
+ char *p0 = style + 1;
char *p0nl = strchr (p0, '\n');
char const *p1 = p0;
if (p0nl)
{
- if (strchr (p0nl + 1, '\n'))
+ p1 = p0nl + 1;
+ if (strchr (p1, '\n'))
error (LS_FAILURE, 0, _("invalid time style format %s"),
quote (p0));
- *p0nl++ = '\0';
- p1 = p0nl;
+ if (envstyle)
+ p0 = xmemdup0 (p0, p0nl - p0);
+ else
+ *p0nl = '\0';
}
long_time_format[0] = p0;
long_time_format[1] = p1;
@@ -2486,7 +2443,7 @@ decode_switches (int argc, char **argv)
{
for (int i = 0; i < 2; i++)
long_time_format[i] =
- dcgettext (nullptr, long_time_format[i], LC_TIME);
+ dcgettext (NULL, long_time_format[i], LC_TIME);
}
}
}
@@ -2747,7 +2704,7 @@ parse_ls_color (void)
char label0, label1; /* Indicator label */
struct color_ext_type *ext; /* Extension we are working on */
- if ((p = getenv ("LS_COLORS")) == nullptr || *p == '\0')
+ if ((p = getenv ("LS_COLORS")) == NULL || *p == '\0')
{
/* LS_COLORS takes precedence, but if that's not set then
honor the COLORTERM and TERM env variables so that
@@ -2759,7 +2716,7 @@ parse_ls_color (void)
return;
}
- ext = nullptr;
+ ext = NULL;
/* This is an overly conservative estimate, but any possible
LS_COLORS string will *not* generate a color_buf longer than
@@ -2862,15 +2819,13 @@ parse_ls_color (void)
if (state == PS_FAIL)
{
- struct color_ext_type *e;
- struct color_ext_type *e2;
-
error (0, 0,
_("unparsable value for LS_COLORS environment variable"));
free (color_buf);
- for (e = color_ext_list; e != nullptr; /* empty */)
+ for (struct color_ext_type *e = color_ext_list; e != NULL;
+ /* empty */)
{
- e2 = e;
+ struct color_ext_type *e2 = e;
e = e->next;
free (e2);
}
@@ -2882,14 +2837,13 @@ parse_ls_color (void)
different cased extensions with separate sequences defined.
Also set ext.len to SIZE_MAX on any entries that can't
match due to precedence, to avoid redundant string compares. */
- struct color_ext_type *e1;
-
- for (e1 = color_ext_list; e1 != nullptr; e1 = e1->next)
+ for (struct color_ext_type *e1 = color_ext_list; e1 != NULL;
+ e1 = e1->next)
{
- struct color_ext_type *e2;
bool case_ignored = false;
- for (e2 = e1->next; e2 != nullptr; e2 = e2->next)
+ for (struct color_ext_type *e2 = e1->next; e2 != NULL;
+ e2 = e2->next)
{
if (e2->ext.len < SIZE_MAX && e1->ext.len == e2->ext.len)
{
@@ -2973,7 +2927,7 @@ file_failure (bool serious, char const *message, char const *file)
If REALNAME is nonzero, it will be used instead of NAME when the
directory name is printed. This allows symbolic links to directories
to be treated as regular directories but still be listed under their
- real names. NAME == nullptr is used to insert a marker entry for the
+ real names. NAME == NULL is used to insert a marker entry for the
directory named in REALNAME.
If NAME is non-null, we use its dev/ino information to save
a call to stat -- when doing a recursive (-R) traversal.
@@ -2983,8 +2937,8 @@ static void
queue_directory (char const *name, char const *realname, bool command_line_arg)
{
struct pending *new = xmalloc (sizeof *new);
- new->realname = realname ? xstrdup (realname) : nullptr;
- new->name = name ? xstrdup (name) : nullptr;
+ new->realname = realname ? xstrdup (realname) : NULL;
+ new->name = name ? xstrdup (name) : NULL;
new->command_line_arg = command_line_arg;
new->next = pending_dirs;
pending_dirs = new;
@@ -3050,7 +3004,7 @@ print_dir (char const *name, char const *realname, bool command_line_arg)
first = false;
dired_indent ();
- char *absolute_name = nullptr;
+ char *absolute_name = NULL;
if (print_hyperlink)
{
absolute_name = canonicalize_filename_mode (name, CAN_MISSING);
@@ -3059,7 +3013,7 @@ print_dir (char const *name, char const *realname, bool command_line_arg)
_("error canonicalizing %s"), name);
}
quote_name (realname ? realname : name, dirname_quoting_options, -1,
- nullptr, true, &subdired_obstack, absolute_name);
+ NULL, true, &subdired_obstack, absolute_name);
free (absolute_name);
@@ -3179,8 +3133,7 @@ add_ignore_pattern (char const *pattern)
static bool
patterns_match (struct ignore_pattern const *patterns, char const *file)
{
- struct ignore_pattern const *p;
- for (p = patterns; p; p = p->next)
+ for (struct ignore_pattern const *p = patterns; p; p = p->next)
if (fnmatch (p->pattern, file, FNM_PERIOD) == 0)
return true;
return false;
@@ -3218,10 +3171,10 @@ has_capability (char const *name)
bool has_cap;
cap_t cap_d = cap_get_file (name);
- if (cap_d == nullptr)
+ if (cap_d == NULL)
return false;
- result = cap_to_text (cap_d, nullptr);
+ result = cap_to_text (cap_d, NULL);
cap_free (cap_d);
if (!result)
return false;
@@ -3296,7 +3249,7 @@ file_has_aclinfo_cache (char const *file, struct fileinfo *f,
if (f->stat_ok && unsupported_scontext
&& f->stat.st_dev == unsupported_device)
{
- ai->buf = ai->u.__gl_acl_ch;
+ ai->buf = ai->u._gl_acl_ch;
ai->size = 0;
ai->scontext = unsupported_scontext;
ai->scontext_err = unsupported_scontext_err;
@@ -3560,7 +3513,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
f->has_capability = has_capability_cache (full_name, f);
f->scontext = ai.scontext;
- ai.scontext = nullptr;
+ ai.scontext = NULL;
aclinfo_free (&ai);
}
@@ -3699,7 +3652,7 @@ static void
get_link_name (char const *filename, struct fileinfo *f, bool command_line_arg)
{
f->linkname = areadlink_with_size (filename, f->stat.st_size);
- if (f->linkname == nullptr)
+ if (f->linkname == NULL)
file_failure (command_line_arg, _("cannot read symbolic link %s"),
filename);
}
@@ -3726,14 +3679,14 @@ static void
extract_dirs_from_files (char const *dirname, bool command_line_arg)
{
idx_t i, j;
- bool ignore_dot_and_dot_dot = (dirname != nullptr);
+ bool ignore_dot_and_dot_dot = (dirname != NULL);
if (dirname && LOOP_DETECT)
{
/* Insert a marker entry first. When we dequeue this marker entry,
we'll know that DIRNAME has been processed and may be removed
from the set of active directories. */
- queue_directory (nullptr, dirname, false);
+ queue_directory (NULL, dirname, false);
}
/* Queue the directories last one first, because queueing reverses the
@@ -3751,7 +3704,7 @@ extract_dirs_from_files (char const *dirname, bool command_line_arg)
queue_directory (f->name, f->linkname, command_line_arg);
else
{
- char *name = file_name_concat (dirname, f->name, nullptr);
+ char *name = file_name_concat (dirname, f->name, NULL);
queue_directory (name, f->linkname, command_line_arg);
free (name);
}
@@ -4009,13 +3962,13 @@ static qsortFunc const sort_functions[][2][2][2] =
{ rev_xstrcoll_version, rev_xstrcoll_df_version },
},
- /* We use nullptr for the strcmp variants of version comparison
+ /* We use NULL for the strcmp variants of version comparison
since as explained in cmp_version definition, version comparison
does not rely on xstrcoll, so it will never longjmp, and never
need to try the strcmp fallback. */
{
- { nullptr, nullptr },
- { nullptr, nullptr },
+ { NULL, NULL },
+ { NULL, NULL },
}
},
@@ -4227,7 +4180,7 @@ static void
format_user (uid_t u, int width, bool stat_ok)
{
format_user_or_group (! stat_ok ? "?" :
- (numeric_ids ? nullptr : getuser (u)), u, width);
+ (numeric_ids ? NULL : getuser (u)), u, width);
}
/* Likewise, for groups. */
@@ -4236,7 +4189,7 @@ static void
format_group (gid_t g, int width, bool stat_ok)
{
format_user_or_group (! stat_ok ? "?" :
- (numeric_ids ? nullptr : getgroup (g)), g, width);
+ (numeric_ids ? NULL : getgroup (g)), g, width);
}
/* Return the number of columns that format_user_or_group will print,
@@ -4247,7 +4200,7 @@ format_user_or_group_width (char const *name, uintmax_t id)
{
return (name
? mbswidth (name, MBSWIDTH_FLAGS)
- : snprintf (nullptr, 0, "%ju", id));
+ : snprintf (NULL, 0, "%ju", id));
}
/* Return the number of columns that format_user will print,
@@ -4256,7 +4209,7 @@ format_user_or_group_width (char const *name, uintmax_t id)
static int
format_user_width (uid_t u)
{
- return format_user_or_group_width (numeric_ids ? nullptr : getuser (u), u);
+ return format_user_or_group_width (numeric_ids ? NULL : getuser (u), u);
}
/* Likewise, for groups. */
@@ -4264,7 +4217,7 @@ format_user_width (uid_t u)
static int
format_group_width (gid_t g)
{
- return format_user_or_group_width (numeric_ids ? nullptr : getgroup (g), g);
+ return format_user_or_group_width (numeric_ids ? NULL : getgroup (g), g);
}
/* Return a pointer to a formatted version of F->stat.st_ino,
@@ -4477,7 +4430,7 @@ print_long_format (const struct fileinfo *f)
if (f->linkname)
{
dired_outstring (" -> ");
- print_name_with_quoting (f, true, nullptr, (p - buf) + w + 4);
+ print_name_with_quoting (f, true, NULL, (p - buf) + w + 4);
if (indicator_style != none)
print_type_indicator (true, f->linkmode, unknown);
}
@@ -4655,7 +4608,7 @@ quote_name_buf (char **inbuf, size_t bufsize, char *name,
displayed_width = len;
}
}
- else if (width != nullptr)
+ else if (width != NULL)
{
if (MB_CUR_MAX > 1)
{
@@ -4682,7 +4635,7 @@ quote_name_buf (char **inbuf, size_t bufsize, char *name,
not actually part of the name. */
*pad = (align_variable_outer_quotes && cwd_some_quoted && ! quoted);
- if (width != nullptr)
+ if (width != NULL)
*width = displayed_width;
*inbuf = buf;
@@ -4744,7 +4697,7 @@ quote_name (char const *name, struct quoting_options const *options,
bool pad;
len = quote_name_buf (&buf, sizeof smallbuf, (char *) name, options,
- needs_general_quoting, nullptr, &pad);
+ needs_general_quoting, NULL, &pad);
if (pad && allow_pad)
dired_outbyte (' ');
@@ -4770,7 +4723,7 @@ quote_name (char const *name, struct quoting_options const *options,
For example since ls is outputting a dense block of URIs
it would be best to not underline by default, and only
do so upon hover etc. */
- printf ("\033]8;;file://%s%s%s\a", h, *n == '/' ? "" : "/", n);
+ printf ("\033]8;;file://%s%s%s\033\\", h, *n == '/' ? "" : "/", n);
free (h);
free (n);
}
@@ -4787,7 +4740,7 @@ quote_name (char const *name, struct quoting_options const *options,
if (absolute_name)
{
- fputs ("\033]8;;\a", stdout);
+ fputs ("\033]8;;\033\\", stdout);
if (skip_quotes)
putchar (*(buf + len - 1));
}
@@ -4807,7 +4760,7 @@ print_name_with_quoting (const struct fileinfo *f,
char const *name = symlink_target ? f->linkname : f->name;
const struct bin_str *color
- = print_with_color ? get_color_indicator (f, symlink_target) : nullptr;
+ = print_with_color ? get_color_indicator (f, symlink_target) : NULL;
bool used_color_this_time = (print_with_color
&& (color || is_colored (C_NORM)));
@@ -4837,7 +4790,7 @@ print_name_with_quoting (const struct fileinfo *f,
static void
prep_non_filename_text (void)
{
- if (color_indicator[C_END].string != nullptr)
+ if (color_indicator[C_END].string != NULL)
put_indicator (&color_indicator[C_END]);
else
{
@@ -4879,7 +4832,7 @@ print_file_name_and_frills (const struct fileinfo *f, size_t start_col)
if (print_scontext)
printf ("%*s ", format == with_commas ? 0 : scontext_width, f->scontext);
- size_t width = print_name_with_quoting (f, false, nullptr, start_col);
+ size_t width = print_name_with_quoting (f, false, NULL, start_col);
if (indicator_style != none)
width += print_type_indicator (f->stat_ok, f->stat.st_mode, f->filetype);
@@ -4944,12 +4897,12 @@ print_color_indicator (const struct bin_str *ind)
put_indicator (&color_indicator[C_RIGHT]);
}
- return ind != nullptr;
+ return ind != NULL;
}
-/* Returns color indicator or nullptr if none. */
+/* Returns color indicator or NULL if none. */
ATTRIBUTE_PURE
-static const struct bin_str*
+static const struct bin_str *
get_color_indicator (const struct fileinfo *f, bool symlink_target)
{
enum indicator_no type;
@@ -5035,14 +4988,14 @@ get_color_indicator (const struct fileinfo *f, bool symlink_target)
}
/* Check the file's suffix only if still classified as C_FILE. */
- ext = nullptr;
+ ext = NULL;
if (type == C_FILE)
{
/* Test if NAME has a recognized suffix. */
len = strlen (name);
name += len; /* Pointer to final \0. */
- for (ext = color_ext_list; ext != nullptr; ext = ext->next)
+ for (ext = color_ext_list; ext != NULL; ext = ext->next)
{
if (ext->ext.len <= len)
{
@@ -5072,7 +5025,7 @@ get_color_indicator (const struct fileinfo *f, bool symlink_target)
const struct bin_str *const s
= ext ? &(ext->seq) : &color_indicator[type];
- return s->string ? s : nullptr;
+ return s->string ? s : NULL;
}
/* Output a color indicator (which may contain nulls). */
@@ -5217,10 +5170,12 @@ print_with_separator (char sep)
if (filesno != 0)
{
char separator;
+ size_t next_pos = 2 + (sep == ',' && filesno < cwd_n_used - 1);
if (! line_length
- || ((pos + len + 2 < line_length)
- && (pos <= SIZE_MAX - len - 2)))
+ || (! ckd_add (&next_pos, next_pos, pos)
+ && ! ckd_add (&next_pos, next_pos, len)
+ && next_pos <= line_length))
{
pos += 2;
separator = ' ';
@@ -5323,9 +5278,10 @@ init_column_info (idx_t max_cols)
for (idx_t i = 0; i < max_cols; ++i)
{
column_info[i].valid_len = true;
- column_info[i].line_len = (i + 1) * MIN_COLUMN_WIDTH;
- for (idx_t j = 0; j <= i; ++j)
+ column_info[i].line_len = i * MIN_COLUMN_WIDTH + 1;
+ for (idx_t j = 0; j < i; ++j)
column_info[i].col_arr[j] = MIN_COLUMN_WIDTH;
+ column_info[i].col_arr[i] = 1;
}
}
@@ -5363,7 +5319,7 @@ calculate_columns (bool by_columns)
- column_info[i].col_arr[idx]);
column_info[i].col_arr[idx] = real_length;
column_info[i].valid_len = (column_info[i].line_len
- < line_length);
+ <= line_length);
}
}
}
@@ -5395,190 +5351,264 @@ Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.\n\
emit_mandatory_arg_note ();
- fputs (_("\
- -a, --all do not ignore entries starting with .\n\
- -A, --almost-all do not list implied . and ..\n\
- --author with -l, print the author of each file\n\
- -b, --escape print C-style escapes for nongraphic characters\n\
-"), stdout);
- fputs (_("\
- --block-size=SIZE with -l, scale sizes by SIZE when printing them;\n\
- e.g., '--block-size=M'; see SIZE format below\n\
-\n\
-"), stdout);
- fputs (_("\
- -B, --ignore-backups do not list implied entries ending with ~\n\
-"), stdout);
- fputs (_("\
- -c with -lt: sort by, and show, ctime (time of last\n\
- change of file status information);\n\
- with -l: show ctime and sort by name;\n\
- otherwise: sort by ctime, newest first\n\
-\n\
-"), stdout);
- fputs (_("\
- -C list entries by columns\n\
- --color[=WHEN] color the output WHEN; more info below\n\
- -d, --directory list directories themselves, not their contents\n\
- -D, --dired generate output designed for Emacs' dired mode\n\
-"), stdout);
- fputs (_("\
- -f same as -a -U\n\
- -F, --classify[=WHEN] append indicator (one of */=>@|) to entries WHEN\n\
- --file-type likewise, except do not append '*'\n\
-"), stdout);
- fputs (_("\
- --format=WORD across,horizontal (-x), commas (-m), long (-l),\n\
- single-column (-1), verbose (-l), vertical (-C)\n\
-\n\
-"), stdout);
- fputs (_("\
- --full-time like -l --time-style=full-iso\n\
-"), stdout);
- fputs (_("\
- -g like -l, but do not list owner\n\
-"), stdout);
- fputs (_("\
+ oputs (_("\
+ -a, --all\n\
+ do not ignore entries starting with .\n\
+"));
+ oputs (_("\
+ -A, --almost-all\n\
+ do not list implied . and ..\n\
+"));
+ oputs (_("\
+ --author\n\
+ with -l, print the author of each file\n\
+"));
+ oputs (_("\
+ -b, --escape\n\
+ print C-style escapes for nongraphic characters\n\
+"));
+ oputs (_("\
+ --block-size=SIZE\n\
+ with -l, scale sizes by SIZE when printing them;\n\
+ e.g., '--block-size=M'; see SIZE format below\n\
+"));
+ oputs (_("\
+ -B, --ignore-backups\n\
+ do not list implied entries ending with ~\n\
+"));
+ oputs (_("\
+ -c\n\
+ with -lt: sort by, and show, ctime\n\
+ (time of last change of file status information);\n\
+ with -l: show ctime and sort by name;\n\
+ otherwise: sort by ctime, newest first\n\
+"));
+ oputs (_("\
+ -C\n\
+ list entries by columns\n\
+"));
+ oputs (_("\
+ --color[=WHEN]\n\
+ color the output WHEN; more info below\n\
+"));
+ oputs (_("\
+ -d, --directory\n\
+ list directories themselves, not their contents\n\
+"));
+ oputs (_("\
+ -D, --dired\n\
+ generate output designed for Emacs' dired mode\n\
+"));
+ oputs (_("\
+ -f\n\
+ same as -a -U\n\
+"));
+ oputs (_("\
+ -F, --classify[=WHEN]\n\
+ append indicator (one of */=>@|) to entries WHEN\n\
+"));
+ oputs (_("\
+ --file-type\n\
+ like -F, except do not append '*'\n\
+"));
+ oputs (_("\
+ --format=WORD\n\
+ across,horizontal (-x), commas (-m), long (-l),\n\
+ single-column (-1), verbose (-l), vertical (-C)\n\
+"));
+ oputs (_("\
+ --full-time\n\
+ like -l --time-style=full-iso\n\
+"));
+ oputs (_("\
+ -g\n\
+ like -l, but do not list owner\n\
+"));
+ oputs (_("\
--group-directories-first\n\
- group directories before files\n\
-"), stdout);
- fputs (_("\
- -G, --no-group in a long listing, don't print group names\n\
-"), stdout);
- fputs (_("\
- -h, --human-readable with -l and -s, print sizes like 1K 234M 2G etc.\n\
- --si likewise, but use powers of 1000 not 1024\n\
-"), stdout);
- fputs (_("\
+ group directories before files\n\
+"));
+ oputs (_("\
+ -G, --no-group\n\
+ in a long listing, don't print group names\n\
+"));
+ oputs (_("\
+ -h, --human-readable\n\
+ with -l and -s, print sizes like 1K 234M 2G etc.\n\
+"));
+ oputs (_("\
+ --si\n\
+ likewise, but use powers of 1000 not 1024\n\
+"));
+ oputs (_("\
-H, --dereference-command-line\n\
- follow symbolic links listed on the command line\n\
-"), stdout);
- fputs (_("\
+ follow symbolic links listed on the command line\n\
+"));
+ oputs (_("\
--dereference-command-line-symlink-to-dir\n\
- follow each command line symbolic link\n\
- that points to a directory\n\
-\n\
-"), stdout);
- fputs (_("\
- --hide=PATTERN do not list implied entries matching shell PATTERN\
-\n\
- (overridden by -a or -A)\n\
-\n\
-"), stdout);
- fputs (_("\
- --hyperlink[=WHEN] hyperlink file names WHEN\n\
-"), stdout);
- fputs (_("\
+ follow each command line symbolic link that points to a directory\n\
+"));
+ oputs (_("\
+ --hide=PATTERN\n\
+ do not list implied entries matching shell PATTERN\n\
+ (overridden by -a or -A)\n\
+"));
+ oputs (_("\
+ --hyperlink[=WHEN]\n\
+ hyperlink file names WHEN\n\
+"));
+ oputs (_("\
--indicator-style=WORD\n\
- append indicator with style WORD to entry names:\n\
- none (default), slash (-p),\n\
- file-type (--file-type), classify (-F)\n\
-\n\
-"), stdout);
- fputs (_("\
- -i, --inode print the index number of each file\n\
- -I, --ignore=PATTERN do not list implied entries matching shell PATTERN\
-\n\
-"), stdout);
- fputs (_("\
- -k, --kibibytes default to 1024-byte blocks for file system usage;\
-\n\
- used only with -s and per directory totals\n\
-\n\
-"), stdout);
- fputs (_("\
- -l use a long listing format\n\
-"), stdout);
- fputs (_("\
- -L, --dereference when showing file information for a symbolic\n\
- link, show information for the file the link\n\
- references rather than for the link itself\n\
-\n\
-"), stdout);
- fputs (_("\
- -m fill width with a comma separated list of entries\
-\n\
-"), stdout);
- fputs (_("\
- -n, --numeric-uid-gid like -l, but list numeric user and group IDs\n\
- -N, --literal print entry names without quoting\n\
- -o like -l, but do not list group information\n\
+ append indicator with style WORD to entry names:\n\
+ none (default), slash (-p), file-type (--file-type), classify (-F)\n\
+"));
+ oputs (_("\
+ -i, --inode\n\
+ print the index number of each file\n\
+"));
+ oputs (_("\
+ -I, --ignore=PATTERN\n\
+ do not list implied entries matching shell PATTERN\n\
+"));
+ oputs (_("\
+ -k, --kibibytes\n\
+ default to 1024-byte blocks for file system usage;\n\
+ used only with -s and per directory totals\n\
+"));
+ oputs (_("\
+ -l\n\
+ use a long listing format\n\
+"));
+ oputs (_("\
+ -L, --dereference\n\
+ when showing file information for a symbolic link,\n\
+ show information for the file the link references\n\
+ rather than for the link itself\n\
+"));
+ oputs (_("\
+ -m\n\
+ fill width with a comma separated list of entries\n\
+"));
+ oputs (_("\
+ -n, --numeric-uid-gid\n\
+ like -l, but list numeric user and group IDs\n\
+"));
+ oputs (_("\
+ -N, --literal\n\
+ print entry names without quoting\n\
+"));
+ oputs (_("\
+ -o\n\
+ like -l, but do not list group information\n\
+"));
+ oputs (_("\
-p, --indicator-style=slash\n\
- append / indicator to directories\n\
-"), stdout);
- fputs (_("\
- -q, --hide-control-chars print ? instead of nongraphic characters\n\
-"), stdout);
- fputs (_("\
- --show-control-chars show nongraphic characters as-is (the default,\n\
- unless program is 'ls' and output is a terminal)\
-\n\
-\n\
-"), stdout);
- fputs (_("\
- -Q, --quote-name enclose entry names in double quotes\n\
-"), stdout);
- fputs (_("\
- --quoting-style=WORD use quoting style WORD for entry names:\n\
- literal, locale, shell, shell-always,\n\
- shell-escape, shell-escape-always, c, escape\n\
- (overrides QUOTING_STYLE environment variable)\n\
-\n\
-"), stdout);
- fputs (_("\
- -r, --reverse reverse order while sorting\n\
- -R, --recursive list subdirectories recursively\n\
- -s, --size print the allocated size of each file, in blocks\n\
-"), stdout);
- fputs (_("\
- -S sort by file size, largest first\n\
-"), stdout);
- fputs (_("\
- --sort=WORD change default 'name' sort to WORD:\n\
- none (-U), size (-S), time (-t),\n\
- version (-v), extension (-X), name, width\n\
-\n\
-"), stdout);
- fputs (_("\
- --time=WORD select which timestamp used to display or sort;\n\
- access time (-u): atime, access, use;\n\
- metadata change time (-c): ctime, status;\n\
- modified time (default): mtime, modification;\n\
- birth time: birth, creation;\n\
- with -l, WORD determines which time to show;\n\
- with --sort=time, sort by WORD (newest first)\n\
-\n\
-"), stdout);
- fputs (_("\
+ append / indicator to directories\n\
+"));
+ oputs (_("\
+ -q, --hide-control-chars\n\
+ print ? instead of nongraphic characters\n\
+"));
+ oputs (_("\
+ --show-control-chars\n\
+ show nongraphic characters as-is;\n\
+ the default, unless program is 'ls' and output is a terminal\n\
+"));
+ oputs (_("\
+ -Q, --quote-name\n\
+ enclose entry names in double quotes\n\
+"));
+ oputs (_("\
+ --quoting-style=WORD\n\
+ use quoting style WORD for entry names:\n\
+ literal, locale, shell, shell-always,\n\
+ shell-escape, shell-escape-always, c, escape\n\
+ (overrides QUOTING_STYLE environment variable)\n\
+"));
+ oputs (_("\
+ -r, --reverse\n\
+ reverse order while sorting\n\
+"));
+ oputs (_("\
+ -R, --recursive\n\
+ list subdirectories recursively\n\
+"));
+ oputs (_("\
+ -s, --size\n\
+ print the allocated size of each file, in blocks\n\
+"));
+ oputs (_("\
+ -S\n\
+ sort by file size, largest first\n\
+"));
+ oputs (_("\
+ --sort=WORD\n\
+ change default 'name' sort to WORD:\n\
+ none (-U), size (-S), time (-t),\n\
+ version (-v), extension (-X), name, width\n\
+"));
+ oputs (_("\
+ --time=WORD\n\
+ select which timestamp used to display or sort;\n\
+ access time (-u): atime, access, use;\n\
+ metadata change time (-c): ctime, status;\n\
+ modified time (default): mtime, modification;\n\
+ birth time: birth, creation;\n\
+ with -l, WORD determines which time to show;\n\
+ with --sort=time, sort by WORD (newest first)\n\
+"));
+ oputs (_("\
--time-style=TIME_STYLE\n\
- time/date format with -l; see TIME_STYLE below\n\
-"), stdout);
- fputs (_("\
- -t sort by time, newest first; see --time\n\
- -T, --tabsize=COLS assume tab stops at each COLS instead of 8\n\
-"), stdout);
- fputs (_("\
- -u with -lt: sort by, and show, access time;\n\
- with -l: show access time and sort by name;\n\
- otherwise: sort by access time, newest first\n\
-\n\
-"), stdout);
- fputs (_("\
- -U do not sort directory entries\n\
-"), stdout);
- fputs (_("\
- -v natural sort of (version) numbers within text\n\
-"), stdout);
- fputs (_("\
- -w, --width=COLS set output width to COLS. 0 means no limit\n\
- -x list entries by lines instead of by columns\n\
- -X sort alphabetically by entry extension\n\
- -Z, --context print any security context of each file\n\
- --zero end each output line with NUL, not newline\n\
- -1 list one file per line\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ time/date format with -l; see TIME_STYLE below\n\
+"));
+ oputs (_("\
+ -t\n\
+ sort by time, newest first; see --time\n\
+"));
+ oputs (_("\
+ -T, --tabsize=COLS\n\
+ assume tab stops at each COLS instead of 8\n\
+"));
+ oputs (_("\
+ -u\n\
+ with -lt: sort by, and show, access time;\n\
+ with -l: show access time and sort by name;\n\
+ otherwise: sort by access time, newest first\n\
+"));
+ oputs (_("\
+ -U\n\
+ do not sort directory entries\n\
+"));
+ oputs (_("\
+ -v\n\
+ natural sort of (version) numbers within text\n\
+"));
+ oputs (_("\
+ -w, --width=COLS\n\
+ set output width to COLS. 0 means no limit\n\
+"));
+ oputs (_("\
+ -x\n\
+ list entries by lines instead of by columns\n\
+"));
+ oputs (_("\
+ -X\n\
+ sort alphabetically by entry extension\n\
+"));
+ oputs (_("\
+ -Z, --context\n\
+ print any security context of each file\n\
+"));
+ oputs (_("\
+ --zero\n\
+ end each output line with NUL, not newline\n\
+"));
+ oputs (_("\
+ -1\n\
+ list one file per line\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_size_note ();
fputs (_("\
\n\
diff --git a/src/ls.h b/src/ls.h
index b35821197..23ddb9f5e 100644
--- a/src/ls.h
+++ b/src/ls.h
@@ -1,10 +1,13 @@
-/* This is for the 'ls' program. */
-#define LS_LS 1
+enum ls_modes
+{
+ /* This is for the 'ls' program. */
+ LS_LS,
-/* This is for the 'dir' program. */
-#define LS_MULTI_COL 2
+ /* This is for the 'dir' program. */
+ LS_MULTI_COL,
-/* This is for the 'vdir' program. */
-#define LS_LONG_FORMAT 3
+ /* This is for the 'vdir' program. */
+ LS_LONG_FORMAT
+};
-extern int ls_mode;
+extern enum ls_modes ls_mode;
diff --git a/src/make-prime-list.c b/src/make-prime-list.c
index 5b7208660..ae954f1da 100644
--- a/src/make-prime-list.c
+++ b/src/make-prime-list.c
@@ -3,7 +3,7 @@
Contributed to the GNU project by Torbjörn Granlund and Niels Möller
Contains code from GNU MP.
-Copyright 2012-2025 Free Software Foundation, Inc.
+Copyright 2012-2026 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
diff --git a/src/mkdir.c b/src/mkdir.c
index df4c81976..d52a498e7 100644
--- a/src/mkdir.c
+++ b/src/mkdir.c
@@ -1,5 +1,5 @@
/* mkdir -- make directories
- Copyright (C) 1990-2025 Free Software Foundation, Inc.
+ Copyright (C) 1990-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -39,12 +39,12 @@
static struct option const longopts[] =
{
{GETOPT_SELINUX_CONTEXT_OPTION_DECL},
- {"mode", required_argument, nullptr, 'm'},
- {"parents", no_argument, nullptr, 'p'},
- {"verbose", no_argument, nullptr, 'v'},
+ {"mode", required_argument, NULL, 'm'},
+ {"parents", no_argument, NULL, 'p'},
+ {"verbose", no_argument, NULL, 'v'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -61,20 +61,31 @@ Create the DIRECTORY(ies), if they do not already exist.\n\
emit_mandatory_arg_note ();
- fputs (_("\
- -m, --mode=MODE set file mode (as in chmod), not a=rwx - umask\n\
- -p, --parents no error if existing, make parent directories as needed,\n\
- with their file modes unaffected by any -m option\n\
- -v, --verbose print a message for each created directory\n\
-"), stdout);
- fputs (_("\
- -Z set SELinux security context of each created directory\n\
- to the default type\n\
- --context[=CTX] like -Z, or if CTX is specified then set the SELinux\n\
- or SMACK security context to CTX\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -m, --mode=MODE\n\
+ set file mode (as in chmod), not a=rwx - umask\n\
+"));
+ oputs (_("\
+ -p, --parents\n\
+ no error if existing, make parent directories as needed,\n\
+ with their file modes unaffected by any -m option\n\
+"));
+ oputs (_("\
+ -v, --verbose\n\
+ print a message for each created directory\n\
+"));
+ oputs (_("\
+ -Z\n\
+ set SELinux security context of each created directory\n\
+ to the default type\n\
+"));
+ oputs (_("\
+ --context[=CTX]\n\
+ like -Z, or if CTX is specified then set the\n\
+ SELinux or SMACK security context to CTX\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -83,7 +94,7 @@ Create the DIRECTORY(ies), if they do not already exist.\n\
/* Options passed to subsidiary functions. */
struct mkdir_options
{
- /* Function to make an ancestor, or nullptr if ancestors should not be
+ /* Function to make an ancestor, or NULL if ancestors should not be
made. */
int (*make_ancestor_function) (char const *, char const *, void *);
@@ -190,16 +201,16 @@ process_dir (char *dir, struct savewd *wd, void *options)
int
main (int argc, char **argv)
{
- char const *specified_mode = nullptr;
+ char const *specified_mode = NULL;
int optc;
- char const *scontext = nullptr;
+ char const *scontext = NULL;
struct mkdir_options options;
- options.make_ancestor_function = nullptr;
+ options.make_ancestor_function = NULL;
options.mode = S_IRWXUGO;
options.mode_bits = 0;
- options.created_directory_format = nullptr;
- options.set_security_context = nullptr;
+ options.created_directory_format = NULL;
+ options.set_security_context = NULL;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -209,7 +220,7 @@ main (int argc, char **argv)
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "pm:vZ", longopts, nullptr)) != -1)
+ while ((optc = getopt_long (argc, argv, "pm:vZ", longopts, NULL)) != -1)
{
switch (optc)
{
@@ -235,7 +246,7 @@ main (int argc, char **argv)
else
{
options.set_security_context = selabel_open (SELABEL_CTX_FILE,
- nullptr, 0);
+ NULL, 0);
if (! options.set_security_context)
error (0, errno, _("warning: ignoring --context"));
}
diff --git a/src/mkfifo.c b/src/mkfifo.c
index 52bfa566a..12d9a423e 100644
--- a/src/mkfifo.c
+++ b/src/mkfifo.c
@@ -1,5 +1,5 @@
/* mkfifo -- make fifo's (named pipes)
- Copyright (C) 1990-2025 Free Software Foundation, Inc.
+ Copyright (C) 1990-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -36,10 +36,10 @@
static struct option const longopts[] =
{
{GETOPT_SELINUX_CONTEXT_OPTION_DECL},
- {"mode", required_argument, nullptr, 'm'},
+ {"mode", required_argument, NULL, 'm'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -56,16 +56,21 @@ Create named pipes (FIFOs) with the given NAMEs.\n\
emit_mandatory_arg_note ();
- fputs (_("\
- -m, --mode=MODE set file permission bits to MODE, not a=rw - umask\n\
-"), stdout);
- fputs (_("\
- -Z set the SELinux security context to default type\n\
- --context[=CTX] like -Z, or if CTX is specified then set the SELinux\n\
- or SMACK security context to CTX\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -m, --mode=MODE\n\
+ set file permission bits to MODE, not a=rw - umask\n\
+"));
+ oputs (_("\
+ -Z\n\
+ set the SELinux security context to default type\n\
+"));
+ oputs (_("\
+ --context[=CTX]\n\
+ like -Z, or if CTX is specified then set the\n\
+ SELinux or SMACK security context to CTX\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -74,12 +79,9 @@ Create named pipes (FIFOs) with the given NAMEs.\n\
int
main (int argc, char **argv)
{
- mode_t newmode;
- char const *specified_mode = nullptr;
- int exit_status = EXIT_SUCCESS;
- int optc;
- char const *scontext = nullptr;
- struct selabel_handle *set_security_context = nullptr;
+ char const *specified_mode = NULL;
+ char const *scontext = NULL;
+ struct selabel_handle *set_security_context = NULL;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -89,7 +91,8 @@ main (int argc, char **argv)
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "m:Z", longopts, nullptr)) != -1)
+ int optc;
+ while ((optc = getopt_long (argc, argv, "m:Z", longopts, NULL)) != -1)
{
switch (optc)
{
@@ -109,7 +112,7 @@ main (int argc, char **argv)
else
{
set_security_context = selabel_open (SELABEL_CTX_FILE,
- nullptr, 0);
+ NULL, 0);
if (! set_security_context)
error (0, errno, _("warning: ignoring --context"));
}
@@ -148,22 +151,22 @@ main (int argc, char **argv)
quote (scontext));
}
- newmode = MODE_RW_UGO;
+ mode_t newmode = MODE_RW_UGO;
if (specified_mode)
{
- mode_t umask_value;
struct mode_change *change = mode_compile (specified_mode);
if (!change)
error (EXIT_FAILURE, 0, _("invalid mode"));
- umask_value = umask (0);
+ mode_t umask_value = umask (0);
umask (umask_value);
- newmode = mode_adjust (newmode, false, umask_value, change, nullptr);
+ newmode = mode_adjust (newmode, false, umask_value, change, NULL);
free (change);
if (newmode & ~S_IRWXUGO)
error (EXIT_FAILURE, 0,
_("mode must specify only file permission bits"));
}
+ int exit_status = EXIT_SUCCESS;
for (; optind < argc; ++optind)
{
if (set_security_context)
diff --git a/src/mknod.c b/src/mknod.c
index 6bfa0f7f7..59b844fcd 100644
--- a/src/mknod.c
+++ b/src/mknod.c
@@ -1,5 +1,5 @@
/* mknod -- make special files
- Copyright (C) 1990-2025 Free Software Foundation, Inc.
+ Copyright (C) 1990-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -37,10 +37,10 @@
static struct option const longopts[] =
{
{GETOPT_SELINUX_CONTEXT_OPTION_DECL},
- {"mode", required_argument, nullptr, 'm'},
+ {"mode", required_argument, NULL, 'm'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -58,16 +58,21 @@ Create the special file NAME of the given TYPE.\n\
emit_mandatory_arg_note ();
- fputs (_("\
- -m, --mode=MODE set file permission bits to MODE, not a=rw - umask\n\
-"), stdout);
- fputs (_("\
- -Z set the SELinux security context to default type\n\
- --context[=CTX] like -Z, or if CTX is specified then set the SELinux\n\
- or SMACK security context to CTX\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -m, --mode=MODE\n\
+ set file permission bits to MODE, not a=rw - umask\n\
+"));
+ oputs (_("\
+ -Z\n\
+ set the SELinux security context to default type\n\
+"));
+ oputs (_("\
+ --context[=CTX]\n\
+ like -Z, or if CTX is specified then set the\n\
+ SELinux or SMACK security context to CTX\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
Both MAJOR and MINOR must be specified when TYPE is b, c, or u, and they\n\
@@ -90,13 +95,9 @@ otherwise, as decimal. TYPE may be:\n\
int
main (int argc, char **argv)
{
- mode_t newmode;
- char const *specified_mode = nullptr;
- int optc;
- size_t expected_operands;
- mode_t node_type;
- char const *scontext = nullptr;
- struct selabel_handle *set_security_context = nullptr;
+ char const *specified_mode = NULL;
+ char const *scontext = NULL;
+ struct selabel_handle *set_security_context = NULL;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -106,7 +107,8 @@ main (int argc, char **argv)
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "m:Z", longopts, nullptr)) != -1)
+ int optc;
+ while ((optc = getopt_long (argc, argv, "m:Z", longopts, NULL)) != -1)
{
switch (optc)
{
@@ -126,7 +128,7 @@ main (int argc, char **argv)
else
{
set_security_context = selabel_open (SELABEL_CTX_FILE,
- nullptr, 0);
+ NULL, 0);
if (! set_security_context)
error (0, errno, _("warning: ignoring --context"));
}
@@ -145,16 +147,15 @@ main (int argc, char **argv)
}
}
- newmode = MODE_RW_UGO;
+ mode_t newmode = MODE_RW_UGO;
if (specified_mode)
{
- mode_t umask_value;
struct mode_change *change = mode_compile (specified_mode);
if (!change)
error (EXIT_FAILURE, 0, _("invalid mode"));
- umask_value = umask (0);
+ mode_t umask_value = umask (0);
umask (umask_value);
- newmode = mode_adjust (newmode, false, umask_value, change, nullptr);
+ newmode = mode_adjust (newmode, false, umask_value, change, NULL);
free (change);
if (newmode & ~S_IRWXUGO)
error (EXIT_FAILURE, 0,
@@ -164,9 +165,9 @@ main (int argc, char **argv)
/* If the number of arguments is 0 or 1,
or (if it's 2 or more and the second one starts with 'p'), then there
must be exactly two operands. Otherwise, there must be four. */
- expected_operands = (argc <= optind
- || (optind + 1 < argc && argv[optind + 1][0] == 'p')
- ? 2 : 4);
+ int expected_operands = (argc <= optind
+ || (optind + 1 < argc && argv[optind + 1][0] == 'p')
+ ? 2 : 4);
if (argc - optind < expected_operands)
{
@@ -207,6 +208,7 @@ main (int argc, char **argv)
/* Only check the first character, to allow mnemonic usage like
'mknod /dev/rst0 character 18 0'. */
+ mode_t node_type;
switch (argv[optind + 1][0])
{
case 'b': /* 'block' or 'buffered' */
@@ -229,21 +231,20 @@ main (int argc, char **argv)
block_or_character:
{
char const *s_major = argv[optind + 2];
- char const *s_minor = argv[optind + 3];
- uintmax_t i_major, i_minor;
- dev_t device;
-
- if (xstrtoumax (s_major, nullptr, 0, &i_major, "") != LONGINT_OK
+ uintmax_t i_major;
+ if (xstrtoumax (s_major, NULL, 0, &i_major, "") != LONGINT_OK
|| i_major != (major_t) i_major)
error (EXIT_FAILURE, 0,
_("invalid major device number %s"), quote (s_major));
- if (xstrtoumax (s_minor, nullptr, 0, &i_minor, "") != LONGINT_OK
+ char const *s_minor = argv[optind + 3];
+ uintmax_t i_minor;
+ if (xstrtoumax (s_minor, NULL, 0, &i_minor, "") != LONGINT_OK
|| i_minor != (minor_t) i_minor)
error (EXIT_FAILURE, 0,
_("invalid minor device number %s"), quote (s_minor));
- device = makedev (i_major, i_minor);
+ dev_t device = makedev (i_major, i_minor);
#ifdef NODEV
if (device == NODEV)
error (EXIT_FAILURE, 0, _("invalid device %s %s"),
diff --git a/src/mktemp.c b/src/mktemp.c
index 706c6518e..1fa797266 100644
--- a/src/mktemp.c
+++ b/src/mktemp.c
@@ -1,5 +1,5 @@
/* Create a temporary file or directory, safely.
- Copyright (C) 2007-2025 Free Software Foundation, Inc.
+ Copyright (C) 2007-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -45,14 +45,14 @@ enum
static struct option const longopts[] =
{
- {"directory", no_argument, nullptr, 'd'},
- {"quiet", no_argument, nullptr, 'q'},
- {"dry-run", no_argument, nullptr, 'u'},
- {"suffix", required_argument, nullptr, SUFFIX_OPTION},
- {"tmpdir", optional_argument, nullptr, 'p'},
+ {"directory", no_argument, NULL, 'd'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"dry-run", no_argument, NULL, 'u'},
+ {"suffix", required_argument, NULL, SUFFIX_OPTION},
+ {"tmpdir", optional_argument, NULL, 'p'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -72,29 +72,39 @@ If TEMPLATE is not specified, use tmp.XXXXXXXXXX, and --tmpdir is implied.\n\
Files are created u+rw, and directories u+rwx, minus umask restrictions.\n\
"), stdout);
fputs ("\n", stdout);
- fputs (_("\
- -d, --directory create a directory, not a file\n\
- -u, --dry-run do not create anything; merely print a name (unsafe)\n\
- -q, --quiet suppress diagnostics about file/dir-creation failure\n\
-"), stdout);
- fputs (_("\
- --suffix=SUFF append SUFF to TEMPLATE; SUFF must not contain a slash.\n\
- This option is implied if TEMPLATE does not end in X\n\
-"), stdout);
- fputs (_("\
- -p DIR, --tmpdir[=DIR] interpret TEMPLATE relative to DIR; if DIR is not\n\
- specified, use $TMPDIR if set, else /tmp. With\n\
- this option, TEMPLATE must not be an absolute name;\n\
- unlike with -t, TEMPLATE may contain slashes, but\n\
- mktemp creates only the final component\n\
-"), stdout);
- fputs (_("\
- -t interpret TEMPLATE as a single file name component,\n\
- relative to a directory: $TMPDIR, if set; else the\n\
- directory specified via -p; else /tmp [deprecated]\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -d, --directory\n\
+ create a directory, not a file\n\
+"));
+ oputs (_("\
+ -u, --dry-run\n\
+ do not create anything; merely print a name (unsafe)\n\
+"));
+ oputs (_("\
+ -q, --quiet\n\
+ suppress diagnostics about file/dir-creation failure\n\
+"));
+ oputs (_("\
+ --suffix=SUFF\n\
+ append SUFF to TEMPLATE; SUFF must not contain a slash.\n\
+ This option is implied if TEMPLATE does not end in X\n\
+"));
+ oputs (_("\
+ -p DIR, --tmpdir[=DIR]\n\
+ interpret TEMPLATE relative to DIR;\n\
+ if DIR is not specified, use $TMPDIR if set, else /tmp.\n\
+ With this option, TEMPLATE must not be an absolute name;\n\
+ unlike with -t, TEMPLATE may contain slashes,\n\
+ but mktemp creates only the final component\n\
+"));
+ oputs (_("\
+ -t\n\
+ interpret TEMPLATE as a single file name component,\n\
+ relative to a directory: $TMPDIR, if set;\n\
+ else the directory specified via -p; else /tmp [deprecated]\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
@@ -144,11 +154,11 @@ int
main (int argc, char **argv)
{
char const *dest_dir;
- char const *dest_dir_arg = nullptr;
+ char const *dest_dir_arg = NULL;
bool suppress_file_err = false;
int c;
char *template;
- char *suffix = nullptr;
+ char *suffix = NULL;
bool use_dest_dir = false;
bool deprecated_t_option = false;
bool create_directory = false;
@@ -156,7 +166,7 @@ main (int argc, char **argv)
int status = EXIT_SUCCESS;
size_t x_count;
size_t suffix_len;
- char *dest_name = nullptr;
+ char *dest_name = NULL;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -166,7 +176,7 @@ main (int argc, char **argv)
atexit (maybe_close_stdout);
- while ((c = getopt_long (argc, argv, "dp:qtuV", longopts, nullptr)) != -1)
+ while ((c = getopt_long (argc, argv, "dp:qtuV", longopts, NULL)) != -1)
{
switch (c)
{
@@ -296,7 +306,7 @@ main (int argc, char **argv)
quote (template));
}
- dest_name = file_name_concat (dest_dir, template, nullptr);
+ dest_name = file_name_concat (dest_dir, template, NULL);
free (template);
template = dest_name;
/* Note that suffix is now invalid. */
@@ -337,7 +347,7 @@ main (int argc, char **argv)
if (!dry_run && ((stdout_closed = true), close_stream (stdout) != 0))
{
int saved_errno = errno;
- remove (dest_name);
+ (create_directory ? rmdir : unlink) (dest_name);
if (!suppress_file_err)
error (0, saved_errno, _("write error"));
status = EXIT_FAILURE;
diff --git a/src/mv.c b/src/mv.c
index cf1ac56e8..cd6aab473 100644
--- a/src/mv.c
+++ b/src/mv.c
@@ -1,5 +1,5 @@
/* mv -- move or rename files
- Copyright (C) 1986-2025 Free Software Foundation, Inc.
+ Copyright (C) 1986-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -55,7 +55,7 @@ enum
static char const *const update_type_string[] =
{
- "all", "none", "none-fail", "older", nullptr
+ "all", "none", "none-fail", "older", NULL
};
static enum Update_type const update_type[] =
{
@@ -65,24 +65,24 @@ ARGMATCH_VERIFY (update_type_string, update_type);
static struct option const long_options[] =
{
- {"backup", optional_argument, nullptr, 'b'},
- {"context", no_argument, nullptr, 'Z'},
- {"debug", no_argument, nullptr, DEBUG_OPTION},
- {"exchange", no_argument, nullptr, EXCHANGE_OPTION},
- {"force", no_argument, nullptr, 'f'},
- {"interactive", no_argument, nullptr, 'i'},
- {"no-clobber", no_argument, nullptr, 'n'}, /* Deprecated. */
- {"no-copy", no_argument, nullptr, NO_COPY_OPTION},
- {"no-target-directory", no_argument, nullptr, 'T'},
- {"strip-trailing-slashes", no_argument, nullptr,
+ {"backup", optional_argument, NULL, 'b'},
+ {"context", no_argument, NULL, 'Z'},
+ {"debug", no_argument, NULL, DEBUG_OPTION},
+ {"exchange", no_argument, NULL, EXCHANGE_OPTION},
+ {"force", no_argument, NULL, 'f'},
+ {"interactive", no_argument, NULL, 'i'},
+ {"no-clobber", no_argument, NULL, 'n'}, /* Deprecated. */
+ {"no-copy", no_argument, NULL, NO_COPY_OPTION},
+ {"no-target-directory", no_argument, NULL, 'T'},
+ {"strip-trailing-slashes", no_argument, NULL,
STRIP_TRAILING_SLASHES_OPTION},
- {"suffix", required_argument, nullptr, 'S'},
- {"target-directory", required_argument, nullptr, 't'},
- {"update", optional_argument, nullptr, 'u'},
- {"verbose", no_argument, nullptr, 'v'},
+ {"suffix", required_argument, NULL, 'S'},
+ {"target-directory", required_argument, NULL, 't'},
+ {"update", optional_argument, NULL, 'u'},
+ {"verbose", no_argument, NULL, 'v'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
static void
@@ -109,7 +109,7 @@ rm_option_init (struct rm_options *x)
{
static struct dev_ino dev_ino_buf;
x->root_dev_ino = get_root_dev_ino (&dev_ino_buf);
- if (x->root_dev_ino == nullptr)
+ if (x->root_dev_ino == NULL)
error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
quoteaf ("/"));
}
@@ -139,7 +139,7 @@ cp_option_init (struct cp_options *x)
x->preserve_timestamps = true;
x->explicit_no_preserve_mode= false;
x->preserve_security_context = selinux_enabled;
- x->set_security_context = nullptr;
+ x->set_security_context = NULL;
x->reduce_diagnostics = false;
x->data_copy_required = true;
x->require_preserve = false; /* FIXME: maybe make this an option */
@@ -156,8 +156,8 @@ cp_option_init (struct cp_options *x)
x->open_dangling_dest_symlink = false;
x->update = UPDATE_ALL;
x->verbose = false;
- x->dest_info = nullptr;
- x->src_info = nullptr;
+ x->dest_info = NULL;
+ x->src_info = NULL;
}
/* Move SOURCE onto DEST aka DEST_DIRFD+DEST_RELNAME.
@@ -190,14 +190,14 @@ do_move (char const *source, char const *dest,
copied-into-self directory, DEST ('b/b' in the example),
and failing. */
- dir_to_remove = nullptr;
+ dir_to_remove = NULL;
ok = false;
}
else if (rename_succeeded)
{
/* No need to remove anything. SOURCE was successfully
renamed to DEST. Or the user declined to rename a file. */
- dir_to_remove = nullptr;
+ dir_to_remove = NULL;
}
else
{
@@ -226,18 +226,14 @@ do_move (char const *source, char const *dest,
dir_to_remove = source;
}
- if (dir_to_remove != nullptr)
+ if (dir_to_remove != NULL)
{
struct rm_options rm_options;
- enum RM_status status;
- char const *dir[2];
-
rm_option_init (&rm_options);
rm_options.verbose = x->verbose;
- dir[0] = dir_to_remove;
- dir[1] = nullptr;
+ char const *dir[2] = { dir_to_remove, NULL };
- status = rm ((void *) dir, &rm_options);
+ enum RM_status status = rm ((void *) dir, &rm_options);
affirm (VALID_STATUS (status));
if (status == RM_ERROR)
ok = false;
@@ -266,45 +262,76 @@ Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\
emit_mandatory_arg_note ();
+ oputs (_("\
+ --backup[=CONTROL]\n\
+ make a backup of each existing destination file\n\
+"));
+ oputs (_("\
+ -b\n\
+ like --backup but does not accept an argument\n\
+"));
+ oputs (_("\
+ --debug\n\
+ explain how a file is copied. Implies -v\n\
+"));
+ oputs (_("\
+ --exchange\n\
+ exchange source and destination\n\
+"));
+ oputs (_("\
+ -f, --force\n\
+ do not prompt before overwriting\n\
+"));
+ oputs (_("\
+ -i, --interactive\n\
+ prompt before overwrite\n\
+"));
+ oputs (_("\
+ -n, --no-clobber\n\
+ do not overwrite an existing file\n\
+"));
fputs (_("\
- --backup[=CONTROL] make a backup of each existing destination file\
-\n\
- -b like --backup but does not accept an argument\n\
-"), stdout);
- fputs (_("\
- --debug explain how a file is copied. Implies -v\n\
-"), stdout);
- fputs (_("\
- --exchange exchange source and destination\n\
-"), stdout);
- fputs (_("\
- -f, --force do not prompt before overwriting\n\
- -i, --interactive prompt before overwrite\n\
- -n, --no-clobber do not overwrite an existing file\n\
If you specify more than one of -i, -f, -n, only the final one takes effect.\n\
"), stdout);
- fputs (_("\
- --no-copy do not copy if renaming fails\n\
- --strip-trailing-slashes remove any trailing slashes from each SOURCE\n\
- argument\n\
- -S, --suffix=SUFFIX override the usual backup suffix\n\
-"), stdout);
- fputs (_("\
- -t, --target-directory=DIRECTORY move all SOURCE arguments into DIRECTORY\n\
- -T, --no-target-directory treat DEST as a normal file\n\
-"), stdout);
- fputs (_("\
- --update[=UPDATE] control which existing files are updated;\n\
- UPDATE={all,none,none-fail,older(default)}\n\
- -u equivalent to --update[=older]. See below\n\
-"), stdout);
- fputs (_("\
- -v, --verbose explain what is being done\n\
- -Z, --context set SELinux security context of destination\n\
- file to default type\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ --no-copy\n\
+ do not copy if renaming fails\n\
+"));
+ oputs (_("\
+ --strip-trailing-slashes\n\
+ remove any trailing slashes from each SOURCE argument\n\
+"));
+ oputs (_("\
+ -S, --suffix=SUFFIX\n\
+ override the usual backup suffix\n\
+"));
+ oputs (_("\
+ -t, --target-directory=DIRECTORY\n\
+ move all SOURCE arguments into DIRECTORY\n\
+"));
+ oputs (_("\
+ -T, --no-target-directory\n\
+ treat DEST as a normal file\n\
+"));
+ oputs (_("\
+ --update[=UPDATE]\n\
+ control which existing files are updated;\n\
+ UPDATE={all,none,none-fail,older(default)}\n\
+"));
+ oputs (_("\
+ -u\n\
+ equivalent to --update[=older]. See below\n\
+"));
+ oputs (_("\
+ -v, --verbose\n\
+ explain what is being done\n\
+"));
+ oputs (_("\
+ -Z, --context\n\
+ set SELinux security context of destination file to default type\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_update_parameters_note ();
emit_backup_suffix_note ();
emit_ancillary_info (PROGRAM_NAME);
@@ -315,17 +342,13 @@ If you specify more than one of -i, -f, -n, only the final one takes effect.\n\
int
main (int argc, char **argv)
{
- int c;
- bool ok;
bool make_backups = false;
- char const *backup_suffix = nullptr;
- char *version_control_string = nullptr;
+ char const *backup_suffix = NULL;
+ char *version_control_string = NULL;
struct cp_options x;
bool remove_trailing_slashes = false;
- char const *target_directory = nullptr;
+ char const *target_directory = NULL;
bool no_target_directory = false;
- int n_files;
- char **file;
bool selinux_enabled = (0 < is_selinux_enabled ());
initialize_main (&argc, &argv);
@@ -341,7 +364,8 @@ main (int argc, char **argv)
/* Try to disable the ability to unlink a directory. */
priv_set_remove_linkdir ();
- while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, nullptr))
+ int c;
+ while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, NULL))
!= -1)
{
switch (c)
@@ -400,7 +424,7 @@ main (int argc, char **argv)
{
x.preserve_security_context = false;
x.set_security_context = selabel_open (SELABEL_CTX_FILE,
- nullptr, 0);
+ NULL, 0);
if (! x.set_security_context)
error (0, errno, _("warning: ignoring --context"));
}
@@ -412,8 +436,8 @@ main (int argc, char **argv)
}
}
- n_files = argc - optind;
- file = argv + optind;
+ int n_files = argc - optind;
+ char **file = argv + optind;
if (n_files <= !target_directory)
{
@@ -516,6 +540,7 @@ main (int argc, char **argv)
hash_init ();
+ bool ok;
if (target_directory)
{
/* Initialize the hash table only if we'll need it.
diff --git a/src/nice.c b/src/nice.c
index 1c3f2a038..71ebe2dad 100644
--- a/src/nice.c
+++ b/src/nice.c
@@ -1,5 +1,5 @@
/* nice -- run a program with modified niceness
- Copyright (C) 1990-2025 Free Software Foundation, Inc.
+ Copyright (C) 1990-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -56,10 +56,10 @@
static struct option const longopts[] =
{
- {"adjustment", required_argument, nullptr, 'n'},
+ {"adjustment", required_argument, NULL, 'n'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -79,11 +79,12 @@ With no COMMAND, print the current niceness. Niceness values range from\n\
emit_mandatory_arg_note ();
- fputs (_("\
- -n, --adjustment=N add integer N to the niceness (default 10)\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -n, --adjustment=N\n\
+ add integer N to the niceness (default 10)\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
emit_exec_status (PROGRAM_NAME);
emit_ancillary_info (PROGRAM_NAME);
@@ -102,7 +103,7 @@ main (int argc, char **argv)
{
int current_niceness;
int adjustment = 10;
- char const *adjustment_given = nullptr;
+ char const *adjustment_given = NULL;
bool ok;
int i;
@@ -136,7 +137,7 @@ main (int argc, char **argv)
/* Initialize getopt_long's internal state. */
optind = 0;
- c = getopt_long (fake_argc, fake_argv, "+n:", longopts, nullptr);
+ c = getopt_long (fake_argc, fake_argv, "+n:", longopts, NULL);
i += optind - 1;
switch (c)
@@ -167,12 +168,46 @@ main (int argc, char **argv)
/* If the requested adjustment is outside the valid range,
silently bring it to just within range; this mimics what
"setpriority" and "nice" do. */
+#if (defined __gnu_hurd__ \
+ && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 43)))
+ /* GNU/Hurd's nice(2) only supported 0 to (2 * NZERO - 2) niceness
+ until glibc 2.43. */
+ enum { MIN_ADJUSTMENT = 0, MAX_ADJUSTMENT = 2 * NZERO - 2 };
+#else
enum { MIN_ADJUSTMENT = 1 - 2 * NZERO, MAX_ADJUSTMENT = 2 * NZERO - 1 };
+#endif
long int tmp;
- if (LONGINT_OVERFLOW < xstrtol (adjustment_given, nullptr, 10, &tmp, ""))
+ if (LONGINT_OVERFLOW < xstrtol (adjustment_given, NULL, 10, &tmp, ""))
error (EXIT_CANCELED, 0, _("invalid adjustment %s"),
quote (adjustment_given));
+#if (defined __gnu_hurd__ \
+ && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 43)))
+ /* GNU/Hurd's nice(2) also did not clamp the new niceness into the
+ supported range until glibc 2.43.
+ See <https://sourceware.org/PR33614>. */
+ errno = 0;
+ current_niceness = GET_NICENESS ();
+ if (current_niceness == -1 && errno != 0)
+ error (EXIT_CANCELED, errno, _("cannot get niceness"));
+ if (tmp < 0)
+ {
+ int sum;
+ if (ckd_add (&sum, current_niceness, tmp) || sum < MIN_ADJUSTMENT)
+ adjustment = MIN_ADJUSTMENT - current_niceness;
+ else
+ adjustment = tmp;
+ }
+ else
+ {
+ int sum;
+ if (ckd_add (&sum, current_niceness, tmp) || MAX_ADJUSTMENT < sum)
+ adjustment = MAX_ADJUSTMENT - current_niceness;
+ else
+ adjustment = tmp;
+ }
+#else
adjustment = MAX (MIN_ADJUSTMENT, MIN (tmp, MAX_ADJUSTMENT));
+#endif
}
if (i == argc)
diff --git a/src/nl.c b/src/nl.c
index 706075162..2d287b36e 100644
--- a/src/nl.c
+++ b/src/nl.c
@@ -1,5 +1,5 @@
/* nl -- number lines of files
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -29,6 +29,7 @@
#include "fadvise.h"
#include "linebuffer.h"
+#include "mcel.h"
#include "quote.h"
#include "xdectoint.h"
@@ -52,7 +53,7 @@ static char const FORMAT_RIGHT_LZ[] = "%0*jd%s";
static char const FORMAT_LEFT[] = "%-*jd%s";
/* Default section delimiter characters. */
-static char DEFAULT_SECTION_DELIMITERS[] = "\\:";
+static char DEFAULT_SECTION_DELIMITERS[MCEL_LEN_MAX * 2 + 1] = "\\:";
/* Types of input lines: either one of the section delimiters,
or text to output. */
@@ -88,7 +89,7 @@ static char header_fastmap[UCHAR_MAX + 1];
static char footer_fastmap[UCHAR_MAX + 1];
/* Pointer to current regex, if any. */
-static struct re_pattern_buffer *current_regex = nullptr;
+static struct re_pattern_buffer *current_regex = NULL;
/* Separator string to print after line number (-s). */
static char const *separator_str = "\t";
@@ -96,20 +97,23 @@ static char const *separator_str = "\t";
/* Input section delimiter string (-d). */
static char *section_del = DEFAULT_SECTION_DELIMITERS;
+/* Input section delimiter length. */
+static size_t section_del_len;
+
/* Header delimiter string. */
-static char *header_del = nullptr;
+static char *header_del = NULL;
/* Header section delimiter length. */
static size_t header_del_len;
/* Body delimiter string. */
-static char *body_del = nullptr;
+static char *body_del = NULL;
/* Body section delimiter length. */
static size_t body_del_len;
/* Footer delimiter string. */
-static char *footer_del = nullptr;
+static char *footer_del = NULL;
/* Footer section delimiter length. */
static size_t footer_del_len;
@@ -118,7 +122,7 @@ static size_t footer_del_len;
static struct linebuffer line_buf;
/* printf format string for unnumbered lines. */
-static char *print_no_line_fmt = nullptr;
+static char *print_no_line_fmt = NULL;
/* Starting line number on each page (-v). */
static intmax_t starting_line_number = 1;
@@ -149,20 +153,20 @@ static bool have_read_stdin;
static struct option const longopts[] =
{
- {"header-numbering", required_argument, nullptr, 'h'},
- {"body-numbering", required_argument, nullptr, 'b'},
- {"footer-numbering", required_argument, nullptr, 'f'},
- {"starting-line-number", required_argument, nullptr, 'v'},
- {"line-increment", required_argument, nullptr, 'i'},
- {"no-renumber", no_argument, nullptr, 'p'},
- {"join-blank-lines", required_argument, nullptr, 'l'},
- {"number-separator", required_argument, nullptr, 's'},
- {"number-width", required_argument, nullptr, 'w'},
- {"number-format", required_argument, nullptr, 'n'},
- {"section-delimiter", required_argument, nullptr, 'd'},
+ {"header-numbering", required_argument, NULL, 'h'},
+ {"body-numbering", required_argument, NULL, 'b'},
+ {"footer-numbering", required_argument, NULL, 'f'},
+ {"starting-line-number", required_argument, NULL, 'v'},
+ {"line-increment", required_argument, NULL, 'i'},
+ {"no-renumber", no_argument, NULL, 'p'},
+ {"join-blank-lines", required_argument, NULL, 'l'},
+ {"number-separator", required_argument, NULL, 's'},
+ {"number-width", required_argument, NULL, 'w'},
+ {"number-format", required_argument, NULL, 'n'},
+ {"section-delimiter", required_argument, NULL, 'd'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* Print a usage message and quit. */
@@ -185,25 +189,41 @@ Write each FILE to standard output, with line numbers added.\n\
emit_stdin_note ();
emit_mandatory_arg_note ();
- fputs (_("\
+ oputs (_("\
-b, --body-numbering=STYLE use STYLE for numbering body lines\n\
+"));
+ oputs (_("\
-d, --section-delimiter=CC use CC for logical page delimiters\n\
+"));
+ oputs (_("\
-f, --footer-numbering=STYLE use STYLE for numbering footer lines\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-h, --header-numbering=STYLE use STYLE for numbering header lines\n\
+"));
+ oputs (_("\
-i, --line-increment=NUMBER line number increment at each line\n\
+"));
+ oputs (_("\
-l, --join-blank-lines=NUMBER group of NUMBER empty lines counted as one\n\
+"));
+ oputs (_("\
-n, --number-format=FORMAT insert line numbers according to FORMAT\n\
+"));
+ oputs (_("\
-p, --no-renumber do not reset line numbers for each section\n\
+"));
+ oputs (_("\
-s, --number-separator=STRING add STRING after (possible) line number\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-v, --starting-line-number=NUMBER first line number for each section\n\
+"));
+ oputs (_("\
-w, --number-width=NUMBER use NUMBER columns for line numbers\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
Default options are: -bt -d'\\:' -fn -hn -i1 -l1 -n'rn' -s<TAB> -v1 -w6\n\
@@ -256,10 +276,10 @@ build_type_arg (char const **typep,
break;
case 'p':
*typep = optarg++;
- regexp->buffer = nullptr;
+ regexp->buffer = NULL;
regexp->allocated = 0;
regexp->fastmap = fastmap;
- regexp->translate = nullptr;
+ regexp->translate = NULL;
re_syntax_options =
RE_SYNTAX_POSIX_BASIC & ~RE_CONTEXT_INVALID_DUP & ~RE_NO_EMPTY_RANGES;
errmsg = re_compile_pattern (optarg, strlen (optarg), regexp);
@@ -364,7 +384,7 @@ proc_text (void)
break;
case 'p':
switch (re_search (current_regex, line_buf.buffer, line_buf.length - 1,
- 0, line_buf.length - 1, nullptr))
+ 0, line_buf.length - 1, NULL))
{
case -2:
error (EXIT_FAILURE, errno, _("error in regular expression search"));
@@ -388,8 +408,8 @@ check_section (void)
{
size_t len = line_buf.length - 1;
- if (len < 2 || footer_del_len < 2
- || !memeq (line_buf.buffer, section_del, 2))
+ if (len < section_del_len || footer_del_len < section_del_len
+ || !memeq (line_buf.buffer, section_del, section_del_len))
return Text;
if (len == header_del_len
&& memeq (line_buf.buffer, header_del, header_del_len))
@@ -448,7 +468,7 @@ nl_file (char const *file)
else
{
stream = fopen (file, "r");
- if (stream == nullptr)
+ if (stream == NULL)
{
error (0, errno, "%s", quotef (file));
return false;
@@ -489,10 +509,8 @@ main (int argc, char **argv)
atexit (close_stdout);
- have_read_stdin = false;
-
while ((c = getopt_long (argc, argv, "h:b:f:v:i:pl:s:w:n:d:", longopts,
- nullptr))
+ NULL))
!= -1)
{
switch (c)
@@ -562,14 +580,25 @@ main (int argc, char **argv)
break;
case 'd':
len = strlen (optarg);
- if (len == 1 || len == 2) /* POSIX. */
+ if (1 < MB_CUR_MAX)
{
- char *p = section_del;
- while (*optarg)
- *p++ = *optarg++;
+ char const *p = optarg;
+ char const *lim = p + len;
+ int n_chars = 0;
+ for (; p < lim && n_chars < 2; ++n_chars)
+ p += mcel_scan (p, lim).len;
+ if (n_chars == 1)
+ memcpy (mempcpy (section_del, optarg, len), ":", sizeof ":");
+ else
+ section_del = optarg;
}
else
- section_del = optarg; /* GNU extension. */
+ {
+ if (len == 1)
+ *section_del = *optarg;
+ else
+ section_del = optarg;
+ }
break;
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
@@ -583,7 +612,7 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
/* Initialize the section delimiters. */
- len = strlen (section_del);
+ section_del_len = len = strlen (section_del);
header_del_len = len * 3;
header_del = xmalloc (header_del_len + 1);
diff --git a/src/nohup.c b/src/nohup.c
index 6218986cb..c711ccd8e 100644
--- a/src/nohup.c
+++ b/src/nohup.c
@@ -1,5 +1,5 @@
/* nohup -- run a command immune to hangups, with output to a non-tty
- Copyright (C) 2003-2025 Free Software Foundation, Inc.
+ Copyright (C) 2003-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -56,8 +56,8 @@ Usage: %s COMMAND [ARG]...\n\
Run COMMAND, ignoring hangup signals.\n\
\n\
"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
printf (_("\n\
If standard input is a terminal, redirect it from an unreadable file.\n\
If standard output is a terminal, append output to 'nohup.out' if possible,\n\
@@ -81,14 +81,6 @@ To save output to FILE, use '%s COMMAND > FILE'.\n"),
int
main (int argc, char **argv)
{
- int out_fd = STDOUT_FILENO;
- int saved_stderr_fd = STDERR_FILENO;
- bool ignoring_input;
- bool redirecting_stdout;
- bool stdout_is_closed;
- bool redirecting_stderr;
- int exit_internal_failure;
-
initialize_main (&argc, &argv);
set_program_name (argv[0]);
setlocale (LC_ALL, "");
@@ -99,14 +91,14 @@ main (int argc, char **argv)
for env, exec, nice, time, and xargs where it requires internal
failure give something in the range 1-125. For consistency with
other tools, fail with EXIT_CANCELED unless POSIXLY_CORRECT. */
- exit_internal_failure = (getenv ("POSIXLY_CORRECT")
- ? POSIX_NOHUP_FAILURE : EXIT_CANCELED);
+ int exit_internal_failure = (getenv ("POSIXLY_CORRECT")
+ ? POSIX_NOHUP_FAILURE : EXIT_CANCELED);
initialize_exit_failure (exit_internal_failure);
atexit (close_stdout);
parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE_NAME,
Version, false, usage, AUTHORS,
- (char const *) nullptr);
+ (char const *) NULL);
if (argc <= optind)
{
@@ -114,10 +106,10 @@ main (int argc, char **argv)
usage (exit_internal_failure);
}
- ignoring_input = isatty (STDIN_FILENO);
- redirecting_stdout = isatty (STDOUT_FILENO);
- stdout_is_closed = (!redirecting_stdout && errno == EBADF);
- redirecting_stderr = isatty (STDERR_FILENO);
+ bool ignoring_input = isatty (STDIN_FILENO);
+ bool redirecting_stdout = isatty (STDOUT_FILENO);
+ bool stdout_is_closed = (!redirecting_stdout && errno == EBADF);
+ bool redirecting_stderr = isatty (STDERR_FILENO);
/* If standard input is a tty, replace it with /dev/null if possible.
Note that it is deliberately opened for *writing*,
@@ -135,9 +127,10 @@ main (int argc, char **argv)
First try nohup.out, then $HOME/nohup.out. If standard error is
a tty and standard output is closed, open nohup.out or
$HOME/nohup.out without redirecting anything. */
+ int out_fd = STDOUT_FILENO;
if (redirecting_stdout || (redirecting_stderr && stdout_is_closed))
{
- char *in_home = nullptr;
+ char *in_home = NULL;
char const *file = "nohup.out";
int flags = O_CREAT | O_WRONLY | O_APPEND;
mode_t mode = S_IRUSR | S_IWUSR;
@@ -152,7 +145,7 @@ main (int argc, char **argv)
char const *home = getenv ("HOME");
if (home)
{
- in_home = file_name_concat (home, file, nullptr);
+ in_home = file_name_concat (home, file, NULL);
out_fd = (redirecting_stdout
? fd_reopen (STDOUT_FILENO, in_home, flags, mode)
: open (in_home, flags, mode));
@@ -179,6 +172,7 @@ main (int argc, char **argv)
}
/* If standard error is a tty, redirect it. */
+ int saved_stderr_fd = STDERR_FILENO;
if (redirecting_stderr)
{
/* Save a copy of stderr before redirecting, so we can use the original
diff --git a/src/nproc.c b/src/nproc.c
index ec196297a..86ae76c4c 100644
--- a/src/nproc.c
+++ b/src/nproc.c
@@ -1,5 +1,5 @@
/* nproc - print the number of processors.
- Copyright (C) 2009-2025 Free Software Foundation, Inc.
+ Copyright (C) 2009-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -39,11 +39,11 @@ enum
static struct option const longopts[] =
{
- {"all", no_argument, nullptr, ALL_OPTION},
- {"ignore", required_argument, nullptr, IGNORE_OPTION},
+ {"all", no_argument, NULL, ALL_OPTION},
+ {"ignore", required_argument, NULL, IGNORE_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -56,16 +56,24 @@ usage (int status)
printf (_("Usage: %s [OPTION]...\n"), program_name);
fputs (_("\
Print the number of processing units available to the current process,\n\
-which may be less than the number of online processors\n\
+which may be less than the number of online processors.\n\
+If the 'OMP_NUM_THREADS' or 'OMP_THREAD_LIMIT' environment variables are set,\n\
+then they will determine the minimum and maximum returned value respectively.\n\
\n\
"), stdout);
- fputs (_("\
- --all print the number of installed processors\n\
- --ignore=N if possible, exclude N processing units\n\
-"), stdout);
-
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ --all\n\
+ print the number of installed processors,\n\
+ disregarding any OpenMP environment variables, or CPU quotas.\n\
+"));
+ oputs (_("\
+ --ignore=N\n\
+ if possible, exclude N processing units.\n\
+ The result is guaranteed to be at least 1.\n\
+"));
+
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -74,7 +82,7 @@ which may be less than the number of online processors\n\
int
main (int argc, char **argv)
{
- unsigned long nproc, ignore = 0;
+ unsigned long ignore = 0;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
setlocale (LC_ALL, "");
@@ -87,7 +95,7 @@ main (int argc, char **argv)
while (true)
{
- int c = getopt_long (argc, argv, "", longopts, nullptr);
+ int c = getopt_long (argc, argv, "", longopts, NULL);
if (c == -1)
break;
switch (c)
@@ -116,7 +124,7 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
- nproc = num_processors (mode);
+ unsigned long nproc = num_processors (mode);
if (ignore < nproc)
nproc -= ignore;
diff --git a/src/numfmt.c b/src/numfmt.c
index 8fc5b478a..467e5d79b 100644
--- a/src/numfmt.c
+++ b/src/numfmt.c
@@ -1,5 +1,5 @@
/* Reformat numbers like 11505426432 to the more human-readable 11G
- Copyright (C) 2012-2025 Free Software Foundation, Inc.
+ Copyright (C) 2012-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -15,7 +15,6 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
#include <config.h>
-#include <ctype.h>
#include <float.h>
#include <getopt.h>
#include <stdio.h>
@@ -75,7 +74,7 @@ enum scale_type
static char const *const scale_from_args[] =
{
- "none", "auto", "si", "iec", "iec-i", nullptr
+ "none", "auto", "si", "iec", "iec-i", NULL
};
static enum scale_type const scale_from_types[] =
@@ -85,7 +84,7 @@ static enum scale_type const scale_from_types[] =
static char const *const scale_to_args[] =
{
- "none", "si", "iec", "iec-i", nullptr
+ "none", "si", "iec", "iec-i", NULL
};
static enum scale_type const scale_to_types[] =
@@ -105,7 +104,7 @@ enum round_type
static char const *const round_args[] =
{
- "up", "down", "from-zero", "towards-zero", "nearest", nullptr
+ "up", "down", "from-zero", "towards-zero", "nearest", NULL
};
static enum round_type const round_types[] =
@@ -124,7 +123,7 @@ enum inval_type
static char const *const inval_args[] =
{
- "abort", "fail", "warn", "ignore", nullptr
+ "abort", "fail", "warn", "ignore", NULL
};
static enum inval_type const inval_types[] =
@@ -134,26 +133,26 @@ static enum inval_type const inval_types[] =
static struct option const longopts[] =
{
- {"from", required_argument, nullptr, FROM_OPTION},
- {"from-unit", required_argument, nullptr, FROM_UNIT_OPTION},
- {"to", required_argument, nullptr, TO_OPTION},
- {"to-unit", required_argument, nullptr, TO_UNIT_OPTION},
- {"round", required_argument, nullptr, ROUND_OPTION},
- {"padding", required_argument, nullptr, PADDING_OPTION},
- {"suffix", required_argument, nullptr, SUFFIX_OPTION},
- {"unit-separator", required_argument, nullptr, UNIT_SEPARATOR_OPTION},
- {"grouping", no_argument, nullptr, GROUPING_OPTION},
- {"delimiter", required_argument, nullptr, 'd'},
- {"field", required_argument, nullptr, FIELD_OPTION},
- {"debug", no_argument, nullptr, DEBUG_OPTION},
- {"-debug", no_argument, nullptr, DEV_DEBUG_OPTION},
- {"header", optional_argument, nullptr, HEADER_OPTION},
- {"format", required_argument, nullptr, FORMAT_OPTION},
- {"invalid", required_argument, nullptr, INVALID_OPTION},
- {"zero-terminated", no_argument, nullptr, 'z'},
+ {"from", required_argument, NULL, FROM_OPTION},
+ {"from-unit", required_argument, NULL, FROM_UNIT_OPTION},
+ {"to", required_argument, NULL, TO_OPTION},
+ {"to-unit", required_argument, NULL, TO_UNIT_OPTION},
+ {"round", required_argument, NULL, ROUND_OPTION},
+ {"padding", required_argument, NULL, PADDING_OPTION},
+ {"suffix", required_argument, NULL, SUFFIX_OPTION},
+ {"unit-separator", required_argument, NULL, UNIT_SEPARATOR_OPTION},
+ {"grouping", no_argument, NULL, GROUPING_OPTION},
+ {"delimiter", required_argument, NULL, 'd'},
+ {"field", required_argument, NULL, FIELD_OPTION},
+ {"debug", no_argument, NULL, DEBUG_OPTION},
+ {"-debug", no_argument, NULL, DEV_DEBUG_OPTION},
+ {"header", optional_argument, NULL, HEADER_OPTION},
+ {"format", required_argument, NULL, FORMAT_OPTION},
+ {"invalid", required_argument, NULL, INVALID_OPTION},
+ {"zero-terminated", no_argument, NULL, 'z'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* Maximum number of digits we can safely handle
@@ -170,19 +169,19 @@ static enum scale_type scale_from = scale_none;
static enum scale_type scale_to = scale_none;
static enum round_type round_style = round_from_zero;
static enum inval_type inval_style = inval_abort;
-static char const *suffix = nullptr;
-static char const *unit_separator = nullptr;
+static char const *suffix = NULL;
+static char const *unit_separator = NULL;
static uintmax_t from_unit_size = 1;
static uintmax_t to_unit_size = 1;
static int grouping = 0;
-static char *padding_buffer = nullptr;
+static char *padding_buffer = NULL;
static idx_t padding_buffer_size = 0;
static intmax_t padding_width = 0;
static int zero_padding_width = 0;
static long int user_precision = -1;
-static char const *format_str = nullptr;
-static char *format_str_prefix = nullptr;
-static char *format_str_suffix = nullptr;
+static char const *format_str = NULL;
+static char *format_str_prefix = NULL;
+static char *format_str_suffix = NULL;
/* By default, any conversion error will terminate the program. */
static int conv_exit_code = EXIT_CONVERSION_WARNINGS;
@@ -191,8 +190,8 @@ static int conv_exit_code = EXIT_CONVERSION_WARNINGS;
/* auto-pad each line based on skipped whitespace. */
static int auto_padding = 0;
-/* field delimiter - if nullptr, blanks separate fields. */
-static char const *delimiter = nullptr;
+/* field delimiter - if NULL, blanks separate fields. */
+static char const *delimiter = NULL;
/* line delimiter. */
static unsigned char line_delim = '\n';
@@ -216,8 +215,7 @@ static bool dev_debug = false;
static bool
newline_or_blank (mcel_t g)
{
- return g.ch == '\n'
- || (c32isblank (g.ch) && ! c32isnbspace (g.ch));
+ return g.ch == '\n' || c32issep (g.ch);
}
static inline int
@@ -243,7 +241,7 @@ static char const *valid_suffixes = 1 + zero_and_valid_suffixes;
static inline bool
valid_suffix (const char suf)
{
- return strchr (valid_suffixes, suf) != nullptr;
+ return strchr (valid_suffixes, suf) != NULL;
}
static inline int
@@ -332,7 +330,7 @@ suffix_power_char (int power)
/* Similar to 'powl(3)' but without requiring 'libm'. */
static long double
-powerld (long double base, int x)
+powerld (long double base, ptrdiff_t x)
{
long double result = base;
if (x == 0)
@@ -590,9 +588,9 @@ simple_strtod_float (char const *input_str,
return SSE_INVALID_NUMBER;
/* number of digits in the fractions. */
- size_t exponent = ptr2 - *endptr;
+ ptrdiff_t exponent = ptr2 - *endptr;
- val_frac = ((long double) val_frac) / powerld (10, exponent);
+ val_frac /= powerld (10, exponent);
/* TODO: detect loss of precision (only really 18 digits
of precision across all digits (before and after '.')). */
@@ -674,7 +672,7 @@ simple_strtod_human (char const *input_str,
if (!matched_unit_sep)
{
mcel_t g = mcel_scanz (*endptr);
- if (c32isblank (g.ch) || c32isnbspace (g.ch))
+ if (c32issep (g.ch) || c32isnbspace (g.ch))
(*endptr) += g.len;
}
@@ -738,7 +736,7 @@ simple_strtod_human (char const *input_str,
static void
simple_strtod_fatal (enum simple_strtod_error err, char const *input_str)
{
- char const *msgid = nullptr;
+ char const *msgid = NULL;
switch (err)
{
@@ -871,9 +869,9 @@ unit_to_umax (char const *n_string)
{
strtol_error s_err;
char const *c_string = n_string;
- char *t_string = nullptr;
+ char *t_string = NULL;
size_t n_len = strlen (n_string);
- char *end = nullptr;
+ char *end = NULL;
uintmax_t n;
char const *suffixes = valid_suffixes;
@@ -923,69 +921,84 @@ Usage: %s [OPTION]... [NUMBER]...\n\
Reformat NUMBER(s), or the numbers from standard input if none are specified.\n\
"), stdout);
emit_mandatory_arg_note ();
- fputs (_("\
- --debug print warnings about invalid input\n\
-"), stdout);
- fputs (_("\
- -d, --delimiter=X use X instead of whitespace for field delimiter\n\
-"), stdout);
- fputs (_("\
- --field=FIELDS replace the numbers in these input fields (default=1);\n\
- see FIELDS below\n\
-"), stdout);
- fputs (_("\
- --format=FORMAT use printf style floating-point FORMAT;\n\
- see FORMAT below for details\n\
-"), stdout);
- fputs (_("\
- --from=UNIT auto-scale input numbers to UNITs; default is 'none';\n\
- see UNIT below\n\
-"), stdout);
- fputs (_("\
- --from-unit=N specify the input unit size (instead of the default 1)\n\
-"), stdout);
- fputs (_("\
- --grouping use locale-defined grouping of digits, e.g. 1,000,000\n\
- (which means it has no effect in the C/POSIX locale)\n\
-"), stdout);
- fputs (_("\
- --header[=N] print (without converting) the first N header lines;\n\
- N defaults to 1 if not specified\n\
-"), stdout);
- fputs (_("\
- --invalid=MODE failure mode for invalid numbers: MODE can be:\n\
- abort (default), fail, warn, ignore\n\
-"), stdout);
- fputs (_("\
- --padding=N pad the output to N characters; positive N will\n\
- right-align; negative N will left-align;\n\
- padding is ignored if the output is wider than N;\n\
- the default is to automatically pad if a whitespace\n\
- is found\n\
-"), stdout);
- fputs (_("\
- --round=METHOD use METHOD for rounding when scaling; METHOD can be:\n\
- up, down, from-zero (default), towards-zero, nearest\n\
-"), stdout);
- fputs (_("\
- --suffix=SUFFIX add SUFFIX to output numbers, and accept optional\n\
- SUFFIX in input numbers\n\
-"), stdout);
- fputs (_("\
- --unit-separator=SEP insert SEP between number and unit on output,\n\
- and accept optional SEP in input numbers\n\
-"), stdout);
- fputs (_("\
- --to=UNIT auto-scale output numbers to UNITs; see UNIT below\n\
-"), stdout);
- fputs (_("\
- --to-unit=N the output unit size (instead of the default 1)\n\
-"), stdout);
- fputs (_("\
- -z, --zero-terminated line delimiter is NUL, not newline\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ --debug\n\
+ print warnings about invalid input\n\
+"));
+ oputs (_("\
+ -d, --delimiter=X\n\
+ use X instead of whitespace for field delimiter\n\
+"));
+ oputs (_("\
+ --field=FIELDS\n\
+ replace the numbers in these input fields (default=1);\n\
+ see FIELDS below for details\n\
+"));
+ oputs (_("\
+ --format=FORMAT\n\
+ use printf style floating-point FORMAT;\n\
+ see FORMAT below for details\n\
+"));
+ oputs (_("\
+ --from=UNIT\n\
+ auto-scale input numbers to UNITs; default is 'none';\n\
+ see UNIT below for details\n\
+"));
+ oputs (_("\
+ --from-unit=N\n\
+ specify the input unit size (instead of the default 1)\n\
+"));
+ oputs (_("\
+ --grouping\n\
+ use locale-defined grouping of digits, e.g. 1,000,000.\n\
+ This has no effect in the C/POSIX locale\n\
+"));
+ oputs (_("\
+ --header[=N]\n\
+ print (without converting) the first N header lines;\n\
+ N defaults to 1 if not specified\n\
+"));
+ oputs (_("\
+ --invalid=MODE\n\
+ failure mode for invalid numbers;\n\
+ MODE can be: abort (default), fail, warn, ignore\n\
+"));
+ oputs (_("\
+ --padding=N\n\
+ pad the output to N characters;\n\
+ positive N will right-align, negative N will left-align;\n\
+ padding is ignored if the output is wider than N;\n\
+ the default is to automatically pad if a whitespace is found\n\
+"));
+ oputs (_("\
+ --round=METHOD\n\
+ use METHOD for rounding when scaling; METHOD can be:\n\
+ up, down, from-zero (default), towards-zero, nearest\n\
+"));
+ oputs (_("\
+ --suffix=SUFFIX\n\
+ add SUFFIX to output numbers,\n\
+ and accept an optional SUFFIX in input numbers\n\
+"));
+ oputs (_("\
+ --unit-separator=SEP\n\
+ insert SEP between number and unit on output,\n\
+ and accept an optional SEP in input numbers\n\
+"));
+ oputs (_("\
+ --to=UNIT\n\
+ auto-scale output numbers to UNITs; see UNIT below\n\
+"));
+ oputs (_("\
+ --to-unit=N\n\
+ the output unit size (instead of the default 1)\n\
+"));
+ oputs (_("\
+ -z, --zero-terminated\n\
+ line delimiter is NUL, not newline\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
@@ -1087,7 +1100,7 @@ parse_format_string (char const *fmt)
size_t i;
size_t prefix_len = 0;
size_t suffix_pos;
- char *endptr = nullptr;
+ char *endptr = NULL;
bool zero_padding = false;
for (i = 0; !(fmt[i] == '%' && fmt[i + 1] != '%'); i += (fmt[i] == '%') + 1)
@@ -1199,7 +1212,7 @@ static enum simple_strtod_error
parse_human_number (char const *str, long double /*output */ *value,
size_t *precision)
{
- char *ptr = nullptr;
+ char *ptr = NULL;
enum simple_strtod_error e =
simple_strtod_human (str, &ptr, value, precision, scale_from);
@@ -1320,14 +1333,17 @@ print_padded_number (intmax_t padding)
/* Converts the TEXT number string to the requested representation,
and handles automatic suffix addition. */
-static int
+static bool
process_suffixed_number (char *text, long double *result,
size_t *precision, long int field)
{
+ char saved_suffix = '\0';
+
if (suffix)
{
if (mbs_endswith (text, suffix))
{
+ saved_suffix = *(text + strlen (text) - strlen (suffix));
*(text + strlen (text) - strlen (suffix)) = '\0';
devmsg ("trimming suffix %s\n", quote (suffix));
}
@@ -1361,42 +1377,35 @@ process_suffixed_number (char *text, long double *result,
*result = val;
- return (e == SSE_OK || e == SSE_OK_PRECISION_LOSS);
-}
-
-/* Return true if the current charset is UTF-8. */
-static bool
-is_utf8_charset (void)
-{
- static int is_utf8 = -1;
- if (is_utf8 == -1)
+ if (e == SSE_OK || e == SSE_OK_PRECISION_LOSS)
+ return true;
+ else
{
- char32_t w;
- mbstate_t mbs; mbszero (&mbs);
- is_utf8 = mbrtoc32 (&w, "\xe2\x9f\xb8", 3, &mbs) == 3 && w == 0x27F8;
+ if (saved_suffix)
+ *(text + strlen (text)) = saved_suffix;
+ return false;
}
- return is_utf8;
}
/* Search for multi-byte character C in multi-byte string S.
- Return a pointer to the character, or nullptr if not found. */
+ Return a pointer to the character, or NULL if not found. */
ATTRIBUTE_PURE
static char *
-mbsmbchr (char const* s, char const* c)
+mbsmbchr (char const *s, char const *c)
{
unsigned char uc = *c;
/* GB18030 is the most restrictive for the 0x30 optimization below. */
if (uc < 0x30 || MB_CUR_MAX == 1)
- return strchr (s, uc);
+ return (char *) strchr (s, uc);
else if (is_utf8_charset ())
- return uc < 0x80 ? strchr (s, uc) : strstr (s, c);
+ return (char *) (uc < 0x80 ? strchr (s, uc) : strstr (s, c));
else
return *(c + 1) == '\0' ? mbschr (s, uc) : (char *) mbsstr (s, c);
}
/* Return a pointer to the beginning of the next field in line.
The line pointer is moved to the end of the next field. */
-static char*
+static char *
next_field (char **line)
{
char *field_start = *line;
@@ -1486,7 +1495,7 @@ process_line (char *line, bool newline)
if (! process_field (next, field))
valid_number = false;
- if (delimiter != nullptr)
+ if (delimiter != NULL)
fputs (delimiter, stdout);
else
fputc (' ', stdout);
@@ -1537,12 +1546,12 @@ main (int argc, char **argv)
#endif
decimal_point = nl_langinfo (RADIXCHAR);
- if (decimal_point == nullptr || strlen (decimal_point) == 0)
+ if (decimal_point == NULL || strlen (decimal_point) == 0)
decimal_point = ".";
decimal_point_length = strlen (decimal_point);
thousands_sep = nl_langinfo (THOUSEP);
- if (thousands_sep == nullptr)
+ if (thousands_sep == NULL)
thousands_sep = "";
thousands_sep_length = strlen (thousands_sep);
@@ -1550,7 +1559,7 @@ main (int argc, char **argv)
while (true)
{
- int c = getopt_long (argc, argv, "d:z", longopts, nullptr);
+ int c = getopt_long (argc, argv, "d:z", longopts, NULL);
if (c == -1)
break;
@@ -1584,7 +1593,7 @@ main (int argc, char **argv)
break;
case PADDING_OPTION:
- if (((xstrtoimax (optarg, nullptr, 10, &padding_width, "")
+ if (((xstrtoimax (optarg, NULL, 10, &padding_width, "")
& ~LONGINT_OVERFLOW)
!= LONGINT_OK)
|| padding_width == 0)
@@ -1639,7 +1648,7 @@ main (int argc, char **argv)
case HEADER_OPTION:
if (optarg)
{
- if (xstrtoumax (optarg, nullptr, 10, &header, "") != LONGINT_OK
+ if (xstrtoumax (optarg, NULL, 10, &header, "") != LONGINT_OK
|| header == 0)
error (EXIT_FAILURE, 0, _("invalid header value %s"),
quote (optarg));
@@ -1667,7 +1676,7 @@ main (int argc, char **argv)
}
}
- if (format_str != nullptr && grouping)
+ if (format_str != NULL && grouping)
error (EXIT_FAILURE, 0, _("--grouping cannot be combined with --format"));
if (debug && ! locale_ok)
@@ -1675,10 +1684,10 @@ main (int argc, char **argv)
/* Warn about no-op. */
if (debug && scale_from == scale_none && scale_to == scale_none
- && !grouping && (padding_width == 0) && (format_str == nullptr))
+ && !grouping && (padding_width == 0) && (format_str == NULL))
error (0, 0, _("no conversion option specified"));
- if (debug && unit_separator && delimiter == nullptr)
+ if (debug && unit_separator && delimiter == NULL)
error (0, 0,
_("field delimiters have higher precedence than unit separators"));
@@ -1693,7 +1702,7 @@ main (int argc, char **argv)
error (0, 0, _("grouping has no effect in this locale"));
}
- auto_padding = (padding_width == 0 && delimiter == nullptr);
+ auto_padding = (padding_width == 0 && delimiter == NULL);
if (inval_style != inval_abort)
conv_exit_code = 0;
@@ -1708,7 +1717,7 @@ main (int argc, char **argv)
}
else
{
- char *line = nullptr;
+ char *line = NULL;
size_t line_allocated = 0;
ssize_t len;
diff --git a/src/od.c b/src/od.c
index 946b6517f..265e3d2ee 100644
--- a/src/od.c
+++ b/src/od.c
@@ -1,5 +1,5 @@
/* od -- dump files in octal and other formats
- Copyright (C) 1992-2025 Free Software Foundation, Inc.
+ Copyright (C) 1992-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
#include <config.h>
-#include <ctype.h>
#include <endian.h>
#include <float.h>
#include <stdio.h>
@@ -249,7 +248,7 @@ static char const *const *file_list;
/* Initializer for file_list if no file-arguments
were specified on the command line. */
-static char const *const default_file_list[] = {"-", nullptr};
+static char const *const default_file_list[] = {"-", NULL};
/* The input stream associated with the current file. */
static FILE *in_stream;
@@ -323,7 +322,7 @@ enum endian_type
static char const *const endian_args[] =
{
- "little", "big", nullptr
+ "little", "big", NULL
};
static enum endian_type const endian_types[] =
@@ -333,19 +332,19 @@ static enum endian_type const endian_types[] =
static struct option const long_options[] =
{
- {"skip-bytes", required_argument, nullptr, 'j'},
- {"address-radix", required_argument, nullptr, 'A'},
- {"read-bytes", required_argument, nullptr, 'N'},
- {"format", required_argument, nullptr, 't'},
- {"output-duplicates", no_argument, nullptr, 'v'},
- {"strings", optional_argument, nullptr, 'S'},
- {"traditional", no_argument, nullptr, TRADITIONAL_OPTION},
- {"width", optional_argument, nullptr, 'w'},
- {"endian", required_argument, nullptr, ENDIAN_OPTION },
+ {"skip-bytes", required_argument, NULL, 'j'},
+ {"address-radix", required_argument, NULL, 'A'},
+ {"read-bytes", required_argument, NULL, 'N'},
+ {"format", required_argument, NULL, 't'},
+ {"output-duplicates", no_argument, NULL, 'v'},
+ {"strings", optional_argument, NULL, 'S'},
+ {"traditional", no_argument, NULL, TRADITIONAL_OPTION},
+ {"width", optional_argument, NULL, 'w'},
+ {"endian", required_argument, NULL, ENDIAN_OPTION },
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -381,41 +380,82 @@ suffixes may be . for octal and b for multiply by 512.\n\
emit_mandatory_arg_note ();
- fputs (_("\
- -A, --address-radix=RADIX output format for file offsets; RADIX is one\n\
- of [doxn], for Decimal, Octal, Hex or None\n\
- --endian={big|little} swap input bytes according the specified order\n\
- -j, --skip-bytes=BYTES skip BYTES input bytes first\n\
-"), stdout);
- fputs (_("\
- -N, --read-bytes=BYTES limit dump to BYTES input bytes\n\
- -S BYTES, --strings[=BYTES] show only NUL terminated strings\n\
- of at least BYTES (3) printable characters\n\
- -t, --format=TYPE select output format or formats\n\
- -v, --output-duplicates do not use * to mark line suppression\n\
- -w[BYTES], --width[=BYTES] output BYTES bytes per output line;\n\
- 32 is implied when BYTES is not specified\n\
- --traditional accept arguments in third form above\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -A, --address-radix=RADIX\n\
+ output format for file offsets;\n\
+ RADIX is one of [doxn], for Decimal, Octal, Hex or None\n\
+"));
+ oputs (_("\
+ --endian={big|little}\n\
+ swap input bytes according the specified order\n\
+"));
+ oputs (_("\
+ -j, --skip-bytes=BYTES\n\
+ skip BYTES input bytes first\n\
+"));
+ oputs (_("\
+ -N, --read-bytes=BYTES\n\
+ limit dump to BYTES input bytes\n\
+"));
+ oputs (_("\
+ -S BYTES, --strings[=BYTES]\n\
+ show only NUL terminated strings\n\
+ of at least BYTES (default 3) printable characters\n\
+"));
+ oputs (_("\
+ -t, --format=TYPE\n\
+ select output format or formats\n\
+"));
+ oputs (_("\
+ -v, --output-duplicates\n\
+ do not use * to mark line suppression\n\
+"));
+ oputs (_("\
+ -w[BYTES], --width[=BYTES]\n\
+ output BYTES bytes per output line;\n\
+ 32 is implied when BYTES is not specified\n\
+"));
+ oputs (_("\
+ --traditional\n\
+ accept arguments in third form above\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
\n\
Traditional format specifications may be intermixed; they accumulate:\n\
+"), stdout);
+ oputs (_("\
-a same as -t a, select named characters, ignoring high-order bit\n\
+"));
+ oputs (_("\
-b same as -t o1, select octal bytes\n\
+"));
+ oputs (_("\
-c same as -t c, select printable characters or backslash escapes\n\
+"));
+ oputs (_("\
-d same as -t u2, select unsigned decimal 2-byte units\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-f same as -t fF, select floats\n\
+"));
+ oputs (_("\
-i same as -t dI, select decimal ints\n\
+"));
+ oputs (_("\
-l same as -t dL, select decimal longs\n\
+"));
+ oputs (_("\
-o same as -t o2, select octal 2-byte units\n\
+"));
+ oputs (_("\
-s same as -t d2, select decimal 2-byte units\n\
+"));
+ oputs (_("\
-x same as -t x2, select hexadecimal 2-byte units\n\
-"), stdout);
+"));
fputs (_("\
\n\
\n\
@@ -825,7 +865,7 @@ decode_one_format (char const *s_orig, char const *s, char const **next,
? print_intmax
: LONG < LONG_LONG && LONG_LONG < INTMAX && size_spec == LONG_LONG
? print_long_long
- : (affirm (false), (print_function_type) nullptr));
+ : (affirm (false), (print_function_type) NULL));
break;
case 'f':
@@ -983,7 +1023,7 @@ open_next_file (void)
do
{
input_filename = *file_list;
- if (input_filename == nullptr)
+ if (input_filename == NULL)
return ok;
++file_list;
@@ -997,17 +1037,17 @@ open_next_file (void)
else
{
in_stream = fopen (input_filename, (O_BINARY ? "rb" : "r"));
- if (in_stream == nullptr)
+ if (in_stream == NULL)
{
error (0, errno, "%s", quotef (input_filename));
ok = false;
}
}
}
- while (in_stream == nullptr);
+ while (in_stream == NULL);
if (0 <= end_offset && !flag_dump_strings)
- setvbuf (in_stream, nullptr, _IONBF, 0);
+ setvbuf (in_stream, NULL, _IONBF, 0);
return ok;
}
@@ -1024,7 +1064,7 @@ check_and_close (int in_errno)
{
bool ok = true;
- if (in_stream != nullptr)
+ if (in_stream != NULL)
{
if (!ferror (in_stream))
in_errno = 0;
@@ -1038,7 +1078,7 @@ check_and_close (int in_errno)
ok = false;
}
- in_stream = nullptr;
+ in_stream = NULL;
}
if (ferror (stdout))
@@ -1093,7 +1133,7 @@ skip (intmax_t n_skip)
if (n_skip == 0)
return true;
- while (in_stream != nullptr) /* EOF. */
+ while (in_stream != NULL) /* EOF. */
{
struct stat file_stats;
@@ -1370,7 +1410,7 @@ read_block (idx_t n, char *block, idx_t *n_bytes_in_buffer)
*n_bytes_in_buffer = 0;
- while (in_stream != nullptr) /* EOF. */
+ while (in_stream != NULL) /* EOF. */
{
idx_t n_needed = n - *n_bytes_in_buffer;
idx_t n_read = fread (block + *n_bytes_in_buffer,
@@ -1403,14 +1443,14 @@ get_lcm (void)
return l_c_m;
}
-/* Act like xstrtoimax (NPTR, nullptr, BASE, VAL, VALID_SUFFIXES),
+/* Act like xstrtoimax (NPTR, NULL, BASE, VAL, VALID_SUFFIXES),
except reject negative values, and *VAL may be set if
LONGINT_INVALID is returned. */
static strtol_error
xstr2nonneg (char const *restrict nptr, int base, intmax_t *val,
char const *restrict valid_suffixes)
{
- strtol_error s_err = xstrtoimax (nptr, nullptr, base, val, valid_suffixes);
+ strtol_error s_err = xstrtoimax (nptr, NULL, base, val, valid_suffixes);
return s_err != LONGINT_INVALID && *val < 0 ? LONGINT_INVALID : s_err;
}
@@ -1435,7 +1475,7 @@ parse_old_offset (char *str, intmax_t *offset)
it's hexadecimal, else octal. */
char *dot = strchr (s, '.');
if (dot && dot[(dot[1] == 'b' || dot[1] == 'B') + 1])
- dot = nullptr;
+ dot = NULL;
int radix = dot ? 10 : s[0] == '0' && (s[1] == 'x' || s[1] == 'X') ? 16 : 8;
if (dot)
@@ -1521,7 +1561,8 @@ dump (void)
current_offset += n_bytes_read;
}
- format_address (current_offset, '\n');
+ if (ok || current_offset)
+ format_address (current_offset, '\n');
if (0 <= end_offset && end_offset <= current_offset)
ok &= check_and_close (0);
@@ -1719,7 +1760,7 @@ main (int argc, char **argv)
case 'S':
modern = true;
- if (optarg == nullptr)
+ if (optarg == NULL)
string_min = 3;
else
{
@@ -1798,7 +1839,7 @@ main (int argc, char **argv)
case 'w':
modern = true;
- if (optarg == nullptr)
+ if (optarg == NULL)
{
desired_width = 32;
}
@@ -1950,12 +1991,12 @@ main (int argc, char **argv)
/* open the first input file */
ok = open_next_file ();
- if (in_stream == nullptr)
+ if (in_stream == NULL)
goto cleanup;
/* skip over any unwanted header bytes */
ok &= skip (n_bytes_to_skip);
- if (in_stream == nullptr)
+ if (in_stream == NULL)
goto cleanup;
pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0);
diff --git a/src/operand2sig.c b/src/operand2sig.c
index 9c69de633..0d8caafc4 100644
--- a/src/operand2sig.c
+++ b/src/operand2sig.c
@@ -1,5 +1,5 @@
/* operand2sig.c -- common function for parsing signal specifications
- Copyright (C) 2008-2025 Free Software Foundation, Inc.
+ Copyright (C) 2008-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -68,8 +68,7 @@ operand2sig (char const *operand)
/* Convert signal to upper case in the C locale, not in the
current locale. Don't assume ASCII; it might be EBCDIC. */
char *upcased = xstrdup (operand);
- char *p;
- for (p = upcased; *p; p++)
+ for (char *p = upcased; *p; p++)
if (strchr ("abcdefghijklmnopqrstuvwxyz", *p))
*p += 'A' - 'a';
diff --git a/src/operand2sig.h b/src/operand2sig.h
index e63801b88..09df376d9 100644
--- a/src/operand2sig.h
+++ b/src/operand2sig.h
@@ -1,6 +1,6 @@
/* operand2sig.h -- prototype for signal specification function
- Copyright (C) 2008-2025 Free Software Foundation, Inc.
+ Copyright (C) 2008-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/paste.c b/src/paste.c
index d2c5c2d42..bcc0b6633 100644
--- a/src/paste.c
+++ b/src/paste.c
@@ -1,5 +1,5 @@
/* paste - merge lines of files
- Copyright (C) 1997-2025 Free Software Foundation, Inc.
+ Copyright (C) 1997-2026 Free Software Foundation, Inc.
Copyright (C) 1984 David M. Ihnat
This program is free software: you can redistribute it and/or modify
@@ -42,6 +42,7 @@
#include <sys/types.h>
#include "system.h"
#include "fadvise.h"
+#include "mcel.h"
/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "paste"
@@ -50,9 +51,6 @@
proper_name ("David M. Ihnat"), \
proper_name ("David MacKenzie")
-/* Indicates that no delimiter should be added in the current position. */
-#define EMPTY_DELIM '\0'
-
/* If nonzero, we have read standard input at some point. */
static bool have_read_stdin;
@@ -60,28 +58,33 @@ static bool have_read_stdin;
corresponding lines from each file in parallel. */
static bool serial_merge;
-/* The delimiters between lines of input files (used cyclically). */
+/* The delimiters between lines of input files (used cyclically).
+ This stores the raw bytes of all delimiters concatenated. */
static char *delims;
-/* A pointer to the character after the end of 'delims'. */
-static char const *delim_end;
+/* Length of each delimiter in bytes (supports multi-byte characters).
+ A length of 0 indicates no delimiter at this position (from \0 escape). */
+static size_t *delim_lens;
+
+/* Number of delimiters. */
+static idx_t num_delims;
static unsigned char line_delim = '\n';
static struct option const longopts[] =
{
- {"serial", no_argument, nullptr, 's'},
- {"delimiters", required_argument, nullptr, 'd'},
- {"zero-terminated", no_argument, nullptr, 'z'},
+ {"serial", no_argument, NULL, 's'},
+ {"delimiters", required_argument, NULL, 'd'},
+ {"zero-terminated", no_argument, NULL, 'z'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
-/* Set globals delims and delim_end. Copy STRPTR to DELIMS, converting
- backslash representations of special characters in STRPTR to their actual
- values. The set of possible backslash characters has been expanded beyond
- that recognized by the Unix version.
+/* Set globals delims, delim_lens, and num_delims.
+ Process STRPTR converting backslash representations of special characters
+ to their actual values. The set of possible backslash characters has been
+ expanded beyond that recognized by the Unix version.
Return 0 upon success.
If the string ends in an odd number of backslashes, ignore the
final backslash and return nonzero. */
@@ -93,62 +96,65 @@ collapse_escapes (char const *strptr)
bool backslash_at_end = false;
delims = strout;
+ delim_lens = xnmalloc (MAX (1, strlen (strptr)), sizeof *delim_lens);
- while (*strptr)
+ char const *s = strptr;
+ idx_t idx = 0;
+
+ while (*s)
{
- if (*strptr != '\\') /* Is it an escape character? */
- *strout++ = *strptr++; /* No, just transfer it. */
- else
+ if (*s == '\\')
{
- switch (*++strptr)
+ s++;
+ if (*s == '\0')
{
- case '0':
- *strout++ = EMPTY_DELIM;
- break;
-
- case 'b':
- *strout++ = '\b';
- break;
-
- case 'f':
- *strout++ = '\f';
- break;
-
- case 'n':
- *strout++ = '\n';
+ backslash_at_end = true;
break;
+ }
+ else if (*s == '0')
+ {
+ /* Empty delimiter at this position. */
+ s++;
+ delim_lens[idx++] = 0;
+ }
+ else
+ {
+ switch (*s)
+ {
+ case 'b': *strout++ = '\b'; break;
+ case 'f': *strout++ = '\f'; break;
+ case 'n': *strout++ = '\n'; break;
+ case 'r': *strout++ = '\r'; break;
+ case 't': *strout++ = '\t'; break;
+ case 'v': *strout++ = '\v'; break;
+ case '\\': *strout++ = '\\'; break;
+ default: goto copy_character;
+ }
- case 'r':
- *strout++ = '\r';
- break;
+ s++;
+ delim_lens[idx++] = 1;
+ }
- case 't':
- *strout++ = '\t';
- break;
+ continue;
+ }
- case 'v':
- *strout++ = '\v';
- break;
+ copy_character:;
+ mcel_t g = mcel_scanz (s);
+ strout = mempcpy (strout, s, g.len);
+ s += g.len;
+ delim_lens[idx++] = g.len;
+ }
- case '\\':
- *strout++ = '\\';
- break;
+ *strout = '\0';
- case '\0':
- backslash_at_end = true;
- goto done;
-
- default:
- *strout++ = *strptr;
- break;
- }
- strptr++;
- }
+ if (idx == 0)
+ {
+ delim_lens[0] = 0;
+ idx = 1;
}
- done:
+ num_delims = idx;
- delim_end = strout;
return backslash_at_end ? 1 : 0;
}
@@ -161,6 +167,16 @@ xputchar (char c)
write_error ();
}
+/* Output the delimiter at DELIMPTR with length LEN.
+ If LEN is 0, nothing is output (empty delimiter from \0 escape). */
+
+static inline void
+output_delim (char const *delimptr, size_t len)
+{
+ if (len > 0 && fwrite (delimptr, 1, len, stdout) != len)
+ write_error ();
+}
+
/* Perform column paste on the NFILES files named in FNAMPTR.
Return true if successful, false if one or more files could not be
opened or read. */
@@ -171,9 +187,9 @@ paste_parallel (size_t nfiles, char **fnamptr)
bool ok = true;
/* If all files are just ready to be closed, or will be on this
round, the string of delimiters must be preserved.
- delbuf[0] through delbuf[nfiles]
- store the delimiters for closed files. */
- char *delbuf = xmalloc (nfiles + 2);
+ delbuf stores the delimiter bytes for closed files.
+ Size it to hold up to (nfiles - 1) delimiters. */
+ char *delbuf = xmalloc ((nfiles - 1) * MB_CUR_MAX + 1);
/* Streams open to the files to process; null if the corresponding
stream is closed. */
@@ -199,7 +215,7 @@ paste_parallel (size_t nfiles, char **fnamptr)
else
{
fileptr[files_open] = fopen (fnamptr[files_open], "r");
- if (fileptr[files_open] == nullptr)
+ if (fileptr[files_open] == NULL)
error (EXIT_FAILURE, errno, "%s", quotef (fnamptr[files_open]));
else if (fileno (fileptr[files_open]) == STDIN_FILENO)
opened_stdin = true;
@@ -218,8 +234,9 @@ paste_parallel (size_t nfiles, char **fnamptr)
{
/* Set up for the next line. */
bool somedone = false;
- char const *delimptr = delims;
- size_t delims_saved = 0; /* Number of delims saved in 'delbuf'. */
+ idx_t delimidx = 0; /* Current delimiter index. */
+ idx_t delimoff = 0; /* Current offset into delims. */
+ idx_t delims_saved = 0; /* Bytes saved in 'delbuf'. */
for (size_t i = 0; i < nfiles && files_open; i++)
{
@@ -267,7 +284,7 @@ paste_parallel (size_t nfiles, char **fnamptr)
ok = false;
}
- fileptr[i] = nullptr;
+ fileptr[i] = NULL;
files_open--;
}
@@ -292,10 +309,18 @@ paste_parallel (size_t nfiles, char **fnamptr)
else
{
/* Closed file; add delimiter to 'delbuf'. */
- if (*delimptr != EMPTY_DELIM)
- delbuf[delims_saved++] = *delimptr;
- if (++delimptr == delim_end)
- delimptr = delims;
+ size_t len = delim_lens[delimidx];
+ if (len > 0)
+ {
+ memcpy (delbuf + delims_saved, delims + delimoff, len);
+ delims_saved += len;
+ }
+ delimoff += len;
+ if (++delimidx == num_delims)
+ {
+ delimidx = 0;
+ delimoff = 0;
+ }
}
}
else
@@ -308,10 +333,13 @@ paste_parallel (size_t nfiles, char **fnamptr)
{
if (chr != line_delim && chr != EOF)
xputchar (chr);
- if (*delimptr != EMPTY_DELIM)
- xputchar (*delimptr);
- if (++delimptr == delim_end)
- delimptr = delims;
+ output_delim (delims + delimoff, delim_lens[delimidx]);
+ delimoff += delim_lens[delimidx];
+ if (++delimidx == num_delims)
+ {
+ delimidx = 0;
+ delimoff = 0;
+ }
}
else
{
@@ -337,7 +365,6 @@ paste_serial (size_t nfiles, char **fnamptr)
{
bool ok = true; /* false if open or read errors occur. */
int charnew, charold; /* Current and previous char read. */
- char const *delimptr; /* Current delimiter char. */
FILE *fileptr; /* Open for reading current file. */
for (; nfiles; nfiles--, fnamptr++)
@@ -352,7 +379,7 @@ paste_serial (size_t nfiles, char **fnamptr)
else
{
fileptr = fopen (*fnamptr, "r");
- if (fileptr == nullptr)
+ if (fileptr == NULL)
{
error (0, errno, "%s", quotef (*fnamptr));
ok = false;
@@ -361,7 +388,8 @@ paste_serial (size_t nfiles, char **fnamptr)
fadvise (fileptr, FADVISE_SEQUENTIAL);
}
- delimptr = delims; /* Set up for delimiter string. */
+ idx_t delimidx = 0; /* Current delimiter index. */
+ idx_t delimoff = 0; /* Current offset into delims. */
charold = getc (fileptr);
saved_errno = errno;
@@ -378,11 +406,13 @@ paste_serial (size_t nfiles, char **fnamptr)
/* Process the old character. */
if (charold == line_delim)
{
- if (*delimptr != EMPTY_DELIM)
- xputchar (*delimptr);
-
- if (++delimptr == delim_end)
- delimptr = delims;
+ output_delim (delims + delimoff, delim_lens[delimidx]);
+ delimoff += delim_lens[delimidx];
+ if (++delimidx == num_delims)
+ {
+ delimidx = 0;
+ delimoff = 0;
+ }
}
else
xputchar (charold);
@@ -427,20 +457,30 @@ Usage: %s [OPTION]... [FILE]...\n\
fputs (_("\
Write lines consisting of the sequentially corresponding lines from\n\
each FILE, separated by TABs, to standard output.\n\
+The newline of every line except the line from the last file\n\
+is replaced with a TAB.\n\
"), stdout);
emit_stdin_note ();
emit_mandatory_arg_note ();
- fputs (_("\
- -d, --delimiters=LIST reuse characters from LIST instead of TABs\n\
- -s, --serial paste one file at a time instead of in parallel\n\
-"), stdout);
- fputs (_("\
- -z, --zero-terminated line delimiter is NUL, not newline\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -d, --delimiters=LIST\n\
+ reuse characters from LIST instead of TABs;\n\
+ backslash escapes are supported\n\
+"));
+ oputs (_("\
+ -s, --serial\n\
+ paste one file at a time instead of in parallel; the newline of\n\
+ every line except the last line in each file is replaced with a TAB\
+\n\
+"));
+ oputs (_("\
+ -z, --zero-terminated\n\
+ line delimiter is NUL, not newline\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
/* FIXME: add a couple of examples. */
emit_ancillary_info (PROGRAM_NAME);
}
@@ -461,10 +501,7 @@ main (int argc, char **argv)
atexit (close_stdout);
- have_read_stdin = false;
- serial_merge = false;
-
- while ((optc = getopt_long (argc, argv, "d:sz", longopts, nullptr)) != -1)
+ while ((optc = getopt_long (argc, argv, "d:sz", longopts, NULL)) != -1)
{
switch (optc)
{
@@ -510,6 +547,7 @@ main (int argc, char **argv)
(nfiles, &argv[optind]));
free (delims);
+ free (delim_lens);
if (have_read_stdin && fclose (stdin) == EOF)
error (EXIT_FAILURE, errno, "-");
diff --git a/src/pathchk.c b/src/pathchk.c
index 38095d040..7774e04a4 100644
--- a/src/pathchk.c
+++ b/src/pathchk.c
@@ -1,5 +1,5 @@
/* pathchk -- check whether file names are valid or portable
- Copyright (C) 1991-2025 Free Software Foundation, Inc.
+ Copyright (C) 1991-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -71,10 +71,10 @@ enum
static struct option const longopts[] =
{
- {"portability", no_argument, nullptr, PORTABILITY_OPTION},
+ {"portability", no_argument, NULL, PORTABILITY_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -88,12 +88,19 @@ usage (int status)
fputs (_("\
Diagnose invalid or non-portable file names.\n\
\n\
- -p check for most POSIX systems\n\
- -P check for empty names and leading \"-\"\n\
- --portability check for all POSIX systems (equivalent to -p -P)\n\
"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -p check for most POSIX systems\n\
+"));
+ oputs (_("\
+ -P check for empty names and leading \"-\"\n\
+"));
+ oputs (_("\
+ --portability\n\
+ check for all POSIX systems (equivalent to -p -P)\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -115,7 +122,7 @@ main (int argc, char **argv)
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "+pP", longopts, nullptr)) != -1)
+ while ((optc = getopt_long (argc, argv, "+pP", longopts, NULL)) != -1)
{
switch (optc)
{
@@ -160,9 +167,7 @@ main (int argc, char **argv)
static bool
no_leading_hyphen (char const *file)
{
- char const *p;
-
- for (p = file; (p = strchr (p, '-')); p++)
+ for (char const *p = file; (p = strchr (p, '-')); p++)
if (p == file || p[-1] == '/')
{
error (0, 0, _("leading '-' in a component of file name %s"),
@@ -250,9 +255,6 @@ validate_file_name (char *file, bool check_basic_portability,
{
idx_t filelen = strlen (file);
- /* Start of file name component being checked. */
- char *start;
-
/* True if component lengths need to be checked. */
bool check_component_lengths;
@@ -334,7 +336,7 @@ validate_file_name (char *file, bool check_basic_portability,
check_component_lengths = check_basic_portability;
if (! check_component_lengths && ! file_exists)
{
- for (start = file; *(start = component_start (start)); )
+ for (char *start = file; *(start = component_start (start)); )
{
size_t length = component_len (start);
@@ -359,7 +361,7 @@ validate_file_name (char *file, bool check_basic_portability,
/* If nonzero, the known limit on file name components. */
idx_t known_name_max = check_basic_portability ? _POSIX_NAME_MAX : 0;
- for (start = file; *(start = component_start (start)); )
+ for (char *start = file; *(start = component_start (start)); )
{
idx_t length;
diff --git a/src/pinky.c b/src/pinky.c
index b49096b35..d336c51c7 100644
--- a/src/pinky.c
+++ b/src/pinky.c
@@ -1,5 +1,5 @@
/* GNU's pinky.
- Copyright (C) 1992-2025 Free Software Foundation, Inc.
+ Copyright (C) 1992-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,7 +17,6 @@
/* Created by hacking who.c by Kaveh Ghazi ghazi@caip.rutgers.edu */
#include <config.h>
-#include <ctype.h>
#include <getopt.h>
#include <pwd.h>
#include <stdio.h>
@@ -26,7 +25,11 @@
#include "system.h"
#include "canon-host.h"
+#include "fadvise.h"
+#include "filenamecat.h"
+#include "full-write.h"
#include "hard-locale.h"
+#include "ioblksize.h"
#include "readutmp.h"
/* The official name of this program (e.g., no 'g' prefix). */
@@ -82,10 +85,10 @@ enum
static struct option const longopts[] =
{
- {"lookup", no_argument, nullptr, LOOKUP_OPTION},
+ {"lookup", no_argument, NULL, LOOKUP_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* Count and return the number of ampersands in STR. */
@@ -110,8 +113,6 @@ static char *
create_fullname (char const *gecos_name, char const *user_name)
{
idx_t rsize = strlen (gecos_name) + 1;
- char *result;
- char *r;
idx_t ampersands = count_ampersands (gecos_name);
if (ampersands != 0)
@@ -123,7 +124,8 @@ create_fullname (char const *gecos_name, char const *user_name)
xalloc_die ();
}
- r = result = xmalloc (rsize);
+ char *result = xmalloc (rsize);
+ char *r = result;
while (*gecos_name)
{
@@ -155,12 +157,11 @@ idle_string (time_t when)
{
static time_t now = 0;
static char buf[INT_STRLEN_BOUND (intmax_t) + sizeof "d"];
- time_t seconds_idle;
if (now == 0)
time (&now);
- seconds_idle = now - when;
+ time_t seconds_idle = now - when;
if (seconds_idle < 60) /* One minute. */
return " ";
if (seconds_idle < (24 * 60 * 60)) /* One day. */
@@ -198,10 +199,6 @@ time_string (STRUCT_UTMP const *utmp_ent)
static void
print_entry (STRUCT_UTMP const *utmp_ent)
{
- struct stat stats;
- time_t last_change;
- char mesg;
-
/* If ut_line contains a space, the device name starts after the space. */
char *line = utmp_ent->ut_line;
char *space = strchr (line, ' ');
@@ -222,6 +219,9 @@ print_entry (STRUCT_UTMP const *utmp_ent)
dirfd = dev_dirfd;
}
+ struct stat stats;
+ time_t last_change;
+ char mesg;
if (AT_FDCWD <= dirfd && fstatat (dirfd, line, &stats, 0) == 0)
{
mesg = (stats.st_mode & S_IWGRP) ? ' ' : '*';
@@ -242,18 +242,17 @@ print_entry (STRUCT_UTMP const *utmp_ent)
if (include_fullname)
{
struct passwd *pw = getpwnam (ut_user);
- if (pw == nullptr)
+ if (pw == NULL)
/* TRANSLATORS: Real name is unknown; at most 19 characters. */
printf (" %19s", _(" ???"));
else
{
char *const comma = strchr (pw->pw_gecos, ',');
- char *result;
if (comma)
*comma = '\0';
- result = create_fullname (pw->pw_gecos, pw->pw_name);
+ char *result = create_fullname (pw->pw_gecos, pw->pw_name);
printf (" %-19.19s", result);
free (result);
}
@@ -280,15 +279,14 @@ print_entry (STRUCT_UTMP const *utmp_ent)
#ifdef HAVE_STRUCT_XTMP_UT_HOST
if (include_where && utmp_ent->ut_host[0])
{
- char *host = nullptr;
- char *display = nullptr;
char *ut_host = utmp_ent->ut_host;
/* Look for an X display. */
- display = strchr (ut_host, ':');
+ char *display = strchr (ut_host, ':');
if (display)
*display++ = '\0';
+ char *host = NULL;
if (*ut_host && do_lookup)
/* See if we can canonicalize it. */
host = canon_host (ut_host);
@@ -309,6 +307,35 @@ print_entry (STRUCT_UTMP const *utmp_ent)
#endif
putchar ('\n');
+
+ if (ferror (stdout))
+ write_error ();
+}
+
+/* If FILE exists in HOME, print it to standard output, preceded by HEADER. */
+
+static void
+cat_file (char const *header, char const *home, char const *file)
+{
+ char *full_name = file_name_concat (home, file, NULL);
+ FILE *fp = fopen (full_name, "r");
+
+ if (fp)
+ {
+ fputs (header, stdout);
+
+ fadvise (fp, FADVISE_SEQUENTIAL);
+
+ char buf[BUFSIZ];
+ for (size_t bytes_read;
+ 0 < (bytes_read = fread (buf, 1, sizeof buf, fp));)
+ if (fwrite (buf, 1, bytes_read, stdout) != bytes_read)
+ write_error ();
+
+ fclose (fp);
+ }
+
+ free (full_name);
}
/* Display a verbose line of information about UTMP_ENT. */
@@ -316,15 +343,13 @@ print_entry (STRUCT_UTMP const *utmp_ent)
static void
print_long_entry (const char name[])
{
- struct passwd *pw;
-
- pw = getpwnam (name);
+ struct passwd *pw = getpwnam (name);
printf (_("Login name: "));
printf ("%-28s", name);
printf (_("In real life: "));
- if (pw == nullptr)
+ if (pw == NULL)
{
/* TRANSLATORS: Real name is unknown; no hard limit. */
printf (" %s", _("???\n"));
@@ -333,12 +358,11 @@ print_long_entry (const char name[])
else
{
char *const comma = strchr (pw->pw_gecos, ',');
- char *result;
if (comma)
*comma = '\0';
- result = create_fullname (pw->pw_gecos, pw->pw_name);
+ char *result = create_fullname (pw->pw_gecos, pw->pw_name);
printf (" %s", result);
free (result);
}
@@ -355,54 +379,15 @@ print_long_entry (const char name[])
}
if (include_project)
- {
- FILE *stream;
- char buf[1024];
- char const *const baseproject = "/.project";
- char *const project =
- xmalloc (strlen (pw->pw_dir) + strlen (baseproject) + 1);
- stpcpy (stpcpy (project, pw->pw_dir), baseproject);
-
- stream = fopen (project, "r");
- if (stream)
- {
- size_t bytes;
-
- printf (_("Project: "));
-
- while ((bytes = fread (buf, 1, sizeof (buf), stream)) > 0)
- fwrite (buf, 1, bytes, stdout);
- fclose (stream);
- }
-
- free (project);
- }
+ cat_file (_("Project: "), pw->pw_dir, ".project");
if (include_plan)
- {
- FILE *stream;
- char buf[1024];
- char const *const baseplan = "/.plan";
- char *const plan =
- xmalloc (strlen (pw->pw_dir) + strlen (baseplan) + 1);
- stpcpy (stpcpy (plan, pw->pw_dir), baseplan);
-
- stream = fopen (plan, "r");
- if (stream)
- {
- size_t bytes;
-
- printf (_("Plan:\n"));
-
- while ((bytes = fread (buf, 1, sizeof (buf), stream)) > 0)
- fwrite (buf, 1, bytes, stdout);
- fclose (stream);
- }
-
- free (plan);
- }
+ cat_file (_("Plan:\n"), pw->pw_dir, ".plan");
putchar ('\n');
+
+ if (ferror (stdout))
+ write_error ();
}
/* Print the username of each valid entry and the number of valid entries
@@ -497,24 +482,40 @@ usage (int status)
printf (_("Usage: %s [OPTION]... [USER]...\n"), program_name);
fputs (_("\
\n\
- -l produce long format output for the specified USERs\n\
- -b omit the user's home directory and shell in long format\n\
- -h omit the user's project file in long format\n\
- -p omit the user's plan file in long format\n\
- -s do short format output, this is the default\n\
-"), stdout);
- fputs (_("\
- -f omit the line of column headings in short format\n\
- -w omit the user's full name in short format\n\
- -i omit the user's full name and remote host in short format\n\
- -q omit the user's full name, remote host and idle time\n\
- in short format\n\
"), stdout);
- fputs (_("\
- --lookup attempt to canonicalize hostnames via DNS\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -l produce long format output for the specified USERs\n\
+"));
+ oputs (_("\
+ -b omit the user's home directory and shell in long format\n\
+"));
+ oputs (_("\
+ -h omit the user's project file in long format\n\
+"));
+ oputs (_("\
+ -p omit the user's plan file in long format\n\
+"));
+ oputs (_("\
+ -s do short format output, this is the default\n\
+"));
+ oputs (_("\
+ -f omit the line of column headings in short format\n\
+"));
+ oputs (_("\
+ -w omit the user's full name in short format\n\
+"));
+ oputs (_("\
+ -i omit the user's full name and remote host in short format\n\
+"));
+ oputs (_("\
+ -q omit the user's full name, remote host and idle time in short format\n\
+"));
+ oputs (_("\
+ --lookup\n\
+ attempt to canonicalize hostnames via DNS\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
printf (_("\
\n\
A lightweight 'finger' program; print user information.\n\
@@ -528,9 +529,6 @@ The utmp file will be %s.\n\
int
main (int argc, char **argv)
{
- int optc;
- int n_users;
-
initialize_main (&argc, &argv);
set_program_name (argv[0]);
setlocale (LC_ALL, "");
@@ -539,7 +537,8 @@ main (int argc, char **argv)
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "sfwiqbhlp", longopts, nullptr))
+ int optc;
+ while ((optc = getopt_long (argc, argv, "sfwiqbhlp", longopts, NULL))
!= -1)
{
switch (optc)
@@ -600,7 +599,7 @@ main (int argc, char **argv)
}
}
- n_users = argc - optind;
+ int n_users = argc - optind;
if (!do_short_format && n_users == 0)
{
diff --git a/src/pr.c b/src/pr.c
index 10b8c528f..09a1b94a8 100644
--- a/src/pr.c
+++ b/src/pr.c
@@ -1,5 +1,5 @@
/* pr -- convert text files for printing.
- Copyright (C) 1988-2025 Free Software Foundation, Inc.
+ Copyright (C) 1988-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -53,7 +53,7 @@
two conflicting POSIX requirements exist:
First "default n-separator is TAB", second "output text columns shall
be of equal width". Moreover POSIX specifies the number+separator a
- part of the column, together with '-COLUMN' and '-a -COLUMN'.
+ part of the column, together with '-COLS' and '-a -COLS'.
(With -m output the number shall occupy each line only once. Exactly
the same situation as single column output exists.)
GNU pr gives priority to the 2nd requirement and observes POSIX
@@ -114,13 +114,13 @@
+FIRST_PAGE[:LAST_PAGE], --pages=FIRST_PAGE[:LAST_PAGE]
begin [stop] printing with page FIRST_[LAST_]PAGE
- -COLUMN, --columns=COLUMN
- Produce output that is COLUMN columns wide and
+ -COLS, --columns=COLS
+ Produce output that is COLS columns wide and
print columns down, unless -a is used. Balance number of
lines in the columns on each page.
-a, --across Print columns across rather than down, used
- together with -COLUMN. The input
+ together with -COLS. The input
one
two
three
@@ -131,7 +131,7 @@
-b Balance columns on the last page.
-b is no longer an independent option. It's always used
- together with -COLUMN (unless -a is used) to get a
+ together with -COLS (unless -a is used) to get a
consistent formulation with "FF set by hand" in input
files. Each formfeed found terminates the number of lines
to be read with the actual page. The situation for
@@ -174,7 +174,7 @@
-J, --join-lines Merge lines of full length, turns off -W/-w
line truncation, no column alignment, --sep-string[=STRING]
sets separators, works with all column options
- (-COLUMN | -a -COLUMN | -m).
+ (-COLS | -a -COLS | -m).
-J has been introduced (together with -W and --sep-string) to
disentangle the old (POSIX compliant) options -w, -s
along with the 3 column options.
@@ -246,7 +246,7 @@
CHAR is the TAB character without -w and 'no char' with -w.
Without '-s' default separator 'space' is set.
-s[CHAR] turns off line truncation of all 3 column options
- (-COLUMN|-a -COLUMN|-m) except -w is set. That is a POSIX
+ (-COLS|-a -COLS|-m) except -w is set. That is a POSIX
compliant formulation. The source code translates -s into
the new options -S and -J, also -W if required.
@@ -287,7 +287,7 @@
Set the page width to PAGE_WIDTH characters. That's valid
with and without a column option. Text lines will be
truncated, unless -J is used. Together with one of the
- column options (-COLUMN| -a -COLUMN| -m) column alignment
+ column options (-COLS| -a -COLS| -m) column alignment
is always used.
Default is 72 characters.
Without -W PAGE_WIDTH
@@ -309,7 +309,6 @@
#include <config.h>
-#include <ctype.h>
#include <getopt.h>
#include <sys/types.h>
#include "system.h"
@@ -742,33 +741,33 @@ static char const short_options[] =
static struct option const long_options[] =
{
- {"pages", required_argument, nullptr, PAGES_OPTION},
- {"columns", required_argument, nullptr, COLUMNS_OPTION},
- {"across", no_argument, nullptr, 'a'},
- {"show-control-chars", no_argument, nullptr, 'c'},
- {"double-space", no_argument, nullptr, 'd'},
- {"date-format", required_argument, nullptr, 'D'},
- {"expand-tabs", optional_argument, nullptr, 'e'},
- {"form-feed", no_argument, nullptr, 'f'},
- {"header", required_argument, nullptr, 'h'},
- {"output-tabs", optional_argument, nullptr, 'i'},
- {"join-lines", no_argument, nullptr, 'J'},
- {"length", required_argument, nullptr, 'l'},
- {"merge", no_argument, nullptr, 'm'},
- {"number-lines", optional_argument, nullptr, 'n'},
- {"first-line-number", required_argument, nullptr, 'N'},
- {"indent", required_argument, nullptr, 'o'},
- {"no-file-warnings", no_argument, nullptr, 'r'},
- {"separator", optional_argument, nullptr, 's'},
- {"sep-string", optional_argument, nullptr, 'S'},
- {"omit-header", no_argument, nullptr, 't'},
- {"omit-pagination", no_argument, nullptr, 'T'},
- {"show-nonprinting", no_argument, nullptr, 'v'},
- {"width", required_argument, nullptr, 'w'},
- {"page-width", required_argument, nullptr, 'W'},
+ {"pages", required_argument, NULL, PAGES_OPTION},
+ {"columns", required_argument, NULL, COLUMNS_OPTION},
+ {"across", no_argument, NULL, 'a'},
+ {"show-control-chars", no_argument, NULL, 'c'},
+ {"double-space", no_argument, NULL, 'd'},
+ {"date-format", required_argument, NULL, 'D'},
+ {"expand-tabs", optional_argument, NULL, 'e'},
+ {"form-feed", no_argument, NULL, 'f'},
+ {"header", required_argument, NULL, 'h'},
+ {"output-tabs", optional_argument, NULL, 'i'},
+ {"join-lines", no_argument, NULL, 'J'},
+ {"length", required_argument, NULL, 'l'},
+ {"merge", no_argument, NULL, 'm'},
+ {"number-lines", optional_argument, NULL, 'n'},
+ {"first-line-number", required_argument, NULL, 'N'},
+ {"indent", required_argument, NULL, 'o'},
+ {"no-file-warnings", no_argument, NULL, 'r'},
+ {"separator", optional_argument, NULL, 's'},
+ {"sep-string", optional_argument, NULL, 'S'},
+ {"omit-header", no_argument, NULL, 't'},
+ {"omit-pagination", no_argument, NULL, 'T'},
+ {"show-nonprinting", no_argument, NULL, 'v'},
+ {"width", required_argument, NULL, 'w'},
+ {"page-width", required_argument, NULL, 'W'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
static _Noreturn void
@@ -864,7 +863,7 @@ main (int argc, char **argv)
char **file_names;
/* Accumulate the digits of old-style options like -99. */
- char *column_count_string = nullptr;
+ char *column_count_string = NULL;
idx_t n_digits = 0;
idx_t n_alloc = 0;
@@ -879,7 +878,7 @@ main (int argc, char **argv)
n_files = 0;
file_names = (argc > 1
? xnmalloc (argc - 1, sizeof (char *))
- : nullptr);
+ : NULL);
while (true)
{
@@ -921,7 +920,7 @@ main (int argc, char **argv)
break;
}
- case COLUMNS_OPTION: /* --columns=COLUMN */
+ case COLUMNS_OPTION: /* --columns=COLS */
{
parse_column_count (optarg);
@@ -929,7 +928,7 @@ main (int argc, char **argv)
short-named option syntax, e.g., -9, ensure that this
long-name-specified value overrides it. */
free (column_count_string);
- column_count_string = nullptr;
+ column_count_string = NULL;
n_alloc = 0;
break;
}
@@ -1132,7 +1131,7 @@ main (int argc, char **argv)
if (n_files == 0)
{
/* No file arguments specified; read from standard input. */
- print_files (0, nullptr);
+ print_files (0, NULL);
}
else
{
@@ -1181,7 +1180,7 @@ getoptarg (char *arg, char switch_char, char *character, int *number)
if (*arg)
{
long int tmp_long;
- strtol_error e = xstrtol (arg, nullptr, 10, &tmp_long, "");
+ strtol_error e = xstrtol (arg, NULL, 10, &tmp_long, "");
if (e == LONGINT_OK)
{
if (tmp_long <= 0)
@@ -1213,7 +1212,7 @@ init_parameters (int number_of_files)
extremities = false;
keep_FF = true;
}
- if (extremities == false)
+ if (! extremities)
lines_per_body = lines_per_page;
if (double_space)
@@ -1494,7 +1493,7 @@ open_file (char *name, COLUMN *p)
p->name = name;
p->fp = fopen (name, "r");
}
- if (p->fp == nullptr)
+ if (p->fp == NULL)
{
failed_opens = true;
if (!ignore_failed_opens)
@@ -1647,7 +1646,7 @@ print_files (int number_of_files, char **av)
static void
init_header (char const *filename, int desc)
{
- char *buf = nullptr;
+ char *buf = NULL;
struct stat st;
struct timespec t;
int ns;
@@ -1669,7 +1668,7 @@ init_header (char const *filename, int desc)
ns = t.tv_nsec;
if (localtime_rz (localtz, &t.tv_sec, &tm))
{
- ptrdiff_t len = nstrftime (nullptr, MIN (PTRDIFF_MAX, SIZE_MAX),
+ ptrdiff_t len = nstrftime (NULL, MIN (PTRDIFF_MAX, SIZE_MAX),
date_format, &tm, localtz, ns);
if (0 <= len)
{
@@ -2649,7 +2648,6 @@ char_to_clump (char c)
{
unsigned char uc = c;
char *s = clump_buff;
- int i;
char esc_buff[4];
int width;
int chars;
@@ -2664,7 +2662,7 @@ char_to_clump (char c)
if (untabify_input)
{
- for (i = width; i; --i)
+ for (int i = width; i; --i)
*s++ = ' ';
chars = width;
}
@@ -2683,7 +2681,7 @@ char_to_clump (char c)
chars = 4;
*s++ = '\\';
sprintf (esc_buff, "%03o", uc);
- for (i = 0; i <= 2; ++i)
+ for (int i = 0; i <= 2; ++i)
*s++ = esc_buff[i];
}
else if (use_cntrl_prefix)
@@ -2701,7 +2699,7 @@ char_to_clump (char c)
chars = 4;
*s++ = '\\';
sprintf (esc_buff, "%03o", uc);
- for (i = 0; i <= 2; ++i)
+ for (int i = 0; i <= 2; ++i)
*s++ = esc_buff[i];
}
}
@@ -2776,102 +2774,124 @@ Paginate or columnate FILE(s) for printing.\n\
emit_stdin_note ();
emit_mandatory_arg_note ();
- fputs (_("\
+ oputs (_("\n\
+FIRST_PAGE[:LAST_PAGE], --pages=FIRST_PAGE[:LAST_PAGE]\n\
- begin [stop] printing with page FIRST_[LAST_]PAGE\n\
- -COLUMN, --columns=COLUMN\n\
- output COLUMN columns and print columns down,\n\
- unless -a is used. Balance number of lines in the\n\
- columns on each page\n\
-"), stdout);
- fputs (_("\
- -a, --across print columns across rather than down, used together\n\
- with -COLUMN\n\
+ begin [stop] printing with page FIRST_[LAST_]PAGE\n\
+"));
+ oputs (_("\
+ -COLS, --columns=COLS\n\
+ output COLS columns and print columns down, unless -a is used.\n\
+ Balance number of lines in the columns on each page\n\
+"));
+ oputs (_("\
+ -a, --across\n\
+ print columns across rather than down, used together with -COLS\n\
+"));
+ oputs (_("\
-c, --show-control-chars\n\
- use hat notation (^G) and octal backslash notation\n\
+ use hat notation (^G) and octal backslash notation\n\
+"));
+ oputs (_("\
-d, --double-space\n\
- double space the output\n\
-"), stdout);
- fputs (_("\
+ double space the output\n\
+"));
+ oputs (_("\
-D, --date-format=FORMAT\n\
- use FORMAT for the header date\n\
+ use FORMAT for the header date\n\
+"));
+ oputs (_("\
-e[CHAR[WIDTH]], --expand-tabs[=CHAR[WIDTH]]\n\
- expand input CHARs (TABs) to tab WIDTH (8)\n\
+ expand input CHARs (TABs) to tab WIDTH (8)\n\
+"));
+ oputs (_("\
-F, -f, --form-feed\n\
- use form feeds instead of newlines to separate pages\n\
- (by a 3-line page header with -F or a 5-line header\n\
- and trailer without -F)\n\
-"), stdout);
- fputs (_("\
+ use form feeds instead of newlines to separate pages\n\
+ (by a 3-line page header with -F or a 5-line header\n\
+ and trailer without -F)\n\
+"));
+ oputs (_("\
-h, --header=HEADER\n\
- use a centered HEADER instead of filename in page header,\n\
- -h \"\" prints a blank line, don't use -h\"\"\n\
+ use a centered HEADER instead of filename in page header,\n\
+ -h \"\" prints a blank line, don't use -h\"\"\n\
+"));
+ oputs (_("\
-i[CHAR[WIDTH]], --output-tabs[=CHAR[WIDTH]]\n\
- replace spaces with CHARs (TABs) to tab WIDTH (8)\n\
- -J, --join-lines merge full lines, turns off -W line truncation, no column\n\
- alignment, --sep-string[=STRING] sets separators\n\
-"), stdout);
- fputs (_("\
+ replace spaces with CHARs (TABs) to tab WIDTH (8)\n\
+"));
+ oputs (_("\
+ -J, --join-lines\n\
+ merge full lines, turns off -W line truncation,\n\
+ no column alignment, --sep-string[=STRING] sets separators\n\
+"));
+ oputs (_("\
-l, --length=PAGE_LENGTH\n\
- set the page length to PAGE_LENGTH (66) lines\n\
- (default number of lines of text 56, and with -F 63).\n\
- implies -t if PAGE_LENGTH <= 10\n\
-"), stdout);
- fputs (_("\
- -m, --merge print all files in parallel, one in each column,\n\
- truncate lines, but join lines of full length with -J\n\
-"), stdout);
- fputs (_("\
+ set the page length to PAGE_LENGTH (66) lines\n\
+ (default number of lines of text 56, and with -F 63).\n\
+ implies -t if PAGE_LENGTH <= 10\n\
+"));
+ oputs (_("\
+ -m, --merge\n\
+ print all files in parallel, one in each column,\n\
+ truncate lines, but join lines of full length with -J\n\
+"));
+ oputs (_("\
-n[SEP[DIGITS]], --number-lines[=SEP[DIGITS]]\n\
- number lines, use DIGITS (5) digits, then SEP (TAB),\n\
- default counting starts with 1st line of input file\n\
+ number lines, use DIGITS (5) digits, then SEP (TAB),\n\
+ default counting starts with 1st line of input file\n\
+"));
+ oputs (_("\
-N, --first-line-number=NUMBER\n\
- start counting with NUMBER at 1st line of first\n\
- page printed (see +FIRST_PAGE)\n\
-"), stdout);
- fputs (_("\
+ start counting with NUMBER at 1st line of first\n\
+ page printed (see +FIRST_PAGE)\n\
+"));
+ oputs (_("\
-o, --indent=MARGIN\n\
- offset each line with MARGIN (zero) spaces, do not\n\
- affect -w or -W, MARGIN will be added to PAGE_WIDTH\n\
+ offset each line with MARGIN (zero) spaces, do not\n\
+ affect -w or -W, MARGIN will be added to PAGE_WIDTH\n\
+"));
+ oputs (_("\
-r, --no-file-warnings\n\
- omit warning when a file cannot be opened\n\
-"), stdout);
- fputs (_("\
+ omit warning when a file cannot be opened\n\
+"));
+ oputs (_("\
-s[CHAR], --separator[=CHAR]\n\
- separate columns by a single character, default for CHAR\n\
- is the <TAB> character without -w and \'no char\' with -w.\
-\n\
- -s[CHAR] turns off line truncation of all 3 column\n\
- options (-COLUMN|-a -COLUMN|-m) except -w is set\n\
-"), stdout);
- fputs (_("\
+ separate columns by a single character, default for CHAR\n\
+ is the <TAB> character without -w and \'no char\' with -w.\n\
+ -s[CHAR] turns off line truncation of all 3 column\n\
+ options (-COLS|-a -COLS|-m) except -w is set\n\
+"));
+ oputs (_("\
-S[STRING], --sep-string[=STRING]\n\
- separate columns by STRING,\n\
- without -S: Default separator <TAB> with -J and <space>\n\
- otherwise (same as -S\" \"), no effect on column options\n\
-"), stdout);
- fputs (_("\
- -t, --omit-header omit page headers and trailers;\n\
- implied if PAGE_LENGTH <= 10\n\
-"), stdout);
- fputs (_("\
+ separate columns by STRING,\n\
+ without -S: Default separator <TAB> with -J and <space>\n\
+ otherwise (same as -S\" \"), no effect on column options\n\
+"));
+ oputs (_("\
+ -t, --omit-header\n\
+ omit page headers and trailers; implied if PAGE_LENGTH <= 10\n\
+"));
+ oputs (_("\
-T, --omit-pagination\n\
- omit page headers and trailers, eliminate any pagination\n\
- by form feeds set in input files\n\
+ omit page headers and trailers,\n\
+ eliminate any pagination by form feeds set in input files\n\
+"));
+ oputs (_("\
-v, --show-nonprinting\n\
- use octal backslash notation\n\
+ use octal backslash notation\n\
+"));
+ oputs (_("\
-w, --width=PAGE_WIDTH\n\
- set page width to PAGE_WIDTH (72) characters for\n\
- multiple text-column output only, -s[char] turns off (72)\n\
-"), stdout);
- fputs (_("\
+ set page width to PAGE_WIDTH (72) characters for\n\
+ multiple text-column output only, -s[char] turns off (72)\n\
+"));
+ oputs (_("\
-W, --page-width=PAGE_WIDTH\n\
- set page width to PAGE_WIDTH (72) characters always,\n\
- truncate lines, except -J option is set, no interference\n\
- with -S or -s\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ set page width to PAGE_WIDTH (72) characters always,\n\
+ truncate lines, except -J option is set,\n\
+ no interference with -S or -s\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
diff --git a/src/printenv.c b/src/printenv.c
index e3d2f5de5..1f68dd2cc 100644
--- a/src/printenv.c
+++ b/src/printenv.c
@@ -1,5 +1,5 @@
/* printenv -- print all or part of environment
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -46,10 +46,10 @@ enum { PRINTENV_FAILURE = 2 };
static struct option const longopts[] =
{
- {"null", no_argument, nullptr, '0'},
+ {"null", no_argument, NULL, '0'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -66,11 +66,12 @@ If no VARIABLE is specified, print name and value pairs for them all.\n\
\n\
"),
program_name);
- fputs (_("\
- -0, --null end each output line with NUL, not newline\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -0, --null\n\
+ end each output line with NUL, not newline\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
emit_ancillary_info (PROGRAM_NAME);
}
@@ -80,11 +81,6 @@ If no VARIABLE is specified, print name and value pairs for them all.\n\
int
main (int argc, char **argv)
{
- char **env;
- char *ep, *ap;
- int i;
- bool ok;
- int optc;
bool opt_nul_terminate_output = false;
initialize_main (&argc, &argv);
@@ -96,7 +92,8 @@ main (int argc, char **argv)
initialize_exit_failure (PRINTENV_FAILURE);
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "+iu:0", longopts, nullptr)) != -1)
+ int optc;
+ while ((optc = getopt_long (argc, argv, "+iu:0", longopts, NULL)) != -1)
{
switch (optc)
{
@@ -110,17 +107,21 @@ main (int argc, char **argv)
}
}
+ bool ok;
if (optind >= argc)
{
- for (env = environ; *env != nullptr; ++env)
- printf ("%s%c", *env, opt_nul_terminate_output ? '\0' : '\n');
+ for (char **env = environ; *env != NULL; ++env)
+ {
+ fputs (*env, stdout);
+ putchar (opt_nul_terminate_output ? '\0' : '\n');
+ }
ok = true;
}
else
{
int matches = 0;
- for (i = optind; i < argc; ++i)
+ for (int i = optind; i < argc; ++i)
{
bool matched = false;
@@ -128,16 +129,16 @@ main (int argc, char **argv)
if (strchr (argv[i], '='))
continue;
- for (env = environ; *env; ++env)
+ for (char **env = environ; *env; ++env)
{
- ep = *env;
- ap = argv[i];
+ char const *ep = *env;
+ char const *ap = argv[i];
while (*ep != '\0' && *ap != '\0' && *ep++ == *ap++)
{
if (*ep == '=' && *ap == '\0')
{
- printf ("%s%c", ep + 1,
- opt_nul_terminate_output ? '\0' : '\n');
+ fputs (ep + 1, stdout);
+ putchar (opt_nul_terminate_output ? '\0' : '\n');
matched = true;
break;
}
diff --git a/src/printf.c b/src/printf.c
index 5ac9e13ee..5fb4c7c86 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -1,5 +1,5 @@
/* printf - format and print data
- Copyright (C) 1990-2025 Free Software Foundation, Inc.
+ Copyright (C) 1990-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -59,8 +59,8 @@ Usage: %s FORMAT [ARGUMENT]...\n\
Print ARGUMENT(s) according to FORMAT, or execute according to OPTION:\n\
\n\
"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
FORMAT controls the output as in C printf. Interpreted sequences are:\n\
@@ -69,7 +69,7 @@ FORMAT controls the output as in C printf. Interpreted sequences are:\n\
"), stdout);
fputs (_("\
\\\\ backslash\n\
- \\a alert (BEL)\n\
+ \\a alert (bell)\n\
\\b backspace\n\
\\c produce no further output\n\
\\e escape\n\
@@ -669,7 +669,7 @@ print_formatted (char const *format, int argc, char **argv)
print_direc (direc, *ac.f,
have_field_width, field_width,
have_precision, precision,
- ac.curr_arg < argc ? argv[ac.curr_arg] : nullptr);
+ ac.curr_arg < argc ? argv[ac.curr_arg] : NULL);
break;
@@ -700,9 +700,7 @@ main (int argc, char **argv)
atexit (close_stdout);
- exit_status = EXIT_SUCCESS;
-
- posixly_correct = (getenv ("POSIXLY_CORRECT") != nullptr);
+ posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
/* We directly parse options, rather than use parse_long_options, in
order to avoid accepting abbreviations. */
@@ -714,7 +712,7 @@ main (int argc, char **argv)
if (streq (argv[1], "--version"))
{
version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version, AUTHORS,
- (char *) nullptr);
+ (char *) NULL);
return EXIT_SUCCESS;
}
}
diff --git a/src/prog-fprintf.c b/src/prog-fprintf.c
index 36d8c1273..cc1fa3bd4 100644
--- a/src/prog-fprintf.c
+++ b/src/prog-fprintf.c
@@ -1,5 +1,5 @@
/* prog-fprintf.c - common formatting output functions and definitions
- Copyright (C) 2008-2025 Free Software Foundation, Inc.
+ Copyright (C) 2008-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/prog-fprintf.h b/src/prog-fprintf.h
index a20c65ba2..b124b91c5 100644
--- a/src/prog-fprintf.h
+++ b/src/prog-fprintf.h
@@ -1,5 +1,5 @@
/* prog-fprintf.h - common formatting output functions and definitions
- Copyright (C) 2008-2025 Free Software Foundation, Inc.
+ Copyright (C) 2008-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/ptx.c b/src/ptx.c
index 463f508c7..8c7d1535a 100644
--- a/src/ptx.c
+++ b/src/ptx.c
@@ -1,5 +1,5 @@
/* Permuted index for GNU, with keywords in their context.
- Copyright (C) 1990-2025 Free Software Foundation, Inc.
+ Copyright (C) 1990-2026 Free Software Foundation, Inc.
François Pinard <pinard@iro.umontreal.ca>, 1988.
This program is free software: you can redistribute it and/or modify
@@ -19,7 +19,6 @@
#include <config.h>
-#include <ctype.h>
#include <getopt.h>
#include <sys/types.h>
#include "system.h"
@@ -27,6 +26,7 @@
#include "argmatch.h"
#include "c-ctype.h"
#include "fadvise.h"
+#include "octhexdigits.h"
#include "quote.h"
#include "read-file.h"
#include "stdio--.h"
@@ -43,11 +43,6 @@
/* Number of possible characters in a byte. */
#define CHAR_SET_SIZE 256
-#define ISODIGIT(C) ((C) >= '0' && (C) <= '7')
-#define HEXTOBIN(C) ((C) >= 'a' && (C) <= 'f' ? (C)-'a'+10 \
- : (C) >= 'A' && (C) <= 'F' ? (C)-'A'+10 : (C)-'0')
-#define OCTTOBIN(C) ((C) - '0')
-
/* Debugging the memory allocator. */
#if WITH_DMALLOC
@@ -76,7 +71,7 @@ static bool gnu_extensions = true; /* trigger all GNU extensions */
static bool auto_reference = false; /* refs are 'file_name:line_number:' */
static bool input_reference = false; /* refs at beginning of input lines */
static bool right_reference = false; /* output refs after right context */
-static idx_t line_width = 72; /* output line width in characters */
+static idx_t line_width = -1; /* output line width in characters */
static idx_t gap_size = 3; /* number of spaces between output fields */
static char const *truncation_string = "/";
/* string used to mark line truncations */
@@ -85,9 +80,9 @@ static enum Format output_format = UNKNOWN_FORMAT;
/* output format */
static bool ignore_case = false; /* fold lower to upper for sorting */
-static char const *break_file = nullptr; /* name of the 'Break chars' file */
-static char const *only_file = nullptr; /* name of the 'Only words' file */
-static char const *ignore_file = nullptr; /* name of the 'Ignore words' file */
+static char const *break_file = NULL; /* name of the 'Break chars' file */
+static char const *only_file = NULL; /* name of the 'Only words' file */
+static char const *ignore_file = NULL; /* name of the 'Ignore words' file */
/* Options that use regular expressions. */
struct regex_data
@@ -186,7 +181,7 @@ static BLOCK *text_buffers; /* files to study */
{ \
regoff_t count; \
count = re_match (&word_regex.pattern, cursor, limit - cursor, \
- 0, nullptr); \
+ 0, NULL); \
if (count == -2) \
matcher_error (); \
cursor += count == -1 ? 1 : count; \
@@ -314,7 +309,7 @@ unescape_string (char *string)
for (length = 0, string++;
length < 3 && c_isxdigit (*string);
length++, string++)
- value = value * 16 + HEXTOBIN (*string);
+ value = value * 16 + fromhex (*string);
if (length == 0)
{
*cursor++ = '\\';
@@ -327,9 +322,9 @@ unescape_string (char *string)
case '0': /* \0ooo escape, 3 chars maximum */
value = 0;
for (length = 0, string++;
- length < 3 && ISODIGIT (*string);
+ length < 3 && isoct (*string);
length++, string++)
- value = value * 8 + OCTTOBIN (*string);
+ value = value * 8 + fromoct (*string);
*cursor++ = value;
break;
@@ -401,10 +396,10 @@ compile_regex (struct regex_data *regex)
char const *string = regex->string;
char const *message;
- pattern->buffer = nullptr;
+ pattern->buffer = NULL;
pattern->allocated = 0;
pattern->fastmap = regex->fastmap;
- pattern->translate = ignore_case ? folded_chars : nullptr;
+ pattern->translate = ignore_case ? folded_chars : NULL;
message = re_compile_pattern (string, strlen (string), pattern);
if (message)
@@ -425,12 +420,10 @@ compile_regex (struct regex_data *regex)
static void
initialize_regex (void)
{
- int character; /* character value */
-
/* Initialize the case folding table. */
if (ignore_case)
- for (character = 0; character < CHAR_SET_SIZE; character++)
+ for (int character = 0; character < CHAR_SET_SIZE; character++)
folded_chars[character] = toupper (character);
/* Unless the user already provided a description of the end of line or
@@ -443,7 +436,7 @@ initialize_regex (void)
if (context_regex.string)
{
if (!*context_regex.string)
- context_regex.string = nullptr;
+ context_regex.string = NULL;
}
else if (gnu_extensions && !input_reference)
context_regex.string = "[.?!][]\"')}]*\\($\\|\t\\| \\)[ \t\n]*";
@@ -470,7 +463,7 @@ initialize_regex (void)
/* Simulate \w+. */
- for (character = 0; character < CHAR_SET_SIZE; character++)
+ for (int character = 0; character < CHAR_SET_SIZE; character++)
word_fastmap[character] = !! isalpha (character);
}
else
@@ -631,14 +624,14 @@ static void
digest_break_file (char const *file_name)
{
BLOCK file_contents; /* to receive a copy of the file */
- char *cursor; /* cursor in file copy */
swallow_file_in_memory (file_name, &file_contents);
/* Make the fastmap and record the file contents in it. */
memset (word_fastmap, 1, CHAR_SET_SIZE);
- for (cursor = file_contents.start; cursor < file_contents.end; cursor++)
+ for (char *cursor = file_contents.start; cursor < file_contents.end;
+ cursor++)
word_fastmap[to_uchar (*cursor)] = 0;
if (!gnu_extensions)
@@ -676,7 +669,7 @@ digest_word_file (char const *file_name, WORD_TABLE *table)
swallow_file_in_memory (file_name, &file_contents);
- table->start = nullptr;
+ table->start = NULL;
table->alloc = 0;
table->length = 0;
@@ -724,8 +717,7 @@ digest_word_file (char const *file_name, WORD_TABLE *table)
static void
find_occurs_in_text (int file_index)
{
- char *cursor; /* for scanning the source text */
- char *scan; /* for scanning the source text also */
+ char *scan; /* for scanning the source text */
char *line_start; /* start of the current input line */
char *line_scan; /* newlines scanned until this point */
idx_t reference_length; /* length of reference in input mode */
@@ -766,7 +758,7 @@ find_occurs_in_text (int file_index)
/* Process the whole buffer, one line or one sentence at a time. */
- for (cursor = text_buffer->start;
+ for (char *cursor = text_buffer->start;
cursor < text_buffer->end;
cursor = next_context_start)
{
@@ -1011,12 +1003,10 @@ print_spaces (ptrdiff_t number)
static void
print_field (BLOCK field)
{
- char *cursor; /* Cursor in field to print */
-
/* Whitespace is not really compressed. Instead, each white space
character (tab, vt, ht etc.) is printed as one single space. */
- for (cursor = field.start; cursor < field.end; cursor++)
+ for (char *cursor = field.start; cursor < field.end; cursor++)
{
unsigned char character = *cursor;
if (edited_flag[character])
@@ -1127,7 +1117,7 @@ fix_output_parameters (void)
if (truncation_string && *truncation_string)
truncation_string_length = strlen (truncation_string);
else
- truncation_string = nullptr;
+ truncation_string = NULL;
if (gnu_extensions)
{
@@ -1342,8 +1332,8 @@ define_all_fields (OCCURS *occurs)
/* No place left for a tail field. */
- tail.start = nullptr;
- tail.end = nullptr;
+ tail.start = NULL;
+ tail.end = NULL;
tail_truncation = false;
}
@@ -1381,8 +1371,8 @@ define_all_fields (OCCURS *occurs)
/* No place left for a head field. */
- head.start = nullptr;
- head.end = nullptr;
+ head.start = NULL;
+ head.end = NULL;
head_truncation = false;
}
@@ -1620,12 +1610,12 @@ generate_all_output (void)
line contexts or references are not used, in which case these variables
would never be computed. */
- tail.start = nullptr;
- tail.end = nullptr;
+ tail.start = NULL;
+ tail.end = NULL;
tail_truncation = false;
- head.start = nullptr;
- head.end = nullptr;
+ head.start = NULL;
+ head.end = NULL;
head_truncation = false;
/* Loop over all keyword occurrences. */
@@ -1690,36 +1680,60 @@ Output a permuted index, including context, of the words in the input files.\n\
emit_stdin_note ();
emit_mandatory_arg_note ();
- fputs (_("\
+ oputs (_("\
-A, --auto-reference output automatically generated references\n\
+"));
+ oputs (_("\
-G, --traditional behave more like System V 'ptx'\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-F, --flag-truncation=STRING use STRING for flagging line truncations.\n\
The default is '/'\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-M, --macro-name=STRING macro name to use instead of 'xx'\n\
+"));
+ oputs (_("\
-O, --format=roff generate output as roff directives\n\
+"));
+ oputs (_("\
-R, --right-side-refs put references at right, not counted in -w\n\
+"));
+ oputs (_("\
-S, --sentence-regexp=REGEXP for end of lines or end of sentences\n\
+"));
+ oputs (_("\
-T, --format=tex generate output as TeX directives\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-W, --word-regexp=REGEXP use REGEXP to match each keyword\n\
+"));
+ oputs (_("\
-b, --break-file=FILE word break characters in this FILE\n\
+"));
+ oputs (_("\
-f, --ignore-case fold lower case to upper case for sorting\n\
+"));
+ oputs (_("\
-g, --gap-size=NUMBER gap size in columns between output fields\n\
+"));
+ oputs (_("\
-i, --ignore-file=FILE read ignore word list from FILE\n\
+"));
+ oputs (_("\
-o, --only-file=FILE read only word list from this FILE\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-r, --references first field of each line is a reference\n\
- -t, --typeset-mode - not implemented -\n\
+"));
+ oputs (_("\
+ -t, --typeset-mode change the default width from 72 to 100\n\
+"));
+ oputs (_("\
-w, --width=NUMBER output width in columns, reference excluded\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -1730,33 +1744,40 @@ Output a permuted index, including context, of the words in the input files.\n\
| strings, then launch execution. |
`----------------------------------------------------------------------*/
+/* For long options that have no equivalent short option, use a
+ non-character as a pseudo short option, starting with CHAR_MAX + 1. */
+enum
+{
+ FORMAT_OPTION = CHAR_MAX + 1
+};
+
/* Long options equivalences. */
static struct option const long_options[] =
{
- {"auto-reference", no_argument, nullptr, 'A'},
- {"break-file", required_argument, nullptr, 'b'},
- {"flag-truncation", required_argument, nullptr, 'F'},
- {"ignore-case", no_argument, nullptr, 'f'},
- {"gap-size", required_argument, nullptr, 'g'},
- {"ignore-file", required_argument, nullptr, 'i'},
- {"macro-name", required_argument, nullptr, 'M'},
- {"only-file", required_argument, nullptr, 'o'},
- {"references", no_argument, nullptr, 'r'},
- {"right-side-refs", no_argument, nullptr, 'R'},
- {"format", required_argument, nullptr, 10},
- {"sentence-regexp", required_argument, nullptr, 'S'},
- {"traditional", no_argument, nullptr, 'G'},
- {"typeset-mode", no_argument, nullptr, 't'},
- {"width", required_argument, nullptr, 'w'},
- {"word-regexp", required_argument, nullptr, 'W'},
+ {"auto-reference", no_argument, NULL, 'A'},
+ {"break-file", required_argument, NULL, 'b'},
+ {"flag-truncation", required_argument, NULL, 'F'},
+ {"ignore-case", no_argument, NULL, 'f'},
+ {"gap-size", required_argument, NULL, 'g'},
+ {"ignore-file", required_argument, NULL, 'i'},
+ {"macro-name", required_argument, NULL, 'M'},
+ {"only-file", required_argument, NULL, 'o'},
+ {"references", no_argument, NULL, 'r'},
+ {"right-side-refs", no_argument, NULL, 'R'},
+ {"format", required_argument, NULL, FORMAT_OPTION},
+ {"sentence-regexp", required_argument, NULL, 'S'},
+ {"traditional", no_argument, NULL, 'G'},
+ {"typeset-mode", no_argument, NULL, 't'},
+ {"width", required_argument, NULL, 'w'},
+ {"word-regexp", required_argument, NULL, 'W'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0},
+ {NULL, 0, NULL, 0},
};
static char const *const format_args[] =
{
- "roff", "tex", nullptr
+ "roff", "tex", NULL
};
static enum Format const format_vals[] =
@@ -1781,7 +1802,7 @@ main (int argc, char **argv)
atexit (close_stdout);
while (optchar = getopt_long (argc, argv, "AF:GM:ORS:TW:b:i:fg:o:trw:",
- long_options, nullptr),
+ long_options, NULL),
optchar != EOF)
{
switch (optchar)
@@ -1804,7 +1825,7 @@ main (int argc, char **argv)
case 'g':
{
intmax_t tmp;
- if (! (xstrtoimax (optarg, nullptr, 0, &tmp, "") == LONGINT_OK
+ if (! (xstrtoimax (optarg, NULL, 0, &tmp, "") == LONGINT_OK
&& 0 < tmp && tmp <= IDX_MAX))
error (EXIT_FAILURE, 0, _("invalid gap width: %s"),
quote (optarg));
@@ -1825,13 +1846,14 @@ main (int argc, char **argv)
break;
case 't':
- /* Yet to understand... */
+ if (line_width < 0)
+ line_width = 100;
break;
case 'w':
{
intmax_t tmp;
- if (! (xstrtoimax (optarg, nullptr, 0, &tmp, "") == LONGINT_OK
+ if (! (xstrtoimax (optarg, NULL, 0, &tmp, "") == LONGINT_OK
&& 0 < tmp && tmp <= IDX_MAX))
error (EXIT_FAILURE, 0, _("invalid line width: %s"),
quote (optarg));
@@ -1873,10 +1895,10 @@ main (int argc, char **argv)
word_regex.string = optarg;
unescape_string (optarg);
if (!*word_regex.string)
- word_regex.string = nullptr;
+ word_regex.string = NULL;
break;
- case 10:
+ case FORMAT_OPTION:
output_format = XARGMATCH ("--format", optarg,
format_args, format_vals);
break;
@@ -1887,6 +1909,9 @@ main (int argc, char **argv)
}
}
+ if (line_width < 0)
+ line_width = 72;
+
/* Process remaining arguments. If GNU extensions are enabled, process
all arguments as input parameters. If disabled, accept at most two
arguments, the second of which is an output parameter. */
@@ -1900,7 +1925,7 @@ main (int argc, char **argv)
file_line_count = xmalloc (sizeof *file_line_count);
text_buffers = xmalloc (sizeof *text_buffers);
number_input_files = 1;
- input_file_name[0] = nullptr;
+ input_file_name[0] = NULL;
}
else if (gnu_extensions)
{
@@ -1912,7 +1937,7 @@ main (int argc, char **argv)
for (file_index = 0; file_index < number_input_files; file_index++)
{
if (!*argv[optind] || streq (argv[optind], "-"))
- input_file_name[file_index] = nullptr;
+ input_file_name[file_index] = NULL;
else
input_file_name[file_index] = argv[optind];
optind++;
@@ -1928,7 +1953,7 @@ main (int argc, char **argv)
file_line_count = xmalloc (sizeof *file_line_count);
text_buffers = xmalloc (sizeof *text_buffers);
if (!*argv[optind] || streq (argv[optind], "-"))
- input_file_name[0] = nullptr;
+ input_file_name[0] = NULL;
else
input_file_name[0] = argv[optind];
optind++;
@@ -1974,14 +1999,14 @@ main (int argc, char **argv)
{
digest_word_file (ignore_file, &ignore_table);
if (ignore_table.length == 0)
- ignore_file = nullptr;
+ ignore_file = NULL;
}
if (only_file)
{
digest_word_file (only_file, &only_table);
if (only_table.length == 0)
- only_file = nullptr;
+ only_file = NULL;
}
/* Prepare to study all the input files. */
diff --git a/src/pwd.c b/src/pwd.c
index 50f98cff2..486e8e670 100644
--- a/src/pwd.c
+++ b/src/pwd.c
@@ -1,5 +1,5 @@
/* pwd - print current directory
- Copyright (C) 1994-2025 Free Software Foundation, Inc.
+ Copyright (C) 1994-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -38,11 +38,11 @@ struct file_name
static struct option const longopts[] =
{
- {"logical", no_argument, nullptr, 'L'},
- {"physical", no_argument, nullptr, 'P'},
+ {"logical", no_argument, NULL, 'L'},
+ {"physical", no_argument, NULL, 'P'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -57,14 +57,16 @@ usage (int status)
Print the full filename of the current working directory.\n\
\n\
"), stdout);
- fputs (_("\
- -L, --logical use PWD from environment, even if it contains symlinks\n\
-"), stdout);
- fputs (_("\
- -P, --physical resolve all symlinks\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -L, --logical\n\
+ use PWD from environment, even if it contains symlinks\n\
+"));
+ oputs (_("\
+ -P, --physical\n\
+ resolve all symlinks\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\n\
If no option is specified, -P is assumed.\n\
"), stdout);
@@ -104,13 +106,13 @@ file_name_prepend (struct file_name *p, char const *s, size_t s_len)
idx_t n_free = p->start - p->buf;
if (n_free < 1 + s_len)
{
- /* Call xpalloc with nullptr not p->buf, since with the latter
+ /* Call xpalloc with NULL not p->buf, since with the latter
we'd end up copying the data twice: once via realloc, then again
- to align it with the end of the new buffer. By passing nullptr we
+ to align it with the end of the new buffer. By passing NULL we
copy it only once. */
idx_t n_used = p->n_alloc - n_free;
- char *buf = xpalloc (nullptr, &p->n_alloc, 1 + s_len - n_free, -1, 1);
- p->start = memcpy (buf + p->n_alloc - n_free, p->start, n_used);
+ char *buf = xpalloc (NULL, &p->n_alloc, 1 + s_len - n_free, -1, 1);
+ p->start = memcpy (buf + p->n_alloc - n_used, p->start, n_used);
free (p->buf);
p->buf = buf;
}
@@ -151,39 +153,32 @@ static void
find_dir_entry (struct stat *dot_sb, struct file_name *file_name,
size_t parent_height)
{
- DIR *dirp;
- int fd;
- struct stat parent_sb;
- bool use_lstat;
- bool found;
-
- dirp = opendir ("..");
- if (dirp == nullptr)
+ DIR *dirp = opendir ("..");
+ if (dirp == NULL)
error (EXIT_FAILURE, errno, _("cannot open directory %s"),
quote (nth_parent (parent_height)));
- fd = dirfd (dirp);
+ int fd = dirfd (dirp);
if ((0 <= fd ? fchdir (fd) : chdir ("..")) < 0)
error (EXIT_FAILURE, errno, _("failed to chdir to %s"),
quote (nth_parent (parent_height)));
+ struct stat parent_sb;
if ((0 <= fd ? fstat (fd, &parent_sb) : stat (".", &parent_sb)) < 0)
error (EXIT_FAILURE, errno, _("failed to stat %s"),
quote (nth_parent (parent_height)));
/* If parent and child directory are on different devices, then we
can't rely on d_ino for useful i-node numbers; use lstat instead. */
- use_lstat = (parent_sb.st_dev != dot_sb->st_dev);
+ bool use_lstat = (parent_sb.st_dev != dot_sb->st_dev);
- found = false;
+ bool found = false;
while (true)
{
struct dirent const *dp;
- struct stat ent_sb;
- ino_t ino;
errno = 0;
- if ((dp = readdir_ignoring_dot_and_dotdot (dirp)) == nullptr)
+ if ((dp = readdir_ignoring_dot_and_dotdot (dirp)) == NULL)
{
if (errno)
{
@@ -193,13 +188,14 @@ find_dir_entry (struct stat *dot_sb, struct file_name *file_name,
errno = e;
/* Arrange to give a diagnostic after exiting this loop. */
- dirp = nullptr;
+ dirp = NULL;
}
break;
}
- ino = D_INO (dp);
+ ino_t ino = D_INO (dp);
+ struct stat ent_sb;
if (ino == NOT_AN_INODE_NUMBER || use_lstat)
{
if (lstat (dp->d_name, &ent_sb) < 0)
@@ -223,7 +219,7 @@ find_dir_entry (struct stat *dot_sb, struct file_name *file_name,
}
}
- if (dirp == nullptr || closedir (dirp) != 0)
+ if (dirp == NULL || closedir (dirp) != 0)
{
/* Note that this diagnostic serves for both readdir
and closedir failures. */
@@ -268,12 +264,12 @@ robust_getcwd (struct file_name *file_name)
size_t height = 1;
struct dev_ino dev_ino_buf;
struct dev_ino *root_dev_ino = get_root_dev_ino (&dev_ino_buf);
- struct stat dot_sb;
- if (root_dev_ino == nullptr)
+ if (root_dev_ino == NULL)
error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
quoteaf ("/"));
+ struct stat dot_sb;
if (stat (".", &dot_sb) < 0)
error (EXIT_FAILURE, errno, _("failed to stat %s"), quoteaf ("."));
@@ -293,42 +289,40 @@ robust_getcwd (struct file_name *file_name)
/* Return PWD from the environment if it is acceptable for 'pwd -L'
- output, otherwise nullptr. */
-static char *
+ output, otherwise NULL. */
+static char const *
logical_getcwd (void)
{
- struct stat st1;
- struct stat st2;
- char *wd = getenv ("PWD");
- char *p;
+ char const *wd = getenv ("PWD");
/* Textual validation first. */
if (!wd || wd[0] != '/')
- return nullptr;
- p = wd;
+ return NULL;
+ char const *p = wd;
while ((p = strstr (p, "/.")))
{
if (!p[2] || p[2] == '/'
|| (p[2] == '.' && (!p[3] || p[3] == '/')))
- return nullptr;
+ return NULL;
p++;
}
/* System call validation. */
+ struct stat st1;
+ struct stat st2;
if (stat (wd, &st1) == 0 && stat (".", &st2) == 0 && psame_inode (&st1, &st2))
return wd;
- return nullptr;
+ return NULL;
}
int
main (int argc, char **argv)
{
- char *wd;
/* POSIX requires a default of -L, but most scripts expect -P.
Currently shells default to -L, while stand-alone
pwd implementations default to -P. */
- bool logical = (getenv ("POSIXLY_CORRECT") != nullptr);
+ bool logical = (getenv ("POSIXLY_CORRECT") != NULL);
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -340,7 +334,7 @@ main (int argc, char **argv)
while (true)
{
- int c = getopt_long (argc, argv, "LP", longopts, nullptr);
+ int c = getopt_long (argc, argv, "LP", longopts, NULL);
if (c == -1)
break;
switch (c)
@@ -366,7 +360,7 @@ main (int argc, char **argv)
if (logical)
{
- wd = logical_getcwd ();
+ char const *wd = logical_getcwd ();
if (wd)
{
puts (wd);
@@ -374,8 +368,8 @@ main (int argc, char **argv)
}
}
- wd = xgetcwd ();
- if (wd != nullptr)
+ char *wd = xgetcwd ();
+ if (wd != NULL)
{
puts (wd);
free (wd);
diff --git a/src/readlink.c b/src/readlink.c
index e77818592..3a2d6aaed 100644
--- a/src/readlink.c
+++ b/src/readlink.c
@@ -1,5 +1,5 @@
/* readlink -- display value of a symbolic link.
- Copyright (C) 2002-2025 Free Software Foundation, Inc.
+ Copyright (C) 2002-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -38,17 +38,17 @@ static bool verbose;
static struct option const longopts[] =
{
- {"canonicalize", no_argument, nullptr, 'f'},
- {"canonicalize-existing", no_argument, nullptr, 'e'},
- {"canonicalize-missing", no_argument, nullptr, 'm'},
- {"no-newline", no_argument, nullptr, 'n'},
- {"quiet", no_argument, nullptr, 'q'},
- {"silent", no_argument, nullptr, 's'},
- {"verbose", no_argument, nullptr, 'v'},
- {"zero", no_argument, nullptr, 'z'},
+ {"canonicalize", no_argument, NULL, 'f'},
+ {"canonicalize-existing", no_argument, NULL, 'e'},
+ {"canonicalize-missing", no_argument, NULL, 'm'},
+ {"no-newline", no_argument, NULL, 'n'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"silent", no_argument, NULL, 's'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"zero", no_argument, NULL, 'z'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -61,31 +61,47 @@ usage (int status)
printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
fputs (_("Print value of a symbolic link or canonical file name\n\n"),
stdout);
- fputs (_("\
- -f, --canonicalize canonicalize by following every symlink in\n\
- every component of the given name recursively;\
-\n\
- all but the last component must exist\n\
- -e, --canonicalize-existing canonicalize by following every symlink in\n\
- every component of the given name recursively,\
-\n\
- all components must exist\n\
-"), stdout);
- fputs (_("\
- -m, --canonicalize-missing canonicalize by following every symlink in\n\
- every component of the given name recursively,\
-\n\
- without requirements on components existence\n\
- -n, --no-newline do not output the trailing delimiter\n\
+ oputs (_("\
+ -f, --canonicalize\n\
+ canonicalize by following every symlink\n\
+ in every component of the given name recursively;\n\
+ all but the last component must exist\n\
+"));
+ oputs (_("\
+ -e, --canonicalize-existing\n\
+ canonicalize by following every symlink\n\
+ in every component of the given name recursively;\n\
+ all components must exist\n\
+"));
+ oputs (_("\
+ -m, --canonicalize-missing\n\
+ canonicalize by following every symlink\n\
+ in every component of the given name recursively,\n\
+ without requirements on components existence\n\
+"));
+ oputs (_("\
+ -n, --no-newline\n\
+ do not output the trailing delimiter\n\
+"));
+ oputs (_("\
-q, --quiet\n\
- -s, --silent suppress most error messages (on by default\n\
- if POSIXLY_CORRECT is not set)\n\
- -v, --verbose report error messages (on by default if\n\
- POSIXLY_CORRECT is set)\n\
- -z, --zero end each output line with NUL, not newline\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+"));
+ oputs (_("\
+ -s, --silent\n\
+ suppress most error messages\n\
+ (on by default if POSIXLY_CORRECT is not set)\n\
+"));
+ oputs (_("\
+ -v, --verbose\n\
+ report error messages\n\
+ (on by default if POSIXLY_CORRECT is set)\n\
+"));
+ oputs (_("\
+ -z, --zero\n\
+ end each output line with NUL, not newline\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -108,7 +124,7 @@ main (int argc, char **argv)
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "efmnqsvz", longopts, nullptr)) != -1)
+ while ((optc = getopt_long (argc, argv, "efmnqsvz", longopts, NULL)) != -1)
{
switch (optc)
{
@@ -156,7 +172,7 @@ main (int argc, char **argv)
/* POSIX requires a diagnostic message written to standard error and a
non-zero exit status when given a file that is not a symbolic link. */
- if (getenv ("POSIXLY_CORRECT") != nullptr)
+ if (getenv ("POSIXLY_CORRECT") != NULL)
verbose = true;
for (; optind < argc; ++optind)
@@ -171,6 +187,8 @@ main (int argc, char **argv)
if (! no_newline)
putchar (use_nuls ? '\0' : '\n');
free (value);
+ if (ferror (stdout))
+ write_error ();
}
else
{
diff --git a/src/realpath.c b/src/realpath.c
index 1f7882b49..8ad0ba68a 100644
--- a/src/realpath.c
+++ b/src/realpath.c
@@ -1,5 +1,5 @@
/* realpath - print the resolved path
- Copyright (C) 2011-2025 Free Software Foundation, Inc.
+ Copyright (C) 2011-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -44,20 +44,20 @@ static char const *can_relative_base;
static struct option const longopts[] =
{
- {"canonicalize", no_argument, nullptr, 'E'},
- {"canonicalize-existing", no_argument, nullptr, 'e'},
- {"canonicalize-missing", no_argument, nullptr, 'm'},
- {"relative-to", required_argument, nullptr, RELATIVE_TO_OPTION},
- {"relative-base", required_argument, nullptr, RELATIVE_BASE_OPTION},
- {"quiet", no_argument, nullptr, 'q'},
- {"strip", no_argument, nullptr, 's'},
- {"no-symlinks", no_argument, nullptr, 's'},
- {"zero", no_argument, nullptr, 'z'},
- {"logical", no_argument, nullptr, 'L'},
- {"physical", no_argument, nullptr, 'P'},
+ {"canonicalize", no_argument, NULL, 'E'},
+ {"canonicalize-existing", no_argument, NULL, 'e'},
+ {"canonicalize-missing", no_argument, NULL, 'm'},
+ {"relative-to", required_argument, NULL, RELATIVE_TO_OPTION},
+ {"relative-base", required_argument, NULL, RELATIVE_BASE_OPTION},
+ {"quiet", no_argument, NULL, 'q'},
+ {"strip", no_argument, NULL, 's'},
+ {"no-symlinks", no_argument, NULL, 's'},
+ {"zero", no_argument, NULL, 'z'},
+ {"logical", no_argument, NULL, 'L'},
+ {"physical", no_argument, NULL, 'P'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -71,22 +71,40 @@ usage (int status)
fputs (_("\
Print the resolved absolute file name.\n\
"), stdout);
- fputs (_("\
+ oputs (_("\
-E, --canonicalize all but the last component must exist (default)\
\n\
+"));
+ oputs (_("\
-e, --canonicalize-existing all components of the path must exist\n\
+"));
+ oputs (_("\
-m, --canonicalize-missing no path components need exist or be a directory\
\n\
+"));
+ oputs (_("\
-L, --logical resolve '..' components before symlinks\n\
+"));
+ oputs (_("\
-P, --physical resolve symlinks as encountered (default)\n\
+"));
+ oputs (_("\
-q, --quiet suppress most error messages\n\
+"));
+ oputs (_("\
--relative-to=DIR print the resolved path relative to DIR\n\
+"));
+ oputs (_("\
--relative-base=DIR print absolute paths unless paths below DIR\n\
+"));
+ oputs (_("\
-s, --strip, --no-symlinks don't expand symlinks\n\
+"));
+ oputs (_("\
-z, --zero end each output line with NUL, not newline\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -159,13 +177,16 @@ process_path (char const *fname, int can_mode)
if (!can_relative_to
|| (can_relative_base && !path_prefix (can_relative_base, can_fname))
- || (can_relative_to && !relpath (can_fname, can_relative_to, nullptr, 0)))
+ || (can_relative_to && !relpath (can_fname, can_relative_to, NULL, 0)))
fputs (can_fname, stdout);
putchar (use_nuls ? '\0' : '\n');
free (can_fname);
+ if (ferror (stdout))
+ write_error ();
+
return true;
}
@@ -174,8 +195,8 @@ main (int argc, char **argv)
{
bool ok = true;
int can_mode = CAN_ALL_BUT_LAST;
- char const *relative_to = nullptr;
- char const *relative_base = nullptr;
+ char const *relative_to = NULL;
+ char const *relative_base = NULL;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -187,7 +208,7 @@ main (int argc, char **argv)
while (true)
{
- int c = getopt_long (argc, argv, "EeLmPqsz", longopts, nullptr);
+ int c = getopt_long (argc, argv, "EeLmPqsz", longopts, NULL);
if (c == -1)
break;
switch (c)
@@ -270,7 +291,7 @@ main (int argc, char **argv)
{
free (base);
can_relative_base = can_relative_to;
- can_relative_to = nullptr;
+ can_relative_to = NULL;
}
}
diff --git a/src/relpath.c b/src/relpath.c
index c6a29b2b3..8cee00973 100644
--- a/src/relpath.c
+++ b/src/relpath.c
@@ -1,5 +1,5 @@
/* relpath - print the relative path
- Copyright (C) 2012-2025 Free Software Foundation, Inc.
+ Copyright (C) 2012-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/relpath.h b/src/relpath.h
index e9e79cfed..4fd171bd4 100644
--- a/src/relpath.h
+++ b/src/relpath.h
@@ -1,5 +1,5 @@
/* relpath - print the relative path
- Copyright (C) 2012-2025 Free Software Foundation, Inc.
+ Copyright (C) 2012-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/remove.c b/src/remove.c
index 86226b1d3..99696e5de 100644
--- a/src/remove.c
+++ b/src/remove.c
@@ -1,5 +1,5 @@
/* remove.c -- core functions for removing files and directories
- Copyright (C) 1988-2025 Free Software Foundation, Inc.
+ Copyright (C) 1988-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -375,8 +375,8 @@ fts_skip_tree (FTS *fts, FTSENT *ent)
static void
mark_ancestor_dirs (FTSENT *ent)
{
- FTSENT *p;
- for (p = ent->fts_parent; FTS_ROOTLEVEL <= p->fts_level; p = p->fts_parent)
+ for (FTSENT *p = ent->fts_parent; FTS_ROOTLEVEL <= p->fts_level;
+ p = p->fts_parent)
{
if (p->fts_number)
break;
@@ -490,7 +490,7 @@ rm_fts (FTS *fts, FTSENT *ent, struct rm_options const *x)
if (x->preserve_all_root)
{
bool failed = false;
- char *parent = file_name_concat (ent->fts_accpath, "..", nullptr);
+ char *parent = file_name_concat (ent->fts_accpath, "..", NULL);
struct stat statbuf;
if (!parent || lstat (parent, &statbuf))
@@ -611,14 +611,14 @@ rm (char *const *file, struct rm_options const *x)
if (x->one_file_system)
bit_flags |= FTS_XDEV;
- FTS *fts = xfts_open (file, bit_flags, nullptr);
+ FTS *fts = xfts_open (file, bit_flags, NULL);
while (true)
{
FTSENT *ent;
ent = fts_read (fts);
- if (ent == nullptr)
+ if (ent == NULL)
{
if (errno != 0)
{
diff --git a/src/remove.h b/src/remove.h
index 398013213..744c13ebb 100644
--- a/src/remove.h
+++ b/src/remove.h
@@ -1,6 +1,6 @@
/* Remove directory entries.
- Copyright (C) 1998-2025 Free Software Foundation, Inc.
+ Copyright (C) 1998-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/rm.c b/src/rm.c
index 2d08e71d9..e85caec5a 100644
--- a/src/rm.c
+++ b/src/rm.c
@@ -1,5 +1,5 @@
/* 'rm' file deletion utility for GNU.
- Copyright (C) 1988-2025 Free Software Foundation, Inc.
+ Copyright (C) 1988-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -60,32 +60,32 @@ enum interactive_type
static struct option const long_opts[] =
{
- {"force", no_argument, nullptr, 'f'},
- {"interactive", optional_argument, nullptr, INTERACTIVE_OPTION},
+ {"force", no_argument, NULL, 'f'},
+ {"interactive", optional_argument, NULL, INTERACTIVE_OPTION},
- {"one-file-system", no_argument, nullptr, ONE_FILE_SYSTEM},
- {"no-preserve-root", no_argument, nullptr, NO_PRESERVE_ROOT},
- {"preserve-root", optional_argument, nullptr, PRESERVE_ROOT},
+ {"one-file-system", no_argument, NULL, ONE_FILE_SYSTEM},
+ {"no-preserve-root", no_argument, NULL, NO_PRESERVE_ROOT},
+ {"preserve-root", optional_argument, NULL, PRESERVE_ROOT},
/* This is solely for testing. Do not document. */
/* It is relatively difficult to ensure that there is a tty on stdin.
Since rm acts differently depending on that, without this option,
it'd be harder to test the parts of rm that depend on that setting. */
- {"-presume-input-tty", no_argument, nullptr, PRESUME_INPUT_TTY_OPTION},
+ {"-presume-input-tty", no_argument, NULL, PRESUME_INPUT_TTY_OPTION},
- {"recursive", no_argument, nullptr, 'r'},
- {"dir", no_argument, nullptr, 'd'},
- {"verbose", no_argument, nullptr, 'v'},
+ {"recursive", no_argument, NULL, 'r'},
+ {"dir", no_argument, NULL, 'd'},
+ {"verbose", no_argument, NULL, 'v'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
static char const *const interactive_args[] =
{
"never", "no", "none",
"once",
- "always", "yes", nullptr
+ "always", "yes", NULL
};
static enum interactive_type const interactive_types[] =
{
@@ -132,34 +132,56 @@ usage (int status)
fputs (_("\
Remove (unlink) the FILE(s).\n\
\n\
- -f, --force ignore nonexistent files and arguments, never prompt\n\
- -i prompt before every removal\n\
"), stdout);
- fputs (_("\
- -I prompt once before removing more than three files, or\n\
- when removing recursively; less intrusive than -i,\n\
- while still giving protection against most mistakes\n\
- --interactive[=WHEN] prompt according to WHEN: never, once (-I), or\n\
- always (-i); without WHEN, prompt always\n\
-"), stdout);
- fputs (_("\
- --one-file-system when removing a hierarchy recursively, skip any\n\
- directory that is on a file system different from\n\
- that of the corresponding command line argument\n\
-"), stdout);
- fputs (_("\
- --no-preserve-root do not treat '/' specially\n\
- --preserve-root[=all] do not remove '/' (default);\n\
- with 'all', reject any command line argument\n\
- on a separate device from its parent\n\
-"), stdout);
- fputs (_("\
- -r, -R, --recursive remove directories and their contents recursively\n\
- -d, --dir remove empty directories\n\
- -v, --verbose explain what is being done\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -f, --force\n\
+ ignore nonexistent files and arguments, never prompt\n\
+"));
+ oputs (_("\
+ -i\n\
+ prompt before every removal\n\
+"));
+ oputs (_("\
+ -I\n\
+ prompt once before removing more than three files,\n\
+ or when removing recursively; less intrusive than -i,\n\
+ while still giving protection against most mistakes\n\
+"));
+ oputs (_("\
+ --interactive[=WHEN]\n\
+ prompt according to WHEN: never, once (-I), or always (-i);\n\
+ without WHEN, prompt always\n\
+"));
+ oputs (_("\
+ --one-file-system\n\
+ when removing a hierarchy recursively,\n\
+ skip any directory that is on a file system different\n\
+ from that of the corresponding command line argument\n\
+"));
+ oputs (_("\
+ --no-preserve-root\n\
+ do not treat '/' specially\n\
+"));
+ oputs (_("\
+ --preserve-root[=all]\n\
+ do not remove '/' (default);\n\
+ with 'all', reject any command line argument\n\
+ on a separate device from its parent\n\
+"));
+ oputs (_("\
+ -r, -R, --recursive\n\
+ remove directories and their contents recursively\n\
+"));
+ oputs (_("\
+ -d, --dir\n\
+ remove empty directories\n\
+"));
+ oputs (_("\
+ -v, --verbose\n\
+ explain what is being done\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
By default, rm does not remove directories. Use the --recursive (-r or -R)\n\
@@ -198,7 +220,7 @@ rm_option_init (struct rm_options *x)
x->one_file_system = false;
x->remove_empty_directories = false;
x->recursive = false;
- x->root_dev_ino = nullptr;
+ x->root_dev_ino = NULL;
x->preserve_all_root = false;
x->stdin_tty = isatty (STDIN_FILENO);
x->verbose = false;
@@ -229,7 +251,7 @@ main (int argc, char **argv)
/* Try to disable the ability to unlink a directory. */
priv_set_remove_linkdir ();
- while ((c = getopt_long (argc, argv, "dfirvIR", long_opts, nullptr)) != -1)
+ while ((c = getopt_long (argc, argv, "dfirvIR", long_opts, NULL)) != -1)
{
switch (c)
{
@@ -345,7 +367,7 @@ main (int argc, char **argv)
{
static struct dev_ino dev_ino_buf;
x.root_dev_ino = get_root_dev_ino (&dev_ino_buf);
- if (x.root_dev_ino == nullptr)
+ if (x.root_dev_ino == NULL)
error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
quoteaf ("/"));
}
diff --git a/src/rmdir.c b/src/rmdir.c
index 9ca3f7ed1..fa2236cca 100644
--- a/src/rmdir.c
+++ b/src/rmdir.c
@@ -1,6 +1,6 @@
/* rmdir -- remove directories
- Copyright (C) 1990-2025 Free Software Foundation, Inc.
+ Copyright (C) 1990-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -57,15 +57,15 @@ static struct option const longopts[] =
{
/* Don't name this '--force' because it's not close enough in meaning
to e.g. rm's -f option. */
- {"ignore-fail-on-non-empty", no_argument, nullptr,
+ {"ignore-fail-on-non-empty", no_argument, NULL,
IGNORE_FAIL_ON_NON_EMPTY_OPTION},
- {"path", no_argument, nullptr, 'p'}, /* Deprecated. */
- {"parents", no_argument, nullptr, 'p'},
- {"verbose", no_argument, nullptr, 'v'},
+ {"path", no_argument, NULL, 'p'}, /* Deprecated. */
+ {"parents", no_argument, NULL, 'p'},
+ {"verbose", no_argument, NULL, 'v'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* Return true if ERROR_NUMBER is one of the values associated
@@ -112,14 +112,13 @@ ignorable_failure (int error_number, char const *dir)
static bool
remove_parents (char *dir)
{
- char *slash;
bool ok = true;
strip_trailing_slashes (dir);
while (true)
{
- slash = strrchr (dir, '/');
- if (slash == nullptr)
+ char *slash = strrchr (dir, '/');
+ if (slash == NULL)
break;
/* Remove any characters after the slash, skipping any extra
slashes in a row. */
@@ -175,20 +174,21 @@ usage (int status)
Remove the DIRECTORY(ies), if they are empty.\n\
\n\
"), stdout);
- fputs (_("\
+ oputs (_("\
--ignore-fail-on-non-empty\n\
- ignore each failure to remove a non-empty directory\n\
-"), stdout);
- fputs (_("\
- -p, --parents remove DIRECTORY and its ancestors;\n\
- e.g., 'rmdir -p a/b' is similar to 'rmdir a/b a'\n\
-\n\
-"), stdout);
- fputs (_("\
- -v, --verbose output a diagnostic for every directory processed\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ ignore each failure to remove a non-empty directory\n\
+"));
+ oputs (_("\
+ -p, --parents\n\
+ remove DIRECTORY and its ancestors;\n\
+ e.g., 'rmdir -p a/b' is similar to 'rmdir a/b a'\n\
+"));
+ oputs (_("\
+ -v, --verbose\n\
+ output a diagnostic for every directory processed\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -197,9 +197,6 @@ Remove the DIRECTORY(ies), if they are empty.\n\
int
main (int argc, char **argv)
{
- bool ok = true;
- int optc;
-
initialize_main (&argc, &argv);
set_program_name (argv[0]);
setlocale (LC_ALL, "");
@@ -208,9 +205,8 @@ main (int argc, char **argv)
atexit (close_stdout);
- remove_empty_parents = false;
-
- while ((optc = getopt_long (argc, argv, "pv", longopts, nullptr)) != -1)
+ int optc;
+ while ((optc = getopt_long (argc, argv, "pv", longopts, NULL)) != -1)
{
switch (optc)
{
@@ -236,6 +232,7 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
+ bool ok = true;
for (; optind < argc; ++optind)
{
char *dir = argv[optind];
diff --git a/src/runcon.c b/src/runcon.c
index 32c419427..272f4b530 100644
--- a/src/runcon.c
+++ b/src/runcon.c
@@ -1,5 +1,5 @@
/* runcon -- run command with specified security context
- Copyright (C) 2005-2025 Free Software Foundation, Inc.
+ Copyright (C) 2005-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -56,14 +56,14 @@
static struct option const long_options[] =
{
- {"role", required_argument, nullptr, 'r'},
- {"type", required_argument, nullptr, 't'},
- {"user", required_argument, nullptr, 'u'},
- {"range", required_argument, nullptr, 'l'},
- {"compute", no_argument, nullptr, 'c'},
+ {"role", required_argument, NULL, 'r'},
+ {"type", required_argument, NULL, 't'},
+ {"user", required_argument, NULL, 'u'},
+ {"range", required_argument, NULL, 'l'},
+ {"compute", no_argument, NULL, 'c'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -74,8 +74,8 @@ usage (int status)
else
{
printf (_("\
-Usage: %s CONTEXT COMMAND [args]\n\
- or: %s [ -c ] [-u USER] [-r ROLE] [-t TYPE] [-l RANGE] COMMAND [args]\n\
+Usage: %s [CONTEXT COMMAND [ARG]...]\n\
+ or: %s [-c] [-u USER] [-r ROLE] [-t TYPE] [-l RANGE] COMMAND [ARG]...\n\
"), program_name, program_name);
fputs (_("\
Run a program in a different SELinux security context.\n\
@@ -86,14 +86,24 @@ With neither CONTEXT nor COMMAND, print the current security context.\n\
fputs (_("\
CONTEXT Complete security context\n\
+"), stdout);
+ oputs (_("\
-c, --compute compute process transition context before modifying\n\
+"));
+ oputs (_("\
-t, --type=TYPE type (for same role as parent)\n\
+"));
+ oputs (_("\
-u, --user=USER user identity\n\
+"));
+ oputs (_("\
-r, --role=ROLE role\n\
+"));
+ oputs (_("\
-l, --range=RANGE levelrange\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_exec_status (PROGRAM_NAME);
emit_ancillary_info (PROGRAM_NAME);
}
@@ -103,14 +113,14 @@ With neither CONTEXT nor COMMAND, print the current security context.\n\
int
main (int argc, char **argv)
{
- char *role = nullptr;
- char *range = nullptr;
- char *user = nullptr;
- char *type = nullptr;
- char *context = nullptr;
- char *cur_context = nullptr;
- char *file_context = nullptr;
- char *new_context = nullptr;
+ char *role = NULL;
+ char *range = NULL;
+ char *user = NULL;
+ char *type = NULL;
+ char *context = NULL;
+ char *cur_context = NULL;
+ char *file_context = NULL;
+ char *new_context = NULL;
bool compute_trans = false;
context_t con;
@@ -252,7 +262,7 @@ main (int argc, char **argv)
if (setexeccon (context_str (con)) != 0)
error (EXIT_CANCELED, errno, _("unable to set security context %s"),
quote (context_str (con)));
- if (cur_context != nullptr)
+ if (cur_context != NULL)
freecon (cur_context);
(compute_trans ? execv : execvp) (argv[optind], argv + optind);
diff --git a/src/selinux.c b/src/selinux.c
index 04b87a335..17103caa0 100644
--- a/src/selinux.c
+++ b/src/selinux.c
@@ -1,5 +1,5 @@
/* selinux - core functions for maintaining SELinux labeling
- Copyright (C) 2012-2025 Free Software Foundation, Inc.
+ Copyright (C) 2012-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -71,8 +71,8 @@ mode_to_security_class (mode_t m)
static int
computecon_raw (char const *path, mode_t mode, char **con)
{
- char *scon_raw = nullptr;
- char *tcon_raw = nullptr;
+ char *scon_raw = NULL;
+ char *tcon_raw = NULL;
security_class_t tclass;
int rc = -1;
@@ -111,12 +111,12 @@ defaultcon (struct selabel_handle *selabel_handle,
char const *path, mode_t mode)
{
int rc = -1;
- char *scon_raw = nullptr;
- char *tcon_raw = nullptr;
- context_t scontext = 0, tcontext = 0;
+ char *scon_raw = NULL;
+ char *tcon_raw = NULL;
+ context_t scontext = NULL, tcontext = NULL;
char const *contype;
char const *constr;
- char *newpath = nullptr;
+ char *newpath = NULL;
if (! IS_ABSOLUTE_FILE_NAME (path))
{
@@ -179,9 +179,9 @@ restorecon_private (struct selabel_handle *selabel_handle, char const *path)
{
int rc = -1;
struct stat sb;
- char *scon_raw = nullptr;
- char *tcon_raw = nullptr;
- context_t scontext = 0, tcontext = 0;
+ char *scon_raw = NULL;
+ char *tcon_raw = NULL;
+ context_t scontext = NULL, tcontext = NULL;
char const *contype;
char const *constr;
int fd;
@@ -285,7 +285,7 @@ bool
restorecon (struct selabel_handle *selabel_handle,
char const *path, bool recurse)
{
- char *newpath = nullptr;
+ char *newpath = NULL;
if (! IS_ABSOLUTE_FILE_NAME (path))
{
@@ -307,8 +307,8 @@ restorecon (struct selabel_handle *selabel_handle,
return ok;
}
- char const *ftspath[2] = { path, nullptr };
- FTS *fts = xfts_open ((char *const *) ftspath, FTS_PHYSICAL, nullptr);
+ char const *ftspath[2] = { path, NULL };
+ FTS *fts = xfts_open ((char *const *) ftspath, FTS_PHYSICAL, NULL);
int err = 0;
for (FTSENT *ent; (ent = fts_read (fts)); )
diff --git a/src/selinux.h b/src/selinux.h
index 9841d78cc..c20d5e51b 100644
--- a/src/selinux.h
+++ b/src/selinux.h
@@ -1,5 +1,5 @@
/* selinux - core functions for maintaining SELinux labeling
- Copyright (C) 2012-2025 Free Software Foundation, Inc.
+ Copyright (C) 2012-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/seq.c b/src/seq.c
index c3b9a4830..a49a18988 100644
--- a/src/seq.c
+++ b/src/seq.c
@@ -1,5 +1,5 @@
/* seq - print sequence of numbers to standard output.
- Copyright (C) 1994-2025 Free Software Foundation, Inc.
+ Copyright (C) 1994-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,7 +17,6 @@
/* Written by Ulrich Drepper. */
#include <config.h>
-#include <ctype.h>
#include <getopt.h>
#include <stdio.h>
#include <sys/types.h>
@@ -28,6 +27,7 @@
#include "full-write.h"
#include "quote.h"
#include "xstrtod.h"
+#include "xvasprintf.h"
/* Roll our own isfinite/isnan rather than using <math.h>, so that we don't
have to worry about linking -lm just for isfinite. */
@@ -55,7 +55,7 @@ static bool locale_ok;
static bool equal_width;
/* The string used to separate two numbers. */
-static char const *separator;
+static char const *separator = "\n";
/* The string output after all numbers have been output.
Usually "\n" or "\0". */
@@ -63,12 +63,12 @@ static char const terminator[] = "\n";
static struct option const long_options[] =
{
- { "equal-width", no_argument, nullptr, 'w'},
- { "format", required_argument, nullptr, 'f'},
- { "separator", required_argument, nullptr, 's'},
+ { "equal-width", no_argument, NULL, 'w'},
+ { "format", required_argument, NULL, 'f'},
+ { "separator", required_argument, NULL, 's'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- { nullptr, 0, nullptr, 0}
+ { NULL, 0, NULL, 0}
};
void
@@ -89,13 +89,20 @@ Print numbers from FIRST to LAST, in steps of INCREMENT.\n\
emit_mandatory_arg_note ();
- fputs (_("\
- -f, --format=FORMAT use printf style floating-point FORMAT\n\
- -s, --separator=STRING use STRING to separate numbers (default: \\n)\n\
- -w, --equal-width equalize width by padding with leading zeroes\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -f, --format=FORMAT\n\
+ use printf style floating-point FORMAT\n\
+"));
+ oputs (_("\
+ -s, --separator=STRING\n\
+ use STRING to separate numbers (default: \\n)\n\
+"));
+ oputs (_("\
+ -w, --equal-width\n\
+ equalize width by padding with leading zeroes\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
If FIRST or INCREMENT is omitted, it defaults to 1. That is, an\n\
@@ -151,7 +158,7 @@ scan_arg (char const *arg)
{
operand ret;
- if (! xstrtold (arg, nullptr, &ret.value, cl_strtold))
+ if (! xstrtold (arg, NULL, &ret.value, cl_strtold))
{
error (0, 0, _("invalid floating point argument: %s"), quote (arg));
usage (EXIT_FAILURE);
@@ -198,7 +205,7 @@ scan_arg (char const *arg)
e = strchr (arg, 'E');
if (e)
{
- long exponent = MAX (strtol (e + 1, nullptr, 10), -LONG_MAX);
+ long exponent = MAX (strtol (e + 1, NULL, 10), -LONG_MAX);
ret.precision += exponent < 0 ? -exponent
: - MIN (ret.precision, exponent);
/* Don't account for e.... in the width since this is not output. */
@@ -238,9 +245,6 @@ long_double_format (char const *fmt, struct layout *layout)
{
size_t i;
size_t prefix_len = 0;
- size_t suffix_len = 0;
- size_t length_modifier_offset;
- bool has_L;
for (i = 0; ! (fmt[i] == '%' && fmt[i + 1] != '%'); i += (fmt[i] == '%') + 1)
{
@@ -259,8 +263,8 @@ long_double_format (char const *fmt, struct layout *layout)
i += strspn (fmt + i, "0123456789");
}
- length_modifier_offset = i;
- has_L = (fmt[i] == 'L');
+ size_t length_modifier_offset = i;
+ bool has_L = (fmt[i] == 'L');
i += has_L;
if (fmt[i] == '\0')
error (EXIT_FAILURE, 0, _("format %s ends in %%"), quote (fmt));
@@ -268,6 +272,7 @@ long_double_format (char const *fmt, struct layout *layout)
error (EXIT_FAILURE, 0,
_("format %s has unknown %%%c directive"), quote (fmt), fmt[i]);
+ size_t suffix_len = 0;
for (i++; ; i += (fmt[i] == '%') + 1)
if (fmt[i] == '%' && fmt[i + 1] != '%')
error (EXIT_FAILURE, 0, _("format %s has too many %% directives"),
@@ -300,9 +305,8 @@ print_numbers (char const *fmt, struct layout layout,
if (! out_of_range)
{
long double x = first;
- long double i;
- for (i = 1; ; i++)
+ for (long double i = 1; ; i++)
{
long double x0 = x;
if (printf (fmt, x) < 0)
@@ -326,23 +330,22 @@ print_numbers (char const *fmt, struct layout layout,
of stopping at 0.000002. */
bool print_extra_number = false;
- long double x_val;
- char *x_str;
- int x_strlen;
if (locale_ok)
setlocale (LC_NUMERIC, "C");
- x_strlen = asprintf (&x_str, fmt, x);
+ char *x_str;
+ int x_strlen = asprintf (&x_str, fmt, x);
if (locale_ok)
setlocale (LC_NUMERIC, "");
if (x_strlen < 0)
xalloc_die ();
x_str[x_strlen - layout.suffix_len] = '\0';
- if (xstrtold (x_str + layout.prefix_len, nullptr,
+ long double x_val;
+ if (xstrtold (x_str + layout.prefix_len, NULL,
&x_val, cl_strtold)
&& x_val == last)
{
- char *x0_str = nullptr;
+ char *x0_str = NULL;
int x0_strlen = asprintf (&x0_str, fmt, x0);
if (x0_strlen < 0)
xalloc_die ();
@@ -508,7 +511,7 @@ seq_fast (char const *a, char const *b, uintmax_t step)
/* Grow number buffer if needed for the inf case. */
if (p == p0)
{
- char *new_p0 = xpalloc (nullptr, &inc_size, 1, -1, 1);
+ char *new_p0 = xpalloc (NULL, &inc_size, 1, -1, 1);
idx_t saved_p_len = endp - p;
endp = new_p0 + inc_size;
p = memcpy (endp - saved_p_len, p0, saved_p_len);
@@ -546,14 +549,8 @@ all_digits_p (char const *s)
int
main (int argc, char **argv)
{
- int optc;
- operand first = { 1, 1, 0 };
- operand step = { 1, 1, 0 };
- operand last;
- struct layout layout = { 0, 0 };
-
/* The printf(3) format used for output. */
- char const *format_str = nullptr;
+ char const *format_str = NULL;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -563,14 +560,12 @@ main (int argc, char **argv)
atexit (close_stdout);
- equal_width = false;
- separator = "\n";
-
/* We have to handle negative numbers in the command line but this
conflicts with the command line arguments. So explicitly check first
whether the next argument looks like a negative number. */
while (optind < argc)
{
+ int optc;
if (argv[optind][0] == '-'
&& ((optc = argv[optind][1]) == '.' || c_isdigit (optc)))
{
@@ -578,7 +573,7 @@ main (int argc, char **argv)
break;
}
- optc = getopt_long (argc, argv, "+f:s:w", long_options, nullptr);
+ optc = getopt_long (argc, argv, "+f:s:w", long_options, NULL);
if (optc == -1)
break;
@@ -618,10 +613,11 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
+ struct layout layout = { 0, 0 };
if (format_str)
format_str = long_double_format (format_str, &layout);
- if (format_str != nullptr && equal_width)
+ if (format_str != NULL && equal_width)
{
error (0, 0, _("format string may not be specified"
" when printing equal width strings"));
@@ -637,10 +633,11 @@ main (int argc, char **argv)
- integer increment <= SEQ_FAST_STEP_LIMIT
then use the much more efficient integer-only code,
operating on arbitrarily large numbers. */
+ operand step = { 1, 1, 0 };
bool fast_step_ok = false;
if (n_args != 3
|| (all_digits_p (argv[optind + 1])
- && xstrtold (argv[optind + 1], nullptr, &step.value, cl_strtold)
+ && xstrtold (argv[optind + 1], NULL, &step.value, cl_strtold)
&& 0 < step.value && step.value <= SEQ_FAST_STEP_LIMIT))
fast_step_ok = true;
@@ -655,7 +652,8 @@ main (int argc, char **argv)
seq_fast (s1, s2, step.value);
}
- last = scan_arg (argv[optind++]);
+ operand first = { 1, 1, 0 };
+ operand last = scan_arg (argv[optind++]);
if (optind < argc)
{
@@ -683,16 +681,11 @@ main (int argc, char **argv)
&& 0 < step.value && step.value <= SEQ_FAST_STEP_LIMIT
&& !equal_width && !format_str && strlen (separator) == 1)
{
- char *s1;
- char *s2;
- if (all_digits_p (user_start))
- s1 = xstrdup (user_start);
- else if (asprintf (&s1, "%0.Lf", first.value) < 0)
- xalloc_die ();
- if (! isfinite (last.value))
- s2 = xstrdup ("inf"); /* Ensure "inf" is used. */
- else if (asprintf (&s2, "%0.Lf", last.value) < 0)
- xalloc_die ();
+ char *s1 = (all_digits_p (user_start)
+ ? xstrdup (user_start) : xasprintf ("%0.Lf", first.value));
+ /* Ensure "inf" is used. */
+ char *s2 = (! isfinite (last.value)
+ ? xstrdup ("inf") : xasprintf ("%0.Lf", last.value));
if (*s1 != '-' && *s2 != '-')
seq_fast (s1, s2, step.value);
@@ -702,7 +695,7 @@ main (int argc, char **argv)
/* Upon any failure, let the more general code deal with it. */
}
- if (format_str == nullptr)
+ if (format_str == NULL)
format_str = get_default_format (first, step, last);
print_numbers (format_str, layout, first.value, step.value, last.value);
diff --git a/src/set-fields.c b/src/set-fields.c
index 14f7e4d65..5b3264d35 100644
--- a/src/set-fields.c
+++ b/src/set-fields.c
@@ -1,5 +1,5 @@
/* set-fields.c -- common functions for parsing field list
- Copyright (C) 2015-2025 Free Software Foundation, Inc.
+ Copyright (C) 2015-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,7 +19,6 @@
#include <config.h>
#include "system.h"
-#include <ctype.h>
#include "c-ctype.h"
#include "quote.h"
#include "set-fields.h"
@@ -73,7 +72,7 @@ complement_rp (void)
struct field_range_pair *c = frp;
idx_t n = n_frp;
- frp = nullptr;
+ frp = NULL;
n_frp = 0;
n_frp_allocated = 0;
diff --git a/src/set-fields.h b/src/set-fields.h
index d96e9960f..7a9967a5f 100644
--- a/src/set-fields.h
+++ b/src/set-fields.h
@@ -1,6 +1,6 @@
/* set-fields.h -- parse field list argument
- Copyright (C) 2015-2025 Free Software Foundation, Inc.
+ Copyright (C) 2015-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/shred.c b/src/shred.c
index f2af24398..b5a212c97 100644
--- a/src/shred.c
+++ b/src/shred.c
@@ -1,6 +1,6 @@
/* shred.c - overwrite files and devices to make it harder to recover data
- Copyright (C) 1999-2025 Free Software Foundation, Inc.
+ Copyright (C) 1999-2026 Free Software Foundation, Inc.
Copyright (C) 1997, 1998, 1999 Colin Plumb.
This program is free software: you can redistribute it and/or modify
@@ -77,7 +77,6 @@
#include <getopt.h>
#include <stdio.h>
-#include <setjmp.h>
#include <sys/types.h>
#if defined __linux__ && HAVE_SYS_MTIO_H
# include <sys/mtio.h>
@@ -89,6 +88,7 @@
#include "assure.h"
#include "xdectoint.h"
#include "fcntl--.h"
+#include "gethrxtime.h"
#include "human.h"
#include "randint.h"
#include "randread.h"
@@ -118,7 +118,7 @@ enum remove_method
static char const *const remove_args[] =
{
- "unlink", "wipe", "wipesync", nullptr
+ "unlink", "wipe", "wipesync", NULL
};
static enum remove_method const remove_methods[] =
@@ -146,17 +146,17 @@ enum
static struct option const long_opts[] =
{
- {"exact", no_argument, nullptr, 'x'},
- {"force", no_argument, nullptr, 'f'},
- {"iterations", required_argument, nullptr, 'n'},
- {"size", required_argument, nullptr, 's'},
- {"random-source", required_argument, nullptr, RANDOM_SOURCE_OPTION},
- {"remove", optional_argument, nullptr, 'u'},
- {"verbose", no_argument, nullptr, 'v'},
- {"zero", no_argument, nullptr, 'z'},
+ {"exact", no_argument, NULL, 'x'},
+ {"force", no_argument, NULL, 'f'},
+ {"iterations", required_argument, NULL, 'n'},
+ {"size", required_argument, NULL, 's'},
+ {"random-source", required_argument, NULL, RANDOM_SOURCE_OPTION},
+ {"remove", optional_argument, NULL, 'u'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"zero", no_argument, NULL, 'z'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -178,22 +178,50 @@ If FILE is -, shred standard output.\n\
emit_mandatory_arg_note ();
- printf (_("\
- -f, --force change permissions to allow writing if necessary\n\
- -n, --iterations=N overwrite N times instead of the default (%d)\n\
- --random-source=FILE get random bytes from FILE\n\
- -s, --size=N shred this many bytes (suffixes like K, M, G accepted)\n\
+ oputs (_("\
+ -f, --force\n\
+ change permissions to allow writing if necessary\n\
+"));
+ oprintf (_("\
+ -n, --iterations=N\n\
+ overwrite N times instead of the default (%d)\n\
"), DEFAULT_PASSES);
+ oputs (_("\
+ --random-source=FILE\n\
+ get random bytes from FILE\n\
+"));
+ oputs (_("\
+ -s, --size=N\n\
+ shred this many bytes (suffixes like K, M, G accepted)\n\
+"));
+ oputs (_("\
+ -u\n\
+ deallocate and remove file after overwriting\n\
+"));
+ oputs (_("\
+ --remove[=HOW]\n\
+ like -u but give control on HOW to delete; See below\n\
+"));
+ oputs (_("\
+ -v, --verbose\n\
+ show details of data and metadata operations performed\n\
+"));
+ oputs (_("\
+ -x, --exact\n\
+ do not round file sizes up to the next full block;\n\
+ this is the default for non-regular files\n\
+"));
+ oputs (_("\
+ -z, --zero\n\
+ add a final overwrite with zeros to hide shredding\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
- -u deallocate and remove file after overwriting\n\
- --remove[=HOW] like -u but give control on HOW to delete; See below\n\
- -v, --verbose show progress\n\
- -x, --exact do not round file sizes up to the next full block;\n\
- this is the default for non-regular files\n\
- -z, --zero add a final overwrite with zeros to hide shredding\n\
+\n\
+FILE will be skipped with a diagnostic message if it is a FIFO, socket, or\n\
+terminal, since its data does not reside on disk.\n\
"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
\n\
Delete FILE(s) if --remove (-u) is specified. The default is not to remove\n\
@@ -395,12 +423,12 @@ dopass (int fd, struct stat const *st, char const *qname, off_t *sizep,
unsigned long int k, unsigned long int n)
{
off_t size = *sizep;
- off_t offset; /* Current file position */
- time_t thresh IF_LINT ( = 0); /* Time to maybe print next status update */
- time_t now = 0; /* Current time */
- size_t lim; /* Amount of data to try writing */
- size_t soff; /* Offset into buffer for next write */
- ssize_t ssize; /* Return value from write */
+ off_t offset; /* Current file position */
+ xtime_t prev IF_LINT ( = 0); /* Time we printed the previous update. */
+ xtime_t now = 0; /* Current time */
+ size_t lim; /* Amount of data to try writing */
+ size_t soff; /* Offset into buffer for next write */
+ ssize_t ssize; /* Return value from write */
/* Fill pattern buffer. Aligning it to a page so we can do direct I/O. */
size_t page_size = getpagesize ();
@@ -421,7 +449,7 @@ dopass (int fd, struct stat const *st, char const *qname, off_t *sizep,
char const *previous_human_offset;
/* As a performance tweak, avoid direct I/O for small sizes,
- as it's just a performance rather then security consideration,
+ as it's just a performance rather than security consideration,
and direct I/O can often be unsupported for small non aligned sizes. */
bool try_without_directio = 0 < size && size < output_size;
if (! try_without_directio)
@@ -443,14 +471,14 @@ dopass (int fd, struct stat const *st, char const *qname, off_t *sizep,
}
else
{
- passname (0, pass_string);
+ passname (NULL, pass_string);
}
/* Set position if first status update */
if (n)
{
error (0, 0, _("%s: pass %lu/%lu (%s)..."), qname, k, n, pass_string);
- thresh = time (nullptr) + VERBOSE_UPDATE;
+ prev = gethrxtime ();
previous_human_offset = "";
}
@@ -540,7 +568,7 @@ dopass (int fd, struct stat const *st, char const *qname, off_t *sizep,
/* Time to print progress? */
if (n && ((done && *previous_human_offset)
- || thresh <= (now = time (nullptr))))
+ || VERBOSE_UPDATE <= xtime_sec ((now = gethrxtime ()) - prev)))
{
char offset_buf[LONGEST_HUMAN_READABLE + 1];
char size_buf[LONGEST_HUMAN_READABLE + 1];
@@ -576,7 +604,7 @@ dopass (int fd, struct stat const *st, char const *qname, off_t *sizep,
strcpy (previous_offset_buf, human_offset);
previous_human_offset = previous_offset_buf;
- thresh = now + VERBOSE_UPDATE;
+ prev = now;
/*
* Force periodic syncs to keep displayed progress accurate
@@ -812,7 +840,6 @@ static bool
do_wipefd (int fd, char const *qname, struct randint_source *s,
struct Options const *flags)
{
- size_t i;
struct stat st;
off_t size; /* Size to write, size to read */
off_t i_size = 0; /* For small files, initial size to overwrite inode */
@@ -915,7 +942,7 @@ do_wipefd (int fd, char const *qname, struct randint_source *s,
else
break;
- for (i = 0; i < flags->n_iterations + flags->zero_fill; i++)
+ for (size_t i = 0; i < flags->n_iterations + flags->zero_fill; i++)
{
int err = 0;
int type = i < flags->n_iterations ? passarray[i] : 0;
@@ -1116,21 +1143,33 @@ static bool
wipefile (char *name, char const *qname,
struct randint_source *s, struct Options const *flags)
{
- bool ok;
- int fd;
-
- fd = open (name, O_WRONLY | O_NOCTTY | O_BINARY);
+ int fd = open (name, O_WRONLY | O_NOCTTY | O_NONBLOCK | O_BINARY);
if (fd < 0
&& (errno == EACCES && flags->force)
&& chmod (name, S_IWUSR) == 0)
- fd = open (name, O_WRONLY | O_NOCTTY | O_BINARY);
+ fd = open (name, O_WRONLY | O_NOCTTY | O_NONBLOCK | O_BINARY);
if (fd < 0)
{
- error (0, errno, _("%s: failed to open for writing"), qname);
+ struct stat st;
+
+ /* Try to give a more meaningful error message if we attempt to
+ open a FIFO with no readers. */
+ if (errno == ENXIO && 0 <= stat (name, &st) && S_ISFIFO (st.st_mode))
+ error (0, 0, _("%s: invalid file type"), qname);
+ else
+ error (0, errno, _("%s: failed to open for writing"), qname);
return false;
}
- ok = do_wipefd (fd, qname, s, flags);
+ /* We used O_NONBLOCK to prevent blocking when opening a FIFO.
+ Reset that here. */
+ int fd_flags = fcntl (fd, F_GETFL);
+ bool ok = 0 <= fd_flags && 0 <= fcntl (fd, F_SETFL, fd_flags & ~O_NONBLOCK);
+ if (ok)
+ ok = do_wipefd (fd, qname, s, flags);
+ else
+ error (0, errno, _("couldn't reset non-blocking mode %s"), qname);
+
if (close (fd) != 0)
{
error (0, errno, _("%s: failed to close"), qname);
@@ -1163,8 +1202,7 @@ main (int argc, char **argv)
char **file;
int n_files;
int c;
- int i;
- char const *random_source = nullptr;
+ char const *random_source = NULL;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -1177,7 +1215,7 @@ main (int argc, char **argv)
flags.n_iterations = DEFAULT_PASSES;
flags.size = -1;
- while ((c = getopt_long (argc, argv, "fn:s:uvxz", long_opts, nullptr)) != -1)
+ while ((c = getopt_long (argc, argv, "fn:s:uvxz", long_opts, NULL)) != -1)
{
switch (c)
{
@@ -1199,7 +1237,7 @@ main (int argc, char **argv)
break;
case 'u':
- if (optarg == nullptr)
+ if (optarg == NULL)
flags.remove_file = remove_wipesync;
else
flags.remove_file = XARGMATCH ("--remove", optarg,
@@ -1247,7 +1285,7 @@ main (int argc, char **argv)
quotef (random_source ? random_source : "getrandom"));
atexit (clear_random_data);
- for (i = 0; i < n_files; i++)
+ for (int i = 0; i < n_files; i++)
{
char *qname = xstrdup (quotef (file[i]));
if (streq (file[i], "-"))
diff --git a/src/shuf.c b/src/shuf.c
index d43606620..b1c645caf 100644
--- a/src/shuf.c
+++ b/src/shuf.c
@@ -1,6 +1,6 @@
/* Shuffle lines of text.
- Copyright (C) 2006-2025 Free Software Foundation, Inc.
+ Copyright (C) 2006-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -66,19 +66,29 @@ Write a random permutation of the input lines to standard output.\n\
emit_stdin_note ();
emit_mandatory_arg_note ();
- fputs (_("\
+ oputs (_("\
-e, --echo treat each ARG as an input line\n\
+"));
+ oputs (_("\
-i, --input-range=LO-HI treat each number LO through HI as an input line\n\
+"));
+ oputs (_("\
-n, --head-count=COUNT output at most COUNT lines\n\
+"));
+ oputs (_("\
-o, --output=FILE write result to FILE instead of standard output\n\
+"));
+ oputs (_("\
--random-source=FILE get random bytes from FILE\n\
+"));
+ oputs (_("\
-r, --repeat output lines can be repeated\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-z, --zero-terminated line delimiter is NUL, not newline\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
@@ -94,16 +104,16 @@ enum
static struct option const long_opts[] =
{
- {"echo", no_argument, nullptr, 'e'},
- {"input-range", required_argument, nullptr, 'i'},
- {"head-count", required_argument, nullptr, 'n'},
- {"output", required_argument, nullptr, 'o'},
- {"random-source", required_argument, nullptr, RANDOM_SOURCE_OPTION},
- {"repeat", no_argument, nullptr, 'r'},
- {"zero-terminated", no_argument, nullptr, 'z'},
+ {"echo", no_argument, NULL, 'e'},
+ {"input-range", required_argument, NULL, 'i'},
+ {"head-count", required_argument, NULL, 'n'},
+ {"output", required_argument, NULL, 'o'},
+ {"random-source", required_argument, NULL, RANDOM_SOURCE_OPTION},
+ {"repeat", no_argument, NULL, 'r'},
+ {"zero-terminated", no_argument, NULL, 'z'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {0, 0, 0, 0},
+ {NULL, 0, NULL, 0},
};
static void
@@ -172,8 +182,8 @@ read_input_reservoir_sampling (FILE *in, char eolbyte, idx_t k,
{
randint n_lines = 0;
idx_t n_alloc_lines = 0;
- struct linebuffer *line = nullptr;
- struct linebuffer *rsrv = nullptr;
+ struct linebuffer *line = NULL;
+ struct linebuffer *rsrv = NULL;
/* Fill the first K lines, directly into the reservoir. */
for (n_lines = 0; n_lines < k; n_lines++)
@@ -192,7 +202,7 @@ read_input_reservoir_sampling (FILE *in, char eolbyte, idx_t k,
}
/* last line wasn't null - so there may be more lines to read. */
- if (line != nullptr)
+ if (line != NULL)
{
struct linebuffer dummy;
initbuffer (&dummy); /* space for lines not put in reservoir. */
@@ -211,7 +221,7 @@ read_input_reservoir_sampling (FILE *in, char eolbyte, idx_t k,
randint j = randint_choose (s, n_lines + 1); /* 0 .. n_lines. */
line = (j < k) ? (&rsrv[j]) : (&dummy);
}
- while (readlinebuffer_delim (line, in, eolbyte) != nullptr && n_lines++);
+ while (readlinebuffer_delim (line, in, eolbyte) != NULL && n_lines++);
if (! n_lines)
error (EXIT_FAILURE, EOVERFLOW, _("too many input lines"));
@@ -251,7 +261,7 @@ static size_t
read_input (FILE *in, char eolbyte, char ***pline)
{
char *p;
- char *buf = nullptr;
+ char *buf = NULL;
size_t used;
char *lim;
char **line;
@@ -306,6 +316,21 @@ write_permuted_lines (size_t n_lines, char *const *line,
return 0;
}
+/* Print NUMBER followed by EOLBYTE to standard output.
+ Return false on failure, true on success. */
+static bool
+print_number (unsigned long int number, char eolbyte)
+{
+ char buf[INT_BUFSIZE_BOUND (unsigned long int)];
+ char *p = buf + INT_STRLEN_BOUND (unsigned long int);
+ *p = eolbyte;
+ do
+ *--p = '0' + number % 10;
+ while ((number /= 10) != 0);
+ idx_t len = buf + sizeof buf - p;
+ return fwrite (p, 1, len, stdout) == len;
+}
+
/* Output N_LINES of numbers to stdout, from PERMUTATION array.
PERMUTATION must have at least N_LINES elements. */
static int
@@ -315,7 +340,7 @@ write_permuted_numbers (size_t n_lines, size_t lo_input,
for (size_t i = 0; i < n_lines; i++)
{
unsigned long int n = lo_input + permutation[i];
- if (printf ("%lu%c", n, eolbyte) < 0)
+ if (! print_number (n, eolbyte))
return -1;
}
@@ -333,7 +358,7 @@ write_random_numbers (struct randint_source *s, size_t count,
for (size_t i = 0; i < count; i++)
{
unsigned long int j = lo_input + randint_choose (s, range);
- if (printf ("%lu%c", j, eolbyte) < 0)
+ if (! print_number (j, eolbyte))
return -1;
}
@@ -367,10 +392,10 @@ main (int argc, char **argv)
size_t lo_input = SIZE_MAX;
size_t hi_input = 0;
idx_t head_lines = MIN (IDX_MAX, SIZE_MAX);
- char const *outfile = nullptr;
- char *random_source = nullptr;
+ char const *outfile = NULL;
+ char *random_source = NULL;
char eolbyte = '\n';
- char **input_lines = nullptr;
+ char **input_lines = NULL;
bool use_reservoir_sampling = false;
bool repeat = false;
@@ -378,10 +403,10 @@ main (int argc, char **argv)
int n_operands;
char **operand;
size_t n_lines;
- char **line = nullptr;
- struct linebuffer *reservoir = nullptr;
+ char **line = NULL;
+ struct linebuffer *reservoir = NULL;
struct randint_source *randint_source;
- size_t *permutation = nullptr;
+ size_t *permutation = NULL;
int i;
initialize_main (&argc, &argv);
@@ -392,7 +417,7 @@ main (int argc, char **argv)
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "ei:n:o:rz", long_opts, nullptr))
+ while ((optc = getopt_long (argc, argv, "ei:n:o:rz", long_opts, NULL))
!= -1)
switch (optc)
{
@@ -408,7 +433,7 @@ main (int argc, char **argv)
uintmax_t u;
char *lo_end;
- strtol_error err = xstrtoumax (optarg, &lo_end, 10, &u, nullptr);
+ strtol_error err = xstrtoumax (optarg, &lo_end, 10, &u, NULL);
if (err == LONGINT_OK)
{
lo_input = u;
@@ -418,7 +443,7 @@ main (int argc, char **argv)
err = LONGINT_INVALID;
else
{
- err = xstrtoumax (lo_end + 1, nullptr, 10, &u, "");
+ err = xstrtoumax (lo_end + 1, NULL, 10, &u, "");
if (err == LONGINT_OK)
{
hi_input = u;
@@ -439,7 +464,7 @@ main (int argc, char **argv)
case 'n':
{
uintmax_t argval;
- strtol_error e = xstrtoumax (optarg, nullptr, 10, &argval, "");
+ strtol_error e = xstrtoumax (optarg, NULL, 10, &argval, "");
if (e == LONGINT_OK)
head_lines = MIN (head_lines, argval);
@@ -494,7 +519,7 @@ main (int argc, char **argv)
if (head_lines == 0)
{
n_lines = 0;
- line = nullptr;
+ line = NULL;
}
else if (echo)
{
@@ -505,7 +530,7 @@ main (int argc, char **argv)
else if (input_range)
{
IF_LINT (n_lines = hi_input - lo_input + 1); /* Avoid GCC 10 warning. */
- line = nullptr;
+ line = NULL;
}
else
{
diff --git a/src/sleep.c b/src/sleep.c
index 13c68a606..60996ceca 100644
--- a/src/sleep.c
+++ b/src/sleep.c
@@ -1,5 +1,5 @@
/* sleep - delay for a specified amount of time.
- Copyright (C) 1984-2025 Free Software Foundation, Inc.
+ Copyright (C) 1984-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -48,8 +48,8 @@ With multiple arguments, pause for the sum of their values.\n\
\n\
"),
program_name, program_name);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -106,7 +106,7 @@ main (int argc, char **argv)
parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE_NAME,
Version, true, usage, AUTHORS,
- (char const *) nullptr);
+ (char const *) NULL);
if (argc == 1)
{
diff --git a/src/sort.c b/src/sort.c
index 05d00cc11..b3a17d6b1 100644
--- a/src/sort.c
+++ b/src/sort.c
@@ -1,5 +1,5 @@
/* sort - sort lines of text (with all kinds of options).
- Copyright (C) 1988-2025 Free Software Foundation, Inc.
+ Copyright (C) 1988-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,7 +22,6 @@
#include <config.h>
-#include <ctype.h>
#include <getopt.h>
#include <pthread.h>
#include <sys/resource.h>
@@ -51,6 +50,8 @@
#include "readtokens0.h"
#include "stdlib--.h"
#include "strnumcmp.h"
+#include "term-sig.h"
+#include "unistd--.h"
#include "xmemcoll.h"
#include "xnanosleep.h"
#include "xstrtol.h"
@@ -72,18 +73,6 @@ struct rlimit { size_t rlim_cur; };
# include <langinfo.h>
#endif
-/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
- present. */
-#ifndef SA_NOCLDSTOP
-# define SA_NOCLDSTOP 0
-/* No sigprocmask. Always 'return' zero. */
-# define sigprocmask(How, Set, Oset) (0)
-# define sigset_t int
-# if ! HAVE_SIGINTERRUPT
-# define siginterrupt(sig, flag) /* empty */
-# endif
-#endif
-
#if !defined OPEN_MAX && defined NR_OPEN
# define OPEN_MAX NR_OPEN
#endif
@@ -199,6 +188,16 @@ struct buffer
bool eof; /* An EOF has been read. */
};
+/* Policy for sizing the main sort buffer. INITIAL is the allocation
+ to try first. LIMIT is the largest allocation to try while reading
+ normal input; a single input line can still force a larger buffer. */
+struct sort_buffer_policy
+{
+ size_t initial;
+ size_t limit;
+ bool growth_failed;
+};
+
/* Sort key. */
struct keyfield
{
@@ -391,11 +390,9 @@ static struct tempnode *volatile *temptail = &temphead;
static void
cleanup (void)
{
- struct tempnode const *node;
-
- for (node = temphead; node; node = node->next)
+ for (struct tempnode const *node = temphead; node; node = node->next)
unlink (node->name);
- temphead = nullptr;
+ temphead = NULL;
}
/* Handle interrupts and hangups. */
@@ -403,9 +400,6 @@ cleanup (void)
static void
sighandler (int sig)
{
- if (! SA_NOCLDSTOP)
- signal (sig, SIG_IGN);
-
cleanup ();
signal (sig, SIG_DFL);
@@ -450,82 +444,137 @@ Write sorted concatenation of all FILE(s) to standard output.\n\
Ordering options:\n\
\n\
"), stdout);
+ oputs (_("\
+ -b, --ignore-leading-blanks\n\
+ ignore leading blanks when finding sort keys in each line\n\
+"));
+ oputs (_("\
+ -d, --dictionary-order\n\
+ consider only blanks and alphanumeric characters\n\
+"));
+ oputs (_("\
+ -f, --ignore-case\n\
+ fold lower case to upper case characters\n\
+"));
+ oputs (_("\
+ -g, --general-numeric-sort\n\
+ compare according to general numerical value\n\
+"));
+ oputs (_("\
+ -i, --ignore-nonprinting\n\
+ consider only printable characters\n\
+"));
+ oputs (_("\
+ -M, --month-sort\n\
+ compare (unknown) < 'JAN' < ... < 'DEC'\n\
+"));
+ oputs (_("\
+ -h, --human-numeric-sort\n\
+ compare human readable numbers (e.g., 2K 1G)\n\
+"));
+ oputs (_("\
+ -n, --numeric-sort\n\
+ compare according to string numerical value;\n\
+ see full documentation for supported strings\n\
+"));
+ oputs (_("\
+ -R, --random-sort\n\
+ shuffle, but group identical keys. See also shuf(1)\n\
+"));
+ oputs (_("\
+ --random-source=FILE\n\
+ get random bytes from FILE\n\
+"));
+ oputs (_("\
+ -r, --reverse\n\
+ reverse the result of comparisons\n\
+"));
+ oputs (_("\
+ --sort=WORD\n\
+ sort according to WORD:\n\
+ general-numeric -g, human-numeric -h, month -M,\n\
+ numeric -n, random -R, version -V\n\
+"));
+ oputs (_("\
+ -V, --version-sort\n\
+ natural sort of (version) numbers within text\n\
+"));
fputs (_("\
- -b, --ignore-leading-blanks ignore leading blanks\n\
- -d, --dictionary-order consider only blanks and alphanumeric characters\
-\n\
- -f, --ignore-case fold lower case to upper case characters\n\
-"), stdout);
- fputs (_("\
- -g, --general-numeric-sort compare according to general numerical value\n\
- -i, --ignore-nonprinting consider only printable characters\n\
- -M, --month-sort compare (unknown) < 'JAN' < ... < 'DEC'\n\
-"), stdout);
- fputs (_("\
- -h, --human-numeric-sort compare human readable numbers (e.g., 2K 1G)\n\
-"), stdout);
- fputs (_("\
- -n, --numeric-sort compare according to string numerical value;\n\
- see full documentation for supported strings\n\
-"), stdout);
- fputs (_("\
- -R, --random-sort shuffle, but group identical keys. See shuf(1)\n\
- --random-source=FILE get random bytes from FILE\n\
- -r, --reverse reverse the result of comparisons\n\
-"), stdout);
- fputs (_("\
- --sort=WORD sort according to WORD:\n\
- general-numeric -g, human-numeric -h, month -M,\
\n\
- numeric -n, random -R, version -V\n\
- -V, --version-sort natural sort of (version) numbers within text\n\
-\n\
-"), stdout);
- fputs (_("\
Other options:\n\
\n\
"), stdout);
- fputs (_("\
- --batch-size=NMERGE merge at most NMERGE inputs at once;\n\
- for more use temp files\n\
-"), stdout);
- fputs (_("\
- -c, --check, --check=diagnose-first check for sorted input; do not sort\n\
- -C, --check=quiet, --check=silent like -c, but do not report first bad line\
+ oputs (_("\
+ --batch-size=NMERGE\n\
+ merge at most NMERGE inputs at once; for more use temp files\n\
+"));
+ oputs (_("\
+ -c, --check, --check=diagnose-first\n\
+ check for sorted input; do not sort\n\
+"));
+ oputs (_("\
+ -C, --check=quiet, --check=silent\n\
+ like -c, but do not report first bad line\n\
+"));
+ oputs (_("\
+ --compress-program=PROG\n\
+ compress temporaries with PROG; decompress them with PROG -d\n\
+"));
+ oputs (_("\
+ --debug\n\
+ annotate the part of the line used to sort,\n\
+ and warn about questionable usage to standard error\n\
+"));
+ oputs (_("\
+ --files0-from=F\n\
+ read input from the files specified by NUL-terminated names in file F;\
\n\
- --compress-program=PROG compress temporaries with PROG;\n\
- decompress them with PROG -d\n\
-"), stdout);
- fputs (_("\
- --debug annotate the part of the line used to sort, and\n\
- warn about questionable usage to standard error\n\
- --files0-from=F read input from the files specified by\n\
- NUL-terminated names in file F;\n\
- If F is - then read names from standard input\n\
-"), stdout);
- fputs (_("\
- -k, --key=KEYDEF sort via a key; KEYDEF gives location and type\n\
- -m, --merge merge already sorted files; do not sort\n\
-"), stdout);
- fputs (_("\
- -o, --output=FILE write result to FILE instead of standard output\n\
- -s, --stable stabilize sort by disabling last-resort comparison\
-\n\
- -S, --buffer-size=SIZE use SIZE for main memory buffer\n\
-"), stdout);
- printf (_("\
- -t, --field-separator=SEP use SEP instead of non-blank to blank transition\n\
- -T, --temporary-directory=DIR use DIR for temporaries, not $TMPDIR or %s;\n\
- multiple options specify multiple directories\n\
- --parallel=N change the number of sorts run concurrently to N\n\
- -u, --unique output only the first of lines with equal keys;\n\
- with -c, check for strict ordering\n\
+ If F is -, read names from standard input\n\
+"));
+ oputs (_("\
+ -k, --key=KEYDEF\n\
+ sort via a key; KEYDEF gives location and type\n\
+"));
+ oputs (_("\
+ -m, --merge\n\
+ merge already sorted files; do not sort\n\
+"));
+ oputs (_("\
+ -o, --output=FILE\n\
+ write result to FILE instead of standard output\n\
+"));
+ oputs (_("\
+ -s, --stable\n\
+ stabilize sort by disabling last-resort comparison\n\
+"));
+ oputs (_("\
+ -S, --buffer-size=SIZE\n\
+ use SIZE for main memory buffer\n\
+"));
+ oputs (_("\
+ -t, --field-separator=SEP\n\
+ use SEP instead of non-blank to blank transition\n\
+"));
+ oprintf (_("\
+ -T, --temporary-directory=DIR\n\
+ use DIR for temporaries, not $TMPDIR or %s;\n\
+ multiple options specify multiple directories\n\
"), DEFAULT_TMPDIR);
- fputs (_("\
- -z, --zero-terminated line delimiter is NUL, not newline\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ --parallel=N\n\
+ change the number of sorts run concurrently to N\n\
+"));
+ oputs (_("\
+ -u, --unique\n\
+ output only the first of lines with equal keys;\n\
+ with -c, check for strict ordering\n\
+"));
+ oputs (_("\
+ -z, --zero-terminated\n\
+ line delimiter is NUL, not newline\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
KEYDEF is F[.C][OPTS][,F[.C][OPTS]] for start and stop position, where F is a\n\
@@ -571,37 +620,37 @@ static char const short_options[] = "-bcCdfghik:mMno:rRsS:t:T:uVy:z";
static struct option const long_options[] =
{
- {"ignore-leading-blanks", no_argument, nullptr, 'b'},
- {"check", optional_argument, nullptr, CHECK_OPTION},
- {"compress-program", required_argument, nullptr, COMPRESS_PROGRAM_OPTION},
- {"debug", no_argument, nullptr, DEBUG_PROGRAM_OPTION},
- {"dictionary-order", no_argument, nullptr, 'd'},
- {"ignore-case", no_argument, nullptr, 'f'},
- {"files0-from", required_argument, nullptr, FILES0_FROM_OPTION},
- {"general-numeric-sort", no_argument, nullptr, 'g'},
- {"ignore-nonprinting", no_argument, nullptr, 'i'},
- {"key", required_argument, nullptr, 'k'},
- {"merge", no_argument, nullptr, 'm'},
- {"month-sort", no_argument, nullptr, 'M'},
- {"numeric-sort", no_argument, nullptr, 'n'},
- {"human-numeric-sort", no_argument, nullptr, 'h'},
- {"version-sort", no_argument, nullptr, 'V'},
- {"random-sort", no_argument, nullptr, 'R'},
- {"random-source", required_argument, nullptr, RANDOM_SOURCE_OPTION},
- {"sort", required_argument, nullptr, SORT_OPTION},
- {"output", required_argument, nullptr, 'o'},
- {"reverse", no_argument, nullptr, 'r'},
- {"stable", no_argument, nullptr, 's'},
- {"batch-size", required_argument, nullptr, NMERGE_OPTION},
- {"buffer-size", required_argument, nullptr, 'S'},
- {"field-separator", required_argument, nullptr, 't'},
- {"temporary-directory", required_argument, nullptr, 'T'},
- {"unique", no_argument, nullptr, 'u'},
- {"zero-terminated", no_argument, nullptr, 'z'},
- {"parallel", required_argument, nullptr, PARALLEL_OPTION},
+ {"ignore-leading-blanks", no_argument, NULL, 'b'},
+ {"check", optional_argument, NULL, CHECK_OPTION},
+ {"compress-program", required_argument, NULL, COMPRESS_PROGRAM_OPTION},
+ {"debug", no_argument, NULL, DEBUG_PROGRAM_OPTION},
+ {"dictionary-order", no_argument, NULL, 'd'},
+ {"ignore-case", no_argument, NULL, 'f'},
+ {"files0-from", required_argument, NULL, FILES0_FROM_OPTION},
+ {"general-numeric-sort", no_argument, NULL, 'g'},
+ {"ignore-nonprinting", no_argument, NULL, 'i'},
+ {"key", required_argument, NULL, 'k'},
+ {"merge", no_argument, NULL, 'm'},
+ {"month-sort", no_argument, NULL, 'M'},
+ {"numeric-sort", no_argument, NULL, 'n'},
+ {"human-numeric-sort", no_argument, NULL, 'h'},
+ {"version-sort", no_argument, NULL, 'V'},
+ {"random-sort", no_argument, NULL, 'R'},
+ {"random-source", required_argument, NULL, RANDOM_SOURCE_OPTION},
+ {"sort", required_argument, NULL, SORT_OPTION},
+ {"output", required_argument, NULL, 'o'},
+ {"reverse", no_argument, NULL, 'r'},
+ {"stable", no_argument, NULL, 's'},
+ {"batch-size", required_argument, NULL, NMERGE_OPTION},
+ {"buffer-size", required_argument, NULL, 'S'},
+ {"field-separator", required_argument, NULL, 't'},
+ {"temporary-directory", required_argument, NULL, 'T'},
+ {"unique", no_argument, NULL, 'u'},
+ {"zero-terminated", no_argument, NULL, 'z'},
+ {"parallel", required_argument, NULL, PARALLEL_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0},
+ {NULL, 0, NULL, 0},
};
#define CHECK_TABLE \
@@ -612,7 +661,7 @@ static struct option const long_options[] =
static char const *const check_args[] =
{
#define _ct_(_s, _c) _s,
- CHECK_TABLE nullptr
+ CHECK_TABLE NULL
#undef _ct_
};
static char const check_types[] =
@@ -633,7 +682,7 @@ static char const check_types[] =
static char const *const sort_args[] =
{
#define _st_(_s, _c) _s,
- SORT_TABLE nullptr
+ SORT_TABLE NULL
#undef _st_
};
static char const sort_types[] =
@@ -668,7 +717,7 @@ cs_leave (struct cs_status const *status)
if (status->valid)
{
/* Ignore failure when restoring the signal mask. */
- pthread_sigmask (SIG_SETMASK, &status->sigs, nullptr);
+ pthread_sigmask (SIG_SETMASK, &status->sigs, NULL);
}
}
@@ -747,10 +796,10 @@ register_proc (struct tempnode *temp)
{
if (! proctab)
{
- proctab = hash_initialize (INIT_PROCTAB_SIZE, nullptr,
+ proctab = hash_initialize (INIT_PROCTAB_SIZE, NULL,
proctab_hasher,
proctab_comparator,
- nullptr);
+ NULL);
if (! proctab)
xalloc_die ();
}
@@ -835,7 +884,7 @@ exit_cleanup (void)
/* Create a new temporary file, returning its newly allocated tempnode.
Store into *PFD the file descriptor open for writing.
- If the creation fails, return nullptr and store -1 into *PFD if the
+ If the creation fails, return NULL and store -1 into *PFD if the
failure is due to file descriptor exhaustion and
SURVIVE_FD_EXHAUSTION; otherwise, die. */
@@ -855,7 +904,7 @@ create_temp_file (int *pfd, bool survive_fd_exhaustion)
memcpy (file, temp_dir, len);
memcpy (file + len, slashbase, sizeof slashbase);
- node->next = nullptr;
+ node->next = NULL;
if (++temp_dir_index == temp_dir_count)
temp_dir_index = 0;
@@ -877,14 +926,14 @@ create_temp_file (int *pfd, bool survive_fd_exhaustion)
error (SORT_FAILURE, errno, _("cannot create temporary file in %s"),
quoteaf (temp_dir));
free (node);
- node = nullptr;
+ node = NULL;
}
*pfd = fd;
return node;
}
-/* Return a pointer to stdout status, or nullptr on failure. */
+/* Return a pointer to stdout status, or NULL on failure. */
static struct stat *
get_outstatus (void)
@@ -893,7 +942,7 @@ get_outstatus (void)
static struct stat outstat;
if (outstat_errno == 0)
outstat_errno = fstat (STDOUT_FILENO, &outstat) == 0 ? -1 : errno;
- return outstat_errno < 0 ? &outstat : nullptr;
+ return outstat_errno < 0 ? &outstat : NULL;
}
/* Return a stream for FILE, opened with mode HOW. If HOW is "w",
@@ -901,7 +950,7 @@ get_outstatus (void)
truncated unless FILE is null. When opening for input, "-"
means standard input. To avoid confusion, do not return file
descriptors STDIN_FILENO, STDOUT_FILENO, or STDERR_FILENO when
- opening an ordinary FILE. Return nullptr if unsuccessful.
+ opening an ordinary FILE. Return NULL if unsuccessful.
Use fadvise to specify an access pattern for input files.
There are a few hints we could possibly provide,
@@ -959,7 +1008,7 @@ stream_open (char const *file, char const *how)
else
{
int fd = open (file, O_RDONLY | O_CLOEXEC);
- fp = fd < 0 ? nullptr : fdopen (fd, how);
+ fp = fd < 0 ? NULL : fdopen (fd, how);
}
fadvise (fp, FADVISE_SEQUENTIAL);
}
@@ -1049,19 +1098,19 @@ posix_spawn_file_actions_move_fd (posix_spawn_file_actions_t *actions,
}
/* Look up COMPRESS_PROGRAM in $PATH, and return the resolved program name.
- Upon error, return nullptr with errno set. */
+ Upon error, return NULL with errno set. */
static char const *
get_resolved_compress_program (void)
{
/* Use a cache, to perform the search only once. */
- static char const *resolved_compress_program_cache /* = nullptr */;
+ static char const *resolved_compress_program_cache /* = NULL */;
- if (resolved_compress_program_cache == nullptr)
+ if (resolved_compress_program_cache == NULL)
{
resolved_compress_program_cache =
- find_in_given_path (compress_program, getenv ("PATH"), nullptr, false);
- /* If resolved_compress_program_cache == nullptr, errno is set here. */
+ find_in_given_path (compress_program, getenv ("PATH"), NULL, false);
+ /* If resolved_compress_program_cache == NULL, errno is set here. */
}
return resolved_compress_program_cache;
@@ -1089,7 +1138,7 @@ pipe_child (pid_t *pid, int pipefds[2], int tempfd, bool decompress,
implementations/emulations of posix_spawn we get only a
generic (fatal) error from the child in that case. */
resolved_compress_program = get_resolved_compress_program ();
- if (resolved_compress_program == nullptr)
+ if (resolved_compress_program == NULL)
return errno;
if ((result = posix_spawnattr_init (&attr)))
@@ -1137,8 +1186,8 @@ pipe_child (pid_t *pid, int pipefds[2], int tempfd, bool decompress,
char const *const argv[] =
{
resolved_compress_program,
- decompress ? "-d" : nullptr,
- nullptr
+ decompress ? "-d" : NULL,
+ NULL
};
/* At least NMERGE + 1 subprocesses are needed. More could be created, but
@@ -1157,7 +1206,7 @@ pipe_child (pid_t *pid, int pipefds[2], int tempfd, bool decompress,
if it receives a signal before exec-ing. */
cs_enter (&cs);
saved_temphead = temphead;
- temphead = nullptr;
+ temphead = NULL;
result = posix_spawnp (pid, resolved_compress_program, &actions, &attr,
(char * const *) argv, environ);
@@ -1195,7 +1244,7 @@ pipe_child (pid_t *pid, int pipefds[2], int tempfd, bool decompress,
/* Create a temporary file and, if asked for, start a compressor
to that file. Set *PFP to the file handle and return
the address of the new temp node. If the creation
- fails, return nullptr if the failure is due to file descriptor
+ fails, return NULL if the failure is due to file descriptor
exhaustion and SURVIVE_FD_EXHAUSTION; otherwise, die. */
static struct tempnode *
@@ -1204,7 +1253,7 @@ maybe_create_temp (FILE **pfp, bool survive_fd_exhaustion)
int tempfd;
struct tempnode *node = create_temp_file (&tempfd, survive_fd_exhaustion);
if (! node)
- return nullptr;
+ return NULL;
node->state = UNCOMPRESSED;
@@ -1252,7 +1301,7 @@ create_temp (FILE **pfp)
}
/* Open a compressed temp file and start a decompression process through
- which to filter the input. Return nullptr (setting errno to
+ which to filter the input. Return NULL (setting errno to
EMFILE) if we ran out of file descriptors, and die on any other
kind of failure. */
@@ -1260,14 +1309,14 @@ static FILE *
open_temp (struct tempnode *temp)
{
int tempfd, pipefds[2];
- FILE *fp = nullptr;
+ FILE *fp = NULL;
if (temp->state == UNREAPED)
wait_proc (temp->pid);
tempfd = open (temp->name, O_RDONLY);
if (tempfd < 0)
- return nullptr;
+ return NULL;
pid_t child;
int result = pipe_child (&child, pipefds, tempfd, true,
@@ -1360,9 +1409,7 @@ struct_month_cmp (void const *m1, void const *m2)
static void
inittables (void)
{
- size_t i;
-
- for (i = 0; i < UCHAR_LIM; ++i)
+ for (size_t i = 0; i < UCHAR_LIM; ++i)
{
blanks[i] = i == '\n' || isblank (i);
nondictionary[i] = ! blanks[i] && ! isalnum (i);
@@ -1374,7 +1421,7 @@ inittables (void)
/* If we're not in the "C" locale, read different names for months. */
if (hard_LC_TIME)
{
- for (i = 0; i < MONTHS_PER_YEAR; i++)
+ for (size_t i = 0; i < MONTHS_PER_YEAR; i++)
{
char const *s;
size_t s_len;
@@ -1404,7 +1451,7 @@ specify_nmerge (int oi, char c, char const *s)
{
uintmax_t n;
struct rlimit rlimit;
- enum strtol_error e = xstrtoumax (s, nullptr, 10, &n, "");
+ enum strtol_error e = xstrtoumax (s, NULL, 10, &n, "");
/* Try to find out how many file descriptors we'll be able
to open. We need at least nmerge + 3 (STDIN_FILENO,
@@ -1511,7 +1558,7 @@ static size_t
specify_nthreads (int oi, char c, char const *s)
{
uintmax_t nthreads;
- enum strtol_error e = xstrtoumax (s, nullptr, 10, &nthreads, "");
+ enum strtol_error e = xstrtoumax (s, NULL, 10, &nthreads, "");
if (e == LONGINT_OVERFLOW)
return SIZE_MAX;
if (e != LONGINT_OK)
@@ -1523,7 +1570,8 @@ specify_nthreads (int oi, char c, char const *s)
return nthreads;
}
-/* Return the default sort size. */
+/* Return the default sort size. This is a growth limit, not necessarily
+ the initial allocation size. */
static size_t
default_sort_size (void)
{
@@ -1569,21 +1617,76 @@ default_sort_size (void)
return MAX (size, MIN_SORT_SIZE);
}
-/* Return the sort buffer size to use with the input files identified
+/* Return true if *ALLOC has been adjusted to a size suitable for a sort
+ buffer. */
+
+static bool
+line_aligned_size (size_t *alloc)
+{
+ size_t size = *alloc;
+ size_t alignment = sizeof (struct line);
+ size_t padding = alignment - size % alignment;
+ size_t aligned;
+
+ if (ckd_add (&aligned, size, padding))
+ return false;
+
+ *alloc = aligned;
+ return true;
+}
+
+/* Return the default initial allocation for a growable sort buffer. */
+
+static size_t
+default_initial_sort_size (size_t line_bytes)
+{
+ size_t size = line_bytes + 2;
+ size_t input_size;
+
+ if (ckd_mul (&input_size, INPUT_FILE_SIZE_GUESS, line_bytes + 1)
+ || ckd_add (&size, size, input_size))
+ return SIZE_MAX;
+
+ return MAX (size, MIN_SORT_SIZE);
+}
+
+/* Store into *WORST_CASE the allocation needed for FILE_SIZE input bytes
+ in the worst case, where each input byte is a line delimiter except
+ for a final non-delimiter byte. Return false on overflow. */
+
+static bool
+input_size_buffer_bytes (uintmax_t file_size, size_t line_bytes,
+ size_t *worst_case)
+{
+ size_t worst_case_per_input_byte = line_bytes + 1;
+
+ if (SIZE_MAX / worst_case_per_input_byte < file_size)
+ return false;
+
+ size_t size = file_size * worst_case_per_input_byte;
+ if (SIZE_MAX - size < 1)
+ return false;
+
+ *worst_case = size + 1;
+ return true;
+}
+
+/* Set *POLICY to the sort buffer policy to use with the input files identified
by FPS and FILES, which are alternate names of the same files.
NFILES gives the number of input files; NFPS may be less. Assume
that each input line requires LINE_BYTES extra bytes' worth of line
information. Do not exceed the size bound specified by the user
(or a default size bound, if the user does not specify one). */
-static size_t
-sort_buffer_size (FILE *const *fps, size_t nfps,
- char *const *files, size_t nfiles,
- size_t line_bytes)
+static void
+sort_buffer_policy (FILE *const *fps, size_t nfps,
+ char *const *files, size_t nfiles,
+ size_t line_bytes,
+ struct sort_buffer_policy *policy)
{
- /* A bound on the input size. If zero, the bound hasn't been
- determined yet. */
- static size_t size_bound;
+ size_t size_bound = sort_size ? sort_size : default_sort_size ();
+ size_t initial_bound =
+ sort_size ? size_bound : default_initial_sort_size (line_bytes);
/* In the worst case, each input byte is a newline. */
size_t worst_case_per_input_byte = line_bytes + 1;
@@ -1592,11 +1695,22 @@ sort_buffer_size (FILE *const *fps, size_t nfps,
This extra room might be needed when preparing to read EOF. */
size_t size = worst_case_per_input_byte + 1;
+ initial_bound = MIN (initial_bound, size_bound);
+ policy->limit = size_bound;
+ policy->growth_failed = false;
+
for (size_t i = 0; i < nfiles; i++)
{
struct stat st;
off_t file_size;
size_t worst_case;
+ bool known_size;
+
+ if (size_bound <= size)
+ {
+ policy->initial = size_bound;
+ return;
+ }
if ((i < nfps ? fstat (fileno (fps[i]), &st)
: streq (files[i], "-") ? fstat (STDIN_FILENO, &st)
@@ -1604,35 +1718,42 @@ sort_buffer_size (FILE *const *fps, size_t nfps,
!= 0)
sort_die (_("stat failed"), files[i]);
- if (usable_st_size (&st) && 0 < st.st_size)
+ known_size = usable_st_size (&st) && 0 < st.st_size;
+ if (known_size)
file_size = st.st_size;
else
{
/* The file has unknown size. If the user specified a sort
buffer size, use that; otherwise, guess the size. */
if (sort_size)
- return sort_size;
+ {
+ policy->initial = size_bound;
+ return;
+ }
file_size = INPUT_FILE_SIZE_GUESS;
}
- if (! size_bound)
- {
- size_bound = sort_size;
- if (! size_bound)
- size_bound = default_sort_size ();
- }
-
/* Add the amount of memory needed to represent the worst case
where the input consists entirely of newlines followed by a
single non-newline. Check for overflow. */
- worst_case = file_size * worst_case_per_input_byte + 1;
- if (file_size != worst_case / worst_case_per_input_byte
+ if (! input_size_buffer_bytes (file_size, line_bytes, &worst_case)
|| size_bound - size <= worst_case)
- return size_bound;
+ {
+ policy->initial = size_bound;
+ return;
+ }
+
+ if (! known_size && ! sort_size
+ && (initial_bound <= size || initial_bound - size <= worst_case))
+ {
+ policy->initial = MAX (size, initial_bound);
+ return;
+ }
+
size += worst_case;
}
- return size;
+ policy->initial = MAX (size, MIN_SORT_SIZE);
}
/* Initialize BUF. Reserve LINE_BYTES bytes for each line; LINE_BYTES
@@ -1648,8 +1769,10 @@ initbuf (struct buffer *buf, size_t line_bytes, size_t alloc)
but that's better than failing. */
while (true)
{
- alloc += sizeof (struct line) - alloc % sizeof (struct line);
- buf->buf = malloc (alloc);
+ if (! line_aligned_size (&alloc))
+ buf->buf = NULL;
+ else
+ buf->buf = malloc (alloc);
if (buf->buf)
break;
alloc /= 2;
@@ -1672,6 +1795,67 @@ buffer_linelim (struct buffer const *buf)
return linelim;
}
+/* Try to resize BUF to ALLOC bytes. Return true if successful. This
+ preserves the input data and the line array, adjusting line pointers
+ when the buffer base changes. */
+
+static bool
+try_growbuf (struct buffer *buf, size_t alloc)
+{
+ if (! line_aligned_size (&alloc) || alloc <= buf->alloc)
+ return false;
+
+ char *newbuf = malloc (alloc);
+ if (! newbuf)
+ return false;
+
+ char *oldbuf = buf->buf;
+ struct line *old_linelim = buffer_linelim (buf);
+ struct line *old_line = old_linelim - buf->nlines;
+
+ memcpy (newbuf, oldbuf, buf->used);
+
+ struct line *new_linelim = (void *) (newbuf + alloc);
+ struct line *new_line = new_linelim - buf->nlines;
+ memcpy (new_line, old_line, buf->nlines * sizeof *new_line);
+
+ for (struct line *line = new_line; line < new_linelim; line++)
+ {
+ line->text = newbuf + (line->text - oldbuf);
+ if (keylist)
+ {
+ line->keybeg = newbuf + (line->keybeg - oldbuf);
+ line->keylim = newbuf + (line->keylim - oldbuf);
+ }
+ }
+
+ free (oldbuf);
+ buf->buf = newbuf;
+ buf->alloc = alloc;
+ return true;
+}
+
+/* Try to grow BUF according to POLICY. Return true if the buffer grew. */
+
+static bool
+maybe_growbuf (struct buffer *buf, struct sort_buffer_policy *policy)
+{
+ if (! policy || policy->growth_failed || policy->limit <= buf->alloc)
+ return false;
+
+ size_t alloc;
+ if (buf->alloc <= policy->limit / 3)
+ alloc = buf->alloc * 3;
+ else
+ alloc = policy->limit;
+
+ if (try_growbuf (buf, alloc))
+ return true;
+
+ policy->growth_failed = true;
+ return false;
+}
+
/* Return a pointer to the first character of the field specified
by KEY in LINE. */
@@ -1688,8 +1872,8 @@ begfield (struct line const *line, struct keyfield const *key)
if (tab != TAB_DEFAULT)
while (ptr < lim && sword--)
{
- while (ptr < lim && *ptr != tab)
- ++ptr;
+ char *sep = memchr (ptr, tab, lim - ptr);
+ ptr = sep ? sep : lim;
if (ptr < lim)
++ptr;
}
@@ -1741,8 +1925,8 @@ limfield (struct line const *line, struct keyfield const *key)
if (tab != TAB_DEFAULT)
while (ptr < lim && eword--)
{
- while (ptr < lim && *ptr != tab)
- ++ptr;
+ char *sep = memchr (ptr, tab, lim - ptr);
+ ptr = sep ? sep : lim;
if (ptr < lim && (eword || echar))
++ptr;
}
@@ -1832,7 +2016,8 @@ limfield (struct line const *line, struct keyfield const *key)
Return true if some input was read. */
static bool
-fillbuf (struct buffer *buf, FILE *fp, char const *file)
+fillbuf (struct buffer *buf, FILE *fp, char const *file,
+ struct sort_buffer_policy *policy)
{
struct keyfield const *key = keylist;
char eol = eolchar;
@@ -1929,6 +2114,8 @@ fillbuf (struct buffer *buf, FILE *fp, char const *file)
buf->nlines = buffer_linelim (buf) - line;
if (buf->nlines != 0)
{
+ if (! buf->eof && maybe_growbuf (buf, policy))
+ continue;
buf->left = ptr - line_start;
merge_buffer_size = mergesize + MIN_MERGE_BUFFER_SIZE;
return true;
@@ -2272,7 +2459,7 @@ compare_random (char *restrict texta, size_t lena,
char stackbuf[4000];
char *buf = stackbuf;
size_t bufsize = sizeof stackbuf;
- void *allocated = nullptr;
+ void *allocated = NULL;
uint32_t dig[2][MD5_DIGEST_SIZE / sizeof (uint32_t)];
struct md5_ctx s[2];
s[0] = s[1] = random_md5_state;
@@ -2317,7 +2504,7 @@ compare_random (char *restrict texta, size_t lena,
bool a_fits = sizea <= bufsize;
size_t sizeb =
(textb < limb
- ? (xstrxfrm ((a_fits ? buf + sizea : nullptr), textb,
+ ? (xstrxfrm ((a_fits ? buf + sizea : NULL), textb,
(a_fits ? bufsize - sizea : 0))
+ 1)
: 0);
@@ -2556,7 +2743,6 @@ key_to_opts (struct keyfield const *key, char *opts)
static void
key_warnings (struct keyfield const *gkey, bool gkey_only)
{
- struct keyfield const *key;
struct keyfield ugkey = *gkey;
unsigned long keynum = 1;
bool basic_numeric_field = false;
@@ -2564,7 +2750,7 @@ key_warnings (struct keyfield const *gkey, bool gkey_only)
bool basic_numeric_field_span = false;
bool general_numeric_field_span = false;
- for (key = keylist; key; key = key->next, keynum++)
+ for (struct keyfield *key = keylist; key; key = key->next, keynum++)
{
if (key_numeric (key))
{
@@ -2640,9 +2826,9 @@ key_warnings (struct keyfield const *gkey, bool gkey_only)
/* Flag global options not copied or specified in any key. */
if (ugkey.ignore && (ugkey.ignore == key->ignore))
- ugkey.ignore = nullptr;
+ ugkey.ignore = NULL;
if (ugkey.translate && (ugkey.translate == key->translate))
- ugkey.translate = nullptr;
+ ugkey.translate = NULL;
ugkey.skipsblanks &= !key->skipsblanks;
ugkey.skipeblanks &= !key->skipeblanks;
ugkey.month &= !key->month;
@@ -2787,7 +2973,7 @@ keycompare (struct line const *a, struct line const *b)
char enda = ta[tlena];
char endb = tb[tlenb];
- void *allocated = nullptr;
+ void *allocated = NULL;
char stackbuf[4000];
if (ignore || translate)
@@ -2831,7 +3017,7 @@ keycompare (struct line const *a, struct line const *b)
else if (key->human_numeric)
diff = human_numcompare (ta, tb);
else if (key->month)
- diff = getmonth (ta, nullptr) - getmonth (tb, nullptr);
+ diff = getmonth (ta, NULL) - getmonth (tb, NULL);
else if (key->random)
diff = compare_random (ta, tlena, tb, tlenb);
else if (key->version)
@@ -3044,9 +3230,9 @@ check (char const *file_name, char checkonly)
initbuf (&buf, sizeof (struct line),
MAX (merge_buffer_size, sort_size));
- temp.text = nullptr;
+ temp.text = NULL;
- while (fillbuf (&buf, fp, file_name))
+ while (fillbuf (&buf, fp, file_name, NULL))
{
struct line const *line = buffer_linelim (&buf);
struct line const *linebase = line - buf.nlines;
@@ -3150,7 +3336,7 @@ mergefps (struct sortfile *files, size_t ntemps, size_t nfiles,
struct buffer *buffer = xnmalloc (nfiles, sizeof *buffer);
/* Input buffers for each file. */
struct line saved; /* Saved line storage for unique check. */
- struct line const *savedline = nullptr;
+ struct line const *savedline = NULL;
/* &saved if there is a saved line. */
size_t savealloc = 0; /* Size allocated for the saved line. */
struct line const **cur = xnmalloc (nfiles, sizeof *cur);
@@ -3161,18 +3347,16 @@ mergefps (struct sortfile *files, size_t ntemps, size_t nfiles,
/* Table representing a permutation of fps,
such that cur[ord[0]] is the smallest line
and will be next output. */
- size_t i;
- size_t j;
size_t t;
struct keyfield const *key = keylist;
- saved.text = nullptr;
+ saved.text = NULL;
/* Read initial lines from each input file. */
- for (i = 0; i < nfiles; )
+ for (size_t i = 0; i < nfiles; )
{
initbuf (&buffer[i], sizeof (struct line),
MAX (merge_buffer_size, sort_size / nfiles));
- if (fillbuf (&buffer[i], fps[i], files[i].name))
+ if (fillbuf (&buffer[i], fps[i], files[i].name, NULL))
{
struct line const *linelim = buffer_linelim (&buffer[i]);
cur[i] = linelim - 1;
@@ -3190,7 +3374,7 @@ mergefps (struct sortfile *files, size_t ntemps, size_t nfiles,
}
free (buffer[i].buf);
--nfiles;
- for (j = i; j < nfiles; ++j)
+ for (size_t j = i; j < nfiles; ++j)
{
files[j] = files[j + 1];
fps[j] = fps[j + 1];
@@ -3201,9 +3385,9 @@ mergefps (struct sortfile *files, size_t ntemps, size_t nfiles,
/* Set up the ord table according to comparisons among input lines.
Since this only reorders two items if one is strictly greater than
the other, it is stable. */
- for (i = 0; i < nfiles; ++i)
+ for (size_t i = 0; i < nfiles; ++i)
ord[i] = i;
- for (i = 1; i < nfiles; ++i)
+ for (size_t i = 1; i < nfiles; ++i)
if (0 < compare (cur[ord[i - 1]], cur[ord[i]]))
t = ord[i - 1], ord[i - 1] = ord[i], ord[i] = t, i = 0;
@@ -3218,7 +3402,7 @@ mergefps (struct sortfile *files, size_t ntemps, size_t nfiles,
{
if (savedline && compare (savedline, smallest))
{
- savedline = nullptr;
+ savedline = NULL;
write_line (&saved, ofp, output_file);
}
if (!savedline)
@@ -3256,7 +3440,8 @@ mergefps (struct sortfile *files, size_t ntemps, size_t nfiles,
cur[ord[0]] = smallest - 1;
else
{
- if (fillbuf (&buffer[ord[0]], fps[ord[0]], files[ord[0]].name))
+ if (fillbuf (&buffer[ord[0]], fps[ord[0]], files[ord[0]].name,
+ NULL))
{
struct line const *linelim = buffer_linelim (&buffer[ord[0]]);
cur[ord[0]] = linelim - 1;
@@ -3265,7 +3450,7 @@ mergefps (struct sortfile *files, size_t ntemps, size_t nfiles,
else
{
/* We reached EOF on fps[ord[0]]. */
- for (i = 1; i < nfiles; ++i)
+ for (size_t i = 1; i < nfiles; ++i)
if (ord[i] > ord[0])
--ord[i];
--nfiles;
@@ -3276,7 +3461,7 @@ mergefps (struct sortfile *files, size_t ntemps, size_t nfiles,
zaptemp (files[ord[0]].name);
}
free (buffer[ord[0]].buf);
- for (i = ord[0]; i < nfiles; ++i)
+ for (size_t i = ord[0]; i < nfiles; ++i)
{
fps[i] = fps[i + 1];
files[i] = files[i + 1];
@@ -3284,7 +3469,7 @@ mergefps (struct sortfile *files, size_t ntemps, size_t nfiles,
cur[i] = cur[i + 1];
base[i] = base[i + 1];
}
- for (i = 0; i < nfiles; ++i)
+ for (size_t i = 0; i < nfiles; ++i)
ord[i] = ord[i + 1];
continue;
}
@@ -3312,7 +3497,7 @@ mergefps (struct sortfile *files, size_t ntemps, size_t nfiles,
}
count_of_smaller_lines = lo - 1;
- for (j = 0; j < count_of_smaller_lines; j++)
+ for (size_t j = 0; j < count_of_smaller_lines; j++)
ord[j] = ord[j + 1];
ord[count_of_smaller_lines] = ord0;
}
@@ -3471,13 +3656,13 @@ merge_tree_init (size_t nthreads, size_t nlines, struct line *dest)
struct merge_node *merge_tree = xmalloc (2 * sizeof *merge_tree * nthreads);
struct merge_node *root = merge_tree;
- root->lo = root->hi = root->end_lo = root->end_hi = nullptr;
- root->dest = nullptr;
+ root->lo = root->hi = root->end_lo = root->end_hi = NULL;
+ root->dest = NULL;
root->nlo = root->nhi = nlines;
- root->parent = nullptr;
+ root->parent = NULL;
root->level = MERGE_END;
root->queued = false;
- pthread_mutex_init (&root->lock, nullptr);
+ pthread_mutex_init (&root->lock, NULL);
init_node (root, root + 1, dest, nthreads, nlines, false);
return merge_tree;
@@ -3528,7 +3713,7 @@ init_node (struct merge_node *restrict parent,
node->parent = parent;
node->level = parent->level + 1;
node->queued = false;
- pthread_mutex_init (&node->lock, nullptr);
+ pthread_mutex_init (&node->lock, NULL);
if (nthreads > 1)
{
@@ -3543,8 +3728,8 @@ init_node (struct merge_node *restrict parent,
}
else
{
- node->lo_child = nullptr;
- node->hi_child = nullptr;
+ node->lo_child = NULL;
+ node->hi_child = NULL;
}
return node_pool;
}
@@ -3598,8 +3783,8 @@ queue_init (struct merge_node_queue *queue, size_t nthreads)
time, the heap should accommodate all of them. Counting a null
dummy head for the heap, reserve 2 * NTHREADS nodes. */
queue->priority_queue = heap_alloc (compare_nodes, 2 * nthreads);
- pthread_mutex_init (&queue->mutex, nullptr);
- pthread_cond_init (&queue->cond, nullptr);
+ pthread_mutex_init (&queue->mutex, NULL);
+ pthread_cond_init (&queue->cond, NULL);
}
/* Insert NODE into QUEUE. The caller either holds a lock on NODE, or
@@ -3828,7 +4013,7 @@ sortlines_thread (void *data)
sortlines (args->lines, args->nthreads, args->total_lines,
args->node, args->queue, args->tfp,
args->output_temp);
- return nullptr;
+ return NULL;
}
/* Sort lines, possibly in parallel. The arguments are as in struct
@@ -3870,11 +4055,11 @@ sortlines (struct line *restrict lines, size_t nthreads,
node->lo_child, queue, tfp, temp_output};
if (nthreads > 1 && SUBTHREAD_LINES_HEURISTIC <= nlines
- && pthread_create (&thread, nullptr, sortlines_thread, &args) == 0)
+ && pthread_create (&thread, NULL, sortlines_thread, &args) == 0)
{
sortlines (lines - node->nlo, hi_threads, total_lines,
node->hi_child, queue, tfp, temp_output);
- pthread_join (thread, nullptr);
+ pthread_join (thread, NULL);
}
else
{
@@ -3920,7 +4105,7 @@ static void
avoid_trashing_input (struct sortfile *files, size_t ntemps,
size_t nfiles, char const *outfile)
{
- struct tempnode *tempcopy = nullptr;
+ struct tempnode *tempcopy = NULL;
for (size_t i = ntemps; i < nfiles; i++)
{
@@ -4123,10 +4308,13 @@ sort (char *const *files, size_t nfiles, char const *output_file,
size_t nthreads)
{
struct buffer buf;
+ struct sort_buffer_policy policy;
size_t ntemps = 0;
bool output_file_created = false;
buf.alloc = 0;
+ policy.initial = policy.limit = 0;
+ policy.growth_failed = false;
while (nfiles)
{
@@ -4152,13 +4340,16 @@ sort (char *const *files, size_t nfiles, char const *output_file,
bytes_per_line = sizeof (struct line) * 3 / 2;
if (! buf.alloc)
- initbuf (&buf, bytes_per_line,
- sort_buffer_size (&fp, 1, files, nfiles, bytes_per_line));
+ {
+ sort_buffer_policy (&fp, 1, files, nfiles, bytes_per_line,
+ &policy);
+ initbuf (&buf, bytes_per_line, policy.initial);
+ }
buf.eof = false;
files++;
nfiles--;
- while (fillbuf (&buf, fp, file))
+ while (fillbuf (&buf, fp, file, &policy))
{
struct line *line;
@@ -4173,7 +4364,7 @@ sort (char *const *files, size_t nfiles, char const *output_file,
break;
}
- saved_line.text = nullptr;
+ saved_line.text = NULL;
line = buffer_linelim (&buf);
if (buf.eof && !nfiles && !ntemps && !buf.left)
{
@@ -4189,16 +4380,26 @@ sort (char *const *files, size_t nfiles, char const *output_file,
}
if (1 < buf.nlines)
{
- struct merge_node_queue queue;
- queue_init (&queue, nthreads);
- struct merge_node *merge_tree =
- merge_tree_init (nthreads, buf.nlines, line);
+ if (nthreads > 1)
+ {
+ struct merge_node_queue queue;
+ queue_init (&queue, nthreads);
+ struct merge_node *merge_tree =
+ merge_tree_init (nthreads, buf.nlines, line);
- sortlines (line, nthreads, buf.nlines, merge_tree + 1,
- &queue, tfp, temp_output);
+ sortlines (line, nthreads, buf.nlines, merge_tree + 1,
+ &queue, tfp, temp_output);
- merge_tree_destroy (nthreads, merge_tree);
- queue_destroy (&queue);
+ merge_tree_destroy (nthreads, merge_tree);
+ queue_destroy (&queue);
+ }
+ else
+ {
+ sequential_sort (line, buf.nlines,
+ line - buf.nlines, false);
+ for (size_t i = 0; i < buf.nlines; i++)
+ write_unique (line - i - 1, tfp, temp_output);
+ }
}
else
write_unique (line - 1, tfp, temp_output);
@@ -4242,7 +4443,7 @@ insertkey (struct keyfield *key_arg)
for (p = &keylist; *p; p = &(*p)->next)
continue;
*p = key;
- key->next = nullptr;
+ key->next = NULL;
}
/* Report a bad field specification SPEC, with extra info MSGID. */
@@ -4267,9 +4468,7 @@ incompatible_options (char const *opts)
static void
check_ordering_compatibility (void)
{
- struct keyfield *key;
-
- for (key = keylist; key; key = key->next)
+ for (struct keyfield *key = keylist; key; key = key->next)
if (1 < (key->numeric + key->general_numeric + key->human_numeric
+ key->month + (key->version | key->random | !!key->ignore)))
{
@@ -4285,7 +4484,7 @@ check_ordering_compatibility (void)
/* Parse the leading integer in STRING and store the resulting value
(which must fit into size_t) into *VAL. Return the address of the
suffix after the integer. If the value is too large, silently
- substitute SIZE_MAX. If MSGID is null, return nullptr after
+ substitute SIZE_MAX. If MSGID is null, return NULL after
failure; otherwise, report MSGID and exit on failure. */
static char const *
@@ -4311,7 +4510,7 @@ parse_field_count (char const *string, size_t *val, char const *msgid)
if (msgid)
error (SORT_FAILURE, 0, _("%s: invalid count at start of %s"),
_(msgid), quote (string));
- return nullptr;
+ return NULL;
}
return suffix;
@@ -4397,17 +4596,17 @@ main (int argc, char **argv)
int c = 0;
char checkonly = 0;
bool mergeonly = false;
- char *random_source = nullptr;
+ char *random_source = NULL;
bool need_random = false;
size_t nthreads = 0;
size_t nfiles = 0;
- bool posixly_correct = (getenv ("POSIXLY_CORRECT") != nullptr);
+ bool posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
int posix_ver = posix2_version ();
bool traditional_usage = ! (200112 <= posix_ver && posix_ver < 200809);
char **files;
- char *files_from = nullptr;
+ char *files_from = NULL;
struct Tokens tok;
- char const *outfile = nullptr;
+ char const *outfile = NULL;
bool locale_ok;
initialize_main (&argc, &argv);
@@ -4442,67 +4641,37 @@ main (int argc, char **argv)
thousands_sep = NON_CHAR;
}
- have_read_stdin = false;
inittables ();
{
- size_t i;
- static int const sig[] =
- {
- /* The usual suspects. */
- SIGALRM, SIGHUP, SIGINT, SIGQUIT, SIGTERM,
-#ifdef SIGPOLL
- SIGPOLL,
-#endif
-#ifdef SIGPROF
- SIGPROF,
-#endif
-#ifdef SIGVTALRM
- SIGVTALRM,
-#endif
-#ifdef SIGXCPU
- SIGXCPU,
-#endif
-#ifdef SIGXFSZ
- SIGXFSZ,
-#endif
- };
- enum { nsigs = countof (sig) };
+ enum { nsigs = countof (term_sig) };
-#if SA_NOCLDSTOP
struct sigaction act;
sigemptyset (&caught_signals);
- for (i = 0; i < nsigs; i++)
+ for (size_t i = 0; i < nsigs; i++)
{
- sigaction (sig[i], nullptr, &act);
+ if (term_sig[i] == SIGPIPE)
+ continue; /* Handled below. */
+ sigaction (term_sig[i], NULL, &act);
if (act.sa_handler != SIG_IGN)
- sigaddset (&caught_signals, sig[i]);
+ sigaddset (&caught_signals, term_sig[i]);
}
act.sa_handler = sighandler;
act.sa_mask = caught_signals;
act.sa_flags = 0;
- for (i = 0; i < nsigs; i++)
- if (sigismember (&caught_signals, sig[i]))
- sigaction (sig[i], &act, nullptr);
-#else
- for (i = 0; i < nsigs; i++)
- if (signal (sig[i], SIG_IGN) != SIG_IGN)
- {
- signal (sig[i], sighandler);
- siginterrupt (sig[i], 1);
- }
-#endif
+ for (size_t i = 0; i < nsigs; i++)
+ {
+ if (term_sig[i] == SIGPIPE)
+ continue; /* Handled below. */
+ if (sigismember (&caught_signals, term_sig[i]))
+ sigaction (term_sig[i], &act, NULL);
+ }
}
signal (SIGCHLD, SIG_DFL); /* Don't inherit CHLD handling from parent. */
- /* Ignore SIGPIPE so write failures are reported via EPIPE errno.
- For stdout, sort_die() will reraise SIGPIPE if it was originally SIG_DFL.
- For compression pipes, sort_die() will exit with SORT_FAILURE. */
- default_SIGPIPE = (signal (SIGPIPE, SIG_IGN) == SIG_DFL);
-
/* The signal mask is known, so it is safe to invoke exit_cleanup. */
atexit (exit_cleanup);
@@ -4537,7 +4706,7 @@ main (int argc, char **argv)
else switch (c)
{
case 1:
- key = nullptr;
+ key = NULL;
if (optarg[0] == '+')
{
bool minus_pos_usage = (optind != argc && argv[optind][0] == '-'
@@ -4548,13 +4717,13 @@ main (int argc, char **argv)
/* Treat +POS1 [-POS2] as a key if possible; but silently
treat an operand as a file if it is not a valid +POS1. */
key = key_init (&key_buf);
- s = parse_field_count (optarg + 1, &key->sword, nullptr);
+ s = parse_field_count (optarg + 1, &key->sword, NULL);
if (s && *s == '.')
- s = parse_field_count (s + 1, &key->schar, nullptr);
+ s = parse_field_count (s + 1, &key->schar, NULL);
if (! (key->sword || key->schar))
key->sword = SIZE_MAX;
if (! s || *set_ordering (s, key, bl_start))
- key = nullptr;
+ key = NULL;
else
{
if (minus_pos_usage)
@@ -4785,6 +4954,11 @@ main (int argc, char **argv)
}
}
+ /* Ignore SIGPIPE so write failures are reported via EPIPE errno.
+ For stdout, sort_die() will reraise SIGPIPE if it was originally SIG_DFL.
+ For compression pipes, sort_die() will exit with SORT_FAILURE. */
+ default_SIGPIPE = (signal (SIGPIPE, SIG_IGN) == SIG_DFL);
+
if (files_from)
{
/* When using --files0-from=F, you may not specify any files
@@ -4885,7 +5059,7 @@ main (int argc, char **argv)
error (0, 0, "%s", _("failed to set locale"));
if (hard_LC_COLLATE)
error (0, 0, _("text ordering performed using %s sorting rules"),
- quote (setlocale (LC_COLLATE, nullptr)));
+ quote (setlocale (LC_COLLATE, NULL)));
else
error (0, 0, "%s",
_("text ordering performed using simple byte comparison"));
diff --git a/src/splice.h b/src/splice.h
new file mode 100644
index 000000000..c146563f7
--- /dev/null
+++ b/src/splice.h
@@ -0,0 +1,46 @@
+/* Common definitions for splice and vmsplice.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+#ifndef SPLICE_H
+# define SPLICE_H 1
+
+# if HAVE_VMSPLICE
+
+/* splice() and vmsplice() were introduced at the same time,
+ so assume splice() is available. Note AIX also has a different
+ splice() which is why we don't explicitly check for that function. */
+# define HAVE_SPLICE 1
+
+/* Empirically determined pipe size for best throughput.
+ Needs to be <= /proc/sys/fs/pipe-max-size */
+enum { SPLICE_PIPE_SIZE = 512 * 1024 };
+
+static inline idx_t
+increase_pipe_size (int fd)
+{
+ int pipe_cap = 0;
+# if defined F_SETPIPE_SZ && defined F_GETPIPE_SZ
+ if ((pipe_cap = fcntl (fd, F_SETPIPE_SZ, SPLICE_PIPE_SIZE)) < 0)
+ pipe_cap = fcntl (fd, F_GETPIPE_SZ);
+# endif
+ if (pipe_cap <= 0)
+ pipe_cap = 64 * 1024;
+ return pipe_cap;
+}
+
+# endif
+
+#endif
diff --git a/src/split.c b/src/split.c
index 3d1509113..a8e6f8bf5 100644
--- a/src/split.c
+++ b/src/split.c
@@ -1,5 +1,5 @@
/* split.c -- split a file into pieces.
- Copyright (C) 1988-2025 Free Software Foundation, Inc.
+ Copyright (C) 1988-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,7 +21,6 @@
* support --suppress-matched as in csplit. */
#include <config.h>
-#include <ctype.h>
#include <stdio.h>
#include <getopt.h>
#include <signal.h>
@@ -41,6 +40,7 @@
#include "sig2str.h"
#include "sys-limits.h"
#include "temp-stream.h"
+#include "unistd--.h"
#include "xbinary-io.h"
#include "xdectoint.h"
#include "xstrtol.h"
@@ -67,7 +67,7 @@ static int n_open_pipes;
static bool default_SIGPIPE;
/* Base name of output files. */
-static char const *outbase;
+static char const *outbase = "x";
/* Name of output files. */
static char *outfile;
@@ -92,7 +92,7 @@ static char const *numeric_suffix_start;
static char const *additional_suffix;
/* Name of input file. May be "-". */
-static char const *infile;
+static char const *infile = "-";
/* stat buf for input file. */
static struct stat in_stat_buf;
@@ -133,25 +133,25 @@ enum
static struct option const longopts[] =
{
- {"bytes", required_argument, nullptr, 'b'},
- {"lines", required_argument, nullptr, 'l'},
- {"line-bytes", required_argument, nullptr, 'C'},
- {"number", required_argument, nullptr, 'n'},
- {"elide-empty-files", no_argument, nullptr, 'e'},
- {"unbuffered", no_argument, nullptr, 'u'},
- {"suffix-length", required_argument, nullptr, 'a'},
- {"additional-suffix", required_argument, nullptr,
+ {"bytes", required_argument, NULL, 'b'},
+ {"lines", required_argument, NULL, 'l'},
+ {"line-bytes", required_argument, NULL, 'C'},
+ {"number", required_argument, NULL, 'n'},
+ {"elide-empty-files", no_argument, NULL, 'e'},
+ {"unbuffered", no_argument, NULL, 'u'},
+ {"suffix-length", required_argument, NULL, 'a'},
+ {"additional-suffix", required_argument, NULL,
ADDITIONAL_SUFFIX_OPTION},
- {"numeric-suffixes", optional_argument, nullptr, 'd'},
- {"hex-suffixes", optional_argument, nullptr, 'x'},
- {"filter", required_argument, nullptr, FILTER_OPTION},
- {"verbose", no_argument, nullptr, VERBOSE_OPTION},
- {"separator", required_argument, nullptr, 't'},
- {"-io-blksize", required_argument, nullptr,
+ {"numeric-suffixes", optional_argument, NULL, 'd'},
+ {"hex-suffixes", optional_argument, NULL, 'x'},
+ {"filter", required_argument, NULL, FILTER_OPTION},
+ {"verbose", no_argument, NULL, VERBOSE_OPTION},
+ {"separator", required_argument, NULL, 't'},
+ {"-io-blksize", required_argument, NULL,
IO_BLKSIZE_OPTION}, /* do not document */
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* Return true if the errno value, ERR, is ignorable. */
@@ -182,7 +182,7 @@ set_suffix_length (intmax_t n_units, enum Split_type split_type)
if (numeric_suffix_start)
{
intmax_t n_start;
- strtol_error e = xstrtoimax (numeric_suffix_start, nullptr, 10,
+ strtol_error e = xstrtoimax (numeric_suffix_start, NULL, 10,
&n_start, "");
if (e == LONGINT_OK && n_start < n_units)
{
@@ -235,30 +235,69 @@ default size is 1000 lines, and default PREFIX is 'x'.\n\
emit_stdin_note ();
emit_mandatory_arg_note ();
- fprintf (stdout, _("\
- -a, --suffix-length=N generate suffixes of length N (default %d)\n\
- --additional-suffix=SUFFIX append an additional SUFFIX to file names\n\
- -b, --bytes=SIZE put SIZE bytes per output file\n\
- -C, --line-bytes=SIZE put at most SIZE bytes of records per output file\n\
- -d use numeric suffixes starting at 0, not alphabetic\n\
- --numeric-suffixes[=FROM] same as -d, but allow setting the start value\
-\n\
- -x use hex suffixes starting at 0, not alphabetic\n\
- --hex-suffixes[=FROM] same as -x, but allow setting the start value\n\
- -e, --elide-empty-files do not generate empty output files with '-n'\n\
- --filter=COMMAND write to shell COMMAND; file name is $FILE\n\
- -l, --lines=NUMBER put NUMBER lines/records per output file\n\
- -n, --number=CHUNKS generate CHUNKS output files; see explanation below\n\
- -t, --separator=SEP use SEP instead of newline as the record separator;\n\
- '\\0' (zero) specifies the NUL character\n\
- -u, --unbuffered immediately copy input to output with '-n r/...'\n\
+ oprintf (_("\
+ -a, --suffix-length=N\n\
+ generate suffixes of length N (default %d)\n\
"), DEFAULT_SUFFIX_LENGTH);
- fputs (_("\
- --verbose print a diagnostic just before each\n\
- output file is opened\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ --additional-suffix=SUFFIX\n\
+ append an additional SUFFIX to file names\n\
+"));
+ oputs (_("\
+ -b, --bytes=SIZE\n\
+ put SIZE bytes per output file\n\
+"));
+ oputs (_("\
+ -C, --line-bytes=SIZE\n\
+ put at most SIZE bytes of records per output file\n\
+"));
+ oputs (_("\
+ -d\n\
+ use numeric suffixes starting at 0, not alphabetic\n\
+"));
+ oputs (_("\
+ --numeric-suffixes[=FROM]\n\
+ same as -d, but allow setting the start value\n\
+"));
+ oputs (_("\
+ -x\n\
+ use hex suffixes starting at 0, not alphabetic\n\
+"));
+ oputs (_("\
+ --hex-suffixes[=FROM]\n\
+ same as -x, but allow setting the start value\n\
+"));
+ oputs (_("\
+ -e, --elide-empty-files\n\
+ do not generate empty output files with '-n'\n\
+"));
+ oputs (_("\
+ --filter=COMMAND\n\
+ write to shell COMMAND; file name is $FILE\n\
+"));
+ oputs (_("\
+ -l, --lines=NUMBER\n\
+ put NUMBER lines/records per output file\n\
+"));
+ oputs (_("\
+ -n, --number=CHUNKS\n\
+ generate CHUNKS output files; see explanation below\n\
+"));
+ oputs (_("\
+ -t, --separator=SEP\n\
+ use SEP instead of newline as the record separator;\n\
+ '\\0' (zero) specifies the NUL character\n\
+"));
+ oputs (_("\
+ -u, --unbuffered\n\
+ immediately copy input to output with '-n r/...'\n\
+"));
+ oputs (_("\
+ --verbose\n\
+ print a diagnostic just before each output file is opened\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_size_note ();
fputs (_("\n\
CHUNKS may be:\n\
@@ -269,6 +308,10 @@ CHUNKS may be:\n\
r/N like 'l' but use round robin distribution\n\
r/K/N likewise but only output Kth of N to standard output\n\
"), stdout);
+ fputs (_("\n\
+-n (except -nr) will buffer to $TMPDIR, defaulting to /tmp,\n\
+if the input size cannot easily be determined.\n\
+"), stdout);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -281,7 +324,7 @@ static off_t
copy_to_tmpfile (int fd, char *buf, idx_t bufsize)
{
FILE *tmp;
- if (!temp_stream (&tmp, nullptr))
+ if (!temp_stream (&tmp, NULL))
return -1;
off_t copied = 0;
off_t r;
@@ -550,10 +593,10 @@ create (char const *name)
char const *shell_prog = getenv ("SHELL");
- if (shell_prog == nullptr)
+ if (shell_prog == NULL)
shell_prog = "/bin/sh";
char const *const argv[] = { last_component (shell_prog), "-c",
- filter_command, nullptr };
+ filter_command, NULL };
result = posix_spawn (&child_pid, shell_prog, &actions, &attr,
(char * const *) argv, environ);
@@ -581,14 +624,13 @@ create (char const *name)
static void
closeout (FILE *fp, int fd, pid_t pid, char const *name)
{
- if (fp != nullptr && fclose (fp) != 0 && ! ignorable (errno))
+ if (fp != NULL && fclose (fp) != 0 && ! ignorable (errno))
error (EXIT_FAILURE, errno, "%s", quotef (name));
if (fd >= 0)
{
- if (fp == nullptr && close (fd) < 0)
+ if (fp == NULL && close (fd) < 0)
error (EXIT_FAILURE, errno, "%s", quotef (name));
- int j;
- for (j = 0; j < n_open_pipes; ++j)
+ for (int j = 0; j < n_open_pipes; ++j)
{
if (open_pipes[j] == fd)
{
@@ -643,7 +685,7 @@ cwrite (bool new_file_flag, char const *bp, idx_t bytes)
{
if (!bp && bytes == 0 && elide_empty_files)
return true;
- closeout (nullptr, output_desc, filter_pid, outfile);
+ closeout (NULL, output_desc, filter_pid, outfile);
next_file_name ();
output_desc = create (outfile);
if (output_desc < 0)
@@ -737,7 +779,7 @@ bytes_split (intmax_t n_bytes, intmax_t rem_bytes,
any existing files or notifies any consumers on fifos.
FIXME: Should we do this before EXIT_FAILURE? */
while (opened++ < max_files)
- cwrite (true, nullptr, 0);
+ cwrite (true, NULL, 0);
}
/* Split into pieces of exactly N_LINES lines.
@@ -796,7 +838,7 @@ line_bytes_split (intmax_t n_bytes, char *buf, idx_t bufsize)
ssize_t n_read;
intmax_t n_out = 0; /* for each split. */
idx_t n_hold = 0;
- char *hold = nullptr; /* for lines > bufsize. */
+ char *hold = NULL; /* for lines > bufsize. */
idx_t hold_size = 0;
bool split_line = false; /* Whether a \n was output in a split. */
@@ -810,7 +852,7 @@ line_bytes_split (intmax_t n_bytes, char *buf, idx_t bufsize)
while (n_left)
{
idx_t split_rest = 0;
- char *eoc = nullptr;
+ char *eoc = NULL;
char *eol;
/* Determine End Of Chunk and/or End of Line,
@@ -1001,7 +1043,7 @@ lines_chunk_split (intmax_t k, intmax_t n, char *buf, idx_t bufsize,
if (chunk_end <= n_written)
{
if (! k)
- cwrite (true, nullptr, 0);
+ cwrite (true, NULL, 0);
}
else
next = false;
@@ -1017,7 +1059,7 @@ lines_chunk_split (intmax_t k, intmax_t n, char *buf, idx_t bufsize,
FIXME: Should we do this before EXIT_FAILURE? */
if (!k)
while (chunk_no++ <= n)
- cwrite (true, nullptr, 0);
+ cwrite (true, NULL, 0);
}
/* -n K/N: Extract Kth of N chunks. */
@@ -1029,7 +1071,7 @@ bytes_chunk_extract (intmax_t k, intmax_t n, char *buf, idx_t bufsize,
off_t start;
off_t end;
- assert (0 < k && k <= n);
+ affirm (0 < k && k <= n);
start = (k - 1) * (file_size / n) + MIN (k - 1, file_size % n);
end = k == n ? file_size : k * (file_size / n) + MIN (k, file_size % n);
@@ -1147,7 +1189,7 @@ ofile_open (of_t *files, idx_t i_check, idx_t nfiles)
if (fclose (files[i_reopen].ofile) != 0)
error (EXIT_FAILURE, errno, "%s", quotef (files[i_reopen].of_name));
- files[i_reopen].ofile = nullptr;
+ files[i_reopen].ofile = NULL;
files[i_reopen].ofd = OFD_APPEND;
}
@@ -1177,7 +1219,7 @@ lines_rr (intmax_t k, intmax_t n, char *buf, idx_t bufsize, of_t **filesp)
bool wrote = false;
bool file_limit;
idx_t i_file;
- of_t *files IF_LINT (= nullptr);
+ of_t *files IF_LINT (= NULL);
intmax_t line_no;
if (k)
@@ -1194,7 +1236,7 @@ lines_rr (intmax_t k, intmax_t n, char *buf, idx_t bufsize, of_t **filesp)
next_file_name ();
files[i_file].of_name = xstrdup (outfile);
files[i_file].ofd = OFD_NEW;
- files[i_file].ofile = nullptr;
+ files[i_file].ofile = NULL;
files[i_file].opid = 0;
}
i_file = 0;
@@ -1267,7 +1309,7 @@ lines_rr (intmax_t k, intmax_t n, char *buf, idx_t bufsize, of_t **filesp)
if (fclose (files[i_file].ofile) != 0)
error (EXIT_FAILURE, errno, "%s",
quotef (files[i_file].of_name));
- files[i_file].ofile = nullptr;
+ files[i_file].ofile = NULL;
files[i_file].ofd = OFD_APPEND;
}
if (next && ++i_file == n)
@@ -1335,7 +1377,7 @@ static intmax_t
parse_n_units (char const *arg, char const *multipliers, char const *msgid)
{
intmax_t n;
- if (OVERFLOW_OK < xstrtoimax (arg, nullptr, 10, &n, multipliers) || n < 1)
+ if (OVERFLOW_OK < xstrtoimax (arg, NULL, 10, &n, multipliers) || n < 1)
strtoint_die (msgid, arg);
return n;
}
@@ -1385,16 +1427,13 @@ main (int argc, char **argv)
/* Parse command line options. */
- infile = "-";
- outbase = "x";
-
while (true)
{
/* This is the argv-index of the option we will read next. */
int this_optind = optind ? optind : 1;
c = getopt_long (argc, argv, "0123456789C:a:b:del:n:t:ux",
- longopts, nullptr);
+ longopts, NULL);
if (c == -1)
break;
@@ -1713,7 +1752,7 @@ main (int argc, char **argv)
if (close (STDIN_FILENO) != 0)
error (EXIT_FAILURE, errno, "%s", quotef (infile));
- closeout (nullptr, output_desc, filter_pid, outfile);
+ closeout (NULL, output_desc, filter_pid, outfile);
main_exit (EXIT_SUCCESS);
}
diff --git a/src/stat.c b/src/stat.c
index 348e33793..e682bc6b9 100644
--- a/src/stat.c
+++ b/src/stat.c
@@ -1,5 +1,5 @@
/* stat.c -- display file or file system status
- Copyright (C) 2001-2025 Free Software Foundation, Inc.
+ Copyright (C) 2001-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -203,7 +203,7 @@ enum cached_mode
static char const *const cached_args[] =
{
- "default", "never", "always", nullptr
+ "default", "never", "always", NULL
};
static enum cached_mode const cached_modes[] =
@@ -213,15 +213,15 @@ static enum cached_mode const cached_modes[] =
static struct option const long_options[] =
{
- {"dereference", no_argument, nullptr, 'L'},
- {"file-system", no_argument, nullptr, 'f'},
- {"format", required_argument, nullptr, 'c'},
- {"printf", required_argument, nullptr, PRINTF_OPTION},
- {"terse", no_argument, nullptr, 't'},
- {"cached", required_argument, nullptr, 0},
+ {"dereference", no_argument, NULL, 'L'},
+ {"file-system", no_argument, NULL, 'f'},
+ {"format", required_argument, NULL, 'c'},
+ {"printf", required_argument, NULL, PRINTF_OPTION},
+ {"terse", no_argument, NULL, 't'},
+ {"cached", required_argument, NULL, 0},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* Whether to follow symbolic links; True for --dereference (-L). */
@@ -385,6 +385,8 @@ human_fstype (STRUCT_STATVFS const *statfsbuf)
return "gfs/gfs2";
case S_MAGIC_GPFS: /* 0x47504653 remote */
return "gpfs";
+ case S_MAGIC_GUEST_MEMFD: /* 0x474D454D remote */
+ return "guest-memfd";
case S_MAGIC_HFS: /* 0x4244 local */
return "hfs";
case S_MAGIC_HFS_PLUS: /* 0x482B local */
@@ -464,8 +466,8 @@ human_fstype (STRUCT_STATVFS const *statfsbuf)
return "pidfs";
case S_MAGIC_PIPEFS: /* 0x50495045 remote */
/* FIXME: change syntax or add an optional attribute like "inotify:no".
- pipefs and prlfs are labeled as "remote" so that tail always polls,
- but these aren't really remote file system types. */
+ pipefs is labeled as "remote" so that tail always polls,
+ but it isn't really remote file system type. */
return "pipefs";
case S_MAGIC_PPC_CMM: /* 0xC7571590 local */
return "ppc-cmm-fs";
@@ -740,7 +742,7 @@ out_epoch_sec (char *pformat, size_t prefix_len,
if (c_isdigit (dot[1]))
{
- long int lprec = strtol (dot + 1, nullptr, 10);
+ long int lprec = strtol (dot + 1, NULL, 10);
precision = (lprec <= INT_MAX ? lprec : INT_MAX);
}
else
@@ -760,7 +762,7 @@ out_epoch_sec (char *pformat, size_t prefix_len,
--p;
while (c_isdigit (p[-1]));
- long int lwidth = strtol (p, nullptr, 10);
+ long int lwidth = strtol (p, NULL, 10);
width = (lwidth <= INT_MAX ? lwidth : INT_MAX);
if (1 < width)
{
@@ -843,7 +845,7 @@ out_file_context (char *pformat, size_t prefix_len, char const *filename)
{
error (0, errno, _("failed to get security context of %s"),
quoteaf (filename));
- scontext = nullptr;
+ scontext = NULL;
fail = true;
}
strcpy (pformat + prefix_len, "s");
@@ -943,12 +945,12 @@ print_statfs (char *pformat, size_t prefix_len, MAYBE_UNUSED char mod, char m,
/* Return any bind mounted source for a path.
The caller should not free the returned buffer.
- Return nullptr if no bind mount found. */
+ Return NULL if no bind mount found. */
NODISCARD
static char const *
find_bind_mount (char const * name)
{
- char const * bind_mount = nullptr;
+ char const * bind_mount = NULL;
static struct mount_entry *mount_list;
static bool tried_mount_list = false;
@@ -961,10 +963,9 @@ find_bind_mount (char const * name)
struct stat name_stats;
if (stat (name, &name_stats) != 0)
- return nullptr;
+ return NULL;
- struct mount_entry *me;
- for (me = mount_list; me; me = me->me_next)
+ for (struct mount_entry *me = mount_list; me; me = me->me_next)
{
if (me->me_dummy && me->me_devname[0] == '/'
&& streq (me->me_mountdir, name))
@@ -990,8 +991,8 @@ out_mount_point (char const *filename, char *pformat, size_t prefix_len,
const struct stat *statp)
{
- char const *np = "?", *bp = nullptr;
- char *mp = nullptr;
+ char const *np = "?", *bp = NULL;
+ char *mp = NULL;
bool fail = true;
/* Look for bind mounts first. Note we output the immediate alias,
@@ -1054,20 +1055,20 @@ getenv_quoting_style (void)
{
int i = ARGMATCH (q_style, quoting_style_args, quoting_style_vals);
if (0 <= i)
- set_quoting_style (nullptr, quoting_style_vals[i]);
+ set_quoting_style (NULL, quoting_style_vals[i]);
else
{
- set_quoting_style (nullptr, shell_escape_always_quoting_style);
+ set_quoting_style (NULL, shell_escape_always_quoting_style);
error (0, 0, _("ignoring invalid value of environment "
"variable QUOTING_STYLE: %s"), quote (q_style));
}
}
else
- set_quoting_style (nullptr, shell_escape_always_quoting_style);
+ set_quoting_style (NULL, shell_escape_always_quoting_style);
}
/* Equivalent to quotearg(), but explicit to avoid syntax checks. */
-#define quoteN(x) quotearg_style (get_quoting_style (nullptr), x)
+#define quoteN(x) quotearg_style (get_quoting_style (NULL), x)
/* Output a single-character \ escape. */
@@ -1145,8 +1146,7 @@ print_it (char const *format, int fd, char const *filename,
};
size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
char *dest = xmalloc (n_alloc);
- char const *b;
- for (b = format; *b; b++)
+ for (char const *b = format; *b; b++)
{
switch (*b)
{
@@ -1347,9 +1347,8 @@ static unsigned int
format_to_mask (char const *format)
{
unsigned int mask = 0;
- char const *b;
- for (b = format; *b; b++)
+ for (char const *b = format; *b; b++)
{
if (*b != '%')
continue;
@@ -1520,7 +1519,7 @@ print_stat (char *pformat, size_t prefix_len, char mod, char m,
if (S_ISLNK (statbuf->st_mode))
{
char *linkname = areadlink_with_size (filename, statbuf->st_size);
- if (linkname == nullptr)
+ if (linkname == NULL)
{
error (0, errno, _("cannot read symbolic link %s"),
quoteaf (filename));
@@ -1760,24 +1759,36 @@ Display file or file system status.\n\
emit_mandatory_arg_note ();
- fputs (_("\
- -L, --dereference follow links\n\
- -f, --file-system display file system status instead of file status\n\
-"), stdout);
- fputs (_("\
- --cached=MODE specify how to use cached attributes;\n\
- useful on remote file systems. See MODE below\n\
-"), stdout);
- fputs (_("\
- -c --format=FORMAT use the specified FORMAT instead of the default;\n\
- output a newline after each use of FORMAT\n\
- --printf=FORMAT like --format, but interpret backslash escapes,\n\
- and do not output a mandatory trailing newline;\n\
- if you want a newline, include \\n in FORMAT\n\
- -t, --terse print the information in terse form\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -L, --dereference\n\
+ follow links\n\
+"));
+ oputs (_("\
+ -f, --file-system\n\
+ display file system status instead of file status\n\
+"));
+ oputs (_("\
+ --cached=MODE\n\
+ specify how to use cached attributes;\n\
+ useful on remote file systems. See MODE below\n\
+"));
+ oputs (_("\
+ -c, --format=FORMAT\n\
+ use the specified FORMAT instead of the default;\n\
+ output a newline after each use of FORMAT\n\
+"));
+ oputs (_("\
+ --printf=FORMAT\n\
+ like --format, but interpret backslash escapes,\n\
+ and do not output a mandatory trailing newline;\n\
+ if you want a newline, include \\n in FORMAT\n\
+"));
+ oputs (_("\
+ -t, --terse\n\
+ print the information in terse form\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\n\
The MODE argument of --cached can be: always, never, or default.\n\
@@ -1881,7 +1892,7 @@ main (int argc, char *argv[])
int c;
bool fs = false;
bool terse = false;
- char *format = nullptr;
+ char *format = NULL;
char *format2;
bool ok = true;
@@ -1897,7 +1908,7 @@ main (int argc, char *argv[])
atexit (close_stdout);
- while ((c = getopt_long (argc, argv, "c:fLt", long_options, nullptr)) != -1)
+ while ((c = getopt_long (argc, argv, "c:fLt", long_options, NULL)) != -1)
{
switch (c)
{
@@ -1959,7 +1970,17 @@ main (int argc, char *argv[])
if (format)
{
- if (strstr (format, "%N"))
+ bool need_quoting_style = false;
+ for (char const *p = format; (p = strchr (p, '%'));
+ p += (p[1] == '%') + 1)
+ {
+ if (p[1] == 'N')
+ {
+ need_quoting_style = true;
+ break;
+ }
+ }
+ if (need_quoting_style)
getenv_quoting_style ();
format2 = format;
}
diff --git a/src/statx.h b/src/statx.h
index 3293c96ab..8fc4ca02a 100644
--- a/src/statx.h
+++ b/src/statx.h
@@ -1,5 +1,5 @@
/* statx -> stat conversion functions for coreutils
- Copyright (C) 2019-2025 Free Software Foundation, Inc.
+ Copyright (C) 2019-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/stdbuf.c b/src/stdbuf.c
index c3d72ce5f..ff19c6056 100644
--- a/src/stdbuf.c
+++ b/src/stdbuf.c
@@ -1,5 +1,5 @@
/* stdbuf -- setup the standard streams for a command
- Copyright (C) 2009-2025 Free Software Foundation, Inc.
+ Copyright (C) 2009-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -46,12 +46,12 @@ static struct
static struct option const longopts[] =
{
- {"input", required_argument, nullptr, 'i'},
- {"output", required_argument, nullptr, 'o'},
- {"error", required_argument, nullptr, 'e'},
+ {"input", required_argument, NULL, 'i'},
+ {"output", required_argument, NULL, 'o'},
+ {"error", required_argument, NULL, 'e'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* Set size to the value of STR, interpreted as a decimal integer,
@@ -64,7 +64,7 @@ static int
parse_size (char const *str, size_t *size)
{
uintmax_t tmp_size;
- enum strtol_error e = xstrtoumax (str, nullptr, 10,
+ enum strtol_error e = xstrtoumax (str, NULL, 10,
&tmp_size, "EGkKMPQRTYZ0");
if (e == LONGINT_OK && SIZE_MAX < tmp_size)
e = LONGINT_OVERFLOW;
@@ -94,13 +94,17 @@ Run COMMAND, with modified buffering operations for its standard streams.\n\
emit_mandatory_arg_note ();
- fputs (_("\
+ oputs (_("\
-i, --input=MODE adjust standard input stream buffering\n\
+"));
+ oputs (_("\
-o, --output=MODE adjust standard output stream buffering\n\
+"));
+ oputs (_("\
-e, --error=MODE adjust standard error stream buffering\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\n\
If MODE is 'L' the corresponding stream will be line buffered.\n\
This option is invalid with standard input.\n"), stdout);
@@ -146,12 +150,11 @@ set_program_path (char const *arg)
program_path = dir_name (path);
else if ((path = getenv ("PATH")))
{
- char *dir;
path = xstrdup (path);
- for (dir = strtok (path, ":"); dir != nullptr;
- dir = strtok (nullptr, ":"))
+ for (char *dir = strtok (path, ":"); dir != NULL;
+ dir = strtok (NULL, ":"))
{
- char *candidate = file_name_concat (dir, arg, nullptr);
+ char *candidate = file_name_concat (dir, arg, NULL);
if (access (candidate, X_OK) == 0)
{
program_path = dir_name (candidate);
@@ -220,7 +223,7 @@ set_LD_PRELOAD (void)
char const *const search_path[] = {
program_path,
PKGLIBEXECDIR,
- nullptr
+ NULL
};
char const *const *path = search_path;
@@ -322,7 +325,7 @@ main (int argc, char **argv)
initialize_exit_failure (EXIT_CANCELED);
atexit (close_stdout);
- while ((c = getopt_long (argc, argv, "+i:o:e:", longopts, nullptr)) != -1)
+ while ((c = getopt_long (argc, argv, "+i:o:e:", longopts, NULL)) != -1)
{
int opt_fileno;
diff --git a/src/stty.c b/src/stty.c
index 919e32bb3..d2de21698 100644
--- a/src/stty.c
+++ b/src/stty.c
@@ -1,5 +1,5 @@
/* stty -- change and print terminal line settings
- Copyright (C) 1990-2025 Free Software Foundation, Inc.
+ Copyright (C) 1990-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -373,7 +373,7 @@ static struct mode_info const mode_info[] =
{"crt", combination, OMIT, 0, 0},
{"dec", combination, OMIT, 0, 0},
- {nullptr, control, 0, 0, 0}
+ {NULL, control, 0, 0, 0}
};
/* Control character settings. */
@@ -430,7 +430,7 @@ static struct control_info const control_info[] =
/* These must be last because of the display routines. */
{"min", 1, VMIN},
{"time", 0, VTIME},
- {nullptr, 0, 0}
+ {NULL, 0, 0}
};
static char const *visible (cc_t ch);
@@ -485,13 +485,13 @@ enum
static struct option const longopts[] =
{
- {"all", no_argument, nullptr, 'a'},
- {"save", no_argument, nullptr, 'g'},
- {"file", required_argument, nullptr, 'F'},
- {"-debug", no_argument, nullptr, DEV_DEBUG_OPTION},
+ {"all", no_argument, NULL, 'a'},
+ {"save", no_argument, NULL, 'g'},
+ {"file", required_argument, NULL, 'F'},
+ {"-debug", no_argument, NULL, DEV_DEBUG_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* Print format string MESSAGE and optional args.
@@ -551,13 +551,17 @@ Print or change terminal characteristics.\n\
emit_mandatory_arg_note ();
- fputs (_("\
+ oputs (_("\
-a, --all print all current settings in human-readable form\n\
+"));
+ oputs (_("\
-g, --save print all current settings in a stty-readable form\n\
+"));
+ oputs (_("\
-F, --file=DEVICE open and use DEVICE instead of standard input\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
Optional - before SETTING indicates negation. An * marks non-POSIX\n\
@@ -1115,7 +1119,6 @@ apply_settings (bool checking, char const *device_name,
bool match_found = false;
bool not_set_attr = false;
bool reversed = false;
- int i;
if (! arg)
continue;
@@ -1130,7 +1133,7 @@ apply_settings (bool checking, char const *device_name,
tcsetattr_options = reversed ? TCSANOW : TCSADRAIN;
continue;
}
- for (i = 0; mode_info[i].name != nullptr; ++i)
+ for (int i = 0; mode_info[i].name != NULL; ++i)
{
if (streq (arg, mode_info[i].name))
{
@@ -1151,7 +1154,7 @@ apply_settings (bool checking, char const *device_name,
}
if (!match_found)
{
- for (i = 0; control_info[i].name != nullptr; ++i)
+ for (int i = 0; control_info[i].name != NULL; ++i)
{
if (streq (arg, control_info[i].name))
{
@@ -1295,7 +1298,7 @@ main (int argc, char **argv)
bool verbose_output;
bool recoverable_output;
bool noargs = true;
- char *file_name = nullptr;
+ char *file_name = NULL;
char const *device_name;
initialize_main (&argc, &argv);
@@ -1321,7 +1324,7 @@ main (int argc, char **argv)
short and long options, --, POSIXLY_CORRECT, etc. */
while ((optc = getopt_long (argc - argi, argv + argi, "-agF:",
- longopts, nullptr))
+ longopts, NULL))
!= -1)
{
switch (optc)
@@ -1370,7 +1373,7 @@ main (int argc, char **argv)
/* Clear fully-parsed arguments, so they don't confuse the 2nd pass. */
while (opti < optind)
- argv[argi + opti++] = nullptr;
+ argv[argi + opti++] = NULL;
}
/* Specifying both -a and -g gets an error. */
@@ -1491,7 +1494,7 @@ set_mode (struct mode_info const *info, bool reversed, struct termios *mode)
bitsp = mode_type_flag (info->type, mode);
- if (bitsp == nullptr)
+ if (bitsp == NULL)
{
/* Combination mode. */
if (streq (info->name, "evenp") || streq (info->name, "parity"))
@@ -1849,8 +1852,8 @@ screen_columns (void)
/* Use $COLUMNS if it's in [1..INT_MAX]. */
char *col_string = getenv ("COLUMNS");
long int n_columns;
- if (!(col_string != nullptr
- && xstrtol (col_string, nullptr, 0, &n_columns, "") == LONGINT_OK
+ if (!(col_string != NULL
+ && xstrtol (col_string, NULL, 0, &n_columns, "") == LONGINT_OK
&& 0 < n_columns
&& n_columns <= INT_MAX))
n_columns = 80;
@@ -1877,7 +1880,7 @@ mode_type_flag (enum mode_type type, struct termios *mode)
return &mode->c_lflag;
case combination:
- return nullptr;
+ return NULL;
default:
unreachable ();
@@ -1907,7 +1910,6 @@ display_settings (enum output_type output_type, struct termios *mode,
static void
display_changed (struct termios *mode)
{
- int i;
bool empty_line;
tcflag_t *bitsp;
unsigned long mask;
@@ -1921,7 +1923,7 @@ display_changed (struct termios *mode)
current_col = 0;
empty_line = true;
- for (i = 0; !streq (control_info[i].name, "min"); ++i)
+ for (int i = 0; !streq (control_info[i].name, "min"); ++i)
{
if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
continue;
@@ -1959,7 +1961,7 @@ display_changed (struct termios *mode)
current_col = 0;
empty_line = true;
- for (i = 0; mode_info[i].name != nullptr; ++i)
+ for (int i = 0; mode_info[i].name != NULL; ++i)
{
if (mode_info[i].flags & OMIT)
continue;
@@ -1998,7 +2000,6 @@ display_changed (struct termios *mode)
static void
display_all (struct termios *mode, char const *device_name)
{
- int i;
tcflag_t *bitsp;
unsigned long mask;
enum mode_type prev_type = control;
@@ -2013,7 +2014,7 @@ display_all (struct termios *mode, char const *device_name)
putchar ('\n');
current_col = 0;
- for (i = 0; ! streq (control_info[i].name, "min"); ++i)
+ for (int i = 0; ! streq (control_info[i].name, "min"); ++i)
{
#ifdef VFLUSHO
/* 'flush' is the deprecated equivalent of 'discard'. */
@@ -2045,7 +2046,7 @@ display_all (struct termios *mode, char const *device_name)
putchar ('\n');
current_col = 0;
- for (i = 0; mode_info[i].name != nullptr; ++i)
+ for (int i = 0; mode_info[i].name != NULL; ++i)
{
if (mode_info[i].flags & OMIT)
continue;
@@ -2145,8 +2146,7 @@ recover_mode (char const *arg, struct termios *mode)
{
tcflag_t flag[4];
char const *s = arg;
- size_t i;
- for (i = 0; i < 4; i++)
+ for (size_t i = 0; i < 4; i++)
{
char *p;
if (strtoul_tcflag_t (s, 16, &p, flag + i, ':') != 0)
@@ -2158,7 +2158,7 @@ recover_mode (char const *arg, struct termios *mode)
mode->c_cflag = flag[2];
mode->c_lflag = flag[3];
- for (i = 0; i < NCCS; ++i)
+ for (size_t i = 0; i < NCCS; ++i)
{
char *p;
char delim = i < NCCS - 1 ? ':' : '\0';
@@ -2234,10 +2234,9 @@ string_to_baud (char const *arg)
static void
sane_mode (struct termios *mode)
{
- int i;
tcflag_t *bitsp;
- for (i = 0; control_info[i].name; ++i)
+ for (int i = 0; control_info[i].name; ++i)
{
#if VMIN == VEOF
if (streq (control_info[i].name, "min"))
@@ -2246,7 +2245,7 @@ sane_mode (struct termios *mode)
mode->c_cc[control_info[i].offset] = control_info[i].saneval;
}
- for (i = 0; mode_info[i].name != nullptr; ++i)
+ for (int i = 0; mode_info[i].name != NULL; ++i)
{
if (mode_info[i].flags & NO_SETATTR)
continue;
diff --git a/src/sum.c b/src/sum.c
index 08bb88b12..51da0acfd 100644
--- a/src/sum.c
+++ b/src/sum.c
@@ -1,5 +1,5 @@
/* sum -- checksum and count the blocks in a file
- Copyright (C) 1986-2025 Free Software Foundation, Inc.
+ Copyright (C) 1986-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -31,13 +31,13 @@
Return -1 on error, 0 on success. */
int
-bsd_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
+bsd_sum_stream (FILE *stream, void *resstream, intmax_t *length)
{
int ret = -1;
- size_t sum, n;
+ idx_t sum;
int checksum = 0; /* The checksum mod 2^16. */
- uintmax_t total_bytes = 0; /* The number of bytes. */
- static const size_t buffer_length = 32768;
+ intmax_t total_bytes = 0; /* The number of bytes. */
+ enum { buffer_length = 32768 };
uint8_t *buffer = malloc (buffer_length);
if (! buffer)
@@ -51,12 +51,7 @@ bsd_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
/* Read block */
while (true)
{
- n = fread (buffer + sum, 1, buffer_length - sum, stream);
- sum += n;
-
- if (buffer_length == sum)
- break;
-
+ size_t n = fread (buffer + sum, 1, buffer_length - sum, stream);
if (n == 0)
{
if (ferror (stream))
@@ -64,6 +59,11 @@ bsd_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
goto final_process;
}
+ sum += n;
+
+ if (buffer_length == sum)
+ break;
+
if (feof (stream))
goto final_process;
}
@@ -74,28 +74,26 @@ bsd_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
checksum += buffer[i];
checksum &= 0xffff; /* Keep it within bounds. */
}
- if (total_bytes + sum < total_bytes)
+ if (ckd_add (&total_bytes, total_bytes, sum))
{
errno = EOVERFLOW;
goto cleanup_buffer;
}
- total_bytes += sum;
}
final_process:;
- for (size_t i = 0; i < sum; i++)
+ for (idx_t i = 0; i < sum; i++)
{
checksum = (checksum >> 1) + ((checksum & 1) << 15);
checksum += buffer[i];
checksum &= 0xffff; /* Keep it within bounds. */
}
- if (total_bytes + sum < total_bytes)
+ if (ckd_add (&total_bytes, total_bytes, sum))
{
errno = EOVERFLOW;
goto cleanup_buffer;
}
- total_bytes += sum;
memcpy (resstream, &checksum, sizeof checksum);
*length = total_bytes;
@@ -109,12 +107,12 @@ cleanup_buffer:
Return -1 on error, 0 on success. */
int
-sysv_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
+sysv_sum_stream (FILE *stream, void *resstream, intmax_t *length)
{
int ret = -1;
- size_t sum, n;
- uintmax_t total_bytes = 0;
- static const size_t buffer_length = 32768;
+ idx_t sum;
+ intmax_t total_bytes = 0;
+ enum { buffer_length = 32768 };
uint8_t *buffer = malloc (buffer_length);
if (! buffer)
@@ -131,12 +129,7 @@ sysv_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
/* Read block */
while (true)
{
- n = fread (buffer + sum, 1, buffer_length - sum, stream);
- sum += n;
-
- if (buffer_length == sum)
- break;
-
+ size_t n = fread (buffer + sum, 1, buffer_length - sum, stream);
if (n == 0)
{
if (ferror (stream))
@@ -144,6 +137,11 @@ sysv_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
goto final_process;
}
+ sum += n;
+
+ if (buffer_length == sum)
+ break;
+
if (feof (stream))
goto final_process;
}
@@ -186,7 +184,7 @@ cleanup_buffer:
void
output_bsd (char const *file, MAYBE_UNUSED int binary_file, void const *digest,
bool raw, MAYBE_UNUSED bool tagged, unsigned char delim, bool args,
- uintmax_t length)
+ intmax_t length)
{
if (raw)
{
@@ -211,7 +209,7 @@ output_bsd (char const *file, MAYBE_UNUSED int binary_file, void const *digest,
void
output_sysv (char const *file, MAYBE_UNUSED int binary_file,
void const *digest, bool raw, MAYBE_UNUSED bool tagged,
- unsigned char delim, bool args, uintmax_t length)
+ unsigned char delim, bool args, intmax_t length)
{
if (raw)
{
diff --git a/src/sum.h b/src/sum.h
index bd251a034..92e8b1b90 100644
--- a/src/sum.h
+++ b/src/sum.h
@@ -1,18 +1,18 @@
extern int
-bsd_sum_stream (FILE *stream, void *resstream, uintmax_t *length);
+bsd_sum_stream (FILE *stream, void *resstream, intmax_t *length);
extern int
-sysv_sum_stream (FILE *stream, void *resstream, uintmax_t *length);
+sysv_sum_stream (FILE *stream, void *resstream, intmax_t *length);
-typedef int (*sumfn)(FILE *, void *, uintmax_t *);
+typedef int (*sumfn)(FILE *, void *, intmax_t *);
extern void
output_bsd (char const *file, int binary_file, void const *digest,
bool raw, bool tagged, unsigned char delim, bool args,
- uintmax_t length);
+ intmax_t length);
extern void
output_sysv (char const *file, int binary_file, void const *digest,
bool raw, bool tagged, unsigned char delim, bool args,
- uintmax_t length);
+ intmax_t length);
diff --git a/src/sync.c b/src/sync.c
index 7c518b5fa..5f94d643f 100644
--- a/src/sync.c
+++ b/src/sync.c
@@ -1,5 +1,5 @@
/* sync - update the super block
- Copyright (C) 1994-2025 Free Software Foundation, Inc.
+ Copyright (C) 1994-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -44,11 +44,11 @@ enum sync_mode
static struct option const long_options[] =
{
- {"data", no_argument, nullptr, 'd'},
- {"file-system", no_argument, nullptr, 'f'},
+ {"data", no_argument, NULL, 'd'},
+ {"file-system", no_argument, NULL, 'f'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -67,15 +67,14 @@ or their containing file systems.\n\
\n\
"), stdout);
- fputs (_("\
+ oputs (_("\
-d, --data sync only file data, no unneeded metadata\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-f, --file-system sync the file systems that contain the files\n\
-"), stdout);
-
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -87,9 +86,7 @@ or their containing file systems.\n\
static bool
sync_arg (enum sync_mode mode, char const *file)
{
- bool ret = true;
int open_flags = O_RDONLY | O_NONBLOCK;
- int fd;
#if defined _AIX || defined __CYGWIN__
/* AIX 7.1, CYGWIN 2.9.0, fsync requires write access to file. */
@@ -99,7 +96,7 @@ sync_arg (enum sync_mode mode, char const *file)
/* Note O_PATH might be supported with syncfs(),
though as of Linux 3.18 is not. */
- fd = open (file, open_flags);
+ int fd = open (file, open_flags);
if (fd < 0)
{
/* Use the O_RDONLY errno, which is significant
@@ -114,6 +111,7 @@ sync_arg (enum sync_mode mode, char const *file)
}
}
+ bool ret = true;
/* We used O_NONBLOCK above to not hang with fifos,
so reset that here. */
int fdflags = fcntl (fd, F_GETFL);
@@ -125,7 +123,7 @@ sync_arg (enum sync_mode mode, char const *file)
ret = false;
}
- if (ret == true)
+ if (ret)
{
int sync_status = -1;
@@ -167,10 +165,7 @@ sync_arg (enum sync_mode mode, char const *file)
int
main (int argc, char **argv)
{
- int c;
- bool args_specified;
bool arg_data = false, arg_file_system = false;
- enum sync_mode mode;
bool ok = true;
initialize_main (&argc, &argv);
@@ -181,7 +176,8 @@ main (int argc, char **argv)
atexit (close_stdout);
- while ((c = getopt_long (argc, argv, "df", long_options, nullptr))
+ int c;
+ while ((c = getopt_long (argc, argv, "df", long_options, NULL))
!= -1)
{
switch (c)
@@ -203,7 +199,7 @@ main (int argc, char **argv)
}
}
- args_specified = optind < argc;
+ bool args_specified = optind < argc;
if (arg_data && arg_file_system)
error (EXIT_FAILURE, 0,
@@ -212,6 +208,7 @@ main (int argc, char **argv)
if (!args_specified && arg_data)
error (EXIT_FAILURE, 0, _("--data needs at least one argument"));
+ enum sync_mode mode;
if (! args_specified || (arg_file_system && ! HAVE_SYNCFS))
mode = MODE_SYNC;
else if (arg_file_system)
diff --git a/src/system.h b/src/system.h
index b8612ff5e..d0f2e0c44 100644
--- a/src/system.h
+++ b/src/system.h
@@ -1,5 +1,5 @@
/* system-dependent definitions for coreutils
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,6 +20,8 @@
#include <alloca.h>
+#include <ctype.h>
+
#include <sys/stat.h>
/* Commonly used file permission combination. */
@@ -158,6 +160,44 @@ c32isnbspace (char32_t wc)
return wc == 0x00A0 || wc == 0x2007 || wc == 0x202F || wc == 0x2060;
}
+ATTRIBUTE_PURE
+static inline int
+c32isvertspace (char32_t wc)
+{
+ return wc == 0x000A || wc == 0x000B || wc == 0x000C || wc == 0x000D
+ || wc == 0x2028 || wc == 0x2029;
+}
+
+
+/* c32isblank() is too variable on non GLIBC platforms.
+ E.g., does not include \u3000 ideographic space on musl.
+ E.g., does include non-breaking space on Solaris and NetBSD.
+ This equivalent is more consistent across systems. */
+ATTRIBUTE_PURE
+static inline bool
+c32issep (char32_t wc)
+{
+#if defined __GLIBC__
+ return !! c32isblank (wc);
+#else
+ return c32isspace (wc) && ! c32isvertspace (wc) && ! c32isnbspace (wc);
+#endif
+}
+
+/* Return true if the current charset is UTF-8. */
+static inline bool
+is_utf8_charset (void)
+{
+ static int is_utf8 = -1;
+ if (is_utf8 == -1)
+ {
+ char32_t w;
+ mbstate_t mbs; mbszero (&mbs);
+ is_utf8 = mbrtoc32 (&w, "\xe2\x9f\xb8", 3, &mbs) == 3 && w == 0x27F8;
+ }
+ return is_utf8;
+}
+
#include <locale.h>
/* Take care of NLS matters. */
@@ -261,7 +301,7 @@ readdir_ignoring_dot_and_dotdot (DIR *dirp)
while (true)
{
struct dirent const *dp = readdir (dirp);
- if (dp == nullptr || ! dot_or_dotdot (dp->d_name))
+ if (dp == NULL || ! dot_or_dotdot (dp->d_name))
return dp;
}
}
@@ -289,7 +329,7 @@ directory_status (int fd_cwd, char const *dir)
return errno;
dirp = fdopendir (fd);
- if (dirp == nullptr)
+ if (dirp == NULL)
{
saved_errno = errno;
close (fd);
@@ -315,11 +355,11 @@ enum
};
#define GETOPT_HELP_OPTION_DECL \
- "help", no_argument, nullptr, GETOPT_HELP_CHAR
+ "help", no_argument, NULL, GETOPT_HELP_CHAR
#define GETOPT_VERSION_OPTION_DECL \
- "version", no_argument, nullptr, GETOPT_VERSION_CHAR
+ "version", no_argument, NULL, GETOPT_VERSION_CHAR
#define GETOPT_SELINUX_CONTEXT_OPTION_DECL \
- "context", optional_argument, nullptr, 'Z'
+ "context", optional_argument, NULL, 'Z'
#define case_GETOPT_HELP_CHAR \
case GETOPT_HELP_CHAR: \
@@ -335,9 +375,9 @@ enum
"for details about the options it supports.\n")
#define HELP_OPTION_DESCRIPTION \
- _(" --help display this help and exit\n")
+ _(" --help\n display this help and exit\n")
#define VERSION_OPTION_DESCRIPTION \
- _(" --version output version information and exit\n")
+ _(" --version\n output version information and exit\n")
#include "closein.h"
#include "closeout.h"
@@ -356,7 +396,7 @@ enum
#define case_GETOPT_VERSION_CHAR(Program_name, Authors) \
case GETOPT_VERSION_CHAR: \
version_etc (stdout, Program_name, PACKAGE_NAME, Version, Authors, \
- (char *) nullptr); \
+ (char *) NULL); \
exit (EXIT_SUCCESS); \
break;
@@ -536,6 +576,167 @@ is_nul (void const *buf, size_t length)
#define DECIMAL_DIGIT_ACCUMULATE(Accum, Digit_val) \
(!ckd_mul (&(Accum), Accum, 10) && !ckd_add (&(Accum), Accum, Digit_val))
+
+/* Output --option descriptions;
+ formatted with ANSI format and hyperlink codes.
+ Any postprocessors like help2man etc. are expected to handle this,
+ though it can be disabled in edge cases with the TERM=dumb env var. */
+
+#define oputs(option) oputs_ (PROGRAM_NAME, option)
+static inline void
+oputs_ (MAYBE_UNUSED char const *program, char const *option)
+{
+ static int help_no_sgr =
+#if ! defined MANUAL_URL && ! defined BOLD_MAN_REFS
+ 1; /* Disable. */
+#else
+ -1; /* Lookup. */
+#endif
+ if (help_no_sgr == -1)
+ {
+ /* Note we don't consult isatty() since usually you
+ would want markup when piping to grep/less etc. */
+ char const *term = getenv ("TERM");
+ help_no_sgr = (!term || !*term || streq (term, "dumb"));
+ }
+ if (help_no_sgr)
+ {
+ fputs (option, stdout);
+ return;
+ }
+
+ bool double_space = true;
+ char const *first_word = option + strspn (option, " \t\n");
+ char const *option_text = strchr (option, '-');
+ if (!option_text) /* for dd(1) option syntax. */
+ {
+ option_text = first_word;
+ /* Just match first word to support single spaced
+ translated dd "foo=bar description" format. */
+ double_space = false;
+ }
+ else if (option_text != first_word) /* for test(1) option syntax. */
+ {
+ /* Ensure only a single space encountered before '-', to avoid
+ matching within descriptions for translated dd option syntax. */
+ char const *s = first_word;
+ size_t spaces = 0;
+ while (s < option_text && spaces < 2)
+ spaces += !!isspace (*s++);
+ if (spaces == 2)
+ {
+ /* Probably mismatched dd format. */
+ option_text = first_word;
+ double_space = false;
+ }
+ }
+
+ size_t anchor_len = strcspn (option_text, ",=[ \n");
+
+ /* Set highlighted text up to spacing after the full option text.
+ Any single space is included in highlighted text,
+ double space or TAB or newline terminates the option text. */
+ char const *desc_text = option_text + anchor_len;
+ while (*desc_text && *desc_text != '\n')
+ {
+ if (*desc_text == '-' && *(desc_text + 1) == '-')
+ double_space = false;
+ if (isspace (*desc_text))
+ {
+ if (*desc_text == '\t' || isspace (*(desc_text + 1)))
+ break;
+ /* With long options we restrict the match as some translations
+ delimit a long option and description with a single space. */
+ if (!double_space && *(desc_text + 1) != '-')
+ break;
+ }
+
+ desc_text++;
+ }
+
+ /* write spaces before option text. */
+ fwrite (option, 1, first_word - option, stdout);
+
+ /* write option text. */
+#ifdef MANUAL_URL
+ char const *url_program = streq (program, "[") ? "test"
+ : streq (program, "dir") ? "ls"
+ : streq (program, "vdir") ? "ls"
+ : streq (program, "b2sum") ? "cksum"
+ : streq (program, "md5sum") ? "cksum"
+ : streq (program, "sha1sum") ? "cksum"
+ : streq (program, "sha224sum") ? "cksum"
+ : streq (program, "sha256sum") ? "cksum"
+ : streq (program, "sha384sum") ? "cksum"
+ : streq (program, "sha512sum") ? "cksum"
+ : program;
+ /* Note we don't add the hostname to the links below, because
+ it's unused with http:// links and not usually useful with file:// links.
+ Also there is the possibility that local (hostname agnostic) links
+ would work if remotely accessing a similar system to the local one. */
+ if (STREQ_LEN (option_text, "--help", 6)
+ || STREQ_LEN (option_text, "--version", 9))
+ {
+ /* We don't have --help and --version links for each command,
+ and they wouldn't be useful to reference anyway.
+ Instead use these to reference the single node manual. */
+ printf ("\033]8;;%s%s#%s%.*s", PACKAGE_URL,
+ url_program, url_program, (int) anchor_len, option_text);
+ }
+ else
+ {
+ /* The single node manual doesn't work for ls, cksum, md5sum, sha*sum,
+ so we link to the full manual. */
+ printf ("\033]8;;%s#%s%.*s", MANUAL_URL, url_program,
+ (int) anchor_len, option_text);
+ }
+ fputs ("\033\\", stdout);
+#endif
+#ifdef BOLD_MAN_REFS
+ /* Note help2man strips this and will reinstate with --bold-refs. */
+ fputs ("\033[1m", stdout);
+#endif
+ /* first_word != option_text for test(1). */
+ fwrite (first_word, 1, desc_text - first_word, stdout);
+#ifdef BOLD_MAN_REFS
+ fputs ("\033[0m", stdout);
+#endif
+#ifdef MANUAL_URL
+ fputs ("\033]8;;\033\\", stdout);
+#endif
+
+ /* write description. */
+ fputs (desc_text, stdout);
+}
+
+/* If required and possible,
+ call oputs with printf formatted message. */
+
+#define oprintf(...) oprintf_ (PROGRAM_NAME, __VA_ARGS__)
+ATTRIBUTE_FORMAT ((printf, 2, 3))
+static inline void
+oprintf_ (char const *program, char const *message, ...)
+{
+ va_list args;
+ char *buf;
+ int buflen = -1;
+
+#if defined MANUAL_URL || defined BOLD_MAN_REFS
+ va_start (args, message);
+ buflen = vasprintf (&buf, message, args);
+ va_end (args);
+#endif
+
+ if (buflen < 0)
+ {
+ vprintf (message, args);
+ return;
+ }
+
+ oputs_ (program, buf);
+ free (buf);
+}
+
static inline void
emit_stdin_note (void)
{
@@ -606,8 +807,10 @@ the VERSION_CONTROL environment variable. Here are the values:\n\
"), stdout);
}
+#define emit_symlink_recurse_options(default_opt) \
+ emit_symlink_recurse_options_ (PROGRAM_NAME, default_opt)
static inline void
-emit_symlink_recurse_options (char const *default_opt)
+emit_symlink_recurse_options_ (char const *program, char const *default_opt)
{
printf (_("\
\n\
@@ -615,13 +818,20 @@ The following options modify how a hierarchy is traversed when the -R\n\
option is also specified. If more than one is specified, only the final\n\
one takes effect. %s is the default.\n\
\n\
- -H if a command line argument is a symbolic link\n\
- to a directory, traverse it\n\
- -L traverse every symbolic link to a directory\n\
- encountered\n\
- -P do not traverse any symbolic links\n\
-\n\
"), default_opt);
+ oputs_ (program, _("\
+ -H\n\
+ if a command line argument is a symlink to a directory, traverse it\n\
+"));
+ oputs_ (program, _("\
+ -L\n\
+ traverse every symbolic link to a directory encountered\n\
+"));
+ oputs_ (program, _("\
+ -P\n\
+ do not traverse any symbolic links\n\
+\n\
+"));
}
static inline void
@@ -646,7 +856,7 @@ emit_ancillary_info (char const *program)
{ "sha256sum", "sha2 utilities" },
{ "sha384sum", "sha2 utilities" },
{ "sha512sum", "sha2 utilities" },
- { nullptr, nullptr }
+ { NULL, NULL }
};
char const *node = program;
@@ -660,18 +870,6 @@ emit_ancillary_info (char const *program)
emit_bug_reporting_address ();
- /* Don't output this redundant message for English locales.
- Note we still output for 'C' so that it gets included in the man page. */
- char const *lc_messages = setlocale (LC_MESSAGES, nullptr);
- if (lc_messages && STRNCMP_LIT (lc_messages, "en_"))
- {
- /* TRANSLATORS: Replace LANG_CODE in this URL with your language code
- <https://translationproject.org/team/LANG_CODE.html> to form one of
- the URLs at https://translationproject.org/team/. Otherwise, replace
- the entire URL with your translation team's email address. */
- fputs (_("Report any translation bugs to "
- "<https://translationproject.org/team/>\n"), stdout);
- }
/* .htaccess on the coreutils web site maps programs to the appropriate page,
however we explicitly handle "[" -> "test" here as the "[" is not
recognized as part of a URL by default in terminals. */
@@ -764,8 +962,7 @@ write_error (void)
static inline char *
stzncpy (char *restrict dest, char const *restrict src, size_t len)
{
- size_t i;
- for (i = 0; i < len && *src; i++)
+ for (size_t i = 0; i < len && *src; i++)
*dest++ = *src++;
*dest = 0;
return dest;
@@ -810,7 +1007,7 @@ x_timestyle_match (char const * style, bool allow_posix,
int fail_status)
{
ptrdiff_t res = argmatch (style, timestyle_args,
- (char const *) timestyle_types,
+ timestyle_types,
timestyle_types_size);
if (res < 0)
{
diff --git a/src/tac-pipe.c b/src/tac-pipe.c
index 88b0dc58c..3a10e655d 100644
--- a/src/tac-pipe.c
+++ b/src/tac-pipe.c
@@ -1,6 +1,6 @@
/* tac from a pipe.
- Copyright (C) 1997-2025 Free Software Foundation, Inc.
+ Copyright (C) 1997-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -60,7 +60,7 @@ buf_init_from_stdin (Buf *x, char eol_byte)
{
char *buf = malloc (BUFFER_SIZE);
- if (buf == nullptr)
+ if (buf == NULL)
{
/* Fall back on the code that relies on a temporary file.
Write all buffers to that file and free them. */
@@ -93,7 +93,7 @@ buf_init_from_stdin (Buf *x, char eol_byte)
if (!last_byte_is_eol_byte)
{
char *buf = malloc (1);
- if (buf == nullptr)
+ if (buf == NULL)
{
/* FIXME: just like above */
ok = false;
@@ -126,7 +126,7 @@ buf_free (Buf *x)
{
for (size_t i = 0; i < x->n_bufs; i++)
free (x->p[i].start);
- obstack_free (OBS, nullptr);
+ obstack_free (OBS, NULL);
}
Line_ptr
diff --git a/src/tac.c b/src/tac.c
index e04933159..1845791ab 100644
--- a/src/tac.c
+++ b/src/tac.c
@@ -1,5 +1,5 @@
/* tac - concatenate and print files in reverse
- Copyright (C) 1988-2025 Free Software Foundation, Inc.
+ Copyright (C) 1988-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -45,6 +45,7 @@ tac -r -s '.\|
#include "filenamecat.h"
#include "full-read.h"
+#include "full-write.h"
#include "temp-stream.h"
#include "xbinary-io.h"
@@ -63,19 +64,19 @@ tac -r -s '.\|
#define WRITESIZE 8192
/* The string that separates the records of the file. */
-static char const *separator;
+static char const *separator = "\n";
/* True if we have ever read standard input. */
static bool have_read_stdin = false;
/* If true, print 'separator' along with the record preceding it
in the file; otherwise with the record following it. */
-static bool separator_ends_record;
+static bool separator_ends_record = true;
/* 0 if 'separator' is to be matched as a regular expression;
otherwise, the length of 'separator', used as a sentinel to
stop the search. */
-static size_t sentinel_length;
+static size_t sentinel_length = 1;
/* The length of a match with 'separator'. If 'sentinel_length' is 0,
'match_length' is computed every time a match succeeds;
@@ -100,12 +101,12 @@ static struct re_registers regs;
static struct option const longopts[] =
{
- {"before", no_argument, nullptr, 'b'},
- {"regex", no_argument, nullptr, 'r'},
- {"separator", required_argument, nullptr, 's'},
+ {"before", no_argument, NULL, 'b'},
+ {"regex", no_argument, NULL, 'r'},
+ {"separator", required_argument, NULL, 's'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -126,13 +127,20 @@ Write each FILE to standard output, last line first.\n\
emit_stdin_note ();
emit_mandatory_arg_note ();
- fputs (_("\
+ oputs (_("\
-b, --before attach the separator before instead of after\n\
+"));
+ oputs (_("\
-r, --regex interpret the separator as a regular expression\n\
+"));
+ oputs (_("\
-s, --separator=STRING use STRING as the separator instead of newline\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
+ fputs (_("\n\
+Non-seekable input is buffered to $TMPDIR, defaulting to /tmp.\n\
"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -149,9 +157,11 @@ output (char const *start, char const *past_end)
size_t bytes_to_add = past_end - start;
size_t bytes_available = WRITESIZE - bytes_in_buffer;
- if (start == 0)
+ if (!start)
{
- fwrite (buffer, 1, bytes_in_buffer, stdout);
+ if (full_write (STDOUT_FILENO, buffer, bytes_in_buffer)
+ != bytes_in_buffer)
+ write_error ();
bytes_in_buffer = 0;
return;
}
@@ -162,7 +172,8 @@ output (char const *start, char const *past_end)
memcpy (buffer + bytes_in_buffer, start, bytes_available);
bytes_to_add -= bytes_available;
start += bytes_available;
- fwrite (buffer, 1, WRITESIZE, stdout);
+ if (full_write (STDOUT_FILENO, buffer, WRITESIZE) != WRITESIZE)
+ write_error ();
bytes_in_buffer = 0;
bytes_available = WRITESIZE;
}
@@ -486,7 +497,7 @@ main (int argc, char **argv)
/* Initializer for file_list if no file-arguments
were specified on the command line. */
- static char const *const default_file_list[] = {"-", nullptr};
+ static char const *const default_file_list[] = {"-", NULL};
char const *const *file;
initialize_main (&argc, &argv);
@@ -497,11 +508,7 @@ main (int argc, char **argv)
atexit (close_stdout);
- separator = "\n";
- sentinel_length = 1;
- separator_ends_record = true;
-
- while ((optc = getopt_long (argc, argv, "brs:", longopts, nullptr)) != -1)
+ while ((optc = getopt_long (argc, argv, "brs:", longopts, NULL)) != -1)
{
switch (optc)
{
@@ -526,10 +533,10 @@ main (int argc, char **argv)
if (*separator == 0)
error (EXIT_FAILURE, 0, _("separator cannot be empty"));
- compiled_separator.buffer = nullptr;
+ compiled_separator.buffer = NULL;
compiled_separator.allocated = 0;
compiled_separator.fastmap = compiled_separator_fastmap;
- compiled_separator.translate = nullptr;
+ compiled_separator.translate = NULL;
error_message = re_compile_pattern (separator, strlen (separator),
&compiled_separator);
if (error_message)
@@ -573,7 +580,7 @@ main (int argc, char **argv)
}
/* Flush the output buffer. */
- output ((char *) nullptr, (char *) nullptr);
+ output ((char *) NULL, (char *) NULL);
if (have_read_stdin && close (STDIN_FILENO) < 0)
{
diff --git a/src/tail.c b/src/tail.c
index c7779c77d..2bc29cb5d 100644
--- a/src/tail.c
+++ b/src/tail.c
@@ -1,5 +1,5 @@
/* tail -- output the last part of file(s)
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -107,7 +107,7 @@ enum Follow_mode
static char const *const follow_mode_string[] =
{
- "descriptor", "name", nullptr
+ "descriptor", "name", NULL
};
static enum Follow_mode const follow_mode_map[] =
@@ -177,7 +177,7 @@ static bool reopen_inaccessible_files;
/* If true, interpret the numeric argument as the number of lines.
Otherwise, interpret it as the number of bytes. */
-static bool count_lines;
+static bool count_lines = true;
/* Whether we follow the name of each file or the file descriptor
that is initially associated with each name. */
@@ -196,7 +196,7 @@ static bool from_start;
static bool print_headers;
/* Character to split lines by. */
-static char line_end;
+static char line_end = '\n';
/* When to print the filename banners. */
enum header_mode
@@ -216,7 +216,7 @@ static count_t max_n_unchanged_stats_between_opens =
/* The process IDs of the processes to watch (those writing the followed
files, or perhaps other processes the user cares about). */
static int nbpids = 0;
-static pid_t * pids = nullptr;
+static pid_t * pids = NULL;
static idx_t pids_alloc;
/* Used to determine the buffer size when scanning backwards in a file. */
@@ -233,6 +233,9 @@ static bool presume_input_pipe;
/* If nonzero then don't use inotify even if available. */
static bool disable_inotify;
+/* Annotate the output with extra info to aid the user. */
+static bool debug;
+
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
@@ -242,30 +245,32 @@ enum
PID_OPTION,
PRESUME_INPUT_PIPE_OPTION,
LONG_FOLLOW_OPTION,
- DISABLE_INOTIFY_OPTION
+ DISABLE_INOTIFY_OPTION,
+ DEBUG_PROGRAM_OPTION,
};
static struct option const long_options[] =
{
- {"bytes", required_argument, nullptr, 'c'},
- {"follow", optional_argument, nullptr, LONG_FOLLOW_OPTION},
- {"lines", required_argument, nullptr, 'n'},
- {"max-unchanged-stats", required_argument, nullptr,
+ {"bytes", required_argument, NULL, 'c'},
+ {"debug", no_argument, NULL, DEBUG_PROGRAM_OPTION},
+ {"follow", optional_argument, NULL, LONG_FOLLOW_OPTION},
+ {"lines", required_argument, NULL, 'n'},
+ {"max-unchanged-stats", required_argument, NULL,
MAX_UNCHANGED_STATS_OPTION},
- {"-disable-inotify", no_argument, nullptr,
+ {"-disable-inotify", no_argument, NULL,
DISABLE_INOTIFY_OPTION}, /* do not document */
- {"pid", required_argument, nullptr, PID_OPTION},
- {"-presume-input-pipe", no_argument, nullptr,
+ {"pid", required_argument, NULL, PID_OPTION},
+ {"-presume-input-pipe", no_argument, NULL,
PRESUME_INPUT_PIPE_OPTION}, /* do not document */
- {"quiet", no_argument, nullptr, 'q'},
- {"retry", no_argument, nullptr, RETRY_OPTION},
- {"silent", no_argument, nullptr, 'q'},
- {"sleep-interval", required_argument, nullptr, 's'},
- {"verbose", no_argument, nullptr, 'v'},
- {"zero-terminated", no_argument, nullptr, 'z'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"retry", no_argument, NULL, RETRY_OPTION},
+ {"silent", no_argument, NULL, 'q'},
+ {"sleep-interval", required_argument, NULL, 's'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"zero-terminated", no_argument, NULL, 'z'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -287,50 +292,67 @@ With more than one FILE, precede each with a header giving the file name.\n\
emit_stdin_note ();
emit_mandatory_arg_note ();
- fputs (_("\
- -c, --bytes=[+]NUM output the last NUM bytes; or use -c +NUM to\n\
- output starting with byte NUM of each file\n\
-"), stdout);
- fputs (_("\
+ oputs (_("\
+ -c, --bytes=[+]NUM\n\
+ output the last NUM bytes;\n\
+ or use -c +NUM to output starting with byte NUM of each file\n\
+"));
+ oputs (_("\
+ --debug\n\
+ indicate which --follow implementation is used\n\
+"));
+ oputs (_("\
-f, --follow[={name|descriptor}]\n\
- output appended data as the file grows;\n\
- an absent option argument means 'descriptor'\n\
- -F same as --follow=name --retry\n\
-"), stdout);
- printf (_("\
- -n, --lines=[+]NUM output the last NUM lines, instead of the last %d;\n\
- or use -n +NUM to skip NUM-1 lines at the start\n\
-"),
- DEFAULT_N_LINES
- );
- printf (_("\
+ output appended data as the file grows;\n\
+ an absent option argument means 'descriptor'\n\
+"));
+ oputs (_("\
+ -F\n\
+ same as --follow=name --retry\n\
+"));
+ oprintf (_("\
+ -n, --lines=[+]NUM\n\
+ output the last NUM lines, instead of the last %d;\n\
+ or use -n +NUM to skip NUM-1 lines at the start\n\
+"), DEFAULT_N_LINES);
+ oprintf (_("\
--max-unchanged-stats=N\n\
- with --follow=name, reopen a FILE which has not\n\
- changed size after N (default %d) iterations\n\
- to see if it has been unlinked or renamed\n\
- (this is the usual case of rotated log files);\n\
- with inotify, this option is rarely useful\n\
-"),
- DEFAULT_MAX_N_UNCHANGED_STATS_BETWEEN_OPENS
- );
- fputs (_("\
- --pid=PID with -f, exit after PID no longer exists;\n\
- can be repeated to watch multiple processes\n\
- -q, --quiet, --silent never output headers giving file names\n\
- --retry keep trying to open a file if it is inaccessible\n\
-"), stdout);
- fputs (_("\
- -s, --sleep-interval=N with -f, sleep for approximately N seconds\n\
- (default 1.0) between iterations;\n\
- with inotify and --pid=P, check process P at\n\
- least once every N seconds\n\
- -v, --verbose always output headers giving file names\n\
-"), stdout);
- fputs (_("\
- -z, --zero-terminated line delimiter is NUL, not newline\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ with --follow=name, reopen a FILE which has not\n\
+ changed size after N (default %d) iterations\n\
+ to see if it has been unlinked or renamed\n\
+ (this is the usual case of rotated log files);\n\
+ with inotify, this option is rarely useful\n\
+"), DEFAULT_MAX_N_UNCHANGED_STATS_BETWEEN_OPENS);
+ oputs (_("\
+ --pid=PID\n\
+ with -f, exit after PID no longer exists;\n\
+ can be repeated to watch multiple processes\n\
+"));
+ oputs (_("\
+ -q, --quiet, --silent\n\
+ never output headers giving file names\n\
+"));
+ oputs (_("\
+ --retry\n\
+ keep trying to open a file if it is inaccessible\n\
+"));
+ oputs (_("\
+ -s, --sleep-interval=N\n\
+ with -f, sleep for approximately N seconds\n\
+ (default 1.0) between iterations;\n\
+ with inotify and --pid=P,\n\
+ check process P at least once every N seconds\n\
+"));
+ oputs (_("\
+ -v, --verbose\n\
+ always output headers giving file names\n\
+"));
+ oputs (_("\
+ -z, --zero-terminated\n\
+ line delimiter is NUL, not newline\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
NUM may have a multiplier suffix:\n\
@@ -573,7 +595,7 @@ file_lines (char const *prettyname, int fd, struct stat const *sb,
{
char const *nl;
nl = memrchr (buffer, line_end, n);
- if (nl == nullptr)
+ if (nl == NULL)
break;
n = nl - buffer;
if (n_lines-- == 0)
@@ -635,7 +657,7 @@ pipe_lines (char const *prettyname, int fd, count_t n_lines)
first = last = xmalloc (sizeof (LBUFFER));
first->nbytes = first->nlines = 0;
- first->next = nullptr;
+ first->next = NULL;
tmp = xmalloc (sizeof (LBUFFER));
/* Input is always read into a fresh buffer. */
@@ -646,7 +668,7 @@ pipe_lines (char const *prettyname, int fd, count_t n_lines)
break;
tmp->nbytes = n_read;
tmp->nlines = 0;
- tmp->next = nullptr;
+ tmp->next = NULL;
/* Count the number of newlines just read. */
{
@@ -725,8 +747,7 @@ pipe_lines (char const *prettyname, int fd, count_t n_lines)
{
/* Skip 'total_lines' - 'n_lines' newlines. We made sure that
'total_lines' - 'n_lines' <= 'tmp->nlines'. */
- idx_t j;
- for (j = total_lines - n_lines; j; --j)
+ for (idx_t j = total_lines - n_lines; j; --j)
{
beg = rawmemchr (beg, line_end);
++beg;
@@ -777,7 +798,7 @@ pipe_bytes (char const *prettyname, int fd, count_t n_bytes,
first = last = xmalloc (sizeof (CBUFFER));
first->nbytes = 0;
- first->next = nullptr;
+ first->next = NULL;
tmp = xmalloc (sizeof (CBUFFER));
/* Input is always read into a fresh buffer. */
@@ -788,7 +809,7 @@ pipe_bytes (char const *prettyname, int fd, count_t n_bytes,
break;
read_pos += n_read;
tmp->nbytes = n_read;
- tmp->next = nullptr;
+ tmp->next = NULL;
total_bytes += tmp->nbytes;
/* If there is enough room in the last buffer read, just append the new
@@ -1154,12 +1175,22 @@ tail_forever (struct File_spec *f, int n_files, double sleep_interval)
{
int last = n_files - 1;
+ static bool debugged;
+
while (true)
{
/* Use blocking I/O as an optimization, when it's easy. */
bool blocking = (!nbpids && follow_mode == Follow_descriptor
&& n_files == 1 && 0 <= f[0].fd && !S_ISREG (f[0].mode));
+ if (debug && !debugged)
+ {
+ debugged = true;
+ error (0, 0, "%s", blocking
+ ? _("using blocking mode")
+ : _("using polling mode"));
+ }
+
bool any_input = false;
for (int i = 0; i < n_files; i++)
@@ -1469,8 +1500,8 @@ tail_forever_inotify (int wd, struct File_spec *f, int n_files,
char *evbuf;
idx_t evbuf_off = 0;
- wd_to_name = hash_initialize (n_files, nullptr, wd_hasher, wd_comparator,
- nullptr);
+ wd_to_name = hash_initialize (n_files, NULL, wd_hasher, wd_comparator,
+ NULL);
if (! wd_to_name)
xalloc_die ();
*wd_to_namep = wd_to_name;
@@ -1543,7 +1574,7 @@ tail_forever_inotify (int wd, struct File_spec *f, int n_files,
continue;
}
- if (hash_insert (wd_to_name, &(f[i])) == nullptr)
+ if (hash_insert (wd_to_name, &(f[i])) == NULL)
xalloc_die ();
found_watchable_file = true;
@@ -1594,6 +1625,9 @@ tail_forever_inotify (int wd, struct File_spec *f, int n_files,
}
}
+ if (debug)
+ error (0, 0, "%s", _("using notification mode"));
+
evlen += sizeof (struct inotify_event) + 1;
evbuf = ximalloc (evlen);
@@ -1650,7 +1684,7 @@ tail_forever_inotify (int wd, struct File_spec *f, int n_files,
pfd[1].events = pfd[1].revents = 0;
file_change = poll (pfd, monitor_output + 1, delay);
}
- while (file_change == 0);
+ while (file_change == 0 || (file_change < 0 && errno == EINTR));
if (file_change < 0)
error (EXIT_FAILURE, errno,
@@ -1768,7 +1802,7 @@ tail_forever_inotify (int wd, struct File_spec *f, int n_files,
close_fd (prev->fd, prev);
}
- if (hash_insert (wd_to_name, fspec) == nullptr)
+ if (hash_insert (wd_to_name, fspec) == NULL)
xalloc_die ();
}
@@ -2147,7 +2181,7 @@ parse_options (int argc, char **argv,
int c;
while ((c = getopt_long (argc, argv, "c:n:fFqs:vz0123456789",
- long_options, nullptr))
+ long_options, NULL))
!= -1)
{
switch (c)
@@ -2176,7 +2210,7 @@ parse_options (int argc, char **argv,
case 'f':
case LONG_FOLLOW_OPTION:
forever = true;
- if (optarg == nullptr)
+ if (optarg == NULL)
follow_mode = DEFAULT_FOLLOW_MODE;
else
follow_mode = XARGMATCH ("--follow", optarg,
@@ -2196,6 +2230,10 @@ parse_options (int argc, char **argv,
0, XTOINT_MAX_QUIET);
break;
+ case DEBUG_PROGRAM_OPTION:
+ debug = true;
+ break;
+
case DISABLE_INOTIFY_OPTION:
disable_inotify = true;
break;
@@ -2269,7 +2307,7 @@ parse_options (int argc, char **argv,
: ("warning: PID ignored;"
" --pid=PID is useful only when following")));
free (pids);
- pids = nullptr;
+ pids = NULL;
}
}
@@ -2338,11 +2376,6 @@ main (int argc, char **argv)
page_size = p;
}
- have_read_stdin = false;
-
- count_lines = true;
- forever = from_start = print_headers = false;
- line_end = '\n';
obsolete_option = parse_obsolete_option (argc, argv, &n_units);
argc -= obsolete_option;
argv += obsolete_option;
diff --git a/src/tee.c b/src/tee.c
index dc55b2cc2..32a18e340 100644
--- a/src/tee.c
+++ b/src/tee.c
@@ -1,5 +1,5 @@
/* tee - read from standard input and write to standard output and files.
- Copyright (C) 1985-2025 Free Software Foundation, Inc.
+ Copyright (C) 1985-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,8 +24,8 @@
#include "system.h"
#include "argmatch.h"
#include "fadvise.h"
+#include "fcntl--.h"
#include "iopoll.h"
-#include "stdio--.h"
#include "xbinary-io.h"
#include "iopoll.h"
@@ -58,17 +58,17 @@ static enum output_error output_error;
static struct option const long_options[] =
{
- {"append", no_argument, nullptr, 'a'},
- {"ignore-interrupts", no_argument, nullptr, 'i'},
- {"output-error", optional_argument, nullptr, 'p'},
+ {"append", no_argument, NULL, 'a'},
+ {"ignore-interrupts", no_argument, NULL, 'i'},
+ {"output-error", optional_argument, NULL, 'p'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
static char const *const output_error_args[] =
{
- "warn", "warn-nopipe", "exit", "exit-nopipe", nullptr
+ "warn", "warn-nopipe", "exit", "exit-nopipe", NULL
};
static enum output_error const output_error_types[] =
{
@@ -88,15 +88,25 @@ usage (int status)
fputs (_("\
Copy standard input to each FILE, and also to standard output.\n\
\n\
- -a, --append append to the given FILEs, do not overwrite\n\
- -i, --ignore-interrupts ignore interrupt signals\n\
"), stdout);
- fputs (_("\
- -p operate in a more appropriate MODE with pipes\n\
- --output-error[=MODE] set behavior on write error. See MODE below\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -a, --append\n\
+ append to the given FILEs, do not overwrite\n\
+"));
+ oputs (_("\
+ -i, --ignore-interrupts\n\
+ ignore interrupt signals\n\
+"));
+ oputs (_("\
+ -p\n\
+ operate in a more appropriate MODE with pipes\n\
+"));
+ oputs (_("\
+ --output-error[=MODE]\n\
+ set behavior on write error. See MODE below\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
MODE determines behavior with write errors on the outputs:\n\
@@ -126,11 +136,8 @@ main (int argc, char **argv)
atexit (close_stdout);
- append = false;
- ignore_interrupts = false;
-
int optc;
- while ((optc = getopt_long (argc, argv, "aip", long_options, nullptr)) != -1)
+ while ((optc = getopt_long (argc, argv, "aip", long_options, NULL)) != -1)
{
switch (optc)
{
@@ -187,10 +194,10 @@ main (int argc, char **argv)
ATTRIBUTE_PURE
static int
-get_next_out (FILE **descriptors, int nfiles, int idx)
+get_next_out (int *descriptors, int nfiles, int idx)
{
for (idx++; idx <= nfiles; idx++)
- if (descriptors[idx])
+ if (0 <= descriptors[idx])
return idx;
return -1; /* no outputs remaining */
}
@@ -199,21 +206,19 @@ get_next_out (FILE **descriptors, int nfiles, int idx)
Return true if this indicates a reportable error. */
static bool
-fail_output (FILE **descriptors, char **files, int i)
+fail_output (int *descriptors, char **files, int i)
{
int w_errno = errno;
bool fail = errno != EPIPE
|| output_error == output_error_exit
|| output_error == output_error_warn;
- if (descriptors[i] == stdout)
- clearerr (stdout); /* Avoid redundant close_stdout diagnostic. */
if (fail)
{
error (output_error == output_error_exit
|| output_error == output_error_exit_nopipe,
w_errno, "%s", quotef (files[i]));
}
- descriptors[i] = nullptr;
+ descriptors[i] = -1;
return fail;
}
@@ -226,17 +231,13 @@ static bool
tee_files (int nfiles, char **files, bool pipe_check)
{
size_t n_outputs = 0;
- FILE **descriptors;
- bool *out_pollable IF_LINT ( = nullptr);
+ int *descriptors;
+ bool *out_pollable IF_LINT ( = NULL);
char buffer[BUFSIZ];
ssize_t bytes_read = 0;
- int i;
int first_out = 0; /* idx of first non-null output in descriptors */
bool ok = true;
- char const *mode_string =
- (O_BINARY
- ? (append ? "ab" : "wb")
- : (append ? "a" : "w"));
+ int flags = O_WRONLY | O_CREAT | O_BINARY | (append ? O_APPEND : O_TRUNC);
xset_binary_mode (STDIN_FILENO, O_BINARY);
xset_binary_mode (STDOUT_FILENO, O_BINARY);
@@ -249,18 +250,17 @@ tee_files (int nfiles, char **files, bool pipe_check)
if (pipe_check)
out_pollable = xnmalloc (nfiles + 1, sizeof *out_pollable);
files--;
- descriptors[0] = stdout;
+ descriptors[0] = STDOUT_FILENO;
if (pipe_check)
- out_pollable[0] = iopoll_output_ok (fileno (descriptors[0]));
+ out_pollable[0] = iopoll_output_ok (descriptors[0]);
files[0] = bad_cast (_("standard output"));
- setvbuf (stdout, nullptr, _IONBF, 0);
n_outputs++;
- for (i = 1; i <= nfiles; i++)
+ for (int i = 1; i <= nfiles; i++)
{
/* Do not treat "-" specially - as mandated by POSIX. */
- descriptors[i] = fopen (files[i], mode_string);
- if (descriptors[i] == nullptr)
+ descriptors[i] = open (files[i], flags, MODE_RW_UGO);
+ if (descriptors[i] < 0)
{
if (pipe_check)
out_pollable[i] = false;
@@ -272,8 +272,7 @@ tee_files (int nfiles, char **files, bool pipe_check)
else
{
if (pipe_check)
- out_pollable[i] = iopoll_output_ok (fileno (descriptors[i]));
- setvbuf (descriptors[i], nullptr, _IONBF, 0);
+ out_pollable[i] = iopoll_output_ok (descriptors[i]);
n_outputs++;
}
}
@@ -283,8 +282,7 @@ tee_files (int nfiles, char **files, bool pipe_check)
if (pipe_check && out_pollable[first_out])
{
/* Monitor for input, or errors on first valid output. */
- int err = iopoll (STDIN_FILENO, fileno (descriptors[first_out]),
- true);
+ int err = iopoll (STDIN_FILENO, descriptors[first_out], true);
/* Close the output if it became a broken pipe. */
if (err == IOPOLL_BROKEN_OUTPUT)
@@ -311,9 +309,9 @@ tee_files (int nfiles, char **files, bool pipe_check)
/* Write to all NFILES + 1 descriptors.
Standard output is the first one. */
- for (i = 0; i <= nfiles; i++)
- if (descriptors[i]
- && ! fwrite_wait (buffer, bytes_read, descriptors[i]))
+ for (int i = 0; i <= nfiles; i++)
+ if (0 <= descriptors[i]
+ && ! write_wait (descriptors[i], buffer, bytes_read))
{
if (fail_output (descriptors, files, i))
ok = false;
@@ -330,8 +328,8 @@ tee_files (int nfiles, char **files, bool pipe_check)
}
/* Close the files, but not standard output. */
- for (i = 1; i <= nfiles; i++)
- if (descriptors[i] && ! fclose_wait (descriptors[i]))
+ for (int i = 1; i <= nfiles; i++)
+ if (0 <= descriptors[i] && ! close_wait (descriptors[i]))
{
error (0, errno, "%s", quotef (files[i]));
ok = false;
diff --git a/src/temp-stream.c b/src/temp-stream.c
index 4db254c0a..6641ed9e8 100644
--- a/src/temp-stream.c
+++ b/src/temp-stream.c
@@ -1,6 +1,6 @@
/* temp-stream.c -- provide a stream to a per process temp file
- Copyright (C) 2023-2025 Free Software Foundation, Inc.
+ Copyright (C) 2023-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -83,11 +83,11 @@ record_or_unlink_tempfile (char const *fn, MAYBE_UNUSED FILE *fp)
bool
temp_stream (FILE **fp, char **file_name)
{
- static char *tempfile = nullptr;
+ static char *tempfile = NULL;
static FILE *tmp_fp;
- if (tempfile == nullptr)
+ if (tempfile == NULL)
{
- char *tempbuf = nullptr;
+ char *tempbuf = NULL;
size_t tempbuf_len = 128;
while (true)
@@ -98,7 +98,7 @@ temp_stream (FILE **fp, char **file_name)
return false;
}
- if (path_search (tempbuf, tempbuf_len, nullptr, "cutmp", true) == 0)
+ if (path_search (tempbuf, tempbuf_len, NULL, "cutmp", true) == 0)
break;
if (errno != EINVAL || PATH_MAX / 2 < tempbuf_len)
@@ -139,7 +139,7 @@ temp_stream (FILE **fp, char **file_name)
unlink (tempfile);
Reset:
free (tempfile);
- tempfile = nullptr;
+ tempfile = NULL;
return false;
}
diff --git a/src/term-sig.h b/src/term-sig.h
new file mode 100644
index 000000000..06ed0a7fd
--- /dev/null
+++ b/src/term-sig.h
@@ -0,0 +1,50 @@
+#ifndef TERM_SIG_H
+# define TERM_SIG_H
+
+# include <signal.h>
+
+static int const term_sig[] =
+ {
+ SIGALRM, /* our timeout. */
+ SIGINT, /* Ctrl-C at terminal for example. */
+ SIGQUIT, /* Ctrl-\ at terminal for example. */
+ SIGHUP, /* terminal closed for example. */
+ SIGTERM, /* if terminated, stop monitored proc. */
+
+ SIGPIPE, SIGUSR1, SIGUSR2,
+
+ SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGSEGV,
+
+# ifdef SIGXCPU
+ SIGXCPU,
+# endif
+# ifdef SIGXFSZ
+ SIGXFSZ,
+# endif
+# ifdef SIGSYS
+ SIGSYS,
+# endif
+# ifdef SIGVTALRM
+ SIGVTALRM,
+# endif
+# ifdef SIGPROF
+ SIGPROF,
+# endif
+# ifdef SIGPOLL
+ SIGPOLL,
+# endif
+# ifdef SIGPWR
+ SIGPWR,
+# endif
+# ifdef SIGSTKFLT
+ SIGSTKFLT,
+# endif
+# ifdef SIGEMT
+ SIGEMT,
+# endif
+# ifdef SIGBREAK
+ SIGBREAK,
+# endif
+ };
+
+#endif
diff --git a/src/termios.c b/src/termios.c
index f17e12ebd..312e50006 100644
--- a/src/termios.c
+++ b/src/termios.c
@@ -1,6 +1,6 @@
/* termios.c -- coax out Bxxx macros from termios.h
- Copyright (C) 2025 Free Software Foundation, Inc.
+ Copyright (C) 2025-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/test.c b/src/test.c
index c11542922..ab7f24992 100644
--- a/src/test.c
+++ b/src/test.c
@@ -2,7 +2,7 @@
/* Modified to run with the GNU shell by bfox. */
-/* Copyright (C) 1987-2025 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,7 +21,6 @@
the shell builtin version. */
#include <config.h>
-#include <ctype.h>
#include <stdio.h>
#include <sys/types.h>
@@ -255,7 +254,7 @@ term (void)
}
value = posixtest (nargs);
- if (argv[pos] == 0)
+ if (!argv[pos])
test_syntax_error (_("%s expected"), quote (")"));
else
if (argv[pos][0] != ')' || argv[pos][1])
@@ -492,7 +491,7 @@ unary_operator (void)
unary_advance ();
arg = find_int (argv[pos - 1]);
errno = 0;
- fd = strtol (arg, nullptr, 10);
+ fd = strtol (arg, NULL, 10);
return (errno != ERANGE && 0 <= fd && fd <= INT_MAX && isatty (fd));
}
@@ -676,8 +675,8 @@ Usage: test EXPRESSION\n\
Exit with the status determined by EXPRESSION.\n\
\n\
"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
An omitted EXPRESSION defaults to false. Otherwise,\n\
@@ -692,9 +691,17 @@ EXPRESSION is true or false and sets exit status. It is one of:\n\
"), stdout);
fputs (_("\
\n\
+"), stdout);
+ oputs (_("\
-n STRING the length of STRING is nonzero\n\
+"));
+ fputs (_("\
STRING equivalent to -n STRING\n\
+"), stdout);
+ oputs (_("\
-z STRING the length of STRING is zero\n\
+"));
+ fputs (_("\
STRING1 = STRING2 the strings are equal\n\
STRING1 != STRING2 the strings are not equal\n\
STRING1 > STRING2 STRING1 is greater than STRING2 in the current locale\n\
@@ -702,48 +709,100 @@ EXPRESSION is true or false and sets exit status. It is one of:\n\
"), stdout);
fputs (_("\
\n\
+"), stdout);
+ oputs (_("\
INTEGER1 -eq INTEGER2 INTEGER1 is equal to INTEGER2\n\
+"));
+ oputs (_("\
INTEGER1 -ge INTEGER2 INTEGER1 is greater than or equal to INTEGER2\n\
+"));
+ oputs (_("\
INTEGER1 -gt INTEGER2 INTEGER1 is greater than INTEGER2\n\
+"));
+ oputs (_("\
INTEGER1 -le INTEGER2 INTEGER1 is less than or equal to INTEGER2\n\
+"));
+ oputs (_("\
INTEGER1 -lt INTEGER2 INTEGER1 is less than INTEGER2\n\
+"));
+ oputs (_("\
INTEGER1 -ne INTEGER2 INTEGER1 is not equal to INTEGER2\n\
-"), stdout);
+"));
fputs (_("\
\n\
+"), stdout);
+ oputs (_("\
FILE1 -ef FILE2 FILE1 and FILE2 have the same device and inode numbers\n\
+"));
+ oputs (_("\
FILE1 -nt FILE2 FILE1 is newer (modification date) than FILE2\n\
+"));
+ oputs (_("\
FILE1 -ot FILE2 FILE1 is older than FILE2\n\
-"), stdout);
+"));
fputs (_("\
\n\
+"), stdout);
+ oputs (_("\
-b FILE FILE exists and is block special\n\
+"));
+ oputs (_("\
-c FILE FILE exists and is character special\n\
+"));
+ oputs (_("\
-d FILE FILE exists and is a directory\n\
+"));
+ oputs (_("\
-e FILE FILE exists\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-f FILE FILE exists and is a regular file\n\
- -g FILE FILE exists and is set-group-ID\n\
+"));
+ oputs (_("\
+ -g FILE FILE exists and its set-group-ID bit is set\n\
+"));
+ oputs (_("\
-G FILE FILE exists and is owned by the effective group ID\n\
+"));
+ oputs (_("\
-h FILE FILE exists and is a symbolic link (same as -L)\n\
+"));
+ oputs (_("\
-k FILE FILE exists and has its sticky bit set\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-L FILE FILE exists and is a symbolic link (same as -h)\n\
+"));
+ oputs (_("\
-N FILE FILE exists and has been modified since it was last read\n\
+"));
+ oputs (_("\
-O FILE FILE exists and is owned by the effective user ID\n\
+"));
+ oputs (_("\
-p FILE FILE exists and is a named pipe\n\
+"));
+ oputs (_("\
-r FILE FILE exists and the user has read access\n\
+"));
+ oputs (_("\
-s FILE FILE exists and has a size greater than zero\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-S FILE FILE exists and is a socket\n\
+"));
+ oputs (_("\
-t FD file descriptor FD is opened on a terminal\n\
+"));
+ oputs (_("\
-u FILE FILE exists and its set-user-ID bit is set\n\
+"));
+ oputs (_("\
-w FILE FILE exists and the user has write access\n\
+"));
+ oputs (_("\
-x FILE FILE exists and the user has execute (or search) access\n\
-"), stdout);
+"));
fputs (_("\
\n\
Except for -h and -L, all FILE-related tests dereference symbolic links.\n\
@@ -821,7 +880,7 @@ main (int margc, char **margv)
if (streq (margv[1], "--version"))
{
version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version, AUTHORS,
- (char *) nullptr);
+ (char *) NULL);
test_main_return (EXIT_SUCCESS);
}
}
diff --git a/src/timeout.c b/src/timeout.c
index 3443cab4b..94d2c41cb 100644
--- a/src/timeout.c
+++ b/src/timeout.c
@@ -1,5 +1,5 @@
/* timeout -- run a command with bounded time
- Copyright (C) 2008-2025 Free Software Foundation, Inc.
+ Copyright (C) 2008-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -59,6 +59,7 @@
#include "dtimespec-bound.h"
#include "sig2str.h"
#include "operand2sig.h"
+#include "term-sig.h"
#include "quote.h"
#if HAVE_SETRLIMIT
@@ -72,6 +73,14 @@
# define SA_RESTART 0
#endif
+#ifndef SIGRTMIN
+# define SIGRTMIN 0
+# undef SIGRTMAX
+#endif
+#ifndef SIGRTMAX
+# define SIGRTMAX (SIGRTMIN - 1)
+#endif
+
#define PROGRAM_NAME "timeout"
#define AUTHORS proper_name_lite ("Padraig Brady", "P\303\241draig Brady")
@@ -87,14 +96,14 @@ static char const *command;
static struct option const long_options[] =
{
- {"foreground", no_argument, nullptr, 'f'},
- {"kill-after", required_argument, nullptr, 'k'},
- {"preserve-status", no_argument, nullptr, 'p'},
- {"signal", required_argument, nullptr, 's'},
- {"verbose", no_argument, nullptr, 'v'},
+ {"foreground", no_argument, NULL, 'f'},
+ {"kill-after", required_argument, NULL, 'k'},
+ {"preserve-status", no_argument, NULL, 'p'},
+ {"signal", required_argument, NULL, 's'},
+ {"verbose", no_argument, NULL, 'v'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* Start the timeout after which we'll receive a SIGALRM.
@@ -112,9 +121,9 @@ settimeout (double duration, bool warn)
struct timespec ts = dtotimespec (duration);
struct itimerspec its = {.it_interval = {0}, .it_value = ts};
timer_t timerid;
- if (timer_create (CLOCK_REALTIME, nullptr, &timerid) == 0)
+ if (timer_create (CLOCK_REALTIME, NULL, &timerid) == 0)
{
- if (timer_settime (timerid, 0, &its, nullptr) == 0)
+ if (timer_settime (timerid, 0, &its, NULL) == 0)
return;
else
{
@@ -145,7 +154,7 @@ settimeout (double duration, bool warn)
tv.tv_usec--;
}
struct itimerval it = {.it_interval = {0}, .it_value = tv };
- if (setitimer (ITIMER_REAL, &it, nullptr) == 0)
+ if (setitimer (ITIMER_REAL, &it, NULL) == 0)
return;
else
{
@@ -191,13 +200,18 @@ chld (MAYBE_UNUSED int sig)
{
}
-
static void
cleanup (int sig)
{
if (sig == SIGALRM)
{
timed_out = 1;
+ /* In case there is an issue with close_stdout,
+ update to a more accurate default exit status.
+ For example we might get failed writes with -v with:
+ timeout -v 1 sleep 10 2>&1 | :
+ */
+ initialize_exit_failure (EXIT_TIMEDOUT);
sig = term_signal;
}
if (0 < monitored_pid)
@@ -218,7 +232,7 @@ cleanup (int sig)
if (verbose)
{
char signame[MAX (SIG2STR_MAX, INT_BUFSIZE_BOUND (int))];
- if (sig2str (sig, signame) != 0)
+ if (sig == 0 || sig2str (sig, signame) != 0)
snprintf (signame, sizeof signame, "%d", sig);
error (0, 0, _("sending signal %s to command %s"),
signame, quote (command));
@@ -262,34 +276,35 @@ Start COMMAND, and kill it if still running after DURATION.\n\
emit_mandatory_arg_note ();
- fputs (_("\
+ oputs (_("\
-f, --foreground\n\
- when not running timeout directly from a shell prompt,\n\
- allow COMMAND to read from the TTY and get TTY signals;\n\
- in this mode, children of COMMAND will not be timed out\n\
-"), stdout);
- fputs (_("\
+ when not running timeout directly from a shell prompt,\n\
+ allow COMMAND to read from the TTY and get TTY signals;\n\
+ in this mode, children of COMMAND will not be timed out\n\
+"));
+ oputs (_("\
-k, --kill-after=DURATION\n\
- also send a KILL signal if COMMAND is still running\n\
- this long after the initial signal was sent\n\
-"), stdout);
- fputs (_("\
+ also send a KILL signal if COMMAND is still running\n\
+ this long after the initial signal was sent\n\
+"));
+ oputs (_("\
-p, --preserve-status\n\
- exit with the same status as COMMAND,\n\
- even when the command times out\n\
-"), stdout);
- fputs (_("\
+ exit with the same status as COMMAND,\n\
+ even when the command times out\n\
+"));
+ oputs (_("\
-s, --signal=SIGNAL\n\
- specify the signal to be sent on timeout;\n\
- SIGNAL may be a name like 'HUP' or a number;\n\
- see 'kill -l' for a list of signals\n\
-"), stdout);
- fputs (_("\
- -v, --verbose diagnose to standard error any signal sent upon timeout\n\
-"), stdout);
-
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ specify the signal to be sent on timeout;\n\
+ SIGNAL may be a name like 'HUP' or a number;\n\
+ see 'kill -l' for a list of signals\n\
+"));
+ oputs (_("\
+ -v, --verbose\n\
+ diagnose to standard error any signal sent upon timeout\n\
+"));
+
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\n\
DURATION is a floating point number with an optional suffix:\n\
@@ -381,7 +396,7 @@ unblock_signal (int sig)
sigset_t unblock_set;
sigemptyset (&unblock_set);
sigaddset (&unblock_set, sig);
- if (sigprocmask (SIG_UNBLOCK, &unblock_set, nullptr) != 0)
+ if (sigprocmask (SIG_UNBLOCK, &unblock_set, NULL) != 0)
error (0, errno, _("warning: sigprocmask"));
}
@@ -394,13 +409,30 @@ install_sigchld (void)
sa.sa_flags = SA_RESTART; /* Restart syscalls if possible, as that's
more likely to work cleanly. */
- sigaction (SIGCHLD, &sa, nullptr);
+ sigaction (SIGCHLD, &sa, NULL);
/* We inherit the signal mask from our parent process,
so ensure SIGCHLD is not blocked. */
unblock_signal (SIGCHLD);
}
+/* Filter out signals that were ignored. */
+
+static bool
+sig_needs_handling (int sig, int sigterm)
+{
+ if (sig == SIGALRM || sig == sigterm)
+ return true; /* We can't ignore these. */
+
+ /* Note background jobs in shells have SIGINT and SIGQUIT
+ set to SIG_IGN by default. I.e., those signals will
+ not be propagated through background timeout jobs. */
+ struct sigaction old_sa;
+ sigaction (sig, NULL, &old_sa);
+ bool ret = old_sa.sa_handler != SIG_IGN;
+ return ret;
+}
+
static void
install_cleanup (int sigterm)
{
@@ -410,12 +442,16 @@ install_cleanup (int sigterm)
sa.sa_flags = SA_RESTART; /* Restart syscalls if possible, as that's
more likely to work cleanly. */
- sigaction (SIGALRM, &sa, nullptr); /* our timeout. */
- sigaction (SIGINT, &sa, nullptr); /* Ctrl-C at terminal for example. */
- sigaction (SIGQUIT, &sa, nullptr); /* Ctrl-\ at terminal for example. */
- sigaction (SIGHUP, &sa, nullptr); /* terminal closed for example. */
- sigaction (SIGTERM, &sa, nullptr); /* if killed, stop monitored proc. */
- sigaction (sigterm, &sa, nullptr); /* user specified termination signal. */
+ for (int i = 0; i < countof (term_sig); i++)
+ if (sig_needs_handling (term_sig[i], sigterm))
+ sigaction (term_sig[i], &sa, NULL);
+
+ /* Real Time signals also terminate by default. */
+ for (int s = SIGRTMIN; s <= SIGRTMAX; s++)
+ if (sig_needs_handling (s, sigterm))
+ sigaction (s, &sa, NULL);
+
+ sigaction (sigterm, &sa, NULL); /* user specified termination signal. */
}
/* Block all signals which were registered with cleanup() as the signal
@@ -429,11 +465,14 @@ block_cleanup_and_chld (int sigterm, sigset_t *old_set)
sigset_t block_set;
sigemptyset (&block_set);
- sigaddset (&block_set, SIGALRM);
- sigaddset (&block_set, SIGINT);
- sigaddset (&block_set, SIGQUIT);
- sigaddset (&block_set, SIGHUP);
- sigaddset (&block_set, SIGTERM);
+ for (int i = 0; i < countof (term_sig); i++)
+ if (sig_needs_handling (term_sig[i], sigterm))
+ sigaddset (&block_set, term_sig[i]);
+
+ for (int s = SIGRTMIN; s <= SIGRTMAX; s++)
+ if (sig_needs_handling (s, sigterm))
+ sigaddset (&block_set, s);
+
sigaddset (&block_set, sigterm);
sigaddset (&block_set, SIGCHLD);
@@ -480,7 +519,7 @@ main (int argc, char **argv)
initialize_exit_failure (EXIT_CANCELED);
atexit (close_stdout);
- while ((c = getopt_long (argc, argv, "+fk:ps:v", long_options, nullptr))
+ while ((c = getopt_long (argc, argv, "+fk:ps:v", long_options, NULL))
!= -1)
{
switch (c)
@@ -547,6 +586,12 @@ main (int argc, char **argv)
sigset_t orig_set;
block_cleanup_and_chld (term_signal, &orig_set);
+ /* Get the parent process ID so we can check if we are reparented later.
+ Traditionally processes would be reparented to init. On Linux a process
+ can be come a subreaper using PR_SET_CHILD_SUBREAPER to fulfill the role
+ of init for child processes, as is done by systemd(1). */
+ pid_t timeout_pid = getpid ();
+
/* We cannot use posix_spawn here since the child will have an exit status of
127 for any failure. If implemented through fork and exec, posix_spawn
will return successfully and 'timeout' will have no way to determine if it
@@ -560,8 +605,18 @@ main (int argc, char **argv)
}
else if (monitored_pid == 0) /* child */
{
+#if HAVE_PRCTL
+ /* Add protection if the parent dies without signaling child. */
+ prctl (PR_SET_PDEATHSIG, term_signal);
+#endif
+ /* If we're already reparented to init, don't proceed. Be aware that
+ 'timeout' may actually be started by the init process. E.g., when
+ the shell is the entrypoint to a container. */
+ if (getppid () != timeout_pid)
+ return EXIT_CANCELED;
+
/* Restore signal mask for child. */
- if (sigprocmask (SIG_SETMASK, &orig_set, nullptr) != 0)
+ if (sigprocmask (SIG_SETMASK, &orig_set, NULL) != 0)
{
error (0, errno, _("child failed to reset signal mask"));
return EXIT_CANCELED;
diff --git a/src/touch.c b/src/touch.c
index 20b5f487b..ad1ea5e0e 100644
--- a/src/touch.c
+++ b/src/touch.c
@@ -1,5 +1,5 @@
/* touch -- change modification and access times of files
- Copyright (C) 1987-2025 Free Software Foundation, Inc.
+ Copyright (C) 1987-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -81,20 +81,20 @@ enum
static struct option const longopts[] =
{
- {"time", required_argument, nullptr, TIME_OPTION},
- {"no-create", no_argument, nullptr, 'c'},
- {"date", required_argument, nullptr, 'd'},
- {"reference", required_argument, nullptr, 'r'},
- {"no-dereference", no_argument, nullptr, 'h'},
+ {"time", required_argument, NULL, TIME_OPTION},
+ {"no-create", no_argument, NULL, 'c'},
+ {"date", required_argument, NULL, 'd'},
+ {"reference", required_argument, NULL, 'r'},
+ {"no-dereference", no_argument, NULL, 'h'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* Valid arguments to the '--time' option. */
static char const *const time_args[] =
{
- "atime", "access", "use", "mtime", "modify", nullptr
+ "atime", "access", "use", "mtime", "modify", NULL
};
/* The bits in 'change_times' that those arguments set. */
@@ -149,12 +149,12 @@ touch (char const *file)
if (amtime_now)
{
- /* Pass nullptr to futimens so it will not fail if we have
+ /* Pass NULL to futimens so it will not fail if we have
write access to the file, but don't own it. */
- t = nullptr;
+ t = NULL;
}
- char const *file_opt = fd == STDOUT_FILENO ? nullptr : file;
+ char const *file_opt = fd == STDOUT_FILENO ? NULL : file;
int atflag = no_dereference ? AT_SYMLINK_NOFOLLOW : 0;
int utime_errno = (fdutimensat (fd, AT_FDCWD, file_opt, t, atflag) == 0
? 0 : errno);
@@ -226,30 +226,48 @@ change the times of the file associated with standard output.\n\
emit_mandatory_arg_note ();
- fputs (_("\
- -a change only the access time\n\
- -c, --no-create do not create any files\n\
- -d, --date=STRING parse STRING and use it instead of current time\n\
- -f (ignored)\n\
-"), stdout);
- fputs (_("\
- -h, --no-dereference affect each symbolic link instead of any referenced\n\
- file (useful only on systems that can change the\n\
- timestamps of a symlink)\n\
- -m change only the modification time\n\
-"), stdout);
- fputs (_("\
- -r, --reference=FILE use this file's times instead of current time\n\
- -t [[CC]YY]MMDDhhmm[.ss] use specified time instead of current time,\n\
- with a date-time format that differs from -d's\n\
-"), stdout);
- fputs (_("\
- --time=WORD specify which time to change:\n\
- access time (-a): 'access', 'atime', 'use';\n\
- modification time (-m): 'modify', 'mtime'\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -a\n\
+ change only the access time\n\
+"));
+ oputs (_("\
+ -c, --no-create\n\
+ do not create any files\n\
+"));
+ oputs (_("\
+ -d, --date=STRING\n\
+ parse STRING and use it instead of current time\n\
+"));
+ oputs (_("\
+ -f\n\
+ (ignored)\n\
+"));
+ oputs (_("\
+ -h, --no-dereference\n\
+ affect each symbolic link instead of any referenced file;\n\
+ useful only on systems that can change the timestamps of a symlink\n\
+"));
+ oputs (_("\
+ -m\n\
+ change only the modification time\n\
+"));
+ oputs (_("\
+ -r, --reference=FILE\n\
+ use this file's times instead of current time\n\
+"));
+ oputs (_("\
+ -t [[CC]YY]MMDDhhmm[.ss]\n\
+ use specified time instead of current time,\n\
+ with a date-time format that differs from -d's\n\
+"));
+ oputs (_("\
+ --time=WORD\n\
+ specify which time to change:\n\
+ access time (-a): 'access', 'atime', 'use';\n\
+ modification time (-m): 'modify', 'mtime'\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -258,10 +276,8 @@ change the times of the file associated with standard output.\n\
int
main (int argc, char **argv)
{
- int c;
bool date_set = false;
- bool ok = true;
- char const *flex_date = nullptr;
+ char const *flex_date = NULL;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
@@ -271,10 +287,8 @@ main (int argc, char **argv)
atexit (close_stdout);
- change_times = 0;
- no_create = use_ref = false;
-
- while ((c = getopt_long (argc, argv, "acd:fhmr:t:", longopts, nullptr)) != -1)
+ int c;
+ while ((c = getopt_long (argc, argv, "acd:fhmr:t:", longopts, NULL)) != -1)
{
switch (c)
{
@@ -371,8 +385,7 @@ main (int argc, char **argv)
it were absent; this lets "touch" succeed more often in
the presence of restrictive permissions. */
if (change_times == (CH_ATIME | CH_MTIME)
- && newtime[0].tv_sec == now.tv_sec
- && newtime[0].tv_nsec == now.tv_nsec)
+ && timespec_cmp (newtime[0], now) == 0)
{
/* Check that it really was "-d now", and not a timestamp
that just happens to be the current time. */
@@ -380,8 +393,7 @@ main (int argc, char **argv)
notnow.tv_sec = now.tv_sec ^ 1;
notnow.tv_nsec = now.tv_nsec;
notnow1 = date_relative (flex_date, notnow);
- if (notnow1.tv_sec == notnow.tv_sec
- && notnow1.tv_nsec == notnow.tv_nsec)
+ if (timespec_cmp (notnow1, notnow) == 0)
date_set = false;
}
}
@@ -402,7 +414,7 @@ main (int argc, char **argv)
struct tm const *tm = localtime (&newtime[0].tv_sec);
/* Technically, it appears that even a deliberate attempt to cause
- the above localtime to return nullptr will always fail because our
+ the above localtime to return NULL will always fail because our
posixtime implementation rejects all dates for which localtime
would fail. However, skip the warning if it ever fails. */
if (tm)
@@ -431,6 +443,7 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
+ bool ok = true;
for (; optind < argc; ++optind)
ok &= touch (argv[optind]);
diff --git a/src/tr.c b/src/tr.c
index 26776395f..df3904f4c 100644
--- a/src/tr.c
+++ b/src/tr.c
@@ -1,5 +1,5 @@
/* tr -- a filter to translate characters
- Copyright (C) 1991-2025 Free Software Foundation, Inc.
+ Copyright (C) 1991-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
#include <config.h>
-#include <ctype.h>
#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>
@@ -267,13 +266,13 @@ static char xlate[N_CHARS];
static struct option const long_options[] =
{
- {"complement", no_argument, nullptr, 'c'},
- {"delete", no_argument, nullptr, 'd'},
- {"squeeze-repeats", no_argument, nullptr, 's'},
- {"truncate-set1", no_argument, nullptr, 't'},
+ {"complement", no_argument, NULL, 'c'},
+ {"delete", no_argument, NULL, 'd'},
+ {"squeeze-repeats", no_argument, NULL, 's'},
+ {"truncate-set1", no_argument, NULL, 't'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -292,15 +291,27 @@ Translate, squeeze, and/or delete characters from standard input,\n\
writing to standard output. STRING1 and STRING2 specify arrays of\n\
characters ARRAY1 and ARRAY2 that control the action.\n\
\n\
- -c, -C, --complement use the complement of ARRAY1\n\
- -d, --delete delete characters in ARRAY1, do not translate\n\
- -s, --squeeze-repeats replace each sequence of a repeated character\n\
- that is listed in the last specified ARRAY,\n\
- with a single occurrence of that character\n\
- -t, --truncate-set1 first truncate ARRAY1 to length of ARRAY2\n\
"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -c, -C, --complement\n\
+ use the complement of ARRAY1\n\
+"));
+ oputs (_("\
+ -d, --delete\n\
+ delete characters in ARRAY1, do not translate\n\
+"));
+ oputs (_("\
+ -s, --squeeze-repeats\n\
+ replace each sequence of a repeated character\n\
+ that is listed in the last specified ARRAY,\n\
+ with a single occurrence of that character\n\
+"));
+ oputs (_("\
+ -t, --truncate-set1\n\
+ first truncate ARRAY1 to length of ARRAY2\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
ARRAYs are specified as strings of characters. Most represent themselves.\n\
@@ -342,8 +353,9 @@ Translation occurs if -d is not given and both STRING1 and STRING2 appear.\n\
-t is only significant when translating. ARRAY2 is extended to length of\n\
ARRAY1 by repeating its last character as necessary. Excess characters\n\
of ARRAY2 are ignored. Character classes expand in unspecified order;\n\
-while translating, [:lower:] and [:upper:] may be used in pairs to\n\
+while translating, '[:lower:]' and '[:upper:]' may be used in pairs to\n\
specify case conversion. Squeezing occurs after translation or deletion.\n\
+Arguments like '[...]' should be quoted, to avoid potential shell globbing.\n\
"), stdout);
emit_ancillary_info (PROGRAM_NAME);
}
@@ -543,9 +555,7 @@ ATTRIBUTE_PURE
static enum Char_class
look_up_char_class (char const *class_str, size_t len)
{
- enum Char_class i;
-
- for (i = 0; i < countof (char_class_name); i++)
+ for (enum Char_class i = 0; i < countof (char_class_name); i++)
if (STREQ_LEN (class_str, char_class_name[i], len)
&& strlen (char_class_name[i]) == len)
return i;
@@ -590,7 +600,7 @@ make_printable_str (char const *s, size_t len)
for (size_t i = 0; i < len; i++)
{
char buf[5];
- char const *tmp = nullptr;
+ char const *tmp = NULL;
unsigned char c = s[i];
switch (c)
@@ -642,7 +652,7 @@ static void
append_normal_char (struct Spec_list *list, unsigned char c)
{
struct List_element *new = xmalloc (sizeof *new);
- new->next = nullptr;
+ new->next = NULL;
new->type = RE_NORMAL_CHAR;
new->u.normal_char = c;
list->tail->next = new;
@@ -670,7 +680,7 @@ append_range (struct Spec_list *list, unsigned char first, unsigned char last)
return false;
}
struct List_element *new = xmalloc (sizeof *new);
- new->next = nullptr;
+ new->next = NULL;
new->type = RE_RANGE;
new->u.range.first_char = first;
new->u.range.last_char = last;
@@ -692,7 +702,7 @@ append_char_class (struct Spec_list *list,
if (char_class == CC_NO_CLASS)
return false;
struct List_element *new = xmalloc (sizeof *new);
- new->next = nullptr;
+ new->next = NULL;
new->type = RE_CHAR_CLASS;
new->u.char_class = char_class;
list->tail->next = new;
@@ -710,7 +720,7 @@ append_repeated_char (struct Spec_list *list, unsigned char the_char,
count repeat_count)
{
struct List_element *new = xmalloc (sizeof *new);
- new->next = nullptr;
+ new->next = NULL;
new->type = RE_REPEATED_CHAR;
new->u.repeated_char.the_repeated_char = the_char;
new->u.repeated_char.repeat_count = repeat_count;
@@ -732,7 +742,7 @@ append_equiv_class (struct Spec_list *list,
return false;
struct List_element *new = xmalloc (sizeof *new);
- new->next = nullptr;
+ new->next = NULL;
new->type = RE_EQUIV_CLASS;
new->u.equiv_code = *equiv_class_str;
list->tail->next = new;
@@ -798,7 +808,7 @@ find_bracketed_repeat (const struct E_string *es, size_t start_idx,
char const *digit_str = &es->s[start_idx + 2];
char *d_end;
if ((xstrtoumax (digit_str, &d_end, *digit_str == '0' ? 8 : 10,
- repeat_count, nullptr)
+ repeat_count, NULL)
!= LONGINT_OK)
|| REPEAT_COUNT_MAXIMUM < *repeat_count
|| digit_str + digit_str_len != d_end)
@@ -1025,7 +1035,7 @@ get_next (struct Spec_list *s, enum Upper_Lower_class *class)
}
p = s->tail;
- if (p == nullptr)
+ if (p == NULL)
return -1;
switch (p->type)
@@ -1140,7 +1150,7 @@ card_of_complement (struct Spec_list *s)
bool in_set[N_CHARS] = {0};
s->state = BEGIN_STATE;
- while ((c = get_next (s, nullptr)) != -1)
+ while ((c = get_next (s, NULL)) != -1)
{
cardinality -= (!in_set[c]);
in_set[c] = true;
@@ -1234,14 +1244,13 @@ validate_case_classes (struct Spec_list *s1, struct Spec_list *s2)
static void
get_spec_stats (struct Spec_list *s)
{
- struct List_element *p;
count length = 0;
s->n_indefinite_repeats = 0;
s->has_equiv_class = false;
s->has_restricted_char_class = false;
s->has_char_class = false;
- for (p = s->head->next; p; p = p->next)
+ for (struct List_element *p = s->head->next; p; p = p->next)
{
count len = 0;
@@ -1329,7 +1338,7 @@ spec_init (struct Spec_list *spec_list)
{
struct List_element *new = xmalloc (sizeof *new);
spec_list->head = spec_list->tail = new;
- spec_list->head->next = nullptr;
+ spec_list->head->next = NULL;
}
/* This function makes two passes over the argument string S. The first
@@ -1412,10 +1421,10 @@ homogeneous_spec_list (struct Spec_list *s)
s->state = BEGIN_STATE;
- if ((b = get_next (s, nullptr)) == -1)
+ if ((b = get_next (s, NULL)) == -1)
return false;
- while ((c = get_next (s, nullptr)) != -1)
+ while ((c = get_next (s, NULL)) != -1)
if (c != b)
return false;
@@ -1662,7 +1671,7 @@ set_initialize (struct Spec_list *s, bool complement_this_set, bool *in_set)
int c;
s->state = BEGIN_STATE;
- while ((c = get_next (s, nullptr)) != -1)
+ while ((c = get_next (s, NULL)) != -1)
in_set[c] = true;
if (complement_this_set)
for (size_t i = 0; i < N_CHARS; i++)
@@ -1688,7 +1697,7 @@ main (int argc, char **argv)
atexit (close_stdout);
- while ((c = getopt_long (argc, argv, "+AcCdst", long_options, nullptr)) != -1)
+ while ((c = getopt_long (argc, argv, "+AcCdst", long_options, NULL)) != -1)
{
switch (c)
{
@@ -1767,7 +1776,7 @@ main (int argc, char **argv)
main_exit (EXIT_FAILURE);
}
else
- s2 = nullptr;
+ s2 = NULL;
validate (s1, s2);
@@ -1816,7 +1825,7 @@ main (int argc, char **argv)
{
if (!in_s1[i])
{
- int ch = get_next (s2, nullptr);
+ int ch = get_next (s2, NULL);
affirm (ch != -1 || truncate_set1);
if (ch == -1)
{
diff --git a/src/true.c b/src/true.c
index 34406b66d..6603177f4 100644
--- a/src/true.c
+++ b/src/true.c
@@ -1,5 +1,5 @@
/* Exit with a status code indicating success.
- Copyright (C) 1999-2025 Free Software Foundation, Inc.
+ Copyright (C) 1999-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -44,8 +44,8 @@ Usage: %s [ignored command line arguments]\n\
_(EXIT_STATUS == EXIT_SUCCESS
? N_("Exit with a status code indicating success.")
: N_("Exit with a status code indicating failure.")));
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
emit_ancillary_info (PROGRAM_NAME);
exit (status);
@@ -73,7 +73,7 @@ main (int argc, char **argv)
if (streq (argv[1], "--version"))
version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version, AUTHORS,
- (char *) nullptr);
+ (char *) NULL);
}
return EXIT_STATUS;
diff --git a/src/truncate.c b/src/truncate.c
index 7afe1c376..7119132e0 100644
--- a/src/truncate.c
+++ b/src/truncate.c
@@ -1,5 +1,5 @@
/* truncate -- truncate or extend the length of files.
- Copyright (C) 2008-2025 Free Software Foundation, Inc.
+ Copyright (C) 2008-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,7 +21,6 @@
to better fit the "GNU" environment. */
#include <config.h> /* sets _FILE_OFFSET_BITS=64 etc. */
-#include <ctype.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
@@ -47,13 +46,13 @@ static char const *ref_file;
static struct option const longopts[] =
{
- {"no-create", no_argument, nullptr, 'c'},
- {"io-blocks", no_argument, nullptr, 'o'},
- {"reference", required_argument, nullptr, 'r'},
- {"size", required_argument, nullptr, 's'},
+ {"no-create", no_argument, NULL, 'c'},
+ {"io-blocks", no_argument, NULL, 'o'},
+ {"reference", required_argument, NULL, 'r'},
+ {"size", required_argument, NULL, 's'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
typedef enum
@@ -79,17 +78,20 @@ reads as zero bytes.\n\
emit_mandatory_arg_note ();
- fputs (_("\
+ oputs (_("\
-c, --no-create do not create any files\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-o, --io-blocks treat SIZE as number of IO blocks instead of bytes\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-r, --reference=RFILE base size on RFILE\n\
- -s, --size=SIZE set or adjust the file size by SIZE bytes\n"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+"));
+ oputs (_("\
+ -s, --size=SIZE set or adjust the file size by SIZE bytes\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_size_note ();
fputs (_("\n\
SIZE may also be prefixed by one of the following modifying characters:\n\
@@ -188,7 +190,7 @@ do_ftruncate (int fd, char const *fname, off_t ssize, off_t rsize,
if (ftruncate (fd, nsize) != 0)
{
error (0, errno, _("failed to truncate %s at %jd bytes"),
- quoteaf (fname), (intmax_t) nsize);
+ quoteaf (fname), (intmax_t) {nsize});
return false;
}
@@ -212,7 +214,7 @@ main (int argc, char **argv)
atexit (close_stdout);
- while ((c = getopt_long (argc, argv, "cor:s:", longopts, nullptr)) != -1)
+ while ((c = getopt_long (argc, argv, "cor:s:", longopts, NULL)) != -1)
{
switch (c)
{
diff --git a/src/tsort.c b/src/tsort.c
index 6e39c0d13..6ac458924 100644
--- a/src/tsort.c
+++ b/src/tsort.c
@@ -1,5 +1,5 @@
/* tsort - topological sort.
- Copyright (C) 1998-2025 Free Software Foundation, Inc.
+ Copyright (C) 1998-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -60,13 +60,13 @@ struct item
};
/* The head of the sorted list. */
-static struct item *head = nullptr;
+static struct item *head = NULL;
/* The tail of the list of 'zeros', strings that have no predecessors. */
-static struct item *zeros = nullptr;
+static struct item *zeros = NULL;
/* Used for loop detection. */
-static struct item *loop = nullptr;
+static struct item *loop = NULL;
/* The number of strings to sort. */
static size_t n_strings = 0;
@@ -88,8 +88,8 @@ Write totally ordered list consistent with the partial ordering in FILE.\n\
fputs (_("\
\n\
"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
@@ -123,7 +123,7 @@ search_item (struct item *root, char const *str)
/* Make sure the tree is not empty, since that is what the algorithm
below expects. */
- if (root->right == nullptr)
+ if (root->right == NULL)
return (root->right = new_item (str));
/* A1. Initialize. */
@@ -143,7 +143,7 @@ search_item (struct item *root, char const *str)
else
q = p->right;
- if (q == nullptr)
+ if (q == NULL)
{
/* A5. Insert. */
q = new_item (str);
@@ -288,7 +288,7 @@ scan_zeros (struct item *k)
/* Ignore strings that have already been printed. */
if (k->count == 0 && !k->printed)
{
- if (head == nullptr)
+ if (head == NULL)
head = k;
else
zeros->qlink = k;
@@ -325,7 +325,7 @@ detect_loop (struct item *k)
/* K does not have to be part of a cycle. It is however part of
a graph that contains a cycle. */
- if (loop == nullptr)
+ if (loop == NULL)
/* Start traversing the graph at K. */
loop = k;
else
@@ -358,7 +358,7 @@ detect_loop (struct item *k)
/* Tidy things up since we might have to
detect another loop. */
- loop->qlink = nullptr;
+ loop->qlink = NULL;
loop = tmp;
}
@@ -366,7 +366,7 @@ detect_loop (struct item *k)
{
struct item *tmp = loop->qlink;
- loop->qlink = nullptr;
+ loop->qlink = NULL;
loop = tmp;
}
@@ -396,16 +396,16 @@ detect_loop (struct item *k)
static bool
recurse_tree (struct item *root, bool (*action) (struct item *))
{
- if (root->left == nullptr && root->right == nullptr)
+ if (root->left == NULL && root->right == NULL)
return (*action) (root);
else
{
- if (root->left != nullptr)
+ if (root->left != NULL)
if (recurse_tree (root->left, action))
return true;
if ((*action) (root))
return true;
- if (root->right != nullptr)
+ if (root->right != NULL)
if (recurse_tree (root->right, action))
return true;
}
@@ -429,13 +429,13 @@ static _Noreturn void
tsort (char const *file)
{
bool ok = true;
- struct item *j = nullptr;
- struct item *k = nullptr;
+ struct item *j = NULL;
+ struct item *k = NULL;
token_buffer tokenbuffer;
bool is_stdin = streq (file, "-");
/* Initialize the head of the tree holding the strings we're sorting. */
- struct item *root = new_item (nullptr);
+ struct item *root = new_item (NULL);
if (!is_stdin && ! freopen (file, "r", stdin))
error (EXIT_FAILURE, errno, "%s", quotef (file));
@@ -462,13 +462,13 @@ tsort (char const *file)
{
/* T3. Record the relation. */
record_relation (j, k);
- k = nullptr;
+ k = NULL;
}
j = k;
}
- if (k != nullptr)
+ if (k != NULL)
error (EXIT_FAILURE, 0, _("%s: input contains an odd number of tokens"),
quotef (file));
@@ -544,9 +544,9 @@ main (int argc, char **argv)
{
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
- int c = getopt_long (argc, argv, "w", long_options, nullptr);
+ int c = getopt_long (argc, argv, "w", long_options, NULL);
if (c == -1)
break;
diff --git a/src/tty.c b/src/tty.c
index e04c72bd7..7946fb78e 100644
--- a/src/tty.c
+++ b/src/tty.c
@@ -1,5 +1,5 @@
/* tty -- print the name of the terminal connected to standard input
- Copyright (C) 1990-2025 Free Software Foundation, Inc.
+ Copyright (C) 1990-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -48,11 +48,11 @@ static bool silent;
static struct option const longopts[] =
{
- {"silent", no_argument, nullptr, 's'},
- {"quiet", no_argument, nullptr, 's'},
+ {"silent", no_argument, NULL, 's'},
+ {"quiet", no_argument, NULL, 's'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -66,10 +66,13 @@ usage (int status)
fputs (_("\
Print the file name of the terminal connected to standard input.\n\
\n\
- -s, --silent, --quiet print nothing, only return an exit status\n\
"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -s, --silent, --quiet\n\
+ print nothing, only return an exit status\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -89,9 +92,7 @@ main (int argc, char **argv)
initialize_exit_failure (TTY_WRITE_ERROR);
atexit (close_stdout);
- silent = false;
-
- while ((optc = getopt_long (argc, argv, "s", longopts, nullptr)) != -1)
+ while ((optc = getopt_long (argc, argv, "s", longopts, NULL)) != -1)
{
switch (optc)
{
diff --git a/src/uname-arch.c b/src/uname-arch.c
index eb429424e..c93a0e65e 100644
--- a/src/uname-arch.c
+++ b/src/uname-arch.c
@@ -1,2 +1,2 @@
#include "uname.h"
-int uname_mode = UNAME_ARCH;
+enum uname_modes uname_mode = UNAME_ARCH;
diff --git a/src/uname-uname.c b/src/uname-uname.c
index 450245dcc..f4e0d39ff 100644
--- a/src/uname-uname.c
+++ b/src/uname-uname.c
@@ -1,2 +1,2 @@
#include "uname.h"
-int uname_mode = UNAME_UNAME;
+enum uname_modes uname_mode = UNAME_UNAME;
diff --git a/src/uname.c b/src/uname.c
index 96c532b94..5f7a35dd6 100644
--- a/src/uname.c
+++ b/src/uname.c
@@ -1,6 +1,6 @@
/* uname -- print system information
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -81,27 +81,27 @@
static struct option const uname_long_options[] =
{
- {"all", no_argument, nullptr, 'a'},
- {"kernel-name", no_argument, nullptr, 's'},
- {"sysname", no_argument, nullptr, 's'}, /* Obsolescent. */
- {"nodename", no_argument, nullptr, 'n'},
- {"kernel-release", no_argument, nullptr, 'r'},
- {"release", no_argument, nullptr, 'r'}, /* Obsolescent. */
- {"kernel-version", no_argument, nullptr, 'v'},
- {"machine", no_argument, nullptr, 'm'},
- {"processor", no_argument, nullptr, 'p'},
- {"hardware-platform", no_argument, nullptr, 'i'},
- {"operating-system", no_argument, nullptr, 'o'},
+ {"all", no_argument, NULL, 'a'},
+ {"kernel-name", no_argument, NULL, 's'},
+ {"sysname", no_argument, NULL, 's'}, /* Obsolescent. */
+ {"nodename", no_argument, NULL, 'n'},
+ {"kernel-release", no_argument, NULL, 'r'},
+ {"release", no_argument, NULL, 'r'}, /* Obsolescent. */
+ {"kernel-version", no_argument, NULL, 'v'},
+ {"machine", no_argument, NULL, 'm'},
+ {"processor", no_argument, NULL, 'p'},
+ {"hardware-platform", no_argument, NULL, 'i'},
+ {"operating-system", no_argument, NULL, 'o'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
static struct option const arch_long_options[] =
{
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -118,19 +118,35 @@ usage (int status)
fputs (_("\
Print certain system information. With no OPTION, same as -s.\n\
\n\
+"), stdout);
+ oputs (_("\
-a, --all print all information, in the following order,\n\
- except omit -p and -i if unknown:\n\
+ except omit -p and -i if unknown\n\
+"));
+ oputs (_("\
-s, --kernel-name print the kernel name\n\
+"));
+ oputs (_("\
-n, --nodename print the network node hostname\n\
+"));
+ oputs (_("\
-r, --kernel-release print the kernel release\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-v, --kernel-version print the kernel version\n\
+"));
+ oputs (_("\
-m, --machine print the machine hardware name\n\
+"));
+ oputs (_("\
-p, --processor print the processor type (non-portable)\n\
+"));
+ oputs (_("\
-i, --hardware-platform print the hardware platform (non-portable)\n\
+"));
+ oputs (_("\
-o, --operating-system print the operating system\n\
-"), stdout);
+"));
}
else
{
@@ -140,8 +156,8 @@ Print machine architecture.\n\
"), stdout);
}
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -191,7 +207,7 @@ decode_switches (int argc, char **argv)
if (uname_mode == UNAME_ARCH)
{
while ((c = getopt_long (argc, argv, "",
- arch_long_options, nullptr))
+ arch_long_options, NULL))
!= -1)
{
switch (c)
@@ -209,7 +225,7 @@ decode_switches (int argc, char **argv)
else
{
while ((c = getopt_long (argc, argv, "asnrvmpio",
- uname_long_options, nullptr))
+ uname_long_options, NULL))
!= -1)
{
switch (c)
diff --git a/src/uname.h b/src/uname.h
index f4da9b0c3..428d8f510 100644
--- a/src/uname.h
+++ b/src/uname.h
@@ -1,7 +1,10 @@
-/* This is for the 'uname' program. */
-#define UNAME_UNAME 1
+enum uname_modes
+{
+ /* This is for the 'uname' program. */
+ UNAME_UNAME,
-/* This is for the 'arch' program. */
-#define UNAME_ARCH 2
+ /* This is for the 'arch' program. */
+ UNAME_ARCH
+};
-extern int uname_mode;
+extern enum uname_modes uname_mode;
diff --git a/src/unexpand.c b/src/unexpand.c
index ff234d7c4..c859c17a3 100644
--- a/src/unexpand.c
+++ b/src/unexpand.c
@@ -1,5 +1,5 @@
/* unexpand - convert blanks to tabs
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -35,11 +35,14 @@
#include <config.h>
-#include <ctype.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
+
#include "system.h"
+#include "ioblksize.h"
+#include "mbbuf.h"
+#include "mcel.h"
#include "expand-common.h"
/* The official name of this program (e.g., no 'g' prefix). */
@@ -58,12 +61,12 @@ enum
static struct option const longopts[] =
{
- {"tabs", required_argument, nullptr, 't'},
- {"all", no_argument, nullptr, 'a'},
- {"first-only", no_argument, nullptr, CONVERT_FIRST_ONLY_OPTION},
+ {"tabs", required_argument, NULL, 't'},
+ {"all", no_argument, NULL, 'a'},
+ {"first-only", no_argument, NULL, CONVERT_FIRST_ONLY_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -84,14 +87,21 @@ Convert blanks in each FILE to tabs, writing to standard output.\n\
emit_stdin_note ();
emit_mandatory_arg_note ();
- fputs (_("\
- -a, --all convert all blanks, instead of just initial blanks\n\
- --first-only convert only leading sequences of blanks (overrides -a)\n\
- -t, --tabs=N have tabs N characters apart instead of 8 (enables -a)\n\
-"), stdout);
- emit_tab_list_info ();
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -a, --all\n\
+ convert all blanks, instead of just initial blanks\n\
+"));
+ oputs (_("\
+ --first-only\n\
+ convert only leading sequences of blanks (overrides -a)\n\
+"));
+ oputs (_("\
+ -t, --tabs=N\n\
+ have tabs N characters apart instead of 8 (enables -a)\n\
+"));
+ emit_tab_list_info (PROGRAM_NAME);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -104,7 +114,7 @@ static void
unexpand (void)
{
/* Input stream. */
- FILE *fp = next_file (nullptr);
+ FILE *fp = next_file (NULL);
/* The array of pending blanks. In non-POSIX locales, blanks can
include characters other than spaces, so the blanks must be
@@ -114,15 +124,19 @@ unexpand (void)
if (!fp)
return;
+ static char line_in[IO_BUFSIZE];
+ mbbuf_t mbbuf;
+ mbbuf_init (&mbbuf, line_in, sizeof line_in, fp);
+
/* The worst case is a non-blank character, then one blank, then a
tab stop, then MAX_COLUMN_WIDTH - 1 blanks, then a non-blank; so
allocate MAX_COLUMN_WIDTH bytes to store the blanks. */
- pending_blank = ximalloc (max_column_width);
+ pending_blank = xinmalloc (max_column_width, MB_CUR_MAX);
while (true)
{
/* Input character, or EOF. */
- int c;
+ mcel_t g;
/* If true, perform translations. */
bool convert = true;
@@ -156,12 +170,13 @@ unexpand (void)
do
{
- while ((c = getc (fp)) < 0 && (fp = next_file (fp)))
- continue;
+ while ((g = mbbuf_get_char (&mbbuf)).ch == MBBUF_EOF
+ && (fp = next_file (fp)))
+ mbbuf_init (&mbbuf, line_in, sizeof line_in, fp);
if (convert)
{
- bool blank = !! isblank (c);
+ bool blank = c32issep (g.ch);
if (blank)
{
@@ -175,7 +190,7 @@ unexpand (void)
if (convert)
{
- if (c == '\t')
+ if (g.ch == '\t')
{
column = next_tab_column;
@@ -184,7 +199,7 @@ unexpand (void)
}
else
{
- column++;
+ column += c32width (g.ch);
if (! (prev_blank && column == next_tab_column))
{
@@ -192,13 +207,18 @@ unexpand (void)
will be replaced by tabs. */
if (column == next_tab_column)
one_blank_before_tab_stop = true;
- pending_blank[pending++] = c;
+ memcpy (pending_blank + pending,
+ mbbuf_char_offset (&mbbuf, g), g.len);
+ pending += g.len;
prev_blank = true;
continue;
}
/* Replace the pending blanks by a tab or two. */
- pending_blank[0] = c = '\t';
+ g.len = 0;
+ if (putc ('\t', stdout) < 0)
+ write_error ();
+ pending_blank[0] = '\t';
}
/* Discard pending blanks, unless it was a single
@@ -206,7 +226,7 @@ unexpand (void)
pending = one_blank_before_tab_stop;
}
}
- else if (c == '\b')
+ else if (g.ch == '\b')
{
/* Go back one column, and force recalculation of the
next tab stop. */
@@ -216,8 +236,8 @@ unexpand (void)
}
else
{
- column++;
- if (!column)
+ int width = c32width (g.ch);
+ if (ckd_add (&column, column, width < 0 ? 1 : width))
error (EXIT_FAILURE, 0, _("input line is too long"));
}
@@ -235,16 +255,17 @@ unexpand (void)
convert &= convert_entire_line || blank;
}
- if (c < 0)
+ if (g.ch == MBBUF_EOF)
{
free (pending_blank);
return;
}
- if (putchar (c) < 0)
+ fwrite (mbbuf_char_offset (&mbbuf, g), sizeof (char), g.len, stdout);
+ if (ferror (stdout))
write_error ();
}
- while (c != '\n');
+ while (g.ch != '\n');
}
}
@@ -267,7 +288,7 @@ main (int argc, char **argv)
atexit (close_stdout);
- while ((c = getopt_long (argc, argv, ",0123456789at:", longopts, nullptr))
+ while ((c = getopt_long (argc, argv, ",0123456789at:", longopts, NULL))
!= -1)
{
switch (c)
@@ -298,7 +319,7 @@ main (int argc, char **argv)
have_tabval = true;
}
if (!DECIMAL_DIGIT_ACCUMULATE (tabval, c - '0'))
- error (EXIT_FAILURE, 0, _("tab stop value is too large"));
+ error (EXIT_FAILURE, 0, _("tab stop is too large"));
break;
}
}
@@ -311,7 +332,7 @@ main (int argc, char **argv)
finalize_tab_stops ();
- set_file_list ((optind < argc) ? &argv[optind] : nullptr);
+ set_file_list ((optind < argc) ? &argv[optind] : NULL);
unexpand ();
diff --git a/src/uniq.c b/src/uniq.c
index bce1949f3..5834596f9 100644
--- a/src/uniq.c
+++ b/src/uniq.c
@@ -1,5 +1,5 @@
/* uniq -- remove duplicate lines from a sorted file
- Copyright (C) 1986-2025 Free Software Foundation, Inc.
+ Copyright (C) 1986-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -85,7 +85,7 @@ enum delimit_method
static char const *const delimit_method_string[] =
{
- "none", "prepend", "separate", nullptr
+ "none", "prepend", "separate", NULL
};
static enum delimit_method const delimit_method_map[] =
@@ -116,7 +116,7 @@ enum grouping_method
static char const *const grouping_method_string[] =
{
- "prepend", "append", "separate", "both", nullptr
+ "prepend", "append", "separate", "both", NULL
};
static enum grouping_method const grouping_method_map[] =
@@ -133,19 +133,19 @@ enum
static struct option const longopts[] =
{
- {"count", no_argument, nullptr, 'c'},
- {"repeated", no_argument, nullptr, 'd'},
- {"all-repeated", optional_argument, nullptr, 'D'},
- {"group", optional_argument, nullptr, GROUP_OPTION},
- {"ignore-case", no_argument, nullptr, 'i'},
- {"unique", no_argument, nullptr, 'u'},
- {"skip-fields", required_argument, nullptr, 'f'},
- {"skip-chars", required_argument, nullptr, 's'},
- {"check-chars", required_argument, nullptr, 'w'},
- {"zero-terminated", no_argument, nullptr, 'z'},
+ {"count", no_argument, NULL, 'c'},
+ {"repeated", no_argument, NULL, 'd'},
+ {"all-repeated", optional_argument, NULL, 'D'},
+ {"group", optional_argument, NULL, GROUP_OPTION},
+ {"ignore-case", no_argument, NULL, 'i'},
+ {"unique", no_argument, NULL, 'u'},
+ {"skip-fields", required_argument, NULL, 'f'},
+ {"skip-chars", required_argument, NULL, 's'},
+ {"check-chars", required_argument, NULL, 'w'},
+ {"zero-terminated", no_argument, NULL, 'z'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
void
@@ -168,36 +168,54 @@ With no options, matching lines are merged to the first occurrence.\n\
emit_mandatory_arg_note ();
- fputs (_("\
- -c, --count prefix lines by the number of occurrences\n\
- -d, --repeated only print duplicate lines, one for each group\n\
-"), stdout);
- fputs (_("\
- -D print all duplicate lines\n\
- --all-repeated[=METHOD] like -D, but allow separating groups\n\
- with an empty line;\n\
- METHOD={none(default),prepend,separate}\n\
-"), stdout);
- fputs (_("\
- -f, --skip-fields=N avoid comparing the first N fields\n\
-"), stdout);
- fputs (_("\
- --group[=METHOD] show all items, separating groups with an empty line;\n\
- METHOD={separate(default),prepend,append,both}\n\
-"), stdout);
- fputs (_("\
- -i, --ignore-case ignore differences in case when comparing\n\
- -s, --skip-chars=N avoid comparing the first N characters\n\
- -u, --unique only print unique lines\n\
-"), stdout);
- fputs (_("\
- -z, --zero-terminated line delimiter is NUL, not newline\n\
-"), stdout);
- fputs (_("\
- -w, --check-chars=N compare no more than N characters in lines\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -c, --count\n\
+ prefix lines by the number of occurrences\n\
+"));
+ oputs (_("\
+ -d, --repeated\n\
+ only print duplicate lines, one for each group\n\
+"));
+ oputs (_("\
+ -D\n\
+ print all duplicate lines\n\
+"));
+ oputs (_("\
+ --all-repeated[=METHOD]\n\
+ like -D, but allow separating groups with an empty line;\n\
+ METHOD={none(default),prepend,separate}\n\
+"));
+ oputs (_("\
+ -f, --skip-fields=N\n\
+ avoid comparing the first N fields\n\
+"));
+ oputs (_("\
+ --group[=METHOD]\n\
+ show all items, separating groups with an empty line;\n\
+ METHOD={separate(default),prepend,append,both}\n\
+"));
+ oputs (_("\
+ -i, --ignore-case\n\
+ ignore differences in case when comparing\n\
+"));
+ oputs (_("\
+ -s, --skip-chars=N\n\
+ avoid comparing the first N characters\n\
+"));
+ oputs (_("\
+ -u, --unique\n\
+ only print unique lines\n\
+"));
+ oputs (_("\
+ -z, --zero-terminated\n\
+ line delimiter is NUL, not newline\n\
+"));
+ oputs (_("\
+ -w, --check-chars=N\n\
+ compare no more than N characters in lines\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
fputs (_("\
\n\
A field is a run of blanks (usually spaces and/or TABs), then non-blank\n\
@@ -227,7 +245,7 @@ static idx_t
size_opt (char const *opt, char const *msgid)
{
intmax_t size;
- if (LONGINT_OVERFLOW < xstrtoimax (opt, nullptr, 10, &size, "")
+ if (LONGINT_OVERFLOW < xstrtoimax (opt, NULL, 10, &size, "")
|| size < 0)
error (EXIT_FAILURE, 0, "%s: %s", opt, _(msgid));
return MIN (size, IDX_MAX);
@@ -236,7 +254,7 @@ size_opt (char const *opt, char const *msgid)
static bool
newline_or_blank (mcel_t g)
{
- return g.ch == '\n' || c32isblank (g.ch);
+ return g.ch == '\n' || c32issep (g.ch);
}
/* Given a linebuffer LINE,
@@ -267,8 +285,8 @@ find_field (struct linebuffer const *line, idx_t *plen)
else
{
char *ep = lp;
- for (idx_t i = check_chars; 0 < i && lp < lim; i--)
- ep += mcel_scan (lp, lim).len;
+ for (idx_t i = check_chars; 0 < i && ep < lim; i--)
+ ep += mcel_scan (ep, lim).len;
len = ep - lp;
}
@@ -352,12 +370,12 @@ check_file (char const *infile, char const *outfile, char delimiter)
*/
if (output_unique && output_first_repeated && !count_occurrences)
{
- char *prevfield = nullptr;
+ char *prevfield = NULL;
idx_t prevlen;
bool first_group_printed = false;
while (!feof (stdin)
- && readlinebuffer_delim (thisline, stdin, delimiter) != 0)
+ && readlinebuffer_delim (thisline, stdin, delimiter))
{
idx_t thislen;
char *thisfield = find_field (thisline, &thislen);
@@ -388,7 +406,7 @@ check_file (char const *infile, char const *outfile, char delimiter)
}
else
{
- if (readlinebuffer_delim (prevline, stdin, delimiter) == 0)
+ if (!readlinebuffer_delim (prevline, stdin, delimiter))
goto closefiles;
idx_t prevlen;
@@ -398,7 +416,7 @@ check_file (char const *infile, char const *outfile, char delimiter)
while (!feof (stdin))
{
- if (readlinebuffer_delim (thisline, stdin, delimiter) == 0)
+ if (!readlinebuffer_delim (thisline, stdin, delimiter))
{
if (ferror (stdin))
goto closefiles;
@@ -467,7 +485,7 @@ int
main (int argc, char **argv)
{
int optc = 0;
- bool posixly_correct = (getenv ("POSIXLY_CORRECT") != nullptr);
+ bool posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
enum Skip_field_option_type skip_field_option_type = SFO_NONE;
int nfiles = 0;
char const *file[2];
@@ -493,7 +511,7 @@ main (int argc, char **argv)
|| (posixly_correct && nfiles != 0)
|| ((optc = getopt_long (argc, argv,
"-0123456789Dcdf:is:uw:z",
- longopts, nullptr))
+ longopts, NULL))
== -1))
{
if (argc <= optind)
@@ -512,7 +530,7 @@ main (int argc, char **argv)
intmax_t size;
if (optarg[0] == '+'
&& ! strict_posix2 ()
- && (xstrtoimax (optarg, nullptr, 10, &size, "")
+ && (xstrtoimax (optarg, NULL, 10, &size, "")
<= LONGINT_OVERFLOW))
skip_chars = MIN (size, IDX_MAX);
else if (nfiles == 2)
@@ -559,7 +577,7 @@ main (int argc, char **argv)
case 'D':
output_unique = false;
output_later_repeated = true;
- if (optarg == nullptr)
+ if (optarg == NULL)
delimit_groups = DM_NONE;
else
delimit_groups = XARGMATCH ("--all-repeated", optarg,
@@ -569,7 +587,7 @@ main (int argc, char **argv)
break;
case GROUP_OPTION:
- if (optarg == nullptr)
+ if (optarg == NULL)
grouping = GM_SEPARATE;
else
grouping = XARGMATCH ("--group", optarg,
diff --git a/src/unlink.c b/src/unlink.c
index 2d6314992..a0ea98704 100644
--- a/src/unlink.c
+++ b/src/unlink.c
@@ -1,5 +1,5 @@
/* unlink utility for GNU.
- Copyright (C) 2001-2025 Free Software Foundation, Inc.
+ Copyright (C) 2001-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -45,8 +45,8 @@ Usage: %s FILE\n\
or: %s OPTION\n"), program_name, program_name);
fputs (_("Call the unlink function to remove the specified FILE.\n\n"),
stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -65,7 +65,7 @@ main (int argc, char **argv)
parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE_NAME,
Version, true, usage, AUTHORS,
- (char const *) nullptr);
+ (char const *) NULL);
if (argc < optind + 1)
{
diff --git a/src/uptime.c b/src/uptime.c
index 0faaf0662..4de00a8cb 100644
--- a/src/uptime.c
+++ b/src/uptime.c
@@ -1,5 +1,5 @@
/* GNU's uptime.
- Copyright (C) 1992-2025 Free Software Foundation, Inc.
+ Copyright (C) 1992-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -60,13 +60,13 @@ print_uptime (idx_t n, STRUCT_UTMP const *utmp_buf)
status = EXIT_FAILURE;
}
- time_t time_now = time (nullptr);
- struct tm *tmn = time_now == (time_t) -1 ? nullptr : localtime (&time_now);
+ time_t time_now = time (NULL);
+ struct tm *tmn = time_now == (time_t) -1 ? NULL : localtime (&time_now);
/* procps' version of uptime also prints the seconds field, but
previous versions of coreutils don't. */
if (tmn)
/* TRANSLATORS: This prints the current clock time. */
- fprintftime (stdout, _(" %H:%M:%S "), tmn, 0, 0);
+ fprintftime (stdout, _(" %H:%M:%S "), tmn, (timezone_t) 0, 0);
else
{
printf (_(" ??:???? "));
@@ -132,7 +132,7 @@ uptime (char const *filename, int options)
{
error (0, errno, "%s", quotef (filename));
n_users = 0;
- utmp_buf = nullptr;
+ utmp_buf = NULL;
}
int print_uptime_status = print_uptime (n_users, utmp_buf);
@@ -166,8 +166,8 @@ an uninterruptible sleep state also contribute to the load average.\n"));
If FILE is not specified, use %s. %s as FILE is common.\n\
\n"),
UTMP_FILE, WTMP_FILE);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -186,7 +186,7 @@ main (int argc, char **argv)
parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE_NAME,
Version, true, usage, AUTHORS,
- (char const *) nullptr);
+ (char const *) NULL);
switch (argc - optind)
{
diff --git a/src/users.c b/src/users.c
index 5e298acd5..b563f3742 100644
--- a/src/users.c
+++ b/src/users.c
@@ -1,5 +1,5 @@
/* GNU's users.
- Copyright (C) 1992-2025 Free Software Foundation, Inc.
+ Copyright (C) 1992-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -45,7 +45,6 @@ static void
list_entries_users (idx_t n, STRUCT_UTMP const *this)
{
char **u = xinmalloc (n, sizeof *u);
- idx_t i;
idx_t n_entries = 0;
while (n--)
@@ -64,14 +63,14 @@ list_entries_users (idx_t n, STRUCT_UTMP const *this)
qsort (u, n_entries, sizeof (u[0]), userid_compare);
- for (i = 0; i < n_entries; i++)
+ for (idx_t i = 0; i < n_entries; i++)
{
char c = (i < n_entries - 1 ? ' ' : '\n');
fputs (u[i], stdout);
putchar (c);
}
- for (i = 0; i < n_entries; i++)
+ for (idx_t i = 0; i < n_entries; i++)
free (u[i]);
free (u);
}
@@ -107,8 +106,8 @@ If FILE is not specified, use %s. %s as FILE is common.\n\
\n\
"),
UTMP_FILE, WTMP_FILE);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -127,7 +126,7 @@ main (int argc, char **argv)
parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE_NAME,
Version, true, usage, AUTHORS,
- (char const *) nullptr);
+ (char const *) NULL);
switch (argc - optind)
{
diff --git a/src/wc.c b/src/wc.c
index 243399393..2e2a9b03c 100644
--- a/src/wc.c
+++ b/src/wc.c
@@ -1,5 +1,5 @@
/* wc - print the number of lines, words, and bytes in files
- Copyright (C) 1985-2025 Free Software Foundation, Inc.
+ Copyright (C) 1985-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,7 +19,6 @@
#include <config.h>
-#include <ctype.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
@@ -32,6 +31,11 @@
#include <stat-size.h>
#include <xbinary-io.h>
+#ifdef USE_NEON_WC_LINECOUNT
+# include <sys/auxv.h>
+# include <asm/hwcap.h>
+#endif
+
#include "system.h"
#include "cpu-supports.h"
#include "ioblksize.h"
@@ -99,17 +103,17 @@ enum
static struct option const longopts[] =
{
- {"bytes", no_argument, nullptr, 'c'},
- {"chars", no_argument, nullptr, 'm'},
- {"lines", no_argument, nullptr, 'l'},
- {"words", no_argument, nullptr, 'w'},
- {"debug", no_argument, nullptr, DEBUG_PROGRAM_OPTION},
- {"files0-from", required_argument, nullptr, FILES0_FROM_OPTION},
- {"max-line-length", no_argument, nullptr, 'L'},
- {"total", required_argument, nullptr, TOTAL_OPTION},
+ {"bytes", no_argument, NULL, 'c'},
+ {"chars", no_argument, NULL, 'm'},
+ {"lines", no_argument, NULL, 'l'},
+ {"words", no_argument, NULL, 'w'},
+ {"debug", no_argument, NULL, DEBUG_PROGRAM_OPTION},
+ {"files0-from", required_argument, NULL, FILES0_FROM_OPTION},
+ {"max-line-length", no_argument, NULL, 'L'},
+ {"total", required_argument, NULL, TOTAL_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
enum total_type
@@ -121,7 +125,7 @@ enum total_type
};
static char const *const total_args[] =
{
- "auto", "always", "only", "never", nullptr
+ "auto", "always", "only", "never", NULL
};
static enum total_type const total_types[] =
{
@@ -160,6 +164,21 @@ avx512_supported (void)
}
#endif
+#ifdef USE_NEON_WC_LINECOUNT
+static bool
+neon_supported (void)
+{
+ bool neon_enabled = (cpu_may_support ("asimd")
+ && 0 < (getauxval (AT_HWCAP) & HWCAP_ASIMD));
+ if (debug)
+ error (0, 0, (neon_enabled
+ ? _("using neon hardware support")
+ : _("neon support not detected")));
+
+ return neon_enabled;
+}
+#endif
+
void
usage (int status)
{
@@ -184,23 +203,44 @@ space delimited by white space characters or by start or end of input.\n\
\n\
The options below may be used to select which counts are printed, always in\n\
the following order: newline, word, character, byte, maximum line length.\n\
- -c, --bytes print the byte counts\n\
- -m, --chars print the character counts\n\
- -l, --lines print the newline counts\n\
-"), stdout);
- fputs (_("\
- --files0-from=F read input from the files specified by\n\
- NUL-terminated names in file F;\n\
- If F is - then read names from standard input\n\
- -L, --max-line-length print the maximum display width\n\
- -w, --words print the word counts\n\
-"), stdout);
- fputs (_("\
- --total=WHEN when to print a line with total counts;\n\
- WHEN can be: auto, always, only, never\n\
"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (_("\
+ -c, --bytes\n\
+ print the byte counts\n\
+"));
+ oputs (_("\
+ -m, --chars\n\
+ print the character counts\n\
+"));
+ oputs (_("\
+ -l, --lines\n\
+ print the newline counts\n\
+"));
+ oputs (_("\
+ --debug\n\
+ indicate what line count acceleration is used\n\
+"));
+ oputs (_("\
+ --files0-from=F\n\
+ read input from the files specified by\n\
+ NUL-terminated names in file F;\n\
+ If F is -, read names from standard input\n\
+"));
+ oputs (_("\
+ -L, --max-line-length\n\
+ print the maximum display width\n\
+"));
+ oputs (_("\
+ -w, --words\n\
+ print the word counts\n\
+"));
+ oputs (_("\
+ --total=WHEN\n\
+ when to print a line with total counts;\n\
+ WHEN can be: auto, always, only, never\n\
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -255,6 +295,9 @@ write_counts (uintmax_t lines,
if (file)
printf (" %s", strchr (file, '\n') ? quotef (file) : file);
putchar ('\n');
+
+ if (ferror (stdout))
+ write_error ();
}
/* Read FD and return a summary. */
@@ -275,6 +318,13 @@ wc_lines (int fd)
if (0 < use_avx2)
return wc_lines_avx2 (fd);
#endif
+#ifdef USE_NEON_WC_LINECOUNT
+ static signed char use_neon;
+ if (!use_neon)
+ use_neon = neon_supported () ? 1 : -1;
+ if (0 < use_neon)
+ return wc_lines_neon (fd);
+#endif
intmax_t lines = 0, bytes = 0;
bool long_lines = false;
@@ -744,7 +794,7 @@ main (int argc, char **argv)
int optc;
idx_t nfiles;
char **files;
- char *files_from = nullptr;
+ char *files_from = NULL;
struct fstatus *fstatus;
struct Tokens tok;
@@ -759,15 +809,11 @@ main (int argc, char **argv)
page_size = getpagesize ();
/* Line buffer stdout to ensure lines are written atomically and immediately
so that processes running in parallel do not intersperse their output. */
- setvbuf (stdout, nullptr, _IOLBF, 0);
-
- posixly_correct = (getenv ("POSIXLY_CORRECT") != nullptr);
+ setvbuf (stdout, NULL, _IOLBF, 0);
- print_lines = print_words = print_chars = print_bytes = false;
- print_linelength = false;
- total_lines = total_words = total_chars = total_bytes = max_line_length = 0;
+ posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
- while ((optc = getopt_long (argc, argv, "clLmw", longopts, nullptr)) != -1)
+ while ((optc = getopt_long (argc, argv, "clLmw", longopts, NULL)) != -1)
switch (optc)
{
case 'c':
@@ -842,7 +888,7 @@ main (int argc, char **argv)
else
{
stream = fopen (files_from, "r");
- if (stream == nullptr)
+ if (stream == NULL)
error (EXIT_FAILURE, errno, _("cannot open %s for reading"),
quoteaf (files_from));
}
@@ -865,14 +911,14 @@ main (int argc, char **argv)
}
else
{
- files = nullptr;
+ files = NULL;
nfiles = 0;
ai = argv_iter_init_stream (stream);
}
}
else
{
- static char *stdin_only[] = { nullptr };
+ static char *stdin_only[] = { NULL };
files = (optind < argc ? argv + optind : stdin_only);
nfiles = (optind < argc ? argc - optind : 1);
ai = argv_iter_init_argv (files);
@@ -909,7 +955,7 @@ main (int argc, char **argv)
among many, knowing the record number may help.
FIXME: currently print the record number only with
--files0-from=FILE. Maybe do it for argv, too? */
- if (files_from == nullptr)
+ if (files_from == NULL)
error (0, 0, "%s", _("invalid zero-length file name"));
else
{
@@ -951,7 +997,7 @@ main (int argc, char **argv)
However, no arguments on the --files0-from input stream is an error
means don't read anything. */
if (ok && !files_from && argv_iter_n_args (ai) == 0)
- ok &= wc_file (nullptr, &fstatus[0]);
+ ok &= wc_file (NULL, &fstatus[0]);
if (read_tokens)
readtokens0_free (&tok);
@@ -986,7 +1032,7 @@ main (int argc, char **argv)
write_counts (total_lines, total_words, total_chars, total_bytes,
max_line_length,
- total_mode != total_only ? _("total") : nullptr);
+ total_mode != total_only ? _("total") : NULL);
}
argv_iter_free (ai);
diff --git a/src/wc.h b/src/wc.h
index f151e92f2..fa0aef990 100644
--- a/src/wc.h
+++ b/src/wc.h
@@ -2,3 +2,4 @@
struct wc_lines { int err; intmax_t lines; intmax_t bytes; };
struct wc_lines wc_lines_avx2 (int);
struct wc_lines wc_lines_avx512 (int);
+struct wc_lines wc_lines_neon (int fd);
diff --git a/src/wc_avx2.c b/src/wc_avx2.c
index d75e5eac4..0cfe53f0d 100644
--- a/src/wc_avx2.c
+++ b/src/wc_avx2.c
@@ -1,5 +1,5 @@
/* wc_avx - Count the number of newlines with avx2 instructions.
- Copyright (C) 2021-2025 Free Software Foundation, Inc.
+ Copyright (C) 2021-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/wc_avx512.c b/src/wc_avx512.c
index 41faea646..0f27fa1c1 100644
--- a/src/wc_avx512.c
+++ b/src/wc_avx512.c
@@ -1,5 +1,5 @@
/* wc_avx512 - Count the number of newlines with avx512 instructions.
- Copyright (C) 2021-2025 Free Software Foundation, Inc.
+ Copyright (C) 2021-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/src/wc_neon.c b/src/wc_neon.c
new file mode 100644
index 000000000..571f5051c
--- /dev/null
+++ b/src/wc_neon.c
@@ -0,0 +1,109 @@
+/* wc_neon - Count the number of newlines with neon instructions.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* Written by Collin Funk <collin.funk1@gmail.com>, 2026. */
+
+#include <config.h>
+
+#include "wc.h"
+#include "system.h"
+#include "ioblksize.h"
+
+#include <arm_neon.h>
+
+/* Read FD and return a summary. */
+extern struct wc_lines
+wc_lines_neon (int fd)
+{
+ intmax_t lines = 0;
+ intmax_t bytes = 0;
+
+ const uint8x16_t endlines = vdupq_n_u8 ('\n');
+
+ while (true)
+ {
+ unsigned char alignas (16) neon_buf[IO_BUFSIZE];
+ ssize_t bytes_read = read (fd, neon_buf, sizeof neon_buf);
+ if (bytes_read <= 0)
+ return (struct wc_lines) { bytes_read == 0 ? 0 : errno, lines, bytes };
+
+ bytes += bytes_read;
+ unsigned char *datap = neon_buf;
+
+ while (8192 <= bytes_read)
+ {
+ /* Accumulator. */
+ int8x16_t acc0 = vdupq_n_s8 (0);
+ int8x16_t acc1 = vdupq_n_s8 (0);
+ int8x16_t acc2 = vdupq_n_s8 (0);
+ int8x16_t acc3 = vdupq_n_s8 (0);
+
+ /* Process all 8192 bytes in 64 byte chunks. */
+ for (int i = 0; i < 128; ++i)
+ {
+ /* Load 64 bytes from DATAP. */
+ uint8x16_t v0 = vld1q_u8 (datap);
+ uint8x16_t v1 = vld1q_u8 (datap + 16);
+ uint8x16_t v2 = vld1q_u8 (datap + 32);
+ uint8x16_t v3 = vld1q_u8 (datap + 48);
+
+ /* Bitwise equal with ENDLINES. We use a reinterpret cast to
+ convert the 0xff if a newline is found into -1. */
+ int8x16_t c0 = vreinterpretq_s8_u8 (vceqq_u8 (v0, endlines));
+ int8x16_t c1 = vreinterpretq_s8_u8 (vceqq_u8 (v1, endlines));
+ int8x16_t c2 = vreinterpretq_s8_u8 (vceqq_u8 (v2, endlines));
+ int8x16_t c3 = vreinterpretq_s8_u8 (vceqq_u8 (v3, endlines));
+
+ /* Increment the accumulator. */
+ acc0 = vaddq_s8 (acc0, c0);
+ acc1 = vaddq_s8 (acc1, c1);
+ acc2 = vaddq_s8 (acc2, c2);
+ acc3 = vaddq_s8 (acc3, c3);
+
+ datap += 64;
+ }
+
+ /* Pairwise sum the vectors. */
+ int16x8_t a0 = vpaddlq_s8 (acc0);
+ int16x8_t a1 = vpaddlq_s8 (acc1);
+ int16x8_t a2 = vpaddlq_s8 (acc2);
+ int16x8_t a3 = vpaddlq_s8 (acc3);
+ int32x4_t b0 = vpaddlq_s16 (a0);
+ int32x4_t b1 = vpaddlq_s16 (a1);
+ int32x4_t b2 = vpaddlq_s16 (a2);
+ int32x4_t b3 = vpaddlq_s16 (a3);
+ int64x2_t c0 = vpaddlq_s32 (b0);
+ int64x2_t c1 = vpaddlq_s32 (b1);
+ int64x2_t c2 = vpaddlq_s32 (b2);
+ int64x2_t c3 = vpaddlq_s32 (b3);
+
+ /* Extract the lane sums. Since each newline was counted as -1, we
+ subtract the sum of them from LINES to get the total number of
+ lines. */
+ lines -= (vgetq_lane_s64 (c0, 0) + vgetq_lane_s64 (c0, 1)
+ + vgetq_lane_s64 (c1, 0) + vgetq_lane_s64 (c1, 1)
+ + vgetq_lane_s64 (c2, 0) + vgetq_lane_s64 (c2, 1)
+ + vgetq_lane_s64 (c3, 0) + vgetq_lane_s64 (c3, 1));
+
+ bytes_read -= 8192;
+ }
+
+ /* Finish up any left over bytes. */
+ unsigned char *end = (unsigned char *) datap + bytes_read;
+ for (unsigned char *p = (unsigned char *) datap; p < end; p++)
+ lines += *p == '\n';
+ }
+}
diff --git a/src/who.c b/src/who.c
index c4205a554..5b35b8bd7 100644
--- a/src/who.c
+++ b/src/who.c
@@ -1,5 +1,5 @@
/* GNU's who.
- Copyright (C) 1992-2025 Free Software Foundation, Inc.
+ Copyright (C) 1992-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -35,6 +35,7 @@
#include "readutmp.h"
#include "hard-locale.h"
#include "quote.h"
+#include "xvasprintf.h"
#ifdef TTY_GROUP_NAME
# include <grp.h>
@@ -152,24 +153,24 @@ enum
static struct option const longopts[] =
{
- {"all", no_argument, nullptr, 'a'},
- {"boot", no_argument, nullptr, 'b'},
- {"count", no_argument, nullptr, 'q'},
- {"dead", no_argument, nullptr, 'd'},
- {"heading", no_argument, nullptr, 'H'},
- {"login", no_argument, nullptr, 'l'},
- {"lookup", no_argument, nullptr, LOOKUP_OPTION},
- {"message", no_argument, nullptr, 'T'},
- {"mesg", no_argument, nullptr, 'T'},
- {"process", no_argument, nullptr, 'p'},
- {"runlevel", no_argument, nullptr, 'r'},
- {"short", no_argument, nullptr, 's'},
- {"time", no_argument, nullptr, 't'},
- {"users", no_argument, nullptr, 'u'},
- {"writable", no_argument, nullptr, 'T'},
+ {"all", no_argument, NULL, 'a'},
+ {"boot", no_argument, NULL, 'b'},
+ {"count", no_argument, NULL, 'q'},
+ {"dead", no_argument, NULL, 'd'},
+ {"heading", no_argument, NULL, 'H'},
+ {"login", no_argument, NULL, 'l'},
+ {"lookup", no_argument, NULL, LOOKUP_OPTION},
+ {"message", no_argument, NULL, 'T'},
+ {"mesg", no_argument, NULL, 'T'},
+ {"process", no_argument, NULL, 'p'},
+ {"runlevel", no_argument, NULL, 'r'},
+ {"short", no_argument, NULL, 's'},
+ {"time", no_argument, NULL, 't'},
+ {"users", no_argument, NULL, 'u'},
+ {"writable", no_argument, NULL, 'T'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
- {nullptr, 0, nullptr, 0}
+ {NULL, 0, NULL, 0}
};
/* Return a string representing the time between WHEN and now.
@@ -233,7 +234,6 @@ print_line (char const *user, const char state,
char x_idle[1 + IDLESTR_LEN + 1];
char x_pid[1 + INT_STRLEN_BOUND (pid_t) + 1];
char *x_exitstr;
- int err;
mesg[1] = state;
@@ -253,32 +253,29 @@ print_line (char const *user, const char state,
else
*x_exitstr = '\0';
- err = asprintf (&buf,
- "%-8s"
- "%s"
- " %-12s"
- " %-*s"
- "%s"
- "%s"
- " %-8s"
- "%s"
- ,
- user ? user : " .",
- include_mesg ? mesg : "",
- line,
- time_format_width,
- time_str,
- x_idle,
- x_pid,
- /* FIXME: it's not really clear whether the following
- field should be in the short_output. A strict reading
- of SUSv2 would suggest not, but I haven't seen any
- implementations that actually work that way... */
- comment,
- x_exitstr
+ buf = xasprintf ("%-8s"
+ "%s"
+ " %-12s"
+ " %-*s"
+ "%s"
+ "%s"
+ " %-8s"
+ "%s"
+ ,
+ user ? user : " .",
+ include_mesg ? mesg : "",
+ line,
+ time_format_width,
+ time_str,
+ x_idle,
+ x_pid,
+ /* FIXME: it's not really clear whether the following
+ field should be in the short_output. A strict reading
+ of SUSv2 would suggest not, but I haven't seen any
+ implementations that actually work that way... */
+ comment,
+ x_exitstr
);
- if (err == -1)
- xalloc_die ();
{
/* Remove any trailing spaces. */
@@ -363,8 +360,8 @@ print_user (STRUCT_UTMP const *utmp_ent, time_t boottime)
#if HAVE_STRUCT_XTMP_UT_HOST
if (utmp_ent->ut_host[0])
{
- char *host = nullptr;
- char *display = nullptr;
+ char *host = NULL;
+ char *display = NULL;
char *ut_host = utmp_ent->ut_host;
/* Look for an X display. */
@@ -387,7 +384,7 @@ print_user (STRUCT_UTMP const *utmp_ent, time_t boottime)
if (hostlen < needed)
{
free (hoststr);
- hoststr = xpalloc (nullptr, &hostlen, needed - hostlen, -1, 1);
+ hoststr = xpalloc (NULL, &hostlen, needed - hostlen, -1, 1);
}
char *p = hoststr;
*p++ = '(';
@@ -401,7 +398,7 @@ print_user (STRUCT_UTMP const *utmp_ent, time_t boottime)
if (hostlen < needed)
{
free (hoststr);
- hoststr = xpalloc (nullptr, &hostlen, needed - hostlen, -1, 1);
+ hoststr = xpalloc (NULL, &hostlen, needed - hostlen, -1, 1);
}
char *p = hoststr;
*p++ = '(';
@@ -518,8 +515,6 @@ print_runlevel (STRUCT_UTMP const *utmp_ent)
print_line ("", ' ', runlevline, time_string (utmp_ent),
"", "", c_isprint (last) ? comment : "", "");
-
- return;
}
/* Print the username of each valid entry and the number of valid entries
@@ -528,7 +523,6 @@ static void
list_entries_who (idx_t n, STRUCT_UTMP const *utmp_buf)
{
idx_t entries = 0;
- char const *separator = "";
while (n--)
{
@@ -538,9 +532,10 @@ list_entries_who (idx_t n, STRUCT_UTMP const *utmp_buf)
trimmed_name = extract_trimmed_name (utmp_buf);
- printf ("%s%s", separator, trimmed_name);
+ if (entries)
+ putchar (' ');
+ fputs (trimmed_name, stdout);
free (trimmed_name);
- separator = " ";
entries++;
}
utmp_buf++;
@@ -559,7 +554,7 @@ print_heading (void)
static void
scan_entries (idx_t n, STRUCT_UTMP const *utmp_buf)
{
- char *ttyname_b IF_LINT ( = nullptr);
+ char *ttyname_b IF_LINT ( = NULL);
time_t boottime = TYPE_MINIMUM (time_t);
if (include_heading)
@@ -638,33 +633,57 @@ Print information about users who are currently logged in.\n\
"), stdout);
fputs (_("\
\n\
+"), stdout);
+ oputs (_("\
-a, --all same as -b -d --login -p -r -t -T -u\n\
+"));
+ oputs (_("\
-b, --boot time of last system boot\n\
+"));
+ oputs (_("\
-d, --dead print dead processes\n\
+"));
+ oputs (_("\
-H, --heading print line of column headings\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-l, --login print system login processes\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
--lookup attempt to canonicalize hostnames via DNS\n\
+"));
+ oputs (_("\
-m only hostname and user associated with standard input\n\
+"));
+ oputs (_("\
-p, --process print active processes spawned by init\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-q, --count all login names and number of users logged on\n\
+"));
+ oputs (_("\
-r, --runlevel print current runlevel\n\
+"));
+ oputs (_("\
-s, --short print only name, line, and time (default)\n\
+"));
+ oputs (_("\
-t, --time print last system clock change\n\
-"), stdout);
- fputs (_("\
+"));
+ oputs (_("\
-T, -w, --mesg add user's message status as +, - or ?\n\
- -u, --users list users logged in\n\
+"));
+ oputs (_("\
+ -u, --users list users logged in, including idle time\n\
+"));
+ oputs (_("\
--message same as -T\n\
+"));
+ oputs (_("\
--writable same as -T\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+"));
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
printf (_("\
\n\
If FILE is not specified, use %s. %s as FILE is common.\n\
@@ -689,7 +708,7 @@ main (int argc, char **argv)
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "abdlmpqrstuwHT", longopts, nullptr))
+ while ((optc = getopt_long (argc, argv, "abdlmpqrstuwHT", longopts, NULL))
!= -1)
{
switch (optc)
diff --git a/src/whoami.c b/src/whoami.c
index 9313eaf3e..fe690fb3a 100644
--- a/src/whoami.c
+++ b/src/whoami.c
@@ -1,6 +1,6 @@
/* whoami -- print effective userid
- Copyright (C) 1989-2025 Free Software Foundation, Inc.
+ Copyright (C) 1989-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -45,8 +45,8 @@ Print the user name associated with the current effective user ID.\n\
Same as id -un.\n\
\n\
"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -55,8 +55,6 @@ Same as id -un.\n\
int
main (int argc, char **argv)
{
- struct passwd *pw;
- uid_t uid;
uid_t NO_UID = -1;
initialize_main (&argc, &argv);
@@ -69,7 +67,7 @@ main (int argc, char **argv)
parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE_NAME,
Version, true, usage, AUTHORS,
- (char const *) nullptr);
+ (char const *) NULL);
if (optind != argc)
{
@@ -78,8 +76,8 @@ main (int argc, char **argv)
}
errno = 0;
- uid = geteuid ();
- pw = uid == NO_UID && errno ? nullptr : getpwuid (uid);
+ uid_t uid = geteuid ();
+ struct passwd *pw = uid == NO_UID && errno ? NULL : getpwuid (uid);
if (!pw)
error (EXIT_FAILURE, errno, _("cannot find name for user ID %ju"),
(uintmax_t) uid);
diff --git a/src/yes.c b/src/yes.c
index ceb7ce447..8774d73f3 100644
--- a/src/yes.c
+++ b/src/yes.c
@@ -1,5 +1,5 @@
/* yes - output a string repeatedly until killed
- Copyright (C) 1991-2025 Free Software Foundation, Inc.
+ Copyright (C) 1991-2026 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,11 +19,16 @@
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
+#include <sys/uio.h>
#include "system.h"
+#include "alignalloc.h"
#include "full-write.h"
+#include "isapipe.h"
#include "long-options.h"
+#include "splice.h"
+#include "unistd--.h"
/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "yes"
@@ -47,13 +52,140 @@ Usage: %s [STRING]...\n\
Repeatedly output a line with all specified STRING(s), or 'y'.\n\
\n\
"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ oputs (HELP_OPTION_DESCRIPTION);
+ oputs (VERSION_OPTION_DESCRIPTION);
emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
+/* Fill DEST[0..BUFSIZE-1] with repeated copies of SRC[0..SRCSIZE-1],
+ doubling the copy size each iteration. DEST may equal SRC. */
+
+static void
+repeat_pattern (char *dest, char const *src, idx_t srcsize, idx_t bufsize)
+{
+ if (dest != src)
+ memcpy (dest, src, srcsize);
+ for (idx_t filled = srcsize; filled < bufsize; )
+ {
+ idx_t chunk = MIN (filled, bufsize - filled);
+ memcpy (dest + filled, dest, chunk);
+ filled += chunk;
+ }
+}
+
+#if HAVE_SPLICE
+
+/* Enlarge a pipe towards SPLICE_PIPE_SIZE and return the actual
+ capacity as a quarter of the pipe size (the empirical sweet spot
+ for vmsplice throughput), rounded down to a multiple of COPYSIZE.
+ Return 0 if the result would be smaller than COPYSIZE. */
+
+static idx_t
+pipe_splice_size (int fd, idx_t copysize)
+{
+ size_t buf_cap = increase_pipe_size (fd) / 4;
+ return buf_cap / copysize * copysize;
+}
+
+#endif
+
+/* Repeatedly write the COPYSIZE-byte pattern in BUF to standard output
+ using vmsplice/splice zero-copy I/O. Since the data never varies,
+ SPLICE_F_GIFT tells the kernel the pages will not be modified.
+
+ Return TRUE if splice I/O was used (caller should check errno and
+ report any error). Return FALSE if splice could not be used. */
+
+static bool
+splice_write (MAYBE_UNUSED char const *buf, MAYBE_UNUSED idx_t copysize)
+{
+ bool output_started = false;
+#if HAVE_SPLICE
+ idx_t page_size = getpagesize ();
+
+ bool stdout_is_pipe = isapipe (STDOUT_FILENO) > 0;
+
+ /* Determine buffer size: enlarge the target pipe,
+ then use 1/4 of actual capacity as the transfer size. */
+ int pipefd[2] = { -1, -1 };
+ idx_t splice_bufsize;
+ char *splice_buf = NULL;
+
+ if (stdout_is_pipe)
+ splice_bufsize = pipe_splice_size (STDOUT_FILENO, copysize);
+ else
+ {
+ if (pipe2 (pipefd, 0) < 0)
+ return false;
+ splice_bufsize = pipe_splice_size (pipefd[0], copysize);
+ }
+
+ if (splice_bufsize == 0)
+ goto done;
+
+ /* Allocate page-aligned buffer for vmsplice.
+ Needed with SPLICE_F_GIFT, but generally good for performance. */
+ if (! (splice_buf = alignalloc (page_size, splice_bufsize)))
+ goto done;
+
+ repeat_pattern (splice_buf, buf, copysize, splice_bufsize);
+
+ /* For the pipe case, vmsplice directly to stdout.
+ For the non-pipe case, vmsplice into the intermediate pipe
+ and then splice from it to stdout. */
+ int vmsplice_fd = stdout_is_pipe ? STDOUT_FILENO : pipefd[1];
+
+ for (;;)
+ {
+ struct iovec iov = { .iov_base = splice_buf,
+ .iov_len = splice_bufsize };
+
+ while (iov.iov_len > 0)
+ {
+ /* Use SPLICE_F_{GIFT,MOVE} to allow the kernel to take references
+ to the pages. I.e., we're indicating we won't make changes.
+ SPLICE_F_GIFT is only appropriate for full pages. */
+ unsigned int flags = iov.iov_len % page_size ? 0 : SPLICE_F_GIFT;
+ ssize_t n = vmsplice (vmsplice_fd, &iov, 1, flags);
+ if (n <= 0)
+ goto done;
+
+ if (stdout_is_pipe)
+ output_started = true;
+ else
+ {
+ idx_t remaining = n;
+ while (remaining > 0)
+ {
+ ssize_t s = splice (pipefd[0], NULL, STDOUT_FILENO, NULL,
+ remaining, SPLICE_F_MOVE);
+ if (s <= 0)
+ goto done;
+ output_started = true;
+ remaining -= s;
+ }
+ }
+
+ iov.iov_base = (char *) iov.iov_base + n;
+ iov.iov_len -= n;
+ }
+ }
+
+done:
+ if (pipefd[0] >= 0)
+ {
+ int saved_errno = errno;
+ close (pipefd[0]);
+ close (pipefd[1]);
+ errno = saved_errno;
+ }
+ alignfree (splice_buf);
+#endif
+ return output_started;
+}
+
int
main (int argc, char **argv)
{
@@ -67,7 +199,7 @@ main (int argc, char **argv)
parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE_NAME,
Version, true, usage, AUTHORS,
- (char const *) nullptr);
+ (char const *) NULL);
char **operands = argv + optind;
char **operand_lim = argv + argc;
@@ -117,18 +249,22 @@ main (int argc, char **argv)
while (++operandp < operand_lim);
buf[bufused - 1] = '\n';
- /* If a larger buffer was allocated, fill it by repeating the buffer
- contents. */
- size_t copysize = bufused;
- for (size_t copies = bufalloc / copysize; --copies; )
+ idx_t copysize = bufused;
+
+ /* Repeatedly output the buffer until there is a write error; then fail.
+ Do a minimal write first to check output with minimal set up cost.
+ If successful then set up for efficient repetition. */
+ if (full_write (STDOUT_FILENO, buf, copysize) == copysize
+ && splice_write (buf, copysize) == 0)
{
- memcpy (buf + bufused, buf, copysize);
- bufused += copysize;
+ /* If a larger buffer was allocated, fill it by repeated copies. */
+ bufused = bufalloc / copysize * copysize;
+ if (bufused > copysize)
+ repeat_pattern (buf, buf, copysize, bufused);
+ while (full_write (STDOUT_FILENO, buf, bufused) == bufused)
+ continue;
}
- /* Repeatedly output the buffer until there is a write error; then fail. */
- while (full_write (STDOUT_FILENO, buf, bufused) == bufused)
- continue;
error (0, errno, _("standard output"));
main_exit (EXIT_FAILURE);
}
diff --git a/tests/Coreutils.pm b/tests/Coreutils.pm
index b55fb9d0a..e396d93a4 100644
--- a/tests/Coreutils.pm
+++ b/tests/Coreutils.pm
@@ -1,7 +1,7 @@
package Coreutils;
# This is a testing framework.
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@ use vars qw($VERSION @ISA @EXPORT);
use FileHandle;
use File::Compare qw(compare);
+use Text::ParseWords qw(shellwords);
@ISA = qw(Exporter);
($VERSION = '$Revision: 1.5 $ ') =~ tr/[0-9].//cd;
@@ -79,8 +80,6 @@ defined $ENV{TERM}
# {ERR_SUBST => 's/variable_output/expected_output/'}
# Transform actual stderr output before comparing it against expected.
# This is useful when verifying that we get a meaningful diagnostic.
-# For example, in rm/fail-2eperm, we have to account for three different
-# diagnostics: Operation not permitted, Not owner, and Permission denied.
# {EXIT => N} expect exit status of cmd to be N
# {ENV => 'VAR=val ...'}
# Prepend 'VAR=val ...' to the command that we execute via 'system'.
@@ -213,7 +212,12 @@ sub getlimits()
{
my $NV;
open $NV, "getlimits |" or die "Error running getlimits\n";
- my %limits = map {split /=|\n/} <$NV>;
+ my %limits = map {
+ chomp;
+ my ($k, $v) = split /=/, $_, 2;
+ $v = (shellwords($v))[0] if defined $v;
+ ($k, $v)
+ } <$NV>;
return \%limits;
}
diff --git a/tests/CuSkip.pm b/tests/CuSkip.pm
index fb5a380ec..5fe7b3217 100644
--- a/tests/CuSkip.pm
+++ b/tests/CuSkip.pm
@@ -1,7 +1,7 @@
package CuSkip;
# Skip a test: emit diag to log and to stderr, and exit 77
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/CuTmpdir.pm b/tests/CuTmpdir.pm
index f03a97a0f..32578fde4 100644
--- a/tests/CuTmpdir.pm
+++ b/tests/CuTmpdir.pm
@@ -1,7 +1,7 @@
package CuTmpdir;
# create, then chdir into a temporary sub-directory
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/basenc/base64.pl b/tests/basenc/base64.pl
index 9ba24cf47..6412b4fc0 100755
--- a/tests/basenc/base64.pl
+++ b/tests/basenc/base64.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Exercise base{32,64}.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/basenc/basenc.pl b/tests/basenc/basenc.pl
index 4cda3bd14..3749e8720 100755
--- a/tests/basenc/basenc.pl
+++ b/tests/basenc/basenc.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Exercise basenc.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/basenc/bounded-memory.sh b/tests/basenc/bounded-memory.sh
index 55e7cc310..a34f7d3e3 100755
--- a/tests/basenc/bounded-memory.sh
+++ b/tests/basenc/bounded-memory.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test that basenc can work on files larger than the systems memory.
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,17 +19,15 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ basenc
-vm=$(get_min_ulimit_v_ basenc --base64 /dev/null) ||
+vm=$(get_min_ulimit_v_ timeout 10 basenc --base64 /dev/null) ||
skip_ 'failed to determine memory limit'
# Check all except for --base58.
for algorithm in '--base64' '--base64url' '--base32' '--base32hex' '--base16' \
'--base2msbf' '--base2lsbf' '--z85'; do
- timeout 0.5 $SHELL -c \
- "(ulimit -v $(($vm+6000)) \
- && basenc $algorithm </dev/zero >/dev/null 2>err)"
+ (ulimit -v $(($vm+6000)) \
+ && timeout 0.5 basenc $algorithm </dev/zero >/dev/null 2>err)
ret=$?
- test -f err || skip_ 'shell ulimit failure'
test $ret = 124 || {
fail=1
cat err
diff --git a/tests/basenc/large-input.sh b/tests/basenc/large-input.sh
index e04db69d8..d90f8bf67 100755
--- a/tests/basenc/large-input.sh
+++ b/tests/basenc/large-input.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test large inputs to basenc
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cat/cat-E.sh b/tests/cat/cat-E.sh
index 6e52a8fd0..91d8fda8f 100755
--- a/tests/cat/cat-E.sh
+++ b/tests/cat/cat-E.sh
@@ -1,5 +1,5 @@
#!/bin/sh
-# Copyright (C) 2021-2025 Free Software Foundation, Inc.
+# Copyright (C) 2021-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cat/cat-buf.sh b/tests/cat/cat-buf.sh
index 7d7cd05d6..7ffebd0fc 100755
--- a/tests/cat/cat-buf.sh
+++ b/tests/cat/cat-buf.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that cat outputs processed data immediately.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cat/cat-proc.sh b/tests/cat/cat-proc.sh
index acedc799b..05c7bcc8e 100755
--- a/tests/cat/cat-proc.sh
+++ b/tests/cat/cat-proc.sh
@@ -2,7 +2,7 @@
# Ensure that cat -E produces same output as cat, modulo '$'s,
# even when applied to a file in /proc.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cat/cat-self.sh b/tests/cat/cat-self.sh
index 2d09fc087..6c77ff460 100755
--- a/tests/cat/cat-self.sh
+++ b/tests/cat/cat-self.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Check that cat operates correctly when the input is the same as the output.
-# Copyright 2014-2025 Free Software Foundation, Inc.
+# Copyright 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cat/splice.sh b/tests/cat/splice.sh
new file mode 100755
index 000000000..55b96711b
--- /dev/null
+++ b/tests/cat/splice.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+# Test some cases where 'cat' uses the splice system call.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cat
+getlimits_
+uses_strace_
+
+# Check the non pipe output case, since that is different with splice
+if timeout 10 true; then
+ timeout .1 cat /dev/zero >/dev/null
+ test $? = 124 || fail=1
+fi
+
+# Verify splice is called multiple times,
+# and doesn't just fall back after the first EINVAL.
+# This also implicitly handles systems without splice at all.
+strace -o splice_count -e splice cat /dev/zero | head -c1M >/dev/null
+splice_count=$(grep '^splice' splice_count | wc -l)
+
+# Test that splice errors are diagnosed.
+# Odd numbers are for input, even for output
+if test "$splice_count" -gt 1 &&
+ strace -o /dev/null -e inject=splice:error=EIO:when=3 true; then
+ for when in 3 4; do
+ test "$when" = 4 && efile='write error' || efile='/dev/zero'
+ printf 'cat: %s: %s\n' "$efile" "$EIO" > exp || framework_failure_
+ returns_ 1 timeout 10 strace -o /dev/null \
+ -e inject=splice:error=EIO:when=$when \
+ cat /dev/zero >/dev/null 2>err || fail=1
+ compare exp err || fail=1
+ done
+fi
+
+# Ensure we fallback to write() if there is an issue with (async) zero-copy
+zc_syscalls='io_uring_setup io_uring_enter io_uring_register memfd_create
+ sendfile splice tee vmsplice'
+syscalls=$(
+ for s in $zc_syscalls; do
+ strace -qe "$s" true >/dev/null 2>&1 && echo "$s"
+ done | paste -s -d,)
+
+no_zero_copy() {
+ strace -f -o /dev/null -e inject=${syscalls}:error=ENOSYS "$@"
+}
+if no_zero_copy true; then
+ test "$(no_zero_copy cat /dev/zero | head -c 2 | tr '\0' 'y')" = 'yy' \
+ || fail=1
+fi
+# Ensure we fallback to write() if there is an issue with pipe2()
+# For example if we don't have enough file descriptors available.
+no_pipe() { strace -f -o /dev/null -e inject=pipe,pipe2:error=EMFILE "$@"; }
+if no_pipe true; then
+ no_pipe timeout .1 cat /dev/zero >/dev/null
+ test $? = 124 || fail=1
+fi
+
+Exit $fail
diff --git a/tests/chcon/chcon-fail.sh b/tests/chcon/chcon-fail.sh
index 79bf6e134..33bcbb52b 100755
--- a/tests/chcon/chcon-fail.sh
+++ b/tests/chcon/chcon-fail.sh
@@ -2,7 +2,7 @@
# Ensure that chcon fails when it should.
# These tests don't use any actual SE Linux syscalls.
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chcon/chcon.sh b/tests/chcon/chcon.sh
index fbd679df8..913afa83a 100755
--- a/tests/chcon/chcon.sh
+++ b/tests/chcon/chcon.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# exercise chcon
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chgrp/basic.sh b/tests/chgrp/basic.sh
index a3dde9aef..4a1dabe62 100755
--- a/tests/chgrp/basic.sh
+++ b/tests/chgrp/basic.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure chgrp is reasonable
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -98,11 +98,11 @@ chgrp $g1 f
chgrp '' f
test "$(ls -C -c -t f g)" = 'f g' || \
{
- case $host_triplet in
- *openbsd*) echo ignoring known OpenBSD-specific chgrp failure 1>&2 ;;
- *darwin7.9.*|*darwin8.*)
+ case $host_os in
+ openbsd*) echo ignoring known OpenBSD-specific chgrp failure 1>&2 ;;
+ darwin7.9.*|darwin8.*)
echo ignoring known MacOS X-specific chgrp failure 1>&2 ;;
- *) echo $host_triplet: no-change chgrp failed to update ctime 1>&2;
+ *) echo $host_os: no-change chgrp failed to update ctime 1>&2;
fail=1 ;;
esac
}
diff --git a/tests/chgrp/default-no-deref.sh b/tests/chgrp/default-no-deref.sh
index 82fd1a56c..98f1a161e 100755
--- a/tests/chgrp/default-no-deref.sh
+++ b/tests/chgrp/default-no-deref.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that chgrp -R does not dereference symlinks.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chgrp/deref.sh b/tests/chgrp/deref.sh
index a2041a624..dace8f0d0 100755
--- a/tests/chgrp/deref.sh
+++ b/tests/chgrp/deref.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# see if chgrp can change the group of a symlink
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chgrp/from.sh b/tests/chgrp/from.sh
index 983f5d8db..4d3636676 100755
--- a/tests/chgrp/from.sh
+++ b/tests/chgrp/from.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure chgrp --from=... works
-# Copyright (C) 2023-2025 Free Software Foundation, Inc.
+# Copyright (C) 2023-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -30,7 +30,7 @@ set _ $(ls -n f); shift; test ":$4" = ':1' || fail=1
# Make sure the correct diagnostic is output
# Note we output a name even though an id was specified.
chgrp -v --from=42 43 f > out || fail=1
-printf "group of 'f' retained as $(id -nu 1)\n" > exp
+printf "group of 'f' retained as $(id -nu 1 || printf 1)\n" > exp
compare exp out || fail=1
chgrp --from=:1 010 f || fail=1
diff --git a/tests/chgrp/no-x.sh b/tests/chgrp/no-x.sh
index d56a98546..8d160957f 100755
--- a/tests/chgrp/no-x.sh
+++ b/tests/chgrp/no-x.sh
@@ -2,7 +2,7 @@
# Make sure chgrp gives the right diagnostic for a readable,
# but inaccessible directory.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,6 +22,7 @@ print_ver_ chgrp
require_membership_in_two_groups_
skip_if_root_
require_local_dir_
+getlimits_
set _ $groups; shift
g1=$1
@@ -47,7 +48,7 @@ sed "s/^$prog: cannot read directory /$prog: /" out > t && mv t out
sed 's,d/no-x/y,d/no-x,' out > t && mv t out
cat <<EOF > exp
-$prog: 'd/no-x': Permission denied
+$prog: 'd/no-x': $EACCES
EOF
compare exp out || fail=1
diff --git a/tests/chgrp/posix-H.sh b/tests/chgrp/posix-H.sh
index 8f0ba54d0..69c2e839b 100755
--- a/tests/chgrp/posix-H.sh
+++ b/tests/chgrp/posix-H.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test POSIX-mandated -H option.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chgrp/recurse.sh b/tests/chgrp/recurse.sh
index 4605aabe0..3e77dad1a 100755
--- a/tests/chgrp/recurse.sh
+++ b/tests/chgrp/recurse.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ad-hoc tests of chgrp with -R and -H or -L and symlinks
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chmod/c-option.sh b/tests/chmod/c-option.sh
index 45d571011..f2d0bae61 100755
--- a/tests/chmod/c-option.sh
+++ b/tests/chmod/c-option.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify that chmod's --changes (-c) option works.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chmod/equal-x.sh b/tests/chmod/equal-x.sh
index d9857fcc6..7d6276fba 100755
--- a/tests/chmod/equal-x.sh
+++ b/tests/chmod/equal-x.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test "chmod =x" and the like.
-# Copyright (C) 1999-2025 Free Software Foundation, Inc.
+# Copyright (C) 1999-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chmod/equals.sh b/tests/chmod/equals.sh
index 678bb91f1..b07f69ace 100755
--- a/tests/chmod/equals.sh
+++ b/tests/chmod/equals.sh
@@ -3,7 +3,7 @@
# Before fileutils-4.1.2, some of them didn't.
# Also, before coreutils-5.3.1, =[ugo] sometimes didn't work.
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chmod/ignore-symlink.sh b/tests/chmod/ignore-symlink.sh
index bdaa80582..96e190ca4 100755
--- a/tests/chmod/ignore-symlink.sh
+++ b/tests/chmod/ignore-symlink.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test for proper exit code of chmod on a processed symlink.
-# Copyright (C) 2021-2025 Free Software Foundation, Inc.
+# Copyright (C) 2021-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chmod/inaccessible.sh b/tests/chmod/inaccessible.sh
index 983a84bae..02c226878 100755
--- a/tests/chmod/inaccessible.sh
+++ b/tests/chmod/inaccessible.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test for the bug fixed on 2006-09-20.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chmod/no-x.sh b/tests/chmod/no-x.sh
index b891d9d75..bbf00375d 100755
--- a/tests/chmod/no-x.sh
+++ b/tests/chmod/no-x.sh
@@ -2,7 +2,7 @@
# Make sure chmod gives the right diagnostic for a readable,
# but inaccessible directory.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ chmod
skip_if_root_
+getlimits_
mkdir -p d/no-x/y a/b || framework_failure_
chmod u=rw d/no-x || framework_failure_
@@ -41,7 +42,7 @@ sed "s/^$prog: cannot read directory /$prog: /" out > t && mv t out
sed 's,d/no-x/y,d/no-x,' out > t && mv t out
cat <<EOF > exp
-$prog: 'd/no-x': Permission denied
+$prog: 'd/no-x': $EACCES
EOF
compare exp out || fail=1
diff --git a/tests/chmod/octal.sh b/tests/chmod/octal.sh
index 0166f62b1..2abe15bfe 100755
--- a/tests/chmod/octal.sh
+++ b/tests/chmod/octal.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that chmod diagnoses a certain type of invalid mode string
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chmod/only-op.sh b/tests/chmod/only-op.sh
new file mode 100755
index 000000000..a2fd2546a
--- /dev/null
+++ b/tests/chmod/only-op.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Test that 'chmod' does not skip calling chmod(2) even when the file mode bits
+# are unchanged.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ chmod stat
+skip_if_root_
+
+test "$(stat --format=%U /)" = 'root' || skip_ 'root does not own /'
+
+for op in '+' '-' '='; do
+ returns_ 1 chmod "$op" / 2>err || fail=1
+ grep -F "chmod: changing permissions of '/'" err || { fail=1; cat err; }
+done
+
+Exit $fail
diff --git a/tests/chmod/partial-fail.sh b/tests/chmod/partial-fail.sh
new file mode 100755
index 000000000..104da9512
--- /dev/null
+++ b/tests/chmod/partial-fail.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Ensure exit with failure if only some operations succeed
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ chmod
+
+touch file || framework_failure_
+returns_ 1 chmod 0 missing_file file || fail=1
+
+touch unreadable || framework_failure_
+chmod 0 unreadable || framework_failure_
+if ! test -r unreadable; then
+ test -r file && fail=1
+fi
+
+Exit $fail
diff --git a/tests/chmod/setgid.sh b/tests/chmod/setgid.sh
index d36a62206..d4bcd086d 100755
--- a/tests/chmod/setgid.sh
+++ b/tests/chmod/setgid.sh
@@ -3,7 +3,7 @@
# on directories with the setgid bit set. Also, check that the GNU octal
# notations work.
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chmod/silent.sh b/tests/chmod/silent.sh
index 83ec5ef90..266a196de 100755
--- a/tests/chmod/silent.sh
+++ b/tests/chmod/silent.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that chgrp, chmod, chown -f don't print some diagnostics
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chmod/symlinks.sh b/tests/chmod/symlinks.sh
index dc8ed269b..f5f87860d 100755
--- a/tests/chmod/symlinks.sh
+++ b/tests/chmod/symlinks.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify chmod symlink handling options
-# Copyright (C) 2024-2025 Free Software Foundation, Inc.
+# Copyright (C) 2024-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chmod/thru-dangling.sh b/tests/chmod/thru-dangling.sh
index a212f5570..47ece88dd 100755
--- a/tests/chmod/thru-dangling.sh
+++ b/tests/chmod/thru-dangling.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test for proper error and exit code of chmod on a dangling symlink.
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chmod/umask-x.sh b/tests/chmod/umask-x.sh
index 614936f78..f067bce19 100755
--- a/tests/chmod/umask-x.sh
+++ b/tests/chmod/umask-x.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test that chmod -x file reports an error if the result is executable.
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chmod/usage.sh b/tests/chmod/usage.sh
index 27d6d129a..b71773391 100755
--- a/tests/chmod/usage.sh
+++ b/tests/chmod/usage.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify that chmod works correctly with odd option combinations.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chown/basic.sh b/tests/chown/basic.sh
index f8be9bd41..77b744f74 100755
--- a/tests/chown/basic.sh
+++ b/tests/chown/basic.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure chown --from=... works
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chown/deref.sh b/tests/chown/deref.sh
index 3b0184dfe..85efd66c2 100755
--- a/tests/chown/deref.sh
+++ b/tests/chown/deref.sh
@@ -2,7 +2,7 @@
# For coreutils-5.2.1 and earlier, chown --dereference would skip
# symlinks having owner/group matching the specified owner/group.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chown/preserve-root.sh b/tests/chown/preserve-root.sh
index 569672bd4..11355dada 100755
--- a/tests/chown/preserve-root.sh
+++ b/tests/chown/preserve-root.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify that --preserve-root works.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chown/separator.sh b/tests/chown/separator.sh
index 2bab0ac43..22e71d274 100755
--- a/tests/chown/separator.sh
+++ b/tests/chown/separator.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure "chown USER:GROUP FILE" works, and similar tests with separators.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -40,8 +40,8 @@ test $(getent group | grep "^$id_gn:" | wc -l) = 1 ||
# FreeBSD 6.x's getgrnam fails to look up a group name containing
# a space. On such a system, skip this test if the group name contains
# a byte not in the portable filename character set.
-case $host_triplet in
- *-freebsd6.*)
+case $host_os in
+ freebsd6.*)
case $id_gn in
*[^a-zA-Z0-9._-]*) skip_ "invalid group name: $id_gn";;
esac;;
diff --git a/tests/chroot/chroot-credentials.sh b/tests/chroot/chroot-credentials.sh
index 32ee45dfd..d9077d500 100755
--- a/tests/chroot/chroot-credentials.sh
+++ b/tests/chroot/chroot-credentials.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify that the credentials are changed correctly.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/chroot/chroot-fail.sh b/tests/chroot/chroot-fail.sh
index 956f2e1b3..be39cca01 100755
--- a/tests/chroot/chroot-fail.sh
+++ b/tests/chroot/chroot-fail.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify that internal failure in chroot gives exact status.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cksum/b2sum.sh b/tests/cksum/b2sum.sh
index 731ddb38c..eda2e7533 100755
--- a/tests/cksum/b2sum.sh
+++ b/tests/cksum/b2sum.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# 'b2sum' tests
-# Copyright (C) 2016-2025 Free Software Foundation, Inc.
+# Copyright (C) 2016-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cksum/cksum-a.sh b/tests/cksum/cksum-a.sh
index 4fa078102..ed80a65d3 100755
--- a/tests/cksum/cksum-a.sh
+++ b/tests/cksum/cksum-a.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Validate cksum --algorithm operation
-# Copyright (C) 2021-2025 Free Software Foundation, Inc.
+# Copyright (C) 2021-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -68,14 +68,15 @@ returns_ 1 cksum -a bsd --check </dev/null || fail=1
# Ensure abbreviations not supported for algorithm selection
returns_ 1 cksum -a sha22 </dev/null || fail=1
+# Ensure --text and --tag combo is disallowed
+returns_ 1 cksum --text --tag -a md5 </dev/null || fail=1
+returns_ 1 cksum --tag --text -a md5 </dev/null || fail=1
+
# Ensure --tag -> --untagged transition resets binary indicator
cksum --tag --untagged -a md5 /dev/null >out-1 || fail=1
-# --binary ignored in this edge case
-cksum --binary --tag --untagged -a md5 /dev/null >out-2 || fail=1
# base case for comparison
cksum --untagged -a md5 /dev/null >out || fail=1
compare out out-1 || fail=1
-compare out out-2 || fail=1
# check that the '*' is present
cksum --tag --untagged --binary -a md5 /dev/null >out || fail=1
@@ -83,5 +84,7 @@ grep " *" out >/dev/null || { fail=1; cat out; }
# Verify that the order does not reset binary indicator
cksum --binary --untagged -a md5 /dev/null >out-1 || fail=1
compare out out-1 || fail=1
+cksum --binary --tag --untagged -a md5 /dev/null >out-2 || fail=1
+compare out out-2 || fail=1
Exit $fail
diff --git a/tests/cksum/cksum-base64-untagged.sh b/tests/cksum/cksum-base64-untagged.sh
index 14e194289..aa8db9714 100755
--- a/tests/cksum/cksum-base64-untagged.sh
+++ b/tests/cksum/cksum-base64-untagged.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test that cksum can guess the digest length from base64 checksums.
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cksum/cksum-base64.pl b/tests/cksum/cksum-base64.pl
index 32f3cae51..11d0ce43d 100755
--- a/tests/cksum/cksum-base64.pl
+++ b/tests/cksum/cksum-base64.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Exercise cksum's --base64 option.
-# Copyright (C) 2023-2025 Free Software Foundation, Inc.
+# Copyright (C) 2023-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cksum/cksum-c.sh b/tests/cksum/cksum-c.sh
index 4f00767f5..49d6a84d3 100755
--- a/tests/cksum/cksum-c.sh
+++ b/tests/cksum/cksum-c.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Validate cksum --check dynamic operation
-# Copyright (C) 2021-2025 Free Software Foundation, Inc.
+# Copyright (C) 2021-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ cksum shuf
+uses_strace_
shuf -i 1-10 > input || framework_failure_
@@ -192,4 +193,32 @@ touch empty-1 empty-2 || framework_failure_
cksum --check empty-1 empty-2 > out 2>&1 && fail=1
grep 'empty-1: no properly formatted checksum lines found' out || fail=1
grep 'empty-2: no properly formatted checksum lines found' out || fail=1
+
+
+# Disallow --tag --check, with appropriate error
+returns_ 1 cksum --tag --check /dev/null 2> err
+grep 'the --tag option is meaningless when verifying checksums' err || fail=1
+# Allow --untagged --check, to better support
+# an emulation wrapper for legacy *sum utils.
+cksum -a md5 /dev/null | cksum --untagged --check || fail=1
+
+# Ensure I/O errors handled appropriately
+if strace -o /dev/null -P _ -e /read,splice -e fault=all:error=EIO true; then
+ touch ok eio || framework_failure_
+ cksum -a md5 eio ok > check.md5 || fail=1
+ # Test one of the files being checked returns EIO
+ returns_ 1 strace -o /dev/null -P eio -e /read,splice -e fault=all:error=EIO \
+ cksum --check check.md5 2>err >out || fail=1
+
+ printf '%s\n' 'eio: FAILED open or read' 'ok: OK' >exp || framework_failure_
+ compare exp out || fail=1
+
+ # Generate the expected error using cat:
+ strace -o /dev/null -P eio -e /read,splice -e fault=all:error=EIO \
+ cat eio 2> exp.t
+ sed 's/cat/cksum/' < exp.t > exp && grep eio: exp && echo \
+ 'cksum: WARNING: 1 listed file could not be read' >>exp || framework_failure_
+ compare exp err || fail=1
+fi
+
Exit $fail
diff --git a/tests/cksum/cksum-raw.sh b/tests/cksum/cksum-raw.sh
index 857dbe35c..1093586a6 100755
--- a/tests/cksum/cksum-raw.sh
+++ b/tests/cksum/cksum-raw.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Validate cksum --raw operation
-# Copyright (C) 2023-2025 Free Software Foundation, Inc.
+# Copyright (C) 2023-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cksum/cksum-sha3.sh b/tests/cksum/cksum-sha3.sh
index 4c414f937..45af7efb9 100755
--- a/tests/cksum/cksum-sha3.sh
+++ b/tests/cksum/cksum-sha3.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# 'cksum -a sha3' tests.
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cksum/cksum.sh b/tests/cksum/cksum.sh
index e216183c6..b1041d81e 100755
--- a/tests/cksum/cksum.sh
+++ b/tests/cksum/cksum.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Validate cksum operation
-# Copyright (C) 2020-2025 Free Software Foundation, Inc.
+# Copyright (C) 2020-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cksum/md5sum-bsd.sh b/tests/cksum/md5sum-bsd.sh
index 4986f0144..7859fbbf5 100755
--- a/tests/cksum/md5sum-bsd.sh
+++ b/tests/cksum/md5sum-bsd.sh
@@ -2,7 +2,7 @@
# 'md5sum' tests for generation and checking of
# BSD traditional and alternate formats (md5 [-r])
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -82,8 +82,8 @@ if echo '' > 'backslash\is\not\dir\sep'; then
md5sum --tag "$i" >> check.md5 || fail=1
done
md5sum --strict -c check.md5 > out || fail=1
- printf '%s: OK\n' '\a\\b' '\a\\' '\\\a' '\a\nb' "a${t}b" > exp ||
- framework_failure_
+ printf '%s: OK\n' "'a\\b'" "'a\\'" "'\\a'" \
+ "'a'\$'\\n''b'" "'a'\$'\\t''b'" > exp || framework_failure_
compare exp out || fail=1
diff --git a/tests/cksum/md5sum-newline.pl b/tests/cksum/md5sum-newline.pl
index f16b9f7d9..27300140e 100755
--- a/tests/cksum/md5sum-newline.pl
+++ b/tests/cksum/md5sum-newline.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Newline tests for "md5sum".
-# Copyright (C) 1999-2025 Free Software Foundation, Inc.
+# Copyright (C) 1999-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cksum/md5sum-parallel.sh b/tests/cksum/md5sum-parallel.sh
index 2db7abdd4..c2e00425c 100755
--- a/tests/cksum/md5sum-parallel.sh
+++ b/tests/cksum/md5sum-parallel.sh
@@ -2,7 +2,7 @@
# Ensure that md5sum prints each checksum atomically
# so that concurrent md5sums don't intersperse their output
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cksum/md5sum.pl b/tests/cksum/md5sum.pl
index d5074c697..f6697e12a 100755
--- a/tests/cksum/md5sum.pl
+++ b/tests/cksum/md5sum.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Basic tests for "md5sum".
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cksum/sha1sum-vec.pl b/tests/cksum/sha1sum-vec.pl
index c3c90487d..fcabab2d3 100755
--- a/tests/cksum/sha1sum-vec.pl
+++ b/tests/cksum/sha1sum-vec.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Sample vectors for "sha1sum".
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cksum/sha1sum.pl b/tests/cksum/sha1sum.pl
index b31738a9e..3e64a7ee0 100755
--- a/tests/cksum/sha1sum.pl
+++ b/tests/cksum/sha1sum.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test "sha1sum".
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cksum/sha224sum.pl b/tests/cksum/sha224sum.pl
index e08bcce6e..a14872aa9 100755
--- a/tests/cksum/sha224sum.pl
+++ b/tests/cksum/sha224sum.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test "sha224sum".
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cksum/sha256sum.pl b/tests/cksum/sha256sum.pl
index 715cd9096..102f367aa 100755
--- a/tests/cksum/sha256sum.pl
+++ b/tests/cksum/sha256sum.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test "sha256sum".
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cksum/sha384sum.pl b/tests/cksum/sha384sum.pl
index 893085576..462da37ce 100755
--- a/tests/cksum/sha384sum.pl
+++ b/tests/cksum/sha384sum.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test "sha384sum".
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cksum/sha512sum.pl b/tests/cksum/sha512sum.pl
index b83105dbe..edafe40e5 100755
--- a/tests/cksum/sha512sum.pl
+++ b/tests/cksum/sha512sum.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test "sha512sum".
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cksum/sm3sum.pl b/tests/cksum/sm3sum.pl
index d73a0738c..a5df45e0e 100755
--- a/tests/cksum/sm3sum.pl
+++ b/tests/cksum/sm3sum.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test "cksum -a sm3".
-# Copyright (C) 2021-2025 Free Software Foundation, Inc.
+# Copyright (C) 2021-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cksum/sum-sysv.sh b/tests/cksum/sum-sysv.sh
index 8cb0c7e6d..854870018 100755
--- a/tests/cksum/sum-sysv.sh
+++ b/tests/cksum/sum-sysv.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure 'sum -s' works for input whose sum of bytes is larger than 2^32
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cksum/sum.pl b/tests/cksum/sum.pl
index 09a5ea7cb..56817c64e 100755
--- a/tests/cksum/sum.pl
+++ b/tests/cksum/sum.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test "sum".
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/comm.pl b/tests/comm/comm.pl
index 65a2f468e..ab05602d5 100755
--- a/tests/misc/comm.pl
+++ b/tests/comm/comm.pl
@@ -2,7 +2,7 @@
# -*- perl -*-
# Test comm
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/comm/dash-dash.sh b/tests/comm/dash-dash.sh
new file mode 100755
index 000000000..9d7dc6afc
--- /dev/null
+++ b/tests/comm/dash-dash.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Test that 'comm - -' works.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ comm
+
+printf '1\n\t2\n\t\t3\n4\n\t5\n\t\t6\n7\n\t8\n\t\t9\n' >exp \
+ || framework_failure_
+
+# In coreutils 9.11 and earlier, running 'comm - -' would close
+# standard input twice, leading to an incorrect exit status.
+seq 9 | sed 'n;n;p' | timeout 10 comm - - >out 2>err || fail=1
+compare exp out || fail=1
+compare /dev/null err || fail=1
+
+Exit $fail
diff --git a/tests/cp/abuse.sh b/tests/cp/abuse.sh
index a12b57d73..6907fd4bb 100755
--- a/tests/cp/abuse.sh
+++ b/tests/cp/abuse.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that cp does not write through a just-copied symlink
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/acl.sh b/tests/cp/acl.sh
index f5186c2bc..418e1e989 100755
--- a/tests/cp/acl.sh
+++ b/tests/cp/acl.sh
@@ -2,7 +2,7 @@
# copy files/directories across file system boundaries
# and make sure acls are preserved appropriately
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/attr-existing.sh b/tests/cp/attr-existing.sh
index 60e6fad66..a82cffd7a 100755
--- a/tests/cp/attr-existing.sh
+++ b/tests/cp/attr-existing.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure cp --attributes-only doesn't truncate existing data
-# Copyright 2012-2025 Free Software Foundation, Inc.
+# Copyright 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/backup-1.sh b/tests/cp/backup-1.sh
index 0577ac38c..b3c808298 100755
--- a/tests/cp/backup-1.sh
+++ b/tests/cp/backup-1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test cp backup.
-# Copyright (C) 1997-2025 Free Software Foundation, Inc.
+# Copyright (C) 1997-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/backup-dir.sh b/tests/cp/backup-dir.sh
index 671cde16e..8b031db85 100755
--- a/tests/cp/backup-dir.sh
+++ b/tests/cp/backup-dir.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that cp -b handles directories appropriately
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/backup-is-src.sh b/tests/cp/backup-is-src.sh
index c5d8a5513..6fc7b45cd 100755
--- a/tests/cp/backup-is-src.sh
+++ b/tests/cp/backup-is-src.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test cp backup to source file.
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/capability.sh b/tests/cp/capability.sh
index 6bff6ef8b..edc54c674 100755
--- a/tests/cp/capability.sh
+++ b/tests/cp/capability.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure cp --preserves copies capabilities
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/copy-FMR.sh b/tests/cp/copy-FMR.sh
index 8b8b18588..7312562e0 100755
--- a/tests/cp/copy-FMR.sh
+++ b/tests/cp/copy-FMR.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Trigger a free-memory read bug in cp from coreutils-[8.11..8.19]
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/cp-HL.sh b/tests/cp/cp-HL.sh
index aa3376fab..06d0c3bec 100755
--- a/tests/cp/cp-HL.sh
+++ b/tests/cp/cp-HL.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test cp's -H and -L options
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/cp-a-selinux.sh b/tests/cp/cp-a-selinux.sh
index a6e9856c3..970851e88 100755
--- a/tests/cp/cp-a-selinux.sh
+++ b/tests/cp/cp-a-selinux.sh
@@ -4,7 +4,7 @@
# Check also locally if --preserve=context, -a and --preserve=all
# does work
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -23,6 +23,7 @@
print_ver_ cp
require_root_
require_selinux_
+getlimits_
cwd=$(pwd)
cleanup_() { cd /; umount "$cwd/mnt"; }
@@ -151,14 +152,14 @@ rm -f g
echo > g
cp --preserve=all ../f g 2>err || fail=1
test -s g || fail=1
-grep "Operation not supported" err && fail=1
+grep "$ENOTSUP" err && fail=1
# =====================================================
# The same as above except destination does not exist
rm -f g
cp --preserve=all ../f g 2>err || fail=1
test -s g || fail=1
-grep "Operation not supported" err && fail=1
+grep "$ENOTSUP" err && fail=1
# An alternative to the following approach would be to run in a confined
# domain (maybe creating/loading it) that lacks the required permissions
diff --git a/tests/cp/cp-deref.sh b/tests/cp/cp-deref.sh
index b3e7aca64..03614b150 100755
--- a/tests/cp/cp-deref.sh
+++ b/tests/cp/cp-deref.sh
@@ -2,7 +2,7 @@
# cp -RL dir1 dir2' must handle the case in which each of dir1 and dir2
# contain a symlink pointing to some third directory.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/cp-i.sh b/tests/cp/cp-i.sh
index 2d673a2b0..0de686f5b 100755
--- a/tests/cp/cp-i.sh
+++ b/tests/cp/cp-i.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test whether cp -i prompts in the right place.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/cp-mv-backup.sh b/tests/cp/cp-mv-backup.sh
index 8150bbd2e..a93c395d0 100755
--- a/tests/cp/cp-mv-backup.sh
+++ b/tests/cp/cp-mv-backup.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test basic --backup functionality for both cp and mv.
-# Copyright (C) 1999-2025 Free Software Foundation, Inc.
+# Copyright (C) 1999-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/cp-mv-enotsup-xattr.sh b/tests/cp/cp-mv-enotsup-xattr.sh
index 2d12a3aca..8936b5c85 100755
--- a/tests/cp/cp-mv-enotsup-xattr.sh
+++ b/tests/cp/cp-mv-enotsup-xattr.sh
@@ -3,7 +3,7 @@
# as expected on file systems without their support and do show correct
# diagnostics when required
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,11 +20,14 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ cp mv
-
+getlimits_
require_root_
cwd=$(pwd)
-cleanup_() { cd /; umount "$cwd/noxattr"; umount "$cwd/xattr"; }
+cleanup_() {
+ cd /
+ umount "$cwd/noxattr"; umount "$cwd/xattr"; umount "$cwd/xattr2";
+}
skip=0
@@ -33,23 +36,29 @@ make_fs() {
where="$1"
opts="$2"
- fs="$where.bin"
+ mkdir "$where" || framework_failure_
- dd if=/dev/zero of="$fs" bs=8192 count=200 > /dev/null 2>&1 \
- || skip=1
- mkdir "$where" || skip=1
- mkfs -t ext2 -F "$fs" ||
- skip_ "failed to create ext2 file system"
- mount -oloop,$opts "$fs" "$where" || skip=1
- echo test > "$where"/f || skip=1
- test -s "$where"/f || skip=1
+ if test "$opts" = user_xattr; then
+ fs="$where.bin"
+ dd if=/dev/zero of="$fs" bs=8192 count=200 > /dev/null 2>&1 || skip=1
+ mkfs -t ext2 -F "$fs" || skip_ "failed to create ext2 file system"
+ mount -oloop,$opts "$fs" "$where" || skip=1
+ else
+ mount -t ramfs ramfs "$where" || skip=1
+ fi
- test $skip = 1 &&
- skip_ "insufficient mount/ext2 support"
+ echo test > "$where"/f && test -s "$where"/f || skip=1
+
+ test $skip = 1 && skip_ 'insufficient mount/file system support'
+
+ test "$opts" = nouser_xattr &&
+ setfattr -n user.test -v value "$where"/f 2>/dev/null &&
+ skip_ 'setfattr worked?'
}
make_fs noxattr nouser_xattr
make_fs xattr user_xattr
+make_fs xattr2 user_xattr
# testing xattr name-value pair
xattr_name="user.foo"
@@ -88,8 +97,8 @@ rm -f err noxattr/a
# This should fail with corresponding diagnostics
cp -a --preserve=xattr xattr/a noxattr/ 2>err && fail=1
if grep '^#define USE_XATTR 1' $CONFIG_HEADER > /dev/null; then
-cat <<\EOF > exp
-cp: setting attributes for 'noxattr/a': Operation not supported
+cat <<EOF > exp
+cp: setting attributes for 'noxattr/a': $ENOTSUP
EOF
else
cat <<\EOF > exp
@@ -117,9 +126,9 @@ txattr='trusted.overlay.whiteout'
if setfattr -hn "$txattr" -v y xattr/symlink; then
# Note only root can read the 'trusted.' namespace
if getfattr -h -m- -d xattr/symlink | grep -F "$txattr"; then
- mv xattr/symlink noxattr/ 2>err || fail=1
+ mv xattr/symlink xattr2/ 2>err || fail=1
if grep '^#define USE_XATTR 1' $CONFIG_HEADER > /dev/null; then
- getfattr -h -m- -d noxattr/symlink | grep -F "$txattr" || fail=1
+ getfattr -h -m- -d xattr2/symlink | grep -F "$txattr" || fail=1
fi
compare /dev/null err || fail=1
else
diff --git a/tests/cp/cp-parents.sh b/tests/cp/cp-parents.sh
index 12267769f..eb64317cd 100755
--- a/tests/cp/cp-parents.sh
+++ b/tests/cp/cp-parents.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/cross-dev-symlink.sh b/tests/cp/cross-dev-symlink.sh
index 418d8d203..f351e5bca 100755
--- a/tests/cp/cross-dev-symlink.sh
+++ b/tests/cp/cross-dev-symlink.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure symlinks can be replaced across devices
-# Copyright (C) 2018-2025 Free Software Foundation, Inc.
+# Copyright (C) 2018-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/debug.sh b/tests/cp/debug.sh
index 9a005eeaa..deff05357 100755
--- a/tests/cp/debug.sh
+++ b/tests/cp/debug.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that cp --debug works as documented
-# Copyright (C) 2023-2025 Free Software Foundation, Inc.
+# Copyright (C) 2023-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -29,4 +29,9 @@ touch file.cp || framework_failure_
cp --debug --update=none file file.cp >cp.out || fail=1
grep 'skipped' cp.out || fail=1
+if test -w /dev/full && test -c /dev/full; then
+ returns_ 1 cp file file.cp2 --debug >/dev/full || fail=1
+ test -e file.cp2 || fail=1
+fi
+
Exit $fail
diff --git a/tests/cp/deref-slink.sh b/tests/cp/deref-slink.sh
index 3c2bce462..0f9f28523 100755
--- a/tests/cp/deref-slink.sh
+++ b/tests/cp/deref-slink.sh
@@ -2,7 +2,7 @@
# Demonstrate bug when using -d with an existing destination file
# that is a symlink.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/dir-rm-dest.sh b/tests/cp/dir-rm-dest.sh
index 4ba9f1c3f..f87801560 100755
--- a/tests/cp/dir-rm-dest.sh
+++ b/tests/cp/dir-rm-dest.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# verify cp's --remove-destination option
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/dir-slash.sh b/tests/cp/dir-slash.sh
index 27992119e..f63ea9e7e 100755
--- a/tests/cp/dir-slash.sh
+++ b/tests/cp/dir-slash.sh
@@ -2,7 +2,7 @@
# Make sure that cp -R DIR1 DIR2 does the right thing
# when DIR1 is written with a trailing slash.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/dir-vs-file.sh b/tests/cp/dir-vs-file.sh
index 823fc8bc3..368f070a1 100755
--- a/tests/cp/dir-vs-file.sh
+++ b/tests/cp/dir-vs-file.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# A directory may not replace an existing file.
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/existing-perm-dir.sh b/tests/cp/existing-perm-dir.sh
index 82317d0c8..ca20e24b1 100755
--- a/tests/cp/existing-perm-dir.sh
+++ b/tests/cp/existing-perm-dir.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure cp -p doesn't "restore" permissions it shouldn't (Bug#9170).
-# Copyright 2011-2025 Free Software Foundation, Inc.
+# Copyright 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/existing-perm-race.sh b/tests/cp/existing-perm-race.sh
index 2a5a9215f..71ffb4799 100755
--- a/tests/cp/existing-perm-race.sh
+++ b/tests/cp/existing-perm-race.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure cp -p isn't too generous with existing file permissions.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/fail-perm.sh b/tests/cp/fail-perm.sh
index 3fe84116d..ed591723d 100755
--- a/tests/cp/fail-perm.sh
+++ b/tests/cp/fail-perm.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ cp
skip_if_root_
+getlimits_
chmod g-s . || framework_failure_
mkdir D D/D || framework_failure_
@@ -37,28 +38,21 @@ test "$mode" = dr-x------ || fail=1
chmod 0 D
ln -s D/D symlink
touch F
-cat > exp <<\EOF
-cp: cannot stat 'symlink': Permission denied
+cat > exp <<EOF
+cp: cannot stat 'symlink': $EACCES
EOF
cp F symlink 2> out && fail=1
-# HPUX appears to fail with EACCES rather than EPERM.
-# Transform their diagnostic
-# ...: The file access permissions do not allow the specified action.
-# to the expected one:
-sed 's/: The file access permissions.*/: Permission denied/'<out>o1;mv o1 out
compare exp out || fail=1
cp --no-target-directory F symlink 2> out && fail=1
-sed 's/: The file access permissions.*/: Permission denied/'<out>o1;mv o1 out
compare exp out || fail=1
-cat > exp <<\EOF
-cp: target directory 'symlink': Permission denied
+cat > exp <<EOF
+cp: target directory 'symlink': $EACCES
EOF
cp --target-directory=symlink F 2> out && fail=1
-sed 's/: The file access permissions.*/: Permission denied/'<out>o1;mv o1 out
compare exp out || fail=1
chmod 700 D
diff --git a/tests/cp/file-perm-race.sh b/tests/cp/file-perm-race.sh
index 5c34e5b4a..b65872450 100755
--- a/tests/cp/file-perm-race.sh
+++ b/tests/cp/file-perm-race.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure cp -p isn't too generous with file permissions.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/into-self.sh b/tests/cp/into-self.sh
index e74fd2e05..06279418c 100755
--- a/tests/cp/into-self.sh
+++ b/tests/cp/into-self.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Confirm that copying a directory into itself gets a proper diagnostic.
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/keep-directory-symlink.sh b/tests/cp/keep-directory-symlink.sh
index 016e1f93c..96557b3a6 100755
--- a/tests/cp/keep-directory-symlink.sh
+++ b/tests/cp/keep-directory-symlink.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test that cp --keep-directory-symlink follows symlinks.
-# Copyright (C) 2024-2025 Free Software Foundation, Inc.
+# Copyright (C) 2024-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/link-deref.sh b/tests/cp/link-deref.sh
index fd29f809c..53e5b1955 100755
--- a/tests/cp/link-deref.sh
+++ b/tests/cp/link-deref.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise cp --link's behavior regarding the dereferencing of symbolic links.
-# Copyright (C) 2013-2025 Free Software Foundation, Inc.
+# Copyright (C) 2013-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/link-heap.sh b/tests/cp/link-heap.sh
index 63836bfb7..ff6137934 100755
--- a/tests/cp/link-heap.sh
+++ b/tests/cp/link-heap.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that cp --preserve=link --link doesn't waste heap
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/link-no-deref.sh b/tests/cp/link-no-deref.sh
index 958e13054..92d4d36b5 100755
--- a/tests/cp/link-no-deref.sh
+++ b/tests/cp/link-no-deref.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that cp --link --no-dereference works properly
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/link-preserve.sh b/tests/cp/link-preserve.sh
index a96f7d176..df1f88983 100755
--- a/tests/cp/link-preserve.sh
+++ b/tests/cp/link-preserve.sh
@@ -2,7 +2,7 @@
# ensure that 'cp -d' preserves hard-links between command line arguments
# ensure that --preserve=links works with -RH and -RL
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/link-symlink.sh b/tests/cp/link-symlink.sh
index ba1acfdd5..2941e2d88 100755
--- a/tests/cp/link-symlink.sh
+++ b/tests/cp/link-symlink.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that cp -a --link maintains timestamps if possible
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/link.sh b/tests/cp/link.sh
index 222b5afa8..8ca6bfc20 100755
--- a/tests/cp/link.sh
+++ b/tests/cp/link.sh
@@ -2,7 +2,7 @@
# Make sure cp --link -f works when the target exists.
# This failed for 4.0z (due to a bug introduced in that test release).
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/nfs-removal-race.sh b/tests/cp/nfs-removal-race.sh
index 799bd1943..1bf59fe76 100755
--- a/tests/cp/nfs-removal-race.sh
+++ b/tests/cp/nfs-removal-race.sh
@@ -11,9 +11,11 @@
# file and return 0.
#
# This test is skipped on systems that lack LD_PRELOAD support; that's fine.
-# Similarly, on a system that lacks <dlfcn.h> or __xstat, skipping it is fine.
+# Similarly, on a system that lacks <dlfcn.h>, skipping it is fine.
+# Note: glibc 2.33+ removed __xstat, so we intercept both __xstat (old glibc)
+# and stat (new glibc) to support all systems.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -33,6 +35,8 @@ print_ver_ cp
require_gcc_shared_
# Replace each stat call with a call to this wrapper.
+# We intercept both __xstat (glibc < 2.33) and stat (glibc >= 2.33)
+# to support all glibc versions.
cat > k.c <<'EOF' || framework_failure_
#define _GNU_SOURCE
#include <stdio.h>
@@ -46,17 +50,61 @@ cat > k.c <<'EOF' || framework_failure_
#undef __xstat
+static int
+is_dest_path (const char *path)
+{
+ return *path == 'd' && path[1] == 0;
+}
+
+static const char *
+redirect_path (const char *path)
+{
+ /* When asked to stat nonexistent "d",
+ return results suggesting it exists. */
+ if (is_dest_path (path))
+ {
+ /* Only mark preloaded when we intercept stat on the destination "d".
+ This ensures the test verifies that cp actually calls stat on
+ the destination, not just any file. */
+ fclose (fopen ("preloaded", "w"));
+ return "d2";
+ }
+ return path;
+}
+
+/* For glibc < 2.33: stat() calls __xstat() internally */
int
__xstat (int ver, const char *path, struct stat *st)
{
- static int (*real_stat)(int ver, const char *path, struct stat *st) = NULL;
- fclose(fopen("preloaded", "w"));
+ static int (*real_xstat) (int, const char *, struct stat *) = NULL;
+ if (!real_xstat)
+ real_xstat = dlsym (RTLD_NEXT, "__xstat");
+ if (!real_xstat)
+ return -1;
+ return real_xstat (ver, redirect_path (path), st);
+}
+
+/* For glibc >= 2.33: stat() is a direct symbol */
+int
+stat (const char *path, struct stat *st)
+{
+ static int (*real_stat) (const char *, struct stat *) = NULL;
if (!real_stat)
- real_stat = dlsym (RTLD_NEXT, "__xstat");
- /* When asked to stat nonexistent "d",
- return results suggesting it exists. */
- return real_stat (ver, *path == 'd' && path[1] == 0 ? "d2" : path, st);
+ real_stat = dlsym (RTLD_NEXT, "stat");
+ return real_stat (redirect_path (path), st);
}
+
+/* Since coreutils v8.32 we use fstatat() rather than stat() */
+int fstatat(int dirfd, const char *restrict path,
+ struct stat *restrict statbuf, int flags)
+{
+ static int (*real_fstatat) (int dirfd, const char *restrict pathname,
+ struct stat *restrict statbuf, int flags) = NULL;
+ if (!real_fstatat)
+ real_fstatat = dlsym (RTLD_NEXT, "fstatat");
+ return real_fstatat (dirfd, redirect_path (path), statbuf, flags);
+}
+
EOF
# Then compile/link it:
@@ -67,7 +115,7 @@ touch d2 || framework_failure_
echo xyz > src || framework_failure_
# Finally, run the test:
-LD_PRELOAD=$LD_PRELOAD:./k.so cp src d || fail=1
+LD_PRELOAD=$LD_PRELOAD:./k.so cp -T src d || fail=1
test -f preloaded || skip_ 'LD_PRELOAD was ineffective?'
diff --git a/tests/cp/no-ctx.sh b/tests/cp/no-ctx.sh
index b36152e38..29fe898f7 100755
--- a/tests/cp/no-ctx.sh
+++ b/tests/cp/no-ctx.sh
@@ -4,7 +4,7 @@
# This test is skipped on systems that lack LD_PRELOAD support; that's fine.
# Similarly, on a system that lacks lgetfilecon altogether, skipping it is fine.
-# Copyright (C) 2014-2025 Free Software Foundation, Inc.
+# Copyright (C) 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/no-deref-link1.sh b/tests/cp/no-deref-link1.sh
index 9783c4e54..7b1af761a 100755
--- a/tests/cp/no-deref-link1.sh
+++ b/tests/cp/no-deref-link1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# cp from 3.16 fails this test
-# Copyright (C) 1997-2025 Free Software Foundation, Inc.
+# Copyright (C) 1997-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/no-deref-link2.sh b/tests/cp/no-deref-link2.sh
index 74229d2dd..e0f2b9519 100755
--- a/tests/cp/no-deref-link2.sh
+++ b/tests/cp/no-deref-link2.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# cp from 3.16 fails this test
-# Copyright (C) 1997-2025 Free Software Foundation, Inc.
+# Copyright (C) 1997-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/no-deref-link3.sh b/tests/cp/no-deref-link3.sh
index a4de1469e..7a23d555e 100755
--- a/tests/cp/no-deref-link3.sh
+++ b/tests/cp/no-deref-link3.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# cp from 3.16 fails this test
-# Copyright (C) 1997-2025 Free Software Foundation, Inc.
+# Copyright (C) 1997-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/non-utf8-name.sh b/tests/cp/non-utf8-name.sh
new file mode 100755
index 000000000..d404b3313
--- /dev/null
+++ b/tests/cp/non-utf8-name.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+# Ensure cp handles dirs with non-UTF8 names when using recursive copy with dot
+# This test covers the case where a directory name contains non-UTF8 bytes
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+non_utf8_dir=$(bad_unicode)
+mkdir "$non_utf8_dir" target \
+ || skip_ 'bad unicode not supported in shell or file system'
+
+# Create some test files in the non-UTF8 directory
+touch "$non_utf8_dir"/file1 "$non_utf8_dir"/file2 || framework_failure_
+
+for loc in C "$LOCALE_FR" "$LOCALE_FR_UTF8"; do
+ test -z "$loc" && continue
+ export LC_ALL="$loc"
+
+ # Test: copy contents of non-UTF8 directory using /. syntax
+ # This should work without panicking or erroring
+ cp -r "$non_utf8_dir"/. target || fail=1
+
+ # Verify the files were copied correctly
+ rm target/file1 || fail=1
+ rm target/file2 || fail=1
+
+ # Verify original files still exist
+ test -f "$non_utf8_dir"/file1 || fail=1
+ test -f "$non_utf8_dir"/file2 || fail=1
+done
+
+Exit $fail
diff --git a/tests/cp/parent-perm-race.sh b/tests/cp/parent-perm-race.sh
index 78d0fe17f..746008c76 100755
--- a/tests/cp/parent-perm-race.sh
+++ b/tests/cp/parent-perm-race.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure cp -pR --parents isn't too generous with parent permissions.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/parent-perm.sh b/tests/cp/parent-perm.sh
index 3cee3b060..7320c04e4 100755
--- a/tests/cp/parent-perm.sh
+++ b/tests/cp/parent-perm.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that cp --parents works properly with a preexisting dest. directory
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/perm.sh b/tests/cp/perm.sh
index 8afe9303a..45500e4b7 100755
--- a/tests/cp/perm.sh
+++ b/tests/cp/perm.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure the permission-preserving code in copy.c (mv, cp, install) works.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/preserve-2.sh b/tests/cp/preserve-2.sh
index 6524fcabb..fb5bb10b8 100755
--- a/tests/cp/preserve-2.sh
+++ b/tests/cp/preserve-2.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that cp's --preserve=X,Y option is parsed properly
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/preserve-gid.sh b/tests/cp/preserve-gid.sh
index d39cda935..d3e368d35 100755
--- a/tests/cp/preserve-gid.sh
+++ b/tests/cp/preserve-gid.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify that cp -p preserves GID when it is possible.
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/preserve-link.sh b/tests/cp/preserve-link.sh
index 62adb3417..e8315b316 100755
--- a/tests/cp/preserve-link.sh
+++ b/tests/cp/preserve-link.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise the fix for https://bugs.gnu.org/8419
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/preserve-mode.sh b/tests/cp/preserve-mode.sh
index fba842aa0..114e25252 100755
--- a/tests/cp/preserve-mode.sh
+++ b/tests/cp/preserve-mode.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Check whether cp generates files with correct modes.
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/preserve-slink-time.sh b/tests/cp/preserve-slink-time.sh
index d7f1c5c8a..363779f20 100755
--- a/tests/cp/preserve-slink-time.sh
+++ b/tests/cp/preserve-slink-time.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify that cp -Pp preserves times even on symlinks.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/proc-short-read.sh b/tests/cp/proc-short-read.sh
index ca6518cae..dc827e382 100755
--- a/tests/cp/proc-short-read.sh
+++ b/tests/cp/proc-short-read.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# exercise cp's short-read failure when operating on >4KB files in /proc
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/proc-zero-len.sh b/tests/cp/proc-zero-len.sh
index 73f5922a1..0b088f604 100755
--- a/tests/cp/proc-zero-len.sh
+++ b/tests/cp/proc-zero-len.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that cp copies contents of non-empty "regular" file with st_size==0
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/r-vs-symlink.sh b/tests/cp/r-vs-symlink.sh
index 59b9f69a7..ddae63c61 100755
--- a/tests/cp/r-vs-symlink.sh
+++ b/tests/cp/r-vs-symlink.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# cp -r should not create symlinks. Fixed in fileutils-4.1.5.
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/readonly-dir.sh b/tests/cp/readonly-dir.sh
new file mode 100755
index 000000000..a9b5379eb
--- /dev/null
+++ b/tests/cp/readonly-dir.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+# Test cp behavior with readonly directories
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+skip_if_setgid_
+
+umask 022
+
+# Test case for readonly directory permission preservation
+# This addresses the specific case where 'cp -r' and 'cp -a' should
+# preserve readonly directory permissions (555) instead of changing
+# them to writable permissions (755).
+
+# Create test directory structure
+mkdir -p a/b/c/d || framework_failure_
+touch a/b/c/d/bar.txt || framework_failure_
+echo "test content" > a/b/c/d/bar.txt || framework_failure_
+
+# Make directories readonly (remove write permissions)
+chmod -R -w a || framework_failure_
+
+# Test 1: cp -r should preserve readonly directory permissions
+cp -r a b || fail=1
+
+# Check that the root copied directory has readonly permissions
+mode_b=$(stat --format=%a b) || fail=1
+test "$mode_b" = "555" || fail=1
+
+# Check subdirectories too
+mode_bb=$(stat --format=%a b/b) || fail=1
+test "$mode_bb" = "555" || fail=1
+
+mode_bbc=$(stat --format=%a b/b/c) || fail=1
+test "$mode_bbc" = "555" || fail=1
+
+mode_bbcd=$(stat --format=%a b/b/c/d) || fail=1
+test "$mode_bbcd" = "555" || fail=1
+
+# Test 2: cp -a should preserve readonly directory permissions and not fail
+cp -a a c || fail=1
+
+# Check that cp -a also preserved readonly permissions
+mode_c=$(stat --format=%a c) || fail=1
+test "$mode_c" = "555" || fail=1
+
+mode_cb=$(stat --format=%a c/b) || fail=1
+test "$mode_cb" = "555" || fail=1
+
+Exit $fail
diff --git a/tests/cp/reflink-auto.sh b/tests/cp/reflink-auto.sh
index ae26e2369..63ad4884a 100755
--- a/tests/cp/reflink-auto.sh
+++ b/tests/cp/reflink-auto.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test cp --reflink=auto
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/reflink-perm.sh b/tests/cp/reflink-perm.sh
index 0dd7d472d..3b31d9c97 100755
--- a/tests/cp/reflink-perm.sh
+++ b/tests/cp/reflink-perm.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test cp --reflink copies permissions
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/same-file.sh b/tests/cp/same-file.sh
index d3d352de9..cfbcf98ad 100755
--- a/tests/cp/same-file.sh
+++ b/tests/cp/same-file.sh
@@ -2,7 +2,7 @@
# Test some of cp's options and how cp handles situations in
# which a naive implementation might overwrite the source file.
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/slink-2-slink.sh b/tests/cp/slink-2-slink.sh
index 8cf994cdf..82e682725 100755
--- a/tests/cp/slink-2-slink.sh
+++ b/tests/cp/slink-2-slink.sh
@@ -2,7 +2,7 @@
# 'test cp --update A B' where A and B are both symlinks that point
# to the same file
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/sparse-2.sh b/tests/cp/sparse-2.sh
index b1e86c17a..d3d78873d 100755
--- a/tests/cp/sparse-2.sh
+++ b/tests/cp/sparse-2.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise a few more corners of the copying code.
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/sparse-extents-2.sh b/tests/cp/sparse-extents-2.sh
index 7644096c0..e7740ae65 100755
--- a/tests/cp/sparse-extents-2.sh
+++ b/tests/cp/sparse-extents-2.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test cp --sparse=always through SEEK_DATA copy
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/sparse-extents.sh b/tests/cp/sparse-extents.sh
index 6ef36aae6..68399c80f 100755
--- a/tests/cp/sparse-extents.sh
+++ b/tests/cp/sparse-extents.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test cp handles extents correctly
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/sparse-perf.sh b/tests/cp/sparse-perf.sh
index 5ee984c52..3908958e9 100755
--- a/tests/cp/sparse-perf.sh
+++ b/tests/cp/sparse-perf.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that a sparse file is copied efficiently, by default
-# Copyright (C) 2021-2025 Free Software Foundation, Inc.
+# Copyright (C) 2021-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ cp
+getlimits_
cleanup_() { rm -rf "$other_partition_tmpdir"; }
. "$abs_srcdir/tests/other-fs-tmpdir"
@@ -30,7 +31,10 @@ truncate -s1M $other_partition_sparse || framework_failure_
# cp should not disable anything by default, even for sparse files. For e.g.
# copy offload is an important performance improvement for sparse files on NFS.
-cp --debug $other_partition_sparse k2 >cp.out || fail=1
+cp --debug $other_partition_sparse k2 >cp.out 2>cp.err; ret=$?
+# Old Centos 7 or WSL 1 can give EINVAL erroneously
+grep -F "$EINVAL" cp.err && skip_ 'received EINVAL when copying sparse file'
+test "$ret" = 0 || { cat cp.err >&2; fail=1; }
cmp $other_partition_sparse k2 || fail=1
grep ': avoided' cp.out && { cat cp.out; fail=1; }
diff --git a/tests/cp/sparse-to-pipe.sh b/tests/cp/sparse-to-pipe.sh
index 9bbba46de..09cebc8bd 100755
--- a/tests/cp/sparse-to-pipe.sh
+++ b/tests/cp/sparse-to-pipe.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# copy a sparse file to a pipe, to exercise some seldom-used parts of copy.c
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/sparse.sh b/tests/cp/sparse.sh
index ea7c6de32..916afb103 100755
--- a/tests/cp/sparse.sh
+++ b/tests/cp/sparse.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test cp --sparse=always
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/special-bits.sh b/tests/cp/special-bits.sh
index bef25481b..947a07a05 100755
--- a/tests/cp/special-bits.sh
+++ b/tests/cp/special-bits.sh
@@ -2,7 +2,7 @@
# make sure 'cp -p' preserves special bits
# This works only when run as root.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/special-f.sh b/tests/cp/special-f.sh
index 5c98f8f32..e99969cab 100755
--- a/tests/cp/special-f.sh
+++ b/tests/cp/special-f.sh
@@ -2,7 +2,7 @@
# Ensure that "cp -Rf fifo E" unlinks E and retries.
# Up until coreutils-6.10.171, it would not.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/src-base-dot.sh b/tests/cp/src-base-dot.sh
index 2830592e8..c7a426854 100755
--- a/tests/cp/src-base-dot.sh
+++ b/tests/cp/src-base-dot.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that "mkdir x y; cd y; cp -ab ../x/. ." is a successful, silent, no-op.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/symlink-slash.sh b/tests/cp/symlink-slash.sh
index 84891169a..623efa3ce 100755
--- a/tests/cp/symlink-slash.sh
+++ b/tests/cp/symlink-slash.sh
@@ -2,7 +2,7 @@
# Make sure that cp -dR dereferences a symlink arg if its name is
# written with a trailing slash.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cp/thru-dangling.sh b/tests/cp/thru-dangling.sh
index 122cf0a35..14065bfff 100755
--- a/tests/cp/thru-dangling.sh
+++ b/tests/cp/thru-dangling.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that cp works as documented, when the destination is a dangling symlink
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/csplit/csplit-1000.sh b/tests/csplit/csplit-1000.sh
index 57bd637a0..6296c6468 100755
--- a/tests/csplit/csplit-1000.sh
+++ b/tests/csplit/csplit-1000.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# cause a 1-byte heap buffer overrun
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/csplit/csplit-heap.sh b/tests/csplit/csplit-heap.sh
index 2a38789dc..9ab086b5d 100755
--- a/tests/csplit/csplit-heap.sh
+++ b/tests/csplit/csplit-heap.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that csplit uses a bounded amount of memory
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/csplit/csplit-io-err.sh b/tests/csplit/csplit-io-err.sh
index 2cb06efd1..6902f11e8 100755
--- a/tests/csplit/csplit-io-err.sh
+++ b/tests/csplit/csplit-io-err.sh
@@ -1,7 +1,7 @@
#!/bin/sh
-# Ensure we handle i/o errors correctly in csplit
+# Ensure we handle i/o errors correctly in csplit via /dev/full
-# Copyright (C) 2015-2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,75 +18,24 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ csplit
-require_gcc_shared_
+getlimits_
-if ! test -w /dev/full || ! test -c /dev/full; then
- skip_ '/dev/full is required'
-fi
-
-# Ensure error messages are in English
-LC_ALL=C
-export LC_ALL
-
-# Replace fwrite and ferror, always returning an error
-cat > k.c <<'EOF' || framework_failure_
-#include <stdio.h>
-#include <errno.h>
-
-#undef fwrite
-#undef fwrite_unlocked
-
-size_t
-fwrite (const void *ptr, size_t size, size_t nitems, FILE *stream)
-{
- if (stream == stderr)
- {
- /* Perform the normal operation of fwrite. */
- const char *p = ptr;
- size_t count = size * nitems;
- size_t i;
- for (i = 0; i < count; i++)
- if (putc ((unsigned char) *p++, stream) == EOF)
- break;
- return i / size;
- }
- else
- {
- fclose (fopen ("preloaded","w")); /* marker for preloaded interception */
- errno = ENOSPC;
- return 0;
- }
-}
-
-size_t
-fwrite_unlocked (const void *ptr, size_t size, size_t nitems, FILE *stream)
-{
- return fwrite (ptr, size, nitems, stream);
-}
-EOF
-
-# Get the wording of the OS-dependent ENOSPC message
-returns_ 1 seq 1 >/dev/full 2>msgt || framework_failure_
-sed 's/seq: write error: //' msgt > msg || framework_failure_
+cp -sf /dev/full xx01 || skip_ '/dev/full is required'
# Create the expected error message
-{ printf "%s" "csplit: write error for 'xx01': " ; cat msg ; } > exp \
- || framework_failure_
-
-# compile/link the interception shared library:
-gcc_shared_ k.c k.so \
- || skip_ 'failed to build forced-fwrite-failure shared library'
+printf '%s\n' "csplit: xx01: $ENOSPC" > exp || framework_failure_
-# Split the input, and force fwrite() failure -
# the 'csplit' command should fail with exit code 1
-# (checked with 'returns_ 1 ... || fail=1')
-seq 10 |
-(export LD_PRELOAD=$LD_PRELOAD:./k.so
- returns_ 1 csplit - 1 4 2>out) || fail=1
-
-test -e preloaded || skip_ 'LD_PRELOAD interception failed'
+seq 2 | returns_ 1 csplit - 1 2> err || fail=1
+# csplit should cleanup broken files
+test -e xx01 && fail=1
# Ensure we got the expected error message
-compare exp out || fail=1
+compare exp err || fail=1
+
+# csplit does not remove xx01 directory
+mkdir xx01 || framework_failure_
+seq 2 | returns_ 1 csplit - 1 || fail=1
+test -d xx01 || fail=1
Exit $fail
diff --git a/tests/csplit/csplit-suppress-matched.pl b/tests/csplit/csplit-suppress-matched.pl
index f7d906179..d42112a05 100755
--- a/tests/csplit/csplit-suppress-matched.pl
+++ b/tests/csplit/csplit-suppress-matched.pl
@@ -1,6 +1,6 @@
#!/usr/bin/perl
-# Copyright (C) 2013-2025 Free Software Foundation, Inc.
+# Copyright (C) 2013-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/csplit/csplit.sh b/tests/csplit/csplit.sh
index 0b600ad8e..b3865b706 100755
--- a/tests/csplit/csplit.sh
+++ b/tests/csplit/csplit.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# various csplit tests
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/cut/bounded-memory.sh b/tests/cut/bounded-memory.sh
new file mode 100755
index 000000000..5c0018a74
--- /dev/null
+++ b/tests/cut/bounded-memory.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+# Ensure that cut uses bounded memory when possible.
+
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cut
+
+vm=$(get_min_ulimit_v_ timeout 10 cut -b1 /dev/null) ||
+ skip_ 'failed to determine memory limit'
+
+# There is no way to implement '-s -f1' without allocating unbounded memory.
+# Check the rest.
+for opts in '-c1' '-b1' '-s -f2' '-f1'; do
+ (ulimit -v $(($vm+6000)) \
+ && timeout 0.5 cut $opts </dev/zero >/dev/null 2>err)
+ ret=$?
+ test $ret = 124 || {
+ fail=1
+ cat err
+ echo "cut $opts used too much memory" >&2
+ }
+done
+
+Exit $fail
diff --git a/tests/cut/cut-huge-range.sh b/tests/cut/cut-huge-range.sh
index 3e7f8122c..61d52d8e3 100755
--- a/tests/cut/cut-huge-range.sh
+++ b/tests/cut/cut-huge-range.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that cut does not allocate mem for large ranges
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,10 +22,9 @@ getlimits_
vm=$(get_min_ulimit_v_ returns_ 0 cut -b1 /dev/null) \
|| skip_ 'shell lacks ulimit, or ASAN enabled'
+vm=$(($vm+1000)) # https://bugzilla.redhat.com/2424302
# Ensure we can cut up to our sentinel value.
-# Don't use expr to subtract one,
-# since UINTMAX_MAX may exceed its maximum value.
CUT_MAX=$(expr $UINTMAX_MAX - 1) || framework_failure_
# From coreutils-8.10 through 8.20, this would make cut try to allocate
diff --git a/tests/cut/cut.pl b/tests/cut/cut.pl
index 4f07bdb25..33d6388b9 100755
--- a/tests/cut/cut.pl
+++ b/tests/cut/cut.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test "cut".
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,6 +18,9 @@
use strict;
+my $limits = getlimits ();
+my $IO_BUFSIZE = $limits->{IO_BUFSIZE};
+
(my $ME = $0) =~ s|.*/||;
# Turn off localization of executable's output.
@@ -34,8 +37,23 @@ my $from_pos1 = "$prog: byte/character positions are numbered from 1\n$try";
my $inval_fld = "$prog: invalid field range\n$try";
my $inval_pos = "$prog: invalid byte or character range\n$try";
my $no_endpoint = "$prog: invalid range with no endpoint: -\n$try";
-my $nofield = "$prog: an input delimiter may be specified only when " .
+my $one_list = "$prog: only one list may be specified\n$try";
+my $nofield = "$prog: an input delimiter makes sense\n\tonly when " .
"operating on fields\n$try";
+my $mutual_dw = "$prog: -d and -w are mutually exclusive\n$try";
+my $single_char = "$prog: the delimiter must be a single character\n$try";
+my $single_byte_locale = 'C';
+
+{
+ my $codeset = qx(LC_ALL=C locale charmap 2>/dev/null);
+ chomp $codeset;
+ if ($codeset eq 'UTF-8')
+ {
+ my $fr_locale = $ENV{LOCALE_FR};
+ $single_byte_locale
+ = defined $fr_locale && $fr_locale ne 'none' ? $fr_locale : undef;
+ }
+}
my @Tests =
(
@@ -64,6 +82,7 @@ my @Tests =
['7', '-c4', {IN=>"123"}, {OUT=>"\n"}],
['8', '-c4', {IN=>"123\n1"}, {OUT=>"\n\n"}],
['9', '-c4', {IN=>""}, {OUT=>""}],
+ ['byte-newline-1', '-b1', {IN=>"a\n"}, {OUT=>"a\n"}],
['a', qw(-s -d:), '-f3-', {IN=>"a:b:c\n"}, {OUT=>"c\n"}],
['b', qw(-s -d:), '-f2,3', {IN=>"a:b:c\n"}, {OUT=>"b:c\n"}],
['c', qw(-s -d:), '-f1,3', {IN=>"a:b:c\n"}, {OUT=>"a:c\n"}],
@@ -113,6 +132,7 @@ my @Tests =
# Missing byte list
['missing-bl', qw(-b --), {IN=>":\n"}, {OUT=>""}, {EXIT=>1},
{ERR=>$inval_pos}],
+ ['multi-list-1', qw(-f 1 -F 2), {EXIT=>1}, {ERR=>$one_list}],
# This test fails with cut from textutils-1.22.
['empty-f1', '-f1', {IN=>""}, {OUT=>""}],
@@ -122,8 +142,7 @@ my @Tests =
['o-delim', qw(-d: --out=_), '-f2,3', {IN=>"a:b:c\n"}, {OUT=>"b_c\n"}],
['nul-idelim', qw(-d '' --out=_), '-f2,3', {IN=>"a\0b\0c\n"}, {OUT=>"b_c\n"}],
['nul-odelim', qw(-d: --out=), '-f2,3', {IN=>"a:b:c\n"}, {OUT=>"b\0c\n"}],
- ['multichar-od', qw(-d: --out=_._), '-f2,3', {IN=>"a:b:c\n"},
- {OUT=>"b_._c\n"}],
+ ['multichar-od', qw(-d: -O _._), '-f2,3', {IN=>"a:b:c\n"}, {OUT=>"b_._c\n"}],
# Ensure delim is not allowed without a field
# Prior to 8.21, a NUL delim was allowed without a field
@@ -134,6 +153,22 @@ my @Tests =
['8bit-delim', '-d', "\255", '--out=_', '-f2,3', {IN=>"a\255b\255c\n"},
{OUT=>"b_c\n"}],
+ ['w-delim-1', '-w', '-f2,3', {IN=>"a\tb c\n"}, {OUT=>"b\tc\n"}],
+ ['w-delim-2', '-w', '-f1,2', {IN=>" a b\n"}, {OUT=>"\ta\n"}],
+ ['w-delim-3', '-s', '-w', '-f2', {IN=>"abc\n"}, {OUT=>""}],
+ ['w-delim-4', '-s', '-w', '-f1', {IN=>"a b c\n"}, {OUT=>"a\n"}],
+ ['w-delim-5', '-w', '-d:', '-f1', {EXIT=>1}, {ERR=>$mutual_dw}],
+ ['w-delim-6', '-w', '-f1,2', {IN=>"a \n"}, {OUT=>"a\t\n"}],
+ ['w-delim-7', '--whitespace-delimited', '-f1,2',
+ {IN=>" a b\n"}, {OUT=>"\ta\n"}],
+ ['F-delim-1', '-F', '2,3', {IN=>"a\tb c\n"}, {OUT=>"b c\n"}],
+ ['F-delim-2', qw(-F 2,3 -O _), {IN=>"a\tb c\n"}, {OUT=>"b_c\n"}],
+ ['F-delim-3', qw(-F 2,3 -d ,), {IN=>"1,2,3\n"}, {OUT=>"2 3\n"}],
+ ['w-trim-1', '--whitespace-delimited=trimmed', '-f1,2',
+ {IN=>" a b \n"}, {OUT=>"a\tb\n"}],
+ ['w-trim-2', '-s', '--whitespace-delimited=trimmed', '-f1',
+ {IN=>" a \n"}, {OUT=>""}],
+
# newline processing for fields
['newline-1', '-f1-', {IN=>"a\nb"}, {OUT=>"a\nb\n"}],
['newline-2', '-f1-', {IN=>""}, {OUT=>""}],
@@ -141,6 +176,7 @@ my @Tests =
['newline-4', '-d:', '-f1', {IN=>"a:1\nb:2"}, {OUT=>"a\nb\n"}],
['newline-5', '-d:', '-f2', {IN=>"a:1\nb:2\n"}, {OUT=>"1\n2\n"}],
['newline-6', '-d:', '-f2', {IN=>"a:1\nb:2"}, {OUT=>"1\n2\n"}],
+ ['newline-6a', '-d:', '-f2', {IN=>"a\nb"}, {OUT=>"a\nb\n"}],
['newline-7', '-s', '-d:', '-f1', {IN=>"a:1\nb:2"}, {OUT=>"a\nb\n"}],
['newline-8', '-s', '-d:', '-f1', {IN=>"a:1\nb:2\n"}, {OUT=>"a\nb\n"}],
['newline-9', '-s', '-d:', '-f1', {IN=>"a1\nb2"}, {OUT=>""}],
@@ -160,6 +196,19 @@ my @Tests =
['newline-22', "-d'\n'", '-f1-', {IN=>"\nb"}, {OUT=>"\nb\n"}],
['newline-23', "-d'\n'", '-f1-', '--ou=:', {IN=>"a\nb\n"}, {OUT=>"a:b\n"}],
['newline-24', "-d'\n'", '-f1,2', '--ou=:', {IN=>"a\nb\n"}, {OUT=>"a:b\n"}],
+ ['newline-26', "-d'\n'", '-f2', {IN=>"a\n"}, {OUT=>"\n"}],
+ ['newline-27', '-s', "-d'\n'", '-f2', {IN=>"a\n"}, {OUT=>""}],
+ ['newline-28', '-s', "-d'\n'", '-f2',
+ {IN=>('a' x ($IO_BUFSIZE - 1)) . "\n"}, {OUT=>""}],
+ ['newline-29', '-s', "-d'\n'", '-f2',
+ {IN=>('a' x ($IO_BUFSIZE - 1)) . "\nb"}, {OUT=>"b\n"}],
+
+ # input without delimiter and -s flag
+ ['newline-25', '-s', "-d'\n'", '-f1', {IN=>"abc"}, {OUT=>""}],
+
+ # Ensure we can skip the remainder of a long line after the selected field.
+ ['line-only-1', '-d:', '-f1',
+ {IN=>"a:" . ('b' x $IO_BUFSIZE) . "\n"}, {OUT=>"a\n"}],
# --zero-terminated
['zerot-1', "-z", '-c1', {IN=>"ab\0cd\0"}, {OUT=>"a\0c\0"}],
@@ -168,6 +217,7 @@ my @Tests =
['zerot-4', '-z -d:', '-f1', {IN=>"a:1\0b:2"}, {OUT=>"a\0b\0"}],
['zerot-5', '-z -d:', '-f1-', {IN=>"a1:\0:"}, {OUT=>"a1:\0:\0"}],
['zerot-6', "-z -d ''", '-f1,2', '--ou=:', {IN=>"a\0b\0"}, {OUT=>"a:b\0"}],
+ ['zerot-7', "-z -d '' -s", '-f1', {IN=>"abc"}, {OUT=>""}],
# New functionality:
['out-delim1', '-c1-3,5-', '--output-d=:', {IN=>"abcdefg\n"},
@@ -243,8 +293,160 @@ if ($mb_locale ne 'C')
push @new, ["$test_name-mb", @new_t, {ENV => "LC_ALL=$mb_locale"}];
}
push @Tests, @new;
+
+ push @Tests,
+ ['mb-char-1', '-c1', {IN=>"\xc3\xa9x\n"}, {OUT=>"\xc3\xa9\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-char-2', '-c2', {IN=>"\xc3\xa9x\n"}, {OUT=>"x\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-char-3', '-c1,3', '--output-d=:',
+ {IN=>"\xc3\xa9a\xe2\x82\xacb\n"}, {OUT=>"\xc3\xa9:\xe2\x82\xac\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-char-4', '-c1,3', "--output-d='\xe2\x90\x9e'",
+ {IN=>"\xc3\xa9ab\n"}, {OUT=>"\xc3\xa9\xe2\x90\x9eb\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-char-5', '-c1-2', {IN=>"\xc3x\n"}, {OUT=>"\xc3x\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ # Note mb-byte-n-1 and mb-byte-n-4 differ from coreutils-i18n patch,
+ # which outputs a character if any byte is selected.
+ # I.e., the i18n patch may output more bytes that the requested range.
+ # Also mb-byte-n-3 differs from coreutils-i18n patch,
+ # but that looks like a bug in that patch rather than a design choice.
+ ['mb-byte-n-1', qw(-b1 -n), {IN=>"\xc3\xa9x\n"}, {OUT=>"\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-byte-n-2', qw(-b2 -n), {IN=>"\xc3\xa9x\n"}, {OUT=>"\xc3\xa9\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-byte-n-3', qw(-b1-2 -n), {IN=>"\xc3\xa9x\n"}, {OUT=>"\xc3\xa9\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-byte-n-4', qw(-b1,3 -n), {IN=>"\xc3\xa9x\n"}, {OUT=>"x\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-byte-n-5', qw(-b2-3 -n), {IN=>"\xc3\xa9x\n"}, {OUT=>"\xc3\xa9x\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-byte-n-6', qw(-b2 -n), {IN=>"\xe2\x82\xacx\n"}, {OUT=>"\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-byte-n-7', qw(-b3 -n), {IN=>"\xe2\x82\xacx\n"},
+ {OUT=>"\xe2\x82\xac\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-byte-n-8', qw(-b2-3 -n), {IN=>"\xe2\x82\xacx\n"},
+ {OUT=>"\xe2\x82\xac\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-delim-1', '-d', "\xc3\xa9", '-f2',
+ {IN=>"a\xc3\xa9b\xc3\xa9c\n"}, {OUT=>"b\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-delim-2', '-d', "\xc3\xa9", '-f1,3',
+ {IN=>"a\xc3\xa9b\xc3\xa9c\n"}, {OUT=>"a\xc3\xa9c\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-delim-3', '-s', '-d', "\xc3\xa9", '-f2',
+ {IN=>"abc\n"}, {OUT=>""},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-delim-4', '-s', '-d', "\xc3\xa9", '-f1', # bug in coreutils-i18n
+ {IN=>"a\xc3\xa9b\n"}, {OUT=>"a\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-delim-5', '-d', "\xa9", '-f2', # Different from coreutils-i18n
+ {IN=>"A\xc3\xa9B\xa9C\n"}, {OUT=>"C\n"}, # (we don't split valid chars)
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-delim-6', '-d', "\xc3\xa9", '-f1,3',
+ {IN=>"a\xc3\xa9b\xc3\xa9c"}, {OUT=>"a\xc3\xa9c\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-delim-7', '-d', "\xc3\xa9", '-f2',
+ {IN=>"a\0b\xc3\xa9c\n"}, {OUT=>"c\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-delim-8', '-d', "\xff", '-f2', # Note 0xF5-0xFF is efficient
+ {IN=>"a\xffb\n"}, {OUT=>"b\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-delim-9', '-d', "\xc3\xa9", '-f2',
+ {IN=>('a' x ($IO_BUFSIZE - 1)) . "\xc3\xa9b\n"}, {OUT=>"b\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-delim-10', '-s', '-d', "\xc3\xa9", '-f2',
+ {IN=>"a\0b\0"}, {OUT=>""},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-w-delim-1', '-w', '-f2', {IN=>"a\xe2\x80\x83b\n"}, {OUT=>"b\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-w-delim-2', '-sw', '-f2', {IN=>"a\xc2\xa0b\n"}, {OUT=>""},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-w-nodelim-1', '-w', '-f2', {IN=>"abc"}, {OUT=>"abc\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+
+ # --complement with multi-byte
+ ['mb-compl-c1', '--complement', '-c1',
+ {IN=>"\xc3\xa9x\n"}, {OUT=>"x\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-compl-c2', '--complement', '-c2',
+ {IN=>"\xc3\xa9x\n"}, {OUT=>"\xc3\xa9\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-compl-f1', '--complement', '-d', "\xc3\xa9", '-f1',
+ {IN=>"a\xc3\xa9b\xc3\xa9c\n"}, {OUT=>"b\xc3\xa9c\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-compl-bn1', '--complement', qw(-b1 -n),
+ {IN=>"\xc3\xa9x\n"}, {OUT=>"\xc3\xa9x\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+
+ # -z with multi-byte
+ ['mb-zerot-c1', '-z', '-c1',
+ {IN=>"\xc3\xa9x\0\xc3\xa9y\0"}, {OUT=>"\xc3\xa9\0\xc3\xa9\0"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-zerot-f1', '-z', '-d', "\xc3\xa9", '-f2',
+ {IN=>"a\xc3\xa9b\0c\xc3\xa9d\0"}, {OUT=>"b\0d\0"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-zerot-f2', '-z', '-d', "\xc3\xa9", '-f1',
+ {IN=>"a\xc3\xa9b\0c\xc3\xa9d"}, {OUT=>"a\0c\0"},
+ {ENV => "LC_ALL=$mb_locale"}],
+
+ # empty fields with multi-byte delimiter
+ ['mb-empty-f1', '-d', "\xc3\xa9", '-f1',
+ {IN=>"\xc3\xa9\xc3\xa9c\n"}, {OUT=>"\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-empty-f2', '-d', "\xc3\xa9", '-f2',
+ {IN=>"\xc3\xa9\xc3\xa9c\n"}, {OUT=>"\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-empty-f3', '-d', "\xc3\xa9", '-f3',
+ {IN=>"\xc3\xa9\xc3\xa9c\n"}, {OUT=>"c\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-empty-f1-3', '-d', "\xc3\xa9", '-f1-3', '--output-d=:',
+ {IN=>"\xc3\xa9\xc3\xa9c\n"}, {OUT=>"::c\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+
+ # multi-byte output delimiter with -f
+ ['mb-odelim-f1', '-d', "\xc3\xa9", '-f1,3',
+ "--output-d=\xe2\x82\xac",
+ {IN=>"a\xc3\xa9b\xc3\xa9c\n"}, {OUT=>"a\xe2\x82\xacc\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+
+ # multi-line, multi-byte delimiter, no trailing newline
+ ['mb-multiline-1', '-d', "\xc3\xa9", '-f2',
+ {IN=>"a\xc3\xa9b\nc\xc3\xa9d"}, {OUT=>"b\nd\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+
+ # -w with multi-byte field content
+ ['mb-w-content-1', '-w', '-f1,2', {IN=>"\xc3\xa9\t\xc3\xbc\n"},
+ {OUT=>"\xc3\xa9\t\xc3\xbc\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+
+ # -b -n with output delimiter
+ ['mb-bn-odelim', qw(-b1,3 -n), '--output-d=:',
+ {IN=>"\xc3\xa9x\n"}, {OUT=>"x\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+ ['mb-bn-odelim-2', qw(-b1-2,4 -n), '--output-d=:',
+ {IN=>"\xc3\xa9\xc3\xbcx\n"}, {OUT=>"\xc3\xa9:\xc3\xbc\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+
+ # -b -n with --complement
+ ['mb-compl-bn2', '--complement', qw(-b3 -n),
+ {IN=>"\xc3\xa9x\n"}, {OUT=>"\xc3\xa9\n"},
+ {ENV => "LC_ALL=$mb_locale"}],
+
+ # -F in multi-byte locale
+ ['mb-F-1', '-F', '2', {IN=>"\xc3\xa9\t\xc3\xbc\n"},
+ {OUT=>"\xc3\xbc\n"},
+ {ENV => "LC_ALL=$mb_locale"}];
}
+defined $single_byte_locale
+ and push @Tests,
+ ['mb-delim-C', '-d', "\xc3\xa9", '-f1',
+ {EXIT=>1}, {ERR=>$single_char},
+ {ENV => "LC_ALL=$single_byte_locale"}];
+
@Tests = triple_test \@Tests;
diff --git a/tests/cut/mb-non-utf8.sh b/tests/cut/mb-non-utf8.sh
new file mode 100755
index 000000000..896018a12
--- /dev/null
+++ b/tests/cut/mb-non-utf8.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+# Test cut with non-UTF-8 multibyte locales.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cut printf
+
+export LC_ALL=zh_CN.gb18030
+
+test "$(locale charmap 2>/dev/null | sed 's/gb/GB/')" = GB18030 ||
+ skip_ 'GB18030 charset support not detected'
+
+delim_gb18030=$(env printf '\xa2\xe3')
+delim_ff=$(env printf '\xff')
+
+# Exercise ASCII, multibyte, and invalid single-byte delimiters.
+# Note 0xFF is invalid in GB18030, but we support all single byte delimiters.
+for delim in ',' ':' "$delim_gb18030" "$delim_ff"; do
+ num_out=$(printf "1${delim}2${delim}3\n" \
+ | cut -d "$delim" -f2,3 --output-delimiter=_)
+ test "$num_out" = "2_3" || fail=1
+done
+
+# A valid 2-byte GB18030 character.
+printf '%sx\n' "$delim_gb18030" | cut -c1 > out || fail=1
+printf '%s\n' "$delim_gb18030" > exp || framework_failure_
+compare exp out || fail=1
+
+printf '%sx\n' "$delim_gb18030" | cut -c2 > out || fail=1
+printf 'x\n' > exp || framework_failure_
+compare exp out || fail=1
+
+printf '%sx\n' "$delim_gb18030" | cut -b1 -n > out || fail=1
+printf '\n' > exp || framework_failure_
+compare exp out || fail=1
+
+printf '%sx\n' "$delim_gb18030" | cut -b2 -n > out || fail=1
+printf '%s\n' "$delim_gb18030" > exp || framework_failure_
+compare exp out || fail=1
+
+printf '1%s2%s3\n' "$delim_gb18030" "$delim_gb18030" \
+ | cut -d "$delim_gb18030" -f1,3 > out || fail=1
+printf '1%s3\n' "$delim_gb18030" > exp || framework_failure_
+compare exp out || fail=1
+
+printf '1%s2%s3\n' "$delim_gb18030" "$delim_gb18030" \
+ | cut --complement -d "$delim_gb18030" -f1 > out || fail=1
+printf '2%s3\n' "$delim_gb18030" > exp || framework_failure_
+compare exp out || fail=1
+
+printf '1%s2' "$delim_gb18030" \
+ | cut -d "$delim_gb18030" -f2 > out || fail=1
+printf '2\n' > exp || framework_failure_
+compare exp out || fail=1
+
+printf '1%s2\0' "$delim_gb18030" \
+ | cut -z -d "$delim_gb18030" -f2 > out || fail=1
+printf '2\0' > exp || framework_failure_
+compare exp out || fail=1
+
+printf '%s%s3\n' "$delim_gb18030" "$delim_gb18030" \
+ | cut -d "$delim_gb18030" -f1-3 --output-delimiter=':' > out || fail=1
+printf '::3\n' > exp || framework_failure_
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/date/date-debug.sh b/tests/date/date-debug.sh
index ee522f2b7..8441dc8e8 100755
--- a/tests/date/date-debug.sh
+++ b/tests/date/date-debug.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test 'date --debug' option.
-# Copyright (C) 2016-2025 Free Software Foundation, Inc.
+# Copyright (C) 2016-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/date/date-ethiopia.sh b/tests/date/date-ethiopia.sh
index eda9a5a63..f4c70d05b 100755
--- a/tests/date/date-ethiopia.sh
+++ b/tests/date/date-ethiopia.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify the Ethiopian calendar is used in the Ethiopian locale.
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/date/date-iran.sh b/tests/date/date-iran.sh
index 7c2d16be5..cb30e18b5 100755
--- a/tests/date/date-iran.sh
+++ b/tests/date/date-iran.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify the Solar Hijri calendar is used in the Iranian locale.
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/date/date-locale-hour.sh b/tests/date/date-locale-hour.sh
index 7e0d12019..749123d85 100755
--- a/tests/date/date-locale-hour.sh
+++ b/tests/date/date-locale-hour.sh
@@ -2,7 +2,7 @@
# Check that 'date' uses the 12-hour or 24-hour clock depending on the
# current locale.
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -27,14 +27,45 @@ for loc in "$LOCALE_FR_UTF8" 'en_US.UTF-8'; do
case $(LC_ALL="$loc" locale date_fmt) in
*%[Ilr]*) compare_time='1:00' ;;
*%[HkRT]*) compare_time='13:00' ;;
- *) skip_ 'unrecognised locale hour format';;
+ *) skip_ 'unrecognized locale hour format';;
esac
case $(LC_ALL="$loc" date -d '2025-10-11T13:00') in
*"$compare_time"*) ;;
*) fail=1 ;;
esac
+
+ # The use of %r requires checking 'locale t_fmt_ampm' for the format.
+ # The use of %X requires checking 'locale t_fmt' for the format.
+ fmt=$(LC_ALL="$loc" locale date_fmt)
+ case "$fmt" in
+ *%r*) fmt=$(LC_ALL="$loc" locale t_fmt_ampm) ;;
+ *%X*) fmt=$(LC_ALL="$loc" locale t_fmt) ;;
+ esac
+
+ case "$fmt" in
+ *%[IHRT]*) compare_time='01:00' ;;
+ *%_[IH]*) compare_time=' 1:00' ;;
+ *%[lk]*) compare_time=' 1:00' ;;
+ *) skip_ 'unrecognized locale hour format';;
+ esac
+
+ case $(LC_ALL="$loc" date -d '2025-10-11T01:00') in
+ *"$compare_time"*) ;;
+ *) fail=1 ;;
+ esac
done
+# Make sure 'date' can use the format string given by 'locale date_fmt'
+# and that it uses it by default when no format string is given.
+for loc in $(locale -a | shuf -n 10); do
+ fmt="$(LC_ALL=$loc locale date_fmt; printf x)"
+ fmt=${fmt%?x} # Retain fmt newlines (seen with Serbian on Centos 7)
+ if test -n "$fmt"; then
+ LC_ALL=$loc date -d '2025-10-11T13:00' +"$fmt" > $loc.exp || fail=1
+ LC_ALL=$loc date -d '2025-10-11T13:00' > $loc.out || fail=1
+ compare $loc.exp $loc.out || fail=1
+ fi
+done
Exit $fail
diff --git a/tests/date/date-next-dow.pl b/tests/date/date-next-dow.pl
index 90b4bdb73..c5e5823b6 100755
--- a/tests/date/date-next-dow.pl
+++ b/tests/date/date-next-dow.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test "date".
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/date/date-sec.sh b/tests/date/date-sec.sh
index c9010ac1c..5ead54e07 100755
--- a/tests/date/date-sec.sh
+++ b/tests/date/date-sec.sh
@@ -3,7 +3,7 @@
# date --date="21:04 +0100" +%S' always prints '00'.
# Before coreutils-5.2.1, it would print the seconds from the current time.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/date/date-thailand.sh b/tests/date/date-thailand.sh
index 18a49f27c..20d9871a8 100755
--- a/tests/date/date-thailand.sh
+++ b/tests/date/date-thailand.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify the Thai solar calendar is used with the Thai locale.
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/date/date-tz.sh b/tests/date/date-tz.sh
index 05736f90d..0fd8ac16a 100755
--- a/tests/date/date-tz.sh
+++ b/tests/date/date-tz.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify TZ processing.
-# Copyright (C) 2017-2025 Free Software Foundation, Inc.
+# Copyright (C) 2017-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/date/date.pl b/tests/date/date.pl
index 6971a7c7c..187c7c6e9 100755
--- a/tests/date/date.pl
+++ b/tests/date/date.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test "date".
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,6 +18,12 @@
use strict;
+my $limits = getlimits ();
+# Most systems support INT_MAX + 1900, but OpenBSD 7.8
+# limits tm_year in mktime to INT_MAX.
+my $large_year = $limits->{INT_MAX};
+my $large_year_out_of_range = $large_year + 1900 + 1;
+
(my $ME = $0) =~ s|.*/||;
# Turn off localization of executable's output.
@@ -196,6 +202,16 @@ my @Tests =
['moname-d-y', '--iso -d May-23-2003', {OUT=>"2003-05-23"}],
['moname-d-y-r', '--rfc-3339=date -d May-23-2003', {OUT=>"2003-05-23"}],
+ # Ensure we can parse DAY.MONTH.YEAR and its short form DAY.MONTH.
+ ['european-0', "-I -d '24.01.2026'", {OUT=>"2026-01-24"}],
+ ['european-1', "-I -d '24.1.2026'", {OUT=>"2026-01-24"}],
+ ['european-2', "-d '24.01.' +%m-%d", {OUT=>"01-24"}],
+ ['european-3', "-d '1.2.' +%m-%d", {OUT=>"02-01"}],
+ ['eu-combo-1', "-d '1.2. 3:4:5.6' +%m-%d-%T", {OUT=>"02-01-03:04:05"}],
+ ['eu-combo-2', "-d '3:4:5.6 1.2.' +%m-%d-%T", {OUT=>"02-01-03:04:05"}],
+ ['eu-combo-3', "-d '1.2. 3' +%m-%d-%H", {OUT=>"02-01-03"}],
+ ['eu-combo-4', "-d '3 1.2.' +%m-%d-%H", {OUT=>"02-01-03"}],
+
['epoch', '--iso=sec -d @31536000',
{OUT=>"1971-01-01T00:00:00+00:00"}],
['epoch-r', '--rfc-3339=sec -d @31536000',
@@ -230,6 +246,8 @@ my @Tests =
['tz-6', '+%Z', {OUT=>"UTC"}],
['tz-7', '+%Z', {OUT=>"JST"}, {ENV=>'TZ=JST-9'}],
+ ['arg-order', '+%Y-%m-%d -d 2026-02-13', {OUT=>"2026-02-13"}],
+
['ns-relative',
'--iso=ns',
"-d'1970-01-01 00:00:00.1234567 UTC +961062237.987654321 sec'",
@@ -275,6 +293,23 @@ my @Tests =
['fill-1', '-d 1999-12-08 +%_3d', {OUT=>' 8'}],
['fill-2', '-d 1999-12-08 +%03d', {OUT=>'008'}],
+ # Test modifier edge cases: flags without explicit width
+ ['underscore-no-width-1', '-d 1999-06-01 +%_d', {OUT=>' 1'}],
+ ['underscore-no-width-2', '-d 1999-06-15 +%_m', {OUT=>' 6'}],
+ ['underscore-no-width-3', '-d "1999-06-01 05:00:00" +%_H', {OUT=>' 5'}],
+ ['underscore-no-width-4', '-d 1999-06-01 +%_Y', {OUT=>'1999'}],
+ ['underscore-no-width-5', '-d 1999-06-01 +%_C', {OUT=>'19'}],
+ ['underscore-no-width-6', '-d 1999-01-01 +%_j', {OUT=>' 1'}],
+
+ # Test zero flag overriding space-padded specifiers
+ ['zero-override-space-1', '-d 1999-06-05 +%0e', {OUT=>'05'}],
+ ['zero-override-space-2', '-d "1999-06-01 05:00:00" +%0k', {OUT=>'05'}],
+ ['zero-override-space-3', '-d "1999-06-01 05:00:00" +%0l', {OUT=>'05'}],
+
+ # Test plus flag without width (should not add sign for 4-digit year)
+ ['plus-no-width-1', '-d 1999-06-01 +%+Y', {OUT=>'1999'}],
+ ['plus-no-width-2', '-d 1999-06-01 +%+6Y', {OUT=>'+01999'}],
+
# Test the combination of the to-upper-case modifier (^) and a conversion
# specifier that expands to a string containing lower case characters.
['subfmt-up1', '-d "1999-12-08 7:30" "+%^c"',
@@ -319,8 +354,67 @@ my @Tests =
# test with %%-N
['pct-pct', '+%%-N', {OUT => '%-N'}],
+
+ # Test parenthesis comment handling
+ # Single parenthesis - should be treated as empty string (midnight today)
+ ['paren-1', "-d '(' +'%H:%M:%S'", {OUT=>"00:00:00"}],
+
+ # Parenthesis with preceding text - comment should be ignored
+ ['paren-2', "-d '1(ignore this comment' +'%H:%M:%S'", {OUT=>"01:00:00"}],
+
+ # Parenthesis with date - comment should be ignored
+ ['paren-3', "-d '2026-01-05(this is a comment' -u +'%Y-%m-%d'",
+ {OUT=>"2026-01-05"}],
+
+ # Text enclosed in parentheses is treated as a comment
+ ['paren-4', "-d '2026(this is a comment)-01-05' -u +'%Y-%m-%d'",
+ {OUT=>"2026-01-05"}],
+
+ # Nested comments are supported
+ ['paren-5', "-d '((nested))2026-01-05' -u +'%Y-%m-%d'",
+ {OUT=>"2026-01-05"}],
+ ['paren-6', "-d '((nested)2026-01-05)' +'%H:%M:%S'", {OUT=>"00:00:00"}],
+
+ # Test timezone conversion with -u -d flag
+ # "10:30 UTC-05" should convert to "15:30 UTC", not "10:30 UTC"
+ ['tz-conversion-est', "-u -d '10:30 UTC-05' +'%H:%M'", {OUT=>"15:30"}],
+
+ # Test multiple --iso-8601 options.
+ ['multiple-iso1', "--iso-8601 --iso-8601 -d '2026-05-11'",
+ {OUT=>"2026-05-11"}],
+ ['multiple-iso2', "--iso-8601=hours --iso-8601=minutes -d '2026-05-11'",
+ {OUT=>"2026-05-11T00:00+00:00"}],
+ ['multiple-iso3', "--iso-8601=minutes --iso-8601=hours -d '2026-05-11'",
+ {OUT=>"2026-05-11T00+00:00"}],
+
+ # Test that using both a format string and format option fails.
+ ['multiple-fmt1', "--iso-8601 -d '2026-05-15' +'%Y'",
+ {ERR => "date: multiple output formats specified\n"},
+ {EXIT => 1},
+ ],
+ ['multiple-fmt2', "--rfc-email -d '2026-05-15' +'%Y'",
+ {ERR => "date: multiple output formats specified\n"},
+ {EXIT => 1},
+ ],
+ ['multiple-fmt3', "--rfc-3339=date -d '2026-05-15' +'%Y'",
+ {ERR => "date: multiple output formats specified\n"},
+ {EXIT => 1},
+ ],
+
);
+$limits->{TIME_T_MAX} == $limits->{INTMAX_MAX}
+ and push @Tests,
+ ['large-year', "-d '$large_year-01-01' +'%Y-%m-%d'",
+ {OUT=>"$large_year-01-01"}];
+
+push @Tests,
+ ['large-year-out-of-range',
+ "-d '$large_year_out_of_range-01-01' +'%Y-%m-%d'",
+ {ERR => "date: invalid date '$large_year_out_of_range-01-01'\n"},
+ {EXIT => 1},
+ ];
+
# Repeat the cross-dst test, using Jan 1, 2005 and every interval from 1..364.
foreach my $i (1..364)
{
diff --git a/tests/date/percent-percent.sh b/tests/date/percent-percent.sh
new file mode 100755
index 000000000..998791c0e
--- /dev/null
+++ b/tests/date/percent-percent.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+# Test that 'date' does not mishandle %% in the format string.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ date
+
+for loc in C "$LOCALE_FR" "$LOCALE_FR_UTF8"; do
+ test -z "$loc" && continue
+ # Time conversion specifiers.
+ fmt=$(echo HIklMNpPrRsSTXzZ | sed 's/./%%&/g') || framework_failure_
+ echo $fmt | sed 's/%%/%/g' > exp || framework_failure_
+ LC_ALL="$loc" date +"$fmt" > out || fail=1
+ compare exp out || fail=1
+ # Date conversion specifiers.
+ fmt=$(echo aAbBcCdDeFgGhjmuUVwWxyY | sed 's/./%%&/g') || framework_failure_
+ echo $fmt | sed 's/%%/%/g' > exp || framework_failure_
+ LC_ALL="$loc" date +"$fmt" > out || fail=1
+ compare exp out || fail=1
+done
+
+Exit $fail
diff --git a/tests/date/reference.sh b/tests/date/reference.sh
index f7db9fd5b..23e31e611 100755
--- a/tests/date/reference.sh
+++ b/tests/date/reference.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test date --reference
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -27,7 +27,7 @@ export TZ
t1='2025-10-23 03:00'
t2='2025-10-23 04:00'
-# date(1) only considers modiication time
+# date(1) only considers modification time
touch -m -d "$t1" a || framework_failure_
touch -m -d "$t2" b || framework_failure_
diff --git a/tests/date/resolution.sh b/tests/date/resolution.sh
index 4fbf4501f..95de2cd5f 100755
--- a/tests/date/resolution.sh
+++ b/tests/date/resolution.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test date resolution interface
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/dd/ascii.sh b/tests/dd/ascii.sh
index 62bc8a62c..f3c77e223 100755
--- a/tests/dd/ascii.sh
+++ b/tests/dd/ascii.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test conv=ascii
-# Copyright (C) 2014-2025 Free Software Foundation, Inc.
+# Copyright (C) 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/dd/bytes.sh b/tests/dd/bytes.sh
index 105a423ca..fa7295ac8 100755
--- a/tests/dd/bytes.sh
+++ b/tests/dd/bytes.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -60,7 +60,7 @@ for operands in "oseek=8B" "seek=8 oflag=seek_bytes"; do
compare expected2 out2 || fail=1
done
-# Check recursive integer parsing
+# Check multiplicative integer parsing
for oseek in '1x2x4 oflag=seek_bytes' '1Bx2x4' '1Bx8' '2Bx4B' '2x4B'; do
# seek bytes
echo abcdefghijklm |
@@ -68,6 +68,13 @@ for oseek in '1x2x4 oflag=seek_bytes' '1Bx2x4' '1Bx8' '2Bx4B' '2x4B'; do
compare expected out || fail=1
done
+# Check that long multiplier chains don't exhaust a restricted stack.
+if (ulimit -S -s 256 && dd if=/dev/null count=1) 2>/dev/null; then
+ long_multiplier=$(yes 1x | head -n 10000 | tr -d '\n')1 || framework_failure_
+ (ulimit -S -s 256 &&
+ dd count="$long_multiplier" if=/dev/null of=/dev/null status=none) || fail=1
+fi
+
# Negative checks for integer parsing
for count in B B1 Bx1 KBB BB KBb KBx x1 1x 1xx1; do
returns_ 1 dd count=$count </dev/null >/dev/null || fail=1
diff --git a/tests/dd/conv-case.sh b/tests/dd/conv-case.sh
new file mode 100755
index 000000000..08607cd4d
--- /dev/null
+++ b/tests/dd/conv-case.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+# test conv=lcase and conv=ucase
+
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ dd
+
+printf 'abcdefghijklmnopqrstuvwxyz\n' > input-lower || framework_failure_
+printf 'ABCDEFGHIJKLMNOPQRSTUVWXYZ\n' > input-upper || framework_failure_
+
+# Check the output when all input characters are already the correct case.
+dd if=input-lower of=output-lower conv=lcase || fail=1
+cmp input-lower output-lower || fail=1
+dd if=input-upper of=output-upper conv=ucase || fail=1
+cmp input-upper output-upper || fail=1
+
+# Check the output when all input characters need case conversion.
+dd if=input-upper of=output-lower conv=lcase || fail=1
+cmp input-lower output-lower || fail=1
+dd if=input-lower of=output-upper conv=ucase || fail=1
+cmp input-upper output-upper || fail=1
+
+export LC_ALL=en_US.iso8859-1 # only lowercase form works on macOS 10.15.7
+if test "$(locale charmap 2>/dev/null | sed 's/iso/ISO-/')" = ISO-8859-1; then
+ # Case conversion should work on all single byte locales.
+ # Check it with é and É in ISO 8859-1.
+ printf '\351\n' > input-lower
+ printf '\311\n' > input-upper
+
+ # Check the output when all input characters are already the correct case.
+ dd if=input-lower of=output-lower conv=lcase || fail=1
+ cmp input-lower output-lower || fail=1
+ dd if=input-upper of=output-upper conv=ucase || fail=1
+ cmp input-upper output-upper || fail=1
+
+ # Check the output when all input characters need case conversion.
+ dd if=input-upper of=output-lower conv=lcase || fail=1
+ cmp input-lower output-lower || fail=1
+ dd if=input-lower of=output-upper conv=ucase || fail=1
+ cmp input-upper output-upper || fail=1
+fi
+
+Exit $fail
diff --git a/tests/dd/direct.sh b/tests/dd/direct.sh
index 1e9764b94..55e6f32a7 100755
--- a/tests/dd/direct.sh
+++ b/tests/dd/direct.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that dd's iflag=direct and oflag=direct work
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/dd/fail-ftruncate-fstat.sh b/tests/dd/fail-ftruncate-fstat.sh
new file mode 100755
index 000000000..219273bbc
--- /dev/null
+++ b/tests/dd/fail-ftruncate-fstat.sh
@@ -0,0 +1,77 @@
+#!/bin/sh
+# Check that 'dd' does not continue copying if ftruncate and fstat fail.
+
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ dd
+require_gcc_shared_
+getlimits_
+
+cat > k.c <<'EOF' || framework_failure_
+#include <sys/stat.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+int
+ftruncate (int fd, off_t length)
+{
+ /* Prove that LD_PRELOAD works: create the evidence file "x". */
+ fclose (fopen ("x", "w"));
+
+ /* Pretend failure. */
+ errno = EPERM;
+ return -1;
+}
+
+int
+fstat (int fd, struct stat *statbuf)
+{
+ /* Prove that LD_PRELOAD works: create the evidence file "y". */
+ fclose (fopen ("y", "w"));
+
+ /* Pretend failure. */
+ errno = EPERM;
+ return -1;
+}
+EOF
+
+# Then compile/link it:
+gcc_shared_ k.c k.so \
+ || framework_failure_ 'failed to build shared library'
+
+# Setup the file "out" and preserve it's original contents in "exp-out".
+yes | head -n 2048 | tr -d '\n' > out || framework_failure_
+cp out exp-out || framework_failure_
+
+LD_PRELOAD=$LD_PRELOAD:./k.so dd if=/dev/zero of=out count=1 \
+ seek=1 status=none 2>err
+ret=$?
+
+test -f x && test -f y \
+ || skip_ "internal test failure: maybe LD_PRELOAD doesn't work?"
+
+# After ftruncate fails, we use fstat to get the file type.
+echo "dd: cannot fstat 'out': $EPERM" > exp
+compare exp err || fail=1
+
+# coreutils 9.1 to 9.9 would mistakenly continue copying after ftruncate
+# failed and exit successfully.
+test "$ret" = 1 || fail=1
+compare exp-out out || fail=1
+
+Exit $fail
diff --git a/tests/dd/misc.sh b/tests/dd/misc.sh
index 6bae34405..aa776b137 100755
--- a/tests/dd/misc.sh
+++ b/tests/dd/misc.sh
@@ -2,7 +2,7 @@
# Ensure dd treats '--' properly.
# Also test some flag values.
-# Copyright (C) 1999-2025 Free Software Foundation, Inc.
+# Copyright (C) 1999-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -124,4 +124,10 @@ dd if=$tmp_in of=$tmp_out count=00x$big status=noxfer 2>err || fail=1
compare /dev/null $tmp_out || fail=1
compare err_ok err || fail=1
+# Ensure of=/dev/tty is possible
+if test -c /dev/tty && >/dev/tty; then
+ dd if=/dev/null of=/dev/tty || fail=1
+fi
+
+
Exit $fail
diff --git a/tests/dd/no-allocate.sh b/tests/dd/no-allocate.sh
index 6746d5c70..02fdef5ee 100755
--- a/tests/dd/no-allocate.sh
+++ b/tests/dd/no-allocate.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure that dd doesn't allocate memory unnecessarily
-# Copyright (C) 2013-2025 Free Software Foundation, Inc.
+# Copyright (C) 2013-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ dd
+getlimits_
# Determine basic amount of memory needed.
echo . > f || framework_failure_
@@ -25,6 +26,16 @@ vm=$(get_min_ulimit_v_ timeout 10 dd if=f of=f2 status=none) \
|| skip_ 'shell lacks ulimit, or ASAN enabled'
rm f f2 || framework_failure_
+# Ensure dd exits with 1 if memory exhausted
+(ulimit -v $(($vm+6000)) && returns_ 1 \
+ dd if=/dev/null of=/dev/null bs=$(($SSIZE_MAX-1))) || fail=1
+# Ensure dd exits with 1 on numeric overflow
+(ulimit -v $(($vm+6000)) && returns_ 1 \
+ dd if=/dev/null of=/dev/null bs=$SIZE_OFLOW) || fail=1
+# Ensure dd exits with 1 on invalid number
+(ulimit -v $(($vm+6000)) && returns_ 1 \
+ dd if=/dev/null of=/dev/null bs=0) || fail=1
+
# count and skip are zero, we don't need to allocate memory
(ulimit -v $vm && dd bs=30M count=0) || fail=1
(ulimit -v $vm && dd ibs=30M count=0) || fail=1
diff --git a/tests/dd/nocache.sh b/tests/dd/nocache.sh
index 48626b063..405a7d12c 100755
--- a/tests/dd/nocache.sh
+++ b/tests/dd/nocache.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure dd handles the 'nocache' flag
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/dd/nocache_eof.sh b/tests/dd/nocache_eof.sh
index feb2d5021..5758e1411 100755
--- a/tests/dd/nocache_eof.sh
+++ b/tests/dd/nocache_eof.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure dd invalidates to EOF when appropriate
-# Copyright (C) 2017-2025 Free Software Foundation, Inc.
+# Copyright (C) 2017-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/dd/nocache_fail.sh b/tests/dd/nocache_fail.sh
index 4d30c8558..0395ccaca 100755
--- a/tests/dd/nocache_fail.sh
+++ b/tests/dd/nocache_fail.sh
@@ -2,7 +2,7 @@
# Ensure we diagnose failure to drop caches
# We didn't check the return from posix_fadvise() correctly before v9.7
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/dd/not-rewound.sh b/tests/dd/not-rewound.sh
index bc107a5d8..752332b74 100755
--- a/tests/dd/not-rewound.sh
+++ b/tests/dd/not-rewound.sh
@@ -2,7 +2,7 @@
# Make sure dd does the right thing when the input file descriptor
# is not rewound.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/dd/partial-write.sh b/tests/dd/partial-write.sh
new file mode 100755
index 000000000..80885d056
--- /dev/null
+++ b/tests/dd/partial-write.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+# Ensure partial writes are properly diagnosed
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ dd
+
+(
+ ulimit -S -f 1024 || skip_ 'unable to set file size ulimit'
+ trap '' XFSZ || skip_ 'unable to ignore SIGXFSZ'
+ dd if=/dev/zero of=f bs=768K count=2 2>err
+)
+ret=$?
+
+if test $ret = 1; then
+ test -s f || skip_ 'The system disallowed all writes'
+ grep -F '+1 records out' err || { cat err; fail=1; }
+elif test $ret = 0; then
+ skip_ 'The system did not limit the file fize'
+else
+ framework_failure_
+fi
+
+Exit $fail
diff --git a/tests/dd/reblock.sh b/tests/dd/reblock.sh
index dd4485597..aba2f9b97 100755
--- a/tests/dd/reblock.sh
+++ b/tests/dd/reblock.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test dd reblocking vs. bs=
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/dd/skip-seek-past-dev.sh b/tests/dd/skip-seek-past-dev.sh
index cb454b908..0305bf6f9 100755
--- a/tests/dd/skip-seek-past-dev.sh
+++ b/tests/dd/skip-seek-past-dev.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test diagnostics are printed immediately when seeking beyond device.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/dd/skip-seek-past-file.sh b/tests/dd/skip-seek-past-file.sh
index 543938c10..8ff842667 100755
--- a/tests/dd/skip-seek-past-file.sh
+++ b/tests/dd/skip-seek-past-file.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test diagnostics are printed when seeking too far in seekable files.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/dd/skip-seek.pl b/tests/dd/skip-seek.pl
index 5bae675b2..b4b37b0b3 100755
--- a/tests/dd/skip-seek.pl
+++ b/tests/dd/skip-seek.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test dd's skip and seek options.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/dd/skip-seek2.sh b/tests/dd/skip-seek2.sh
index 0a8aa3517..47df40204 100755
--- a/tests/dd/skip-seek2.sh
+++ b/tests/dd/skip-seek2.sh
@@ -2,7 +2,7 @@
# show how to skip an amount that is smaller than the nominal block size.
# There's a more realistic example in the documentation.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/dd/sparse.sh b/tests/dd/sparse.sh
index 5ecb5664a..d2a631dbe 100755
--- a/tests/dd/sparse.sh
+++ b/tests/dd/sparse.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/dd/stats.sh b/tests/dd/stats.sh
index c760cc7f9..db650f80d 100755
--- a/tests/dd/stats.sh
+++ b/tests/dd/stats.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Check stats output for SIG{INFO,USR1} and status=progress
-# Copyright (C) 2014-2025 Free Software Foundation, Inc.
+# Copyright (C) 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/dd/stderr.sh b/tests/dd/stderr.sh
index 3a5e56a1c..9b9165579 100755
--- a/tests/dd/stderr.sh
+++ b/tests/dd/stderr.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure dd recognizes failure to write to stderr.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/dd/unblock-sync.sh b/tests/dd/unblock-sync.sh
index f897767d9..e91577ada 100755
--- a/tests/dd/unblock-sync.sh
+++ b/tests/dd/unblock-sync.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that dd conv=unblock,sync works.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/dd/unblock.pl b/tests/dd/unblock.pl
index 5904e00e6..c963be40a 100755
--- a/tests/dd/unblock.pl
+++ b/tests/dd/unblock.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Exercise dd's conv=unblock mode
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/df/df-P.sh b/tests/df/df-P.sh
index d35597fe9..9a970c237 100755
--- a/tests/df/df-P.sh
+++ b/tests/df/df-P.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that df -P is not affected by BLOCK_SIZE settings
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/df/df-output.sh b/tests/df/df-output.sh
index d9ec5ef27..de062ee78 100755
--- a/tests/df/df-output.sh
+++ b/tests/df/df-output.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise df's --output option.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -125,7 +125,7 @@ compare exp out2 || fail=1
# Ensure that --output is mentioned in the usage.
df --help > out || fail=1
-grep ' --output' out >/dev/null || { fail=1; cat out; }
+grep -- '--output' out >/dev/null || { fail=1; cat out; }
# Ensure that the FILE field contains the argument.
cat <<\EOF > exp || framework_failure_
diff --git a/tests/df/df-symlink.sh b/tests/df/df-symlink.sh
index 863c4f029..bfbfbb6d7 100755
--- a/tests/df/df-symlink.sh
+++ b/tests/df/df-symlink.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that df dereferences symlinks to file system nodes
-# Copyright (C) 2013-2025 Free Software Foundation, Inc.
+# Copyright (C) 2013-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/df/header.sh b/tests/df/header.sh
index 59e6f75f4..727139c63 100755
--- a/tests/df/header.sh
+++ b/tests/df/header.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that "df ." outputs a header.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/df/no-mtab-status-masked-proc.sh b/tests/df/no-mtab-status-masked-proc.sh
new file mode 100755
index 000000000..d65c339f0
--- /dev/null
+++ b/tests/df/no-mtab-status-masked-proc.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+# Test df's behavior when /proc cannot be read.
+# This is an alternative for no-mtab-status.sh for static binaries.
+# This test is skipped if User namespace sandbox is unavailable.
+
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ df
+skip_if_root_
+
+# Sanitizers need to read from /proc
+sanitizer_build_ df && skip_ 'Sanitizer not supported'
+
+# Protect against inaccessible remote mounts etc.
+timeout 10 df || skip_ "df fails"
+
+unshare -rm unshare --version || skip_ 'User namespace sandbox is disabled'
+
+# mask /proc
+df() {
+ unshare -rm $SHELL -c \
+ "mount -t tmpfs tmpfs /proc && env df \"\$@\"" -- "$@";
+}
+
+df /proc || fail=1
+returns_ 1 df /proc/self || framework_failure_
+
+# Keep the following in sync with no-mtab-status.sh
+
+# These tests are supposed to succeed:
+df '.' || fail=1
+df -i '.' || fail=1
+df -T '.' || fail=1
+df -Ti '.' || fail=1
+df --total '.' || fail=1
+
+# These tests are supposed to fail:
+returns_ 1 df || fail=1
+returns_ 1 df -i || fail=1
+returns_ 1 df -T || fail=1
+returns_ 1 df -Ti || fail=1
+returns_ 1 df --total || fail=1
+
+returns_ 1 df -a || fail=1
+returns_ 1 df -a '.' || fail=1
+
+returns_ 1 df -l || fail=1
+returns_ 1 df -l '.' || fail=1
+
+returns_ 1 df -t hello || fail=1
+returns_ 1 df -t hello '.' || fail=1
+
+returns_ 1 df -x hello || fail=1
+returns_ 1 df -x hello '.' || fail=1
+
+Exit $fail
diff --git a/tests/df/no-mtab-status.sh b/tests/df/no-mtab-status.sh
index 021e94991..6573476ff 100755
--- a/tests/df/no-mtab-status.sh
+++ b/tests/df/no-mtab-status.sh
@@ -2,7 +2,7 @@
# Test df's behavior when the mount list cannot be read.
# This test is skipped on systems that lack LD_PRELOAD support; that's fine.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -28,8 +28,8 @@ grep '^#define HAVE_GETMNTENT 1' $CONFIG_HEADER > /dev/null \
|| skip_ "getmntent is not used on this system"
# Simulate "mtab" failure.
-# Replace gnulib streq and C23 nullptr as that are not available here.
-sed 's/streq/0==str''cmp/; s/nullptr/NU''LL/' > k.c <<EOF || framework_failure_
+# Replace gnulib streq as that is not available here.
+sed 's/streq/0==str''cmp/' > k.c <<EOF || framework_failure_
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
@@ -40,9 +40,9 @@ sed 's/streq/0==str''cmp/; s/nullptr/NU''LL/' > k.c <<EOF || framework_failure_
#include <stdarg.h>
#include <dlfcn.h>
-static FILE* (*fopen_func)(const char *, const char *);
+static FILE *(*fopen_func) (const char *, const char *);
-FILE* fopen(const char *path, const char *mode)
+FILE *fopen(const char *path, const char *mode)
{
/* get reference to original (libc provided) fopen */
@@ -54,7 +54,7 @@ FILE* fopen(const char *path, const char *mode)
{
fprintf (stderr, "Failed to find fopen()\n");
errno = ESRCH;
- return nullptr;
+ return NULL;
}
}
@@ -63,7 +63,7 @@ FILE* fopen(const char *path, const char *mode)
if (streq (path, "/proc/self/mountinfo"))
{
errno = ENOENT;
- return nullptr;
+ return NULL;
}
return fopen_func(path, mode);
@@ -115,7 +115,7 @@ struct mntent *getmntent (FILE *fp)
}
/* Now simulate the failure. */
errno = ENOENT;
- return nullptr;
+ return NULL;
}
EOF
diff --git a/tests/df/over-mount-device.sh b/tests/df/over-mount-device.sh
index 436f823b4..6691d8f6e 100755
--- a/tests/df/over-mount-device.sh
+++ b/tests/df/over-mount-device.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that df /dev/loop0 errors out if overmounted by another device
-# Copyright (C) 2014-2025 Free Software Foundation, Inc.
+# Copyright (C) 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/df/problematic-chars.sh b/tests/df/problematic-chars.sh
index 12e1b9bb4..f31f1774f 100755
--- a/tests/df/problematic-chars.sh
+++ b/tests/df/problematic-chars.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that df outputs one line per entry
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/df/skip-duplicates.sh b/tests/df/skip-duplicates.sh
index 8521a9964..aa1a9ac30 100755
--- a/tests/df/skip-duplicates.sh
+++ b/tests/df/skip-duplicates.sh
@@ -2,7 +2,7 @@
# Test df's behavior when the mount list contains duplicate entries.
# This test is skipped on systems that lack LD_PRELOAD support; that's fine.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -38,8 +38,8 @@ grep '^#define HAVE_GETMNTENT 1' $CONFIG_HEADER > /dev/null \
|| skip_ "getmntent is not used on this system"
# Simulate an mtab file to test various cases.
-# Replace gnulib streq and C23 nullptr as that are not available here.
-sed 's/streq/0==str''cmp/; s/nullptr/NU''LL/' > k.c <<EOF || framework_failure_
+# Replace gnulib streq as that is not available here.
+sed 's/streq/0==str''cmp/' > k.c <<EOF || framework_failure_
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
@@ -64,7 +64,7 @@ FILE* fopen(const char *path, const char *mode)
{
fprintf (stderr, "Failed to find fopen()\n");
errno = ESRCH;
- return nullptr;
+ return NULL;
}
}
@@ -73,7 +73,7 @@ FILE* fopen(const char *path, const char *mode)
if (streq (path, "/proc/self/mountinfo"))
{
errno = ENOENT;
- return nullptr;
+ return NULL;
}
return fopen_func(path, mode);
@@ -138,6 +138,8 @@ struct mntent *getmntent (FILE *fp)
{.mnt_fsname="rem:ote1",.mnt_dir="/REMOTE", .mnt_opts=""},
{.mnt_fsname="rem:ote1",.mnt_dir="/REMOTE", .mnt_opts=""},
{.mnt_fsname="rem:ote2",.mnt_dir="/REMOTE", .mnt_opts=""},
+ {.mnt_fsname="rem:ote1",.mnt_dir="/REMOTE", .mnt_opts=""},
+ {.mnt_fsname="rem:ote2",.mnt_dir="/REMOTE", .mnt_opts=""},
};
if (done == 1)
@@ -152,7 +154,7 @@ struct mntent *getmntent (FILE *fp)
if (done == 1 && !getenv ("CU_TEST_DUPE_INVALID"))
done++; /* skip the first entry. */
- while (done++ <= 10)
+ while (done++ <= 12)
{
if (!mntents[done-2].mnt_type)
mntents[done-2].mnt_type = "-";
@@ -210,7 +212,7 @@ test $(grep -c 'virtfs2.*t2' <out) -eq 1 || { fail=1; cat out; }
# Ensure that filtering duplicates does not affect -a processing.
LD_PRELOAD=$LD_PRELOAD:./k.so df -a >out || fail=1
-total_fs=6; test "$CU_REMOTE_FS" && total_fs=$(expr $total_fs + 3)
+total_fs=6; test "$CU_REMOTE_FS" && total_fs=$(expr $total_fs + 5)
test $(wc -l <out) -eq $total_fs || { fail=1; cat out; }
# Ensure placeholder "-" values used for the eclipsed "virtfs"
test $(grep -c 'virtfs *-' <out) -eq 1 || { fail=1; cat out; }
diff --git a/tests/df/skip-rootfs.sh b/tests/df/skip-rootfs.sh
index c751c8d4e..1cca54700 100755
--- a/tests/df/skip-rootfs.sh
+++ b/tests/df/skip-rootfs.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test df's behavior for skipping the pseudo "rootfs" file system.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/df/sync.sh b/tests/df/sync.sh
new file mode 100755
index 000000000..6bef02980
--- /dev/null
+++ b/tests/df/sync.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+# Ensure 'df --sync' works.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ df
+
+# This test is marked "very expensive" since calling sync() can take a long
+# time on a busy system.
+very_expensive_
+require_strace_ 'sync,statfs,fstatfs'
+
+# Check that sync was called before statfs or statvfs.
+check_sync ()
+{
+ seen_sync=0
+ while IFS= read line; do
+ case "$line" in
+ sync\(*) seen_sync=1 ;;
+ statfs\(*|fstatfs\(*)
+ if test "$seen_sync" -eq 1; then
+ return 0
+ else
+ return 1
+ fi
+ ;;
+ esac
+ done < "$1"
+ # Fail if we don't see statfs or fstatfs.
+ return 1
+}
+
+# Make sure that 'df --sync' calls sync() before gathering usage information.
+strace -o strace.out -e trace=sync,statfs,fstatfs df --sync || fail=1
+check_sync strace.out || fail=1
+
+# Also check it with a file given as the argument.
+strace -o strace.out -e trace=sync,statfs,fstatfs df --sync . || fail=1
+check_sync strace.out || fail=1
+
+Exit $fail
diff --git a/tests/df/total-unprocessed.sh b/tests/df/total-unprocessed.sh
index 1f69ed34a..6747fdb90 100755
--- a/tests/df/total-unprocessed.sh
+++ b/tests/df/total-unprocessed.sh
@@ -2,7 +2,7 @@
# Ensure that df exits non-Zero and writes an error message when
# --total is used but no file system has been processed.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/df/total-verify.sh b/tests/df/total-verify.sh
index db4cf00cc..2bfadb0ba 100755
--- a/tests/df/total-verify.sh
+++ b/tests/df/total-verify.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure "df --total" computes accurate totals
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/df/unreadable.sh b/tests/df/unreadable.sh
index 6e28ab6fb..7a4d4f776 100755
--- a/tests/df/unreadable.sh
+++ b/tests/df/unreadable.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that df can handle an unreadable argument
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/2g.sh b/tests/du/2g.sh
index b78fe80c0..eca77d954 100755
--- a/tests/du/2g.sh
+++ b/tests/du/2g.sh
@@ -3,7 +3,7 @@
# Before coreutils-5.93, on systems with a signed, 32-bit stat.st_blocks
# one of du's computations would overflow.
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/8gb.sh b/tests/du/8gb.sh
index 803285e2c..902db0028 100755
--- a/tests/du/8gb.sh
+++ b/tests/du/8gb.sh
@@ -2,7 +2,7 @@
# Ensure that du does not rely on narrow types like size_t for
# file sizes or sums.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/apparent.sh b/tests/du/apparent.sh
index 4a1064fc6..f740accec 100755
--- a/tests/du/apparent.sh
+++ b/tests/du/apparent.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise du's --apparent-size option.
-# Copyright 2023-2025 Free Software Foundation, Inc.
+# Copyright 2023-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -24,10 +24,13 @@ for f in $(seq 100); do
echo foo >d/$f || framework_failure_
done
-du -b d/* >separate || fail=1
-du -b d >together || fail=1
-separate_sum=$($AWK '{sum+=$1}END{print sum}' separate) || framework_failure_
-together_sum=$($AWK '{sum+=$1}END{print sum}' together) || framework_failure_
-test $separate_sum -eq $together_sum || fail=1
+# Check that the following options are equivalent.
+for opts in '-b' '-A -B 1' '--apparent-size --block-size 1'; do
+ du $opts d/* >separate || fail=1
+ du $opts d >together || fail=1
+ separate_sum=$($AWK '{sum+=$1}END{print sum}' separate) || framework_failure_
+ together_sum=$($AWK '{sum+=$1}END{print sum}' together) || framework_failure_
+ test $separate_sum -eq $together_sum || fail=1
+done
Exit $fail
diff --git a/tests/du/basic.sh b/tests/du/basic.sh
index 88ea7f7ea..3bf74f712 100755
--- a/tests/du/basic.sh
+++ b/tests/du/basic.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Compare actual numbers from du, assuming block size matches mine.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/bigtime.sh b/tests/du/bigtime.sh
index cba55aaa7..d44b0d46f 100755
--- a/tests/du/bigtime.sh
+++ b/tests/du/bigtime.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise du on a file with a big timestamp.
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/bind-mount-dir-cycle-v2.sh b/tests/du/bind-mount-dir-cycle-v2.sh
index 2788e2a24..4e41ff49d 100755
--- a/tests/du/bind-mount-dir-cycle-v2.sh
+++ b/tests/du/bind-mount-dir-cycle-v2.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Check that du can handle sub-bind-mounts cycles as well.
-# Copyright (C) 2014-2025 Free Software Foundation, Inc.
+# Copyright (C) 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/bind-mount-dir-cycle.sh b/tests/du/bind-mount-dir-cycle.sh
index f30ea7b43..7db9e7550 100755
--- a/tests/du/bind-mount-dir-cycle.sh
+++ b/tests/du/bind-mount-dir-cycle.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise du's new ability to handle bind-mount-induced dir cycles.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/deref-args.sh b/tests/du/deref-args.sh
index 0b3bafe9a..dd2fa21fb 100755
--- a/tests/du/deref-args.sh
+++ b/tests/du/deref-args.sh
@@ -2,7 +2,7 @@
# Ensure that --dereference-args (-D) gives reasonable names.
# This test would fail for coreutils-5.0.91.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/deref.sh b/tests/du/deref.sh
index 08b2ab783..0f1518285 100755
--- a/tests/du/deref.sh
+++ b/tests/du/deref.sh
@@ -4,7 +4,7 @@
# Also, up to coreutils-8.5, du -L sometimes incorrectly
# counted the space of the followed symlinks.
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/exclude.sh b/tests/du/exclude.sh
index 7076afb37..e2cc162df 100755
--- a/tests/du/exclude.sh
+++ b/tests/du/exclude.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure du's --exclude option works
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/fd-leak.sh b/tests/du/fd-leak.sh
index b5e8805a8..b09e9beee 100755
--- a/tests/du/fd-leak.sh
+++ b/tests/du/fd-leak.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# check for file descriptor leak
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/files0-from-dir.sh b/tests/du/files0-from-dir.sh
index 45e78278e..5c659f186 100755
--- a/tests/du/files0-from-dir.sh
+++ b/tests/du/files0-from-dir.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that du and wc handle --files0-from=DIR
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/files0-from.pl b/tests/du/files0-from.pl
index fe053f648..5bbc671cb 100755
--- a/tests/du/files0-from.pl
+++ b/tests/du/files0-from.pl
@@ -2,7 +2,7 @@
# Exercise du's --files0-from option.
# FIXME: keep this file in sync with tests/misc/wc-files0-from.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -23,6 +23,8 @@ use strict;
my $prog = 'du';
+my $limits = getlimits ();
+
# Turn off localization of executable's output.
@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3;
@@ -36,9 +38,21 @@ my @Tests =
],
# missing input file
- ['missing', '--files0-from=missing', {EXIT=>1},
+ ['missing1', '--files0-from=missing', {EXIT=>1},
{ERR => "$prog: cannot open 'missing' for reading: "
- . "No such file or directory\n"}],
+ . "$limits->{ENOENT}\n"}],
+
+ # Input file listing missing files.
+ ['missing2', '--files0-from=-', '<', {IN=>"missing\0missing\0"}, {EXIT=>1},
+ {ERR => "$prog: cannot access 'missing': $limits->{ENOENT}\n" x 2}],
+
+ # Input file listing duplicate files.
+ ['duplicate1', '--files0-from=-', '<', {IN=>"g\0g\0"}, {AUX=>{g=>''}},
+ {OUT=>"0\tg\n"}, {OUT_SUBST=>'s/^\d+/0/'}],
+
+ # Input file listing duplicate files, using the -l option.
+ ['duplicate2', '-l --files0-from=-', '<', {IN=>"g\0g\0"}, {AUX=>{g=>''}},
+ {OUT=>"0\tg\n" x 2}, {OUT_SUBST=>'s/^\d+/0/'}],
# input file name of '-'
['minus-in-stdin', '--files0-from=-', '<', {IN=>{f=>'-'}}, {EXIT=>1},
diff --git a/tests/du/hard-link.sh b/tests/du/hard-link.sh
index 2debaa36f..ea967f2ab 100755
--- a/tests/du/hard-link.sh
+++ b/tests/du/hard-link.sh
@@ -3,7 +3,7 @@
# Likewise for excluded directories.
# Ensure that hard links _are_ listed twice when using --count-links.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -61,4 +61,16 @@ EOF
compare exp out || fail=1
+# Test du -l (--count-links) without -a flag
+# This should count hard-linked files separately
+mkdir test-dir &&
+echo 'content' > test-dir/file1 &&
+ln test-dir/file1 test-dir/file2 || framework_failure_
+du_normal=$(du test-dir | cut -f1) || fail=1
+du_count_links=$(du -l test-dir | cut -f1) || fail=1
+# The count-links version should be larger
+if test "$du_normal" -gt 0; then
+ test "$du_count_links" -gt "$du_normal" || fail=1
+fi
+
Exit $fail
diff --git a/tests/du/inacc-dest.sh b/tests/du/inacc-dest.sh
index 1548a6246..b578d76f1 100755
--- a/tests/du/inacc-dest.sh
+++ b/tests/du/inacc-dest.sh
@@ -2,7 +2,7 @@
# Prior to coreutils-6.5, an inaccessible destination dir (chmod a-x)
# would cause du to exit prematurely on systems with native openat support.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ du
skip_if_root_
+getlimits_
mkdir f && cd f && mkdir a b c d e && touch c/j && chmod a-x c \
|| framework_failure_
@@ -33,14 +34,14 @@ du > ../t 2>&1 && fail=1
# /proc support, nor native openat support.
sed 's/^[0-9][0-9]* //' ../t | sort -u > out
-cat <<\EOF > exp || framework_failure_
+cat <<EOF > exp || framework_failure_
.
./a
./b
./c
./d
./e
-du: cannot read directory './c': Permission denied
+du: cannot read directory './c': $EACCES
EOF
# Map a diagnostic like this
diff --git a/tests/du/inacc-dir.sh b/tests/du/inacc-dir.sh
index 91a41a889..ed932f353 100755
--- a/tests/du/inacc-dir.sh
+++ b/tests/du/inacc-dir.sh
@@ -1,6 +1,6 @@
#!/bin/sh
# Ensure that du counts the size of an inaccessible directory.
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/inaccessible-cwd.sh b/tests/du/inaccessible-cwd.sh
index 64daa8793..2cec43aa7 100755
--- a/tests/du/inaccessible-cwd.sh
+++ b/tests/du/inaccessible-cwd.sh
@@ -2,7 +2,7 @@
# Ensure that even when run from an inaccessible directory, du can still
# operate on accessible directories elsewhere.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/inodes.sh b/tests/du/inodes.sh
index 42df4386a..1d2dcd13e 100755
--- a/tests/du/inodes.sh
+++ b/tests/du/inodes.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# exercise du's --inodes option
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -136,5 +136,5 @@ grep ' ineffective ' err >/dev/null || { fail=1; cat out err; }
# Ensure that --inodes is mentioned in the usage.
du --help > out || fail=1
-grep ' --inodes ' out >/dev/null || { fail=1; cat out; }
+grep -- '--inodes' out >/dev/null || { fail=1; cat out; }
Exit $fail
diff --git a/tests/du/long-from-unreadable.sh b/tests/du/long-from-unreadable.sh
index 1bce78bbc..29dea748d 100755
--- a/tests/du/long-from-unreadable.sh
+++ b/tests/du/long-from-unreadable.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Show fts fails on old-fashioned systems.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/long-sloop.sh b/tests/du/long-sloop.sh
index e5ca20212..13d92f185 100755
--- a/tests/du/long-sloop.sh
+++ b/tests/du/long-sloop.sh
@@ -3,7 +3,7 @@
# Show that du fails with ELOOP (Too many levels of symbolic links)
# when it encounters that condition.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ du
+getlimits_
# Create lots of directories, each containing a single symlink
# pointing at the next directory in the list.
@@ -46,18 +47,9 @@ echo foo > $i || framework_failure_
# If a system can handle this many symlinks in a file name,
# just skip this test.
-
-# The following also serves to record in 'err' the string
-# corresponding to strerror (ELOOP). This is necessary because while
-# Linux/libc gives 'Too many levels of symbolic links', Solaris
-# renders it as "Number of symbolic links encountered during path
-# name traversal exceeds MAXSYMLINKS".
-
cat $file > /dev/null 2> err &&
skip_ 'Your system appears to be able to handle more than $n symlinks
in file name resolution'
-too_many=$(sed 's/.*: //' err)
-
# With coreutils-5.93 there was no failure.
# With coreutils-5.94 we get the desired diagnostic:
@@ -66,7 +58,7 @@ du -L 1 > /dev/null 2> out1 && fail=1
sed "s, .1/s/s/s/[/s]*',," out1 > out2 || framework_failure_
sed "s/cannot read directory/cannot access/" out2 > out || framework_failure_
-echo "du: cannot access: $too_many" > exp || framework_failure_
+echo "du: cannot access: $ELOOP" > exp || framework_failure_
compare exp out || fail=1
diff --git a/tests/du/max-depth.sh b/tests/du/max-depth.sh
index f23f4dde8..8684379eb 100755
--- a/tests/du/max-depth.sh
+++ b/tests/du/max-depth.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# exercise du's --max-depth=N option
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/move-dir-while-traversing.sh b/tests/du/move-dir-while-traversing.sh
index adf482b8e..9639ac30f 100755
--- a/tests/du/move-dir-while-traversing.sh
+++ b/tests/du/move-dir-while-traversing.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Trigger a failed assertion in coreutils-8.9 and earlier.
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/no-deref.sh b/tests/du/no-deref.sh
index b60a2b7ea..0242732a9 100755
--- a/tests/du/no-deref.sh
+++ b/tests/du/no-deref.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that by default, du doesn't dereference command-line symlinks.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/no-x.sh b/tests/du/no-x.sh
index 49f0b6770..238b5d022 100755
--- a/tests/du/no-x.sh
+++ b/tests/du/no-x.sh
@@ -2,7 +2,7 @@
# Make sure du gives the right diagnostic for a readable,
# but inaccessible directory.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ du
skip_if_root_
+getlimits_
mkdir -p d/no-x/y || framework_failure_
chmod u=rw d/no-x || framework_failure_
@@ -41,7 +42,7 @@ sed "s/^$prog: cannot read directory /$prog: /" out > t && mv t out
sed 's,d/no-x/y,d/no-x,' out > t && mv t out
cat <<EOF > exp
-$prog: 'd/no-x': Permission denied
+$prog: 'd/no-x': $EACCES
EOF
compare exp out || fail=1
diff --git a/tests/du/one-file-system.sh b/tests/du/one-file-system.sh
index a393d80af..6211b7e25 100755
--- a/tests/du/one-file-system.sh
+++ b/tests/du/one-file-system.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test for bugs in du's --one-file-system (-x) option.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/restore-wd.sh b/tests/du/restore-wd.sh
index 0e51ce4b7..2214ccedb 100755
--- a/tests/du/restore-wd.sh
+++ b/tests/du/restore-wd.sh
@@ -2,7 +2,7 @@
# due to a bug in glibc's ftw.c, in some cases, nftw w/FTW_CHDIR
# would not restore the working directory.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/slash.sh b/tests/du/slash.sh
index c71e20585..a2f16694a 100755
--- a/tests/du/slash.sh
+++ b/tests/du/slash.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# 'du /' would omit the '/' on the last line.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/threshold.sh b/tests/du/threshold.sh
index 902c0d035..d7ec5cfca 100755
--- a/tests/du/threshold.sh
+++ b/tests/du/threshold.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise du's --threshold option.
-# Copyright (C) 2013-2025 Free Software Foundation, Inc.
+# Copyright (C) 2013-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/trailing-slash.sh b/tests/du/trailing-slash.sh
index a13b9777c..18fb5ce1e 100755
--- a/tests/du/trailing-slash.sh
+++ b/tests/du/trailing-slash.sh
@@ -2,7 +2,7 @@
# Ensure that du works properly for an argument that refers to a
# symbolic link, and that is specified with a trailing slash.
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/du/two-args.sh b/tests/du/two-args.sh
index 82dbc1b10..4c7e27ce8 100755
--- a/tests/du/two-args.sh
+++ b/tests/du/two-args.sh
@@ -2,7 +2,7 @@
# Make sure 'du d/1 d/2' works.
# That command failed with du from fileutils-4.0q.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/env/env-S-script.sh b/tests/env/env-S-script.sh
index 10e1f9d65..a671404ce 100755
--- a/tests/env/env-S-script.sh
+++ b/tests/env/env-S-script.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test env -S in a #! line of a script.
-# Copyright (C) 2018-2025 Free Software Foundation, Inc.
+# Copyright (C) 2018-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/env/env-S.pl b/tests/env/env-S.pl
index 6e01975c7..745f45723 100755
--- a/tests/env/env-S.pl
+++ b/tests/env/env-S.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test 'env -S' feature
-# Copyright (C) 2018-2025 Free Software Foundation, Inc.
+# Copyright (C) 2018-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/env/env-null.sh b/tests/env/env-null.sh
index c62e87402..bbe5727e2 100755
--- a/tests/env/env-null.sh
+++ b/tests/env/env-null.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify behavior of env -0 and printenv -0.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/env/env-signal-handler.sh b/tests/env/env-signal-handler.sh
index 603826d86..a27afd10b 100755
--- a/tests/env/env-signal-handler.sh
+++ b/tests/env/env-signal-handler.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test env --default-signal=PIPE feature.
-# Copyright (C) 2019-2025 Free Software Foundation, Inc.
+# Copyright (C) 2019-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -23,6 +23,8 @@ trap_sigpipe_or_skip_
# /bin/sh has an intermittent failure in ignoring SIGPIPE on OpenIndiana 11
# so we require bash as discussed at:
# https://lists.gnu.org/archive/html/coreutils/2020-03/msg00004.html
+# This test also relies on bash's 'kill' builtin which allows the signal name
+# "RTMIN" and "RTMAX" if those real-time signals are supported.
require_bash_as_SHELL_
# Paraphrasing https://bugs.gnu.org/34488#8:
@@ -87,7 +89,7 @@ env_ignore_delay_()
local delay="$1"
# The first 'env' is just to ensure timeout is not a shell built-in.
- env timeout --verbose --kill-after=.1 --signal=INT $delay \
+ env timeout --verbose --kill-after=.1 --signal=$timeout_sig $delay \
env $env_opt sleep 10 > /dev/null 2>outt
# check only the first two lines from stderr, which are printed by timeout.
# (operating systems might add more messages, like "killed").
@@ -102,7 +104,8 @@ env_ignore_delay_()
cat <<\EOF >exp || framework_failure_
timeout: sending signal INT to command 'env'
EOF
-env_opt='' retry_delay_ env_ignore_delay_ .1 6 || fail=1
+timeout_sig=INT env_opt='' \
+ retry_delay_ env_ignore_delay_ .1 6 || fail=1
# env test - ignore signal handler
# --------------------------------
@@ -112,15 +115,55 @@ cat <<\EOF >exp || framework_failure_
timeout: sending signal INT to command 'env'
timeout: sending signal KILL to command 'env'
EOF
-env_opt='--ignore-signal=INT' retry_delay_ env_ignore_delay_ .1 6 || fail=1
-env_opt='--ignore-signal' retry_delay_ env_ignore_delay_ .1 6 || fail=1
+timeout_sig=INT env_opt='--ignore-signal=INT' \
+ retry_delay_ env_ignore_delay_ .1 6 || fail=1
+timeout_sig=INT env_opt='--ignore-signal' \
+ retry_delay_ env_ignore_delay_ .1 6 || fail=1
+
+# env test - ignore signal handler for PIPE
+# SIGPIPE is often incorrectly interfered with, as per:
+# http://www.pixelbeat.org/programming/sigpipe_handling.html
+cat <<\EOF >exp || framework_failure_
+timeout: sending signal PIPE to command 'env'
+timeout: sending signal KILL to command 'env'
+EOF
+timeout_sig=PIPE env_opt='--ignore-signal=PIPE' \
+ retry_delay_ env_ignore_delay_ .1 6 || fail=1
+
+if kill -l RTMIN RTMAX; then
+ for sig in RTMIN RTMAX; do
+ # Baseline test - ignore signal handler
+ # -------------------------------------
+ # Terminate 'sleep' with $sig
+ # All real-time signals terminate the program by default.
+ cat <<EOF >exp || framework_failure_
+timeout: sending signal $sig to command 'env'
+EOF
+ timeout_sig=$sig env_opt='' \
+ retry_delay_ env_ignore_delay_ .1 6 || fail=1
+
+ # env test - ignore signal handler
+ # --------------------------------
+ # Use env to ignore $sig - "sleep" should continue running
+ # after timeout sends $sig, and be killed using SIGKILL.
+ cat <<EOF >exp || framework_failure_
+timeout: sending signal $sig to command 'env'
+timeout: sending signal KILL to command 'env'
+EOF
+ timeout_sig=$sig env_opt="--ignore-signal=$sig" \
+ retry_delay_ env_ignore_delay_ .1 6 || fail=1
+ timeout_sig=$sig env_opt='--ignore-signal' \
+ retry_delay_ env_ignore_delay_ .1 6 || fail=1
+ done
+fi
# env test --list-signal-handling
-env --default-signal --ignore-signal=INT --list-signal-handling true \
- 2> err8t || fail=1
-sed 's/(.*)/()/' err8t > err8 || framework_failure_
-env printf 'INT (): IGNORE\n' > exp-err8 || framework_failure_
-compare exp-err8 err8 || fail=1
-
+for sig in INT PIPE; do
+ env --default-signal --ignore-signal=$sig --list-signal-handling true \
+ 2> err8t || fail=1
+ sed 's/ *(.*)//' err8t > err8 || framework_failure_
+ env printf '%s: IGNORE\n' "$sig" > exp-err8 || framework_failure_
+ compare exp-err8 err8 || fail=1
+done
Exit $fail
diff --git a/tests/env/env.sh b/tests/env/env.sh
index c838a49a2..425a82724 100755
--- a/tests/env/env.sh
+++ b/tests/env/env.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify behavior of env.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,13 +21,13 @@
print_ver_ env pwd nice
# A simple shebang program to call "echo" from symlinks like "./-u" or "./--".
-echo "#!$abs_top_builddir/src/echo simple_echo" > simple_echo \
+echo '#!'"$(command -v env | sed 's|/env|/echo|')"' foo' > show_args \
|| framework_failure_
-chmod a+x simple_echo || framework_failure_
+chmod a+x show_args || framework_failure_
# Verify we can run the shebang which is not the case if
# there are spaces in $abs_top_builddir.
-./simple_echo || skip_ "Error running simple_echo script"
+./show_args || skip_ "Error running show_args script"
# Verify clearing the environment
a=1
@@ -90,6 +90,14 @@ ENV_TEST1=b
EOF
compare exp out || fail=1
+# env shouldn't care what encoding name or value is
+for nv in 'NON_UTF8_TEST=\240' 'NON_UTF8_TEST\240=1'; do
+ env $(printf "$nv") env > all || fail=1
+ grep '^NON_UTF8_TEST' all | LC_ALL=C sort > out || framework_failure_
+ printf "$nv\\n" > exp || framework_failure_
+ compare exp out || fail=1
+done
+
# PATH modifications affect exec.
mkdir unlikely_name || framework_failure_
cat <<EOF > unlikely_name/also_unlikely || framework_failure_
@@ -109,9 +117,9 @@ export PATH
# On some systems, execve("-i") invokes a shebang script ./-i on PATH as
# '/bin/sh -i', rather than '/bin/sh -- -i', which doesn't do what we want.
# Avoid the issue by using a shebang to 'echo' passing a second parameter
-# before the '-i'. See the definition of simple_echo before.
+# before the '-i'. See the definition of show_args before.
# Test -u, rather than -i, to minimize PATH problems.
-ln -s "simple_echo" ./-u || framework_failure_
+ln -s "show_args" ./-u || framework_failure_
case $(env -u echo echo good) in
good) ;;
*) fail=1 ;;
@@ -127,7 +135,7 @@ esac
# After options have ended, the first argument not containing = is a program.
returns_ 127 env a=b -- true || fail=1
-ln -s "simple_echo" ./-- || framework_failure_
+ln -s "show_args" ./-- || framework_failure_
case $(env a=b -- true || echo fail) in
*true) ;;
*) fail=1 ;;
@@ -164,13 +172,18 @@ got=$(env --chdir=empty pwd) || fail=1
test "$exp" = "$got" || fail=1
# Verify argv0 overriding
+cat <<EOF > truetrue || framework_failure_
+#!$SHELL
+EOF
+chmod +x truetrue || framework_failure_
for arg in 'argv0' ''; do
-env -v -a short --argv0=$arg true 2>err || fail=1
+env -v -a short --argv0=$arg ./truetrue 2>err || fail=1
cat <<EOF >err_exp || framework_failure_
argv0: '$arg'
-executing: true
+executing: ./truetrue
arg[0]= '$arg'
EOF
+compare err_exp err || fail=1
done
Exit $fail
diff --git a/tests/envvar-check b/tests/envvar-check
index 769f330f1..ea5d6a1c1 100644
--- a/tests/envvar-check
+++ b/tests/envvar-check
@@ -1,7 +1,7 @@
# -*- sh -*-
# Check environment variables for sane values while testing.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/expand/bounded-memory.sh b/tests/expand/bounded-memory.sh
new file mode 100755
index 000000000..76cde9c65
--- /dev/null
+++ b/tests/expand/bounded-memory.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+# Ensure that expand uses bounded memory.
+
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ expand
+
+vm=$(get_min_ulimit_v_ timeout 10 expand /dev/null) ||
+ skip_ 'failed to determine memory limit'
+
+(ulimit -v $(($vm+6000)) \
+ && timeout 0.5 expand </dev/zero >/dev/null 2>err)
+ret=$?
+test $ret = 124 || {
+ fail=1
+ cat err
+ echo "expand used too much memory" >&2
+}
+
+Exit $fail
diff --git a/tests/misc/expand.pl b/tests/expand/expand.pl
index 4b07210df..aea388abe 100755
--- a/tests/misc/expand.pl
+++ b/tests/expand/expand.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Exercise expand.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/expand/mb.sh b/tests/expand/mb.sh
new file mode 100755
index 000000000..a869e74c7
--- /dev/null
+++ b/tests/expand/mb.sh
@@ -0,0 +1,172 @@
+#!/bin/sh
+
+# Copyright (C) 2012-2015 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ expand printf
+
+test "$LOCALE_FR_UTF8" != none || skip_ "French UTF-8 locale not available"
+export LC_ALL="$LOCALE_FR_UTF8"
+
+#input containing multibyte characters
+cat <<\EOF > in || framework_failure_
+1234567812345678123456781
+. . . .
+a b c d
+. . . .
+ä ö ü ß
+. . . .
+EOF
+env printf ' äöü\t. öüä. \tä xx\n' >> in || framework_failure_
+
+cat <<\EOF > exp || framework_failure_
+1234567812345678123456781
+. . . .
+a b c d
+. . . .
+ä ö ü ß
+. . . .
+ äöü . öüä. ä xx
+EOF
+
+expand < in > out || fail=1
+compare exp out > /dev/null 2>&1 || fail=1
+
+#multiple files as an input
+cat <<\EOF >> exp || framework_failure_
+1234567812345678123456781
+. . . .
+a b c d
+. . . .
+ä ö ü ß
+. . . .
+ äöü . öüä. ä xx
+EOF
+
+expand ./in ./in > out || fail=1
+compare exp out > /dev/null 2>&1 || fail=1
+
+#test characters with display widths != 1
+env printf '12345678
+e\t|ascii(1)
+\u00E9\t|composed(1)
+e\u0301\t|decomposed(1)
+\u3000\t|ideo-space(2)
+\uFF0D\t|full-hypen(2)
+' > in || framework_failure_
+
+env printf '12345678
+e |ascii(1)
+\u00E9 |composed(1)
+e\u0301 |decomposed(1)
+\u3000 |ideo-space(2)
+\uFF0D |full-hypen(2)
+' > exp || framework_failure_
+
+expand < in > out || fail=1
+compare exp out > /dev/null 2>&1 || fail=1
+
+#shouldn't fail with "input line too long"
+#when a line starts with a control character
+env printf '\n' > in || framework_failure_
+
+expand < in > out || fail=1
+compare in out > /dev/null 2>&1 || fail=1
+
+#non-Unicode characters interspersed between Unicode ones
+env printf '12345678
+\t\xFF|
+\xFF\t|
+\t\xFFä|
+ä\xFF\t|
+\tä\xFF|
+\xFF\tä|
+äbcdef\xFF\t|
+' > in || framework_failure_
+
+env printf '12345678
+ \xFF|
+\xFF |
+ \xFFä|
+ä\xFF |
+ ä\xFF|
+\xFF ä|
+äbcdef\xFF |
+' > exp || framework_failure_
+
+expand < in > out || fail=1
+compare exp out > /dev/null 2>&1 || fail=1
+
+
+
+#BOM header test 1
+env printf "\xEF\xBB\xBF" > in; cat <<\EOF >> in || framework_failure_
+1234567812345678123456781
+. . . .
+a b c d
+. . . .
+ä ö ü ß
+. . . .
+EOF
+env printf ' äöü\t. öüä. \tä xx\n' >> in || framework_failure_
+
+env printf "\xEF\xBB\xBF" > exp; cat <<\EOF >> exp || framework_failure_
+1234567812345678123456781
+. . . .
+a b c d
+. . . .
+ä ö ü ß
+. . . .
+ äöü . öüä. ä xx
+EOF
+
+expand < in > out || fail=1
+compare exp out > /dev/null 2>&1 || fail=1
+
+env printf '\xEF\xBB\xBF' > in1; cat <<\EOF >> in1 || framework_failure_
+1234567812345678123456781
+. . . .
+a b c d
+. . . .
+ä ö ü ß
+. . . .
+EOF
+env printf ' äöü\t. öüä. \tä xx\n' >> in1 || framework_failure_
+
+
+env printf '\xEF\xBB\xBF' > exp; cat <<\EOF >> exp || framework_failure_
+1234567812345678123456781
+. . . .
+a b c d
+. . . .
+ä ö ü ß
+. . . .
+ äöü . öüä. ä xx
+EOF
+env printf '\xEF\xBB\xBF' >> exp; cat <<\EOF >> exp || framework_failure_
+1234567812345678123456781
+. . . .
+a b c d
+. . . .
+ä ö ü ß
+. . . .
+ äöü . öüä. ä xx
+EOF
+
+expand in1 in1 > out || fail=1
+compare exp out > /dev/null 2>&1 || fail=1
+
+Exit $fail
diff --git a/tests/expr/expr-multibyte.pl b/tests/expr/expr-multibyte.pl
index 64aa3ee78..199c33795 100755
--- a/tests/expr/expr-multibyte.pl
+++ b/tests/expr/expr-multibyte.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Exercise expr with multibyte input
-# Copyright (C) 2017-2025 Free Software Foundation, Inc.
+# Copyright (C) 2017-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/expr/expr.pl b/tests/expr/expr.pl
index 8506c159c..b582a85fd 100755
--- a/tests/expr/expr.pl
+++ b/tests/expr/expr.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Basic tests for "expr".
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -69,6 +69,12 @@ my @Tests =
['andand', '0 \& 1 / 0', {OUT => '0'}, {EXIT => 1}],
['oror', '1 \| 1 / 0', {OUT => '1'}, {EXIT => 0}],
+ # Short-circuit must also skip parenthesized dead branches.
+ ['or-paren', '1 \| \( 1 / 0 \)', {OUT => '1'}, {EXIT => 0}],
+ ['and-paren', '0 \& \( 1 / 0 \)', {OUT => '0'}, {EXIT => 1}],
+ ['or-nested', '1 \| \( 0 \& \( 1 / 0 \) \)', {OUT => '1'}, {EXIT => 0}],
+ ['and-nested', '0 \& \( 1 \| \( 1 / 0 \) \)', {OUT => '0'}, {EXIT => 1}],
+
# In 5.1.3 and earlier, this would output the empty string.
['orempty', '"" \| ""', {OUT => '0'}, {EXIT => 1}],
diff --git a/tests/factor/create-test.sh b/tests/factor/create-test.sh
index dce41e35b..905117981 100755
--- a/tests/factor/create-test.sh
+++ b/tests/factor/create-test.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Create the factor test scripts.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
test_name=$1
template=$2
diff --git a/tests/factor/factor-parallel.sh b/tests/factor/factor-parallel.sh
index 7a22b19b8..0a690adb3 100755
--- a/tests/factor/factor-parallel.sh
+++ b/tests/factor/factor-parallel.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test for complete lines on output
-# Copyright (C) 2015-2025 Free Software Foundation, Inc.
+# Copyright (C) 2015-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/factor/factor.pl b/tests/factor/factor.pl
index 782deefe6..2b8e54086 100755
--- a/tests/factor/factor.pl
+++ b/tests/factor/factor.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Basic tests for "factor".
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -97,6 +97,13 @@ my @Tests =
{OUT => '4999 340282366920938463463374607431768211297'}],
['h-1', '-h 3000', {OUT => '2^3 3 5^3'}],
['h-2', '3000 --exponents', {OUT => '2^3 3 5^3'}],
+ ['nul1', {IN_PIPE => '3'}, {OUT => '3'}],
+ ['nul2', {IN_PIPE => "3\000"}, {OUT => '3'}],
+ ['nul3', {IN_PIPE => "3\000foo"}, {OUT => '3'}],
+ ['nul4', {IN_PIPE => "3\000foo bar"}, {OUT => "3: 3\n"},
+ {EXIT => 1},
+ {ERR => "$prog: 'bar' is not a valid positive integer\n"}],
+ # ['nul5', "3\000"}, {OUT => '3'}], # Not supported by perl framework
);
@@ -107,8 +114,22 @@ my $t;
Test:
foreach $t (@Tests)
{
- (my $arg1 = $t->[1]) =~ s| *\+?||; # strip '+'
- ($arg1 = $arg1) =~ s| *-[^ ]+ *||; # strip option
+ my $arg1;
+
+ # For IN_PIPE tests, the input number comes from the pipe, not $t->[1].
+ foreach my $e (@$t)
+ {
+ ref $e eq 'HASH' && exists $e->{IN_PIPE}
+ and $arg1 = $e->{IN_PIPE};
+ }
+ if (!defined $arg1)
+ {
+ $arg1 = $t->[1];
+ }
+
+ $arg1 =~ s| *\+?||; # strip '+'
+ $arg1 =~ s| *-[^ ]+ *||; # strip option
+ $arg1 =~ s|\0[^ \t\n]*||g; # strip NUL to whitespace
# Don't fiddle with expected OUT string if there's a nonzero exit status.
foreach my $e (@$t)
diff --git a/tests/factor/run.sh b/tests/factor/run.sh
index 944886f90..eb3a6ecb0 100755
--- a/tests/factor/run.sh
+++ b/tests/factor/run.sh
@@ -10,7 +10,7 @@
#
# See: tests/factor/create-test.sh
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
diff --git a/tests/fmt/base.pl b/tests/fmt/base.pl
index 56274dfea..9fb63704e 100755
--- a/tests/fmt/base.pl
+++ b/tests/fmt/base.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Basic tests for "fmt".
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,8 +18,9 @@
use strict;
+my $limits = getlimits ();
+
(my $program_name = $0) =~ s|.*/||;
-my $normalize_strerror = "s/': .*/'/";
my @Tests =
(
@@ -27,11 +28,10 @@ my @Tests =
{IN=> "ça\nçb\n"},
{OUT=>"ça b\n"}],
['wide-1', '-w 32768',
- {ERR => "fmt: invalid width: '32768'\n"}, {EXIT => 1},
- {ERR_SUBST => $normalize_strerror}],
+ {ERR => "fmt: invalid width: '32768': $limits->{ERANGE}\n"}, {EXIT => 1}],
['wide-2', '-w 2147483647',
- {ERR => "fmt: invalid width: '2147483647'\n"}, {EXIT => 1},
- {ERR_SUBST => $normalize_strerror}],
+ {ERR => "fmt: invalid width: '2147483647': $limits->{ERANGE}\n"},
+ {EXIT => 1}],
['bad-suffix', '-72x', {IN=> ''},
{ERR => "fmt: invalid width: '72x'\n"}, {EXIT => 1}],
['no-file', 'no-such-file',
diff --git a/tests/fmt/goal-option.sh b/tests/fmt/goal-option.sh
index c371d77d5..cb7da9d59 100755
--- a/tests/fmt/goal-option.sh
+++ b/tests/fmt/goal-option.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise the fmt -g option.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/fmt/long-line.sh b/tests/fmt/long-line.sh
index 8d50757e7..ca0b6bb59 100755
--- a/tests/fmt/long-line.sh
+++ b/tests/fmt/long-line.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure fmt -s works even on long lines
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/fmt/non-space.sh b/tests/fmt/non-space.sh
index 64dd5072d..9771121c7 100755
--- a/tests/fmt/non-space.sh
+++ b/tests/fmt/non-space.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test fmt space handling
-# Copyright (C) 2022-2025 Free Software Foundation, Inc.
+# Copyright (C) 2022-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/fmt/width.sh b/tests/fmt/width.sh
new file mode 100755
index 000000000..981ca9666
--- /dev/null
+++ b/tests/fmt/width.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+# Exercise the fmt -w option.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ fmt
+
+# Ensure width is max display width.
+# Before v9.10, width incorrectly included the \n character
+printf 'aa bb cc dd ee' | fmt -w 8 > out || fail=1
+cat <<\_EOF_ > exp || framework_failure_
+aa bb cc
+dd ee
+_EOF_
+compare exp out || fail=1
+
+printf 'aa bb cc dd ee' | fmt -w 7 > out || fail=1
+cat <<\_EOF_ > exp || framework_failure_
+aa
+bb cc
+dd ee
+_EOF_
+compare exp out || fail=1
+
+
+Exit $fail
diff --git a/tests/fold/fold-characters.sh b/tests/fold/fold-characters.sh
index 5c72c9e97..6edaa9c05 100755
--- a/tests/fold/fold-characters.sh
+++ b/tests/fold/fold-characters.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test fold --characters.
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -83,14 +83,11 @@ env printf '\naaaa\n' >> exp3 || framework_failure_
fold --characters input3 | tail -n 4 > out3 || fail=1
compare exp3 out3 || fail=1
-# Sequence derived from <https://datatracker.ietf.org/doc/rfc9839>.
-bad_unicode ()
-{
- # invalid UTF8|unpaired surrogate|NUL|C1 control|noncharacter
- env printf '\xC3|\xED\xBA\xAD|\u0000|\u0089|\xED\xA6\xBF\xED\xBF\xBF\n'
-}
-bad_unicode > /dev/null || framework_failure_
-test $({ bad_unicode | fold; bad_unicode; } | uniq | wc -l) = 1 || fail=1
+bad_unicode_with_nul () { env printf '%s|\u0000\n' "$(bad_unicode)"; }
+bad_unicode_with_nul > exp4 || framework_failure_
+bad_unicode_with_nul | fold > out4 || fail=1
+compare exp4 out4 || fail=1
+
# Check bad character at EOF
test $(env printf '\xC3' | fold | wc -c) = 1 || fail=1
diff --git a/tests/fold/fold-nbsp.sh b/tests/fold/fold-nbsp.sh
index 2c1e8fd51..d30c14bb4 100755
--- a/tests/fold/fold-nbsp.sh
+++ b/tests/fold/fold-nbsp.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test fold --spaces with various Unicode non-breaking space characters.
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/fold/fold-spaces.sh b/tests/fold/fold-spaces.sh
index e30a2bcbb..acf89d255 100755
--- a/tests/fold/fold-spaces.sh
+++ b/tests/fold/fold-spaces.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test fold --spaces with various Unicode breaking space characters.
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/fold/fold-zero-width.sh b/tests/fold/fold-zero-width.sh
index d90fd0c9c..1bec0ef34 100755
--- a/tests/fold/fold-zero-width.sh
+++ b/tests/fold/fold-zero-width.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test fold with zero width characters.
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -33,8 +33,11 @@ test $(wc -l < out) -eq $(($IO_BUFSIZE_TIMES2 / 80)) || fail=1
test "$LOCALE_FR_UTF8" != none || skip_ "French UTF-8 locale not available"
-LC_ALL=$LOCALE_FR_UTF8
-export LC_ALL
+# Only set LC_CTYPE so messages are not translated
+# as we're verifying $ENOSPC below
+unset LC_ALL
+LC_CTYPE=$LOCALE_FR_UTF8
+export LC_CTYPE
test $(env printf '\u200B' | wc -L) -eq 0 ||
skip_ "character width mismatch"
@@ -51,14 +54,13 @@ test $(wc -l < out) -eq $(($IO_BUFSIZE_TIMES2 / 80)) || fail=1
# Ensure bounded memory operation.
test -w /dev/full && test -c /dev/full &&
-vm=$(get_min_ulimit_v_ fold /dev/null) && {
+vm=$(get_min_ulimit_v_ timeout 10 fold /dev/null) && {
# \303 results in EILSEQ on input
for c in '\n' '\0' '\303'; do
- tr '\0' "$c" < /dev/zero | timeout 10 $SHELL -c \
- "(ulimit -v $(($vm+12000)) && fold 2>err >/dev/full)"
+ tr '\0' "$c" < /dev/zero |
+ (ulimit -v $(($vm+6000)) && timeout 10 fold 2>err >/dev/full)
ret=$?
- test -f err || skip_ 'shell ulimit failure'
- { test $ret = 124 || ! grep 'space' err >/dev/null; } &&
+ { test $ret = 124 || ! grep "$ENOSPC" err >/dev/null; } &&
{ fail=1; cat err; echo "fold didn't diagnose ENOSPC" >&2; }
done
}
diff --git a/tests/fold/fold.pl b/tests/fold/fold.pl
index c4c14e2fd..6d8e72d62 100755
--- a/tests/fold/fold.pl
+++ b/tests/fold/fold.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Exercise fold.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/fold/multiple-files.sh b/tests/fold/multiple-files.sh
new file mode 100755
index 000000000..1e132d601
--- /dev/null
+++ b/tests/fold/multiple-files.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+# Test fold with multiple files.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ fold
+
+# Test that all files are processed.
+echo a > file1
+echo b > file2
+returns_ 1 fold file1 missing file2 > out 2> err || fail=1
+cat <<EOF > exp-out || framework_failure_
+a
+b
+EOF
+cat <<EOF > exp-err || framework_failure_
+fold: missing: No such file or directory
+EOF
+compare exp-out out || fail=1
+compare exp-err err || fail=1
+
+Exit $fail
diff --git a/tests/groups/groups-dash.sh b/tests/groups/groups-dash.sh
index ef21941c0..a039e6ffb 100755
--- a/tests/groups/groups-dash.sh
+++ b/tests/groups/groups-dash.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure groups handles -- sanely
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/groups/groups-process-all.sh b/tests/groups/groups-process-all.sh
index c30fb1452..d39386e14 100755
--- a/tests/groups/groups-process-all.sh
+++ b/tests/groups/groups-process-all.sh
@@ -2,7 +2,7 @@
# Ensure groups processes all arguments before exiting.
# With coreutils-2.27 and prior, it would exit upon first failure.
-# Copyright (C) 2017-2025 Free Software Foundation, Inc.
+# Copyright (C) 2017-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/groups/groups-version.sh b/tests/groups/groups-version.sh
index 61b50f95c..441fa36d6 100755
--- a/tests/groups/groups-version.sh
+++ b/tests/groups/groups-version.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure groups --version output is similar to id --version
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/head/head-c.sh b/tests/head/head-c.sh
index 0a9d311ac..735dca315 100755
--- a/tests/head/head-c.sh
+++ b/tests/head/head-c.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# exercise head -c
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/head/head-elide-tail.pl b/tests/head/head-elide-tail.pl
index f66eeeb55..7ec02c267 100755
--- a/tests/head/head-elide-tail.pl
+++ b/tests/head/head-elide-tail.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Exercise head's --bytes=-N option.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/head/head-n0.sh b/tests/head/head-n0.sh
new file mode 100755
index 000000000..8da4597e7
--- /dev/null
+++ b/tests/head/head-n0.sh
@@ -0,0 +1,81 @@
+#!/bin/sh
+# Make sure that 'head -n 0' and 'head -c 0' opens files for reading.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ head
+getlimits_
+
+mkdir dir || framework_failure_
+echo a > file || framework_failure_
+
+# Test 'head -n 0' with an existing file or directory.
+for args in dir file; do
+ for opt in -n -c; do
+ head $opt 0 $args >out 2>err || fail=1
+ compare /dev/null out || fail=1
+ compare /dev/null err || fail=1
+ done
+done
+
+# Test 'head -n 0' with multiple existing arguments and headers disabled.
+for args in 'dir file' 'file dir'; do
+ for opt in -n -c; do
+ head -q $opt 0 $args >out 2>err || fail=1
+ compare /dev/null out || fail=1
+ compare /dev/null err || fail=1
+ done
+done
+
+# Test 'head -n 0' with multiple existing arguments and headers enabled.
+for args in 'dir file' 'file dir'; do
+ file1=$(echo "$args" | cut -d ' ' -f1)
+ file2=$(echo "$args" | cut -d ' ' -f2)
+ cat <<EOF > exp || framework_failure_
+==> $file1 <==
+
+==> $file2 <==
+EOF
+ for opt in -n -c; do
+ head $opt 0 $args >out 2>err || fail=1
+ compare exp out || fail=1
+ compare /dev/null err || fail=1
+ done
+done
+
+# Test 'head -n 0' with a missing file.
+cat <<EOF >exp || framework_failure_
+head: cannot open 'missing1' for reading: $ENOENT
+EOF
+for opt in -n -c; do
+ returns_ 1 head $opt 0 missing1 >out 2>err || fail=1
+ compare /dev/null out || fail=1
+ compare exp err || fail=1
+done
+
+# Test 'head -n 0' with multiple missing files.
+cat <<EOF >exp || framework_failure_
+head: cannot open 'missing1' for reading: $ENOENT
+head: cannot open 'missing2' for reading: $ENOENT
+EOF
+for opt in -n -c; do
+ returns_ 1 head $opt 0 missing1 missing2 >out 2>err || fail=1
+ compare /dev/null out || fail=1
+ compare exp err || fail=1
+done
+
+Exit $fail
diff --git a/tests/head/head-pos.sh b/tests/head/head-pos.sh
index 7c113626a..e2565ea11 100755
--- a/tests/head/head-pos.sh
+++ b/tests/head/head-pos.sh
@@ -2,7 +2,7 @@
# When reading a specified number of lines, ensure that the output
# file pointer is positioned just after those lines.
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/head/head-write-error.sh b/tests/head/head-write-error.sh
index aa5f4fda5..3244adf83 100755
--- a/tests/head/head-write-error.sh
+++ b/tests/head/head-write-error.sh
@@ -2,7 +2,7 @@
# Ensure we diagnose and not continue writing to
# the output if we get a write error.
-# Copyright (C) 2014-2025 Free Software Foundation, Inc.
+# Copyright (C) 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ head
+getlimits_
if ! test -w /dev/full || ! test -c /dev/full; then
skip_ '/dev/full is required'
@@ -29,21 +30,18 @@ fi
yes | head -c10M > bigseek || framework_failure_
# This is the single output diagnostic expected,
-# (without the possibly varying :strerror(ENOSPC) suffix).
-printf '%s\n' "head: error writing 'standard output'" > exp
+printf '%s\n' "head: error writing 'standard output': $ENOSPC" > exp
# Memory is bounded in these cases
for item in lines bytes; do
for N in 0 1; do
# pipe case
- yes | returns_ 1 timeout 10s head --$item=-$N > /dev/full 2> errt || fail=1
- sed 's/\(head:.*\):.*/\1/' errt > err
+ yes | returns_ 1 timeout 10s head --$item=-$N > /dev/full 2> err || fail=1
compare exp err || fail=1
# seekable case
- returns_ 1 timeout 10s head --$item=-$N bigseek > /dev/full 2> errt \
+ returns_ 1 timeout 10s head --$item=-$N bigseek > /dev/full 2> err \
|| fail=1
- sed 's/\(head:.*\):.*/\1/' errt > err
compare exp err || fail=1
done
done
diff --git a/tests/head/head.pl b/tests/head/head.pl
index d715b1bb5..aec2d7192 100755
--- a/tests/head/head.pl
+++ b/tests/head/head.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# test head
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/help/help-version-getopt.sh b/tests/help/help-version-getopt.sh
index 949d6f713..067ebe026 100755
--- a/tests/help/help-version-getopt.sh
+++ b/tests/help/help-version-getopt.sh
@@ -2,7 +2,7 @@
# Ensure --version and --help options are processed before
# any other options by some programs.
-# Copyright (C) 2019-2025 Free Software Foundation, Inc.
+# Copyright (C) 2019-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/help/help-version.sh b/tests/help/help-version.sh
index 0ebc1186e..f6dd7a490 100755
--- a/tests/help/help-version.sh
+++ b/tests/help/help-version.sh
@@ -2,7 +2,7 @@
# Make sure all of these programs work properly
# when invoked with --help or --version.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/id/context.sh b/tests/id/context.sh
index 0178110d2..4a25cbdad 100755
--- a/tests/id/context.sh
+++ b/tests/id/context.sh
@@ -1,6 +1,6 @@
#!/bin/sh
# Ensure that "id" outputs SELinux context only without specified user
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/id/gnu-zero-uids.sh b/tests/id/gnu-zero-uids.sh
index 03913b1d3..d095635cd 100755
--- a/tests/id/gnu-zero-uids.sh
+++ b/tests/id/gnu-zero-uids.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# On GNU, 'id' must fail for processes with zero UIDs.
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/id/no-context.sh b/tests/id/no-context.sh
index 6c5e72e0b..c9ab71f18 100755
--- a/tests/id/no-context.sh
+++ b/tests/id/no-context.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# With POSIXLY_CORRECT, id must not print context=...
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/id/setgid.sh b/tests/id/setgid.sh
index e5b54121a..3b27988ef 100755
--- a/tests/id/setgid.sh
+++ b/tests/id/setgid.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify that id [-G] prints the right group when run set-GID.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/id/smack.sh b/tests/id/smack.sh
index 45c866ec8..b00fa2913 100755
--- a/tests/id/smack.sh
+++ b/tests/id/smack.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# SMACK test for the id-command.
# Derived from tests/id/context.sh and tests/id/no-context.sh.
-# Copyright (C) 2014-2025 Free Software Foundation, Inc.
+# Copyright (C) 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/id/uid.sh b/tests/id/uid.sh
index d0f62abca..d48088ec5 100755
--- a/tests/id/uid.sh
+++ b/tests/id/uid.sh
@@ -1,6 +1,6 @@
#!/bin/sh
# Ensure that "id" works with numeric user ids
-# Copyright (C) 2013-2025 Free Software Foundation, Inc.
+# Copyright (C) 2013-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/id/zero.sh b/tests/id/zero.sh
index f55ce1c21..be16b27db 100755
--- a/tests/id/zero.sh
+++ b/tests/id/zero.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise "id --zero".
-# Copyright (C) 2013-2025 Free Software Foundation, Inc.
+# Copyright (C) 2013-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/install/basic-1.sh b/tests/install/basic-1.sh
index b529f2af9..03ceee20c 100755
--- a/tests/install/basic-1.sh
+++ b/tests/install/basic-1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Basic tests for "install".
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -160,4 +160,25 @@ if ! mkdir sub-ro/d; then
grep 'cannot create directory' err || { cat err; fail=1; }
fi
+# Test install with --mode=+w (relative mode) ignores umask
+umask 0022 || framework_failure_
+touch file1 || framework_failure_
+ginstall file1 file2 --mode=+w || fail=1
+# Check that file2 has permissions --w--w--w-
+mode=$(ls -l file2|cut -b-10)
+test "$mode" = --w--w--w- || fail=1
+
+# Test comma-separated mode strings (like chmod)
+touch file3 || framework_failure_
+ginstall file3 file4 --mode='ug+rw,o+r' || fail=1
+# Check that file4 has permissions -rw-rw-r--
+mode=$(ls -l file4|cut -b-10)
+test "$mode" = -rw-rw-r-- || fail=1
+
+# Test comma-separated mode with directory creation
+ginstall -d testdir --mode='u+rwx,g+rx,o+r' || fail=1
+# Check that testdir has permissions drwxr-xr--
+mode=$(ls -ld testdir|cut -b-10)
+test "$mode" = drwxr-xr-- || fail=1
+
Exit $fail
diff --git a/tests/install/create-leading.sh b/tests/install/create-leading.sh
index ac7b1b180..f1398ecec 100755
--- a/tests/install/create-leading.sh
+++ b/tests/install/create-leading.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test -D option.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/install/d-slashdot.sh b/tests/install/d-slashdot.sh
index 5b301431d..bef08f202 100755
--- a/tests/install/d-slashdot.sh
+++ b/tests/install/d-slashdot.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that ginstall -d works with arguments specified with a trailing "/.".
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/install/install-C-root.sh b/tests/install/install-C-root.sh
index 2e9377bd5..cba3c167d 100755
--- a/tests/install/install-C-root.sh
+++ b/tests/install/install-C-root.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure "install -C" compares owner and group.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/install/install-C-selinux.sh b/tests/install/install-C-selinux.sh
index b1331198a..604247aed 100755
--- a/tests/install/install-C-selinux.sh
+++ b/tests/install/install-C-selinux.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure "install -C" compares SELinux context.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/install/install-C.sh b/tests/install/install-C.sh
index c2bdc334d..447247c8f 100755
--- a/tests/install/install-C.sh
+++ b/tests/install/install-C.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure "install -C" works. (basic tests)
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -115,8 +115,32 @@ compare out out_installed_second || fail=1
ginstall -Cv -m$mode2 a b > out || fail=1
compare out out_empty || fail=1
-# options -C and --preserve-timestamps are mutually exclusive
-returns_ 1 ginstall -C --preserve-timestamps a b || fail=1
+# Check -C without --preserve-timestamps with files having the same contents.
+echo a > a || framework_failure_
+echo a > b || framework_failure_
+touch -d 2026-01-01 a || framework_failure_
+test b -nt a || framework_failure_ # Handle systems with bad time.
+ginstall -C a b || fail=1
+test b -nt a || fail=1
+
+# Likewise, but with --preserve-timestamps.
+ginstall -C --preserve-timestamps a b || fail=1
+case $(stat --format=%y b) in
+ 2026-01-01*) ;;
+ *) fail=1 ;;
+esac
+
+# Check -C without --preserve-timestamps with files having differing contents.
+echo b > b || framework_failure_
+ginstall -C a b || fail=1
+test b -nt a || fail=1
+
+# Check -C with --preserve-timestamps with files having differing contents.
+ginstall -C --preserve-timestamps a b || fail=1
+case $(stat --format=%y b) in
+ 2026-01-01*) ;;
+ *) fail=1 ;;
+esac
# options -C and --strip are mutually exclusive
returns_ 1 ginstall -C --strip --strip-program=echo a b || fail=1
diff --git a/tests/install/install-Z-selinux.sh b/tests/install/install-Z-selinux.sh
index f0e734822..1b746da33 100755
--- a/tests/install/install-Z-selinux.sh
+++ b/tests/install/install-Z-selinux.sh
@@ -2,7 +2,7 @@
# test 'install -Z -D' and 'install -Z -d'
# based on tests/mkdir/restorecon.sh
-# Copyright (C) 2013-2025 Free Software Foundation, Inc.
+# Copyright (C) 2013-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/install/strip-program.sh b/tests/install/strip-program.sh
index 55ac77e9b..33f13ecca 100755
--- a/tests/install/strip-program.sh
+++ b/tests/install/strip-program.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure "install -s --strip-program=PROGRAM" uses the program to strip
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/install/trap.sh b/tests/install/trap.sh
index dae3e55eb..da0ee473b 100755
--- a/tests/install/trap.sh
+++ b/tests/install/trap.sh
@@ -2,7 +2,7 @@
# Ensure that 'install -s' doesn't infloop when its parent
# process traps CHLD signal.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/join/join-utf8.sh b/tests/join/join-utf8.sh
index 9b3a498f5..793f95951 100755
--- a/tests/join/join-utf8.sh
+++ b/tests/join/join-utf8.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test join in a UTF-8 locale.
-# Copyright 2023-2025 Free Software Foundation, Inc.
+# Copyright 2023-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/join/join.pl b/tests/join/join.pl
index 497cd95e8..fa1cfd77c 100755
--- a/tests/join/join.pl
+++ b/tests/join/join.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test join.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ln/backup-1.sh b/tests/ln/backup-1.sh
index a7a1f8405..2f65b0ca5 100755
--- a/tests/ln/backup-1.sh
+++ b/tests/ln/backup-1.sh
@@ -2,7 +2,7 @@
# Try to create a symlink with backup where the destination file exists
# and the backup file name is a hard link to the destination file.
-# Copyright (C) 1999-2025 Free Software Foundation, Inc.
+# Copyright (C) 1999-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ln/backup-suffix-traversal.sh b/tests/ln/backup-suffix-traversal.sh
new file mode 100755
index 000000000..9bf357855
--- /dev/null
+++ b/tests/ln/backup-suffix-traversal.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+# Ensure ln rejects backup suffixes containing path separators
+# to prevent path traversal
+# This test verifies that backup suffixes with '/' are sanitized
+# to prevent writing backup files outside the intended directory.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ ln
+
+touch a b || framework_failure_
+mkdir subdir || framework_failure_
+
+# Test 1: Command line suffix with path traversal attempt
+# This should NOT create 'c' file due to path traversal in suffix
+ln -S '_/../c' -b -s a b || fail=1
+
+# Verify the traversal was blocked - 'c' should not exist
+test -f c && fail=1
+
+# Verify backup was created with default suffix instead
+test -f b~ || fail=1
+
+rm -f b b~ || framework_failure_
+
+# Test 2: Environment variable suffix with path traversal attempt
+# This should also be sanitized
+touch b || framework_failure_
+SIMPLE_BACKUP_SUFFIX='_/../../malicious' ln -b -s a b || fail=1
+
+# Verify no traversal occurred
+test -f malicious && fail=1
+test -f ../malicious && fail=1
+test -f ../../malicious && fail=1
+
+# Verify backup created with default suffix
+test -f b~ || fail=1
+
+rm -f b b~ || framework_failure_
+
+# Test 3: Verify normal suffixes still work
+touch b || framework_failure_
+ln -S '.backup' -b -s a b || fail=1
+
+# Should create backup with custom suffix
+test -f b.backup || fail=1
+
+# Should NOT create backup with default suffix
+test -f b~ && fail=1
+
+Exit $fail
diff --git a/tests/ln/hard-backup.sh b/tests/ln/hard-backup.sh
index 6bfa9af29..296af9b6d 100755
--- a/tests/ln/hard-backup.sh
+++ b/tests/ln/hard-backup.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that 'ln --backup F F' gives a proper diagnostic.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ln/hard-to-sym.sh b/tests/ln/hard-to-sym.sh
index 2b18f4adb..5bd82ec10 100755
--- a/tests/ln/hard-to-sym.sh
+++ b/tests/ln/hard-to-sym.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Tests for ln -L/-P.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ln/misc.sh b/tests/ln/misc.sh
index 79ea5abe5..612cc4bc7 100755
--- a/tests/ln/misc.sh
+++ b/tests/ln/misc.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Miscellaneous tests for "ln".
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -128,5 +128,26 @@ done
ln foo '' 2> /dev/null
# ===================================================
+# Verify that -f and -i override each other, last one wins.
+
+# -sif: force should win (no prompt, overwrite)
+rm -f a b
+touch a b || framework_failure_
+ln -sif a b || fail=1
+test -L b || fail=1
+
+# -sfi: interactive should win; answering "n" should keep b unchanged
+rm -f a b
+touch a b || framework_failure_
+echo n | ln -sfi a b || :
+test -L b && fail=1
+
+# -sfi: interactive should win; answering "y" should create symlink
+rm -f a b
+touch a b || framework_failure_
+echo y | ln -sfi a b || fail=1
+test -L b || fail=1
+
+# ===================================================
Exit $fail
diff --git a/tests/ln/non-utf8-src.sh b/tests/ln/non-utf8-src.sh
new file mode 100755
index 000000000..845748a17
--- /dev/null
+++ b/tests/ln/non-utf8-src.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+# Ensure ln handles non-UTF-8 source names in target-dir mode.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ ln
+
+echo a > "$(bad_unicode)" \
+ || skip_ 'bad unicode not supported in shell or file system'
+
+name="$(bad_unicode)"
+
+# Check source file and create target directory.
+test -f "$name" || framework_failure_
+mkdir dst || framework_failure_
+
+# Hard link in target directory.
+ln "$name" dst || fail=1
+test -f "dst/$name" || fail=1
+rm -f "dst/$name" || framework_failure_
+
+# Symbolic link with -t.
+ln -s -t dst "$name" || fail=1
+test -L "dst/$name" || fail=1
+
+Exit $fail
diff --git a/tests/ln/relative.sh b/tests/ln/relative.sh
index 3a0ded413..e4bd9d7b1 100755
--- a/tests/ln/relative.sh
+++ b/tests/ln/relative.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test "ln --relative".
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ln/sf-1.sh b/tests/ln/sf-1.sh
index 81ebc15d3..cedfcb1f4 100755
--- a/tests/ln/sf-1.sh
+++ b/tests/ln/sf-1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test "ln -sf".
-# Copyright (C) 1997-2025 Free Software Foundation, Inc.
+# Copyright (C) 1997-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ln/slash-decorated-nonexistent-dest.sh b/tests/ln/slash-decorated-nonexistent-dest.sh
index a6fd49f75..0d80b6473 100755
--- a/tests/ln/slash-decorated-nonexistent-dest.sh
+++ b/tests/ln/slash-decorated-nonexistent-dest.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that touch f; ln -T f no-such-file/ does not mistakenly succeed
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ln/target-1.sh b/tests/ln/target-1.sh
index c648d768e..51798ed57 100755
--- a/tests/ln/target-1.sh
+++ b/tests/ln/target-1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test "ln --target-dir" with one file.
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/local.mk b/tests/local.mk
index 53fc53e00..fd96d7f3a 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -1,6 +1,6 @@
## Process this file with automake to produce Makefile.in -*-Makefile-*-.
-## Copyright (C) 2007-2025 Free Software Foundation, Inc.
+## Copyright (C) 2007-2026 Free Software Foundation, Inc.
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
@@ -23,7 +23,7 @@ root_tests = $(all_root_tests)
EXTRA_DIST += $(all_tests)
-TEST_EXTENSIONS = .sh .pl .xpl
+TEST_EXTENSIONS = .sh .pl
if HAVE_PERL
TESTSUITE_PERL = $(PERL)
@@ -68,7 +68,6 @@ TESTS_ENVIRONMENT = \
built_programs='$(built_programs) $(single_binary_progs)' \
fail=0 \
host_os=$(host_os) \
- host_triplet='$(host_triplet)' \
srcdir='$(srcdir)' \
top_srcdir='$(top_srcdir)' \
CONFIG_HEADER='$(abs_top_builddir)/$(CONFIG_INCLUDE)' \
@@ -127,6 +126,7 @@ all_root_tests = \
tests/ls/no-cap.sh \
tests/ls/nameless-uid.sh \
tests/nproc/nproc-quota.sh \
+ tests/nproc/nproc-quota-systemd.sh \
tests/chcon/chcon.sh \
tests/chroot/chroot-credentials.sh \
tests/misc/selinux.sh \
@@ -135,12 +135,15 @@ all_root_tests = \
tests/mkdir/smack-root.sh \
tests/mv/hardlink-case.sh \
tests/mv/sticky-to-xpart.sh \
+ tests/mv/meta-to-xpart.sh \
+ tests/mv/mv-special-2.sh \
tests/rm/fail-2eperm.sh \
tests/rm/no-give-up.sh \
tests/rm/one-file-system.sh \
tests/rm/read-only.sh \
tests/rm/empty-immutable-skip.sh \
tests/split/l-chunk-root.sh \
+ tests/tac/tac-continue.sh \
tests/tail/append-only.sh \
tests/tail/end-of-device.sh \
tests/touch/now-owned-by-other.sh
@@ -172,12 +175,16 @@ all_tests = \
tests/tail/inotify-race.sh \
tests/tail/inotify-race2.sh \
tests/misc/invalid-opt.pl \
+ tests/misc/user.sh \
tests/rm/ext3-perf.sh \
tests/rm/cycle.sh \
tests/cp/link-heap.sh \
tests/cp/no-ctx.sh \
- tests/tty/tty-eof.pl \
+ tests/misc/tty-eof.pl \
+ tests/misc/io-errors.sh \
tests/misc/read-errors.sh \
+ tests/misc/responsive.sh \
+ tests/misc/warning-errors.sh \
tests/misc/write-errors.sh \
tests/tail/basic-seek.sh \
tests/tail/inotify-hash-abuse.sh \
@@ -193,6 +200,7 @@ all_tests = \
tests/chmod/no-x.sh \
tests/chgrp/basic.sh \
tests/rm/dangling-symlink.sh \
+ tests/rm/dash-hint.sh \
tests/ls/ls-time.sh \
tests/rm/d-1.sh \
tests/rm/d-2.sh \
@@ -207,7 +215,6 @@ all_tests = \
tests/rm/empty-name.pl \
tests/rm/f-1.sh \
tests/rm/fail-eacces.sh \
- tests/rm/fail-eperm.xpl \
tests/tail/assert.sh \
tests/rm/hash.sh \
tests/rm/i-1.sh \
@@ -247,6 +254,7 @@ all_tests = \
tests/fmt/goal-option.sh \
tests/fmt/long-line.sh \
tests/fmt/non-space.sh \
+ tests/fmt/width.sh \
tests/misc/echo.sh \
tests/env/env.sh \
tests/env/env-signal-handler.sh \
@@ -275,13 +283,20 @@ all_tests = \
tests/od/od.pl \
tests/od/od-endian.sh \
tests/od/od-float.sh \
- tests/misc/mktemp.pl \
+ tests/mktemp/bad-unicode.sh \
+ tests/mktemp/mktemp-misc.sh \
+ tests/mktemp/mktemp.pl \
+ tests/mktemp/write-error.sh \
tests/misc/arch.sh \
+ tests/pr/bounded-memory.sh \
tests/pr/pr-tests.pl \
+ tests/pwd/argument.sh \
tests/pwd/pwd-option.sh \
tests/chcon/chcon-fail.sh \
tests/misc/coreutils.sh \
tests/cut/cut.pl \
+ tests/cut/mb-non-utf8.sh \
+ tests/cut/bounded-memory.sh \
tests/cut/cut-huge-range.sh \
tests/wc/wc.pl \
tests/wc/wc-cpu.sh \
@@ -295,11 +310,13 @@ all_tests = \
tests/cat/cat-proc.sh \
tests/cat/cat-buf.sh \
tests/cat/cat-self.sh \
+ tests/cat/splice.sh \
tests/misc/basename.pl \
tests/basenc/base64.pl \
tests/basenc/basenc.pl \
tests/basenc/bounded-memory.sh \
tests/basenc/large-input.sh \
+ tests/misc/close-stdin.sh \
tests/misc/close-stdout.sh \
tests/chroot/chroot-fail.sh \
tests/cksum/cksum.sh \
@@ -308,7 +325,8 @@ all_tests = \
tests/cksum/cksum-base64.pl \
tests/cksum/cksum-base64-untagged.sh \
tests/cksum/cksum-raw.sh \
- tests/misc/comm.pl \
+ tests/comm/comm.pl \
+ tests/comm/dash-dash.sh \
tests/csplit/csplit.sh \
tests/csplit/csplit-1000.sh \
tests/csplit/csplit-heap.sh \
@@ -318,6 +336,7 @@ all_tests = \
tests/date/date-ethiopia.sh \
tests/date/date-iran.sh \
tests/date/date-locale-hour.sh \
+ tests/date/percent-percent.sh \
tests/date/reference.sh \
tests/date/resolution.sh \
tests/date/date-sec.sh \
@@ -328,7 +347,9 @@ all_tests = \
tests/env/env-null.sh \
tests/env/env-S.pl \
tests/env/env-S-script.sh \
- tests/misc/expand.pl \
+ tests/expand/expand.pl \
+ tests/expand/bounded-memory.sh \
+ tests/expand/mb.sh \
tests/expr/expr.pl \
tests/expr/expr-multibyte.pl \
tests/factor/factor.pl \
@@ -339,10 +360,12 @@ all_tests = \
tests/fold/fold-spaces.sh \
tests/fold/fold-zero-width.sh \
tests/fold/fold.pl \
+ tests/fold/multiple-files.sh \
tests/groups/groups-dash.sh \
tests/groups/groups-process-all.sh \
tests/groups/groups-version.sh \
tests/head/head-c.sh \
+ tests/head/head-n0.sh \
tests/head/head-pos.sh \
tests/head/head-write-error.sh \
tests/misc/kill.sh \
@@ -356,7 +379,9 @@ all_tests = \
tests/misc/mknod.sh \
tests/nice/nice.sh \
tests/nice/nice-fail.sh \
- tests/misc/nl.sh \
+ tests/nl/nl.sh \
+ tests/nl/multibyte.sh \
+ tests/nl/multiple-files.sh \
tests/misc/nohup.sh \
tests/nproc/nproc-avail.sh \
tests/nproc/nproc-positive.sh \
@@ -368,9 +393,10 @@ all_tests = \
tests/od/od-j.sh \
tests/od/od-multiple-t.sh \
tests/od/od-x8.sh \
- tests/misc/paste.pl \
tests/misc/pathchk.sh \
tests/misc/printenv.sh \
+ tests/paste/paste.pl \
+ tests/paste/multi-byte.sh \
tests/printf/printf.sh \
tests/printf/printf-cov.pl \
tests/printf/printf-hex.sh \
@@ -392,6 +418,7 @@ all_tests = \
tests/cksum/sha384sum.pl \
tests/cksum/sha512sum.pl \
tests/cksum/cksum-sha3.sh \
+ tests/shred/fifo.sh \
tests/shred/shred-exact.sh \
tests/shred/shred-passes.sh \
tests/shred/shred-remove.sh \
@@ -402,6 +429,7 @@ all_tests = \
tests/cksum/sm3sum.pl \
tests/sort/sort.pl \
tests/sort/sort-benchmark-random.sh \
+ tests/sort/sort-buffer-size.sh \
tests/sort/sort-compress.sh \
tests/sort/sort-compress-hang.sh \
tests/sort/sort-compress-proc.sh \
@@ -413,6 +441,7 @@ all_tests = \
tests/sort/sort-files0-from.pl \
tests/sort/sort-float.sh \
tests/sort/sort-h-thousands-sep.sh \
+ tests/sort/sort-locale.sh \
tests/sort/sort-merge.pl \
tests/sort/sort-merge-fdlimit.sh \
tests/sort/sort-month.sh \
@@ -438,6 +467,8 @@ all_tests = \
tests/split/record-sep.sh \
tests/split/numeric.sh \
tests/split/guard-input.sh \
+ tests/split/non-utf8.sh \
+ tests/split/split-io-err.sh \
tests/stat/stat-birthtime.sh \
tests/stat/stat-fmt.sh \
tests/stat/stat-hyphen.sh \
@@ -455,14 +486,16 @@ all_tests = \
tests/cksum/sum-sysv.sh \
tests/misc/sync.sh \
tests/tac/tac.pl \
- tests/tac/tac-continue.sh \
+ tests/tac/tac-locale.sh \
tests/tac/tac-2-nonseekable.sh \
tests/tail/tail.pl \
- tests/misc/tee.sh \
+ tests/tee/append.sh \
+ tests/tee/tee.sh \
tests/test/test-N.sh \
tests/test/test-diag.pl \
tests/test/test-file.sh \
tests/misc/time-style.sh \
+ tests/timeout/init-parent.sh \
tests/timeout/timeout.sh \
tests/timeout/timeout-blocked.pl \
tests/timeout/timeout-group.sh \
@@ -470,6 +503,7 @@ all_tests = \
tests/timeout/timeout-parameters.sh \
tests/tr/tr.pl \
tests/tr/tr-case-class.sh \
+ tests/truncate/multiple-files.sh \
tests/truncate/truncate-dangling-symlink.sh \
tests/truncate/truncate-dir-fail.sh \
tests/truncate/truncate-fail-diag.sh \
@@ -481,7 +515,11 @@ all_tests = \
tests/misc/tsort.pl \
tests/tty/tty.sh \
tests/misc/usage_vs_getopt.sh \
- tests/misc/unexpand.pl \
+ tests/misc/getopt_vs_usage.sh \
+ tests/misc/usage_vs_refs.sh \
+ tests/unexpand/unexpand.pl \
+ tests/unexpand/bounded-memory.sh \
+ tests/unexpand/mb.sh \
tests/uniq/uniq.pl \
tests/uniq/uniq-perf.sh \
tests/uniq/uniq-collate.sh \
@@ -498,12 +536,14 @@ all_tests = \
tests/chmod/ignore-symlink.sh \
tests/chmod/inaccessible.sh \
tests/chmod/octal.sh \
+ tests/chmod/only-op.sh \
+ tests/chmod/partial-fail.sh \
tests/chmod/setgid.sh \
tests/chmod/silent.sh \
tests/chmod/thru-dangling.sh \
tests/chmod/umask-x.sh \
tests/chmod/usage.sh \
- tests/chmod/symlinks.sh \
+ tests/chmod/symlinks.sh \
tests/chown/deref.sh \
tests/chown/preserve-root.sh \
tests/chown/separator.sh \
@@ -539,6 +579,7 @@ all_tests = \
tests/cp/link-preserve.sh \
tests/cp/link-symlink.sh \
tests/cp/nfs-removal-race.sh \
+ tests/cp/non-utf8-name.sh \
tests/cp/no-deref-link1.sh \
tests/cp/no-deref-link2.sh \
tests/cp/no-deref-link3.sh \
@@ -552,6 +593,7 @@ all_tests = \
tests/cp/proc-short-read.sh \
tests/cp/proc-zero-len.sh \
tests/cp/r-vs-symlink.sh \
+ tests/cp/readonly-dir.sh \
tests/cp/reflink-auto.sh \
tests/cp/reflink-perm.sh \
tests/cp/same-file.sh \
@@ -566,19 +608,24 @@ all_tests = \
tests/df/df-P.sh \
tests/df/df-output.sh \
tests/df/df-symlink.sh \
+ tests/df/sync.sh \
tests/df/unreadable.sh \
tests/df/total-unprocessed.sh \
tests/df/no-mtab-status.sh \
+ tests/df/no-mtab-status-masked-proc.sh \
tests/df/skip-duplicates.sh \
tests/df/skip-rootfs.sh \
tests/dd/ascii.sh \
+ tests/dd/conv-case.sh \
tests/dd/direct.sh \
+ tests/dd/fail-ftruncate-fstat.sh \
tests/dd/misc.sh \
tests/dd/no-allocate.sh \
tests/dd/nocache.sh \
tests/dd/nocache_eof.sh \
tests/dd/nocache_fail.sh \
tests/dd/not-rewound.sh \
+ tests/dd/partial-write.sh \
tests/dd/reblock.sh \
tests/dd/skip-seek.pl \
tests/dd/skip-seek2.sh \
@@ -633,9 +680,11 @@ all_tests = \
tests/install/strip-program.sh \
tests/install/trap.sh \
tests/ln/backup-1.sh \
+ tests/ln/backup-suffix-traversal.sh \
tests/ln/hard-backup.sh \
tests/ln/hard-to-sym.sh \
tests/ln/misc.sh \
+ tests/ln/non-utf8-src.sh \
tests/ln/relative.sh \
tests/ln/sf-1.sh \
tests/ln/slash-decorated-nonexistent-dest.sh \
@@ -665,8 +714,10 @@ all_tests = \
tests/ls/w-option.sh \
tests/ls/multihardlink.sh \
tests/ls/no-arg.sh \
+ tests/ls/non-utf8-hidden.sh \
tests/ls/selinux-segfault.sh \
tests/ls/quote-align.sh \
+ tests/ls/quoting-utf8.sh \
tests/ls/size-align.sh \
tests/ls/readdir-mountpoint-inode.sh \
tests/ls/recursive.sh \
@@ -754,6 +805,7 @@ all_tests = \
tests/rmdir/t-slash.sh \
tests/tail/assert-2.sh \
tests/tail/big-4gb.sh \
+ tests/tail/debug.sh \
tests/tail/flush-initial.sh \
tests/tail/follow-name.sh \
tests/tail/follow-stdin.sh \
diff --git a/tests/ls/a-option.sh b/tests/ls/a-option.sh
index a4d763134..059072c48 100755
--- a/tests/ls/a-option.sh
+++ b/tests/ls/a-option.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# exercise the -a option
-# Copyright 2018-2025 Free Software Foundation, Inc.
+# Copyright 2018-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/abmon-align.sh b/tests/ls/abmon-align.sh
index 2ca020f0c..ef8adde99 100755
--- a/tests/ls/abmon-align.sh
+++ b/tests/ls/abmon-align.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure ls output is aligned when using abbreviated months from the locale
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/acl.sh b/tests/ls/acl.sh
index ef323341f..74e63226b 100755
--- a/tests/ls/acl.sh
+++ b/tests/ls/acl.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# verify that ls -al with acl displays the "+"
-# Copyright (C) 2024-2025 Free Software Foundation, Inc.
+# Copyright (C) 2024-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -37,4 +37,19 @@ case $ls_l in
*) fail=1; ;;
esac
+# Verify appropriate alignment in the presence of ACLs
+uid=$(id -u) || framework_failure_
+mkdir pad &&
+(
+cd pad &&
+touch f1 f2 f3 f4 f5 &&
+setfacl -m "u:${uid}:rw" f1 f2 f3 f4 f5
+) || framework_failure_
+
+# The gap between the '+' indicator and the link count must be the same
+# whether the listing contains one or several ACL entries.
+gap1=$(ls -l pad/f1 | sed -n 's/^[^+]*+\( *\)[0-9].*/\1/p')
+gap5=$(ls -l pad | sed -n 's/^[^+]*+\( *\)[0-9].*/\1/p' | head -n1)
+test "x$gap1" = "x$gap5" || fail=1
+
Exit $fail
diff --git a/tests/ls/birthtime.sh b/tests/ls/birthtime.sh
index 393cd2fe3..494920f10 100755
--- a/tests/ls/birthtime.sh
+++ b/tests/ls/birthtime.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that ls attempts birthtime access
-# Copyright (C) 2020-2025 Free Software Foundation, Inc.
+# Copyright (C) 2020-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/block-size.sh b/tests/ls/block-size.sh
index d3111c279..076a9e281 100755
--- a/tests/ls/block-size.sh
+++ b/tests/ls/block-size.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise ls --block-size and related options.
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/capability.sh b/tests/ls/capability.sh
index 484cc325d..948c38184 100755
--- a/tests/ls/capability.sh
+++ b/tests/ls/capability.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure "ls --color" properly colors name of file with capability.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/classify.sh b/tests/ls/classify.sh
index f8019973a..913a7ba94 100755
--- a/tests/ls/classify.sh
+++ b/tests/ls/classify.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test --classify processing
-# Copyright (C) 2020-2025 Free Software Foundation, Inc.
+# Copyright (C) 2020-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/color-clear-to-eol.sh b/tests/ls/color-clear-to-eol.sh
index e074ea2f1..9425a41f6 100755
--- a/tests/ls/color-clear-to-eol.sh
+++ b/tests/ls/color-clear-to-eol.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that ls --color works well when a colored name is wrapped
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/color-dtype-dir.sh b/tests/ls/color-dtype-dir.sh
index 0bbf1ea9e..65c567d77 100755
--- a/tests/ls/color-dtype-dir.sh
+++ b/tests/ls/color-dtype-dir.sh
@@ -4,7 +4,7 @@
# directories the same as the first one -- but only on a file system
# with dirent.d_type support.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/color-ext.sh b/tests/ls/color-ext.sh
index 0fc3c5f91..ba9c2fe1b 100755
--- a/tests/ls/color-ext.sh
+++ b/tests/ls/color-ext.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure "ls --color" properly colors file name extensions.
-# Copyright (C) 2018-2025 Free Software Foundation, Inc.
+# Copyright (C) 2018-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/color-norm.sh b/tests/ls/color-norm.sh
index ce189d20e..81ea53092 100755
--- a/tests/ls/color-norm.sh
+++ b/tests/ls/color-norm.sh
@@ -3,7 +3,7 @@
# I.e., that it uses NORMAL to style non file name output and
# file names with no associated color (unless FILE is also set).
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/color-term.sh b/tests/ls/color-term.sh
index 3e79a7442..7e2c1ca2e 100755
--- a/tests/ls/color-term.sh
+++ b/tests/ls/color-term.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure "ls --color" doesn't output colors for TERM=dumb
-# Copyright (C) 2014-2025 Free Software Foundation, Inc.
+# Copyright (C) 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/dangle.sh b/tests/ls/dangle.sh
index 7d7ff4bfd..9897373ca 100755
--- a/tests/ls/dangle.sh
+++ b/tests/ls/dangle.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure ls properly handles dangling symlinks vs. ls's -L, -H, options.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/dired.sh b/tests/ls/dired.sh
index 7e9784b9a..8b80b855b 100755
--- a/tests/ls/dired.sh
+++ b/tests/ls/dired.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure --dired option works
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/file-type.sh b/tests/ls/file-type.sh
index 9b4d2fbfa..620da337d 100755
--- a/tests/ls/file-type.sh
+++ b/tests/ls/file-type.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# contrast ls -F, ls -p, and ls --indicator-style=file-type
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/follow-slink.sh b/tests/ls/follow-slink.sh
index 01d597cd1..5d344f8ca 100755
--- a/tests/ls/follow-slink.sh
+++ b/tests/ls/follow-slink.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure ls -L always follows symlinks
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/getxattr-speedup.sh b/tests/ls/getxattr-speedup.sh
index 4996b3f82..4a955a3be 100755
--- a/tests/ls/getxattr-speedup.sh
+++ b/tests/ls/getxattr-speedup.sh
@@ -4,7 +4,7 @@
# This test is skipped on systems that lack LD_PRELOAD support; that's fine.
# Similarly, on a system that lacks getxattr altogether, skipping it is fine.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/group-dirs.sh b/tests/ls/group-dirs.sh
index 4e18b1b94..372587483 100755
--- a/tests/ls/group-dirs.sh
+++ b/tests/ls/group-dirs.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test --group-directories-first
-# Copyright (C) 2018-2025 Free Software Foundation, Inc.
+# Copyright (C) 2018-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/hex-option.sh b/tests/ls/hex-option.sh
index 55911a70f..63c9af168 100755
--- a/tests/ls/hex-option.sh
+++ b/tests/ls/hex-option.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# accept hex/oct numbers to -w and -T
-# Copyright (C) 2014-2025 Free Software Foundation, Inc.
+# Copyright (C) 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/hyperlink.sh b/tests/ls/hyperlink.sh
index 8ab112fd2..dc83ea34a 100755
--- a/tests/ls/hyperlink.sh
+++ b/tests/ls/hyperlink.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test --hyperlink processing
-# Copyright (C) 2017-2025 Free Software Foundation, Inc.
+# Copyright (C) 2017-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -23,6 +23,7 @@ print_ver_ ls
encode() {
printf '%s\n' \
'sp%20ace' 'ques%3ftion' 'back%5cslash' 'encoded%253Fquestion' 'testdir' \
+ 'an%23chor' 'utf8%c3%a1' 'invalidutf8%e9' 'col%3aon' \
"$1" |
sort -k1,1.1 -s | uniq -w1 -d
}
@@ -30,7 +31,7 @@ encode() {
ls_encoded() {
ef=$(encode "$1")
echo "$ef" | grep 'dir$' >/dev/null && dir=: || dir=''
- printf '\033]8;;file:///%s\a%s\033]8;;\a%s\n' \
+ printf '\033]8;;file:///%s\033\\%s\033]8;;\033\\%s\n' \
"$ef" "$1" "$dir"
}
@@ -43,13 +44,14 @@ mkdir testdir || framework_failure_
(
cd testdir
ls_encoded "testdir" > ../exp.t || framework_failure_
-for f in 'back\slash' 'encoded%3Fquestion' 'ques?tion' 'sp ace'; do
+for f in 'an#chor' 'back\slash' 'col:on' 'encoded%3Fquestion' \
+ "$(printf 'invalidutf8\351')" 'ques?tion' 'sp ace' 'utf8á'; do
touch "$f" || framework_failure_
ls_encoded "$f" >> ../exp.t || framework_failure_
done
)
ln -s testdir testdirl || framework_failure_
-(cat exp.t && printf '\n' && sed 's/[^\/]testdir/&l/' exp.t) > exp \
+(cat exp.t && printf '\n' && sed 's|[^/]testdir|&l|' exp.t) > exp \
|| framework_failure_
ls --hyper testdir testdirl >out.t || fail=1
strip_host_and_path <out.t >out || framework_failure_
diff --git a/tests/ls/infloop.sh b/tests/ls/infloop.sh
index 698c2f031..9eee34f4c 100755
--- a/tests/ls/infloop.sh
+++ b/tests/ls/infloop.sh
@@ -2,7 +2,7 @@
# show that the following no longer makes ls infloop
# mkdir loop; cd loop; ln -s ../loop sub; ls -RL
# Also ensure ls exits with status = 2 in that case.
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/inode.sh b/tests/ls/inode.sh
index 5df521f76..af452c6a5 100755
--- a/tests/ls/inode.sh
+++ b/tests/ls/inode.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure that ls -i works properly on symlinks.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/ls-misc.pl b/tests/ls/ls-misc.pl
index 4009e4381..4a22e35f4 100755
--- a/tests/ls/ls-misc.pl
+++ b/tests/ls/ls-misc.pl
@@ -1,6 +1,6 @@
#!/usr/bin/perl
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/ls-time.sh b/tests/ls/ls-time.sh
index ef72644d3..467c494cb 100755
--- a/tests/ls/ls-time.sh
+++ b/tests/ls/ls-time.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test some of ls's sorting options.
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/m-option.sh b/tests/ls/m-option.sh
index 4a2176aa2..c9502e915 100755
--- a/tests/ls/m-option.sh
+++ b/tests/ls/m-option.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# exercise the -m option
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -37,4 +37,17 @@ EOF
compare exp out || fail=1
+# Ensure exact-fit comma output accounts for the trailing separator.
+touch bb c || framework_failure_
+cat <<\EOF > exp || framework_failure_
+a,
+bb, c
+EOF
+ls -w5 -m a bb c > out || fail=1
+compare exp out || fail=1
+
+printf '%s\n' 'a, bb' > exp || framework_failure_
+ls -w5 -m a bb > out || fail=1
+compare exp out || fail=1
+
Exit $fail
diff --git a/tests/ls/multihardlink.sh b/tests/ls/multihardlink.sh
index a2c0d7eef..17162f3e9 100755
--- a/tests/ls/multihardlink.sh
+++ b/tests/ls/multihardlink.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure "ls --color" properly colors names of hard linked files.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/nameless-uid.sh b/tests/ls/nameless-uid.sh
index caaf767e9..567143753 100755
--- a/tests/ls/nameless-uid.sh
+++ b/tests/ls/nameless-uid.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that ls -l works on files with nameless uid and/or gid
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/no-arg.sh b/tests/ls/no-arg.sh
index 874ebbcc8..3116bfbb7 100755
--- a/tests/ls/no-arg.sh
+++ b/tests/ls/no-arg.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure ls and 'ls -R' do the right thing when invoked with no arguments.
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/no-cap.sh b/tests/ls/no-cap.sh
index 99f0563bc..6f639625b 100755
--- a/tests/ls/no-cap.sh
+++ b/tests/ls/no-cap.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that an empty "ca=" attribute disables ls's capability-checking
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/non-utf8-hidden.sh b/tests/ls/non-utf8-hidden.sh
new file mode 100755
index 000000000..06dc4084a
--- /dev/null
+++ b/tests/ls/non-utf8-hidden.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+# Test that files starting with a dot are treated as hidden
+# even with invalid UTF-8
+
+# Copyright 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ ls
+
+mkdir d || framework_failure_
+echo "content" > d/visible || framework_failure_
+
+# Create a hidden file with valid UTF-8
+touch d/.hidden_valid || framework_failure_
+
+# Create a hidden file with invalid UTF-8 (without trailing newline)
+printf 'content\n' > d/$(printf '.hidden_invalid%s' "$(bad_unicode)") \
+ || skip_ 'bad unicode not supported in shell or file system'
+
+for loc in C "$LOCALE_FR" "$LOCALE_FR_UTF8"; do
+ test -z "$loc" && continue
+ export LC_ALL="$loc"
+
+ # Test 1: Without -a flag, only visible file should appear
+ ls -U d > out1 || fail=1
+ echo 'visible' > exp1
+ compare exp1 out1 || fail=1
+
+ # Test 2: With -a flag, all files including hidden ones should appear
+ # The invalid UTF-8 filename will be shown in some escaped form
+ ls -a -U d > out2 || fail=1
+
+ # Check that we have at least 5 entries (., .., visible, and 2 hidden files)
+ line_count=$(wc -l < out2)
+ test "$line_count" -ge 5 || fail=1
+
+ # Test 3: Verify the visible file is still there with -a
+ grep -F "visible" out2 > /dev/null || fail=1
+
+ # Test 4: Verify the valid hidden file appears with -a
+ grep -F ".hidden_valid" out2 > /dev/null || fail=1
+
+ # Test 5: Verify the invalid hidden file appears with -a
+ grep -F ".hidden_invalid" out2 > /dev/null || fail=1
+done
+
+Exit $fail
diff --git a/tests/ls/quote-align.sh b/tests/ls/quote-align.sh
index 7cacdfcac..7e5692c94 100755
--- a/tests/ls/quote-align.sh
+++ b/tests/ls/quote-align.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test quote alignment combinations
-# Copyright (C) 2016-2025 Free Software Foundation, Inc.
+# Copyright (C) 2016-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/quoting-utf8.sh b/tests/ls/quoting-utf8.sh
new file mode 100755
index 000000000..a89c0db31
--- /dev/null
+++ b/tests/ls/quoting-utf8.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+# Ensure --quoting-style=locale/clocale uses Unicode quotes in UTF-8 locales
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh";
+print_ver_ ls
+
+test "$(LC_ALL=en_US.UTF-8 locale charmap 2>/dev/null)" = UTF-8 ||
+ skip_ 'en_US.UTF-8 locale not available'
+
+touch 'hello world' "it's" 'say "hi"' 'tab here' || framework_failure_
+
+# Note we use en_US as there are no translations provided,
+# and so locale quoting for UTF-8 is hardcoded to these quoting characters:
+# U+2018 = \xe2\x80\x98 (LEFT SINGLE QUOTATION MARK)
+# U+2019 = \xe2\x80\x99 (RIGHT SINGLE QUOTATION MARK)
+lq=$(printf '\342\200\230')
+rq=$(printf '\342\200\231')
+
+# In UTF-8 locales, both locale and clocale should use Unicode quotes
+for style in locale clocale; do
+ LC_ALL=en_US.UTF-8 ls --quoting-style=$style > out_${style}_utf8 || fail=1
+
+ # Verify Unicode left/right quotes are present
+ grep "$lq" out_${style}_utf8 > /dev/null 2>&1 \
+ || { echo "$style UTF-8: missing Unicode left quote"; fail=1; }
+ grep "$rq" out_${style}_utf8 > /dev/null 2>&1 \
+ || { echo "$style UTF-8: missing Unicode right quote"; fail=1; }
+
+ # Verify 'hello world' is quoted with Unicode quotes
+ grep "^${lq}hello world${rq}\$" out_${style}_utf8 > /dev/null 2>&1 \
+ || { echo "$style UTF-8: 'hello world' not properly quoted"; fail=1; }
+
+ # Embedded apostrophe should NOT be escaped (delimiters are different chars)
+ grep "^${lq}it's${rq}\$" out_${style}_utf8 > /dev/null 2>&1 || \
+ { echo "$style UTF-8: embedded apostrophe shouldn't be escaped"; fail=1; }
+
+ # Embedded double quote should NOT be escaped
+ grep "^${lq}say \"hi\"${rq}\$" out_${style}_utf8 > /dev/null 2>&1 || \
+ { echo "$style UTF-8: embedded double quote shouldn't be escaped"; fail=1; }
+
+ # Control characters should still be C-escaped
+ grep "tab\\\\there" out_${style}_utf8 > /dev/null 2>&1 \
+ || { echo "$style UTF-8: tab should be escaped as \\t"; fail=1; }
+done
+
+# In C locale, locale uses ASCII single quotes, clocale uses ASCII double quotes
+LC_ALL=C ls --quoting-style=locale > out_locale_c || fail=1
+grep "^'hello world'\$" out_locale_c > /dev/null 2>&1 \
+ || { echo "locale C: expected ASCII single quotes"; fail=1; }
+
+LC_ALL=C ls --quoting-style=clocale > out_clocale_c || fail=1
+grep '^"hello world"$' out_clocale_c > /dev/null 2>&1 \
+ || { echo "clocale C: expected ASCII double quotes"; fail=1; }
+
+Exit $fail
diff --git a/tests/ls/readdir-mountpoint-inode.sh b/tests/ls/readdir-mountpoint-inode.sh
index d6e967a15..56d3a11d5 100755
--- a/tests/ls/readdir-mountpoint-inode.sh
+++ b/tests/ls/readdir-mountpoint-inode.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that ls -i works also for mount points
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/recursive.sh b/tests/ls/recursive.sh
index cf16b64fb..477f92f54 100755
--- a/tests/ls/recursive.sh
+++ b/tests/ls/recursive.sh
@@ -2,7 +2,7 @@
# 4.1.1 and 4.1.2 had a bug whereby some recursive listings
# didn't include a blank line between per-directory groups of files.
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -59,4 +59,11 @@ EOF
compare exp out || fail=1
+# Check that we don't run out of file descriptors when visiting
+# directories recursively.
+mkdir -p $(seq 30 | tr '\n' '/') || framework_failure_
+(ulimit -n 20; ls -R 1 > out 2> err) || fail=1
+test $(wc -l < out) = 88 || fail=1
+test $(wc -l < err) = 0 || fail=1
+
Exit $fail
diff --git a/tests/ls/removed-directory.sh b/tests/ls/removed-directory.sh
index ef622b5da..2034b7ac8 100755
--- a/tests/ls/removed-directory.sh
+++ b/tests/ls/removed-directory.sh
@@ -3,7 +3,7 @@
# current working directory has been removed by another process), it
# should not emit an error message merely because the directory is removed.
-# Copyright (C) 2020-2025 Free Software Foundation, Inc.
+# Copyright (C) 2020-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/root-rel-symlink-color.sh b/tests/ls/root-rel-symlink-color.sh
index 97792466c..b07ebfbef 100755
--- a/tests/ls/root-rel-symlink-color.sh
+++ b/tests/ls/root-rel-symlink-color.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise the 8.17 ls bug with coloring relative-named symlinks in "/".
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/rt-1.sh b/tests/ls/rt-1.sh
index 78d0c0e3c..97382507e 100755
--- a/tests/ls/rt-1.sh
+++ b/tests/ls/rt-1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure name is used as secondary key when sorting on mtime or ctime.
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/selinux-segfault.sh b/tests/ls/selinux-segfault.sh
index 1cac2b5fc..385fe956f 100755
--- a/tests/ls/selinux-segfault.sh
+++ b/tests/ls/selinux-segfault.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure we don't segfault in selinux handling
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/selinux.sh b/tests/ls/selinux.sh
index ab7b25b4b..93775540f 100755
--- a/tests/ls/selinux.sh
+++ b/tests/ls/selinux.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test ls SELinux file context output
-# Copyright (C) 2024-2025 Free Software Foundation, Inc.
+# Copyright (C) 2024-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/size-align.sh b/tests/ls/size-align.sh
index 714af1461..52e5e87fb 100755
--- a/tests/ls/size-align.sh
+++ b/tests/ls/size-align.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test size alignment
-# Copyright (C) 2023-2025 Free Software Foundation, Inc.
+# Copyright (C) 2023-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/slink-acl.sh b/tests/ls/slink-acl.sh
index b0b02b5e6..8030a8538 100755
--- a/tests/ls/slink-acl.sh
+++ b/tests/ls/slink-acl.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# verify that ls -lL works when applied to a symlink to an ACL'd file
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/sort-width-option.sh b/tests/ls/sort-width-option.sh
index 8a3844186..7ec6983b1 100755
--- a/tests/ls/sort-width-option.sh
+++ b/tests/ls/sort-width-option.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise the --sort=width option.
-# Copyright (C) 2021-2025 Free Software Foundation, Inc.
+# Copyright (C) 2021-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/stat-dtype.sh b/tests/ls/stat-dtype.sh
index 855141895..5cbce6862 100755
--- a/tests/ls/stat-dtype.sh
+++ b/tests/ls/stat-dtype.sh
@@ -3,7 +3,7 @@
# Also check for the dtype-related (and fs-type dependent) bug
# in coreutils-6.0 that made ls -CF columns misaligned.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/stat-failed.sh b/tests/ls/stat-failed.sh
index e9fd56fe8..3a2c4b5ca 100755
--- a/tests/ls/stat-failed.sh
+++ b/tests/ls/stat-failed.sh
@@ -2,7 +2,7 @@
# Verify that ls works properly when it fails to stat a file that is
# not mentioned on the command line.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/stat-free-color.sh b/tests/ls/stat-free-color.sh
index 4d0544914..7e95671f5 100755
--- a/tests/ls/stat-free-color.sh
+++ b/tests/ls/stat-free-color.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Show that --color need not use stat, as long as we have d_type support.
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/stat-free-symlinks.sh b/tests/ls/stat-free-symlinks.sh
index b95e73eda..5597c04cc 100755
--- a/tests/ls/stat-free-symlinks.sh
+++ b/tests/ls/stat-free-symlinks.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that ls does not stat a symlink in an unusual case
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/stat-vs-dirent.sh b/tests/ls/stat-vs-dirent.sh
index 5cc864520..3be3a8a55 100755
--- a/tests/ls/stat-vs-dirent.sh
+++ b/tests/ls/stat-vs-dirent.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that d_ino (from ls -di) and st_ino (from stat --format=%i) match.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/symlink-loop.sh b/tests/ls/symlink-loop.sh
index f408117a2..b2995f7fc 100755
--- a/tests/ls/symlink-loop.sh
+++ b/tests/ls/symlink-loop.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise ls symlink ELOOP handling
-# Copyright (C) 2023-2025 Free Software Foundation, Inc.
+# Copyright (C) 2023-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/symlink-quote.sh b/tests/ls/symlink-quote.sh
index 3d4f0c2ab..b083148d1 100755
--- a/tests/ls/symlink-quote.sh
+++ b/tests/ls/symlink-quote.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure symlinks are quoted appropriately
-# Copyright (C) 2017-2025 Free Software Foundation, Inc.
+# Copyright (C) 2017-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/symlink-slash.sh b/tests/ls/symlink-slash.sh
index 88dd60133..b18d8e917 100755
--- a/tests/ls/symlink-slash.sh
+++ b/tests/ls/symlink-slash.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Do dereference a symlink arg if its name is written with a trailing slash.
-# Copyright (C) 1999-2025 Free Software Foundation, Inc.
+# Copyright (C) 1999-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/time-style-diag.sh b/tests/ls/time-style-diag.sh
index 95d613eaa..61c19f855 100755
--- a/tests/ls/time-style-diag.sh
+++ b/tests/ls/time-style-diag.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that an invalid --time-style=ARG is diagnosed the way we want.
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/w-option.sh b/tests/ls/w-option.sh
index dbab6d62b..418121f97 100755
--- a/tests/ls/w-option.sh
+++ b/tests/ls/w-option.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# exercise the -w option
-# Copyright (C) 2015-2025 Free Software Foundation, Inc.
+# Copyright (C) 2015-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -45,4 +45,34 @@ TERM=xterm ls -w0 -x --color=always || fail=1
ls -w4 -x -T0 a b > out || fail=1
compare exp out || fail=1
+# coreutils <= 9.11 could display 1 column too few
+touch aa c || framework_failure_
+cat <<\EOF > exp || framework_failure_
+aa b
+c
+EOF
+ls -w5 -x -T0 aa b c > out || fail=1
+compare exp out || fail=1
+
+# coreutils <= 9.11 could display 1 column too few
+cat <<\EOF > exp || framework_failure_
+aa c
+b
+EOF
+ls -w5 -C -T0 aa b c > out || fail=1
+compare exp out || fail=1
+
+# These entries span 79 columns with a separator of two spaces
+# coreutils <= 9.11, and BSDs wrap with -w79 as new line included
+# Solaris 11 wraps with width <= 84? (COLUMNS=84 ls -m)
+# uutils 0.7.0 wraps with width <= 96?
+files="\
+Desktop Documents Downloads Music Pictures Public Templates Videos code"
+mkdir subdir2 && (cd subdir2 && touch $files) || framework_failure_
+printf '%s\n' "$files" > exp || framework_failure_
+ls -x -T0 -w79 subdir2 > out || fail=1 # Should not wrap at 79
+compare exp out || fail=1
+ls -x -T0 -w78 subdir2 > out || fail=1 # Should wrap at 78
+test "$(wc -l < out)" -gt 1 || fail=1
+
Exit $fail
diff --git a/tests/ls/x-option.sh b/tests/ls/x-option.sh
index 6f7013c01..4b59c9e40 100755
--- a/tests/ls/x-option.sh
+++ b/tests/ls/x-option.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise the -x option.
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ls/zero-option.sh b/tests/ls/zero-option.sh
index 7c7214b9c..8c06b5a04 100755
--- a/tests/ls/zero-option.sh
+++ b/tests/ls/zero-option.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify behavior of ls --zero.
-# Copyright 2021-2025 Free Software Foundation, Inc.
+# Copyright 2021-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/arch.sh b/tests/misc/arch.sh
index 198e904b3..68dc46044 100755
--- a/tests/misc/arch.sh
+++ b/tests/misc/arch.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that arch output is equal to uname -m
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,7 +21,9 @@ print_ver_ arch
arch > out || fail=1
uname -m > exp || fail=1
+compare exp out || fail=1
+arch -- > out || fail=1
compare exp out || fail=1
Exit $fail
diff --git a/tests/misc/basename.pl b/tests/misc/basename.pl
index 75c9646b8..8188817b8 100755
--- a/tests/misc/basename.pl
+++ b/tests/misc/basename.pl
@@ -1,6 +1,6 @@
#!/usr/bin/perl
# Test basename.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/close-stdin.sh b/tests/misc/close-stdin.sh
new file mode 100755
index 000000000..1bec15f79
--- /dev/null
+++ b/tests/misc/close-stdin.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+# Ensure that several programs work fine, even with stdin initially closed.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+getlimits_
+
+printf '%s' "\
+basenc --base16 -
+basenc --base2lsbf -
+basenc --base2msbf -
+basenc --base32 -
+basenc --base32hex -
+basenc --base58 -
+basenc --base64 -
+basenc --base64url -
+cat -
+cksum -a blake2b -
+cksum -a bsd -
+cksum -a crc -
+cksum -a crc32b -
+cksum -a md5 -
+cksum -a sha1 -
+cksum -a sha2 -l 256 -
+cksum -a sha3 -l 256 -
+cksum -a sm3 -
+cksum -a sysv -
+cksum -c -
+comm - -
+cut -f1 -
+date --file=-
+dd
+dircolors -
+du --files0-from=-
+expand -
+factor
+fmt -
+fold -
+head -n1 -
+join - /dev/null
+nl -
+numfmt
+od -
+paste -
+pr -
+ptx -
+shuf -
+split -
+stty
+tac -
+tail -
+tee
+tr a a
+tsort
+unexpand -
+uniq -
+wc -
+" |
+sort -k 1b,1 > all_readers || framework_failure_
+
+printf '%s\n' $built_programs |
+sort -k 1b,1 > built_programs || framework_failure_
+
+join all_readers built_programs > built_writers || framework_failure_
+
+while read reader; do
+ $reader 2>err <&- && fail=1
+ ! grep -E "$EBADF" err >/dev/null \
+ && { fail=1; cat err; echo "$reader: failed to diagnose EBADF" >&2; }
+done < built_writers
+
+Exit $fail
diff --git a/tests/misc/close-stdout.sh b/tests/misc/close-stdout.sh
index 94b833cc2..37dd0c888 100755
--- a/tests/misc/close-stdout.sh
+++ b/tests/misc/close-stdout.sh
@@ -2,7 +2,7 @@
# Ensure that several programs work fine, even with stdout initially closed.
# This is effectively a test of closeout.c's close_stdout function.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/coreutils.sh b/tests/misc/coreutils.sh
index 2f0697c8d..36829360e 100755
--- a/tests/misc/coreutils.sh
+++ b/tests/misc/coreutils.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify behavior of separate coreutils multicall binary
-# Copyright (C) 2014-2025 Free Software Foundation, Inc.
+# Copyright (C) 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,17 +20,25 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ coreutils
-test -s "$abs_top_builddir/src/coreutils.h" \
- || skip_ "multicall binary is disabled"
+cp -s "$(command -v coreutils)" blah || skip_ 'multicall binary is disabled'
# Yes outputs all its params so is good to verify argv manipulations
-echo 'y' > exp
-coreutils --coreutils-prog=yes | head -n10 | uniq > out
+echo 'y' > exp &&
+coreutils --coreutils-prog=yes | head -n10 | uniq > out || framework_failure_
compare exp out || fail=1
+# Ensure empty arg is rejected
+returns_ 1 coreutils $unset_command || fail=1
+
# Ensure if incorrect program passed, we diagnose
-echo "coreutils: unknown program 'blah'" > exp
-coreutils --coreutils-prog='blah' --help 2>err && fail=1
+echo "coreutils: unknown program 'blah'" > exp || framework_failure_
+
+returns_ 1 coreutils --coreutils-prog='blah' --help 2>err || fail=1
+compare exp err || fail=1
+
+returns_ 1 ./blah 2>err || fail=1
+compare exp err || fail=1
+returns_ 1 ./blah --version 2>err || fail=1
compare exp err || fail=1
Exit $fail
diff --git a/tests/misc/dircolors.pl b/tests/misc/dircolors.pl
index 979bd26bc..faa8402b3 100755
--- a/tests/misc/dircolors.pl
+++ b/tests/misc/dircolors.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Simple dircolors tests.
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/dirname.pl b/tests/misc/dirname.pl
index 5ad719f74..47701750f 100755
--- a/tests/misc/dirname.pl
+++ b/tests/misc/dirname.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test "dirname".
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/echo.sh b/tests/misc/echo.sh
index d89369d3b..9156b7dc1 100755
--- a/tests/misc/echo.sh
+++ b/tests/misc/echo.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# basic tests for echo
-# Copyright (C) 2018-2025 Free Software Foundation, Inc.
+# Copyright (C) 2018-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/false-status.sh b/tests/misc/false-status.sh
index d0048b1c8..ab8585d09 100755
--- a/tests/misc/false-status.sh
+++ b/tests/misc/false-status.sh
@@ -2,7 +2,7 @@
# ensure that false exits nonzero even with --help or --version
# and ensure that true exits nonzero when it can't write --help or --version
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/getopt_vs_usage.sh b/tests/misc/getopt_vs_usage.sh
new file mode 100755
index 000000000..51b0324de
--- /dev/null
+++ b/tests/misc/getopt_vs_usage.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+# Verify that all supported options are mentioned in usage
+
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+
+getopts() {
+ sed -n '/long_*opt.*\[/,/^}/{/^ *{/p}' "$abs_top_srcdir/src/$1.c" |
+ grep -Ev "(\"-|Deprecated|Obsolescent|Not in $1)"
+}
+# Currently only check short opts with corresponding long opt
+shortopts() { getopts $1 | cut -s -d"'" -f2; }
+longopts() { getopts $1 | cut -s -d'"' -f2; }
+
+for prg in $built_programs; do
+
+ sprg=$prg
+ test $prg = ginstall && sprg=install
+
+ # Only check cases where the command matches the source name.
+ # One could lookup the actual source file for main with nm -l
+ # but then we'd have to deal with cases like one source file
+ # only enabling some options for certain commands.
+ test -f "$abs_top_srcdir/src/$sprg.c" || {
+ test "$DEBUG" && echo skipping $sprg
+ continue
+ }
+
+ test "$DEBUG" && echo processing $sprg
+ got_option=false
+ for opt in $(shortopts $sprg); do
+ got_option=true
+ env $prg --help | grep -F -- " -$opt" >/dev/null ||
+ { printf -- '%s -%s missing from --help\n' $sprg $opt >&2; fail=1; }
+ done
+ for opt in $(longopts $sprg); do
+ got_option=true
+ env $prg --help | grep -F -- "--$opt" >/dev/null ||
+ { printf -- '%s --%s missing from --help\n' $sprg $opt >&2; fail=1; }
+ done
+ test "$DEBUG" && test $got_option = false && echo No options for $prg ?
+
+done
+
+Exit $fail
diff --git a/tests/misc/invalid-opt.pl b/tests/misc/invalid-opt.pl
index 3531a26b2..1d1ce8777 100755
--- a/tests/misc/invalid-opt.pl
+++ b/tests/misc/invalid-opt.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# exercise the 'invalid option' handling code in each program
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/io-errors.sh b/tests/misc/io-errors.sh
new file mode 100755
index 000000000..a7f03a5b2
--- /dev/null
+++ b/tests/misc/io-errors.sh
@@ -0,0 +1,133 @@
+#!/bin/sh
+# Make sure all of these programs diagnose write errors.
+
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ env
+getlimits_
+
+if ! test -w /dev/full || ! test -c /dev/full; then
+ skip_ '/dev/full is required'
+fi
+
+echo foo > foo || framework_failure_
+
+# Specific write paths to check in addition to --version.
+# Note writers that may output data indefinitely
+# are handled in write-errors.sh.
+# Commands tagged with $generic are not checked for a specific error.
+# First word in command line is checked against built programs
+{
+printf '%s' "\
+cat foo
+cksum -a sysv foo
+cksum -a bsd foo
+cksum -a crc foo
+cksum -a crc32b foo
+cksum -a md5 foo
+cksum -a sha1 foo
+cksum -a sha2 -l 256 foo
+cksum -a sha3 -l 256 foo
+cksum -a blake2b foo
+cksum -a sm3 foo
+comm foo foo
+cut -c1- foo
+cut -f1- foo
+date +%s
+dd status=none if=foo
+expand foo
+factor 1
+fmt foo
+fold foo
+fold -b foo
+fold -c foo
+groups
+head -n1 foo
+id
+join foo foo
+nl foo
+numfmt --invalid=ignore < foo
+od -v foo
+paste foo
+pr foo
+seq 1
+tac --version; seq 10000 | tac
+tail -n1 foo
+tee < foo
+tr . . < foo
+unexpand foo
+uniq foo
+";
+printf '%s --version $generic\n' $built_programs;
+} |
+sort -k 1b,1 > all_writers || framework_failure_
+
+printf '%s\n' $built_programs |
+sort -k 1b,1 > built_programs || framework_failure_
+
+join all_writers built_programs > built_writers || framework_failure_
+
+while read writer; do
+
+ # Skip edge cases
+ printf '%s' "$writer" | grep -E '^(test|false|true|install)' &&
+ { echo "Skipping $writer" >&2; continue; }
+
+ # Check /dev/full diagnosed.
+ # Note we usually give a specific diagnostic (ENOSPC),
+ # but that's not guaranteed in the generic close_stream() handling.
+ # For e.g. with _IOLBF etc, stdio will discard pending data at each line,
+ # thus only giving a generic error upon ferror() in close_stream().
+ ere="$ENOSPC|$EBADF"
+
+ # musl writes the first line immediately when it should be fully buffered.
+ # As a result, when we print a single line there is no bytes buffered when
+ # we close the stream and errno is not set. See:
+ # <https://www.openwall.com/lists/musl/2026/04/02/1>.
+ case $host_os in
+ *-musl*) ere="$ere|" ;;
+ esac
+
+ printf '%s' "$writer" | grep 'generic' >/dev/null &&
+ { ere="write error|$ere"; }
+
+ rm -f full.err || framework_failure_
+ timeout 10 env --default-signal=PIPE $SHELL -c \
+ "(env $writer 2>full.err >/dev/full)"
+ { test $? = 124 || test $? = 0 || ! grep -E "$ere" full.err >/dev/null; } &&
+ { fail=1; cat full.err; echo "$writer: failed to diagose ENOSPC" >&2; }
+
+ # Check closed stdout handling
+ rm -f closed.err || framework_failure_
+ timeout 10 env --default-signal=PIPE $SHELL -c \
+ "(env $writer 2>closed.err >&-)"
+ { test $? = 124 || test $? = 0 || ! grep -E "$ere" closed.err >/dev/null; } &&
+ { fail=1; cat closed.err; echo "$writer: failed to diagnose EBADF" >&2; }
+
+ # https://github.com/ksh93/ksh/issues/741
+ $SHELL -c 'test -n "$KSH_VERSION"' && continue
+
+ # Check closed pipe handling
+ rm -f pipe.err || framework_failure_
+ timeout 10 env --default-signal=PIPE $SHELL -c \
+ "(env $writer 2>pipe.err | :)"
+ { test $? = 0 && compare /dev/null pipe.err; } ||
+ { fail=1; cat pipe.err; echo "$writer: failed writing to closed pipe" >&2; }
+
+done < built_writers
+
+Exit $fail
diff --git a/tests/misc/kill.sh b/tests/misc/kill.sh
index 4f9c11ab7..0f942e6c1 100755
--- a/tests/misc/kill.sh
+++ b/tests/misc/kill.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Validate kill operation
-# Copyright (C) 2015-2025 Free Software Foundation, Inc.
+# Copyright (C) 2015-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ kill seq
+getlimits_
# params required
returns_ 1 env kill || fail=1
@@ -71,4 +72,16 @@ env kill -t -- $SIG_SEQ || fail=1
# Verify first signal number listed is 0
test $(env kill -l $(env kill -l | head -n1)) = 0 || fail=1
+# If the system's signal.h defines SIGRTMIN and SIGRTMAX, assume that real-time
+# signals are supported.
+if test $SIGRTMIN -gt 0 && test $SIGRTMAX -gt $SIGRTMIN; then
+ rtmin=$(env kill -l | grep -c '^RTMIN')
+ rtmax=$(env kill -l | grep -c '^RTMAX')
+ # We only check that at least RTMIN and RTMAX are listed in the output.
+ # POSIX states the range of signals between SIGRTMIN and SIGRTMAX are
+ # reserved for real-time signals, but does not require that all signals
+ # in the range are usable.
+ test $rtmin -gt 0 && test $rtmax -gt 0 || fail=1
+fi
+
Exit $fail
diff --git a/tests/misc/mknod.sh b/tests/misc/mknod.sh
index 24ba8f1eb..001dce8e9 100755
--- a/tests/misc/mknod.sh
+++ b/tests/misc/mknod.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that mknod, mkfifo, mkdir -m MODE work with a restrictive umask
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -36,4 +36,18 @@ mkdir -m 734 f3 || fail=1
mode=$(ls -dgo f3|cut -b-10)
test $mode = drwx-wxr-- || test $mode = drwx-wsr-- || fail=1
+mknod --mode='ug+rw,o+r' f4 p || fail=1
+mode=$(ls -dgo f4 | cut -b-10)
+test "$mode" = prw-rw-rw- || fail=1
+
+mkfifo --mode='ug+rw,o+r' f5 || fail=1
+mode=$(ls -dgo f5 | cut -b-10)
+test "$mode" = prw-rw-rw- || fail=1
+
+if ! test -g .; then
+ mkdir --mode='ug+rw,o+r' f6 || fail=1
+ mode=$(ls -dgo f6 | cut -b-10)
+ test "$mode" = drwxrwxrwx || fail=1
+fi
+
Exit $fail
diff --git a/tests/misc/nohup.sh b/tests/misc/nohup.sh
index f49997f26..5c9c96606 100755
--- a/tests/misc/nohup.sh
+++ b/tests/misc/nohup.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test nohup
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ nohup
-
+getlimits_
nohup sh -c 'echo stdout; echo stderr 1>&2' 2>err || fail=1
@@ -108,9 +108,9 @@ if test -t 1; then
compare /dev/null nohup.out || fail=1
fi
-cat <<\EOF > exp || framework_failure_
+cat <<EOF > exp || framework_failure_
nohup: appending output to 'nohup.out'
-nohup: cannot run command './k': Permission denied
+nohup: cannot run command './k': $EACCES
EOF
# Disable these comparisons. Too much variation in 2nd line.
# compare exp err || fail=1
diff --git a/tests/misc/option-aliases.sh b/tests/misc/option-aliases.sh
index d1c4f4b6c..923185e75 100755
--- a/tests/misc/option-aliases.sh
+++ b/tests/misc/option-aliases.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure commands support all aliased options
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/pathchk.sh b/tests/misc/pathchk.sh
index 8909c22b2..859400027 100755
--- a/tests/misc/pathchk.sh
+++ b/tests/misc/pathchk.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# pathchk tests
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/printenv.sh b/tests/misc/printenv.sh
index 288005353..f146d46b2 100755
--- a/tests/misc/printenv.sh
+++ b/tests/misc/printenv.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify behavior of printenv.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/read-errors.sh b/tests/misc/read-errors.sh
index 0a5f7d825..81fa5b9dd 100755
--- a/tests/misc/read-errors.sh
+++ b/tests/misc/read-errors.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure all of these programs diagnose read errors
-# Copyright (C) 2023-2025 Free Software Foundation, Inc.
+# Copyright (C) 2023-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -17,49 +17,54 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+uses_strace_
+getlimits_
! cat . >/dev/null 2>&1 || skip_ "Need unreadable directories"
-printf '%s' "\
+# commands tagged with $count are checked that they output
+# the correct number of errors
+printf '%s' '\
basenc --base32 .
basenc -d --base64 .
-cat .
-cksum -a blake2b .
-cksum -a bsd .
-cksum -a crc .
-cksum -a crc32b .
-cksum -a md5 .
-cksum -a sha1 .
-cksum -a sha2 -l 224 .
-cksum -a sha2 -l 256 .
-cksum -a sha2 -l 384 .
-cksum -a sha2 -l 512 .
-cksum -a sha3 -l 224 .
-cksum -a sha3 -l 256 .
-cksum -a sha3 -l 384 .
-cksum -a sha3 -l 512 .
-cksum -a sm3 .
-cksum -a sysv .
+cat . $count
+cksum -a blake2b . $count
+cksum -a bsd . $count
+cksum -a crc . $count
+cksum -a crc32b . $count
+cksum -a md5 . $count
+cksum -a sha1 . $count
+cksum -a sha2 -l 224 . $count
+cksum -a sha2 -l 256 . $count
+cksum -a sha2 -l 384 . $count
+cksum -a sha2 -l 512 . $count
+cksum -a sha3 -l 224 . $count
+cksum -a sha3 -l 256 . $count
+cksum -a sha3 -l 384 . $count
+cksum -a sha3 -l 512 . $count
+cksum -a sm3 . $count
+cksum -a sysv . $count
+cksum -a md5 /dev/null | sed "s|/dev/null|.|" | cksum --check
comm . .
csplit . 1
-cut -c1 .
-cut -f1 .
+cut -b1 . $count
+cut -f1 . $count
date -f .
dd if=.
dircolors .
-expand .
+expand . $count
factor < .
-fmt .
-fold .
-head -n1 .
-head -n-1 .
-head -c1 .
-head -c-1 .
+fmt . $count
+fold . $count
+head -n1 . $count
+head -n-1 . $count
+head -c1 . $count
+head -c-1 . $count
join . .
-nl .
+nl . $count
numfmt < .
-od .
-paste .
+od . $count
+paste . $count
pr .
ptx .
shuf -r .
@@ -71,21 +76,21 @@ split -C1 .
split -n1 .
split -nl/1 .
split -nr/1 .
-tac .
-tail -n1 .
-tail -c1 .
-tail -n+1 .
-tail -c+1 .
+tac . $count
+tail -n1 . $count
+tail -c1 . $FIXME_count
+tail -n+1 . $FIXME_count
+tail -c+1 . $FIXME_count
tee < .
tr 1 1 < .
tsort .
-unexpand .
+unexpand . $count
uniq .
uniq -c .
-wc .
-wc -c .
-wc -l .
-" |
+wc . $count
+wc -c . $count
+wc -l . $count
+' |
sort -k 1b,1 > all_readers || framework_failure_
printf '%s\n' $built_programs |
@@ -93,8 +98,36 @@ sort -k 1b,1 > built_programs || framework_failure_
join all_readers built_programs > built_readers || framework_failure_
+count=''
while read reader; do
eval $reader >/dev/null && { fail=1; echo "$reader: exited with 0" >&2; }
done < built_readers
+count='.' # read '.' twice
+while read reader; do
+ printf '%s' "$reader" | grep -F '$count' >/dev/null || continue
+ eval $reader 2>errs >/dev/null
+ test "$?" = 1 ||
+ { fail=1; echo "$reader: did not exit with 1" >&2; }
+ test "$(grep -F : errs | wc -l)" = 2 ||
+ { fail=1; echo "$reader: did not produce 2 errors" >&2; cat errs; }
+done < built_readers
+
+expected_failure_status_sort=2
+
+# Ensure read is called, otherwise it's a layering violation.
+# Also ensure a read error is diagnosed appropriately.
+if strace -o /dev/null -P _ -e '/read,splice' -e fault=all:error=EIO true; then
+ while read reader; do
+ cmd=$(printf '%s\n' "$reader" | cut -d ' ' -f1) || framework_failure_
+ eval "expected=\$expected_failure_status_$cmd"
+ test x$expected = x && expected=1
+ returns_ $expected \
+ strace -f -o /dev/null -P . -e '/read,splice' -e fault=all:error=EIO \
+ $SHELL -c "$reader" 2>err || fail=1
+ grep -F "$EIO" err >/dev/null || { cat err; fail=1; }
+ done < built_readers
+fi
+
+
Exit $fail
diff --git a/tests/misc/realpath.sh b/tests/misc/realpath.sh
index 3ba9bd36c..56bd8597a 100755
--- a/tests/misc/realpath.sh
+++ b/tests/misc/realpath.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Validate realpath operation
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/responsive.sh b/tests/misc/responsive.sh
new file mode 100755
index 000000000..a63b55ca1
--- /dev/null
+++ b/tests/misc/responsive.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+# Make sure all of these programs are responsive to input
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ stdbuf
+
+# stdbuf fails when the absolute top build dir name contains e.g.,
+# space, TAB, NL
+lf='
+'
+case $abs_top_builddir in
+ *[\\\"\#\$\&\'\`$lf\ \ ]*)
+ skip_ "unsafe absolute build directory name: $abs_top_builddir";;
+esac
+
+stdbuf -oL true || skip_ 'stdbuf not supported'
+
+mkfifo_or_skip_ in
+mkfifo_or_skip_ hold
+
+printf '%s' "\
+cat
+cut -b1
+cut -c1
+cut -f1
+date -f -
+expand
+#factor TODO
+fold
+head -n1
+head -c1
+nl
+numfmt
+paste
+pr
+tail -n+1
+tail -c+1
+tee
+tr 1 1
+unexpand
+uniq
+" |
+sort -k 1b,1 > all_readers || framework_failure_
+
+printf '%s\n' $built_programs |
+sort -k 1b,1 > built_programs || framework_failure_
+
+join all_readers built_programs > built_readers || framework_failure_
+
+nonempty() { sleep $1; test -s out; }
+
+cleanup_()
+{
+ kill $writer_pid 2>/dev/null && wait $writer_pid
+ kill $reader_pid 2>/dev/null && wait $reader_pid
+}
+
+while read reader; do
+ rm -f out || framework_failure_
+ stdbuf -oL $reader <in >out & reader_pid=$!
+ { printf '1\n'; read x <hold || :; } >in & writer_pid=$!
+ retry_delay_ nonempty .1 6 ||
+ { printf 'no output from: %s\n' "$reader" >&2; fail=1; }
+ printf release >hold || framework_failure_
+ wait $writer_pid || framework_failure_
+ wait $reader_pid || fail=1
+done < built_readers
+
+
+Exit $fail
diff --git a/tests/misc/selinux.sh b/tests/misc/selinux.sh
index 6a073b694..627b5dc30 100755
--- a/tests/misc/selinux.sh
+++ b/tests/misc/selinux.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test SELinux-related options.
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/sleep.sh b/tests/misc/sleep.sh
index 6806a6fe2..91db200f7 100755
--- a/tests/misc/sleep.sh
+++ b/tests/misc/sleep.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Validate sleep parameters
-# Copyright (C) 2016-2025 Free Software Foundation, Inc.
+# Copyright (C) 2016-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/stdbuf.sh b/tests/misc/stdbuf.sh
index 3e88695e9..170d25c5f 100755
--- a/tests/misc/stdbuf.sh
+++ b/tests/misc/stdbuf.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise stdbuf functionality
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/sync.sh b/tests/misc/sync.sh
index 3d9b2e342..2a005566b 100755
--- a/tests/misc/sync.sh
+++ b/tests/misc/sync.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test various sync(1) operations
-# Copyright (C) 2015-2025 Free Software Foundation, Inc.
+# Copyright (C) 2015-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ sync
+getlimits_
touch file || framework_failure_
@@ -38,21 +39,36 @@ sync file || fail=1
# Ensure multiple args are processed and diagnosed
returns_ 1 sync file nofile || fail=1
+# Ensure all arguments a processed even if one results in an error
+returns_ 1 sync nofile1 file nofile2 2>err || fail=1
+cat <<\EOF > exp || framework_failure_
+sync: error opening 'nofile1': No such file or directory
+sync: error opening 'nofile2': No such file or directory
+EOF
+compare exp err || fail=1
+
# Ensure inaccessible dirs give an appropriate error
mkdir norw || framework_failure_
chmod 0 norw || framework_failure_
if ! test -r norw; then
returns_ 1 sync norw 2>errt || fail=1
# AIX gives "Is a directory"
- sed 's/Is a directory/Permission denied/' <errt >err || framework_failure_
- printf "sync: error opening 'norw': Permission denied\n" >exp
+ sed "s/$EISDIR/$EACCES/" <errt >err || framework_failure_
+ printf "sync: error opening 'norw': $EACCES\n" >exp
compare exp err || fail=1
fi
if test "$fail" != '1'; then
# Ensure a fifo doesn't block
mkfifo_or_skip_ fifo
- returns_ 124 timeout 10 sync fifo && fail=1
+ for opt in '' '-f' '-d'; do
+ timeout=10
+ if test "$opt" = '-f'; then
+ test "$RUN_VERY_EXPENSIVE_TESTS" = yes || continue
+ timeout=60
+ fi
+ returns_ 124 timeout "$timeout" sync $opt fifo && fail=1
+ done
fi
Exit $fail
diff --git a/tests/misc/time-style.sh b/tests/misc/time-style.sh
index ebc3bb3c9..3488dcb58 100755
--- a/tests/misc/time-style.sh
+++ b/tests/misc/time-style.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test --time-style in programs like 'ls'.
-# Copyright (C) 2016-2025 Free Software Foundation, Inc.
+# Copyright (C) 2016-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/tsort.pl b/tests/misc/tsort.pl
index d2e8d2b85..1adf91ca3 100755
--- a/tests/misc/tsort.pl
+++ b/tests/misc/tsort.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test "tsort".
-# Copyright (C) 1999-2025 Free Software Foundation, Inc.
+# Copyright (C) 1999-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/tty-eof.pl b/tests/misc/tty-eof.pl
new file mode 100755
index 000000000..3f5abbe98
--- /dev/null
+++ b/tests/misc/tty-eof.pl
@@ -0,0 +1,179 @@
+#!/usr/bin/perl
+# Test whether programs exit upon a single EOF from a tty.
+# Ensure that e.g., cat exits upon a single configured EOF from a tty.
+# Do the same for all programs that can read stdin,
+# require no arguments and that write to standard output.
+
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+(my $ME = $0) =~ s|.*/||;
+
+eval { require POSIX; };
+$@
+ and CuSkip::skip "$ME: this script requires Perl's POSIX module\n";
+
+use POSIX qw(:termios_h);
+
+# Some older versions of Expect.pm (e.g. 1.07) lack the log_user method,
+# so check for that, too.
+eval { require Expect; Expect->require_version('1.11') };
+$@
+ and CuSkip::skip "$ME: this script requires Perl's Expect package >=1.11\n";
+
+# Try to explicitly set eof character in case it's not the usual Ctrl-d
+sub
+set_tty_eof_char ($$)
+{
+ my ($tty, $eof_char) = @_;
+
+ return eval
+ {
+ my $termios = POSIX::Termios->new;
+ my $fd = fileno ($tty);
+ defined $fd
+ or die "no file descriptor for tty";
+ $termios->getattr ($fd)
+ or die "tcgetattr failed: $!";
+ $termios->setcc (VEOF, ord $eof_char);
+ $termios->setattr ($fd, TCSANOW)
+ or die "tcsetattr failed: $!";
+ 1;
+ };
+}
+
+sub
+normalize_tty_output ($)
+{
+ my ($output) = @_;
+
+ $output =~ s/\r\n/\n/g;
+ $output =~ s/\r/\n/g;
+ return $output;
+}
+
+{
+ my $fail = 0;
+ my $eof_char = "\cD";
+ my $eof_name = '^D';
+ my @stdin_reading_commands = qw(
+ b2sum
+ base32
+ base64
+ cat
+ cksum
+ expand
+ fmt
+ fold
+ head
+ md5sum
+ nl
+ od
+ paste
+ pr
+ ptx
+ sha1sum
+ sha224sum
+ sha256sum
+ sha384sum
+ sha512sum
+ shuf
+ sort
+ sum
+ tac
+ tail
+ tee
+ tsort
+ unexpand
+ uniq
+ wc
+ );
+ my @commands = (@stdin_reading_commands,
+ 'basenc --z85',
+ 'cut -d " " -f2',
+ 'cut -b1-3',
+ 'dd status=none',
+ 'numfmt --invalid=ignore'
+ );
+ my $stderr = 'tty-eof.err';
+ foreach my $with_input (1, 0)
+ {
+ foreach my $cmd (@commands)
+ {
+ my $exp = new Expect;
+ $exp->log_user(0);
+ my $cmd_name = (split(' ', $cmd))[0];
+ my $mode = $with_input ? 'with input' : 'without input';
+ $ENV{built_programs} =~ /\b$cmd_name\b/ || next;
+ $exp->spawn("$cmd 2> $stderr")
+ or (warn "$ME: cannot run '$cmd' ($mode): $!\n"),
+ $fail=1, next;
+ # Fails on perl-IO-Tty >= 1.24 https://bugzilla.redhat.com/2463168
+ # set_tty_eof_char ($exp->slave, $eof_char);
+
+ my $input = "a b\n";
+ if ($with_input)
+ {
+ $exp->send($input);
+ }
+ $exp->send($eof_char);
+
+ $exp->expect(10, 'eof');
+ if (! defined $exp->exitstatus)
+ {
+ warn "$ME: $cmd didn't exit after $eof_name from standard input"
+ . " ($mode)\n";
+ $fail = 1;
+ }
+ else
+ {
+ my $output = normalize_tty_output ($exp->before ());
+
+ if ($with_input)
+ {
+ $output =~ s/\Q$input\E//
+ or (warn "$ME: $cmd ($mode) didn't echo expected input\n"),
+ $fail = 1;
+ $output =~ /\S/
+ or (warn "$ME: $cmd ($mode) didn't write expected output\n"),
+ $fail = 1;
+ }
+
+ my $s = $exp->exitstatus;
+ $s == 0
+ or (warn "$ME: $cmd exited with status $s (expected 0)"
+ . " ($mode)\n"),
+ $fail = 1;
+ }
+
+ $exp->hard_close();
+
+ if (-s $stderr)
+ {
+ warn "$ME: $cmd wrote to stderr ($mode):\n";
+ system "cat $stderr";
+ $fail = 1;
+ }
+ }
+ }
+ continue
+ {
+ unlink $stderr
+ or warn "$ME: failed to remove stderr file $stderr: $!\n";
+ }
+
+ exit $fail
+}
diff --git a/tests/misc/usage_vs_getopt.sh b/tests/misc/usage_vs_getopt.sh
index 032f7fc89..46e45fac3 100755
--- a/tests/misc/usage_vs_getopt.sh
+++ b/tests/misc/usage_vs_getopt.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify that all options mentioned in usage are recognized by getopt.
-# Copyright (C) 2017-2025 Free Software Foundation, Inc.
+# Copyright (C) 2017-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -70,7 +70,7 @@ checkprg () {
# Else $prg should have complained about a missing argument.
# Catch false positives.
case "$prg/$opt" in
- 'pr/-COLUMN') continue;;
+ 'pr/-COLS') continue;;
esac
# Replace $opt in stderr output by the neutral placeholder.
# Handle both long and short option error messages.
diff --git a/tests/misc/usage_vs_refs.sh b/tests/misc/usage_vs_refs.sh
new file mode 100755
index 000000000..e112f04b3
--- /dev/null
+++ b/tests/misc/usage_vs_refs.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+# Verify that all supported options have references in docs
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+
+
+getopts() {
+ skip='--help|--version' # These refs treated specially
+
+ env "$1" --help |
+ grep -E '^( -| --)' | # find options
+ grep -Ev -- " - |-M.*from first" | # exclude invalid matches
+ sed -e 's/^ *//' -e's/ .*//' | # strip leading space and descriptions
+ tr ' ' '\n' | # split to one per line
+ sed -n 's/^\(--\?[^,=[]*\).*/\1/p' | # Remove parameters
+ grep -Ev -- "$skip" # These refs treated specially
+}
+
+for prg in $built_programs; do
+
+ dprg=$prg
+ test $prg = ginstall && dprg=install
+ test $prg = '[' && dprg=test
+ test $prg = 'dir' && dprg=ls
+ test $prg = 'vdir' && dprg=ls
+ test $prg = 'md5sum' && dprg=cksum
+ test $prg = 'b2sum' && dprg=cksum
+ test $prg = 'sha1sum' && dprg=cksum
+ test $prg = 'sha224sum' && dprg=cksum
+ test $prg = 'sha256sum' && dprg=cksum
+ test $prg = 'sha384sum' && dprg=cksum
+ test $prg = 'sha512sum' && dprg=cksum
+
+ test "$DEBUG" && echo processing $sprg
+ got_option=false
+ for opt in $(getopts $prg); do
+ got_option=true
+ if ! grep -E "opt(Itemx?|Anchor)\\{$dprg,$opt[,}]" \
+ "$abs_top_srcdir/doc/coreutils.texi" >/dev/null; then
+ if ! grep "optItemx\\?{\\\\cmd\\\\,$opt," \
+ "$abs_top_srcdir/doc/coreutils.texi" >/dev/null; then
+ printf -- '%s %s reference missing in texi\n' $dprg $opt >&2
+ fail=1
+ elif test "$DEBUG"; then
+ echo "only matched $dprg $opt in a general macro"
+ fi
+ fi
+ done
+ test "$DEBUG" && test $got_option = false && echo No options for $prg ?
+
+done
+
+Exit $fail
diff --git a/tests/misc/user.sh b/tests/misc/user.sh
new file mode 100755
index 000000000..931287e41
--- /dev/null
+++ b/tests/misc/user.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ whoami logname
+skip_if_root_
+
+unshare -U unshare --version || skip_ 'unshare -U is unavailable'
+overflow_uid=$(cat /proc/sys/kernel/overflowuid) ||
+ skip_ 'overflow uid is unavailable'
+
+test "$(unshare -U whoami)" = "$(id -un $overflow_uid)" || fail=1
+
+# The "</dev/null" disables a fallback lookup via utmp/utmpx,
+# that existed in glibc < 2.28 and exists again in glibc >= 2.38.
+returns_ 1 unshare -U logname </dev/null 2>err || fail=1
+test "$(cat err)" = "logname: no login name" || fail=1
+
+Exit $fail
diff --git a/tests/misc/warning-errors.sh b/tests/misc/warning-errors.sh
new file mode 100755
index 000000000..fb93b1eaa
--- /dev/null
+++ b/tests/misc/warning-errors.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+# Make sure failure to write warnings is handled gracefully
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+
+if ! test -w /dev/full || ! test -c /dev/full; then
+ skip_ '/dev/full is required'
+fi
+
+touch foo || framework_failure_
+
+# Commands that write to stderr but don't exit immediately
+# Escapes must be double escaped.
+printf '%s' "\
+cksum --debug /dev/null
+date --debug -d 'now'
+du --max-depth=0 --summarize /dev/null
+env --debug true
+ginstall --strip-program=foo foo bar
+numfmt --debug 1
+od -w1 /dev/null
+printf 'foo' 'bar'
+sort --debug /dev/null
+stat --printf '\\\\' .
+tail -n0 --retry foo
+tr . '\\\\' < /dev/null
+wc -l --debug /dev/null
+" |
+sort -k 1b,1 > all_writers || framework_failure_
+
+printf '%s\n' $built_programs |
+sort -k 1b,1 > built_programs || framework_failure_
+
+join all_writers built_programs > built_writers || framework_failure_
+
+expected_failure_status_sort=2
+expected_failure_status_env=0 # env's exec resets default exit handlers
+
+while read writer; do
+ cmd=$(printf '%s\n' "$writer" | cut -d ' ' -f1) || framework_failure_
+ sanitizer_build_ $cmd && continue # standard error not closed/checked
+ eval "expected=\$expected_failure_status_$cmd"
+ test x$expected = x && expected=1
+ $SHELL -c "env $writer" 2>err || fail=1
+ test -s err || { echo "no standard error output from: $writer"; continue; }
+ returns_ $expected \
+ $SHELL -c "env $writer" 2>/dev/full || fail=1
+done < built_writers
+
+Exit $fail
diff --git a/tests/misc/write-errors.sh b/tests/misc/write-errors.sh
index 47220584a..6d13f3c0f 100755
--- a/tests/misc/write-errors.sh
+++ b/tests/misc/write-errors.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure all of these programs promptly diagnose write errors.
-# Copyright (C) 2023-2025 Free Software Foundation, Inc.
+# Copyright (C) 2023-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -24,16 +24,24 @@ if ! test -w /dev/full || ! test -c /dev/full; then
skip_ '/dev/full is required'
fi
-# Writers that may output data indefinitely
-# First word in command line is checked against built programs
+dev_null_hash=$(cksum -a sha3 -l 256 /dev/null) || framework_failure_
+
+# Writers that may output data indefinitely.
+# First word in command line is checked against built programs.
+# Escapes must be double escaped.
printf '%s' "\
cat /dev/zero
+cksum --version; yes '${dev_null_hash}' | cksum --check
comm -z /dev/zero /dev/zero
cut -z -c1- /dev/zero
cut -z -f1- /dev/zero
+cut -f1 /dev/zero
+cut -b1- /dev/zero
+cut -c1- /dev/zero
date +%${OFF64_T_MAX}c
date --version; yes 0 | date -f-
dd if=/dev/zero
+du --version; yes /dev/null | tr '\\\\n' '\\\\0' | du -l --files0-from=-
expand /dev/zero
factor --version; yes 1 | factor
fmt /dev/zero
@@ -41,7 +49,7 @@ fmt --version; yes | fmt
fold /dev/zero
fold -b /dev/zero
fold -c /dev/zero
-fold --version; yes | tr -d '\\n' | fold
+fold --version; yes | fold
head -z -n-1 /dev/zero
join -a 1 -z /dev/zero /dev/null
nl --version; yes | nl
@@ -51,11 +59,13 @@ paste /dev/zero
pr /dev/zero
pr --version; yes 1 | pr
seq inf
+shuf -i 0-1 -r
tail -n+1 -z /dev/zero
tee < /dev/zero
tr . . < /dev/zero
unexpand /dev/zero
uniq -z -D /dev/zero
+wc --version; yes /dev/null | tr '\\\\n' '\\\\0' | wc --files0-from=-
yes
" |
sort -k 1b,1 > all_writers || framework_failure_
@@ -68,7 +78,7 @@ join all_writers built_programs > built_writers || framework_failure_
while read writer; do
# Enforce mem usage limits if possible
cmd=$(printf '%s\n' "$writer" | cut -d ' ' -f1) || framework_failure_
- base_mem=$(get_min_ulimit_v_ $cmd --version) \
+ base_mem=$(get_min_ulimit_v_ $SHELL -c "$cmd --version") \
&& ulimit="ulimit -v $(($base_mem+12000))" \
|| skip_ 'unable to determine ulimit -v'
@@ -76,8 +86,18 @@ while read writer; do
rm -f full.err || framework_failure_
timeout 10 env --default-signal=PIPE $SHELL -c \
"($ulimit && $writer 2>full.err >/dev/full)"
- { test $? = 124 || ! grep 'space' full.err >/dev/null; } &&
- { fail=1; cat full.err; echo "$writer: failed to exit" >&2; }
+ { test $? = 124 || test $? = 0 || ! grep "$ENOSPC" full.err >/dev/null; } &&
+ { fail=1; cat full.err; echo "$writer: failed to diagnose ENOSPC" >&2; }
+
+ # Check closed stdout handling
+ rm -f closed.err || framework_failure_
+ timeout 10 env --default-signal=PIPE $SHELL -c \
+ "($ulimit && $writer 2>closed.err >&-)"
+ { test $? = 124 || test $? = 0 || ! grep -E "$EBADF" closed.err >/dev/null; }\
+ && { fail=1; cat closed.err; echo "$writer: failed to diagnose EBADF" >&2; }
+
+ # https://github.com/ksh93/ksh/issues/741
+ $SHELL -c 'test -n "$KSH_VERSION"' && continue
# Check closed pipe handling
rm -f pipe.err || framework_failure_
@@ -85,6 +105,7 @@ while read writer; do
"($ulimit && $writer 2>pipe.err | :)"
{ test $? = 0 && compare /dev/null pipe.err; } ||
{ fail=1; cat pipe.err; echo "$writer: failed to write to closed pipe" >&2; }
+
done < built_writers
Exit $fail
diff --git a/tests/misc/xattr.sh b/tests/misc/xattr.sh
index 46bd96c72..018b277cd 100755
--- a/tests/misc/xattr.sh
+++ b/tests/misc/xattr.sh
@@ -3,7 +3,7 @@
# attributes and install does not preserve extended attributes.
# cp -a should preserve xattr, error diagnostics should not be displayed
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/xstrtol.pl b/tests/misc/xstrtol.pl
index 36ad50a1a..46f49a903 100755
--- a/tests/misc/xstrtol.pl
+++ b/tests/misc/xstrtol.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# exercise xstrtol's diagnostics via pr
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/misc/yes.sh b/tests/misc/yes.sh
index a41cce7d9..a31001b7b 100755
--- a/tests/misc/yes.sh
+++ b/tests/misc/yes.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Validate yes buffer handling
-# Copyright (C) 2015-2025 Free Software Foundation, Inc.
+# Copyright (C) 2015-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,6 +18,8 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ yes
+getlimits_
+uses_strace_
# Check basic operation
test "$(yes | head -n1)" = 'y' || fail=1
@@ -47,13 +49,40 @@ fi
if test -w /dev/full && test -c /dev/full; then
# The single output diagnostic expected,
# (without the possibly varying :strerror(ENOSPC) suffix).
- printf '%s\n' "yes: standard output" > exp
+ printf '%s\n' "yes: standard output: $ENOSPC" > exp
for size in 1 16384; do
- returns_ 1 yes "$(printf %${size}s '')" >/dev/full 2>errt || fail=1
- sed 's/\(yes:.*\):.*/\1/' errt > err
+ returns_ 1 yes "$(printf %${size}s '')" >/dev/full 2>err || fail=1
compare exp err || fail=1
done
fi
+# Check the non pipe output case, since that is different with splice
+if timeout 10 true; then
+ timeout .1 yes >/dev/null
+ test $? = 124 || fail=1
+fi
+
+# Ensure we fallback to write() if there is an issue with (async) zero-copy
+zc_syscalls='io_uring_setup io_uring_enter io_uring_register memfd_create
+ sendfile splice tee vmsplice'
+syscalls=$(
+ for s in $zc_syscalls; do
+ strace -qe "$s" true >/dev/null 2>&1 && echo "$s"
+ done | paste -s -d,)
+
+no_zero_copy() {
+ strace -f -o /dev/null -e inject=${syscalls}:error=ENOSYS "$@"
+}
+if no_zero_copy true; then
+ test "$(no_zero_copy yes | head -n2 | paste -s -d '')" = 'yy' || fail=1
+fi
+# Ensure we fallback to write() if there is an issue with pipe2()
+# For example if we don't have enough file descriptors available.
+no_pipe() { strace -f -o /dev/null -e inject=pipe,pipe2:error=EMFILE "$@"; }
+if no_pipe true; then
+ no_pipe timeout .1 yes >/dev/null
+ test $? = 124 || fail=1
+fi
+
Exit $fail
diff --git a/tests/mkdir/p-1.sh b/tests/mkdir/p-1.sh
index 5713e2b90..c0b70908f 100755
--- a/tests/mkdir/p-1.sh
+++ b/tests/mkdir/p-1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test "mkdir -p".
-# Copyright (C) 1997-2025 Free Software Foundation, Inc.
+# Copyright (C) 1997-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mkdir/p-2.sh b/tests/mkdir/p-2.sh
index ba8299910..95770b18c 100755
--- a/tests/mkdir/p-2.sh
+++ b/tests/mkdir/p-2.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Just like p-1, but with an absolute path.
-# Copyright (C) 1997-2025 Free Software Foundation, Inc.
+# Copyright (C) 1997-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mkdir/p-3.sh b/tests/mkdir/p-3.sh
index b1debaa0f..23459b837 100755
--- a/tests/mkdir/p-3.sh
+++ b/tests/mkdir/p-3.sh
@@ -3,7 +3,7 @@
# causes immediate failure. Also, ensure that we don't create
# subsequent, relative command-line arguments in the wrong place.
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mkdir/p-acl.sh b/tests/mkdir/p-acl.sh
index 5667d90b9..f0d104280 100755
--- a/tests/mkdir/p-acl.sh
+++ b/tests/mkdir/p-acl.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test "mkdir -p" with default ACLs.
-# Copyright (C) 1997-2025 Free Software Foundation, Inc.
+# Copyright (C) 1997-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mkdir/p-slashdot.sh b/tests/mkdir/p-slashdot.sh
index 037c7083c..34f5e1034 100755
--- a/tests/mkdir/p-slashdot.sh
+++ b/tests/mkdir/p-slashdot.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that mkdir -p works with arguments specified with a trailing "/.".
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mkdir/p-thru-slink.sh b/tests/mkdir/p-thru-slink.sh
index 356285ebe..0e5e81262 100755
--- a/tests/mkdir/p-thru-slink.sh
+++ b/tests/mkdir/p-thru-slink.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that mkdir -p foo/bar works when foo is a symbolic link to a directory
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mkdir/p-v.sh b/tests/mkdir/p-v.sh
index c76829d04..50a73e393 100755
--- a/tests/mkdir/p-v.sh
+++ b/tests/mkdir/p-v.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test mkdir -pv.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mkdir/parents.sh b/tests/mkdir/parents.sh
index a9392d7b2..854ec8098 100755
--- a/tests/mkdir/parents.sh
+++ b/tests/mkdir/parents.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure mkdir's -p options works properly
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mkdir/perm.sh b/tests/mkdir/perm.sh
index cdfe29b5f..3eb3662c3 100755
--- a/tests/mkdir/perm.sh
+++ b/tests/mkdir/perm.sh
@@ -2,7 +2,7 @@
# Verify that mkdir's '-m MODE' option works properly
# with various umask settings.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mkdir/restorecon.sh b/tests/mkdir/restorecon.sh
index 05b2df8d4..724796f1d 100755
--- a/tests/mkdir/restorecon.sh
+++ b/tests/mkdir/restorecon.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test mkdir, mknod, mkfifo -Z
-# Copyright (C) 2013-2025 Free Software Foundation, Inc.
+# Copyright (C) 2013-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mkdir/selinux.sh b/tests/mkdir/selinux.sh
index d7d63c663..f9e1e918c 100755
--- a/tests/mkdir/selinux.sh
+++ b/tests/mkdir/selinux.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that an invalid context doesn't cause a segfault
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mkdir/smack-no-root.sh b/tests/mkdir/smack-no-root.sh
index f19d1a291..5a16c4e20 100755
--- a/tests/mkdir/smack-no-root.sh
+++ b/tests/mkdir/smack-no-root.sh
@@ -3,7 +3,7 @@
# Derived from tests/mkdir/selinux.sh.
# Ensure that an unsettable SMACK label doesn't cause a segfault.
-# Copyright (C) 2014-2025 Free Software Foundation, Inc.
+# Copyright (C) 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,19 +20,17 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ mkdir mkfifo mknod
-
require_smack_
+getlimits_
c=arbitrary-smack-label
-msg="failed to set default file creation context to '$c':"
+msg="failed to set default file creation context to '$c': $EPERM"
for cmd in 'mkdir dir' 'mknod b p' 'mkfifo f'; do
$cmd --context="$c" 2> out && fail=1
set -- $cmd
echo "$1: $msg" > exp || framework_failure_
- sed -e 's/ Operation not permitted$//' out > k || framework_failure_
- mv k out || fail=1
compare exp out || fail=1
done
diff --git a/tests/mkdir/smack-root.sh b/tests/mkdir/smack-root.sh
index 373d5df34..e3ede82db 100755
--- a/tests/mkdir/smack-root.sh
+++ b/tests/mkdir/smack-root.sh
@@ -3,7 +3,7 @@
# Derived from tests/mkdir/selinux.sh.
# Ensure that SMACK label gets set.
-# Copyright (C) 2014-2025 Free Software Foundation, Inc.
+# Copyright (C) 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mkdir/special-1.sh b/tests/mkdir/special-1.sh
index 94c342c06..e7aaccac5 100755
--- a/tests/mkdir/special-1.sh
+++ b/tests/mkdir/special-1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# verify that mkdir honors special bits in MODE
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mkdir/t-slash.sh b/tests/mkdir/t-slash.sh
index 2829e39e0..d0009806e 100755
--- a/tests/mkdir/t-slash.sh
+++ b/tests/mkdir/t-slash.sh
@@ -2,7 +2,7 @@
# Ensure that mkdir works with arguments specified with and without
# a trailing slash.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mkdir/writable-under-readonly.sh b/tests/mkdir/writable-under-readonly.sh
index dc07ec844..44f16b32a 100755
--- a/tests/mkdir/writable-under-readonly.sh
+++ b/tests/mkdir/writable-under-readonly.sh
@@ -1,7 +1,5 @@
#!/bin/sh
-# FIXME: convert this to a root-only test.
-
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,32 +19,23 @@
#
# "mkdir -p /a/b/c" no longer fails merely because a leading prefix
# directory (e.g., /a or /a/b) exists on a read-only file system.
-#
-# Demonstrate the problem, as root:
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ mkdir
require_root_
-# FIXME: for now, skip it unconditionally
-skip_ temporarily disabled
+cwd=$(pwd)
+cleanup_() { cd /; umount "$cwd/mnt-ro/rw"; umount "$cwd/mnt-ro"; }
-# FIXME: define cleanup_ to do the umount
-
-# FIXME: use mktemp
-cd /tmp \
- && dd if=/dev/zero of=1 bs=8192 count=50 \
- && dd if=/dev/zero of=2 bs=8192 count=50 \
- && mkdir -p mnt-ro && mkfs -t ext2 1 && mkfs -t ext2 2 \
- && mount -o loop=/dev/loop3 1 mnt-ro \
- && mkdir -p mnt-ro/rw \
- && mount -o remount,ro mnt-ro \
- && mount -o loop=/dev/loop4 2 mnt-ro/rw
+dd if=/dev/zero of=1 bs=8192 count=50 &&
+dd if=/dev/zero of=2 bs=8192 count=50 &&
+mkdir -p mnt-ro && mkfs -t ext2 1 && mkfs -t ext2 2 &&
+mount -o loop 1 mnt-ro &&
+mkdir -p mnt-ro/rw &&
+mount -o remount,ro mnt-ro &&
+mount -o loop 2 mnt-ro/rw ||
+skip_ 'Failed to setup loopback mounts'
mkdir -p mnt-ro/rw/sub || fail=1
-# To clean up
-umount /tmp/2
-umount /tmp/1
-
Exit $fail
diff --git a/tests/mktemp/bad-unicode.sh b/tests/mktemp/bad-unicode.sh
new file mode 100755
index 000000000..c010b6373
--- /dev/null
+++ b/tests/mktemp/bad-unicode.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+# Test 'mktemp' with bad Unicode characters.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ mktemp
+
+echo a > "$(bad_unicode)" \
+ || skip_ 'bad unicode not supported in shell or file system'
+
+for loc in C "$LOCALE_FR" "$LOCALE_FR_UTF8"; do
+ test -z "$loc" && continue
+ export LC_ALL="$loc"
+ # Bad Unicode as a suffix.
+ file1=$(mktemp --tmpdir='.' --suffix=$(bad_unicode)) || fail=1
+ test -n "$file1" && test -f "$file1" || fail=1
+ dir1=$(mktemp --tmpdir='.' -d --suffix=$(bad_unicode)) || fail=1
+ test -n "$dir1" &&
+ test -d "$dir1" &&
+ # Bad Unicode in the argument to --tmpdir.
+ file2=$(mktemp --tmpdir="$dir1") &&
+ test -n "$file2" && test -f "$file2" &&
+ dir2=$(mktemp -d --tmpdir="$dir1") &&
+ test -n "$dir2" && test -d "$dir2" &&
+ # Bad Unicode in $TMPDIR.
+ file3=$(TMPDIR="$dir1" mktemp) &&
+ test -n "$file3" && test -f "$file3" &&
+ dir3=$(TMPDIR="$dir1" mktemp -d) &&
+ test -n "$dir3" && test -d "$dir3" &&
+ # Bad Unicode in the argument to -t.
+ file4=$(TMPDIR='.' mktemp -t "$(bad_unicode)XXXXXX") &&
+ test -n "$file4" && test -f "$file4" &&
+ dir4=$(TMPDIR='.' mktemp -d -t "$(bad_unicode)XXXXXX") &&
+ test -n "$dir4" && test -d "$dir4" || fail=1
+done
+
+Exit $fail
diff --git a/tests/mktemp/mktemp-misc.sh b/tests/mktemp/mktemp-misc.sh
new file mode 100755
index 000000000..676fba7e2
--- /dev/null
+++ b/tests/mktemp/mktemp-misc.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+# Test 'mktemp' under specialized situations.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ mktemp
+uses_strace_
+
+# ensure randomization doesn't depend solely on ASLR
+# Since this is a probabilistic test, we use a long template.
+mktemp_rand() { setarch -R mktemp -u XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; }
+mktemp_rand >out1 && mktemp_rand >out2 && compare out1 out2 && fail=1
+
+# ensure mktemp does not strongly depend on getrandom
+if strace -o /dev/null -e inject=getrandom:error=ENOSYS true; then
+ strace -o /dev/null -e inject=getrandom:error=ENOSYS mktemp -u || fail=1
+fi
+
+Exit $fail
diff --git a/tests/misc/mktemp.pl b/tests/mktemp/mktemp.pl
index be5a6f17f..a7322880e 100755
--- a/tests/misc/mktemp.pl
+++ b/tests/mktemp/mktemp.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test "mktemp".
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -174,13 +174,41 @@ my @Tests =
check_tmp $f, 'F'; }}],
- # Test template with subdirectory
- ['tmp-w-slash', '--tmpdir=. a/bXXXX',
+ # Test creation of a file where the template has a subdirectory.
+ ['tmp-w-slash1', '--tmpdir=. a/bXXXX',
{PRE => sub {mkdir 'a',0755 or die "a: $!\n"}},
{OUT_SUBST => 's,b....$,bZZZZ,'},
{OUT => "./a/bZZZZ\n"},
{POST => sub { my ($f) = @_; defined $f or return; chomp $f;
- check_tmp $f, 'F'; unlink $f; rmdir 'a' or die "rmdir a: $!\n" }}
+ check_tmp $f, 'F'; rmdir 'a' or die "rmdir a: $!\n" }}
+ ],
+
+ # Likewise, but create a directory.
+ ['tmp-w-slash2', '--tmpdir=. -d a/bXXXX',
+ {PRE => sub {mkdir 'a',0755 or die "a: $!\n"}},
+ {OUT_SUBST => 's,b....$,bZZZZ,'},
+ {OUT => "./a/bZZZZ\n"},
+ {POST => sub { my ($f) = @_; defined $f or return; chomp $f;
+ check_tmp $f, 'D'; rmdir 'a' or die "rmdir a: $!\n" }}
+ ],
+
+ # Similar to 'tmp-w-slash1', but with a file name that starts with a
+ # period.
+ ['subdir-hidden1', '--tmpdir=. a/.XXXX',
+ {PRE => sub {mkdir 'a',0755 or die "a: $!\n"}},
+ {OUT_SUBST => 's,\.....$,.ZZZZ,'},
+ {OUT => "./a/.ZZZZ\n"},
+ {POST => sub { my ($f) = @_; defined $f or return; chomp $f;
+ check_tmp $f, 'F'; rmdir 'a' or die "rmdir a: $!\n" }}
+ ],
+
+ # Likewise, but create a directory.
+ ['subdir-hidden2', '--tmpdir=. -d a/.XXXX',
+ {PRE => sub {mkdir 'a',0755 or die "a: $!\n"}},
+ {OUT_SUBST => 's,\.....$,.ZZZZ,'},
+ {OUT => "./a/.ZZZZ\n"},
+ {POST => sub { my ($f) = @_; defined $f or return; chomp $f;
+ check_tmp $f, 'D'; rmdir 'a' or die "rmdir a: $!\n" }}
],
['priority-t-tmpdir', "-t -p $bad_dir foo.XXX",
diff --git a/tests/mktemp/write-error.sh b/tests/mktemp/write-error.sh
new file mode 100755
index 000000000..7542f8931
--- /dev/null
+++ b/tests/mktemp/write-error.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+# Test the behavior of 'mktemp' when it fails to write the file name
+# to standard output.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ mktemp
+
+if ! test -w /dev/full || ! test -c /dev/full; then
+ skip_ '/dev/full is required'
+fi
+
+mkdir -p a/b || framework_failure_
+returns_ 1 mktemp -p a/b > /dev/full || fail=1
+returns_ 1 mktemp -p a/b -d > /dev/full || fail=1
+
+# Check that the directory is empty.
+for file in a/b/*; do
+ test -e "$file" && fail=1
+done
+
+Exit $fail
diff --git a/tests/mv/acl.sh b/tests/mv/acl.sh
index 8330c1842..932a76b97 100755
--- a/tests/mv/acl.sh
+++ b/tests/mv/acl.sh
@@ -2,7 +2,7 @@
# move files/directories across file system boundaries
# and make sure acls are preserved
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/atomic.sh b/tests/mv/atomic.sh
index df1e23b35..a249853d9 100755
--- a/tests/mv/atomic.sh
+++ b/tests/mv/atomic.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that mv doesn't first unlink its destination in one particular case
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/atomic2.sh b/tests/mv/atomic2.sh
index 9048b6c09..22149e067 100755
--- a/tests/mv/atomic2.sh
+++ b/tests/mv/atomic2.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that mv doesn't first unlink a multi-hard-linked destination
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/backup-dir.sh b/tests/mv/backup-dir.sh
index 215da559d..78ce2f96b 100755
--- a/tests/mv/backup-dir.sh
+++ b/tests/mv/backup-dir.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure "mv --verbose --backup" works the same for dirs and non-dirs.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/backup-is-src.sh b/tests/mv/backup-is-src.sh
index 5bf18b97b..672dbb8dd 100755
--- a/tests/mv/backup-is-src.sh
+++ b/tests/mv/backup-is-src.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Force mv to use the copying code.
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/childproof.sh b/tests/mv/childproof.sh
index a4ffa8010..5a5178d90 100755
--- a/tests/mv/childproof.sh
+++ b/tests/mv/childproof.sh
@@ -3,7 +3,7 @@
# With fileutils-4.1 and earlier, this test would fail for cp and mv.
# With coreutils-6.9 and earlier, this test would fail for ln.
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/diag.sh b/tests/mv/diag.sh
index e7e10c6e9..f139d7821 100755
--- a/tests/mv/diag.sh
+++ b/tests/mv/diag.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure we get proper diagnostics: e.g., with --target-dir=d but no args
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/dir-file.sh b/tests/mv/dir-file.sh
index 1c6ab1edd..7209a5069 100755
--- a/tests/mv/dir-file.sh
+++ b/tests/mv/dir-file.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# mv must fail when src and dest are mismatched directory/non-directory.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/dir2dir.sh b/tests/mv/dir2dir.sh
index f8fe15e98..574e58acc 100755
--- a/tests/mv/dir2dir.sh
+++ b/tests/mv/dir2dir.sh
@@ -2,7 +2,7 @@
# Ensure that mv prints the right diagnostic for a dir->dir move
# where the destination directory is not empty.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ mv
+getlimits_
mkdir -p a/t b/t || framework_failure_
touch a/t/f || framework_failure_
@@ -29,12 +30,11 @@ touch a/t/f || framework_failure_
# diagnostic about moving one directory to a subdirectory of itself.
mv b/t a 2> out && fail=1
-# Accept any of these: EEXIST, ENOTEMPTY, EBUSY.
-sed 's/: File exists/: Directory not empty/'<out>o1;mv o1 out
-sed 's/: Device or resource busy/: Directory not empty/'<out>o1;mv o1 out
+sed "s/: $EEXIST/: $ENOTEMPTY/"<out>o1;mv o1 out
+sed "s/: $EBUSY/: $ENOTEMPTY/"<out>o1;mv o1 out
-cat <<\EOF > exp || framework_failure_
-mv: cannot overwrite 'a/t': Directory not empty
+cat <<EOF > exp || framework_failure_
+mv: cannot overwrite 'a/t': $ENOTEMPTY
EOF
compare exp out || fail=1
diff --git a/tests/mv/dup-source.sh b/tests/mv/dup-source.sh
index 8d795689f..75a4d8c23 100755
--- a/tests/mv/dup-source.sh
+++ b/tests/mv/dup-source.sh
@@ -4,7 +4,7 @@
# made this fail: cp a a d/
# Ensure that mv fails with a similar command.
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/force.sh b/tests/mv/force.sh
index e81b9de0f..59cdea0e3 100755
--- a/tests/mv/force.sh
+++ b/tests/mv/force.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# move a file onto itself
-# Copyright (C) 1999-2025 Free Software Foundation, Inc.
+# Copyright (C) 1999-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/hard-2.sh b/tests/mv/hard-2.sh
index 0f89f4107..a640daa46 100755
--- a/tests/mv/hard-2.sh
+++ b/tests/mv/hard-2.sh
@@ -2,7 +2,7 @@
# Ensure that moving hard-linked arguments onto existing destinations works.
# Likewise when using cp --preserve=link.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/hard-3.sh b/tests/mv/hard-3.sh
index f58f47744..349c59657 100755
--- a/tests/mv/hard-3.sh
+++ b/tests/mv/hard-3.sh
@@ -2,7 +2,7 @@
# Ensure that using 'cp --preserve=link' to copy hard-linked arguments
# onto existing destinations works, even when one of the link operations fails.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/hard-4.sh b/tests/mv/hard-4.sh
index 4ee8eb252..4746c445f 100755
--- a/tests/mv/hard-4.sh
+++ b/tests/mv/hard-4.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that mv maintains a in this case: touch a; ln a b; mv a b
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/hard-link-1.sh b/tests/mv/hard-link-1.sh
index 9b029d87b..7d5b71dbd 100755
--- a/tests/mv/hard-link-1.sh
+++ b/tests/mv/hard-link-1.sh
@@ -2,7 +2,7 @@
# move a directory containing hard-linked files and
# make sure the links are preserved
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/hardlink-case.sh b/tests/mv/hardlink-case.sh
index 2884af6eb..1098b9327 100755
--- a/tests/mv/hardlink-case.sh
+++ b/tests/mv/hardlink-case.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure multi-hardlinked files are not lost on case insensitive file systems
-# Copyright (C) 2014-2025 Free Software Foundation, Inc.
+# Copyright (C) 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -23,10 +23,22 @@ require_root_
cwd=$(pwd)
cleanup_() { cd /; umount "$cwd/mnt"; }
-truncate -s100M hfs.img || framework_failure_
-mkfs -t hfsplus hfs.img || skip_ 'failed to create hfs file system'
-mkdir mnt || framework_failure_
-mount hfs.img mnt || skip_ 'failed to mount hfs file system'
+for ocase in '-t ext4 -O casefold' '-t hfsplus'; do
+ rm -f case.img
+ truncate -s100M case.img || framework_failure_
+ mkfs $ocase case.img &&
+ mkdir mnt &&
+ mount case.img mnt &&
+ printf '%s\n' "$ocase" > mnt/type &&
+ break
+done
+
+test -f mnt/type || skip_ 'failed to create case insensitive file system'
+
+if grep 'ext4' mnt/type; then
+ rm -d mnt/type mnt/lost+found || framework_failure_
+ chattr +F mnt || skip_ 'failed to create case insensitive file system'
+fi
cd mnt
touch foo
diff --git a/tests/mv/i-1.pl b/tests/mv/i-1.pl
index 6073dd0d7..bc5c48d47 100755
--- a/tests/mv/i-1.pl
+++ b/tests/mv/i-1.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Make sure a 'n' reply to 'mv -i...' aborts the move operation.
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/i-2.sh b/tests/mv/i-2.sh
index 6c794044e..adfdfae29 100755
--- a/tests/mv/i-2.sh
+++ b/tests/mv/i-2.sh
@@ -2,7 +2,7 @@
# Test both cp and mv for their behavior with -if and -fi
# The standards (POSIX and SuS) dictate annoyingly inconsistent behavior.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/i-3.sh b/tests/mv/i-3.sh
index 2f70e9ed8..f26476e9a 100755
--- a/tests/mv/i-3.sh
+++ b/tests/mv/i-3.sh
@@ -2,7 +2,7 @@
# Make sure that 'mv file unwritable-file' prompts the user
# and that 'mv -f file unwritable-file' doesn't.
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/i-4.sh b/tests/mv/i-4.sh
index 4d7169015..2a81ecdfc 100755
--- a/tests/mv/i-4.sh
+++ b/tests/mv/i-4.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure 'mv -i a b' does its job with a positive response
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/i-5.sh b/tests/mv/i-5.sh
index 576e216d5..306c670eb 100755
--- a/tests/mv/i-5.sh
+++ b/tests/mv/i-5.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure 'mv -i dir file' prompts before failing.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/i-link-no.sh b/tests/mv/i-link-no.sh
index 314748681..087a272af 100755
--- a/tests/mv/i-link-no.sh
+++ b/tests/mv/i-link-no.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Show that mv doesn't preserve links to files the user has declined to move.
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/into-self-2.sh b/tests/mv/into-self-2.sh
index 7d5de519f..f3ca89918 100755
--- a/tests/mv/into-self-2.sh
+++ b/tests/mv/into-self-2.sh
@@ -3,7 +3,7 @@
# Consider the case where SRC and DEST are on different
# partitions and DEST is a symlink to SRC.
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/into-self-3.sh b/tests/mv/into-self-3.sh
index dfeda15c4..1f034dded 100755
--- a/tests/mv/into-self-3.sh
+++ b/tests/mv/into-self-3.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# move a directory into itself, with a twist
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/into-self-4.sh b/tests/mv/into-self-4.sh
index 959f6d307..e2cc830cd 100755
--- a/tests/mv/into-self-4.sh
+++ b/tests/mv/into-self-4.sh
@@ -2,7 +2,7 @@
# confirm that 'mv symlink symlink' doesn't remove symlink
# Based on an example from David Luyer.
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/into-self.sh b/tests/mv/into-self.sh
index 98e8ee595..65e6d2827 100755
--- a/tests/mv/into-self.sh
+++ b/tests/mv/into-self.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Demonstrate how mv fails when it tries to move a directory into itself.
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/leak-fd.sh b/tests/mv/leak-fd.sh
index 27b974025..ed89c15af 100755
--- a/tests/mv/leak-fd.sh
+++ b/tests/mv/leak-fd.sh
@@ -2,7 +2,7 @@
# Exercise mv's file-descriptor-leak bug, reported against coreutils-5.2.1
# and fixed (properly) on 2004-10-21.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/meta-to-xpart.sh b/tests/mv/meta-to-xpart.sh
new file mode 100755
index 000000000..657c715dc
--- /dev/null
+++ b/tests/mv/meta-to-xpart.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+# A cross-partition move of a file should maintain user and permissions
+
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ mv
+require_root_
+
+cleanup_() { rm -rf "$other_partition_tmpdir"; }
+. "$abs_srcdir/tests/other-fs-tmpdir"
+
+touch non-root-owned || framework_failure_
+chown "$NON_ROOT_USERNAME" non-root-owned ||
+ skip_ "can't chown to $NON_ROOT_USERNAME"
+# Also check setuid bit, since it's security sensitive
+# and also dependent on correct order of meta data update.
+chmod u+s non-root-owned ||
+ skip_ "can't set setuid bit"
+
+mv non-root-owned "$other_partition_tmpdir" || fail=1
+
+test -u "$other_partition_tmpdir"/non-root-owned || fail=1
+test $(stat -c %U "$other_partition_tmpdir"/non-root-owned) = \
+ "$NON_ROOT_USERNAME" || fail=1
+
+Exit $fail
diff --git a/tests/mv/mv-exchange.sh b/tests/mv/mv-exchange.sh
index c295dbc66..63348a1ec 100755
--- a/tests/mv/mv-exchange.sh
+++ b/tests/mv/mv-exchange.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test mv --exchange.
-# Copyright 2024-2025 Free Software Foundation, Inc.
+# Copyright 2024-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,16 +18,14 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ mv
+getlimits_
# Test exchanging files.
touch a || framework_failure_
mkdir b || framework_failure_
-if ! mv -T --exchange a b 2>errt; then
- # AIX gives "Unsupported attribute value" (errno 124)
- # NetBSD and OpenBSD give "Not supported"
- sed 's/Not /not /; s/[Uu]nsupported/not supported/' < errt > exchange_err
- grep 'not supported' exchange_err || { cat exchange_err; fail=1; }
+if ! mv -T --exchange a b 2>exchange_err; then
+ grep "$ENOTSUP" exchange_err || { cat exchange_err; fail=1; }
else
test -d a || fail=1
test -f b || fail=1
diff --git a/tests/mv/mv-n.sh b/tests/mv/mv-n.sh
index e50ffea83..58e92cedb 100755
--- a/tests/mv/mv-n.sh
+++ b/tests/mv/mv-n.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test whether mv -n works as documented (not overwrite target).
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/mv-special-1.sh b/tests/mv/mv-special-1.sh
index d07a1ceac..fccdf6eee 100755
--- a/tests/mv/mv-special-1.sh
+++ b/tests/mv/mv-special-1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test "mv" with special files.
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -23,12 +23,16 @@ cleanup_() { rm -rf "$other_partition_tmpdir"; }
null=mv-null
dir=mv-dir
+dir2=mv-dir2
rm -f $null || framework_failure_
mknod $null p || framework_failure_
test -p $null || framework_failure_
mkdir -p $dir/a/b/c $dir/d/e/f || framework_failure_
touch $dir/a/b/c/file1 $dir/d/e/f/file2 || framework_failure_
+mkdir $dir2/ || framework_failure_
+mknod $dir2/$null p || framework_failure_
+test -p $dir2/$null || framework_failure_
# We used to...
# exit 77 here to indicate that we couldn't run the test.
@@ -36,13 +40,15 @@ touch $dir/a/b/c/file1 $dir/d/e/f/file2 || framework_failure_
# from an OpenBSD system, the above mknod fails.
# It's not worth making an exception any more.
-mv --verbose $null $dir "$other_partition_tmpdir" > out || fail=1
+timeout 60 mv -v $null $dir $dir2 "$other_partition_tmpdir" > out || fail=1
# Make sure the files are gone.
test -p $null && fail=1
test -d $dir && fail=1
+test -p $dir2/$null && fail=1
# Make sure they were moved.
test -p "$other_partition_tmpdir/$null" || fail=1
test -d "$other_partition_tmpdir/$dir/a/b/c" || fail=1
+test -p "$other_partition_tmpdir/$dir2/$null" || fail=1
# POSIX says rename (A, B) can succeed if A and B are on different file systems,
# so ignore chatter about when files are removed and copied rather than renamed.
@@ -65,6 +71,8 @@ cat <<EOF | sort > exp
'$dir/d/e' -> 'XXX/$dir/d/e'
'$dir/d/e/f' -> 'XXX/$dir/d/e/f'
'$dir/d/e/f/file2' -> 'XXX/$dir/d/e/f/file2'
+'$dir2' -> 'XXX/$dir2'
+'$dir2/$null' -> 'XXX/$dir2/$null'
EOF
compare exp out2 || fail=1
diff --git a/tests/mv/mv-special-2.sh b/tests/mv/mv-special-2.sh
new file mode 100755
index 000000000..9ecea4e97
--- /dev/null
+++ b/tests/mv/mv-special-2.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+# Ensure that mv works with non standard copies across file systems
+
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ mv
+
+require_root_
+
+cwd=$(pwd)
+cleanup_() { cd /; umount "$cwd/xdev"; }
+
+skip=0
+
+# Mount an ext2 loopback file system at $WHERE
+make_fs() {
+ where="$1"
+
+ mkdir "$where" || framework_failure_
+
+ fs="$where.bin"
+ dd if=/dev/zero of="$fs" bs=8192 count=200 > /dev/null 2>&1 || skip=1
+ mkfs -t ext2 -F "$fs" || skip_ "failed to create ext2 file system"
+ mount -oloop "$fs" "$where" || skip=1
+
+ echo test > "$where"/f && test -s "$where"/f || skip=1
+
+ test $skip = 1 && skip_ 'insufficient mount/ext2 support'
+}
+
+make_fs xdev
+
+truncate -s 8G huge || framework_failure_
+mv --verbose huge xdev &&
+returns_ 1 test -e huge &&
+test -s xdev/huge || fail=1
+
+mknod devzero c 1 5 || framework_failure_
+mv --verbose devzero xdev &&
+returns_ 1 test -c devzero &&
+test -c xdev/devzero || fail=1
+
+ln -nsf blah blah || framework_failure_
+mv --verbose blah xdev &&
+returns_ 1 test -L blah &&
+test -L xdev/blah || fail=1
+
+# Test moving a broken symlink to another file system
+ln -nsf nonexistent broken_symlink || framework_failure_
+mv --verbose broken_symlink xdev &&
+returns_ 1 test -L broken_symlink &&
+test -L xdev/broken_symlink || fail=1
+
+if python -c "import socket as s; s.socket(s.AF_UNIX).bind('test.sock')" &&
+ test -S 'test.sock'; then
+ mv --verbose test.sock xdev &&
+ returns_ 1 test -S test.sock &&
+ test -S xdev/test.sock || fail=1
+fi
+
+Exit $fail
diff --git a/tests/mv/no-copy.sh b/tests/mv/no-copy.sh
index 9b582aecc..7a2b97508 100755
--- a/tests/mv/no-copy.sh
+++ b/tests/mv/no-copy.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test mv --no-copy.
-# Copyright (C) 2023-2025 Free Software Foundation, Inc.
+# Copyright (C) 2023-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/no-target-dir.sh b/tests/mv/no-target-dir.sh
index 61c91f238..96769b774 100755
--- a/tests/mv/no-target-dir.sh
+++ b/tests/mv/no-target-dir.sh
@@ -2,7 +2,7 @@
# ensure that --no-target-directory (-T) works when the destination is
# an empty directory.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/part-fail.sh b/tests/mv/part-fail.sh
index b0af78c09..d3145bcb6 100755
--- a/tests/mv/part-fail.sh
+++ b/tests/mv/part-fail.sh
@@ -4,7 +4,7 @@
# This is a bit fragile since it relies on the string used
# for EPERM: 'permission denied'.
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,6 +22,8 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ mv
skip_if_root_
+getlimits_
+
cleanup_() { t=$other_partition_tmpdir; chmod -R 700 "$t"; rm -rf "$t"; }
. "$abs_srcdir/tests/other-fs-tmpdir"
@@ -32,13 +34,13 @@ chmod u-w "$other_partition_tmpdir" || framework_failure_
mv -f k "$other_partition_tmpdir" 2> out && fail=1
printf \
"mv: inter-device move failed: '%s' to '%s';"\
-' unable to remove target: Permission denied\n' \
- k "$other_partition_tmpdir/k" >exp
+' unable to remove target: %s\n' \
+ k "$other_partition_tmpdir/k" "$EACCES" >exp
# On some (less-compliant) systems, we get EPERM in this case.
# Accept either diagnostic.
cat <<EOF > exp2
-mv: cannot move 'k' to '$other_partition_tmpdir/k': Permission denied
+mv: cannot move 'k' to '$other_partition_tmpdir/k': $EACCES
EOF
if cmp out exp >/dev/null 2>&1; then
diff --git a/tests/mv/part-hardlink.sh b/tests/mv/part-hardlink.sh
index 738426604..823969f79 100755
--- a/tests/mv/part-hardlink.sh
+++ b/tests/mv/part-hardlink.sh
@@ -4,7 +4,7 @@
# For additional constraints, see the comment in copy.c.
# Before coreutils-5.2.1, this test would fail.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/part-rename.sh b/tests/mv/part-rename.sh
index d07a49460..0d4a8f876 100755
--- a/tests/mv/part-rename.sh
+++ b/tests/mv/part-rename.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test various cases for moving directories across file systems
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/part-symlink.sh b/tests/mv/part-symlink.sh
index 532c8340d..b7711df72 100755
--- a/tests/mv/part-symlink.sh
+++ b/tests/mv/part-symlink.sh
@@ -2,7 +2,7 @@
# make sure cp and mv can handle many combinations of local and
# other-partition regular/symlink'd files.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/partition-perm.sh b/tests/mv/partition-perm.sh
index d3789fb6b..82dd36388 100755
--- a/tests/mv/partition-perm.sh
+++ b/tests/mv/partition-perm.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure permissions are preserved when moving from one partition to another.
-# Copyright (C) 1999-2025 Free Software Foundation, Inc.
+# Copyright (C) 1999-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/perm-1.sh b/tests/mv/perm-1.sh
index 26f2aa3ee..6d99f8557 100755
--- a/tests/mv/perm-1.sh
+++ b/tests/mv/perm-1.sh
@@ -2,7 +2,7 @@
# ensure that mv gives one diagnostic, not two, when failing
# due to lack of permissions
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,14 +20,15 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ mv
skip_if_root_
+getlimits_
mkdir -p no-write/dir || framework_failure_
chmod ug-w no-write || framework_failure_
mv no-write/dir . > out 2>&1 && fail=1
-cat <<\EOF > exp
-mv: cannot move 'no-write/dir' to './dir': Permission denied
+cat <<EOF > exp
+mv: cannot move 'no-write/dir' to './dir': $EACCES
EOF
compare exp out || fail=1
diff --git a/tests/mv/sticky-to-xpart.sh b/tests/mv/sticky-to-xpart.sh
index 4c9a67f02..8b3f7029d 100755
--- a/tests/mv/sticky-to-xpart.sh
+++ b/tests/mv/sticky-to-xpart.sh
@@ -4,7 +4,7 @@
# mv: cannot remove 'x': Operation not permitted
# Affects coreutils-6.0-6.9.
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,6 +22,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ mv
require_root_
+getlimits_
cleanup_() { rm -rf "$other_partition_tmpdir"; }
. "$abs_srcdir/tests/other-fs-tmpdir"
@@ -54,21 +55,19 @@ esac
chroot --skip-chdir --user=$NON_ROOT_USERNAME / env PATH="$PATH" \
mv t/root-owned "$other_partition_tmpdir" 2> out-t && fail=1
-# On some systems, we get 'Not owner'. Convert it.
-# On other systems (HPUX), we get 'Permission denied'. Convert it, too.
-onp='Operation not permitted'
-sed "s/Not owner/$onp/;s/Permission denied/$onp/" out-t > out
+# On some systems (HPUX), we get 'Permission denied'. Convert it.
+sed "s/$EACCES/$EPERM/;" out-t > out
# On some systems (OpenBSD 7.5), the initial rename fails with EPERM,
# which is arguably better than the Linux kernel's EXDEV.
cat <<EOF >exp1 || framework_failure_
-mv: cannot move 't/root-owned' to '$other_partition_tmpdir/root-owned': $onp
+mv: cannot move 't/root-owned' to '$other_partition_tmpdir/root-owned': $EPERM
EOF
compare exp1 out >/dev/null || {
cat <<EOF >exp || framework_failure_
-mv: cannot remove 't/root-owned': $onp
+mv: cannot remove 't/root-owned': $EPERM
EOF
compare exp out || fail=1
diff --git a/tests/mv/symlink-onto-hardlink-to-self.sh b/tests/mv/symlink-onto-hardlink-to-self.sh
index 610faeaf4..110c8a893 100755
--- a/tests/mv/symlink-onto-hardlink-to-self.sh
+++ b/tests/mv/symlink-onto-hardlink-to-self.sh
@@ -4,7 +4,7 @@
# but not NetBSD), prior to coreutils-8.16, the mv would successfully perform
# a no-op. I.e., surprisingly, mv s1 s2 would succeed, yet fail to remove s1.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/symlink-onto-hardlink.sh b/tests/mv/symlink-onto-hardlink.sh
index 0fe5d7f98..53b6506f4 100755
--- a/tests/mv/symlink-onto-hardlink.sh
+++ b/tests/mv/symlink-onto-hardlink.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that mv works with a few symlink-onto-hard-link cases.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/to-symlink.sh b/tests/mv/to-symlink.sh
index 94e0e9a7d..7f407c77e 100755
--- a/tests/mv/to-symlink.sh
+++ b/tests/mv/to-symlink.sh
@@ -2,7 +2,7 @@
# Make sure that the copying code used in an inter-partition
# move unlinks a destination symlink before opening it.
-# Copyright (C) 1999-2025 Free Software Foundation, Inc.
+# Copyright (C) 1999-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/trailing-slash.sh b/tests/mv/trailing-slash.sh
index bb5583b99..ab9dd1e61 100755
--- a/tests/mv/trailing-slash.sh
+++ b/tests/mv/trailing-slash.sh
@@ -4,7 +4,7 @@
# Also, ensure that "mv dir non-exist-dir/" works.
# Also, ensure that "cp dir non-exist-dir/" works.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/mv/update.sh b/tests/mv/update.sh
index cc4214724..d0f8eea33 100755
--- a/tests/mv/update.sh
+++ b/tests/mv/update.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure --update works as advertised
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/nice/nice-fail.sh b/tests/nice/nice-fail.sh
index cf22f6b05..99f982431 100755
--- a/tests/nice/nice-fail.sh
+++ b/tests/nice/nice-fail.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify that internal failure in nice gives exact status.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -25,6 +25,7 @@ print_ver_ nice env
returns_ 125 nice -n 1 || fail=1 # missing command
returns_ 125 nice --- || fail=1 # unknown option
returns_ 125 nice -n 1a || fail=1 # invalid adjustment
+returns_ 125 nice -n 1+2-3 nice || fail=1 # invalid adjustment
returns_ 2 nice sh -c 'exit 2' || fail=1 # exit status propagation
returns_ 126 env . && { returns_ 126 nice . || fail=1; } # invalid command
returns_ 127 nice no_such || fail=1 # no such command
diff --git a/tests/nice/nice.sh b/tests/nice/nice.sh
index 7e274ca0d..3badb0186 100755
--- a/tests/nice/nice.sh
+++ b/tests/nice/nice.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test "nice".
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ nice
+getlimits_
tests='
0 empty 10
@@ -71,16 +72,45 @@ done
# Test negative niceness - command must be run whether or not change happens.
if test x$(nice -n -1 nice 2> /dev/null) = x0 ; then
- # unprivileged user - warn about failure to change
- nice -n -1 true 2> err || fail=1
- compare /dev/null err && fail=1
- mv err exp || framework_failure_
- nice --1 true 2> err || fail=1
- compare exp err || fail=1
- # Failure to write advisory message is fatal. Buggy through coreutils 8.0.
- if test -w /dev/full && test -c /dev/full; then
- returns_ 125 nice -n -1 nice > out 2> /dev/full || fail=1
- compare /dev/null out || fail=1
+ # GNU/Hurd does not allow negative niceness even if we are a privileged user.
+ if test "$(uname)" = GNU; then
+ max_nice=$(nice -n "$INT_MAX" nice) || fail=1
+ # Check that the lowest niceness is 0.
+ nice -n -1 nice > out || fail=1
+ echo '0' > exp || framework_failure_
+ compare exp out || fail=1
+ # Exceeding the max niceness would lead to the program not being executed on
+ # GNU/Hurd with coreutils 9.8 and earlier.
+ nice -n $(("$max_nice" + 1)) nice > out || fail=1
+ echo "$max_nice" > exp || framework_failure_
+ compare exp out || fail=1
+ # GNU/Hurd's nice(2) with glibc 2.42 and earlier does not clamp the
+ # niceness to the supported range. Check that we workaround the bug.
+ # See <https://sourceware.org/PR33614>.
+ nice -n 1 nice -n $(("$max_nice" - 2)) nice > out|| fail=1
+ echo $(("$max_nice" - 1)) > exp || framework_failure_
+ compare exp out || fail=1
+ nice -n 1 nice -n $(("$max_nice" + 1)) nice > out || fail=1
+ echo "$max_nice" > exp || framework_failure_
+ compare exp out || fail=1
+ nice -n 2 nice -n -1 nice > out || fail=1
+ echo '1' > exp || framework_failure_
+ compare exp out || fail=1
+ nice -n 2 nice -n -3 nice > out || fail=1
+ echo '0' > exp || framework_failure_
+ compare exp out || fail=1
+ else
+ # unprivileged user - warn about failure to change
+ nice -n -1 true 2> err || fail=1
+ compare /dev/null err && fail=1
+ mv err exp || framework_failure_
+ nice --1 true 2> err || fail=1
+ compare exp err || fail=1
+ # Failure to write advisory message is fatal. Buggy through coreutils 8.0.
+ if test "$(uname)" != GNU && test -w /dev/full && test -c /dev/full; then
+ returns_ 125 nice -n -1 nice > out 2> /dev/full || fail=1
+ compare /dev/null out || fail=1
+ fi
fi
else
# superuser - change succeeds
@@ -90,4 +120,7 @@ else
test x$(nice --1 nice) = x-1 || fail=1
fi
+# Ensure large values are clamped
+nice -n $UINTMAX_OFLOW nice || fail=1
+
Exit $fail
diff --git a/tests/nl/multibyte.sh b/tests/nl/multibyte.sh
new file mode 100755
index 000000000..e1eb40467
--- /dev/null
+++ b/tests/nl/multibyte.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+# Test nl with multibyte section delimiters.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ nl printf
+
+test "$LOCALE_FR_UTF8" != none || skip_ "French UTF-8 locale not available"
+
+cat <<\EOF > exp || framework_failure_
+
+ 1 a
+
+ 2 b
+
+ 3 c
+EOF
+
+test_nl_multibyte ()
+{
+ {
+ export LC_ALL="$LOCALE_FR_UTF8"
+ # A missing second character implies ':'.
+ env printf "$2$2$2\na\n$2$2\nb\n$2\nc\n" > inp || framework_failure_
+ nl -p -ha -fa -d $(env printf "$1") < inp > out || fail=1
+ }
+ compare exp out
+}
+
+# Implied ':' character.
+test_nl_multibyte '\xc3' '\xc3:' || fail=1
+test_nl_multibyte '\uB250' '\uB250:' || fail=1
+
+# Two characters.
+test_nl_multibyte '\xc3\xc3' '\xc3\xc3' || fail=1
+test_nl_multibyte '\uB250\uB250' '\uB250\uB250' || fail=1
+
+# More than 2 characters is a GNU extension.
+test_nl_multibyte '\uB250\uB250\uB250' '\uB250\uB250\uB250' || fail=1
+test_nl_multibyte "$(bad_unicode)" "$(bad_unicode)" || fail=1
+
+Exit $fail
diff --git a/tests/nl/multiple-files.sh b/tests/nl/multiple-files.sh
new file mode 100755
index 000000000..304102cab
--- /dev/null
+++ b/tests/nl/multiple-files.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+# Test that 'nl' processes all arguments.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ nl
+getlimits_
+
+echo a > file1 || framework_failure_
+echo b > file2 || framework_failure_
+returns_ 1 nl file1 missing file2 > out 2> err || fail=1
+printf ' 1\ta\n 2\tb\n' > exp-out || framework_failure_
+cat <<EOF > exp-err || framework_failure_
+nl: missing: $ENOENT
+EOF
+compare exp-out out || fail=1
+compare exp-err err || fail=1
+
+Exit $fail
diff --git a/tests/misc/nl.sh b/tests/nl/nl.sh
index a0b1dcdba..6c0a35457 100755
--- a/tests/misc/nl.sh
+++ b/tests/nl/nl.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# exercise nl functionality
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -110,4 +110,18 @@ cat <<EOF > exp
EOF
compare exp out || fail=1
+# Test that all files are processed.
+echo a > file1
+echo b > file2
+returns_ 1 nl file1 missing file2 > out 2> err || fail=1
+cat <<EOF > exp-out || framework_failure_
+ 1 a
+ 2 b
+EOF
+cat <<EOF > exp-err || framework_failure_
+nl: missing: No such file or directory
+EOF
+compare exp-out out || fail=1
+compare exp-err err || fail=1
+
Exit $fail
diff --git a/tests/nproc/nproc-avail.sh b/tests/nproc/nproc-avail.sh
index 4550417d1..30e3db06b 100755
--- a/tests/nproc/nproc-avail.sh
+++ b/tests/nproc/nproc-avail.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that "nproc" is less than or equal to "nproc --all".
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/nproc/nproc-override.sh b/tests/nproc/nproc-override.sh
index 79e5f6391..c740bfee8 100755
--- a/tests/nproc/nproc-override.sh
+++ b/tests/nproc/nproc-override.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test the various OpenMP override options
-# Copyright (C) 2017-2025 Free Software Foundation, Inc.
+# Copyright (C) 2017-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/nproc/nproc-positive.sh b/tests/nproc/nproc-positive.sh
index 1b99e0a22..923db10d9 100755
--- a/tests/nproc/nproc-positive.sh
+++ b/tests/nproc/nproc-positive.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that nproc prints a number > 0
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/nproc/nproc-quota-systemd.sh b/tests/nproc/nproc-quota-systemd.sh
new file mode 100755
index 000000000..4d8add993
--- /dev/null
+++ b/tests/nproc/nproc-quota-systemd.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+# Ensure that "nproc" honors cgroup quotas and kernel scheduler from systemd
+
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ nproc
+require_root_
+
+systemd-run --version || skip_ 'systemd-run not available'
+
+all=$(nproc --all)
+test ${all} != 1 || skip_ 'Single CPU detected'
+
+check_sched_quota()
+{
+ cpu=$1; shift
+ pct=$1; shift
+
+ if systemd-run --scope -q -p CPUQuota=$pct chrt $* true; then
+ test $(systemd-run --scope -q -p CPUQuota=$pct chrt $* nproc) = $cpu ||
+ return 1
+ fi
+ return 0
+}
+
+check_sched_quota 1 100% --other 0 || fail=1
+check_sched_quota 1 149% --other 0 || fail=1
+check_sched_quota 2 150% --other 0 || fail=1
+check_sched_quota 2 249% --other 0 || fail=1
+
+check_sched_quota 1 100% --idle 0 || fail=1
+check_sched_quota 1 100% --batch 0 || fail=1
+
+# some schedulers should use all threads
+check_sched_quota $all 100% --fifo 1 || fail=1
+check_sched_quota $all 100% --rr 1 || fail=1
+check_sched_quota $all 100% --deadline \
+ --sched-runtime 100000000 --sched-deadline 1000000000 \
+ --sched-period 1000000000 0 || fail=1
+
+Exit $fail
diff --git a/tests/nproc/nproc-quota.sh b/tests/nproc/nproc-quota.sh
index 7d8e3c60a..0d4923133 100755
--- a/tests/nproc/nproc-quota.sh
+++ b/tests/nproc/nproc-quota.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that "nproc" honors cgroup quotas
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -32,7 +32,7 @@ int
sched_getscheduler (pid_t pid)
{
fclose (fopen ("preloaded","w")); /* marker for preloaded interception */
- FILE* policyf = fopen ("/proc/self/sched", "r");
+ FILE *policyf = fopen ("/proc/self/sched", "r");
int policy;
#define fscanfmt fscanf /* Avoid syntax check. */
if (pid == 0 && fscanfmt (policyf, "policy : %d", &policy) == 1)
diff --git a/tests/numfmt/mb-non-utf8.sh b/tests/numfmt/mb-non-utf8.sh
index 934d1766a..cbaa1ca53 100755
--- a/tests/numfmt/mb-non-utf8.sh
+++ b/tests/numfmt/mb-non-utf8.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test handling of non-utf8 locales
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/numfmt/numfmt.pl b/tests/numfmt/numfmt.pl
index 75de1a9f9..1c64319e4 100755
--- a/tests/numfmt/numfmt.pl
+++ b/tests/numfmt/numfmt.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Basic tests for "numfmt".
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -43,7 +43,8 @@ my @Tests =
['5.1', '--from=iec-i 1Ki', {OUT => "1024"}],
['5.2', '--from=iec-i 1', {OUT => "1"}],
- ['6', {IN_PIPE => "1234\n"}, {OUT => "1234"}],
+ ['6', {IN_PIPE => "1234\n"}, {OUT => "1234"}],
+ ['6nul', {IN_PIPE => "1234\000foo bar\n"},{OUT => "1234"}],
['7', '--from=si', {IN_PIPE => "2K\n"}, {OUT => "2000"}],
['7a', '--invalid=fail', {IN_PIPE => "no_NL"}, {OUT => "no_NL"},
{ERR => "$prog: invalid number: 'no_NL'\n"},
@@ -157,6 +158,10 @@ my @Tests =
['suf-18', '--suffix=Foo --to=si 7000FooF',
{ERR => "$prog: invalid suffix in input: '7000FooF'\n"},
{EXIT => '2'}],
+ ['suf-18.1', '--invalid=ignore --suffix=QWE 12q3QWE', {OUT=>"12q3QWE"}],
+ ['suf-18.2', '--invalid=abort --suffix=QWE 12q3QWE',
+ {ERR => "$prog: invalid suffix in input: '12q3'\n"},
+ {EXIT => '2'}],
# space(s) between number and suffix. Note only field 1 is used
# by default so specify the NUL delimiter to consider the whole "line".
['suf-19', "-d '' --from=si '4.0 K'", {OUT => "4000"}],
@@ -885,6 +890,40 @@ my @Tests =
{OUT => "A 1000 x\nB Foo y\nC 2.8G z\n"},
{ERR => "$prog: invalid number: 'Foo'\n"},
{EXIT => 2}],
+
+ # A leading '-9...' must be treated as an invalid short option, not as
+ # a negative positional argument.
+ ['neg-arg-not-option', '--to=iec -9923868',
+ {ERR_SUBST => q!s/'9'/9/;s/unknown/invalid/!},
+ {ERR => "$prog: invalid option -- 9\n" .
+ "Try '$prog --help' for more information.\n"},
+ {EXIT => 1}],
+
+ # Scientific notation ('1e9') is not accepted as numeric input;
+ # it is reported as an invalid suffix.
+ ['reject-sci-notation', '1e9',
+ {ERR => "$prog: invalid suffix in input: '1e9'\n"},
+ {EXIT => 2}],
+
+ # '--from-unit' scaling must preserve fractional precision of the input
+ # rather than rounding to an integer.
+ ['from-unit-fraction', '--from=iec --from-unit=959 -- -615484.454',
+ {OUT => "-590249591.386"}],
+
+ # Zero-padded '--format' must place the sign before the padding zeros
+ # for negative numbers (matches C printf).
+ ['zero-pad-neg-sign', '--from=none --format=%018.2f -- -9869647',
+ {OUT => "-00000009869647.00"}],
+
+ # '--to-unit=N' must select the output prefix based on the scaled value
+ # (value / N), not the unscaled input.
+ ['to-unit-prefix', '--to=iec-i --to-unit=885 100000',
+ {OUT => "113"}],
+
+ # '--format=%.0f' combined with '--to=<scale>' must honor the '.0'
+ # precision specifier (no fractional digit).
+ ['fmt-zero-prec-scale', '--to=iec --format=%.0f 5183776',
+ {OUT => "5M"}],
);
# test null-terminated lines
@@ -919,6 +958,11 @@ my @Limit_Tests =
['large-1','1000000000000000', {OUT=>"1000000000000000"}],
# 18 digits is OK
['large-2','1000000000000000000', {OUT=>"1000000000000000000"}],
+
+ # Large integers beyond 2^53 must be preserved exactly
+ # (no double round-trip).
+ ['large-2a', '915339622755539213', {OUT => "915339622755539213"}],
+
# 19 digits is too much (without output scaling)
['large-3','10000000000000000000',
{ERR => "$prog: value too large to be printed: '1e+19' " .
diff --git a/tests/od/big-w.sh b/tests/od/big-w.sh
index 27c125c69..7d5c53fcb 100755
--- a/tests/od/big-w.sh
+++ b/tests/od/big-w.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Check whether od -wN works with big N
-# Copyright 2025 Free Software Foundation, Inc.
+# Copyright 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/od/od-N.sh b/tests/od/od-N.sh
index 7c97eeee2..97c1a8f46 100755
--- a/tests/od/od-N.sh
+++ b/tests/od/od-N.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify that 'od -N N' reads no more than N bytes of input.
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -54,4 +54,11 @@ printf '\001%10s\000%10s\000' | od -S10 > out || fail=1
printf '%07o %10s\n' 1 '' 12 '' > exp || framework_failure_
compare exp out || fail=1
+# Ensure no address output for read error
+# (Users may use -N0 -j... to do base conversion)
+if ! cat . >/dev/null 2>&1; then
+ returns_ 1 od -N1 . > out || fail=1
+ compare /dev/null out || fail=1
+fi
+
Exit $fail
diff --git a/tests/od/od-endian.sh b/tests/od/od-endian.sh
index 0668ceb47..b18f884d2 100755
--- a/tests/od/od-endian.sh
+++ b/tests/od/od-endian.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# verify that od --endian works properly
-# Copyright (C) 2014-2025 Free Software Foundation, Inc.
+# Copyright (C) 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/od/od-float.sh b/tests/od/od-float.sh
index 548a7944f..6b547945c 100755
--- a/tests/od/od-float.sh
+++ b/tests/od/od-float.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test od on floating-point values.
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -90,4 +90,19 @@ else
compare exp_err err || fail=1
fi
+# Ensure -t f defaults to double precision (8 bytes), not float (4 bytes).
+env printf '\x00\x00\x80\x3f\x00\x00\x00\x40' |
+od -An -t f --endian=little > out || fail=1
+printf ' 2.000000473111868\n' > exp || framework_failure_
+compare exp out || fail=1
+
+env printf '\x00\x00\x80\x3f\x00\x00\x00\x40' |
+od -An -t fD --endian=little > out || fail=1
+compare exp out || fail=1
+
+env printf '\x00\x00\x80\x3f\x00\x00\x00\x40' |
+od -An -t fF --endian=little > out || fail=1
+printf ' 1 2\n' > exp || framework_failure_
+compare exp out || fail=1
+
Exit $fail
diff --git a/tests/od/od-j.sh b/tests/od/od-j.sh
index 79ec7bf44..98e5f2c09 100755
--- a/tests/od/od-j.sh
+++ b/tests/od/od-j.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify that 'od -j N' skips N bytes of input.
-# Copyright 2014-2025 Free Software Foundation, Inc.
+# Copyright 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,6 +19,8 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ od
+od -j1 /dev/null || fail=1
+
for file in ${srcdir=.}/tests/init.sh /proc/version /sys/kernel/profiling; do
test -r $file || continue
diff --git a/tests/od/od-multiple-t.sh b/tests/od/od-multiple-t.sh
index 77c0d469f..e00338662 100755
--- a/tests/od/od-multiple-t.sh
+++ b/tests/od/od-multiple-t.sh
@@ -2,7 +2,7 @@
# verify that multiple -t specifiers to od align well
# This would fail before coreutils-6.13.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/od/od-x8.sh b/tests/od/od-x8.sh
index b0dd09c5b..960ad094e 100755
--- a/tests/od/od-x8.sh
+++ b/tests/od/od-x8.sh
@@ -2,7 +2,7 @@
# verify that od -t x8 works properly
# This would fail before coreutils-4.5.2.
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/od/od.pl b/tests/od/od.pl
index 40cc4289a..1e515c026 100755
--- a/tests/od/od.pl
+++ b/tests/od/od.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Exercise od
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,6 +18,8 @@
use strict;
+my $limits = getlimits ();
+
(my $program_name = $0) =~ s|.*/||;
# Turn off localization of executable's output.
@@ -25,9 +27,6 @@ use strict;
my $prog = 'od';
-use Errno qw(ERANGE);
-my $ERANGE = do { local $! = ERANGE; "$!" };
-
# Use a file in /proc whose size is not likely to
# change between the wc and od invocations.
my $proc_file = '/proc/version';
@@ -79,11 +78,11 @@ my @Tests =
# Overflowing traditional offsets should be diagnosed.
['overflow-off-1', '-', '7' x 255, {IN_PIPE=>""}, {EXIT=>1},
- {ERR=>"od: ".('7' x 255).": $ERANGE\n"}],
+ {ERR=>"od: ".('7' x 255).": $limits->{ERANGE}\n"}],
['overflow-off-2', '-', ('9' x 254).'.', {IN_PIPE=>""}, {EXIT=>1},
- {ERR=>"od: ".('9' x 254).".: $ERANGE\n"}],
+ {ERR=>"od: ".('9' x 254).".: $limits->{ERANGE}\n"}],
['overflow-off-3', '-', '0x'.('f' x 253), {IN_PIPE=>""}, {EXIT=>1},
- {ERR=>"od: 0x".('f' x 253).": $ERANGE\n"}],
+ {ERR=>"od: 0x".('f' x 253).": $limits->{ERANGE}\n"}],
# Ensure that a large width does not cause trouble.
# From coreutils-7.0 through coreutils-8.21, these would print
diff --git a/tests/other-fs-tmpdir b/tests/other-fs-tmpdir
index 511decca4..d0c16fae9 100644
--- a/tests/other-fs-tmpdir
+++ b/tests/other-fs-tmpdir
@@ -4,7 +4,7 @@
# of the current directory. If one is found, create a temporary directory
# inside it.
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/paste/multi-byte.sh b/tests/paste/multi-byte.sh
new file mode 100755
index 000000000..d0749d47d
--- /dev/null
+++ b/tests/paste/multi-byte.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Test multi-byte delimiter handling in paste
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ paste printf
+
+test "$LOCALE_FR_UTF8" != none || skip_ 'French UTF-8 locale not available'
+
+LC_ALL=$LOCALE_FR_UTF8
+export LC_ALL
+
+# UTF-8 test: 2-byte character (e.g., cent sign)
+delim_cent=$(env printf '\xc2\xa2')
+# UTF-8 test: 3-byte character (e.g., euro sign)
+delim_euro=$(env printf '\xe2\x82\xac')
+# UTF-8 test: 4-byte character (e.g., emoji: U+1F600)
+delim_emoji=$(env printf '\xf0\x9f\x98\x80')
+
+printf '1\n2\n' > f1 || framework_failure_
+printf 'a\nb\n' > f2 || framework_failure_
+
+# Test parallel mode with multi-byte delimiters
+for delim in "$delim_cent" "$delim_euro" "$delim_emoji"; do
+ paste -d "$delim" f1 f2 > out || fail=1
+ printf "1${delim}a\n2${delim}b\n" > exp || framework_failure_
+ compare exp out || fail=1
+done
+
+# Test serial mode with multi-byte delimiters
+printf '1\n2\n3\n' > f3 || framework_failure_
+for delim in "$delim_cent" "$delim_euro"; do
+ paste -s -d "$delim" f3 > out || fail=1
+ printf "1${delim}2${delim}3\n" > exp || framework_failure_
+ compare exp out || fail=1
+done
+
+# Test multiple multi-byte delimiters cycling
+printf 'a\nb\nc\n' > f4 || framework_failure_
+printf '1\n2\n3\n' > f5 || framework_failure_
+printf 'x\ny\nz\n' > f6 || framework_failure_
+paste -d "${delim_cent}${delim_euro}" f4 f5 f6 > out || fail=1
+printf "a${delim_cent}1${delim_euro}x\n" > exp || framework_failure_
+printf "b${delim_cent}2${delim_euro}y\n" >> exp || framework_failure_
+printf "c${delim_cent}3${delim_euro}z\n" >> exp || framework_failure_
+compare exp out || fail=1
+
+# Test multi-byte delimiters mixed with empty delimiter (\0)
+paste -s -d "${delim_euro}\\0" f3 > out || fail=1
+printf "1${delim_euro}23\n" > exp || framework_failure_
+compare exp out || fail=1
+
+# Test invalid UTF-8 sequences are still passed through
+delims_invalid=$(bad_unicode)
+delim_invalid=$(env printf '%s' "$delims_invalid" | cut -b1)
+paste -d "$delims_invalid" f1 f2 > out || fail=1
+printf "1${delim_invalid}a\n2${delim_invalid}b\n" > exp || framework_failure_
+compare exp out || fail=1
+
+# Test that \<multi-byte char> is treated like <multi-byte char>
+# (unknown escapes pass through the escaped character)
+paste -d "\\${delim_euro}" f1 f2 > out || fail=1
+paste -d "$delim_euro" f1 f2 > exp || fail=1
+compare exp out || fail=1
+
+
+# Test GB18030 encoding if available
+export LC_ALL=zh_CN.gb18030
+
+if test "$(locale charmap 2>/dev/null | sed 's/gb/GB/')" = GB18030; then
+ # GB18030 2-byte character (e.g., 0xA2 0xE3 is a valid GB18030 char)
+ delim_gb18030=$(env printf '\xa2\xe3')
+
+ paste -d "$delim_gb18030" f1 f2 > out || fail=1
+ printf "1${delim_gb18030}a\n2${delim_gb18030}b\n" > exp || framework_failure_
+ compare exp out || fail=1
+
+ paste -s -d "$delim_gb18030" f3 > out || fail=1
+ printf "1${delim_gb18030}2${delim_gb18030}3\n" > exp || framework_failure_
+ compare exp out || fail=1
+
+ # Note 0xFF is invalid in GB18030, but we support all single byte delimiters
+ delim_ff=$(env printf '\xff')
+ paste -d "$delim_ff" f1 f2 > out || fail=1
+ printf "1${delim_ff}a\n2${delim_ff}b\n" > exp || framework_failure_
+ compare exp out || fail=1
+fi
+
+Exit $fail
diff --git a/tests/misc/paste.pl b/tests/paste/paste.pl
index 7de1ac44b..c890a2a56 100755
--- a/tests/misc/paste.pl
+++ b/tests/paste/paste.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test paste.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -65,6 +65,21 @@ my @Tests =
# paste: 23^H^H^H.....@...@: No such file or directory$
['delim-bs2', q!-d'\'!, {IN=>{'123\b\b\b.....@'=>''}}, {EXIT => 1},
{ERR => $msg . q!\\! . "\n"} ],
+
+ # \0 allows cycling through an empty delimiter (while "-d ''" does not)
+ ['delim-empty-1', q{-s -d '\0,'}, {IN=>"1\n2\n3\n"}, {OUT=>"12,3\n"}],
+
+ # POSIX escapes
+ ['delim-esc-0', q{-s -d '\0'}, {IN=>"1\n2\n"}, {OUT=>"12\n"}],
+ ['delim-esc-n', q{-s -d '\n'}, {IN=>"1\n2\n"}, {OUT=>"1\n2\n"}],
+ ['delim-esc-t', q{-s -d '\t'}, {IN=>"1\n2\n"}, {OUT=>"1\t2\n"}],
+ ['delim-esc-s', q{-s -d '\\\\'}, {IN=>"1\n2\n"}, {OUT=>"1\\2\n"}],
+ # GNU escapes
+ ['delim-esc-b', q{-s -d '\b'}, {IN=>"1\n2\n"}, {OUT=>"1\b2\n"}],
+ ['delim-esc-f', q{-s -d '\f'}, {IN=>"1\n2\n"}, {OUT=>"1\f2\n"}],
+ ['delim-esc-r', q{-s -d '\r'}, {IN=>"1\n2\n"}, {OUT=>"1\r2\n"}],
+ ['delim-esc-v', q{-s -d '\v'}, {IN=>"1\n2\n"}, {OUT=>"1\0132\n"}],
+ ['delim-esc-foo', q{-s -d '\q'}, {IN=>"1\n2\n"}, {OUT=>"1q2\n"}],
);
my $save_temps = $ENV{DEBUG};
diff --git a/tests/pr/bounded-memory.sh b/tests/pr/bounded-memory.sh
new file mode 100755
index 000000000..42083e92a
--- /dev/null
+++ b/tests/pr/bounded-memory.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+# Test that 'pr' can work on files larger than the systems memory.
+
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ pr
+
+vm=$(get_min_ulimit_v_ timeout 10 pr < /dev/null) ||
+ skip_ 'failed to determine memory limit'
+
+(ulimit -v $(($vm+6000)) \
+ && timeout 0.5 pr < /dev/zero > /dev/null 2>err)
+ret=$?
+test $ret = 124 || {
+ fail=1
+ cat err
+ echo "pr used too much memory" >&2
+}
+
+Exit $fail
diff --git a/tests/pr/pr-tests.pl b/tests/pr/pr-tests.pl
index 60e610664..812e215ac 100755
--- a/tests/pr/pr-tests.pl
+++ b/tests/pr/pr-tests.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test pr.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/printf/printf-cov.pl b/tests/printf/printf-cov.pl
index 17cf315e4..bb48d1552 100755
--- a/tests/printf/printf-cov.pl
+++ b/tests/printf/printf-cov.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# improve printf.c test coverage
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/printf/printf-hex.sh b/tests/printf/printf-hex.sh
index faacab41f..37eee999e 100755
--- a/tests/printf/printf-hex.sh
+++ b/tests/printf/printf-hex.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure that only two hex. digits are consumed in a \xHHH sequence
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/printf/printf-indexed.sh b/tests/printf/printf-indexed.sh
index f2e95eae7..07443f83d 100755
--- a/tests/printf/printf-indexed.sh
+++ b/tests/printf/printf-indexed.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# tests for printf %i$ indexed format processing
-# Copyright (C) 2024-2025 Free Software Foundation, Inc.
+# Copyright (C) 2024-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/printf/printf-mb.sh b/tests/printf/printf-mb.sh
index 532343427..71f1bfc1c 100755
--- a/tests/printf/printf-mb.sh
+++ b/tests/printf/printf-mb.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# tests for printing multi-byte values of characters
-# Copyright (C) 2022-2025 Free Software Foundation, Inc.
+# Copyright (C) 2022-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/printf/printf-quote.sh b/tests/printf/printf-quote.sh
index 15ff6265a..baa355157 100755
--- a/tests/printf/printf-quote.sh
+++ b/tests/printf/printf-quote.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# tests for printf %q
-# Copyright (C) 2015-2025 Free Software Foundation, Inc.
+# Copyright (C) 2015-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,7 +22,8 @@ print_ver_ printf
prog='env printf'
# Equivalent output to ls --quoting=shell-escape
-$prog '%q\n' '' "'" a 'a b' '~a' 'a~' "$($prog %b 'a\r')" > out
+$prog '%q\n' '' "'" a 'a b' '~a' 'a~' \
+ "$($prog %b 'a\r')" "$($prog "\001'\001")" > out
cat <<\EOF > exp || framework_failure_
''
"'"
@@ -31,6 +32,7 @@ a
'~a'
a~
'a'$'\r'
+''$'\001'\'''$'\001'
EOF
compare exp out || fail=1
diff --git a/tests/printf/printf-surprise.sh b/tests/printf/printf-surprise.sh
index 9ad42a8dc..01ee51c1c 100755
--- a/tests/printf/printf-surprise.sh
+++ b/tests/printf/printf-surprise.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Detect printf(3) failure even when it doesn't set stream error indicator
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/printf/printf.sh b/tests/printf/printf.sh
index 72e15c5ef..a436b3711 100755
--- a/tests/printf/printf.sh
+++ b/tests/printf/printf.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# basic tests for printf
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/ptx/ptx-overrun.sh b/tests/ptx/ptx-overrun.sh
index afb294527..ff8b57ad5 100755
--- a/tests/ptx/ptx-overrun.sh
+++ b/tests/ptx/ptx-overrun.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,6 +18,10 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ ptx
+# Ensure these inputs are processed
+printf '%s' '012345678901234567890123456789🛠' | ptx || fail=1
+bad_unicode | ptx || fail=1
+
# Trigger a heap-clobbering bug in ptx from coreutils-6.10 and earlier.
# Using a long file name makes an abort more likely.
# Even with no file name, valgrind detects the buffer overrun.
diff --git a/tests/ptx/ptx.pl b/tests/ptx/ptx.pl
index 51c05be2c..1561996ca 100755
--- a/tests/ptx/ptx.pl
+++ b/tests/ptx/ptx.pl
@@ -1,6 +1,6 @@
#!/usr/bin/perl
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -27,6 +27,13 @@ my @Tests =
["1tok", '-w10', {IN=>"bar\n"}, {OUT=>" bar\n"}],
["2tok", '-w10', {IN=>"foo bar\n"}, {OUT=>" / bar\n foo/\n"}],
+# Ensure -w overrides -t
+["width-1", '-t -w10', {IN=>"bar\n"}, {OUT=>" " x 8 . "bar\n"}],
+# Ensure default width is 72
+["width-3", '', {IN=>"bar\n"}, {OUT=>" " x 39 . "bar\n"}],
+# Ensure default width is 100 with -t
+["width-2", '-t', {IN=>"bar\n"}, {OUT=>" " x 53 . "bar\n"}],
+
# with coreutils-6.12 and earlier, this would infloop with -wN, N < 10
["narrow", '-w2', {IN=>"qux\n"}, {OUT=>" qux\n"}],
["narrow-g", '-g1 -w2', {IN=>"ta\n"}, {OUT=>" ta\n"}],
diff --git a/tests/pwd/argument.sh b/tests/pwd/argument.sh
new file mode 100755
index 000000000..b816b11ec
--- /dev/null
+++ b/tests/pwd/argument.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+# Test that 'pwd' works when given an argument.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ pwd
+getlimits_
+
+base=$(env pwd)
+mkdir -p a/b/c || framework_failure_
+cd a/b/c || framework_failure_
+
+echo 'pwd: ignoring non-option arguments' > exp-err
+echo "$base/a/b/c" > exp-out
+env pwd a > out 2> err || fail=1
+compare exp-out out || fail=1
+compare exp-err err || fail=1
+
+Exit $fail
diff --git a/tests/pwd/pwd-long.sh b/tests/pwd/pwd-long.sh
index 076637b5f..26e1822e7 100755
--- a/tests/pwd/pwd-long.sh
+++ b/tests/pwd/pwd-long.sh
@@ -2,7 +2,7 @@
# -*- perl -*-
# Ensure that pwd works even when run from a very deep directory.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ pwd
+uses_strace_
require_readable_root_
require_perl_
@@ -26,9 +27,15 @@ require_perl_
ARGV_0=$0
export ARGV_0
+# Disable the getcwd syscall if possible, so more of our code is exercised.
+no_sys_getcwd() {
+ strace -f -o /dev/null -e 'getcwd' -e fault=all:error=ENOSYS "$@"
+}
+no_sys_getcwd true || no_sys_getcwd() { "$@"; }
+
# Don't use CuTmpdir here, since File::Temp's use of rmtree can't
# remove the deep tree we create.
-$PERL -Tw -I"$abs_srcdir/tests" -MCuSkip -- - <<\EOF
+no_sys_getcwd $PERL -Tw -I"$abs_srcdir/tests" -MCuSkip -- - <<\EOF
# Show that pwd works even when the length of the resulting
# directory name is longer than PATH_MAX.
diff --git a/tests/pwd/pwd-option.sh b/tests/pwd/pwd-option.sh
index 4a6a88bb7..d9adbd951 100755
--- a/tests/pwd/pwd-option.sh
+++ b/tests/pwd/pwd-option.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that pwd options work.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/readlink/can-e.sh b/tests/readlink/can-e.sh
index c1effd502..a9e74d569 100755
--- a/tests/readlink/can-e.sh
+++ b/tests/readlink/can-e.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# tests for canonicalize-existing mode (readlink -e).
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/readlink/can-f.sh b/tests/readlink/can-f.sh
index 88146656c..ac34d0681 100755
--- a/tests/readlink/can-f.sh
+++ b/tests/readlink/can-f.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# tests for canonicalize mode (readlink -f).
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/readlink/can-m.sh b/tests/readlink/can-m.sh
index 29fe09f8e..351031790 100755
--- a/tests/readlink/can-m.sh
+++ b/tests/readlink/can-m.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# tests for canonicalize-missing mode (readlink -m).
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/readlink/multi.sh b/tests/readlink/multi.sh
index 0ccd7ee10..c491d0b8f 100755
--- a/tests/readlink/multi.sh
+++ b/tests/readlink/multi.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test multiple argument handling.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/readlink/readlink-fp-loop.sh b/tests/readlink/readlink-fp-loop.sh
index 032b0df3f..fb0d2e2fb 100755
--- a/tests/readlink/readlink-fp-loop.sh
+++ b/tests/readlink/readlink-fp-loop.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# readlink from 6.9 would fail with a false-positive symlink loop error
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/readlink/readlink-posix.sh b/tests/readlink/readlink-posix.sh
index 2dcc2e601..db3b26f62 100755
--- a/tests/readlink/readlink-posix.sh
+++ b/tests/readlink/readlink-posix.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test readlink with POSIXLY_CORRECT defined.
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -27,7 +27,9 @@ ln -s file link1 || framework_failure_
cat <<\EOF > exp || framework_failure_
readlink: file: Invalid argument
EOF
-returns_ 1 env POSIXLY_CORRECT=1 readlink file 2>err || fail=1
+returns_ 1 env POSIXLY_CORRECT=1 readlink file 2>errt || fail=1
+# Haiku capitalizes this.
+sed 's/Argument/argument/g' < errt > err
compare exp err || fail=1
# Does not occur for non-POSIX options.
diff --git a/tests/readlink/readlink-root.sh b/tests/readlink/readlink-root.sh
index cea129fdf..d20d7962c 100755
--- a/tests/readlink/readlink-root.sh
+++ b/tests/readlink/readlink-root.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# tests for canonicalize-existing mode (readlink -e) on /.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/readlink/rl-1.sh b/tests/readlink/rl-1.sh
index a6fd98110..b3f6a2049 100755
--- a/tests/readlink/rl-1.sh
+++ b/tests/readlink/rl-1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test for readlink mode.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/cycle.sh b/tests/rm/cycle.sh
index b7fe439ee..352ef37a8 100755
--- a/tests/rm/cycle.sh
+++ b/tests/rm/cycle.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# rm (coreutils-4.5.4) could be tricked into mistakenly reporting a cycle.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/d-1.sh b/tests/rm/d-1.sh
index d31d42b2f..d031ad66c 100755
--- a/tests/rm/d-1.sh
+++ b/tests/rm/d-1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test "rm --dir --verbose".
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/d-2.sh b/tests/rm/d-2.sh
index 8c807a992..641adbc3b 100755
--- a/tests/rm/d-2.sh
+++ b/tests/rm/d-2.sh
@@ -2,7 +2,7 @@
# Ensure that 'rm -d dir' (i.e., without --recursive) gives a reasonable
# diagnostic when failing.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,17 +19,17 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ rm
+getlimits_
mkdir d || framework_failure_
> d/a || framework_failure_
rm -d d 2> out && fail=1
-# Accept any of these: EEXIST, ENOTEMPTY
-sed 's/: File exists/: Directory not empty/' out > out2
+sed "s/: $EEXIST/: $ENOTEMPTY/" out > out2
printf "%s\n" \
- "rm: cannot remove 'd': Directory not empty" \
+ "rm: cannot remove 'd': $ENOTEMPTY" \
> exp || framework_failure_
compare exp out2 || fail=1
diff --git a/tests/rm/d-3.sh b/tests/rm/d-3.sh
index 3238b6f13..0cf339d54 100755
--- a/tests/rm/d-3.sh
+++ b/tests/rm/d-3.sh
@@ -2,7 +2,7 @@
# Ensure that 'rm -d -i dir' (i.e., without --recursive) gives a prompt and
# then deletes the directory if it is empty
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/dangling-symlink.sh b/tests/rm/dangling-symlink.sh
index fc90498a7..d96acffa6 100755
--- a/tests/rm/dangling-symlink.sh
+++ b/tests/rm/dangling-symlink.sh
@@ -4,7 +4,7 @@
# But for fileutils-4.1.9, it would do the former and
# for fileutils-4.1.10 the latter.
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/dash-hint.sh b/tests/rm/dash-hint.sh
new file mode 100755
index 000000000..0b72f8eef
--- /dev/null
+++ b/tests/rm/dash-hint.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+# Test that 'rm -foo' gives a hint to the user in some situations.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ rm
+
+# Check that the hint is not shown when there isn't file named "-foo".
+returns_ 1 rm -foo > out 2> err || fail=1
+compare /dev/null out || fail=1
+grep -F 'to remove the file' err && fail=1
+
+# Check that the hint is shown when there is file named "-foo".
+echo a > -foo || framework_failure_
+cat <<\EOF > exp || framework_failure_
+Try 'rm ./-foo' to remove the file '-foo'.
+Try 'rm --help' for more information.
+EOF
+returns_ 1 rm -foo > out 2> errt || fail=1
+compare /dev/null out || fail=1
+# Ignore any invalid option error messages from getopt.
+tail -n 2 errt > err || framework_failure_
+cmp exp err || fail=1
+
+nl='
+'
+# Check that the output is shell escaped so it can be copy pasted.
+echo a > "-foo${nl}bar" || framework_failure_
+cat <<\EOF > exp || framework_failure_
+Try 'rm ./'-foo'$'\n''bar'' to remove the file '-foo'$'\n''bar'.
+Try 'rm --help' for more information.
+EOF
+returns_ 1 rm "-foo${nl}bar" > out 2> errt || fail=1
+compare /dev/null out || fail=1
+# Ignore any invalid option error messages from getopt.
+tail -n 2 errt > err || framework_failure_
+cmp exp err || fail=1
+
+Exit $fail
diff --git a/tests/rm/deep-1.sh b/tests/rm/deep-1.sh
index be1f09254..59719c53c 100755
--- a/tests/rm/deep-1.sh
+++ b/tests/rm/deep-1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test "rm" with a deep hierarchy.
-# Copyright (C) 1997-2025 Free Software Foundation, Inc.
+# Copyright (C) 1997-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/deep-2.sh b/tests/rm/deep-2.sh
index 466ca88d5..a643ed4bd 100755
--- a/tests/rm/deep-2.sh
+++ b/tests/rm/deep-2.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure rm -r DIR does not prompt for very long full relative names in DIR.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/dir-no-w.sh b/tests/rm/dir-no-w.sh
index 6b00a18d7..9c0aa6a3d 100755
--- a/tests/rm/dir-no-w.sh
+++ b/tests/rm/dir-no-w.sh
@@ -2,7 +2,7 @@
# rm (without -r) must give a diagnostic for any directory.
# It must not prompt, even if that directory is unwritable.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/dir-nonrecur.sh b/tests/rm/dir-nonrecur.sh
index c2908d3fb..0a01b2680 100755
--- a/tests/rm/dir-nonrecur.sh
+++ b/tests/rm/dir-nonrecur.sh
@@ -2,7 +2,7 @@
# Ensure that 'rm dir' (i.e., without --recursive) gives a reasonable
# diagnostic when failing.
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/dot-rel.sh b/tests/rm/dot-rel.sh
index 08f3514d0..b8acbaba4 100755
--- a/tests/rm/dot-rel.sh
+++ b/tests/rm/dot-rel.sh
@@ -2,7 +2,7 @@
# Use rm -r to remove two non-empty dot-relative directories.
# This would have failed between 2004-10-18 and 2004-10-21.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/empty-immutable-skip.sh b/tests/rm/empty-immutable-skip.sh
index 64997abdb..5b8ea6606 100755
--- a/tests/rm/empty-immutable-skip.sh
+++ b/tests/rm/empty-immutable-skip.sh
@@ -2,7 +2,7 @@
# Ensure that rm does not skip extra files after hitting an empty immutable dir.
# Requires root access to do chattr +i, as well as an ext[23] or xfs file system
-# Copyright (C) 2020-2025 Free Software Foundation, Inc.
+# Copyright (C) 2020-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/empty-inacc.sh b/tests/rm/empty-inacc.sh
index 9389dc7d4..bc0089b5a 100755
--- a/tests/rm/empty-inacc.sh
+++ b/tests/rm/empty-inacc.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that rm -rf removes an empty-and-inaccessible directory.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/empty-name.pl b/tests/rm/empty-name.pl
index 81611f33d..87cadbb70 100755
--- a/tests/rm/empty-name.pl
+++ b/tests/rm/empty-name.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Make sure that rm -r '' fails.
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/ext3-perf.sh b/tests/rm/ext3-perf.sh
index f95487b44..f146d9438 100755
--- a/tests/rm/ext3-perf.sh
+++ b/tests/rm/ext3-perf.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that "rm -rf DIR-with-many-entries" is not O(N^2)
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/f-1.sh b/tests/rm/f-1.sh
index 66dc7ca71..e120fac5b 100755
--- a/tests/rm/f-1.sh
+++ b/tests/rm/f-1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test "rm -f" with a nonexistent file.
-# Copyright (C) 1997-2025 Free Software Foundation, Inc.
+# Copyright (C) 1997-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/fail-2eperm.sh b/tests/rm/fail-2eperm.sh
index f23c89164..49dbbb48e 100755
--- a/tests/rm/fail-2eperm.sh
+++ b/tests/rm/fail-2eperm.sh
@@ -1,8 +1,8 @@
#!/bin/sh
-# Like fail-eperm, but the failure must be for a file encountered
-# while trying to remove the containing directory with the sticky bit set.
+# Ensure that rm gives the expected diagnostic when failing to remove a file
+# owned by some other user in a directory with the sticky bit set.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ rm
require_root_
+getlimits_
# The containing directory must be owned by the user who eventually runs rm.
chown $NON_ROOT_USERNAME .
@@ -40,18 +41,20 @@ case $rm_version in
$PACKAGE_VERSION) ;;
*) skip_ "cannot access just-built rm as user $NON_ROOT_USERNAME";;
esac
-chroot --skip-chdir --user=$NON_ROOT_USERNAME / \
- env PATH="$PATH" rm -rf a 2> out-t && fail=1
-# On some systems, we get 'Not owner'. Convert it.
-# On other systems (HPUX), we get 'Permission denied'. Convert it, too.
-onp='Operation not permitted'
-sed "s/Not owner/$onp/;s/Permission denied/$onp/" out-t > out
-cat <<\EOF > exp
-rm: cannot remove 'a/b': Operation not permitted
-EOF
+# Ensure that rm gives the expected diagnostic when
+# failing to remove the file directly, or when traversed
+for file in 'a/b' 'a'; do
+ echo "$file" | grep / && recurse='-r'
+ returns_ 1 chroot --skip-chdir --user=$NON_ROOT_USERNAME / \
+ env PATH="$PATH" rm $recurse -f "$file" 2> out-t || fail=1
-compare exp out || fail=1
+ # On some systems (HPUX), we get 'Permission denied'. Convert it.
+ sed "s/$EACCES/$EPERM/" out-t > out
+
+ echo "rm: cannot remove 'a/b': $EPERM" > exp
+ compare exp out || fail=1
+done
Exit $fail
diff --git a/tests/rm/fail-eacces.sh b/tests/rm/fail-eacces.sh
index 8640b1e4f..e2d47e31f 100755
--- a/tests/rm/fail-eacces.sh
+++ b/tests/rm/fail-eacces.sh
@@ -4,7 +4,7 @@
# With the symlink, rm from coreutils-6.9 would fail with a misleading
# ELOOP diagnostic.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,6 +22,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ rm
skip_if_root_
+getlimits_
ok=0
mkdir d &&
@@ -39,15 +40,15 @@ test $ok = 1 || framework_failure_
rm -rf d/f 2> out && fail=1
-cat <<\EOF > exp
-rm: cannot remove 'd/f': Permission denied
+cat <<EOF > exp
+rm: cannot remove 'd/f': $EACCES
EOF
compare exp out || fail=1
# This used to fail with ELOOP.
rm -rf e 2> out && fail=1
-cat <<\EOF > exp
-rm: cannot remove 'e/slink': Permission denied
+cat <<EOF > exp
+rm: cannot remove 'e/slink': $EACCES
EOF
compare exp out || fail=1
diff --git a/tests/rm/fail-eperm.xpl b/tests/rm/fail-eperm.xpl
deleted file mode 100755
index ab75630a8..000000000
--- a/tests/rm/fail-eperm.xpl
+++ /dev/null
@@ -1,150 +0,0 @@
-#!/usr/bin/perl -Tw
-# Ensure that rm gives the expected diagnostic when failing to remove a file
-# owned by some other user in a directory with the sticky bit set.
-
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-use strict;
-
-(my $ME = $0) =~ s|.*/||;
-
-my $uid = $<;
-# skip if root
-$uid == 0
- and CuSkip::skip "$ME: can't run this test as root: skipping this test";
-
-my $verbose = $ENV{VERBOSE} && $ENV{VERBOSE} eq 'yes';
-
-# Ensure that the diagnostics are in English.
-$ENV{LC_ALL} = 'C';
-
-# Set up a safe, well-known environment
-$ENV{IFS} = '';
-
-# Taint checking requires a sanitized $PATH. This script performs no $PATH
-# search, so on most Unix-based systems, it is fine simply to clear $ENV{PATH}.
-# However, on Cygwin, it's used to find cygwin1.dll, so set it.
-$ENV{PATH} = '/bin:/usr/bin';
-
-my @dir_list = qw(/tmp /var/tmp /usr/tmp);
-my $rm = "$ENV{abs_top_builddir}/src/rm";
-
-# Untaint for upcoming popen.
-$rm =~ m!^([-+\@\w./]+)$!
- or CuSkip::skip "$ME: unusual absolute builddir name; skipping this test\n";
-$rm = $1;
-
-# Find a directory with the sticky bit set.
-my $found_dir;
-my $found_file;
-foreach my $dir (@dir_list)
- {
- if (-d $dir && -k _ && -r _ && -w _ && -x _)
- {
- $found_dir = 1;
-
- # Find a non-directory there that is owned by some other user.
- opendir DIR_HANDLE, $dir
- or die "$ME: couldn't open $dir: $!\n";
-
- foreach my $f (readdir DIR_HANDLE)
- {
- # Consider only names containing "safe" characters.
- $f =~ /^([-\@\w.]+)$/
- or next;
- $f = $1; # untaint $f
-
- my $target_file = "$dir/$f";
- $verbose
- and warn "$ME: considering $target_file\n";
-
- # Skip files owned by self, symlinks, and directories.
- # It's not technically necessary to skip symlinks, but it's simpler.
- # SVR4-like systems (e.g., Solaris 9) let you unlink files that
- # you can write, so skip writable files too.
- -l $target_file || -o _ || -d _ || -w _
- and next;
-
- $found_file = 1;
-
- # Invoke rm on this file and ensure that we get the
- # expected exit code and diagnostic.
- my $cmd = "$rm -f -- $target_file";
- open RM, "$cmd 2>&1 |"
- or die "$ME: cannot execute '$cmd'\n";
-
- my $line = <RM>;
-
- close RM;
- my $rc = $?;
- # This test opportunistically looks for files that can't
- # be removed but those files may already have been removed
- # by their owners by the time we get to them. It is a
- # race condition. If so then the rm is successful and our
- # test is thwarted. Detect this case and ignore.
- if ($rc == 0)
- {
- next if ! -e $target_file;
- die "$ME: unexpected exit status from '$cmd';\n"
- . " got 0, expected 1\n";
- }
- if (0x80 < $rc)
- {
- my $status = $rc >> 8;
- $status == 1
- or die "$ME: unexpected exit status from '$cmd';\n"
- . " got $status, expected 1\n";
- }
- else
- {
- # Terminated by a signal.
- my $sig_num = $rc & 0x7F;
- die "$ME: command '$cmd' died with signal $sig_num\n";
- }
-
- my $exp = "rm: cannot remove '$target_file':";
- $line
- or die "$ME: no output from '$cmd';\n"
- . "expected something like '$exp ...'\n";
-
- # Transform the actual diagnostic so that it starts with "rm:".
- # Depending on your system, it might be "rm:" already, or
- # "../../src/rm:".
- $line =~ s,^\Q$rm\E:,rm:,;
-
- my $regex = quotemeta $exp;
- $line =~ /^$regex/
- or die "$ME: unexpected diagnostic from '$cmd';\n"
- . " got $line"
- . " expected $exp ...\n";
-
- last;
- }
-
- closedir DIR_HANDLE;
- $found_file
- and last;
- }
- }
-
-$found_dir
- or CuSkip::skip "$ME: couldn't find a directory with the sticky bit set;"
- . " skipping this test\n";
-
-$found_file
- or CuSkip::skip "$ME: couldn't find a file not owned by you\n"
- . " in any of the following directories:\n @dir_list\n"
- . "...so, skipping this test\n";
diff --git a/tests/rm/hash.sh b/tests/rm/hash.sh
index 55de65dee..401af643f 100755
--- a/tests/rm/hash.sh
+++ b/tests/rm/hash.sh
@@ -3,7 +3,7 @@
# Before then, rm would fail occasionally, sometimes via
# a failed assertion, others with a seg fault.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/i-1.sh b/tests/rm/i-1.sh
index ade624ef6..9385a8bfa 100755
--- a/tests/rm/i-1.sh
+++ b/tests/rm/i-1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test "rm -i".
-# Copyright (C) 1997-2025 Free Software Foundation, Inc.
+# Copyright (C) 1997-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/i-never.sh b/tests/rm/i-never.sh
index a0fdf905b..22149ac1b 100755
--- a/tests/rm/i-never.sh
+++ b/tests/rm/i-never.sh
@@ -2,7 +2,7 @@
# Ensure that rm --interactive=never works does not prompt, even for
# an unwritable file.
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/i-no-r.sh b/tests/rm/i-no-r.sh
index 9c31f21e3..6716eb19b 100755
--- a/tests/rm/i-no-r.sh
+++ b/tests/rm/i-no-r.sh
@@ -3,7 +3,7 @@
# recurse into directory DIR. rm -i (without -r) must fail in that case.
# Fixed in coreutils-4.5.2.
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/ignorable.sh b/tests/rm/ignorable.sh
index 336a860c4..35dea3c20 100755
--- a/tests/rm/ignorable.sh
+++ b/tests/rm/ignorable.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that rm -f existing-non-dir/anything exits successfully
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/inaccessible.sh b/tests/rm/inaccessible.sh
index fafc4d3f6..5383c682b 100755
--- a/tests/rm/inaccessible.sh
+++ b/tests/rm/inaccessible.sh
@@ -2,7 +2,7 @@
# Ensure that rm works even when run from a directory
# for which the user has no access at all.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,11 +19,12 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ rm
+skip_if_root_
+getlimits_
# Skip this test if your system has neither the openat-style functions
# nor /proc/self/fd support with which to emulate them.
require_openat_support_
-skip_if_root_
p=$(pwd)
mkdir abs1 abs2 no-access || framework_failure_
@@ -34,16 +35,10 @@ set +x
test -d "$p/abs1" && fail=1
test -d "$p/abs2" && fail=1
-cat <<\EOF > exp || framework_failure_
-rm: cannot remove 'rel': Permission denied
+cat <<EOF > exp || framework_failure_
+rm: cannot remove 'rel': $EACCES
EOF
-# AIX 4.3.3 fails with a different diagnostic.
-# Transform their diagnostic
-# ...: The file access permissions do not allow the specified action.
-# to the expected one:
-sed 's/: The file access permissions.*/: Permission denied/'<out>o1;mv o1 out
-
compare exp out || fail=1
Exit $fail
diff --git a/tests/rm/interactive-always.sh b/tests/rm/interactive-always.sh
index 63c2263ef..b49e0ab51 100755
--- a/tests/rm/interactive-always.sh
+++ b/tests/rm/interactive-always.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test the --interactive[=WHEN] changes added to coreutils 6.0
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/interactive-once.sh b/tests/rm/interactive-once.sh
index a6df1a106..bfccf767b 100755
--- a/tests/rm/interactive-once.sh
+++ b/tests/rm/interactive-once.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test the -I option added to coreutils 6.0
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/ir-1.sh b/tests/rm/ir-1.sh
index 998f80a91..ed23dd444 100755
--- a/tests/rm/ir-1.sh
+++ b/tests/rm/ir-1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test "rm -ir".
-# Copyright (C) 1997-2025 Free Software Foundation, Inc.
+# Copyright (C) 1997-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/isatty.sh b/tests/rm/isatty.sh
index c64eaccb4..c35d76772 100755
--- a/tests/rm/isatty.sh
+++ b/tests/rm/isatty.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure 'chown 0 f; rm f' prompts before removing f.
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/many-dir-entries-vs-OOM.sh b/tests/rm/many-dir-entries-vs-OOM.sh
index 9b4d8cb80..4dd2b3644 100755
--- a/tests/rm/many-dir-entries-vs-OOM.sh
+++ b/tests/rm/many-dir-entries-vs-OOM.sh
@@ -2,7 +2,7 @@
# In coreutils-8.12, rm,du,chmod, etc. would use too much memory
# when processing a directory with many entries (as in > 100,000).
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/no-give-up.sh b/tests/rm/no-give-up.sh
index 31d2251fa..6253a2eda 100755
--- a/tests/rm/no-give-up.sh
+++ b/tests/rm/no-give-up.sh
@@ -2,7 +2,7 @@
# With rm from coreutils-5.2.1 and earlier, 'rm -r' would mistakenly
# give up too early under some conditions.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/one-file-system.sh b/tests/rm/one-file-system.sh
index e9b08c19e..88f4c5bd4 100755
--- a/tests/rm/one-file-system.sh
+++ b/tests/rm/one-file-system.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Demonstrate rm's new --one-file-system option.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/one-file-system2.sh b/tests/rm/one-file-system2.sh
index 2e5a79174..1fc3fc46a 100755
--- a/tests/rm/one-file-system2.sh
+++ b/tests/rm/one-file-system2.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify --one-file-system does delete within a file system
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/r-1.sh b/tests/rm/r-1.sh
index 1060d11c9..8a50dddba 100755
--- a/tests/rm/r-1.sh
+++ b/tests/rm/r-1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test "rm -r --verbose".
-# Copyright (C) 1997-2025 Free Software Foundation, Inc.
+# Copyright (C) 1997-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/r-2.sh b/tests/rm/r-2.sh
index 11f943881..7efc04bb1 100755
--- a/tests/rm/r-2.sh
+++ b/tests/rm/r-2.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test "rm -r --verbose".
-# Copyright (C) 1997-2025 Free Software Foundation, Inc.
+# Copyright (C) 1997-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/r-3.sh b/tests/rm/r-3.sh
index 681dfcdda..413e29aad 100755
--- a/tests/rm/r-3.sh
+++ b/tests/rm/r-3.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Create and remove a directory with more than 254 files.
-# Copyright (C) 1997-2025 Free Software Foundation, Inc.
+# Copyright (C) 1997-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/r-4.sh b/tests/rm/r-4.sh
index a087cbab9..1fd062343 100755
--- a/tests/rm/r-4.sh
+++ b/tests/rm/r-4.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Try to remove '.' and '..' recursively.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/r-root.sh b/tests/rm/r-root.sh
index 63202ca2a..aaf063ddc 100755
--- a/tests/rm/r-root.sh
+++ b/tests/rm/r-root.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Try to remove '/' recursively.
-# Copyright (C) 2013-2025 Free Software Foundation, Inc.
+# Copyright (C) 2013-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -41,8 +41,8 @@ unset CU_TEST_SKIP_EXIT
USE_GDB=1
if test $USE_GDB = 1; then
- case $host_triplet in
- *darwin*) skip_ 'avoiding due to potentially non functioning gdb' ;;
+ case $host_os in
+ darwin*) skip_ 'avoiding due to potentially non functioning gdb' ;;
*) ;;
esac
diff --git a/tests/rm/read-only.sh b/tests/rm/read-only.sh
index 5194cb62a..e00d346a5 100755
--- a/tests/rm/read-only.sh
+++ b/tests/rm/read-only.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that rm -f nonexistent-file-on-read-only-fs succeeds.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/readdir-bug.sh b/tests/rm/readdir-bug.sh
index 741c98834..1ed8682a6 100755
--- a/tests/rm/readdir-bug.sh
+++ b/tests/rm/readdir-bug.sh
@@ -2,7 +2,7 @@
# Exercise the Darwin/MacOS bug worked around on 2006-09-29,
# whereby rm would fail to remove all entries in a directory.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/rm-readdir-fail.sh b/tests/rm/rm-readdir-fail.sh
index 1c2401b65..130685cba 100755
--- a/tests/rm/rm-readdir-fail.sh
+++ b/tests/rm/rm-readdir-fail.sh
@@ -2,7 +2,7 @@
# Test rm's behavior when the directory cannot be read.
# This test is skipped on systems that lack LD_PRELOAD support.
-# Copyright (C) 2016-2025 Free Software Foundation, Inc.
+# Copyright (C) 2016-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -56,7 +56,7 @@ struct dirent *readdir (DIR *dirp)
errno = ESRCH;
return NULL;
}
- struct dirent* d;
+ struct dirent *d;
if (! (d = real_readdir (dirp)))
{
fprintf (stderr, "Failed to get dirent\n");
diff --git a/tests/rm/rm1.sh b/tests/rm/rm1.sh
index 59e66f1bd..5a9b6c436 100755
--- a/tests/rm/rm1.sh
+++ b/tests/rm/rm1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# exercise another small part of remove.c
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ rm
skip_if_root_
+getlimits_
mkdir -p b/a/p b/c b/d || framework_failure_
chmod ug-w b/a || framework_failure_
@@ -26,14 +27,14 @@ chmod ug-w b/a || framework_failure_
# This should fail.
rm -rf b > out 2>&1 && fail=1
-cat <<\EOF > exp
-rm: cannot remove directory 'b/a/p': Permission denied
+cat <<EOF > exp
+rm: cannot remove directory 'b/a/p': $EACCES
EOF
# On some systems, rm doesn't have enough information to
# say it's a directory.
-cat <<\EOF > exp2
-rm: cannot remove 'b/a/p': Permission denied
+cat <<EOF > exp2
+rm: cannot remove 'b/a/p': $EACCES
EOF
cmp out exp > /dev/null 2>&1 || {
diff --git a/tests/rm/rm2.sh b/tests/rm/rm2.sh
index 690f53e31..ee4df1852 100755
--- a/tests/rm/rm2.sh
+++ b/tests/rm/rm2.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# exercise another small part of remove.c
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ rm
skip_if_root_
+getlimits_
mkdir -p a/0 || framework_failure_
mkdir -p a/1/2 b/3 || framework_failure_
@@ -30,14 +31,14 @@ chmod u-x a/1 b || framework_failure_
# in the same sort of diagnostic.
# Both of these should fail.
rm -rf a b > out 2>&1 && fail=1
-cat <<\EOF > exp
-rm: cannot remove 'a/1': Permission denied
-rm: cannot remove 'b': Permission denied
+cat <<EOF > exp
+rm: cannot remove 'a/1': $EACCES
+rm: cannot remove 'b': $EACCES
EOF
-cat <<\EOF > exp-solaris
-rm: cannot remove 'a/1/2': Permission denied
-rm: cannot remove 'b/3': Permission denied
+cat <<EOF > exp-solaris
+rm: cannot remove 'a/1/2': $EACCES
+rm: cannot remove 'b/3': $EACCES
EOF
cmp out exp > /dev/null 2>&1 \
diff --git a/tests/rm/rm3.sh b/tests/rm/rm3.sh
index 1e00c069c..23e8c35a2 100755
--- a/tests/rm/rm3.sh
+++ b/tests/rm/rm3.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# exercise another small part of remove.c
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/rm4.sh b/tests/rm/rm4.sh
index eaf1a80ba..bb66b50b9 100755
--- a/tests/rm/rm4.sh
+++ b/tests/rm/rm4.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that 'rm dir' fails without --recursive
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/rm5.sh b/tests/rm/rm5.sh
index 147b303be..59aab9a6b 100755
--- a/tests/rm/rm5.sh
+++ b/tests/rm/rm5.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# a basic test of rm -ri
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/sunos-1.sh b/tests/rm/sunos-1.sh
index 7703a9e31..5d6a1aa94 100755
--- a/tests/rm/sunos-1.sh
+++ b/tests/rm/sunos-1.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure that rm -r '' fails.
-# Copyright (C) 1997-2025 Free Software Foundation, Inc.
+# Copyright (C) 1997-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/unread2.sh b/tests/rm/unread2.sh
index d3140397e..84b2fa9e3 100755
--- a/tests/rm/unread2.sh
+++ b/tests/rm/unread2.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# exercise one small part of remove.c
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ rm
skip_if_root_
+getlimits_
mkdir -p a/b || framework_failure_
chmod u-r a
@@ -26,8 +27,8 @@ chmod u-r a
# This should fail.
rm -rf a > out 2>&1 && fail=1
-cat <<\EOF > exp
-rm: cannot remove 'a': Permission denied
+cat <<EOF > exp
+rm: cannot remove 'a': $EACCES
EOF
compare exp out || fail=1
diff --git a/tests/rm/unread3.sh b/tests/rm/unread3.sh
index b92d2ff57..a0ecae053 100755
--- a/tests/rm/unread3.sh
+++ b/tests/rm/unread3.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that rm works even from an unreadable working directory.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rm/unreadable.pl b/tests/rm/unreadable.pl
index 9f548174d..7c65d39e3 100755
--- a/tests/rm/unreadable.pl
+++ b/tests/rm/unreadable.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test "rm" and unreadable directories.
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,6 +18,8 @@
use strict;
+my $limits = getlimits ();
+
(my $program_name = $0) =~ s|.*/||;
# Turn off localization of executable's output.
@@ -38,7 +40,7 @@ my @Tests =
['unreadable-2', '-rf', $d,
{EXIT => $uid == 0 ? 0 : 1},
{ERR => $uid == 0 ? ''
- : "$prog: cannot remove '$d': Permission denied\n"},
+ : "$prog: cannot remove '$d': $limits->{EACCES}\n"},
{PRE => sub { (mkdir $d,0700 and mkdir "$d/x",0700 and chmod 0100,$d)
or die "$d: $!\n"}} ],
);
diff --git a/tests/rm/v-slash.sh b/tests/rm/v-slash.sh
index 220df213a..34bb72197 100755
--- a/tests/rm/v-slash.sh
+++ b/tests/rm/v-slash.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# avoid extra slashes in --verbose output
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rmdir/fail-perm.sh b/tests/rmdir/fail-perm.sh
index 8a38e5eff..6eb2377e6 100755
--- a/tests/rmdir/fail-perm.sh
+++ b/tests/rmdir/fail-perm.sh
@@ -2,7 +2,7 @@
# For unwritable directory 'd', 'rmdir -p d d/e/f' would emit
# diagnostics but would not fail. Fixed in 5.1.2.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rmdir/ignore.sh b/tests/rmdir/ignore.sh
index e7eec4de7..4ac0050de 100755
--- a/tests/rmdir/ignore.sh
+++ b/tests/rmdir/ignore.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure rmdir's --ignore-fail-on-non-empty option works
-# Copyright (C) 1999-2025 Free Software Foundation, Inc.
+# Copyright (C) 1999-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rmdir/symlink-errors.sh b/tests/rmdir/symlink-errors.sh
index 068fbb6ba..6e3937ccd 100755
--- a/tests/rmdir/symlink-errors.sh
+++ b/tests/rmdir/symlink-errors.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure rmdir outputs clear errors in the presence of symlinks
-# Copyright (C) 2021-2025 Free Software Foundation, Inc.
+# Copyright (C) 2021-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/rmdir/t-slash.sh b/tests/rmdir/t-slash.sh
index ba8ff654f..54f9d0263 100755
--- a/tests/rmdir/t-slash.sh
+++ b/tests/rmdir/t-slash.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure rmdir -p works on a directory specified with a trailing slash
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/runcon/runcon-compute.sh b/tests/runcon/runcon-compute.sh
index 64ac77c39..6719cfa0d 100755
--- a/tests/runcon/runcon-compute.sh
+++ b/tests/runcon/runcon-compute.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that runcon -c uses absolute file names
-# Copyright (C) 2022-2025 Free Software Foundation, Inc.
+# Copyright (C) 2022-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,6 +19,8 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ runcon
+runcon --version || fail=1
+
# Create an executable that's sure to fail
printf '%s\n' '#!/bin/sh' 'exit 1' >> 'true' || framework_failure_
chmod a+x 'true' || framework_failure_
diff --git a/tests/runcon/runcon-no-reorder.sh b/tests/runcon/runcon-no-reorder.sh
index 202755562..6714ffce0 100755
--- a/tests/runcon/runcon-no-reorder.sh
+++ b/tests/runcon/runcon-no-reorder.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that runcon does not reorder its arguments.
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sample-test b/tests/sample-test
index 196ab21dd..1a74bbd56 100644
--- a/tests/sample-test
+++ b/tests/sample-test
@@ -1,7 +1,7 @@
#!/bin/sh
# FIXME
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/seq/seq-epipe.sh b/tests/seq/seq-epipe.sh
index 499b66f37..1c9fdf302 100755
--- a/tests/seq/seq-epipe.sh
+++ b/tests/seq/seq-epipe.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test for proper detection of EPIPE with ignored SIGPIPE
-# Copyright (C) 2016-2025 Free Software Foundation, Inc.
+# Copyright (C) 2016-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/seq/seq-extra-number.sh b/tests/seq/seq-extra-number.sh
index 8faec9b6d..257546670 100755
--- a/tests/seq/seq-extra-number.sh
+++ b/tests/seq/seq-extra-number.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test the "print_extra_number" logic seq.c:print_numbers()
-# Copyright (C) 2019-2025 Free Software Foundation, Inc.
+# Copyright (C) 2019-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/seq/seq-io-errors.sh b/tests/seq/seq-io-errors.sh
index a093eb6f4..89cb44243 100755
--- a/tests/seq/seq-io-errors.sh
+++ b/tests/seq/seq-io-errors.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test for proper detection of I/O errors in seq
-# Copyright (C) 2016-2025 Free Software Foundation, Inc.
+# Copyright (C) 2016-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/seq/seq-locale.sh b/tests/seq/seq-locale.sh
index 12b2d767a..76cee7882 100755
--- a/tests/seq/seq-locale.sh
+++ b/tests/seq/seq-locale.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test for output with appropriate precision
-# Copyright (C) 2019-2025 Free Software Foundation, Inc.
+# Copyright (C) 2019-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/seq/seq-long-double.sh b/tests/seq/seq-long-double.sh
index 356ee6e3a..34d0e7cd2 100755
--- a/tests/seq/seq-long-double.sh
+++ b/tests/seq/seq-long-double.sh
@@ -3,7 +3,7 @@
# Ensure that seq prints exactly two numbers for a 2-number integral
# range at the limit of floating point precision.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/seq/seq-precision.sh b/tests/seq/seq-precision.sh
index cdb07d573..85105b26f 100755
--- a/tests/seq/seq-precision.sh
+++ b/tests/seq/seq-precision.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test for output with appropriate precision
-# Copyright (C) 2015-2025 Free Software Foundation, Inc.
+# Copyright (C) 2015-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/seq/seq.pl b/tests/seq/seq.pl
index 16583a71a..59a3e5e3a 100755
--- a/tests/seq/seq.pl
+++ b/tests/seq/seq.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test "seq".
-# Copyright (C) 1999-2025 Free Software Foundation, Inc.
+# Copyright (C) 1999-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/shred/fifo.sh b/tests/shred/fifo.sh
new file mode 100755
index 000000000..f182d3c9e
--- /dev/null
+++ b/tests/shred/fifo.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+# Test that 'shred' behaves correctly on FIFOs.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ shred
+
+mkfifo_or_skip_ fifo
+
+# Test 'shred' on a FIFO with no readers.
+cat <<\EOF >exp || framework_failure_
+shred: fifo: invalid file type
+EOF
+returns_ 1 timeout 10 shred fifo >out 2>err || fail=1
+compare /dev/null out || fail=1
+compare exp err || fail=1
+
+# Terminate any background processes.
+cleanup_() { kill $pid 2>/dev/null && wait $pid; }
+
+# Test 'shred' on a FIFO with a reader. After the file descriptor is
+# opened, 'cat' will be unblocked.
+timeout 10 cat pipe > /dev/null & pid=$!
+returns_ 1 timeout 10 shred fifo >out 2>err || fail=1
+compare /dev/null out || fail=1
+compare exp err || fail=1
+kill -0 $pid && fail=1
+
+Exit $fail
diff --git a/tests/shred/shred-exact.sh b/tests/shred/shred-exact.sh
index b6abb562d..15eba75b3 100755
--- a/tests/shred/shred-exact.sh
+++ b/tests/shred/shred-exact.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test functionality of --exact
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/shred/shred-passes.sh b/tests/shred/shred-passes.sh
index 6e8fb16c2..39e486db6 100755
--- a/tests/shred/shred-passes.sh
+++ b/tests/shred/shred-passes.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify the operations done by shred
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/shred/shred-remove.sh b/tests/shred/shred-remove.sh
index 585b6bdd6..6df278561 100755
--- a/tests/shred/shred-remove.sh
+++ b/tests/shred/shred-remove.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise shred --remove
-# Copyright (C) 1999-2025 Free Software Foundation, Inc.
+# Copyright (C) 1999-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -63,8 +63,17 @@ shred: test: removed
EOF
compare exp out || fail=1
+# Ensure with stdout, file is deallocated but not removed
+echo > $file || framework_failure_
+shred -u - > $file || fail=1
+test -e $file || fail=1
+returns_ 1 test -s $file || fail=1
+
# Ensure renames are only retried for EEXIST
-mkdir rodir && cd rodir && touch $file && chmod a-w . || framework_failure_
+mkdir rodir && cd rodir && echo > $file && chmod a-w . || framework_failure_
returns_ 1 timeout 10 shred -u $file || fail=1
+# Also ensure file is deallocated separately to unlink
+returns_ 1 test -s $file || fail=1
+
Exit $fail
diff --git a/tests/shred/shred-size.sh b/tests/shred/shred-size.sh
index 8073b0a33..2bf3903e0 100755
--- a/tests/shred/shred-size.sh
+++ b/tests/shred/shred-size.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise shred --size
-# Copyright (C) 2014-2025 Free Software Foundation, Inc.
+# Copyright (C) 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/shuf/shuf-reservoir.sh b/tests/shuf/shuf-reservoir.sh
index a8d636ddf..fb1518d2c 100755
--- a/tests/shuf/shuf-reservoir.sh
+++ b/tests/shuf/shuf-reservoir.sh
@@ -4,7 +4,7 @@
# These tests do not check valid randomness,
# they just check memory allocation related code.
-# Copyright (C) 2013-2025 Free Software Foundation, Inc.
+# Copyright (C) 2013-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/shuf/shuf.sh b/tests/shuf/shuf.sh
index 7bd1840b0..387bbb649 100755
--- a/tests/shuf/shuf.sh
+++ b/tests/shuf/shuf.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that shuf randomizes its input.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,6 +19,18 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ shuf
getlimits_
+uses_strace_
+
+# Ensure we handle unsupported getrandom syscall gracefully
+strace -o /dev/null -e inject=getrandom:error=ENOSYS shuf -i 1-9
+ret=$?
+test $ret = 0 || test $ret = 1 || fail=1
+
+# ensure randomization doesn't depend solely on ASLR
+# This is a probabilistic test :-)
+# However, the odds of failure are low: 1 in $UINTMAX_MAX (~ 1 in 4B on 32 bit)
+shuf_rand() { setarch -R shuf -i 1-$UINTMAX_MAX -n 1; }
+shuf_rand >out1 && shuf_rand >out2 && compare out1 out2 && fail=1
seq 100 > in || framework_failure_
@@ -65,6 +77,11 @@ shuf --zero-terminated -i 1-1 > out || fail=1
printf '1\0' > exp || framework_failure_
cmp out exp || { fail=1; echo "missing NUL terminator?" 1>&2; }
+# Ensure shuf exits with 1 if memory exhausted
+vm=$(get_min_ulimit_v_ shuf -i1-1) && (ulimit -v $vm shuf -i1-1) && {
+ (ulimit -v $vm && returns_ 1 shuf -i1-$SIZE_MAX) || fail=1
+}
+
# Ensure shuf -n operates efficiently for small n. Before coreutils-8.13
# this would try to allocate $SIZE_MAX * sizeof(size_t)
timeout 10 shuf -i1-$SIZE_MAX -n2 >/dev/null ||
@@ -83,6 +100,18 @@ shuf -n10 -i0-9 -n3 -n20 > exp || framework_failure_
c=$(wc -l < exp) || framework_failure_
test "$c" -eq 3 || { fail=1; echo "Multiple -n failed">&2 ; }
+# Test that the conversion from integer to string doesn't write past a buffer.
+# Note that the value is too large for shell arithmetic.
+v=$ULONG_MAX
+while :; do
+ v=$(echo $v | sed 's/^0/1/')
+ test -z "$v" && break
+ echo $v > exp
+ shuf -i $v-$v > out || fail=1
+ compare exp out || fail=1
+ v=$(echo $v | cut -b2-)
+done
+
# Test error conditions
# -i and -e must not be used together
diff --git a/tests/sort/sort-NaN-infloop.sh b/tests/sort/sort-NaN-infloop.sh
index 230f3151c..50a53d414 100755
--- a/tests/sort/sort-NaN-infloop.sh
+++ b/tests/sort/sort-NaN-infloop.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# exercise the NaN-infloop bug in coreutils-8.13
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-benchmark-random.sh b/tests/sort/sort-benchmark-random.sh
index cb6f3d80b..240f7c9fe 100755
--- a/tests/sort/sort-benchmark-random.sh
+++ b/tests/sort/sort-benchmark-random.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Benchmark sort on randomly generated data.
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-buffer-size.sh b/tests/sort/sort-buffer-size.sh
new file mode 100755
index 000000000..ed4b6c0d3
--- /dev/null
+++ b/tests/sort/sort-buffer-size.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+# Exercise sort buffer sizing for streaming input.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ sort
+
+seq -w 200000 > exp || framework_failure_
+
+# The no-temp-file checks below need about 12 MiB of sort buffer,
+# so test 10x that available as the check is somewhat racy.
+avail_mem_mb=$(free -m 2>/dev/null | grep '^Mem:' | cut -F 7)
+test "$avail_mem_mb" -gt 120 ||
+ skip_ 'unable to determine enough memory available'
+rlimit=$(ulimit -v)
+test "$rlimit" = 'unlimited' ||
+test "$(($rlimit/1024))" -gt 120 ||
+ skip_ 'unable to determine enough resources available'
+
+cwd=$(pwd)
+
+# Without an explicit -S, sort should grow a pipe buffer enough to avoid
+# creating a temporary file for this input.
+tac exp | TMPDIR="$cwd"/no-temp sort --parallel=1 > out || fail=1
+{ test -s out && compare exp out; } || fail=1
+
+# Check the same path when sort has precomputed key pointers in the
+# line table that must be adjusted after the buffer moves.
+sed 's/^/k /' exp > exp-key || framework_failure_
+tac exp-key | TMPDIR="$cwd"/no-temp sort --parallel=1 -k2,2 > out || fail=1
+{ test -s out && compare exp-key out; } || fail=1
+
+# An explicit -S remains a limit for ordinary buffer growth, so this must
+# still need a temporary file and fail because TMPDIR is not a directory.
+returns_ 2 env TMPDIR="$cwd"/no-temp \
+ $SHELL -c 'tac exp | sort --parallel=1 -S 1M > small-out' || fail=1
+
+Exit $fail
diff --git a/tests/sort/sort-compress-hang.sh b/tests/sort/sort-compress-hang.sh
index 668450d3c..537e94c06 100755
--- a/tests/sort/sort-compress-hang.sh
+++ b/tests/sort/sort-compress-hang.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test for sort --compress hang
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-compress-proc.sh b/tests/sort/sort-compress-proc.sh
index 203342924..1f3b91352 100755
--- a/tests/sort/sort-compress-proc.sh
+++ b/tests/sort/sort-compress-proc.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test use of compression subprocesses by sort
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -17,7 +17,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
-print_ver_ sort kill
+print_ver_ sort
expensive_
# Terminate any background processes
@@ -58,7 +58,7 @@ chmod +x compress
# while not reading all the data presented.
PRE_COMPRESS='exit 0' \
sort --compress-program=./compress -S 1k --batch-size=30 ./in ./in > out
-test $(env kill -l $?) = 'PIPE' && fail=1
+test $(kill -l $?) = 'PIPE' && fail=1
# "Impatient exit" tests
#
diff --git a/tests/sort/sort-compress.sh b/tests/sort/sort-compress.sh
index 773e34006..12802ca02 100755
--- a/tests/sort/sort-compress.sh
+++ b/tests/sort/sort-compress.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test use of compression by sort
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-continue.sh b/tests/sort/sort-continue.sh
index 05fea1c1d..0f443e232 100755
--- a/tests/sort/sort-continue.sh
+++ b/tests/sort/sort-continue.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Tests for file descriptor exhaustion.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-debug-keys.sh b/tests/sort/sort-debug-keys.sh
index 8d71ac762..12fa03fb3 100755
--- a/tests/sort/sort-debug-keys.sh
+++ b/tests/sort/sort-debug-keys.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test annotation of sort keys
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-debug-warn.sh b/tests/sort/sort-debug-warn.sh
index be3d1ddaf..b459fff20 100755
--- a/tests/sort/sort-debug-warn.sh
+++ b/tests/sort/sort-debug-warn.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test warnings for sort options
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-discrim.sh b/tests/sort/sort-discrim.sh
index eb8d68ead..91defc70f 100755
--- a/tests/sort/sort-discrim.sh
+++ b/tests/sort/sort-discrim.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test discriminator-based sorting.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-exit-early.sh b/tests/sort/sort-exit-early.sh
index 8bcdb93af..fa0a753a6 100755
--- a/tests/sort/sort-exit-early.sh
+++ b/tests/sort/sort-exit-early.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test 'sort' exits early on inaccessible inputs or output
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-field-limit.sh b/tests/sort/sort-field-limit.sh
index 52d8e1d17..0d42e521b 100755
--- a/tests/sort/sort-field-limit.sh
+++ b/tests/sort/sort-field-limit.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# From 7.2-9.7, this would trigger an out of bounds mem read
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-files0-from.pl b/tests/sort/sort-files0-from.pl
index d91924aac..a4df8633d 100755
--- a/tests/sort/sort-files0-from.pl
+++ b/tests/sort/sort-files0-from.pl
@@ -2,7 +2,7 @@
# Exercise sort's --files0-from option.
# FIXME: keep this file in sync with tests/du/files0-from.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-float.sh b/tests/sort/sort-float.sh
index 1439edd70..b0f373d32 100755
--- a/tests/sort/sort-float.sh
+++ b/tests/sort/sort-float.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure sort -g sorts floating point limits correctly
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-h-thousands-sep.sh b/tests/sort/sort-h-thousands-sep.sh
index 11d1cc475..4ba3ee71a 100755
--- a/tests/sort/sort-h-thousands-sep.sh
+++ b/tests/sort/sort-h-thousands-sep.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# exercise 'sort -h' in locales where thousands separator is blank
-# Copyright (C) 2016-2025 Free Software Foundation, Inc.
+# Copyright (C) 2016-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-locale.sh b/tests/sort/sort-locale.sh
new file mode 100755
index 000000000..18c591abd
--- /dev/null
+++ b/tests/sort/sort-locale.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+# Test sort locale ordering
+
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ sort ls
+
+check_different_from_C() {
+ test "$(printf '%s\n' "$1" "$2" | LC_ALL=C sort)" != \
+ "$(printf '%s\n' "$1" "$2" | sort)"
+}
+
+check_hard_collate() {
+ # Correlate with ls
+ touch "$1" "$2" || framework_failure_
+ test "$(printf '%s\n' "$1" "$2" | sort)" = "$(ls -1 "$1" "$2")" || fail=1
+
+ if test "$fail" != 1; then
+ check_different_from_C "$1" "$2" ||
+ skip_ "Strings '$1' '$2' sort the same in C and $LC_ALL locales"
+ fi
+}
+
+export LC_ALL=en_US.iso8859-1 # only lowercase form works on macOS 10.15.7
+if test "$(locale charmap 2>/dev/null | sed 's/iso/ISO-/')" = ISO-8859-1; then
+ check_hard_collate 'a_a' 'a b' # underscore and space considered equal
+ check_hard_collate 'aaa' 'BBB' # case insensitive ordering
+ check_hard_collate "$(printf 'aa\351')" 'aaf' # é comes before f
+fi
+
+export LC_ALL=$LOCALE_FR_UTF8
+if test "$(locale charmap 2>/dev/null)" = UTF-8; then
+ check_hard_collate 'aaé' 'aaf' # é comes before f
+ check_hard_collate 'aéY' "$(printf 'ae\314\201Z')" # NFC/NFD é are equal
+fi
+
+Exit $fail
diff --git a/tests/sort/sort-merge-fdlimit.sh b/tests/sort/sort-merge-fdlimit.sh
index 4d09cefa5..54e790d06 100755
--- a/tests/sort/sort-merge-fdlimit.sh
+++ b/tests/sort/sort-merge-fdlimit.sh
@@ -2,7 +2,7 @@
# Test whether sort avoids opening more file descriptors than it is
# allowed when merging files.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -34,10 +34,15 @@ seq 17 >some-data
# inside the ATF, then the same test with batch size b+1 may pass outside
# the ATF but fail inside it.
+(ulimit -n 19 && touch ulimit-worked &&
+ returns_ 2 sort --batch-size=20 /dev/null) || fail=1
+rm ulimit-worked || skip_ 'cannot modify open file descriptor limit'
+
# The default batch size (nmerge) is 16.
-(ulimit -n 19 \
+(ulimit -n 19 && touch ulimit-worked \
&& sort -m --batch-size=16 in/* 2>err/merge-default-err \
|| ! grep "open failed" err/merge-default-err) || fail=1
+rm ulimit-worked || skip_ 'cannot modify open file descriptor limit'
# If sort opens a file to sort by random hashes of keys,
# it needs to consider this file against its limit on open file
diff --git a/tests/sort/sort-merge.pl b/tests/sort/sort-merge.pl
index a3204d3fe..33824790b 100755
--- a/tests/sort/sort-merge.pl
+++ b/tests/sort/sort-merge.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test "sort -m".
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-month.sh b/tests/sort/sort-month.sh
index 87fd52998..87803089a 100755
--- a/tests/sort/sort-month.sh
+++ b/tests/sort/sort-month.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test sorting of abbreviated months from the locale
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-rand.sh b/tests/sort/sort-rand.sh
index f06826528..90cbdd8c8 100755
--- a/tests/sort/sort-rand.sh
+++ b/tests/sort/sort-rand.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that sort --sort-random doesn't sort.
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-spinlock-abuse.sh b/tests/sort/sort-spinlock-abuse.sh
index 90e1463a0..b2a763a76 100755
--- a/tests/sort/sort-spinlock-abuse.sh
+++ b/tests/sort/sort-spinlock-abuse.sh
@@ -2,7 +2,7 @@
# trigger a bug that would make parallel sort use 100% of one or more
# CPU while blocked on output.
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-stale-thread-mem.sh b/tests/sort/sort-stale-thread-mem.sh
index 4bc037649..b9e362a0e 100755
--- a/tests/sort/sort-stale-thread-mem.sh
+++ b/tests/sort/sort-stale-thread-mem.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Trigger a bug that would cause 'sort' to reference stale thread stack memory.
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-u-FMR.sh b/tests/sort/sort-u-FMR.sh
index 4b7c130d2..68dd669d3 100755
--- a/tests/sort/sort-u-FMR.sh
+++ b/tests/sort/sort-u-FMR.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Before 8.19, this would trigger a free-memory read.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-unique-segv.sh b/tests/sort/sort-unique-segv.sh
index 8fb558b07..08d106288 100755
--- a/tests/sort/sort-unique-segv.sh
+++ b/tests/sort/sort-unique-segv.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# parallel sort with --unique (-u) would segfault
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-unique.sh b/tests/sort/sort-unique.sh
index 93d8c1968..90d566d22 100755
--- a/tests/sort/sort-unique.sh
+++ b/tests/sort/sort-unique.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test 'sort -u'.
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort-version.sh b/tests/sort/sort-version.sh
index f68dbfba8..3ff0de4a6 100755
--- a/tests/sort/sort-version.sh
+++ b/tests/sort/sort-version.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# exercise sort's --sort=version option
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/sort/sort.pl b/tests/sort/sort.pl
index 5fa9d5252..e37a746ec 100755
--- a/tests/sort/sort.pl
+++ b/tests/sort/sort.pl
@@ -1,6 +1,6 @@
#!/usr/bin/perl
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/split/additional-suffix.sh b/tests/split/additional-suffix.sh
index cb6865f54..ad37e35e9 100755
--- a/tests/split/additional-suffix.sh
+++ b/tests/split/additional-suffix.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# show that 'split --additional-suffix=SUFFIX' works.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/split/b-chunk.sh b/tests/split/b-chunk.sh
index 3e745642d..5ee12a6e3 100755
--- a/tests/split/b-chunk.sh
+++ b/tests/split/b-chunk.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test splitting into 3 chunks
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/split/fail.sh b/tests/split/fail.sh
index 6852e1209..7cc8652e4 100755
--- a/tests/split/fail.sh
+++ b/tests/split/fail.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# split must fail when given length/count of zero.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/split/filter.sh b/tests/split/filter.sh
index 105b6d929..ba8f5e447 100755
--- a/tests/split/filter.sh
+++ b/tests/split/filter.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise split's new --filter option.
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/split/guard-input.sh b/tests/split/guard-input.sh
index 1eba9f1de..6800df6b7 100755
--- a/tests/split/guard-input.sh
+++ b/tests/split/guard-input.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure split doesn't overwrite input with output.
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/split/l-chunk-root.sh b/tests/split/l-chunk-root.sh
index df74a4384..dc618e715 100755
--- a/tests/split/l-chunk-root.sh
+++ b/tests/split/l-chunk-root.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test splitting into newline delineated chunks from infinite input
-# Copyright (C) 2023-2025 Free Software Foundation, Inc.
+# Copyright (C) 2023-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/split/l-chunk.sh b/tests/split/l-chunk.sh
index d616978b6..d79ed9b00 100755
--- a/tests/split/l-chunk.sh
+++ b/tests/split/l-chunk.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test splitting into newline delineated chunks (-n l/...)
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/split/line-bytes.sh b/tests/split/line-bytes.sh
index 740c72ab2..2fbf1226d 100755
--- a/tests/split/line-bytes.sh
+++ b/tests/split/line-bytes.sh
@@ -1,7 +1,7 @@
#!/bin/sh
-# test -C, --lines-bytes
+# test -C, --line-bytes
-# Copyright (C) 2013-2025 Free Software Foundation, Inc.
+# Copyright (C) 2013-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -84,7 +84,7 @@ for b in $(seq 10); do
compare no_eol_splits_exp no_eol_splits || fail=1
done
-# Test hold buffer management with --lines-bytes.
+# Test hold buffer management with --line-bytes.
# The following triggers (with ASAN) a heap overflow issue
# between coreutils 9.2 and 9.4 inclusive.
printf '%131070s\n' '' >expaa || framework_failure_
diff --git a/tests/split/lines.sh b/tests/split/lines.sh
index ff4f14e30..f63f9607a 100755
--- a/tests/split/lines.sh
+++ b/tests/split/lines.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# show that 'split --lines=2' works.
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/split/non-utf8.sh b/tests/split/non-utf8.sh
new file mode 100755
index 000000000..224b7a9b5
--- /dev/null
+++ b/tests/split/non-utf8.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+# Verify that split preserves non-UTF-8 bytes in prefix and suffix.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ split
+
+echo a > "$(bad_unicode)" \
+ || skip_ 'bad unicode not supported in shell or file system'
+
+# Non-UTF-8 bytes in prefix should be preserved, not replaced
+# by UTF-8 replacement characters (0xEF 0xBF 0xBD).
+prefix="$(bad_unicode)"
+printf 'AB' | split -b1 - "$prefix" || fail=1
+test -f "$(printf '%saa' $prefix)" || fail=1
+test -f "$(printf '%sab' $prefix)" || fail=1
+
+# Non-UTF-8 bytes in --additional-suffix should also be preserved.
+suffix="$(bad_unicode)"
+printf 'AB' | split -b1 --additional-suffix="$suffix" - q || fail=1
+test -f "$(printf 'qaa%s' "$suffix")" || fail=1
+test -f "$(printf 'qab%s' "$suffix")" || fail=1
+
+Exit $fail
diff --git a/tests/split/numeric.sh b/tests/split/numeric.sh
index 2469bd025..9ae6cb280 100755
--- a/tests/split/numeric.sh
+++ b/tests/split/numeric.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test --{hex,numeric}-suffixes[=from]
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/split/r-chunk.sh b/tests/split/r-chunk.sh
index 472690d28..71fdb3ad5 100755
--- a/tests/split/r-chunk.sh
+++ b/tests/split/r-chunk.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test splitting into round-robin chunks
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -58,7 +58,7 @@ compare exp out || fail=1
# Ensure we fall back to appending to a file at a time
# if we hit the limit for the number of open files.
rm x*
-(ulimit -n 20 && yes | head -n90 | split -n r/30 ) || fail=1
+(ulimit -n 20; yes | head -n90 | split -n r/30 ) || fail=1
test "$(stat -c %s x* | uniq -c | sed 's/^ *//; s/ /x/')" = "30x6" || fail=1
Exit $fail
diff --git a/tests/split/record-sep.sh b/tests/split/record-sep.sh
index 0b9870284..c1d1edbd3 100755
--- a/tests/split/record-sep.sh
+++ b/tests/split/record-sep.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test split with custom record separators
-# Copyright (C) 2015-2025 Free Software Foundation, Inc.
+# Copyright (C) 2015-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/split/split-io-err.sh b/tests/split/split-io-err.sh
new file mode 100755
index 000000000..2e3b3f632
--- /dev/null
+++ b/tests/split/split-io-err.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+# Ensure we handle i/o errors correctly in split via /dev/full
+
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ split
+getlimits_
+
+cp -sf /dev/full xaa || skip_ '/dev/full is required'
+
+# Create the expected error message
+printf '%s\n' "split: xaa: $ENOSPC" > exp || framework_failure_
+
+# the 'split' command should fail with exit code 1
+seq 2 | returns_ 1 split -b 1 2> err || fail=1
+# split does not cleanup broken file (while csplit does)
+test -e xaa || fail=1
+# split should not continue
+test -e xab && fail=1
+
+# Ensure we got the expected error message
+compare exp err || fail=1
+
+rm xaa || framework_failure_
+# Similar for directory
+mkdir xaa || framework_failure_
+seq 2 | returns_ 1 split -b 1 2 || fail=1
+test -d xaa || fail=1
+
+Exit $fail
diff --git a/tests/split/suffix-auto-length.sh b/tests/split/suffix-auto-length.sh
index 4aee286bb..231c43f29 100755
--- a/tests/split/suffix-auto-length.sh
+++ b/tests/split/suffix-auto-length.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test the suffix auto width functionality
-# Copyright (C) 2012-2025 Free Software Foundation, Inc.
+# Copyright (C) 2012-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/split/suffix-length.sh b/tests/split/suffix-length.sh
index ba003d62b..84fce04a2 100755
--- a/tests/split/suffix-length.sh
+++ b/tests/split/suffix-length.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Show that split -a works.
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/stat/stat-birthtime.sh b/tests/stat/stat-birthtime.sh
index 2af9118c0..3f0b54906 100755
--- a/tests/stat/stat-birthtime.sh
+++ b/tests/stat/stat-birthtime.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that stat attempts birthtime access
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/stat/stat-fmt.sh b/tests/stat/stat-fmt.sh
index dbc1d0a92..dec5f98db 100755
--- a/tests/stat/stat-fmt.sh
+++ b/tests/stat/stat-fmt.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# stat --format tests
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -40,6 +40,36 @@ cat <<\EOF >exp
EOF
compare exp out || fail=1
+# Check the behavior with invalid values of QUOTING_STYLE.
+for style in '' 'abcdef'; do
+ QUOTING_STYLE="$style" stat -c%%%N \' > out 2> err || fail=1
+ cat <<\EOF > exp-out || framework_failure_
+%"'"
+EOF
+ cat <<EOF > exp-err || framework_failure_
+stat: ignoring invalid value of environment variable QUOTING_STYLE: '$style'
+EOF
+ compare exp-out out || fail=1
+ compare exp-err err || fail=1
+ # coreutils-9.10 and earlier would unnecessarily check QUOTING_STYLE in
+ # these cases.
+ for format in '%%N' 'abc%%Ndef' 'abc%%Ndef%%N' '%%%%N'; do
+ QUOTING_STYLE="$style" stat -c"$format" \' > out 2> err || fail=1
+ printf "$format\n" > exp-out || framework_failure_
+ compare exp-out out || fail=1
+ compare /dev/null err || fail=1
+ done
+done
+
+# Check the behavior with %N following %%N.
+stat -cabc%%Ndef%N \' > out || fail=1
+QUOTING_STYLE=locale stat -cabc%%Ndef%N \' >> out || fail=1
+cat <<\EOF >exp
+abc%Ndef"'"
+abc%Ndef'\''
+EOF
+compare exp out || fail=1
+
# ensure %H and %L modifiers are handled
stat -c '%r %R %Hd,%Ld %Hr,%Lr' . > out || fail=1
grep -F '?' out && fail=1
diff --git a/tests/stat/stat-hyphen.sh b/tests/stat/stat-hyphen.sh
index 40f091ef2..de3a7d252 100755
--- a/tests/stat/stat-hyphen.sh
+++ b/tests/stat/stat-hyphen.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# demonstrate that stat - works and stat -f - does not.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/stat/stat-mount.sh b/tests/stat/stat-mount.sh
index 975b22518..1a3d21996 100755
--- a/tests/stat/stat-mount.sh
+++ b/tests/stat/stat-mount.sh
@@ -1,7 +1,7 @@
#!/bin/sh
-# Test stat -c%m
+# Test stat interactions with mounts
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -25,4 +25,15 @@ case "$stat_mnt" in
*) fail=1;;
esac
+# Ensure stat works without mounting /proc
+hide_proc() {
+ unshare -rm $SHELL -c 'mount -t tmpfs tmpfs /proc && "$@"' -- "$@"
+}
+if hide_proc true; then
+ hide_proc mount; ret=$?
+ if test "$ret" = 2; then # Avoid segfaults under unshare on Guix
+ hide_proc stat -c '0%#a' / || fail=1
+ fi
+fi
+
Exit $fail
diff --git a/tests/stat/stat-nanoseconds.sh b/tests/stat/stat-nanoseconds.sh
index ffcb5a8b8..44914751a 100755
--- a/tests/stat/stat-nanoseconds.sh
+++ b/tests/stat/stat-nanoseconds.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise format strings involving %:X, %:Y, etc.
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/stat/stat-printf.pl b/tests/stat/stat-printf.pl
index 6d6de6cf4..a2f41dcf6 100755
--- a/tests/stat/stat-printf.pl
+++ b/tests/stat/stat-printf.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test "stat --printf".
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/stat/stat-slash.sh b/tests/stat/stat-slash.sh
index e34986c80..ee5743664 100755
--- a/tests/stat/stat-slash.sh
+++ b/tests/stat/stat-slash.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# demonstrate that stat handles trailing slashes correctly
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/stty/bad-speed.sh b/tests/stty/bad-speed.sh
index d80d2e7c7..fe4ccfb7d 100755
--- a/tests/stty/bad-speed.sh
+++ b/tests/stty/bad-speed.sh
@@ -2,7 +2,7 @@
# Ensure we handle cfsetispeed failing
# which we did not before coreutils v9.1
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ stty
+require_controlling_input_terminal_
require_gcc_shared_
# Replace each cfsetispeed call with a call to these stubs.
diff --git a/tests/stty/stty-invalid.sh b/tests/stty/stty-invalid.sh
index 868ed1d16..46d39bfa4 100755
--- a/tests/stty/stty-invalid.sh
+++ b/tests/stty/stty-invalid.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that stty diagnoses invalid inputs, rather than silently misbehaving.
-# Copyright (C) 2007-2025 Free Software Foundation, Inc.
+# Copyright (C) 2007-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/stty/stty-pairs.sh b/tests/stty/stty-pairs.sh
index 2af9ac78c..4b447ab09 100755
--- a/tests/stty/stty-pairs.sh
+++ b/tests/stty/stty-pairs.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure stty can parse most of its options - in pairs [expensive].
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/stty/stty-row-col.sh b/tests/stty/stty-row-col.sh
index ccf8e5380..4229c28bb 100755
--- a/tests/stty/stty-row-col.sh
+++ b/tests/stty/stty-row-col.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test "stty" with rows and columns.
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -58,6 +58,15 @@ set -- $tests
saved_size=$(stty size) && test -n "$saved_size" \
|| skip_ "can't get window size"
+cleanup_()
+{
+ trap - WINCH
+ if test -n "$saved_size" && test "x$saved_size" != "x0 0"; then
+ set x $saved_size
+ stty rows $2 columns $3
+ fi
+}
+
# Linux virtual consoles issue an error if you
# try to increase their size. So skip in that case.
if test "x$saved_size" != "x0 0"; then
@@ -78,14 +87,14 @@ while :; do
# echo "testing \$(stty $args; stty size\) = $expected_result ..."
echo "test $test_name... " | tr -d '\n'
fi
+ trap '' WINCH # Ignore terminal resizes
stty $args || exit 1
+ # Skip if other terminal resizes detected
+ trap 'skip_ "terminal resize detected"' WINCH
test x"$(stty size 2> /dev/null)" = "x$expected_result" \
&& ok=ok || ok=FAIL fail=1
test "$VERBOSE" = yes && echo $ok
shift; shift; shift
done
-set x $saved_size
-stty rows $2 columns $3 || exit 1
-
Exit $fail
diff --git a/tests/stty/stty.sh b/tests/stty/stty.sh
index b224ae41f..21af07830 100755
--- a/tests/stty/stty.sh
+++ b/tests/stty/stty.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure stty can parse most of its options.
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tac/tac-2-nonseekable.sh b/tests/tac/tac-2-nonseekable.sh
index 0b9ed3986..f96efe98d 100755
--- a/tests/tac/tac-2-nonseekable.sh
+++ b/tests/tac/tac-2-nonseekable.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that tac works with non-seekable or quasi-seekable inputs
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tac/tac-continue.sh b/tests/tac/tac-continue.sh
index b4c92c1fd..4123755f2 100755
--- a/tests/tac/tac-continue.sh
+++ b/tests/tac/tac-continue.sh
@@ -3,7 +3,7 @@
# when it encounters an error with say the first one.
# With coreutils-5.2.1 and earlier, this test would fail.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,57 +20,25 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ tac
+require_root_
+getlimits_
-# See if the envvar is defined.
-if test x = "x$FULL_PARTITION_TMPDIR"; then
- skip_ "FULL_PARTITION_TMPDIR not defined"
-fi
+cwd=$(pwd)
+cleanup_() { cd /; umount "$cwd/full_tmp"; }
-if ! test -d "$FULL_PARTITION_TMPDIR"; then
- echo "$0: $FULL_PARTITION_TMPDIR:" \
- "\$FULL_PARTITION_TMPDIR does not specify a directory" 1>&2
- Exit 1
-fi
+mkdir full_tmp || framework_failure_
-fp_tmp="$FULL_PARTITION_TMPDIR/tac-cont-$$"
-cleanup_()
-{
- # Terminate any background process
- # and remove tmp dir
- rm -f "$fp_tmp"
- kill $pid 2>/dev/null && wait $pid
-}
+mount -t tmpfs --options size=1M tmpfs $cwd/full_tmp ||
+ skip_ 'Unable to mount small tmpfs'
# Make sure we can create an empty file there (i.e., no shortage of inodes).
-if ! touch $fp_tmp; then
- echo "$0: $fp_tmp: cannot create empty file" 1>&2
- Exit 1
-fi
+touch "$cwd/full_tmp/tac-empty" || framework_failure_
-# Make sure that we fail when trying to create a file large enough
-# to consume a non-inode block.
-if seq 1000 > $fp_tmp 2>/dev/null; then
- echo "$0: $FULL_PARTITION_TMPDIR: not a full partition" 1>&2
- Exit 1
-fi
-
-seq 5 > in
-
-
-# Give tac a fifo command line argument.
-# This makes it try to create a temporary file in $TMPDIR.
-mkfifo_or_skip_ fifo
-seq 1000 > fifo & pid=$!
-TMPDIR=$FULL_PARTITION_TMPDIR tac fifo in >out 2>err && fail=1
-
-cat <<\EOF > exp || framework_failure_
-5
-4
-3
-2
-1
-EOF
+seq 5 > five && seq 5 -1 1 > exp || framework_failure_
+# Make sure we diagnose the failure but continue to subsequent files
+yes | TMPDIR=$cwd/full_tmp timeout 10 tac - five >out 2>err && fail=1
+{ test $? = 124 || ! grep "$ENOSPC" err >/dev/null; } && fail=1
compare exp out || fail=1
Exit $fail
diff --git a/tests/tac/tac-locale.sh b/tests/tac/tac-locale.sh
new file mode 100755
index 000000000..70833db11
--- /dev/null
+++ b/tests/tac/tac-locale.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+# Test that tac --separator=SEP works if SEP is not ASCII.
+
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ tac printf
+
+check_separator ()
+{
+ env printf "1$12$13$1" > inp || framework_failure_
+ env printf "3$12$11$1\n" > exp || framework_failure_
+ tac --separator="$(env printf -- "$1")" inp > out || fail=1
+ env printf '\n' >> out || framework_failure_
+ compare exp out || fail=1
+}
+
+export LC_ALL=en_US.iso8859-1 # only lowercase form works on macOS 10.15.7
+if test "$(locale charmap 2>/dev/null | sed 's/iso/ISO-/')" = ISO-8859-1; then
+ check_separator '\xe9' # é
+ check_separator '\xe9\xea' # éê
+fi
+
+export LC_ALL=$LOCALE_FR_UTF8
+if test "$(locale charmap 2>/dev/null)" = UTF-8; then
+ check_separator '\u0434' # д
+ check_separator '\u0434\u0436' # дж
+ check_separator "$(bad_unicode)"
+fi
+
+Exit $fail
diff --git a/tests/tac/tac.pl b/tests/tac/tac.pl
index fa14bd54c..99d2d0d50 100755
--- a/tests/tac/tac.pl
+++ b/tests/tac/tac.pl
@@ -1,6 +1,6 @@
#!/usr/bin/perl
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -63,6 +63,10 @@ my @Tests =
{IN=>"a.___b.__1._2.__3.___4._"},
{OUT=>"4._3.___2.__1._b.__a.___"}],
+ ['opt-r3', qw(-r -s '^'), {IN=>"a\nb\nc\n"}, {OUT=>"c\nb\na\n"}],
+ ['opt-r4', qw(-r -s '$'), {IN=>"a\nb\nc\n"}, {OUT=>"\n\nc\nba"}],
+ ['opt-r5', qw(-r -s '^$'), {IN=>"a\nb\nc\n"}, {OUT=>"a\nb\nc\n"}],
+
# This gave incorrect output (.___4._2.__3._1) with tac-1.22.
['opt-br', qw(-b -r -s '\._+'),
{IN=>"._1._2.__3.___4"}, {OUT=>".___4.__3._2._1"}],
diff --git a/tests/tail/F-headers.sh b/tests/tail/F-headers.sh
index e3a8eac0b..153a5779d 100755
--- a/tests/tail/F-headers.sh
+++ b/tests/tail/F-headers.sh
@@ -3,7 +3,7 @@
# Between coreutils 7.5 and 8.23 inclusive, 'tail -F ...' would
# not output headers for or created/renamed files in certain cases.
-# Copyright (C) 2015-2025 Free Software Foundation, Inc.
+# Copyright (C) 2015-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/F-vs-missing.sh b/tests/tail/F-vs-missing.sh
index 3841393a3..75a534734 100755
--- a/tests/tail/F-vs-missing.sh
+++ b/tests/tail/F-vs-missing.sh
@@ -3,7 +3,7 @@
# Before coreutils-8.6, tail -F missing/file would not
# notice any subsequent availability of the missing/file.
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/F-vs-rename.sh b/tests/tail/F-vs-rename.sh
index a10cd8131..1ee3aa860 100755
--- a/tests/tail/F-vs-rename.sh
+++ b/tests/tail/F-vs-rename.sh
@@ -3,7 +3,7 @@
# Between coreutils 7.5 and 8.2 inclusive, 'tail -F a b' would
# stop tracking additions to b after 'mv a b'.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/append-only.sh b/tests/tail/append-only.sh
index a9aff3c3d..fa24c926a 100755
--- a/tests/tail/append-only.sh
+++ b/tests/tail/append-only.sh
@@ -2,7 +2,7 @@
# Ensure that tail -f works on an append-only file
# Requires root access to do chattr +a, as well as an ext[23] or xfs file system
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/assert-2.sh b/tests/tail/assert-2.sh
index afa6e4d18..0ce619542 100755
--- a/tests/tail/assert-2.sh
+++ b/tests/tail/assert-2.sh
@@ -3,7 +3,7 @@
# Due to a race condition in the test, the 'assert' script would get
# the UMR on Solaris only some of the time, and not at all on Linux/GNU.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/assert.sh b/tests/tail/assert.sh
index 826ab4d6d..d982c8a0b 100755
--- a/tests/tail/assert.sh
+++ b/tests/tail/assert.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test for assertion failure in "test".
-# Copyright (C) 1999-2025 Free Software Foundation, Inc.
+# Copyright (C) 1999-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/basic-seek.sh b/tests/tail/basic-seek.sh
index 307ed667e..2789c8aa5 100755
--- a/tests/tail/basic-seek.sh
+++ b/tests/tail/basic-seek.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Verify that tail works when seeking within a file
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/big-4gb.sh b/tests/tail/big-4gb.sh
index 734f113a2..39613507b 100755
--- a/tests/tail/big-4gb.sh
+++ b/tests/tail/big-4gb.sh
@@ -2,7 +2,7 @@
# Demonstrate a bug in 'tail -cN' when operating on files of size 4G and larger
# Fixed in coreutils-4.5.2.
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/debug.sh b/tests/tail/debug.sh
new file mode 100755
index 000000000..6f3869921
--- /dev/null
+++ b/tests/tail/debug.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+# Test --debug output
+
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ tail
+
+# Terminate any background tail process
+cleanup_() { kill $pid 2>/dev/null && wait $pid; }
+
+cleanup_fail_ ()
+{
+ warn_ $1
+ cleanup_
+ fail=1
+}
+
+# $check_re - string to be found
+# $check_f - file to be searched
+check_tail_output_ ()
+{
+ local delay="$1"
+ grep "$check_re" $check_f > /dev/null ||
+ { sleep $delay ; return 1; }
+}
+
+grep_timeout_ ()
+{
+ check_re="$1"
+ check_f="$2"
+ retry_delay_ check_tail_output_ .1 5
+}
+
+
+timeout 10 tail --debug -f /dev/null 2>debug.out & pid=$!
+grep_timeout_ 'tail: using blocking mode' 'debug.out' || fail=1
+cleanup_
+
+timeout 10 tail --debug -F /dev/null 2>debug.out & pid=$!
+grep_timeout_ 'tail: using polling mode' 'debug.out' || fail=1
+cleanup_
+
+touch file.debug
+require_strace_ 'inotify_add_watch'
+returns_ 124 timeout .1 strace -e inotify_add_watch -o strace.out \
+ tail -F file.debug || fail=1
+if grep 'inotify' strace.out; then
+ timeout 10 tail --debug -n0 -f file.debug 2>debug.out & pid=$!
+ grep_timeout_ 'tail: using notification mode' 'debug.out' || fail=1
+ cleanup_
+
+ timeout 10 tail --debug ---disable-inotify -f file.debug 2>debug.out & pid=$!
+ grep_timeout_ 'tail: using polling mode' 'debug.out' || fail=1
+ cleanup_
+fi
+
+Exit $fail
diff --git a/tests/tail/descriptor-vs-rename.sh b/tests/tail/descriptor-vs-rename.sh
index eff2f706a..3b701610d 100755
--- a/tests/tail/descriptor-vs-rename.sh
+++ b/tests/tail/descriptor-vs-rename.sh
@@ -3,7 +3,7 @@
# Between coreutils 7.5 and 8.23 inclusive, 'tail -f a' would
# stop tracking additions to b after 'mv a b'.
-# Copyright (C) 2015-2025 Free Software Foundation, Inc.
+# Copyright (C) 2015-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/end-of-device.sh b/tests/tail/end-of-device.sh
index ca79bdce9..f6fd609ae 100755
--- a/tests/tail/end-of-device.sh
+++ b/tests/tail/end-of-device.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that tail seeks to the end of a device
-# Copyright (C) 2017-2025 Free Software Foundation, Inc.
+# Copyright (C) 2017-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/flush-initial.sh b/tests/tail/flush-initial.sh
index 016ce78c3..7aed701fe 100755
--- a/tests/tail/flush-initial.sh
+++ b/tests/tail/flush-initial.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# inotify-based tail -f didn't flush its initial output before blocking
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/follow-name.sh b/tests/tail/follow-name.sh
index 0c55cb9a0..c578cb7df 100755
--- a/tests/tail/follow-name.sh
+++ b/tests/tail/follow-name.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that --follow=name does not imply --retry
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/follow-stdin.sh b/tests/tail/follow-stdin.sh
index 8aa8b78ac..28858a06c 100755
--- a/tests/tail/follow-stdin.sh
+++ b/tests/tail/follow-stdin.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test tail -f -
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/inotify-dir-recreate.sh b/tests/tail/inotify-dir-recreate.sh
index 569a3b1aa..8be4f0ee8 100755
--- a/tests/tail/inotify-dir-recreate.sh
+++ b/tests/tail/inotify-dir-recreate.sh
@@ -3,7 +3,7 @@
# of the watched file was removed and recreated.
# (...instead of getting stuck forever)
-# Copyright (C) 2017-2025 Free Software Foundation, Inc.
+# Copyright (C) 2017-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,17 +20,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ tail
-
-grep '^#define HAVE_INOTIFY 1' "$CONFIG_HEADER" >/dev/null && is_local_dir_ . \
- || skip_ 'inotify is not supported'
-
-# There may be a mismatch between is_local_dir_ (gnulib's remoteness check),
-# and coreutils' is_local_fs_type(), so double check we're using inotify.
-touch file.strace
-require_strace_ 'inotify_add_watch'
-returns_ 124 timeout .1 strace -e inotify_add_watch -o strace.out \
- tail -F file.strace || fail=1
-grep 'inotify' strace.out || skip_ 'inotify not detected'
+require_inotify_supported_
# Terminate any background tail process
cleanup_() { kill $pid 2>/dev/null && wait $pid; }
@@ -47,7 +37,7 @@ cleanup_fail_ ()
check_tail_output_ ()
{
local delay="$1"
- grep $check_re $check_f > /dev/null ||
+ grep "$check_re" "$check_f" > /dev/null ||
{ sleep $delay ; return 1; }
}
@@ -61,23 +51,29 @@ grep_timeout_ ()
# Prepare the file to be watched
mkdir dir && echo 'inotify' > dir/file || framework_failure_
+# Speed up the fallback-to-polling phase.
+fastpoll='-s.1 --max-unchanged-stats=1'
+
#tail must print content of the file to stdout, verify
-timeout 60 tail --pid=$$ -F dir/file >out 2>&1 & pid=$!
-grep_timeout_ 'inotify' 'out' ||
+timeout 60 tail --debug --pid=$$ -F $fastpoll dir/file >out 2>&1 & pid=$!
+grep_timeout_ 'inotify' out ||
{ cleanup_fail_ 'file to be tailed does not exist'; }
inotify_failed_re='inotify (resources exhausted|cannot be used)'
grep -E "$inotify_failed_re" out &&
skip_ "inotify can't be used"
+grep_timeout_ 'using notification mode' out ||
+{ cleanup_fail_ 'tail did not switch to notification mode'; }
+
# Remove the directory, should get the message about the deletion
rm -r dir || framework_failure_
-grep_timeout_ 'polling' 'out' ||
+grep_timeout_ 'polling' out ||
{ cleanup_fail_ 'tail did not switch to polling mode'; }
# Recreate the dir, must get a message about recreation
mkdir dir && touch dir/file || framework_failure_
-grep_timeout_ 'appeared' 'out' ||
+grep_timeout_ 'appeared' out ||
{ cleanup_fail_ 'previously removed file did not appear'; }
cleanup_
@@ -85,9 +81,11 @@ cleanup_
# Expected result for the whole process
cat <<\EOF > exp || framework_failure_
inotify
+tail: using notification mode
tail: 'dir/file' has become inaccessible: No such file or directory
tail: directory containing watched file was removed
tail: inotify cannot be used, reverting to polling
+tail: using polling mode
tail: 'dir/file' has appeared; following new file
EOF
diff --git a/tests/tail/inotify-hash-abuse.sh b/tests/tail/inotify-hash-abuse.sh
index a7c3ac822..cd5256fe1 100755
--- a/tests/tail/inotify-hash-abuse.sh
+++ b/tests/tail/inotify-hash-abuse.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise an abort-inducing flaw in inotify-enabled tail -F.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/inotify-hash-abuse2.sh b/tests/tail/inotify-hash-abuse2.sh
index a447be975..590ff24ac 100755
--- a/tests/tail/inotify-hash-abuse2.sh
+++ b/tests/tail/inotify-hash-abuse2.sh
@@ -2,7 +2,7 @@
# Exercise an abort-inducing flaw in inotify-enabled tail -F.
# Like inotify-hash-abuse, but without a hard-coded "9".
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/inotify-only-regular.sh b/tests/tail/inotify-only-regular.sh
index f1b63b3d8..d6f9281b0 100755
--- a/tests/tail/inotify-only-regular.sh
+++ b/tests/tail/inotify-only-regular.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that tail -f only uses inotify for regular files or fifos
-# Copyright (C) 2017-2025 Free Software Foundation, Inc.
+# Copyright (C) 2017-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,8 +19,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ tail
-grep '^#define HAVE_INOTIFY 1' "$CONFIG_HEADER" >/dev/null \
- || skip_ 'inotify support required'
+require_inotify_supported_
require_strace_ 'inotify_add_watch'
diff --git a/tests/tail/inotify-race.sh b/tests/tail/inotify-race.sh
index bb7df48f9..714cfe53a 100755
--- a/tests/tail/inotify-race.sh
+++ b/tests/tail/inotify-race.sh
@@ -5,7 +5,7 @@
# indefinitely if no *other* data is appended, but it would be printed as
# soon as any additional appended data is detected.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,9 +22,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ tail sleep
-
-grep '^#define HAVE_INOTIFY 1' "$CONFIG_HEADER" >/dev/null && is_local_dir_ . \
- || skip_ 'inotify is not supported'
+require_inotify_supported_
# Terminate any background gdb/tail process
cleanup_() {
diff --git a/tests/tail/inotify-race2.sh b/tests/tail/inotify-race2.sh
index 3db0cc74c..de2ea915c 100755
--- a/tests/tail/inotify-race2.sh
+++ b/tests/tail/inotify-race2.sh
@@ -4,7 +4,7 @@
# are established in tail_forever_inotify. That new file would be ignored
# indefinitely.
-# Copyright (C) 2015-2025 Free Software Foundation, Inc.
+# Copyright (C) 2015-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,9 +21,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ tail sleep
-
-grep '^#define HAVE_INOTIFY 1' "$CONFIG_HEADER" >/dev/null && is_local_dir_ . \
- || skip_ 'inotify is not supported'
+require_inotify_supported_
# Terminate any background gdb/tail process
cleanup_() {
diff --git a/tests/tail/inotify-rotate-resources.sh b/tests/tail/inotify-rotate-resources.sh
index 26d1dedcd..066fdea30 100755
--- a/tests/tail/inotify-rotate-resources.sh
+++ b/tests/tail/inotify-rotate-resources.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that tail -F doesn't leak inotify resources
-# Copyright (C) 2015-2025 Free Software Foundation, Inc.
+# Copyright (C) 2015-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/inotify-rotate.sh b/tests/tail/inotify-rotate.sh
index d113da0ce..3e8bc63f1 100755
--- a/tests/tail/inotify-rotate.sh
+++ b/tests/tail/inotify-rotate.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that tail -F handles rotation
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/overlay-headers.sh b/tests/tail/overlay-headers.sh
index 7b61cbcf8..52c7da056 100755
--- a/tests/tail/overlay-headers.sh
+++ b/tests/tail/overlay-headers.sh
@@ -2,7 +2,7 @@
# inotify-based tail would output redundant headers for
# overlapping inotify events while it was suspended
-# Copyright (C) 2017-2025 Free Software Foundation, Inc.
+# Copyright (C) 2017-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,6 +20,8 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ tail sleep
+setsid true || skip_ 'setsid required to control groups'
+
# Function to count number of lines from tail
# while ignoring transient errors due to resource limits
countlines_ ()
@@ -38,11 +40,12 @@ wait4lines_ ()
}
# Speedup the non inotify case
-fastpoll='---dis -s.1 --max-unchanged-stats=1'
+fastpoll='-s.1 --max-unchanged-stats=1'
# Terminate any background tail process
cleanup_() {
- kill $pid 2>/dev/null && wait $pid;
+ kill -CONT $pid 2>/dev/null
+ kill $pid 2>/dev/null && wait $pid
kill $sleep 2>/dev/null && wait $sleep
}
@@ -52,16 +55,36 @@ echo start > file2 || framework_failure_
# Use this as a way to gracefully terminate tail
env sleep 60 & sleep=$!
-timeout 60 tail $fastpoll --pid=$sleep -f file1 file2 > out & pid=$!
+# Note don't use timeout(1) here as it currently
+# does not propagate SIGCONT.
+# Note use setsid here to ensure we're in a separate process group
+# as we're going to STOP this tail process, and this can trigger
+# the kernel to send SIGHUP to a group if other tests have
+# processes that are reparented. (See tests/timeout/timeout.sh).
+setsid tail $fastpoll --pid=$sleep -f file1 file2 > out & pid=$!
+# Ensure tail is running
kill -0 $pid || fail=1
+# Ensure SIGCONT is supported
+kill -CONT $pid || framework_failure_
+
# Wait for 5 initial lines
retry_delay_ wait4lines_ .1 6 5 || fail=1
# Suspend tail so single read() caters for multiple inotify events
kill -STOP $pid || fail=1
+wait4stopped_() {
+ local delay=$1
+ case $(ps -o state= -p "$pid" 2>/dev/null) in
+ T*) return 0 ;;
+ *) sleep $delay; return 1 ;;
+ esac
+}
+
+retry_delay_ wait4stopped_ .1 6 || skip_ 'failed to detect stopped tail'
+
# Interleave writes to files to generate overlapping inotify events
echo line >> file1 || framework_failure_
echo line >> file2 || framework_failure_
@@ -76,6 +99,6 @@ retry_delay_ wait4lines_ .1 6 13 || fail=1
kill $sleep && wait || framework_failure_
-test "$(countlines_)" = 13 || fail=1
+test "$(countlines_)" = 13 || { cat out; fail=1; }
Exit $fail
diff --git a/tests/tail/pid-pipe.sh b/tests/tail/pid-pipe.sh
index 61df3be8c..b4a964e3a 100755
--- a/tests/tail/pid-pipe.sh
+++ b/tests/tail/pid-pipe.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that "tail --pid=PID fifo" exits responsively
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,16 +21,55 @@ print_ver_ tail
mkfifo_or_skip_ fifo
+
# Terminate any background process
-cleanup_() { kill $pid 2>/dev/null && wait $pid; }
+cleanup_()
+{
+ for p in $pid $writer_pid; do
+ kill $p 2>/dev/null
+ done
+ for p in $pid $writer_pid; do
+ wait $p 2>/dev/null
+ done
+
+ pid=
+ writer_pid=
+}
# Speedup the non inotify case
fastpoll='-s.1 --max-unchanged-stats=1'
+
+# Ensure an absent FIFO writer doesn't block tail from checking --pid.
sleep 1 & pid=$!
+returns_ 124 timeout 10 tail -f $fastpoll --pid=$pid fifo && fail=1
+cleanup_
+
-returns_ 124 timeout 10 tail -f $fastpoll --pid=$! fifo && fail=1
+# Ensure a silent FIFO writer doesn't block tail from checking --pid.
+rm -f writer-ready || framework_failure_
+writer_ready_()
+{
+ sleep $1
+ test -e writer-ready
+}
+
+# Simulate a writer that may wait a long time between writes
+silent_writer() {
+ exec 3>fifo &&
+ touch writer-ready &&
+ exec sleep 20
+}
+silent_writer & writer_pid=$!
+
+# allow fifo to open
+timeout 10 $SHELL -c ': < fifo' || framework_failure_
+retry_delay_ writer_ready_ .1 6 || framework_failure_
+
+sleep 1 & pid=$!
+returns_ 124 timeout 10 tail -f $fastpoll --pid=$pid fifo && fail=1
cleanup_
+
Exit $fail
diff --git a/tests/tail/pid.sh b/tests/tail/pid.sh
index 8a50b8704..3c9a8d08c 100755
--- a/tests/tail/pid.sh
+++ b/tests/tail/pid.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test the --pid option of tail.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/pipe-f.sh b/tests/tail/pipe-f.sh
index 0819c5063..eeaa92b60 100755
--- a/tests/tail/pipe-f.sh
+++ b/tests/tail/pipe-f.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that tail -f doesn't hang in various cases
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -41,8 +41,8 @@ compare exp out || fail=1
# Also check tail exits if SIGPIPE is being ignored.
# Note 'trap - SIGPIPE' is ineffective if the initiating shell
# has ignored SIGPIPE, but that's not the normal case.
-case $host_triplet in
- *aix*) echo 'avoiding due to no way to detect closed outputs on AIX' ;;
+case $host_os in
+ aix*) echo 'avoiding due to no way to detect closed outputs on AIX' ;;
*)
for disposition in '' '-'; do
(trap "$disposition" PIPE;
diff --git a/tests/tail/pipe-f2.sh b/tests/tail/pipe-f2.sh
index 613b5a770..6d26c422a 100755
--- a/tests/tail/pipe-f2.sh
+++ b/tests/tail/pipe-f2.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that "tail -f fifo" tails indefinitely.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/proc-ksyms.sh b/tests/tail/proc-ksyms.sh
index f92512c87..472029302 100755
--- a/tests/tail/proc-ksyms.sh
+++ b/tests/tail/proc-ksyms.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Prior to textutils-2.0.17, 'tail /proc/ksyms' would segfault on Linux.
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/retry.sh b/tests/tail/retry.sh
index b5e8b8cae..5ece3a2c4 100755
--- a/tests/tail/retry.sh
+++ b/tests/tail/retry.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Exercise tail's behavior regarding missing files with/without --retry.
-# Copyright (C) 2013-2025 Free Software Foundation, Inc.
+# Copyright (C) 2013-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/start-middle.sh b/tests/tail/start-middle.sh
index 3ad3d8553..ff1534db7 100755
--- a/tests/tail/start-middle.sh
+++ b/tests/tail/start-middle.sh
@@ -2,7 +2,7 @@
# Verify that tail works even when it's reading from a file
# that is not at its beginning. Based on a report from John Roll.
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/symlink.sh b/tests/tail/symlink.sh
index 532fbd345..1044adaf9 100755
--- a/tests/tail/symlink.sh
+++ b/tests/tail/symlink.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure tail tracks symlinks properly.
-# Copyright (C) 2013-2025 Free Software Foundation, Inc.
+# Copyright (C) 2013-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/tail-c.sh b/tests/tail/tail-c.sh
index 9672b9fb6..33058751f 100755
--- a/tests/tail/tail-c.sh
+++ b/tests/tail/tail-c.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# exercise tail -c
-# Copyright 2014-2025 Free Software Foundation, Inc.
+# Copyright 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -49,13 +49,14 @@ if test -r /dev/urandom; then
# Solaris 11 allows negative seek but then gives EINVAL on read
1) grep 'Invalid argument' err || fail=1;;
*)
- case $host_triplet in
- *linux-gnu*)
+ case $host_os in
+ linux-gnu*)
case "$(uname -r)" in
[12].*) ;; # Older Linux versions timeout
*) fail=1 ;;
esac ;;
- *) fail=1 ;;
+ # GNU/Hurd cannot seek on /dev/urandom.
+ *) test "$(uname)" = GNU || fail=1 ;;
esac ;;
esac
fi
diff --git a/tests/tail/tail-n0f.sh b/tests/tail/tail-n0f.sh
index 322412d51..82ffbf675 100755
--- a/tests/tail/tail-n0f.sh
+++ b/tests/tail/tail-n0f.sh
@@ -2,7 +2,7 @@
# Make sure that 'tail -n0 -f' and 'tail -c0 -f' sleep
# rather than doing what amounted to a busy-wait.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/tail-sysfs.sh b/tests/tail/tail-sysfs.sh
index 1387b7f8d..663aa8114 100755
--- a/tests/tail/tail-sysfs.sh
+++ b/tests/tail/tail-sysfs.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# sysfs files have weird properties that can be influenced by page size
-# Copyright 2023-2025 Free Software Foundation, Inc.
+# Copyright 2023-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/tail.pl b/tests/tail/tail.pl
index eeddc6b07..bac1269ff 100755
--- a/tests/tail/tail.pl
+++ b/tests/tail/tail.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test tail.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/truncate.sh b/tests/tail/truncate.sh
index 43ff8a129..d64bfb693 100755
--- a/tests/tail/truncate.sh
+++ b/tests/tail/truncate.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure all logs are output upon file truncation
-# Copyright (C) 2015-2025 Free Software Foundation, Inc.
+# Copyright (C) 2015-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tail/wait.sh b/tests/tail/wait.sh
index 87fe7540f..ec69ff775 100755
--- a/tests/tail/wait.sh
+++ b/tests/tail/wait.sh
@@ -2,7 +2,7 @@
# Make sure that 'tail -f' returns immediately if a file doesn't exist
# while 'tail -F' waits for it to appear.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tee/append.sh b/tests/tee/append.sh
new file mode 100755
index 000000000..87b83ca5b
--- /dev/null
+++ b/tests/tee/append.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+# test the behavior of 'tee --append'
+
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ tee
+
+# Test the short and long option.
+for opt in -a --append; do
+ echo 'line 1' > inp || framework_failure_
+ cat inp > exp || framework_failure_
+ # POSIX says: "Processing of at least 13 file operands shall be supported."
+ files=$(seq 13)
+ # Setup and verify the files.
+ tee $files <inp >out || fail=1
+ for f in out $files; do
+ compare exp $f || fail=1
+ done
+ echo 'line 2' > inp || framework_failure_
+ cat inp >> exp || framework_failure_
+ # Only one -a or --append option is needed, but check that 'tee' behaves the
+ # same with duplicate options.
+ opts=$(for f in $files; do printf -- ' %s %s' $opt $f; done)
+ tee $opts < inp > out || framework_failure_
+ # Standard output should only have the line we appended.
+ compare inp out || fail=1
+ # The files should have line 1 and line 2.
+ for f in $files; do
+ compare exp $f || fail=1
+ done
+done
+
+Exit $fail
diff --git a/tests/misc/tee.sh b/tests/tee/tee.sh
index 5d0f7b339..5c4de6a5e 100755
--- a/tests/misc/tee.sh
+++ b/tests/tee/tee.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test for basic tee functionality.
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,6 +21,17 @@ print_ver_ tee
echo line >sample || framework_failure_
+# Ensure tee detects EOF correctly
+printf "1\n2\n" > exp || framework_failure_
+(echo 1;sleep .1;echo 2) | tee > out || fail=1
+compare exp out || fail=1
+
+# Ensure tee is not buffered
+tee_output() { sleep $1; test -s tee_output; }
+{ printf 'a'; retry_delay_ tee_output .1 7 || touch no_output; } |
+ tee > tee_output
+test -e no_output && fail=1;
+
# POSIX says: "Processing of at least 13 file operands shall be supported."
for n in 0 1 2 12 13; do
files=$(seq $n)
@@ -63,8 +74,8 @@ if test -w /dev/full && test -c /dev/full; then
test $(wc -l < err) = 1 || { cat err; fail=1; }
fi
-case $host_triplet in
- *aix*) echo 'avoiding due to no way to detect closed outputs on AIX' ;;
+case $host_os in
+ aix*) echo 'avoiding due to no way to detect closed outputs on AIX' ;;
*)
# Test iopoll-powered early exit for closed pipes
tee_exited() { sleep $1; test -f tee.exited; }
@@ -97,7 +108,7 @@ read_fifo_delayed & pid=$!
dd count=20 bs=100K if=/dev/zero status=none |
{
dd count=0 oflag=nonblock status=none
- tee || { cleanup_; touch tee.fail; }
+ timeout 10 tee || { cleanup_; touch tee.fail; }
} >fifo
test -f tee.fail && fail=1 || cleanup_
diff --git a/tests/test/test-N.sh b/tests/test/test-N.sh
index e151b40b1..91b68cba9 100755
--- a/tests/test/test-N.sh
+++ b/tests/test/test-N.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test 'test -N file'.
-# Copyright (C) 2018-2025 Free Software Foundation, Inc.
+# Copyright (C) 2018-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/test/test-diag.pl b/tests/test/test-diag.pl
index 05b4f2ed5..b9929d9c5 100755
--- a/tests/test/test-diag.pl
+++ b/tests/test/test-diag.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test the diagnostics of "test".
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/test/test-file.sh b/tests/test/test-file.sh
index 0bc917348..2732c0fd0 100755
--- a/tests/test/test-file.sh
+++ b/tests/test/test-file.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test test's file related options
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -37,4 +37,58 @@ returns_ 1 env test -c file || fail=1
returns_ 1 env test -b file || fail=1
returns_ 1 env test -p file || fail=1
+# Check that 'test' returns true if the file name following "-nt" does not
+# resolve.
+env test file -nt missing || fail=1
+returns_ 1 env test missing -nt file || fail=1
+
+# Check that 'test' returns true if the file name preceding "-ot" does not
+# resolve.
+env test missing -ot file || fail=1
+returns_ 1 env test file -ot missing || fail=1
+
+# Create two files with modification times an hour apart.
+t1='2025-10-23 03:00'
+t2='2025-10-23 04:00'
+touch -m -d "$t1" file1 || framework_failure_
+touch -m -d "$t2" file2 || framework_failure_
+
+# Test "-nt" on two existing files.
+env test file2 -nt file1 || fail=1
+returns_ 1 env test file1 -nt file2 || fail=1
+
+# Test "-ot" on two existing files.
+env test file1 -ot file2 || fail=1
+returns_ 1 env test file2 -ot file1 || fail=1
+
+# Test "-ef" on files that do not resolve.
+returns_ 1 env test missing1 -ef missing2 || fail=1
+returns_ 1 env test missing2 -ef missing1 || fail=1
+returns_ 1 env test file1 -ef missing1 || fail=1
+returns_ 1 env test missing1 -ef file1 || fail=1
+
+# Test "-ef" on normal files.
+env test file1 -ef file1 || fail=1
+returns_ 1 env test file1 -ef file2 || fail=1
+
+# Test "-ef" on symbolic links.
+ln -s file1 symlink1 || framework_failure_
+ln -s file2 symlink2 || framework_failure_
+env test file1 -ef symlink1 || fail=1
+env test symlink1 -ef file1 || fail=1
+returns_ 1 env test file1 -ef symlink2 || fail=1
+returns_ 1 env test symlink2 -ef file1 || fail=1
+returns_ 1 env test symlink1 -ef symlink2 || fail=1
+returns_ 1 env test symlink2 -ef symlink1 || fail=1
+
+# Test "-ef" on hard links.
+if ln file1 hardlink1 && ln file2 hardlink2; then
+ env test file1 -ef hardlink1 || fail=1
+ env test hardlink1 -ef file1 || fail=1
+ returns_ 1 env test file1 -ef hardlink2 || fail=1
+ returns_ 1 env test hardlink2 -ef file1 || fail=1
+ returns_ 1 env test hardlink1 -ef hardlink2 || fail=1
+ returns_ 1 env test hardlink2 -ef hardlink1 || fail=1
+fi
+
Exit $fail
diff --git a/tests/test/test.pl b/tests/test/test.pl
index 3e016819f..1de18ba7e 100755
--- a/tests/test/test.pl
+++ b/tests/test/test.pl
@@ -1,6 +1,6 @@
#!/usr/bin/perl
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -161,6 +161,7 @@ my @Tests =
['eq-4', qw(8 -eq 9), {EXIT=>1}],
['eq-5', qw(1 -eq 0), {EXIT=>1}],
['eq-6', "$limits->{UINTMAX_OFLOW} -eq 0", {EXIT=>1}],
+ ['eq-7', qw(0 -eq ' 0 ')],
['gt-1', qw(5 -gt 5), {EXIT=>1}],
['gt-2', qw(5 -gt 4)],
diff --git a/tests/timeout/init-parent.sh b/tests/timeout/init-parent.sh
new file mode 100755
index 000000000..765927aab
--- /dev/null
+++ b/tests/timeout/init-parent.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+# Test the behavior of 'timeout' with the init process as its parent.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ timeout
+
+newpids () { unshare --pid --fork --map-root-user "$@"; }
+
+newpids true || skip_ 'unshare --pid --fork --map-root-user is unavailable'
+
+# In coreutils-9.10 'timeout' would exit immediately if its parent was the init
+# process. This was discovered because Docker entrypoints have a process ID
+# of 1.
+newpids timeout .1 sleep 10
+test $? = 124 || fail=1
+
+Exit $fail
diff --git a/tests/timeout/timeout-blocked.pl b/tests/timeout/timeout-blocked.pl
index 64f11fbe8..56dce6e4c 100755
--- a/tests/timeout/timeout-blocked.pl
+++ b/tests/timeout/timeout-blocked.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test that timeout handles blocked SIGALRM from its parent.
-# Copyright (C) 2013-2025 Free Software Foundation, Inc.
+# Copyright (C) 2013-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/timeout/timeout-group.sh b/tests/timeout/timeout-group.sh
index f73e6fb7c..959cb6452 100755
--- a/tests/timeout/timeout-group.sh
+++ b/tests/timeout/timeout-group.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# test program group handling
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -17,7 +17,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
-print_ver_ timeout
+print_ver_ timeout env
require_trap_signame_
require_kill_group_
@@ -26,18 +26,19 @@ require_kill_group_
# group.sh - separate group
# timeout.cmd - same group as group.sh
#
-# We then send a SIGINT to the "separate group"
-# to simulate what happens when a Ctrl-C
+# We then send a SIGUSR1 to the "separate group"
+# to simulate what happens when a terminating signal
# is sent to the foreground group.
setsid true || skip_ "setsid required to control groups"
printf '%s\n' '#!'"$SHELL" > timeout.cmd || framework_failure_
cat >> timeout.cmd <<\EOF
-trap 'touch int.received; exit' INT
+trap 'touch sig.received; exit' USR1
+trap
touch timeout.running
count=$1
-until test -e int.received || test $count = 0; do
+until test -e sig.received || test $count = 0; do
sleep 1
count=$(expr $count - 1)
done
@@ -46,9 +47,25 @@ chmod a+x timeout.cmd
cat > group.sh <<EOF
#!$SHELL
-trap '' INT
-timeout --foreground 25 ./timeout.cmd 20&
+
+# trap '' ensures this script ignores the signal,
+# so that the 'wait' below is not interrupted.
+# Note this then requires env --default... to reset
+# the signal disposition so that 'timeout' handles it.
+# Alternatively one could use trap ':' USR1
+# and then handle the retry in wait like:
+# while wait; test \$? -gt 128; do :; done
+# Note also INT and QUIT signals are special for backgrounded
+# processes like this in shell as they're auto ignored
+# and can't be reset with trap to any other disposition.
+# Therefore we use the ignored signal method so any
+# termination signal can be used.
+trap '' USR1
+
+env --default-signal=USR1 \
+timeout -v --foreground 25 ./timeout.cmd 20&
wait
+echo group.sh wait returned \$ret
EOF
chmod a+x group.sh
@@ -59,6 +76,13 @@ check_timeout_cmd_running()
{ sleep $delay; return 1; }
}
+check_timeout_cmd_exiting()
+{
+ local delay="$1"
+ test -e sig.received ||
+ { sleep $delay; return 1; }
+}
+
# Terminate any background processes
cleanup_() { kill $pid 2>/dev/null && wait $pid; }
@@ -68,12 +92,23 @@ setsid ./group.sh & pid=$!
# Wait 6.3s for timeout.cmd to start
retry_delay_ check_timeout_cmd_running .1 6 || fail=1
# Simulate a Ctrl-C to the group to test timely exit
-kill -INT -- -$pid
+kill -USR1 -- -$pid
wait
-test -e int.received || fail=1
-
-rm -f int.received timeout.running
-
+test -e sig.received || fail=1
+rm -f sig.received timeout.running
+
+# On Linux ensure we kill the monitored command
+# even if we're terminated abnormally (e.g., get SIGKILL).
+if grep '^#define HAVE_PRCTL 1' "$CONFIG_HEADER" >/dev/null; then
+ timeout -sUSR1 25 ./timeout.cmd 20 & pid=$!
+ # Wait 6.3s for timeout.cmd to start
+ retry_delay_ check_timeout_cmd_running .1 6 || fail=1
+ kill -KILL -- $pid
+ wait
+ # Wait 6.3s for timeout.cmd to exit
+ retry_delay_ check_timeout_cmd_exiting .1 6 || fail=1
+ rm -f sig.received timeout.running
+fi
# Ensure cascaded timeouts work
# or more generally, ensure we timeout
@@ -84,8 +119,8 @@ start=$(date +%s)
# Note the first timeout must send a signal that
# the second is handling for it to be propagated to the command.
-# SIGINT, SIGTERM, SIGALRM etc. are implicit.
-timeout -sALRM 30 timeout -sINT 25 ./timeout.cmd 20 & pid=$!
+# termination signals are implicitly handled unless ignored.
+timeout -sALRM 30 timeout -sUSR1 25 ./timeout.cmd 20 & pid=$!
# Wait 6.3s for timeout.cmd to start
retry_delay_ check_timeout_cmd_running .1 6 || fail=1
kill -ALRM $pid # trigger the alarm of the first timeout command
@@ -93,7 +128,7 @@ wait $pid
ret=$?
test $ret -eq 124 ||
skip_ "timeout returned $ret. SIGALRM not handled?"
-test -e int.received || fail=1
+test -e sig.received || fail=1
end=$(date +%s)
diff --git a/tests/timeout/timeout-large-parameters.sh b/tests/timeout/timeout-large-parameters.sh
index 5669810e8..f5c2bf3b0 100755
--- a/tests/timeout/timeout-large-parameters.sh
+++ b/tests/timeout/timeout-large-parameters.sh
@@ -2,7 +2,7 @@
# Validate large timeout parameters
# Separated from standard parameter testing due to kernel overflow bugs.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/timeout/timeout-parameters.sh b/tests/timeout/timeout-parameters.sh
index c4b2202b4..9303fecf0 100755
--- a/tests/timeout/timeout-parameters.sh
+++ b/tests/timeout/timeout-parameters.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Validate timeout parameter combinations
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/timeout/timeout.sh b/tests/timeout/timeout.sh
index e3defdd4e..f0591073b 100755
--- a/tests/timeout/timeout.sh
+++ b/tests/timeout/timeout.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Validate timeout basic operation
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -56,13 +56,18 @@ returns_ 124 timeout --foreground -s0 -k1 .1 sleep 10 && fail=1
) || fail=1
# Don't be confused when starting off with a child (Bug#9098).
-out=$(sleep .1 & exec timeout .5 sh -c 'sleep 2; echo foo')
-status=$?
-test "$out" = "" && test $status = 124 || fail=1
+# Use setsid to avoid sleep being in the test's process group, as
+# upon reparenting it can trigger an orphaned process group SIGHUP
+# (if there were stopped processes like in tests/tail/overlay-headers.sh).
+if setsid true; then
+ out=$(setsid sleep .1 & exec timeout .5 sh -c 'sleep 2; echo foo')
+ status=$?
+ test "$out" = "" && test $status = 124 || fail=1
+fi
# Verify --verbose output
cat > exp <<\EOF
-timeout: sending signal EXIT to command 'sleep'
+timeout: sending signal 0 to command 'sleep'
timeout: sending signal KILL to command 'sleep'
EOF
for opt in -v --verbose; do
@@ -71,4 +76,15 @@ for opt in -v --verbose; do
compare exp err || fail=1
done
+# Ensure we propagate all terminating signals.
+# Specifically here we're testing that SIGPIPE is handled.
+# I.e., that we're not killed by the SIGPIPE (and leave the sleep running).
+# timeout would exit with 141 usually if SIGPIPE wasn't being handled.
+echo 124 > timeout.exp || framework_failure_
+{ timeout -v .1 sleep 10 2>&1; echo $? >timeout.status; } | :
+compare timeout.exp timeout.status || fail=1
+# Ensure we don't catch/propagate ignored signals
+(trap '' PIPE && timeout 10 yes |:) 2>&1 |
+ grep 'Broken pipe' >/dev/null || fail=1
+
Exit $fail
diff --git a/tests/touch/60-seconds.sh b/tests/touch/60-seconds.sh
index 7edeb6592..14f2b6eb0 100755
--- a/tests/touch/60-seconds.sh
+++ b/tests/touch/60-seconds.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# touch -t would mistakenly reject a time specifying "60" seconds
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/touch/dangling-symlink.sh b/tests/touch/dangling-symlink.sh
index c103d5f58..e57e2641e 100755
--- a/tests/touch/dangling-symlink.sh
+++ b/tests/touch/dangling-symlink.sh
@@ -2,7 +2,7 @@
# Make sure touch can create a file through a dangling symlink.
# This was broken in the 4.0[e-i] test releases.
-# Copyright (C) 1999-2025 Free Software Foundation, Inc.
+# Copyright (C) 1999-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -30,8 +30,8 @@ test -f touch-target || fail=1
rm -f touch-target t-symlink
if test $fail = 1; then
- case $host_triplet in
- *linux-gnu*)
+ case $host_os in
+ linux-gnu*)
case "$(uname -r)" in
2.3.9[0-9]*)
skip_ \
diff --git a/tests/touch/empty-file.sh b/tests/touch/empty-file.sh
index 7ff239f17..5e0a3ecdb 100755
--- a/tests/touch/empty-file.sh
+++ b/tests/touch/empty-file.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure touch can set the mtime on an empty file.
-# Copyright (C) 1998-2025 Free Software Foundation, Inc.
+# Copyright (C) 1998-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/touch/fail-diag.sh b/tests/touch/fail-diag.sh
index e8933ff6d..0c792e405 100755
--- a/tests/touch/fail-diag.sh
+++ b/tests/touch/fail-diag.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# make sure touch gives reasonable diagnostics
-# Copyright (C) 2001-2025 Free Software Foundation, Inc.
+# Copyright (C) 2001-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/touch/fifo.sh b/tests/touch/fifo.sh
index d9f7704ac..2f8a30cf9 100755
--- a/tests/touch/fifo.sh
+++ b/tests/touch/fifo.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure touch works on fifos without hanging.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/touch/no-create-missing.sh b/tests/touch/no-create-missing.sh
index d35c76c4c..ff2a0da35 100755
--- a/tests/touch/no-create-missing.sh
+++ b/tests/touch/no-create-missing.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that touch -c no-such-file no longer fails (it did in 4.1.8).
-# Copyright (C) 2002-2025 Free Software Foundation, Inc.
+# Copyright (C) 2002-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/touch/no-dereference.sh b/tests/touch/no-dereference.sh
index 0a5f674fe..85ca8ccf5 100755
--- a/tests/touch/no-dereference.sh
+++ b/tests/touch/no-dereference.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that touch -h works.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/touch/no-rights.sh b/tests/touch/no-rights.sh
index 7751d1a0a..3d5b626ab 100755
--- a/tests/touch/no-rights.sh
+++ b/tests/touch/no-rights.sh
@@ -2,7 +2,7 @@
# Make sure touch can update the times on a file that is neither
# readable nor writable.
-# Copyright (C) 1999-2025 Free Software Foundation, Inc.
+# Copyright (C) 1999-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/touch/not-owner.sh b/tests/touch/not-owner.sh
index 4e4739821..9e3a23d09 100755
--- a/tests/touch/not-owner.sh
+++ b/tests/touch/not-owner.sh
@@ -2,7 +2,7 @@
# Make sure that touch gives reasonable diagnostics when applied
# to an unwritable directory owned by some other user.
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
+# Copyright (C) 2003-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ touch
+getlimits_
if env -- test -w /; then
skip_ you have write access to /.
@@ -36,12 +37,9 @@ skip_if_root_
# touch: creating '/': Is a directory
touch / > out 2>&1 && fail=1
-# On SunOS4, EPERM is 'Not owner'.
-# On some *BSD systems it's 'Operation not permitted'.
# On a system where root file system is mounted read-only
# it's 'Read-only file system'.
-for msg in 'Not owner' 'Operation not permitted' 'Permission denied' \
- 'Read-only file system'; do
+for msg in "$EACCES" "$EPERM" "$EROFS"; do
cat > exp <<EOF
touch: setting times of '/': $msg
EOF
diff --git a/tests/touch/now-owned-by-other.sh b/tests/touch/now-owned-by-other.sh
index a561a9769..68fc6edb5 100755
--- a/tests/touch/now-owned-by-other.sh
+++ b/tests/touch/now-owned-by-other.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Demonstrate that "touch -d now writable-but-owned-by-other" works.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/touch/obsolescent.sh b/tests/touch/obsolescent.sh
index 6f8ac29f4..40907935d 100755
--- a/tests/touch/obsolescent.sh
+++ b/tests/touch/obsolescent.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test touch with obsolescent 8- or 10-digit timestamps.
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/touch/read-only.sh b/tests/touch/read-only.sh
index c4beffcc3..8853eb786 100755
--- a/tests/touch/read-only.sh
+++ b/tests/touch/read-only.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# ensure that touch can operate on read-only files
-# Copyright (C) 2005-2025 Free Software Foundation, Inc.
+# Copyright (C) 2005-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/touch/relative.sh b/tests/touch/relative.sh
index bf2386528..1e640d540 100755
--- a/tests/touch/relative.sh
+++ b/tests/touch/relative.sh
@@ -2,7 +2,7 @@
# Demonstrate using a combination of --reference and --date to
# set the time of a file back by an arbitrary amount.
-# Copyright (C) 2004-2025 Free Software Foundation, Inc.
+# Copyright (C) 2004-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/touch/trailing-slash.sh b/tests/touch/trailing-slash.sh
index 1a2f1ff01..bf9644235 100755
--- a/tests/touch/trailing-slash.sh
+++ b/tests/touch/trailing-slash.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that touch honors trailing slash.
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tr/tr-case-class.sh b/tests/tr/tr-case-class.sh
index 779b94d3d..89111836b 100755
--- a/tests/tr/tr-case-class.sh
+++ b/tests/tr/tr-case-class.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test case conversion classes
-# Copyright (C) 2010-2025 Free Software Foundation, Inc.
+# Copyright (C) 2010-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tr/tr.pl b/tests/tr/tr.pl
index 340fee37c..dc37e5c19 100755
--- a/tests/tr/tr.pl
+++ b/tests/tr/tr.pl
@@ -1,6 +1,6 @@
#!/usr/bin/perl
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -158,6 +158,10 @@ my @Tests =
# Up to coreutils-6.9, this would provoke a failed assertion.
['no-abort-1', qw(-c a '[b*256]'), {IN=>'abc'}, {OUT=>'abb'}],
+
+ # Reject unknown character class name.
+ ['invalid-class', qw('[:fooclass:]' x), {IN=>'abc'}, {OUT=>''}, {EXIT=>1},
+ {ERR=>"$prog: invalid character class 'fooclass'\n"}],
);
@Tests = triple_test \@Tests;
diff --git a/tests/truncate/multiple-files.sh b/tests/truncate/multiple-files.sh
new file mode 100755
index 000000000..a079b66ad
--- /dev/null
+++ b/tests/truncate/multiple-files.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+# Test that 'truncate' processes all arguments.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ truncate
+getlimits_
+
+returns_ 1 truncate -s0 a . b > out 2> err || fail=1
+test -f a || fail=1
+test -f b || fail=1
+compare /dev/null out || fail=1
+compare /dev/null err && fail=1
+
+Exit $fail
diff --git a/tests/truncate/truncate-dangling-symlink.sh b/tests/truncate/truncate-dangling-symlink.sh
index 942d82010..474ddcc3f 100755
--- a/tests/truncate/truncate-dangling-symlink.sh
+++ b/tests/truncate/truncate-dangling-symlink.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure truncate can create a file through a dangling symlink.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/truncate/truncate-dir-fail.sh b/tests/truncate/truncate-dir-fail.sh
index 6e0a683f3..977e79df2 100755
--- a/tests/truncate/truncate-dir-fail.sh
+++ b/tests/truncate/truncate-dir-fail.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure truncate fails for a directory.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/truncate/truncate-fail-diag.sh b/tests/truncate/truncate-fail-diag.sh
index 0fda80f8b..7907adc25 100755
--- a/tests/truncate/truncate-fail-diag.sh
+++ b/tests/truncate/truncate-fail-diag.sh
@@ -5,7 +5,7 @@
# open ("missing/", O_CREAT & (O_WRONLY | O_RDWR), ...) -> EISDIR
# open ("missing/file", O_CREAT & (O_WRONLY | O_RDWR), ...) -> ENOENT
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/truncate/truncate-fifo.sh b/tests/truncate/truncate-fifo.sh
index e5047a559..c456c76ff 100755
--- a/tests/truncate/truncate-fifo.sh
+++ b/tests/truncate/truncate-fifo.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Make sure truncate works on fifos without hanging
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/truncate/truncate-no-create-missing.sh b/tests/truncate/truncate-no-create-missing.sh
index 0ae46cb8d..e007b834b 100755
--- a/tests/truncate/truncate-no-create-missing.sh
+++ b/tests/truncate/truncate-no-create-missing.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure that truncate -c no-such-file doesn't fail.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/truncate/truncate-overflow.sh b/tests/truncate/truncate-overflow.sh
index 235ff6098..7fdab4fc4 100755
--- a/tests/truncate/truncate-overflow.sh
+++ b/tests/truncate/truncate-overflow.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Validate truncate integer overflow
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/truncate/truncate-owned-by-other.sh b/tests/truncate/truncate-owned-by-other.sh
index 70a8ac0b1..2bd72ecae 100755
--- a/tests/truncate/truncate-owned-by-other.sh
+++ b/tests/truncate/truncate-owned-by-other.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Demonstrate that "truncate -s0 writable-but-owned-by-other" works.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/truncate/truncate-parameters.sh b/tests/truncate/truncate-parameters.sh
index 50c4f9669..f27348b2c 100755
--- a/tests/truncate/truncate-parameters.sh
+++ b/tests/truncate/truncate-parameters.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Validate truncate parameter combinations
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/truncate/truncate-relative.sh b/tests/truncate/truncate-relative.sh
index 32a20705d..cefcd86bc 100755
--- a/tests/truncate/truncate-relative.sh
+++ b/tests/truncate/truncate-relative.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Validate truncate relative sizes
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/tty/tty-eof.pl b/tests/tty/tty-eof.pl
deleted file mode 100755
index 2ea5ade46..000000000
--- a/tests/tty/tty-eof.pl
+++ /dev/null
@@ -1,118 +0,0 @@
-#!/usr/bin/perl
-# Test whether programs exit upon a single EOF from a tty.
-# Ensure that e.g., cat exits upon a single EOF (^D) from a tty.
-# Do the same for all programs that can read stdin,
-# require no arguments and that write to standard output.
-
-# Copyright (C) 2003-2025 Free Software Foundation, Inc.
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-use strict;
-(my $ME = $0) =~ s|.*/||;
-
-# Some older versions of Expect.pm (e.g. 1.07) lack the log_user method,
-# so check for that, too.
-eval { require Expect; Expect->require_version('1.11') };
-$@
- and CuSkip::skip "$ME: this script requires Perl's Expect package >=1.11\n";
-
-{
- my $fail = 0;
- my @stdin_reading_commands = qw(
- b2sum
- base32
- base64
- cat
- cksum
- dd
- expand
- fmt
- fold
- head
- md5sum
- nl
- od
- paste
- pr
- ptx
- sha1sum
- sha224sum
- sha256sum
- sha384sum
- sha512sum
- shuf
- sort
- sum
- tac
- tail
- tee
- tsort
- unexpand
- uniq
- wc
- );
- my $stderr = 'tty-eof.err';
- foreach my $cmd ((@stdin_reading_commands), 'basenc --z85', 'cut -f2',
- 'numfmt --invalid=ignore')
- {
- my $exp = new Expect;
- $exp->log_user(0);
- my $cmd_name = (split(' ', $cmd))[0];
- $ENV{built_programs} =~ /\b$cmd_name\b/ || next;
- $exp->spawn("$cmd 2> $stderr")
- or (warn "$ME: cannot run '$cmd': $!\n"), $fail=1, next;
- # Test cut in a different mode, even though it supports the standard flow
- # Ensure that it exits with no input as it used to not do so
- $cmd =~ /^cut/
- or $exp->send("a b\n");
- $exp->send("\cD"); # This is Control-D. FIXME: what if that's not EOF?
- $cmd =~ /^cut/
- or $exp->expect (0, '-re', "^a b\\r?\$");
- $cmd =~ /^cut/
- or my $found = $exp->expect (1, '-re', "^.+\$");
- $found and warn "F: $found: " . $exp->exp_match () . "\n";
- $exp->expect(10, 'eof');
- # Expect no output from cut, since we gave it no input.
- defined $found || $cmd =~ /^cut/
- or (warn "$ME: $cmd didn't produce expected output\n"),
- $fail=1, next;
- defined $exp->exitstatus
- or (warn "$ME: $cmd didn't exit after ^D from standard input\n"),
- $fail=1, next;
- my $s = $exp->exitstatus;
- $s == 0
- or (warn "$ME: $cmd exited with status $s (expected 0)\n"),
- $fail=1;
- $exp->hard_close();
-
- # dd normally writes to stderr. If it exits successfully, we're done.
- $cmd eq 'dd' && $s == 0
- and next;
-
- if (-s $stderr)
- {
- warn "$ME: $cmd wrote to stderr:\n";
- system "cat $stderr";
- $fail = 1;
- }
- }
- continue
- {
- unlink $stderr
- or warn "$ME: failed to remove stderr file from $cmd, $stderr: $!\n";
- }
-
- exit $fail
-}
diff --git a/tests/tty/tty.sh b/tests/tty/tty.sh
index b2b8aa25a..e79d41aaf 100755
--- a/tests/tty/tty.sh
+++ b/tests/tty/tty.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test 'tty'.
-# Copyright 2017-2025 Free Software Foundation, Inc.
+# Copyright 2017-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/unexpand/bounded-memory.sh b/tests/unexpand/bounded-memory.sh
new file mode 100755
index 000000000..d16f6c6b1
--- /dev/null
+++ b/tests/unexpand/bounded-memory.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+# Ensure that unexpand uses bounded memory.
+
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ unexpand
+
+vm=$(get_min_ulimit_v_ timeout 10 unexpand /dev/null) ||
+ skip_ 'failed to determine memory limit'
+
+(ulimit -v $(($vm+6000)) \
+ && timeout 0.5 unexpand </dev/zero >/dev/null 2>err)
+ret=$?
+test $ret = 124 || {
+ fail=1
+ cat err
+ echo "unexpand used too much memory" >&2
+}
+
+Exit $fail
diff --git a/tests/unexpand/mb.sh b/tests/unexpand/mb.sh
new file mode 100755
index 000000000..076a1c1ae
--- /dev/null
+++ b/tests/unexpand/mb.sh
@@ -0,0 +1,172 @@
+#!/bin/sh
+
+# Copyright (C) 2012-2015 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ unexpand printf
+getlimits_
+
+test "$LOCALE_FR_UTF8" != none || skip_ "French UTF-8 locale not available"
+export LC_ALL="$LOCALE_FR_UTF8"
+
+#input containing multibyte characters
+cat > in <<\EOF
+1234567812345678123456781
+. . . .
+a b c d
+. . . .
+ä ö ü ß
+. . . .
+ äöü . öüä. ä xx
+EOF
+
+cat > exp <<\EOF
+1234567812345678123456781
+. . . .
+a b c d
+. . . .
+ä ö ü ß
+. . . .
+ äöü . öüä. ä xx
+EOF
+
+unexpand -a < in > out || fail=1
+compare exp out > /dev/null 2>&1 || fail=1
+
+
+#multiple files as an input
+cat >> exp <<\EOF
+1234567812345678123456781
+. . . .
+a b c d
+. . . .
+ä ö ü ß
+. . . .
+ äöü . öüä. ä xx
+EOF
+
+
+unexpand -a ./in ./in > out || fail=1
+compare exp out > /dev/null 2>&1 || fail=1
+
+#test characters with a display width larger than 1
+
+env printf '12345678
+e |ascii(1)
+\u00E9 |composed(1)
+e\u0301 |decomposed(1)
+\u3000 |ideo-space(2)
+\u3000\u3000\u3000\u3000|ideo-space(2) * 4
+\uFF0D |full-hypen(2)
+' > in || framework_failure_
+
+env printf '12345678
+e\t|ascii(1)
+\u00E9\t|composed(1)
+e\u0301\t|decomposed(1)
+\t|ideo-space(2)
+\t|ideo-space(2) * 4
+\uFF0D\t|full-hypen(2)
+' > exp || framework_failure_
+
+unexpand -a < in > out || fail=1
+compare exp out > /dev/null 2>&1 || fail=1
+
+#test input where a blank of width > 1 is not being substituted
+in="$(env printf ' \u3000 ö ü ß')"
+exp='   ö ü ß'
+
+unexpand -a < in > out || fail=1
+compare exp out > /dev/null 2>&1 || fail=1
+
+#non-Unicode characters interspersed between Unicode ones
+env printf '12345678
+ \xFF|
+\xFF |
+ \xFFä|
+ä\xFF |
+ ä\xFF|
+\xFF ä|
+äbcde\xFF |
+' > in || framework_failure_
+
+env printf '12345678
+\t\xFF|
+\xFF\t|
+\t\xFFä|
+ä\xFF\t|
+\tä\xFF|
+\xFF\tä|
+äbcde\xFF\t|
+' > exp || framework_failure_
+
+unexpand -a < in > out || fail=1
+compare exp out > /dev/null 2>&1 || fail=1
+
+#BOM header test 1
+env printf "\xEF\xBB\xBF" > in; cat <<\EOF >> in || framework_failure_
+1234567812345678123456781
+. . . .
+a b c d
+. . . .
+ä ö ü ß
+. . . .
+ äöü . öüä. ä xx
+EOF
+
+env printf "\xEF\xBB\xBF" > exp; cat <<\EOF >> exp || framework_failure_
+1234567812345678123456781
+. . . .
+a b c d
+. . . .
+ä ö ü ß
+. . . .
+ äöü . öüä. ä xx
+EOF
+
+unexpand -a < in > out || fail=1
+compare exp out > /dev/null 2>&1 || fail=1
+
+env printf "\xEF\xBB\xBF" > exp; cat <<\EOF >> exp || framework_failure_
+1234567812345678123456781
+. . . .
+a b c d
+. . . .
+ä ö ü ß
+. . . .
+ äöü . öüä. ä xx
+EOF
+env printf "\xEF\xBB\xBF" >> exp; cat <<\EOF >> exp || framework_failure_
+1234567812345678123456781
+. . . .
+a b c d
+. . . .
+ä ö ü ß
+. . . .
+ äöü . öüä. ä xx
+EOF
+
+unexpand -a ./in ./in > out || fail=1
+compare exp out > /dev/null 2>&1 || fail=1
+
+# Ensure overflow is handed gracefully
+# coreutils v9.11 induced a buffer overflow with mb_mul=4 (or 16).
+for mb_mul in 4 6; do
+ printf ' \n' | unexpand -t $(expr $SIZE_MAX / $mb_mul + 1) 2>err; ret=$?
+ test "$ret" = 1 || test "$ret" = 0 || { cat err; fail=1; }
+done
+
+Exit $fail
diff --git a/tests/misc/unexpand.pl b/tests/unexpand/unexpand.pl
index bb7469cae..d46d12324 100755
--- a/tests/misc/unexpand.pl
+++ b/tests/unexpand/unexpand.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test "unexpand".
-# Copyright (C) 2000-2025 Free Software Foundation, Inc.
+# Copyright (C) 2000-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -93,7 +93,7 @@ my @Tests =
# It is debatable whether this test should require an environment
# setting of e.g., _POSIX2_VERSION=1.
['obs-ovflo', "-$limits->{UINTMAX_OFLOW}", {IN=>''}, {OUT=>''},
- {EXIT => 1}, {ERR => "$prog: tab stop value is too large\n"}],
+ {EXIT => 1}, {ERR => "$prog: tab stop is too large\n"}],
# Test input with backspaces '\b' ('bs1' is the baseline, without \b)
diff --git a/tests/uniq/uniq-collate.sh b/tests/uniq/uniq-collate.sh
index 96a3717d9..92069daf4 100755
--- a/tests/uniq/uniq-collate.sh
+++ b/tests/uniq/uniq-collate.sh
@@ -3,7 +3,7 @@
# items which compared equal with strcoll()
# So ensure we avoid strcoll() for the following cases.
-# Copyright (C) 2020-2025 Free Software Foundation, Inc.
+# Copyright (C) 2020-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/uniq/uniq-perf.sh b/tests/uniq/uniq-perf.sh
index c585f3d79..be8efd6cb 100755
--- a/tests/uniq/uniq-perf.sh
+++ b/tests/uniq/uniq-perf.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# before coreutils-8.10, seq 100000|uniq -f 10000000000 would run for days
-# Copyright (C) 2011-2025 Free Software Foundation, Inc.
+# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/uniq/uniq.pl b/tests/uniq/uniq.pl
index 4f5faf3ed..0df7ec62d 100755
--- a/tests/uniq/uniq.pl
+++ b/tests/uniq/uniq.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Test uniq.
-# Copyright (C) 2008-2025 Free Software Foundation, Inc.
+# Copyright (C) 2008-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -234,6 +234,9 @@ my @Tests =
" - 'separate'\n" .
" - 'both'\n" .
"Try '$prog --help' for more information.\n"}],
+ # Test for read buffer overrun.
+ do { my $longline = "\360\237\230\200" . "A" x 255 . "\n";
+ ['146', '-w256', {IN => $longline x 2}, {OUT => $longline}] },
);
# Locale related tests
diff --git a/tests/wc/wc-cpu.sh b/tests/wc/wc-cpu.sh
index 6ad4f5f9c..8aed6f808 100755
--- a/tests/wc/wc-cpu.sh
+++ b/tests/wc/wc-cpu.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Ensure cpu specific code operates correctly
-# Copyright (C) 2025 Free Software Foundation, Inc.
+# Copyright (C) 2025-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,7 +19,7 @@
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ wc
-GLIBC_TUNABLES='glibc.cpu.hwcaps=-AVX2,-AVX512F' \
+GLIBC_TUNABLES='glibc.cpu.hwcaps=-ASIMD,-AVX2,-AVX512F' \
wc -l --debug /dev/null 2>debug || fail=1
grep 'using.*hardware support' debug && fail=1
@@ -32,7 +32,7 @@ wc_accelerated_no_avx512=$(
wc -l < lines
) || fail=1
wc_base=$(
- GLIBC_TUNABLES='glibc.cpu.hwcaps=-AVX2,-AVX512F' \
+ GLIBC_TUNABLES='glibc.cpu.hwcaps=-ASIMD,-AVX2,-AVX512F' \
wc -l < lines
) || fail=1
diff --git a/tests/wc/wc-files0-from.pl b/tests/wc/wc-files0-from.pl
index 5d803083e..c7b7b39e7 100755
--- a/tests/wc/wc-files0-from.pl
+++ b/tests/wc/wc-files0-from.pl
@@ -2,7 +2,7 @@
# Exercise wc's --files0-from option.
# FIXME: keep this file in sync with tests/du/files0-from.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -23,6 +23,8 @@ use strict;
my $prog = 'wc';
+my $limits = getlimits ();
+
# Turn off localization of executable's output.
@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3;
@@ -36,9 +38,18 @@ my @Tests =
],
# missing input file
- ['missing', '--files0-from=missing', {EXIT=>1},
+ ['missing1', '--files0-from=missing', {EXIT=>1},
{ERR => "$prog: cannot open 'missing' for reading: "
- . "No such file or directory\n"}],
+ . "$limits->{ENOENT}\n"}],
+
+ # Input file listing missing files.
+ ['missing2', '--files0-from=-', '<', {IN=>"missing\0missing\0"}, {EXIT=>1},
+ {OUT=>"0 0 0 total\n"},
+ {ERR => "$prog: missing: $limits->{ENOENT}\n" x 2}],
+
+ # Input file listing duplicate files.
+ ['duplicate1', '--files0-from=-', '<', {IN=>"g\0g\0"}, {AUX=>{g=>''}},
+ {OUT=>"0 0 0 g\n" x 2 . "0 0 0 total\n"}],
# input file name of '-'
['minus-in-stdin', '--files0-from=-', '<', {IN=>{f=>'-'}}, {EXIT=>1},
diff --git a/tests/wc/wc-files0.sh b/tests/wc/wc-files0.sh
index 5243bc18c..8b764bde0 100755
--- a/tests/wc/wc-files0.sh
+++ b/tests/wc/wc-files0.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Show that wc's new --files0-from option works.
-# Copyright (C) 2006-2025 Free Software Foundation, Inc.
+# Copyright (C) 2006-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/wc/wc-nbsp.sh b/tests/wc/wc-nbsp.sh
index 457cb008f..ee64527d0 100755
--- a/tests/wc/wc-nbsp.sh
+++ b/tests/wc/wc-nbsp.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test non breaking space handling
-# Copyright (C) 2019-2025 Free Software Foundation, Inc.
+# Copyright (C) 2019-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/wc/wc-parallel.sh b/tests/wc/wc-parallel.sh
index c414b336c..50106416f 100755
--- a/tests/wc/wc-parallel.sh
+++ b/tests/wc/wc-parallel.sh
@@ -2,7 +2,7 @@
# Ensure that wc prints counts atomically
# so that concurrent processes don't intersperse their output
-# Copyright (C) 2009-2025 Free Software Foundation, Inc.
+# Copyright (C) 2009-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/wc/wc-proc.sh b/tests/wc/wc-proc.sh
index 8d26d569d..59281a69c 100755
--- a/tests/wc/wc-proc.sh
+++ b/tests/wc/wc-proc.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Test wc on /proc and /sys files.
-# Copyright 2014-2025 Free Software Foundation, Inc.
+# Copyright 2014-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/wc/wc-total.sh b/tests/wc/wc-total.sh
index e62840c2c..f4ff080ef 100755
--- a/tests/wc/wc-total.sh
+++ b/tests/wc/wc-total.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# Show that wc's --total option works.
-# Copyright (C) 2022-2025 Free Software Foundation, Inc.
+# Copyright (C) 2022-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/tests/wc/wc.pl b/tests/wc/wc.pl
index 6dd73318a..266f88d55 100755
--- a/tests/wc/wc.pl
+++ b/tests/wc/wc.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
# Basic tests for "wc".
-# Copyright (C) 1997-2025 Free Software Foundation, Inc.
+# Copyright (C) 1997-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by