summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am365
-rw-r--r--src/Makefile.in2065
-rw-r--r--src/base64.c329
-rw-r--r--src/basename.c153
-rw-r--r--src/c99-to-c89.diff118
-rw-r--r--src/cat.c827
-rw-r--r--src/chcon.c586
-rw-r--r--src/chgrp.c214
-rw-r--r--src/chmod.c435
-rw-r--r--src/chown-core.c469
-rw-r--r--src/chown-core.h23
-rw-r--r--src/chown.c246
-rw-r--r--src/chroot.c379
-rw-r--r--src/cksum.c82
-rw-r--r--src/comm.c338
-rw-r--r--src/copy.c3364
-rw-r--r--src/copy.h136
-rw-r--r--src/coreutils-arch.c33
-rw-r--r--src/coreutils-dir.c33
-rw-r--r--src/coreutils-vdir.c33
-rw-r--r--src/coreutils.c206
-rw-r--r--src/cp-hash.c45
-rw-r--r--src/cp-hash.h1
-rw-r--r--src/cp.c1240
-rw-r--r--src/csplit.c819
-rw-r--r--src/cu-progs.mk112
-rw-r--r--src/cut.c825
-rw-r--r--src/date.c448
-rwxr-xr-xsrc/dcgen12
-rw-r--r--src/dd.c1985
-rw-r--r--src/df.c1868
-rw-r--r--src/dircolors.c368
-rw-r--r--src/dircolors.h86
-rw-r--r--src/dircolors.hin90
-rw-r--r--src/dirname.c105
-rw-r--r--src/du.c1189
-rw-r--r--src/echo.c320
-rw-r--r--src/env.c174
-rw-r--r--src/expand.c364
-rw-r--r--src/expr.c577
-rw-r--r--src/extent-scan.c227
-rw-r--r--src/extent-scan.h73
-rw-r--r--src/extract-magic73
-rw-r--r--src/factor.c2646
-rw-r--r--src/fiemap.h107
-rw-r--r--src/find-mount-point.c112
-rw-r--r--src/find-mount-point.h17
-rw-r--r--src/fmt.c392
-rw-r--r--src/fold.c258
-rw-r--r--src/fs-is-local.h121
-rw-r--r--src/fs.h115
-rw-r--r--src/getlimits.c172
-rw-r--r--src/group-list.c123
-rw-r--r--src/group-list.h19
-rw-r--r--src/groups.c142
-rwxr-xr-xsrc/groups.sh83
-rw-r--r--src/head.c1037
-rw-r--r--src/hostid.c38
-rw-r--r--src/hostname.c44
-rw-r--r--src/id.c470
-rw-r--r--src/install.c1111
-rw-r--r--src/ioblksize.h78
-rw-r--r--src/join.c1043
-rw-r--r--src/kill.c285
-rw-r--r--src/libstdbuf.c141
-rw-r--r--src/link.c39
-rw-r--r--src/ln.c606
-rw-r--r--src/local.mk651
-rw-r--r--src/logname.c44
-rw-r--r--src/longlong.h2201
-rw-r--r--src/ls.c4054
-rw-r--r--src/ls.h6
-rw-r--r--src/make-prime-list.c230
-rw-r--r--src/md5sum.c787
-rw-r--r--src/mkdir.c212
-rw-r--r--src/mkfifo.c132
-rw-r--r--src/mknod.c197
-rw-r--r--src/mktemp.c349
-rw-r--r--src/mv.c393
-rw-r--r--src/nice.c159
-rw-r--r--src/nl.c331
-rw-r--r--src/nohup.c182
-rw-r--r--src/nproc.c129
-rw-r--r--src/numfmt.c1650
-rw-r--r--src/od.c1822
-rw-r--r--src/operand2sig.c86
-rw-r--r--src/operand2sig.h18
-rw-r--r--src/paste.c535
-rw-r--r--src/pathchk.c303
-rw-r--r--src/pinky.c294
-rw-r--r--src/pr.c1821
-rw-r--r--src/primes.h4014
-rw-r--r--src/printenv.c116
-rw-r--r--src/printf.c587
-rw-r--r--src/prog-fprintf.c37
-rw-r--r--src/prog-fprintf.h25
-rw-r--r--src/ptx.c1752
-rw-r--r--src/pwd.c211
-rw-r--r--src/readlink.c150
-rw-r--r--src/realpath.c277
-rw-r--r--src/relpath.c133
-rw-r--r--src/relpath.h25
-rw-r--r--src/remove.c1779
-rw-r--r--src/remove.h27
-rw-r--r--src/rm.c324
-rw-r--r--src/rmdir.c165
-rw-r--r--src/runcon.c263
-rw-r--r--src/selinux.c340
-rw-r--r--src/selinux.h49
-rw-r--r--src/seq.c551
-rw-r--r--src/set-fields.c322
-rw-r--r--src/set-fields.h47
-rw-r--r--src/setuidgid.c129
-rw-r--r--src/shred.c1002
-rw-r--r--src/shuf.c566
-rw-r--r--src/single-binary.mk487
-rw-r--r--src/sleep.c63
-rw-r--r--src/sort.c4451
-rw-r--r--src/split.c1640
-rw-r--r--src/stat.c1400
-rw-r--r--src/stdbuf.c393
-rw-r--r--src/stty.c1579
-rw-r--r--src/su.c526
-rw-r--r--src/sum.c124
-rw-r--r--src/sync.c208
-rw-r--r--src/system.h570
-rw-r--r--src/tac-pipe.c95
-rw-r--r--src/tac.c622
-rw-r--r--src/tail.c2086
-rw-r--r--src/tee.c210
-rw-r--r--src/test.c423
-rw-r--r--src/timeout.c506
-rw-r--r--src/touch.c353
-rw-r--r--src/tr.c1507
-rw-r--r--src/true.c52
-rw-r--r--src/truncate.c398
-rw-r--r--src/tsort.c476
-rw-r--r--src/tty.c49
-rw-r--r--src/uname-arch.c2
-rw-r--r--src/uname-uname.c2
-rw-r--r--src/uname.c302
-rw-r--r--src/uname.h7
-rw-r--r--src/unexpand.c466
-rw-r--r--src/uniq.c575
-rw-r--r--src/unlink.c35
-rw-r--r--src/uptime.c124
-rw-r--r--src/users.c51
-rw-r--r--src/wc.c801
-rwxr-xr-xsrc/wheel-gen.pl115
-rw-r--r--src/wheel-size.h1
-rw-r--r--src/wheel.h491
-rw-r--r--src/who.c515
-rw-r--r--src/whoami.c49
-rw-r--r--src/yes.c96
154 files changed, 52764 insertions, 29197 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
deleted file mode 100644
index 863a32b..0000000
--- a/src/Makefile.am
+++ /dev/null
@@ -1,365 +0,0 @@
-## Process this file with automake to produce Makefile.in -*-Makefile-*-
-
-## Copyright (C) 1990, 1991, 1993-2007 Free Software Foundation, Inc.
-
-## This 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 2, 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, write to the Free Software Foundation,
-## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-EXTRA_PROGRAMS = chroot df hostid nice pinky stty su uname uptime users who
-
-bin_SCRIPTS = groups
-bin_PROGRAMS = [ chgrp chown chmod cp dd dircolors du \
- ginstall link ln dir vdir ls mkdir \
- mkfifo mknod mv nohup readlink rm rmdir shred stat sync touch unlink \
- cat cksum comm csplit cut expand fmt fold head join md5sum \
- nl od paste pr ptx sha1sum sha224sum sha256sum sha384sum sha512sum \
- shuf sort split sum tac tail tr tsort unexpand uniq wc \
- basename date dirname echo env expr factor false \
- hostname id kill logname pathchk printenv printf pwd seq sleep tee \
- test true tty whoami yes \
- base64 \
- $(OPTIONAL_BIN_PROGS) $(DF_PROG)
-
-noinst_PROGRAMS = setuidgid
-
-noinst_HEADERS = \
- chown-core.h \
- copy.h \
- cp-hash.h \
- dircolors.h \
- fs.h \
- ls.h \
- remove.h \
- system.h \
- wheel-size.h \
- wheel.h
-
-EXTRA_DIST = dcgen dircolors.hin tac-pipe.c \
- groups.sh wheel-gen.pl extract-magic c99-to-c89.diff
-BUILT_SOURCES =
-CLEANFILES = $(SCRIPTS) su
-
-AM_CPPFLAGS = -I$(top_srcdir)/lib
-
-# Sometimes, the expansion of $(LIBINTL) includes -lc which may
-# include modules defining variables like `optind', so libcoreutils.a
-# must precede $(LIBINTL) in order to ensure we use GNU getopt.
-# But libcoreutils.a must also follow $(LIBINTL), since libintl uses
-# replacement functions defined in libcoreutils.a.
-LDADD = ../lib/libcoreutils.a $(LIBINTL) ../lib/libcoreutils.a
-
-# for eaccess in lib/euidaccess.c.
-cp_LDADD = $(LDADD) $(LIB_EACCESS)
-ginstall_LDADD = $(LDADD) $(LIB_EACCESS)
-mv_LDADD = $(LDADD) $(LIB_EACCESS)
-pathchk_LDADD = $(LDADD) $(LIB_EACCESS)
-rm_LDADD = $(LDADD) $(LIB_EACCESS)
-test_LDADD = $(LDADD) $(LIB_EACCESS)
-# This is for the '[' program. Automake transliterates '[' to '_'.
-__LDADD = $(LDADD) $(LIB_EACCESS)
-
-# for clock_gettime and fdatasync
-dd_LDADD = $(LDADD) $(LIB_GETHRXTIME) $(LIB_FDATASYNC)
-dir_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME)
-ls_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME)
-pr_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME)
-shred_LDADD = $(LDADD) $(LIB_GETHRXTIME) $(LIB_FDATASYNC)
-shuf_LDADD = $(LDADD) $(LIB_GETHRXTIME)
-vdir_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME)
-
-## If necessary, add -lm to resolve use of pow in lib/strtod.c.
-sort_LDADD = $(LDADD) $(POW_LIB) $(LIB_GETHRXTIME)
-
-# for get_date and gettime
-date_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME)
-touch_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME)
-
-# If necessary, add -lm to resolve use of pow in lib/strtod.c.
-# If necessary, add -liconv to resolve use of iconv in lib/unicodeio.c.
-printf_LDADD = $(LDADD) $(POW_LIB) $(LIBICONV)
-
-# If necessary, add -lm to resolve use of pow in lib/strtod.c.
-seq_LDADD = $(LDADD) $(POW_LIB)
-
-# If necessary, add libraries to resolve the `pow' reference in lib/strtod.c
-# and the `nanosleep' reference in lib/xnanosleep.c.
-nanosec_libs = $(LDADD) $(POW_LIB) $(LIB_NANOSLEEP)
-
-sleep_LDADD = $(nanosec_libs)
-tail_LDADD = $(nanosec_libs)
-
-# If necessary, add -lm to resolve use of pow in lib/strtod.c.
-uptime_LDADD = $(LDADD) $(POW_LIB) $(GETLOADAVG_LIBS)
-
-su_LDADD = $(LDADD) $(LIB_CRYPT)
-
-dir_LDADD += $(LIB_ACL_TRIVIAL) $(LIB_ACL)
-ls_LDADD += $(LIB_ACL_TRIVIAL) $(LIB_ACL)
-vdir_LDADD += $(LIB_ACL_TRIVIAL) $(LIB_ACL)
-cp_LDADD += $(LIB_ACL)
-mv_LDADD += $(LIB_ACL)
-ginstall_LDADD += $(LIB_ACL)
-
-$(PROGRAMS): ../lib/libcoreutils.a
-
-SUFFIXES = .sh
-
-# 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' \
- $(top_srcdir)/lib/version-etc.c`
-
-.sh:
- rm -f $@ $@-t
- sed \
- -e 's!@''bindir''@!$(bindir)!' \
- -e 's/@''RELEASE_YEAR'@/$(RELEASE_YEAR)/ \
- -e 's/@''GNU_PACKAGE''@/$(GNU_PACKAGE)/' \
- -e 's/@''PACKAGE_BUGREPORT''@/$(PACKAGE_BUGREPORT)/' \
- -e 's/@''VERSION''@/$(VERSION)/' $< > $@-t
- chmod +x $@-t
- mv $@-t $@
-
-all-local: su$(EXEEXT)
-
-installed_su = $(DESTDIR)$(bindir)/`echo su|sed '$(transform)'`
-
-setuid_root_mode = a=rx,u+s
-
-INSTALL_SU = \
- p=su; \
- echo " $(INSTALL_PROGRAM) $$p $(installed_su)"; \
- $(INSTALL_PROGRAM) $$p $(installed_su); \
- echo " chown root $(installed_su)"; \
- chown root $(installed_su); \
- echo " chmod $(setuid_root_mode) $(installed_su)"; \
- chmod $(setuid_root_mode) $(installed_su)
-
-install-root: su$(EXEEXT)
- @$(INSTALL_SU)
-
-install-exec-local: su$(EXEEXT)
- @TMPFILE=$(DESTDIR)$(bindir)/.su-$$$$; \
- rm -f $$TMPFILE; \
- echo > $$TMPFILE; \
-## See if we can create a setuid root executable in $(bindir).
-## If not, then don't even try to install su.
- can_create_suid_root_executable=no; \
- chown root $$TMPFILE > /dev/null 2>&1 \
- && chmod $(setuid_root_mode) $$TMPFILE > /dev/null 2>&1 \
- && can_create_suid_root_executable=yes; \
- rm -f $$TMPFILE; \
- if test $$can_create_suid_root_executable = yes; then \
- $(INSTALL_SU); \
- else \
- echo "WARNING: insufficient access; not installing su"; \
- echo "NOTE: to install su, run 'make install-root' as root"; \
- fi
-
-uninstall-local:
-# Remove su only if it's one we installed.
- @if grep '$(GNU_PACKAGE)' $(installed_su) > /dev/null 2>&1; then \
- echo " rm -f $(installed_su)"; \
- rm -f $(installed_su); \
- else :; fi
-
-# Use `ginstall' in the definition of PROGRAMS and in dependencies to avoid
-# confusion with the `install' target. The install rule transforms `ginstall'
-# to install before applying any user-specified name transformations.
-
-transform = s/ginstall/install/; @program_transform_name@
-ginstall_SOURCES = install.c copy.c cp-hash.c
-
-# This is for the '[' program. Automake transliterates '[' to '_'.
-__SOURCES = lbracket.c
-
-cp_SOURCES = cp.c copy.c cp-hash.c
-dir_SOURCES = ls.c ls-dir.c
-vdir_SOURCES = ls.c ls-vdir.c
-ls_SOURCES = ls.c ls-ls.c
-chown_SOURCES = chown.c chown-core.c
-chgrp_SOURCES = chgrp.c chown-core.c
-
-mv_SOURCES = mv.c copy.c cp-hash.c remove.c
-rm_SOURCES = rm.c remove.c
-
-md5sum_SOURCES = md5sum.c
-md5sum_CPPFLAGS = -DHASH_ALGO_MD5=1 $(AM_CPPFLAGS)
-sha1sum_SOURCES = md5sum.c
-sha1sum_CPPFLAGS = -DHASH_ALGO_SHA1=1 $(AM_CPPFLAGS)
-sha224sum_SOURCES = md5sum.c
-sha224sum_CPPFLAGS = -DHASH_ALGO_SHA224=1 $(AM_CPPFLAGS)
-sha256sum_SOURCES = md5sum.c
-sha256sum_CPPFLAGS = -DHASH_ALGO_SHA256=1 $(AM_CPPFLAGS)
-sha384sum_SOURCES = md5sum.c
-sha384sum_CPPFLAGS = -DHASH_ALGO_SHA384=1 $(AM_CPPFLAGS)
-sha512sum_SOURCES = md5sum.c
-sha512sum_CPPFLAGS = -DHASH_ALGO_SHA512=1 $(AM_CPPFLAGS)
-
-editpl = sed -e 's,@''PERL''@,$(PERL),g'
-
-BUILT_SOURCES += dircolors.h
-dircolors.h: dcgen dircolors.hin
- @rm -f $@ $@-t
- $(PERL) -w -- $(srcdir)/dcgen $(srcdir)/dircolors.hin > $@-t
- @chmod a-w $@-t
- mv $@-t $@
-
-wheel_size = 5
-
-BUILT_SOURCES += wheel-size.h
-wheel-size.h: Makefile.am
- @rm -f $@ $@-t
- echo '#define WHEEL_SIZE $(wheel_size)' > $@-t
- @chmod a-w $@-t
- mv $@-t $@
-
-BUILT_SOURCES += wheel.h
-wheel.h: wheel-gen.pl Makefile.am
- @rm -f $@ $@-t
- $(srcdir)/wheel-gen.pl $(wheel_size) > $@-t
- @chmod a-w $@-t
- mv $@-t $@
-
-# false exits nonzero even with --help or --version.
-# test doesn't support --help or --version.
-# Tell automake to exempt then from that installcheck test.
-AM_INSTALLCHECK_STD_OPTIONS_EXEMPT = false test
-
-BUILT_SOURCES += fs.h
-fs.h: stat.c extract-magic
- rm -f $@
- $(PERL) $(srcdir)/extract-magic $(srcdir)/stat.c > $@t
- @chmod a-w $@t
- mv $@t $@
-
-MAINTAINERCLEANFILES = $(BUILT_SOURCES)
-
-# Sort in traditional ASCII order, regardless of the current locale;
-# otherwise we may get into trouble with distinct strings that the
-# current locale considers to be equal.
-ASSORT = LC_ALL=C sort
-
-all_programs = \
- $(bin_PROGRAMS) \
- $(bin_SCRIPTS) \
- $(EXTRA_PROGRAMS)
-
-all_programs.list:
- @echo $(all_programs) | tr ' ' '\n' | sed -e 's,$(EXEEXT)$$,,' \
- | $(ASSORT) -u
-
-pm = progs-makefile
-pr = progs-readme
-# Ensure that the list of programs in README matches the list
-# of programs we can build.
-check: check-README check-misc
-.PHONY: check-README
-check-README:
- rm -rf $(pr) $(pm)
- echo $(all_programs) \
- | tr -s ' ' '\n' | sed -e 's,$(EXEEXT)$$,,' \
- | $(ASSORT) -u > $(pm) && \
- sed -n '/^The programs .* are:/,/^[a-zA-Z]/p' $(top_srcdir)/README \
- | sed -n '/^ */s///p' | tr -s ' ' '\n' > $(pr)
- diff $(pm) $(pr) && rm -rf $(pr) $(pm)
-
-# Ensure that the list of programs and author names is accurate.
-au_dotdot = authors-dotdot
-au_actual = authors-actual
-.PHONY: check-AUTHORS
-check-AUTHORS: $(all_programs)
- rm -f $(au_actual) $(au_dotdot)
- for i in `ls $(all_programs) | sed -e 's,$(EXEEXT)$$,,' \
- | $(ASSORT) -u`; do \
- test "$$i" = '[' && continue; \
- exe=$$i; \
- if test "$$i" = install; then \
- exe=ginstall; \
- elif test "$$i" = test; then \
- exe='['; \
- fi; \
- ./$$exe --version \
- |sed -n '/Written by /{ s//'"$$i"': /; s/,* and /, /; s/\.$$//; p; }'; \
- done > $(au_actual)
- sed -n '/:/p' $(top_srcdir)/AUTHORS > $(au_dotdot)
- diff $(au_actual) $(au_dotdot) && rm -f $(au_actual) $(au_dotdot)
-
-# Make sure we don't define any S_IS* macros in src/*.c files.
-# Not a big deal, but they're already defined via system.h.
-#
-# Also make sure we don't use st_blocks. Use ST_NBLOCKS instead.
-# This is a bit of a kludge, since it prevents use of the string
-# even in comments, but for now it does the job with no false positives.
-.PHONY: check-misc
-check-misc:
- cd $(srcdir); grep '^# *define *S_IS' $(SOURCES) && exit 1 || :
- cd $(srcdir); grep st_blocks $(SOURCES) && exit 1 || :
- cd $(srcdir); grep '^# *define .*defined' $(SOURCES) && exit 1 || :
-
-# Extract the list of authors from each file.
-sed_filter = s/^ *//;s/N_ (//;s/^"//;s/")*$$//
-# Sometimes the string is on the same line as the #define...
-s1 = '/^\#define AUTHORS \([^\\]\)/{;s//\1/;$(sed_filter);p;q;}'
-# Sometimes the string is on the backslash-continued line after the #define.
-s2 = '/^\#define AUTHORS \\\\/{;n;$(sed_filter);p;q;}'
-# FIXME: handle *.sh; and use $(all_programs), not $(SOURCES)
-../AUTHORS: $(SOURCES)
- rm -f $@-t
- ( \
- set -e; \
- echo "Here are the names of the programs in this package,"; \
- echo "each followed by the name(s) of its author(s)."; \
- echo; \
- for i in $(SOURCES); do \
- a=`sed -n $(s1) $$i`; \
- test "$$a" && : \
- || a=`sed -n $(s2) $$i`; \
- if test "$$a"; then \
- prog=`echo $$i|sed 's/\.c$$//'`; \
- echo "$$prog: $$a"; \
- fi; \
- done | $(ASSORT) -u ) > $@-t
- chmod a-w $@-t
- mv $@-t $@
-
-# The following rule is not designed to be portable,
-# and relies on tools that not everyone has.
-
-# Most functions in src/*.c should have static scope.
-# Any that don't must be marked with `extern', but `main'
-# and `usage' are exceptions. They're always extern, but
-# don't need to be marked.
-#
-# The second nm|grep checks for file-scope variables with `extern' scope.
-.PHONY: sc_tight_scope
-sc_tight_scope: $(all_programs)
- @t=exceptions-$$$$; \
- trap "s=$$?; rm -f $$t; exit $$s" 0 1 2 13 15; \
- ( printf '^main$$\n^usage$$\n'; \
- grep -h -A1 '^extern .*[^;]$$' $(SOURCES) \
- | grep -vE '^(extern |--)' |sed 's/^/^/;s/ .*/$$/' ) > $$t; \
- nm -e *.$(OBJEXT) \
- | sed -n 's/.* T //p' \
- | grep -Ev -f $$t && \
- { echo 'the above functions should have static scope' 1>&2; \
- exit 1; } || : ; \
- ( printf '^program_name$$\n'; \
- sed -n 's/^extern int \([^ ][^ ]*\);$$/^\1$$/p' \
- $(noinst_HEADERS) ) > $$t; \
- nm -e *.$(OBJEXT) \
- | sed -n 's/.* [BD] //p' \
- | grep -Ev -f $$t && \
- { echo 'the above variables should have static scope' 1>&2; \
- exit 1; } || :
diff --git a/src/Makefile.in b/src/Makefile.in
deleted file mode 100644
index 18965a9..0000000
--- a/src/Makefile.in
+++ /dev/null
@@ -1,2065 +0,0 @@
-# Makefile.in generated by automake 1.10 from Makefile.am.
-# @configure_input@
-
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
-# This Makefile.in is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE.
-
-@SET_MAKE@
-
-
-
-VPATH = @srcdir@
-pkgdatadir = $(datadir)/@PACKAGE@
-pkglibdir = $(libdir)/@PACKAGE@
-pkgincludedir = $(includedir)/@PACKAGE@
-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
-install_sh_DATA = $(install_sh) -c -m 644
-install_sh_PROGRAM = $(install_sh) -c
-install_sh_SCRIPT = $(install_sh) -c
-INSTALL_HEADER = $(INSTALL_DATA)
-NORMAL_INSTALL = :
-PRE_INSTALL = :
-POST_INSTALL = :
-NORMAL_UNINSTALL = :
-PRE_UNINSTALL = :
-POST_UNINSTALL = :
-build_triplet = @build@
-host_triplet = @host@
-EXTRA_PROGRAMS = chroot$(EXEEXT) df$(EXEEXT) hostid$(EXEEXT) \
- nice$(EXEEXT) pinky$(EXEEXT) stty$(EXEEXT) su$(EXEEXT) \
- uname$(EXEEXT) uptime$(EXEEXT) users$(EXEEXT) who$(EXEEXT)
-bin_PROGRAMS = [$(EXEEXT) chgrp$(EXEEXT) chown$(EXEEXT) chmod$(EXEEXT) \
- cp$(EXEEXT) dd$(EXEEXT) dircolors$(EXEEXT) du$(EXEEXT) \
- ginstall$(EXEEXT) link$(EXEEXT) ln$(EXEEXT) dir$(EXEEXT) \
- vdir$(EXEEXT) ls$(EXEEXT) mkdir$(EXEEXT) mkfifo$(EXEEXT) \
- mknod$(EXEEXT) mv$(EXEEXT) nohup$(EXEEXT) readlink$(EXEEXT) \
- rm$(EXEEXT) rmdir$(EXEEXT) shred$(EXEEXT) stat$(EXEEXT) \
- sync$(EXEEXT) touch$(EXEEXT) unlink$(EXEEXT) cat$(EXEEXT) \
- cksum$(EXEEXT) comm$(EXEEXT) csplit$(EXEEXT) cut$(EXEEXT) \
- expand$(EXEEXT) fmt$(EXEEXT) fold$(EXEEXT) head$(EXEEXT) \
- join$(EXEEXT) md5sum$(EXEEXT) nl$(EXEEXT) od$(EXEEXT) \
- paste$(EXEEXT) pr$(EXEEXT) ptx$(EXEEXT) sha1sum$(EXEEXT) \
- sha224sum$(EXEEXT) sha256sum$(EXEEXT) sha384sum$(EXEEXT) \
- sha512sum$(EXEEXT) shuf$(EXEEXT) sort$(EXEEXT) split$(EXEEXT) \
- sum$(EXEEXT) tac$(EXEEXT) tail$(EXEEXT) tr$(EXEEXT) \
- tsort$(EXEEXT) unexpand$(EXEEXT) uniq$(EXEEXT) wc$(EXEEXT) \
- basename$(EXEEXT) date$(EXEEXT) dirname$(EXEEXT) echo$(EXEEXT) \
- env$(EXEEXT) expr$(EXEEXT) factor$(EXEEXT) false$(EXEEXT) \
- hostname$(EXEEXT) id$(EXEEXT) kill$(EXEEXT) logname$(EXEEXT) \
- pathchk$(EXEEXT) printenv$(EXEEXT) printf$(EXEEXT) \
- pwd$(EXEEXT) seq$(EXEEXT) sleep$(EXEEXT) tee$(EXEEXT) \
- test$(EXEEXT) true$(EXEEXT) tty$(EXEEXT) whoami$(EXEEXT) \
- yes$(EXEEXT) base64$(EXEEXT) $(OPTIONAL_BIN_PROGS) $(DF_PROG)
-noinst_PROGRAMS = setuidgid$(EXEEXT)
-subdir = src
-DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
- $(srcdir)/Makefile.in
-ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/absolute-header.m4 \
- $(top_srcdir)/m4/acl.m4 $(top_srcdir)/m4/alloca.m4 \
- $(top_srcdir)/m4/allocsa.m4 $(top_srcdir)/m4/argmatch.m4 \
- $(top_srcdir)/m4/arpa_inet_h.m4 $(top_srcdir)/m4/assert.m4 \
- $(top_srcdir)/m4/atexit.m4 $(top_srcdir)/m4/autobuild.m4 \
- $(top_srcdir)/m4/backupfile.m4 $(top_srcdir)/m4/base64.m4 \
- $(top_srcdir)/m4/bison.m4 $(top_srcdir)/m4/boottime.m4 \
- $(top_srcdir)/m4/c-strtod.m4 $(top_srcdir)/m4/calloc.m4 \
- $(top_srcdir)/m4/canon-host.m4 \
- $(top_srcdir)/m4/canonicalize.m4 \
- $(top_srcdir)/m4/chdir-long.m4 $(top_srcdir)/m4/check-decl.m4 \
- $(top_srcdir)/m4/chown.m4 $(top_srcdir)/m4/clock_time.m4 \
- $(top_srcdir)/m4/cloexec.m4 $(top_srcdir)/m4/close-stream.m4 \
- $(top_srcdir)/m4/closeout.m4 $(top_srcdir)/m4/codeset.m4 \
- $(top_srcdir)/m4/config-h.m4 $(top_srcdir)/m4/cycle-check.m4 \
- $(top_srcdir)/m4/d-ino.m4 $(top_srcdir)/m4/d-type.m4 \
- $(top_srcdir)/m4/dirfd.m4 $(top_srcdir)/m4/dirname.m4 \
- $(top_srcdir)/m4/dos.m4 $(top_srcdir)/m4/double-slash-root.m4 \
- $(top_srcdir)/m4/dup2.m4 $(top_srcdir)/m4/eealloc.m4 \
- $(top_srcdir)/m4/eoverflow.m4 $(top_srcdir)/m4/error.m4 \
- $(top_srcdir)/m4/euidaccess-stat.m4 \
- $(top_srcdir)/m4/euidaccess.m4 $(top_srcdir)/m4/exclude.m4 \
- $(top_srcdir)/m4/exitfail.m4 $(top_srcdir)/m4/extensions.m4 \
- $(top_srcdir)/m4/fchdir.m4 $(top_srcdir)/m4/fcntl-safer.m4 \
- $(top_srcdir)/m4/fcntl_h.m4 $(top_srcdir)/m4/fd-reopen.m4 \
- $(top_srcdir)/m4/file-type.m4 $(top_srcdir)/m4/fileblocks.m4 \
- $(top_srcdir)/m4/filemode.m4 $(top_srcdir)/m4/filenamecat.m4 \
- $(top_srcdir)/m4/flexmember.m4 $(top_srcdir)/m4/fnmatch.m4 \
- $(top_srcdir)/m4/fpending.m4 $(top_srcdir)/m4/fprintftime.m4 \
- $(top_srcdir)/m4/free.m4 $(top_srcdir)/m4/fstypename.m4 \
- $(top_srcdir)/m4/fsusage.m4 $(top_srcdir)/m4/ftruncate.m4 \
- $(top_srcdir)/m4/fts.m4 $(top_srcdir)/m4/getaddrinfo.m4 \
- $(top_srcdir)/m4/getcwd-abort-bug.m4 \
- $(top_srcdir)/m4/getcwd-path-max.m4 $(top_srcdir)/m4/getcwd.m4 \
- $(top_srcdir)/m4/getdate.m4 $(top_srcdir)/m4/getdelim.m4 \
- $(top_srcdir)/m4/getgroups.m4 $(top_srcdir)/m4/gethostname.m4 \
- $(top_srcdir)/m4/gethrxtime.m4 $(top_srcdir)/m4/getline.m4 \
- $(top_srcdir)/m4/getloadavg.m4 $(top_srcdir)/m4/getndelim2.m4 \
- $(top_srcdir)/m4/getopt.m4 $(top_srcdir)/m4/getpagesize.m4 \
- $(top_srcdir)/m4/getpass.m4 $(top_srcdir)/m4/gettext.m4 \
- $(top_srcdir)/m4/gettime.m4 $(top_srcdir)/m4/gettimeofday.m4 \
- $(top_srcdir)/m4/getugroups.m4 \
- $(top_srcdir)/m4/getusershell.m4 $(top_srcdir)/m4/glibc21.m4 \
- $(top_srcdir)/m4/gnulib-common.m4 \
- $(top_srcdir)/m4/gnulib-comp.m4 \
- $(top_srcdir)/m4/group-member.m4 \
- $(top_srcdir)/m4/hard-locale.m4 $(top_srcdir)/m4/hash.m4 \
- $(top_srcdir)/m4/host-os.m4 $(top_srcdir)/m4/human.m4 \
- $(top_srcdir)/m4/i-ring.m4 $(top_srcdir)/m4/iconv.m4 \
- $(top_srcdir)/m4/idcache.m4 $(top_srcdir)/m4/inet_ntop.m4 \
- $(top_srcdir)/m4/inline.m4 $(top_srcdir)/m4/intmax_t.m4 \
- $(top_srcdir)/m4/inttostr.m4 $(top_srcdir)/m4/inttypes-pri.m4 \
- $(top_srcdir)/m4/inttypes.m4 $(top_srcdir)/m4/inttypes_h.m4 \
- $(top_srcdir)/m4/isapipe.m4 $(top_srcdir)/m4/jm-macros.m4 \
- $(top_srcdir)/m4/jm-winsz1.m4 $(top_srcdir)/m4/jm-winsz2.m4 \
- $(top_srcdir)/m4/lchmod.m4 $(top_srcdir)/m4/lchown.m4 \
- $(top_srcdir)/m4/lib-check.m4 $(top_srcdir)/m4/lib-ignore.m4 \
- $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
- $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/link-follow.m4 \
- $(top_srcdir)/m4/localcharset.m4 \
- $(top_srcdir)/m4/long-options.m4 \
- $(top_srcdir)/m4/longdouble.m4 $(top_srcdir)/m4/longlong.m4 \
- $(top_srcdir)/m4/ls-mntd-fs.m4 $(top_srcdir)/m4/lstat.m4 \
- $(top_srcdir)/m4/mbchar.m4 $(top_srcdir)/m4/mbiter.m4 \
- $(top_srcdir)/m4/mbrtowc.m4 $(top_srcdir)/m4/mbscasecmp.m4 \
- $(top_srcdir)/m4/mbstate_t.m4 $(top_srcdir)/m4/mbswidth.m4 \
- $(top_srcdir)/m4/md5.m4 $(top_srcdir)/m4/memcasecmp.m4 \
- $(top_srcdir)/m4/memchr.m4 $(top_srcdir)/m4/memcmp.m4 \
- $(top_srcdir)/m4/memcoll.m4 $(top_srcdir)/m4/memcpy.m4 \
- $(top_srcdir)/m4/memmove.m4 $(top_srcdir)/m4/mempcpy.m4 \
- $(top_srcdir)/m4/memrchr.m4 $(top_srcdir)/m4/memset.m4 \
- $(top_srcdir)/m4/memxfrm.m4 $(top_srcdir)/m4/mkancesdirs.m4 \
- $(top_srcdir)/m4/mkdir-p.m4 $(top_srcdir)/m4/mkdir-slash.m4 \
- $(top_srcdir)/m4/mkstemp.m4 $(top_srcdir)/m4/mktime.m4 \
- $(top_srcdir)/m4/modechange.m4 $(top_srcdir)/m4/mountlist.m4 \
- $(top_srcdir)/m4/mpsort.m4 $(top_srcdir)/m4/nanosleep.m4 \
- $(top_srcdir)/m4/netinet_in_h.m4 $(top_srcdir)/m4/nls.m4 \
- $(top_srcdir)/m4/openat.m4 $(top_srcdir)/m4/pathmax.m4 \
- $(top_srcdir)/m4/perl.m4 $(top_srcdir)/m4/physmem.m4 \
- $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/posixtm.m4 \
- $(top_srcdir)/m4/posixver.m4 $(top_srcdir)/m4/prereq.m4 \
- $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/putenv.m4 \
- $(top_srcdir)/m4/quote.m4 $(top_srcdir)/m4/quotearg.m4 \
- $(top_srcdir)/m4/randint.m4 $(top_srcdir)/m4/randperm.m4 \
- $(top_srcdir)/m4/randread.m4 $(top_srcdir)/m4/readlink.m4 \
- $(top_srcdir)/m4/readtokens.m4 $(top_srcdir)/m4/readutmp.m4 \
- $(top_srcdir)/m4/regex.m4 \
- $(top_srcdir)/m4/rename-dest-slash.m4 \
- $(top_srcdir)/m4/rename.m4 $(top_srcdir)/m4/rmdir-errno.m4 \
- $(top_srcdir)/m4/rmdir.m4 $(top_srcdir)/m4/root-dev-ino.m4 \
- $(top_srcdir)/m4/rpmatch.m4 $(top_srcdir)/m4/safe-read.m4 \
- $(top_srcdir)/m4/safe-write.m4 $(top_srcdir)/m4/same.m4 \
- $(top_srcdir)/m4/save-cwd.m4 $(top_srcdir)/m4/savedir.m4 \
- $(top_srcdir)/m4/savewd.m4 $(top_srcdir)/m4/setenv.m4 \
- $(top_srcdir)/m4/settime.m4 $(top_srcdir)/m4/sha1.m4 \
- $(top_srcdir)/m4/sha256.m4 $(top_srcdir)/m4/sha512.m4 \
- $(top_srcdir)/m4/sig2str.m4 $(top_srcdir)/m4/snprintf.m4 \
- $(top_srcdir)/m4/socklen.m4 $(top_srcdir)/m4/sockpfaf.m4 \
- $(top_srcdir)/m4/ssize_t.m4 $(top_srcdir)/m4/st_dm_mode.m4 \
- $(top_srcdir)/m4/stat-prog.m4 $(top_srcdir)/m4/stat-time.m4 \
- $(top_srcdir)/m4/stdarg.m4 $(top_srcdir)/m4/stdbool.m4 \
- $(top_srcdir)/m4/stdint.m4 $(top_srcdir)/m4/stdint_h.m4 \
- $(top_srcdir)/m4/stdio-safer.m4 $(top_srcdir)/m4/stdio_h.m4 \
- $(top_srcdir)/m4/stdlib-safer.m4 $(top_srcdir)/m4/stdlib_h.m4 \
- $(top_srcdir)/m4/stpcpy.m4 $(top_srcdir)/m4/strcspn.m4 \
- $(top_srcdir)/m4/strdup.m4 $(top_srcdir)/m4/strftime.m4 \
- $(top_srcdir)/m4/string_h.m4 $(top_srcdir)/m4/strndup.m4 \
- $(top_srcdir)/m4/strnlen.m4 $(top_srcdir)/m4/strnumcmp.m4 \
- $(top_srcdir)/m4/strpbrk.m4 $(top_srcdir)/m4/strtod.m4 \
- $(top_srcdir)/m4/strtoimax.m4 $(top_srcdir)/m4/strtol.m4 \
- $(top_srcdir)/m4/strtoll.m4 $(top_srcdir)/m4/strtoul.m4 \
- $(top_srcdir)/m4/strtoull.m4 $(top_srcdir)/m4/strtoumax.m4 \
- $(top_srcdir)/m4/strverscmp.m4 \
- $(top_srcdir)/m4/sys_socket_h.m4 \
- $(top_srcdir)/m4/sys_stat_h.m4 $(top_srcdir)/m4/sys_time_h.m4 \
- $(top_srcdir)/m4/tempname.m4 $(top_srcdir)/m4/time_h.m4 \
- $(top_srcdir)/m4/time_r.m4 $(top_srcdir)/m4/timespec.m4 \
- $(top_srcdir)/m4/tm_gmtoff.m4 $(top_srcdir)/m4/tzset.m4 \
- $(top_srcdir)/m4/unicodeio.m4 $(top_srcdir)/m4/unistd-safer.m4 \
- $(top_srcdir)/m4/unistd_h.m4 $(top_srcdir)/m4/unlink-busy.m4 \
- $(top_srcdir)/m4/unlinkdir.m4 $(top_srcdir)/m4/unlocked-io.m4 \
- $(top_srcdir)/m4/uptime.m4 $(top_srcdir)/m4/userspec.m4 \
- $(top_srcdir)/m4/utimbuf.m4 $(top_srcdir)/m4/utime.m4 \
- $(top_srcdir)/m4/utimecmp.m4 $(top_srcdir)/m4/utimens.m4 \
- $(top_srcdir)/m4/utimes-null.m4 $(top_srcdir)/m4/utimes.m4 \
- $(top_srcdir)/m4/vasnprintf.m4 $(top_srcdir)/m4/vasprintf.m4 \
- $(top_srcdir)/m4/wchar.m4 $(top_srcdir)/m4/wchar_t.m4 \
- $(top_srcdir)/m4/wctype.m4 $(top_srcdir)/m4/wcwidth.m4 \
- $(top_srcdir)/m4/wint_t.m4 $(top_srcdir)/m4/xalloc.m4 \
- $(top_srcdir)/m4/xfts.m4 $(top_srcdir)/m4/xgetcwd.m4 \
- $(top_srcdir)/m4/xnanosleep.m4 $(top_srcdir)/m4/xstrndup.m4 \
- $(top_srcdir)/m4/xstrtod.m4 $(top_srcdir)/m4/xstrtol.m4 \
- $(top_srcdir)/m4/yesno.m4 $(top_srcdir)/configure.ac
-am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
- $(ACLOCAL_M4)
-mkinstalldirs = $(install_sh) -d
-CONFIG_HEADER = $(top_builddir)/lib/config.h
-CONFIG_CLEAN_FILES =
-am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)"
-binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
-PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
-am___OBJECTS = lbracket.$(OBJEXT)
-__OBJECTS = $(am___OBJECTS)
-am__DEPENDENCIES_1 =
-am__DEPENDENCIES_2 = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-__DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1)
-base64_SOURCES = base64.c
-base64_OBJECTS = base64.$(OBJEXT)
-base64_LDADD = $(LDADD)
-base64_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-basename_SOURCES = basename.c
-basename_OBJECTS = basename.$(OBJEXT)
-basename_LDADD = $(LDADD)
-basename_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-cat_SOURCES = cat.c
-cat_OBJECTS = cat.$(OBJEXT)
-cat_LDADD = $(LDADD)
-cat_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-am_chgrp_OBJECTS = chgrp.$(OBJEXT) chown-core.$(OBJEXT)
-chgrp_OBJECTS = $(am_chgrp_OBJECTS)
-chgrp_LDADD = $(LDADD)
-chgrp_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-chmod_SOURCES = chmod.c
-chmod_OBJECTS = chmod.$(OBJEXT)
-chmod_LDADD = $(LDADD)
-chmod_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-am_chown_OBJECTS = chown.$(OBJEXT) chown-core.$(OBJEXT)
-chown_OBJECTS = $(am_chown_OBJECTS)
-chown_LDADD = $(LDADD)
-chown_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-chroot_SOURCES = chroot.c
-chroot_OBJECTS = chroot.$(OBJEXT)
-chroot_LDADD = $(LDADD)
-chroot_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-cksum_SOURCES = cksum.c
-cksum_OBJECTS = cksum.$(OBJEXT)
-cksum_LDADD = $(LDADD)
-cksum_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-comm_SOURCES = comm.c
-comm_OBJECTS = comm.$(OBJEXT)
-comm_LDADD = $(LDADD)
-comm_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-am_cp_OBJECTS = cp.$(OBJEXT) copy.$(OBJEXT) cp-hash.$(OBJEXT)
-cp_OBJECTS = $(am_cp_OBJECTS)
-cp_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1)
-csplit_SOURCES = csplit.c
-csplit_OBJECTS = csplit.$(OBJEXT)
-csplit_LDADD = $(LDADD)
-csplit_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-cut_SOURCES = cut.c
-cut_OBJECTS = cut.$(OBJEXT)
-cut_LDADD = $(LDADD)
-cut_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-date_SOURCES = date.c
-date_OBJECTS = date.$(OBJEXT)
-date_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1)
-dd_SOURCES = dd.c
-dd_OBJECTS = dd.$(OBJEXT)
-dd_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1)
-df_SOURCES = df.c
-df_OBJECTS = df.$(OBJEXT)
-df_LDADD = $(LDADD)
-df_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-am_dir_OBJECTS = ls.$(OBJEXT) ls-dir.$(OBJEXT)
-dir_OBJECTS = $(am_dir_OBJECTS)
-dir_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
-dircolors_SOURCES = dircolors.c
-dircolors_OBJECTS = dircolors.$(OBJEXT)
-dircolors_LDADD = $(LDADD)
-dircolors_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-dirname_SOURCES = dirname.c
-dirname_OBJECTS = dirname.$(OBJEXT)
-dirname_LDADD = $(LDADD)
-dirname_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-du_SOURCES = du.c
-du_OBJECTS = du.$(OBJEXT)
-du_LDADD = $(LDADD)
-du_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-echo_SOURCES = echo.c
-echo_OBJECTS = echo.$(OBJEXT)
-echo_LDADD = $(LDADD)
-echo_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-env_SOURCES = env.c
-env_OBJECTS = env.$(OBJEXT)
-env_LDADD = $(LDADD)
-env_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-expand_SOURCES = expand.c
-expand_OBJECTS = expand.$(OBJEXT)
-expand_LDADD = $(LDADD)
-expand_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-expr_SOURCES = expr.c
-expr_OBJECTS = expr.$(OBJEXT)
-expr_LDADD = $(LDADD)
-expr_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-factor_SOURCES = factor.c
-factor_OBJECTS = factor.$(OBJEXT)
-factor_LDADD = $(LDADD)
-factor_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-false_SOURCES = false.c
-false_OBJECTS = false.$(OBJEXT)
-false_LDADD = $(LDADD)
-false_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-fmt_SOURCES = fmt.c
-fmt_OBJECTS = fmt.$(OBJEXT)
-fmt_LDADD = $(LDADD)
-fmt_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-fold_SOURCES = fold.c
-fold_OBJECTS = fold.$(OBJEXT)
-fold_LDADD = $(LDADD)
-fold_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-am_ginstall_OBJECTS = install.$(OBJEXT) copy.$(OBJEXT) \
- cp-hash.$(OBJEXT)
-ginstall_OBJECTS = $(am_ginstall_OBJECTS)
-ginstall_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1)
-head_SOURCES = head.c
-head_OBJECTS = head.$(OBJEXT)
-head_LDADD = $(LDADD)
-head_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-hostid_SOURCES = hostid.c
-hostid_OBJECTS = hostid.$(OBJEXT)
-hostid_LDADD = $(LDADD)
-hostid_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-hostname_SOURCES = hostname.c
-hostname_OBJECTS = hostname.$(OBJEXT)
-hostname_LDADD = $(LDADD)
-hostname_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-id_SOURCES = id.c
-id_OBJECTS = id.$(OBJEXT)
-id_LDADD = $(LDADD)
-id_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-join_SOURCES = join.c
-join_OBJECTS = join.$(OBJEXT)
-join_LDADD = $(LDADD)
-join_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-kill_SOURCES = kill.c
-kill_OBJECTS = kill.$(OBJEXT)
-kill_LDADD = $(LDADD)
-kill_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-link_SOURCES = link.c
-link_OBJECTS = link.$(OBJEXT)
-link_LDADD = $(LDADD)
-link_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-ln_SOURCES = ln.c
-ln_OBJECTS = ln.$(OBJEXT)
-ln_LDADD = $(LDADD)
-ln_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-logname_SOURCES = logname.c
-logname_OBJECTS = logname.$(OBJEXT)
-logname_LDADD = $(LDADD)
-logname_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-am_ls_OBJECTS = ls.$(OBJEXT) ls-ls.$(OBJEXT)
-ls_OBJECTS = $(am_ls_OBJECTS)
-ls_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
-am_md5sum_OBJECTS = md5sum-md5sum.$(OBJEXT)
-md5sum_OBJECTS = $(am_md5sum_OBJECTS)
-md5sum_LDADD = $(LDADD)
-md5sum_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-mkdir_SOURCES = mkdir.c
-mkdir_OBJECTS = mkdir.$(OBJEXT)
-mkdir_LDADD = $(LDADD)
-mkdir_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-mkfifo_SOURCES = mkfifo.c
-mkfifo_OBJECTS = mkfifo.$(OBJEXT)
-mkfifo_LDADD = $(LDADD)
-mkfifo_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-mknod_SOURCES = mknod.c
-mknod_OBJECTS = mknod.$(OBJEXT)
-mknod_LDADD = $(LDADD)
-mknod_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-am_mv_OBJECTS = mv.$(OBJEXT) copy.$(OBJEXT) cp-hash.$(OBJEXT) \
- remove.$(OBJEXT)
-mv_OBJECTS = $(am_mv_OBJECTS)
-mv_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1)
-nice_SOURCES = nice.c
-nice_OBJECTS = nice.$(OBJEXT)
-nice_LDADD = $(LDADD)
-nice_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-nl_SOURCES = nl.c
-nl_OBJECTS = nl.$(OBJEXT)
-nl_LDADD = $(LDADD)
-nl_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-nohup_SOURCES = nohup.c
-nohup_OBJECTS = nohup.$(OBJEXT)
-nohup_LDADD = $(LDADD)
-nohup_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-od_SOURCES = od.c
-od_OBJECTS = od.$(OBJEXT)
-od_LDADD = $(LDADD)
-od_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-paste_SOURCES = paste.c
-paste_OBJECTS = paste.$(OBJEXT)
-paste_LDADD = $(LDADD)
-paste_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-pathchk_SOURCES = pathchk.c
-pathchk_OBJECTS = pathchk.$(OBJEXT)
-pathchk_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1)
-pinky_SOURCES = pinky.c
-pinky_OBJECTS = pinky.$(OBJEXT)
-pinky_LDADD = $(LDADD)
-pinky_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-pr_SOURCES = pr.c
-pr_OBJECTS = pr.$(OBJEXT)
-pr_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1)
-printenv_SOURCES = printenv.c
-printenv_OBJECTS = printenv.$(OBJEXT)
-printenv_LDADD = $(LDADD)
-printenv_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-printf_SOURCES = printf.c
-printf_OBJECTS = printf.$(OBJEXT)
-printf_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1)
-ptx_SOURCES = ptx.c
-ptx_OBJECTS = ptx.$(OBJEXT)
-ptx_LDADD = $(LDADD)
-ptx_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-pwd_SOURCES = pwd.c
-pwd_OBJECTS = pwd.$(OBJEXT)
-pwd_LDADD = $(LDADD)
-pwd_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-readlink_SOURCES = readlink.c
-readlink_OBJECTS = readlink.$(OBJEXT)
-readlink_LDADD = $(LDADD)
-readlink_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-am_rm_OBJECTS = rm.$(OBJEXT) remove.$(OBJEXT)
-rm_OBJECTS = $(am_rm_OBJECTS)
-rm_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1)
-rmdir_SOURCES = rmdir.c
-rmdir_OBJECTS = rmdir.$(OBJEXT)
-rmdir_LDADD = $(LDADD)
-rmdir_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-seq_SOURCES = seq.c
-seq_OBJECTS = seq.$(OBJEXT)
-seq_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1)
-setuidgid_SOURCES = setuidgid.c
-setuidgid_OBJECTS = setuidgid.$(OBJEXT)
-setuidgid_LDADD = $(LDADD)
-setuidgid_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-am_sha1sum_OBJECTS = sha1sum-md5sum.$(OBJEXT)
-sha1sum_OBJECTS = $(am_sha1sum_OBJECTS)
-sha1sum_LDADD = $(LDADD)
-sha1sum_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-am_sha224sum_OBJECTS = sha224sum-md5sum.$(OBJEXT)
-sha224sum_OBJECTS = $(am_sha224sum_OBJECTS)
-sha224sum_LDADD = $(LDADD)
-sha224sum_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-am_sha256sum_OBJECTS = sha256sum-md5sum.$(OBJEXT)
-sha256sum_OBJECTS = $(am_sha256sum_OBJECTS)
-sha256sum_LDADD = $(LDADD)
-sha256sum_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-am_sha384sum_OBJECTS = sha384sum-md5sum.$(OBJEXT)
-sha384sum_OBJECTS = $(am_sha384sum_OBJECTS)
-sha384sum_LDADD = $(LDADD)
-sha384sum_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-am_sha512sum_OBJECTS = sha512sum-md5sum.$(OBJEXT)
-sha512sum_OBJECTS = $(am_sha512sum_OBJECTS)
-sha512sum_LDADD = $(LDADD)
-sha512sum_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-shred_SOURCES = shred.c
-shred_OBJECTS = shred.$(OBJEXT)
-shred_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1)
-shuf_SOURCES = shuf.c
-shuf_OBJECTS = shuf.$(OBJEXT)
-shuf_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1)
-sleep_SOURCES = sleep.c
-sleep_OBJECTS = sleep.$(OBJEXT)
-am__DEPENDENCIES_3 = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1)
-sleep_DEPENDENCIES = $(am__DEPENDENCIES_3)
-sort_SOURCES = sort.c
-sort_OBJECTS = sort.$(OBJEXT)
-sort_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1)
-split_SOURCES = split.c
-split_OBJECTS = split.$(OBJEXT)
-split_LDADD = $(LDADD)
-split_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-stat_SOURCES = stat.c
-stat_OBJECTS = stat.$(OBJEXT)
-stat_LDADD = $(LDADD)
-stat_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-stty_SOURCES = stty.c
-stty_OBJECTS = stty.$(OBJEXT)
-stty_LDADD = $(LDADD)
-stty_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-su_SOURCES = su.c
-su_OBJECTS = su.$(OBJEXT)
-su_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1)
-sum_SOURCES = sum.c
-sum_OBJECTS = sum.$(OBJEXT)
-sum_LDADD = $(LDADD)
-sum_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-sync_SOURCES = sync.c
-sync_OBJECTS = sync.$(OBJEXT)
-sync_LDADD = $(LDADD)
-sync_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-tac_SOURCES = tac.c
-tac_OBJECTS = tac.$(OBJEXT)
-tac_LDADD = $(LDADD)
-tac_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-tail_SOURCES = tail.c
-tail_OBJECTS = tail.$(OBJEXT)
-tail_DEPENDENCIES = $(am__DEPENDENCIES_3)
-tee_SOURCES = tee.c
-tee_OBJECTS = tee.$(OBJEXT)
-tee_LDADD = $(LDADD)
-tee_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-test_SOURCES = test.c
-test_OBJECTS = test.$(OBJEXT)
-test_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1)
-touch_SOURCES = touch.c
-touch_OBJECTS = touch.$(OBJEXT)
-touch_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1)
-tr_SOURCES = tr.c
-tr_OBJECTS = tr.$(OBJEXT)
-tr_LDADD = $(LDADD)
-tr_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-true_SOURCES = true.c
-true_OBJECTS = true.$(OBJEXT)
-true_LDADD = $(LDADD)
-true_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-tsort_SOURCES = tsort.c
-tsort_OBJECTS = tsort.$(OBJEXT)
-tsort_LDADD = $(LDADD)
-tsort_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-tty_SOURCES = tty.c
-tty_OBJECTS = tty.$(OBJEXT)
-tty_LDADD = $(LDADD)
-tty_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-uname_SOURCES = uname.c
-uname_OBJECTS = uname.$(OBJEXT)
-uname_LDADD = $(LDADD)
-uname_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-unexpand_SOURCES = unexpand.c
-unexpand_OBJECTS = unexpand.$(OBJEXT)
-unexpand_LDADD = $(LDADD)
-unexpand_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-uniq_SOURCES = uniq.c
-uniq_OBJECTS = uniq.$(OBJEXT)
-uniq_LDADD = $(LDADD)
-uniq_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-unlink_SOURCES = unlink.c
-unlink_OBJECTS = unlink.$(OBJEXT)
-unlink_LDADD = $(LDADD)
-unlink_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-uptime_SOURCES = uptime.c
-uptime_OBJECTS = uptime.$(OBJEXT)
-uptime_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1)
-users_SOURCES = users.c
-users_OBJECTS = users.$(OBJEXT)
-users_LDADD = $(LDADD)
-users_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-am_vdir_OBJECTS = ls.$(OBJEXT) ls-vdir.$(OBJEXT)
-vdir_OBJECTS = $(am_vdir_OBJECTS)
-vdir_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
-wc_SOURCES = wc.c
-wc_OBJECTS = wc.$(OBJEXT)
-wc_LDADD = $(LDADD)
-wc_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-who_SOURCES = who.c
-who_OBJECTS = who.$(OBJEXT)
-who_LDADD = $(LDADD)
-who_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-whoami_SOURCES = whoami.c
-whoami_OBJECTS = whoami.$(OBJEXT)
-whoami_LDADD = $(LDADD)
-whoami_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-yes_SOURCES = yes.c
-yes_OBJECTS = yes.$(OBJEXT)
-yes_LDADD = $(LDADD)
-yes_DEPENDENCIES = ../lib/libcoreutils.a $(am__DEPENDENCIES_1) \
- ../lib/libcoreutils.a
-binSCRIPT_INSTALL = $(INSTALL_SCRIPT)
-SCRIPTS = $(bin_SCRIPTS)
-DEFAULT_INCLUDES = -I. -I$(top_builddir)/lib@am__isrc@
-depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
-am__depfiles_maybe = depfiles
-COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
- $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-CCLD = $(CC)
-LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
-SOURCES = $(__SOURCES) base64.c basename.c cat.c $(chgrp_SOURCES) \
- chmod.c $(chown_SOURCES) chroot.c cksum.c comm.c $(cp_SOURCES) \
- csplit.c cut.c date.c dd.c df.c $(dir_SOURCES) dircolors.c \
- dirname.c du.c echo.c env.c expand.c expr.c factor.c false.c \
- fmt.c fold.c $(ginstall_SOURCES) head.c hostid.c hostname.c \
- id.c join.c kill.c link.c ln.c logname.c $(ls_SOURCES) \
- $(md5sum_SOURCES) mkdir.c mkfifo.c mknod.c $(mv_SOURCES) \
- nice.c nl.c nohup.c od.c paste.c pathchk.c pinky.c pr.c \
- printenv.c printf.c ptx.c pwd.c readlink.c $(rm_SOURCES) \
- rmdir.c seq.c setuidgid.c $(sha1sum_SOURCES) \
- $(sha224sum_SOURCES) $(sha256sum_SOURCES) $(sha384sum_SOURCES) \
- $(sha512sum_SOURCES) shred.c shuf.c sleep.c sort.c split.c \
- stat.c stty.c su.c sum.c sync.c tac.c tail.c tee.c test.c \
- touch.c tr.c true.c tsort.c tty.c uname.c unexpand.c uniq.c \
- unlink.c uptime.c users.c $(vdir_SOURCES) wc.c who.c whoami.c \
- yes.c
-DIST_SOURCES = $(__SOURCES) base64.c basename.c cat.c $(chgrp_SOURCES) \
- chmod.c $(chown_SOURCES) chroot.c cksum.c comm.c $(cp_SOURCES) \
- csplit.c cut.c date.c dd.c df.c $(dir_SOURCES) dircolors.c \
- dirname.c du.c echo.c env.c expand.c expr.c factor.c false.c \
- fmt.c fold.c $(ginstall_SOURCES) head.c hostid.c hostname.c \
- id.c join.c kill.c link.c ln.c logname.c $(ls_SOURCES) \
- $(md5sum_SOURCES) mkdir.c mkfifo.c mknod.c $(mv_SOURCES) \
- nice.c nl.c nohup.c od.c paste.c pathchk.c pinky.c pr.c \
- printenv.c printf.c ptx.c pwd.c readlink.c $(rm_SOURCES) \
- rmdir.c seq.c setuidgid.c $(sha1sum_SOURCES) \
- $(sha224sum_SOURCES) $(sha256sum_SOURCES) $(sha384sum_SOURCES) \
- $(sha512sum_SOURCES) shred.c shuf.c sleep.c sort.c split.c \
- stat.c stty.c su.c sum.c sync.c tac.c tail.c tee.c test.c \
- touch.c tr.c true.c tsort.c tty.c uname.c unexpand.c uniq.c \
- unlink.c uptime.c users.c $(vdir_SOURCES) wc.c who.c whoami.c \
- yes.c
-HEADERS = $(noinst_HEADERS)
-ETAGS = etags
-CTAGS = ctags
-DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
-
-# Use `ginstall' in the definition of PROGRAMS and in dependencies to avoid
-# confusion with the `install' target. The install rule transforms `ginstall'
-# to install before applying any user-specified name transformations.
-transform = s/ginstall/install/; @program_transform_name@
-ABSOLUTE_DIRENT_H = @ABSOLUTE_DIRENT_H@
-ABSOLUTE_FCNTL_H = @ABSOLUTE_FCNTL_H@
-ABSOLUTE_INTTYPES_H = @ABSOLUTE_INTTYPES_H@
-ABSOLUTE_NETINET_IN_H = @ABSOLUTE_NETINET_IN_H@
-ABSOLUTE_STDINT_H = @ABSOLUTE_STDINT_H@
-ABSOLUTE_STDIO_H = @ABSOLUTE_STDIO_H@
-ABSOLUTE_STDLIB_H = @ABSOLUTE_STDLIB_H@
-ABSOLUTE_STRING_H = @ABSOLUTE_STRING_H@
-ABSOLUTE_SYS_SOCKET_H = @ABSOLUTE_SYS_SOCKET_H@
-ABSOLUTE_SYS_STAT_H = @ABSOLUTE_SYS_STAT_H@
-ABSOLUTE_SYS_TIME_H = @ABSOLUTE_SYS_TIME_H@
-ABSOLUTE_TIME_H = @ABSOLUTE_TIME_H@
-ABSOLUTE_UNISTD_H = @ABSOLUTE_UNISTD_H@
-ABSOLUTE_WCHAR_H = @ABSOLUTE_WCHAR_H@
-ABSOLUTE_WCTYPE_H = @ABSOLUTE_WCTYPE_H@
-ACLOCAL = @ACLOCAL@
-ALLOCA = @ALLOCA@
-ALLOCA_H = @ALLOCA_H@
-AMTAR = @AMTAR@
-ARPA_INET_H = @ARPA_INET_H@
-AUTOCONF = @AUTOCONF@
-AUTOHEADER = @AUTOHEADER@
-AUTOMAKE = @AUTOMAKE@
-AWK = @AWK@
-BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@
-BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@
-BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@
-BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@
-BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@
-CC = @CC@
-CCDEPMODE = @CCDEPMODE@
-CFLAGS = @CFLAGS@
-CPP = @CPP@
-CPPFLAGS = @CPPFLAGS@
-CYGPATH_W = @CYGPATH_W@
-DEFAULT_POSIX2_VERSION = @DEFAULT_POSIX2_VERSION@
-DEFS = @DEFS@
-DEPDIR = @DEPDIR@
-DF_PROG = @DF_PROG@
-DIRENT_H = @DIRENT_H@
-ECHO_C = @ECHO_C@
-ECHO_N = @ECHO_N@
-ECHO_T = @ECHO_T@
-EGREP = @EGREP@
-EOVERFLOW = @EOVERFLOW@
-EXEEXT = @EXEEXT@
-FCNTL_H = @FCNTL_H@
-FNMATCH_H = @FNMATCH_H@
-GETLOADAVG_LIBS = @GETLOADAVG_LIBS@
-GETOPT_H = @GETOPT_H@
-GLIBC21 = @GLIBC21@
-GMSGFMT = @GMSGFMT@
-GMSGFMT_015 = @GMSGFMT_015@
-GNULIB_CHOWN = @GNULIB_CHOWN@
-GNULIB_DUP2 = @GNULIB_DUP2@
-GNULIB_FCHDIR = @GNULIB_FCHDIR@
-GNULIB_FPRINTF_POSIX = @GNULIB_FPRINTF_POSIX@
-GNULIB_FTRUNCATE = @GNULIB_FTRUNCATE@
-GNULIB_GETCWD = @GNULIB_GETCWD@
-GNULIB_GETLOGIN_R = @GNULIB_GETLOGIN_R@
-GNULIB_GETSUBOPT = @GNULIB_GETSUBOPT@
-GNULIB_IMAXABS = @GNULIB_IMAXABS@
-GNULIB_IMAXDIV = @GNULIB_IMAXDIV@
-GNULIB_MBSCASECMP = @GNULIB_MBSCASECMP@
-GNULIB_MBSCASESTR = @GNULIB_MBSCASESTR@
-GNULIB_MBSCHR = @GNULIB_MBSCHR@
-GNULIB_MBSCSPN = @GNULIB_MBSCSPN@
-GNULIB_MBSLEN = @GNULIB_MBSLEN@
-GNULIB_MBSNCASECMP = @GNULIB_MBSNCASECMP@
-GNULIB_MBSPBRK = @GNULIB_MBSPBRK@
-GNULIB_MBSPCASECMP = @GNULIB_MBSPCASECMP@
-GNULIB_MBSRCHR = @GNULIB_MBSRCHR@
-GNULIB_MBSSEP = @GNULIB_MBSSEP@
-GNULIB_MBSSPN = @GNULIB_MBSSPN@
-GNULIB_MBSSTR = @GNULIB_MBSSTR@
-GNULIB_MBSTOK_R = @GNULIB_MBSTOK_R@
-GNULIB_MEMMEM = @GNULIB_MEMMEM@
-GNULIB_MEMPCPY = @GNULIB_MEMPCPY@
-GNULIB_MEMRCHR = @GNULIB_MEMRCHR@
-GNULIB_MKDTEMP = @GNULIB_MKDTEMP@
-GNULIB_MKSTEMP = @GNULIB_MKSTEMP@
-GNULIB_PRINTF_POSIX = @GNULIB_PRINTF_POSIX@
-GNULIB_READLINK = @GNULIB_READLINK@
-GNULIB_SNPRINTF = @GNULIB_SNPRINTF@
-GNULIB_SPRINTF_POSIX = @GNULIB_SPRINTF_POSIX@
-GNULIB_STPCPY = @GNULIB_STPCPY@
-GNULIB_STPNCPY = @GNULIB_STPNCPY@
-GNULIB_STRCASESTR = @GNULIB_STRCASESTR@
-GNULIB_STRCHRNUL = @GNULIB_STRCHRNUL@
-GNULIB_STRDUP = @GNULIB_STRDUP@
-GNULIB_STRNDUP = @GNULIB_STRNDUP@
-GNULIB_STRNLEN = @GNULIB_STRNLEN@
-GNULIB_STRPBRK = @GNULIB_STRPBRK@
-GNULIB_STRSEP = @GNULIB_STRSEP@
-GNULIB_STRTOIMAX = @GNULIB_STRTOIMAX@
-GNULIB_STRTOK_R = @GNULIB_STRTOK_R@
-GNULIB_STRTOUMAX = @GNULIB_STRTOUMAX@
-GNULIB_VFPRINTF_POSIX = @GNULIB_VFPRINTF_POSIX@
-GNULIB_VPRINTF_POSIX = @GNULIB_VPRINTF_POSIX@
-GNULIB_VSNPRINTF = @GNULIB_VSNPRINTF@
-GNULIB_VSPRINTF_POSIX = @GNULIB_VSPRINTF_POSIX@
-GNU_PACKAGE = @GNU_PACKAGE@
-GREP = @GREP@
-HAVE_DECL_GETLOGIN_R = @HAVE_DECL_GETLOGIN_R@
-HAVE_DECL_IMAXABS = @HAVE_DECL_IMAXABS@
-HAVE_DECL_IMAXDIV = @HAVE_DECL_IMAXDIV@
-HAVE_DECL_MEMMEM = @HAVE_DECL_MEMMEM@
-HAVE_DECL_MEMRCHR = @HAVE_DECL_MEMRCHR@
-HAVE_DECL_SNPRINTF = @HAVE_DECL_SNPRINTF@
-HAVE_DECL_STRDUP = @HAVE_DECL_STRDUP@
-HAVE_DECL_STRNCASECMP = @HAVE_DECL_STRNCASECMP@
-HAVE_DECL_STRNDUP = @HAVE_DECL_STRNDUP@
-HAVE_DECL_STRNLEN = @HAVE_DECL_STRNLEN@
-HAVE_DECL_STRTOIMAX = @HAVE_DECL_STRTOIMAX@
-HAVE_DECL_STRTOK_R = @HAVE_DECL_STRTOK_R@
-HAVE_DECL_STRTOUMAX = @HAVE_DECL_STRTOUMAX@
-HAVE_DECL_VSNPRINTF = @HAVE_DECL_VSNPRINTF@
-HAVE_DUP2 = @HAVE_DUP2@
-HAVE_FTRUNCATE = @HAVE_FTRUNCATE@
-HAVE_GETSUBOPT = @HAVE_GETSUBOPT@
-HAVE_INTTYPES_H = @HAVE_INTTYPES_H@
-HAVE_LONG_LONG_INT = @HAVE_LONG_LONG_INT@
-HAVE_MEMPCPY = @HAVE_MEMPCPY@
-HAVE_MKDTEMP = @HAVE_MKDTEMP@
-HAVE_NETINET_IN_H = @HAVE_NETINET_IN_H@
-HAVE_READLINK = @HAVE_READLINK@
-HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@
-HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@
-HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@
-HAVE_STDINT_H = @HAVE_STDINT_H@
-HAVE_STPCPY = @HAVE_STPCPY@
-HAVE_STPNCPY = @HAVE_STPNCPY@
-HAVE_STRCASECMP = @HAVE_STRCASECMP@
-HAVE_STRCASESTR = @HAVE_STRCASESTR@
-HAVE_STRCHRNUL = @HAVE_STRCHRNUL@
-HAVE_STRNDUP = @HAVE_STRNDUP@
-HAVE_STRPBRK = @HAVE_STRPBRK@
-HAVE_STRSEP = @HAVE_STRSEP@
-HAVE_STRUCT_TIMEVAL = @HAVE_STRUCT_TIMEVAL@
-HAVE_SYS_BITYPES_H = @HAVE_SYS_BITYPES_H@
-HAVE_SYS_INTTYPES_H = @HAVE_SYS_INTTYPES_H@
-HAVE_SYS_SOCKET_H = @HAVE_SYS_SOCKET_H@
-HAVE_SYS_TIME_H = @HAVE_SYS_TIME_H@
-HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@
-HAVE_UNISTD_H = @HAVE_UNISTD_H@
-HAVE_UNSIGNED_LONG_LONG_INT = @HAVE_UNSIGNED_LONG_LONG_INT@
-HAVE_WCTYPE_H = @HAVE_WCTYPE_H@
-HAVE_WINSOCK2_H = @HAVE_WINSOCK2_H@
-HAVE_WINT_T = @HAVE_WINT_T@
-HAVE_WS2TCPIP_H = @HAVE_WS2TCPIP_H@
-HAVE__BOOL = @HAVE__BOOL@
-HELP2MAN = @HELP2MAN@
-INSTALL = @INSTALL@
-INSTALL_DATA = @INSTALL_DATA@
-INSTALL_PROGRAM = @INSTALL_PROGRAM@
-INSTALL_SCRIPT = @INSTALL_SCRIPT@
-INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
-INTLLIBS = @INTLLIBS@
-INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
-INTTYPES_H = @INTTYPES_H@
-KMEM_GROUP = @KMEM_GROUP@
-LDFLAGS = @LDFLAGS@
-LIBCOREUTILS_LIBDEPS = @LIBCOREUTILS_LIBDEPS@
-LIBCOREUTILS_LTLIBDEPS = @LIBCOREUTILS_LTLIBDEPS@
-LIBICONV = @LIBICONV@
-LIBINTL = @LIBINTL@
-LIBOBJS = @LIBOBJS@
-LIBS = @LIBS@
-LIB_ACL = @LIB_ACL@
-LIB_ACL_TRIVIAL = @LIB_ACL_TRIVIAL@
-LIB_CLOCK_GETTIME = @LIB_CLOCK_GETTIME@
-LIB_CRYPT = @LIB_CRYPT@
-LIB_EACCESS = @LIB_EACCESS@
-LIB_FDATASYNC = @LIB_FDATASYNC@
-LIB_GETHRXTIME = @LIB_GETHRXTIME@
-LIB_NANOSLEEP = @LIB_NANOSLEEP@
-LN_S = @LN_S@
-LTLIBICONV = @LTLIBICONV@
-LTLIBINTL = @LTLIBINTL@
-LTLIBOBJS = @LTLIBOBJS@
-MAKEINFO = @MAKEINFO@
-MAN = @MAN@
-MKDIR_P = @MKDIR_P@
-MSGFMT = @MSGFMT@
-MSGFMT_015 = @MSGFMT_015@
-MSGMERGE = @MSGMERGE@
-NEED_SETGID = @NEED_SETGID@
-NETINET_IN_H = @NETINET_IN_H@
-OBJEXT = @OBJEXT@
-OPTIONAL_BIN_PROGS = @OPTIONAL_BIN_PROGS@
-PACKAGE = @PACKAGE@
-PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
-PACKAGE_NAME = @PACKAGE_NAME@
-PACKAGE_STRING = @PACKAGE_STRING@
-PACKAGE_TARNAME = @PACKAGE_TARNAME@
-PACKAGE_VERSION = @PACKAGE_VERSION@
-PATH_SEPARATOR = @PATH_SEPARATOR@
-PERL = @PERL@
-POSUB = @POSUB@
-POW_LIB = @POW_LIB@
-PRIPTR_PREFIX = @PRIPTR_PREFIX@
-PRI_MACROS_BROKEN = @PRI_MACROS_BROKEN@
-PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@
-RANLIB = @RANLIB@
-REPLACE_CHOWN = @REPLACE_CHOWN@
-REPLACE_FCHDIR = @REPLACE_FCHDIR@
-REPLACE_FPRINTF = @REPLACE_FPRINTF@
-REPLACE_GETCWD = @REPLACE_GETCWD@
-REPLACE_GETTIMEOFDAY = @REPLACE_GETTIMEOFDAY@
-REPLACE_LOCALTIME_R = @REPLACE_LOCALTIME_R@
-REPLACE_MKSTEMP = @REPLACE_MKSTEMP@
-REPLACE_NANOSLEEP = @REPLACE_NANOSLEEP@
-REPLACE_PRINTF = @REPLACE_PRINTF@
-REPLACE_SNPRINTF = @REPLACE_SNPRINTF@
-REPLACE_SPRINTF = @REPLACE_SPRINTF@
-REPLACE_STRPTIME = @REPLACE_STRPTIME@
-REPLACE_TIMEGM = @REPLACE_TIMEGM@
-REPLACE_VFPRINTF = @REPLACE_VFPRINTF@
-REPLACE_VPRINTF = @REPLACE_VPRINTF@
-REPLACE_VSNPRINTF = @REPLACE_VSNPRINTF@
-REPLACE_VSPRINTF = @REPLACE_VSPRINTF@
-SEQ_LIBM = @SEQ_LIBM@
-SET_MAKE = @SET_MAKE@
-SHELL = @SHELL@
-SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@
-SIZE_T_SUFFIX = @SIZE_T_SUFFIX@
-STDBOOL_H = @STDBOOL_H@
-STDINT_H = @STDINT_H@
-STRIP = @STRIP@
-SYS_SOCKET_H = @SYS_SOCKET_H@
-SYS_STAT_H = @SYS_STAT_H@
-SYS_TIME_H = @SYS_TIME_H@
-SYS_TIME_H_DEFINES_STRUCT_TIMESPEC = @SYS_TIME_H_DEFINES_STRUCT_TIMESPEC@
-TIME_H_DEFINES_STRUCT_TIMESPEC = @TIME_H_DEFINES_STRUCT_TIMESPEC@
-U = @U@
-USE_NLS = @USE_NLS@
-VERSION = @VERSION@
-WCHAR_H = @WCHAR_H@
-WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@
-WCTYPE_H = @WCTYPE_H@
-WINT_T_SUFFIX = @WINT_T_SUFFIX@
-XGETTEXT = @XGETTEXT@
-XGETTEXT_015 = @XGETTEXT_015@
-YACC = @YACC@
-YFLAGS = @YFLAGS@
-abs_builddir = @abs_builddir@
-abs_srcdir = @abs_srcdir@
-abs_top_builddir = @abs_top_builddir@
-abs_top_srcdir = @abs_top_srcdir@
-ac_ct_CC = @ac_ct_CC@
-am__include = @am__include@
-am__leading_dot = @am__leading_dot@
-am__quote = @am__quote@
-am__tar = @am__tar@
-am__untar = @am__untar@
-bindir = @bindir@
-build = @build@
-build_alias = @build_alias@
-build_cpu = @build_cpu@
-build_os = @build_os@
-build_vendor = @build_vendor@
-builddir = @builddir@
-datadir = @datadir@
-datarootdir = @datarootdir@
-docdir = @docdir@
-dvidir = @dvidir@
-exec_prefix = @exec_prefix@
-gl_LIBOBJS = @gl_LIBOBJS@
-gl_LTLIBOBJS = @gl_LTLIBOBJS@
-host = @host@
-host_alias = @host_alias@
-host_cpu = @host_cpu@
-host_os = @host_os@
-host_vendor = @host_vendor@
-htmldir = @htmldir@
-includedir = @includedir@
-infodir = @infodir@
-install_sh = @install_sh@
-libdir = @libdir@
-libexecdir = @libexecdir@
-localedir = @localedir@
-localstatedir = @localstatedir@
-mandir = @mandir@
-mkdir_p = @mkdir_p@
-oldincludedir = @oldincludedir@
-pdfdir = @pdfdir@
-prefix = @prefix@
-program_transform_name = @program_transform_name@
-psdir = @psdir@
-sbindir = @sbindir@
-sharedstatedir = @sharedstatedir@
-srcdir = @srcdir@
-sysconfdir = @sysconfdir@
-target_alias = @target_alias@
-top_builddir = @top_builddir@
-top_srcdir = @top_srcdir@
-bin_SCRIPTS = groups
-noinst_HEADERS = \
- chown-core.h \
- copy.h \
- cp-hash.h \
- dircolors.h \
- fs.h \
- ls.h \
- remove.h \
- system.h \
- wheel-size.h \
- wheel.h
-
-EXTRA_DIST = dcgen dircolors.hin tac-pipe.c \
- groups.sh wheel-gen.pl extract-magic c99-to-c89.diff
-
-BUILT_SOURCES = dircolors.h wheel-size.h wheel.h fs.h
-CLEANFILES = $(SCRIPTS) su
-AM_CPPFLAGS = -I$(top_srcdir)/lib
-
-# Sometimes, the expansion of $(LIBINTL) includes -lc which may
-# include modules defining variables like `optind', so libcoreutils.a
-# must precede $(LIBINTL) in order to ensure we use GNU getopt.
-# But libcoreutils.a must also follow $(LIBINTL), since libintl uses
-# replacement functions defined in libcoreutils.a.
-LDADD = ../lib/libcoreutils.a $(LIBINTL) ../lib/libcoreutils.a
-
-# for eaccess in lib/euidaccess.c.
-cp_LDADD = $(LDADD) $(LIB_EACCESS) $(LIB_ACL)
-ginstall_LDADD = $(LDADD) $(LIB_EACCESS) $(LIB_ACL)
-mv_LDADD = $(LDADD) $(LIB_EACCESS) $(LIB_ACL)
-pathchk_LDADD = $(LDADD) $(LIB_EACCESS)
-rm_LDADD = $(LDADD) $(LIB_EACCESS)
-test_LDADD = $(LDADD) $(LIB_EACCESS)
-# This is for the '[' program. Automake transliterates '[' to '_'.
-__LDADD = $(LDADD) $(LIB_EACCESS)
-
-# for clock_gettime and fdatasync
-dd_LDADD = $(LDADD) $(LIB_GETHRXTIME) $(LIB_FDATASYNC)
-dir_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_ACL_TRIVIAL) \
- $(LIB_ACL)
-ls_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_ACL_TRIVIAL) $(LIB_ACL)
-pr_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME)
-shred_LDADD = $(LDADD) $(LIB_GETHRXTIME) $(LIB_FDATASYNC)
-shuf_LDADD = $(LDADD) $(LIB_GETHRXTIME)
-vdir_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_ACL_TRIVIAL) \
- $(LIB_ACL)
-sort_LDADD = $(LDADD) $(POW_LIB) $(LIB_GETHRXTIME)
-
-# for get_date and gettime
-date_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME)
-touch_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME)
-
-# If necessary, add -lm to resolve use of pow in lib/strtod.c.
-# If necessary, add -liconv to resolve use of iconv in lib/unicodeio.c.
-printf_LDADD = $(LDADD) $(POW_LIB) $(LIBICONV)
-
-# If necessary, add -lm to resolve use of pow in lib/strtod.c.
-seq_LDADD = $(LDADD) $(POW_LIB)
-
-# If necessary, add libraries to resolve the `pow' reference in lib/strtod.c
-# and the `nanosleep' reference in lib/xnanosleep.c.
-nanosec_libs = $(LDADD) $(POW_LIB) $(LIB_NANOSLEEP)
-sleep_LDADD = $(nanosec_libs)
-tail_LDADD = $(nanosec_libs)
-
-# If necessary, add -lm to resolve use of pow in lib/strtod.c.
-uptime_LDADD = $(LDADD) $(POW_LIB) $(GETLOADAVG_LIBS)
-su_LDADD = $(LDADD) $(LIB_CRYPT)
-SUFFIXES = .sh
-
-# 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' \
- $(top_srcdir)/lib/version-etc.c`
-
-installed_su = $(DESTDIR)$(bindir)/`echo su|sed '$(transform)'`
-setuid_root_mode = a=rx,u+s
-INSTALL_SU = \
- p=su; \
- echo " $(INSTALL_PROGRAM) $$p $(installed_su)"; \
- $(INSTALL_PROGRAM) $$p $(installed_su); \
- echo " chown root $(installed_su)"; \
- chown root $(installed_su); \
- echo " chmod $(setuid_root_mode) $(installed_su)"; \
- chmod $(setuid_root_mode) $(installed_su)
-
-ginstall_SOURCES = install.c copy.c cp-hash.c
-
-# This is for the '[' program. Automake transliterates '[' to '_'.
-__SOURCES = lbracket.c
-cp_SOURCES = cp.c copy.c cp-hash.c
-dir_SOURCES = ls.c ls-dir.c
-vdir_SOURCES = ls.c ls-vdir.c
-ls_SOURCES = ls.c ls-ls.c
-chown_SOURCES = chown.c chown-core.c
-chgrp_SOURCES = chgrp.c chown-core.c
-mv_SOURCES = mv.c copy.c cp-hash.c remove.c
-rm_SOURCES = rm.c remove.c
-md5sum_SOURCES = md5sum.c
-md5sum_CPPFLAGS = -DHASH_ALGO_MD5=1 $(AM_CPPFLAGS)
-sha1sum_SOURCES = md5sum.c
-sha1sum_CPPFLAGS = -DHASH_ALGO_SHA1=1 $(AM_CPPFLAGS)
-sha224sum_SOURCES = md5sum.c
-sha224sum_CPPFLAGS = -DHASH_ALGO_SHA224=1 $(AM_CPPFLAGS)
-sha256sum_SOURCES = md5sum.c
-sha256sum_CPPFLAGS = -DHASH_ALGO_SHA256=1 $(AM_CPPFLAGS)
-sha384sum_SOURCES = md5sum.c
-sha384sum_CPPFLAGS = -DHASH_ALGO_SHA384=1 $(AM_CPPFLAGS)
-sha512sum_SOURCES = md5sum.c
-sha512sum_CPPFLAGS = -DHASH_ALGO_SHA512=1 $(AM_CPPFLAGS)
-editpl = sed -e 's,@''PERL''@,$(PERL),g'
-wheel_size = 5
-
-# false exits nonzero even with --help or --version.
-# test doesn't support --help or --version.
-# Tell automake to exempt then from that installcheck test.
-AM_INSTALLCHECK_STD_OPTIONS_EXEMPT = false test
-MAINTAINERCLEANFILES = $(BUILT_SOURCES)
-
-# Sort in traditional ASCII order, regardless of the current locale;
-# otherwise we may get into trouble with distinct strings that the
-# current locale considers to be equal.
-ASSORT = LC_ALL=C sort
-all_programs = \
- $(bin_PROGRAMS) \
- $(bin_SCRIPTS) \
- $(EXTRA_PROGRAMS)
-
-pm = progs-makefile
-pr = progs-readme
-
-# Ensure that the list of programs and author names is accurate.
-au_dotdot = authors-dotdot
-au_actual = authors-actual
-
-# Extract the list of authors from each file.
-sed_filter = s/^ *//;s/N_ (//;s/^"//;s/")*$$//
-# Sometimes the string is on the same line as the #define...
-s1 = '/^\#define AUTHORS \([^\\]\)/{;s//\1/;$(sed_filter);p;q;}'
-# Sometimes the string is on the backslash-continued line after the #define.
-s2 = '/^\#define AUTHORS \\\\/{;n;$(sed_filter);p;q;}'
-all: $(BUILT_SOURCES)
- $(MAKE) $(AM_MAKEFLAGS) all-am
-
-.SUFFIXES:
-.SUFFIXES: .sh .c .o .obj
-$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
- @for dep in $?; do \
- case '$(am__configure_deps)' in \
- *$$dep*) \
- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
- && exit 0; \
- exit 1;; \
- esac; \
- done; \
- echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
- cd $(top_srcdir) && \
- $(AUTOMAKE) --gnu src/Makefile
-.PRECIOUS: Makefile
-Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
- @case '$?' in \
- *config.status*) \
- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
- *) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
- esac;
-
-$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-
-$(top_srcdir)/configure: $(am__configure_deps)
- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(ACLOCAL_M4): $(am__aclocal_m4_deps)
- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-install-binPROGRAMS: $(bin_PROGRAMS)
- @$(NORMAL_INSTALL)
- test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
- @list='$(bin_PROGRAMS)'; for p in $$list; do \
- p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
- if test -f $$p \
- ; then \
- f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
- echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
- $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
- else :; fi; \
- done
-
-uninstall-binPROGRAMS:
- @$(NORMAL_UNINSTALL)
- @list='$(bin_PROGRAMS)'; for p in $$list; do \
- f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
- echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
- rm -f "$(DESTDIR)$(bindir)/$$f"; \
- done
-
-clean-binPROGRAMS:
- -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) > /dev/null 2>&1 || /bin/rm -f $(bin_PROGRAMS)
-
-clean-noinstPROGRAMS:
- -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
-[$(EXEEXT): $(__OBJECTS) $(__DEPENDENCIES)
- @rm -f [$(EXEEXT)
- $(LINK) $(__OBJECTS) $(__LDADD) $(LIBS)
-base64$(EXEEXT): $(base64_OBJECTS) $(base64_DEPENDENCIES)
- @rm -f base64$(EXEEXT)
- $(LINK) $(base64_OBJECTS) $(base64_LDADD) $(LIBS)
-basename$(EXEEXT): $(basename_OBJECTS) $(basename_DEPENDENCIES)
- @rm -f basename$(EXEEXT)
- $(LINK) $(basename_OBJECTS) $(basename_LDADD) $(LIBS)
-cat$(EXEEXT): $(cat_OBJECTS) $(cat_DEPENDENCIES)
- @rm -f cat$(EXEEXT)
- $(LINK) $(cat_OBJECTS) $(cat_LDADD) $(LIBS)
-chgrp$(EXEEXT): $(chgrp_OBJECTS) $(chgrp_DEPENDENCIES)
- @rm -f chgrp$(EXEEXT)
- $(LINK) $(chgrp_OBJECTS) $(chgrp_LDADD) $(LIBS)
-chmod$(EXEEXT): $(chmod_OBJECTS) $(chmod_DEPENDENCIES)
- @rm -f chmod$(EXEEXT)
- $(LINK) $(chmod_OBJECTS) $(chmod_LDADD) $(LIBS)
-chown$(EXEEXT): $(chown_OBJECTS) $(chown_DEPENDENCIES)
- @rm -f chown$(EXEEXT)
- $(LINK) $(chown_OBJECTS) $(chown_LDADD) $(LIBS)
-chroot$(EXEEXT): $(chroot_OBJECTS) $(chroot_DEPENDENCIES)
- @rm -f chroot$(EXEEXT)
- $(LINK) $(chroot_OBJECTS) $(chroot_LDADD) $(LIBS)
-cksum$(EXEEXT): $(cksum_OBJECTS) $(cksum_DEPENDENCIES)
- @rm -f cksum$(EXEEXT)
- $(LINK) $(cksum_OBJECTS) $(cksum_LDADD) $(LIBS)
-comm$(EXEEXT): $(comm_OBJECTS) $(comm_DEPENDENCIES)
- @rm -f comm$(EXEEXT)
- $(LINK) $(comm_OBJECTS) $(comm_LDADD) $(LIBS)
-cp$(EXEEXT): $(cp_OBJECTS) $(cp_DEPENDENCIES)
- @rm -f cp$(EXEEXT)
- $(LINK) $(cp_OBJECTS) $(cp_LDADD) $(LIBS)
-csplit$(EXEEXT): $(csplit_OBJECTS) $(csplit_DEPENDENCIES)
- @rm -f csplit$(EXEEXT)
- $(LINK) $(csplit_OBJECTS) $(csplit_LDADD) $(LIBS)
-cut$(EXEEXT): $(cut_OBJECTS) $(cut_DEPENDENCIES)
- @rm -f cut$(EXEEXT)
- $(LINK) $(cut_OBJECTS) $(cut_LDADD) $(LIBS)
-date$(EXEEXT): $(date_OBJECTS) $(date_DEPENDENCIES)
- @rm -f date$(EXEEXT)
- $(LINK) $(date_OBJECTS) $(date_LDADD) $(LIBS)
-dd$(EXEEXT): $(dd_OBJECTS) $(dd_DEPENDENCIES)
- @rm -f dd$(EXEEXT)
- $(LINK) $(dd_OBJECTS) $(dd_LDADD) $(LIBS)
-df$(EXEEXT): $(df_OBJECTS) $(df_DEPENDENCIES)
- @rm -f df$(EXEEXT)
- $(LINK) $(df_OBJECTS) $(df_LDADD) $(LIBS)
-dir$(EXEEXT): $(dir_OBJECTS) $(dir_DEPENDENCIES)
- @rm -f dir$(EXEEXT)
- $(LINK) $(dir_OBJECTS) $(dir_LDADD) $(LIBS)
-dircolors$(EXEEXT): $(dircolors_OBJECTS) $(dircolors_DEPENDENCIES)
- @rm -f dircolors$(EXEEXT)
- $(LINK) $(dircolors_OBJECTS) $(dircolors_LDADD) $(LIBS)
-dirname$(EXEEXT): $(dirname_OBJECTS) $(dirname_DEPENDENCIES)
- @rm -f dirname$(EXEEXT)
- $(LINK) $(dirname_OBJECTS) $(dirname_LDADD) $(LIBS)
-du$(EXEEXT): $(du_OBJECTS) $(du_DEPENDENCIES)
- @rm -f du$(EXEEXT)
- $(LINK) $(du_OBJECTS) $(du_LDADD) $(LIBS)
-echo$(EXEEXT): $(echo_OBJECTS) $(echo_DEPENDENCIES)
- @rm -f echo$(EXEEXT)
- $(LINK) $(echo_OBJECTS) $(echo_LDADD) $(LIBS)
-env$(EXEEXT): $(env_OBJECTS) $(env_DEPENDENCIES)
- @rm -f env$(EXEEXT)
- $(LINK) $(env_OBJECTS) $(env_LDADD) $(LIBS)
-expand$(EXEEXT): $(expand_OBJECTS) $(expand_DEPENDENCIES)
- @rm -f expand$(EXEEXT)
- $(LINK) $(expand_OBJECTS) $(expand_LDADD) $(LIBS)
-expr$(EXEEXT): $(expr_OBJECTS) $(expr_DEPENDENCIES)
- @rm -f expr$(EXEEXT)
- $(LINK) $(expr_OBJECTS) $(expr_LDADD) $(LIBS)
-factor$(EXEEXT): $(factor_OBJECTS) $(factor_DEPENDENCIES)
- @rm -f factor$(EXEEXT)
- $(LINK) $(factor_OBJECTS) $(factor_LDADD) $(LIBS)
-false$(EXEEXT): $(false_OBJECTS) $(false_DEPENDENCIES)
- @rm -f false$(EXEEXT)
- $(LINK) $(false_OBJECTS) $(false_LDADD) $(LIBS)
-fmt$(EXEEXT): $(fmt_OBJECTS) $(fmt_DEPENDENCIES)
- @rm -f fmt$(EXEEXT)
- $(LINK) $(fmt_OBJECTS) $(fmt_LDADD) $(LIBS)
-fold$(EXEEXT): $(fold_OBJECTS) $(fold_DEPENDENCIES)
- @rm -f fold$(EXEEXT)
- $(LINK) $(fold_OBJECTS) $(fold_LDADD) $(LIBS)
-ginstall$(EXEEXT): $(ginstall_OBJECTS) $(ginstall_DEPENDENCIES)
- @rm -f ginstall$(EXEEXT)
- $(LINK) $(ginstall_OBJECTS) $(ginstall_LDADD) $(LIBS)
-head$(EXEEXT): $(head_OBJECTS) $(head_DEPENDENCIES)
- @rm -f head$(EXEEXT)
- $(LINK) $(head_OBJECTS) $(head_LDADD) $(LIBS)
-hostid$(EXEEXT): $(hostid_OBJECTS) $(hostid_DEPENDENCIES)
- @rm -f hostid$(EXEEXT)
- $(LINK) $(hostid_OBJECTS) $(hostid_LDADD) $(LIBS)
-hostname$(EXEEXT): $(hostname_OBJECTS) $(hostname_DEPENDENCIES)
- @rm -f hostname$(EXEEXT)
- $(LINK) $(hostname_OBJECTS) $(hostname_LDADD) $(LIBS)
-id$(EXEEXT): $(id_OBJECTS) $(id_DEPENDENCIES)
- @rm -f id$(EXEEXT)
- $(LINK) $(id_OBJECTS) $(id_LDADD) $(LIBS)
-join$(EXEEXT): $(join_OBJECTS) $(join_DEPENDENCIES)
- @rm -f join$(EXEEXT)
- $(LINK) $(join_OBJECTS) $(join_LDADD) $(LIBS)
-kill$(EXEEXT): $(kill_OBJECTS) $(kill_DEPENDENCIES)
- @rm -f kill$(EXEEXT)
- $(LINK) $(kill_OBJECTS) $(kill_LDADD) $(LIBS)
-link$(EXEEXT): $(link_OBJECTS) $(link_DEPENDENCIES)
- @rm -f link$(EXEEXT)
- $(LINK) $(link_OBJECTS) $(link_LDADD) $(LIBS)
-ln$(EXEEXT): $(ln_OBJECTS) $(ln_DEPENDENCIES)
- @rm -f ln$(EXEEXT)
- $(LINK) $(ln_OBJECTS) $(ln_LDADD) $(LIBS)
-logname$(EXEEXT): $(logname_OBJECTS) $(logname_DEPENDENCIES)
- @rm -f logname$(EXEEXT)
- $(LINK) $(logname_OBJECTS) $(logname_LDADD) $(LIBS)
-ls$(EXEEXT): $(ls_OBJECTS) $(ls_DEPENDENCIES)
- @rm -f ls$(EXEEXT)
- $(LINK) $(ls_OBJECTS) $(ls_LDADD) $(LIBS)
-md5sum$(EXEEXT): $(md5sum_OBJECTS) $(md5sum_DEPENDENCIES)
- @rm -f md5sum$(EXEEXT)
- $(LINK) $(md5sum_OBJECTS) $(md5sum_LDADD) $(LIBS)
-mkdir$(EXEEXT): $(mkdir_OBJECTS) $(mkdir_DEPENDENCIES)
- @rm -f mkdir$(EXEEXT)
- $(LINK) $(mkdir_OBJECTS) $(mkdir_LDADD) $(LIBS)
-mkfifo$(EXEEXT): $(mkfifo_OBJECTS) $(mkfifo_DEPENDENCIES)
- @rm -f mkfifo$(EXEEXT)
- $(LINK) $(mkfifo_OBJECTS) $(mkfifo_LDADD) $(LIBS)
-mknod$(EXEEXT): $(mknod_OBJECTS) $(mknod_DEPENDENCIES)
- @rm -f mknod$(EXEEXT)
- $(LINK) $(mknod_OBJECTS) $(mknod_LDADD) $(LIBS)
-mv$(EXEEXT): $(mv_OBJECTS) $(mv_DEPENDENCIES)
- @rm -f mv$(EXEEXT)
- $(LINK) $(mv_OBJECTS) $(mv_LDADD) $(LIBS)
-nice$(EXEEXT): $(nice_OBJECTS) $(nice_DEPENDENCIES)
- @rm -f nice$(EXEEXT)
- $(LINK) $(nice_OBJECTS) $(nice_LDADD) $(LIBS)
-nl$(EXEEXT): $(nl_OBJECTS) $(nl_DEPENDENCIES)
- @rm -f nl$(EXEEXT)
- $(LINK) $(nl_OBJECTS) $(nl_LDADD) $(LIBS)
-nohup$(EXEEXT): $(nohup_OBJECTS) $(nohup_DEPENDENCIES)
- @rm -f nohup$(EXEEXT)
- $(LINK) $(nohup_OBJECTS) $(nohup_LDADD) $(LIBS)
-od$(EXEEXT): $(od_OBJECTS) $(od_DEPENDENCIES)
- @rm -f od$(EXEEXT)
- $(LINK) $(od_OBJECTS) $(od_LDADD) $(LIBS)
-paste$(EXEEXT): $(paste_OBJECTS) $(paste_DEPENDENCIES)
- @rm -f paste$(EXEEXT)
- $(LINK) $(paste_OBJECTS) $(paste_LDADD) $(LIBS)
-pathchk$(EXEEXT): $(pathchk_OBJECTS) $(pathchk_DEPENDENCIES)
- @rm -f pathchk$(EXEEXT)
- $(LINK) $(pathchk_OBJECTS) $(pathchk_LDADD) $(LIBS)
-pinky$(EXEEXT): $(pinky_OBJECTS) $(pinky_DEPENDENCIES)
- @rm -f pinky$(EXEEXT)
- $(LINK) $(pinky_OBJECTS) $(pinky_LDADD) $(LIBS)
-pr$(EXEEXT): $(pr_OBJECTS) $(pr_DEPENDENCIES)
- @rm -f pr$(EXEEXT)
- $(LINK) $(pr_OBJECTS) $(pr_LDADD) $(LIBS)
-printenv$(EXEEXT): $(printenv_OBJECTS) $(printenv_DEPENDENCIES)
- @rm -f printenv$(EXEEXT)
- $(LINK) $(printenv_OBJECTS) $(printenv_LDADD) $(LIBS)
-printf$(EXEEXT): $(printf_OBJECTS) $(printf_DEPENDENCIES)
- @rm -f printf$(EXEEXT)
- $(LINK) $(printf_OBJECTS) $(printf_LDADD) $(LIBS)
-ptx$(EXEEXT): $(ptx_OBJECTS) $(ptx_DEPENDENCIES)
- @rm -f ptx$(EXEEXT)
- $(LINK) $(ptx_OBJECTS) $(ptx_LDADD) $(LIBS)
-pwd$(EXEEXT): $(pwd_OBJECTS) $(pwd_DEPENDENCIES)
- @rm -f pwd$(EXEEXT)
- $(LINK) $(pwd_OBJECTS) $(pwd_LDADD) $(LIBS)
-readlink$(EXEEXT): $(readlink_OBJECTS) $(readlink_DEPENDENCIES)
- @rm -f readlink$(EXEEXT)
- $(LINK) $(readlink_OBJECTS) $(readlink_LDADD) $(LIBS)
-rm$(EXEEXT): $(rm_OBJECTS) $(rm_DEPENDENCIES)
- @rm -f rm$(EXEEXT)
- $(LINK) $(rm_OBJECTS) $(rm_LDADD) $(LIBS)
-rmdir$(EXEEXT): $(rmdir_OBJECTS) $(rmdir_DEPENDENCIES)
- @rm -f rmdir$(EXEEXT)
- $(LINK) $(rmdir_OBJECTS) $(rmdir_LDADD) $(LIBS)
-seq$(EXEEXT): $(seq_OBJECTS) $(seq_DEPENDENCIES)
- @rm -f seq$(EXEEXT)
- $(LINK) $(seq_OBJECTS) $(seq_LDADD) $(LIBS)
-setuidgid$(EXEEXT): $(setuidgid_OBJECTS) $(setuidgid_DEPENDENCIES)
- @rm -f setuidgid$(EXEEXT)
- $(LINK) $(setuidgid_OBJECTS) $(setuidgid_LDADD) $(LIBS)
-sha1sum$(EXEEXT): $(sha1sum_OBJECTS) $(sha1sum_DEPENDENCIES)
- @rm -f sha1sum$(EXEEXT)
- $(LINK) $(sha1sum_OBJECTS) $(sha1sum_LDADD) $(LIBS)
-sha224sum$(EXEEXT): $(sha224sum_OBJECTS) $(sha224sum_DEPENDENCIES)
- @rm -f sha224sum$(EXEEXT)
- $(LINK) $(sha224sum_OBJECTS) $(sha224sum_LDADD) $(LIBS)
-sha256sum$(EXEEXT): $(sha256sum_OBJECTS) $(sha256sum_DEPENDENCIES)
- @rm -f sha256sum$(EXEEXT)
- $(LINK) $(sha256sum_OBJECTS) $(sha256sum_LDADD) $(LIBS)
-sha384sum$(EXEEXT): $(sha384sum_OBJECTS) $(sha384sum_DEPENDENCIES)
- @rm -f sha384sum$(EXEEXT)
- $(LINK) $(sha384sum_OBJECTS) $(sha384sum_LDADD) $(LIBS)
-sha512sum$(EXEEXT): $(sha512sum_OBJECTS) $(sha512sum_DEPENDENCIES)
- @rm -f sha512sum$(EXEEXT)
- $(LINK) $(sha512sum_OBJECTS) $(sha512sum_LDADD) $(LIBS)
-shred$(EXEEXT): $(shred_OBJECTS) $(shred_DEPENDENCIES)
- @rm -f shred$(EXEEXT)
- $(LINK) $(shred_OBJECTS) $(shred_LDADD) $(LIBS)
-shuf$(EXEEXT): $(shuf_OBJECTS) $(shuf_DEPENDENCIES)
- @rm -f shuf$(EXEEXT)
- $(LINK) $(shuf_OBJECTS) $(shuf_LDADD) $(LIBS)
-sleep$(EXEEXT): $(sleep_OBJECTS) $(sleep_DEPENDENCIES)
- @rm -f sleep$(EXEEXT)
- $(LINK) $(sleep_OBJECTS) $(sleep_LDADD) $(LIBS)
-sort$(EXEEXT): $(sort_OBJECTS) $(sort_DEPENDENCIES)
- @rm -f sort$(EXEEXT)
- $(LINK) $(sort_OBJECTS) $(sort_LDADD) $(LIBS)
-split$(EXEEXT): $(split_OBJECTS) $(split_DEPENDENCIES)
- @rm -f split$(EXEEXT)
- $(LINK) $(split_OBJECTS) $(split_LDADD) $(LIBS)
-stat$(EXEEXT): $(stat_OBJECTS) $(stat_DEPENDENCIES)
- @rm -f stat$(EXEEXT)
- $(LINK) $(stat_OBJECTS) $(stat_LDADD) $(LIBS)
-stty$(EXEEXT): $(stty_OBJECTS) $(stty_DEPENDENCIES)
- @rm -f stty$(EXEEXT)
- $(LINK) $(stty_OBJECTS) $(stty_LDADD) $(LIBS)
-su$(EXEEXT): $(su_OBJECTS) $(su_DEPENDENCIES)
- @rm -f su$(EXEEXT)
- $(LINK) $(su_OBJECTS) $(su_LDADD) $(LIBS)
-sum$(EXEEXT): $(sum_OBJECTS) $(sum_DEPENDENCIES)
- @rm -f sum$(EXEEXT)
- $(LINK) $(sum_OBJECTS) $(sum_LDADD) $(LIBS)
-sync$(EXEEXT): $(sync_OBJECTS) $(sync_DEPENDENCIES)
- @rm -f sync$(EXEEXT)
- $(LINK) $(sync_OBJECTS) $(sync_LDADD) $(LIBS)
-tac$(EXEEXT): $(tac_OBJECTS) $(tac_DEPENDENCIES)
- @rm -f tac$(EXEEXT)
- $(LINK) $(tac_OBJECTS) $(tac_LDADD) $(LIBS)
-tail$(EXEEXT): $(tail_OBJECTS) $(tail_DEPENDENCIES)
- @rm -f tail$(EXEEXT)
- $(LINK) $(tail_OBJECTS) $(tail_LDADD) $(LIBS)
-tee$(EXEEXT): $(tee_OBJECTS) $(tee_DEPENDENCIES)
- @rm -f tee$(EXEEXT)
- $(LINK) $(tee_OBJECTS) $(tee_LDADD) $(LIBS)
-test$(EXEEXT): $(test_OBJECTS) $(test_DEPENDENCIES)
- @rm -f test$(EXEEXT)
- $(LINK) $(test_OBJECTS) $(test_LDADD) $(LIBS)
-touch$(EXEEXT): $(touch_OBJECTS) $(touch_DEPENDENCIES)
- @rm -f touch$(EXEEXT)
- $(LINK) $(touch_OBJECTS) $(touch_LDADD) $(LIBS)
-tr$(EXEEXT): $(tr_OBJECTS) $(tr_DEPENDENCIES)
- @rm -f tr$(EXEEXT)
- $(LINK) $(tr_OBJECTS) $(tr_LDADD) $(LIBS)
-true$(EXEEXT): $(true_OBJECTS) $(true_DEPENDENCIES)
- @rm -f true$(EXEEXT)
- $(LINK) $(true_OBJECTS) $(true_LDADD) $(LIBS)
-tsort$(EXEEXT): $(tsort_OBJECTS) $(tsort_DEPENDENCIES)
- @rm -f tsort$(EXEEXT)
- $(LINK) $(tsort_OBJECTS) $(tsort_LDADD) $(LIBS)
-tty$(EXEEXT): $(tty_OBJECTS) $(tty_DEPENDENCIES)
- @rm -f tty$(EXEEXT)
- $(LINK) $(tty_OBJECTS) $(tty_LDADD) $(LIBS)
-uname$(EXEEXT): $(uname_OBJECTS) $(uname_DEPENDENCIES)
- @rm -f uname$(EXEEXT)
- $(LINK) $(uname_OBJECTS) $(uname_LDADD) $(LIBS)
-unexpand$(EXEEXT): $(unexpand_OBJECTS) $(unexpand_DEPENDENCIES)
- @rm -f unexpand$(EXEEXT)
- $(LINK) $(unexpand_OBJECTS) $(unexpand_LDADD) $(LIBS)
-uniq$(EXEEXT): $(uniq_OBJECTS) $(uniq_DEPENDENCIES)
- @rm -f uniq$(EXEEXT)
- $(LINK) $(uniq_OBJECTS) $(uniq_LDADD) $(LIBS)
-unlink$(EXEEXT): $(unlink_OBJECTS) $(unlink_DEPENDENCIES)
- @rm -f unlink$(EXEEXT)
- $(LINK) $(unlink_OBJECTS) $(unlink_LDADD) $(LIBS)
-uptime$(EXEEXT): $(uptime_OBJECTS) $(uptime_DEPENDENCIES)
- @rm -f uptime$(EXEEXT)
- $(LINK) $(uptime_OBJECTS) $(uptime_LDADD) $(LIBS)
-users$(EXEEXT): $(users_OBJECTS) $(users_DEPENDENCIES)
- @rm -f users$(EXEEXT)
- $(LINK) $(users_OBJECTS) $(users_LDADD) $(LIBS)
-vdir$(EXEEXT): $(vdir_OBJECTS) $(vdir_DEPENDENCIES)
- @rm -f vdir$(EXEEXT)
- $(LINK) $(vdir_OBJECTS) $(vdir_LDADD) $(LIBS)
-wc$(EXEEXT): $(wc_OBJECTS) $(wc_DEPENDENCIES)
- @rm -f wc$(EXEEXT)
- $(LINK) $(wc_OBJECTS) $(wc_LDADD) $(LIBS)
-who$(EXEEXT): $(who_OBJECTS) $(who_DEPENDENCIES)
- @rm -f who$(EXEEXT)
- $(LINK) $(who_OBJECTS) $(who_LDADD) $(LIBS)
-whoami$(EXEEXT): $(whoami_OBJECTS) $(whoami_DEPENDENCIES)
- @rm -f whoami$(EXEEXT)
- $(LINK) $(whoami_OBJECTS) $(whoami_LDADD) $(LIBS)
-yes$(EXEEXT): $(yes_OBJECTS) $(yes_DEPENDENCIES)
- @rm -f yes$(EXEEXT)
- $(LINK) $(yes_OBJECTS) $(yes_LDADD) $(LIBS)
-install-binSCRIPTS: $(bin_SCRIPTS)
- @$(NORMAL_INSTALL)
- test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
- @list='$(bin_SCRIPTS)'; for p in $$list; do \
- if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
- if test -f $$d$$p; then \
- f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
- echo " $(binSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(bindir)/$$f'"; \
- $(binSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(bindir)/$$f"; \
- else :; fi; \
- done
-
-uninstall-binSCRIPTS:
- @$(NORMAL_UNINSTALL)
- @list='$(bin_SCRIPTS)'; for p in $$list; do \
- f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
- echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
- rm -f "$(DESTDIR)$(bindir)/$$f"; \
- done
-
-mostlyclean-compile:
- -rm -f *.$(OBJEXT)
-
-distclean-compile:
- -rm -f *.tab.c
-
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/basename.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cat.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chgrp.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chmod.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chown-core.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chown.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chroot.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cksum.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/comm.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/copy.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cp-hash.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cp.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/csplit.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cut.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/date.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dd.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/df.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dircolors.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dirname.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/du.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/echo.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/env.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/expand.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/expr.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/factor.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/false.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fmt.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fold.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/head.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hostid.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hostname.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/id.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/install.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/join.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kill.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lbracket.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/link.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ln.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logname.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ls-dir.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ls-ls.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ls-vdir.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ls.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5sum-md5sum.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mkdir.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mkfifo.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mknod.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mv.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nice.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nl.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nohup.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/od.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/paste.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pathchk.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pinky.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pr.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/printenv.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/printf.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ptx.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pwd.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readlink.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remove.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rm.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rmdir.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/seq.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/setuidgid.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha1sum-md5sum.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha224sum-md5sum.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha256sum-md5sum.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha384sum-md5sum.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha512sum-md5sum.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shred.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shuf.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sleep.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sort.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/split.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stat.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stty.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/su.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sum.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sync.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tac.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tail.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tee.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/touch.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tr.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/true.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsort.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tty.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/uname.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unexpand.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/uniq.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unlink.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/uptime.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/users.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wc.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/who.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/whoami.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/yes.Po@am__quote@
-
-.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
-
-.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
-
-md5sum-md5sum.o: md5sum.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(md5sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT md5sum-md5sum.o -MD -MP -MF $(DEPDIR)/md5sum-md5sum.Tpo -c -o md5sum-md5sum.o `test -f 'md5sum.c' || echo '$(srcdir)/'`md5sum.c
-@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/md5sum-md5sum.Tpo $(DEPDIR)/md5sum-md5sum.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5sum.c' object='md5sum-md5sum.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(md5sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o md5sum-md5sum.o `test -f 'md5sum.c' || echo '$(srcdir)/'`md5sum.c
-
-md5sum-md5sum.obj: md5sum.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(md5sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT md5sum-md5sum.obj -MD -MP -MF $(DEPDIR)/md5sum-md5sum.Tpo -c -o md5sum-md5sum.obj `if test -f 'md5sum.c'; then $(CYGPATH_W) 'md5sum.c'; else $(CYGPATH_W) '$(srcdir)/md5sum.c'; fi`
-@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/md5sum-md5sum.Tpo $(DEPDIR)/md5sum-md5sum.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5sum.c' object='md5sum-md5sum.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(md5sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o md5sum-md5sum.obj `if test -f 'md5sum.c'; then $(CYGPATH_W) 'md5sum.c'; else $(CYGPATH_W) '$(srcdir)/md5sum.c'; fi`
-
-sha1sum-md5sum.o: md5sum.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha1sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sha1sum-md5sum.o -MD -MP -MF $(DEPDIR)/sha1sum-md5sum.Tpo -c -o sha1sum-md5sum.o `test -f 'md5sum.c' || echo '$(srcdir)/'`md5sum.c
-@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/sha1sum-md5sum.Tpo $(DEPDIR)/sha1sum-md5sum.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5sum.c' object='sha1sum-md5sum.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha1sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sha1sum-md5sum.o `test -f 'md5sum.c' || echo '$(srcdir)/'`md5sum.c
-
-sha1sum-md5sum.obj: md5sum.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha1sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sha1sum-md5sum.obj -MD -MP -MF $(DEPDIR)/sha1sum-md5sum.Tpo -c -o sha1sum-md5sum.obj `if test -f 'md5sum.c'; then $(CYGPATH_W) 'md5sum.c'; else $(CYGPATH_W) '$(srcdir)/md5sum.c'; fi`
-@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/sha1sum-md5sum.Tpo $(DEPDIR)/sha1sum-md5sum.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5sum.c' object='sha1sum-md5sum.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha1sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sha1sum-md5sum.obj `if test -f 'md5sum.c'; then $(CYGPATH_W) 'md5sum.c'; else $(CYGPATH_W) '$(srcdir)/md5sum.c'; fi`
-
-sha224sum-md5sum.o: md5sum.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha224sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sha224sum-md5sum.o -MD -MP -MF $(DEPDIR)/sha224sum-md5sum.Tpo -c -o sha224sum-md5sum.o `test -f 'md5sum.c' || echo '$(srcdir)/'`md5sum.c
-@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/sha224sum-md5sum.Tpo $(DEPDIR)/sha224sum-md5sum.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5sum.c' object='sha224sum-md5sum.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha224sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sha224sum-md5sum.o `test -f 'md5sum.c' || echo '$(srcdir)/'`md5sum.c
-
-sha224sum-md5sum.obj: md5sum.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha224sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sha224sum-md5sum.obj -MD -MP -MF $(DEPDIR)/sha224sum-md5sum.Tpo -c -o sha224sum-md5sum.obj `if test -f 'md5sum.c'; then $(CYGPATH_W) 'md5sum.c'; else $(CYGPATH_W) '$(srcdir)/md5sum.c'; fi`
-@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/sha224sum-md5sum.Tpo $(DEPDIR)/sha224sum-md5sum.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5sum.c' object='sha224sum-md5sum.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha224sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sha224sum-md5sum.obj `if test -f 'md5sum.c'; then $(CYGPATH_W) 'md5sum.c'; else $(CYGPATH_W) '$(srcdir)/md5sum.c'; fi`
-
-sha256sum-md5sum.o: md5sum.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha256sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sha256sum-md5sum.o -MD -MP -MF $(DEPDIR)/sha256sum-md5sum.Tpo -c -o sha256sum-md5sum.o `test -f 'md5sum.c' || echo '$(srcdir)/'`md5sum.c
-@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/sha256sum-md5sum.Tpo $(DEPDIR)/sha256sum-md5sum.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5sum.c' object='sha256sum-md5sum.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha256sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sha256sum-md5sum.o `test -f 'md5sum.c' || echo '$(srcdir)/'`md5sum.c
-
-sha256sum-md5sum.obj: md5sum.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha256sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sha256sum-md5sum.obj -MD -MP -MF $(DEPDIR)/sha256sum-md5sum.Tpo -c -o sha256sum-md5sum.obj `if test -f 'md5sum.c'; then $(CYGPATH_W) 'md5sum.c'; else $(CYGPATH_W) '$(srcdir)/md5sum.c'; fi`
-@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/sha256sum-md5sum.Tpo $(DEPDIR)/sha256sum-md5sum.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5sum.c' object='sha256sum-md5sum.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha256sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sha256sum-md5sum.obj `if test -f 'md5sum.c'; then $(CYGPATH_W) 'md5sum.c'; else $(CYGPATH_W) '$(srcdir)/md5sum.c'; fi`
-
-sha384sum-md5sum.o: md5sum.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha384sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sha384sum-md5sum.o -MD -MP -MF $(DEPDIR)/sha384sum-md5sum.Tpo -c -o sha384sum-md5sum.o `test -f 'md5sum.c' || echo '$(srcdir)/'`md5sum.c
-@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/sha384sum-md5sum.Tpo $(DEPDIR)/sha384sum-md5sum.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5sum.c' object='sha384sum-md5sum.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha384sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sha384sum-md5sum.o `test -f 'md5sum.c' || echo '$(srcdir)/'`md5sum.c
-
-sha384sum-md5sum.obj: md5sum.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha384sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sha384sum-md5sum.obj -MD -MP -MF $(DEPDIR)/sha384sum-md5sum.Tpo -c -o sha384sum-md5sum.obj `if test -f 'md5sum.c'; then $(CYGPATH_W) 'md5sum.c'; else $(CYGPATH_W) '$(srcdir)/md5sum.c'; fi`
-@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/sha384sum-md5sum.Tpo $(DEPDIR)/sha384sum-md5sum.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5sum.c' object='sha384sum-md5sum.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha384sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sha384sum-md5sum.obj `if test -f 'md5sum.c'; then $(CYGPATH_W) 'md5sum.c'; else $(CYGPATH_W) '$(srcdir)/md5sum.c'; fi`
-
-sha512sum-md5sum.o: md5sum.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha512sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sha512sum-md5sum.o -MD -MP -MF $(DEPDIR)/sha512sum-md5sum.Tpo -c -o sha512sum-md5sum.o `test -f 'md5sum.c' || echo '$(srcdir)/'`md5sum.c
-@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/sha512sum-md5sum.Tpo $(DEPDIR)/sha512sum-md5sum.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5sum.c' object='sha512sum-md5sum.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha512sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sha512sum-md5sum.o `test -f 'md5sum.c' || echo '$(srcdir)/'`md5sum.c
-
-sha512sum-md5sum.obj: md5sum.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha512sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sha512sum-md5sum.obj -MD -MP -MF $(DEPDIR)/sha512sum-md5sum.Tpo -c -o sha512sum-md5sum.obj `if test -f 'md5sum.c'; then $(CYGPATH_W) 'md5sum.c'; else $(CYGPATH_W) '$(srcdir)/md5sum.c'; fi`
-@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/sha512sum-md5sum.Tpo $(DEPDIR)/sha512sum-md5sum.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5sum.c' object='sha512sum-md5sum.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(sha512sum_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sha512sum-md5sum.obj `if test -f 'md5sum.c'; then $(CYGPATH_W) 'md5sum.c'; else $(CYGPATH_W) '$(srcdir)/md5sum.c'; fi`
-
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) ' { files[$$0] = 1; } \
- END { for (i in files) print i; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- tags=; \
- here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) ' { files[$$0] = 1; } \
- END { for (i in files) print i; }'`; \
- if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
- test -n "$$unique" || unique=$$empty_fix; \
- $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
- $$tags $$unique; \
- fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- tags=; \
- here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) ' { files[$$0] = 1; } \
- END { for (i in files) print i; }'`; \
- test -z "$(CTAGS_ARGS)$$tags$$unique" \
- || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
- $$tags $$unique
-
-GTAGS:
- here=`$(am__cd) $(top_builddir) && pwd` \
- && cd $(top_srcdir) \
- && gtags -i $(GTAGS_ARGS) $$here
-
-distclean-tags:
- -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-
-distdir: $(DISTFILES)
- @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
- topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
- list='$(DISTFILES)'; \
- dist_files=`for file in $$list; do echo $$file; done | \
- sed -e "s|^$$srcdirstrip/||;t" \
- -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
- case $$dist_files in \
- */*) $(MKDIR_P) `echo "$$dist_files" | \
- sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
- sort -u` ;; \
- esac; \
- for file in $$dist_files; do \
- if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
- if test -d $$d/$$file; then \
- dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
- if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
- cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
- fi; \
- cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
- else \
- test -f $(distdir)/$$file \
- || cp -p $$d/$$file $(distdir)/$$file \
- || exit 1; \
- fi; \
- done
-check-am: all-am
-check: $(BUILT_SOURCES)
- $(MAKE) $(AM_MAKEFLAGS) check-am
-all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(HEADERS) all-local
-installdirs:
- for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)"; do \
- test -z "$$dir" || $(MKDIR_P) "$$dir"; \
- done
-install: $(BUILT_SOURCES)
- $(MAKE) $(AM_MAKEFLAGS) install-am
-install-exec: install-exec-am
-install-data: install-data-am
-uninstall: uninstall-am
-
-install-am: all-am
- @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
-
-installcheck: installcheck-am
-install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
-mostlyclean-generic:
-
-clean-generic:
- -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
-
-distclean-generic:
- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-
-maintainer-clean-generic:
- @echo "This command is intended for maintainers to use"
- @echo "it deletes files that may require special tools to rebuild."
- -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
- -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
-clean: clean-am
-
-clean-am: clean-binPROGRAMS clean-generic clean-noinstPROGRAMS \
- mostlyclean-am
-
-distclean: distclean-am
- -rm -rf ./$(DEPDIR)
- -rm -f Makefile
-distclean-am: clean-am distclean-compile distclean-generic \
- distclean-tags
-
-dvi: dvi-am
-
-dvi-am:
-
-html: html-am
-
-info: info-am
-
-info-am:
-
-install-data-am:
-
-install-dvi: install-dvi-am
-
-install-exec-am: install-binPROGRAMS install-binSCRIPTS \
- install-exec-local
-
-install-html: install-html-am
-
-install-info: install-info-am
-
-install-man:
-
-install-pdf: install-pdf-am
-
-install-ps: install-ps-am
-
-installcheck-am:
-
-maintainer-clean: maintainer-clean-am
- -rm -rf ./$(DEPDIR)
- -rm -f Makefile
-maintainer-clean-am: distclean-am maintainer-clean-generic
-
-mostlyclean: mostlyclean-am
-
-mostlyclean-am: mostlyclean-compile mostlyclean-generic
-
-pdf: pdf-am
-
-pdf-am:
-
-ps: ps-am
-
-ps-am:
-
-uninstall-am: uninstall-binPROGRAMS uninstall-binSCRIPTS \
- uninstall-local
-
-.MAKE: install-am install-strip
-
-.PHONY: CTAGS GTAGS all all-am all-local check check-am clean \
- clean-binPROGRAMS clean-generic clean-noinstPROGRAMS ctags \
- distclean distclean-compile distclean-generic distclean-tags \
- distdir dvi dvi-am html html-am info info-am install \
- install-am install-binPROGRAMS install-binSCRIPTS install-data \
- install-data-am install-dvi install-dvi-am install-exec \
- install-exec-am install-exec-local install-html \
- install-html-am install-info install-info-am install-man \
- install-pdf install-pdf-am install-ps install-ps-am \
- install-strip installcheck installcheck-am installdirs \
- maintainer-clean maintainer-clean-generic mostlyclean \
- mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
- tags uninstall uninstall-am uninstall-binPROGRAMS \
- uninstall-binSCRIPTS uninstall-local
-
-
-$(PROGRAMS): ../lib/libcoreutils.a
-
-.sh:
- rm -f $@ $@-t
- sed \
- -e 's!@''bindir''@!$(bindir)!' \
- -e 's/@''RELEASE_YEAR'@/$(RELEASE_YEAR)/ \
- -e 's/@''GNU_PACKAGE''@/$(GNU_PACKAGE)/' \
- -e 's/@''PACKAGE_BUGREPORT''@/$(PACKAGE_BUGREPORT)/' \
- -e 's/@''VERSION''@/$(VERSION)/' $< > $@-t
- chmod +x $@-t
- mv $@-t $@
-
-all-local: su$(EXEEXT)
-
-install-root: su$(EXEEXT)
- @$(INSTALL_SU)
-
-install-exec-local: su$(EXEEXT)
- @TMPFILE=$(DESTDIR)$(bindir)/.su-$$$$; \
- rm -f $$TMPFILE; \
- echo > $$TMPFILE; \
- can_create_suid_root_executable=no; \
- chown root $$TMPFILE > /dev/null 2>&1 \
- && chmod $(setuid_root_mode) $$TMPFILE > /dev/null 2>&1 \
- && can_create_suid_root_executable=yes; \
- rm -f $$TMPFILE; \
- if test $$can_create_suid_root_executable = yes; then \
- $(INSTALL_SU); \
- else \
- echo "WARNING: insufficient access; not installing su"; \
- echo "NOTE: to install su, run 'make install-root' as root"; \
- fi
-
-uninstall-local:
-# Remove su only if it's one we installed.
- @if grep '$(GNU_PACKAGE)' $(installed_su) > /dev/null 2>&1; then \
- echo " rm -f $(installed_su)"; \
- rm -f $(installed_su); \
- else :; fi
-dircolors.h: dcgen dircolors.hin
- @rm -f $@ $@-t
- $(PERL) -w -- $(srcdir)/dcgen $(srcdir)/dircolors.hin > $@-t
- @chmod a-w $@-t
- mv $@-t $@
-wheel-size.h: Makefile.am
- @rm -f $@ $@-t
- echo '#define WHEEL_SIZE $(wheel_size)' > $@-t
- @chmod a-w $@-t
- mv $@-t $@
-wheel.h: wheel-gen.pl Makefile.am
- @rm -f $@ $@-t
- $(srcdir)/wheel-gen.pl $(wheel_size) > $@-t
- @chmod a-w $@-t
- mv $@-t $@
-fs.h: stat.c extract-magic
- rm -f $@
- $(PERL) $(srcdir)/extract-magic $(srcdir)/stat.c > $@t
- @chmod a-w $@t
- mv $@t $@
-
-all_programs.list:
- @echo $(all_programs) | tr ' ' '\n' | sed -e 's,$(EXEEXT)$$,,' \
- | $(ASSORT) -u
-# Ensure that the list of programs in README matches the list
-# of programs we can build.
-check: check-README check-misc
-.PHONY: check-README
-check-README:
- rm -rf $(pr) $(pm)
- echo $(all_programs) \
- | tr -s ' ' '\n' | sed -e 's,$(EXEEXT)$$,,' \
- | $(ASSORT) -u > $(pm) && \
- sed -n '/^The programs .* are:/,/^[a-zA-Z]/p' $(top_srcdir)/README \
- | sed -n '/^ */s///p' | tr -s ' ' '\n' > $(pr)
- diff $(pm) $(pr) && rm -rf $(pr) $(pm)
-.PHONY: check-AUTHORS
-check-AUTHORS: $(all_programs)
- rm -f $(au_actual) $(au_dotdot)
- for i in `ls $(all_programs) | sed -e 's,$(EXEEXT)$$,,' \
- | $(ASSORT) -u`; do \
- test "$$i" = '[' && continue; \
- exe=$$i; \
- if test "$$i" = install; then \
- exe=ginstall; \
- elif test "$$i" = test; then \
- exe='['; \
- fi; \
- ./$$exe --version \
- |sed -n '/Written by /{ s//'"$$i"': /; s/,* and /, /; s/\.$$//; p; }'; \
- done > $(au_actual)
- sed -n '/:/p' $(top_srcdir)/AUTHORS > $(au_dotdot)
- diff $(au_actual) $(au_dotdot) && rm -f $(au_actual) $(au_dotdot)
-
-# Make sure we don't define any S_IS* macros in src/*.c files.
-# Not a big deal, but they're already defined via system.h.
-#
-# Also make sure we don't use st_blocks. Use ST_NBLOCKS instead.
-# This is a bit of a kludge, since it prevents use of the string
-# even in comments, but for now it does the job with no false positives.
-.PHONY: check-misc
-check-misc:
- cd $(srcdir); grep '^# *define *S_IS' $(SOURCES) && exit 1 || :
- cd $(srcdir); grep st_blocks $(SOURCES) && exit 1 || :
- cd $(srcdir); grep '^# *define .*defined' $(SOURCES) && exit 1 || :
-# FIXME: handle *.sh; and use $(all_programs), not $(SOURCES)
-../AUTHORS: $(SOURCES)
- rm -f $@-t
- ( \
- set -e; \
- echo "Here are the names of the programs in this package,"; \
- echo "each followed by the name(s) of its author(s)."; \
- echo; \
- for i in $(SOURCES); do \
- a=`sed -n $(s1) $$i`; \
- test "$$a" && : \
- || a=`sed -n $(s2) $$i`; \
- if test "$$a"; then \
- prog=`echo $$i|sed 's/\.c$$//'`; \
- echo "$$prog: $$a"; \
- fi; \
- done | $(ASSORT) -u ) > $@-t
- chmod a-w $@-t
- mv $@-t $@
-
-# The following rule is not designed to be portable,
-# and relies on tools that not everyone has.
-
-# Most functions in src/*.c should have static scope.
-# Any that don't must be marked with `extern', but `main'
-# and `usage' are exceptions. They're always extern, but
-# don't need to be marked.
-#
-# The second nm|grep checks for file-scope variables with `extern' scope.
-.PHONY: sc_tight_scope
-sc_tight_scope: $(all_programs)
- @t=exceptions-$$$$; \
- trap "s=$$?; rm -f $$t; exit $$s" 0 1 2 13 15; \
- ( printf '^main$$\n^usage$$\n'; \
- grep -h -A1 '^extern .*[^;]$$' $(SOURCES) \
- | grep -vE '^(extern |--)' |sed 's/^/^/;s/ .*/$$/' ) > $$t; \
- nm -e *.$(OBJEXT) \
- | sed -n 's/.* T //p' \
- | grep -Ev -f $$t && \
- { echo 'the above functions should have static scope' 1>&2; \
- exit 1; } || : ; \
- ( printf '^program_name$$\n'; \
- sed -n 's/^extern int \([^ ][^ ]*\);$$/^\1$$/p' \
- $(noinst_HEADERS) ) > $$t; \
- nm -e *.$(OBJEXT) \
- | sed -n 's/.* [BD] //p' \
- | grep -Ev -f $$t && \
- { echo 'the above variables should have static scope' 1>&2; \
- exit 1; } || :
-# Tell versions [3.59,3.63) of GNU make to not export all variables.
-# Otherwise a system limit (for SysV at least) may be exceeded.
-.NOEXPORT:
diff --git a/src/base64.c b/src/base64.c
index 9f8ff41..83f0e9d 100644
--- a/src/base64.c
+++ b/src/base64.c
@@ -1,22 +1,20 @@
/* Base64 encode/decode strings or files.
- Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+ Copyright (C) 2004-2016 Free Software Foundation, Inc.
This file is part of Base64.
- Base64 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 2, or (at your option)
- any later version.
+ This 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.
- Base64 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.
+ 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 Base64; see the file COPYING. If not, write to the Free
- Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Simon Josefsson <simon@josefsson.org>. */
@@ -28,84 +26,108 @@
#include "system.h"
#include "error.h"
-#include "xstrtol.h"
+#include "fadvise.h"
#include "quote.h"
-#include "quotearg.h"
-
-#include "base64.h"
+#include "xstrtol.h"
+#include "xdectoint.h"
+#include "xfreopen.h"
-/* The official name of this program (e.g., no `g' prefix). */
-#define PROGRAM_NAME "base64"
+#define AUTHORS proper_name ("Simon Josefsson")
-#define AUTHOR "Simon Josefsson"
+#if BASE_TYPE == 32
+# include "base32.h"
+# define PROGRAM_NAME "base32"
+#else
+# include "base64.h"
+# define PROGRAM_NAME "base64"
+#endif
-/* The invocation name of this program. */
-char *program_name;
-static const struct option long_options[] = {
+static struct option const long_options[] =
+{
{"decode", no_argument, 0, 'd'},
{"wrap", required_argument, 0, 'w'},
{"ignore-garbage", no_argument, 0, 'i'},
- {"help", no_argument, 0, GETOPT_HELP_CHAR},
- {"version", no_argument, 0, GETOPT_VERSION_CHAR},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
};
-static void
+void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
-Usage: %s [OPTION] [FILE]\n\
-Base64 encode or decode FILE, or standard input, to standard output.\n\
-\n"), program_name);
+Usage: %s [OPTION]... [FILE]\n\
+Base%d encode or decode FILE, or standard input, to standard output.\n\
+"), program_name, BASE_TYPE);
+
+ emit_stdin_note ();
+ emit_mandatory_arg_note ();
+
fputs (_("\
- -w, --wrap=COLS Wrap encoded lines after COLS character (default 76).\n\
- Use 0 to disable line wrapping.\n\
-\n\
- -d, --decode Decode data.\n\
- -i, --ignore-garbage When decoding, ignore non-alphabet characters.\n\
+ -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\
\n\
"), stdout);
- fputs (_("\
- --help Display this help and exit.\n\
- --version Output version information and exit.\n"), stdout);
- fputs (_("\
-\n\
-With no FILE, or when FILE is -, read standard input.\n"), stdout);
- fputs (_("\
+ fputs (HELP_OPTION_DESCRIPTION, stdout);
+ fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ printf (_("\
\n\
-The data are encoded as described for the base64 alphabet in RFC 3548.\n\
+The data are encoded as described for the %s alphabet in RFC 4648.\n\
When decoding, the input may contain newlines in addition to the bytes of\n\
-the formal base64 alphabet. Use --ignore-garbage to attempt to recover\n\
+the formal %s alphabet. Use --ignore-garbage to attempt to recover\n\
from any other non-alphabet bytes in the encoded stream.\n"),
- stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ PROGRAM_NAME, PROGRAM_NAME);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
+#define ENC_BLOCKSIZE (1024*3*10)
+
+#if BASE_TYPE == 32
+# define BASE_LENGTH BASE32_LENGTH
/* Note that increasing this may decrease performance if --ignore-garbage
- is used, because of the memmove operation below. */
-#define BLOCKSIZE 3072
-#define B64BLOCKSIZE BASE64_LENGTH (BLOCKSIZE)
+ is used, because of the memmove operation below. */
+# define DEC_BLOCKSIZE (1024*5)
+
+/* Ensure that BLOCKSIZE is a multiple of 5 and 8. */
+verify (ENC_BLOCKSIZE % 40 == 0); /* So padding chars only on last block. */
+verify (DEC_BLOCKSIZE % 40 == 0); /* So complete encoded blocks are used. */
+
+# define base_encode base32_encode
+# define base_decode_context base32_decode_context
+# define base_decode_ctx_init base32_decode_ctx_init
+# define base_decode_ctx base32_decode_ctx
+# define isbase isbase32
+#else
+# define BASE_LENGTH BASE64_LENGTH
+/* Note that increasing this may decrease performance if --ignore-garbage
+ is used, because of the memmove operation below. */
+# define DEC_BLOCKSIZE (1024*3)
/* Ensure that BLOCKSIZE is a multiple of 3 and 4. */
-#if BLOCKSIZE % 12 != 0
-# error "invalid BLOCKSIZE"
+verify (ENC_BLOCKSIZE % 12 == 0); /* So padding chars only on last block. */
+verify (DEC_BLOCKSIZE % 12 == 0); /* So complete encoded blocks are used. */
+
+# define base_encode base64_encode
+# define base_decode_context base64_decode_context
+# define base_decode_ctx_init base64_decode_ctx_init
+# define base_decode_ctx base64_decode_ctx
+# define isbase isbase64
#endif
static void
wrap_write (const char *buffer, size_t len,
- uintmax_t wrap_column, size_t *current_column, FILE *out)
+ uintmax_t wrap_column, size_t *current_column, FILE *out)
{
size_t written;
@@ -113,28 +135,28 @@ wrap_write (const char *buffer, size_t len,
{
/* Simple write. */
if (fwrite (buffer, 1, len, stdout) < len)
- error (EXIT_FAILURE, errno, _("write error"));
+ error (EXIT_FAILURE, errno, _("write error"));
}
else
for (written = 0; written < len;)
{
- uintmax_t cols_remaining = wrap_column - *current_column;
- size_t to_write = MIN (cols_remaining, SIZE_MAX);
- to_write = MIN (to_write, len - written);
-
- if (to_write == 0)
- {
- if (fputs ("\n", out) < 0)
- error (EXIT_FAILURE, errno, _("write error"));
- *current_column = 0;
- }
- else
- {
- if (fwrite (buffer + written, 1, to_write, stdout) < to_write)
- error (EXIT_FAILURE, errno, _("write error"));
- *current_column += to_write;
- written += to_write;
- }
+ uintmax_t cols_remaining = wrap_column - *current_column;
+ size_t to_write = MIN (cols_remaining, SIZE_MAX);
+ to_write = MIN (to_write, len - written);
+
+ if (to_write == 0)
+ {
+ if (fputc ('\n', out) == EOF)
+ error (EXIT_FAILURE, errno, _("write error"));
+ *current_column = 0;
+ }
+ else
+ {
+ if (fwrite (buffer + written, 1, to_write, stdout) < to_write)
+ error (EXIT_FAILURE, errno, _("write error"));
+ *current_column += to_write;
+ written += to_write;
+ }
}
}
@@ -142,8 +164,8 @@ static void
do_encode (FILE *in, FILE *out, uintmax_t wrap_column)
{
size_t current_column = 0;
- char inbuf[BLOCKSIZE];
- char outbuf[B64BLOCKSIZE];
+ char inbuf[ENC_BLOCKSIZE];
+ char outbuf[BASE_LENGTH (ENC_BLOCKSIZE)];
size_t sum;
do
@@ -152,26 +174,26 @@ do_encode (FILE *in, FILE *out, uintmax_t wrap_column)
sum = 0;
do
- {
- n = fread (inbuf + sum, 1, BLOCKSIZE - sum, in);
- sum += n;
- }
- while (!feof (in) && !ferror (in) && sum < BLOCKSIZE);
+ {
+ n = fread (inbuf + sum, 1, ENC_BLOCKSIZE - sum, in);
+ sum += n;
+ }
+ while (!feof (in) && !ferror (in) && sum < ENC_BLOCKSIZE);
if (sum > 0)
- {
- /* Process input one block at a time. Note that BLOCKSIZE %
- 3 == 0, so that no base64 pads will appear in output. */
- base64_encode (inbuf, sum, outbuf, BASE64_LENGTH (sum));
-
- wrap_write (outbuf, BASE64_LENGTH (sum), wrap_column,
- &current_column, out);
- }
+ {
+ /* Process input one block at a time. Note that ENC_BLOCKSIZE
+ is sized so that no pad chars will appear in output. */
+ base_encode (inbuf, sum, outbuf, BASE_LENGTH (sum));
+
+ wrap_write (outbuf, BASE_LENGTH (sum), wrap_column,
+ &current_column, out);
+ }
}
- while (!feof (in) && !ferror (in) && sum == BLOCKSIZE);
+ while (!feof (in) && !ferror (in) && sum == ENC_BLOCKSIZE);
/* When wrapping, terminate last line. */
- if (wrap_column && current_column > 0 && fputs ("\n", out) < 0)
+ if (wrap_column && current_column > 0 && fputc ('\n', out) == EOF)
error (EXIT_FAILURE, errno, _("write error"));
if (ferror (in))
@@ -181,12 +203,12 @@ do_encode (FILE *in, FILE *out, uintmax_t wrap_column)
static void
do_decode (FILE *in, FILE *out, bool ignore_garbage)
{
- char inbuf[B64BLOCKSIZE];
- char outbuf[BLOCKSIZE];
+ char inbuf[BASE_LENGTH (DEC_BLOCKSIZE)];
+ char outbuf[DEC_BLOCKSIZE];
size_t sum;
- struct base64_decode_context ctx;
+ struct base_decode_context ctx;
- base64_decode_ctx_init (&ctx);
+ base_decode_ctx_init (&ctx);
do
{
@@ -196,43 +218,43 @@ do_decode (FILE *in, FILE *out, bool ignore_garbage)
sum = 0;
do
- {
- n = fread (inbuf + sum, 1, B64BLOCKSIZE - sum, in);
-
- if (ignore_garbage)
- {
- size_t i;
- for (i = 0; n > 0 && i < n;)
- if (isbase64 (inbuf[sum + i]) || inbuf[sum + i] == '=')
- i++;
- else
- memmove (inbuf + sum + i, inbuf + sum + i + 1, --n - i);
- }
-
- sum += n;
-
- if (ferror (in))
- error (EXIT_FAILURE, errno, _("read error"));
- }
- while (sum < B64BLOCKSIZE && !feof (in));
+ {
+ n = fread (inbuf + sum, 1, BASE_LENGTH (DEC_BLOCKSIZE) - sum, in);
+
+ if (ignore_garbage)
+ {
+ size_t i;
+ for (i = 0; n > 0 && i < n;)
+ if (isbase (inbuf[sum + i]) || inbuf[sum + i] == '=')
+ i++;
+ else
+ memmove (inbuf + sum + i, inbuf + sum + i + 1, --n - i);
+ }
+
+ sum += n;
+
+ if (ferror (in))
+ error (EXIT_FAILURE, errno, _("read error"));
+ }
+ while (sum < BASE_LENGTH (DEC_BLOCKSIZE) && !feof (in));
/* The following "loop" is usually iterated just once.
- However, when it processes the final input buffer, we want
- to iterate it one additional time, but with an indicator
- telling it to flush what is in CTX. */
- for (k = 0; k < 1 + feof (in); k++)
- {
- if (k == 1 && ctx.i == 0)
- break;
- n = BLOCKSIZE;
- ok = base64_decode (&ctx, inbuf, (k == 0 ? sum : 0), outbuf, &n);
-
- if (fwrite (outbuf, 1, n, out) < n)
- error (EXIT_FAILURE, errno, _("write error"));
-
- if (!ok)
- error (EXIT_FAILURE, 0, _("invalid input"));
- }
+ However, when it processes the final input buffer, we want
+ to iterate it one additional time, but with an indicator
+ telling it to flush what is in CTX. */
+ for (k = 0; k < 1 + !!feof (in); k++)
+ {
+ if (k == 1 && ctx.i == 0)
+ break;
+ n = DEC_BLOCKSIZE;
+ ok = base_decode_ctx (&ctx, inbuf, (k == 0 ? sum : 0), outbuf, &n);
+
+ if (fwrite (outbuf, 1, n, out) < n)
+ error (EXIT_FAILURE, errno, _("write error"));
+
+ if (!ok)
+ error (EXIT_FAILURE, 0, _("invalid input"));
+ }
}
while (!feof (in));
}
@@ -244,45 +266,44 @@ main (int argc, char **argv)
FILE *input_fh;
const char *infile;
- /* True if --decode has bene given and we should decode data. */
+ /* True if --decode has been given and we should decode data. */
bool decode = false;
- /* True if we should ignore non-alphabetic characters. */
+ /* True if we should ignore non-base-alphabetic characters. */
bool ignore_garbage = false;
- /* Wrap encoded base64 data around the 76:th column, by default. */
+ /* Wrap encoded data around the 76:th column, by default. */
uintmax_t wrap_column = 76;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- while ((opt = getopt_long (argc, argv, "dqiw:", long_options, NULL)) != -1)
+ while ((opt = getopt_long (argc, argv, "diw:", long_options, NULL)) != -1)
switch (opt)
{
case 'd':
- decode = true;
- break;
+ decode = true;
+ break;
case 'w':
- if (xstrtoumax (optarg, NULL, 0, &wrap_column, NULL) != LONGINT_OK)
- error (EXIT_FAILURE, 0, _("invalid wrap size: %s"),
- quotearg (optarg));
- break;
+ wrap_column = xdectoumax (optarg, 0, UINTMAX_MAX, "",
+ _("invalid wrap size"), 0);
+ break;
case 'i':
- ignore_garbage = true;
- break;
+ ignore_garbage = true;
+ break;
- case_GETOPT_HELP_CHAR;
+ case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHOR);
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
- usage (EXIT_FAILURE);
- break;
+ usage (EXIT_FAILURE);
+ break;
}
if (argc - optind > 1)
@@ -296,15 +317,21 @@ main (int argc, char **argv)
else
infile = "-";
- if (strcmp (infile, "-") == 0)
- input_fh = stdin;
+ if (STREQ (infile, "-"))
+ {
+ if (O_BINARY)
+ xfreopen (NULL, "rb", stdin);
+ input_fh = stdin;
+ }
else
{
- input_fh = fopen (infile, "r");
+ input_fh = fopen (infile, "rb");
if (input_fh == NULL)
- error (EXIT_FAILURE, errno, "%s", infile);
+ error (EXIT_FAILURE, errno, "%s", quotef (infile));
}
+ fadvise (input_fh, FADVISE_SEQUENTIAL);
+
if (decode)
do_decode (input_fh, stdout, ignore_garbage);
else
@@ -312,11 +339,11 @@ main (int argc, char **argv)
if (fclose (input_fh) == EOF)
{
- if (strcmp (infile, "-") == 0)
- error (EXIT_FAILURE, errno, _("closing standard input"));
+ if (STREQ (infile, "-"))
+ error (EXIT_FAILURE, errno, _("closing standard input"));
else
- error (EXIT_FAILURE, errno, "%s", infile);
+ error (EXIT_FAILURE, errno, "%s", quotef (infile));
}
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/basename.c b/src/basename.c
index f2617b1..e2ed879 100644
--- a/src/basename.c
+++ b/src/basename.c
@@ -1,10 +1,10 @@
/* basename -- strip directory and suffix from file names
- Copyright (C) 1990-1997, 1999-2006 Free Software Foundation, Inc.
+ Copyright (C) 1990-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,18 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
-
-/* Usage: basename name [suffix]
- NAME is a file name; SUFFIX is a suffix to strip from it.
-
- basename /usr/foo/lossage/functions.l
- => functions.l
- basename /usr/foo/lossage/functions.l .l
- => functions
- basename functions.lisp p
- => functions.lis */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#include <getopt.h>
@@ -31,52 +20,66 @@
#include <sys/types.h>
#include "system.h"
-#include "long-options.h"
#include "error.h"
#include "quote.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "basename"
-#define AUTHORS "FIXME unknown"
+#define AUTHORS proper_name ("David MacKenzie")
-/* The name this program was run with. */
-char *program_name;
+static struct option const longopts[] =
+{
+ {"multiple", no_argument, NULL, 'a'},
+ {"suffix", required_argument, NULL, 's'},
+ {"zero", no_argument, NULL, 'z'},
+ {GETOPT_HELP_OPTION_DECL},
+ {GETOPT_VERSION_OPTION_DECL},
+ {NULL, 0, NULL, 0}
+};
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s NAME [SUFFIX]\n\
- or: %s OPTION\n\
+ or: %s OPTION... NAME...\n\
"),
- program_name, program_name);
+ program_name, program_name);
fputs (_("\
Print NAME with any leading directory components removed.\n\
If specified, also remove a trailing SUFFIX.\n\
-\n\
+"), stdout);
+
+ 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);
printf (_("\
\n\
Examples:\n\
- %s /usr/bin/sort Output \"sort\".\n\
- %s include/stdio.h .h Output \"stdio\".\n\
+ %s /usr/bin/sort -> \"sort\"\n\
+ %s include/stdio.h .h -> \"stdio\"\n\
+ %s -s .h include/stdio.h -> \"stdio\"\n\
+ %s -a any/str1 any/str2 -> \"str1\" followed by \"str2\"\n\
"),
- program_name, program_name);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ program_name, program_name, program_name, program_name);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
/* Remove SUFFIX from the end of NAME if it is there, unless NAME
- consists entirely of SUFFIX. */
+ consists entirely of SUFFIX. */
static void
remove_suffix (char *name, const char *suffix)
@@ -94,23 +97,72 @@ remove_suffix (char *name, const char *suffix)
*np = '\0';
}
+/* Perform the basename operation on STRING. If SUFFIX is non-NULL, remove
+ the trailing SUFFIX. Finally, output the result string. */
+
+static void
+perform_basename (const char *string, const char *suffix, bool use_nuls)
+{
+ char *name = base_name (string);
+ strip_trailing_slashes (name);
+
+ /* Per POSIX, 'basename // /' must return '//' on platforms with
+ distinct //. On platforms with drive letters, this generalizes
+ to making 'basename c: :' return 'c:'. This rule is captured by
+ 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);
+
+ fputs (name, stdout);
+ putchar (use_nuls ? '\0' : '\n');
+ free (name);
+}
+
int
main (int argc, char **argv)
{
- char *name;
+ bool multiple_names = false;
+ bool use_nuls = false;
+ const char *suffix = NULL;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
- if (getopt_long (argc, argv, "+", NULL, NULL) != -1)
- usage (EXIT_FAILURE);
+ while (true)
+ {
+ int c = getopt_long (argc, argv, "+as:z", longopts, NULL);
+
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 's':
+ suffix = optarg;
+ /* Fall through: -s implies -a. */
+
+ case 'a':
+ multiple_names = true;
+ break;
+
+ case 'z':
+ use_nuls = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
+ }
if (argc < optind + 1)
{
@@ -118,27 +170,20 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
- if (optind + 2 < argc)
+ if (!multiple_names && optind + 2 < argc)
{
error (0, 0, _("extra operand %s"), quote (argv[optind + 2]));
usage (EXIT_FAILURE);
}
- name = base_name (argv[optind]);
- strip_trailing_slashes (name);
-
- /* Per POSIX, `basename // /' must return `//' on platforms with
- distinct //. On platforms with drive letters, this generalizes
- to making `basename c: :' return `c:'. This rule is captured by
- skipping suffix stripping if base_name returned an absolute path
- or a drive letter (only possible if name is a file-system
- root). */
- if (argc == optind + 2 && IS_RELATIVE_FILE_NAME (name)
- && ! FILE_SYSTEM_PREFIX_LEN (name))
- remove_suffix (name, argv[optind + 1]);
-
- puts (name);
- free (name);
+ 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] : NULL, use_nuls);
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/c99-to-c89.diff b/src/c99-to-c89.diff
deleted file mode 100644
index 5545f6a..0000000
--- a/src/c99-to-c89.diff
+++ /dev/null
@@ -1,118 +0,0 @@
-Index: src/remove.c
-===================================================================
-RCS file: /fetish/cu/src/remove.c,v
-retrieving revision 1.158
-diff --git a/src/remove.c b/src/remove.c
-index 4728bdd..7477da5 100644
---- a/src/remove.c
-+++ b/src/remove.c
-@@ -236,9 +236,10 @@ pop_dir (Dirstack_state *ds)
- {
- size_t n_lengths = obstack_object_size (&ds->len_stack) / sizeof (size_t);
- size_t *length = obstack_base (&ds->len_stack);
-+ size_t top_len;
-
- assert (n_lengths > 0);
-- size_t top_len = length[n_lengths - 1];
-+ top_len = length[n_lengths - 1];
- assert (top_len >= 2);
-
- /* Pop the specified length of file name. */
-@@ -370,10 +371,11 @@ AD_stack_top (Dirstack_state const *ds)
- static void
- AD_stack_pop (Dirstack_state *ds)
- {
-+ struct AD_ent *top;
- assert (0 < AD_stack_height (ds));
-
- /* operate on Active_dir. pop and free top entry */
-- struct AD_ent *top = AD_stack_top (ds);
-+ top = AD_stack_top (ds);
- if (top->unremovable)
- hash_free (top->unremovable);
- obstack_blank (&ds->Active_dir, -(int) sizeof (struct AD_ent));
-@@ -815,6 +817,7 @@ prompt (int fd_cwd, Dirstack_state const *ds, char const *filename,
-
- if (write_protected || x->interactive == RMI_ALWAYS)
- {
-+ char const *quoted_name = quote (full_filename (filename));
- if (write_protected <= 0
- && cache_fstatat (fd_cwd, filename, sbuf, AT_SYMLINK_NOFOLLOW) != 0)
- {
-@@ -832,8 +835,6 @@ prompt (int fd_cwd, Dirstack_state const *ds, char const *filename,
- write_protected = EISDIR;
- }
-
-- char const *quoted_name = quote (full_filename (filename));
--
- if (0 < write_protected)
- {
- error (0, write_protected, _("cannot remove %s"), quoted_name);
-@@ -1487,6 +1488,7 @@ rm_1 (Dirstack_state *ds, char const *filename,
- return RM_ERROR;
- }
-
-+ {
- struct stat st;
- cache_stat_init (&st);
- cycle_check_init (&ds->cycle_check_state);
-@@ -1509,6 +1511,7 @@ rm_1 (Dirstack_state *ds, char const *filename,
- AD_push_initial (ds);
- AD_INIT_OTHER_MEMBERS ();
-
-+ {
- enum RM_status status = remove_entry (AT_FDCWD, ds, filename, &st, x, NULL);
- if (status == RM_NONEMPTY_DIR)
- {
-@@ -1525,6 +1528,8 @@ rm_1 (Dirstack_state *ds, char const *filename,
-
- ds_clear (ds);
- return status;
-+ }
-+ }
- }
-
- /* Remove all files and/or directories specified by N_FILES and FILE.
-Index: src/rm.c
-===================================================================
-RCS file: /fetish/cu/src/rm.c,v
-retrieving revision 1.140
-diff --git a/src/rm.c b/src/rm.c
-index 364a21c..7a24014 100644
---- a/src/rm.c
-+++ b/src/rm.c
-@@ -355,6 +355,7 @@ main (int argc, char **argv)
- quote ("/"));
- }
-
-+ {
- size_t n_files = argc - optind;
- char const *const *file = (char const *const *) argv + optind;
-
-@@ -368,7 +369,10 @@ main (int argc, char **argv)
- if (!yesno ())
- exit (EXIT_SUCCESS);
- }
-+ {
- enum RM_status status = rm (n_files, file, &x);
- assert (VALID_STATUS (status));
- exit (status == RM_ERROR ? EXIT_FAILURE : EXIT_SUCCESS);
-+ }
-+ }
- }
-Index: src/shred.c
-===================================================================
-RCS file: /fetish/cu/src/shred.c,v
-retrieving revision 1.130
-diff -u -p -r1.130 shred.c
---- a/src/shred.c 3 Sep 2006 02:53:16 -0000 1.130
-+++ b/src/shred.c 3 Oct 2006 13:48:24 -0000
-@@ -464,7 +464,7 @@ dopass (int fd, char const *qname, off_t
- out. Thus, it shouldn't give up on bad blocks. This
- code works because lim is always a multiple of
- SECTOR_SIZE, except at the end. */
-- verify (sizeof r % SECTOR_SIZE == 0);
-+ { verify (sizeof r % SECTOR_SIZE == 0); }
- if (errnum == EIO && 0 <= size && (soff | SECTOR_MASK) < lim)
- {
- size_t soff1 = (soff | SECTOR_MASK) + 1;
diff --git a/src/cat.c b/src/cat.c
index 90a73c2..8c39244 100644
--- a/src/cat.c
+++ b/src/cat.c
@@ -1,10 +1,10 @@
/* cat -- concatenate files and print on the standard output.
- Copyright (C) 88, 90, 91, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1988-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Differences from the Unix cat:
* Always unbuffered, -u is ignored.
@@ -31,28 +30,22 @@
#if HAVE_STROPTS_H
# include <stropts.h>
#endif
-#if HAVE_SYS_IOCTL_H
-# include <sys/ioctl.h>
-#endif
+#include <sys/ioctl.h>
#include "system.h"
+#include "ioblksize.h"
#include "error.h"
+#include "fadvise.h"
#include "full-write.h"
-#include "getpagesize.h"
-#include "quote.h"
#include "safe-read.h"
+#include "xfreopen.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "cat"
-#define AUTHORS "Torbjorn Granlund", "Richard M. Stallman"
-
-/* Undefine, to avoid warning about redefinition on some systems. */
-#undef max
-#define max(h,i) ((h) > (i) ? (h) : (i))
-
-/* Name under which this program was invoked. */
-char *program_name;
+#define AUTHORS \
+ proper_name_utf8 ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \
+ proper_name ("Richard M. Stallman")
/* Name of input file. May be "-". */
static char const *infile;
@@ -71,40 +64,44 @@ static char line_buf[LINE_COUNTER_BUF_LEN] =
'\t', '\0'
};
-/* Position in `line_buf' where printing starts. This will not change
+/* Position in 'line_buf' where printing starts. This will not change
unless the number of lines is larger than 999999. */
static char *line_num_print = line_buf + LINE_COUNTER_BUF_LEN - 8;
-/* Position of the first digit in `line_buf'. */
+/* Position of the first digit in 'line_buf'. */
static char *line_num_start = line_buf + LINE_COUNTER_BUF_LEN - 3;
-/* Position of the last digit in `line_buf'. */
+/* Position of the last digit in 'line_buf'. */
static char *line_num_end = line_buf + LINE_COUNTER_BUF_LEN - 3;
-/* Preserves the `cat' function's local `newlines' between invocations. */
+/* Preserves the 'cat' function's local 'newlines' between invocations. */
static int newlines2 = 0;
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
-Usage: %s [OPTION] [FILE]...\n\
+Usage: %s [OPTION]... [FILE]...\n\
"),
- program_name);
+ program_name);
+ fputs (_("\
+Concatenate FILE(s) to standard output.\n\
+"), stdout);
+
+ emit_stdin_note ();
+
fputs (_("\
-Concatenate FILE(s), or standard input, to standard output.\n\
\n\
-A, --show-all equivalent to -vET\n\
- -b, --number-nonblank number nonblank output lines\n\
+ -b, --number-nonblank number nonempty output lines, overrides -n\n\
-e equivalent to -vE\n\
-E, --show-ends display $ at end of each line\n\
-n, --number number all output lines\n\
- -s, --squeeze-blank never more than one single blank line\n\
+ -s, --squeeze-blank suppress repeated empty output lines\n\
"), stdout);
fputs (_("\
-t equivalent to -vT\n\
@@ -114,18 +111,14 @@ Concatenate FILE(s), or standard input, to standard output.\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- fputs (_("\
-\n\
-With no FILE, or when FILE is -, read standard input.\n\
-"), stdout);
printf (_("\
\n\
Examples:\n\
%s f - g Output f's contents, then standard input, then g's contents.\n\
%s Copy standard input to standard output.\n\
"),
- program_name, program_name);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ program_name, program_name);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -139,7 +132,7 @@ next_line_num (void)
do
{
if ((*endp)++ < '9')
- return;
+ return;
*endp-- = '0';
}
while (endp >= line_num_start);
@@ -151,7 +144,7 @@ next_line_num (void)
line_num_print--;
}
-/* Plain cat. Copies the file behind `input_desc' to STDOUT_FILENO.
+/* Plain cat. Copies the file behind 'input_desc' to STDOUT_FILENO.
Return true if successful. */
static bool
@@ -168,29 +161,29 @@ simple_cat (
/* Loop until the end of the file. */
- for (;;)
+ while (true)
{
/* Read a block of input. */
n_read = safe_read (input_desc, buf, bufsize);
if (n_read == SAFE_READ_ERROR)
- {
- error (0, errno, "%s", infile);
- return false;
- }
+ {
+ error (0, errno, "%s", quotef (infile));
+ return false;
+ }
/* End of this file? */
if (n_read == 0)
- return true;
+ return true;
/* Write this block out. */
{
- /* The following is ok, since we know that 0 < n_read. */
- size_t n = n_read;
- if (full_write (STDOUT_FILENO, buf, n) != n)
- error (EXIT_FAILURE, errno, _("write error"));
+ /* The following is ok, since we know that 0 < n_read. */
+ size_t n = n_read;
+ if (full_write (STDOUT_FILENO, buf, n) != n)
+ error (EXIT_FAILURE, errno, _("write error"));
}
}
}
@@ -246,7 +239,7 @@ cat (
/* Pointer to the next character in the input buffer. */
char *bpin;
- /* Pointer to the first non-valid byte in the input buffer, i.e. the
+ /* Pointer to the first non-valid byte in the input buffer, i.e., the
current end of the buffer. */
char *eob;
@@ -277,230 +270,231 @@ cat (
bpout = outbuf;
- for (;;)
+ while (true)
{
do
- {
- /* Write if there are at least OUTSIZE bytes in OUTBUF. */
-
- if (outbuf + outsize <= bpout)
- {
- char *wp = outbuf;
- size_t remaining_bytes;
- do
- {
- if (full_write (STDOUT_FILENO, wp, outsize) != outsize)
- error (EXIT_FAILURE, errno, _("write error"));
- wp += outsize;
- remaining_bytes = bpout - wp;
- }
- while (outsize <= remaining_bytes);
-
- /* Move the remaining bytes to the beginning of the
- buffer. */
-
- memmove (outbuf, wp, remaining_bytes);
- bpout = outbuf + remaining_bytes;
- }
-
- /* Is INBUF empty? */
-
- if (bpin > eob)
- {
- bool input_pending = false;
+ {
+ /* Write if there are at least OUTSIZE bytes in OUTBUF. */
+
+ if (outbuf + outsize <= bpout)
+ {
+ char *wp = outbuf;
+ size_t remaining_bytes;
+ do
+ {
+ if (full_write (STDOUT_FILENO, wp, outsize) != outsize)
+ error (EXIT_FAILURE, errno, _("write error"));
+ wp += outsize;
+ remaining_bytes = bpout - wp;
+ }
+ while (outsize <= remaining_bytes);
+
+ /* Move the remaining bytes to the beginning of the
+ buffer. */
+
+ memmove (outbuf, wp, remaining_bytes);
+ bpout = outbuf + remaining_bytes;
+ }
+
+ /* Is INBUF empty? */
+
+ if (bpin > eob)
+ {
+ bool input_pending = false;
#ifdef FIONREAD
- int n_to_read = 0;
-
- /* Is there any input to read immediately?
- If not, we are about to wait,
- so write all buffered output before waiting. */
-
- if (use_fionread
- && ioctl (input_desc, FIONREAD, &n_to_read) < 0)
- {
- /* Ultrix returns EOPNOTSUPP on NFS;
- HP-UX returns ENOTTY on pipes.
- SunOS returns EINVAL and
- More/BSD returns ENODEV on special files
- like /dev/null.
- Irix-5 returns ENOSYS on pipes. */
- if (errno == EOPNOTSUPP || errno == ENOTTY
- || errno == EINVAL || errno == ENODEV
- || errno == ENOSYS)
- use_fionread = false;
- else
- {
- error (0, errno, _("cannot do ioctl on %s"), quote (infile));
- newlines2 = newlines;
- return false;
- }
- }
- if (n_to_read != 0)
- input_pending = true;
+ int n_to_read = 0;
+
+ /* Is there any input to read immediately?
+ If not, we are about to wait,
+ so write all buffered output before waiting. */
+
+ if (use_fionread
+ && ioctl (input_desc, FIONREAD, &n_to_read) < 0)
+ {
+ /* Ultrix returns EOPNOTSUPP on NFS;
+ HP-UX returns ENOTTY on pipes.
+ SunOS returns EINVAL and
+ More/BSD returns ENODEV on special files
+ like /dev/null.
+ Irix-5 returns ENOSYS on pipes. */
+ if (errno == EOPNOTSUPP || errno == ENOTTY
+ || errno == EINVAL || errno == ENODEV
+ || errno == ENOSYS)
+ use_fionread = false;
+ else
+ {
+ error (0, errno, _("cannot do ioctl on %s"),
+ quoteaf (infile));
+ newlines2 = newlines;
+ return false;
+ }
+ }
+ if (n_to_read != 0)
+ input_pending = true;
#endif
- if (input_pending)
- write_pending (outbuf, &bpout);
-
- /* Read more input into INBUF. */
-
- n_read = safe_read (input_desc, inbuf, insize);
- if (n_read == SAFE_READ_ERROR)
- {
- error (0, errno, "%s", infile);
- write_pending (outbuf, &bpout);
- newlines2 = newlines;
- return false;
- }
- if (n_read == 0)
- {
- write_pending (outbuf, &bpout);
- newlines2 = newlines;
- return true;
- }
-
- /* Update the pointers and insert a sentinel at the buffer
- end. */
-
- bpin = inbuf;
- eob = bpin + n_read;
- *eob = '\n';
- }
- else
- {
- /* It was a real (not a sentinel) newline. */
-
- /* Was the last line empty?
- (i.e. have two or more consecutive newlines been read?) */
-
- if (++newlines > 0)
- {
- if (newlines >= 2)
- {
- /* Limit this to 2 here. Otherwise, with lots of
- consecutive newlines, the counter could wrap
- around at INT_MAX. */
- newlines = 2;
-
- /* Are multiple adjacent empty lines to be substituted
- by single ditto (-s), and this was the second empty
- line? */
- if (squeeze_blank)
- {
- ch = *bpin++;
- continue;
- }
- }
-
- /* Are line numbers to be written at empty lines (-n)? */
-
- if (number & !number_nonblank)
- {
- next_line_num ();
- bpout = stpcpy (bpout, line_num_print);
- }
- }
-
- /* Output a currency symbol if requested (-e). */
-
- if (show_ends)
- *bpout++ = '$';
-
- /* Output the newline. */
-
- *bpout++ = '\n';
- }
- ch = *bpin++;
- }
+ if (!input_pending)
+ write_pending (outbuf, &bpout);
+
+ /* Read more input into INBUF. */
+
+ n_read = safe_read (input_desc, inbuf, insize);
+ if (n_read == SAFE_READ_ERROR)
+ {
+ error (0, errno, "%s", quotef (infile));
+ write_pending (outbuf, &bpout);
+ newlines2 = newlines;
+ return false;
+ }
+ if (n_read == 0)
+ {
+ write_pending (outbuf, &bpout);
+ newlines2 = newlines;
+ return true;
+ }
+
+ /* Update the pointers and insert a sentinel at the buffer
+ end. */
+
+ bpin = inbuf;
+ eob = bpin + n_read;
+ *eob = '\n';
+ }
+ else
+ {
+ /* It was a real (not a sentinel) newline. */
+
+ /* Was the last line empty?
+ (i.e., have two or more consecutive newlines been read?) */
+
+ if (++newlines > 0)
+ {
+ if (newlines >= 2)
+ {
+ /* Limit this to 2 here. Otherwise, with lots of
+ consecutive newlines, the counter could wrap
+ around at INT_MAX. */
+ newlines = 2;
+
+ /* Are multiple adjacent empty lines to be substituted
+ by single ditto (-s), and this was the second empty
+ line? */
+ if (squeeze_blank)
+ {
+ ch = *bpin++;
+ continue;
+ }
+ }
+
+ /* Are line numbers to be written at empty lines (-n)? */
+
+ if (number && !number_nonblank)
+ {
+ next_line_num ();
+ bpout = stpcpy (bpout, line_num_print);
+ }
+ }
+
+ /* Output a currency symbol if requested (-e). */
+
+ if (show_ends)
+ *bpout++ = '$';
+
+ /* Output the newline. */
+
+ *bpout++ = '\n';
+ }
+ ch = *bpin++;
+ }
while (ch == '\n');
/* Are we at the beginning of a line, and line numbers are requested? */
if (newlines >= 0 && number)
- {
- next_line_num ();
- bpout = stpcpy (bpout, line_num_print);
- }
+ {
+ next_line_num ();
+ bpout = stpcpy (bpout, line_num_print);
+ }
/* Here CH cannot contain a newline character. */
/* The loops below continue until a newline character is found,
- which means that the buffer is empty or that a proper newline
- has been found. */
+ which means that the buffer is empty or that a proper newline
+ has been found. */
- /* If quoting, i.e. at least one of -v, -e, or -t specified,
- scan for chars that need conversion. */
+ /* If quoting, i.e., at least one of -v, -e, or -t specified,
+ scan for chars that need conversion. */
if (show_nonprinting)
- {
- for (;;)
- {
- if (ch >= 32)
- {
- if (ch < 127)
- *bpout++ = ch;
- else if (ch == 127)
- {
- *bpout++ = '^';
- *bpout++ = '?';
- }
- else
- {
- *bpout++ = 'M';
- *bpout++ = '-';
- if (ch >= 128 + 32)
- {
- if (ch < 128 + 127)
- *bpout++ = ch - 128;
- else
- {
- *bpout++ = '^';
- *bpout++ = '?';
- }
- }
- else
- {
- *bpout++ = '^';
- *bpout++ = ch - 128 + 64;
- }
- }
- }
- else if (ch == '\t' && !show_tabs)
- *bpout++ = '\t';
- else if (ch == '\n')
- {
- newlines = -1;
- break;
- }
- else
- {
- *bpout++ = '^';
- *bpout++ = ch + 64;
- }
-
- ch = *bpin++;
- }
- }
+ {
+ while (true)
+ {
+ if (ch >= 32)
+ {
+ if (ch < 127)
+ *bpout++ = ch;
+ else if (ch == 127)
+ {
+ *bpout++ = '^';
+ *bpout++ = '?';
+ }
+ else
+ {
+ *bpout++ = 'M';
+ *bpout++ = '-';
+ if (ch >= 128 + 32)
+ {
+ if (ch < 128 + 127)
+ *bpout++ = ch - 128;
+ else
+ {
+ *bpout++ = '^';
+ *bpout++ = '?';
+ }
+ }
+ else
+ {
+ *bpout++ = '^';
+ *bpout++ = ch - 128 + 64;
+ }
+ }
+ }
+ else if (ch == '\t' && !show_tabs)
+ *bpout++ = '\t';
+ else if (ch == '\n')
+ {
+ newlines = -1;
+ break;
+ }
+ else
+ {
+ *bpout++ = '^';
+ *bpout++ = ch + 64;
+ }
+
+ ch = *bpin++;
+ }
+ }
else
- {
- /* Not quoting, neither of -v, -e, or -t specified. */
- for (;;)
- {
- if (ch == '\t' && show_tabs)
- {
- *bpout++ = '^';
- *bpout++ = ch + 64;
- }
- else if (ch != '\n')
- *bpout++ = ch;
- else
- {
- newlines = -1;
- break;
- }
-
- ch = *bpin++;
- }
- }
+ {
+ /* Not quoting, neither of -v, -e, or -t specified. */
+ while (true)
+ {
+ if (ch == '\t' && show_tabs)
+ {
+ *bpout++ = '^';
+ *bpout++ = ch + 64;
+ }
+ else if (ch != '\n')
+ *bpout++ = ch;
+ else
+ {
+ newlines = -1;
+ break;
+ }
+
+ ch = *bpin++;
+ }
+ }
}
}
@@ -533,8 +527,8 @@ main (int argc, char **argv)
/* I-node number of the output. */
ino_t out_ino;
- /* True if the output file should not be the same as any input file. */
- bool check_redirection = true;
+ /* True if the output is a regular file. */
+ bool out_isreg;
/* Nonzero if we have ever read standard input. */
bool have_read_stdin = false;
@@ -565,7 +559,7 @@ main (int argc, char **argv)
};
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -579,62 +573,62 @@ main (int argc, char **argv)
/* Parse command line options. */
while ((c = getopt_long (argc, argv, "benstuvAET", long_options, NULL))
- != -1)
+ != -1)
{
switch (c)
- {
- case 'b':
- number = true;
- number_nonblank = true;
- break;
-
- case 'e':
- show_ends = true;
- show_nonprinting = true;
- break;
-
- case 'n':
- number = true;
- break;
-
- case 's':
- squeeze_blank = true;
- break;
-
- case 't':
- show_tabs = true;
- show_nonprinting = true;
- break;
-
- case 'u':
- /* We provide the -u feature unconditionally. */
- break;
-
- case 'v':
- show_nonprinting = true;
- break;
-
- case 'A':
- show_nonprinting = true;
- show_ends = true;
- show_tabs = true;
- break;
-
- case 'E':
- show_ends = true;
- break;
-
- case 'T':
- show_tabs = true;
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'b':
+ number = true;
+ number_nonblank = true;
+ break;
+
+ case 'e':
+ show_ends = true;
+ show_nonprinting = true;
+ break;
+
+ case 'n':
+ number = true;
+ break;
+
+ case 's':
+ squeeze_blank = true;
+ break;
+
+ case 't':
+ show_tabs = true;
+ show_nonprinting = true;
+ break;
+
+ case 'u':
+ /* We provide the -u feature unconditionally. */
+ break;
+
+ case 'v':
+ show_nonprinting = true;
+ break;
+
+ case 'A':
+ show_nonprinting = true;
+ show_ends = true;
+ show_tabs = true;
+ break;
+
+ case 'E':
+ show_ends = true;
+ break;
+
+ case 'T':
+ show_tabs = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
}
/* Get device, i-node number, and optimal blocksize of output. */
@@ -642,32 +636,16 @@ main (int argc, char **argv)
if (fstat (STDOUT_FILENO, &stat_buf) < 0)
error (EXIT_FAILURE, errno, _("standard output"));
- outsize = ST_BLKSIZE (stat_buf);
- /* Input file can be output file for non-regular files.
- fstat on pipes returns S_IFSOCK on some systems, S_IFIFO
- on others, so the checking should not be done for those types,
- and to allow things like cat < /dev/tty > /dev/tty, checking
- is not done for device files either. */
-
- if (S_ISREG (stat_buf.st_mode))
- {
- out_dev = stat_buf.st_dev;
- out_ino = stat_buf.st_ino;
- }
- else
- {
- check_redirection = false;
-#ifdef lint /* Suppress `used before initialized' warning. */
- out_dev = 0;
- out_ino = 0;
-#endif
- }
+ outsize = io_blksize (stat_buf);
+ out_dev = stat_buf.st_dev;
+ out_ino = stat_buf.st_ino;
+ out_isreg = S_ISREG (stat_buf.st_mode) != 0;
- if (! (number | show_ends | squeeze_blank))
+ if (! (number || show_ends || squeeze_blank))
{
file_open_mode |= O_BINARY;
if (O_BINARY && ! isatty (STDOUT_FILENO))
- freopen (NULL, "wb", stdout);
+ xfreopen (NULL, "wb", stdout);
}
/* Check if any of the input files are the same as the output file. */
@@ -680,109 +658,110 @@ main (int argc, char **argv)
do
{
if (argind < argc)
- infile = argv[argind];
+ infile = argv[argind];
if (STREQ (infile, "-"))
- {
- have_read_stdin = true;
- input_desc = STDIN_FILENO;
- if ((file_open_mode & O_BINARY) && ! isatty (STDIN_FILENO))
- freopen (NULL, "rb", stdin);
- }
+ {
+ have_read_stdin = true;
+ input_desc = STDIN_FILENO;
+ if ((file_open_mode & O_BINARY) && ! isatty (STDIN_FILENO))
+ xfreopen (NULL, "rb", stdin);
+ }
else
- {
- input_desc = open (infile, file_open_mode);
- if (input_desc < 0)
- {
- error (0, errno, "%s", infile);
- ok = false;
- continue;
- }
- }
+ {
+ input_desc = open (infile, file_open_mode);
+ if (input_desc < 0)
+ {
+ error (0, errno, "%s", quotef (infile));
+ ok = false;
+ continue;
+ }
+ }
if (fstat (input_desc, &stat_buf) < 0)
- {
- error (0, errno, "%s", infile);
- ok = false;
- goto contin;
- }
- insize = ST_BLKSIZE (stat_buf);
-
- /* Compare the device and i-node numbers of this input file with
- the corresponding values of the (output file associated with)
- stdout, and skip this input file if they coincide. Input
- files cannot be redirected to themselves. */
-
- if (check_redirection
- && stat_buf.st_dev == out_dev && stat_buf.st_ino == out_ino
- && (input_desc != STDIN_FILENO))
- {
- error (0, 0, _("%s: input file is output file"), infile);
- ok = false;
- goto contin;
- }
-
- /* Select which version of `cat' to use. If any format-oriented
- options were given use `cat'; otherwise use `simple_cat'. */
-
- if (! (number | show_ends | show_nonprinting
- | show_tabs | squeeze_blank))
- {
- insize = max (insize, outsize);
- inbuf = xmalloc (insize + page_size - 1);
-
- ok &= simple_cat (ptr_align (inbuf, page_size), insize);
- }
+ {
+ error (0, errno, "%s", quotef (infile));
+ ok = false;
+ goto contin;
+ }
+ insize = io_blksize (stat_buf);
+
+ fdadvise (input_desc, 0, 0, FADVISE_SEQUENTIAL);
+
+ /* Don't copy a nonempty regular file to itself, as that would
+ merely exhaust the output device. It's better to catch this
+ error earlier rather than later. */
+
+ if (out_isreg
+ && stat_buf.st_dev == out_dev && stat_buf.st_ino == out_ino
+ && lseek (input_desc, 0, SEEK_CUR) < stat_buf.st_size)
+ {
+ error (0, 0, _("%s: input file is output file"), quotef (infile));
+ ok = false;
+ goto contin;
+ }
+
+ /* Select which version of 'cat' to use. If any format-oriented
+ options were given use 'cat'; otherwise use 'simple_cat'. */
+
+ if (! (number || show_ends || show_nonprinting
+ || show_tabs || squeeze_blank))
+ {
+ insize = MAX (insize, outsize);
+ inbuf = xmalloc (insize + page_size - 1);
+
+ ok &= simple_cat (ptr_align (inbuf, page_size), insize);
+ }
else
- {
- inbuf = xmalloc (insize + 1 + page_size - 1);
-
- /* Why are
- (OUTSIZE - 1 + INSIZE * 4 + LINE_COUNTER_BUF_LEN + PAGE_SIZE - 1)
- bytes allocated for the output buffer?
-
- A test whether output needs to be written is done when the input
- buffer empties or when a newline appears in the input. After
- output is written, at most (OUTSIZE - 1) bytes will remain in the
- buffer. Now INSIZE bytes of input is read. Each input character
- may grow by a factor of 4 (by the prepending of M-^). If all
- characters do, and no newlines appear in this block of input, we
- will have at most (OUTSIZE - 1 + INSIZE * 4) bytes in the buffer.
- If the last character in the preceding block of input was a
- newline, a line number may be written (according to the given
- options) as the first thing in the output buffer. (Done after the
- new input is read, but before processing of the input begins.)
- A line number requires seldom more than LINE_COUNTER_BUF_LEN
- positions.
-
- Align the output buffer to a page size boundary, for efficency on
- some paging implementations, so add PAGE_SIZE - 1 bytes to the
- request to make room for the alignment. */
-
- outbuf = xmalloc (outsize - 1 + insize * 4 + LINE_COUNTER_BUF_LEN
- + page_size - 1);
-
- ok &= cat (ptr_align (inbuf, page_size), insize,
- ptr_align (outbuf, page_size), outsize, show_nonprinting,
- show_tabs, number, number_nonblank, show_ends,
- squeeze_blank);
-
- free (outbuf);
- }
+ {
+ inbuf = xmalloc (insize + 1 + page_size - 1);
+
+ /* Why are
+ (OUTSIZE - 1 + INSIZE * 4 + LINE_COUNTER_BUF_LEN + PAGE_SIZE - 1)
+ bytes allocated for the output buffer?
+
+ A test whether output needs to be written is done when the input
+ buffer empties or when a newline appears in the input. After
+ output is written, at most (OUTSIZE - 1) bytes will remain in the
+ buffer. Now INSIZE bytes of input is read. Each input character
+ may grow by a factor of 4 (by the prepending of M-^). If all
+ characters do, and no newlines appear in this block of input, we
+ will have at most (OUTSIZE - 1 + INSIZE * 4) bytes in the buffer.
+ If the last character in the preceding block of input was a
+ newline, a line number may be written (according to the given
+ options) as the first thing in the output buffer. (Done after the
+ new input is read, but before processing of the input begins.)
+ A line number requires seldom more than LINE_COUNTER_BUF_LEN
+ positions.
+
+ Align the output buffer to a page size boundary, for efficiency
+ on some paging implementations, so add PAGE_SIZE - 1 bytes to the
+ request to make room for the alignment. */
+
+ outbuf = xmalloc (outsize - 1 + insize * 4 + LINE_COUNTER_BUF_LEN
+ + page_size - 1);
+
+ ok &= cat (ptr_align (inbuf, page_size), insize,
+ ptr_align (outbuf, page_size), outsize, show_nonprinting,
+ show_tabs, number, number_nonblank, show_ends,
+ squeeze_blank);
+
+ free (outbuf);
+ }
free (inbuf);
contin:
if (!STREQ (infile, "-") && close (input_desc) < 0)
- {
- error (0, errno, "%s", infile);
- ok = false;
- }
+ {
+ error (0, errno, "%s", quotef (infile));
+ ok = false;
+ }
}
while (++argind < argc);
if (have_read_stdin && close (STDIN_FILENO) < 0)
error (EXIT_FAILURE, errno, _("closing standard input"));
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/chcon.c b/src/chcon.c
new file mode 100644
index 0000000..88bc53e
--- /dev/null
+++ b/src/chcon.c
@@ -0,0 +1,586 @@
+/* chcon -- change security context of files
+ Copyright (C) 2005-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+#include <config.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <getopt.h>
+
+#include "system.h"
+#include "dev-ino.h"
+#include "error.h"
+#include "ignore-value.h"
+#include "quote.h"
+#include "root-dev-ino.h"
+#include "selinux-at.h"
+#include "xfts.h"
+
+/* The official name of this program (e.g., no 'g' prefix). */
+#define PROGRAM_NAME "chcon"
+
+#define AUTHORS \
+ proper_name ("Russell Coker"), \
+ proper_name ("Jim Meyering")
+
+/* If nonzero, and the systems has support for it, change the context
+ of symbolic links rather than any files they point to. */
+static bool affect_symlink_referent;
+
+/* If true, change the modes of directories recursively. */
+static bool recurse;
+
+/* Level of verbosity. */
+static bool verbose;
+
+/* Pointer to the device and inode numbers of '/', when --recursive.
+ Otherwise NULL. */
+static struct dev_ino *root_dev_ino;
+
+/* The name of the context file is being given. */
+static char const *specified_context;
+
+/* Specific components of the context */
+static char const *specified_user;
+static char const *specified_role;
+static char const *specified_range;
+static char const *specified_type;
+
+/* For long options that have no equivalent short option, use a
+ non-character as a pseudo short option, starting with CHAR_MAX + 1. */
+enum
+{
+ DEREFERENCE_OPTION = CHAR_MAX + 1,
+ NO_PRESERVE_ROOT,
+ PRESERVE_ROOT,
+ REFERENCE_FILE_OPTION
+};
+
+static struct option const long_options[] =
+{
+ {"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},
+ {NULL, 0, NULL, 0}
+};
+
+/* Given a security context, CONTEXT, derive a context_t (*RET),
+ setting any portions selected via the global variables, specified_user,
+ specified_role, etc. */
+static int
+compute_context_from_mask (char const *context, context_t *ret)
+{
+ bool ok = true;
+ context_t new_context = context_new (context);
+ if (!new_context)
+ {
+ error (0, errno, _("failed to create security context: %s"),
+ quote (context));
+ return 1;
+ }
+
+#define SET_COMPONENT(C, comp) \
+ do \
+ { \
+ if (specified_ ## comp \
+ && context_ ## comp ## _set ((C), specified_ ## comp)) \
+ { \
+ error (0, errno, \
+ _("failed to set %s security context component to %s"), \
+ #comp, quote (specified_ ## comp)); \
+ ok = false; \
+ } \
+ } \
+ while (0)
+
+ SET_COMPONENT (new_context, user);
+ SET_COMPONENT (new_context, range);
+ SET_COMPONENT (new_context, role);
+ SET_COMPONENT (new_context, type);
+
+ if (!ok)
+ {
+ int saved_errno = errno;
+ context_free (new_context);
+ errno = saved_errno;
+ return 1;
+ }
+
+ *ret = new_context;
+ return 0;
+}
+
+/* Change the context of FILE, using specified components.
+ If it is a directory and -R is given, recurse.
+ Return 0 if successful, 1 if errors occurred. */
+
+static int
+change_file_context (int fd, char const *file)
+{
+ char *file_context = NULL;
+ context_t context IF_LINT (= 0);
+ char const * context_string;
+ int errors = 0;
+
+ if (specified_context == NULL)
+ {
+ int status = (affect_symlink_referent
+ ? getfileconat (fd, file, &file_context)
+ : lgetfileconat (fd, file, &file_context));
+
+ if (status < 0 && errno != ENODATA)
+ {
+ error (0, errno, _("failed to get security context of %s"),
+ quoteaf (file));
+ return 1;
+ }
+
+ /* 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 == NULL)
+ {
+ error (0, 0, _("can't apply partial context to unlabeled file %s"),
+ quoteaf (file));
+ return 1;
+ }
+
+ if (compute_context_from_mask (file_context, &context))
+ return 1;
+
+ context_string = context_str (context);
+ }
+ else
+ {
+ context_string = specified_context;
+ }
+
+ if (file_context == NULL || ! STREQ (context_string, file_context))
+ {
+ int fail = (affect_symlink_referent
+ ? setfileconat (fd, file, se_const (context_string))
+ : lsetfileconat (fd, file, se_const (context_string)));
+
+ if (fail)
+ {
+ errors = 1;
+ error (0, errno, _("failed to change context of %s to %s"),
+ quoteaf_n (0, file), quote_n (1, context_string));
+ }
+ }
+
+ if (specified_context == NULL)
+ {
+ context_free (context);
+ freecon (file_context);
+ }
+
+ return errors;
+}
+
+/* Change the context of FILE.
+ Return true if successful. This function is called
+ once for every file system object that fts encounters. */
+
+static bool
+process_file (FTS *fts, FTSENT *ent)
+{
+ char const *file_full_name = ent->fts_path;
+ char const *file = ent->fts_accpath;
+ const struct stat *file_stats = ent->fts_statp;
+ bool ok = true;
+
+ switch (ent->fts_info)
+ {
+ case FTS_D:
+ if (recurse)
+ {
+ if (ROOT_DEV_INO_CHECK (root_dev_ino, ent->fts_statp))
+ {
+ /* This happens e.g., with "chcon -R --preserve-root ... /"
+ and with "chcon -RH --preserve-root ... symlink-to-root". */
+ ROOT_DEV_INO_WARN (file_full_name);
+ /* Tell fts not to traverse into this hierarchy. */
+ fts_set (fts, ent, FTS_SKIP);
+ /* Ensure that we do not process "/" on the second visit. */
+ ignore_value (fts_read (fts));
+ return false;
+ }
+ return true;
+ }
+ break;
+
+ case FTS_DP:
+ if (! recurse)
+ return true;
+ break;
+
+ case FTS_NS:
+ /* For a top-level file or directory, this FTS_NS (stat failed)
+ indicator is determined at the time of the initial fts_open call.
+ With programs like chmod, chown, and chgrp, that modify
+ permissions, it is possible that the file in question is
+ accessible when control reaches this point. So, if this is
+ the first time we've seen the FTS_NS for this file, tell
+ fts_read to stat it "again". */
+ if (ent->fts_level == 0 && ent->fts_number == 0)
+ {
+ ent->fts_number = 1;
+ fts_set (fts, ent, FTS_AGAIN);
+ return true;
+ }
+ error (0, ent->fts_errno, _("cannot access %s"),
+ quoteaf (file_full_name));
+ ok = false;
+ break;
+
+ case FTS_ERR:
+ error (0, ent->fts_errno, "%s", quotef (file_full_name));
+ ok = false;
+ break;
+
+ case FTS_DNR:
+ error (0, ent->fts_errno, _("cannot read directory %s"),
+ quoteaf (file_full_name));
+ ok = false;
+ break;
+
+ case FTS_DC: /* directory that causes cycles */
+ if (cycle_warning_required (fts, ent))
+ {
+ emit_cycle_warning (file_full_name);
+ return false;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (ent->fts_info == FTS_DP
+ && ok && ROOT_DEV_INO_CHECK (root_dev_ino, file_stats))
+ {
+ ROOT_DEV_INO_WARN (file_full_name);
+ ok = false;
+ }
+
+ if (ok)
+ {
+ if (verbose)
+ printf (_("changing security context of %s\n"),
+ quoteaf (file_full_name));
+
+ if (change_file_context (fts->fts_cwd_fd, file) != 0)
+ ok = false;
+ }
+
+ if ( ! recurse)
+ fts_set (fts, ent, FTS_SKIP);
+
+ return ok;
+}
+
+/* Recursively operate on the specified FILES (the last entry
+ of which is NULL). BIT_FLAGS controls how fts works.
+ Return true if successful. */
+
+static bool
+process_files (char **files, int bit_flags)
+{
+ bool ok = true;
+
+ FTS *fts = xfts_open (files, bit_flags, NULL);
+
+ while (1)
+ {
+ FTSENT *ent;
+
+ ent = fts_read (fts);
+ if (ent == NULL)
+ {
+ if (errno != 0)
+ {
+ /* FIXME: try to give a better message */
+ error (0, errno, _("fts_read failed"));
+ ok = false;
+ }
+ break;
+ }
+
+ ok &= process_file (fts, ent);
+ }
+
+ if (fts_close (fts) != 0)
+ {
+ error (0, errno, _("fts_close failed"));
+ ok = false;
+ }
+
+ return ok;
+}
+
+void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ emit_try_help ();
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION]... CONTEXT FILE...\n\
+ or: %s [OPTION]... [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE...\n\
+ or: %s [OPTION]... --reference=RFILE FILE...\n\
+"),
+ program_name, program_name, program_name);
+ fputs (_("\
+Change the SELinux security context of each FILE to CONTEXT.\n\
+With --reference, change the security context of each FILE to that of RFILE.\n\
+"), stdout);
+
+ 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);
+ emit_ancillary_info (PROGRAM_NAME);
+ }
+ exit (status);
+}
+
+int
+main (int argc, char **argv)
+{
+ /* Bit flags that control how fts works. */
+ int bit_flags = FTS_PHYSICAL;
+
+ /* 1 if --dereference, 0 if --no-dereference, -1 if neither has been
+ specified. */
+ int dereference = -1;
+
+ bool ok;
+ bool preserve_root = false;
+ bool component_specified = false;
+ char *reference_file = NULL;
+ int optc;
+
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ atexit (close_stdout);
+
+ while ((optc = getopt_long (argc, argv, "HLPRhvu:r:t:l:", long_options, NULL))
+ != -1)
+ {
+ switch (optc)
+ {
+ case 'H': /* Traverse command-line symlinks-to-directories. */
+ bit_flags = FTS_COMFOLLOW | FTS_PHYSICAL;
+ break;
+
+ case 'L': /* Traverse all symlinks-to-directories. */
+ bit_flags = FTS_LOGICAL;
+ break;
+
+ case 'P': /* Traverse no symlinks-to-directories. */
+ bit_flags = FTS_PHYSICAL;
+ break;
+
+ case 'h': /* --no-dereference: affect symlinks */
+ dereference = 0;
+ break;
+
+ case DEREFERENCE_OPTION: /* --dereference: affect the referent
+ of each symlink */
+ dereference = 1;
+ break;
+
+ case NO_PRESERVE_ROOT:
+ preserve_root = false;
+ break;
+
+ case PRESERVE_ROOT:
+ preserve_root = true;
+ break;
+
+ case REFERENCE_FILE_OPTION:
+ reference_file = optarg;
+ break;
+
+ case 'R':
+ recurse = true;
+ break;
+
+ case 'f':
+ /* ignore */
+ break;
+
+ case 'v':
+ verbose = true;
+ break;
+
+ case 'u':
+ specified_user = optarg;
+ component_specified = true;
+ break;
+
+ case 'r':
+ specified_role = optarg;
+ component_specified = true;
+ break;
+
+ case 't':
+ specified_type = optarg;
+ component_specified = true;
+ break;
+
+ case 'l':
+ specified_range = optarg;
+ component_specified = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
+ }
+
+ if (recurse)
+ {
+ if (bit_flags == FTS_PHYSICAL)
+ {
+ if (dereference == 1)
+ error (EXIT_FAILURE, 0,
+ _("-R --dereference requires either -H or -L"));
+ affect_symlink_referent = false;
+ }
+ else
+ {
+ if (dereference == 0)
+ error (EXIT_FAILURE, 0, _("-R -h requires -P"));
+ affect_symlink_referent = true;
+ }
+ }
+ else
+ {
+ bit_flags = FTS_PHYSICAL;
+ affect_symlink_referent = (dereference != 0);
+ }
+
+ if (argc - optind < (reference_file || component_specified ? 1 : 2))
+ {
+ if (argc <= optind)
+ error (0, 0, _("missing operand"));
+ else
+ error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
+ usage (EXIT_FAILURE);
+ }
+
+ if (reference_file)
+ {
+ char *ref_context = NULL;
+
+ if (getfilecon (reference_file, &ref_context) < 0)
+ error (EXIT_FAILURE, errno, _("failed to get security context of %s"),
+ quoteaf (reference_file));
+
+ specified_context = ref_context;
+ }
+ else if (component_specified)
+ {
+ /* FIXME: it's already null, so this is a no-op. */
+ specified_context = NULL;
+ }
+ else
+ {
+ specified_context = argv[optind++];
+ if (security_check_context (se_const (specified_context)) < 0)
+ error (EXIT_FAILURE, errno, _("invalid context: %s"),
+ quote (specified_context));
+ }
+
+ if (reference_file && component_specified)
+ {
+ error (0, 0, _("conflicting security context specifiers given"));
+ usage (EXIT_FAILURE);
+ }
+
+ if (recurse && preserve_root)
+ {
+ static struct dev_ino dev_ino_buf;
+ root_dev_ino = get_root_dev_ino (&dev_ino_buf);
+ if (root_dev_ino == NULL)
+ error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
+ quoteaf ("/"));
+ }
+ else
+ {
+ root_dev_ino = NULL;
+ }
+
+ ok = process_files (argv + optind, bit_flags | FTS_NOSTAT);
+
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/chgrp.c b/src/chgrp.c
index faf58d3..711737d 100644
--- a/src/chgrp.c
+++ b/src/chgrp.c
@@ -1,10 +1,10 @@
/* chgrp -- change group ownership of files
- Copyright (C) 89, 90, 91, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
@@ -27,23 +26,21 @@
#include "chown-core.h"
#include "error.h"
#include "fts_.h"
-#include "group-member.h"
#include "quote.h"
#include "root-dev-ino.h"
#include "xstrtol.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "chgrp"
-#define AUTHORS "David MacKenzie", "Jim Meyering"
+#define AUTHORS \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Jim Meyering")
#if ! HAVE_ENDGRENT
# define endgrent() ((void) 0)
#endif
-/* The name the program was run with. */
-char *program_name;
-
/* The argument to the --reference option. Use the group ID of this file.
This file must exist. */
static char *reference_file;
@@ -86,15 +83,16 @@ parse_group (const char *name)
{
struct group *grp = getgrnam (name);
if (grp)
- gid = grp->gr_gid;
+ gid = grp->gr_gid;
else
- {
- unsigned long int tmp;
- if (! (xstrtoul (name, NULL, 10, &tmp, "") == LONGINT_OK
- && tmp <= GID_T_MAX))
- error (EXIT_FAILURE, 0, _("invalid group %s"), quote (name));
- gid = tmp;
- }
+ {
+ unsigned long int tmp;
+ if (! (xstrtoul (name, NULL, 10, &tmp, "") == LONGINT_OK
+ && tmp <= GID_T_MAX))
+ error (EXIT_FAILURE, 0, _("invalid group: %s"),
+ quote (name));
+ gid = tmp;
+ }
endgrent (); /* Save a file descriptor. */
}
@@ -105,41 +103,46 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... GROUP FILE...\n\
or: %s [OPTION]... --reference=RFILE FILE...\n\
"),
- program_name, program_name);
+ program_name, program_name);
fputs (_("\
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 (_("\
- -h, --no-dereference affect each symbolic link instead of any referenced\n\
- file (useful only on systems that can change the\n\
+ (useful only on systems that can change the\n\
ownership of a symlink)\n\
"), stdout);
fputs (_("\
- --no-preserve-root do not treat `/' specially (the default)\n\
- --preserve-root fail to operate recursively on `/'\n\
+ --no-preserve-root do not treat '/' specially (the default)\n\
+ --preserve-root fail to operate recursively on '/'\n\
"), stdout);
fputs (_("\
- -f, --silent, --quiet suppress most error messages\n\
--reference=RFILE use RFILE's group rather than specifying a\n\
GROUP value\n\
+"), stdout);
+ fputs (_("\
-R, --recursive operate on files and directories recursively\n\
- -v, --verbose output a diagnostic for every file processed\n\
-\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\
@@ -159,8 +162,8 @@ Examples:\n\
%s staff /u Change the group of /u to \"staff\".\n\
%s -hR staff /u Change the group of /u and subfiles to \"staff\".\n\
"),
- program_name, program_name);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ program_name, program_name);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -183,7 +186,7 @@ main (int argc, char **argv)
int optc;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -193,75 +196,75 @@ main (int argc, char **argv)
chopt_init (&chopt);
while ((optc = getopt_long (argc, argv, "HLPRcfhv", long_options, NULL))
- != -1)
+ != -1)
{
switch (optc)
- {
- case 'H': /* Traverse command-line symlinks-to-directories. */
- bit_flags = FTS_COMFOLLOW | FTS_PHYSICAL;
- break;
-
- case 'L': /* Traverse all symlinks-to-directories. */
- bit_flags = FTS_LOGICAL;
- break;
-
- case 'P': /* Traverse no symlinks-to-directories. */
- bit_flags = FTS_PHYSICAL;
- break;
-
- case 'h': /* --no-dereference: affect symlinks */
- dereference = 0;
- break;
-
- case DEREFERENCE_OPTION: /* --dereference: affect the referent
- of each symlink */
- dereference = 1;
- break;
-
- case NO_PRESERVE_ROOT:
- preserve_root = false;
- break;
-
- case PRESERVE_ROOT:
- preserve_root = true;
- break;
-
- case REFERENCE_FILE_OPTION:
- reference_file = optarg;
- break;
-
- case 'R':
- chopt.recurse = true;
- break;
-
- case 'c':
- chopt.verbosity = V_changes_only;
- break;
-
- case 'f':
- chopt.force_silent = true;
- break;
-
- case 'v':
- chopt.verbosity = V_high;
- break;
-
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'H': /* Traverse command-line symlinks-to-directories. */
+ bit_flags = FTS_COMFOLLOW | FTS_PHYSICAL;
+ break;
+
+ case 'L': /* Traverse all symlinks-to-directories. */
+ bit_flags = FTS_LOGICAL;
+ break;
+
+ case 'P': /* Traverse no symlinks-to-directories. */
+ bit_flags = FTS_PHYSICAL;
+ break;
+
+ case 'h': /* --no-dereference: affect symlinks */
+ dereference = 0;
+ break;
+
+ case DEREFERENCE_OPTION: /* --dereference: affect the referent
+ of each symlink */
+ dereference = 1;
+ break;
+
+ case NO_PRESERVE_ROOT:
+ preserve_root = false;
+ break;
+
+ case PRESERVE_ROOT:
+ preserve_root = true;
+ break;
+
+ case REFERENCE_FILE_OPTION:
+ reference_file = optarg;
+ break;
+
+ case 'R':
+ chopt.recurse = true;
+ break;
+
+ case 'c':
+ chopt.verbosity = V_changes_only;
+ break;
+
+ case 'f':
+ chopt.force_silent = true;
+ break;
+
+ case 'v':
+ chopt.verbosity = V_high;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
}
if (chopt.recurse)
{
if (bit_flags == FTS_PHYSICAL)
- {
- if (dereference == 1)
- error (EXIT_FAILURE, 0,
- _("-R --dereference requires either -H or -L"));
- dereference = 0;
- }
+ {
+ if (dereference == 1)
+ error (EXIT_FAILURE, 0,
+ _("-R --dereference requires either -H or -L"));
+ dereference = 0;
+ }
}
else
{
@@ -272,9 +275,9 @@ main (int argc, char **argv)
if (argc - optind < (reference_file ? 1 : 2))
{
if (argc <= optind)
- error (0, 0, _("missing operand"));
+ error (0, 0, _("missing operand"));
else
- error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
+ error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
usage (EXIT_FAILURE);
}
@@ -282,8 +285,8 @@ main (int argc, char **argv)
{
struct stat ref_stats;
if (stat (reference_file, &ref_stats))
- error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
- quote (reference_file));
+ error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
+ quoteaf (reference_file));
gid = ref_stats.st_gid;
chopt.group_name = gid_to_name (ref_stats.st_gid);
@@ -295,20 +298,21 @@ main (int argc, char **argv)
gid = parse_group (group_name);
}
- if (chopt.recurse & preserve_root)
+ if (chopt.recurse && preserve_root)
{
static struct dev_ino dev_ino_buf;
chopt.root_dev_ino = get_root_dev_ino (&dev_ino_buf);
if (chopt.root_dev_ino == NULL)
- error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
- quote ("/"));
+ error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
+ quoteaf ("/"));
}
+ bit_flags |= FTS_DEFER_STAT;
ok = chown_files (argv + optind, bit_flags,
- (uid_t) -1, gid,
- (uid_t) -1, (gid_t) -1, &chopt);
+ (uid_t) -1, gid,
+ (uid_t) -1, (gid_t) -1, &chopt);
chopt_free (&chopt);
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/chmod.c b/src/chmod.c
index 028c882..3ea66eb 100644
--- a/src/chmod.c
+++ b/src/chmod.c
@@ -1,10 +1,10 @@
/* chmod -- change permission modes of files
- Copyright (C) 89, 90, 91, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by David MacKenzie <djm@gnu.ai.mit.edu> */
@@ -26,17 +25,18 @@
#include "dev-ino.h"
#include "error.h"
#include "filemode.h"
+#include "ignore-value.h"
#include "modechange.h"
-#include "openat.h"
#include "quote.h"
-#include "quotearg.h"
#include "root-dev-ino.h"
#include "xfts.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "chmod"
-#define AUTHORS "David MacKenzie", "Jim Meyering"
+#define AUTHORS \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Jim Meyering")
enum Change_status
{
@@ -58,9 +58,6 @@ enum Verbosity
V_off
};
-/* The name the program was run with. */
-char *program_name;
-
/* The desired change to the mode. */
static struct mode_change *change;
@@ -70,7 +67,7 @@ static mode_t umask_value;
/* If true, change the modes of directories recursively. */
static bool recurse;
-/* If true, force silence (no error messages). */
+/* If true, force silence (suppress most of error messages). */
static bool force_silent;
/* If true, diagnose surprises from naive misuses like "chmod -r file".
@@ -81,7 +78,7 @@ static bool diagnose_surprises;
/* Level of verbosity. */
static enum Verbosity verbosity = V_off;
-/* Pointer to the device and inode numbers of `/', when --recursive.
+/* Pointer to the device and inode numbers of '/', when --recursive.
Otherwise NULL. */
static struct dev_ino *root_dev_ino;
@@ -113,21 +110,23 @@ static struct option const long_options[] =
The old mode was OLD_MODE, but it was changed to NEW_MODE. */
static bool
-mode_changed (char const *file, mode_t old_mode, mode_t new_mode)
+mode_changed (int dir_fd, char const *file, char const *file_full_name,
+ mode_t old_mode, mode_t new_mode)
{
if (new_mode & (S_ISUID | S_ISGID | S_ISVTX))
{
/* The new mode contains unusual bits that the call to chmod may
- have silently cleared. Check whether they actually changed. */
+ have silently cleared. Check whether they actually changed. */
struct stat new_stats;
- if (stat (file, &new_stats) != 0)
- {
- if (!force_silent)
- error (0, errno, _("getting new attributes of %s"), quote (file));
- return false;
- }
+ if (fstatat (dir_fd, file, &new_stats, 0) != 0)
+ {
+ if (! force_silent)
+ error (0, errno, _("getting new attributes of %s"),
+ quoteaf (file_full_name));
+ return false;
+ }
new_mode = new_stats.st_mode;
}
@@ -139,37 +138,45 @@ mode_changed (char const *file, mode_t old_mode, mode_t new_mode)
CHANGED describes what (if anything) has happened. */
static void
-describe_change (const char *file, mode_t mode,
- enum Change_status changed)
+describe_change (const char *file, mode_t old_mode, mode_t mode,
+ enum Change_status changed)
{
char perms[12]; /* "-rwxrwxrwx" ls-style modes. */
+ char old_perms[12];
const char *fmt;
if (changed == CH_NOT_APPLIED)
{
printf (_("neither symbolic link %s nor referent has been changed\n"),
- quote (file));
+ quoteaf (file));
return;
}
strmode (mode, perms);
perms[10] = '\0'; /* Remove trailing space. */
+
+ strmode (old_mode, old_perms);
+ old_perms[10] = '\0'; /* Remove trailing space. */
+
switch (changed)
{
case CH_SUCCEEDED:
- fmt = _("mode of %s changed to %04lo (%s)\n");
+ fmt = _("mode of %s changed from %04lo (%s) to %04lo (%s)\n");
break;
case CH_FAILED:
- fmt = _("failed to change mode of %s to %04lo (%s)\n");
+ fmt = _("failed to change mode of %s from %04lo (%s) to %04lo (%s)\n");
break;
case CH_NO_CHANGE_REQUESTED:
fmt = _("mode of %s retained as %04lo (%s)\n");
- break;
+ printf (fmt, quoteaf (file),
+ (unsigned long int) (mode & CHMOD_MODE_BITS), &perms[1]);
+ return;
default:
abort ();
}
- printf (fmt, quote (file),
- (unsigned long int) (mode & CHMOD_MODE_BITS), &perms[1]);
+ printf (fmt, quoteaf (file),
+ (unsigned long int) (old_mode & CHMOD_MODE_BITS), &old_perms[1],
+ (unsigned long int) (mode & CHMOD_MODE_BITS), &perms[1]);
}
/* Change the mode of FILE.
@@ -182,8 +189,8 @@ process_file (FTS *fts, FTSENT *ent)
char const *file_full_name = ent->fts_path;
char const *file = ent->fts_accpath;
const struct stat *file_stats = ent->fts_statp;
- mode_t old_mode IF_LINT (= 0);
- mode_t new_mode IF_LINT (= 0);
+ mode_t old_mode IF_LINT ( = 0);
+ mode_t new_mode IF_LINT ( = 0);
bool ok = true;
bool chmod_succeeded = false;
@@ -194,33 +201,52 @@ process_file (FTS *fts, FTSENT *ent)
case FTS_NS:
/* For a top-level file or directory, this FTS_NS (stat failed)
- indicator is determined at the time of the initial fts_open call.
- With programs like chmod, chown, and chgrp, that modify
- permissions, it is possible that the file in question is
- accessible when control reaches this point. So, if this is
- the first time we've seen the FTS_NS for this file, tell
- fts_read to stat it "again". */
+ indicator is determined at the time of the initial fts_open call.
+ With programs like chmod, chown, and chgrp, that modify
+ permissions, it is possible that the file in question is
+ accessible when control reaches this point. So, if this is
+ the first time we've seen the FTS_NS for this file, tell
+ fts_read to stat it "again". */
if (ent->fts_level == 0 && ent->fts_number == 0)
- {
- ent->fts_number = 1;
- fts_set (fts, ent, FTS_AGAIN);
- return true;
- }
- error (0, ent->fts_errno, _("cannot access %s"), quote (file_full_name));
+ {
+ ent->fts_number = 1;
+ fts_set (fts, ent, FTS_AGAIN);
+ return true;
+ }
+ if (! force_silent)
+ error (0, ent->fts_errno, _("cannot access %s"),
+ quoteaf (file_full_name));
ok = false;
break;
case FTS_ERR:
- error (0, ent->fts_errno, _("%s"), quote (file_full_name));
+ if (! force_silent)
+ error (0, ent->fts_errno, "%s", quotef (file_full_name));
ok = false;
break;
case FTS_DNR:
- error (0, ent->fts_errno, _("cannot read directory %s"),
- quote (file_full_name));
+ if (! force_silent)
+ error (0, ent->fts_errno, _("cannot read directory %s"),
+ quoteaf (file_full_name));
ok = false;
break;
+ case FTS_SLNONE:
+ if (! force_silent)
+ error (0, 0, _("cannot operate on dangling symlink %s"),
+ quoteaf (file_full_name));
+ ok = false;
+ break;
+
+ case FTS_DC: /* directory that causes cycles */
+ if (cycle_warning_required (fts, ent))
+ {
+ emit_cycle_warning (file_full_name);
+ return false;
+ }
+ break;
+
default:
break;
}
@@ -231,63 +257,64 @@ process_file (FTS *fts, FTSENT *ent)
/* Tell fts not to traverse into this hierarchy. */
fts_set (fts, ent, FTS_SKIP);
/* Ensure that we do not process "/" on the second visit. */
- ent = fts_read (fts);
- ok = false;
+ ignore_value (fts_read (fts));
+ return false;
}
if (ok)
{
old_mode = file_stats->st_mode;
new_mode = mode_adjust (old_mode, S_ISDIR (old_mode) != 0, umask_value,
- change, NULL);
+ change, NULL);
if (! S_ISLNK (old_mode))
- {
- if (chmodat (fts->fts_cwd_fd, file, new_mode) == 0)
- chmod_succeeded = true;
- else
- {
- if (! force_silent)
- error (0, errno, _("changing permissions of %s"),
- quote (file_full_name));
- ok = false;
- }
- }
+ {
+ if (chmodat (fts->fts_cwd_fd, file, new_mode) == 0)
+ chmod_succeeded = true;
+ else
+ {
+ if (! force_silent)
+ error (0, errno, _("changing permissions of %s"),
+ quoteaf (file_full_name));
+ ok = false;
+ }
+ }
}
if (verbosity != V_off)
{
bool changed = (chmod_succeeded
- && mode_changed (file, old_mode, new_mode));
+ && mode_changed (fts->fts_cwd_fd, file, file_full_name,
+ old_mode, new_mode));
if (changed || verbosity == V_high)
- {
- enum Change_status ch_status =
- (!ok ? CH_FAILED
- : !chmod_succeeded ? CH_NOT_APPLIED
- : !changed ? CH_NO_CHANGE_REQUESTED
- : CH_SUCCEEDED);
- describe_change (file_full_name, new_mode, ch_status);
- }
+ {
+ enum Change_status ch_status =
+ (!ok ? CH_FAILED
+ : !chmod_succeeded ? CH_NOT_APPLIED
+ : !changed ? CH_NO_CHANGE_REQUESTED
+ : CH_SUCCEEDED);
+ describe_change (file_full_name, old_mode, new_mode, ch_status);
+ }
}
- if (chmod_succeeded & diagnose_surprises)
+ if (chmod_succeeded && diagnose_surprises)
{
mode_t naively_expected_mode =
- mode_adjust (old_mode, S_ISDIR (old_mode) != 0, 0, change, NULL);
+ mode_adjust (old_mode, S_ISDIR (old_mode) != 0, 0, change, NULL);
if (new_mode & ~naively_expected_mode)
- {
- char new_perms[12];
- char naively_expected_perms[12];
- strmode (new_mode, new_perms);
- strmode (naively_expected_mode, naively_expected_perms);
- new_perms[10] = naively_expected_perms[10] = '\0';
- error (0, 0,
- _("%s: new permissions are %s, not %s"),
- quotearg_colon (file_full_name),
- new_perms + 1, naively_expected_perms + 1);
- ok = false;
- }
+ {
+ char new_perms[12];
+ char naively_expected_perms[12];
+ strmode (new_mode, new_perms);
+ strmode (naively_expected_mode, naively_expected_perms);
+ new_perms[10] = naively_expected_perms[10] = '\0';
+ error (0, 0,
+ _("%s: new permissions are %s, not %s"),
+ quotef (file_full_name),
+ new_perms + 1, naively_expected_perms + 1);
+ ok = false;
+ }
}
if ( ! recurse)
@@ -313,23 +340,25 @@ process_files (char **files, int bit_flags)
ent = fts_read (fts);
if (ent == NULL)
- {
- if (errno != 0)
- {
- /* FIXME: try to give a better message */
- error (0, errno, _("fts_read failed"));
- ok = false;
- }
- break;
- }
+ {
+ if (errno != 0)
+ {
+ /* FIXME: try to give a better message */
+ if (! force_silent)
+ error (0, errno, _("fts_read failed"));
+ ok = false;
+ }
+ break;
+ }
ok &= process_file (fts, ent);
}
- /* Ignore failure, since the only way it can do so is in failing to
- return to the original directory, and since we're about to exit,
- that doesn't matter. */
- fts_close (fts);
+ if (fts_close (fts) != 0)
+ {
+ error (0, errno, _("fts_close failed"));
+ ok = false;
+ }
return ok;
}
@@ -338,8 +367,7 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
@@ -347,35 +375,40 @@ Usage: %s [OPTION]... MODE[,MODE]... FILE...\n\
or: %s [OPTION]... OCTAL-MODE FILE...\n\
or: %s [OPTION]... --reference=RFILE FILE...\n\
"),
- program_name, program_name, program_name);
+ program_name, program_name, program_name);
fputs (_("\
Change the mode of each FILE to MODE.\n\
+With --reference, change the mode of each FILE to that of RFILE.\n\
\n\
- -c, --changes like verbose but report only when a change is made\n\
"), stdout);
fputs (_("\
- --no-preserve-root do not treat `/' specially (the default)\n\
- --preserve-root fail to operate recursively on `/'\n\
+ -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 (_("\
+ --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 MODE values\n\
"), stdout);
fputs (_("\
- -f, --silent, --quiet suppress most error messages\n\
- -v, --verbose output a diagnostic for every file processed\n\
- --reference=RFILE use RFILE's mode instead of MODE values\n\
- -R, --recursive change files and directories recursively\n\
+ -R, --recursive change files and directories recursively\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
\n\
-Each MODE is of the form `[ugoa]*([-+=]([rwxXst]*|[ugo]))+'.\n\
+Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=][0-7]+'.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
/* Parse the ASCII mode given on the command line into a linked list
- of `struct mode_change' and apply that to each file argument. */
+ of 'struct mode_change' and apply that to each file argument. */
int
main (int argc, char **argv)
@@ -389,7 +422,7 @@ main (int argc, char **argv)
int c;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -399,99 +432,102 @@ main (int argc, char **argv)
recurse = force_silent = diagnose_surprises = false;
while ((c = getopt_long (argc, argv,
- "Rcfvr::w::x::X::s::t::u::g::o::a::,::+::=::",
- long_options, NULL))
- != -1)
+ ("Rcfvr::w::x::X::s::t::u::g::o::a::,::+::=::"
+ "0::1::2::3::4::5::6::7::"),
+ long_options, NULL))
+ != -1)
{
switch (c)
- {
- case 'r':
- case 'w':
- case 'x':
- case 'X':
- case 's':
- case 't':
- case 'u':
- case 'g':
- case 'o':
- case 'a':
- case ',':
- case '+':
- case '=':
- /* Support nonportable uses like "chmod -w", but diagnose
- surprises due to umask confusion. Even though "--", "--r",
- etc., are valid modes, there is no "case '-'" here since
- getopt_long reserves leading "--" for long options. */
- {
- /* Allocate a mode string (e.g., "-rwx") by concatenating
- the argument containing this option. If a previous mode
- string was given, concatenate the previous string, a
- comma, and the new string (e.g., "-s,-rwx"). */
-
- char const *arg = argv[optind - 1];
- size_t arg_len = strlen (arg);
- size_t mode_comma_len = mode_len + !!mode_len;
- size_t new_mode_len = mode_comma_len + arg_len;
- if (mode_alloc <= new_mode_len)
- {
- mode_alloc = new_mode_len + 1;
- mode = X2REALLOC (mode, &mode_alloc);
- }
- mode[mode_len] = ',';
- strcpy (mode + mode_comma_len, arg);
- mode_len = new_mode_len;
-
- diagnose_surprises = true;
- }
- break;
- case NO_PRESERVE_ROOT:
- preserve_root = false;
- break;
- case PRESERVE_ROOT:
- preserve_root = true;
- break;
- case REFERENCE_FILE_OPTION:
- reference_file = optarg;
- break;
- case 'R':
- recurse = true;
- break;
- case 'c':
- verbosity = V_changes_only;
- break;
- case 'f':
- force_silent = true;
- break;
- case 'v':
- verbosity = V_high;
- break;
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'r':
+ case 'w':
+ case 'x':
+ case 'X':
+ case 's':
+ case 't':
+ case 'u':
+ case 'g':
+ case 'o':
+ case 'a':
+ case ',':
+ case '+':
+ case '=':
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ /* Support nonportable uses like "chmod -w", but diagnose
+ surprises due to umask confusion. Even though "--", "--r",
+ etc., are valid modes, there is no "case '-'" here since
+ getopt_long reserves leading "--" for long options. */
+ {
+ /* Allocate a mode string (e.g., "-rwx") by concatenating
+ the argument containing this option. If a previous mode
+ string was given, concatenate the previous string, a
+ comma, and the new string (e.g., "-s,-rwx"). */
+
+ char const *arg = argv[optind - 1];
+ size_t arg_len = strlen (arg);
+ size_t mode_comma_len = mode_len + !!mode_len;
+ size_t new_mode_len = mode_comma_len + arg_len;
+ if (mode_alloc <= new_mode_len)
+ {
+ mode_alloc = new_mode_len + 1;
+ mode = X2REALLOC (mode, &mode_alloc);
+ }
+ mode[mode_len] = ',';
+ memcpy (mode + mode_comma_len, arg, arg_len + 1);
+ mode_len = new_mode_len;
+
+ diagnose_surprises = true;
+ }
+ break;
+ case NO_PRESERVE_ROOT:
+ preserve_root = false;
+ break;
+ case PRESERVE_ROOT:
+ preserve_root = true;
+ break;
+ case REFERENCE_FILE_OPTION:
+ reference_file = optarg;
+ break;
+ case 'R':
+ recurse = true;
+ break;
+ case 'c':
+ verbosity = V_changes_only;
+ break;
+ case 'f':
+ force_silent = true;
+ break;
+ case 'v':
+ verbosity = V_high;
+ break;
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
}
if (reference_file)
{
if (mode)
- {
- error (0, 0, _("cannot combine mode and --reference options"));
- usage (EXIT_FAILURE);
- }
+ {
+ error (0, 0, _("cannot combine mode and --reference options"));
+ usage (EXIT_FAILURE);
+ }
}
else
{
if (!mode)
- mode = argv[optind++];
+ mode = argv[optind++];
}
if (optind >= argc)
{
if (!mode || mode != argv[optind - 1])
- error (0, 0, _("missing operand"));
+ error (0, 0, _("missing operand"));
else
- error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
+ error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
usage (EXIT_FAILURE);
}
@@ -499,34 +535,35 @@ main (int argc, char **argv)
{
change = mode_create_from_ref (reference_file);
if (!change)
- error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
- quote (reference_file));
+ error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
+ quoteaf (reference_file));
}
else
{
change = mode_compile (mode);
if (!change)
- {
- error (0, 0, _("invalid mode: %s"), quote (mode));
- usage (EXIT_FAILURE);
- }
+ {
+ error (0, 0, _("invalid mode: %s"), quote (mode));
+ usage (EXIT_FAILURE);
+ }
umask_value = umask (0);
}
- if (recurse & preserve_root)
+ if (recurse && preserve_root)
{
static struct dev_ino dev_ino_buf;
root_dev_ino = get_root_dev_ino (&dev_ino_buf);
if (root_dev_ino == NULL)
- error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
- quote ("/"));
+ error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
+ quoteaf ("/"));
}
else
{
root_dev_ino = NULL;
}
- ok = process_files (argv + optind, FTS_COMFOLLOW | FTS_PHYSICAL);
+ ok = process_files (argv + optind,
+ FTS_COMFOLLOW | FTS_PHYSICAL | FTS_DEFER_STAT);
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/chown-core.c b/src/chown-core.c
index bd987a8..b699993 100644
--- a/src/chown-core.c
+++ b/src/chown-core.c
@@ -1,10 +1,10 @@
/* chown-core.c -- core functions for changing ownership.
- Copyright (C) 2000, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation.
+ Copyright (C) 2000-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Extracted from chown.c/chgrp.c and librarified by Jim Meyering. */
@@ -26,9 +25,7 @@
#include "system.h"
#include "chown-core.h"
#include "error.h"
-#include "inttostr.h"
-#include "openat.h"
-#include "quote.h"
+#include "ignore-value.h"
#include "root-dev-ino.h"
#include "xfts.h"
@@ -70,7 +67,7 @@ chopt_init (struct Chown_option *chopt)
}
extern void
-chopt_free (struct Chown_option *chopt ATTRIBUTE_UNUSED)
+chopt_free (struct Chown_option *chopt _GL_UNUSED)
{
/* Deliberately do not free chopt->user_name or ->group_name.
They're not always allocated. */
@@ -86,8 +83,8 @@ gid_to_name (gid_t gid)
char buf[INT_BUFSIZE_BOUND (intmax_t)];
struct group *grp = getgrgid (gid);
return xstrdup (grp ? grp->gr_name
- : TYPE_SIGNED (gid_t) ? imaxtostr (gid, buf)
- : umaxtostr (gid, buf));
+ : TYPE_SIGNED (gid_t) ? imaxtostr (gid, buf)
+ : umaxtostr (gid, buf));
}
/* Convert the numeric user-id, UID, to a string stored in xmalloc'd memory,
@@ -100,8 +97,35 @@ uid_to_name (uid_t uid)
char buf[INT_BUFSIZE_BOUND (intmax_t)];
struct passwd *pwd = getpwuid (uid);
return xstrdup (pwd ? pwd->pw_name
- : TYPE_SIGNED (uid_t) ? imaxtostr (uid, buf)
- : umaxtostr (uid, buf));
+ : TYPE_SIGNED (uid_t) ? imaxtostr (uid, buf)
+ : umaxtostr (uid, buf));
+}
+
+/* Allocate a string representing USER and GROUP. */
+
+static char *
+user_group_str (char const *user, char const *group)
+{
+ char *spec = NULL;
+
+ if (user)
+ {
+ if (group)
+ {
+ spec = xmalloc (strlen (user) + 1 + strlen (group) + 1);
+ stpcpy (stpcpy (stpcpy (spec, user), ":"), group);
+ }
+ else
+ {
+ spec = xstrdup (user);
+ }
+ }
+ else if (group)
+ {
+ spec = xstrdup (group);
+ }
+
+ return spec;
}
/* Tell the user how/if the user and group of FILE have been changed.
@@ -110,68 +134,67 @@ uid_to_name (uid_t uid)
static void
describe_change (const char *file, enum Change_status changed,
- char const *user, char const *group)
+ char const *old_user, char const *old_group,
+ char const *user, char const *group)
{
const char *fmt;
- char const *spec;
- char *spec_allocated = NULL;
+ char *old_spec;
+ char *spec;
if (changed == CH_NOT_APPLIED)
{
printf (_("neither symbolic link %s nor referent has been changed\n"),
- quote (file));
+ quoteaf (file));
return;
}
- if (user)
- {
- if (group)
- {
- spec_allocated = xmalloc (strlen (user) + 1 + strlen (group) + 1);
- stpcpy (stpcpy (stpcpy (spec_allocated, user), ":"), group);
- spec = spec_allocated;
- }
- else
- {
- spec = user;
- }
- }
- else
- {
- spec = group;
- }
+ spec = user_group_str (user, group);
+ old_spec = user_group_str (user ? old_user : NULL, group ? old_group : NULL);
switch (changed)
{
case CH_SUCCEEDED:
- fmt = (user ? _("changed ownership of %s to %s\n")
- : group ? _("changed group of %s to %s\n")
- : _("no change to ownership of %s\n"));
+ fmt = (user ? _("changed ownership of %s from %s to %s\n")
+ : group ? _("changed group of %s from %s to %s\n")
+ : _("no change to ownership of %s\n"));
break;
case CH_FAILED:
- fmt = (user ? _("failed to change ownership of %s to %s\n")
- : group ? _("failed to change group of %s to %s\n")
- : _("failed to change ownership of %s\n"));
+ if (old_spec)
+ {
+ fmt = (user ? _("failed to change ownership of %s from %s to %s\n")
+ : group ? _("failed to change group of %s from %s to %s\n")
+ : _("failed to change ownership of %s\n"));
+ }
+ else
+ {
+ fmt = (user ? _("failed to change ownership of %s to %s\n")
+ : group ? _("failed to change group of %s to %s\n")
+ : _("failed to change ownership of %s\n"));
+ free (old_spec);
+ old_spec = spec;
+ spec = NULL;
+ }
break;
case CH_NO_CHANGE_REQUESTED:
fmt = (user ? _("ownership of %s retained as %s\n")
- : group ? _("group of %s retained as %s\n")
- : _("ownership of %s retained\n"));
+ : group ? _("group of %s retained as %s\n")
+ : _("ownership of %s retained\n"));
break;
default:
abort ();
}
- printf (fmt, quote (file), spec);
+ printf (fmt, quoteaf (file), old_spec, spec);
- free (spec_allocated);
+ free (old_spec);
+ free (spec);
}
/* Change the owner and/or group of the FILE to UID and/or GID (safely)
only if REQUIRED_UID and REQUIRED_GID match the owner and group IDs
- of FILE. ORIG_ST must be the result of `stat'ing FILE.
+ of FILE. ORIG_ST must be the result of 'stat'ing FILE.
- The `safely' part above means that we can't simply use chown(2),
+ The 'safely' part above means that we can't simply use chown(2),
since FILE might be replaced with some other file between the time
of the preceding stat/lstat and this chown call. So here we open
FILE and do everything else via the resulting file descriptor.
@@ -187,9 +210,9 @@ describe_change (const char *file, enum Change_status changed,
static enum RCH_status
restricted_chown (int cwd_fd, char const *file,
- struct stat const *orig_st,
- uid_t uid, gid_t gid,
- uid_t required_uid, gid_t required_gid)
+ struct stat const *orig_st,
+ uid_t uid, gid_t gid,
+ uid_t required_uid, gid_t required_gid)
{
enum RCH_status status = RC_ok;
struct stat st;
@@ -202,15 +225,15 @@ restricted_chown (int cwd_fd, char const *file,
if (! S_ISREG (orig_st->st_mode))
{
if (S_ISDIR (orig_st->st_mode))
- open_flags |= O_DIRECTORY;
+ open_flags |= O_DIRECTORY;
else
- return RC_do_ordinary_chown;
+ return RC_do_ordinary_chown;
}
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)))))
+ || (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);
if (fstat (fd, &st) != 0)
@@ -218,26 +241,24 @@ restricted_chown (int cwd_fd, char const *file,
else if (! SAME_INODE (*orig_st, st))
status = RC_inode_changed;
else if ((required_uid == (uid_t) -1 || required_uid == st.st_uid)
- && (required_gid == (gid_t) -1 || required_gid == st.st_gid))
+ && (required_gid == (gid_t) -1 || required_gid == st.st_gid))
{
if (fchown (fd, uid, gid) == 0)
- {
- status = (close (fd) == 0
- ? RC_ok : RC_error);
- return status;
- }
+ {
+ status = (close (fd) == 0
+ ? RC_ok : RC_error);
+ return status;
+ }
else
- {
- status = RC_error;
- }
+ {
+ status = RC_error;
+ }
}
- { /* FIXME: remove these curly braces when we assume C99. */
- int saved_errno = errno;
- close (fd);
- errno = saved_errno;
- return status;
- }
+ int saved_errno = errno;
+ close (fd);
+ errno = saved_errno;
+ return status;
}
/* Change the owner and/or group of the file specified by FTS and ENT
@@ -248,9 +269,9 @@ restricted_chown (int cwd_fd, char const *file,
Return true if successful. */
static bool
change_file_owner (FTS *fts, FTSENT *ent,
- uid_t uid, gid_t gid,
- uid_t required_uid, gid_t required_gid,
- struct Chown_option const *chopt)
+ uid_t uid, gid_t gid,
+ uid_t required_uid, gid_t required_gid,
+ struct Chown_option const *chopt)
{
char const *file_full_name = ent->fts_path;
char const *file = ent->fts_accpath;
@@ -264,56 +285,68 @@ change_file_owner (FTS *fts, FTSENT *ent,
{
case FTS_D:
if (chopt->recurse)
- {
- if (ROOT_DEV_INO_CHECK (chopt->root_dev_ino, ent->fts_statp))
- {
- /* This happens e.g., with "chown -R --preserve-root 0 /"
- and with "chown -RH --preserve-root 0 symlink-to-root". */
- ROOT_DEV_INO_WARN (file_full_name);
- /* Tell fts not to traverse into this hierarchy. */
- fts_set (fts, ent, FTS_SKIP);
- /* Ensure that we do not process "/" on the second visit. */
- ent = fts_read (fts);
- return false;
- }
- return true;
- }
+ {
+ if (ROOT_DEV_INO_CHECK (chopt->root_dev_ino, ent->fts_statp))
+ {
+ /* This happens e.g., with "chown -R --preserve-root 0 /"
+ and with "chown -RH --preserve-root 0 symlink-to-root". */
+ ROOT_DEV_INO_WARN (file_full_name);
+ /* Tell fts not to traverse into this hierarchy. */
+ fts_set (fts, ent, FTS_SKIP);
+ /* Ensure that we do not process "/" on the second visit. */
+ ignore_value (fts_read (fts));
+ return false;
+ }
+ return true;
+ }
break;
case FTS_DP:
if (! chopt->recurse)
- return true;
+ return true;
break;
case FTS_NS:
/* For a top-level file or directory, this FTS_NS (stat failed)
- indicator is determined at the time of the initial fts_open call.
- With programs like chmod, chown, and chgrp, that modify
- permissions, it is possible that the file in question is
- accessible when control reaches this point. So, if this is
- the first time we've seen the FTS_NS for this file, tell
- fts_read to stat it "again". */
+ indicator is determined at the time of the initial fts_open call.
+ With programs like chmod, chown, and chgrp, that modify
+ permissions, it is possible that the file in question is
+ accessible when control reaches this point. So, if this is
+ the first time we've seen the FTS_NS for this file, tell
+ fts_read to stat it "again". */
if (ent->fts_level == 0 && ent->fts_number == 0)
- {
- ent->fts_number = 1;
- fts_set (fts, ent, FTS_AGAIN);
- return true;
- }
- error (0, ent->fts_errno, _("cannot access %s"), quote (file_full_name));
+ {
+ ent->fts_number = 1;
+ fts_set (fts, ent, FTS_AGAIN);
+ return true;
+ }
+ if (! chopt->force_silent)
+ error (0, ent->fts_errno, _("cannot access %s"),
+ quoteaf (file_full_name));
ok = false;
break;
case FTS_ERR:
- error (0, ent->fts_errno, _("%s"), quote (file_full_name));
+ if (! chopt->force_silent)
+ error (0, ent->fts_errno, "%s", quotef (file_full_name));
ok = false;
break;
case FTS_DNR:
- error (0, ent->fts_errno, _("cannot read directory %s"),
- quote (file_full_name));
+ if (! chopt->force_silent)
+ error (0, ent->fts_errno, _("cannot read directory %s"),
+ quoteaf (file_full_name));
ok = false;
break;
+ case FTS_DC: /* directory that causes cycles */
+ if (cycle_warning_required (fts, ent))
+ {
+ emit_cycle_warning (file_full_name);
+ return false;
+ }
+ break;
+
default:
break;
}
@@ -324,9 +357,9 @@ change_file_owner (FTS *fts, FTSENT *ent,
file_stats = NULL;
}
else if (required_uid == (uid_t) -1 && required_gid == (gid_t) -1
- && chopt->verbosity == V_off
- && ! chopt->root_dev_ino
- && ! chopt->affect_symlink_referent)
+ && chopt->verbosity == V_off
+ && ! chopt->root_dev_ino
+ && ! chopt->affect_symlink_referent)
{
do_chown = true;
file_stats = ent->fts_statp;
@@ -336,24 +369,25 @@ change_file_owner (FTS *fts, FTSENT *ent,
file_stats = ent->fts_statp;
/* If this is a symlink and we're dereferencing them,
- stat it to get info on the referent. */
+ stat it to get info on the referent. */
if (chopt->affect_symlink_referent && S_ISLNK (file_stats->st_mode))
- {
- if (fstatat (fts->fts_cwd_fd, file, &stat_buf, 0) != 0)
- {
- error (0, errno, _("cannot dereference %s"),
- quote (file_full_name));
- ok = false;
- }
-
- file_stats = &stat_buf;
- }
+ {
+ if (fstatat (fts->fts_cwd_fd, file, &stat_buf, 0) != 0)
+ {
+ if (! chopt->force_silent)
+ error (0, errno, _("cannot dereference %s"),
+ quoteaf (file_full_name));
+ ok = false;
+ }
+
+ file_stats = &stat_buf;
+ }
do_chown = (ok
- && (required_uid == (uid_t) -1
- || required_uid == file_stats->st_uid)
- && (required_gid == (gid_t) -1
- || required_gid == file_stats->st_gid));
+ && (required_uid == (uid_t) -1
+ || required_uid == file_stats->st_uid)
+ && (required_gid == (gid_t) -1
+ || required_gid == file_stats->st_gid));
}
/* This happens when chown -LR --preserve-root encounters a symlink-to-/. */
@@ -368,89 +402,94 @@ change_file_owner (FTS *fts, FTSENT *ent,
if (do_chown)
{
if ( ! chopt->affect_symlink_referent)
- {
- ok = (lchownat (fts->fts_cwd_fd, file, uid, gid) == 0);
-
- /* Ignore any error due to lack of support; POSIX requires
- this behavior for top-level symbolic links with -h, and
- implies that it's required for all symbolic links. */
- if (!ok && errno == EOPNOTSUPP)
- {
- ok = true;
- symlink_changed = false;
- }
- }
+ {
+ ok = (lchownat (fts->fts_cwd_fd, file, uid, gid) == 0);
+
+ /* Ignore any error due to lack of support; POSIX requires
+ this behavior for top-level symbolic links with -h, and
+ implies that it's required for all symbolic links. */
+ if (!ok && errno == EOPNOTSUPP)
+ {
+ ok = true;
+ symlink_changed = false;
+ }
+ }
else
- {
- /* If possible, avoid a race condition with --from=O:G and without the
- (-h) --no-dereference option. If fts's stat call determined
- that the uid/gid of FILE matched the --from=O:G-selected
- owner and group IDs, blindly using chown(2) here could lead
- chown(1) or chgrp(1) mistakenly to dereference a *symlink*
- to an arbitrary file that an attacker had moved into the
- place of FILE during the window between the stat and
- chown(2) calls. If FILE is a regular file or a directory
- that can be opened, this race condition can be avoided safely. */
-
- enum RCH_status err
- = restricted_chown (fts->fts_cwd_fd, file, file_stats, uid, gid,
- required_uid, required_gid);
- switch (err)
- {
- case RC_ok:
- break;
-
- case RC_do_ordinary_chown:
- ok = (chownat (fts->fts_cwd_fd, file, uid, gid) == 0);
- break;
-
- case RC_error:
- ok = false;
- break;
-
- case RC_inode_changed:
- /* FIXME: give a diagnostic in this case? */
- case RC_excluded:
- do_chown = false;
- ok = false;
- break;
-
- default:
- abort ();
- }
- }
-
- /* On some systems (e.g., Linux-2.4.x),
- the chown function resets the `special' permission bits.
- Do *not* restore those bits; doing so would open a window in
- which a malicious user, M, could subvert a chown command run
- by some other user and operating on files in a directory
- where M has write access. */
+ {
+ /* If possible, avoid a race condition with --from=O:G and without the
+ (-h) --no-dereference option. If fts's stat call determined
+ that the uid/gid of FILE matched the --from=O:G-selected
+ owner and group IDs, blindly using chown(2) here could lead
+ chown(1) or chgrp(1) mistakenly to dereference a *symlink*
+ to an arbitrary file that an attacker had moved into the
+ place of FILE during the window between the stat and
+ chown(2) calls. If FILE is a regular file or a directory
+ that can be opened, this race condition can be avoided safely. */
+
+ enum RCH_status err
+ = restricted_chown (fts->fts_cwd_fd, file, file_stats, uid, gid,
+ required_uid, required_gid);
+ switch (err)
+ {
+ case RC_ok:
+ break;
+
+ case RC_do_ordinary_chown:
+ ok = (chownat (fts->fts_cwd_fd, file, uid, gid) == 0);
+ break;
+
+ case RC_error:
+ ok = false;
+ break;
+
+ case RC_inode_changed:
+ /* FIXME: give a diagnostic in this case? */
+ case RC_excluded:
+ do_chown = false;
+ ok = false;
+ break;
+
+ default:
+ abort ();
+ }
+ }
+
+ /* On some systems (e.g., GNU/Linux 2.4.x),
+ the chown function resets the 'special' permission bits.
+ Do *not* restore those bits; doing so would open a window in
+ which a malicious user, M, could subvert a chown command run
+ by some other user and operating on files in a directory
+ where M has write access. */
if (do_chown && !ok && ! chopt->force_silent)
- error (0, errno, (uid != (uid_t) -1
- ? _("changing ownership of %s")
- : _("changing group of %s")),
- quote (file_full_name));
+ error (0, errno, (uid != (uid_t) -1
+ ? _("changing ownership of %s")
+ : _("changing group of %s")),
+ quoteaf (file_full_name));
}
if (chopt->verbosity != V_off)
{
bool changed =
- ((do_chown & ok & symlink_changed)
- && ! ((uid == (uid_t) -1 || uid == file_stats->st_uid)
- && (gid == (gid_t) -1 || gid == file_stats->st_gid)));
+ ((do_chown && ok && symlink_changed)
+ && ! ((uid == (uid_t) -1 || uid == file_stats->st_uid)
+ && (gid == (gid_t) -1 || gid == file_stats->st_gid)));
if (changed || chopt->verbosity == V_high)
- {
- enum Change_status ch_status =
- (!ok ? CH_FAILED
- : !symlink_changed ? CH_NOT_APPLIED
- : !changed ? CH_NO_CHANGE_REQUESTED
- : CH_SUCCEEDED);
- describe_change (file_full_name, ch_status,
- chopt->user_name, chopt->group_name);
- }
+ {
+ enum Change_status ch_status =
+ (!ok ? CH_FAILED
+ : !symlink_changed ? CH_NOT_APPLIED
+ : !changed ? CH_NO_CHANGE_REQUESTED
+ : CH_SUCCEEDED);
+ char *old_usr = file_stats ? uid_to_name (file_stats->st_uid) : NULL;
+ char *old_grp = file_stats ? gid_to_name (file_stats->st_gid) : NULL;
+ describe_change (file_full_name, ch_status,
+ old_usr, old_grp,
+ chopt->user_name, chopt->group_name);
+ free (old_usr);
+ free (old_grp);
+ }
}
if ( ! chopt->recurse)
@@ -470,18 +509,18 @@ change_file_owner (FTS *fts, FTSENT *ent,
Return true if successful. */
extern bool
chown_files (char **files, int bit_flags,
- uid_t uid, gid_t gid,
- uid_t required_uid, gid_t required_gid,
- struct Chown_option const *chopt)
+ uid_t uid, gid_t gid,
+ uid_t required_uid, gid_t required_gid,
+ struct Chown_option const *chopt)
{
bool ok = true;
/* Use lstat and stat only if they're needed. */
int stat_flags = ((required_uid != (uid_t) -1 || required_gid != (gid_t) -1
- || chopt->affect_symlink_referent
- || chopt->verbosity != V_off)
- ? 0
- : FTS_NOSTAT);
+ || chopt->affect_symlink_referent
+ || chopt->verbosity != V_off)
+ ? 0
+ : FTS_NOSTAT);
FTS *fts = xfts_open (files, bit_flags | stat_flags, NULL);
@@ -491,24 +530,26 @@ chown_files (char **files, int bit_flags,
ent = fts_read (fts);
if (ent == NULL)
- {
- if (errno != 0)
- {
- /* FIXME: try to give a better message */
- error (0, errno, _("fts_read failed"));
- ok = false;
- }
- break;
- }
+ {
+ if (errno != 0)
+ {
+ /* FIXME: try to give a better message */
+ if (! chopt->force_silent)
+ error (0, errno, _("fts_read failed"));
+ ok = false;
+ }
+ break;
+ }
ok &= change_file_owner (fts, ent, uid, gid,
- required_uid, required_gid, chopt);
+ required_uid, required_gid, chopt);
}
- /* Ignore failure, since the only way it can do so is in failing to
- return to the original directory, and since we're about to exit,
- that doesn't matter. */
- fts_close (fts);
+ if (fts_close (fts) != 0)
+ {
+ error (0, errno, _("fts_close failed"));
+ ok = false;
+ }
return ok;
}
diff --git a/src/chown-core.h b/src/chown-core.h
index 1c83cfc..c778aeb 100644
--- a/src/chown-core.h
+++ b/src/chown-core.h
@@ -1,11 +1,11 @@
/* chown-core.h -- types and prototypes shared by chown and chgrp.
- Copyright (C) 2000, 2003, 2004 Free Software Foundation.
+ Copyright (C) 2000-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -13,8 +13,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#ifndef CHOWN_CORE_H
# define CHOWN_CORE_H
@@ -49,7 +48,7 @@ struct Chown_option
/* If nonzero, change the ownership of directories recursively. */
bool recurse;
- /* Pointer to the device and inode numbers of `/', when --recursive.
+ /* Pointer to the device and inode numbers of '/', when --recursive.
Need not be freed. Otherwise NULL. */
struct dev_ino *root_dev_ino;
@@ -69,19 +68,19 @@ struct Chown_option
void
chopt_init (struct Chown_option *);
-void
+void _GL_ATTRIBUTE_PURE _GL_ATTRIBUTE_CONST
chopt_free (struct Chown_option *);
char *
gid_to_name (gid_t);
-char *
+char * _GL_ATTRIBUTE_PURE
uid_to_name (uid_t);
bool
chown_files (char **files, int bit_flags,
- uid_t uid, gid_t gid,
- uid_t required_uid, gid_t required_gid,
- struct Chown_option const *chopt);
+ uid_t uid, gid_t gid,
+ uid_t required_uid, gid_t required_gid,
+ struct Chown_option const *chopt);
#endif /* CHOWN_CORE_H */
diff --git a/src/chown.c b/src/chown.c
index 63a32f5..108e629 100644
--- a/src/chown.c
+++ b/src/chown.c
@@ -1,10 +1,10 @@
/* chown -- change user and group ownership of files
- Copyright (C) 89, 90, 91, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,21 +12,9 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
-
-/*
- | user
- | unchanged explicit
- -------------|-------------------------+-------------------------|
- g unchanged | --- | chown u |
- r |-------------------------+-------------------------|
- o explicit | chgrp g or chown .g | chown u.g |
- u |-------------------------+-------------------------|
- p from passwd| --- | chown u. |
- |-------------------------+-------------------------|
-
- Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
#include <config.h>
#include <stdio.h>
@@ -41,13 +29,12 @@
#include "root-dev-ino.h"
#include "userspec.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "chown"
-#define AUTHORS "David MacKenzie", "Jim Meyering"
-
-/* The name the program was run with. */
-char *program_name;
+#define AUTHORS \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Jim Meyering")
/* The argument to the --reference option. Use the owner and group IDs
of this file. This file must exist. */
@@ -86,26 +73,31 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... [OWNER][:[GROUP]] FILE...\n\
or: %s [OPTION]... --reference=RFILE FILE...\n\
"),
- program_name, program_name);
+ program_name, program_name);
fputs (_("\
Change the owner and/or group of each FILE to OWNER and/or GROUP.\n\
With --reference, change the owner and group of each FILE to those 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 (_("\
- -h, --no-dereference affect each symbolic link instead of any referenced\n\
- file (useful only on systems that can change the\n\
+ (useful only on systems that can change the\n\
ownership of a symlink)\n\
"), stdout);
fputs (_("\
@@ -113,21 +105,21 @@ With --reference, change the owner and group of each FILE to those of RFILE.\n\
change the owner and/or group 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\
+ 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\
+ --no-preserve-root do not treat '/' specially (the default)\n\
+ --preserve-root fail to operate recursively on '/'\n\
"), stdout);
fputs (_("\
- -f, --silent, --quiet suppress most error messages\n\
--reference=RFILE use RFILE's owner and group rather than\n\
specifying OWNER:GROUP values\n\
+"), stdout);
+ fputs (_("\
-R, --recursive operate on files and directories recursively\n\
- -v, --verbose output a diagnostic for every file processed\n\
-\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\
@@ -144,7 +136,7 @@ one takes effect.\n\
fputs (_("\
\n\
Owner is unchanged if missing. Group is unchanged if missing, but changed\n\
-to login group if implied by a `:' following a symbolic OWNER.\n\
+to login group if implied by a ':' following a symbolic OWNER.\n\
OWNER and GROUP may be numeric as well as symbolic.\n\
"), stdout);
printf (_("\
@@ -154,8 +146,8 @@ Examples:\n\
%s root:staff /u Likewise, but also change its group to \"staff\".\n\
%s -hR root /u Change the owner of /u and subfiles to \"root\".\n\
"),
- program_name, program_name, program_name);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ program_name, program_name, program_name);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -185,7 +177,7 @@ main (int argc, char **argv)
int optc;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -195,86 +187,85 @@ main (int argc, char **argv)
chopt_init (&chopt);
while ((optc = getopt_long (argc, argv, "HLPRcfhv", long_options, NULL))
- != -1)
+ != -1)
{
switch (optc)
- {
- case 'H': /* Traverse command-line symlinks-to-directories. */
- bit_flags = FTS_COMFOLLOW | FTS_PHYSICAL;
- break;
-
- case 'L': /* Traverse all symlinks-to-directories. */
- bit_flags = FTS_LOGICAL;
- break;
-
- case 'P': /* Traverse no symlinks-to-directories. */
- bit_flags = FTS_PHYSICAL;
- break;
-
- case 'h': /* --no-dereference: affect symlinks */
- dereference = 0;
- break;
-
- case DEREFERENCE_OPTION: /* --dereference: affect the referent
- of each symlink */
- dereference = 1;
- break;
-
- case NO_PRESERVE_ROOT:
- preserve_root = false;
- break;
-
- case PRESERVE_ROOT:
- preserve_root = true;
- break;
-
- case REFERENCE_FILE_OPTION:
- reference_file = optarg;
- break;
-
- case FROM_OPTION:
- {
- char *u_dummy, *g_dummy;
- const char *e = parse_user_spec (optarg,
- &required_uid, &required_gid,
- &u_dummy, &g_dummy);
- if (e)
- error (EXIT_FAILURE, 0, "%s: %s", quote (optarg), e);
- break;
- }
-
- case 'R':
- chopt.recurse = true;
- break;
-
- case 'c':
- chopt.verbosity = V_changes_only;
- break;
-
- case 'f':
- chopt.force_silent = true;
- break;
-
- case 'v':
- chopt.verbosity = V_high;
- break;
-
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'H': /* Traverse command-line symlinks-to-directories. */
+ bit_flags = FTS_COMFOLLOW | FTS_PHYSICAL;
+ break;
+
+ case 'L': /* Traverse all symlinks-to-directories. */
+ bit_flags = FTS_LOGICAL;
+ break;
+
+ case 'P': /* Traverse no symlinks-to-directories. */
+ bit_flags = FTS_PHYSICAL;
+ break;
+
+ case 'h': /* --no-dereference: affect symlinks */
+ dereference = 0;
+ break;
+
+ case DEREFERENCE_OPTION: /* --dereference: affect the referent
+ of each symlink */
+ dereference = 1;
+ break;
+
+ case NO_PRESERVE_ROOT:
+ preserve_root = false;
+ break;
+
+ case PRESERVE_ROOT:
+ preserve_root = true;
+ break;
+
+ case REFERENCE_FILE_OPTION:
+ reference_file = optarg;
+ break;
+
+ case FROM_OPTION:
+ {
+ const char *e = parse_user_spec (optarg,
+ &required_uid, &required_gid,
+ NULL, NULL);
+ if (e)
+ error (EXIT_FAILURE, 0, "%s: %s", e, quote (optarg));
+ break;
+ }
+
+ case 'R':
+ chopt.recurse = true;
+ break;
+
+ case 'c':
+ chopt.verbosity = V_changes_only;
+ break;
+
+ case 'f':
+ chopt.force_silent = true;
+ break;
+
+ case 'v':
+ chopt.verbosity = V_high;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
}
if (chopt.recurse)
{
if (bit_flags == FTS_PHYSICAL)
- {
- if (dereference == 1)
- error (EXIT_FAILURE, 0,
- _("-R --dereference requires either -H or -L"));
- dereference = 0;
- }
+ {
+ if (dereference == 1)
+ error (EXIT_FAILURE, 0,
+ _("-R --dereference requires either -H or -L"));
+ dereference = 0;
+ }
}
else
{
@@ -285,9 +276,9 @@ main (int argc, char **argv)
if (argc - optind < (reference_file ? 1 : 2))
{
if (argc <= optind)
- error (0, 0, _("missing operand"));
+ error (0, 0, _("missing operand"));
else
- error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
+ error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
usage (EXIT_FAILURE);
}
@@ -295,8 +286,8 @@ main (int argc, char **argv)
{
struct stat ref_stats;
if (stat (reference_file, &ref_stats))
- error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
- quote (reference_file));
+ error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
+ quoteaf (reference_file));
uid = ref_stats.st_uid;
gid = ref_stats.st_gid;
@@ -306,33 +297,34 @@ main (int argc, char **argv)
else
{
const char *e = parse_user_spec (argv[optind], &uid, &gid,
- &chopt.user_name, &chopt.group_name);
+ &chopt.user_name, &chopt.group_name);
if (e)
- error (EXIT_FAILURE, 0, "%s: %s", quote (argv[optind]), e);
+ error (EXIT_FAILURE, 0, "%s: %s", e, quote (argv[optind]));
/* If a group is specified but no user, set the user name to the
- empty string so that diagnostics say "ownership :GROUP"
- rather than "group GROUP". */
+ empty string so that diagnostics say "ownership :GROUP"
+ rather than "group GROUP". */
if (!chopt.user_name && chopt.group_name)
- chopt.user_name = "";
+ chopt.user_name = bad_cast ("");
optind++;
}
- if (chopt.recurse & preserve_root)
+ if (chopt.recurse && preserve_root)
{
static struct dev_ino dev_ino_buf;
chopt.root_dev_ino = get_root_dev_ino (&dev_ino_buf);
if (chopt.root_dev_ino == NULL)
- error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
- quote ("/"));
+ 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);
+ uid, gid,
+ required_uid, required_gid, &chopt);
chopt_free (&chopt);
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/chroot.c b/src/chroot.c
index d2ae0e5..d804cc6 100644
--- a/src/chroot.c
+++ b/src/chroot.c
@@ -1,10 +1,10 @@
/* chroot -- run command or shell with special root directory
- Copyright (C) 95, 96, 1997, 1999-2004 Free Software Foundation, Inc.
+ Copyright (C) 1995-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Roland McGrath. */
@@ -21,43 +20,195 @@
#include <getopt.h>
#include <stdio.h>
#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
#include "system.h"
#include "error.h"
-#include "long-options.h"
+#include "ignore-value.h"
+#include "mgetgroups.h"
#include "quote.h"
+#include "root-dev-ino.h"
+#include "userspec.h"
+#include "xstrtol.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "chroot"
-#define AUTHORS "Roland McGrath"
+#define AUTHORS proper_name ("Roland McGrath")
-/* The name this program was run with, for error messages. */
-char *program_name;
+#ifndef MAXGID
+# define MAXGID GID_T_MAX
+#endif
+
+static inline bool uid_unset (uid_t uid) { return uid == (uid_t) -1; }
+static inline bool gid_unset (gid_t gid) { return gid == (gid_t) -1; }
+#define uid_set(x) (!uid_unset (x))
+#define gid_set(x) (!gid_unset (x))
+
+enum
+{
+ GROUPS = UCHAR_MAX + 1,
+ USERSPEC,
+ SKIP_CHDIR
+};
+
+static struct option const long_opts[] =
+{
+ {"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},
+ {NULL, 0, NULL, 0}
+};
+
+#if ! HAVE_SETGROUPS
+/* At least Interix lacks supplemental group support. */
+static int
+setgroups (size_t size, gid_t const *list _GL_UNUSED)
+{
+ if (size == 0)
+ {
+ /* Return success when clearing supplemental groups
+ as ! HAVE_SETGROUPS should only be the case on
+ platforms that don't support supplemental groups. */
+ return 0;
+ }
+ else
+ {
+ errno = ENOTSUP;
+ return -1;
+ }
+}
+#endif
+
+/* Determine the group IDs for the specified supplementary GROUPS,
+ which is a comma separated list of supplementary groups (names or numbers).
+ Allocate an array for the parsed IDs and store it in PGIDS,
+ which may be allocated even on parse failure.
+ Update the number of parsed groups in PN_GIDS on success.
+ Upon any failure return nonzero, and issue diagnostic if SHOW_ERRORS is true.
+ Otherwise return zero. */
+
+static int
+parse_additional_groups (char const *groups, GETGROUPS_T **pgids,
+ size_t *pn_gids, bool show_errors)
+{
+ GETGROUPS_T *gids = NULL;
+ size_t n_gids_allocated = 0;
+ size_t n_gids = 0;
+ char *buffer = xstrdup (groups);
+ char const *tmp;
+ int ret = 0;
+
+ for (tmp = strtok (buffer, ","); tmp; tmp = strtok (NULL, ","))
+ {
+ struct group *g;
+ unsigned long int value;
+
+ if (xstrtoul (tmp, NULL, 10, &value, "") == LONGINT_OK && value <= MAXGID)
+ {
+ while (isspace (to_uchar (*tmp)))
+ tmp++;
+ if (*tmp != '+')
+ {
+ /* Handle the case where the name is numeric. */
+ g = getgrnam (tmp);
+ if (g != NULL)
+ value = g->gr_gid;
+ }
+ /* Flag that we've got a group from the number. */
+ g = (struct group *) (intptr_t) ! NULL;
+ }
+ else
+ {
+ g = getgrnam (tmp);
+ if (g != NULL)
+ value = g->gr_gid;
+ }
+
+ if (g == NULL)
+ {
+ ret = -1;
+
+ if (show_errors)
+ {
+ error (0, errno, _("invalid group %s"), quote (tmp));
+ continue;
+ }
+
+ break;
+ }
+
+ if (n_gids == n_gids_allocated)
+ gids = X2NREALLOC (gids, &n_gids_allocated);
+ gids[n_gids++] = value;
+ }
+
+ if (ret == 0 && n_gids == 0)
+ {
+ if (show_errors)
+ error (0, 0, _("invalid group list %s"), quote (groups));
+ ret = -1;
+ }
+
+ *pgids = gids;
+
+ if (ret == 0)
+ *pn_gids = n_gids;
+
+ free (buffer);
+ return ret;
+}
+
+/* Return whether the passed path is equivalent to "/".
+ Note we don't compare against get_root_dev_ino() as "/"
+ could be bind mounted to a separate location. */
+
+static bool
+is_root (const char* dir)
+{
+ char *resolved = canonicalize_file_name (dir);
+ bool is_res_root = resolved && STREQ ("/", resolved);
+ free (resolved);
+ return is_res_root;
+}
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
-Usage: %s NEWROOT [COMMAND...]\n\
+Usage: %s [OPTION] NEWROOT [COMMAND [ARG]...]\n\
or: %s OPTION\n\
"), program_name, program_name);
+
fputs (_("\
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\
+"), quoteaf ("/"));
+
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
\n\
-If no command is given, run ``${SHELL} -i'' (default: /bin/sh).\n\
+If no command is given, run '${SHELL} -i' (default: '/bin/sh -i').\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -65,40 +216,130 @@ If no command is given, run ``${SHELL} -i'' (default: /bin/sh).\n\
int
main (int argc, char **argv)
{
+ int c;
+
+ /* Input user and groups spec. */
+ 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 = NULL;
+ size_t n_gids = 0;
+
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
- initialize_exit_failure (EXIT_FAIL);
+ initialize_exit_failure (EXIT_CANCELED);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
- if (getopt_long (argc, argv, "+", NULL, NULL) != -1)
- usage (EXIT_FAIL);
+ while ((c = getopt_long (argc, argv, "+", long_opts, NULL)) != -1)
+ {
+ switch (c)
+ {
+ case USERSPEC:
+ {
+ userspec = optarg;
+ /* Treat 'user:' just like 'user'
+ as we lookup the primary group by default
+ (and support doing so for UIDs as well as names. */
+ size_t userlen = strlen (userspec);
+ if (userlen && userspec[userlen - 1] == ':')
+ userspec[userlen - 1] = '\0';
+ break;
+ }
+
+ case GROUPS:
+ groups = optarg;
+ break;
+
+ case SKIP_CHDIR:
+ skip_chdir = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_CANCELED);
+ }
+ }
if (argc <= optind)
{
error (0, 0, _("missing operand"));
- usage (EXIT_FAIL);
+ usage (EXIT_CANCELED);
+ }
+
+ char const *newroot = argv[optind];
+ bool is_oldroot = is_root (newroot);
+
+ if (! is_oldroot && skip_chdir)
+ {
+ error (0, 0, _("option --skip-chdir only permitted if NEWROOT is old %s"),
+ quoteaf ("/"));
+ usage (EXIT_CANCELED);
}
- if (chroot (argv[optind]) != 0)
- error (EXIT_FAIL, errno, _("cannot change root directory to %s"), argv[1]);
+ if (! is_oldroot)
+ {
+ /* We have to look up users and groups twice.
+ - First, outside the chroot to load potentially necessary passwd/group
+ parsing plugins (e.g. NSS);
+ - Second, inside chroot to redo parsing in case IDs are different.
+ 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, NULL, NULL));
- if (chdir ("/"))
- error (EXIT_FAIL, errno, _("cannot chdir to root directory"));
+ /* If no gid is supplied or looked up, do so now.
+ Also lookup the username for use with getgroups. */
+ if (uid_set (uid) && (! groups || gid_unset (gid)))
+ {
+ const struct passwd *pwd;
+ if ((pwd = getpwuid (uid)))
+ {
+ if (gid_unset (gid))
+ gid = pwd->pw_gid;
+ username = pwd->pw_name;
+ }
+ }
+
+ if (groups && *groups)
+ ignore_value (parse_additional_groups (groups, &out_gids, &n_gids,
+ false));
+#if HAVE_SETGROUPS
+ else if (! groups && gid_set (gid) && username)
+ {
+ int ngroups = xgetgroups (username, gid, &out_gids);
+ if (0 < ngroups)
+ n_gids = ngroups;
+ }
+#endif
+ }
+
+ if (chroot (newroot) != 0)
+ error (EXIT_CANCELED, errno, _("cannot change root directory to %s"),
+ quoteaf (newroot));
+
+ if (! skip_chdir && chdir ("/"))
+ error (EXIT_CANCELED, errno, _("cannot chdir to root directory"));
if (argc == optind + 1)
{
/* No command. Run an interactive shell. */
char *shell = getenv ("SHELL");
if (shell == NULL)
- shell = "/bin/sh";
+ shell = bad_cast ("/bin/sh");
argv[0] = shell;
- argv[1] = "-i";
+ argv[1] = bad_cast ("-i");
argv[2] = NULL;
}
else
@@ -107,12 +348,82 @@ main (int argc, char **argv)
argv += optind + 1;
}
+ /* Attempt to set all three: supplementary groups, group ID, user ID.
+ Diagnose any failures. If any have failed, exit before execvp. */
+ if (userspec)
+ {
+ char const *err = parse_user_spec (userspec, &uid, &gid, NULL, NULL);
+
+ if (err && uid_unset (uid) && gid_unset (gid))
+ error (EXIT_CANCELED, errno, "%s", (err));
+ }
+
+ /* If no gid is supplied or looked up, do so now.
+ Also lookup the username for use with getgroups. */
+ if (uid_set (uid) && (! groups || gid_unset (gid)))
+ {
+ const struct passwd *pwd;
+ if ((pwd = getpwuid (uid)))
+ {
+ if (gid_unset (gid))
+ gid = pwd->pw_gid;
+ username = pwd->pw_name;
+ }
+ else if (gid_unset (gid))
+ {
+ error (EXIT_CANCELED, errno,
+ _("no group specified for unknown uid: %d"), (int) uid);
+ }
+ }
+
+ GETGROUPS_T *gids = out_gids;
+ GETGROUPS_T *in_gids = NULL;
+ if (groups && *groups)
+ {
+ if (parse_additional_groups (groups, &in_gids, &n_gids, !n_gids) != 0)
+ {
+ if (! n_gids)
+ return EXIT_CANCELED;
+ /* else look-up outside the chroot worked, then go with those. */
+ }
+ else
+ gids = in_gids;
+ }
+#if HAVE_SETGROUPS
+ else if (! groups && gid_set (gid) && username)
+ {
+ int ngroups = xgetgroups (username, gid, &in_gids);
+ if (ngroups <= 0)
+ {
+ if (! n_gids)
+ error (EXIT_CANCELED, errno,
+ _("failed to get supplemental groups"));
+ /* else look-up outside the chroot worked, then go with those. */
+ }
+ else
+ {
+ n_gids = ngroups;
+ gids = in_gids;
+ }
+ }
+#endif
+
+ if ((uid_set (uid) || groups) && setgroups (n_gids, gids) != 0)
+ error (EXIT_CANCELED, errno, _("failed to set supplemental groups"));
+
+ free (in_gids);
+ free (out_gids);
+
+ if (gid_set (gid) && setgid (gid))
+ error (EXIT_CANCELED, errno, _("failed to set group-ID"));
+
+ if (uid_set (uid) && setuid (uid))
+ error (EXIT_CANCELED, errno, _("failed to set user-ID"));
+
/* Execute the given command. */
execvp (argv[0], argv);
- {
- int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
- error (0, errno, _("cannot run command %s"), quote (argv[0]));
- exit (exit_status);
- }
+ int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
+ error (0, errno, _("failed to run command %s"), quote (argv[0]));
+ return exit_status;
}
diff --git a/src/cksum.c b/src/cksum.c
index d93877f..0c47243 100644
--- a/src/cksum.c
+++ b/src/cksum.c
@@ -1,10 +1,10 @@
/* cksum -- calculate and print POSIX checksums and sizes of files
- Copyright (C) 92, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1992-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,9 +12,8 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
-
+ along with this program. If not, see <http://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.
@@ -29,21 +28,23 @@
crctab > crctab.h
This software is compatible with neither the System V nor the BSD
- `sum' program. It is supposed to conform to POSIX, except perhaps
+ '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 <config.h>
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "cksum"
-#define AUTHORS "Q. Frank Xia"
+#define AUTHORS proper_name ("Q. Frank Xia")
#include <stdio.h>
#include <sys/types.h>
#include <stdint.h>
#include "system.h"
+#include "fadvise.h"
+#include "xfreopen.h"
#ifdef CRCTAB
@@ -58,8 +59,8 @@
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))
+ | BIT (11) | BIT (10) | BIT (8) | BIT (7) | BIT (5) \
+ | BIT (4) | BIT (2) | BIT (1) | BIT (0))
static uint_fast32_t r[8];
@@ -96,12 +97,12 @@ main (void)
for (i = 0; i < 51; i++)
{
printf (",\n 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x",
- crc_remainder (i * 5 + 1), crc_remainder (i * 5 + 2),
- crc_remainder (i * 5 + 3), crc_remainder (i * 5 + 4),
- crc_remainder (i * 5 + 5));
+ crc_remainder (i * 5 + 1), crc_remainder (i * 5 + 2),
+ crc_remainder (i * 5 + 3), crc_remainder (i * 5 + 4),
+ crc_remainder (i * 5 + 5));
}
printf ("\n};\n");
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
#else /* !CRCTAB */
@@ -109,14 +110,10 @@ main (void)
# include <getopt.h>
# include "long-options.h"
# include "error.h"
-# include "inttostr.h"
/* Number of bytes to read at once. */
# define BUFLEN (1 << 16)
-/* The name this program was run with. */
-char *program_name;
-
static uint_fast32_t const crctab[256] =
{
0x00000000,
@@ -197,42 +194,44 @@ cksum (const char *file, bool print_name)
fp = stdin;
have_read_stdin = true;
if (O_BINARY && ! isatty (STDIN_FILENO))
- freopen (NULL, "rb", stdin);
+ xfreopen (NULL, "rb", stdin);
}
else
{
fp = fopen (file, (O_BINARY ? "rb" : "r"));
if (fp == NULL)
- {
- error (0, errno, "%s", file);
- return false;
- }
+ {
+ error (0, errno, "%s", quotef (file));
+ return false;
+ }
}
+ fadvise (fp, FADVISE_SEQUENTIAL);
+
while ((bytes_read = fread (buf, 1, BUFLEN, fp)) > 0)
{
unsigned char *cp = buf;
if (length + bytes_read < length)
- error (EXIT_FAILURE, 0, _("%s: file too long"), file);
+ error (EXIT_FAILURE, 0, _("%s: file too long"), quotef (file));
length += bytes_read;
while (bytes_read--)
- crc = (crc << 8) ^ crctab[((crc >> 24) ^ *cp++) & 0xFF];
+ crc = (crc << 8) ^ crctab[((crc >> 24) ^ *cp++) & 0xFF];
if (feof (fp))
- break;
+ break;
}
if (ferror (fp))
{
- error (0, errno, "%s", file);
+ error (0, errno, "%s", quotef (file));
if (!STREQ (file, "-"))
- fclose (fp);
+ fclose (fp);
return false;
}
if (!STREQ (file, "-") && fclose (fp) == EOF)
{
- error (0, errno, "%s", file);
+ error (0, errno, "%s", quotef (file));
return false;
}
@@ -258,22 +257,21 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [FILE]...\n\
or: %s [OPTION]\n\
"),
- program_name, program_name);
+ program_name, program_name);
fputs (_("\
Print CRC checksum and byte counts of each FILE.\n\
\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -285,15 +283,19 @@ main (int argc, char **argv)
bool ok;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
+ /* 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);
+
+ parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE, Version,
+ usage, AUTHORS, (char const *) NULL);
if (getopt_long (argc, argv, "", NULL, NULL) != -1)
usage (EXIT_FAILURE);
@@ -305,12 +307,12 @@ main (int argc, char **argv)
{
ok = true;
for (i = optind; i < argc; i++)
- ok &= cksum (argv[i], true);
+ ok &= cksum (argv[i], true);
}
if (have_read_stdin && fclose (stdin) == EOF)
error (EXIT_FAILURE, errno, "-");
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
#endif /* !CRCTAB */
diff --git a/src/comm.c b/src/comm.c
index 9b7e03f..802bf90 100644
--- a/src/comm.c
+++ b/src/comm.c
@@ -1,10 +1,10 @@
/* comm -- compare two sorted files line by line.
- Copyright (C) 86, 90, 91, 1995-2005 Free Software Foundation, Inc.
+ Copyright (C) 1986-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,11 +12,10 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Richard Stallman and David MacKenzie. */
-
+
#include <config.h>
#include <getopt.h>
@@ -24,23 +23,24 @@
#include "system.h"
#include "linebuffer.h"
#include "error.h"
+#include "fadvise.h"
#include "hard-locale.h"
#include "quote.h"
#include "stdio--.h"
+#include "memcmp2.h"
#include "xmemcoll.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "comm"
-#define AUTHORS "Richard Stallman", "David MacKenzie"
+#define AUTHORS \
+ proper_name ("Richard M. Stallman"), \
+ proper_name ("David MacKenzie")
/* Undefine, to avoid warning about redefinition on some systems. */
#undef min
#define min(x, y) ((x) < (y) ? (x) : (y))
-/* The name this program was run with. */
-char *program_name;
-
/* True if the LC_COLLATE locale is hard. */
static bool hard_LC_COLLATE;
@@ -53,45 +53,105 @@ static bool only_file_2;
/* If true, print lines that are found in both files. */
static bool both;
+/* If nonzero, we have seen at least one unpairable line. */
+static bool seen_unpairable;
+
+/* If nonzero, we have warned about disorder in that file. */
+static bool issued_disorder_warning[2];
+
+/* line delimiter. */
+static unsigned char delim = '\n';
+
+/* If nonzero, check that the input is correctly ordered. */
+static enum
+ {
+ CHECK_ORDER_DEFAULT,
+ CHECK_ORDER_ENABLED,
+ CHECK_ORDER_DISABLED
+ } check_input_order;
+
+/* Output columns will be delimited with this string, which may be set
+ on the command-line with --output-delimiter=STR. */
+static char const *col_sep = "\t";
+static size_t col_sep_len = 0;
+
+/* For long options that have no equivalent short option, use a
+ non-character as a pseudo short option, starting with CHAR_MAX + 1. */
+enum
+{
+ CHECK_ORDER_OPTION = CHAR_MAX + 1,
+ NOCHECK_ORDER_OPTION,
+ OUTPUT_DELIMITER_OPTION
+};
+
static struct option const long_options[] =
{
+ {"check-order", no_argument, NULL, CHECK_ORDER_OPTION},
+ {"nocheck-order", no_argument, NULL, NOCHECK_ORDER_OPTION},
+ {"output-delimiter", required_argument, NULL, OUTPUT_DELIMITER_OPTION},
+ {"zero-terminated", no_argument, NULL, 'z'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
};
-
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... FILE1 FILE2\n\
"),
- program_name);
+ program_name);
fputs (_("\
Compare sorted files FILE1 and FILE2 line by line.\n\
"), stdout);
fputs (_("\
\n\
+When FILE1 or FILE2 (not both) is -, read standard input.\n\
+"), stdout);
+ fputs (_("\
+\n\
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 lines unique to FILE1\n\
- -2 suppress lines unique to FILE2\n\
- -3 suppress lines that appear in both files\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 (_("\
+ -z, --zero-terminated line delimiter is NUL, not newline\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ fputs (_("\
+\n\
+Note, comparisons honor the rules specified by 'LC_COLLATE'.\n\
+"), stdout);
+ printf (_("\
+\n\
+Examples:\n\
+ %s -12 file1 file2 Print only lines present in both file1 and file2.\n\
+ %s -3 file1 file2 Print lines in file1 not in file2, and vice versa.\n\
+"),
+ program_name, program_name);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -102,38 +162,80 @@ and column three contains lines common to both files.\n\
2 for a line only in file 2, 3 for a line in both. */
static void
-writeline (const struct linebuffer *line, FILE *stream, int class)
+writeline (struct linebuffer const *line, FILE *stream, int class)
{
switch (class)
{
case 1:
if (!only_file_1)
- return;
+ return;
break;
case 2:
if (!only_file_2)
- return;
- /* Print a TAB if we are printing lines from file 1. */
+ return;
if (only_file_1)
- putc ('\t', stream);
+ fwrite (col_sep, 1, col_sep_len, stream);
break;
case 3:
if (!both)
- return;
- /* Print a TAB if we are printing lines from file 1. */
+ return;
if (only_file_1)
- putc ('\t', stream);
- /* Print a TAB if we are printing lines from file 2. */
+ fwrite (col_sep, 1, col_sep_len, stream);
if (only_file_2)
- putc ('\t', stream);
+ fwrite (col_sep, 1, col_sep_len, stream);
break;
}
fwrite (line->buffer, sizeof (char), line->length, stream);
}
+/* Check that successive input lines PREV and CURRENT from input file
+ WHATFILE are presented in order.
+
+ If the user specified --nocheck-order, the check is not made.
+ If the user specified --check-order, the problem is fatal.
+ Otherwise (the default), the message is simply a warning.
+
+ A message is printed at most once per input file.
+
+ This function was copied (nearly) verbatim from 'src/join.c'. */
+
+static void
+check_order (struct linebuffer const *prev,
+ struct linebuffer const *current,
+ int whatfile)
+{
+
+ if (check_input_order != CHECK_ORDER_DISABLED
+ && ((check_input_order == CHECK_ORDER_ENABLED) || seen_unpairable))
+ {
+ if (!issued_disorder_warning[whatfile - 1])
+ {
+ int order;
+
+ if (hard_LC_COLLATE)
+ order = xmemcoll (prev->buffer, prev->length - 1,
+ current->buffer, current->length - 1);
+ else
+ order = memcmp2 (prev->buffer, prev->length - 1,
+ current->buffer, current->length - 1);
+
+ if (0 < order)
+ {
+ error ((check_input_order == CHECK_ORDER_ENABLED
+ ? EXIT_FAILURE : 0),
+ 0, _("file %d is not in sorted order"), whatfile);
+
+ /* If we get to here, the message was just a warning, but we
+ want only to issue it once. */
+ issued_disorder_warning[whatfile - 1] = true;
+ }
+ }
+ }
+}
+
/* Compare INFILES[0] and INFILES[1].
If either is "-", use the standard input for that file.
Assume that each input file is sorted;
@@ -142,85 +244,126 @@ writeline (const struct linebuffer *line, FILE *stream, int class)
static void
compare_files (char **infiles)
{
- /* For each file, we have one linebuffer in lb1. */
- struct linebuffer lb1[2];
+ /* For each file, we have four linebuffers in lba. */
+ struct linebuffer lba[2][4];
/* thisline[i] points to the linebuffer holding the next available line
in file i, or is NULL if there are no lines left in that file. */
struct linebuffer *thisline[2];
+ /* all_line[i][alt[i][0]] also points to the linebuffer holding the
+ current line in file i. We keep two buffers of history around so we
+ can look two lines back when we get to the end of a file. */
+ struct linebuffer *all_line[2][4];
+
+ /* This is used to rotate through the buffers for each input file. */
+ int alt[2][3];
+
/* streams[i] holds the input stream for file i. */
FILE *streams[2];
- int i;
+ int i, j;
/* Initialize the storage. */
for (i = 0; i < 2; i++)
{
- initbuffer (&lb1[i]);
- thisline[i] = &lb1[i];
+ for (j = 0; j < 4; j++)
+ {
+ initbuffer (&lba[i][j]);
+ all_line[i][j] = &lba[i][j];
+ }
+ alt[i][0] = 0;
+ alt[i][1] = 0;
+ alt[i][2] = 0;
streams[i] = (STREQ (infiles[i], "-") ? stdin : fopen (infiles[i], "r"));
if (!streams[i])
- error (EXIT_FAILURE, errno, "%s", infiles[i]);
+ error (EXIT_FAILURE, errno, "%s", quotef (infiles[i]));
- thisline[i] = readlinebuffer (thisline[i], streams[i]);
+ fadvise (streams[i], FADVISE_SEQUENTIAL);
+
+ thisline[i] = readlinebuffer_delim (all_line[i][alt[i][0]], streams[i],
+ delim);
if (ferror (streams[i]))
- error (EXIT_FAILURE, errno, "%s", infiles[i]);
+ error (EXIT_FAILURE, errno, "%s", quotef (infiles[i]));
}
while (thisline[0] || thisline[1])
{
int order;
+ bool fill_up[2] = { false, false };
/* Compare the next available lines of the two files. */
if (!thisline[0])
- order = 1;
+ order = 1;
else if (!thisline[1])
- order = -1;
+ order = -1;
else
- {
- if (hard_LC_COLLATE)
- order = xmemcoll (thisline[0]->buffer, thisline[0]->length - 1,
- thisline[1]->buffer, thisline[1]->length - 1);
- else
- {
- size_t len = min (thisline[0]->length, thisline[1]->length) - 1;
- order = memcmp (thisline[0]->buffer, thisline[1]->buffer, len);
- if (order == 0)
- order = (thisline[0]->length < thisline[1]->length
- ? -1
- : thisline[0]->length != thisline[1]->length);
- }
- }
+ {
+ if (hard_LC_COLLATE)
+ order = xmemcoll (thisline[0]->buffer, thisline[0]->length - 1,
+ thisline[1]->buffer, thisline[1]->length - 1);
+ else
+ {
+ size_t len = min (thisline[0]->length, thisline[1]->length) - 1;
+ order = memcmp (thisline[0]->buffer, thisline[1]->buffer, len);
+ if (order == 0)
+ order = (thisline[0]->length < thisline[1]->length
+ ? -1
+ : thisline[0]->length != thisline[1]->length);
+ }
+ }
/* Output the line that is lesser. */
if (order == 0)
- writeline (thisline[1], stdout, 3);
- else if (order > 0)
- writeline (thisline[1], stdout, 2);
+ writeline (thisline[1], stdout, 3);
else
- writeline (thisline[0], stdout, 1);
+ {
+ seen_unpairable = true;
+ if (order <= 0)
+ writeline (thisline[0], stdout, 1);
+ else
+ writeline (thisline[1], stdout, 2);
+ }
/* Step the file the line came from.
- If the files match, step both files. */
- if (order >= 0)
- {
- thisline[1] = readlinebuffer (thisline[1], streams[1]);
- if (ferror (streams[1]))
- error (EXIT_FAILURE, errno, "%s", infiles[1]);
- }
+ If the files match, step both files. */
+ if (0 <= order)
+ fill_up[1] = true;
if (order <= 0)
- {
- thisline[0] = readlinebuffer (thisline[0], streams[0]);
- if (ferror (streams[0]))
- error (EXIT_FAILURE, errno, "%s", infiles[0]);
- }
+ fill_up[0] = true;
+
+ for (i = 0; i < 2; i++)
+ if (fill_up[i])
+ {
+ /* Rotate the buffers for this file. */
+ alt[i][2] = alt[i][1];
+ alt[i][1] = alt[i][0];
+ alt[i][0] = (alt[i][0] + 1) & 0x03;
+
+ thisline[i] = readlinebuffer_delim (all_line[i][alt[i][0]],
+ streams[i], delim);
+
+ if (thisline[i])
+ check_order (all_line[i][alt[i][1]], thisline[i], i + 1);
+
+ /* If this is the end of the file we may need to re-check
+ the order of the previous two lines, since we might have
+ discovered an unpairable match since we checked before. */
+ else if (all_line[i][alt[i][2]]->buffer)
+ check_order (all_line[i][alt[i][2]],
+ all_line[i][alt[i][1]], i + 1);
+
+ if (ferror (streams[i]))
+ error (EXIT_FAILURE, errno, "%s", quotef (infiles[i]));
+
+ fill_up[i] = false;
+ }
}
for (i = 0; i < 2; i++)
if (fclose (streams[i]) != 0)
- error (EXIT_FAILURE, errno, "%s", infiles[i]);
+ error (EXIT_FAILURE, errno, "%s", quotef (infiles[i]));
}
int
@@ -229,7 +372,7 @@ main (int argc, char **argv)
int c;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -241,35 +384,61 @@ main (int argc, char **argv)
only_file_2 = true;
both = true;
- while ((c = getopt_long (argc, argv, "123", long_options, NULL)) != -1)
+ seen_unpairable = false;
+ issued_disorder_warning[0] = issued_disorder_warning[1] = false;
+ check_input_order = CHECK_ORDER_DEFAULT;
+
+ while ((c = getopt_long (argc, argv, "123z", long_options, NULL)) != -1)
switch (c)
{
case '1':
- only_file_1 = false;
- break;
+ only_file_1 = false;
+ break;
case '2':
- only_file_2 = false;
- break;
+ only_file_2 = false;
+ break;
case '3':
- both = false;
- break;
+ both = false;
+ break;
+
+ case 'z':
+ delim = '\0';
+ break;
+
+ case NOCHECK_ORDER_OPTION:
+ check_input_order = CHECK_ORDER_DISABLED;
+ break;
+
+ case CHECK_ORDER_OPTION:
+ check_input_order = CHECK_ORDER_ENABLED;
+ break;
+
+ case OUTPUT_DELIMITER_OPTION:
+ if (col_sep_len && !STREQ (col_sep, optarg))
+ error (EXIT_FAILURE, 0, _("multiple output delimiters specified"));
+ col_sep = optarg;
+ col_sep_len = *optarg ? strlen (optarg) : 1;
+ break;
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
- usage (EXIT_FAILURE);
+ usage (EXIT_FAILURE);
}
+ if (! col_sep_len)
+ col_sep_len = 1;
+
if (argc - optind < 2)
{
if (argc <= optind)
- error (0, 0, _("missing operand"));
+ error (0, 0, _("missing operand"));
else
- error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
+ error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
usage (EXIT_FAILURE);
}
@@ -281,5 +450,8 @@ main (int argc, char **argv)
compare_files (argv + optind);
- exit (EXIT_SUCCESS);
+ if (issued_disorder_warning[0] || issued_disorder_warning[1])
+ return EXIT_FAILURE;
+ else
+ return EXIT_SUCCESS;
}
diff --git a/src/copy.c b/src/copy.c
index 4bdb75c..191ccaa 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -1,10 +1,10 @@
/* copy.c -- core functions for copying files and directories
- Copyright (C) 89, 90, 91, 1995-2007 Free Software Foundation.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,15 +12,16 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Extracted from cp.c and librarified by Jim Meyering. */
#include <config.h>
#include <stdio.h>
#include <assert.h>
+#include <sys/ioctl.h>
#include <sys/types.h>
+#include <selinux/selinux.h>
#if HAVE_HURD_H
# include <hurd.h>
@@ -33,40 +34,81 @@
#include "acl.h"
#include "backupfile.h"
#include "buffer-lcm.h"
+#include "canonicalize.h"
#include "copy.h"
#include "cp-hash.h"
-#include "euidaccess.h"
+#include "extent-scan.h"
#include "error.h"
+#include "fadvise.h"
#include "fcntl--.h"
+#include "fiemap.h"
+#include "file-set.h"
#include "filemode.h"
#include "filenamecat.h"
#include "full-write.h"
-#include "getpagesize.h"
#include "hash.h"
-#include "hash-pjw.h"
-#include "lchmod.h"
+#include "hash-triple.h"
+#include "ignore-value.h"
+#include "ioblksize.h"
#include "quote.h"
+#include "root-uid.h"
#include "same.h"
#include "savedir.h"
+#include "stat-size.h"
#include "stat-time.h"
#include "utimecmp.h"
#include "utimens.h"
-#include "xreadlink.h"
+#include "write-any-file.h"
+#include "areadlink.h"
#include "yesno.h"
+#include "selinux.h"
+
+#if USE_XATTR
+# include <attr/error_context.h>
+# include <attr/libattr.h>
+# include <stdarg.h>
+# include "verror.h"
+#endif
+
+#if HAVE_LINUX_FALLOC_H
+# include <linux/falloc.h>
+#endif
#ifndef HAVE_FCHOWN
# define HAVE_FCHOWN false
# define fchown(fd, uid, gid) (-1)
#endif
+#ifndef HAVE_LCHOWN
+# define HAVE_LCHOWN false
+# define lchown(name, uid, gid) chown (name, uid, gid)
+#endif
+
+#ifndef HAVE_MKFIFO
+static int
+rpl_mkfifo (char const *file, mode_t mode)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+# define mkfifo rpl_mkfifo
+#endif
+
+#ifndef USE_ACL
+# define USE_ACL 0
+#endif
+
#define SAME_OWNER(A, B) ((A).st_uid == (B).st_uid)
#define SAME_GROUP(A, B) ((A).st_gid == (B).st_gid)
#define SAME_OWNER_AND_GROUP(A, B) (SAME_OWNER (A, B) && SAME_GROUP (A, B))
-#define UNWRITABLE(File_name, File_mode) \
- ( /* euidaccess is not meaningful for symlinks */ \
- ! S_ISLNK (File_mode) \
- && euidaccess (File_name, W_OK) != 0)
+/* LINK_FOLLOWS_SYMLINKS is tri-state; if it is -1, we don't know
+ how link() behaves, so assume we can't hardlink symlinks in that case. */
+#if (defined HAVE_LINKAT && ! LINKAT_SYMLINK_NOTSUP) || ! LINK_FOLLOWS_SYMLINKS
+# define CAN_HARDLINK_SYMLINKS 1
+#else
+# define CAN_HARDLINK_SYMLINKS 0
+#endif
struct dir_list
{
@@ -75,74 +117,604 @@ struct dir_list
dev_t dev;
};
-/* Describe a just-created or just-renamed destination file. */
-struct F_triple
-{
- char *name;
- ino_t st_ino;
- dev_t st_dev;
-};
-
-/* Initial size of the above hash table. */
+/* Initial size of the cp.dest_info hash table. */
#define DEST_INFO_INITIAL_CAPACITY 61
static bool copy_internal (char const *src_name, char const *dst_name,
- bool new_dst, dev_t device,
- struct dir_list *ancestors,
- const struct cp_options *x,
- bool command_line_arg,
- bool *copy_into_self,
- bool *rename_succeeded);
+ bool new_dst, struct stat const *parent,
+ struct dir_list *ancestors,
+ const struct cp_options *x,
+ bool command_line_arg,
+ bool *first_dir_created_per_command_line_arg,
+ bool *copy_into_self,
+ bool *rename_succeeded);
+static bool owner_failure_ok (struct cp_options const *x);
/* Pointers to the file names: they're used in the diagnostic that is issued
when we detect the user is trying to copy a directory into itself. */
static char const *top_level_src_name;
static char const *top_level_dst_name;
-/* The invocation name of this program. */
-extern char *program_name;
+/* Set the timestamp of symlink, FILE, to TIMESPEC.
+ If this system lacks support for that, simply return 0. */
+static inline int
+utimens_symlink (char const *file, struct timespec const *timespec)
+{
+ int err = lutimens (file, timespec);
+ /* When configuring on a system with new headers and libraries, and
+ running on one with a kernel that is old enough to lack the syscall,
+ utimensat fails with ENOSYS. Ignore that. */
+ if (err && errno == ENOSYS)
+ err = 0;
+ return err;
+}
+
+/* Attempt to punch a hole to avoid any permanent
+ speculative preallocation on file systems such as XFS.
+ Return values as per fallocate(2) except ENOSYS etc. are ignored. */
+
+static int
+punch_hole (int fd, off_t offset, off_t length)
+{
+ int ret = 0;
+#if HAVE_FALLOCATE
+# if defined FALLOC_FL_PUNCH_HOLE && defined FALLOC_FL_KEEP_SIZE
+ ret = fallocate (fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+ offset, length);
+ if (ret < 0 && (is_ENOTSUP (errno) || errno == ENOSYS))
+ ret = 0;
+# endif
+#endif
+ return ret;
+}
+
+/* Create a hole at the end of a file,
+ avoiding preallocation if requested. */
+
+static bool
+create_hole (int fd, char const *name, bool punch_holes, off_t size)
+{
+ off_t file_end = lseek (fd, size, SEEK_CUR);
+
+ if (file_end < 0)
+ {
+ error (0, errno, _("cannot lseek %s"), quoteaf (name));
+ return false;
+ }
+
+ /* Some file systems (like XFS) preallocate when write extending a file.
+ I.e., a previous write() may have preallocated extra space
+ that the seek above will not discard. A subsequent write() could
+ then make this allocation permanent. */
+ if (punch_holes && punch_hole (fd, file_end - size, size) < 0)
+ {
+ error (0, errno, _("error deallocating %s"), quoteaf (name));
+ return false;
+ }
+
+ return true;
+}
+
+
+/* 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
+ BUF for temporary storage. Copy no more than MAX_N_READ bytes.
+ Return true upon successful completion;
+ print a diagnostic and return false upon error.
+ Note that for best results, BUF should be "well"-aligned.
+ BUF must have sizeof(uintptr_t)-1 bytes of additional space
+ beyond BUF[BUF_SIZE-1].
+ Set *LAST_WRITE_MADE_HOLE to true if the final operation on
+ DEST_FD introduced a hole. Set *TOTAL_N_READ to the number of
+ bytes read. */
+static bool
+sparse_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
+ size_t hole_size, bool punch_holes,
+ char const *src_name, char const *dst_name,
+ uintmax_t max_n_read, off_t *total_n_read,
+ bool *last_write_made_hole)
+{
+ *last_write_made_hole = false;
+ *total_n_read = 0;
+ bool make_hole = false;
+ off_t psize = 0;
+
+ while (max_n_read)
+ {
+ ssize_t n_read = read (src_fd, buf, MIN (max_n_read, buf_size));
+ if (n_read < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ error (0, errno, _("error reading %s"), quoteaf (src_name));
+ return false;
+ }
+ if (n_read == 0)
+ break;
+ max_n_read -= n_read;
+ *total_n_read += n_read;
+
+ /* Loop over the input buffer in chunks of hole_size. */
+ size_t csize = hole_size ? hole_size : buf_size;
+ char *cbuf = buf;
+ char *pbuf = buf;
+
+ while (n_read)
+ {
+ bool prev_hole = make_hole;
+ csize = MIN (csize, n_read);
+
+ if (hole_size && csize)
+ make_hole = is_nul (cbuf, csize);
+
+ bool transition = (make_hole != prev_hole) && psize;
+ bool last_chunk = (n_read == csize && ! make_hole) || ! csize;
+
+ if (transition || last_chunk)
+ {
+ if (! transition)
+ psize += csize;
+
+ if (! prev_hole)
+ {
+ if (full_write (dest_fd, pbuf, psize) != psize)
+ {
+ error (0, errno, _("error writing %s"),
+ quoteaf (dst_name));
+ return false;
+ }
+ }
+ else
+ {
+ if (! create_hole (dest_fd, dst_name, punch_holes, psize))
+ return false;
+ }
+
+ pbuf = cbuf;
+ psize = csize;
+
+ if (last_chunk)
+ {
+ if (! csize)
+ n_read = 0; /* Finished processing buffer. */
+
+ if (transition)
+ csize = 0; /* Loop again to deal with last chunk. */
+ else
+ psize = 0; /* Reset for next read loop. */
+ }
+ }
+ else /* Coalesce writes/seeks. */
+ {
+ if (psize <= OFF_T_MAX - csize)
+ psize += csize;
+ else
+ {
+ error (0, 0, _("overflow reading %s"), quoteaf (src_name));
+ return false;
+ }
+ }
+
+ n_read -= csize;
+ cbuf += csize;
+ }
+
+ *last_write_made_hole = make_hole;
+
+ /* It's tempting to break early here upon a short read from
+ a regular file. That would save the final read syscall
+ for each file. Unfortunately that doesn't work for
+ certain files in /proc or /sys with linux kernels. */
+ }
+
+ /* Ensure a trailing hole is created, so that subsequent
+ calls of sparse_copy() start at the correct offset. */
+ if (make_hole && ! create_hole (dest_fd, dst_name, punch_holes, psize))
+ return false;
+ else
+ return true;
+}
+
+/* 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)
+{
+#ifdef __linux__
+# undef BTRFS_IOCTL_MAGIC
+# define BTRFS_IOCTL_MAGIC 0x94
+# undef BTRFS_IOC_CLONE
+# define BTRFS_IOC_CLONE _IOW (BTRFS_IOCTL_MAGIC, 9, int)
+ return ioctl (dest_fd, BTRFS_IOC_CLONE, src_fd);
+#else
+ (void) dest_fd;
+ (void) src_fd;
+ errno = ENOTSUP;
+ return -1;
+#endif
+}
+
+/* Write N_BYTES zero bytes to file descriptor FD. Return true if successful.
+ Upon write failure, set errno and return false. */
+static bool
+write_zeros (int fd, off_t n_bytes)
+{
+ static char *zeros;
+ static size_t nz = IO_BUFSIZE;
+
+ /* Attempt to use a relatively large calloc'd source buffer for
+ efficiency, but if that allocation fails, resort to a smaller
+ statically allocated one. */
+ if (zeros == NULL)
+ {
+ static char fallback[1024];
+ zeros = calloc (nz, 1);
+ if (zeros == NULL)
+ {
+ zeros = fallback;
+ nz = sizeof fallback;
+ }
+ }
+
+ while (n_bytes)
+ {
+ size_t n = MIN (nz, n_bytes);
+ if ((full_write (fd, zeros, n)) != n)
+ return false;
+ n_bytes -= n;
+ }
+
+ return true;
+}
+
+/* Perform an efficient extent copy, if possible. This avoids
+ the overhead of detecting holes in hole-introducing/preserving
+ copy, and thus makes copying sparse files much more efficient.
+ Upon a successful copy, return true. If the initial extent scan
+ fails, set *NORMAL_COPY_REQUIRED to true and return false.
+ Upon any other failure, set *NORMAL_COPY_REQUIRED to false and
+ return false. */
+static bool
+extent_copy (int src_fd, int dest_fd, char *buf, size_t buf_size,
+ size_t hole_size, off_t src_total_size,
+ enum Sparse_type sparse_mode,
+ char const *src_name, char const *dst_name,
+ bool *require_normal_copy)
+{
+ struct extent_scan scan;
+ off_t last_ext_start = 0;
+ off_t last_ext_len = 0;
+
+ /* Keep track of the output position.
+ We may need this at the end, for a final ftruncate. */
+ off_t dest_pos = 0;
+
+ extent_scan_init (src_fd, &scan);
+
+ *require_normal_copy = false;
+ bool wrote_hole_at_eof = true;
+ do
+ {
+ bool ok = extent_scan_read (&scan);
+ if (! ok)
+ {
+ if (scan.hit_final_extent)
+ break;
+
+ if (scan.initial_scan_failed)
+ {
+ *require_normal_copy = true;
+ return false;
+ }
+
+ error (0, errno, _("%s: failed to get extents info"),
+ quotef (src_name));
+ return false;
+ }
+
+ unsigned int i;
+ bool empty_extent = false;
+ for (i = 0; i < scan.ei_count || empty_extent; i++)
+ {
+ off_t ext_start;
+ off_t ext_len;
+ off_t ext_hole_size;
+
+ if (i < scan.ei_count)
+ {
+ ext_start = scan.ext_info[i].ext_logical;
+ ext_len = scan.ext_info[i].ext_length;
+ }
+ else /* empty extent at EOF. */
+ {
+ i--;
+ ext_start = last_ext_start + scan.ext_info[i].ext_length;
+ ext_len = 0;
+ }
+
+ /* Truncate extent to EOF. Extents starting after EOF are
+ treated as zero length extents starting right after EOF.
+ Generally this will trigger with an extent starting after
+ src_total_size, and result in creating a hole or zeros until EOF.
+ Though in a file in which extents have changed since src_total_size
+ was determined, we might have an extent spanning that size,
+ in which case we'll only copy data up to that size. */
+ if (src_total_size < ext_start + ext_len)
+ {
+ if (src_total_size < ext_start)
+ ext_start = src_total_size;
+ ext_len = src_total_size - ext_start;
+ }
+
+ ext_hole_size = ext_start - last_ext_start - last_ext_len;
+
+ wrote_hole_at_eof = false;
+
+ if (ext_hole_size)
+ {
+ if (lseek (src_fd, ext_start, SEEK_SET) < 0)
+ {
+ error (0, errno, _("cannot lseek %s"), quoteaf (src_name));
+ fail:
+ extent_scan_free (&scan);
+ return false;
+ }
+
+ if ((empty_extent && sparse_mode == SPARSE_ALWAYS)
+ || (!empty_extent && sparse_mode != SPARSE_NEVER))
+ {
+ if (! create_hole (dest_fd, dst_name,
+ sparse_mode == SPARSE_ALWAYS,
+ ext_hole_size))
+ goto fail;
+ wrote_hole_at_eof = true;
+ }
+ else
+ {
+ /* When not inducing holes and when there is a hole between
+ the end of the previous extent and the beginning of the
+ current one, write zeros to the destination file. */
+ off_t nzeros = ext_hole_size;
+ if (empty_extent)
+ nzeros = MIN (src_total_size - dest_pos, ext_hole_size);
+
+ if (! write_zeros (dest_fd, nzeros))
+ {
+ error (0, errno, _("%s: write failed"),
+ quotef (dst_name));
+ goto fail;
+ }
+
+ dest_pos = MIN (src_total_size, ext_start);
+ }
+ }
+
+ last_ext_start = ext_start;
+
+ /* Treat an unwritten but allocated extent much like a hole.
+ I.e., don't read, but don't convert to a hole in the destination,
+ unless SPARSE_ALWAYS. */
+ /* For now, do not treat FIEMAP_EXTENT_UNWRITTEN specially,
+ because that (in combination with no sync) would lead to data
+ loss at least on XFS and ext4 when using 2.6.39-rc3 kernels. */
+ if (0 && (scan.ext_info[i].ext_flags & FIEMAP_EXTENT_UNWRITTEN))
+ {
+ empty_extent = true;
+ last_ext_len = 0;
+ if (ext_len == 0) /* The last extent is empty and processed. */
+ empty_extent = false;
+ }
+ else
+ {
+ off_t n_read;
+ empty_extent = false;
+ last_ext_len = ext_len;
+ bool read_hole;
+
+ if ( ! sparse_copy (src_fd, dest_fd, buf, buf_size,
+ sparse_mode == SPARSE_ALWAYS ? hole_size: 0,
+ true, src_name, dst_name, ext_len, &n_read,
+ &read_hole))
+ goto fail;
+
+ dest_pos = ext_start + n_read;
+ if (n_read)
+ wrote_hole_at_eof = read_hole;
+ }
+
+ /* If the file ends with unwritten extents not accounted for in the
+ size, then skip processing them, and the associated redundant
+ read() calls which will always return 0. We will need to
+ remove this when we add fallocate() so that we can maintain
+ extents beyond the apparent size. */
+ if (dest_pos == src_total_size)
+ {
+ scan.hit_final_extent = true;
+ break;
+ }
+ }
+
+ /* Release the space allocated to scan->ext_info. */
+ extent_scan_free (&scan);
+
+ }
+ while (! scan.hit_final_extent);
+
+ /* When the source file ends with a hole, we have to do a little more work,
+ since the above copied only up to and including the final extent.
+ In order to complete the copy, we may have to insert a hole or write
+ zeros in the destination corresponding to the source file's hole-at-EOF.
+
+ In addition, if the final extent was a block of zeros at EOF and we've
+ just converted them to a hole in the destination, we must call ftruncate
+ here in order to record the proper length in the destination. */
+ if ((dest_pos < src_total_size || wrote_hole_at_eof)
+ && (sparse_mode != SPARSE_NEVER
+ ? ftruncate (dest_fd, src_total_size)
+ : ! write_zeros (dest_fd, src_total_size - dest_pos)))
+ {
+ error (0, errno, _("failed to extend %s"), quoteaf (dst_name));
+ return false;
+ }
+
+ if (sparse_mode == SPARSE_ALWAYS && dest_pos < src_total_size
+ && punch_hole (dest_fd, dest_pos, src_total_size - dest_pos) < 0)
+ {
+ error (0, errno, _("error deallocating %s"), quoteaf (dst_name));
+ return false;
+ }
+
+ return true;
+}
/* FIXME: describe */
/* FIXME: rewrite this to use a hash table so we avoid the quadratic
performance hit that's probably noticeable only on trees deeper
than a few hundred levels. See use of active_dir_map in remove.c */
-static bool
+static bool _GL_ATTRIBUTE_PURE
is_ancestor (const struct stat *sb, const struct dir_list *ancestors)
{
while (ancestors != 0)
{
if (ancestors->ino == sb->st_ino && ancestors->dev == sb->st_dev)
- return true;
+ return true;
ancestors = ancestors->parent;
}
return false;
}
+static bool
+errno_unsupported (int err)
+{
+ return err == ENOTSUP || err == ENODATA;
+}
+
+#if USE_XATTR
+static void
+copy_attr_error (struct error_context *ctx _GL_UNUSED,
+ char const *fmt, ...)
+{
+ if (!errno_unsupported (errno))
+ {
+ int err = errno;
+ va_list ap;
+
+ /* use verror module to print error message */
+ va_start (ap, fmt);
+ verror (0, err, fmt, ap);
+ va_end (ap);
+ }
+}
+
+static void
+copy_attr_allerror (struct error_context *ctx _GL_UNUSED,
+ char const *fmt, ...)
+{
+ int err = errno;
+ va_list ap;
+
+ /* use verror module to print error message */
+ va_start (ap, fmt);
+ verror (0, err, fmt, ap);
+ va_end (ap);
+}
+
+static char const *
+copy_attr_quote (struct error_context *ctx _GL_UNUSED, char const *str)
+{
+ return quoteaf (str);
+}
+
+static void
+copy_attr_free (struct error_context *ctx _GL_UNUSED,
+ char const *str _GL_UNUSED)
+{
+}
+
+/* Exclude SELinux extended attributes that are otherwise handled,
+ and are problematic to copy again. Also honor attributes
+ configured for exclusion in /etc/xattr.conf.
+ FIXME: Should we handle POSIX ACLs similarly?
+ Return zero to skip. */
+static int
+check_selinux_attr (const char *name, struct error_context *ctx)
+{
+ return STRNCMP_LIT (name, "security.selinux")
+ && attr_copy_check_permissions (name, ctx);
+}
+
+/* If positive SRC_FD and DST_FD descriptors are passed,
+ then copy by fd, otherwise copy by name. */
+
+static bool
+copy_attr (char const *src_path, int src_fd,
+ char const *dst_path, int dst_fd, struct cp_options const *x)
+{
+ int ret;
+ bool all_errors = (!x->data_copy_required || x->require_preserve_xattr);
+ bool some_errors = (!all_errors && !x->reduce_diagnostics);
+ bool selinux_done = (x->preserve_security_context || x->set_security_context);
+ struct error_context ctx =
+ {
+ .error = all_errors ? copy_attr_allerror : copy_attr_error,
+ .quote = copy_attr_quote,
+ .quote_free = copy_attr_free
+ };
+ if (0 <= src_fd && 0 <= dst_fd)
+ ret = attr_copy_fd (src_path, src_fd, dst_path, dst_fd,
+ selinux_done ? check_selinux_attr : NULL,
+ (all_errors || some_errors ? &ctx : NULL));
+ else
+ ret = attr_copy_file (src_path, dst_path,
+ selinux_done ? check_selinux_attr : NULL,
+ (all_errors || some_errors ? &ctx : NULL));
+
+ return ret == 0;
+}
+#else /* USE_XATTR */
+
+static bool
+copy_attr (char const *src_path _GL_UNUSED,
+ int src_fd _GL_UNUSED,
+ char const *dst_path _GL_UNUSED,
+ int dst_fd _GL_UNUSED,
+ struct cp_options const *x _GL_UNUSED)
+{
+ return true;
+}
+#endif /* USE_XATTR */
+
/* Read the contents of the directory SRC_NAME_IN, and recursively
copy the contents to DST_NAME_IN. NEW_DST is true if
DST_NAME_IN is a directory that was created previously in the
recursion. SRC_SB and ANCESTORS describe SRC_NAME_IN.
Set *COPY_INTO_SELF if SRC_NAME_IN is a parent of
(or the same as) DST_NAME_IN; otherwise, clear it.
+ Propagate *FIRST_DIR_CREATED_PER_COMMAND_LINE_ARG from
+ caller to each invocation of copy_internal. Be careful to
+ pass the address of a temporary, and to update
+ *FIRST_DIR_CREATED_PER_COMMAND_LINE_ARG only upon completion.
Return true if successful. */
static bool
copy_dir (char const *src_name_in, char const *dst_name_in, bool new_dst,
- const struct stat *src_sb, struct dir_list *ancestors,
- const struct cp_options *x, bool *copy_into_self)
+ const struct stat *src_sb, struct dir_list *ancestors,
+ const struct cp_options *x,
+ bool *first_dir_created_per_command_line_arg,
+ bool *copy_into_self)
{
char *name_space;
char *namep;
struct cp_options non_command_line_options = *x;
bool ok = true;
- name_space = savedir (src_name_in);
+ name_space = savedir (src_name_in, SAVEDIR_SORT_FASTREAD);
if (name_space == NULL)
{
/* This diagnostic is a bit vague because savedir can fail in
several different ways. */
- error (0, errno, _("cannot access %s"), quote (src_name_in));
+ error (0, errno, _("cannot access %s"), quoteaf (src_name_in));
return false;
}
@@ -151,55 +723,117 @@ copy_dir (char const *src_name_in, char const *dst_name_in, bool new_dst,
if (x->dereference == DEREF_COMMAND_LINE_ARGUMENTS)
non_command_line_options.dereference = DEREF_NEVER;
+ bool new_first_dir_created = false;
namep = name_space;
while (*namep != '\0')
{
bool local_copy_into_self;
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;
- ok &= copy_internal (src_name, dst_name, new_dst, src_sb->st_dev,
- ancestors, &non_command_line_options, false,
- &local_copy_into_self, NULL);
+ ok &= copy_internal (src_name, dst_name, new_dst, src_sb,
+ ancestors, &non_command_line_options, false,
+ &first_dir_created,
+ &local_copy_into_self, NULL);
*copy_into_self |= local_copy_into_self;
free (dst_name);
free (src_name);
+ /* If we're copying into self, there's no point in continuing,
+ and in fact, that would even infloop, now that we record only
+ the first created directory per command line argument. */
+ if (local_copy_into_self)
+ break;
+
+ new_first_dir_created |= first_dir_created;
namep += strlen (namep) + 1;
}
free (name_space);
+ *first_dir_created_per_command_line_arg = new_first_dir_created;
+
return ok;
}
/* Set the owner and owning group of DEST_DESC to the st_uid and
st_gid fields of SRC_SB. If DEST_DESC is undefined (-1), set
- the owner and owning group of DST_NAME instead. DEST_DESC must
+ the owner and owning group of DST_NAME instead; for
+ safety prefer lchown if the system supports it since no
+ symbolic links should be involved. DEST_DESC must
refer to the same file as DEST_NAME if defined.
- Return 1 if the syscall succeeds, 0 if it fails but it's OK
+ Upon failure to set both UID and GID, try to set only the GID.
+ NEW_DST is true if the file was newly created; otherwise,
+ DST_SB is the status of the destination.
+ Return 1 if the initial syscall succeeds, 0 if it fails but it's OK
not to preserve ownership, -1 otherwise. */
static int
set_owner (const struct cp_options *x, char const *dst_name, int dest_desc,
- uid_t uid, gid_t gid)
+ struct stat const *src_sb, bool new_dst,
+ struct stat const *dst_sb)
{
+ uid_t uid = src_sb->st_uid;
+ gid_t gid = src_sb->st_gid;
+
+ /* Naively changing the ownership of an already-existing file before
+ changing its permissions would create a window of vulnerability if
+ the file's old permissions are too generous for the new owner and
+ group. Avoid the window by first changing to a restrictive
+ temporary mode if necessary. */
+
+ if (!new_dst && (x->preserve_mode || x->move_mode || x->set_mode))
+ {
+ mode_t old_mode = dst_sb->st_mode;
+ mode_t new_mode =
+ (x->preserve_mode || x->move_mode ? src_sb->st_mode : x->mode);
+ mode_t restrictive_temp_mode = old_mode & new_mode & S_IRWXU;
+
+ if ((USE_ACL
+ || (old_mode & CHMOD_MODE_BITS
+ & (~new_mode | S_ISUID | S_ISGID | S_ISVTX)))
+ && qset_acl (dst_name, dest_desc, restrictive_temp_mode) != 0)
+ {
+ if (! owner_failure_ok (x))
+ error (0, errno, _("clearing permissions for %s"),
+ quoteaf (dst_name));
+ return -x->require_preserve;
+ }
+ }
+
if (HAVE_FCHOWN && dest_desc != -1)
{
if (fchown (dest_desc, uid, gid) == 0)
- return 1;
+ return 1;
+ if (errno == EPERM || errno == EINVAL)
+ {
+ /* We've failed to set *both*. Now, try to set just the group
+ ID, but ignore any failure here, and don't change errno. */
+ int saved_errno = errno;
+ ignore_value (fchown (dest_desc, -1, gid));
+ errno = saved_errno;
+ }
}
else
{
- if (chown (dst_name, uid, gid) == 0)
- return 1;
+ if (lchown (dst_name, uid, gid) == 0)
+ return 1;
+ if (errno == EPERM || errno == EINVAL)
+ {
+ /* We've failed to set *both*. Now, try to set just the group
+ ID, but ignore any failure here, and don't change errno. */
+ int saved_errno = errno;
+ ignore_value (lchown (dst_name, -1, gid));
+ errno = saved_errno;
+ }
}
if (! chown_failure_ok (x))
{
error (0, errno, _("failed to preserve ownership for %s"),
- quote (dst_name));
+ quoteaf (dst_name));
if (x->require_preserve)
- return -1;
+ return -1;
}
return 0;
@@ -214,23 +848,120 @@ static void
set_author (const char *dst_name, int dest_desc, const struct stat *src_sb)
{
#if HAVE_STRUCT_STAT_ST_AUTHOR
+ /* FIXME: Modify the following code so that it does not
+ follow symbolic links. */
+
/* Preserve the st_author field. */
file_t file = (dest_desc < 0
- ? file_name_lookup (dst_name, 0, 0)
- : getdport (dest_desc));
+ ? file_name_lookup (dst_name, 0, 0)
+ : getdport (dest_desc));
if (file == MACH_PORT_NULL)
- error (0, errno, _("failed to lookup file %s"), quote (dst_name));
+ error (0, errno, _("failed to lookup file %s"), quoteaf (dst_name));
else
{
error_t err = file_chauthor (file, src_sb->st_author);
if (err)
- error (0, err, _("failed to preserve authorship for %s"),
- quote (dst_name));
+ error (0, err, _("failed to preserve authorship for %s"),
+ quoteaf (dst_name));
mach_port_deallocate (mach_task_self (), file);
}
+#else
+ (void) dst_name;
+ (void) dest_desc;
+ (void) src_sb;
#endif
}
+/* Set the default security context for the process. New files will
+ have this security context set. Also existing files can have their
+ context adjusted based on this process context, by
+ set_file_security_ctx() called with PROCESS_LOCAL=true.
+ This should be called before files are created so there is no race
+ where a file may be present without an appropriate security context.
+ Based on CP_OPTIONS, diagnose warnings and fail when appropriate.
+ Return FALSE on failure, TRUE on success. */
+
+static bool
+set_process_security_ctx (char const *src_name, char const *dst_name,
+ mode_t mode, bool new_dst, const struct cp_options *x)
+{
+ if (x->preserve_security_context)
+ {
+ /* Set the default context for the process to match the source. */
+ bool all_errors = !x->data_copy_required || x->require_preserve_context;
+ bool some_errors = !all_errors && !x->reduce_diagnostics;
+ char *con;
+
+ if (0 <= lgetfilecon (src_name, &con))
+ {
+ if (setfscreatecon (con) < 0)
+ {
+ if (all_errors || (some_errors && !errno_unsupported (errno)))
+ error (0, errno,
+ _("failed to set default file creation context to %s"),
+ quote (con));
+ if (x->require_preserve_context)
+ {
+ freecon (con);
+ return false;
+ }
+ }
+ freecon (con);
+ }
+ else
+ {
+ if (all_errors || (some_errors && !errno_unsupported (errno)))
+ {
+ error (0, errno,
+ _("failed to get security context of %s"),
+ quoteaf (src_name));
+ }
+ if (x->require_preserve_context)
+ return false;
+ }
+ }
+ else if (x->set_security_context)
+ {
+ /* With -Z, adjust the default context for the process
+ to have the type component adjusted as per the destination path. */
+ if (new_dst && defaultcon (dst_name, mode) < 0
+ && ! ignorable_ctx_err (errno))
+ {
+ error (0, errno,
+ _("failed to set default file creation context for %s"),
+ quoteaf (dst_name));
+ }
+ }
+
+ return true;
+}
+
+/* Reset the security context of DST_NAME, to that already set
+ as the process default if PROCESS_LOCAL is true. Otherwise
+ adjust the type component of DST_NAME's security context as
+ per the system default for that path. Issue warnings upon
+ failure, when allowed by various settings in CP_OPTIONS.
+ Return FALSE on failure, TRUE on success. */
+
+static bool
+set_file_security_ctx (char const *dst_name, bool process_local,
+ bool recurse, const struct cp_options *x)
+{
+ bool all_errors = (!x->data_copy_required
+ || x->require_preserve_context);
+ bool some_errors = !all_errors && !x->reduce_diagnostics;
+
+ if (! restorecon (dst_name, recurse, process_local))
+ {
+ if (all_errors || (some_errors && !errno_unsupported (errno)))
+ error (0, errno, _("failed to set the security context of %s"),
+ quoteaf_n (0, dst_name));
+ return false;
+ }
+
+ return true;
+}
+
/* Change the file mode bits of the file identified by DESC or NAME to MODE.
Use DESC if DESC is valid and fchmod is available, NAME otherwise. */
@@ -244,10 +975,27 @@ fchmod_or_lchmod (int desc, char const *name, mode_t mode)
return lchmod (name, mode);
}
+#ifndef HAVE_STRUCT_STAT_ST_BLOCKS
+# define HAVE_STRUCT_STAT_ST_BLOCKS 0
+#endif
+
+/* Use a heuristic to determine whether stat buffer SB comes from a file
+ with sparse blocks. If the file has fewer blocks than would normally
+ be needed for a file of its size, then at least one of the blocks in
+ the file is a hole. In that case, return true. */
+static bool
+is_probably_sparse (struct stat const *sb)
+{
+ return (HAVE_STRUCT_STAT_ST_BLOCKS
+ && S_ISREG (sb->st_mode)
+ && ST_NBLOCKS (*sb) < sb->st_size / ST_NBLOCKSIZE);
+}
+
+
/* Copy a regular file from SRC_NAME to DST_NAME.
If the source file contains holes, copies holes and blocks of zeros
in the source file as holes in the destination file.
- (Holes are read as zeroes by the `read' system call.)
+ (Holes are read as zeroes by the 'read' system call.)
When creating the destination, use DST_MODE & ~OMITTED_PERMISSIONS
as the third argument in the call to open, adding
OMITTED_PERMISSIONS after copying as needed.
@@ -258,29 +1006,34 @@ fchmod_or_lchmod (int desc, char const *name, mode_t mode)
static bool
copy_reg (char const *src_name, char const *dst_name,
- const struct cp_options *x,
- mode_t dst_mode, mode_t omitted_permissions, bool *new_dst,
- struct stat const *src_sb)
+ const struct cp_options *x,
+ mode_t dst_mode, mode_t omitted_permissions, bool *new_dst,
+ struct stat const *src_sb)
{
char *buf;
char *buf_alloc = NULL;
+ char *name_alloc = NULL;
int dest_desc;
+ int dest_errno;
int source_desc;
mode_t src_mode = src_sb->st_mode;
struct stat sb;
struct stat src_open_sb;
bool return_val = true;
+ bool data_copy_required = x->data_copy_required;
- source_desc = open (src_name, O_RDONLY | O_BINARY);
+ source_desc = open (src_name,
+ (O_RDONLY | O_BINARY
+ | (x->dereference == DEREF_NEVER ? O_NOFOLLOW : 0)));
if (source_desc < 0)
{
- error (0, errno, _("cannot open %s for reading"), quote (src_name));
+ error (0, errno, _("cannot open %s for reading"), quoteaf (src_name));
return false;
}
if (fstat (source_desc, &src_open_sb) != 0)
{
- error (0, errno, _("cannot fstat %s"), quote (src_name));
+ error (0, errno, _("cannot fstat %s"), quoteaf (src_name));
return_val = false;
goto close_src_desc;
}
@@ -290,8 +1043,8 @@ copy_reg (char const *src_name, char const *dst_name,
if (! SAME_INODE (*src_sb, src_open_sb))
{
error (0, 0,
- _("skipping file %s, as it was replaced while being copied"),
- quote (src_name));
+ _("skipping file %s, as it was replaced while being copied"),
+ quoteaf (src_name));
return_val = false;
goto close_src_desc;
}
@@ -300,248 +1053,306 @@ copy_reg (char const *src_name, char const *dst_name,
by the specs for both cp and mv. */
if (! *new_dst)
{
- dest_desc = open (dst_name, O_WRONLY | O_TRUNC | O_BINARY);
+ int open_flags =
+ O_WRONLY | O_BINARY | (x->data_copy_required ? O_TRUNC : 0);
+ dest_desc = open (dst_name, open_flags);
+ dest_errno = errno;
+
+ /* When using cp --preserve=context to copy to an existing destination,
+ reset the context as per the default context, which has already been
+ set according to the src.
+ When using the mutually exclusive -Z option, then adjust the type of
+ the existing context according to the system default for the dest.
+ Note we set the context here, _after_ the file is opened, lest the
+ new context disallow that. */
+ if ((x->set_security_context || x->preserve_security_context)
+ && 0 <= dest_desc)
+ {
+ if (! set_file_security_ctx (dst_name, x->preserve_security_context,
+ false, x))
+ {
+ if (x->require_preserve_context)
+ {
+ return_val = false;
+ goto close_src_and_dst_desc;
+ }
+ }
+ }
if (dest_desc < 0 && x->unlink_dest_after_failed_open)
- {
- if (unlink (dst_name) != 0)
- {
- error (0, errno, _("cannot remove %s"), quote (dst_name));
- return_val = false;
- goto close_src_desc;
- }
- if (x->verbose)
- printf (_("removed %s\n"), quote (dst_name));
-
- /* Tell caller that the destination file was unlinked. */
- *new_dst = true;
- }
+ {
+ if (unlink (dst_name) != 0)
+ {
+ error (0, errno, _("cannot remove %s"), quoteaf (dst_name));
+ return_val = false;
+ goto close_src_desc;
+ }
+ if (x->verbose)
+ printf (_("removed %s\n"), quoteaf (dst_name));
+
+ /* Tell caller that the destination file was unlinked. */
+ *new_dst = true;
+
+ /* Ensure there is no race where a file may be left without
+ an appropriate security context. */
+ if (x->set_security_context)
+ {
+ if (! set_process_security_ctx (src_name, dst_name, dst_mode,
+ *new_dst, x))
+ {
+ return_val = false;
+ goto close_src_desc;
+ }
+ }
+ }
}
if (*new_dst)
- dest_desc = open (dst_name, O_WRONLY | O_CREAT | O_EXCL | O_BINARY,
- dst_mode & ~omitted_permissions);
+ {
+ open_with_O_CREAT:;
+
+ int open_flags = O_WRONLY | O_CREAT | O_BINARY;
+ dest_desc = open (dst_name, open_flags | O_EXCL,
+ dst_mode & ~omitted_permissions);
+ dest_errno = errno;
+
+ /* When trying to copy through a dangling destination symlink,
+ the above open fails with EEXIST. If that happens, and
+ lstat'ing the DST_NAME shows that it is a symlink, then we
+ have a problem: trying to resolve this dangling symlink to
+ a directory/destination-entry pair is fundamentally racy,
+ so punt. If x->open_dangling_dest_symlink is set (cp sets
+ that when POSIXLY_CORRECT is set in the environment), simply
+ call open again, but without O_EXCL (potentially dangerous).
+ If not, fail with a diagnostic. These shenanigans are necessary
+ only when copying, i.e., not in move_mode. */
+ if (dest_desc < 0 && dest_errno == EEXIST && ! x->move_mode)
+ {
+ struct stat dangling_link_sb;
+ if (lstat (dst_name, &dangling_link_sb) == 0
+ && S_ISLNK (dangling_link_sb.st_mode))
+ {
+ if (x->open_dangling_dest_symlink)
+ {
+ dest_desc = open (dst_name, open_flags,
+ dst_mode & ~omitted_permissions);
+ dest_errno = errno;
+ }
+ else
+ {
+ error (0, 0, _("not writing through dangling symlink %s"),
+ quoteaf (dst_name));
+ return_val = false;
+ goto close_src_desc;
+ }
+ }
+ }
+
+ /* Improve quality of diagnostic when a nonexistent dst_name
+ ends in a slash and open fails with errno == EISDIR. */
+ if (dest_desc < 0 && dest_errno == EISDIR
+ && *dst_name && dst_name[strlen (dst_name) - 1] == '/')
+ dest_errno = ENOTDIR;
+ }
else
- omitted_permissions = 0;
+ {
+ omitted_permissions = 0;
+ }
if (dest_desc < 0)
{
- error (0, errno, _("cannot create regular file %s"), quote (dst_name));
+ /* If we've just failed due to ENOENT for an ostensibly preexisting
+ destination (*new_dst was 0), that's a bit of a contradiction/race:
+ the prior stat/lstat said the file existed (*new_dst was 0), yet
+ the subsequent open-existing-file failed with ENOENT. With NFS,
+ the race window is wider still, since its meta-data caching tends
+ to make the stat succeed for a just-removed remote file, while the
+ more-definitive initial open call will fail with ENOENT. When this
+ situation arises, we attempt to open again, but this time with
+ O_CREAT. Do this only when not in move-mode, since when handling
+ a cross-device move, we must never open an existing destination. */
+ if (dest_errno == ENOENT && ! *new_dst && ! x->move_mode)
+ {
+ *new_dst = 1;
+ goto open_with_O_CREAT;
+ }
+
+ /* Otherwise, it's an error. */
+ error (0, dest_errno, _("cannot create regular file %s"),
+ quoteaf (dst_name));
return_val = false;
goto close_src_desc;
}
if (fstat (dest_desc, &sb) != 0)
{
- error (0, errno, _("cannot fstat %s"), quote (dst_name));
+ error (0, errno, _("cannot fstat %s"), quoteaf (dst_name));
return_val = false;
goto close_src_and_dst_desc;
}
- if (! (S_ISREG (src_open_sb.st_mode) && src_open_sb.st_size == 0))
+ /* --attributes-only overrides --reflink. */
+ if (data_copy_required && x->reflink_mode)
{
- typedef uintptr_t word;
- off_t n_read_total = 0;
+ bool clone_ok = clone_file (dest_desc, source_desc) == 0;
+ if (clone_ok || x->reflink_mode == REFLINK_ALWAYS)
+ {
+ if (!clone_ok)
+ {
+ error (0, errno, _("failed to clone %s from %s"),
+ quoteaf_n (0, dst_name), quoteaf_n (1, src_name));
+ return_val = false;
+ goto close_src_and_dst_desc;
+ }
+ data_copy_required = false;
+ }
+ }
+ if (data_copy_required)
+ {
/* Choose a suitable buffer size; it may be adjusted later. */
- size_t buf_alignment = lcm (getpagesize (), sizeof (word));
- size_t buf_alignment_slop = sizeof (word) + buf_alignment - 1;
- size_t buf_size = ST_BLKSIZE (sb);
+ size_t buf_alignment = getpagesize ();
+ size_t buf_size = io_blksize (sb);
+ size_t hole_size = ST_BLKSIZE (sb);
+
+ fdadvise (source_desc, 0, 0, FADVISE_SEQUENTIAL);
/* Deal with sparse files. */
- bool last_write_made_hole = false;
bool make_holes = false;
+ bool sparse_src = is_probably_sparse (&src_open_sb);
if (S_ISREG (sb.st_mode))
- {
- /* Even with --sparse=always, try to create holes only
- if the destination is a regular file. */
- if (x->sparse_mode == SPARSE_ALWAYS)
- make_holes = true;
-
-#if HAVE_STRUCT_STAT_ST_BLOCKS
- /* Use a heuristic to determine whether SRC_NAME contains any sparse
- blocks. If the file has fewer blocks than would normally be
- needed for a file of its size, then at least one of the blocks in
- the file is a hole. */
- if (x->sparse_mode == SPARSE_AUTO && S_ISREG (src_open_sb.st_mode)
- && ST_NBLOCKS (src_open_sb) < src_open_sb.st_size / ST_NBLOCKSIZE)
- make_holes = true;
-#endif
- }
+ {
+ /* Even with --sparse=always, try to create holes only
+ if the destination is a regular file. */
+ if (x->sparse_mode == SPARSE_ALWAYS)
+ make_holes = true;
+
+ /* Use a heuristic to determine whether SRC_NAME contains any sparse
+ blocks. If the file has fewer blocks than would normally be
+ needed for a file of its size, then at least one of the blocks in
+ the file is a hole. */
+ if (x->sparse_mode == SPARSE_AUTO && sparse_src)
+ make_holes = true;
+ }
/* If not making a sparse file, try to use a more-efficient
- buffer size. */
+ buffer size. */
if (! make_holes)
- {
- /* These days there's no point ever messing with buffers smaller
- than 8 KiB. It would be nice to configure SMALL_BUF_SIZE
- dynamically for this host and pair of files, but there doesn't
- seem to be a good way to get readahead info portably. */
- enum { SMALL_BUF_SIZE = 8 * 1024 };
-
- /* Compute the least common multiple of the input and output
- buffer sizes, adjusting for outlandish values. */
- size_t blcm_max = MIN (SIZE_MAX, SSIZE_MAX) - buf_alignment_slop;
- size_t blcm = buffer_lcm (ST_BLKSIZE (src_open_sb), buf_size,
- blcm_max);
-
- /* Do not use a block size that is too small. */
- buf_size = MAX (SMALL_BUF_SIZE, blcm);
-
- /* Do not bother with a buffer larger than the input file, plus one
- byte to make sure the file has not grown while reading it. */
- if (S_ISREG (src_open_sb.st_mode) && src_open_sb.st_size < buf_size)
- buf_size = src_open_sb.st_size + 1;
-
- /* However, stick with a block size that is a positive multiple of
- blcm, overriding the above adjustments. Watch out for
- overflow. */
- buf_size += blcm - 1;
- buf_size -= buf_size % blcm;
- if (buf_size == 0 || blcm_max < buf_size)
- buf_size = blcm;
- }
-
- /* Make a buffer with space for a sentinel at the end. */
- buf_alloc = xmalloc (buf_size + buf_alignment_slop);
+ {
+ /* Compute the least common multiple of the input and output
+ buffer sizes, adjusting for outlandish values. */
+ size_t blcm_max = MIN (SIZE_MAX, SSIZE_MAX) - buf_alignment;
+ size_t blcm = buffer_lcm (io_blksize (src_open_sb), buf_size,
+ blcm_max);
+
+ /* Do not bother with a buffer larger than the input file, plus one
+ byte to make sure the file has not grown while reading it. */
+ if (S_ISREG (src_open_sb.st_mode) && src_open_sb.st_size < buf_size)
+ buf_size = src_open_sb.st_size + 1;
+
+ /* However, stick with a block size that is a positive multiple of
+ blcm, overriding the above adjustments. Watch out for
+ overflow. */
+ buf_size += blcm - 1;
+ buf_size -= buf_size % blcm;
+ if (buf_size == 0 || blcm_max < buf_size)
+ buf_size = blcm;
+ }
+
+ buf_alloc = xmalloc (buf_size + buf_alignment);
buf = ptr_align (buf_alloc, buf_alignment);
- for (;;)
- {
- word *wp = NULL;
-
- ssize_t n_read = read (source_desc, buf, buf_size);
- if (n_read < 0)
- {
-#ifdef EINTR
- if (errno == EINTR)
- continue;
-#endif
- error (0, errno, _("reading %s"), quote (src_name));
- return_val = false;
- goto close_src_and_dst_desc;
- }
- if (n_read == 0)
- break;
-
- n_read_total += n_read;
-
- if (make_holes)
- {
- char *cp;
-
- /* Sentinel to stop loop. */
- buf[n_read] = '\1';
-#ifdef lint
- /* Usually, buf[n_read] is not the byte just before a "word"
- (aka uintptr_t) boundary. In that case, the word-oriented
- test below (*wp++ == 0) would read some uninitialized bytes
- after the sentinel. To avoid false-positive reports about
- this condition (e.g., from a tool like valgrind), set the
- remaining bytes -- to any value. */
- memset (buf + n_read + 1, 0, sizeof (word) - 1);
-#endif
-
- /* Find first nonzero *word*, or the word with the sentinel. */
-
- wp = (word *) buf;
- while (*wp++ == 0)
- continue;
-
- /* Find the first nonzero *byte*, or the sentinel. */
-
- cp = (char *) (wp - 1);
- while (*cp++ == 0)
- continue;
-
- if (cp <= buf + n_read)
- /* Clear to indicate that a normal write is needed. */
- wp = NULL;
- else
- {
- /* We found the sentinel, so the whole input block was zero.
- Make a hole. */
- if (lseek (dest_desc, n_read, SEEK_CUR) < 0)
- {
- error (0, errno, _("cannot lseek %s"), quote (dst_name));
- return_val = false;
- goto close_src_and_dst_desc;
- }
- last_write_made_hole = true;
- }
- }
-
- if (!wp)
- {
- size_t n = n_read;
- if (full_write (dest_desc, buf, n) != n)
- {
- error (0, errno, _("writing %s"), quote (dst_name));
- return_val = false;
- goto close_src_and_dst_desc;
- }
- last_write_made_hole = false;
-
- /* A short read on a regular file means EOF. */
- if (n_read != buf_size && S_ISREG (src_open_sb.st_mode))
- break;
- }
- }
-
- /* If the file ends with a `hole', we need to do something to record
- the length of the file. On modern systems, calling ftruncate does
- the job. On systems without native ftruncate support, we have to
- write a byte at the ending position. Otherwise the kernel would
- truncate the file at the end of the last write operation. */
-
- if (last_write_made_hole)
- {
- if (HAVE_FTRUNCATE
- ? /* ftruncate sets the file size,
- so there is no need for a write. */
- ftruncate (dest_desc, n_read_total) < 0
- : /* Seek backwards one character and write a null. */
- (lseek (dest_desc, (off_t) -1, SEEK_CUR) < 0L
- || full_write (dest_desc, "", 1) != 1))
- {
- error (0, errno, _("writing %s"), quote (dst_name));
- return_val = false;
- goto close_src_and_dst_desc;
- }
- }
+ if (sparse_src)
+ {
+ bool normal_copy_required;
+
+ /* Perform an efficient extent-based copy, falling back to the
+ standard copy only if the initial extent scan fails. If the
+ '--sparse=never' option is specified, write all data but use
+ any extents to read more efficiently. */
+ if (extent_copy (source_desc, dest_desc, buf, buf_size, hole_size,
+ src_open_sb.st_size,
+ make_holes ? x->sparse_mode : SPARSE_NEVER,
+ src_name, dst_name, &normal_copy_required))
+ goto preserve_metadata;
+
+ if (! normal_copy_required)
+ {
+ return_val = false;
+ goto close_src_and_dst_desc;
+ }
+ }
+
+ off_t n_read;
+ bool wrote_hole_at_eof;
+ if (! sparse_copy (source_desc, dest_desc, buf, buf_size,
+ make_holes ? hole_size : 0,
+ x->sparse_mode == SPARSE_ALWAYS, src_name, dst_name,
+ UINTMAX_MAX, &n_read,
+ &wrote_hole_at_eof))
+ {
+ return_val = false;
+ goto close_src_and_dst_desc;
+ }
+ else if (wrote_hole_at_eof && ftruncate (dest_desc, n_read) < 0)
+ {
+ error (0, errno, _("failed to extend %s"), quoteaf (dst_name));
+ return_val = false;
+ goto close_src_and_dst_desc;
+ }
}
+preserve_metadata:
if (x->preserve_timestamps)
{
struct timespec timespec[2];
timespec[0] = get_stat_atime (src_sb);
timespec[1] = get_stat_mtime (src_sb);
- if (futimens (dest_desc, dst_name, timespec) != 0)
- {
- error (0, errno, _("preserving times for %s"), quote (dst_name));
- if (x->require_preserve)
- {
- return_val = false;
- goto close_src_and_dst_desc;
- }
- }
+ if (fdutimens (dest_desc, dst_name, timespec) != 0)
+ {
+ error (0, errno, _("preserving times for %s"), quoteaf (dst_name));
+ if (x->require_preserve)
+ {
+ return_val = false;
+ goto close_src_and_dst_desc;
+ }
+ }
}
+ /* Set ownership before xattrs as changing owners will
+ clear capabilities. */
if (x->preserve_ownership && ! SAME_OWNER_AND_GROUP (*src_sb, sb))
{
- switch (set_owner (x, dst_name, dest_desc,
- src_sb->st_uid, src_sb->st_gid))
- {
- case -1:
- return_val = false;
- goto close_src_and_dst_desc;
+ switch (set_owner (x, dst_name, dest_desc, src_sb, *new_dst, &sb))
+ {
+ case -1:
+ return_val = false;
+ goto close_src_and_dst_desc;
+
+ case 0:
+ src_mode &= ~ (S_ISUID | S_ISGID | S_ISVTX);
+ break;
+ }
+ }
+
+ /* To allow copying xattrs on read-only files, temporarily chmod u+rw.
+ This workaround is required as an inode permission check is done
+ by xattr_permission() in fs/xattr.c of the GNU/Linux kernel tree. */
+ if (x->preserve_xattr)
+ {
+ bool access_changed = false;
- case 0:
- src_mode &= ~ (S_ISUID | S_ISGID | S_ISVTX);
- break;
- }
+ if (!(sb.st_mode & S_IWUSR) && geteuid () != ROOT_UID)
+ access_changed = fchmod_or_lchmod (dest_desc, dst_name, 0600) == 0;
+
+ if (!copy_attr (src_name, source_desc, dst_name, dest_desc, x)
+ && x->require_preserve_xattr)
+ return_val = false;
+
+ if (access_changed)
+ fchmod_or_lchmod (dest_desc, dst_name, dst_mode & ~omitted_permissions);
}
set_author (dst_name, dest_desc, src_sb);
@@ -549,65 +1360,63 @@ copy_reg (char const *src_name, char const *dst_name,
if (x->preserve_mode || x->move_mode)
{
if (copy_acl (src_name, source_desc, dst_name, dest_desc, src_mode) != 0
- && x->require_preserve)
- return_val = false;
+ && x->require_preserve)
+ return_val = false;
}
else if (x->set_mode)
{
if (set_acl (dst_name, dest_desc, x->mode) != 0)
- return_val = false;
+ return_val = false;
+ }
+ else if (x->explicit_no_preserve_mode)
+ {
+ if (set_acl (dst_name, dest_desc, 0666 & ~cached_umask ()) != 0)
+ return_val = false;
}
else if (omitted_permissions)
{
omitted_permissions &= ~ cached_umask ();
if (omitted_permissions
- && fchmod_or_lchmod (dest_desc, dst_name, dst_mode) != 0)
- {
- error (0, errno, _("preserving permissions for %s"),
- quote (dst_name));
- if (x->require_preserve)
- return_val = false;
- }
+ && fchmod_or_lchmod (dest_desc, dst_name, dst_mode) != 0)
+ {
+ error (0, errno, _("preserving permissions for %s"),
+ quoteaf (dst_name));
+ if (x->require_preserve)
+ return_val = false;
+ }
}
close_src_and_dst_desc:
if (close (dest_desc) < 0)
{
- error (0, errno, _("closing %s"), quote (dst_name));
+ error (0, errno, _("failed to close %s"), quoteaf (dst_name));
return_val = false;
}
close_src_desc:
if (close (source_desc) < 0)
{
- error (0, errno, _("closing %s"), quote (src_name));
+ error (0, errno, _("failed to close %s"), quoteaf (src_name));
return_val = false;
}
free (buf_alloc);
+ free (name_alloc);
return return_val;
}
/* Return true if it's ok that the source and destination
- files are the `same' by some measure. The goal is to avoid
- making the `copy' operation remove both copies of the file
+ files are the 'same' by some measure. The goal is to avoid
+ making the 'copy' operation remove both copies of the file
in that case, while still allowing the user to e.g., move or
copy a regular file onto a symlink that points to it.
Try to minimize the cost of this function in the common case.
Set *RETURN_NOW if we've determined that the caller has no more
- work to do and should return successfully, right away.
-
- Set *UNLINK_SRC if we've determined that the caller wants to do
- `rename (a, b)' where `a' and `b' are distinct hard links to the same
- file. In that case, the caller should try to unlink `a' and then return
- successfully. Ideally, we wouldn't have to do that, and we'd be
- able to rely on rename to remove the source file. However, POSIX
- mistakenly requires that such a rename call do *nothing* and return
- successfully. */
+ work to do and should return successfully, right away. */
static bool
same_file_ok (char const *src_name, struct stat const *src_sb,
- char const *dst_name, struct stat const *dst_sb,
- const struct cp_options *x, bool *return_now, bool *unlink_src)
+ char const *dst_name, struct stat const *dst_sb,
+ const struct cp_options *x, bool *return_now)
{
const struct stat *src_sb_link;
const struct stat *dst_sb_link;
@@ -618,7 +1427,6 @@ same_file_ok (char const *src_name, struct stat const *src_sb,
bool same = SAME_INODE (*src_sb, *dst_sb);
*return_now = false;
- *unlink_src = false;
/* FIXME: this should (at the very least) be moved into the following
if-block. More likely, it should be removed, because it inhibits
@@ -636,10 +1444,30 @@ same_file_ok (char const *src_name, struct stat const *src_sb,
same_link = same;
/* If both the source and destination files are symlinks (and we'll
- know this here IFF preserving symlinks), then it's ok -- as long
- as they are distinct. */
+ know this here IFF preserving symlinks), then it's usually ok
+ when they are distinct. */
if (S_ISLNK (src_sb->st_mode) && S_ISLNK (dst_sb->st_mode))
- return ! same_name (src_name, dst_name);
+ {
+ bool sn = same_name (src_name, dst_name);
+ if ( ! sn)
+ {
+ /* It's fine when we're making any type of backup. */
+ if (x->backup_type != no_backups)
+ return true;
+
+ /* Here we have two symlinks that are hard-linked together,
+ and we're not making backups. In this unusual case, simply
+ returning true would lead to mv calling "rename(A,B)",
+ which would do nothing and return 0. */
+ if (same_link)
+ {
+ *return_now = true;
+ return ! x->move_mode;
+ }
+ }
+
+ return ! sn;
+ }
src_sb_link = src_sb;
dst_sb_link = dst_sb;
@@ -647,11 +1475,11 @@ same_file_ok (char const *src_name, struct stat const *src_sb,
else
{
if (!same)
- return true;
+ return true;
if (lstat (dst_name, &tmp_dst_sb) != 0
- || lstat (src_name, &tmp_src_sb) != 0)
- return true;
+ || lstat (src_name, &tmp_src_sb) != 0)
+ return true;
src_sb_link = &tmp_src_sb;
dst_sb_link = &tmp_dst_sb;
@@ -659,13 +1487,13 @@ same_file_ok (char const *src_name, struct stat const *src_sb,
same_link = SAME_INODE (*src_sb_link, *dst_sb_link);
/* If both are symlinks, then it's ok, but only if the destination
- will be unlinked before being opened. This is like the test
- above, but with the addition of the unlink_dest_before_opening
- conjunct because otherwise, with two symlinks to the same target,
- we'd end up truncating the source file. */
+ will be unlinked before being opened. This is like the test
+ above, but with the addition of the unlink_dest_before_opening
+ conjunct because otherwise, with two symlinks to the same target,
+ we'd end up truncating the source file. */
if (S_ISLNK (src_sb_link->st_mode) && S_ISLNK (dst_sb_link->st_mode)
- && x->unlink_dest_before_opening)
- return true;
+ && x->unlink_dest_before_opening)
+ return true;
}
/* The backup code ensures there's a copy, so it's usually ok to
@@ -676,32 +1504,33 @@ same_file_ok (char const *src_name, struct stat const *src_sb,
if (x->backup_type != no_backups)
{
if (!same_link)
- {
- /* In copy mode when dereferencing symlinks, if the source is a
- symlink and the dest is not, then backing up the destination
- (moving it aside) would make it a dangling symlink, and the
- subsequent attempt to open it in copy_reg would fail with
- a misleading diagnostic. Avoid that by returning zero in
- that case so the caller can make cp (or mv when it has to
- resort to reading the source file) fail now. */
-
- /* FIXME-note: even with the following kludge, we can still provoke
- the offending diagnostic. It's just a little harder to do :-)
- $ rm -f a b c; touch c; ln -s c b; ln -s b a; cp -b a b
- cp: cannot open `a' for reading: No such file or directory
- That's misleading, since a subsequent `ls' shows that `a'
- is still there.
- One solution would be to open the source file *before* moving
- aside the destination, but that'd involve a big rewrite. */
- if ( ! x->move_mode
- && x->dereference != DEREF_NEVER
- && S_ISLNK (src_sb_link->st_mode)
- && ! S_ISLNK (dst_sb_link->st_mode))
- return false;
-
- return true;
- }
-
+ {
+ /* In copy mode when dereferencing symlinks, if the source is a
+ symlink and the dest is not, then backing up the destination
+ (moving it aside) would make it a dangling symlink, and the
+ subsequent attempt to open it in copy_reg would fail with
+ a misleading diagnostic. Avoid that by returning zero in
+ that case so the caller can make cp (or mv when it has to
+ resort to reading the source file) fail now. */
+
+ /* FIXME-note: even with the following kludge, we can still provoke
+ the offending diagnostic. It's just a little harder to do :-)
+ $ rm -f a b c; touch c; ln -s c b; ln -s b a; cp -b a b
+ cp: cannot open 'a' for reading: No such file or directory
+ That's misleading, since a subsequent 'ls' shows that 'a'
+ is still there.
+ One solution would be to open the source file *before* moving
+ aside the destination, but that'd involve a big rewrite. */
+ if ( ! x->move_mode
+ && x->dereference != DEREF_NEVER
+ && S_ISLNK (src_sb_link->st_mode)
+ && ! S_ISLNK (dst_sb_link->st_mode))
+ return false;
+
+ return true;
+ }
+
+ /* FIXME: What about case insensitive file systems ? */
return ! same_name (src_name, dst_name);
}
@@ -722,27 +1551,21 @@ same_file_ok (char const *src_name, struct stat const *src_sb,
return true;
#endif
- /* They may refer to the same file if we're in move mode and the
- target is a symlink. That is ok, since we remove any existing
- destination file before opening it -- via `rename' if they're on
- the same file system, via `unlink (DST_NAME)' otherwise.
- It's also ok if they're distinct hard links to the same file. */
if (x->move_mode || x->unlink_dest_before_opening)
{
+ /* They may refer to the same file if we're in move mode and the
+ target is a symlink. That is ok, since we remove any existing
+ destination file before opening it -- via 'rename' if they're on
+ the same file system, via 'unlink (DST_NAME)' otherwise. */
if (S_ISLNK (dst_sb_link->st_mode))
- return true;
+ return true;
+ /* It's not ok if they're distinct hard links to the same file as
+ this causes a race condition and we may lose data in this case. */
if (same_link
- && 1 < dst_sb_link->st_nlink
- && ! same_name (src_name, dst_name))
- {
- if (x->move_mode)
- {
- *unlink_src = true;
- *return_now = true;
- }
- return true;
- }
+ && 1 < dst_sb_link->st_nlink
+ && ! same_name (src_name, dst_name))
+ return ! x->move_mode;
}
/* If neither is a symlink, then it's ok as long as they aren't
@@ -750,14 +1573,47 @@ same_file_ok (char const *src_name, struct stat const *src_sb,
if (!S_ISLNK (src_sb_link->st_mode) && !S_ISLNK (dst_sb_link->st_mode))
{
if (!SAME_INODE (*src_sb_link, *dst_sb_link))
- return true;
+ return true;
/* If they are the same file, it's ok if we're making hard links. */
if (x->hard_link)
- {
- *return_now = true;
- return true;
- }
+ {
+ *return_now = true;
+ return true;
+ }
+ }
+
+ /* At this point, it is normally an error (data loss) to move a symlink
+ onto its referent, but in at least one narrow case, it is not:
+ In move mode, when
+ 1) src is a symlink,
+ 2) dest has a link count of 2 or more and
+ 3) dest and the referent of src are not the same directory entry,
+ then it's ok, since while we'll lose one of those hard links,
+ src will still point to a remaining link.
+ Note that technically, condition #3 obviates condition #2, but we
+ retain the 1 < st_nlink condition because that means fewer invocations
+ of the more expensive #3.
+
+ Given this,
+ $ touch f && ln f l && ln -s f s
+ $ ls -og f l s
+ -rw-------. 2 0 Jan 4 22:46 f
+ -rw-------. 2 0 Jan 4 22:46 l
+ lrwxrwxrwx. 1 1 Jan 4 22:46 s -> f
+ this must fail: mv s f
+ this must succeed: mv s l */
+ if (x->move_mode
+ && S_ISLNK (src_sb->st_mode)
+ && 1 < dst_sb_link->st_nlink)
+ {
+ char *abs_src = canonicalize_file_name (src_name);
+ if (abs_src)
+ {
+ bool result = ! same_name (abs_src, dst_name);
+ free (abs_src);
+ return result;
+ }
}
/* It's ok to remove a destination symlink. But that works only when we
@@ -770,96 +1626,64 @@ same_file_ok (char const *src_name, struct stat const *src_sb,
if (x->dereference == DEREF_NEVER)
{
if ( ! S_ISLNK (src_sb_link->st_mode))
- tmp_src_sb = *src_sb_link;
+ tmp_src_sb = *src_sb_link;
else if (stat (src_name, &tmp_src_sb) != 0)
- return true;
+ return true;
if ( ! S_ISLNK (dst_sb_link->st_mode))
- tmp_dst_sb = *dst_sb_link;
+ tmp_dst_sb = *dst_sb_link;
else if (stat (dst_name, &tmp_dst_sb) != 0)
- return true;
+ return true;
if ( ! SAME_INODE (tmp_src_sb, tmp_dst_sb))
- return true;
+ return true;
/* FIXME: shouldn't this be testing whether we're making symlinks? */
if (x->hard_link)
- {
- *return_now = true;
- return true;
- }
+ {
+ *return_now = true;
+ return true;
+ }
}
return false;
}
-static void
-overwrite_prompt (char const *dst_name, struct stat const *dst_sb)
+/* Return true if FILE, with mode MODE, is writable in the sense of 'mv'.
+ Always consider a symbolic link to be writable. */
+static bool
+writable_destination (char const *file, mode_t mode)
+{
+ return (S_ISLNK (mode)
+ || can_write_any_file ()
+ || euidaccess (file, W_OK) == 0);
+}
+
+static bool
+overwrite_ok (struct cp_options const *x, char const *dst_name,
+ struct stat const *dst_sb)
{
- if (euidaccess (dst_name, W_OK) != 0)
+ if (! writable_destination (dst_name, dst_sb->st_mode))
{
char perms[12]; /* "-rwxrwxrwx " ls-style modes. */
strmode (dst_sb->st_mode, perms);
perms[10] = '\0';
fprintf (stderr,
- _("%s: try to overwrite %s, overriding mode %04lo (%s)? "),
- program_name, quote (dst_name),
- (unsigned long int) (dst_sb->st_mode & CHMOD_MODE_BITS),
- &perms[1]);
+ (x->move_mode || x->unlink_dest_before_opening
+ || x->unlink_dest_after_failed_open)
+ ? _("%s: replace %s, overriding mode %04lo (%s)? ")
+ : _("%s: unwritable %s (mode %04lo, %s); try anyway? "),
+ program_name, quoteaf (dst_name),
+ (unsigned long int) (dst_sb->st_mode & CHMOD_MODE_BITS),
+ &perms[1]);
}
else
{
fprintf (stderr, _("%s: overwrite %s? "),
- program_name, quote (dst_name));
+ program_name, quoteaf (dst_name));
}
-}
-
-/* Hash an F_triple. */
-static size_t
-triple_hash (void const *x, size_t table_size)
-{
- struct F_triple const *p = x;
-
- /* Also take the name into account, so that when moving N hard links to the
- same file (all listed on the command line) all into the same directory,
- we don't experience any N^2 behavior. */
- /* FIXME-maybe: is it worth the overhead of doing this
- just to avoid N^2 in such an unusual case? N would have
- to be very large to make the N^2 factor noticable, and
- one would probably encounter a limit on the length of
- a command line before it became a problem. */
- size_t tmp = hash_pjw (p->name, table_size);
-
- /* Ignoring the device number here should be fine. */
- return (tmp | p->st_ino) % table_size;
-}
-
-/* Hash an F_triple. */
-static size_t
-triple_hash_no_name (void const *x, size_t table_size)
-{
- struct F_triple const *p = x;
-
- /* Ignoring the device number here should be fine. */
- return p->st_ino % table_size;
-}
-/* Compare two F_triple structs. */
-static bool
-triple_compare (void const *x, void const *y)
-{
- struct F_triple const *a = x;
- struct F_triple const *b = y;
- return (SAME_INODE (*a, *b) && same_name (a->name, b->name)) ? true : false;
-}
-
-/* Free an F_triple. */
-static void
-triple_free (void *x)
-{
- struct F_triple *a = x;
- free (a->name);
- free (a);
+ return yesno ();
}
/* Initialize the hash table implementing a set of F_triple entries
@@ -869,10 +1693,10 @@ dest_info_init (struct cp_options *x)
{
x->dest_info
= hash_initialize (DEST_INFO_INITIAL_CAPACITY,
- NULL,
- triple_hash,
- triple_compare,
- triple_free);
+ NULL,
+ triple_hash,
+ triple_compare,
+ triple_free);
}
/* Initialize the hash table implementing a set of F_triple entries
@@ -891,80 +1715,15 @@ src_info_init (struct cp_options *x)
*/
x->src_info
= hash_initialize (DEST_INFO_INITIAL_CAPACITY,
- NULL,
- triple_hash_no_name,
- triple_compare,
- triple_free);
-}
-
-/* Return true if there is an entry in hash table, HT,
- for the file described by FILE and STATS. */
-static bool
-seen_file (Hash_table const *ht, char const *file,
- struct stat const *stats)
-{
- struct F_triple new_ent;
-
- if (ht == NULL)
- return false;
-
- new_ent.name = (char *) file;
- new_ent.st_ino = stats->st_ino;
- new_ent.st_dev = stats->st_dev;
-
- return !!hash_lookup (ht, &new_ent);
-}
-
-/* Record destination file, FILE, and dev/ino from *STATS,
- in the hash table, HT. If HT is NULL, return immediately.
- If STATS is NULL, call lstat on FILE to get the device
- and inode numbers. If that lstat fails, simply return.
- If memory allocation fails, exit immediately. */
-static void
-record_file (Hash_table *ht, char const *file,
- struct stat const *stats)
-{
- struct F_triple *ent;
-
- if (ht == NULL)
- return;
-
- ent = xmalloc (sizeof *ent);
- ent->name = xstrdup (file);
- if (stats)
- {
- ent->st_ino = stats->st_ino;
- ent->st_dev = stats->st_dev;
- }
- else
- {
- struct stat sb;
- if (lstat (file, &sb) != 0)
- return;
- ent->st_ino = sb.st_ino;
- ent->st_dev = sb.st_dev;
- }
-
- {
- struct F_triple *ent_from_table = hash_insert (ht, ent);
- if (ent_from_table == NULL)
- {
- /* Insertion failed due to lack of memory. */
- xalloc_die ();
- }
-
- if (ent_from_table != ent)
- {
- /* There was alread a matching entry in the table, so ENT was
- not inserted. Free it. */
- triple_free (ent);
- }
- }
+ NULL,
+ triple_hash_no_name,
+ triple_compare,
+ triple_free);
}
/* When effecting a move (e.g., for mv(1)), and given the name DST_NAME
of the destination and a corresponding stat buffer, DST_SB, return
- true if the logical `move' operation should _not_ proceed.
+ true if the logical 'move' operation should _not_ proceed.
Otherwise, return false.
Depending on options specified in X, this code may issue an
interactive prompt asking whether it's ok to overwrite DST_NAME. */
@@ -978,48 +1737,111 @@ abandon_move (const struct cp_options *x,
|| ((x->interactive == I_ASK_USER
|| (x->interactive == I_UNSPECIFIED
&& x->stdin_tty
- && UNWRITABLE (dst_name, dst_sb->st_mode)))
- && (overwrite_prompt (dst_name, dst_sb), 1)
- && ! yesno ()));
+ && ! writable_destination (dst_name, dst_sb->st_mode)))
+ && ! overwrite_ok (x, dst_name, dst_sb)));
}
-/* Print --verbose output on standard output, e.g. `new' -> `old'.
+/* Print --verbose output on standard output, e.g. 'new' -> 'old'.
If BACKUP_DST_NAME is non-NULL, then also indicate that it is
the name of a backup file. */
static void
emit_verbose (char const *src, char const *dst, char const *backup_dst_name)
{
- printf ("%s -> %s", quote_n (0, src), quote_n (1, dst));
+ printf ("%s -> %s", quoteaf_n (0, src), quoteaf_n (1, dst));
if (backup_dst_name)
- printf (_(" (backup: %s)"), quote (backup_dst_name));
+ printf (_(" (backup: %s)"), quoteaf (backup_dst_name));
putchar ('\n');
}
+/* A wrapper around "setfscreatecon (NULL)" that exits upon failure. */
+static void
+restore_default_fscreatecon_or_die (void)
+{
+ if (setfscreatecon (NULL) != 0)
+ error (EXIT_FAILURE, errno,
+ _("failed to restore the default file creation context"));
+}
+
+/* Create a hard link DST_NAME to SRC_NAME, honoring the REPLACE, VERBOSE and
+ DEREFERENCE settings. Return true upon success. Otherwise, diagnose the
+ failure and return false. If SRC_NAME is a symbolic link, then it will not
+ be followed unless DEREFERENCE is true.
+ If the system doesn't support hard links to symbolic links, then DST_NAME
+ will be created as a symbolic link to SRC_NAME. */
+static bool
+create_hard_link (char const *src_name, char const *dst_name,
+ bool replace, bool verbose, bool dereference)
+{
+ /* We want to guarantee that symlinks are not followed, unless requested. */
+ int flags = 0;
+ if (dereference)
+ flags = AT_SYMLINK_FOLLOW;
+
+ bool link_failed = (linkat (AT_FDCWD, src_name, AT_FDCWD, dst_name, flags)
+ != 0);
+
+ /* If the link failed because of an existing destination,
+ remove that file and then call link again. */
+ if (link_failed && replace && errno == EEXIST)
+ {
+ if (unlink (dst_name) != 0)
+ {
+ error (0, errno, _("cannot remove %s"), quoteaf (dst_name));
+ return false;
+ }
+ if (verbose)
+ printf (_("removed %s\n"), quoteaf (dst_name));
+ link_failed = (linkat (AT_FDCWD, src_name, AT_FDCWD, dst_name, flags)
+ != 0);
+ }
+
+ if (link_failed)
+ {
+ error (0, errno, _("cannot create hard link %s to %s"),
+ quoteaf_n (0, dst_name), quoteaf_n (1, src_name));
+ return false;
+ }
+
+ return true;
+}
+
+/* Return true if the current file should be (tried to be) dereferenced:
+ either for DEREF_ALWAYS or for DEREF_COMMAND_LINE_ARGUMENTS in the case
+ where the current file is a COMMAND_LINE_ARG; otherwise return false. */
+static inline bool _GL_ATTRIBUTE_PURE
+should_dereference (const struct cp_options *x, bool command_line_arg)
+{
+ return x->dereference == DEREF_ALWAYS
+ || (x->dereference == DEREF_COMMAND_LINE_ARGUMENTS
+ && command_line_arg);
+}
+
/* Copy the file SRC_NAME to the file DST_NAME. The files may be of
any type. NEW_DST should be true if the file DST_NAME cannot
exist because its parent directory was just created; NEW_DST should
- be false if DST_NAME might already exist. DEVICE is the device
- number of the parent directory, or 0 if the parent of this file is
- not known. ANCESTORS points to a linked, null terminated list of
+ be false if DST_NAME might already exist. A non-null PARENT describes the
+ parent directory. ANCESTORS points to a linked, null terminated list of
devices and inodes of parent directories of SRC_NAME. COMMAND_LINE_ARG
is true iff SRC_NAME was specified on the command line.
+ FIRST_DIR_CREATED_PER_COMMAND_LINE_ARG is both input and output.
Set *COPY_INTO_SELF if SRC_NAME is a parent of (or the
same as) DST_NAME; otherwise, clear it.
Return true if successful. */
static bool
copy_internal (char const *src_name, char const *dst_name,
- bool new_dst,
- dev_t device,
- struct dir_list *ancestors,
- const struct cp_options *x,
- bool command_line_arg,
- bool *copy_into_self,
- bool *rename_succeeded)
+ bool new_dst,
+ struct stat const *parent,
+ struct dir_list *ancestors,
+ const struct cp_options *x,
+ bool command_line_arg,
+ bool *first_dir_created_per_command_line_arg,
+ bool *copy_into_self,
+ bool *rename_succeeded)
{
struct stat src_sb;
struct stat dst_sb;
mode_t src_mode;
- mode_t dst_mode IF_LINT (= 0);
+ mode_t dst_mode IF_LINT ( = 0);
mode_t dst_mode_bits;
mode_t omitted_permissions;
bool restore_dst_mode = false;
@@ -1028,7 +1850,8 @@ copy_internal (char const *src_name, char const *dst_name,
bool backup_succeeded = false;
bool delayed_ok;
bool copied_as_regular = false;
- bool preserve_metadata;
+ bool dest_is_symlink = false;
+ bool have_dst_lstat = false;
if (x->move_mode && rename_succeeded)
*rename_succeeded = false;
@@ -1037,7 +1860,7 @@ copy_internal (char const *src_name, char const *dst_name,
if (XSTAT (x, src_name, &src_sb) != 0)
{
- error (0, errno, _("cannot stat %s"), quote (src_name));
+ error (0, errno, _("cannot stat %s"), quoteaf (src_name));
return false;
}
@@ -1045,7 +1868,7 @@ copy_internal (char const *src_name, char const *dst_name,
if (S_ISDIR (src_mode) && !x->recursive)
{
- error (0, 0, _("omitting directory %s"), quote (src_name));
+ error (0, 0, _("omitting directory %s"), quoteaf (src_name));
return false;
}
@@ -1056,252 +1879,321 @@ copy_internal (char const *src_name, char const *dst_name,
if (command_line_arg)
{
if ( ! S_ISDIR (src_sb.st_mode)
- && x->backup_type == no_backups
- && seen_file (x->src_info, src_name, &src_sb))
- {
- error (0, 0, _("warning: source file %s specified more than once"),
- quote (src_name));
- return true;
- }
+ && x->backup_type == no_backups
+ && seen_file (x->src_info, src_name, &src_sb))
+ {
+ error (0, 0, _("warning: source file %s specified more than once"),
+ quoteaf (src_name));
+ return true;
+ }
record_file (x->src_info, src_name, &src_sb);
}
+ bool dereference = should_dereference (x, command_line_arg);
+
if (!new_dst)
{
- if (XSTAT (x, dst_name, &dst_sb) != 0)
- {
- if (errno != ENOENT)
- {
- error (0, errno, _("cannot stat %s"), quote (dst_name));
- return false;
- }
- else
- {
- new_dst = true;
- }
- }
+ /* Regular files can be created by writing through symbolic
+ links, but other files cannot. So use stat on the
+ destination when copying a regular file, and lstat otherwise.
+ However, if we intend to unlink or remove the destination
+ first, use lstat, since a copy won't actually be made to the
+ destination in that case. */
+ bool use_stat =
+ ((S_ISREG (src_mode)
+ || (x->copy_as_regular
+ && ! (S_ISDIR (src_mode) || S_ISLNK (src_mode))))
+ && ! (x->move_mode || x->symbolic_link || x->hard_link
+ || x->backup_type != no_backups
+ || x->unlink_dest_before_opening));
+ if ((use_stat
+ ? stat (dst_name, &dst_sb)
+ : lstat (dst_name, &dst_sb))
+ != 0)
+ {
+ if (errno != ENOENT)
+ {
+ error (0, errno, _("cannot stat %s"), quoteaf (dst_name));
+ return false;
+ }
+ else
+ {
+ new_dst = true;
+ }
+ }
else
- { /* Here, we know that dst_name exists, at least to the point
- that it is XSTAT'able. */
- bool return_now;
- bool unlink_src;
-
- if (! same_file_ok (src_name, &src_sb, dst_name, &dst_sb,
- x, &return_now, &unlink_src))
- {
- error (0, 0, _("%s and %s are the same file"),
- quote_n (0, src_name), quote_n (1, dst_name));
- return false;
- }
-
- /* When there is an existing destination file, we may end up
- returning early, and hence not copying/moving the file.
- This may be due to an interactive `negative' reply to the
- prompt about the existing file. It may also be due to the
- use of the --reply=no option.
-
- cp and mv treat -i and -f differently. */
- if (x->move_mode)
- {
- if (abandon_move (x, dst_name, &dst_sb)
- || (unlink_src && unlink (src_name) == 0))
- {
- /* Pretend the rename succeeded, so the caller (mv)
- doesn't end up removing the source file. */
- if (rename_succeeded)
- *rename_succeeded = true;
- if (unlink_src && x->verbose)
- printf (_("removed %s\n"), quote (src_name));
- return true;
- }
- if (unlink_src)
- {
- error (0, errno, _("cannot remove %s"), quote (src_name));
- return false;
- }
- }
- else
- {
- if (! S_ISDIR (src_mode)
- && (x->interactive == I_ALWAYS_NO
- || (x->interactive == I_ASK_USER
- && (overwrite_prompt (dst_name, &dst_sb), 1)
- && ! yesno ())))
- return true;
- }
-
- if (return_now)
- return true;
-
- if (!S_ISDIR (dst_sb.st_mode))
- {
- if (S_ISDIR (src_mode))
- {
- if (x->move_mode && x->backup_type != no_backups)
- {
- /* Moving a directory onto an existing
- non-directory is ok only with --backup. */
- }
- else
- {
- error (0, 0,
- _("cannot overwrite non-directory %s with directory %s"),
- quote_n (0, dst_name), quote_n (1, src_name));
- return false;
- }
- }
-
- /* Don't let the user destroy their data, even if they try hard:
- This mv command must fail (likewise for cp):
- rm -rf a b c; mkdir a b c; touch a/f b/f; mv a/f b/f c
- Otherwise, the contents of b/f would be lost.
- In the case of `cp', b/f would be lost if the user simulated
- a move using cp and rm.
- Note that it works fine if you use --backup=numbered. */
- if (command_line_arg
- && x->backup_type != numbered_backups
- && seen_file (x->dest_info, dst_name, &dst_sb))
- {
- error (0, 0,
- _("will not overwrite just-created %s with %s"),
- quote_n (0, dst_name), quote_n (1, src_name));
- return false;
- }
- }
-
- if (!S_ISDIR (src_mode))
- {
- if (S_ISDIR (dst_sb.st_mode))
- {
- if (x->move_mode && x->backup_type != no_backups)
- {
- /* Moving a non-directory onto an existing
- directory is ok only with --backup. */
- }
- else
- {
- error (0, 0,
- _("cannot overwrite directory %s with non-directory"),
- quote (dst_name));
- return false;
- }
- }
-
- if (x->update)
- {
- /* When preserving time stamps (but not moving within a file
- system), don't worry if the destination time stamp is
- less than the source merely because of time stamp
- truncation. */
- int options = ((x->preserve_timestamps
- && ! (x->move_mode
- && dst_sb.st_dev == src_sb.st_dev))
- ? UTIMECMP_TRUNCATE_SOURCE
- : 0);
-
- if (0 <= utimecmp (dst_name, &dst_sb, &src_sb, options))
- {
- /* We're using --update and the destination is not older
- than the source, so do not copy or move. Pretend the
- rename succeeded, so the caller (if it's mv) doesn't
- end up removing the source file. */
- if (rename_succeeded)
- *rename_succeeded = true;
- return true;
- }
- }
- }
-
- if (x->move_mode)
- {
- /* Don't allow user to move a directory onto a non-directory. */
- if (S_ISDIR (src_sb.st_mode) && !S_ISDIR (dst_sb.st_mode)
- && x->backup_type == no_backups)
- {
- error (0, 0,
- _("cannot move directory onto non-directory: %s -> %s"),
- quote_n (0, src_name), quote_n (0, dst_name));
- return false;
- }
- }
-
- if (x->backup_type != no_backups
- /* Don't try to back up a destination if the last
- component of src_name is "." or "..". */
- && ! dot_or_dotdot (last_component (src_name))
- /* Create a backup of each destination directory in move mode,
- but not in copy mode. FIXME: it might make sense to add an
- option to suppress backup creation also for move mode.
- That would let one use mv to merge new content into an
- existing hierarchy. */
- && (x->move_mode || ! S_ISDIR (dst_sb.st_mode)))
- {
- char *tmp_backup = find_backup_file_name (dst_name,
- x->backup_type);
-
- /* Detect (and fail) when creating the backup file would
- destroy the source file. Before, running the commands
- cd /tmp; rm -f a a~; : > a; echo A > a~; cp --b=simple a~ a
- would leave two zero-length files: a and a~. */
- /* FIXME: but simply change e.g., the final a~ to `./a~'
- and the source will still be destroyed. */
- if (STREQ (tmp_backup, src_name))
- {
- const char *fmt;
- fmt = (x->move_mode
- ? _("backing up %s would destroy source; %s not moved")
- : _("backing up %s would destroy source; %s not copied"));
- error (0, 0, fmt,
- quote_n (0, dst_name),
- quote_n (1, src_name));
- free (tmp_backup);
- return false;
- }
-
- /* FIXME: use fts:
- Using alloca for a file name that may be arbitrarily
- long is not recommended. In fact, even forming such a name
- should be discouraged. Eventually, this code will be rewritten
- to use fts, so using alloca here will be less of a problem. */
- ASSIGN_STRDUPA (dst_backup, tmp_backup);
- free (tmp_backup);
- if (rename (dst_name, dst_backup) != 0)
- {
- if (errno != ENOENT)
- {
- error (0, errno, _("cannot backup %s"), quote (dst_name));
- return false;
- }
- else
- {
- dst_backup = NULL;
- }
- }
- else
- {
- backup_succeeded = true;
- }
- new_dst = true;
- }
- else if (! S_ISDIR (dst_sb.st_mode)
- && (x->unlink_dest_before_opening
- || (x->preserve_links && 1 < dst_sb.st_nlink)
- || (!x->move_mode
- && x->dereference == DEREF_NEVER
- && S_ISLNK (src_sb.st_mode))
- ))
- {
- if (unlink (dst_name) != 0 && errno != ENOENT)
- {
- error (0, errno, _("cannot remove %s"), quote (dst_name));
- return false;
- }
- new_dst = true;
- if (x->verbose)
- printf (_("removed %s\n"), quote (dst_name));
- }
- }
+ { /* Here, we know that dst_name exists, at least to the point
+ that it is stat'able or lstat'able. */
+ bool return_now;
+
+ have_dst_lstat = !use_stat;
+ if (! same_file_ok (src_name, &src_sb, dst_name, &dst_sb,
+ x, &return_now))
+ {
+ error (0, 0, _("%s and %s are the same file"),
+ quoteaf_n (0, src_name), quoteaf_n (1, dst_name));
+ return false;
+ }
+
+ if (!S_ISDIR (src_mode) && x->update)
+ {
+ /* When preserving time stamps (but not moving within a file
+ system), don't worry if the destination time stamp is
+ less than the source merely because of time stamp
+ truncation. */
+ int options = ((x->preserve_timestamps
+ && ! (x->move_mode
+ && dst_sb.st_dev == src_sb.st_dev))
+ ? UTIMECMP_TRUNCATE_SOURCE
+ : 0);
+
+ if (0 <= utimecmp (dst_name, &dst_sb, &src_sb, options))
+ {
+ /* We're using --update and the destination is not older
+ than the source, so do not copy or move. Pretend the
+ rename succeeded, so the caller (if it's mv) doesn't
+ end up removing the source file. */
+ if (rename_succeeded)
+ *rename_succeeded = true;
+
+ /* However, we still must record that we've processed
+ this src/dest pair, in case this source file is
+ hard-linked to another one. In that case, we'll use
+ the mapping information to link the corresponding
+ destination names. */
+ earlier_file = remember_copied (dst_name, src_sb.st_ino,
+ src_sb.st_dev);
+ if (earlier_file)
+ {
+ /* Note we currently replace DST_NAME unconditionally,
+ even if it was a newer separate file. */
+ if (! create_hard_link (earlier_file, dst_name, true,
+ x->verbose, dereference))
+ {
+ goto un_backup;
+ }
+ }
+
+ return true;
+ }
+ }
+
+ /* When there is an existing destination file, we may end up
+ returning early, and hence not copying/moving the file.
+ This may be due to an interactive 'negative' reply to the
+ prompt about the existing file. It may also be due to the
+ use of the --no-clobber option.
+
+ cp and mv treat -i and -f differently. */
+ if (x->move_mode)
+ {
+ if (abandon_move (x, dst_name, &dst_sb))
+ {
+ /* Pretend the rename succeeded, so the caller (mv)
+ doesn't end up removing the source file. */
+ if (rename_succeeded)
+ *rename_succeeded = true;
+ return true;
+ }
+ }
+ else
+ {
+ if (! S_ISDIR (src_mode)
+ && (x->interactive == I_ALWAYS_NO
+ || (x->interactive == I_ASK_USER
+ && ! overwrite_ok (x, dst_name, &dst_sb))))
+ return true;
+ }
+
+ if (return_now)
+ return true;
+
+ if (!S_ISDIR (dst_sb.st_mode))
+ {
+ if (S_ISDIR (src_mode))
+ {
+ if (x->move_mode && x->backup_type != no_backups)
+ {
+ /* Moving a directory onto an existing
+ non-directory is ok only with --backup. */
+ }
+ else
+ {
+ error (0, 0,
+ _("cannot overwrite non-directory %s with directory %s"),
+ quoteaf_n (0, dst_name), quoteaf_n (1, src_name));
+ return false;
+ }
+ }
+
+ /* Don't let the user destroy their data, even if they try hard:
+ This mv command must fail (likewise for cp):
+ rm -rf a b c; mkdir a b c; touch a/f b/f; mv a/f b/f c
+ Otherwise, the contents of b/f would be lost.
+ In the case of 'cp', b/f would be lost if the user simulated
+ a move using cp and rm.
+ Note that it works fine if you use --backup=numbered. */
+ if (command_line_arg
+ && x->backup_type != numbered_backups
+ && seen_file (x->dest_info, dst_name, &dst_sb))
+ {
+ error (0, 0,
+ _("will not overwrite just-created %s with %s"),
+ quoteaf_n (0, dst_name), quoteaf_n (1, src_name));
+ return false;
+ }
+ }
+
+ if (!S_ISDIR (src_mode))
+ {
+ if (S_ISDIR (dst_sb.st_mode))
+ {
+ if (x->move_mode && x->backup_type != no_backups)
+ {
+ /* Moving a non-directory onto an existing
+ directory is ok only with --backup. */
+ }
+ else
+ {
+ error (0, 0,
+ _("cannot overwrite directory %s with non-directory"),
+ quoteaf (dst_name));
+ return false;
+ }
+ }
+ }
+
+ if (x->move_mode)
+ {
+ /* Don't allow user to move a directory onto a non-directory. */
+ if (S_ISDIR (src_sb.st_mode) && !S_ISDIR (dst_sb.st_mode)
+ && x->backup_type == no_backups)
+ {
+ error (0, 0,
+ _("cannot move directory onto non-directory: %s -> %s"),
+ quotef_n (0, src_name), quotef_n (0, dst_name));
+ return false;
+ }
+ }
+
+ if (x->backup_type != no_backups
+ /* Don't try to back up a destination if the last
+ component of src_name is "." or "..". */
+ && ! dot_or_dotdot (last_component (src_name))
+ /* Create a backup of each destination directory in move mode,
+ but not in copy mode. FIXME: it might make sense to add an
+ option to suppress backup creation also for move mode.
+ That would let one use mv to merge new content into an
+ existing hierarchy. */
+ && (x->move_mode || ! S_ISDIR (dst_sb.st_mode)))
+ {
+ char *tmp_backup = find_backup_file_name (dst_name,
+ x->backup_type);
+
+ /* Detect (and fail) when creating the backup file would
+ destroy the source file. Before, running the commands
+ cd /tmp; rm -f a a~; : > a; echo A > a~; cp --b=simple a~ a
+ would leave two zero-length files: a and a~. */
+ /* FIXME: but simply change e.g., the final a~ to './a~'
+ and the source will still be destroyed. */
+ if (STREQ (tmp_backup, src_name))
+ {
+ const char *fmt;
+ fmt = (x->move_mode
+ ? _("backing up %s would destroy source; %s not moved")
+ : _("backing up %s would destroy source; %s not copied"));
+ error (0, 0, fmt,
+ quoteaf_n (0, dst_name),
+ quoteaf_n (1, src_name));
+ free (tmp_backup);
+ return false;
+ }
+
+ /* FIXME: use fts:
+ Using alloca for a file name that may be arbitrarily
+ long is not recommended. In fact, even forming such a name
+ should be discouraged. Eventually, this code will be rewritten
+ to use fts, so using alloca here will be less of a problem. */
+ ASSIGN_STRDUPA (dst_backup, tmp_backup);
+ free (tmp_backup);
+ /* In move mode, when src_name and dst_name are on the
+ same partition (FIXME, and when they are non-directories),
+ make the operation atomic: link dest
+ to backup, then rename src to dest. */
+ if (rename (dst_name, dst_backup) != 0)
+ {
+ if (errno != ENOENT)
+ {
+ error (0, errno, _("cannot backup %s"),
+ quoteaf (dst_name));
+ return false;
+ }
+ else
+ {
+ dst_backup = NULL;
+ }
+ }
+ else
+ {
+ backup_succeeded = true;
+ }
+ new_dst = true;
+ }
+ else if (! S_ISDIR (dst_sb.st_mode)
+ /* Never unlink dst_name when in move mode. */
+ && ! x->move_mode
+ && (x->unlink_dest_before_opening
+ || (x->preserve_links && 1 < dst_sb.st_nlink)
+ || (x->dereference == DEREF_NEVER
+ && ! S_ISREG (src_sb.st_mode))
+ ))
+ {
+ if (unlink (dst_name) != 0 && errno != ENOENT)
+ {
+ error (0, errno, _("cannot remove %s"), quoteaf (dst_name));
+ return false;
+ }
+ new_dst = true;
+ if (x->verbose)
+ printf (_("removed %s\n"), quoteaf (dst_name));
+ }
+ }
+ }
+
+ /* Ensure we don't try to copy through a symlink that was
+ created by a prior call to this function. */
+ if (command_line_arg
+ && x->dest_info
+ && ! x->move_mode
+ && x->backup_type == no_backups)
+ {
+ bool lstat_ok = true;
+ struct stat tmp_buf;
+ struct stat *dst_lstat_sb;
+
+ /* If we called lstat above, good: use that data.
+ Otherwise, call lstat here, in case dst_name is a symlink. */
+ if (have_dst_lstat)
+ dst_lstat_sb = &dst_sb;
+ else
+ {
+ if (lstat (dst_name, &tmp_buf) == 0)
+ dst_lstat_sb = &tmp_buf;
+ else
+ lstat_ok = false;
+ }
+
+ /* Never copy through a symlink we've just created. */
+ if (lstat_ok
+ && S_ISLNK (dst_lstat_sb->st_mode)
+ && seen_file (x->dest_info, dst_name, dst_lstat_sb))
+ {
+ error (0, 0,
+ _("will not copy %s through just-created symlink %s"),
+ quoteaf_n (0, src_name), quoteaf_n (1, dst_name));
+ return false;
+ }
}
/* If the source is a directory, we don't always create the destination
@@ -1315,42 +2207,48 @@ copy_internal (char const *src_name, char const *dst_name,
we can arrange to create a hard link between the corresponding names
in the destination tree.
+ When using the --link (-l) option, there is no need to take special
+ measures, because (barring race conditions) files that are hard-linked
+ in the source tree will also be hard-linked in the destination tree.
+
Sometimes, when preserving links, we have to record dev/ino even
though st_nlink == 1:
- when in move_mode, since we may be moving a group of N hard-linked
- files (via two or more command line arguments) to a different
- partition; the links may be distributed among the command line
- arguments (possibly hierarchies) so that the link count of
- the final, once-linked source file is reduced to 1 when it is
- considered below. But in this case (for mv) we don't need to
- incur the expense of recording the dev/ino => name mapping; all we
- really need is a lookup, to see if the dev/ino pair has already
- been copied.
+ files (via two or more command line arguments) to a different
+ partition; the links may be distributed among the command line
+ arguments (possibly hierarchies) so that the link count of
+ the final, once-linked source file is reduced to 1 when it is
+ considered below. But in this case (for mv) we don't need to
+ incur the expense of recording the dev/ino => name mapping; all we
+ really need is a lookup, to see if the dev/ino pair has already
+ been copied.
- when using -H and processing a command line argument;
- that command line argument could be a symlink pointing to another
- command line argument. With `cp -H --preserve=link', we hard-link
- those two destination files.
+ that command line argument could be a symlink pointing to another
+ command line argument. With 'cp -H --preserve=link', we hard-link
+ those two destination files.
- likewise for -L except that it applies to all files, not just
- command line arguments.
+ command line arguments.
- Also record directory dev/ino when using --recursive. We'll use that
- info to detect this problem: cp -R dir dir. FIXME-maybe: ideally,
- directory info would be recorded in a separate hash table, since
- such entries are useful only while a single command line hierarchy
- is being copied -- so that separate table could be cleared between
- command line args. Using the same hash table to preserve hard
- links means that it may not be cleared. */
+ Also, with --recursive, record dev/ino of each command-line directory.
+ We'll use that info to detect this problem: cp -R dir dir. */
- if (x->move_mode && src_sb.st_nlink == 1)
+ if (x->recursive && S_ISDIR (src_mode))
{
- earlier_file = src_to_dest_lookup (src_sb.st_ino, src_sb.st_dev);
+ if (command_line_arg)
+ earlier_file = remember_copied (dst_name, src_sb.st_ino, src_sb.st_dev);
+ else
+ earlier_file = src_to_dest_lookup (src_sb.st_ino, src_sb.st_dev);
}
- else if ((x->preserve_links
- && (1 < src_sb.st_nlink
- || (command_line_arg
- && x->dereference == DEREF_COMMAND_LINE_ARGUMENTS)
- || x->dereference == DEREF_ALWAYS))
- || (x->recursive && S_ISDIR (src_mode)))
+ else if (x->move_mode && src_sb.st_nlink == 1)
+ {
+ earlier_file = src_to_dest_lookup (src_sb.st_ino, src_sb.st_dev);
+ }
+ else if (x->preserve_links
+ && !x->hard_link
+ && (1 < src_sb.st_nlink
+ || (command_line_arg
+ && x->dereference == DEREF_COMMAND_LINE_ARGUMENTS)
+ || x->dereference == DEREF_ALWAYS))
{
earlier_file = remember_copied (dst_name, src_sb.st_ino, src_sb.st_dev);
}
@@ -1361,161 +2259,169 @@ copy_internal (char const *src_name, char const *dst_name,
if (earlier_file)
{
/* Avoid damaging the destination file system by refusing to preserve
- hard-linked directories (which are found at least in Netapp snapshot
- directories). */
+ hard-linked directories (which are found at least in Netapp snapshot
+ directories). */
if (S_ISDIR (src_mode))
- {
- /* If src_name and earlier_file refer to the same directory entry,
- then warn about copying a directory into itself. */
- if (same_name (src_name, earlier_file))
- {
- error (0, 0, _("cannot copy a directory, %s, into itself, %s"),
- quote_n (0, top_level_src_name),
- quote_n (1, top_level_dst_name));
- *copy_into_self = true;
- goto un_backup;
- }
- else if (x->dereference == DEREF_ALWAYS)
- {
- /* This happens when e.g., encountering a directory for the
- second or subsequent time via symlinks when cp is invoked
- with -R and -L. E.g.,
- rm -rf a b c d; mkdir a b c d; ln -s ../c a; ln -s ../c b;
- cp -RL a b d
- */
- }
- else
- {
- error (0, 0, _("will not create hard link %s to directory %s"),
- quote_n (0, dst_name), quote_n (1, earlier_file));
- goto un_backup;
- }
- }
+ {
+ /* If src_name and earlier_file refer to the same directory entry,
+ then warn about copying a directory into itself. */
+ if (same_name (src_name, earlier_file))
+ {
+ error (0, 0, _("cannot copy a directory, %s, into itself, %s"),
+ quoteaf_n (0, top_level_src_name),
+ quoteaf_n (1, top_level_dst_name));
+ *copy_into_self = true;
+ goto un_backup;
+ }
+ else if (same_name (dst_name, earlier_file))
+ {
+ error (0, 0, _("warning: source directory %s "
+ "specified more than once"),
+ quoteaf (top_level_src_name));
+ /* In move mode, if a previous rename succeeded, then
+ we won't be in this path as the source is missing. If the
+ rename previously failed, then that has been handled, so
+ pretend this attempt succeeded so the source isn't removed. */
+ if (x->move_mode && rename_succeeded)
+ *rename_succeeded = true;
+ /* We only do backups in move mode, and for non directories.
+ So just ignore this repeated entry. */
+ return true;
+ }
+ else if (x->dereference == DEREF_ALWAYS
+ || (command_line_arg
+ && x->dereference == DEREF_COMMAND_LINE_ARGUMENTS))
+ {
+ /* This happens when e.g., encountering a directory for the
+ second or subsequent time via symlinks when cp is invoked
+ with -R and -L. E.g.,
+ rm -rf a b c d; mkdir a b c d; ln -s ../c a; ln -s ../c b;
+ cp -RL a b d
+ */
+ }
+ else
+ {
+ error (0, 0, _("will not create hard link %s to directory %s"),
+ quoteaf_n (0, dst_name), quoteaf_n (1, earlier_file));
+ goto un_backup;
+ }
+ }
else
- {
- bool link_failed = (link (earlier_file, dst_name) != 0);
-
- /* If the link failed because of an existing destination,
- remove that file and then call link again. */
- if (link_failed && errno == EEXIST)
- {
- if (unlink (dst_name) != 0)
- {
- error (0, errno, _("cannot remove %s"), quote (dst_name));
- goto un_backup;
- }
- if (x->verbose)
- printf (_("removed %s\n"), quote (dst_name));
- link_failed = (link (earlier_file, dst_name) != 0);
- }
-
- if (link_failed)
- {
- error (0, errno, _("cannot create hard link %s to %s"),
- quote_n (0, dst_name), quote_n (1, earlier_file));
- goto un_backup;
- }
-
- return true;
- }
+ {
+ if (! create_hard_link (earlier_file, dst_name, true, x->verbose,
+ dereference))
+ goto un_backup;
+
+ return true;
+ }
}
if (x->move_mode)
{
if (rename (src_name, dst_name) == 0)
- {
- if (x->verbose && S_ISDIR (src_mode))
- emit_verbose (src_name, dst_name,
- backup_succeeded ? dst_backup : NULL);
-
- if (rename_succeeded)
- *rename_succeeded = true;
-
- if (command_line_arg)
- {
- /* Record destination dev/ino/name, so that if we are asked
- to overwrite that file again, we can detect it and fail. */
- /* It's fine to use the _source_ stat buffer (src_sb) to get the
- _destination_ dev/ino, since the rename above can't have
- changed those, and `mv' always uses lstat.
- We could limit it further by operating
- only on non-directories. */
- record_file (x->dest_info, dst_name, &src_sb);
- }
-
- return true;
- }
+ {
+ if (x->verbose && S_ISDIR (src_mode))
+ emit_verbose (src_name, dst_name,
+ backup_succeeded ? dst_backup : NULL);
+
+ if (x->set_security_context)
+ {
+ /* -Z failures are only warnings currently. */
+ (void) set_file_security_ctx (dst_name, false, true, x);
+ }
+
+ if (rename_succeeded)
+ *rename_succeeded = true;
+
+ if (command_line_arg)
+ {
+ /* Record destination dev/ino/name, so that if we are asked
+ to overwrite that file again, we can detect it and fail. */
+ /* It's fine to use the _source_ stat buffer (src_sb) to get the
+ _destination_ dev/ino, since the rename above can't have
+ changed those, and 'mv' always uses lstat.
+ We could limit it further by operating
+ only on non-directories. */
+ record_file (x->dest_info, dst_name, &src_sb);
+ }
+
+ return true;
+ }
/* FIXME: someday, consider what to do when moving a directory into
- itself but when source and destination are on different devices. */
+ itself but when source and destination are on different devices. */
/* This happens when attempting to rename a directory to a
- subdirectory of itself. */
+ subdirectory of itself. */
if (errno == EINVAL)
- {
- /* FIXME: this is a little fragile in that it relies on rename(2)
- failing with a specific errno value. Expect problems on
- non-POSIX systems. */
- error (0, 0, _("cannot move %s to a subdirectory of itself, %s"),
- quote_n (0, top_level_src_name),
- quote_n (1, top_level_dst_name));
-
- /* Note that there is no need to call forget_created here,
- (compare with the other calls in this file) since the
- destination directory didn't exist before. */
-
- *copy_into_self = true;
- /* FIXME-cleanup: Don't return true here; adjust mv.c accordingly.
- The only caller that uses this code (mv.c) ends up setting its
- exit status to nonzero when copy_into_self is nonzero. */
- return true;
- }
+ {
+ /* FIXME: this is a little fragile in that it relies on rename(2)
+ failing with a specific errno value. Expect problems on
+ non-POSIX systems. */
+ error (0, 0, _("cannot move %s to a subdirectory of itself, %s"),
+ quoteaf_n (0, top_level_src_name),
+ quoteaf_n (1, top_level_dst_name));
+
+ /* Note that there is no need to call forget_created here,
+ (compare with the other calls in this file) since the
+ destination directory didn't exist before. */
+
+ *copy_into_self = true;
+ /* FIXME-cleanup: Don't return true here; adjust mv.c accordingly.
+ The only caller that uses this code (mv.c) ends up setting its
+ exit status to nonzero when copy_into_self is nonzero. */
+ return true;
+ }
/* WARNING: there probably exist systems for which an inter-device
- rename fails with a value of errno not handled here.
- If/as those are reported, add them to the condition below.
- If this happens to you, please do the following and send the output
- to the bug-reporting address (e.g., in the output of cp --help):
- touch k; perl -e 'rename "k","/tmp/k" or print "$!(",$!+0,")\n"'
- where your current directory is on one partion and /tmp is the other.
- Also, please try to find the E* errno macro name corresponding to
- the diagnostic and parenthesized integer, and include that in your
- e-mail. One way to do that is to run a command like this
- find /usr/include/. -type f \
- | xargs grep 'define.*\<E[A-Z]*\>.*\<18\>' /dev/null
- where you'd replace `18' with the integer in parentheses that
- was output from the perl one-liner above.
- If necessary, of course, change `/tmp' to some other directory. */
+ rename fails with a value of errno not handled here.
+ If/as those are reported, add them to the condition below.
+ If this happens to you, please do the following and send the output
+ to the bug-reporting address (e.g., in the output of cp --help):
+ touch k; perl -e 'rename "k","/tmp/k" or print "$!(",$!+0,")\n"'
+ where your current directory is on one partition and /tmp is the other.
+ Also, please try to find the E* errno macro name corresponding to
+ the diagnostic and parenthesized integer, and include that in your
+ e-mail. One way to do that is to run a command like this
+ find /usr/include/. -type f \
+ | xargs grep 'define.*\<E[A-Z]*\>.*\<18\>' /dev/null
+ where you'd replace '18' with the integer in parentheses that
+ was output from the perl one-liner above.
+ If necessary, of course, change '/tmp' to some other directory. */
if (errno != EXDEV)
- {
- /* There are many ways this can happen due to a race condition.
- When something happens between the initial XSTAT and the
- subsequent rename, we can get many different types of errors.
- For example, if the destination is initially a non-directory
- or non-existent, but it is created as a directory, the rename
- fails. If two `mv' commands try to rename the same file at
- about the same time, one will succeed and the other will fail.
- If the permissions on the directory containing the source or
- destination file are made too restrictive, the rename will
- fail. Etc. */
- error (0, errno,
- _("cannot move %s to %s"),
- quote_n (0, src_name), quote_n (1, dst_name));
- forget_created (src_sb.st_ino, src_sb.st_dev);
- return false;
- }
+ {
+ /* There are many ways this can happen due to a race condition.
+ When something happens between the initial XSTAT and the
+ subsequent rename, we can get many different types of errors.
+ For example, if the destination is initially a non-directory
+ or non-existent, but it is created as a directory, the rename
+ fails. If two 'mv' commands try to rename the same file at
+ about the same time, one will succeed and the other will fail.
+ If the permissions on the directory containing the source or
+ destination file are made too restrictive, the rename will
+ fail. Etc. */
+ error (0, errno,
+ _("cannot move %s to %s"),
+ quoteaf_n (0, src_name), quoteaf_n (1, dst_name));
+ forget_created (src_sb.st_ino, src_sb.st_dev);
+ return false;
+ }
/* The rename attempt has failed. Remove any existing destination
- file so that a cross-device `mv' acts as if it were really using
- the rename syscall. */
- if (unlink (dst_name) != 0 && errno != ENOENT)
- {
- error (0, errno,
- _("inter-device move failed: %s to %s; unable to remove target"),
- quote_n (0, src_name), quote_n (1, dst_name));
- forget_created (src_sb.st_ino, src_sb.st_dev);
- return false;
- }
+ file so that a cross-device 'mv' acts as if it were really using
+ the rename syscall. Note both src and dst must both be directories
+ or not, and this is enforced above. Therefore we check the src_mode
+ and operate on dst_name here as a tighter constraint and also because
+ src_mode is readily available here. */
+ if ((S_ISDIR (src_mode) ? rmdir (dst_name) : unlink (dst_name)) != 0
+ && errno != ENOENT)
+ {
+ error (0, errno,
+ _("inter-device move failed: %s to %s; unable to remove target"),
+ quoteaf_n (0, src_name), quoteaf_n (1, dst_name));
+ forget_created (src_sb.st_ino, src_sb.st_dev);
+ return false;
+ }
new_dst = true;
}
@@ -1528,16 +2434,17 @@ copy_internal (char const *src_name, char const *dst_name,
omitted_permissions =
(dst_mode_bits
& (x->preserve_ownership ? S_IRWXG | S_IRWXO
- : S_ISDIR (src_mode) ? S_IWGRP | S_IWOTH
- : 0));
+ : S_ISDIR (src_mode) ? S_IWGRP | S_IWOTH
+ : 0));
delayed_ok = true;
- /* In certain modes (cp's --symbolic-link), and for certain file types
- (symlinks and hard links) it doesn't make sense to preserve metadata,
- or it's possible to preserve only some of it.
- In such cases, set this variable to zero. */
- preserve_metadata = true;
+ /* If required, set the default security context for new files.
+ Also for existing files this is used as a reference
+ when copying the context with --preserve=context.
+ FIXME: Do we need to consider dst_mode_bits here? */
+ if (! set_process_security_ctx (src_name, dst_name, src_mode, new_dst, x))
+ return false;
if (S_ISDIR (src_mode))
{
@@ -1549,11 +2456,11 @@ copy_internal (char const *src_name, char const *dst_name,
continue to copy this, unless we've got an infinite disk. */
if (is_ancestor (&src_sb, ancestors))
- {
- error (0, 0, _("cannot copy cyclic symbolic link %s"),
- quote (src_name));
- goto un_backup;
- }
+ {
+ error (0, 0, _("cannot copy cyclic symbolic link %s"),
+ quoteaf (src_name));
+ goto un_backup;
+ }
/* Insert the current directory in the list of parents. */
@@ -1563,244 +2470,289 @@ copy_internal (char const *src_name, char const *dst_name,
dir->dev = src_sb.st_dev;
if (new_dst || !S_ISDIR (dst_sb.st_mode))
- {
- /* 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. */
- if (mkdir (dst_name, dst_mode_bits & ~omitted_permissions) != 0)
- {
- error (0, errno, _("cannot create directory %s"),
- quote (dst_name));
- goto un_backup;
- }
-
- /* We need search and write permissions to the new directory
- for writing the directory's contents. Check if these
- permissions are there. */
-
- if (lstat (dst_name, &dst_sb) != 0)
- {
- error (0, errno, _("cannot stat %s"), quote (dst_name));
- goto un_backup;
- }
- else if ((dst_sb.st_mode & S_IRWXU) != S_IRWXU)
- {
- /* Make the new directory searchable and writable. */
-
- dst_mode = dst_sb.st_mode;
- restore_dst_mode = true;
-
- if (lchmod (dst_name, dst_mode | S_IRWXU) != 0)
- {
- error (0, errno, _("setting permissions for %s"),
- quote (dst_name));
- goto un_backup;
- }
- }
-
- /* Insert the created directory's inode and device
- numbers into the search structure, so that we can
- avoid copying it again. */
-
- remember_copied (dst_name, dst_sb.st_ino, dst_sb.st_dev);
-
- if (x->verbose)
- emit_verbose (src_name, dst_name, NULL);
- }
+ {
+ /* 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. */
+ if (mkdir (dst_name, dst_mode_bits & ~omitted_permissions) != 0)
+ {
+ error (0, errno, _("cannot create directory %s"),
+ quoteaf (dst_name));
+ goto un_backup;
+ }
+
+ /* We need search and write permissions to the new directory
+ for writing the directory's contents. Check if these
+ permissions are there. */
+
+ if (lstat (dst_name, &dst_sb) != 0)
+ {
+ error (0, errno, _("cannot stat %s"), quoteaf (dst_name));
+ goto un_backup;
+ }
+ else if ((dst_sb.st_mode & S_IRWXU) != S_IRWXU)
+ {
+ /* Make the new directory searchable and writable. */
+
+ dst_mode = dst_sb.st_mode;
+ restore_dst_mode = true;
+
+ if (lchmod (dst_name, dst_mode | S_IRWXU) != 0)
+ {
+ error (0, errno, _("setting permissions for %s"),
+ quoteaf (dst_name));
+ goto un_backup;
+ }
+ }
+
+ /* Record the created directory's inode and device numbers into
+ the search structure, so that we can avoid copying it again.
+ Do this only for the first directory that is created for each
+ source command line argument. */
+ if (!*first_dir_created_per_command_line_arg)
+ {
+ remember_copied (dst_name, dst_sb.st_ino, dst_sb.st_dev);
+ *first_dir_created_per_command_line_arg = true;
+ }
+
+ if (x->verbose)
+ emit_verbose (src_name, dst_name, NULL);
+ }
+ else
+ {
+ omitted_permissions = 0;
+
+ /* For directories, the process global context could be reset for
+ descendents, so use it to set the context for existing dirs here.
+ This will also give earlier indication of failure to set ctx. */
+ if (x->set_security_context || x->preserve_security_context)
+ if (! set_file_security_ctx (dst_name, x->preserve_security_context,
+ false, x))
+ {
+ if (x->require_preserve_context)
+ goto un_backup;
+ }
+ }
/* Decide whether to copy the contents of the directory. */
- if (x->one_file_system && device != 0 && device != src_sb.st_dev)
- {
- /* Here, we are crossing a file system boundary and cp's -x option
- is in effect: so don't copy the contents of this directory. */
- }
+ if (x->one_file_system && parent && parent->st_dev != src_sb.st_dev)
+ {
+ /* Here, we are crossing a file system boundary and cp's -x option
+ is in effect: so don't copy the contents of this directory. */
+ }
else
- {
- /* Copy the contents of the directory. Don't just return if
- this fails -- otherwise, the failure to read a single file
- in a source directory would cause the containing destination
- directory not to have owner/perms set properly. */
- delayed_ok = copy_dir (src_name, dst_name, new_dst, &src_sb, dir, x,
- copy_into_self);
- }
+ {
+ /* Copy the contents of the directory. Don't just return if
+ this fails -- otherwise, the failure to read a single file
+ in a source directory would cause the containing destination
+ directory not to have owner/perms set properly. */
+ delayed_ok = copy_dir (src_name, dst_name, new_dst, &src_sb, dir, x,
+ first_dir_created_per_command_line_arg,
+ copy_into_self);
+ }
}
else if (x->symbolic_link)
{
- preserve_metadata = false;
-
+ dest_is_symlink = true;
if (*src_name != '/')
- {
- /* Check that DST_NAME denotes a file in the current directory. */
- struct stat dot_sb;
- struct stat dst_parent_sb;
- char *dst_parent;
- bool in_current_dir;
-
- dst_parent = dir_name (dst_name);
-
- in_current_dir = (STREQ (".", dst_parent)
- /* If either stat call fails, it's ok not to report
- the failure and say dst_name is in the current
- directory. Other things will fail later. */
- || stat (".", &dot_sb) != 0
- || stat (dst_parent, &dst_parent_sb) != 0
- || SAME_INODE (dot_sb, dst_parent_sb));
- free (dst_parent);
-
- if (! in_current_dir)
- {
- error (0, 0,
- _("%s: can make relative symbolic links only in current directory"),
- quote (dst_name));
- goto un_backup;
- }
- }
+ {
+ /* Check that DST_NAME denotes a file in the current directory. */
+ struct stat dot_sb;
+ struct stat dst_parent_sb;
+ char *dst_parent;
+ bool in_current_dir;
+
+ dst_parent = dir_name (dst_name);
+
+ in_current_dir = (STREQ (".", dst_parent)
+ /* If either stat call fails, it's ok not to report
+ the failure and say dst_name is in the current
+ directory. Other things will fail later. */
+ || stat (".", &dot_sb) != 0
+ || stat (dst_parent, &dst_parent_sb) != 0
+ || SAME_INODE (dot_sb, dst_parent_sb));
+ free (dst_parent);
+
+ if (! in_current_dir)
+ {
+ error (0, 0,
+ _("%s: can make relative symbolic links only in current directory"),
+ quotef (dst_name));
+ goto un_backup;
+ }
+ }
if (symlink (src_name, dst_name) != 0)
- {
- error (0, errno, _("cannot create symbolic link %s to %s"),
- quote_n (0, dst_name), quote_n (1, src_name));
- goto un_backup;
- }
+ {
+ error (0, errno, _("cannot create symbolic link %s to %s"),
+ quoteaf_n (0, dst_name), quoteaf_n (1, src_name));
+ goto un_backup;
+ }
}
+ /* POSIX 2008 states that it is implementation-defined whether
+ link() on a symlink creates a hard-link to the symlink, or only
+ to the referent (effectively dereferencing the symlink) (POSIX
+ 2001 required the latter behavior, although many systems provided
+ the former). Yet cp, invoked with '--link --no-dereference',
+ should not follow the link. We can approximate the desired
+ behavior by skipping this hard-link creating block and instead
+ copying the symlink, via the 'S_ISLNK'- copying code below.
+
+ Note gnulib's linkat module, guarantees that the symlink is not
+ dereferenced. However its emulation currently doesn't maintain
+ timestamps or ownership so we only call it when we know the
+ emulation will not be needed. */
else if (x->hard_link
-#ifdef LINK_FOLLOWS_SYMLINKS
- /* A POSIX-conforming link syscall dereferences a symlink, yet cp,
- invoked with `--link --no-dereference', should not. Thus, with
- a POSIX-conforming link system call, we can't use link() here,
- since that would create a hard link to the referent (effectively
- dereferencing the symlink), rather than to the symlink itself.
- We can approximate the desired behavior by skipping this hard-link
- creating block and instead copying the symlink, via the `S_ISLNK'-
- copying code below.
- When link operates on the symlinks themselves, we use this block
- and just call link(). */
- && !(S_ISLNK (src_mode) && x->dereference == DEREF_NEVER)
-#endif
- )
+ && !(! CAN_HARDLINK_SYMLINKS && S_ISLNK (src_mode)
+ && x->dereference == DEREF_NEVER))
{
- preserve_metadata = false;
- if (link (src_name, dst_name))
- {
- error (0, errno, _("cannot create link %s"), quote (dst_name));
- goto un_backup;
- }
+ if (! create_hard_link (src_name, dst_name, false, false, dereference))
+ goto un_backup;
}
else if (S_ISREG (src_mode)
- || (x->copy_as_regular && !S_ISLNK (src_mode)))
+ || (x->copy_as_regular && !S_ISLNK (src_mode)))
{
copied_as_regular = true;
/* POSIX says the permission bits of the source file must be
- used as the 3rd argument in the open call. Historical
- practice passed all the source mode bits to 'open', but the extra
- bits were ignored, so it should be the same either way. */
- if (! copy_reg (src_name, dst_name, x, src_mode & S_IRWXUGO,
- omitted_permissions, &new_dst, &src_sb))
- goto un_backup;
+ used as the 3rd argument in the open call. Historical
+ practice passed all the source mode bits to 'open', but the extra
+ bits were ignored, so it should be the same either way.
+
+ This call uses DST_MODE_BITS, not SRC_MODE. These are
+ normally the same, and the exception (where x->set_mode) is
+ used only by 'install', which POSIX does not specify and
+ where DST_MODE_BITS is what's wanted. */
+ if (! copy_reg (src_name, dst_name, x, dst_mode_bits & S_IRWXUGO,
+ omitted_permissions, &new_dst, &src_sb))
+ goto un_backup;
}
else if (S_ISFIFO (src_mode))
{
/* Use mknod, rather than mkfifo, because the former preserves
- the special mode bits of a fifo on Solaris 10, while mkfifo
- does not. But fall back on mkfifo, because on some BSD systems,
- mknod always fails when asked to create a FIFO. */
+ the special mode bits of a fifo on Solaris 10, while mkfifo
+ does not. But fall back on mkfifo, because on some BSD systems,
+ mknod always fails when asked to create a FIFO. */
if (mknod (dst_name, src_mode & ~omitted_permissions, 0) != 0)
-#if HAVE_MKFIFO
- if (mkfifo (dst_name, src_mode & ~S_IFIFO & ~omitted_permissions) != 0)
-#endif
- {
- error (0, errno, _("cannot create fifo %s"), quote (dst_name));
- goto un_backup;
- }
+ if (mkfifo (dst_name, src_mode & ~S_IFIFO & ~omitted_permissions) != 0)
+ {
+ error (0, errno, _("cannot create fifo %s"), quoteaf (dst_name));
+ goto un_backup;
+ }
}
else if (S_ISBLK (src_mode) || S_ISCHR (src_mode) || S_ISSOCK (src_mode))
{
if (mknod (dst_name, src_mode & ~omitted_permissions, src_sb.st_rdev)
- != 0)
- {
- error (0, errno, _("cannot create special file %s"),
- quote (dst_name));
- goto un_backup;
- }
+ != 0)
+ {
+ error (0, errno, _("cannot create special file %s"),
+ quoteaf (dst_name));
+ goto un_backup;
+ }
}
else if (S_ISLNK (src_mode))
{
- char *src_link_val = xreadlink_with_size (src_name, src_sb.st_size);
+ char *src_link_val = areadlink_with_size (src_name, src_sb.st_size);
+ dest_is_symlink = true;
if (src_link_val == NULL)
- {
- error (0, errno, _("cannot read symbolic link %s"), quote (src_name));
- goto un_backup;
- }
+ {
+ error (0, errno, _("cannot read symbolic link %s"),
+ quoteaf (src_name));
+ goto un_backup;
+ }
if (symlink (src_link_val, dst_name) == 0)
- free (src_link_val);
+ free (src_link_val);
else
- {
- int saved_errno = errno;
- bool same_link = false;
- if (x->update && !new_dst && S_ISLNK (dst_sb.st_mode)
- && dst_sb.st_size == strlen (src_link_val))
- {
- /* See if the destination is already the desired symlink.
- FIXME: This behavior isn't documented, and seems wrong
- in some cases, e.g., if the destination symlink has the
- wrong ownership, permissions, or time stamps. */
- char *dest_link_val =
- xreadlink_with_size (dst_name, dst_sb.st_size);
- if (STREQ (dest_link_val, src_link_val))
- same_link = true;
- free (dest_link_val);
- }
- free (src_link_val);
-
- if (! same_link)
- {
- error (0, saved_errno, _("cannot create symbolic link %s"),
- quote (dst_name));
- goto un_backup;
- }
- }
-
- /* There's no need to preserve timestamps or permissions. */
- preserve_metadata = false;
+ {
+ int saved_errno = errno;
+ bool same_link = false;
+ if (x->update && !new_dst && S_ISLNK (dst_sb.st_mode)
+ && dst_sb.st_size == strlen (src_link_val))
+ {
+ /* See if the destination is already the desired symlink.
+ FIXME: This behavior isn't documented, and seems wrong
+ in some cases, e.g., if the destination symlink has the
+ wrong ownership, permissions, or time stamps. */
+ char *dest_link_val =
+ areadlink_with_size (dst_name, dst_sb.st_size);
+ if (dest_link_val && STREQ (dest_link_val, src_link_val))
+ same_link = true;
+ free (dest_link_val);
+ }
+ free (src_link_val);
+
+ if (! same_link)
+ {
+ error (0, saved_errno, _("cannot create symbolic link %s"),
+ quoteaf (dst_name));
+ goto un_backup;
+ }
+ }
+
+ if (x->preserve_security_context)
+ restore_default_fscreatecon_or_die ();
if (x->preserve_ownership)
- {
- /* Preserve the owner and group of the just-`copied'
- symbolic link, if possible. */
-#if HAVE_LCHOWN
- if (lchown (dst_name, src_sb.st_uid, src_sb.st_gid) != 0
- && ! chown_failure_ok (x))
- {
- error (0, errno, _("failed to preserve ownership for %s"),
- dst_name);
- goto un_backup;
- }
-#else
- /* Can't preserve ownership of symlinks.
- FIXME: maybe give a warning or even error for symlinks
- in directories with the sticky bit set -- there, not
- preserving owner/group is a potential security problem. */
-#endif
- }
+ {
+ /* Preserve the owner and group of the just-'copied'
+ symbolic link, if possible. */
+ if (HAVE_LCHOWN
+ && lchown (dst_name, src_sb.st_uid, src_sb.st_gid) != 0
+ && ! chown_failure_ok (x))
+ {
+ error (0, errno, _("failed to preserve ownership for %s"),
+ dst_name);
+ goto un_backup;
+ }
+ else
+ {
+ /* Can't preserve ownership of symlinks.
+ FIXME: maybe give a warning or even error for symlinks
+ in directories with the sticky bit set -- there, not
+ preserving owner/group is a potential security problem. */
+ }
+ }
}
else
{
- error (0, 0, _("%s has unknown file type"), quote (src_name));
+ error (0, 0, _("%s has unknown file type"), quoteaf (src_name));
goto un_backup;
}
- if (command_line_arg)
- record_file (x->dest_info, dst_name, NULL);
+ /* With -Z or --preserve=context, set the context for existing files.
+ Note this is done already for copy_reg() for reasons described therein. */
+ if (!new_dst && !x->copy_as_regular && !S_ISDIR (src_mode)
+ && (x->set_security_context || x->preserve_security_context))
+ {
+ if (! set_file_security_ctx (dst_name, x->preserve_security_context,
+ false, x))
+ {
+ if (x->require_preserve_context)
+ goto un_backup;
+ }
+ }
- if ( ! preserve_metadata)
- return true;
+ if (command_line_arg && x->dest_info)
+ {
+ /* Now that the destination file is very likely to exist,
+ add its info to the set. */
+ struct stat sb;
+ if (lstat (dst_name, &sb) == 0)
+ record_file (x->dest_info, dst_name, &sb);
+ }
+
+ /* If we've just created a hard-link due to cp's --link option,
+ we're done. */
+ if (x->hard_link && ! S_ISDIR (src_mode)
+ && !(! CAN_HARDLINK_SYMLINKS && S_ISLNK (src_mode)
+ && x->dereference == DEREF_NEVER))
+ return delayed_ok;
if (copied_as_regular)
return delayed_ok;
- /* POSIX says that `cp -p' must restore the following:
+ /* POSIX says that 'cp -p' must restore the following:
- permission bits
- setuid, setgid bits
- owner and group
@@ -1818,88 +2770,108 @@ copy_internal (char const *src_name, char const *dst_name,
timespec[0] = get_stat_atime (&src_sb);
timespec[1] = get_stat_mtime (&src_sb);
- if (utimens (dst_name, timespec) != 0)
- {
- error (0, errno, _("preserving times for %s"), quote (dst_name));
- if (x->require_preserve)
- return false;
- }
+ if ((dest_is_symlink
+ ? utimens_symlink (dst_name, timespec)
+ : utimens (dst_name, timespec))
+ != 0)
+ {
+ error (0, errno, _("preserving times for %s"), quoteaf (dst_name));
+ if (x->require_preserve)
+ return false;
+ }
}
/* Avoid calling chown if we know it's not necessary. */
- if (x->preserve_ownership
+ if (!dest_is_symlink && x->preserve_ownership
&& (new_dst || !SAME_OWNER_AND_GROUP (src_sb, dst_sb)))
{
- switch (set_owner (x, dst_name, -1, src_sb.st_uid, src_sb.st_gid))
- {
- case -1:
- return false;
-
- case 0:
- src_mode &= ~ (S_ISUID | S_ISGID | S_ISVTX);
- break;
- }
+ switch (set_owner (x, dst_name, -1, &src_sb, new_dst, &dst_sb))
+ {
+ case -1:
+ return false;
+
+ case 0:
+ src_mode &= ~ (S_ISUID | S_ISGID | S_ISVTX);
+ break;
+ }
}
+ /* Set xattrs after ownership as changing owners will clear capabilities. */
+ if (x->preserve_xattr && ! copy_attr (src_name, -1, dst_name, -1, x)
+ && x->require_preserve_xattr)
+ return false;
+
+ /* The operations beyond this point may dereference a symlink. */
+ if (dest_is_symlink)
+ return delayed_ok;
+
set_author (dst_name, -1, &src_sb);
if (x->preserve_mode || x->move_mode)
{
if (copy_acl (src_name, -1, dst_name, -1, src_mode) != 0
- && x->require_preserve)
- return false;
+ && x->require_preserve)
+ return false;
}
else if (x->set_mode)
{
if (set_acl (dst_name, -1, x->mode) != 0)
- return false;
+ return false;
+ }
+ else if (x->explicit_no_preserve_mode)
+ {
+ if (set_acl (dst_name, -1, 0777 & ~cached_umask ()) != 0)
+ return false;
}
else
{
if (omitted_permissions)
- {
- omitted_permissions &= ~ cached_umask ();
-
- if (omitted_permissions && !restore_dst_mode)
- {
- /* Permissions were deliberately omitted when the file
- was created due to security concerns. See whether
- they need to be re-added now. It'd be faster to omit
- the lstat, but deducing the current destination mode
- is tricky in the presence of implementation-defined
- rules for special mode bits. */
- if (new_dst && lstat (dst_name, &dst_sb) != 0)
- {
- error (0, errno, _("cannot stat %s"), quote (dst_name));
- return false;
- }
- dst_mode = dst_sb.st_mode;
- if (omitted_permissions & ~dst_mode)
- restore_dst_mode = true;
- }
- }
+ {
+ omitted_permissions &= ~ cached_umask ();
+
+ if (omitted_permissions && !restore_dst_mode)
+ {
+ /* Permissions were deliberately omitted when the file
+ was created due to security concerns. See whether
+ they need to be re-added now. It'd be faster to omit
+ the lstat, but deducing the current destination mode
+ is tricky in the presence of implementation-defined
+ rules for special mode bits. */
+ if (new_dst && lstat (dst_name, &dst_sb) != 0)
+ {
+ error (0, errno, _("cannot stat %s"), quoteaf (dst_name));
+ return false;
+ }
+ dst_mode = dst_sb.st_mode;
+ if (omitted_permissions & ~dst_mode)
+ restore_dst_mode = true;
+ }
+ }
if (restore_dst_mode)
- {
- if (lchmod (dst_name, dst_mode | omitted_permissions) != 0)
- {
- error (0, errno, _("preserving permissions for %s"),
- quote (dst_name));
- if (x->require_preserve)
- return false;
- }
- }
+ {
+ if (lchmod (dst_name, dst_mode | omitted_permissions) != 0)
+ {
+ error (0, errno, _("preserving permissions for %s"),
+ quoteaf (dst_name));
+ if (x->require_preserve)
+ return false;
+ }
+ }
}
return delayed_ok;
un_backup:
+ if (x->preserve_security_context)
+ restore_default_fscreatecon_or_die ();
+
/* We have failed to create the destination file.
If we've just added a dev/ino entry via the remember_copied
call above (i.e., unless we've just failed to create a hard link),
remove the entry associating the source dev/ino with the
- destination file name, so we don't try to `preserve' a link
+ destination file name, so we don't try to 'preserve' a link
to a file we didn't create. */
if (earlier_file == NULL)
forget_created (src_sb.st_ino, src_sb.st_dev);
@@ -1907,24 +2879,28 @@ un_backup:
if (dst_backup)
{
if (rename (dst_backup, dst_name) != 0)
- error (0, errno, _("cannot un-backup %s"), quote (dst_name));
+ error (0, errno, _("cannot un-backup %s"), quoteaf (dst_name));
else
- {
- if (x->verbose)
- printf (_("%s -> %s (unbackup)\n"),
- quote_n (0, dst_backup), quote_n (1, dst_name));
- }
+ {
+ if (x->verbose)
+ printf (_("%s -> %s (unbackup)\n"),
+ quoteaf_n (0, dst_backup), quoteaf_n (1, dst_name));
+ }
}
return false;
}
-static bool
+static bool _GL_ATTRIBUTE_PURE
valid_options (const struct cp_options *co)
{
assert (co != NULL);
assert (VALID_BACKUP_TYPE (co->backup_type));
assert (VALID_SPARSE_MODE (co->sparse_mode));
+ assert (VALID_REFLINK_MODE (co->reflink_mode));
assert (!(co->hard_link && co->symbolic_link));
+ assert (!
+ (co->reflink_mode == REFLINK_ALWAYS
+ && co->sparse_mode != SPARSE_AUTO));
return true;
}
@@ -1954,27 +2930,33 @@ copy (char const *src_name, char const *dst_name,
top_level_src_name = src_name;
top_level_dst_name = dst_name;
- return copy_internal (src_name, dst_name, nonexistent_dst, 0, NULL,
- options, true, copy_into_self, rename_succeeded);
+ bool first_dir_created_per_command_line_arg = false;
+ return copy_internal (src_name, dst_name, nonexistent_dst, NULL, NULL,
+ options, true,
+ &first_dir_created_per_command_line_arg,
+ copy_into_self, rename_succeeded);
}
-/* Return true if this process has appropriate privileges to chown a
- file whose owner is not the effective user ID. */
+/* Set *X to the default options for a value of type struct cp_options. */
-extern bool
-chown_privileges (void)
+extern void
+cp_options_default (struct cp_options *x)
{
+ memset (x, 0, sizeof *x);
#ifdef PRIV_FILE_CHOWN
- bool result;
- priv_set_t *pset = priv_allocset ();
- if (!pset)
- xalloc_die ();
- result = (getppriv (PRIV_EFFECTIVE, pset) == 0
- && priv_ismember (pset, PRIV_FILE_CHOWN));
- priv_freeset (pset);
- return result;
+ {
+ priv_set_t *pset = priv_allocset ();
+ if (!pset)
+ xalloc_die ();
+ if (getppriv (PRIV_EFFECTIVE, pset) == 0)
+ {
+ x->chown_privileges = priv_ismember (pset, PRIV_FILE_CHOWN);
+ x->owner_privileges = priv_ismember (pset, PRIV_FILE_OWNER);
+ }
+ priv_freeset (pset);
+ }
#else
- return (geteuid () == 0);
+ x->chown_privileges = x->owner_privileges = (geteuid () == ROOT_UID);
#endif
}
@@ -1992,8 +2974,22 @@ chown_failure_ok (struct cp_options const *x)
return ((errno == EPERM || errno == EINVAL) && !x->chown_privileges);
}
-/* Return the user's umask, caching the result. */
+/* Similarly, return true if it's OK for chmod and similar operations
+ to fail, where errno is the error number that chmod failed with and
+ X is the copying option set. */
+
+static bool
+owner_failure_ok (struct cp_options const *x)
+{
+ return ((errno == EPERM || errno == EINVAL) && !x->owner_privileges);
+}
+
+/* Return the user's umask, caching the result.
+ FIXME: If the destination's parent directory has has a default ACL,
+ some operating systems (e.g., GNU/Linux's "POSIX" ACLs) use that
+ ACL's mask rather than the process umask. Currently, the callers
+ of cached_umask incorrectly assume that this situation cannot occur. */
extern mode_t
cached_umask (void)
{
diff --git a/src/copy.h b/src/copy.h
index c815baf..fa0c2ae 100644
--- a/src/copy.h
+++ b/src/copy.h
@@ -1,10 +1,10 @@
/* core functions for copying files and directories
- Copyright (C) 89, 90, 91, 1995-2005 Free Software Foundation.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Extracted from cp.c and librarified by Jim Meyering. */
@@ -22,7 +21,6 @@
# include <stdbool.h>
# include "hash.h"
-# include "lstat.h"
/* Control creation of sparse files (files with holes). */
enum Sparse_type
@@ -45,6 +43,19 @@ enum Sparse_type
SPARSE_ALWAYS
};
+/* Control creation of COW files. */
+enum Reflink_type
+{
+ /* Default to a standard copy. */
+ REFLINK_NEVER,
+
+ /* Try a COW copy and fall back to a standard copy. */
+ REFLINK_AUTO,
+
+ /* Require a COW copy and fail if not available. */
+ REFLINK_ALWAYS
+};
+
/* This type is used to help mv (via copy.c) distinguish these cases. */
enum Interactive
{
@@ -75,6 +86,11 @@ enum Dereference_symlink
|| (Mode) == SPARSE_AUTO \
|| (Mode) == SPARSE_ALWAYS)
+# define VALID_REFLINK_MODE(Mode) \
+ ((Mode) == REFLINK_NEVER \
+ || (Mode) == REFLINK_AUTO \
+ || (Mode) == REFLINK_ALWAYS)
+
/* These options control how files are copied by at least the
following programs: mv (when rename doesn't work), cp, install.
So, if you add a new member, be sure to initialize it in
@@ -83,21 +99,33 @@ struct cp_options
{
enum backup_type backup_type;
+ /* How to handle symlinks in the source. */
+ enum Dereference_symlink dereference;
+
+ /* This value is used to determine whether to prompt before removing
+ each existing destination file. It works differently depending on
+ whether move_mode is set. See code/comments in copy.c. */
+ enum Interactive interactive;
+
+ /* Control creation of sparse files. */
+ enum Sparse_type sparse_mode;
+
+ /* Set the mode of the destination file to exactly this value
+ if SET_MODE is nonzero. */
+ mode_t mode;
+
/* If true, copy all files except (directories and, if not dereferencing
them, symbolic links,) as if they were regular files. */
bool copy_as_regular;
- /* How to handle symlinks. */
- enum Dereference_symlink dereference;
-
/* If true, remove each existing destination nondirectory before
trying to open it. */
bool unlink_dest_before_opening;
/* If true, first try to open each existing destination nondirectory,
then, if the open fails, unlink and try again.
- This option must be set for `cp -f', in case the destination file
- exists when the open is attempted. It is irrelevant to `mv' since
+ This option must be set for 'cp -f', in case the destination file
+ exists when the open is attempted. It is irrelevant to 'mv' since
any destination is sure to be removed before the open. */
bool unlink_dest_after_failed_open;
@@ -105,11 +133,6 @@ struct cp_options
Create destination directories as usual. */
bool hard_link;
- /* This value is used to determine whether to prompt before removing
- each existing destination file. It works differently depending on
- whether move_mode is set. See code/comments in copy.c. */
- enum Interactive interactive;
-
/* If true, rather than copying, first attempt to use rename.
If that fails, then resort to copying. */
bool move_mode;
@@ -118,6 +141,13 @@ struct cp_options
whose owner is not the effective user ID. */
bool chown_privileges;
+ /* Whether this process has appropriate privileges to do the
+ following operations on a file even when it is owned by some
+ other user: set the file's atime, mtime, mode, or ACL; remove or
+ rename an entry in the file even though it is a sticky directory,
+ or to mount on the file. */
+ bool owner_privileges;
+
/* If true, when copying recursively, skip any subdirectories that are
on different file systems from the one we started on. */
bool one_file_system;
@@ -127,6 +157,10 @@ struct cp_options
bool preserve_ownership;
bool preserve_mode;
bool preserve_timestamps;
+ bool explicit_no_preserve_mode;
+
+ /* If true, attempt to set specified security context */
+ bool set_security_context;
/* Enabled for mv, and for cp by the --preserve=links option.
If true, attempt to preserve in the destination files any
@@ -140,12 +174,51 @@ struct cp_options
will be hard links to the same file (a copy of F). */
bool preserve_links;
+ /* Optionally don't copy the data, either with CoW reflink files or
+ explicitly with the --attributes-only option. */
+ bool data_copy_required;
+
/* If true and any of the above (for preserve) file attributes cannot
be applied to a destination file, treat it as a failure and return
- nonzero immediately. E.g. cp -p requires this be nonzero, mv requires
- it be zero. */
+ nonzero immediately. E.g. for cp -p this must be true, for mv it
+ must be false. */
bool require_preserve;
+ /* If true, attempt to preserve the SELinux security context, too.
+ Set this only if the kernel is SELinux enabled. */
+ bool preserve_security_context;
+
+ /* Useful only when preserve_context is true.
+ If true, a failed attempt to preserve file's security context
+ propagates failure "out" to the caller, along with full diagnostics.
+ If false, a failure to preserve file's security context does not
+ change the invoking application's exit status, but may output diagnostics.
+ For example, with 'cp --preserve=context' this flag is "true",
+ while with 'cp --preserve=all' or 'cp -a', it is "false". */
+ bool require_preserve_context;
+
+ /* If true, attempt to preserve extended attributes using libattr.
+ Ignored if coreutils are compiled without xattr support. */
+ bool preserve_xattr;
+
+ /* Useful only when preserve_xattr is true.
+ If true, a failed attempt to preserve file's extended attributes
+ propagates failure "out" to the caller, along with full diagnostics.
+ If false, a failure to preserve file's extended attributes does not
+ change the invoking application's exit status, but may output diagnostics.
+ For example, with 'cp --preserve=xattr' this flag is "true",
+ while with 'cp --preserve=all' or 'cp -a', it is "false". */
+ bool require_preserve_xattr;
+
+ /* This allows us to output warnings in cases 2 and 4 below,
+ while being quiet for case 1 (when reduce_diagnostics is true).
+ 1. cp -a try to copy xattrs with no errors
+ 2. cp --preserve=all copy xattrs with all but ENOTSUP warnings
+ 3. cp --preserve=xattr,context copy xattrs with all errors
+ 4. mv copy xattrs with all but ENOTSUP warnings
+ */
+ bool reduce_diagnostics;
+
/* If true, copy directories recursively and copy special files
as themselves rather than copying their contents. */
bool recursive;
@@ -154,13 +227,6 @@ struct cp_options
set it based on current umask modified by UMASK_KILL. */
bool set_mode;
- /* Set the mode of the destination file to exactly this value
- if SET_MODE is nonzero. */
- mode_t mode;
-
- /* Control creation of sparse files. */
- enum Sparse_type sparse_mode;
-
/* If true, create symbolic links instead of copying files.
Create destination directories as usual. */
bool symbolic_link;
@@ -175,12 +241,20 @@ struct cp_options
/* If true, stdin is a tty. */
bool stdin_tty;
+ /* If true, open a dangling destination symlink when not in move_mode.
+ Otherwise, copy_reg gives a diagnostic (it refuses to write through
+ such a symlink) and returns false. */
+ bool open_dangling_dest_symlink;
+
+ /* Control creation of COW files. */
+ enum Reflink_type reflink_mode;
+
/* This is a set of destination name/inode/dev triples. Each such triple
represents a file we have created corresponding to a source file name
that was specified on the command line. Use it to avoid clobbering
source files in commands like this:
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).
+ 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 NULL to disable
this feature. */
@@ -205,14 +279,14 @@ int rpl_rename (const char *, const char *);
# endif
bool copy (char const *src_name, char const *dst_name,
- bool nonexistent_dst, const struct cp_options *options,
- bool *copy_into_self, bool *rename_succeeded);
+ bool nonexistent_dst, const struct cp_options *options,
+ bool *copy_into_self, bool *rename_succeeded);
void dest_info_init (struct cp_options *);
void src_info_init (struct cp_options *);
-bool chown_privileges (void);
-bool chown_failure_ok (struct cp_options const *);
+void cp_options_default (struct cp_options *);
+bool chown_failure_ok (struct cp_options const *) _GL_ATTRIBUTE_PURE;
mode_t cached_umask (void);
#endif
diff --git a/src/coreutils-arch.c b/src/coreutils-arch.c
new file mode 100644
index 0000000..b878348
--- /dev/null
+++ b/src/coreutils-arch.c
@@ -0,0 +1,33 @@
+/* arch -- wrapper to uname with the right uname_mode.
+ Copyright (C) 2014-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Written by Alex Deymo <deymo@chromium.org>. */
+
+#include <config.h>
+#include "system.h"
+
+#include "uname.h"
+/* Ensure that the main for uname is declared even if the tool is not being
+ built in this single-binary. */
+int single_binary_main_uname (int argc, char **argv);
+int single_binary_main_arch (int argc, char **argv);
+
+int
+single_binary_main_arch (int argc, char **argv)
+{
+ uname_mode = UNAME_ARCH;
+ return single_binary_main_uname (argc, argv);
+}
diff --git a/src/coreutils-dir.c b/src/coreutils-dir.c
new file mode 100644
index 0000000..8546ea7
--- /dev/null
+++ b/src/coreutils-dir.c
@@ -0,0 +1,33 @@
+/* dir -- wrapper to ls with the right ls_mode.
+ Copyright (C) 2014-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Written by Alex Deymo <deymo@chromium.org>. */
+
+#include <config.h>
+#include "system.h"
+
+#include "ls.h"
+/* Ensure that the main for ls is declared even if the tool is not being built
+ in this single-binary. */
+int single_binary_main_ls (int argc, char **argv);
+int single_binary_main_dir (int argc, char **argv);
+
+int
+single_binary_main_dir (int argc, char **argv)
+{
+ ls_mode = LS_MULTI_COL;
+ return single_binary_main_ls (argc, argv);
+}
diff --git a/src/coreutils-vdir.c b/src/coreutils-vdir.c
new file mode 100644
index 0000000..ffae665
--- /dev/null
+++ b/src/coreutils-vdir.c
@@ -0,0 +1,33 @@
+/* vdir -- wrapper to ls with the right ls_mode.
+ Copyright (C) 2014-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Written by Alex Deymo <deymo@chromium.org>. */
+
+#include <config.h>
+#include "system.h"
+
+#include "ls.h"
+/* Ensure that the main for ls is declared even if the tool is not being built
+ in this single-binary. */
+int single_binary_main_ls (int argc, char **argv);
+int single_binary_main_vdir (int argc, char **argv);
+
+int
+single_binary_main_vdir (int argc, char** argv)
+{
+ ls_mode = LS_LONG_FORMAT;
+ return single_binary_main_ls (argc, argv);
+}
diff --git a/src/coreutils.c b/src/coreutils.c
new file mode 100644
index 0000000..4e44448
--- /dev/null
+++ b/src/coreutils.c
@@ -0,0 +1,206 @@
+/* Copyright (C) 2014-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* coreutils.c aggregates the functionality of every other tool into a single
+ binary multiplexed by the value of argv[0]. This is enabled by passing
+ --enable-single-binary to configure.
+
+ Written by Alex Deymo <deymo@chromium.org>. */
+
+#include <config.h>
+#include <getopt.h>
+#include <stdio.h>
+#if HAVE_PRCTL
+# include <sys/prctl.h>
+#endif
+
+#include "system.h"
+#include "error.h"
+#include "quote.h"
+
+#ifdef SINGLE_BINARY
+/* Declare the main function on each one of the selected tools. This name
+ needs to match the one passed as CFLAGS on single-binary.mk (generated
+ by gen-single-binary.sh). */
+# define SINGLE_BINARY_PROGRAM(prog_name_str, main_name) \
+ int single_binary_main_##main_name (int, char **);
+# include "coreutils.h"
+# undef SINGLE_BINARY_PROGRAM
+#endif
+
+/* The official name of this program (e.g., no 'g' prefix). */
+#define PROGRAM_NAME "coreutils"
+
+#define AUTHORS \
+ proper_name ("Alex Deymo")
+
+static struct option const long_options[] =
+{
+ {GETOPT_HELP_OPTION_DECL},
+ {GETOPT_VERSION_OPTION_DECL},
+ {NULL, 0, NULL, 0}
+};
+
+
+void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ emit_try_help ();
+ else
+ {
+ printf (_("\
+Usage: %s --coreutils-prog=PROGRAM_NAME [PARAMETERS]... \n"),
+ program_name);
+ 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);
+
+#ifdef SINGLE_BINARY
+/* XXX: Ideally we'd like to present "install" here, not "ginstall". */
+ char const *prog_name_list =
+# define SINGLE_BINARY_PROGRAM(prog_name_str, main_name) " " prog_name_str
+# include "coreutils.h"
+# undef SINGLE_BINARY_PROGRAM
+ ;
+ printf ("\n\
+Built-in programs:\n\
+%s\n", prog_name_list);
+#endif
+
+ printf (_("\
+\n\
+Use: '%s --coreutils-prog=PROGRAM_NAME --help' for individual program help.\n"),
+ program_name);
+ emit_ancillary_info (PROGRAM_NAME);
+ }
+ exit (status);
+}
+
+static void
+launch_program (const char *prog_name, int prog_argc, char **prog_argv)
+{
+ int (*prog_main) (int, char **) = NULL;
+
+ /* Ensure that at least one parameter was passed. */
+ if (!prog_argc || !prog_argv || !prog_argv[0] || !prog_name)
+ return;
+
+#ifdef SINGLE_BINARY
+ if (false);
+ /* Look up the right main program. */
+# define SINGLE_BINARY_PROGRAM(prog_name_str, main_name) \
+ else if (STREQ (prog_name_str, prog_name)) \
+ prog_main = single_binary_main_##main_name;
+# include "coreutils.h"
+# undef SINGLE_BINARY_PROGRAM
+#endif
+
+ if (! prog_main)
+ return;
+
+#if HAVE_PRCTL && defined PR_SET_NAME
+ /* Not being able to set the program name is not a fatal error. */
+ prctl (PR_SET_NAME, prog_argv[0]);
+#endif
+#if HAVE_PRCTL && defined PR_SET_MM_ARG_START
+ /* Shift the beginning of the command line to prog_argv[0] (if set) so
+ /proc/pid/cmdline reflects the right value. */
+ prctl (PR_SET_MM_ARG_START, prog_argv[0]);
+#endif
+
+ exit (prog_main (prog_argc, prog_argv));
+}
+
+int
+main (int argc, char **argv)
+{
+ char *prog_name = last_component (argv[0]);
+ int optc;
+
+ /* Map external name to internal name. */
+ char ginstall[] = "ginstall";
+ if (STREQ (prog_name, "install"))
+ prog_name = ginstall;
+
+ /* If this program is called directly as "coreutils" or if the value of
+ argv[0] is an unknown tool (which "coreutils" is), we proceed and parse
+ the options. */
+ launch_program (prog_name, argc, argv);
+
+ /* No known program was selected via argv[0]. Try parsing the first
+ argument as --coreutils-prog=PROGRAM to determine the program. The
+ invocation for this case should be:
+ path/to/coreutils --coreutils-prog=someprog someprog ...
+ The third argument is what the program will see as argv[0]. */
+
+ if (argc >= 2)
+ {
+ size_t nskip = 0;
+ char *arg_name = NULL;
+
+ /* If calling coreutils directly, the "script" name isn't passed.
+ Distinguish the two cases with a -shebang suffix. */
+ if (STRPREFIX (argv[1], "--coreutils-prog="))
+ {
+ nskip = 1;
+ arg_name = prog_name = argv[1] + strlen ("--coreutils-prog=");
+ }
+ else if (STRPREFIX (argv[1], "--coreutils-prog-shebang="))
+ {
+ nskip = 2;
+ prog_name = argv[1] + strlen ("--coreutils-prog-shebang=");
+ if (argc >= 3)
+ arg_name = last_component (argv[2]);
+ else
+ arg_name = prog_name;
+ }
+
+ if (nskip)
+ {
+ 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. */
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+ atexit (close_stdout);
+
+ if ((optc = getopt_long (argc, argv, "", long_options, NULL)) != -1)
+ switch (optc)
+ {
+ case_GETOPT_HELP_CHAR;
+
+ 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 a4b5157..1b95132 100644
--- a/src/cp-hash.c
+++ b/src/cp-hash.c
@@ -1,10 +1,10 @@
/* cp-hash.c -- file copying (hash search routines)
- Copyright (C) 89, 90, 91, 1995-2005 Free Software Foundation.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,22 +12,17 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
Written by Torbjorn Granlund, Sweden (tege@sics.se).
Rewritten to use lib/hash.c by Jim Meyering. */
#include <config.h>
-#include <stdio.h>
#include <sys/types.h>
#include "system.h"
-#include "same.h"
-#include "quote.h"
#include "hash.h"
-#include "error.h"
#include "cp-hash.h"
/* Use ST_DEV and ST_INO as the key, FILENAME as the value.
@@ -64,7 +59,7 @@ src_to_dest_hash (void const *x, size_t table_size)
}
/* Compare two Src_to_dest entries.
- Return true if their keys are judged `equal'. */
+ Return true if their keys are judged 'equal'. */
static bool
src_to_dest_compare (void const *x, void const *y)
{
@@ -98,24 +93,6 @@ forget_created (ino_t ino, dev_t dev)
src_to_dest_free (ent);
}
-/* Add FILE to the list of files that we have created.
- Return true if successful. */
-
-extern bool
-remember_created (char const *file)
-{
- struct stat sb;
-
- if (stat (file, &sb) < 0)
- {
- error (0, errno, "%s", quote (file));
- return false;
- }
-
- remember_copied (file, sb.st_ino, sb.st_dev);
- return true;
-}
-
/* If INO/DEV correspond to an already-copied source file, return the
name of the corresponding destination file. Otherwise, return NULL. */
@@ -154,7 +131,7 @@ remember_copied (const char *name, ino_t ino, dev_t dev)
/* Determine whether there was already an entry in the table
with a matching key. If so, free ENT (it wasn't inserted) and
- return the `name' from the table entry. */
+ return the 'name' from the table entry. */
if (ent_from_table != ent)
{
src_to_dest_free (ent);
@@ -170,14 +147,14 @@ extern void
hash_init (void)
{
src_to_dest = hash_initialize (INITIAL_TABLE_SIZE, NULL,
- src_to_dest_hash,
- src_to_dest_compare,
- src_to_dest_free);
+ src_to_dest_hash,
+ src_to_dest_compare,
+ src_to_dest_free);
if (src_to_dest == NULL)
xalloc_die ();
}
-/* Reset the hash structure in the global variable `htab' to
+/* Reset the hash structure in the global variable 'htab' to
contain no entries. */
extern void
diff --git a/src/cp-hash.h b/src/cp-hash.h
index d142925..c2c5085 100644
--- a/src/cp-hash.h
+++ b/src/cp-hash.h
@@ -2,5 +2,4 @@ void hash_init (void);
void forget_all (void);
void forget_created (ino_t ino, dev_t dev);
char *remember_copied (const char *node, ino_t ino, dev_t dev);
-bool remember_created (char const *file);
char *src_to_dest_lookup (ino_t ino, dev_t dev);
diff --git a/src/cp.c b/src/cp.c
index 5759e0d..3b5b6cb 100644
--- a/src/cp.c
+++ b/src/cp.c
@@ -1,10 +1,10 @@
/* cp.c -- file copying (main routines)
- Copyright (C) 89, 90, 91, 1995-2007 Free Software Foundation.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
Written by Torbjorn Granlund, David MacKenzie, and Jim Meyering. */
@@ -21,6 +20,7 @@
#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>
+#include <selinux/selinux.h>
#include "system.h"
#include "argmatch.h"
@@ -29,13 +29,16 @@
#include "cp-hash.h"
#include "error.h"
#include "filenamecat.h"
-#include "lchmod.h"
+#include "ignore-value.h"
#include "quote.h"
-#include "quotearg.h"
#include "stat-time.h"
#include "utimens.h"
#include "acl.h"
+#if ! HAVE_LCHOWN
+# define lchown(name, uid, gid) chown (name, uid, gid)
+#endif
+
#define ASSIGN_BASENAME_STRDUPA(Dest, File_name) \
do \
{ \
@@ -46,17 +49,20 @@
} \
while (0)
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "cp"
-#define AUTHORS "Torbjorn Granlund", "David MacKenzie", "Jim Meyering"
+#define AUTHORS \
+ proper_name_utf8 ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Jim Meyering")
/* Used by do_copy, make_dir_parents_private, and re_protect
to keep a list of leading directories whose protections
need to be fixed after copying. */
struct dir_attr
{
- mode_t mode;
+ struct stat st;
bool restore_mode;
size_t slash_offset;
struct dir_attr *next;
@@ -66,24 +72,19 @@ struct dir_attr
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
{
- COPY_CONTENTS_OPTION = CHAR_MAX + 1,
+ ATTRIBUTES_ONLY_OPTION = CHAR_MAX + 1,
+ COPY_CONTENTS_OPTION,
NO_PRESERVE_ATTRIBUTES_OPTION,
PARENTS_OPTION,
PRESERVE_ATTRIBUTES_OPTION,
- REPLY_OPTION,
+ REFLINK_OPTION,
SPARSE_OPTION,
STRIP_TRAILING_SLASHES_OPTION,
UNLINK_DEST_BEFORE_OPENING
};
-/* Initial number of entries in each hash table entry's table of inodes. */
-#define INITIAL_HASH_MODULE 100
-
-/* Initial number of entries in the inode hash table. */
-#define INITIAL_ENTRY_TAB_SIZE 70
-
-/* The invocation name of this program. */
-char *program_name;
+/* True if the kernel is SELinux enabled. */
+static bool selinux_enabled;
/* If true, the command "cp x/e_file e_dir" uses "e_dir/x/e_file"
as its destination instead of the usual "e_dir/e_file." */
@@ -102,27 +103,27 @@ static enum Sparse_type const sparse_type[] =
};
ARGMATCH_VERIFY (sparse_type_string, sparse_type);
-/* Valid arguments to the `--reply' option. */
-static char const* const reply_args[] =
+static char const *const reflink_type_string[] =
{
- "yes", "no", "query", NULL
+ "auto", "always", NULL
};
-/* The values that correspond to the above strings. */
-static int const reply_vals[] =
+static enum Reflink_type const reflink_type[] =
{
- I_ALWAYS_YES, I_ALWAYS_NO, I_ASK_USER
+ REFLINK_AUTO, REFLINK_ALWAYS
};
-ARGMATCH_VERIFY (reply_args, reply_vals);
+ARGMATCH_VERIFY (reflink_type_string, reflink_type);
static struct option const long_opts[] =
{
{"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},
{"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'},
{"no-dereference", no_argument, NULL, 'P'},
{"no-preserve", required_argument, NULL, NO_PRESERVE_ATTRIBUTES_OPTION},
{"no-target-directory", no_argument, NULL, 'T'},
@@ -132,15 +133,15 @@ static struct option const long_opts[] =
{"preserve", optional_argument, NULL, PRESERVE_ATTRIBUTES_OPTION},
{"recursive", no_argument, NULL, 'R'},
{"remove-destination", no_argument, NULL, UNLINK_DEST_BEFORE_OPENING},
- {"reply", required_argument, NULL, REPLY_OPTION}, /* Deprecated 2005-07-03,
- remove in 2008. */
{"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, NULL, 'S'},
{"symbolic-link", no_argument, NULL, 's'},
{"target-directory", required_argument, NULL, 't'},
{"update", no_argument, NULL, 'u'},
{"verbose", no_argument, NULL, 'v'},
+ {GETOPT_SELINUX_CONTEXT_OPTION_DECL},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
@@ -150,8 +151,7 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
@@ -159,39 +159,47 @@ Usage: %s [OPTION]... [-T] SOURCE DEST\n\
or: %s [OPTION]... SOURCE... DIRECTORY\n\
or: %s [OPTION]... -t DIRECTORY SOURCE...\n\
"),
- program_name, program_name, program_name);
+ program_name, program_name, program_name);
fputs (_("\
Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
+
+ emit_mandatory_arg_note ();
+
fputs (_("\
- -a, --archive same as -dpPR\n\
- --backup[=CONTROL] make a backup of each existing destination file\n\
+ -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=link\n\
+ -d same as --no-dereference --preserve=links\n\
"), stdout);
fputs (_("\
-f, --force if an existing destination file cannot be\n\
- opened, remove it and try again\n\
- -i, --interactive prompt before overwrite\n\
- -H follow command-line symbolic links\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 link files instead of copying\n\
- -L, --dereference always follow symbolic links\n\
+ -l, --link hard link files instead of copying\n\
+ -L, --dereference always follow symbolic links in SOURCE\n\
"), stdout);
fputs (_("\
- -P, --no-dereference never follow symbolic links\n\
+ -n, --no-clobber do not overwrite an existing file (overrides\n\
+ a previous -i option)\n\
+ -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 (default:\n\
mode,ownership,timestamps), if possible\n\
- additional attributes: links, all\n\
+ additional attributes: context, links, xattr,\
+\n\
+ all\n\
"), stdout);
fputs (_("\
--no-preserve=ATTR_LIST don't preserve the specified attributes\n\
@@ -199,11 +207,12 @@ Mandatory arguments to long options are mandatory for short options too.\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);
+ attempting to open it (contrast with --force)\
+\n"), stdout);
fputs (_("\
- --sparse=WHEN control creation of sparse files\n\
+ --sparse=WHEN control creation of sparse files. See below\n\
--strip-trailing-slashes remove any trailing slashes from each SOURCE\n\
argument\n\
"), stdout);
@@ -220,6 +229,12 @@ Mandatory arguments to long options are mandatory for short options too.\n\
-v, --verbose explain what is being done\n\
-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);
fputs (_("\
@@ -230,9 +245,13 @@ selected by --sparse=auto. Specify --sparse=always to create a sparse DEST\n\
file whenever the SOURCE file contains a long enough sequence of zero bytes.\n\
Use --sparse=never to inhibit creation of sparse files.\n\
\n\
+When --reflink[=always] is specified, perform a lightweight copy, where the\n\
+data blocks are copied only when modified. If this is not possible the copy\n\
+fails, or if --reflink=auto is specified, fall back to a standard copy.\n\
"), stdout);
fputs (_("\
-The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
+\n\
+The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
The version control method may be selected via the --backup option or through\n\
the VERSION_CONTROL environment variable. Here are the values:\n\
\n\
@@ -249,7 +268,7 @@ As a special case, cp makes a backup of SOURCE when the force and backup\n\
options are given and SOURCE and DEST are the same name for an existing,\n\
regular file.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -265,10 +284,10 @@ regular file.\n\
ATTR_LIST is a null-terminated linked list of structures that
indicates the end of the filename of each intermediate directory
in CONST_DST_NAME that may need to have its attributes changed.
- The command `cp --parents --preserve a/b/c d/e_dir' changes the
+ The command 'cp --parents --preserve a/b/c d/e_dir' changes the
attributes of the directories d/e_dir/a and d/e_dir/a/b to match
the corresponding source directories regardless of whether they
- existed before the `cp' command was given.
+ existed before the 'cp' command was given.
Return true if the parent of CONST_DST_NAME and any intermediate
directories specified by ATTR_LIST have the proper permissions
@@ -276,72 +295,68 @@ regular file.\n\
static bool
re_protect (char const *const_dst_name, size_t src_offset,
- struct dir_attr *attr_list, const struct cp_options *x)
+ 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. */
- char *src_name; /* The source name in `dst_name'. */
+ char *src_name; /* The source name in 'dst_name'. */
ASSIGN_STRDUPA (dst_name, const_dst_name);
src_name = dst_name + src_offset;
for (p = attr_list; p; p = p->next)
{
- struct stat src_sb;
-
dst_name[p->slash_offset] = '\0';
- if (XSTAT (x, src_name, &src_sb))
- {
- error (0, errno, _("failed to get attributes of %s"),
- quote (src_name));
- return false;
- }
-
/* Adjust the times (and if possible, ownership) for the copy.
- chown turns off set[ug]id bits for non-root,
- so do the chmod last. */
+ chown turns off set[ug]id bits for non-root,
+ so do the chmod last. */
if (x->preserve_timestamps)
- {
- struct timespec timespec[2];
+ {
+ struct timespec timespec[2];
- timespec[0] = get_stat_atime (&src_sb);
- timespec[1] = get_stat_mtime (&src_sb);
+ timespec[0] = get_stat_atime (&p->st);
+ timespec[1] = get_stat_mtime (&p->st);
- if (utimens (dst_name, timespec))
- {
- error (0, errno, _("failed to preserve times for %s"),
- quote (dst_name));
- return false;
- }
- }
+ if (utimens (dst_name, timespec))
+ {
+ error (0, errno, _("failed to preserve times for %s"),
+ quoteaf (dst_name));
+ return false;
+ }
+ }
if (x->preserve_ownership)
- {
- if (chown (dst_name, src_sb.st_uid, src_sb.st_gid) != 0
- && ! chown_failure_ok (x))
- {
- error (0, errno, _("failed to preserve ownership for %s"),
- quote (dst_name));
- return false;
- }
- }
+ {
+ if (lchown (dst_name, p->st.st_uid, p->st.st_gid) != 0)
+ {
+ if (! chown_failure_ok (x))
+ {
+ error (0, errno, _("failed to preserve ownership for %s"),
+ quoteaf (dst_name));
+ return false;
+ }
+ /* Failing to preserve ownership is OK. Still, try to preserve
+ the group, but ignore the possible error. */
+ ignore_value (lchown (dst_name, -1, p->st.st_gid));
+ }
+ }
if (x->preserve_mode)
- {
- if (copy_acl (src_name, -1, dst_name, -1, src_sb.st_mode))
- return false;
- }
+ {
+ if (copy_acl (src_name, -1, dst_name, -1, p->st.st_mode) != 0)
+ return false;
+ }
else if (p->restore_mode)
- {
- if (lchmod (dst_name, p->mode) != 0)
- {
- error (0, errno, _("failed to preserve permissions for %s"),
- quote (dst_name));
- return false;
- }
- }
+ {
+ if (lchmod (dst_name, p->st.st_mode) != 0)
+ {
+ error (0, errno, _("failed to preserve permissions for %s"),
+ quoteaf (dst_name));
+ return false;
+ }
+ }
dst_name[p->slash_offset] = '/';
}
@@ -369,9 +384,9 @@ re_protect (char const *const_dst_name, size_t src_offset,
static bool
make_dir_parents_private (char const *const_dir, size_t src_offset,
- char const *verbose_fmt_string,
- struct dir_attr **attr_list, bool *new_dst,
- const struct cp_options *x)
+ char const *verbose_fmt_string,
+ 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. */
@@ -390,138 +405,149 @@ make_dir_parents_private (char const *const_dir, size_t src_offset,
*attr_list = NULL;
- if (XSTAT (x, dst_dir, &stats))
+ if (stat (dst_dir, &stats) != 0)
{
/* A parent of CONST_DIR does not exist.
- Make all missing intermediate directories. */
+ Make all missing intermediate directories. */
char *slash;
slash = src;
while (*slash == '/')
- slash++;
+ slash++;
while ((slash = strchr (slash, '/')))
- {
- /* Add this directory to the list of directories whose modes need
- fixing later. */
- struct dir_attr *new = xmalloc (sizeof *new);
- new->slash_offset = slash - dir;
- new->restore_mode = false;
- new->next = *attr_list;
- *attr_list = new;
-
- *slash = '\0';
- if (XSTAT (x, dir, &stats))
- {
- mode_t src_mode;
- mode_t omitted_permissions;
- mode_t mkdir_mode;
- int src_errno;
-
- /* This component does not exist. We must set
- *new_dst and new->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_errno = (XSTAT (x, src, &stats) != 0
- ? errno
- : S_ISDIR (stats.st_mode)
- ? 0
- : ENOTDIR);
- if (src_errno)
- {
- error (0, src_errno, _("failed to get attributes of %s"),
- quote (src));
- return false;
- }
- src_mode = stats.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));
-
- /* 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 = src_mode & CHMOD_MODE_BITS & ~omitted_permissions;
- if (mkdir (dir, mkdir_mode) != 0)
- {
- error (0, errno, _("cannot make directory %s"),
- quote (dir));
- return false;
- }
- else
- {
- if (verbose_fmt_string != NULL)
- printf (verbose_fmt_string, src, dir);
- }
-
- /* We need search and write permissions to the new directory
- for writing the directory's contents. Check if these
- permissions are there. */
-
- if (lstat (dir, &stats))
- {
- error (0, errno, _("failed to get attributes of %s"),
- quote (dir));
- return false;
- }
-
-
- if (! x->preserve_mode)
- {
- if (omitted_permissions & ~stats.st_mode)
- omitted_permissions &= ~ cached_umask ();
- if (omitted_permissions & ~stats.st_mode
- || (stats.st_mode & S_IRWXU) != S_IRWXU)
- {
- new->mode = stats.st_mode | omitted_permissions;
- new->restore_mode = true;
- }
- }
-
- if ((stats.st_mode & S_IRWXU) != S_IRWXU)
- {
- /* Make the new directory searchable and writable.
- The original permissions will be restored later. */
-
- if (lchmod (dir, stats.st_mode | S_IRWXU) != 0)
- {
- error (0, errno, _("setting permissions for %s"),
- quote (dir));
- return false;
- }
- }
- }
- else if (!S_ISDIR (stats.st_mode))
- {
- error (0, 0, _("%s exists but is not a directory"),
- quote (dir));
- return false;
- }
- else
- *new_dst = false;
- *slash++ = '/';
-
- /* Avoid unnecessary calls to `stat' when given
- file names containing multiple adjacent slashes. */
- while (*slash == '/')
- slash++;
- }
+ {
+ struct dir_attr *new IF_LINT ( = NULL);
+ bool missing_dir;
+
+ *slash = '\0';
+ missing_dir = (stat (dir, &stats) != 0);
+
+ if (missing_dir || x->preserve_ownership || x->preserve_mode
+ || x->preserve_timestamps)
+ {
+ /* Add this directory to the list of directories whose
+ modes might need fixing later. */
+ struct stat src_st;
+ int src_errno = (stat (src, &src_st) != 0
+ ? errno
+ : S_ISDIR (src_st.st_mode)
+ ? 0
+ : ENOTDIR);
+ if (src_errno)
+ {
+ error (0, src_errno, _("failed to get attributes of %s"),
+ quoteaf (src));
+ return false;
+ }
+
+ new = xmalloc (sizeof *new);
+ new->st = src_st;
+ new->slash_offset = slash - dir;
+ new->restore_mode = false;
+ new->next = *attr_list;
+ *attr_list = new;
+ }
+
+ 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;
+
+ /* 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));
+
+ /* 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 = src_mode & CHMOD_MODE_BITS & ~omitted_permissions;
+ if (mkdir (dir, mkdir_mode) != 0)
+ {
+ error (0, errno, _("cannot make directory %s"),
+ quoteaf (dir));
+ return false;
+ }
+ else
+ {
+ if (verbose_fmt_string != NULL)
+ printf (verbose_fmt_string, src, dir);
+ }
+
+ /* We need search and write permissions to the new directory
+ for writing the directory's contents. Check if these
+ permissions are there. */
+
+ if (lstat (dir, &stats))
+ {
+ error (0, errno, _("failed to get attributes of %s"),
+ quoteaf (dir));
+ return false;
+ }
+
+
+ if (! x->preserve_mode)
+ {
+ if (omitted_permissions & ~stats.st_mode)
+ omitted_permissions &= ~ cached_umask ();
+ if (omitted_permissions & ~stats.st_mode
+ || (stats.st_mode & S_IRWXU) != S_IRWXU)
+ {
+ new->st.st_mode = stats.st_mode | omitted_permissions;
+ new->restore_mode = true;
+ }
+ }
+
+ if ((stats.st_mode & S_IRWXU) != S_IRWXU)
+ {
+ /* Make the new directory searchable and writable.
+ The original permissions will be restored later. */
+
+ if (lchmod (dir, stats.st_mode | S_IRWXU) != 0)
+ {
+ error (0, errno, _("setting permissions for %s"),
+ quoteaf (dir));
+ return false;
+ }
+ }
+ }
+ else if (!S_ISDIR (stats.st_mode))
+ {
+ error (0, 0, _("%s exists but is not a directory"),
+ quoteaf (dir));
+ return false;
+ }
+ else
+ *new_dst = false;
+ *slash++ = '/';
+
+ /* Avoid unnecessary calls to 'stat' when given
+ file names containing multiple adjacent slashes. */
+ while (*slash == '/')
+ slash++;
+ }
}
/* We get here if the parent of DIR already exists. */
else if (!S_ISDIR (stats.st_mode))
{
- error (0, 0, _("%s exists but is not a directory"), quote (dst_dir));
+ error (0, 0, _("%s exists but is not a directory"), quoteaf (dst_dir));
return false;
}
else
@@ -548,7 +574,7 @@ target_directory_operand (char const *file, struct stat *st, bool *new_dst)
if (err)
{
if (err != ENOENT)
- error (EXIT_FAILURE, err, _("accessing %s"), quote (file));
+ error (EXIT_FAILURE, err, _("failed to access %s"), quoteaf (file));
*new_dst = true;
}
return is_a_dir;
@@ -559,7 +585,7 @@ target_directory_operand (char const *file, struct stat *st, bool *new_dst)
static bool
do_copy (int n_files, char **file, const char *target_directory,
- bool no_target_directory, struct cp_options *x)
+ bool no_target_directory, struct cp_options *x)
{
struct stat sb;
bool new_dst = false;
@@ -568,121 +594,133 @@ do_copy (int n_files, char **file, const char *target_directory,
if (n_files <= !target_directory)
{
if (n_files <= 0)
- error (0, 0, _("missing file operand"));
+ error (0, 0, _("missing file operand"));
else
- error (0, 0, _("missing destination file operand after %s"),
- quote (file[0]));
+ error (0, 0, _("missing destination file operand after %s"),
+ quoteaf (file[0]));
usage (EXIT_FAILURE);
}
if (no_target_directory)
{
if (target_directory)
- error (EXIT_FAILURE, 0,
- _("Cannot combine --target-directory (-t) "
- "and --no-target-directory (-T)"));
+ error (EXIT_FAILURE, 0,
+ _("cannot combine --target-directory (-t) "
+ "and --no-target-directory (-T)"));
if (2 < n_files)
- {
- error (0, 0, _("extra operand %s"), quote (file[2]));
- usage (EXIT_FAILURE);
- }
+ {
+ error (0, 0, _("extra operand %s"), quoteaf (file[2]));
+ usage (EXIT_FAILURE);
+ }
+ /* Update NEW_DST and SB, which may be checked below. */
+ ignore_value (target_directory_operand (file[n_files -1], &sb, &new_dst));
}
else if (!target_directory)
{
if (2 <= n_files
- && target_directory_operand (file[n_files - 1], &sb, &new_dst))
- target_directory = file[--n_files];
+ && target_directory_operand (file[n_files - 1], &sb, &new_dst))
+ target_directory = file[--n_files];
else if (2 < n_files)
- error (EXIT_FAILURE, 0, _("target %s is not a directory"),
- quote (file[n_files - 1]));
+ error (EXIT_FAILURE, 0, _("target %s is not a directory"),
+ quoteaf (file[n_files - 1]));
}
if (target_directory)
{
/* cp file1...filen edir
- Copy the files `file1' through `filen'
- to the existing directory `edir'. */
+ Copy the files 'file1' through 'filen'
+ to the existing directory 'edir'. */
int i;
/* Initialize these hash tables only if we'll need them.
- The problems they're used to detect can arise only if
- there are two or more files to copy. */
+ The problems they're used to detect can arise only if
+ there are two or more files to copy. */
if (2 <= n_files)
- {
- dest_info_init (x);
- src_info_init (x);
- }
+ {
+ dest_info_init (x);
+ src_info_init (x);
+ }
for (i = 0; i < n_files; i++)
- {
- char *dst_name;
- bool parent_exists = true; /* True if dir_name (dst_name) exists. */
- struct dir_attr *attr_list;
- char *arg_in_concat = NULL;
- char *arg = file[i];
-
- /* Trailing slashes are meaningful (i.e., maybe worth preserving)
- only in the source file names. */
- if (remove_trailing_slashes)
- strip_trailing_slashes (arg);
-
- if (parents_option)
- {
- char *arg_no_trailing_slash;
-
- /* Use `arg' without trailing slashes in constructing destination
- file names. Otherwise, we can end up trying to create a
- directory via `mkdir ("dst/foo/"...', which is not portable.
- It fails, due to the trailing slash, on at least
- NetBSD 1.[34] systems. */
- ASSIGN_STRDUPA (arg_no_trailing_slash, arg);
- strip_trailing_slashes (arg_no_trailing_slash);
-
- /* Append all of `arg' (minus any trailing slash) to `dest'. */
- dst_name = file_name_concat (target_directory,
- arg_no_trailing_slash,
- &arg_in_concat);
-
- /* For --parents, we have to make sure that the directory
- dir_name (dst_name) exists. We may have to create a few
- leading directories. */
- parent_exists =
- (make_dir_parents_private
- (dst_name, arg_in_concat - dst_name,
- (x->verbose ? "%s -> %s\n" : NULL),
- &attr_list, &new_dst, x));
- }
- else
- {
- char *arg_base;
- /* Append the last component of `arg' to `target_directory'. */
-
- ASSIGN_BASENAME_STRDUPA (arg_base, arg);
- /* For `cp -R source/.. dest', don't copy into `dest/..'. */
- dst_name = (STREQ (arg_base, "..")
- ? xstrdup (target_directory)
- : file_name_concat (target_directory, arg_base,
- NULL));
- }
-
- if (!parent_exists)
- {
- /* make_dir_parents_private failed, so don't even
- attempt the copy. */
- ok = false;
- }
- else
- {
- bool copy_into_self;
- ok &= copy (arg, dst_name, new_dst, x, &copy_into_self, NULL);
-
- if (parents_option)
- ok &= re_protect (dst_name, arg_in_concat - dst_name,
- attr_list, x);
- }
-
- free (dst_name);
- }
+ {
+ char *dst_name;
+ bool parent_exists = true; /* True if dir_name (dst_name) exists. */
+ struct dir_attr *attr_list;
+ char *arg_in_concat = NULL;
+ char *arg = file[i];
+
+ /* Trailing slashes are meaningful (i.e., maybe worth preserving)
+ only in the source file names. */
+ if (remove_trailing_slashes)
+ strip_trailing_slashes (arg);
+
+ if (parents_option)
+ {
+ char *arg_no_trailing_slash;
+
+ /* Use 'arg' without trailing slashes in constructing destination
+ file names. Otherwise, we can end up trying to create a
+ directory via 'mkdir ("dst/foo/"...', which is not portable.
+ It fails, due to the trailing slash, on at least
+ NetBSD 1.[34] systems. */
+ ASSIGN_STRDUPA (arg_no_trailing_slash, arg);
+ strip_trailing_slashes (arg_no_trailing_slash);
+
+ /* Append all of 'arg' (minus any trailing slash) to 'dest'. */
+ dst_name = file_name_concat (target_directory,
+ arg_no_trailing_slash,
+ &arg_in_concat);
+
+ /* For --parents, we have to make sure that the directory
+ dir_name (dst_name) exists. We may have to create a few
+ leading directories. */
+ parent_exists =
+ (make_dir_parents_private
+ (dst_name, arg_in_concat - dst_name,
+ (x->verbose ? "%s -> %s\n" : NULL),
+ &attr_list, &new_dst, x));
+ }
+ else
+ {
+ char *arg_base;
+ /* Append the last component of 'arg' to 'target_directory'. */
+
+ ASSIGN_BASENAME_STRDUPA (arg_base, arg);
+ /* For 'cp -R source/.. dest', don't copy into 'dest/..'. */
+ dst_name = (STREQ (arg_base, "..")
+ ? xstrdup (target_directory)
+ : file_name_concat (target_directory, arg_base,
+ NULL));
+ }
+
+ if (!parent_exists)
+ {
+ /* make_dir_parents_private failed, so don't even
+ attempt the copy. */
+ ok = false;
+ }
+ else
+ {
+ bool copy_into_self;
+ ok &= copy (arg, dst_name, new_dst, x, &copy_into_self, NULL);
+
+ if (parents_option)
+ ok &= re_protect (dst_name, arg_in_concat - dst_name,
+ attr_list, x);
+ }
+
+ if (parents_option)
+ {
+ while (attr_list)
+ {
+ struct dir_attr *p = attr_list;
+ attr_list = attr_list->next;
+ free (p);
+ }
+ }
+
+ free (dst_name);
+ }
}
else /* !target_directory */
{
@@ -692,39 +730,39 @@ do_copy (int n_files, char **file, const char *target_directory,
bool unused;
if (parents_option)
- {
- error (0, 0,
- _("with --parents, the destination must be a directory"));
- usage (EXIT_FAILURE);
- }
+ {
+ error (0, 0,
+ _("with --parents, the destination must be a directory"));
+ usage (EXIT_FAILURE);
+ }
/* When the force and backup options have been specified and
- the source and destination are the same name for an existing
- regular file, convert the user's command, e.g.,
- `cp --force --backup foo foo' to `cp --force foo fooSUFFIX'
- where SUFFIX is determined by any version control options used. */
+ the source and destination are the same name for an existing
+ regular file, convert the user's command, e.g.,
+ 'cp --force --backup foo foo' to 'cp --force foo fooSUFFIX'
+ where SUFFIX is determined by any version control options used. */
if (x->unlink_dest_after_failed_open
- && x->backup_type != no_backups
- && STREQ (source, dest)
- && !new_dst && S_ISREG (sb.st_mode))
- {
- static struct cp_options x_tmp;
-
- new_dest = find_backup_file_name (dest, x->backup_type);
- /* Set x->backup_type to `no_backups' so that the normal backup
- mechanism is not used when performing the actual copy.
- backup_type must be set to `no_backups' only *after* the above
- call to find_backup_file_name -- that function uses
- backup_type to determine the suffix it applies. */
- x_tmp = *x;
- x_tmp.backup_type = no_backups;
- x = &x_tmp;
- }
+ && x->backup_type != no_backups
+ && STREQ (source, dest)
+ && !new_dst && S_ISREG (sb.st_mode))
+ {
+ static struct cp_options x_tmp;
+
+ new_dest = find_backup_file_name (dest, x->backup_type);
+ /* Set x->backup_type to 'no_backups' so that the normal backup
+ mechanism is not used when performing the actual copy.
+ backup_type must be set to 'no_backups' only *after* the above
+ call to find_backup_file_name -- that function uses
+ backup_type to determine the suffix it applies. */
+ x_tmp = *x;
+ x_tmp.backup_type = no_backups;
+ x = &x_tmp;
+ }
else
- {
- new_dest = dest;
- }
+ {
+ new_dest = dest;
+ }
ok = copy (source, new_dest, 0, x, &unused, NULL);
}
@@ -735,21 +773,30 @@ do_copy (int n_files, char **file, const char *target_directory,
static void
cp_option_init (struct cp_options *x)
{
+ cp_options_default (x);
x->copy_as_regular = true;
x->dereference = DEREF_UNDEFINED;
x->unlink_dest_before_opening = false;
x->unlink_dest_after_failed_open = false;
x->hard_link = false;
x->interactive = I_UNSPECIFIED;
- x->chown_privileges = chown_privileges ();
x->move_mode = false;
x->one_file_system = false;
+ x->reflink_mode = REFLINK_NEVER;
x->preserve_ownership = false;
x->preserve_links = false;
x->preserve_mode = false;
x->preserve_timestamps = false;
-
+ 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 = false; /* -Z, set sys default context. */
+ x->preserve_xattr = false;
+ x->reduce_diagnostics = false;
+ x->require_preserve_xattr = false;
+
+ x->data_copy_required = true;
x->require_preserve = false;
x->recursive = false;
x->sparse_mode = SPARSE_AUTO;
@@ -762,6 +809,13 @@ cp_option_init (struct cp_options *x)
x->update = false;
x->verbose = false;
+
+ /* By default, refuse to open a dangling destination symlink, because
+ 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") != NULL;
+
x->dest_info = NULL;
x->src_info = NULL;
}
@@ -777,18 +831,21 @@ decode_preserve_arg (char const *arg, struct cp_options *x, bool on_off)
PRESERVE_TIMESTAMPS,
PRESERVE_OWNERSHIP,
PRESERVE_LINK,
+ PRESERVE_CONTEXT,
+ PRESERVE_XATTR,
PRESERVE_ALL
};
static enum File_attribute const preserve_vals[] =
{
PRESERVE_MODE, PRESERVE_TIMESTAMPS,
- PRESERVE_OWNERSHIP, PRESERVE_LINK, PRESERVE_ALL
+ PRESERVE_OWNERSHIP, PRESERVE_LINK, PRESERVE_CONTEXT, PRESERVE_XATTR,
+ PRESERVE_ALL
};
- /* Valid arguments to the `--preserve' option. */
+ /* Valid arguments to the '--preserve' option. */
static char const* const preserve_args[] =
{
"mode", "timestamps",
- "ownership", "links", "all", NULL
+ "ownership", "links", "context", "xattr", "all", NULL
};
ARGMATCH_VERIFY (preserve_args, preserve_vals);
@@ -802,38 +859,54 @@ decode_preserve_arg (char const *arg, struct cp_options *x, bool on_off)
/* If we found a comma, put a NUL in its place and advance. */
if (comma)
- *comma++ = 0;
+ *comma++ = 0;
/* process S. */
- val = XARGMATCH ("--preserve", s, preserve_args, preserve_vals);
+ val = XARGMATCH (on_off ? "--preserve" : "--no-preserve",
+ s, preserve_args, preserve_vals);
switch (val)
- {
- case PRESERVE_MODE:
- x->preserve_mode = on_off;
- break;
-
- case PRESERVE_TIMESTAMPS:
- x->preserve_timestamps = on_off;
- break;
-
- case PRESERVE_OWNERSHIP:
- x->preserve_ownership = on_off;
- break;
-
- case PRESERVE_LINK:
- x->preserve_links = on_off;
- break;
-
- case PRESERVE_ALL:
- x->preserve_mode = on_off;
- x->preserve_timestamps = on_off;
- x->preserve_ownership = on_off;
- x->preserve_links = on_off;
- break;
-
- default:
- abort ();
- }
+ {
+ case PRESERVE_MODE:
+ x->preserve_mode = on_off;
+ x->explicit_no_preserve_mode = !on_off;
+ break;
+
+ case PRESERVE_TIMESTAMPS:
+ x->preserve_timestamps = on_off;
+ break;
+
+ case PRESERVE_OWNERSHIP:
+ x->preserve_ownership = on_off;
+ break;
+
+ case PRESERVE_LINK:
+ x->preserve_links = on_off;
+ break;
+
+ case PRESERVE_CONTEXT:
+ x->require_preserve_context = on_off;
+ x->preserve_security_context = on_off;
+ break;
+
+ case PRESERVE_XATTR:
+ x->preserve_xattr = on_off;
+ x->require_preserve_xattr = on_off;
+ break;
+
+ case PRESERVE_ALL:
+ x->preserve_mode = on_off;
+ x->preserve_timestamps = on_off;
+ x->preserve_ownership = on_off;
+ x->preserve_links = on_off;
+ x->explicit_no_preserve_mode = !on_off;
+ if (selinux_enabled)
+ x->preserve_security_context = on_off;
+ x->preserve_xattr = on_off;
+ break;
+
+ default:
+ abort ();
+ }
s = comma;
}
while (s);
@@ -853,220 +926,297 @@ main (int argc, char **argv)
bool copy_contents = false;
char *target_directory = NULL;
bool no_target_directory = false;
+ char const *scontext = NULL;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
- atexit (close_stdout);
+ atexit (close_stdin);
+ selinux_enabled = (0 < is_selinux_enabled ());
cp_option_init (&x);
/* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
we'll actually use backup_suffix_string. */
backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
- while ((c = getopt_long (argc, argv, "abdfHilLprst:uvxPRS:T",
- long_opts, NULL))
- != -1)
+ while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ",
+ long_opts, NULL))
+ != -1)
{
switch (c)
- {
- case SPARSE_OPTION:
- x.sparse_mode = XARGMATCH ("--sparse", optarg,
- sparse_type_string, sparse_type);
- break;
-
- case 'a': /* Like -dpPR. */
- x.dereference = DEREF_NEVER;
- x.preserve_links = true;
- x.preserve_ownership = true;
- x.preserve_mode = true;
- x.preserve_timestamps = true;
- x.require_preserve = true;
- x.recursive = true;
- break;
-
- case 'b':
- make_backups = true;
- if (optarg)
- version_control_string = optarg;
- break;
-
- case COPY_CONTENTS_OPTION:
- copy_contents = true;
- break;
-
- case 'd':
- x.preserve_links = true;
- x.dereference = DEREF_NEVER;
- break;
-
- case 'f':
- x.unlink_dest_after_failed_open = true;
- break;
-
- case 'H':
- x.dereference = DEREF_COMMAND_LINE_ARGUMENTS;
- break;
-
- case 'i':
- x.interactive = I_ASK_USER;
- break;
-
- case 'l':
- x.hard_link = true;
- break;
-
- case 'L':
- x.dereference = DEREF_ALWAYS;
- break;
-
- case 'P':
- x.dereference = DEREF_NEVER;
- break;
-
- case NO_PRESERVE_ATTRIBUTES_OPTION:
- decode_preserve_arg (optarg, &x, false);
- break;
-
- case PRESERVE_ATTRIBUTES_OPTION:
- if (optarg == NULL)
- {
- /* Fall through to the case for `p' below. */
- }
- else
- {
- decode_preserve_arg (optarg, &x, true);
- x.require_preserve = true;
- break;
- }
-
- case 'p':
- x.preserve_ownership = true;
- x.preserve_mode = true;
- x.preserve_timestamps = true;
- x.require_preserve = true;
- break;
-
- case PARENTS_OPTION:
- parents_option = true;
- break;
-
- case 'r':
- case 'R':
- x.recursive = true;
- break;
-
- case REPLY_OPTION: /* Deprecated */
- x.interactive = XARGMATCH ("--reply", optarg,
- reply_args, reply_vals);
- error (0, 0,
- _("the --reply option is deprecated; use -i or -f instead"));
- break;
-
- case UNLINK_DEST_BEFORE_OPENING:
- x.unlink_dest_before_opening = true;
- break;
-
- case STRIP_TRAILING_SLASHES_OPTION:
- remove_trailing_slashes = true;
- break;
-
- case 's':
- x.symbolic_link = true;
- break;
-
- case 't':
- if (target_directory)
- error (EXIT_FAILURE, 0,
- _("multiple target directories specified"));
- else
- {
- struct stat st;
- if (stat (optarg, &st) != 0)
- error (EXIT_FAILURE, errno, _("accessing %s"), quote (optarg));
- if (! S_ISDIR (st.st_mode))
- error (EXIT_FAILURE, 0, _("target %s is not a directory"),
- quote (optarg));
- }
- target_directory = optarg;
- break;
-
- case 'T':
- no_target_directory = true;
- break;
-
- case 'u':
- x.update = true;
- break;
-
- case 'v':
- x.verbose = true;
- break;
-
- case 'x':
- x.one_file_system = true;
- break;
-
- case 'S':
- make_backups = true;
- backup_suffix_string = optarg;
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case SPARSE_OPTION:
+ x.sparse_mode = XARGMATCH ("--sparse", optarg,
+ sparse_type_string, sparse_type);
+ break;
+
+ case REFLINK_OPTION:
+ if (optarg == NULL)
+ x.reflink_mode = REFLINK_ALWAYS;
+ else
+ x.reflink_mode = XARGMATCH ("--reflink", optarg,
+ reflink_type_string, reflink_type);
+ break;
+
+ case 'a':
+ /* Like -dR --preserve=all with reduced failure diagnostics. */
+ x.dereference = DEREF_NEVER;
+ x.preserve_links = true;
+ x.preserve_ownership = true;
+ x.preserve_mode = true;
+ x.preserve_timestamps = true;
+ x.require_preserve = true;
+ if (selinux_enabled)
+ x.preserve_security_context = true;
+ x.preserve_xattr = true;
+ x.reduce_diagnostics = true;
+ x.recursive = true;
+ break;
+
+ case 'b':
+ make_backups = true;
+ if (optarg)
+ version_control_string = optarg;
+ break;
+
+ case ATTRIBUTES_ONLY_OPTION:
+ x.data_copy_required = false;
+ break;
+
+ case COPY_CONTENTS_OPTION:
+ copy_contents = true;
+ break;
+
+ case 'd':
+ x.preserve_links = true;
+ x.dereference = DEREF_NEVER;
+ break;
+
+ case 'f':
+ x.unlink_dest_after_failed_open = true;
+ break;
+
+ case 'H':
+ x.dereference = DEREF_COMMAND_LINE_ARGUMENTS;
+ break;
+
+ case 'i':
+ x.interactive = I_ASK_USER;
+ break;
+
+ case 'l':
+ x.hard_link = true;
+ break;
+
+ case 'L':
+ x.dereference = DEREF_ALWAYS;
+ break;
+
+ case 'n':
+ x.interactive = I_ALWAYS_NO;
+ break;
+
+ case 'P':
+ x.dereference = DEREF_NEVER;
+ break;
+
+ case NO_PRESERVE_ATTRIBUTES_OPTION:
+ decode_preserve_arg (optarg, &x, false);
+ break;
+
+ case PRESERVE_ATTRIBUTES_OPTION:
+ if (optarg == NULL)
+ {
+ /* Fall through to the case for 'p' below. */
+ }
+ else
+ {
+ decode_preserve_arg (optarg, &x, true);
+ x.require_preserve = true;
+ break;
+ }
+
+ case 'p':
+ x.preserve_ownership = true;
+ x.preserve_mode = true;
+ x.preserve_timestamps = true;
+ x.require_preserve = true;
+ break;
+
+ case PARENTS_OPTION:
+ parents_option = true;
+ break;
+
+ case 'r':
+ case 'R':
+ x.recursive = true;
+ break;
+
+ case UNLINK_DEST_BEFORE_OPENING:
+ x.unlink_dest_before_opening = true;
+ break;
+
+ case STRIP_TRAILING_SLASHES_OPTION:
+ remove_trailing_slashes = true;
+ break;
+
+ case 's':
+ x.symbolic_link = true;
+ break;
+
+ case 't':
+ if (target_directory)
+ error (EXIT_FAILURE, 0,
+ _("multiple target directories specified"));
+ else
+ {
+ struct stat st;
+ if (stat (optarg, &st) != 0)
+ error (EXIT_FAILURE, errno, _("failed to access %s"),
+ quoteaf (optarg));
+ if (! S_ISDIR (st.st_mode))
+ error (EXIT_FAILURE, 0, _("target %s is not a directory"),
+ quoteaf (optarg));
+ }
+ target_directory = optarg;
+ break;
+
+ case 'T':
+ no_target_directory = true;
+ break;
+
+ case 'u':
+ x.update = true;
+ break;
+
+ case 'v':
+ x.verbose = true;
+ break;
+
+ case 'x':
+ x.one_file_system = true;
+ break;
+
+ case 'Z':
+ /* politely decline if we're not on a selinux-enabled kernel. */
+ if (selinux_enabled)
+ {
+ if (optarg)
+ scontext = optarg;
+ else
+ x.set_security_context = true;
+ }
+ else if (optarg)
+ {
+ error (0, 0,
+ _("warning: ignoring --context; "
+ "it requires an SELinux-enabled kernel"));
+ }
+ break;
+
+ case 'S':
+ make_backups = true;
+ backup_suffix_string = optarg;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
}
- if (x.hard_link & x.symbolic_link)
+ if (x.hard_link && x.symbolic_link)
{
error (0, 0, _("cannot make both hard and symbolic links"));
usage (EXIT_FAILURE);
}
+ if (make_backups && x.interactive == I_ALWAYS_NO)
+ {
+ error (0, 0,
+ _("options --backup and --no-clobber are mutually exclusive"));
+ usage (EXIT_FAILURE);
+ }
+
+ if (x.reflink_mode == REFLINK_ALWAYS && x.sparse_mode != SPARSE_AUTO)
+ {
+ error (0, 0, _("--reflink can be used only with --sparse=auto"));
+ usage (EXIT_FAILURE);
+ }
+
if (backup_suffix_string)
simple_backup_suffix = xstrdup (backup_suffix_string);
x.backup_type = (make_backups
- ? xget_version (_("backup type"),
- version_control_string)
- : no_backups);
+ ? xget_version (_("backup type"),
+ version_control_string)
+ : no_backups);
if (x.dereference == DEREF_UNDEFINED)
{
- if (x.recursive)
- /* This is compatible with FreeBSD. */
- x.dereference = DEREF_NEVER;
+ if (x.recursive && ! x.hard_link)
+ /* This is compatible with FreeBSD. */
+ x.dereference = DEREF_NEVER;
else
- x.dereference = DEREF_ALWAYS;
+ x.dereference = DEREF_ALWAYS;
}
- /* The key difference between -d (--no-dereference) and not is the version
- of `stat' to call. */
-
if (x.recursive)
x.copy_as_regular = copy_contents;
/* If --force (-f) was specified and we're in link-creation mode,
first remove any existing destination file. */
- if (x.unlink_dest_after_failed_open & (x.hard_link | x.symbolic_link))
+ if (x.unlink_dest_after_failed_open && (x.hard_link || x.symbolic_link))
x.unlink_dest_before_opening = true;
+ /* Ensure -Z overrides -a. */
+ if ((x.set_security_context || scontext)
+ && ! x.require_preserve_context)
+ x.preserve_security_context = false;
+
+ if (x.preserve_security_context && (x.set_security_context || scontext))
+ error (EXIT_FAILURE, 0,
+ _("cannot set target context and preserve it"));
+
+ if (x.require_preserve_context && ! selinux_enabled)
+ error (EXIT_FAILURE, 0,
+ _("cannot preserve security context "
+ "without an SELinux-enabled kernel"));
+
+ /* 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 (dst_path, 0, true);
+ */
+ if (scontext && setfscreatecon (se_const (scontext)) < 0)
+ error (EXIT_FAILURE, errno,
+ _("failed to set default file creation context to %s"),
+ quote (scontext));
+
+#if !USE_XATTR
+ if (x.require_preserve_xattr)
+ error (EXIT_FAILURE, 0, _("cannot preserve extended attributes, cp is "
+ "built without xattr support"));
+#endif
+
/* Allocate space for remembering copied and created files. */
hash_init ();
ok = do_copy (argc - optind, argv + optind,
- target_directory, no_target_directory, &x);
+ target_directory, no_target_directory, &x);
+#ifdef lint
forget_all ();
+#endif
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/csplit.c b/src/csplit.c
index c2105bc..ac92d4c 100644
--- a/src/csplit.c
+++ b/src/csplit.c
@@ -1,10 +1,10 @@
/* csplit - split a file into sections determined by context lines
- Copyright (C) 91, 1995-2007 Free Software Foundation, Inc.
+ Copyright (C) 1991-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,14 +12,14 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Stuart Kemp, cpsrk@groper.jcu.edu.au.
Modified by David MacKenzie, djm@gnu.ai.mit.edu. */
#include <config.h>
+#include <assert.h>
#include <getopt.h>
#include <sys/types.h>
#include <signal.h>
@@ -30,30 +30,18 @@
#include "error.h"
#include "fd-reopen.h"
-#include "inttostr.h"
#include "quote.h"
#include "safe-read.h"
#include "stdio--.h"
+#include "xdectoint.h"
#include "xstrtol.h"
-/* 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
-
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "csplit"
-#define AUTHORS "Stuart Kemp", "David MacKenzie"
-
-/* Increment size of area for control records. */
-#define ALLOC_SIZE 20
+#define AUTHORS \
+ proper_name ("Stuart Kemp"), \
+ proper_name ("David MacKenzie")
/* The default prefix for output file names. */
#define DEFAULT_PREFIX "xx"
@@ -65,7 +53,7 @@ struct control
uintmax_t lines_required; /* Number of lines required. */
uintmax_t repeat; /* Repeat count. */
int argnum; /* ARGV index. */
- bool repeat_forever; /* True if `*' used as a repeat count. */
+ bool repeat_forever; /* True if '*' used as a repeat count. */
bool ignore; /* If true, produce no output (for regexp). */
bool regexpr; /* True if regular expression was used. */
struct re_pattern_buffer re_compiled; /* Compiled regular expression. */
@@ -125,10 +113,6 @@ static void close_output_file (void);
static void create_output_file (void);
static void delete_all_files (bool);
static void save_line_to_file (const struct cstring *line);
-void usage (int status);
-
-/* The name this program was run with. */
-char *program_name;
/* Start of buffer list. */
static struct buffer_record *head = NULL;
@@ -136,7 +120,7 @@ static struct buffer_record *head = NULL;
/* Partially read line. */
static char *hold_area = NULL;
-/* Number of bytes in `hold_area'. */
+/* Number of bytes in 'hold_area'. */
static size_t hold_count = 0;
/* Number of the last line in the buffers. */
@@ -184,16 +168,26 @@ static bool volatile remove_files;
/* If true, remove all output files which have a zero length. */
static bool elide_empty_files;
+/* If true, suppress the lines that match the PATTERN */
+static bool suppress_matched;
+
/* The compiled pattern arguments, which determine how to split
the input file. */
static struct control *controls;
-/* Number of elements in `controls'. */
+/* Number of elements in 'controls'. */
static size_t control_used;
/* The set of signals that are caught. */
static sigset_t caught_signals;
+/* For long options that have no equivalent short option, use a
+ non-character as a pseudo short option, starting with CHAR_MAX + 1. */
+enum
+{
+ SUPPRESS_MATCHED_OPTION = CHAR_MAX + 1
+};
+
static struct option const longopts[] =
{
{"digits", required_argument, NULL, 'n'},
@@ -203,6 +197,7 @@ static struct option const longopts[] =
{"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},
{NULL, 0, NULL, 0}
@@ -241,12 +236,11 @@ xalloc_die (void)
static void
interrupt_handler (int sig)
{
- if (! SA_NOCLDSTOP)
- signal (sig, SIG_IGN);
-
delete_all_files (true);
-
signal (sig, SIG_DFL);
+ /* The signal has been reset to SIG_DFL, but blocked during this
+ handler. Force the default action of this signal once the
+ handler returns and the block is removed. */
raise (sig);
}
@@ -361,11 +355,11 @@ record_line_starts (struct buffer_record *b)
line_start = b->buffer;
bytes_left = b->bytes_used;
- for (;;)
+ while (true)
{
line_end = memchr (line_start, '\n', bytes_left);
if (line_end == NULL)
- break;
+ break;
line_length = line_end - line_start + 1;
keep_new_line (b, line_start, line_length);
bytes_left -= line_length;
@@ -377,12 +371,12 @@ record_line_starts (struct buffer_record *b)
if (bytes_left)
{
if (have_read_eof)
- {
- keep_new_line (b, line_start, bytes_left);
- lines++;
- }
+ {
+ keep_new_line (b, line_start, bytes_left);
+ lines++;
+ }
else
- save_to_hold_area (xmemdup (line_start, bytes_left), bytes_left);
+ save_to_hold_area (xmemdup (line_start, bytes_left), bytes_left);
}
b->num_lines = lines;
@@ -437,6 +431,14 @@ get_new_buffer (size_t min_size)
static void
free_buffer (struct buffer_record *buf)
{
+ struct line *l;
+ for (l = buf->line_start; l;)
+ {
+ struct line *n = l->next;
+ free (l);
+ l = n;
+ }
+ buf->line_start = NULL;
free (buf->buffer);
buf->buffer = NULL;
}
@@ -457,7 +459,7 @@ save_buffer (struct buffer_record *buf)
else
{
for (p = head; p->next; p = p->next)
- /* Do nothing. */ ;
+ /* Do nothing. */ ;
p->next = buf;
}
}
@@ -498,27 +500,25 @@ load_buffer (void)
bytes_avail = b->bytes_alloc; /* Size of buffer returned. */
p = b->buffer;
- /* First check the `holding' area for a partial line. */
+ /* First check the 'holding' area for a partial line. */
if (hold_count)
- {
- memcpy (p, hold_area, hold_count);
- p += hold_count;
- b->bytes_used += hold_count;
- bytes_avail -= hold_count;
- hold_count = 0;
- }
+ {
+ memcpy (p, hold_area, hold_count);
+ p += hold_count;
+ b->bytes_used += hold_count;
+ bytes_avail -= hold_count;
+ hold_count = 0;
+ }
b->bytes_used += read_input (p, bytes_avail);
lines_found = record_line_starts (b);
- if (!lines_found)
- free_buffer (b);
if (lines_found || have_read_eof)
- break;
+ break;
if (xalloc_oversized (2, b->bytes_alloc))
- xalloc_die ();
+ xalloc_die ();
bytes_wanted = 2 * b->bytes_alloc;
free_buffer (b);
free (b);
@@ -527,7 +527,10 @@ load_buffer (void)
if (lines_found)
save_buffer (b);
else
- free (b);
+ {
+ free_buffer (b);
+ free (b);
+ }
return lines_found != 0;
}
@@ -561,6 +564,7 @@ remove_line (void)
if (prev_buf)
{
free_buffer (prev_buf);
+ free (prev_buf);
prev_buf = NULL;
}
@@ -582,13 +586,13 @@ remove_line (void)
/* Go on to the next line record. */
head->curr_line = l->next;
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
- once the line we're returning has been processed. */
- prev_buf = head;
- head = head->next;
- }
+ {
+ /* Go on to the next data block.
+ but first record the current one so we can free it
+ once the line we're returning has been processed. */
+ prev_buf = head;
+ head = head->next;
+ }
}
return line;
@@ -610,24 +614,25 @@ find_line (uintmax_t linenum)
for (b = head;;)
{
+ assert (b);
if (linenum < b->start_line + b->num_lines)
- {
- /* The line is in this buffer. */
- struct line *l;
- size_t offset; /* How far into the buffer the line is. */
-
- l = b->line_start;
- offset = linenum - b->start_line;
- /* Find the control record. */
- while (offset >= CTRL_SIZE)
- {
- l = l->next;
- offset -= CTRL_SIZE;
- }
- return &l->starts[offset];
- }
+ {
+ /* The line is in this buffer. */
+ struct line *l;
+ size_t offset; /* How far into the buffer the line is. */
+
+ l = b->line_start;
+ offset = linenum - b->start_line;
+ /* Find the control record. */
+ while (offset >= CTRL_SIZE)
+ {
+ l = l->next;
+ offset -= CTRL_SIZE;
+ }
+ return &l->starts[offset];
+ }
if (b->next == NULL && !load_buffer ())
- return NULL;
+ return NULL;
b = b->next; /* Try the next data block. */
}
}
@@ -646,7 +651,8 @@ static void
set_input_file (const char *name)
{
if (! STREQ (name, "-") && fd_reopen (STDIN_FILENO, name, O_RDONLY, 0) < 0)
- error (EXIT_FAILURE, errno, _("cannot open %s for reading"), quote (name));
+ error (EXIT_FAILURE, errno, _("cannot open %s for reading"),
+ quoteaf (name));
}
/* Write all lines from the beginning of the buffer up to, but
@@ -666,7 +672,8 @@ write_to_file (uintmax_t last_line, bool ignore, int argnum)
if (first_line > last_line)
{
- error (0, 0, _("%s: line number out of range"), global_argv[argnum]);
+ error (0, 0, _("%s: line number out of range"),
+ quote (global_argv[argnum]));
cleanup_fatal ();
}
@@ -676,12 +683,13 @@ write_to_file (uintmax_t last_line, bool ignore, int argnum)
{
line = remove_line ();
if (line == NULL)
- {
- error (0, 0, _("%s: line number out of range"), global_argv[argnum]);
- cleanup_fatal ();
- }
+ {
+ error (0, 0, _("%s: line number out of range"),
+ quote (global_argv[argnum]));
+ cleanup_fatal ();
+ }
if (!ignore)
- save_line_to_file (line);
+ save_line_to_file (line);
}
}
@@ -707,7 +715,7 @@ handle_line_error (const struct control *p, uintmax_t repetition)
char buf[INT_BUFSIZE_BOUND (uintmax_t)];
fprintf (stderr, _("%s: %s: line number out of range"),
- program_name, quote (umaxtostr (p->lines_required, buf)));
+ program_name, quote (umaxtostr (p->lines_required, buf)));
if (repetition)
fprintf (stderr, _(" on repetition %s\n"), umaxtostr (repetition, buf));
else
@@ -726,25 +734,32 @@ process_line_count (const struct control *p, uintmax_t repetition)
{
uintmax_t linenum;
uintmax_t last_line_to_save = p->lines_required * (repetition + 1);
- struct cstring *line;
create_output_file ();
- linenum = get_first_line_in_buffer ();
+ /* Ensure that the line number specified is not 1 greater than
+ the number of lines in the file.
+ When suppressing matched lines, check before the loop. */
+ if (no_more_lines () && suppress_matched)
+ handle_line_error (p, repetition);
+ linenum = get_first_line_in_buffer ();
while (linenum++ < last_line_to_save)
{
- line = remove_line ();
+ struct cstring *line = remove_line ();
if (line == NULL)
- handle_line_error (p, repetition);
+ handle_line_error (p, repetition);
save_line_to_file (line);
}
close_output_file ();
+ if (suppress_matched)
+ remove_line ();
+
/* Ensure that the line number specified is not 1 greater than
the number of lines in the file. */
- if (no_more_lines ())
+ if (no_more_lines () && !suppress_matched)
handle_line_error (p, repetition);
}
@@ -753,7 +768,7 @@ static void
regexp_error (struct control *p, uintmax_t repetition, bool ignore)
{
fprintf (stderr, _("%s: %s: match not found"),
- program_name, quote (global_argv[p->argnum]));
+ program_name, quote (global_argv[p->argnum]));
if (repetition)
{
@@ -787,81 +802,84 @@ process_regexp (struct control *p, uintmax_t repetition)
if (!ignore)
create_output_file ();
+ if (suppress_matched && current_line > 0)
+ remove_line ();
+
/* If there is no offset for the regular expression, or
it is positive, then it is not necessary to buffer the lines. */
if (p->offset >= 0)
{
- for (;;)
- {
- line = find_line (++current_line);
- if (line == NULL)
- {
- if (p->repeat_forever)
- {
- if (!ignore)
- {
- dump_rest_of_file ();
- close_output_file ();
- }
- exit (EXIT_SUCCESS);
- }
- else
- regexp_error (p, repetition, ignore);
- }
- line_len = line->len;
- if (line->str[line_len - 1] == '\n')
- line_len--;
- ret = re_search (&p->re_compiled, line->str, line_len,
- 0, line_len, NULL);
- if (ret == -2)
- {
- error (0, 0, _("error in regular expression search"));
- cleanup_fatal ();
- }
- if (ret == -1)
- {
- line = remove_line ();
- if (!ignore)
- save_line_to_file (line);
- }
- else
- break;
- }
+ while (true)
+ {
+ line = find_line (++current_line);
+ if (line == NULL)
+ {
+ if (p->repeat_forever)
+ {
+ if (!ignore)
+ {
+ dump_rest_of_file ();
+ close_output_file ();
+ }
+ exit (EXIT_SUCCESS);
+ }
+ else
+ regexp_error (p, repetition, ignore);
+ }
+ line_len = line->len;
+ if (line->str[line_len - 1] == '\n')
+ line_len--;
+ ret = re_search (&p->re_compiled, line->str, line_len,
+ 0, line_len, NULL);
+ if (ret == -2)
+ {
+ error (0, 0, _("error in regular expression search"));
+ cleanup_fatal ();
+ }
+ if (ret == -1)
+ {
+ line = remove_line ();
+ if (!ignore)
+ save_line_to_file (line);
+ }
+ else
+ break;
+ }
}
else
{
/* Buffer the lines. */
- for (;;)
- {
- line = find_line (++current_line);
- if (line == NULL)
- {
- if (p->repeat_forever)
- {
- if (!ignore)
- {
- dump_rest_of_file ();
- close_output_file ();
- }
- exit (EXIT_SUCCESS);
- }
- else
- regexp_error (p, repetition, ignore);
- }
- line_len = line->len;
- if (line->str[line_len - 1] == '\n')
- line_len--;
- ret = re_search (&p->re_compiled, line->str, line_len,
- 0, line_len, NULL);
- if (ret == -2)
- {
- error (0, 0, _("error in regular expression search"));
- cleanup_fatal ();
- }
- if (ret != -1)
- break;
- }
+ while (true)
+ {
+ line = find_line (++current_line);
+ if (line == NULL)
+ {
+ if (p->repeat_forever)
+ {
+ if (!ignore)
+ {
+ dump_rest_of_file ();
+ close_output_file ();
+ }
+ exit (EXIT_SUCCESS);
+ }
+ else
+ regexp_error (p, repetition, ignore);
+ }
+ line_len = line->len;
+ if (line->str[line_len - 1] == '\n')
+ line_len--;
+ ret = re_search (&p->re_compiled, line->str, line_len,
+ 0, line_len, NULL);
+ if (ret == -2)
+ {
+ error (0, 0, _("error in regular expression search"));
+ cleanup_fatal ();
+ }
+ if (ret != -1)
+ break;
+ }
}
/* Account for any offset from this regexp. */
@@ -887,17 +905,17 @@ split_file (void)
{
uintmax_t j;
if (controls[i].regexpr)
- {
- for (j = 0; (controls[i].repeat_forever
- || j <= controls[i].repeat); j++)
- process_regexp (&controls[i], j);
- }
+ {
+ for (j = 0; (controls[i].repeat_forever
+ || j <= controls[i].repeat); j++)
+ process_regexp (&controls[i], j);
+ }
else
- {
- for (j = 0; (controls[i].repeat_forever
- || j <= controls[i].repeat); j++)
- process_line_count (&controls[i], j);
- }
+ {
+ for (j = 0; (controls[i].repeat_forever
+ || j <= controls[i].repeat); j++)
+ process_line_count (&controls[i], j);
+ }
}
create_output_file ();
@@ -928,23 +946,31 @@ make_filename (unsigned int num)
static void
create_output_file (void)
{
- sigset_t oldset;
bool fopen_ok;
int fopen_errno;
output_filename = make_filename (files_created);
- /* Create the output file in a critical section, to avoid races. */
- sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
- output_stream = fopen (output_filename, "w");
- fopen_ok = (output_stream != NULL);
- fopen_errno = errno;
- files_created += fopen_ok;
- sigprocmask (SIG_SETMASK, &oldset, NULL);
+ if (files_created == UINT_MAX)
+ {
+ fopen_ok = false;
+ fopen_errno = EOVERFLOW;
+ }
+ else
+ {
+ /* Create the output file in a critical section, to avoid races. */
+ sigset_t oldset;
+ sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
+ output_stream = fopen (output_filename, "w");
+ fopen_ok = (output_stream != NULL);
+ fopen_errno = errno;
+ files_created += fopen_ok;
+ sigprocmask (SIG_SETMASK, &oldset, NULL);
+ }
if (! fopen_ok)
{
- error (0, fopen_errno, "%s", output_filename);
+ error (0, fopen_errno, "%s", quotef (output_filename));
cleanup_fatal ();
}
bytes_written = 0;
@@ -965,7 +991,7 @@ delete_all_files (bool in_signal_handler)
{
const char *name = make_filename (i);
if (unlink (name) != 0 && !in_signal_handler)
- error (0, errno, "%s", name);
+ error (0, errno, "%s", quotef (name));
}
files_created = 0;
@@ -980,41 +1006,41 @@ close_output_file (void)
if (output_stream)
{
if (ferror (output_stream))
- {
- error (0, 0, _("write error for %s"), quote (output_filename));
- output_stream = NULL;
- cleanup_fatal ();
- }
+ {
+ error (0, 0, _("write error for %s"), quoteaf (output_filename));
+ output_stream = NULL;
+ cleanup_fatal ();
+ }
if (fclose (output_stream) != 0)
- {
- error (0, errno, "%s", output_filename);
- output_stream = NULL;
- cleanup_fatal ();
- }
+ {
+ error (0, errno, "%s", quotef (output_filename));
+ output_stream = NULL;
+ cleanup_fatal ();
+ }
if (bytes_written == 0 && elide_empty_files)
- {
- sigset_t oldset;
- bool unlink_ok;
- int unlink_errno;
-
- /* Remove the output file in a critical section, to avoid races. */
- sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
- unlink_ok = (unlink (output_filename) == 0);
- unlink_errno = errno;
- files_created -= unlink_ok;
- sigprocmask (SIG_SETMASK, &oldset, NULL);
-
- if (! unlink_ok)
- error (0, unlink_errno, "%s", output_filename);
- }
+ {
+ sigset_t oldset;
+ bool unlink_ok;
+ int unlink_errno;
+
+ /* Remove the output file in a critical section, to avoid races. */
+ sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
+ unlink_ok = (unlink (output_filename) == 0);
+ unlink_errno = errno;
+ files_created -= unlink_ok;
+ sigprocmask (SIG_SETMASK, &oldset, NULL);
+
+ if (! unlink_ok)
+ error (0, unlink_errno, "%s", quotef (output_filename));
+ }
else
- {
- if (!suppress_count)
- {
- char buf[INT_BUFSIZE_BOUND (uintmax_t)];
- fprintf (stdout, "%s\n", umaxtostr (bytes_written, buf));
- }
- }
+ {
+ if (!suppress_count)
+ {
+ char buf[INT_BUFSIZE_BOUND (uintmax_t)];
+ fprintf (stdout, "%s\n", umaxtostr (bytes_written, buf));
+ }
+ }
output_stream = NULL;
}
}
@@ -1025,7 +1051,13 @@ close_output_file (void)
static void
save_line_to_file (const struct cstring *line)
{
- fwrite (line->str, sizeof (char), line->len, output_stream);
+ size_t l = fwrite (line->str, sizeof (char), line->len, output_stream);
+ if (l != line->len)
+ {
+ error (0, errno, _("write error for %s"), quoteaf (output_filename));
+ output_stream = NULL;
+ cleanup_fatal ();
+ }
bytes_written += line->len;
}
@@ -1057,7 +1089,8 @@ static void
check_for_offset (struct control *p, const char *str, const char *num)
{
if (xstrtoimax (num, NULL, 10, &p->offset, "") != LONGINT_OK)
- error (EXIT_FAILURE, 0, _("%s: integer expected after delimiter"), str);
+ error (EXIT_FAILURE, 0, _("%s: integer expected after delimiter"),
+ quote (str));
}
/* Given that the first character of command line arg STR is '{',
@@ -1073,7 +1106,8 @@ parse_repeat_count (int argnum, struct control *p, char *str)
end = str + strlen (str) - 1;
if (*end != '}')
- error (EXIT_FAILURE, 0, _("%s: `}' is required in repeat count"), str);
+ error (EXIT_FAILURE, 0, _("%s: '}' is required in repeat count"),
+ quote (str));
*end = '\0';
if (str+1 == end-1 && *(str+1) == '*')
@@ -1081,11 +1115,11 @@ parse_repeat_count (int argnum, struct control *p, char *str)
else
{
if (xstrtoumax (str + 1, NULL, 10, &val, "") != LONGINT_OK)
- {
- error (EXIT_FAILURE, 0,
- _("%s}: integer required between `{' and `}'"),
- global_argv[argnum]);
- }
+ {
+ error (EXIT_FAILURE, 0,
+ _("%s}: integer required between '{' and '}'"),
+ quote (global_argv[argnum]));
+ }
p->repeat = val;
}
@@ -1110,7 +1144,7 @@ extract_regexp (int argnum, bool ignore, char const *str)
closing_delim = strrchr (str + 1, delim);
if (closing_delim == NULL)
error (EXIT_FAILURE, 0,
- _("%s: closing delimiter `%c' missing"), str, delim);
+ _("%s: closing delimiter '%c' missing"), str, delim);
len = closing_delim - str - 1;
p = new_control_record ();
@@ -1127,7 +1161,7 @@ extract_regexp (int argnum, bool ignore, char const *str)
err = re_compile_pattern (str + 1, len, &p->re_compiled);
if (err)
{
- error (0, 0, _("%s: invalid regular expression: %s"), str, err);
+ error (0, 0, _("%s: invalid regular expression: %s"), quote (str), err);
cleanup_fatal ();
}
@@ -1151,122 +1185,105 @@ parse_patterns (int argc, int start, char **argv)
for (i = start; i < argc; i++)
{
if (*argv[i] == '/' || *argv[i] == '%')
- {
- p = extract_regexp (i, *argv[i] == '%', argv[i]);
- }
+ {
+ p = extract_regexp (i, *argv[i] == '%', argv[i]);
+ }
else
- {
- p = new_control_record ();
- p->argnum = i;
-
- if (xstrtoumax (argv[i], NULL, 10, &val, "") != LONGINT_OK)
- error (EXIT_FAILURE, 0, _("%s: invalid pattern"), argv[i]);
- if (val == 0)
- error (EXIT_FAILURE, 0,
- _("%s: line number must be greater than zero"),
- argv[i]);
- if (val < last_val)
- {
- char buf[INT_BUFSIZE_BOUND (uintmax_t)];
- error (EXIT_FAILURE, 0,
- _("line number %s is smaller than preceding line number, %s"),
- quote (argv[i]), umaxtostr (last_val, buf));
- }
-
- if (val == last_val)
- error (0, 0,
- _("warning: line number %s is the same as preceding line number"),
- quote (argv[i]));
-
- last_val = val;
-
- p->lines_required = val;
- }
+ {
+ p = new_control_record ();
+ p->argnum = i;
+
+ if (xstrtoumax (argv[i], NULL, 10, &val, "") != LONGINT_OK)
+ error (EXIT_FAILURE, 0, _("%s: invalid pattern"), quote (argv[i]));
+ if (val == 0)
+ error (EXIT_FAILURE, 0,
+ _("%s: line number must be greater than zero"),
+ argv[i]);
+ if (val < last_val)
+ {
+ char buf[INT_BUFSIZE_BOUND (uintmax_t)];
+ error (EXIT_FAILURE, 0,
+ _("line number %s is smaller than preceding line number, %s"),
+ quote (argv[i]), umaxtostr (last_val, buf));
+ }
+
+ if (val == last_val)
+ error (0, 0,
+ _("warning: line number %s is the same as preceding line number"),
+ quote (argv[i]));
+
+ last_val = val;
+
+ p->lines_required = val;
+ }
if (i + 1 < argc && *argv[i + 1] == '{')
- {
- /* We have a repeat count. */
- i++;
- parse_repeat_count (i, p, argv[i]);
- }
- }
-}
-
-static unsigned int
-get_format_flags (char **format_ptr)
-{
- unsigned int count = 0;
-
- for (; **format_ptr; (*format_ptr)++)
- {
- switch (**format_ptr)
- {
- case '-':
- break;
-
- case '+':
- case ' ':
- count |= 1;
- break;
-
- case '#':
- count |= 2; /* Allow for 0x prefix preceding an `x' conversion. */
- break;
-
- default:
- return count;
- }
+ {
+ /* We have a repeat count. */
+ i++;
+ parse_repeat_count (i, p, argv[i]);
+ }
}
- return count;
}
-static size_t
-get_format_width (char **format_ptr)
-{
- unsigned long int val = 0;
- if (ISDIGIT (**format_ptr)
- && (xstrtoul (*format_ptr, format_ptr, 10, &val, NULL) != LONGINT_OK
- || SIZE_MAX < val))
- error (EXIT_FAILURE, 0, _("invalid format width"));
- /* Allow for enough octal digits to represent the value of UINT_MAX,
- even if the field width is less than that. */
- return MAX (val, (sizeof (unsigned int) * CHAR_BIT + 2) / 3);
-}
+/* Names for the printf format flags ' and #. These can be ORed together. */
+enum { FLAG_THOUSANDS = 1, FLAG_ALTERNATIVE = 2 };
+/* Scan the printf format flags in FORMAT, storing info about the
+ flags into *FLAGS_PTR. Return the number of flags found. */
static size_t
-get_format_prec (char **format_ptr)
+get_format_flags (char const *format, int *flags_ptr)
{
- if (**format_ptr != '.')
- return 0;
- (*format_ptr)++;
+ int flags = 0;
- if (! ISDIGIT (**format_ptr))
- return 0;
- else
+ for (size_t count = 0; ; count++)
{
- unsigned long int val;
- if (xstrtoul (*format_ptr, format_ptr, 10, &val, NULL) != LONGINT_OK
- || SIZE_MAX < val)
- error (EXIT_FAILURE, 0, _("invalid format precision"));
- return val;
+ switch (format[count])
+ {
+ case '-':
+ case '0':
+ break;
+
+ case '\'':
+ flags |= FLAG_THOUSANDS;
+ break;
+
+ case '#':
+ flags |= FLAG_ALTERNATIVE;
+ break;
+
+ default:
+ *flags_ptr = flags;
+ return count;
+ }
}
}
+/* Check that the printf format conversion specifier *FORMAT is valid
+ and compatible with FLAGS. Change it to 'u' if it is 'd' or 'i',
+ since the format will be used with an unsigned value. */
static void
-get_format_conv_type (char **format_ptr)
+check_format_conv_type (char *format, int flags)
{
- unsigned char ch = *(*format_ptr)++;
+ unsigned char ch = *format;
+ int compatible_flags = FLAG_THOUSANDS;
switch (ch)
{
case 'd':
case 'i':
- case 'o':
+ *format = 'u';
+ break;
+
case 'u':
+ break;
+
+ case 'o':
case 'x':
case 'X':
+ compatible_flags = FLAG_ALTERNATIVE;
break;
case 0:
@@ -1276,60 +1293,60 @@ get_format_conv_type (char **format_ptr)
default:
if (isprint (ch))
error (EXIT_FAILURE, 0,
- _("invalid conversion specifier in suffix: %c"), ch);
+ _("invalid conversion specifier in suffix: %c"), ch);
else
- error (EXIT_FAILURE, 0,
- _("invalid conversion specifier in suffix: \\%.3o"), ch);
+ error (EXIT_FAILURE, 0,
+ _("invalid conversion specifier in suffix: \\%.3o"), ch);
}
+
+ if (flags & ~ compatible_flags)
+ error (EXIT_FAILURE, 0,
+ _("invalid flags in conversion specification: %%%c%c"),
+ (flags & ~ compatible_flags & FLAG_ALTERNATIVE ? '#' : '\''), ch);
}
+/* Return the maximum number of bytes that can be generated by
+ applying FORMAT to an unsigned int value. If the format is
+ invalid, diagnose the problem and exit. */
static size_t
max_out (char *format)
{
- size_t out_count = 0;
bool percent = false;
- while (*format)
- {
- if (*format++ != '%')
- out_count++;
- else if (*format == '%')
- {
- format++;
- out_count++;
- }
- else
- {
- if (percent)
- error (EXIT_FAILURE, 0,
- _("too many %% conversion specifications in suffix"));
- percent = true;
- out_count += get_format_flags (&format);
- {
- size_t width = get_format_width (&format);
- size_t prec = get_format_prec (&format);
-
- out_count += MAX (width, prec);
- }
- get_format_conv_type (&format);
- }
- }
+ for (char *f = format; *f; f++)
+ if (*f == '%' && *++f != '%')
+ {
+ if (percent)
+ error (EXIT_FAILURE, 0,
+ _("too many %% conversion specifications in suffix"));
+ percent = true;
+ int flags;
+ f += get_format_flags (f, &flags);
+ while (ISDIGIT (*f))
+ f++;
+ if (*f == '.')
+ while (ISDIGIT (*++f))
+ continue;
+ check_format_conv_type (f, flags);
+ }
if (! percent)
error (EXIT_FAILURE, 0,
- _("missing %% conversion specification in suffix"));
+ _("missing %% conversion specification in suffix"));
- return out_count;
+ int maxlen = snprintf (NULL, 0, format, UINT_MAX);
+ if (! (0 <= maxlen && maxlen <= SIZE_MAX))
+ xalloc_die ();
+ return maxlen;
}
int
main (int argc, char **argv)
{
int optc;
- unsigned long int val;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -1341,60 +1358,67 @@ main (int argc, char **argv)
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, NULL)) != -1)
switch (optc)
{
case 'f':
- prefix = optarg;
- break;
+ prefix = optarg;
+ break;
case 'b':
- suffix = optarg;
- break;
+ suffix = optarg;
+ break;
case 'k':
- remove_files = false;
- break;
+ remove_files = false;
+ break;
case 'n':
- if (xstrtoul (optarg, NULL, 10, &val, "") != LONGINT_OK
- || val > INT_MAX)
- error (EXIT_FAILURE, 0, _("%s: invalid number"), optarg);
- digits = val;
- break;
+ digits = xdectoimax (optarg, 0, MIN (INT_MAX, SIZE_MAX), "",
+ _("invalid number"), 0);
+ break;
case 's':
case 'q':
- suppress_count = true;
- break;
+ suppress_count = true;
+ break;
case 'z':
- elide_empty_files = true;
- break;
+ elide_empty_files = true;
+ break;
+
+ case SUPPRESS_MATCHED_OPTION:
+ suppress_matched = true;
+ break;
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
- usage (EXIT_FAILURE);
+ usage (EXIT_FAILURE);
}
if (argc - optind < 2)
{
if (argc <= optind)
- error (0, 0, _("missing operand"));
+ error (0, 0, _("missing operand"));
else
- error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
+ error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
usage (EXIT_FAILURE);
}
- if (suffix)
- filename_space = xmalloc (strlen (prefix) + max_out (suffix) + 2);
- else
- filename_space = xmalloc (strlen (prefix) + digits + 2);
+ size_t prefix_len = strlen (prefix);
+ size_t max_digit_string_len
+ = (suffix
+ ? max_out (suffix)
+ : MAX (INT_STRLEN_BOUND (unsigned int), digits));
+ if (SIZE_MAX - 1 - prefix_len < max_digit_string_len)
+ xalloc_die ();
+ filename_space = xmalloc (prefix_len + max_digit_string_len + 1);
set_input_file (argv[optind++]);
@@ -1404,35 +1428,34 @@ main (int argc, char **argv)
int i;
static int const sig[] =
{
- /* The usual suspects. */
- SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM,
+ /* The usual suspects. */
+ SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM,
#ifdef SIGPOLL
- SIGPOLL,
+ SIGPOLL,
#endif
#ifdef SIGPROF
- SIGPROF,
+ SIGPROF,
#endif
#ifdef SIGVTALRM
- SIGVTALRM,
+ SIGVTALRM,
#endif
#ifdef SIGXCPU
- SIGXCPU,
+ SIGXCPU,
#endif
#ifdef SIGXFSZ
- SIGXFSZ,
+ SIGXFSZ,
#endif
};
- enum { nsigs = sizeof sig / sizeof sig[0] };
+ enum { nsigs = ARRAY_CARDINALITY (sig) };
-#if SA_NOCLDSTOP
struct sigaction act;
sigemptyset (&caught_signals);
for (i = 0; i < nsigs; i++)
{
- sigaction (sig[i], NULL, &act);
- if (act.sa_handler != SIG_IGN)
- sigaddset (&caught_signals, sig[i]);
+ sigaction (sig[i], NULL, &act);
+ if (act.sa_handler != SIG_IGN)
+ sigaddset (&caught_signals, sig[i]);
}
act.sa_handler = interrupt_handler;
@@ -1441,15 +1464,7 @@ main (int argc, char **argv)
for (i = 0; i < nsigs; i++)
if (sigismember (&caught_signals, sig[i]))
- sigaction (sig[i], &act, NULL);
-#else
- for (i = 0; i < nsigs; i++)
- if (signal (sig[i], SIG_IGN) != SIG_IGN)
- {
- signal (sig[i], interrupt_handler);
- siginterrupt (sig[i], 1);
- }
-#endif
+ sigaction (sig[i], &act, NULL);
}
split_file ();
@@ -1460,35 +1475,40 @@ main (int argc, char **argv)
cleanup_fatal ();
}
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... FILE PATTERN...\n\
"),
- program_name);
+ program_name);
fputs (_("\
-Output pieces of FILE separated by PATTERN(s) to files `xx00', `xx01', ...,\n\
+Output pieces of FILE separated by PATTERN(s) to files 'xx00', 'xx01', ...,\n\
and output byte counts of each piece to standard output.\n\
-\n\
"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
+ fputs (_("\
+\n\
+Read standard input if FILE is -\n\
"), stdout);
+
+ 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\
+ -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 remove empty output files\n\
@@ -1497,19 +1517,16 @@ Mandatory arguments to long options are mandatory for short options too.\n\
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
\n\
-Read standard input if FILE is -. Each PATTERN may be:\n\
-"), stdout);
- fputs (_("\
-\n\
+Each PATTERN may be:\n\
INTEGER copy up to but not including specified line number\n\
/REGEXP/[OFFSET] copy up to but not including a matching line\n\
%REGEXP%[OFFSET] skip to, but not including a matching line\n\
{INTEGER} repeat the previous pattern specified number of times\n\
{*} repeat the previous pattern as many times as possible\n\
\n\
-A line OFFSET is a required `+' or `-' followed by a positive integer.\n\
+A line OFFSET is a required '+' or '-' followed by a positive integer.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
diff --git a/src/cu-progs.mk b/src/cu-progs.mk
new file mode 100644
index 0000000..923d50a
--- /dev/null
+++ b/src/cu-progs.mk
@@ -0,0 +1,112 @@
+## Automatically generated by gen-lists-of-programs.sh. DO NOT EDIT BY HAND!
+no_install__progs =
+no_install__progs += src/arch
+no_install__progs += src/coreutils
+no_install__progs += src/hostname
+build_if_possible__progs =
+build_if_possible__progs += src/chroot
+build_if_possible__progs += src/df
+build_if_possible__progs += src/hostid
+build_if_possible__progs += src/libstdbuf.so
+build_if_possible__progs += src/nice
+build_if_possible__progs += src/pinky
+build_if_possible__progs += src/stdbuf
+build_if_possible__progs += src/stty
+build_if_possible__progs += src/uptime
+build_if_possible__progs += src/users
+build_if_possible__progs += src/who
+default__progs =
+default__progs += src/[
+default__progs += src/base64
+default__progs += src/base32
+default__progs += src/basename
+default__progs += src/cat
+default__progs += src/chcon
+default__progs += src/chgrp
+default__progs += src/chmod
+default__progs += src/chown
+default__progs += src/cksum
+default__progs += src/comm
+default__progs += src/cp
+default__progs += src/csplit
+default__progs += src/cut
+default__progs += src/date
+default__progs += src/dd
+default__progs += src/dir
+default__progs += src/dircolors
+default__progs += src/dirname
+default__progs += src/du
+default__progs += src/echo
+default__progs += src/env
+default__progs += src/expand
+default__progs += src/expr
+default__progs += src/factor
+default__progs += src/false
+default__progs += src/fmt
+default__progs += src/fold
+default__progs += src/ginstall
+default__progs += src/groups
+default__progs += src/head
+default__progs += src/id
+default__progs += src/join
+default__progs += src/kill
+default__progs += src/link
+default__progs += src/ln
+default__progs += src/logname
+default__progs += src/ls
+default__progs += src/md5sum
+default__progs += src/mkdir
+default__progs += src/mkfifo
+default__progs += src/mknod
+default__progs += src/mktemp
+default__progs += src/mv
+default__progs += src/nl
+default__progs += src/nproc
+default__progs += src/nohup
+default__progs += src/numfmt
+default__progs += src/od
+default__progs += src/paste
+default__progs += src/pathchk
+default__progs += src/pr
+default__progs += src/printenv
+default__progs += src/printf
+default__progs += src/ptx
+default__progs += src/pwd
+default__progs += src/readlink
+default__progs += src/realpath
+default__progs += src/rm
+default__progs += src/rmdir
+default__progs += src/runcon
+default__progs += src/seq
+default__progs += src/sha1sum
+default__progs += src/sha224sum
+default__progs += src/sha256sum
+default__progs += src/sha384sum
+default__progs += src/sha512sum
+default__progs += src/shred
+default__progs += src/shuf
+default__progs += src/sleep
+default__progs += src/sort
+default__progs += src/split
+default__progs += src/stat
+default__progs += src/sum
+default__progs += src/sync
+default__progs += src/tac
+default__progs += src/tail
+default__progs += src/tee
+default__progs += src/test
+default__progs += src/timeout
+default__progs += src/touch
+default__progs += src/tr
+default__progs += src/true
+default__progs += src/truncate
+default__progs += src/tsort
+default__progs += src/tty
+default__progs += src/uname
+default__progs += src/unexpand
+default__progs += src/uniq
+default__progs += src/unlink
+default__progs += src/vdir
+default__progs += src/wc
+default__progs += src/whoami
+default__progs += src/yes
diff --git a/src/cut.c b/src/cut.c
index c9b8359..7ab6be4 100644
--- a/src/cut.c
+++ b/src/cut.c
@@ -1,11 +1,11 @@
/* cut - remove parts of lines of files
- Copyright (C) 1997-2006 Free Software Foundation, Inc.
+ Copyright (C) 1997-2016 Free Software Foundation, Inc.
Copyright (C) 1984 David M. Ihnat
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -13,8 +13,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by David Ihnat. */
@@ -32,15 +31,20 @@
#include "system.h"
#include "error.h"
+#include "fadvise.h"
#include "getndelim2.h"
#include "hash.h"
-#include "quote.h"
#include "xstrndup.h"
-/* The official name of this program (e.g., no `g' prefix). */
+#include "set-fields.h"
+
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "cut"
-#define AUTHORS "David Ihnat", "David MacKenzie", "Jim Meyering"
+#define AUTHORS \
+ proper_name ("David M. Ihnat"), \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Jim Meyering")
#define FATAL_ERROR(Message) \
do \
@@ -50,28 +54,12 @@
} \
while (0)
-/* Append LOW, HIGH to the list RP of range pairs, allocating additional
- space if necessary. Update local variable N_RP. When allocating,
- update global variable N_RP_ALLOCATED. */
-
-#define ADD_RANGE_PAIR(rp, low, high) \
- do \
- { \
- if (n_rp >= n_rp_allocated) \
- { \
- (rp) = X2NREALLOC (rp, &n_rp_allocated); \
- } \
- rp[n_rp].lo = (low); \
- rp[n_rp].hi = (high); \
- ++n_rp; \
- } \
- while (0)
-struct range_pair
- {
- size_t lo;
- size_t hi;
- };
+/* Pointer inside RP. When checking if a byte or field 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
+ CURRENT_RP.HI then we make CURRENT_RP to point to the next range pair. */
+static struct field_range_pair *current_rp;
/* This buffer is used to support the semantics of the -s option
(or lack of same) when the specified field list includes (does
@@ -85,26 +73,6 @@ static char *field_1_buffer;
/* The number of bytes allocated for FIELD_1_BUFFER. */
static size_t field_1_bufsize;
-/* The largest field or byte index used as an endpoint of a closed
- or degenerate range specification; this doesn't include the starting
- index of right-open-ended ranges. For example, with either range spec
- `2-5,9-', `2-3,5,9-' this variable would be set to 5. */
-static size_t max_range_endpoint;
-
-/* If nonzero, this is the index of the first field in a range that goes
- to end of line. */
-static size_t eol_range_start;
-
-/* This is a bit vector.
- In byte mode, which bytes to output.
- In field mode, which DELIM-separated fields to output.
- Both bytes and fields are numbered starting with 1,
- so the zeroth bit of this array is unused.
- A field or byte K has been selected if
- (K <= MAX_RANGE_ENDPOINT and is_printable_field(K))
- || (EOL_RANGE_START > 0 && K >= EOL_RANGE_START). */
-static unsigned char *printable_field;
-
enum operating_mode
{
undefined_mode,
@@ -112,27 +80,27 @@ enum operating_mode
/* Output characters that are in the given bytes. */
byte_mode,
- /* Output the given delimeter-separated fields. */
+ /* Output the given delimiter-separated fields. */
field_mode
};
-/* The name this program was run with. */
-char *program_name;
-
static enum operating_mode operating_mode;
-/* If true do not output lines containing no delimeter characters.
+/* If true do not output lines containing no delimiter characters.
Otherwise, all such lines are printed. This option is valid only
with field mode. */
static bool suppress_non_delimited;
-/* If nonzero, print all bytes, characters, or fields _except_
+/* If true, print all bytes, characters, or fields _except_
those that were specified. */
static bool complement;
-/* The delimeter character for field mode. */
+/* The delimiter character for field mode. */
static unsigned char delim;
+/* The delimiter for each line/record. */
+static unsigned char line_delim = '\n';
+
/* True if the --output-delimiter=STRING option was specified. */
static bool output_delimiter_specified;
@@ -146,15 +114,6 @@ static char *output_delimiter_string;
/* True if we have ever read standard input. */
static bool have_read_stdin;
-#define HT_RANGE_START_INDEX_INITIAL_CAPACITY 31
-
-/* The set of range-start indices. For example, given a range-spec list like
- `-b1,3-5,4-9,15-', the following indices will be recorded here: 1, 3, 15.
- Note that although `4' looks like a range-start index, it is in the middle
- of the `3-5' range, so it doesn't count.
- This table is created/used IFF output_delimiter_specified is set. */
-static Hash_table *range_start_ht;
-
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
@@ -172,6 +131,7 @@ static struct option const longopts[] =
{"only-delimited", no_argument, NULL, 's'},
{"output-delimiter", required_argument, NULL, OUTPUT_DELIMITER_OPTION},
{"complement", no_argument, NULL, COMPLEMENT_OPTION},
+ {"zero-terminated", no_argument, NULL, 'z'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
@@ -181,21 +141,20 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
-Usage: %s [OPTION]... [FILE]...\n\
+Usage: %s OPTION... [FILE]...\n\
"),
- program_name);
+ program_name);
fputs (_("\
Print selected parts of lines from each FILE to standard output.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
+
+ emit_stdin_note ();
+ emit_mandatory_arg_note ();
+
fputs (_("\
-b, --bytes=LIST select only these bytes\n\
-c, --characters=LIST select only these characters\n\
@@ -209,13 +168,16 @@ Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
fputs (_("\
--complement complement the set of selected bytes, characters\n\
- or fields.\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);
fputs (_("\
@@ -231,295 +193,38 @@ Each range is one of:\n\
N- from N'th byte, character or field, to end of line\n\
N-M from N'th to M'th (included) byte, character or field\n\
-M from first to M'th (included) byte, character or field\n\
-\n\
-With no FILE, or when FILE is -, read standard input.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
-static inline void
-mark_range_start (size_t i)
-{
- /* Record the fact that `i' is a range-start index. */
- void *ent_from_table = hash_insert (range_start_ht, (void*) i);
- if (ent_from_table == NULL)
- {
- /* Insertion failed due to lack of memory. */
- xalloc_die ();
- }
- assert ((size_t) ent_from_table == i);
-}
-
-static inline void
-mark_printable_field (size_t i)
-{
- size_t n = i / CHAR_BIT;
- printable_field[n] |= (1 << (i % CHAR_BIT));
-}
-
-static inline bool
-is_printable_field (size_t i)
-{
- size_t n = i / CHAR_BIT;
- return (printable_field[n] >> (i % CHAR_BIT)) & 1;
-}
-static size_t
-hash_int (const void *x, size_t tablesize)
-{
-#ifdef UINTPTR_MAX
- uintptr_t y = (uintptr_t) x;
-#else
- size_t y = (size_t) x;
-#endif
- return y % tablesize;
-}
+/* Increment *ITEM_IDX (i.e., a field or byte index),
+ and if required CURRENT_RP. */
-static bool
-hash_compare_ints (void const *x, void const *y)
-{
- return (x == y) ? true : false;
-}
-
-static bool
-is_range_start_index (size_t i)
+static inline void
+next_item (size_t *item_idx)
{
- return hash_lookup (range_start_ht, (void *) i) ? true : false;
+ (*item_idx)++;
+ if ((*item_idx) > current_rp->hi)
+ current_rp++;
}
-/* Return nonzero if the K'th field or byte is printable.
- When returning nonzero, if RANGE_START is non-NULL,
- set *RANGE_START to true if K is the beginning of a range, and to
- false otherwise. */
-
-static bool
-print_kth (size_t k, bool *range_start)
-{
- bool k_selected
- = ((0 < eol_range_start && eol_range_start <= k)
- || (k <= max_range_endpoint && is_printable_field (k)));
-
- bool is_selected = k_selected ^ complement;
- if (range_start && is_selected)
- *range_start = is_range_start_index (k);
+/* Return nonzero if the K'th field or byte is printable. */
- return is_selected;
-}
-
-/* Comparison function for qsort to order the list of
- struct range_pairs. */
-static int
-compare_ranges (const void *a, const void *b)
+static inline bool
+print_kth (size_t k)
{
- int a_start = ((const struct range_pair *) a)->lo;
- int b_start = ((const struct range_pair *) b)->lo;
- return a_start < b_start ? -1 : a_start > b_start;
+ return current_rp->lo <= k;
}
-/* Given the list of field or byte range specifications FIELDSTR, set
- MAX_RANGE_ENDPOINT and allocate and initialize the PRINTABLE_FIELD
- array. If there is a right-open-ended range, set EOL_RANGE_START
- to its starting index. FIELDSTR should be composed of one or more
- numbers or ranges of numbers, separated by blanks or commas.
- Incomplete ranges may be given: `-m' means `1-m'; `n-' means `n'
- through end of line. Return true if FIELDSTR contains at least
- one field specification, false otherwise. */
-
-/* FIXME-someday: What if the user wants to cut out the 1,000,000-th
- field of some huge input file? This function shouldn't have to
- allocate a table of a million bits just so we can test every
- field < 10^6 with an array dereference. Instead, consider using
- an adaptive approach: if the range of selected fields is too large,
- but only a few fields/byte-offsets are actually selected, use a
- hash table. If the range of selected fields is too large, and
- too many are selected, then resort to using the range-pairs (the
- `rp' array) directly. */
+/* Return nonzero if K'th byte is the beginning of a range. */
-static bool
-set_fields (const char *fieldstr)
+static inline bool
+is_range_start_index (size_t k)
{
- size_t initial = 1; /* Value of first number in a range. */
- size_t value = 0; /* If nonzero, a number being accumulated. */
- bool dash_found = false; /* True if a '-' is found in this field. */
- bool field_found = false; /* True if at least one field spec
- has been processed. */
-
- struct range_pair *rp = NULL;
- size_t n_rp = 0;
- size_t n_rp_allocated = 0;
- size_t i;
- bool in_digits = false;
-
- /* Collect and store in RP the range end points.
- It also sets EOL_RANGE_START if appropriate. */
-
- for (;;)
- {
- if (*fieldstr == '-')
- {
- in_digits = false;
- /* Starting a range. */
- if (dash_found)
- FATAL_ERROR (_("invalid byte or field list"));
- dash_found = true;
- fieldstr++;
-
- if (value)
- {
- initial = value;
- value = 0;
- }
- else
- initial = 1;
- }
- else if (*fieldstr == ',' || isblank (*fieldstr) || *fieldstr == '\0')
- {
- in_digits = false;
- /* Ending the string, or this field/byte sublist. */
- if (dash_found)
- {
- dash_found = false;
-
- /* A range. Possibilites: -n, m-n, n-.
- In any case, `initial' contains the start of the range. */
- if (value == 0)
- {
- /* `n-'. From `initial' to end of line. */
- eol_range_start = initial;
- field_found = true;
- }
- else
- {
- /* `m-n' or `-n' (1-n). */
- if (value < initial)
- FATAL_ERROR (_("invalid byte or field list"));
-
- /* Is there already a range going to end of line? */
- if (eol_range_start != 0)
- {
- /* Yes. Is the new sequence already contained
- in the old one? If so, no processing is
- necessary. */
- if (initial < eol_range_start)
- {
- /* No, the new sequence starts before the
- old. Does the old range going to end of line
- extend into the new range? */
- if (eol_range_start <= value)
- {
- /* Yes. Simply move the end of line marker. */
- eol_range_start = initial;
- }
- else
- {
- /* No. A simple range, before and disjoint from
- the range going to end of line. Fill it. */
- ADD_RANGE_PAIR (rp, initial, value);
- }
-
- /* In any case, some fields were selected. */
- field_found = true;
- }
- }
- else
- {
- /* There is no range going to end of line. */
- ADD_RANGE_PAIR (rp, initial, value);
- field_found = true;
- }
- value = 0;
- }
- }
- else if (value != 0)
- {
- /* A simple field number, not a range. */
- ADD_RANGE_PAIR (rp, value, value);
- value = 0;
- field_found = true;
- }
-
- if (*fieldstr == '\0')
- {
- break;
- }
-
- fieldstr++;
- }
- else if (ISDIGIT (*fieldstr))
- {
- /* Record beginning of digit string, in case we have to
- complain about it. */
- static char const *num_start;
- if (!in_digits || !num_start)
- num_start = fieldstr;
- in_digits = true;
-
- /* Detect overflow. */
- if (!DECIMAL_DIGIT_ACCUMULATE (value, *fieldstr - '0', size_t))
- {
- /* In case the user specified -c4294967296,22,
- complain only about the first number. */
- /* Determine the length of the offending number. */
- size_t len = strspn (num_start, "0123456789");
- char *bad_num = xstrndup (num_start, len);
- if (operating_mode == byte_mode)
- error (0, 0,
- _("byte offset %s is too large"), quote (bad_num));
- else
- error (0, 0,
- _("field number %s is too large"), quote (bad_num));
- free (bad_num);
- exit (EXIT_FAILURE);
- }
-
- fieldstr++;
- }
- else
- FATAL_ERROR (_("invalid byte or field list"));
- }
-
- max_range_endpoint = 0;
- for (i = 0; i < n_rp; i++)
- {
- if (rp[i].hi > max_range_endpoint)
- max_range_endpoint = rp[i].hi;
- }
-
- /* Allocate an array large enough so that it may be indexed by
- the field numbers corresponding to all finite ranges
- (i.e. `2-6' or `-4', but not `5-') in FIELDSTR. */
-
- printable_field = xzalloc (max_range_endpoint / CHAR_BIT + 1);
-
- qsort (rp, n_rp, sizeof (rp[0]), compare_ranges);
-
- /* Set the array entries corresponding to integers in the ranges of RP. */
- for (i = 0; i < n_rp; i++)
- {
- size_t j;
- size_t rsi_candidate;
-
- /* Record the range-start indices, i.e., record each start
- index that is not part of any other (lo..hi] range. */
- rsi_candidate = complement ? rp[i].hi + 1 : rp[i].lo;
- if (output_delimiter_specified
- && !is_printable_field (rsi_candidate))
- mark_range_start (rsi_candidate);
-
- for (j = rp[i].lo; j <= rp[i].hi; j++)
- mark_printable_field (j);
- }
-
- if (output_delimiter_specified
- && !complement
- && eol_range_start && !is_printable_field (eol_range_start))
- mark_range_start (eol_range_start);
-
- free (rp);
-
- return field_found;
+ return k == current_rp->lo;
}
/* Read from stream STREAM, printing to standard output any selected bytes. */
@@ -534,39 +239,44 @@ cut_bytes (FILE *stream)
byte_idx = 0;
print_delimiter = false;
- while (1)
+ current_rp = frp;
+ while (true)
{
int c; /* Each character from the file. */
c = getc (stream);
- if (c == '\n')
- {
- putchar ('\n');
- byte_idx = 0;
- print_delimiter = false;
- }
+ if (c == line_delim)
+ {
+ putchar (c);
+ byte_idx = 0;
+ print_delimiter = false;
+ current_rp = frp;
+ }
else if (c == EOF)
- {
- if (byte_idx > 0)
- putchar ('\n');
- break;
- }
+ {
+ if (byte_idx > 0)
+ putchar (line_delim);
+ break;
+ }
else
- {
- bool range_start;
- bool *rs = output_delimiter_specified ? &range_start : NULL;
- if (print_kth (++byte_idx, rs))
- {
- if (rs && *rs && print_delimiter)
- {
- fwrite (output_delimiter_string, sizeof (char),
- output_delimiter_length, stdout);
- }
- print_delimiter = true;
- putchar (c);
- }
- }
+ {
+ next_item (&byte_idx);
+ if (print_kth (byte_idx))
+ {
+ if (output_delimiter_specified)
+ {
+ if (print_delimiter && is_range_start_index (byte_idx))
+ {
+ fwrite (output_delimiter_string, sizeof (char),
+ output_delimiter_length, stdout);
+ }
+ print_delimiter = true;
+ }
+
+ putchar (c);
+ }
+ }
}
}
@@ -580,115 +290,138 @@ cut_fields (FILE *stream)
bool found_any_selected_field = false;
bool buffer_first_field;
+ current_rp = frp;
+
c = getc (stream);
if (c == EOF)
return;
ungetc (c, stream);
+ c = 0;
/* To support the semantics of the -s flag, we may have to buffer
- all of the first field to determine whether it is `delimited.'
+ 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, NULL));
+ buffer_first_field = (suppress_non_delimited ^ !print_kth (1));
while (1)
{
if (field_idx == 1 && buffer_first_field)
- {
- ssize_t len;
- size_t n_bytes;
-
- len = getndelim2 (&field_1_buffer, &field_1_bufsize, 0,
- GETNLINE_NO_LIMIT, delim, '\n', stream);
- if (len < 0)
- {
- free (field_1_buffer);
- field_1_buffer = NULL;
- if (ferror (stream) || feof (stream))
- break;
- xalloc_die ();
- }
-
- n_bytes = len;
- assert (n_bytes != 0);
-
- /* 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)
- {
- if (suppress_non_delimited)
- {
- /* Empty. */
- }
- else
- {
- fwrite (field_1_buffer, sizeof (char), n_bytes, stdout);
- /* Make sure the output line is newline terminated. */
- if (field_1_buffer[n_bytes - 1] != '\n')
- putchar ('\n');
- }
- continue;
- }
- if (print_kth (1, NULL))
- {
- /* Print the field, but not the trailing delimiter. */
- fwrite (field_1_buffer, sizeof (char), n_bytes - 1, stdout);
- found_any_selected_field = true;
- }
- ++field_idx;
- }
-
- if (c != EOF)
- {
- if (print_kth (field_idx, NULL))
- {
- if (found_any_selected_field)
- {
- fwrite (output_delimiter_string, sizeof (char),
- output_delimiter_length, stdout);
- }
- found_any_selected_field = true;
-
- while ((c = getc (stream)) != delim && c != '\n' && c != EOF)
- {
- putchar (c);
- }
- }
- else
- {
- while ((c = getc (stream)) != delim && c != '\n' && c != EOF)
- {
- /* Empty. */
- }
- }
- }
-
- if (c == '\n')
- {
- c = getc (stream);
- if (c != EOF)
- {
- ungetc (c, stream);
- c = '\n';
- }
- }
+ {
+ 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 = NULL;
+ if (ferror (stream) || feof (stream))
+ break;
+ xalloc_die ();
+ }
+
+ n_bytes = len;
+ assert (n_bytes != 0);
+
+ c = 0;
+
+ /* 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)
+ {
+ if (suppress_non_delimited)
+ {
+ /* Empty. */
+ }
+ else
+ {
+ fwrite (field_1_buffer, sizeof (char), n_bytes, stdout);
+ /* Make sure the output line is newline terminated. */
+ if (field_1_buffer[n_bytes - 1] != line_delim)
+ putchar (line_delim);
+ c = line_delim;
+ }
+ continue;
+ }
+ if (print_kth (1))
+ {
+ /* Print the field, but not the trailing delimiter. */
+ fwrite (field_1_buffer, sizeof (char), n_bytes - 1, stdout);
+
+ /* With -d$'\n' don't treat the last '\n' as a delimiter. */
+ if (delim == line_delim)
+ {
+ int last_c = getc (stream);
+ if (last_c != EOF)
+ {
+ ungetc (last_c, stream);
+ found_any_selected_field = true;
+ }
+ }
+ else
+ found_any_selected_field = true;
+ }
+ next_item (&field_idx);
+ }
+
+ int prev_c = c;
+
+ if (print_kth (field_idx))
+ {
+ if (found_any_selected_field)
+ {
+ fwrite (output_delimiter_string, sizeof (char),
+ output_delimiter_length, stdout);
+ }
+ found_any_selected_field = true;
+
+ while ((c = getc (stream)) != delim && c != line_delim && c != EOF)
+ {
+ putchar (c);
+ prev_c = c;
+ }
+ }
+ else
+ {
+ while ((c = getc (stream)) != delim && c != line_delim && c != EOF)
+ {
+ prev_c = c;
+ }
+ }
+
+ /* With -d$'\n' don't treat the last '\n' as a delimiter. */
+ if (delim == line_delim && c == delim)
+ {
+ int last_c = getc (stream);
+ if (last_c != EOF)
+ ungetc (last_c, stream);
+ else
+ c = last_c;
+ }
if (c == delim)
- ++field_idx;
- else if (c == '\n' || c == EOF)
- {
- if (found_any_selected_field
- || !(suppress_non_delimited && field_idx == 1))
- putchar ('\n');
- if (c == EOF)
- break;
- field_idx = 1;
- found_any_selected_field = false;
- }
+ next_item (&field_idx);
+ else if (c == line_delim || c == EOF)
+ {
+ if (found_any_selected_field
+ || !(suppress_non_delimited && field_idx == 1))
+ {
+ if (c == line_delim || prev_c != line_delim
+ || delim == line_delim)
+ putchar (line_delim);
+ }
+ if (c == EOF)
+ break;
+ field_idx = 1;
+ current_rp = frp;
+ found_any_selected_field = false;
+ }
}
}
@@ -718,24 +451,26 @@ cut_file (char const *file)
{
stream = fopen (file, "r");
if (stream == NULL)
- {
- error (0, errno, "%s", file);
- return false;
- }
+ {
+ error (0, errno, "%s", quotef (file));
+ return false;
+ }
}
+ fadvise (stream, FADVISE_SEQUENTIAL);
+
cut_stream (stream);
if (ferror (stream))
{
- error (0, errno, "%s", file);
+ error (0, errno, "%s", quotef (file));
return false;
}
if (STREQ (file, "-"))
clearerr (stream); /* Also clear EOF. */
else if (fclose (stream) == EOF)
{
- error (0, errno, "%s", file);
+ error (0, errno, "%s", quotef (file));
return false;
}
return true;
@@ -747,10 +482,10 @@ main (int argc, char **argv)
int optc;
bool ok;
bool delim_specified = false;
- char *spec_list_string IF_LINT(= NULL);
+ char *spec_list_string IF_LINT ( = NULL);
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -765,69 +500,73 @@ main (int argc, char **argv)
delim = '\0';
have_read_stdin = false;
- while ((optc = getopt_long (argc, argv, "b:c:d:f:ns", longopts, NULL)) != -1)
+ while ((optc = getopt_long (argc, argv, "b:c:d:f:nsz", longopts, NULL)) != -1)
{
switch (optc)
- {
- case 'b':
- case 'c':
- /* Build the byte list. */
- if (operating_mode != undefined_mode)
- FATAL_ERROR (_("only one type of list may be specified"));
- operating_mode = byte_mode;
- spec_list_string = optarg;
- break;
-
- case 'f':
- /* Build the field list. */
- if (operating_mode != undefined_mode)
- FATAL_ERROR (_("only one type of list may be specified"));
- operating_mode = field_mode;
- 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;
- break;
-
- case OUTPUT_DELIMITER_OPTION:
- output_delimiter_specified = true;
- /* Interpret --output-delimiter='' to mean
- `use the NUL byte as the delimiter.' */
- output_delimiter_length = (optarg[0] == '\0'
- ? 1 : strlen (optarg));
- output_delimiter_string = xstrdup (optarg);
- break;
-
- case 'n':
- break;
-
- case 's':
- suppress_non_delimited = true;
- break;
-
- case COMPLEMENT_OPTION:
- complement = true;
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'b':
+ case 'c':
+ /* Build the byte list. */
+ if (operating_mode != undefined_mode)
+ FATAL_ERROR (_("only one type of list may be specified"));
+ operating_mode = byte_mode;
+ spec_list_string = optarg;
+ break;
+
+ case 'f':
+ /* Build the field list. */
+ if (operating_mode != undefined_mode)
+ FATAL_ERROR (_("only one type of list may be specified"));
+ operating_mode = field_mode;
+ 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;
+ break;
+
+ case OUTPUT_DELIMITER_OPTION:
+ output_delimiter_specified = true;
+ /* Interpret --output-delimiter='' to mean
+ 'use the NUL byte as the delimiter.' */
+ output_delimiter_length = (optarg[0] == '\0'
+ ? 1 : strlen (optarg));
+ output_delimiter_string = xstrdup (optarg);
+ break;
+
+ case 'n':
+ break;
+
+ case 's':
+ suppress_non_delimited = true;
+ break;
+
+ case 'z':
+ line_delim = '\0';
+ break;
+
+ case COMPLEMENT_OPTION:
+ complement = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
}
if (operating_mode == undefined_mode)
FATAL_ERROR (_("you must specify a list of bytes, characters, or fields"));
- if (delim != '\0' && operating_mode != field_mode)
+ if (delim_specified && operating_mode != field_mode)
FATAL_ERROR (_("an input delimiter may be specified only\
when operating on fields"));
@@ -835,23 +574,9 @@ main (int argc, char **argv)
FATAL_ERROR (_("suppressing non-delimited lines makes sense\n\
\tonly when operating on fields"));
- if (output_delimiter_specified)
- {
- range_start_ht = hash_initialize (HT_RANGE_START_INDEX_INITIAL_CAPACITY,
- NULL, hash_int,
- hash_compare_ints, NULL);
- if (range_start_ht == NULL)
- xalloc_die ();
-
- }
-
- if (! set_fields (spec_list_string))
- {
- if (operating_mode == field_mode)
- FATAL_ERROR (_("missing list of fields"));
- else
- FATAL_ERROR (_("missing list of positions"));
- }
+ set_fields (spec_list_string,
+ ( (operating_mode == field_mode) ? 0 : SETFLD_ERRMSG_USE_POS)
+ | (complement ? SETFLD_COMPLEMENT : 0) );
if (!delim_specified)
delim = '\t';
@@ -871,8 +596,6 @@ main (int argc, char **argv)
for (ok = true; optind < argc; optind++)
ok &= cut_file (argv[optind]);
- if (range_start_ht)
- hash_free (range_start_ht);
if (have_read_stdin && fclose (stdin) == EOF)
{
@@ -880,5 +603,7 @@ main (int argc, char **argv)
ok = false;
}
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ IF_LINT (reset_fields ());
+
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/date.c b/src/date.c
index c64ab1c..a42eb3c 100644
--- a/src/date.c
+++ b/src/date.c
@@ -1,10 +1,10 @@
/* date - print or set the system date and time
- Copyright (C) 1989-2007 Free Software Foundation, Inc.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
David MacKenzie <djm@gnu.ai.mit.edu> */
@@ -28,22 +27,18 @@
#include "system.h"
#include "argmatch.h"
#include "error.h"
-#include "getdate.h"
-#include "getline.h"
-#include "inttostr.h"
+#include "parse-datetime.h"
#include "posixtm.h"
#include "quote.h"
#include "stat-time.h"
#include "fprintftime.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "date"
-#define AUTHORS "David MacKenzie"
+#define AUTHORS proper_name ("David MacKenzie")
-int putenv ();
-
-static bool show_date (const char *format, struct timespec when);
+static bool show_date (const char *, struct timespec, timezone_t);
enum Time_spec
{
@@ -79,9 +74,6 @@ ARGMATCH_VERIFY (time_spec_string, time_spec);
/* A format suitable for Internet RFC 2822. */
static char const rfc_2822_format[] = "%a, %d %b %Y %H:%M:%S %z";
-/* The name this program was run with, for error messages. */
-char *program_name;
-
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
@@ -95,7 +87,7 @@ static struct option const long_options[] =
{
{"date", required_argument, NULL, 'd'},
{"file", required_argument, NULL, 'f'},
- {"iso-8601", optional_argument, NULL, 'I'}, /* Deprecated. */
+ {"iso-8601", optional_argument, NULL, 'I'},
{"reference", required_argument, NULL, 'r'},
{"rfc-822", no_argument, NULL, 'R'},
{"rfc-2822", no_argument, NULL, 'R'},
@@ -125,41 +117,53 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... [+FORMAT]\n\
or: %s [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]\n\
"),
- program_name, program_name);
+ program_name, program_name);
fputs (_("\
Display the current time in the given FORMAT, or set the system date.\n\
-\n\
- -d, --date=STRING display time described by STRING, not `now'\n\
- -f, --file=DATEFILE like --date once for each line of DATEFILE\n\
+"), stdout);
+
+ emit_mandatory_arg_note ();
+
+ fputs (_("\
+ -d, --date=STRING display time described by STRING, not 'now'\n\
+ -f, --file=DATEFILE like --date; once for each line of DATEFILE\n\
"), stdout);
fputs (_("\
- -r, --reference=FILE display the last modification time of FILE\n\
- -R, --rfc-2822 output date and time in RFC 2822 format.\n\
- Example: Mon, 07 Aug 2006 12:34:56 -0600\n\
+ -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-0600\n\
"), stdout);
fputs (_("\
- --rfc-3339=TIMESPEC output date and time in RFC 3339 format.\n\
- TIMESPEC=`date', `seconds', or `ns' for\n\
- date and time to the indicated precision.\n\
- Date and time components are separated by\n\
- a single space: 2006-08-07 12:34:56-06:00\n\
- -s, --set=STRING set time described by STRING\n\
- -u, --utc, --universal print or set Coordinated Universal Time\n\
+ -R, --rfc-2822 output date and time in RFC 2822 format.\n\
+ Example: Mon, 14 Aug 2006 02:34:56 -0600\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\
+"), stdout);
+ fputs (_("\
+ -r, --reference=FILE display the last modification time of FILE\n\
+"), stdout);
+ fputs (_("\
+ -s, --set=STRING set time described by STRING\n\
+ -u, --utc, --universal print or set Coordinated Universal Time (UTC)\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
\n\
-FORMAT controls the output. The only valid option for the second form\n\
-specifies Coordinated Universal Time. Interpreted sequences are:\n\
+FORMAT controls the output. Interpreted sequences are:\n\
\n\
%% a literal %\n\
%a locale's abbreviated weekday name (e.g., Sun)\n\
@@ -171,8 +175,8 @@ specifies Coordinated Universal Time. Interpreted sequences are:\n\
%c locale's date and time (e.g., Thu Mar 3 23:05:25 2005)\n\
"), stdout);
fputs (_("\
- %C century; like %Y, except omit last two digits (e.g., 21)\n\
- %d day of month (e.g, 01)\n\
+ %C century; like %Y, except omit last two digits (e.g., 20)\n\
+ %d day of month (e.g., 01)\n\
%D date; same as %m/%d/%y\n\
%e day of month, space padded; same as %_d\n\
"), stdout);
@@ -188,8 +192,8 @@ specifies Coordinated Universal Time. Interpreted sequences are:\n\
%j day of year (001..366)\n\
"), stdout);
fputs (_("\
- %k hour ( 0..23)\n\
- %l hour ( 1..12)\n\
+ %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\
"), stdout);
@@ -221,8 +225,8 @@ specifies Coordinated Universal Time. Interpreted sequences are:\n\
%Y year\n\
"), stdout);
fputs (_("\
- %z +hhmm numeric timezone (e.g., -0400)\n\
- %:z +hh:mm numeric timezone (e.g., -04:00)\n\
+ %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\
@@ -230,7 +234,7 @@ specifies Coordinated Universal Time. Interpreted sequences are:\n\
By default, date pads numeric fields with zeroes.\n\
"), stdout);
fputs (_("\
-The following optional flags may follow `%':\n\
+The following optional flags may follow '%':\n\
\n\
- (hyphen) do not pad the field\n\
_ (underscore) pad with spaces\n\
@@ -245,7 +249,19 @@ then an optional modifier, which is either\n\
E to use the locale's alternate representations if available, or\n\
O to use the locale's alternate numeric symbols if available.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ fputs (_("\
+\n\
+Examples:\n\
+Convert seconds since the epoch (1970-01-01 UTC) to a date\n\
+ $ date --date='@2147483647'\n\
+\n\
+Show the time on the west coast of the US (use tzselect(1) to find TZ)\n\
+ $ TZ='America/Los_Angeles' date\n\
+\n\
+Show the local time for 9AM next Friday on the west coast of the US\n\
+ $ date --date='TZ=\"America/Los_Angeles\" 09:00 next Fri'\n\
+"), stdout);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -256,7 +272,7 @@ O to use the locale's alternate numeric symbols if available.\n\
Return true if successful. */
static bool
-batch_convert (const char *input_filename, const char *format)
+batch_convert (const char *input_filename, const char *format, timezone_t tz)
{
bool ok;
FILE *in_stream;
@@ -273,9 +289,9 @@ batch_convert (const char *input_filename, const char *format)
{
in_stream = fopen (input_filename, "r");
if (in_stream == NULL)
- {
- error (EXIT_FAILURE, errno, "%s", quote (input_filename));
- }
+ {
+ error (EXIT_FAILURE, errno, "%s", quotef (input_filename));
+ }
}
line = NULL;
@@ -285,26 +301,26 @@ batch_convert (const char *input_filename, const char *format)
{
ssize_t line_length = getline (&line, &buflen, in_stream);
if (line_length < 0)
- {
- /* FIXME: detect/handle error here. */
- break;
- }
-
- if (! get_date (&when, line, NULL))
- {
- if (line[line_length - 1] == '\n')
- line[line_length - 1] = '\0';
- error (0, 0, _("invalid date %s"), quote (line));
- ok = false;
- }
+ {
+ /* FIXME: detect/handle error here. */
+ break;
+ }
+
+ if (! parse_datetime (&when, line, NULL))
+ {
+ if (line[line_length - 1] == '\n')
+ line[line_length - 1] = '\0';
+ error (0, 0, _("invalid date %s"), quote (line));
+ ok = false;
+ }
else
- {
- ok &= show_date (format, when);
- }
+ {
+ ok &= show_date (format, when, tz);
+ }
}
if (fclose (in_stream) == EOF)
- error (EXIT_FAILURE, errno, "%s", quote (input_filename));
+ error (EXIT_FAILURE, errno, "%s", quotef (input_filename));
free (line);
@@ -327,7 +343,7 @@ main (int argc, char **argv)
int option_specified_date;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -335,209 +351,211 @@ main (int argc, char **argv)
atexit (close_stdout);
while ((optc = getopt_long (argc, argv, short_options, long_options, NULL))
- != -1)
+ != -1)
{
char const *new_format = NULL;
switch (optc)
- {
- case 'd':
- datestr = optarg;
- break;
- case 'f':
- batch_file = optarg;
- break;
- case RFC_3339_OPTION:
- {
- static char const rfc_3339_format[][32] =
- {
- "%Y-%m-%d",
- "%Y-%m-%d %H:%M:%S%:z",
- "%Y-%m-%d %H:%M:%S.%N%:z"
- };
- enum Time_spec i =
- XARGMATCH ("--rfc-3339", optarg,
- time_spec_string + 2, time_spec + 2);
- new_format = rfc_3339_format[i];
- break;
- }
- case 'I':
- {
- static char const iso_8601_format[][32] =
- {
- "%Y-%m-%d",
- "%Y-%m-%dT%H:%M:%S%z",
- "%Y-%m-%dT%H:%M:%S,%N%z",
- "%Y-%m-%dT%H%z",
- "%Y-%m-%dT%H:%M%z"
- };
- enum Time_spec i =
- (optarg
- ? XARGMATCH ("--iso-8601", optarg, time_spec_string, time_spec)
- : TIME_SPEC_DATE);
- new_format = iso_8601_format[i];
- break;
- }
- case 'r':
- reference = optarg;
- break;
- case 'R':
- new_format = rfc_2822_format;
- break;
- case 's':
- set_datestr = optarg;
- 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 ("TZ=UTC0") != 0)
- xalloc_die ();
- TZSET;
- break;
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'd':
+ datestr = optarg;
+ break;
+ case 'f':
+ batch_file = optarg;
+ break;
+ case RFC_3339_OPTION:
+ {
+ static char const rfc_3339_format[][32] =
+ {
+ "%Y-%m-%d",
+ "%Y-%m-%d %H:%M:%S%:z",
+ "%Y-%m-%d %H:%M:%S.%N%:z"
+ };
+ enum Time_spec i =
+ XARGMATCH ("--rfc-3339", optarg,
+ time_spec_string + 2, time_spec + 2);
+ new_format = rfc_3339_format[i];
+ break;
+ }
+ case 'I':
+ {
+ static char const iso_8601_format[][32] =
+ {
+ "%Y-%m-%d",
+ "%Y-%m-%dT%H:%M:%S%:z",
+ "%Y-%m-%dT%H:%M:%S,%N%:z",
+ "%Y-%m-%dT%H%:z",
+ "%Y-%m-%dT%H:%M%:z"
+ };
+ enum Time_spec i =
+ (optarg
+ ? XARGMATCH ("--iso-8601", optarg, time_spec_string, time_spec)
+ : TIME_SPEC_DATE);
+ new_format = iso_8601_format[i];
+ break;
+ }
+ case 'r':
+ reference = optarg;
+ break;
+ case 'R':
+ new_format = rfc_2822_format;
+ break;
+ case 's':
+ set_datestr = optarg;
+ 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;
+ break;
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
if (new_format)
- {
- if (format)
- error (EXIT_FAILURE, 0, _("multiple output formats specified"));
- format = new_format;
- }
+ {
+ if (format)
+ error (EXIT_FAILURE, 0, _("multiple output formats specified"));
+ format = new_format;
+ }
}
option_specified_date = ((datestr ? 1 : 0)
- + (batch_file ? 1 : 0)
- + (reference ? 1 : 0));
+ + (batch_file ? 1 : 0)
+ + (reference ? 1 : 0));
if (option_specified_date > 1)
{
error (0, 0,
- _("the options to specify dates for printing are mutually exclusive"));
+ _("the options to specify dates for printing are mutually exclusive"));
usage (EXIT_FAILURE);
}
if (set_date && option_specified_date)
{
error (0, 0,
- _("the options to print and set the time may not be used together"));
+ _("the options to print and set the time may not be used together"));
usage (EXIT_FAILURE);
}
if (optind < argc)
{
if (optind + 1 < argc)
- {
- error (0, 0, _("extra operand %s"), quote (argv[optind + 1]));
- usage (EXIT_FAILURE);
- }
+ {
+ error (0, 0, _("extra operand %s"), quote (argv[optind + 1]));
+ usage (EXIT_FAILURE);
+ }
if (argv[optind][0] == '+')
- {
- if (format)
- error (EXIT_FAILURE, 0, _("multiple output formats specified"));
- format = argv[optind++] + 1;
- }
+ {
+ if (format)
+ error (EXIT_FAILURE, 0, _("multiple output formats specified"));
+ format = argv[optind++] + 1;
+ }
else if (set_date || option_specified_date)
- {
- error (0, 0,
- _("the argument %s lacks a leading `+';\n"
- "When using an option to specify date(s), any non-option\n"
- "argument must be a format string beginning with `+'."),
- quote (argv[optind]));
- usage (EXIT_FAILURE);
- }
+ {
+ error (0, 0,
+ _("the argument %s lacks a leading '+';\n"
+ "when using an option to specify date(s), any non-option\n"
+ "argument must be a format string beginning with '+'"),
+ quote (argv[optind]));
+ usage (EXIT_FAILURE);
+ }
}
if (!format)
{
format = DATE_FMT_LANGINFO ();
if (! *format)
- {
- /* Do not wrap the following literal format string with _(...).
- For example, suppose LC_ALL is unset, LC_TIME="POSIX",
- and LANG="ko_KR". In that case, POSIX says that LC_TIME
- determines the format and contents of date and time strings
- written by date, which means "date" must generate output
- using the POSIX locale; but adding _() would cause "date"
- to use a Korean translation of the format. */
- format = "%a %b %e %H:%M:%S %Z %Y";
- }
+ {
+ /* Do not wrap the following literal format string with _(...).
+ For example, suppose LC_ALL is unset, LC_TIME=POSIX,
+ and LANG="ko_KR". In that case, POSIX says that LC_TIME
+ determines the format and contents of date and time strings
+ written by date, which means "date" must generate output
+ using the POSIX locale; but adding _() would cause "date"
+ to use a Korean translation of the format. */
+ format = "%a %b %e %H:%M:%S %Z %Y";
+ }
}
+ timezone_t tz = tzalloc (getenv ("TZ"));
+
if (batch_file != NULL)
- ok = batch_convert (batch_file, format);
+ ok = batch_convert (batch_file, format, tz);
else
{
bool valid_date = true;
ok = true;
if (!option_specified_date && !set_date)
- {
- if (optind < argc)
- {
- /* Prepare to set system clock to the specified date/time
- given in the POSIX-format. */
- set_date = true;
- datestr = argv[optind];
- valid_date = posixtime (&when.tv_sec,
- datestr,
- (PDS_TRAILING_YEAR
- | PDS_CENTURY | PDS_SECONDS));
- when.tv_nsec = 0; /* FIXME: posixtime should set this. */
- }
- else
- {
- /* Prepare to print the current date/time. */
- gettime (&when);
- }
- }
+ {
+ if (optind < argc)
+ {
+ /* Prepare to set system clock to the specified date/time
+ given in the POSIX-format. */
+ set_date = true;
+ datestr = argv[optind];
+ valid_date = posixtime (&when.tv_sec,
+ datestr,
+ (PDS_TRAILING_YEAR
+ | PDS_CENTURY | PDS_SECONDS));
+ when.tv_nsec = 0; /* FIXME: posixtime should set this. */
+ }
+ else
+ {
+ /* Prepare to print the current date/time. */
+ gettime (&when);
+ }
+ }
else
- {
- /* (option_specified_date || set_date) */
- if (reference != NULL)
- {
- if (stat (reference, &refstats) != 0)
- error (EXIT_FAILURE, errno, "%s", reference);
- when = get_stat_mtime (&refstats);
- }
- else
- {
- if (set_datestr)
- datestr = set_datestr;
- valid_date = get_date (&when, datestr, NULL);
- }
- }
+ {
+ /* (option_specified_date || set_date) */
+ if (reference != NULL)
+ {
+ if (stat (reference, &refstats) != 0)
+ error (EXIT_FAILURE, errno, "%s", quotef (reference));
+ when = get_stat_mtime (&refstats);
+ }
+ else
+ {
+ if (set_datestr)
+ datestr = set_datestr;
+ valid_date = parse_datetime (&when, datestr, NULL);
+ }
+ }
if (! valid_date)
- error (EXIT_FAILURE, 0, _("invalid date %s"), quote (datestr));
+ error (EXIT_FAILURE, 0, _("invalid date %s"), quote (datestr));
if (set_date)
- {
- /* Set the system clock to the specified date, then regardless of
- the success of that operation, format and print that date. */
- if (settime (&when) != 0)
- {
- error (0, errno, _("cannot set date"));
- ok = false;
- }
- }
-
- ok &= show_date (format, when);
+ {
+ /* Set the system clock to the specified date, then regardless of
+ the success of that operation, format and print that date. */
+ if (settime (&when) != 0)
+ {
+ error (0, errno, _("cannot set date"));
+ ok = false;
+ }
+ }
+
+ ok &= show_date (format, when, tz);
}
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
/* Display the date and/or time in WHEN according to the format specified
in FORMAT, followed by a newline. Return true if successful. */
static bool
-show_date (const char *format, struct timespec when)
+show_date (const char *format, struct timespec when, timezone_t tz)
{
struct tm *tm;
@@ -546,15 +564,13 @@ show_date (const char *format, struct timespec when)
{
char buf[INT_BUFSIZE_BOUND (intmax_t)];
error (0, 0, _("time %s is out of range"),
- (TYPE_SIGNED (time_t)
- ? imaxtostr (when.tv_sec, buf)
- : umaxtostr (when.tv_sec, buf)));
+ quote (timetostr (when.tv_sec, buf)));
return false;
}
if (format == rfc_2822_format)
setlocale (LC_TIME, "C");
- fprintftime (stdout, format, tm, 0, when.tv_nsec);
+ fprintftime (stdout, format, tm, tz, when.tv_nsec);
fputc ('\n', stdout);
if (format == rfc_2822_format)
setlocale (LC_TIME, "");
diff --git a/src/dcgen b/src/dcgen
index 5df3c56..dc18d17 100755
--- a/src/dcgen
+++ b/src/dcgen
@@ -1,12 +1,12 @@
#!/usr/bin/perl -w
# dcgen -- convert dircolors.hin to dircolors.h.
-# Copyright (C) 1996, 1998, 2004, 2005, 2006 Free Software Foundation, Inc.
+# Copyright (C) 1996-2016 Free Software Foundation, Inc.
-# This program is free software; you can redistribute it and/or modify
+# This 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 2, or (at your option)
-# any later version.
+# 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
@@ -14,9 +14,7 @@
# 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, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA.
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
# written by Jim Meyering
diff --git a/src/dd.c b/src/dd.c
index 27a4a08..440950a 100644
--- a/src/dd.c
+++ b/src/dd.c
@@ -1,10 +1,10 @@
/* dd -- convert a file while copying it.
- Copyright (C) 85, 90, 91, 1995-2007 Free Software Foundation, Inc.
+ Copyright (C) 1985-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Paul Rubin, David MacKenzie, and Stuart Kemp. */
@@ -26,25 +25,27 @@
#include <getopt.h>
#include "system.h"
+#include "close-stream.h"
#include "error.h"
#include "fd-reopen.h"
#include "gethrxtime.h"
-#include "getpagesize.h"
#include "human.h"
#include "long-options.h"
#include "quote.h"
+#include "verror.h"
#include "xstrtol.h"
#include "xtime.h"
-static void process_signals (void);
-
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "dd"
-#define AUTHORS "Paul Rubin", "David MacKenzie", "Stuart Kemp"
+#define AUTHORS \
+ proper_name ("Paul Rubin"), \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Stuart Kemp")
/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
- present. SA_NODEFER and SA_RESETHAND are XSI extensions. */
+ present. */
#ifndef SA_NOCLDSTOP
# define SA_NOCLDSTOP 0
# define sigprocmask(How, Set, Oset) /* empty */
@@ -53,9 +54,8 @@ static void process_signals (void);
# define siginterrupt(sig, flag) /* empty */
# endif
#endif
-#ifndef SA_NODEFER
-# define SA_NODEFER 0
-#endif
+
+/* NonStop circa 2011 lacks SA_RESETHAND; see Bug#9076. */
#ifndef SA_RESETHAND
# define SA_RESETHAND 0
#endif
@@ -64,17 +64,26 @@ static void process_signals (void);
# define SIGINFO SIGUSR1
#endif
+/* This may belong in GNULIB's fcntl module instead.
+ Define O_CIO to 0 if it is not supported by this OS. */
+#ifndef O_CIO
+# define O_CIO 0
+#endif
+
+/* On AIX 5.1 and AIX 5.2, O_NOCACHE is defined via <fcntl.h>
+ and would interfere with our use of that name, below. */
+#undef O_NOCACHE
+
#if ! HAVE_FDATASYNC
# define fdatasync(fd) (errno = ENOSYS, -1)
#endif
-#define max(a, b) ((a) > (b) ? (a) : (b))
#define output_char(c) \
do \
{ \
obuf[oc++] = (c); \
if (oc >= output_blocksize) \
- write_output (); \
+ write_output (); \
} \
while (0)
@@ -117,18 +126,20 @@ enum
C_NOCREAT = 010000,
C_EXCL = 020000,
C_FDATASYNC = 040000,
- C_FSYNC = 0100000
+ C_FSYNC = 0100000,
+
+ C_SPARSE = 0200000
};
-/* Status bit masks. */
+/* Status levels. */
enum
{
- STATUS_NOXFER = 01
+ STATUS_NONE = 1,
+ STATUS_NOXFER = 2,
+ STATUS_DEFAULT = 3,
+ STATUS_PROGRESS = 4
};
-/* The name this program was run with. */
-char *program_name;
-
/* The name of the input file, or NULL for the standard input. */
static char const *input_file = NULL;
@@ -147,15 +158,29 @@ static size_t output_blocksize = 0;
/* Conversion buffer size, in bytes. 0 prevents conversions. */
static size_t conversion_blocksize = 0;
-/* Skip this many records of `input_blocksize' bytes before input. */
+/* Skip this many records of 'input_blocksize' bytes before input. */
static uintmax_t skip_records = 0;
-/* Skip this many records of `output_blocksize' bytes before output. */
+/* Skip this many bytes before input in addition of 'skip_records'
+ records. */
+static size_t skip_bytes = 0;
+
+/* Skip this many records of 'output_blocksize' bytes before output. */
static uintmax_t seek_records = 0;
+/* Skip this many bytes in addition to 'seek_records' records before
+ output. */
+static uintmax_t seek_bytes = 0;
+
+/* Whether the final output was done with a seek (rather than a write). */
+static bool final_op_was_seek;
+
/* Copy only this many records. The default is effectively infinity. */
static uintmax_t max_records = (uintmax_t) -1;
+/* Copy this many bytes in addition to 'max_records' records. */
+static size_t max_bytes = 0;
+
/* Bit vector of conversions to apply. */
static int conversions_mask = 0;
@@ -164,7 +189,7 @@ static int input_flags = 0;
static int output_flags = 0;
/* Status flags for what is printed to stderr. */
-static int status_flags = 0;
+static int status_level = STATUS_DEFAULT;
/* If nonzero, filter characters through the translation table. */
static bool translation_needed = false;
@@ -187,6 +212,12 @@ static uintmax_t w_bytes = 0;
/* Time that dd started. */
static xtime_t start_time;
+/* Next time to report periodic progress. */
+static xtime_t next_time;
+
+/* If positive, the number of bytes output in the current progress line. */
+static int progress_len;
+
/* True if input is seekable. */
static bool input_seekable;
@@ -195,11 +226,13 @@ static bool input_seekable;
static int input_seek_errno;
/* File offset of the input, in bytes, along with a flag recording
- whether it overflowed. The offset is valid only if the input is
- seekable and if the offset has not overflowed. */
+ whether it overflowed. */
static uintmax_t input_offset;
static bool input_offset_overflow;
+/* True if a partial read should be diagnosed. */
+static bool warn_partial_read;
+
/* Records truncated by conv=block. */
static uintmax_t r_truncate = 0;
@@ -208,13 +241,16 @@ static uintmax_t r_truncate = 0;
static char newline_character = '\n';
static char space_character = ' ';
+/* Input buffer. */
+static char *ibuf;
+
/* Output buffer. */
static char *obuf;
-/* Current index into `obuf'. */
+/* Current index into 'obuf'. */
static size_t oc = 0;
-/* Index into current line, for `conv=block' and `conv=unblock'. */
+/* Index into current line, for 'conv=block' and 'conv=unblock'. */
static size_t col = 0;
/* The set of signals that are caught. */
@@ -226,8 +262,14 @@ static sig_atomic_t volatile interrupt_signal;
/* A count of the number of pending info signals that have been received. */
static sig_atomic_t volatile info_signal_count;
+/* Whether to discard cache for input or output. */
+static bool i_nocache, o_nocache;
+
+/* Function used for read (to handle iflag=fullblock parameter). */
+static ssize_t (*iread_fnc) (int fd, char *buf, size_t size);
+
/* A longest symbol in the struct symbol_values tables below. */
-#define LONGEST_SYMBOL "fdatasync"
+#define LONGEST_SYMBOL "count_bytes"
/* A symbol and the corresponding integer value. */
struct symbol_value
@@ -239,13 +281,14 @@ struct symbol_value
/* Conversion symbols, for conv="...". */
static struct symbol_value const conversions[] =
{
- {"ascii", C_ASCII | C_TWOBUFS}, /* EBCDIC to ASCII. */
- {"ebcdic", C_EBCDIC | C_TWOBUFS}, /* ASCII to EBCDIC. */
- {"ibm", C_IBM | C_TWOBUFS}, /* Slightly different ASCII to EBCDIC. */
+ {"ascii", C_ASCII | C_UNBLOCK | C_TWOBUFS}, /* EBCDIC to ASCII. */
+ {"ebcdic", C_EBCDIC | C_BLOCK | C_TWOBUFS}, /* ASCII to EBCDIC. */
+ {"ibm", C_IBM | C_BLOCK | C_TWOBUFS}, /* Different ASCII to EBCDIC. */
{"block", C_BLOCK | C_TWOBUFS}, /* Variable to fixed length records. */
{"unblock", C_UNBLOCK | C_TWOBUFS}, /* Fixed to variable length records. */
{"lcase", C_LCASE | C_TWOBUFS}, /* Translate upper to lower case. */
{"ucase", C_UCASE | C_TWOBUFS}, /* Translate lower to upper case. */
+ {"sparse", C_SPARSE}, /* Try to sparsely write output. */
{"swab", C_SWAB | C_TWOBUFS}, /* Swap bytes of input. */
{"noerror", C_NOERROR}, /* Ignore i/o errors. */
{"nocreat", C_NOCREAT}, /* Do not create output file. */
@@ -257,52 +300,118 @@ static struct symbol_value const conversions[] =
{"", 0}
};
+#define FFS_MASK(x) ((x) ^ ((x) & ((x) - 1)))
+enum
+ {
+ /* Compute a value that's bitwise disjoint from the union
+ of all O_ values. */
+ v = ~(0
+ | O_APPEND
+ | O_BINARY
+ | O_CIO
+ | O_DIRECT
+ | O_DIRECTORY
+ | O_DSYNC
+ | O_NOATIME
+ | O_NOCTTY
+ | O_NOFOLLOW
+ | O_NOLINKS
+ | O_NONBLOCK
+ | O_SYNC
+ | O_TEXT
+ ),
+
+ /* Use its lowest bits for private flags. */
+ O_FULLBLOCK = FFS_MASK (v),
+ v2 = v ^ O_FULLBLOCK,
+
+ O_NOCACHE = FFS_MASK (v2),
+ v3 = v2 ^ O_NOCACHE,
+
+ O_COUNT_BYTES = FFS_MASK (v3),
+ v4 = v3 ^ O_COUNT_BYTES,
+
+ O_SKIP_BYTES = FFS_MASK (v4),
+ v5 = v4 ^ O_SKIP_BYTES,
+
+ O_SEEK_BYTES = FFS_MASK (v5)
+ };
+
+/* Ensure that we got something. */
+verify (O_FULLBLOCK != 0);
+verify (O_NOCACHE != 0);
+verify (O_COUNT_BYTES != 0);
+verify (O_SKIP_BYTES != 0);
+verify (O_SEEK_BYTES != 0);
+
+#define MULTIPLE_BITS_SET(i) (((i) & ((i) - 1)) != 0)
+
+/* Ensure that this is a single-bit value. */
+verify ( ! MULTIPLE_BITS_SET (O_FULLBLOCK));
+verify ( ! MULTIPLE_BITS_SET (O_NOCACHE));
+verify ( ! MULTIPLE_BITS_SET (O_COUNT_BYTES));
+verify ( ! MULTIPLE_BITS_SET (O_SKIP_BYTES));
+verify ( ! MULTIPLE_BITS_SET (O_SEEK_BYTES));
+
/* Flags, for iflag="..." and oflag="...". */
static struct symbol_value const flags[] =
{
- {"append", O_APPEND},
- {"binary", O_BINARY},
- {"direct", O_DIRECT},
- {"directory", O_DIRECTORY},
- {"dsync", O_DSYNC},
- {"noatime", O_NOATIME},
- {"noctty", O_NOCTTY},
- {"nofollow", HAVE_WORKING_O_NOFOLLOW ? O_NOFOLLOW : 0},
- {"nolinks", O_NOLINKS},
- {"nonblock", O_NONBLOCK},
- {"sync", O_SYNC},
- {"text", O_TEXT},
+ {"append", O_APPEND},
+ {"binary", O_BINARY},
+ {"cio", O_CIO},
+ {"direct", O_DIRECT},
+ {"directory", O_DIRECTORY},
+ {"dsync", O_DSYNC},
+ {"noatime", O_NOATIME},
+ {"nocache", O_NOCACHE}, /* Discard cache. */
+ {"noctty", O_NOCTTY},
+ {"nofollow", HAVE_WORKING_O_NOFOLLOW ? O_NOFOLLOW : 0},
+ {"nolinks", O_NOLINKS},
+ {"nonblock", O_NONBLOCK},
+ {"sync", O_SYNC},
+ {"text", O_TEXT},
+ {"fullblock", O_FULLBLOCK}, /* Accumulate full blocks from input. */
+ {"count_bytes", O_COUNT_BYTES},
+ {"skip_bytes", O_SKIP_BYTES},
+ {"seek_bytes", O_SEEK_BYTES},
{"", 0}
};
/* Status, for status="...". */
static struct symbol_value const statuses[] =
{
+ {"none", STATUS_NONE},
{"noxfer", STATUS_NOXFER},
+ {"progress", STATUS_PROGRESS},
{"", 0}
};
/* Translation table formed by applying successive transformations. */
static unsigned char trans_table[256];
+/* Standard translation tables, taken from POSIX 1003.1-2013.
+ Beware of imitations; there are lots of ASCII<->EBCDIC tables
+ floating around the net, perhaps valid for some applications but
+ not correct here. */
+
static char const ascii_to_ebcdic[] =
{
'\000', '\001', '\002', '\003', '\067', '\055', '\056', '\057',
'\026', '\005', '\045', '\013', '\014', '\015', '\016', '\017',
'\020', '\021', '\022', '\023', '\074', '\075', '\062', '\046',
'\030', '\031', '\077', '\047', '\034', '\035', '\036', '\037',
- '\100', '\117', '\177', '\173', '\133', '\154', '\120', '\175',
+ '\100', '\132', '\177', '\173', '\133', '\154', '\120', '\175',
'\115', '\135', '\134', '\116', '\153', '\140', '\113', '\141',
'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
'\370', '\371', '\172', '\136', '\114', '\176', '\156', '\157',
'\174', '\301', '\302', '\303', '\304', '\305', '\306', '\307',
'\310', '\311', '\321', '\322', '\323', '\324', '\325', '\326',
'\327', '\330', '\331', '\342', '\343', '\344', '\345', '\346',
- '\347', '\350', '\351', '\112', '\340', '\132', '\137', '\155',
+ '\347', '\350', '\351', '\255', '\340', '\275', '\232', '\155',
'\171', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
'\210', '\211', '\221', '\222', '\223', '\224', '\225', '\226',
'\227', '\230', '\231', '\242', '\243', '\244', '\245', '\246',
- '\247', '\250', '\251', '\300', '\152', '\320', '\241', '\007',
+ '\247', '\250', '\251', '\300', '\117', '\320', '\137', '\007',
'\040', '\041', '\042', '\043', '\044', '\025', '\006', '\027',
'\050', '\051', '\052', '\053', '\054', '\011', '\012', '\033',
'\060', '\061', '\032', '\063', '\064', '\065', '\066', '\010',
@@ -312,10 +421,10 @@ static char const ascii_to_ebcdic[] =
'\130', '\131', '\142', '\143', '\144', '\145', '\146', '\147',
'\150', '\151', '\160', '\161', '\162', '\163', '\164', '\165',
'\166', '\167', '\170', '\200', '\212', '\213', '\214', '\215',
- '\216', '\217', '\220', '\232', '\233', '\234', '\235', '\236',
- '\237', '\240', '\252', '\253', '\254', '\255', '\256', '\257',
+ '\216', '\217', '\220', '\152', '\233', '\234', '\235', '\236',
+ '\237', '\240', '\252', '\253', '\254', '\112', '\256', '\257',
'\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
- '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
+ '\270', '\271', '\272', '\273', '\274', '\241', '\276', '\277',
'\312', '\313', '\314', '\315', '\316', '\317', '\332', '\333',
'\334', '\335', '\336', '\337', '\352', '\353', '\354', '\355',
'\356', '\357', '\372', '\373', '\374', '\375', '\376', '\377'
@@ -368,21 +477,21 @@ static char const ebcdic_to_ascii[] =
'\220', '\221', '\026', '\223', '\224', '\225', '\226', '\004',
'\230', '\231', '\232', '\233', '\024', '\025', '\236', '\032',
'\040', '\240', '\241', '\242', '\243', '\244', '\245', '\246',
- '\247', '\250', '\133', '\056', '\074', '\050', '\053', '\041',
+ '\247', '\250', '\325', '\056', '\074', '\050', '\053', '\174',
'\046', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
- '\260', '\261', '\135', '\044', '\052', '\051', '\073', '\136',
+ '\260', '\261', '\041', '\044', '\052', '\051', '\073', '\176',
'\055', '\057', '\262', '\263', '\264', '\265', '\266', '\267',
- '\270', '\271', '\174', '\054', '\045', '\137', '\076', '\077',
+ '\270', '\271', '\313', '\054', '\045', '\137', '\076', '\077',
'\272', '\273', '\274', '\275', '\276', '\277', '\300', '\301',
'\302', '\140', '\072', '\043', '\100', '\047', '\075', '\042',
'\303', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
'\150', '\151', '\304', '\305', '\306', '\307', '\310', '\311',
'\312', '\152', '\153', '\154', '\155', '\156', '\157', '\160',
- '\161', '\162', '\313', '\314', '\315', '\316', '\317', '\320',
- '\321', '\176', '\163', '\164', '\165', '\166', '\167', '\170',
- '\171', '\172', '\322', '\323', '\324', '\325', '\326', '\327',
+ '\161', '\162', '\136', '\314', '\315', '\316', '\317', '\320',
+ '\321', '\345', '\163', '\164', '\165', '\166', '\167', '\170',
+ '\171', '\172', '\322', '\323', '\324', '\133', '\326', '\327',
'\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337',
- '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
+ '\340', '\341', '\342', '\343', '\344', '\135', '\346', '\347',
'\173', '\101', '\102', '\103', '\104', '\105', '\106', '\107',
'\110', '\111', '\350', '\351', '\352', '\353', '\354', '\355',
'\175', '\112', '\113', '\114', '\115', '\116', '\117', '\120',
@@ -393,43 +502,87 @@ static char const ebcdic_to_ascii[] =
'\070', '\071', '\372', '\373', '\374', '\375', '\376', '\377'
};
+/* True if we need to close the standard output *stream*. */
+static bool close_stdout_required = true;
+
+/* The only reason to close the standard output *stream* is if
+ parse_long_options fails (as it does for --help or --version).
+ In any other case, dd uses only the STDOUT_FILENO file descriptor,
+ and the "cleanup" function calls "close (STDOUT_FILENO)".
+ Closing the file descriptor and then letting the usual atexit-run
+ close_stdout function call "fclose (stdout)" would result in a
+ harmless failure of the close syscall (with errno EBADF).
+ This function serves solely to avoid the unnecessary close_stdout
+ call, once parse_long_options has succeeded.
+ Meanwhile, we guarantee that the standard error stream is flushed,
+ by inlining the last half of close_stdout as needed. */
+static void
+maybe_close_stdout (void)
+{
+ if (close_stdout_required)
+ close_stdout ();
+ else if (close_stream (stderr) != 0)
+ _exit (EXIT_FAILURE);
+}
+
+/* Like error() but handle any pending newline. */
+
+static void _GL_ATTRIBUTE_FORMAT ((__printf__, 3, 4))
+nl_error (int status, int errnum, const char *fmt, ...)
+{
+ if (0 < progress_len)
+ {
+ fputc ('\n', stderr);
+ progress_len = 0;
+ }
+
+ va_list ap;
+ va_start (ap, fmt);
+ verror (status, errnum, fmt, ap);
+ va_end (ap);
+}
+
+#define error nl_error
+
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPERAND]...\n\
or: %s OPTION\n\
"),
- program_name, program_name);
+ program_name, program_name);
fputs (_("\
Copy a file, converting and formatting according to the operands.\n\
\n\
- bs=BYTES force ibs=BYTES and obs=BYTES\n\
+ bs=BYTES read and write up to BYTES bytes at a time\n\
cbs=BYTES convert BYTES bytes at a time\n\
conv=CONVS convert the file as per the comma separated symbol list\n\
- count=BLOCKS copy only BLOCKS input blocks\n\
- ibs=BYTES read BYTES bytes at a time\n\
+ count=N copy only N input blocks\n\
+ ibs=BYTES read up to BYTES bytes at a time (default: 512)\n\
"), stdout);
fputs (_("\
if=FILE read from FILE instead of stdin\n\
iflag=FLAGS read as per the comma separated symbol list\n\
- obs=BYTES write BYTES bytes at a time\n\
+ obs=BYTES write BYTES bytes at a time (default: 512)\n\
of=FILE write to FILE instead of stdout\n\
oflag=FLAGS write as per the comma separated symbol list\n\
- seek=BLOCKS skip BLOCKS obs-sized blocks at start of output\n\
- skip=BLOCKS skip BLOCKS ibs-sized blocks at start of input\n\
- status=noxfer suppress transfer statistics\n\
+ seek=N skip N obs-sized blocks at start of output\n\
+ skip=N skip N ibs-sized blocks at start of input\n\
+ status=LEVEL The LEVEL of information to print to stderr;\n\
+ 'none' suppresses everything but error messages,\n\
+ 'noxfer' suppresses the final transfer statistics,\n\
+ 'progress' shows periodic transfer statistics\n\
"), stdout);
fputs (_("\
\n\
-BLOCKS and BYTES may be followed by the following multiplicative suffixes:\n\
-xM M, c 1, w 2, b 512, kB 1000, K 1024, MB 1000*1000, M 1024*1024,\n\
-GB 1000*1000*1000, G 1024*1024*1024, and so on for T, P, E, Z, Y.\n\
+N and BYTES may be followed by the following multiplicative suffixes:\n\
+c =1, w =2, b =512, kB =1000, K =1024, MB =1000*1000, M =1024*1024, xM =M\n\
+GB =1000*1000*1000, G =1024*1024*1024, and so on for T, P, E, Z, Y.\n\
\n\
Each CONV symbol may be:\n\
\n\
@@ -441,18 +594,17 @@ Each CONV symbol may be:\n\
block pad newline-terminated records with spaces to cbs-size\n\
unblock replace trailing spaces in cbs-size records with newline\n\
lcase change upper case to lower case\n\
-"), stdout);
- fputs (_("\
- nocreat do not create the output file\n\
- excl fail if the output file already exists\n\
- notrunc do not truncate the output file\n\
ucase change lower case to upper case\n\
+ sparse try to seek rather than write the output for NUL input blocks\n\
swab swap every pair of input bytes\n\
+ sync pad every input block with NULs to ibs-size; when used\n\
+ with block or unblock, pad with spaces rather than NULs\n\
"), stdout);
fputs (_("\
+ excl fail if the output file already exists\n\
+ nocreat do not create the output file\n\
+ notrunc do not truncate the output file\n\
noerror continue after read errors\n\
- sync pad every input block with NULs to ibs-size; when used\n\
- with block or unblock, pad with spaces rather than NULs\n\
fdatasync physically write output file data before finishing\n\
fsync likewise, but also write metadata\n\
"), stdout);
@@ -462,56 +614,129 @@ Each FLAG symbol may be:\n\
\n\
append append mode (makes sense only for output; conv=notrunc suggested)\n\
"), stdout);
+ if (O_CIO)
+ fputs (_(" cio use concurrent I/O for data\n"), stdout);
if (O_DIRECT)
- fputs (_(" direct use direct I/O for data\n"), stdout);
+ fputs (_(" direct use direct I/O for data\n"), stdout);
if (O_DIRECTORY)
- fputs (_(" directory fail unless a directory\n"), stdout);
+ fputs (_(" directory fail unless a directory\n"), stdout);
if (O_DSYNC)
- fputs (_(" dsync use synchronized I/O for data\n"), stdout);
+ fputs (_(" dsync use synchronized I/O for data\n"), stdout);
if (O_SYNC)
- fputs (_(" sync likewise, but also for metadata\n"), stdout);
+ fputs (_(" sync likewise, but also for metadata\n"), stdout);
+ fputs (_(" fullblock accumulate full blocks of input (iflag only)\n"),
+ stdout);
if (O_NONBLOCK)
- fputs (_(" nonblock use non-blocking I/O\n"), stdout);
+ fputs (_(" nonblock use non-blocking I/O\n"), stdout);
if (O_NOATIME)
- fputs (_(" noatime do not update access time\n"), stdout);
+ fputs (_(" noatime do not update access time\n"), stdout);
+#if HAVE_POSIX_FADVISE
+ if (O_NOCACHE)
+ fputs (_(" nocache Request to drop cache. See also oflag=sync\n"),
+ stdout);
+#endif
if (O_NOCTTY)
- fputs (_(" noctty do not assign controlling terminal from file\n"),
- stdout);
+ fputs (_(" noctty do not assign controlling terminal from file\n"),
+ stdout);
if (HAVE_WORKING_O_NOFOLLOW)
- fputs (_(" nofollow do not follow symlinks\n"), stdout);
+ fputs (_(" nofollow do not follow symlinks\n"), stdout);
if (O_NOLINKS)
- fputs (_(" nolinks fail if multiply-linked\n"), stdout);
+ fputs (_(" nolinks fail if multiply-linked\n"), stdout);
if (O_BINARY)
- fputs (_(" binary use binary I/O for data\n"), stdout);
+ fputs (_(" binary use binary I/O for data\n"), stdout);
if (O_TEXT)
- fputs (_(" text use text I/O for data\n"), stdout);
+ fputs (_(" text use text I/O for data\n"), stdout);
+ if (O_COUNT_BYTES)
+ fputs (_(" count_bytes treat 'count=N' as a byte count (iflag only)\n\
+"), stdout);
+ if (O_SKIP_BYTES)
+ fputs (_(" skip_bytes treat 'skip=N' as a byte count (iflag only)\n\
+"), stdout);
+ if (O_SEEK_BYTES)
+ fputs (_(" seek_bytes treat 'seek=N' as a byte count (oflag only)\n\
+"), stdout);
{
- char const *siginfo_name = (SIGINFO == SIGUSR1 ? "USR1" : "INFO");
- printf (_("\
+ printf (_("\
\n\
-Sending a %s signal to a running `dd' process makes it\n\
+Sending a %s signal to a running 'dd' process makes it\n\
print I/O statistics to standard error and then resume copying.\n\
\n\
- $ dd if=/dev/zero of=/dev/null& pid=$!\n\
- $ kill -%s $pid; sleep 1; kill $pid\n\
- 18335302+0 records in\n\
- 18335302+0 records out\n\
- 9387674624 bytes (9.4 GB) copied, 34.6279 seconds, 271 MB/s\n\
-\n\
Options are:\n\
\n\
-"),
- siginfo_name, siginfo_name);
+"), SIGINFO == SIGUSR1 ? "USR1" : "INFO");
}
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
+/* Common options to use when displaying sizes and rates. */
+
+enum { human_opts = (human_autoscale | human_round_to_nearest
+ | human_space_before_unit | human_SI | human_B) };
+
+/* Ensure input buffer IBUF is allocated. */
+
+static void
+alloc_ibuf (void)
+{
+ if (ibuf)
+ return;
+
+ char *real_buf = malloc (input_blocksize + INPUT_BLOCK_SLOP);
+ if (!real_buf)
+ {
+ uintmax_t ibs = input_blocksize;
+ char hbuf[LONGEST_HUMAN_READABLE + 1];
+ error (EXIT_FAILURE, 0,
+ _("memory exhausted by input buffer of size %"PRIuMAX
+ " bytes (%s)"),
+ ibs,
+ human_readable (input_blocksize, hbuf,
+ human_opts | human_base_1024, 1, 1));
+ }
+
+ real_buf += SWAB_ALIGN_OFFSET; /* allow space for swab */
+
+ ibuf = ptr_align (real_buf, page_size);
+}
+
+/* Ensure output buffer OBUF is allocated/initialized. */
+
+static void
+alloc_obuf (void)
+{
+ if (obuf)
+ return;
+
+ if (conversions_mask & C_TWOBUFS)
+ {
+ /* Page-align the output buffer, too. */
+ char *real_obuf = malloc (output_blocksize + OUTPUT_BLOCK_SLOP);
+ if (!real_obuf)
+ {
+ uintmax_t obs = output_blocksize;
+ char hbuf[LONGEST_HUMAN_READABLE + 1];
+ error (EXIT_FAILURE, 0,
+ _("memory exhausted by output buffer of size %"PRIuMAX
+ " bytes (%s)"),
+ obs,
+ human_readable (output_blocksize, hbuf,
+ human_opts | human_base_1024, 1, 1));
+ }
+ obuf = ptr_align (real_obuf, page_size);
+ }
+ else
+ {
+ alloc_ibuf ();
+ obuf = ibuf;
+ }
+}
+
static void
translate_charset (char const *new_trans)
{
@@ -527,55 +752,38 @@ translate_charset (char const *new_trans)
static inline bool
multiple_bits_set (int i)
{
- return (i & (i - 1)) != 0;
+ return MULTIPLE_BITS_SET (i);
+}
+
+static bool
+abbreviation_lacks_prefix (char const *message)
+{
+ return message[strlen (message) - 2] == ' ';
}
/* Print transfer statistics. */
static void
-print_stats (void)
+print_xfer_stats (xtime_t progress_time)
{
- xtime_t now = gethrxtime ();
- char hbuf[LONGEST_HUMAN_READABLE + 1];
- int human_opts =
- (human_autoscale | human_round_to_nearest
- | human_space_before_unit | human_SI | human_B);
+ xtime_t now = progress_time ? progress_time : gethrxtime ();
+ char hbuf[3][LONGEST_HUMAN_READABLE + 1];
double delta_s;
char const *bytes_per_second;
-
- fprintf (stderr,
- _("%"PRIuMAX"+%"PRIuMAX" records in\n"
- "%"PRIuMAX"+%"PRIuMAX" records out\n"),
- r_full, r_partial, w_full, w_partial);
-
- if (r_truncate != 0)
- fprintf (stderr,
- ngettext ("%"PRIuMAX" truncated record\n",
- "%"PRIuMAX" truncated records\n",
- select_plural (r_truncate)),
- r_truncate);
-
- if (status_flags & STATUS_NOXFER)
- return;
+ char const *si = human_readable (w_bytes, hbuf[0], human_opts, 1, 1);
+ char const *iec = human_readable (w_bytes, hbuf[1],
+ human_opts | human_base_1024, 1, 1);
/* Use integer arithmetic to compute the transfer rate,
since that makes it easy to use SI abbreviations. */
-
- fprintf (stderr,
- ngettext ("%"PRIuMAX" byte (%s) copied",
- "%"PRIuMAX" bytes (%s) copied",
- select_plural (w_bytes)),
- w_bytes,
- human_readable (w_bytes, hbuf, human_opts, 1, 1));
-
if (start_time < now)
{
double XTIME_PRECISIONe0 = XTIME_PRECISION;
uintmax_t delta_xtime = now;
delta_xtime -= start_time;
delta_s = delta_xtime / XTIME_PRECISIONe0;
- bytes_per_second = human_readable (w_bytes, hbuf, human_opts,
- XTIME_PRECISION, delta_xtime);
+ bytes_per_second = human_readable (w_bytes, hbuf[2], human_opts,
+ XTIME_PRECISION, delta_xtime);
}
else
{
@@ -583,41 +791,71 @@ print_stats (void)
bytes_per_second = _("Infinity B");
}
- /* TRANSLATORS: The two instances of "s" in this string are the SI
- symbol "s" (meaning second), and should not be translated.
-
- This format used to be:
-
- ngettext (", %g second, %s/s\n", ", %g seconds, %s/s\n", delta_s == 1)
-
- but that was incorrect for languages like Polish. To fix this
- bug we now use SI symbols even though they're a bit more
- confusing in English. */
- fprintf (stderr, _(", %g s, %s/s\n"), delta_s, bytes_per_second);
+ if (progress_time)
+ fputc ('\r', stderr);
+
+ /* TRANSLATORS: The instances of "s" in the following formats are
+ the SI symbol "s" (meaning second), and should not be translated.
+ The strings use SI symbols for better internationalization even
+ though they may be a bit more confusing in English. If one of
+ these formats A looks shorter on the screen than another format
+ B, then A's string length should be less than B's, and appending
+ strlen (B) - strlen (A) spaces to A should make it appear to be
+ at least as long as B. */
+
+ int stats_len
+ = (abbreviation_lacks_prefix (si)
+ ? fprintf (stderr,
+ ngettext ("%"PRIuMAX" byte copied, %g s, %s/s",
+ "%"PRIuMAX" bytes copied, %g s, %s/s",
+ select_plural (w_bytes)),
+ w_bytes, delta_s, bytes_per_second)
+ : abbreviation_lacks_prefix (iec)
+ ? fprintf (stderr,
+ _("%"PRIuMAX" bytes (%s) copied, %g s, %s/s"),
+ w_bytes, si, delta_s, bytes_per_second)
+ : fprintf (stderr,
+ _("%"PRIuMAX" bytes (%s, %s) copied, %g s, %s/s"),
+ w_bytes, si, iec, delta_s, bytes_per_second));
+
+ if (progress_time)
+ {
+ if (0 <= stats_len && stats_len < progress_len)
+ fprintf (stderr, "%*s", progress_len - stats_len, "");
+ progress_len = stats_len;
+ }
+ else
+ fputc ('\n', stderr);
}
static void
-cleanup (void)
+print_stats (void)
{
- if (close (STDIN_FILENO) < 0)
- error (EXIT_FAILURE, errno,
- _("closing input file %s"), quote (input_file));
+ if (status_level == STATUS_NONE)
+ return;
- /* Don't remove this call to close, even though close_stdout
- closes standard output. This close is necessary when cleanup
- is called as part of a signal handler. */
- if (close (STDOUT_FILENO) < 0)
- error (EXIT_FAILURE, errno,
- _("closing output file %s"), quote (output_file));
-}
+ if (0 < progress_len)
+ {
+ fputc ('\n', stderr);
+ progress_len = 0;
+ }
-static inline void ATTRIBUTE_NORETURN
-quit (int code)
-{
- cleanup ();
- print_stats ();
- process_signals ();
- exit (code);
+ fprintf (stderr,
+ _("%"PRIuMAX"+%"PRIuMAX" records in\n"
+ "%"PRIuMAX"+%"PRIuMAX" records out\n"),
+ r_full, r_partial, w_full, w_partial);
+
+ if (r_truncate != 0)
+ fprintf (stderr,
+ ngettext ("%"PRIuMAX" truncated record\n",
+ "%"PRIuMAX" truncated records\n",
+ select_plural (r_truncate)),
+ r_truncate);
+
+ if (status_level == STATUS_NOXFER)
+ return;
+
+ print_xfer_stats (0);
}
/* An ordinary signal was received; arrange for the program to exit. */
@@ -652,11 +890,7 @@ install_signal_handlers (void)
struct sigaction act;
sigemptyset (&caught_signals);
if (catch_siginfo)
- {
- sigaction (SIGINFO, NULL, &act);
- if (act.sa_handler != SIG_IGN)
- sigaddset (&caught_signals, SIGINFO);
- }
+ sigaddset (&caught_signals, SIGINFO);
sigaction (SIGINT, NULL, &act);
if (act.sa_handler != SIG_IGN)
sigaddset (&caught_signals, SIGINT);
@@ -665,15 +899,15 @@ install_signal_handlers (void)
if (sigismember (&caught_signals, SIGINFO))
{
act.sa_handler = siginfo_handler;
+ /* Note we don't use SA_RESTART here and instead
+ handle EINTR explicitly in iftruncate() etc.
+ to avoid blocking on noncommitted read()/write() calls. */
act.sa_flags = 0;
sigaction (SIGINFO, &act, NULL);
}
if (sigismember (&caught_signals, SIGINT))
{
- /* POSIX 1003.1-2001 says SA_RESETHAND implies SA_NODEFER,
- but this is not true on Solaris 8 at least. It doesn't
- hurt to use SA_NODEFER here, so leave it in. */
act.sa_handler = interrupt_handler;
act.sa_flags = SA_NODEFER | SA_RESETHAND;
sigaction (SIGINT, &act, NULL);
@@ -681,7 +915,7 @@ install_signal_handlers (void)
#else
- if (catch_siginfo && signal (SIGINFO, SIG_IGN) != SIG_IGN)
+ if (catch_siginfo)
{
signal (SIGINFO, siginfo_handler);
siginterrupt (SIGINFO, 1);
@@ -694,6 +928,21 @@ install_signal_handlers (void)
#endif
}
+static void
+cleanup (void)
+{
+ if (close (STDIN_FILENO) < 0)
+ error (EXIT_FAILURE, errno,
+ _("closing input file %s"), quoteaf (input_file));
+
+ /* Don't remove this call to close, even though close_stdout
+ closes standard output. This close is necessary when cleanup
+ is called as part of a signal handler. */
+ if (close (STDOUT_FILENO) < 0)
+ error (EXIT_FAILURE, errno,
+ _("closing output file %s"), quoteaf (output_file));
+}
+
/* Process any pending signals. If signals are caught, this function
should be called periodically. Ideally there should never be an
unbounded amount of time when signals are not being processed. */
@@ -701,7 +950,7 @@ install_signal_handlers (void)
static void
process_signals (void)
{
- while (interrupt_signal | info_signal_count)
+ while (interrupt_signal || info_signal_count)
{
int interrupt;
int infos;
@@ -710,23 +959,123 @@ process_signals (void)
sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
/* Reload interrupt_signal and info_signal_count, in case a new
- signal was handled before sigprocmask took effect. */
+ signal was handled before sigprocmask took effect. */
interrupt = interrupt_signal;
infos = info_signal_count;
if (infos)
- info_signal_count = infos - 1;
+ info_signal_count = infos - 1;
sigprocmask (SIG_SETMASK, &oldset, NULL);
if (interrupt)
- cleanup ();
+ cleanup ();
print_stats ();
if (interrupt)
- raise (interrupt);
+ raise (interrupt);
}
}
+static void
+finish_up (void)
+{
+ cleanup ();
+ print_stats ();
+ process_signals ();
+}
+
+static void ATTRIBUTE_NORETURN
+quit (int code)
+{
+ finish_up ();
+ exit (code);
+}
+
+/* Return LEN rounded down to a multiple of PAGE_SIZE
+ while storing the remainder internally per FD.
+ Pass LEN == 0 to get the current remainder. */
+
+static off_t
+cache_round (int fd, off_t len)
+{
+ static off_t i_pending, o_pending;
+ off_t *pending = (fd == STDIN_FILENO ? &i_pending : &o_pending);
+
+ if (len)
+ {
+ uintmax_t c_pending = *pending + len;
+ *pending = c_pending % page_size;
+ if (c_pending > *pending)
+ len = c_pending - *pending;
+ else
+ len = 0;
+ }
+ else
+ len = *pending;
+
+ return len;
+}
+
+/* Discard the cache from the current offset of either
+ STDIN_FILENO or STDOUT_FILENO.
+ Return true on success. */
+
+static bool
+invalidate_cache (int fd, off_t len)
+{
+ int adv_ret = -1;
+
+ /* Minimize syscalls. */
+ off_t clen = cache_round (fd, len);
+ if (len && !clen)
+ return true; /* Don't advise this time. */
+ if (!len && !clen && max_records)
+ return true; /* Nothing pending. */
+ off_t pending = len ? cache_round (fd, 0) : 0;
+
+ if (fd == STDIN_FILENO)
+ {
+ if (input_seekable)
+ {
+ /* Note we're being careful here to only invalidate what
+ we've read, so as not to dump any read ahead cache. */
+#if HAVE_POSIX_FADVISE
+ adv_ret = posix_fadvise (fd, input_offset - clen - pending, clen,
+ POSIX_FADV_DONTNEED);
+#else
+ errno = ENOTSUP;
+#endif
+ }
+ else
+ errno = ESPIPE;
+ }
+ else if (fd == STDOUT_FILENO)
+ {
+ static off_t output_offset = -2;
+
+ if (output_offset != -1)
+ {
+ if (0 > output_offset)
+ {
+ output_offset = lseek (fd, 0, SEEK_CUR);
+ output_offset -= clen + pending;
+ }
+ if (0 <= output_offset)
+ {
+#if HAVE_POSIX_FADVISE
+ adv_ret = posix_fadvise (fd, output_offset, clen,
+ POSIX_FADV_DONTNEED);
+#else
+ errno = ENOTSUP;
+#endif
+ output_offset += clen + pending;
+ }
+ }
+ }
+
+ return adv_ret != -1 ? true : false;
+}
+
/* Read from FD into the buffer BUF of size SIZE, processing any
signals that arrive before bytes are read. Return the number of
bytes read if successful, -1 (setting errno) on failure. */
@@ -734,14 +1083,61 @@ process_signals (void)
static ssize_t
iread (int fd, char *buf, size_t size)
{
- for (;;)
+ ssize_t nread;
+
+ do
{
- ssize_t nread;
process_signals ();
nread = read (fd, buf, size);
- if (! (nread < 0 && errno == EINTR))
- return nread;
}
+ while (nread < 0 && errno == EINTR);
+
+ /* Short read may be due to received signal. */
+ if (0 < nread && nread < size)
+ process_signals ();
+
+ if (0 < nread && warn_partial_read)
+ {
+ static ssize_t prev_nread;
+
+ if (0 < prev_nread && prev_nread < size)
+ {
+ uintmax_t prev = prev_nread;
+ if (status_level != STATUS_NONE)
+ error (0, 0, ngettext (("warning: partial read (%"PRIuMAX" byte); "
+ "suggest iflag=fullblock"),
+ ("warning: partial read (%"PRIuMAX" bytes); "
+ "suggest iflag=fullblock"),
+ select_plural (prev)),
+ prev);
+ warn_partial_read = false;
+ }
+
+ prev_nread = nread;
+ }
+
+ return nread;
+}
+
+/* Wrapper around iread function to accumulate full blocks. */
+static ssize_t
+iread_fullblock (int fd, char *buf, size_t size)
+{
+ ssize_t nread = 0;
+
+ while (0 < size)
+ {
+ ssize_t ncurr = iread (fd, buf, size);
+ if (ncurr < 0)
+ return ncurr;
+ if (ncurr == 0)
+ break;
+ nread += ncurr;
+ buf += ncurr;
+ size -= ncurr;
+ }
+
+ return nread;
}
/* Write to FD the buffer BUF of size SIZE, processing any signals
@@ -754,32 +1150,74 @@ iwrite (int fd, char const *buf, size_t size)
{
size_t total_written = 0;
+ if ((output_flags & O_DIRECT) && size < output_blocksize)
+ {
+ int old_flags = fcntl (STDOUT_FILENO, F_GETFL);
+ if (fcntl (STDOUT_FILENO, F_SETFL, old_flags & ~O_DIRECT) != 0
+ && status_level != STATUS_NONE)
+ error (0, errno, _("failed to turn off O_DIRECT: %s"),
+ quotef (output_file));
+
+ /* Since we have just turned off O_DIRECT for the final write,
+ here we try to preserve some of its semantics. First, use
+ posix_fadvise to tell the system not to pollute the buffer
+ cache with this data. Don't bother to diagnose lseek or
+ posix_fadvise failure. */
+ invalidate_cache (STDOUT_FILENO, 0);
+
+ /* Attempt to ensure that that final block is committed
+ to disk as quickly as possible. */
+ conversions_mask |= C_FSYNC;
+ }
+
while (total_written < size)
{
- ssize_t nwritten;
+ ssize_t nwritten = 0;
process_signals ();
- nwritten = write (fd, buf + total_written, size - total_written);
+
+ /* Perform a seek for a NUL block if sparse output is enabled. */
+ final_op_was_seek = false;
+ if ((conversions_mask & C_SPARSE) && is_nul (buf, size))
+ {
+ if (lseek (fd, size, SEEK_CUR) < 0)
+ {
+ conversions_mask &= ~C_SPARSE;
+ /* Don't warn about the advisory sparse request. */
+ }
+ else
+ {
+ final_op_was_seek = true;
+ nwritten = size;
+ }
+ }
+
+ if (!nwritten)
+ nwritten = write (fd, buf + total_written, size - total_written);
+
if (nwritten < 0)
- {
- if (errno != EINTR)
- break;
- }
+ {
+ if (errno != EINTR)
+ break;
+ }
else if (nwritten == 0)
- {
- /* Some buggy drivers return 0 when one tries to write beyond
- a device's end. (Example: Linux 1.2.13 on /dev/fd0.)
- Set errno to ENOSPC so they get a sensible diagnostic. */
- errno = ENOSPC;
- break;
- }
+ {
+ /* Some buggy drivers return 0 when one tries to write beyond
+ a device's end. (Example: Linux kernel 1.2.13 on /dev/fd0.)
+ Set errno to ENOSPC so they get a sensible diagnostic. */
+ errno = ENOSPC;
+ break;
+ }
else
- total_written += nwritten;
+ total_written += nwritten;
}
+ if (o_nocache && total_written)
+ invalidate_cache (fd, total_written);
+
return total_written;
}
-/* Write, then empty, the output buffer `obuf'. */
+/* Write, then empty, the output buffer 'obuf'. */
static void
write_output (void)
@@ -788,9 +1226,9 @@ write_output (void)
w_bytes += nwritten;
if (nwritten != output_blocksize)
{
- error (0, errno, _("writing to %s"), quote (output_file));
+ error (0, errno, _("writing to %s"), quoteaf (output_file));
if (nwritten != 0)
- w_partial++;
+ w_partial++;
quit (EXIT_FAILURE);
}
else
@@ -798,165 +1236,234 @@ write_output (void)
oc = 0;
}
+/* Restart on EINTR from fd_reopen(). */
+
+static int
+ifd_reopen (int desired_fd, char const *file, int flag, mode_t mode)
+{
+ int ret;
+
+ do
+ {
+ process_signals ();
+ ret = fd_reopen (desired_fd, file, flag, mode);
+ }
+ while (ret < 0 && errno == EINTR);
+
+ return ret;
+}
+
+/* Restart on EINTR from ftruncate(). */
+
+static int
+iftruncate (int fd, off_t length)
+{
+ int ret;
+
+ do
+ {
+ process_signals ();
+ ret = ftruncate (fd, length);
+ }
+ while (ret < 0 && errno == EINTR);
+
+ return ret;
+}
+
+/* Return true if STR is of the form "PATTERN" or "PATTERNDELIM...". */
+
+static bool _GL_ATTRIBUTE_PURE
+operand_matches (char const *str, char const *pattern, char delim)
+{
+ while (*pattern)
+ if (*str++ != *pattern++)
+ return false;
+ return !*str || *str == delim;
+}
+
/* Interpret one "conv=..." or similar operand STR according to the
symbols in TABLE, returning the flags specified. If the operand
- cannot be parsed, use ERROR_MSGID to generate a diagnostic.
- As a by product, this function replaces each `,' in STR with a NUL byte. */
+ cannot be parsed, use ERROR_MSGID to generate a diagnostic. */
static int
-parse_symbols (char *str, struct symbol_value const *table,
- char const *error_msgid)
+parse_symbols (char const *str, struct symbol_value const *table,
+ bool exclusive, char const *error_msgid)
{
int value = 0;
- do
+ while (true)
{
+ char const *strcomma = strchr (str, ',');
struct symbol_value const *entry;
- char *new = strchr (str, ',');
- if (new != NULL)
- *new++ = '\0';
- for (entry = table; ; entry++)
- {
- if (! entry->symbol[0])
- {
- error (0, 0, _(error_msgid), quote (str));
- usage (EXIT_FAILURE);
- }
- if (STREQ (entry->symbol, str))
- {
- if (! entry->value)
- error (EXIT_FAILURE, 0, _(error_msgid), quote (str));
- value |= entry->value;
- break;
- }
- }
- str = new;
- }
- while (str);
+
+ for (entry = table;
+ ! (operand_matches (str, entry->symbol, ',') && entry->value);
+ entry++)
+ {
+ if (! entry->symbol[0])
+ {
+ size_t slen = strcomma ? strcomma - str : strlen (str);
+ error (0, 0, "%s: %s", _(error_msgid),
+ quotearg_n_style_mem (0, locale_quoting_style, str, slen));
+ usage (EXIT_FAILURE);
+ }
+ }
+
+ if (exclusive)
+ value = entry->value;
+ else
+ value |= entry->value;
+ if (!strcomma)
+ break;
+ str = strcomma + 1;
+ }
return value;
}
/* Return the value of STR, interpreted as a non-negative decimal integer,
optionally multiplied by various values.
- Set *INVALID if STR does not represent a number in this format. */
+ Set *INVALID to a nonzero error value if STR does not represent a
+ number in this format. */
static uintmax_t
-parse_integer (const char *str, bool *invalid)
+parse_integer (const char *str, strtol_error *invalid)
{
uintmax_t n;
char *suffix;
- enum strtol_error e = xstrtoumax (str, &suffix, 10, &n, "bcEGkKMPTwYZ0");
+ strtol_error e = xstrtoumax (str, &suffix, 10, &n, "bcEGkKMPTwYZ0");
if (e == LONGINT_INVALID_SUFFIX_CHAR && *suffix == 'x')
{
uintmax_t multiplier = parse_integer (suffix + 1, invalid);
if (multiplier != 0 && n * multiplier / multiplier != n)
- {
- *invalid = true;
- return 0;
- }
+ {
+ *invalid = LONGINT_OVERFLOW;
+ return 0;
+ }
n *= multiplier;
}
else if (e != LONGINT_OK)
{
- *invalid = true;
+ *invalid = e;
return 0;
}
return n;
}
+/* OPERAND is of the form "X=...". Return true if X is NAME. */
+
+static bool _GL_ATTRIBUTE_PURE
+operand_is (char const *operand, char const *name)
+{
+ return operand_matches (operand, name, '=');
+}
+
static void
-scanargs (int argc, char **argv)
+scanargs (int argc, char *const *argv)
{
int i;
size_t blocksize = 0;
+ uintmax_t count = (uintmax_t) -1;
+ uintmax_t skip = 0;
+ uintmax_t seek = 0;
for (i = optind; i < argc; i++)
{
- char *name, *val;
+ char const *name = argv[i];
+ char const *val = strchr (name, '=');
- name = argv[i];
- val = strchr (name, '=');
if (val == NULL)
- {
- error (0, 0, _("unrecognized operand %s"), quote (name));
- usage (EXIT_FAILURE);
- }
- *val++ = '\0';
-
- if (STREQ (name, "if"))
- input_file = val;
- else if (STREQ (name, "of"))
- output_file = val;
- else if (STREQ (name, "conv"))
- conversions_mask |= parse_symbols (val, conversions,
- N_("invalid conversion: %s"));
- else if (STREQ (name, "iflag"))
- input_flags |= parse_symbols (val, flags,
- N_("invalid input flag: %s"));
- else if (STREQ (name, "oflag"))
- output_flags |= parse_symbols (val, flags,
- N_("invalid output flag: %s"));
- else if (STREQ (name, "status"))
- status_flags |= parse_symbols (val, statuses,
- N_("invalid status flag: %s"));
+ {
+ error (0, 0, _("unrecognized operand %s"),
+ quote (name));
+ usage (EXIT_FAILURE);
+ }
+ val++;
+
+ if (operand_is (name, "if"))
+ input_file = val;
+ else if (operand_is (name, "of"))
+ output_file = val;
+ else if (operand_is (name, "conv"))
+ conversions_mask |= parse_symbols (val, conversions, false,
+ N_("invalid conversion"));
+ else if (operand_is (name, "iflag"))
+ input_flags |= parse_symbols (val, flags, false,
+ N_("invalid input flag"));
+ else if (operand_is (name, "oflag"))
+ output_flags |= parse_symbols (val, flags, false,
+ N_("invalid output flag"));
+ else if (operand_is (name, "status"))
+ status_level = parse_symbols (val, statuses, true,
+ N_("invalid status level"));
else
- {
- bool invalid = false;
- uintmax_t n = parse_integer (val, &invalid);
-
- if (STREQ (name, "ibs"))
- {
- invalid |= ! (0 < n && n <= MAX_BLOCKSIZE (INPUT_BLOCK_SLOP));
- input_blocksize = n;
- conversions_mask |= C_TWOBUFS;
- }
- else if (STREQ (name, "obs"))
- {
- invalid |= ! (0 < n && n <= MAX_BLOCKSIZE (OUTPUT_BLOCK_SLOP));
- output_blocksize = n;
- conversions_mask |= C_TWOBUFS;
- }
- else if (STREQ (name, "bs"))
- {
- invalid |= ! (0 < n && n <= MAX_BLOCKSIZE (INPUT_BLOCK_SLOP));
- blocksize = n;
- }
- else if (STREQ (name, "cbs"))
- {
- invalid |= ! (0 < n && n <= SIZE_MAX);
- conversion_blocksize = n;
- }
- else if (STREQ (name, "skip"))
- skip_records = n;
- else if (STREQ (name, "seek"))
- seek_records = n;
- else if (STREQ (name, "count"))
- max_records = n;
- else
- {
- error (0, 0, _("unrecognized operand %s=%s"),
- quote_n (0, name), quote_n (1, val));
- usage (EXIT_FAILURE);
- }
-
- if (invalid)
- error (EXIT_FAILURE, 0, _("invalid number %s"), quote (val));
- }
+ {
+ strtol_error invalid = LONGINT_OK;
+ uintmax_t n = parse_integer (val, &invalid);
+ uintmax_t n_min = 0;
+ uintmax_t n_max = UINTMAX_MAX;
+
+ if (operand_is (name, "ibs"))
+ {
+ n_min = 1;
+ n_max = MAX_BLOCKSIZE (INPUT_BLOCK_SLOP);
+ input_blocksize = n;
+ }
+ else if (operand_is (name, "obs"))
+ {
+ n_min = 1;
+ n_max = MAX_BLOCKSIZE (OUTPUT_BLOCK_SLOP);
+ output_blocksize = n;
+ }
+ else if (operand_is (name, "bs"))
+ {
+ n_min = 1;
+ n_max = MAX_BLOCKSIZE (INPUT_BLOCK_SLOP);
+ blocksize = n;
+ }
+ else if (operand_is (name, "cbs"))
+ {
+ n_min = 1;
+ n_max = SIZE_MAX;
+ conversion_blocksize = n;
+ }
+ else if (operand_is (name, "skip"))
+ skip = n;
+ else if (operand_is (name, "seek"))
+ seek = n;
+ else if (operand_is (name, "count"))
+ count = n;
+ else
+ {
+ error (0, 0, _("unrecognized operand %s"),
+ quote (name));
+ usage (EXIT_FAILURE);
+ }
+
+ if (n < n_min)
+ invalid = LONGINT_INVALID;
+ else if (n_max < n)
+ invalid = LONGINT_OVERFLOW;
+
+ if (invalid != LONGINT_OK)
+ error (EXIT_FAILURE, invalid == LONGINT_OVERFLOW ? EOVERFLOW : 0,
+ "%s: %s", _("invalid number"), quote (val));
+ }
}
if (blocksize)
input_blocksize = output_blocksize = blocksize;
+ else
+ {
+ /* POSIX says dd aggregates partial reads into
+ output_blocksize if bs= is not specified. */
+ conversions_mask |= C_TWOBUFS;
+ }
- /* If bs= was given, both `input_blocksize' and `output_blocksize' will
- have been set to positive values. If either has not been set,
- bs= was not given, so make sure two buffers are used. */
- if (input_blocksize == 0 || output_blocksize == 0)
- conversions_mask |= C_TWOBUFS;
if (input_blocksize == 0)
input_blocksize = DEFAULT_BLOCKSIZE;
if (output_blocksize == 0)
@@ -967,6 +1474,65 @@ scanargs (int argc, char **argv)
if (input_flags & (O_DSYNC | O_SYNC))
input_flags |= O_RSYNC;
+ if (output_flags & O_FULLBLOCK)
+ {
+ error (0, 0, "%s: %s", _("invalid output flag"), quote ("fullblock"));
+ usage (EXIT_FAILURE);
+ }
+
+ if (input_flags & O_SEEK_BYTES)
+ {
+ error (0, 0, "%s: %s", _("invalid input flag"), quote ("seek_bytes"));
+ usage (EXIT_FAILURE);
+ }
+
+ if (output_flags & (O_COUNT_BYTES | O_SKIP_BYTES))
+ {
+ error (0, 0, "%s: %s", _("invalid output flag"),
+ quote (output_flags & O_COUNT_BYTES
+ ? "count_bytes" : "skip_bytes"));
+ usage (EXIT_FAILURE);
+ }
+
+ if (input_flags & O_SKIP_BYTES && skip != 0)
+ {
+ skip_records = skip / input_blocksize;
+ skip_bytes = skip % input_blocksize;
+ }
+ else if (skip != 0)
+ skip_records = skip;
+
+ if (input_flags & O_COUNT_BYTES && count != (uintmax_t) -1)
+ {
+ max_records = count / input_blocksize;
+ max_bytes = count % input_blocksize;
+ }
+ else if (count != (uintmax_t) -1)
+ max_records = count;
+
+ if (output_flags & O_SEEK_BYTES && seek != 0)
+ {
+ seek_records = seek / output_blocksize;
+ seek_bytes = seek % output_blocksize;
+ }
+ else if (seek != 0)
+ seek_records = seek;
+
+ /* Warn about partial reads if bs=SIZE is given and iflag=fullblock
+ is not, and if counting or skipping bytes or using direct I/O.
+ This helps to avoid confusion with miscounts, and to avoid issues
+ with direct I/O on GNU/Linux. */
+ warn_partial_read =
+ (! (conversions_mask & C_TWOBUFS) && ! (input_flags & O_FULLBLOCK)
+ && (skip_records
+ || (0 < max_records && max_records < (uintmax_t) -1)
+ || (input_flags | output_flags) & O_DIRECT));
+
+ iread_fnc = ((input_flags & O_FULLBLOCK)
+ ? iread_fullblock
+ : iread);
+ input_flags &= ~O_FULLBLOCK;
+
if (multiple_bits_set (conversions_mask & (C_ASCII | C_EBCDIC | C_IBM)))
error (EXIT_FAILURE, 0, _("cannot combine any two of {ascii,ebcdic,ibm}"));
if (multiple_bits_set (conversions_mask & (C_BLOCK | C_UNBLOCK)))
@@ -975,6 +1541,20 @@ scanargs (int argc, char **argv)
error (EXIT_FAILURE, 0, _("cannot combine lcase and ucase"));
if (multiple_bits_set (conversions_mask & (C_EXCL | C_NOCREAT)))
error (EXIT_FAILURE, 0, _("cannot combine excl and nocreat"));
+ if (multiple_bits_set (input_flags & (O_DIRECT | O_NOCACHE))
+ || multiple_bits_set (output_flags & (O_DIRECT | O_NOCACHE)))
+ error (EXIT_FAILURE, 0, _("cannot combine direct and nocache"));
+
+ if (input_flags & O_NOCACHE)
+ {
+ i_nocache = true;
+ input_flags &= ~O_NOCACHE;
+ }
+ if (output_flags & O_NOCACHE)
+ {
+ o_nocache = true;
+ output_flags &= ~O_NOCACHE;
+ }
}
/* Fix up translation table. */
@@ -990,13 +1570,13 @@ apply_translations (void)
if (conversions_mask & C_UCASE)
{
for (i = 0; i < 256; i++)
- trans_table[i] = toupper (trans_table[i]);
+ trans_table[i] = toupper (trans_table[i]);
translation_needed = true;
}
else if (conversions_mask & C_LCASE)
{
for (i = 0; i < 256; i++)
- trans_table[i] = tolower (trans_table[i]);
+ trans_table[i] = tolower (trans_table[i]);
translation_needed = true;
}
@@ -1027,8 +1607,8 @@ translate_buffer (char *buf, size_t nread)
*cp = trans_table[to_uchar (*cp)];
}
-/* If true, the last char from the previous call to `swab_buffer'
- is saved in `saved_char'. */
+/* If true, the last char from the previous call to 'swab_buffer'
+ is saved in 'saved_char'. */
static bool char_is_saved = false;
/* Odd char from previous call. */
@@ -1094,7 +1674,7 @@ advance_input_offset (uintmax_t offset)
to indicate that lseek failed.
The offending behavior has been confirmed with an Exabyte SCSI tape
- drive accessed via /dev/nst0 on both Linux-2.2.17 and Linux-2.4.16. */
+ drive accessed via /dev/nst0 on both Linux 2.2.17 and 2.4.16 kernels. */
#ifdef __linux__
@@ -1120,9 +1700,11 @@ skip_via_lseek (char const *filename, int fdesc, off_t offset, int whence)
&& ioctl (fdesc, MTIOCGET, &s2) == 0
&& MT_SAME_POSITION (s1, s2))
{
- error (0, 0, _("warning: working around lseek kernel bug for file (%s)\n\
- of mt_type=0x%0lx -- see <sys/mtio.h> for the list of types"),
- filename, s2.mt_type);
+ if (status_level != STATUS_NONE)
+ error (0, 0, _("warning: working around lseek kernel bug for file "
+ "(%s)\n of mt_type=0x%0lx -- "
+ "see <sys/mtio.h> for the list of types"),
+ filename, s2.mt_type + 0Lu);
errno = 0;
new_position = -1;
}
@@ -1133,18 +1715,20 @@ skip_via_lseek (char const *filename, int fdesc, off_t offset, int whence)
# define skip_via_lseek(Filename, Fd, Offset, Whence) lseek (Fd, Offset, Whence)
#endif
-/* Throw away RECORDS blocks of BLOCKSIZE bytes on file descriptor FDESC,
- which is open with read permission for FILE. Store up to BLOCKSIZE
- bytes of the data at a time in BUF, if necessary. RECORDS must be
- nonzero. If fdesc is STDIN_FILENO, advance the input offset.
- Return the number of records remaining, i.e., that were not skipped
- because EOF was reached. */
+/* Throw away RECORDS blocks of BLOCKSIZE bytes plus BYTES bytes on
+ file descriptor FDESC, which is open with read permission for FILE.
+ Store up to BLOCKSIZE bytes of the data at a time in IBUF or OBUF, if
+ necessary. RECORDS or BYTES must be nonzero. If FDESC is
+ STDIN_FILENO, advance the input offset. Return the number of
+ records remaining, i.e., that were not skipped because EOF was
+ reached. If FDESC is STDOUT_FILENO, on return, BYTES is the
+ remaining bytes in addition to the remaining records. */
static uintmax_t
skip (int fdesc, char const *file, uintmax_t records, size_t blocksize,
- char *buf)
+ size_t *bytes)
{
- uintmax_t offset = records * blocksize;
+ uintmax_t offset = records * blocksize + *bytes;
/* Try lseek and if an error indicates it was an inappropriate operation --
or if the file offset is not representable as an off_t --
@@ -1155,38 +1739,103 @@ skip (int fdesc, char const *file, uintmax_t records, size_t blocksize,
&& 0 <= skip_via_lseek (file, fdesc, offset, SEEK_CUR))
{
if (fdesc == STDIN_FILENO)
- advance_input_offset (offset);
- return 0;
+ {
+ struct stat st;
+ if (fstat (STDIN_FILENO, &st) != 0)
+ error (EXIT_FAILURE, errno, _("cannot fstat %s"), quoteaf (file));
+ if (usable_st_size (&st) && st.st_size < input_offset + offset)
+ {
+ /* When skipping past EOF, return the number of _full_ blocks
+ * that are not skipped, and set offset to EOF, so the caller
+ * can determine the requested skip was not satisfied. */
+ records = ( offset - st.st_size ) / blocksize;
+ offset = st.st_size - input_offset;
+ }
+ else
+ records = 0;
+ advance_input_offset (offset);
+ }
+ else
+ {
+ records = 0;
+ *bytes = 0;
+ }
+ return records;
}
else
{
int lseek_errno = errno;
+ /* The seek request may have failed above if it was too big
+ (> device size, > max file size, etc.)
+ Or it may not have been done at all (> OFF_T_MAX).
+ Therefore try to seek to the end of the file,
+ to avoid redundant reading. */
+ if ((skip_via_lseek (file, fdesc, 0, SEEK_END)) >= 0)
+ {
+ /* File is seekable, and we're at the end of it, and
+ size <= OFF_T_MAX. So there's no point using read to advance. */
+
+ if (!lseek_errno)
+ {
+ /* The original seek was not attempted as offset > OFF_T_MAX.
+ We should error for write as can't get to the desired
+ location, even if OFF_T_MAX < max file size.
+ For read we're not going to read any data anyway,
+ so we should error for consistency.
+ It would be nice to not error for /dev/{zero,null}
+ for any offset, but that's not a significant issue. */
+ lseek_errno = EOVERFLOW;
+ }
+
+ if (fdesc == STDIN_FILENO)
+ error (0, lseek_errno, _("%s: cannot skip"), quotef (file));
+ else
+ error (0, lseek_errno, _("%s: cannot seek"), quotef (file));
+ /* If the file has a specific size and we've asked
+ to skip/seek beyond the max allowable, then quit. */
+ quit (EXIT_FAILURE);
+ }
+ /* else file_size && offset > OFF_T_MAX or file ! seekable */
+
+ char *buf;
+ if (fdesc == STDIN_FILENO)
+ {
+ alloc_ibuf ();
+ buf = ibuf;
+ }
+ else
+ {
+ alloc_obuf ();
+ buf = obuf;
+ }
+
do
- {
- ssize_t nread = iread (fdesc, buf, blocksize);
- if (nread < 0)
- {
- if (fdesc == STDIN_FILENO)
- {
- error (0, errno, _("reading %s"), quote (file));
- if (conversions_mask & C_NOERROR)
- {
- print_stats ();
- continue;
- }
- }
- else
- error (0, lseek_errno, _("%s: cannot seek"), quote (file));
- quit (EXIT_FAILURE);
- }
-
- if (nread == 0)
- break;
- if (fdesc == STDIN_FILENO)
- advance_input_offset (nread);
- }
- while (--records != 0);
+ {
+ ssize_t nread = iread_fnc (fdesc, buf, records ? blocksize : *bytes);
+ if (nread < 0)
+ {
+ if (fdesc == STDIN_FILENO)
+ {
+ error (0, errno, _("error reading %s"), quoteaf (file));
+ if (conversions_mask & C_NOERROR)
+ print_stats ();
+ }
+ else
+ error (0, lseek_errno, _("%s: cannot seek"), quotef (file));
+ quit (EXIT_FAILURE);
+ }
+ else if (nread == 0)
+ break;
+ else if (fdesc == STDIN_FILENO)
+ advance_input_offset (nread);
+
+ if (records != 0)
+ records--;
+ else
+ *bytes = 0;
+ }
+ while (records || *bytes);
return records;
}
@@ -1204,7 +1853,7 @@ advance_input_after_read_error (size_t nbytes)
if (! input_seekable)
{
if (input_seek_errno == ESPIPE)
- return true;
+ return true;
errno = input_seek_errno;
}
else
@@ -1213,28 +1862,28 @@ advance_input_after_read_error (size_t nbytes)
advance_input_offset (nbytes);
input_offset_overflow |= (OFF_T_MAX < input_offset);
if (input_offset_overflow)
- {
- error (0, 0, _("offset overflow while reading file %s"),
- quote (input_file));
- return false;
- }
+ {
+ error (0, 0, _("offset overflow while reading file %s"),
+ quoteaf (input_file));
+ return false;
+ }
offset = lseek (STDIN_FILENO, 0, SEEK_CUR);
if (0 <= offset)
- {
- off_t diff;
- if (offset == input_offset)
- return true;
- diff = input_offset - offset;
- if (! (0 <= diff && diff <= nbytes))
- error (0, 0, _("warning: invalid file offset after failed read"));
- if (0 <= skip_via_lseek (input_file, STDIN_FILENO, diff, SEEK_CUR))
- return true;
- if (errno == 0)
- error (0, 0, _("cannot work around kernel bug after all"));
- }
- }
-
- error (0, errno, _("%s: cannot seek"), quote (input_file));
+ {
+ off_t diff;
+ if (offset == input_offset)
+ return true;
+ diff = input_offset - offset;
+ if (! (0 <= diff && diff <= nbytes) && status_level != STATUS_NONE)
+ error (0, 0, _("warning: invalid file offset after failed read"));
+ if (0 <= skip_via_lseek (input_file, STDIN_FILENO, diff, SEEK_CUR))
+ return true;
+ if (errno == 0)
+ error (0, 0, _("cannot work around kernel bug after all"));
+ }
+ }
+
+ error (0, errno, _("%s: cannot seek"), quotef (input_file));
return false;
}
@@ -1255,13 +1904,13 @@ copy_simple (char const *buf, size_t nread)
start += nfree;
oc += nfree;
if (oc >= output_blocksize)
- write_output ();
+ write_output ();
}
while (nread != 0);
}
/* Copy NREAD bytes of BUF, doing conv=block
- (pad newline-terminated records to `conversion_blocksize',
+ (pad newline-terminated records to 'conversion_blocksize',
replacing the newline with trailing spaces). */
static void
@@ -1272,28 +1921,28 @@ copy_with_block (char const *buf, size_t nread)
for (i = nread; i; i--, buf++)
{
if (*buf == newline_character)
- {
- if (col < conversion_blocksize)
- {
- size_t j;
- for (j = col; j < conversion_blocksize; j++)
- output_char (space_character);
- }
- col = 0;
- }
+ {
+ if (col < conversion_blocksize)
+ {
+ size_t j;
+ for (j = col; j < conversion_blocksize; j++)
+ output_char (space_character);
+ }
+ col = 0;
+ }
else
- {
- if (col == conversion_blocksize)
- r_truncate++;
- else if (col < conversion_blocksize)
- output_char (*buf);
- col++;
- }
+ {
+ if (col == conversion_blocksize)
+ r_truncate++;
+ else if (col < conversion_blocksize)
+ output_char (*buf);
+ col++;
+ }
}
}
/* Copy NREAD bytes of BUF, doing conv=unblock
- (replace trailing spaces in `conversion_blocksize'-sized records
+ (replace trailing spaces in 'conversion_blocksize'-sized records
with a newline). */
static void
@@ -1308,24 +1957,24 @@ copy_with_unblock (char const *buf, size_t nread)
c = buf[i];
if (col++ >= conversion_blocksize)
- {
- col = pending_spaces = 0; /* Wipe out any pending spaces. */
- i--; /* Push the char back; get it later. */
- output_char (newline_character);
- }
+ {
+ col = pending_spaces = 0; /* Wipe out any pending spaces. */
+ i--; /* Push the char back; get it later. */
+ output_char (newline_character);
+ }
else if (c == space_character)
- pending_spaces++;
+ pending_spaces++;
else
- {
- /* `c' is the character after a run of spaces that were not
- at the end of the conversion buffer. Output them. */
- while (pending_spaces)
- {
- output_char (space_character);
- --pending_spaces;
- }
- output_char (c);
- }
+ {
+ /* 'c' is the character after a run of spaces that were not
+ at the end of the conversion buffer. Output them. */
+ while (pending_spaces)
+ {
+ output_char (space_character);
+ --pending_spaces;
+ }
+ output_char (c);
+ }
}
}
@@ -1344,36 +1993,36 @@ set_fd_flags (int fd, int add_flags, char const *name)
int new_flags = old_flags | add_flags;
bool ok = true;
if (old_flags < 0)
- ok = false;
+ ok = false;
else if (old_flags != new_flags)
- {
- if (new_flags & (O_DIRECTORY | O_NOLINKS))
- {
- /* NEW_FLAGS contains at least one file creation flag that
- requires some checking of the open file descriptor. */
- struct stat st;
- if (fstat (fd, &st) != 0)
- ok = false;
- else if ((new_flags & O_DIRECTORY) && ! S_ISDIR (st.st_mode))
- {
- errno = ENOTDIR;
- ok = false;
- }
- else if ((new_flags & O_NOLINKS) && 1 < st.st_nlink)
- {
- errno = EMLINK;
- ok = false;
- }
- new_flags &= ~ (O_DIRECTORY | O_NOLINKS);
- }
-
- if (ok && old_flags != new_flags
- && fcntl (fd, F_SETFL, new_flags) == -1)
- ok = false;
- }
+ {
+ if (new_flags & (O_DIRECTORY | O_NOLINKS))
+ {
+ /* NEW_FLAGS contains at least one file creation flag that
+ requires some checking of the open file descriptor. */
+ struct stat st;
+ if (fstat (fd, &st) != 0)
+ ok = false;
+ else if ((new_flags & O_DIRECTORY) && ! S_ISDIR (st.st_mode))
+ {
+ errno = ENOTDIR;
+ ok = false;
+ }
+ else if ((new_flags & O_NOLINKS) && 1 < st.st_nlink)
+ {
+ errno = EMLINK;
+ ok = false;
+ }
+ new_flags &= ~ (O_DIRECTORY | O_NOLINKS);
+ }
+
+ if (ok && old_flags != new_flags
+ && fcntl (fd, F_SETFL, new_flags) == -1)
+ ok = false;
+ }
if (!ok)
- error (EXIT_FAILURE, errno, _("setting flags for %s"), quote (name));
+ error (EXIT_FAILURE, errno, _("setting flags for %s"), quoteaf (name));
}
}
@@ -1382,11 +2031,7 @@ set_fd_flags (int fd, int add_flags, char const *name)
static int
dd_copy (void)
{
- char *ibuf, *bufstart; /* Input buffer. */
- /* These are declared static so that even though we don't free the
- buffers, valgrind will recognize that there is no "real" leak. */
- static char *real_buf; /* real buffer address before alignment */
- static char *real_obuf;
+ char *bufstart; /* Input buffer. */
ssize_t nread; /* Bytes read in the current block. */
/* If nonzero, then the previously read block was partial and
@@ -1396,7 +2041,7 @@ dd_copy (void)
int exit_status = EXIT_SUCCESS;
size_t n_bytes_read;
- /* Leave at least one extra byte at the beginning and end of `ibuf'
+ /* Leave at least one extra byte at the beginning and end of 'ibuf'
for conv=swab, but keep the buffer address even. But some peculiar
device drivers work only with word-aligned buffers, so leave an
extra two bytes. */
@@ -1408,163 +2053,194 @@ dd_copy (void)
the input buffer; thus we allocate 2 pages of slop in the
real buffer. 8k above the blocksize shouldn't bother anyone.
- The page alignment is necessary on any linux system that supports
+ The page alignment is necessary on any Linux kernel that supports
either the SGI raw I/O patch or Steven Tweedies raw I/O patch.
- It is necessary when accessing raw (i.e. character special) disk
+ It is necessary when accessing raw (i.e., character special) disk
devices on Unixware or other SVR4-derived system. */
- real_buf = xmalloc (input_blocksize + INPUT_BLOCK_SLOP);
- ibuf = real_buf;
- ibuf += SWAB_ALIGN_OFFSET; /* allow space for swab */
-
- ibuf = ptr_align (ibuf, page_size);
-
- if (conversions_mask & C_TWOBUFS)
- {
- /* Page-align the output buffer, too. */
- real_obuf = xmalloc (output_blocksize + OUTPUT_BLOCK_SLOP);
- obuf = ptr_align (real_obuf, page_size);
- }
- else
+ if (skip_records != 0 || skip_bytes != 0)
{
- real_obuf = NULL;
- obuf = ibuf;
- }
+ uintmax_t us_bytes = input_offset + (skip_records * input_blocksize)
+ + skip_bytes;
+ uintmax_t us_blocks = skip (STDIN_FILENO, input_file,
+ skip_records, input_blocksize, &skip_bytes);
+ us_bytes -= input_offset;
- if (skip_records != 0)
- {
- skip (STDIN_FILENO, input_file, skip_records, input_blocksize, ibuf);
/* POSIX doesn't say what to do when dd detects it has been
- asked to skip past EOF, so I assume it's non-fatal if the
- call to 'skip' returns nonzero. FIXME: maybe give a warning. */
+ asked to skip past EOF, so I assume it's non-fatal.
+ There are 3 reasons why there might be unskipped blocks/bytes:
+ 1. file is too small
+ 2. pipe has not enough data
+ 3. partial reads */
+ if ((us_blocks || (!input_offset_overflow && us_bytes))
+ && status_level != STATUS_NONE)
+ {
+ error (0, 0,
+ _("%s: cannot skip to specified offset"), quotef (input_file));
+ }
}
- if (seek_records != 0)
+ if (seek_records != 0 || seek_bytes != 0)
{
+ size_t bytes = seek_bytes;
uintmax_t write_records = skip (STDOUT_FILENO, output_file,
- seek_records, output_blocksize, obuf);
-
- if (write_records != 0)
- {
- memset (obuf, 0, output_blocksize);
-
- do
- if (iwrite (STDOUT_FILENO, obuf, output_blocksize)
- != output_blocksize)
- {
- error (0, errno, _("writing to %s"), quote (output_file));
- quit (EXIT_FAILURE);
- }
- while (--write_records != 0);
- }
+ seek_records, output_blocksize, &bytes);
+
+ if (write_records != 0 || bytes != 0)
+ {
+ memset (obuf, 0, write_records ? output_blocksize : bytes);
+
+ do
+ {
+ size_t size = write_records ? output_blocksize : bytes;
+ if (iwrite (STDOUT_FILENO, obuf, size) != size)
+ {
+ error (0, errno, _("writing to %s"), quoteaf (output_file));
+ quit (EXIT_FAILURE);
+ }
+
+ if (write_records != 0)
+ write_records--;
+ else
+ bytes = 0;
+ }
+ while (write_records || bytes);
+ }
}
- if (max_records == 0)
+ if (max_records == 0 && max_bytes == 0)
return exit_status;
+ alloc_ibuf ();
+ alloc_obuf ();
+
while (1)
{
- if (r_partial + r_full >= max_records)
- break;
+ if (status_level == STATUS_PROGRESS)
+ {
+ xtime_t progress_time = gethrxtime ();
+ if (next_time <= progress_time)
+ {
+ print_xfer_stats (progress_time);
+ next_time += XTIME_PRECISION;
+ }
+ }
+
+ if (r_partial + r_full >= max_records + !!max_bytes)
+ break;
/* Zero the buffer before reading, so that if we get a read error,
- whatever data we are able to read is followed by zeros.
- This minimizes data loss. */
+ whatever data we are able to read is followed by zeros.
+ This minimizes data loss. */
if ((conversions_mask & C_SYNC) && (conversions_mask & C_NOERROR))
- memset (ibuf,
- (conversions_mask & (C_BLOCK | C_UNBLOCK)) ? ' ' : '\0',
- input_blocksize);
+ memset (ibuf,
+ (conversions_mask & (C_BLOCK | C_UNBLOCK)) ? ' ' : '\0',
+ input_blocksize);
- nread = iread (STDIN_FILENO, ibuf, input_blocksize);
+ if (r_partial + r_full >= max_records)
+ nread = iread_fnc (STDIN_FILENO, ibuf, max_bytes);
+ else
+ nread = iread_fnc (STDIN_FILENO, ibuf, input_blocksize);
+
+ if (nread >= 0 && i_nocache)
+ invalidate_cache (STDIN_FILENO, nread);
if (nread == 0)
- break; /* EOF. */
+ break; /* EOF. */
if (nread < 0)
- {
- error (0, errno, _("reading %s"), quote (input_file));
- if (conversions_mask & C_NOERROR)
- {
- print_stats ();
- /* Seek past the bad block if possible. */
- if (!advance_input_after_read_error (input_blocksize - partread))
- {
- exit_status = EXIT_FAILURE;
-
- /* Suppress duplicate diagnostics. */
- input_seekable = false;
- input_seek_errno = ESPIPE;
- }
- if ((conversions_mask & C_SYNC) && !partread)
- /* Replace the missing input with null bytes and
- proceed normally. */
- nread = 0;
- else
- continue;
- }
- else
- {
- /* Write any partial block. */
- exit_status = EXIT_FAILURE;
- break;
- }
- }
+ {
+ if (!(conversions_mask & C_NOERROR) || status_level != STATUS_NONE)
+ error (0, errno, _("error reading %s"), quoteaf (input_file));
+
+ if (conversions_mask & C_NOERROR)
+ {
+ print_stats ();
+ size_t bad_portion = input_blocksize - partread;
+
+ /* We already know this data is not cached,
+ but call this so that correct offsets are maintained. */
+ invalidate_cache (STDIN_FILENO, bad_portion);
+
+ /* Seek past the bad block if possible. */
+ if (!advance_input_after_read_error (bad_portion))
+ {
+ exit_status = EXIT_FAILURE;
+
+ /* Suppress duplicate diagnostics. */
+ input_seekable = false;
+ input_seek_errno = ESPIPE;
+ }
+ if ((conversions_mask & C_SYNC) && !partread)
+ /* Replace the missing input with null bytes and
+ proceed normally. */
+ nread = 0;
+ else
+ continue;
+ }
+ else
+ {
+ /* Write any partial block. */
+ exit_status = EXIT_FAILURE;
+ break;
+ }
+ }
n_bytes_read = nread;
advance_input_offset (nread);
if (n_bytes_read < input_blocksize)
- {
- r_partial++;
- partread = n_bytes_read;
- if (conversions_mask & C_SYNC)
- {
- if (!(conversions_mask & C_NOERROR))
- /* If C_NOERROR, we zeroed the block before reading. */
- memset (ibuf + n_bytes_read,
- (conversions_mask & (C_BLOCK | C_UNBLOCK)) ? ' ' : '\0',
- input_blocksize - n_bytes_read);
- n_bytes_read = input_blocksize;
- }
- }
+ {
+ r_partial++;
+ partread = n_bytes_read;
+ if (conversions_mask & C_SYNC)
+ {
+ if (!(conversions_mask & C_NOERROR))
+ /* If C_NOERROR, we zeroed the block before reading. */
+ memset (ibuf + n_bytes_read,
+ (conversions_mask & (C_BLOCK | C_UNBLOCK)) ? ' ' : '\0',
+ input_blocksize - n_bytes_read);
+ n_bytes_read = input_blocksize;
+ }
+ }
else
- {
- r_full++;
- partread = 0;
- }
+ {
+ r_full++;
+ partread = 0;
+ }
if (ibuf == obuf) /* If not C_TWOBUFS. */
- {
- size_t nwritten = iwrite (STDOUT_FILENO, obuf, n_bytes_read);
- w_bytes += nwritten;
- if (nwritten != n_bytes_read)
- {
- error (0, errno, _("writing %s"), quote (output_file));
- return EXIT_FAILURE;
- }
- else if (n_bytes_read == input_blocksize)
- w_full++;
- else
- w_partial++;
- continue;
- }
+ {
+ size_t nwritten = iwrite (STDOUT_FILENO, obuf, n_bytes_read);
+ w_bytes += nwritten;
+ if (nwritten != n_bytes_read)
+ {
+ error (0, errno, _("error writing %s"), quoteaf (output_file));
+ return EXIT_FAILURE;
+ }
+ else if (n_bytes_read == input_blocksize)
+ w_full++;
+ else
+ w_partial++;
+ continue;
+ }
/* Do any translations on the whole buffer at once. */
if (translation_needed)
- translate_buffer (ibuf, n_bytes_read);
+ translate_buffer (ibuf, n_bytes_read);
if (conversions_mask & C_SWAB)
- bufstart = swab_buffer (ibuf, &n_bytes_read);
+ bufstart = swab_buffer (ibuf, &n_bytes_read);
else
- bufstart = ibuf;
+ bufstart = ibuf;
if (conversions_mask & C_BLOCK)
copy_with_block (bufstart, n_bytes_read);
else if (conversions_mask & C_UNBLOCK)
- copy_with_unblock (bufstart, n_bytes_read);
+ copy_with_unblock (bufstart, n_bytes_read);
else
- copy_simple (bufstart, n_bytes_read);
+ copy_simple (bufstart, n_bytes_read);
}
/* If we have a char left as a result of conv=swab, output it. */
@@ -1573,24 +2249,25 @@ dd_copy (void)
if (conversions_mask & C_BLOCK)
copy_with_block (&saved_char, 1);
else if (conversions_mask & C_UNBLOCK)
- copy_with_unblock (&saved_char, 1);
+ copy_with_unblock (&saved_char, 1);
else
- output_char (saved_char);
+ output_char (saved_char);
}
if ((conversions_mask & C_BLOCK) && col > 0)
{
/* If the final input line didn't end with a '\n', pad
- the output block to `conversion_blocksize' chars. */
+ the output block to 'conversion_blocksize' chars. */
size_t i;
for (i = col; i < conversion_blocksize; i++)
- output_char (space_character);
+ output_char (space_character);
}
- if ((conversions_mask & C_UNBLOCK) && col == conversion_blocksize)
- /* Add a final '\n' if there are exactly `conversion_blocksize'
- characters in the final record. */
- output_char (newline_character);
+ if (col && (conversions_mask & C_UNBLOCK))
+ {
+ /* If there was any output, add a final '\n'. */
+ output_char (newline_character);
+ }
/* Write out the last block. */
if (oc != 0)
@@ -1598,31 +2275,58 @@ dd_copy (void)
size_t nwritten = iwrite (STDOUT_FILENO, obuf, oc);
w_bytes += nwritten;
if (nwritten != 0)
- w_partial++;
+ w_partial++;
if (nwritten != oc)
- {
- error (0, errno, _("writing %s"), quote (output_file));
- return EXIT_FAILURE;
- }
+ {
+ error (0, errno, _("error writing %s"), quoteaf (output_file));
+ return EXIT_FAILURE;
+ }
+ }
+
+ /* If the last write was converted to a seek, then for a regular file
+ or shared memory object, ftruncate to extend the size. */
+ if (final_op_was_seek)
+ {
+ struct stat stdout_stat;
+ if (fstat (STDOUT_FILENO, &stdout_stat) != 0)
+ {
+ error (0, errno, _("cannot fstat %s"), quoteaf (output_file));
+ return EXIT_FAILURE;
+ }
+ if (S_ISREG (stdout_stat.st_mode) || S_TYPEISSHM (&stdout_stat))
+ {
+ off_t output_offset = lseek (STDOUT_FILENO, 0, SEEK_CUR);
+ if (0 <= output_offset && stdout_stat.st_size < output_offset)
+ {
+ if (iftruncate (STDOUT_FILENO, output_offset) != 0)
+ {
+ error (0, errno,
+ _("failed to truncate to %" PRIdMAX " bytes"
+ " in output file %s"),
+ (intmax_t) output_offset, quoteaf (output_file));
+ return EXIT_FAILURE;
+ }
+ }
+ }
}
if ((conversions_mask & C_FDATASYNC) && fdatasync (STDOUT_FILENO) != 0)
{
if (errno != ENOSYS && errno != EINVAL)
- {
- error (0, errno, _("fdatasync failed for %s"), quote (output_file));
- exit_status = EXIT_FAILURE;
- }
+ {
+ error (0, errno, _("fdatasync failed for %s"), quoteaf (output_file));
+ exit_status = EXIT_FAILURE;
+ }
conversions_mask |= C_FSYNC;
}
if (conversions_mask & C_FSYNC)
while (fsync (STDOUT_FILENO) != 0)
if (errno != EINTR)
- {
- error (0, errno, _("fsync failed for %s"), quote (output_file));
- return EXIT_FAILURE;
- }
+ {
+ error (0, errno, _("fsync failed for %s"), quoteaf (output_file));
+ return EXIT_FAILURE;
+ }
return exit_status;
}
@@ -1634,19 +2338,23 @@ main (int argc, char **argv)
int exit_status;
off_t offset;
+ install_signal_handlers ();
+
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
/* Arrange to close stdout if parse_long_options exits. */
- atexit (close_stdout);
+ atexit (maybe_close_stdout);
page_size = getpagesize ();
- parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
+ parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE, Version,
+ usage, AUTHORS, (char const *) NULL);
+ close_stdout_required = false;
+
if (getopt_long (argc, argv, "", NULL, NULL) != -1)
usage (EXIT_FAILURE);
@@ -1666,13 +2374,14 @@ main (int argc, char **argv)
}
else
{
- if (fd_reopen (STDIN_FILENO, input_file, O_RDONLY | input_flags, 0) < 0)
- error (EXIT_FAILURE, errno, _("opening %s"), quote (input_file));
+ if (ifd_reopen (STDIN_FILENO, input_file, O_RDONLY | input_flags, 0) < 0)
+ error (EXIT_FAILURE, errno, _("failed to open %s"),
+ quoteaf (input_file));
}
offset = lseek (STDIN_FILENO, 0, SEEK_CUR);
input_seekable = (0 <= offset);
- input_offset = offset;
+ input_offset = MAX (0, offset);
input_seek_errno = errno;
if (output_file == NULL)
@@ -1682,63 +2391,89 @@ main (int argc, char **argv)
}
else
{
- mode_t perms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ mode_t perms = MODE_RW_UGO;
int opts
- = (output_flags
- | (conversions_mask & C_NOCREAT ? 0 : O_CREAT)
- | (conversions_mask & C_EXCL ? O_EXCL : 0)
- | (seek_records || (conversions_mask & C_NOTRUNC) ? 0 : O_TRUNC));
+ = (output_flags
+ | (conversions_mask & C_NOCREAT ? 0 : O_CREAT)
+ | (conversions_mask & C_EXCL ? O_EXCL : 0)
+ | (seek_records || (conversions_mask & C_NOTRUNC) ? 0 : O_TRUNC));
/* Open the output file with *read* access only if we might
- need to read to satisfy a `seek=' request. If we can't read
- the file, go ahead with write-only access; it might work. */
+ need to read to satisfy a 'seek=' request. If we can't read
+ the file, go ahead with write-only access; it might work. */
if ((! seek_records
- || fd_reopen (STDOUT_FILENO, output_file, O_RDWR | opts, perms) < 0)
- && (fd_reopen (STDOUT_FILENO, output_file, O_WRONLY | opts, perms)
- < 0))
- error (EXIT_FAILURE, errno, _("opening %s"), quote (output_file));
+ || ifd_reopen (STDOUT_FILENO, output_file, O_RDWR | opts, perms) < 0)
+ && (ifd_reopen (STDOUT_FILENO, output_file, O_WRONLY | opts, perms)
+ < 0))
+ error (EXIT_FAILURE, errno, _("failed to open %s"),
+ quoteaf (output_file));
-#if HAVE_FTRUNCATE
if (seek_records != 0 && !(conversions_mask & C_NOTRUNC))
- {
- uintmax_t size = seek_records * output_blocksize;
- unsigned long int obs = output_blocksize;
-
- if (OFF_T_MAX / output_blocksize < seek_records)
- error (EXIT_FAILURE, 0,
- _("offset too large: "
- "cannot truncate to a length of seek=%"PRIuMAX""
- " (%lu-byte) blocks"),
- seek_records, obs);
-
- if (ftruncate (STDOUT_FILENO, size) != 0)
- {
- /* Complain only when ftruncate fails on a regular file, a
- directory, or a shared memory object, as POSIX 1003.1-2004
- specifies ftruncate's behavior only for these file types.
- For example, do not complain when Linux 2.4 ftruncate
- fails on /dev/fd0. */
- int ftruncate_errno = errno;
- struct stat stdout_stat;
- if (fstat (STDOUT_FILENO, &stdout_stat) != 0)
- error (EXIT_FAILURE, errno, _("cannot fstat %s"),
- quote (output_file));
- if (S_ISREG (stdout_stat.st_mode)
- || S_ISDIR (stdout_stat.st_mode)
- || S_TYPEISSHM (&stdout_stat))
- error (EXIT_FAILURE, ftruncate_errno,
- _("truncating at %"PRIuMAX" bytes in output file %s"),
- size, quote (output_file));
- }
- }
-#endif
+ {
+ uintmax_t size = seek_records * output_blocksize + seek_bytes;
+ unsigned long int obs = output_blocksize;
+
+ if (OFF_T_MAX / output_blocksize < seek_records)
+ error (EXIT_FAILURE, 0,
+ _("offset too large: "
+ "cannot truncate to a length of seek=%"PRIuMAX""
+ " (%lu-byte) blocks"),
+ seek_records, obs);
+
+ if (iftruncate (STDOUT_FILENO, size) != 0)
+ {
+ /* Complain only when ftruncate fails on a regular file, a
+ directory, or a shared memory object, as POSIX 1003.1-2004
+ specifies ftruncate's behavior only for these file types.
+ For example, do not complain when Linux kernel 2.4 ftruncate
+ fails on /dev/fd0. */
+ int ftruncate_errno = errno;
+ struct stat stdout_stat;
+ if (fstat (STDOUT_FILENO, &stdout_stat) != 0)
+ error (EXIT_FAILURE, errno, _("cannot fstat %s"),
+ quoteaf (output_file));
+ if (S_ISREG (stdout_stat.st_mode)
+ || S_ISDIR (stdout_stat.st_mode)
+ || S_TYPEISSHM (&stdout_stat))
+ error (EXIT_FAILURE, ftruncate_errno,
+ _("failed to truncate to %"PRIuMAX" bytes"
+ " in output file %s"),
+ size, quoteaf (output_file));
+ }
+ }
}
- install_signal_handlers ();
-
start_time = gethrxtime ();
+ next_time = start_time + XTIME_PRECISION;
exit_status = dd_copy ();
- quit (exit_status);
+ if (max_records == 0 && max_bytes == 0)
+ {
+ /* Special case to invalidate cache to end of file. */
+ if (i_nocache && !invalidate_cache (STDIN_FILENO, 0))
+ {
+ error (0, errno, _("failed to discard cache for: %s"),
+ quotef (input_file));
+ exit_status = EXIT_FAILURE;
+ }
+ if (o_nocache && !invalidate_cache (STDOUT_FILENO, 0))
+ {
+ error (0, errno, _("failed to discard cache for: %s"),
+ quotef (output_file));
+ exit_status = EXIT_FAILURE;
+ }
+ }
+ else if (max_records != (uintmax_t) -1)
+ {
+ /* Invalidate any pending region less than page size,
+ in case the kernel might round up. */
+ if (i_nocache)
+ invalidate_cache (STDIN_FILENO, 0);
+ if (o_nocache)
+ invalidate_cache (STDOUT_FILENO, 0);
+ }
+
+ finish_up ();
+ return exit_status;
}
diff --git a/src/df.c b/src/df.c
index 609787e..cbd8ef5 100644
--- a/src/df.c
+++ b/src/df.c
@@ -1,10 +1,10 @@
/* df - summarize free disk space
- Copyright (C) 91, 1995-2007 Free Software Foundation, Inc.
+ Copyright (C) 1991-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,43 +12,48 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by David MacKenzie <djm@gnu.ai.mit.edu>.
- --human-readable and --megabyte options added by lm@sgi.com.
+ --human-readable option added by lm@sgi.com.
--si and large file support added by eggert@twinsun.com. */
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>
+#include <assert.h>
#include "system.h"
#include "canonicalize.h"
#include "error.h"
#include "fsusage.h"
#include "human.h"
-#include "inttostr.h"
+#include "mbsalign.h"
+#include "mbswidth.h"
#include "mountlist.h"
#include "quote.h"
-#include "save-cwd.h"
-#include "xgetcwd.h"
+#include "find-mount-point.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "df"
#define AUTHORS \
- "Torbjorn Granlund", "David MacKenzie", "Paul Eggert"
+ proper_name_utf8 ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Paul Eggert")
-/* Name this program was run with. */
-char *program_name;
-
-/* If true, show inode information. */
-static bool inode_format;
+/* Filled with device numbers of examined file systems to avoid
+ duplicates in output. */
+static struct devlist
+{
+ dev_t dev_num;
+ struct mount_entry *me;
+ struct devlist *next;
+} *device_list;
/* If true, show even file systems with zero size or
- uninteresting types. */
+ uninteresting types. */
static bool show_all_fs;
/* If true, show only local file systems. */
@@ -64,22 +69,19 @@ static int human_output_opts;
/* The units to use when printing sizes. */
static uintmax_t output_block_size;
-/* If true, use the POSIX output format. */
-static bool posix_format;
-
/* True if a file system has been processed for output. */
static bool file_systems_processed;
-/* If true, invoke the `sync' system call before getting any usage data.
+/* If true, invoke the 'sync' system call before getting any usage data.
Using this option can make df very slow, especially with many or very
busy disks. Note that this may make a difference on some systems --
- SunOS 4.1.3, for one. It is *not* necessary on Linux. */
+ SunOS 4.1.3, for one. It is *not* necessary on GNU/Linux. */
static bool require_sync;
/* Desired exit status. */
static int exit_status;
-/* A file system type to display. */
+/* A file system type to display. */
struct fs_type_list
{
@@ -88,7 +90,7 @@ struct fs_type_list
};
/* Linked list of file system types to display.
- If `fs_select_list' is NULL, list all types.
+ If 'fs_select_list' is NULL, list all types.
This table is generated dynamically from command-line options,
rather than hardcoding into the program what it thinks are the
valid file system types; let the user specify any file system type
@@ -105,20 +107,143 @@ static struct fs_type_list *fs_select_list;
static struct fs_type_list *fs_exclude_list;
-/* Linked list of mounted file systems. */
+/* Linked list of mounted file systems. */
static struct mount_entry *mount_list;
/* If true, print file system type as well. */
static bool print_type;
+/* If true, print a grand total at the end. */
+static bool print_grand_total;
+
+/* Grand total data. */
+static struct fs_usage grand_fsu;
+
+/* Display modes. */
+enum
+{
+ DEFAULT_MODE,
+ INODES_MODE,
+ HUMAN_MODE,
+ POSIX_MODE,
+ OUTPUT_MODE
+};
+static int header_mode = DEFAULT_MODE;
+
+/* Displayable fields. */
+typedef enum
+{
+ SOURCE_FIELD, /* file system */
+ FSTYPE_FIELD, /* FS type */
+ SIZE_FIELD, /* FS size */
+ USED_FIELD, /* FS size used */
+ AVAIL_FIELD, /* FS size available */
+ PCENT_FIELD, /* percent used */
+ ITOTAL_FIELD, /* inode total */
+ IUSED_FIELD, /* inodes used */
+ IAVAIL_FIELD, /* inodes available */
+ IPCENT_FIELD, /* inodes used in percent */
+ TARGET_FIELD, /* mount point */
+ FILE_FIELD, /* specified file name */
+ INVALID_FIELD /* validation marker */
+} display_field_t;
+
+/* Flag if a field contains a block, an inode or another value. */
+typedef enum
+{
+ BLOCK_FLD, /* Block values field */
+ INODE_FLD, /* Inode values field */
+ OTHER_FLD /* Neutral field, e.g. target */
+} field_type_t;
+
+/* Attributes of a display field. */
+struct field_data_t
+{
+ display_field_t field;
+ char const *arg;
+ field_type_t field_type;
+ const char *caption;/* NULL means to use the default header of this field. */
+ size_t width; /* Auto adjusted (up) widths used to align columns. */
+ mbs_align_t align; /* Alignment for this field. */
+ bool used;
+};
+
+/* Header strings, minimum width and alignment for the above fields. */
+static struct field_data_t field_data[] = {
+ [SOURCE_FIELD] = { SOURCE_FIELD,
+ "source", OTHER_FLD, N_("Filesystem"), 14, MBS_ALIGN_LEFT, false },
+
+ [FSTYPE_FIELD] = { FSTYPE_FIELD,
+ "fstype", OTHER_FLD, N_("Type"), 4, MBS_ALIGN_LEFT, false },
+
+ [SIZE_FIELD] = { SIZE_FIELD,
+ "size", BLOCK_FLD, N_("blocks"), 5, MBS_ALIGN_RIGHT, false },
+
+ [USED_FIELD] = { USED_FIELD,
+ "used", BLOCK_FLD, N_("Used"), 5, MBS_ALIGN_RIGHT, false },
+
+ [AVAIL_FIELD] = { AVAIL_FIELD,
+ "avail", BLOCK_FLD, N_("Available"), 5, MBS_ALIGN_RIGHT, false },
+
+ [PCENT_FIELD] = { PCENT_FIELD,
+ "pcent", BLOCK_FLD, N_("Use%"), 4, MBS_ALIGN_RIGHT, false },
+
+ [ITOTAL_FIELD] = { ITOTAL_FIELD,
+ "itotal", INODE_FLD, N_("Inodes"), 5, MBS_ALIGN_RIGHT, false },
+
+ [IUSED_FIELD] = { IUSED_FIELD,
+ "iused", INODE_FLD, N_("IUsed"), 5, MBS_ALIGN_RIGHT, false },
+
+ [IAVAIL_FIELD] = { IAVAIL_FIELD,
+ "iavail", INODE_FLD, N_("IFree"), 5, MBS_ALIGN_RIGHT, false },
+
+ [IPCENT_FIELD] = { IPCENT_FIELD,
+ "ipcent", INODE_FLD, N_("IUse%"), 4, MBS_ALIGN_RIGHT, false },
+
+ [TARGET_FIELD] = { TARGET_FIELD,
+ "target", OTHER_FLD, N_("Mounted on"), 0, MBS_ALIGN_LEFT, false },
+
+ [FILE_FIELD] = { FILE_FIELD,
+ "file", OTHER_FLD, N_("File"), 0, MBS_ALIGN_LEFT, false }
+};
+
+static char const *all_args_string =
+ "source,fstype,itotal,iused,iavail,ipcent,size,"
+ "used,avail,pcent,file,target";
+
+/* Storage for the definition of output columns. */
+static struct field_data_t **columns;
+
+/* The current number of output columns. */
+static size_t ncolumns;
+
+/* Field values. */
+struct field_values_t
+{
+ uintmax_t input_units;
+ uintmax_t output_units;
+ uintmax_t total;
+ uintmax_t available;
+ bool negate_available;
+ uintmax_t available_to_root;
+ uintmax_t used;
+ bool negate_used;
+};
+
+/* Storage for pointers for each string (cell of table). */
+static char ***table;
+
+/* The current number of processed rows (including header). */
+static size_t nrows;
+
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
{
NO_SYNC_OPTION = CHAR_MAX + 1,
- /* FIXME: --kilobytes is deprecated (but not -k); remove in late 2006 */
- KILOBYTES_LONG_OPTION,
- SYNC_OPTION
+ SYNC_OPTION,
+ TOTAL_OPTION,
+ OUTPUT_OPTION
};
static struct option const long_options[] =
@@ -128,13 +253,13 @@ static struct option const long_options[] =
{"inodes", no_argument, NULL, 'i'},
{"human-readable", no_argument, NULL, 'h'},
{"si", no_argument, NULL, 'H'},
- {"kilobytes", no_argument, NULL, KILOBYTES_LONG_OPTION},
{"local", no_argument, NULL, 'l'},
- {"megabytes", no_argument, NULL, 'm'}, /* obsolescent */
+ {"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},
@@ -142,67 +267,315 @@ static struct option const long_options[] =
{NULL, 0, NULL, 0}
};
+/* Replace problematic chars with '?'.
+ Since only control characters are currently considered,
+ this should work in all encodings. */
+
+static char*
+hide_problematic_chars (char *cell)
+{
+ char *p = cell;
+ while (*p)
+ {
+ if (iscntrl (to_uchar (*p)))
+ *p = '?';
+ p++;
+ }
+ return cell;
+}
+
+/* Dynamically allocate a row of pointers in TABLE, which
+ can then be accessed with standard 2D array notation. */
+
static void
-print_header (void)
+alloc_table_row (void)
{
- char buf[MAX (LONGEST_HUMAN_READABLE + 1, INT_BUFSIZE_BOUND (uintmax_t))];
+ nrows++;
+ table = xnrealloc (table, nrows, sizeof (char **));
+ table[nrows - 1] = xnmalloc (ncolumns, sizeof (char *));
+}
- if (print_type)
- fputs (_("Filesystem Type"), stdout);
- else
- fputs (_("Filesystem "), stdout);
+/* Output each cell in the table, accounting for the
+ alignment and max width of each column. */
- if (inode_format)
- printf (_(" Inodes IUsed IFree IUse%%"));
- else if (human_output_opts & human_autoscale)
+static void
+print_table (void)
+{
+ size_t row;
+
+ for (row = 0; row < nrows; row++)
{
- if (human_output_opts & human_base_1024)
- printf (_(" Size Used Avail Use%%"));
- else
- printf (_(" Size Used Avail Use%%"));
+ size_t col;
+ for (col = 0; col < ncolumns; col++)
+ {
+ char *cell = table[row][col];
+
+ /* Note the SOURCE_FIELD used to be displayed on it's own line
+ if (!posix_format && mbswidth (cell) > 20), but that
+ functionality was probably more problematic than helpful,
+ hence changed in commit v8.10-40-g99679ff. */
+ if (col != 0)
+ putchar (' ');
+
+ int flags = 0;
+ if (col == ncolumns - 1) /* The last one. */
+ flags = MBA_NO_RIGHT_PAD;
+
+ size_t width = columns[col]->width;
+ cell = ambsalign (cell, &width, columns[col]->align, flags);
+ /* When ambsalign fails, output unaligned data. */
+ fputs (cell ? cell : table[row][col], stdout);
+ free (cell);
+
+ IF_LINT (free (table[row][col]));
+ }
+ putchar ('\n');
+ IF_LINT (free (table[row]));
}
- else if (posix_format)
- printf (_(" %s-blocks Used Available Capacity"),
- umaxtostr (output_block_size, buf));
- else
+
+ IF_LINT (free (table));
+}
+
+/* Dynamically allocate a struct field_t in COLUMNS, which
+ can then be accessed with standard array notation. */
+
+static void
+alloc_field (int f, const char *c)
+{
+ ncolumns++;
+ columns = xnrealloc (columns, ncolumns, sizeof (struct field_data_t *));
+ columns[ncolumns - 1] = &field_data[f];
+ if (c != NULL)
+ columns[ncolumns - 1]->caption = c;
+
+ if (field_data[f].used)
+ assert (!"field used");
+
+ /* Mark field as used. */
+ field_data[f].used = true;
+}
+
+
+/* Given a string, ARG, containing a comma-separated list of arguments
+ to the --output option, add the appropriate fields to columns. */
+static void
+decode_output_arg (char const *arg)
+{
+ char *arg_writable = xstrdup (arg);
+ char *s = arg_writable;
+ do
{
- int opts = (human_suppress_point_zero
- | human_autoscale | human_SI
- | (human_output_opts
- & (human_group_digits | human_base_1024 | human_B)));
-
- /* Prefer the base that makes the human-readable value more exact,
- if there is a difference. */
-
- uintmax_t q1000 = output_block_size;
- uintmax_t q1024 = output_block_size;
- bool divisible_by_1000;
- bool divisible_by_1024;
-
- do
- {
- divisible_by_1000 = q1000 % 1000 == 0; q1000 /= 1000;
- divisible_by_1024 = q1024 % 1024 == 0; q1024 /= 1024;
- }
- while (divisible_by_1000 & divisible_by_1024);
-
- if (divisible_by_1000 < divisible_by_1024)
- opts |= human_base_1024;
- if (divisible_by_1024 < divisible_by_1000)
- opts &= ~human_base_1024;
- if (! (opts & human_base_1024))
- opts |= human_B;
-
- printf (_(" %4s-blocks Used Available Use%%"),
- human_readable (output_block_size, buf, opts, 1, 1));
+ /* find next comma */
+ char *comma = strchr (s, ',');
+
+ /* If we found a comma, put a NUL in its place and advance. */
+ if (comma)
+ *comma++ = 0;
+
+ /* process S. */
+ display_field_t field = INVALID_FIELD;
+ for (unsigned int i = 0; i < ARRAY_CARDINALITY (field_data); i++)
+ {
+ if (STREQ (field_data[i].arg, s))
+ {
+ field = i;
+ break;
+ }
+ }
+ if (field == INVALID_FIELD)
+ {
+ error (0, 0, _("option --output: field %s unknown"), quote (s));
+ usage (EXIT_FAILURE);
+ }
+
+ if (field_data[field].used)
+ {
+ /* Prevent the fields from being used more than once. */
+ error (0, 0, _("option --output: field %s used more than once"),
+ quote (field_data[field].arg));
+ usage (EXIT_FAILURE);
+ }
+
+ switch (field)
+ {
+ case SOURCE_FIELD:
+ case FSTYPE_FIELD:
+ case USED_FIELD:
+ case PCENT_FIELD:
+ case ITOTAL_FIELD:
+ case IUSED_FIELD:
+ case IAVAIL_FIELD:
+ case IPCENT_FIELD:
+ case TARGET_FIELD:
+ case FILE_FIELD:
+ alloc_field (field, NULL);
+ break;
+
+ case SIZE_FIELD:
+ alloc_field (field, N_("Size"));
+ break;
+
+ case AVAIL_FIELD:
+ alloc_field (field, N_("Avail"));
+ break;
+
+ default:
+ assert (!"invalid field");
+ }
+ s = comma;
}
+ while (s);
- printf (_(" Mounted on\n"));
+ free (arg_writable);
+}
+
+/* Get the appropriate columns for the mode. */
+static void
+get_field_list (void)
+{
+ switch (header_mode)
+ {
+ case DEFAULT_MODE:
+ alloc_field (SOURCE_FIELD, NULL);
+ if (print_type)
+ 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, NULL);
+ if (print_type)
+ alloc_field (FSTYPE_FIELD, NULL);
+
+ alloc_field (SIZE_FIELD, N_("Size"));
+ alloc_field (USED_FIELD, NULL);
+ alloc_field (AVAIL_FIELD, N_("Avail"));
+ alloc_field (PCENT_FIELD, NULL);
+ alloc_field (TARGET_FIELD, NULL);
+ break;
+
+ case INODES_MODE:
+ alloc_field (SOURCE_FIELD, NULL);
+ if (print_type)
+ 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, NULL);
+ if (print_type)
+ 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, NULL);
+ break;
+
+ case OUTPUT_MODE:
+ if (!ncolumns)
+ {
+ /* Add all fields if --output was given without a field list. */
+ decode_output_arg (all_args_string);
+ }
+ break;
+
+ default:
+ assert (!"invalid header_mode");
+ }
+}
+
+/* Obtain the appropriate header entries. */
+
+static void
+get_header (void)
+{
+ size_t col;
+
+ alloc_table_row ();
+
+ for (col = 0; col < ncolumns; col++)
+ {
+ char *cell = NULL;
+ char const *header = _(columns[col]->caption);
+
+ if (columns[col]->field == SIZE_FIELD
+ && (header_mode == DEFAULT_MODE
+ || (header_mode == OUTPUT_MODE
+ && !(human_output_opts & human_autoscale))))
+ {
+ char buf[LONGEST_HUMAN_READABLE + 1];
+
+ int opts = (human_suppress_point_zero
+ | human_autoscale | human_SI
+ | (human_output_opts
+ & (human_group_digits | human_base_1024 | human_B)));
+
+ /* Prefer the base that makes the human-readable value more exact,
+ if there is a difference. */
+
+ uintmax_t q1000 = output_block_size;
+ uintmax_t q1024 = output_block_size;
+ bool divisible_by_1000;
+ bool divisible_by_1024;
+
+ do
+ {
+ divisible_by_1000 = q1000 % 1000 == 0; q1000 /= 1000;
+ divisible_by_1024 = q1024 % 1024 == 0; q1024 /= 1024;
+ }
+ while (divisible_by_1000 & divisible_by_1024);
+
+ if (divisible_by_1000 < divisible_by_1024)
+ opts |= human_base_1024;
+ if (divisible_by_1024 < divisible_by_1000)
+ opts &= ~human_base_1024;
+ if (! (opts & human_base_1024))
+ opts |= human_B;
+
+ char *num = human_readable (output_block_size, buf, opts, 1, 1);
+
+ /* Reset the header back to the default in OUTPUT_MODE. */
+ header = _("blocks");
+
+ /* TRANSLATORS: this is the "1K-blocks" header in "df" output. */
+ if (asprintf (&cell, _("%s-%s"), num, header) == -1)
+ cell = NULL;
+ }
+ else if (header_mode == POSIX_MODE && columns[col]->field == SIZE_FIELD)
+ {
+ char buf[INT_BUFSIZE_BOUND (uintmax_t)];
+ char *num = umaxtostr (output_block_size, buf);
+
+ /* TRANSLATORS: this is the "1024-blocks" header in "df -P". */
+ if (asprintf (&cell, _("%s-%s"), num, header) == -1)
+ cell = NULL;
+ }
+ else
+ cell = strdup (header);
+
+ if (!cell)
+ xalloc_die ();
+
+ hide_problematic_chars (cell);
+
+ table[nrows - 1][col] = cell;
+
+ columns[col]->width = MAX (columns[col]->width, mbswidth (cell, 0));
+ }
}
/* Is FSTYPE a type of file system that should be listed? */
-static bool
+static bool _GL_ATTRIBUTE_PURE
selected_fstype (const char *fstype)
{
const struct fs_type_list *fsp;
@@ -217,7 +590,7 @@ selected_fstype (const char *fstype)
/* Is FSTYPE a type of file system that should be omitted? */
-static bool
+static bool _GL_ATTRIBUTE_PURE
excluded_fstype (const char *fstype)
{
const struct fs_type_list *fsp;
@@ -230,30 +603,279 @@ excluded_fstype (const char *fstype)
return false;
}
+/* Filter mount list by skipping duplicate entries.
+ 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
+ the global device_list, rather than filtering the global mount_list. */
+
+static void
+filter_mount_list (bool devices_only)
+{
+ struct mount_entry *me;
+
+ /* Sort all 'wanted' entries into the list device_list. */
+ for (me = mount_list; me;)
+ {
+ struct stat buf;
+ struct devlist *devlist;
+ struct mount_entry *discard_me = NULL;
+
+ /* Avoid stating remote file systems as that may hang.
+ On Linux we probably have me_dev populated from /proc/self/mountinfo,
+ however we still stat() in case another device was mounted later. */
+ if ((me->me_remote && show_local_fs)
+ || -1 == stat (me->me_mountdir, &buf))
+ {
+ /* If remote, and showing just local, add ME for filtering later.
+ If stat failed; add ME to be able to complain about it later. */
+ buf.st_dev = me->me_dev;
+ }
+ else
+ {
+ /* If we've already seen this device... */
+ for (devlist = device_list; devlist; devlist = devlist->next)
+ if (devlist->dev_num == buf.st_dev)
+ break;
+
+ if (devlist)
+ {
+ bool target_nearer_root = strlen (devlist->me->me_mountdir)
+ > strlen (me->me_mountdir);
+ /* With bind mounts, prefer items nearer the root of the source */
+ bool source_below_root = devlist->me->me_mntroot != NULL
+ && me->me_mntroot != NULL
+ && (strlen (devlist->me->me_mntroot)
+ < strlen (me->me_mntroot));
+ if (! print_grand_total && me->me_remote && devlist->me->me_remote
+ && ! STREQ (devlist->me->me_devname, me->me_devname))
+ {
+ /* Don't discard remote entries with different locations,
+ as these are more likely to be explicitly mounted.
+ However avoid this when producing a total to give
+ a more accurate value in that case. */
+ }
+ else if ((strchr (me->me_devname, '/')
+ /* let "real" devices with '/' in the name win. */
+ && ! strchr (devlist->me->me_devname, '/'))
+ /* let points towards the root of the device win. */
+ || (target_nearer_root && ! source_below_root)
+ /* let an entry overmounted on a new device win... */
+ || (! STREQ (devlist->me->me_devname, me->me_devname)
+ /* ... but only when matching an existing mnt point,
+ to avoid problematic replacement when given
+ inaccurate mount lists, seen with some chroot
+ environments for example. */
+ && STREQ (me->me_mountdir,
+ devlist->me->me_mountdir)))
+ {
+ /* Discard mount entry for existing device. */
+ discard_me = devlist->me;
+ devlist->me = me;
+ }
+ else
+ {
+ /* Discard mount entry currently being processed. */
+ discard_me = me;
+ }
+
+ }
+ }
+
+ if (discard_me)
+ {
+ me = me->me_next;
+ if (! devices_only)
+ free_mount_entry (discard_me);
+ }
+ else
+ {
+ /* Add the device number to the global list devlist. */
+ devlist = xmalloc (sizeof *devlist);
+ devlist->me = me;
+ devlist->dev_num = buf.st_dev;
+ devlist->next = device_list;
+ device_list = devlist;
+
+ me = me->me_next;
+ }
+ }
+
+ /* Finally rebuild the mount_list from the devlist. */
+ if (! devices_only) {
+ mount_list = NULL;
+ while (device_list)
+ {
+ /* Add the mount entry. */
+ me = device_list->me;
+ me->me_next = mount_list;
+ mount_list = me;
+ /* Free devlist entry and advance. */
+ struct devlist *devlist = device_list->next;
+ free (device_list);
+ device_list = devlist;
+ }
+ }
+}
+
+/* Search a mount entry list for device id DEV.
+ Return the corresponding mount entry if found or NULL if not. */
+
+static struct mount_entry const * _GL_ATTRIBUTE_PURE
+me_for_dev (dev_t dev)
+{
+ struct devlist *dl = device_list;
+
+ while (dl)
+ {
+ if (dl->dev_num == dev)
+ return dl->me;
+ dl = dl->next;
+ }
+
+ return NULL;
+}
+
+/* Return true if N is a known integer value. On many file systems,
+ UINTMAX_MAX represents an unknown value; on AIX, UINTMAX_MAX - 1
+ represents unknown. Use a rule that works on AIX file systems, and
+ that almost-always works on other types. */
+static bool
+known_value (uintmax_t n)
+{
+ return n < UINTMAX_MAX - 1;
+}
+
/* Like human_readable (N, BUF, human_output_opts, INPUT_UNITS, OUTPUT_UNITS),
except:
- If NEGATIVE, then N represents a negative number,
expressed in two's complement.
- - Otherwise, return "-" if N is UINTMAX_MAX. */
+ - Otherwise, return "-" if N is unknown. */
static char const *
df_readable (bool negative, uintmax_t n, char *buf,
- uintmax_t input_units, uintmax_t output_units)
+ uintmax_t input_units, uintmax_t output_units)
{
- if (n == UINTMAX_MAX && !negative)
+ if (! known_value (n) && !negative)
return "-";
else
{
char *p = human_readable (negative ? -n : n, buf + negative,
- human_output_opts, input_units, output_units);
+ human_output_opts, input_units, output_units);
if (negative)
- *--p = '-';
+ *--p = '-';
return p;
}
}
-/* Display a space listing for the disk device with absolute file name DISK.
+/* Logical equivalence */
+#define LOG_EQ(a, b) (!(a) == !(b))
+
+/* Add integral value while using uintmax_t for value part and separate
+ negation flag. It adds value of SRC and SRC_NEG to DEST and DEST_NEG.
+ The result will be in DEST and DEST_NEG. See df_readable to understand
+ how the negation flag is used. */
+static void
+add_uint_with_neg_flag (uintmax_t *dest, bool *dest_neg,
+ uintmax_t src, bool src_neg)
+{
+ if (LOG_EQ (*dest_neg, src_neg))
+ {
+ *dest += src;
+ return;
+ }
+
+ if (*dest_neg)
+ *dest = -*dest;
+
+ if (src_neg)
+ src = -src;
+
+ if (src < *dest)
+ *dest -= src;
+ else
+ {
+ *dest = src - *dest;
+ *dest_neg = src_neg;
+ }
+
+ if (*dest_neg)
+ *dest = -*dest;
+}
+
+/* Return true if S ends in a string that may be a 36-byte UUID,
+ i.e., of the form HHHHHHHH-HHHH-HHHH-HHHH-HHHHHHHHHHHH, where
+ each H is an upper or lower case hexadecimal digit. */
+static bool _GL_ATTRIBUTE_PURE
+has_uuid_suffix (char const *s)
+{
+ size_t len = strlen (s);
+ return (36 < len
+ && strspn (s + len - 36, "-0123456789abcdefABCDEF") == 36);
+}
+
+/* Obtain the block values BV and inode values IV
+ from the file system usage FSU. */
+static void
+get_field_values (struct field_values_t *bv,
+ struct field_values_t *iv,
+ const struct fs_usage *fsu)
+{
+ /* Inode values. */
+ iv->input_units = iv->output_units = 1;
+ iv->total = fsu->fsu_files;
+ iv->available = iv->available_to_root = fsu->fsu_ffree;
+ iv->negate_available = false;
+
+ iv->used = UINTMAX_MAX;
+ iv->negate_used = false;
+ if (known_value (iv->total) && known_value (iv->available_to_root))
+ {
+ iv->used = iv->total - iv->available_to_root;
+ iv->negate_used = (iv->total < iv->available_to_root);
+ }
+
+ /* Block values. */
+ bv->input_units = fsu->fsu_blocksize;
+ bv->output_units = output_block_size;
+ bv->total = fsu->fsu_blocks;
+ bv->available = fsu->fsu_bavail;
+ bv->available_to_root = fsu->fsu_bfree;
+ bv->negate_available = (fsu->fsu_bavail_top_bit_set
+ && known_value (fsu->fsu_bavail));
+
+ bv->used = UINTMAX_MAX;
+ bv->negate_used = false;
+ if (known_value (bv->total) && known_value (bv->available_to_root))
+ {
+ bv->used = bv->total - bv->available_to_root;
+ bv->negate_used = (bv->total < bv->available_to_root);
+ }
+}
+
+/* Add block and inode values to grand total. */
+static void
+add_to_grand_total (struct field_values_t *bv, struct field_values_t *iv)
+{
+ if (known_value (iv->total))
+ grand_fsu.fsu_files += iv->total;
+ if (known_value (iv->available))
+ grand_fsu.fsu_ffree += iv->available;
+
+ if (known_value (bv->total))
+ grand_fsu.fsu_blocks += bv->input_units * bv->total;
+ if (known_value (bv->available_to_root))
+ grand_fsu.fsu_bfree += bv->input_units * bv->available_to_root;
+ if (known_value (bv->available))
+ add_uint_with_neg_flag (&grand_fsu.fsu_bavail,
+ &grand_fsu.fsu_bavail_top_bit_set,
+ bv->input_units * bv->available,
+ bv->negate_available);
+}
+
+/* Obtain a space listing for the disk device with absolute file name DISK.
If MOUNT_POINT is non-NULL, it is the name of the root of the
file system on DISK.
If STAT_FILE is non-null, it is the name of a file within the file
@@ -264,37 +886,31 @@ df_readable (bool negative, uintmax_t n, char *buf,
If FSTYPE is non-NULL, it is the type of the file system on DISK.
If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
not be able to produce statistics in this case.
- ME_DUMMY and ME_REMOTE are the mount entry flags. */
+ ME_DUMMY and ME_REMOTE are the mount entry flags.
+ Caller must set PROCESS_ALL to true when iterating over all entries, as
+ when df is invoked with no non-option argument. See below for details. */
static void
-show_dev (char const *disk, char const *mount_point,
- char const *stat_file, char const *fstype,
- bool me_dummy, bool me_remote)
+get_dev (char const *disk, char const *mount_point, char const* file,
+ char const *stat_file, char const *fstype,
+ bool me_dummy, bool me_remote,
+ const struct fs_usage *force_fsu,
+ bool process_all)
{
- struct fs_usage fsu;
- char buf[3][LONGEST_HUMAN_READABLE + 2];
- int width;
- int col1_adjustment = 0;
- int use_width;
- uintmax_t input_units;
- uintmax_t output_units;
- uintmax_t total;
- uintmax_t available;
- bool negate_available;
- uintmax_t available_to_root;
- uintmax_t used;
- bool negate_used;
- double pct = -1;
-
- if (me_remote & show_local_fs)
+ if (me_remote && show_local_fs)
return;
- if (me_dummy & !show_all_fs & !show_listed_fs)
+ if (me_dummy && !show_all_fs && !show_listed_fs)
return;
if (!selected_fstype (fstype) || excluded_fstype (fstype))
return;
+ /* Ignore relative MOUNT_POINTs, which are present for example
+ in /proc/mounts on Linux with network namespaces. */
+ if (!force_fsu && mount_point && ! IS_ABSOLUTE_FILE_NAME (mount_point))
+ return;
+
/* If MOUNT_POINT is NULL, then the file system is not mounted, and this
program reports on the file system that the special file is on.
It would be better to report on the unmounted file system,
@@ -302,261 +918,330 @@ show_dev (char const *disk, char const *mount_point,
if (!stat_file)
stat_file = mount_point ? mount_point : disk;
- if (get_fs_usage (stat_file, disk, &fsu))
+ struct fs_usage fsu;
+ if (force_fsu)
+ fsu = *force_fsu;
+ else if (get_fs_usage (stat_file, disk, &fsu))
{
- error (0, errno, "%s", quote (stat_file));
- exit_status = EXIT_FAILURE;
- return;
+ /* If we can't access a system provided entry due
+ to it not being present (now), or due to permissions,
+ just output placeholder values rather than failing. */
+ if (process_all && (errno == EACCES || errno == ENOENT))
+ {
+ if (! show_all_fs)
+ return;
+
+ fstype = "-";
+ fsu.fsu_bavail_top_bit_set = false;
+ fsu.fsu_blocksize = fsu.fsu_blocks = fsu.fsu_bfree =
+ fsu.fsu_bavail = fsu.fsu_files = fsu.fsu_ffree = UINTMAX_MAX;
+ }
+ else
+ {
+ error (0, errno, "%s", quotef (stat_file));
+ exit_status = EXIT_FAILURE;
+ return;
+ }
+ }
+ else if (process_all && show_all_fs)
+ {
+ /* Ensure we don't output incorrect stats for over-mounted directories.
+ Discard stats when the device name doesn't match. Though don't
+ discard when used and current mount entries are both remote due
+ to the possibility of aliased host names or exports. */
+ struct stat sb;
+ if (stat (stat_file, &sb) == 0)
+ {
+ struct mount_entry const * dev_me = me_for_dev (sb.st_dev);
+ if (dev_me && ! STREQ (dev_me->me_devname, disk)
+ && (! dev_me->me_remote || ! me_remote))
+ {
+ fstype = "-";
+ fsu.fsu_bavail_top_bit_set = false;
+ fsu.fsu_blocksize = fsu.fsu_blocks = fsu.fsu_bfree =
+ fsu.fsu_bavail = fsu.fsu_files = fsu.fsu_ffree = UINTMAX_MAX;
+ }
+ }
}
if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs)
return;
- if (! file_systems_processed)
- {
- file_systems_processed = true;
- print_header ();
- }
+ if (! force_fsu)
+ file_systems_processed = true;
+
+ alloc_table_row ();
if (! disk)
disk = "-"; /* unknown */
- if (! fstype)
- fstype = "-"; /* unknown */
-
- /* df.c reserved 5 positions for fstype,
- but that does not suffice for type iso9660 */
- if (print_type)
- {
- size_t disk_name_len = strlen (disk);
- size_t fstype_len = strlen (fstype);
- if (disk_name_len + fstype_len < 18)
- printf ("%s%*s ", disk, 18 - (int) disk_name_len, fstype);
- else if (!posix_format)
- printf ("%s\n%18s ", disk, fstype);
- else
- printf ("%s %s", disk, fstype);
- }
- else
- {
- if (strlen (disk) > 20 && !posix_format)
- printf ("%s\n%20s", disk, "");
- else
- printf ("%-20s", disk);
- }
-
- if (inode_format)
- {
- width = 7;
- use_width = 5;
- input_units = output_units = 1;
- total = fsu.fsu_files;
- available = fsu.fsu_ffree;
- negate_available = false;
- available_to_root = available;
- }
- else
- {
- if (human_output_opts & human_autoscale)
- width = 5 + ! (human_output_opts & human_base_1024);
- else
- {
- width = 9;
- if (posix_format)
- {
- uintmax_t b;
- col1_adjustment = -3;
- for (b = output_block_size; 9 < b; b /= 10)
- col1_adjustment++;
- }
- }
- use_width = ((posix_format
- && ! (human_output_opts & human_autoscale))
- ? 8 : 4);
- input_units = fsu.fsu_blocksize;
- output_units = output_block_size;
- total = fsu.fsu_blocks;
- available = fsu.fsu_bavail;
- negate_available = (fsu.fsu_bavail_top_bit_set
- & (available != UINTMAX_MAX));
- available_to_root = fsu.fsu_bfree;
- }
- used = UINTMAX_MAX;
- negate_used = false;
- if (total != UINTMAX_MAX && available_to_root != UINTMAX_MAX)
+ if (! file)
+ file = "-"; /* unspecified */
+
+ char *dev_name = xstrdup (disk);
+ char *resolved_dev;
+
+ /* On some systems, dev_name is a long-named symlink like
+ /dev/disk/by-uuid/828fc648-9f30-43d8-a0b1-f7196a2edb66 pointing to a
+ much shorter and more useful name like /dev/sda1. It may also look
+ like /dev/mapper/luks-828fc648-9f30-43d8-a0b1-f7196a2edb66 and point to
+ /dev/dm-0. When process_all is true and dev_name is a symlink whose
+ name ends with a UUID use the resolved name instead. */
+ if (process_all
+ && has_uuid_suffix (dev_name)
+ && (resolved_dev = canonicalize_filename_mode (dev_name, CAN_EXISTING)))
{
- used = total - available_to_root;
- negate_used = (total < available_to_root);
+ free (dev_name);
+ dev_name = resolved_dev;
}
- printf (" %*s %*s %*s ",
- width + col1_adjustment,
- df_readable (false, total,
- buf[0], input_units, output_units),
- width, df_readable (negate_used, used,
- buf[1], input_units, output_units),
- width, df_readable (negate_available, available,
- buf[2], input_units, output_units));
+ if (! fstype)
+ fstype = "-"; /* unknown */
- if (used == UINTMAX_MAX || available == UINTMAX_MAX)
- ;
- else if (!negate_used
- && used <= TYPE_MAXIMUM (uintmax_t) / 100
- && used + available != 0
- && (used + available < used) == negate_available)
- {
- uintmax_t u100 = used * 100;
- uintmax_t nonroot_total = used + available;
- pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
- }
- else
- {
- /* The calculation cannot be done easily with integer
- arithmetic. Fall back on floating point. This can suffer
- from minor rounding errors, but doing it exactly requires
- multiple precision arithmetic, and it's not worth the
- aggravation. */
- double u = negate_used ? - (double) - used : used;
- double a = negate_available ? - (double) - available : available;
- double nonroot_total = u + a;
- if (nonroot_total)
- {
- long int lipct = pct = u * 100 / nonroot_total;
- double ipct = lipct;
-
- /* Like `pct = ceil (dpct);', but avoid ceil so that
- the math library needn't be linked. */
- if (ipct - 1 < pct && pct <= ipct + 1)
- pct = ipct + (ipct < pct);
- }
- }
+ struct field_values_t block_values;
+ struct field_values_t inode_values;
+ get_field_values (&block_values, &inode_values, &fsu);
- if (0 <= pct)
- printf ("%*.0f%%", use_width - 1, pct);
- else
- printf ("%*s", use_width, "- ");
+ /* Add to grand total unless processing grand total line. */
+ if (print_grand_total && ! force_fsu)
+ add_to_grand_total (&block_values, &inode_values);
- if (mount_point)
+ size_t col;
+ for (col = 0; col < ncolumns; col++)
{
+ char buf[LONGEST_HUMAN_READABLE + 2];
+ char *cell;
+
+ struct field_values_t *v;
+ switch (columns[col]->field_type)
+ {
+ case BLOCK_FLD:
+ v = &block_values;
+ break;
+ case INODE_FLD:
+ v = &inode_values;
+ break;
+ case OTHER_FLD:
+ v = NULL;
+ break;
+ default:
+ v = NULL; /* Avoid warnings where assert() is not __noreturn__. */
+ assert (!"bad field_type");
+ }
+
+ switch (columns[col]->field)
+ {
+ case SOURCE_FIELD:
+ cell = xstrdup (dev_name);
+ break;
+
+ case FSTYPE_FIELD:
+ cell = xstrdup (fstype);
+ break;
+
+ case SIZE_FIELD:
+ case ITOTAL_FIELD:
+ cell = xstrdup (df_readable (false, v->total, buf,
+ v->input_units, v->output_units));
+ break;
+
+ case USED_FIELD:
+ case IUSED_FIELD:
+ cell = xstrdup (df_readable (v->negate_used, v->used, buf,
+ v->input_units, v->output_units));
+ break;
+
+ case AVAIL_FIELD:
+ case IAVAIL_FIELD:
+ cell = xstrdup (df_readable (v->negate_available, v->available, buf,
+ v->input_units, v->output_units));
+ break;
+
+ case PCENT_FIELD:
+ case IPCENT_FIELD:
+ {
+ double pct = -1;
+ if (! known_value (v->used) || ! known_value (v->available))
+ ;
+ else if (!v->negate_used
+ && v->used <= TYPE_MAXIMUM (uintmax_t) / 100
+ && v->used + v->available != 0
+ && (v->used + v->available < v->used)
+ == v->negate_available)
+ {
+ uintmax_t u100 = v->used * 100;
+ uintmax_t nonroot_total = v->used + v->available;
+ pct = u100 / nonroot_total + (u100 % nonroot_total != 0);
+ }
+ else
+ {
+ /* The calculation cannot be done easily with integer
+ arithmetic. Fall back on floating point. This can suffer
+ from minor rounding errors, but doing it exactly requires
+ multiple precision arithmetic, and it's not worth the
+ aggravation. */
+ double u = v->negate_used ? - (double) - v->used : v->used;
+ double a = v->negate_available
+ ? - (double) - v->available : v->available;
+ double nonroot_total = u + a;
+ if (nonroot_total)
+ {
+ long int lipct = pct = u * 100 / nonroot_total;
+ double ipct = lipct;
+
+ /* Like 'pct = ceil (dpct);', but avoid ceil so that
+ the math library needn't be linked. */
+ if (ipct - 1 < pct && pct <= ipct + 1)
+ pct = ipct + (ipct < pct);
+ }
+ }
+
+ if (0 <= pct)
+ {
+ if (asprintf (&cell, "%.0f%%", pct) == -1)
+ cell = NULL;
+ }
+ else
+ cell = strdup ("-");
+
+ if (!cell)
+ xalloc_die ();
+
+ break;
+ }
+
+ case FILE_FIELD:
+ cell = xstrdup (file);
+ break;
+
+ case TARGET_FIELD:
#ifdef HIDE_AUTOMOUNT_PREFIX
- /* Don't print the first directory name in MOUNT_POINT if it's an
- artifact of an automounter. This is a bit too aggressive to be
- the default. */
- if (strncmp ("/auto/", mount_point, 6) == 0)
- mount_point += 5;
- else if (strncmp ("/tmp_mnt/", mount_point, 9) == 0)
- mount_point += 8;
+ /* Don't print the first directory name in MOUNT_POINT if it's an
+ artifact of an automounter. This is a bit too aggressive to be
+ the default. */
+ if (STRNCMP_LIT (mount_point, "/auto/") == 0)
+ mount_point += 5;
+ else if (STRNCMP_LIT (mount_point, "/tmp_mnt/") == 0)
+ mount_point += 8;
#endif
- printf (" %s", mount_point);
+ cell = xstrdup (mount_point);
+ break;
+
+ default:
+ assert (!"unhandled field");
+ }
+
+ if (!cell)
+ assert (!"empty cell");
+
+ hide_problematic_chars (cell);
+ columns[col]->width = MAX (columns[col]->width, mbswidth (cell, 0));
+ table[nrows - 1][col] = cell;
}
- putchar ('\n');
+ free (dev_name);
}
-/* 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 NULL if unable to determine the mount point.
- Exit if unable to restore current working directory. */
+/* Scan the mount list returning the _last_ device found for MOUNT.
+ NULL is returned if MOUNT not found. The result is malloced. */
static char *
-find_mount_point (const char *file, const struct stat *file_stat)
+last_device_for_mount (char const* mount)
{
- struct saved_cwd cwd;
- struct stat last_stat;
- char *mp = NULL; /* The malloced mount point. */
+ struct mount_entry const *me;
+ struct mount_entry const *le = NULL;
- if (save_cwd (&cwd) != 0)
+ for (me = mount_list; me; me = me->me_next)
{
- error (0, errno, _("cannot get current directory"));
- return NULL;
+ if (STREQ (me->me_mountdir, mount))
+ le = me;
}
- if (S_ISDIR (file_stat->st_mode))
- /* FILE is a directory, so just chdir there directly. */
+ if (le)
{
- last_stat = *file_stat;
- if (chdir (file) < 0)
- {
- error (0, errno, _("cannot change to directory %s"), quote (file));
- return NULL;
- }
+ char *devname = le->me_devname;
+ char *canon_dev = canonicalize_file_name (devname);
+ if (canon_dev && IS_ABSOLUTE_FILE_NAME (canon_dev))
+ return canon_dev;
+ free (canon_dev);
+ return xstrdup (le->me_devname);
}
else
- /* FILE is some other kind of file; use its directory. */
- {
- char *xdir = dir_name (file);
- char *dir;
- ASSIGN_STRDUPA (dir, xdir);
- free (xdir);
-
- if (chdir (dir) < 0)
- {
- error (0, errno, _("cannot change to directory %s"), quote (dir));
- return NULL;
- }
-
- if (stat (".", &last_stat) < 0)
- {
- error (0, errno, _("cannot stat current directory (now %s)"),
- quote (dir));
- goto done;
- }
- }
-
- /* Now walk up FILE's parents until we find another file system or /,
- chdiring as we go. LAST_STAT holds stat information for the last place
- we visited. */
- for (;;)
- {
- struct stat st;
- if (stat ("..", &st) < 0)
- {
- error (0, errno, _("cannot stat %s"), quote (".."));
- goto done;
- }
- if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
- /* cwd is the mount point. */
- break;
- if (chdir ("..") < 0)
- {
- error (0, errno, _("cannot change to directory %s"), quote (".."));
- goto done;
- }
- last_stat = st;
- }
-
- /* Finally reached a mount point, see what it's called. */
- mp = xgetcwd ();
-
-done:
- /* Restore the original cwd. */
- {
- int save_errno = errno;
- if (restore_cwd (&cwd) != 0)
- error (EXIT_FAILURE, errno,
- _("failed to return to initial working directory"));
- free_cwd (&cwd);
- errno = save_errno;
- }
-
- return mp;
+ return NULL;
}
/* If DISK corresponds to a mount point, show its usage
and return true. Otherwise, return false. */
static bool
-show_disk (char const *disk)
+get_disk (char const *disk)
{
struct mount_entry const *me;
struct mount_entry const *best_match = NULL;
+ bool best_match_accessible = false;
+ bool eclipsed_device = false;
+ char const *file = disk;
+ char *resolved = canonicalize_file_name (disk);
+ if (resolved && IS_ABSOLUTE_FILE_NAME (resolved))
+ disk = resolved;
+
+ size_t best_match_len = SIZE_MAX;
for (me = mount_list; me; me = me->me_next)
- if (STREQ (disk, me->me_devname))
- best_match = me;
+ {
+ /* TODO: Should cache canon_dev in the mount_entry struct. */
+ char *devname = me->me_devname;
+ char *canon_dev = canonicalize_file_name (me->me_devname);
+ if (canon_dev && IS_ABSOLUTE_FILE_NAME (canon_dev))
+ devname = canon_dev;
+
+ if (STREQ (disk, devname))
+ {
+ char *last_device = last_device_for_mount (me->me_mountdir);
+ eclipsed_device = last_device && ! STREQ (last_device, devname);
+ size_t len = strlen (me->me_mountdir);
+
+ if (! eclipsed_device
+ && (! best_match_accessible || len < best_match_len))
+ {
+ struct stat disk_stats;
+ bool this_match_accessible = false;
+
+ if (stat (me->me_mountdir, &disk_stats) == 0)
+ best_match_accessible = this_match_accessible = true;
+
+ if (this_match_accessible
+ || (! best_match_accessible && len < best_match_len))
+ {
+ best_match = me;
+ if (len == 1) /* Traditional root. */
+ {
+ free (last_device);
+ free (canon_dev);
+ break;
+ }
+ else
+ best_match_len = len;
+ }
+ }
+
+ free (last_device);
+ }
+
+ free (canon_dev);
+ }
+
+ free (resolved);
if (best_match)
{
- show_dev (best_match->me_devname, best_match->me_mountdir, NULL,
- best_match->me_type, best_match->me_dummy,
- best_match->me_remote);
+ get_dev (best_match->me_devname, best_match->me_mountdir, file, NULL,
+ best_match->me_type, best_match->me_dummy,
+ best_match->me_remote, NULL, false);
+ return true;
+ }
+ else if (eclipsed_device)
+ {
+ error (0, 0, _("cannot access %s: over-mounted by another device"),
+ quoteaf (file));
+ exit_status = EXIT_FAILURE;
return true;
}
@@ -565,145 +1250,131 @@ show_disk (char const *disk)
/* Figure out which device file or directory POINT is mounted on
and show its disk usage.
- STATP must be the result of `stat (POINT, STATP)'. */
+ STATP must be the result of 'stat (POINT, STATP)'. */
static void
-show_point (const char *point, const struct stat *statp)
+get_point (const char *point, const struct stat *statp)
{
struct stat disk_stats;
struct mount_entry *me;
struct mount_entry const *best_match = NULL;
- /* If POINT is an absolute file name, see if we can find the
- mount point without performing any extra stat calls at all. */
- if (*point == '/')
- {
- /* Find the best match: prefer non-dummies, and then prefer the
- last match if there are ties. */
-
- for (me = mount_list; me; me = me->me_next)
- if (STREQ (me->me_mountdir, point) && !STREQ (me->me_type, "lofs")
- && (!best_match || best_match->me_dummy || !me->me_dummy))
- best_match = me;
- }
-
/* Calculate the real absolute file name for POINT, and use that to find
the mount point. This avoids statting unavailable mount points,
which can hang df. */
- if (! best_match)
+ char *resolved = canonicalize_file_name (point);
+ if (resolved && resolved[0] == '/')
{
- char *resolved = canonicalize_file_name (point);
-
- if (resolved && resolved[0] == '/')
- {
- size_t resolved_len = strlen (resolved);
- size_t best_match_len = 0;
-
- for (me = mount_list; me; me = me->me_next)
- if (!STREQ (me->me_type, "lofs")
- && (!best_match || best_match->me_dummy || !me->me_dummy))
- {
- size_t len = strlen (me->me_mountdir);
- if (best_match_len <= len && len <= resolved_len
- && (len == 1 /* root file system */
- || ((len == resolved_len || resolved[len] == '/')
- && strncmp (me->me_mountdir, resolved, len) == 0)))
- {
- best_match = me;
- best_match_len = len;
- }
- }
- }
-
- free (resolved);
-
- if (best_match
- && (stat (best_match->me_mountdir, &disk_stats) != 0
- || disk_stats.st_dev != statp->st_dev))
- best_match = NULL;
+ size_t resolved_len = strlen (resolved);
+ size_t best_match_len = 0;
+
+ for (me = mount_list; me; me = me->me_next)
+ {
+ if (!STREQ (me->me_type, "lofs")
+ && (!best_match || best_match->me_dummy || !me->me_dummy))
+ {
+ size_t len = strlen (me->me_mountdir);
+ if (best_match_len <= len && len <= resolved_len
+ && (len == 1 /* root file system */
+ || ((len == resolved_len || resolved[len] == '/')
+ && STREQ_LEN (me->me_mountdir, resolved, len))))
+ {
+ best_match = me;
+ best_match_len = len;
+ }
+ }
+ }
}
+ free (resolved);
+ if (best_match
+ && (stat (best_match->me_mountdir, &disk_stats) != 0
+ || disk_stats.st_dev != statp->st_dev))
+ best_match = NULL;
if (! best_match)
for (me = mount_list; me; me = me->me_next)
{
- if (me->me_dev == (dev_t) -1)
- {
- if (stat (me->me_mountdir, &disk_stats) == 0)
- me->me_dev = disk_stats.st_dev;
- else
- {
- /* Report only I/O errors. Other errors might be
- caused by shadowed mount points, which means POINT
- can't possibly be on this file system. */
- if (errno == EIO)
- {
- error (0, errno, "%s", quote (me->me_mountdir));
- exit_status = EXIT_FAILURE;
- }
-
- /* So we won't try and fail repeatedly. */
- me->me_dev = (dev_t) -2;
- }
- }
-
- if (statp->st_dev == me->me_dev
- && !STREQ (me->me_type, "lofs")
- && (!best_match || best_match->me_dummy || !me->me_dummy))
- {
- /* Skip bogus mtab entries. */
- if (stat (me->me_mountdir, &disk_stats) != 0
- || disk_stats.st_dev != me->me_dev)
- me->me_dev = (dev_t) -2;
- else
- best_match = me;
- }
+ if (me->me_dev == (dev_t) -1)
+ {
+ if (stat (me->me_mountdir, &disk_stats) == 0)
+ me->me_dev = disk_stats.st_dev;
+ else
+ {
+ /* Report only I/O errors. Other errors might be
+ caused by shadowed mount points, which means POINT
+ can't possibly be on this file system. */
+ if (errno == EIO)
+ {
+ error (0, errno, "%s", quotef (me->me_mountdir));
+ exit_status = EXIT_FAILURE;
+ }
+
+ /* So we won't try and fail repeatedly. */
+ me->me_dev = (dev_t) -2;
+ }
+ }
+
+ if (statp->st_dev == me->me_dev
+ && !STREQ (me->me_type, "lofs")
+ && (!best_match || best_match->me_dummy || !me->me_dummy))
+ {
+ /* Skip bogus mtab entries. */
+ if (stat (me->me_mountdir, &disk_stats) != 0
+ || disk_stats.st_dev != me->me_dev)
+ me->me_dev = (dev_t) -2;
+ else
+ best_match = me;
+ }
}
if (best_match)
- show_dev (best_match->me_devname, best_match->me_mountdir, point,
- best_match->me_type, best_match->me_dummy, best_match->me_remote);
+ get_dev (best_match->me_devname, best_match->me_mountdir, point, point,
+ best_match->me_type, best_match->me_dummy, best_match->me_remote,
+ NULL, false);
else
{
/* We couldn't find the mount entry corresponding to POINT. Go ahead and
- print as much info as we can; methods that require the device to be
- present will fail at a later point. */
+ print as much info as we can; methods that require the device to be
+ present will fail at a later point. */
/* Find the actual mount point. */
char *mp = find_mount_point (point, statp);
if (mp)
- {
- show_dev (NULL, mp, NULL, NULL, false, false);
- free (mp);
- }
+ {
+ get_dev (NULL, mp, point, NULL, NULL, false, false, NULL, false);
+ free (mp);
+ }
}
}
/* Determine what kind of node NAME is and show the disk usage
- for it. STATP is the results of `stat' on NAME. */
+ for it. STATP is the results of 'stat' on NAME. */
static void
-show_entry (char const *name, struct stat const *statp)
+get_entry (char const *name, struct stat const *statp)
{
if ((S_ISBLK (statp->st_mode) || S_ISCHR (statp->st_mode))
- && show_disk (name))
+ && get_disk (name))
return;
- show_point (name, statp);
+ get_point (name, statp);
}
/* Show all mounted file systems, except perhaps those that are of
- an unselected type or are empty. */
+ an unselected type or are empty. */
static void
-show_all_entries (void)
+get_all_entries (void)
{
struct mount_entry *me;
+ filter_mount_list (show_all_fs);
+
for (me = mount_list; me; me = me->me_next)
- show_dev (me->me_devname, me->me_mountdir, NULL, me->me_type,
- me->me_dummy, me->me_remote);
+ 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. */
+/* Add FSTYPE to the list of file system types to display. */
static void
add_fs_type (const char *fstype)
@@ -716,7 +1387,7 @@ add_fs_type (const char *fstype)
fs_select_list = fsp;
}
-/* Add FSTYPE to the list of file system types to be omitted. */
+/* Add FSTYPE to the list of file system types to be omitted. */
static void
add_excluded_fs_type (const char *fstype)
@@ -733,34 +1404,45 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
fputs (_("\
Show information about the file system on which each FILE resides,\n\
or all file systems by default.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
+
+ emit_mandatory_arg_note ();
+
+ /* TRANSLATORS: The thousands and decimal separators are best
+ adjusted to an appropriate default for your locale. */
fputs (_("\
- -a, --all include dummy file systems\n\
- -B, --block-size=SIZE use SIZE-byte blocks\n\
- -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
- -H, --si likewise, but use powers of 1000 not 1024\n\
+ -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\
+ --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\
-P, --portability use the POSIX output format\n\
--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\
@@ -768,11 +1450,14 @@ Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ emit_blocksize_note ("DF");
+ emit_size_note ();
fputs (_("\n\
-SIZE may be (or may be an integer optionally followed by) one of following:\n\
-kB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
+FIELD_LIST is a comma-separated list of columns to be included. Valid\n\
+field names are: 'source', 'fstype', 'itotal', 'iused', 'iavail', 'ipcent',\n\
+'size', 'used', 'avail', 'pcent', 'file' and 'target' (see info page).\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -780,11 +1465,10 @@ kB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
int
main (int argc, char **argv)
{
- int c;
- struct stat *stats IF_LINT (= 0);
+ struct stat *stats IF_LINT ( = 0);
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -793,175 +1477,273 @@ main (int argc, char **argv)
fs_select_list = NULL;
fs_exclude_list = NULL;
- inode_format = false;
show_all_fs = false;
show_listed_fs = false;
human_output_opts = -1;
print_type = false;
file_systems_processed = false;
- posix_format = 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;
- while ((c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options, NULL))
- != -1)
+ const char *msg_mut_excl = _("options %s and %s are mutually exclusive");
+
+ while (true)
{
+ int oi = -1;
+ int c = getopt_long (argc, argv, "aB:iF:hHklmPTt:vx:", long_options,
+ &oi);
+ if (c == -1)
+ break;
+
switch (c)
- {
- case 'a':
- show_all_fs = true;
- break;
- case 'B':
- human_output_opts = human_options (optarg, true, &output_block_size);
- break;
- case 'i':
- inode_format = true;
- break;
- case 'h':
- human_output_opts = human_autoscale | human_SI | human_base_1024;
- output_block_size = 1;
- break;
- case 'H':
- human_output_opts = human_autoscale | human_SI;
- output_block_size = 1;
- break;
- case KILOBYTES_LONG_OPTION:
- error (0, 0,
- _("the --kilobytes option is deprecated; use -k instead"));
- /* fall through */
- case 'k':
- human_output_opts = 0;
- output_block_size = 1024;
- break;
- case 'l':
- show_local_fs = true;
- break;
- case 'm': /* obsolescent */
- human_output_opts = 0;
- output_block_size = 1024 * 1024;
- break;
- case 'T':
- print_type = true;
- break;
- case 'P':
- posix_format = true;
- break;
- case SYNC_OPTION:
- require_sync = true;
- break;
- case NO_SYNC_OPTION:
- require_sync = false;
- break;
-
- case 'F':
- /* Accept -F as a synonym for -t for compatibility with Solaris. */
- case 't':
- add_fs_type (optarg);
- break;
-
- case 'v': /* For SysV compatibility. */
- /* ignore */
- break;
- case 'x':
- add_excluded_fs_type (optarg);
- break;
-
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'a':
+ show_all_fs = true;
+ break;
+ case 'B':
+ {
+ enum strtol_error e = human_options (optarg, &human_output_opts,
+ &output_block_size);
+ if (e != LONGINT_OK)
+ xstrtol_fatal (e, oi, c, long_options, optarg);
+ }
+ break;
+ case 'i':
+ if (header_mode == OUTPUT_MODE)
+ {
+ error (0, 0, msg_mut_excl, "-i", "--output");
+ usage (EXIT_FAILURE);
+ }
+ header_mode = INODES_MODE;
+ break;
+ case 'h':
+ human_output_opts = human_autoscale | human_SI | human_base_1024;
+ output_block_size = 1;
+ break;
+ case 'H':
+ human_output_opts = human_autoscale | human_SI;
+ output_block_size = 1;
+ break;
+ case 'k':
+ human_output_opts = 0;
+ output_block_size = 1024;
+ break;
+ case 'l':
+ show_local_fs = true;
+ break;
+ case 'm': /* obsolescent, exists for BSD compatibility */
+ human_output_opts = 0;
+ output_block_size = 1024 * 1024;
+ break;
+ case 'T':
+ if (header_mode == OUTPUT_MODE)
+ {
+ error (0, 0, msg_mut_excl, "-T", "--output");
+ usage (EXIT_FAILURE);
+ }
+ print_type = true;
+ break;
+ case 'P':
+ if (header_mode == OUTPUT_MODE)
+ {
+ error (0, 0, msg_mut_excl, "-P", "--output");
+ usage (EXIT_FAILURE);
+ }
+ posix_format = true;
+ break;
+ case SYNC_OPTION:
+ require_sync = true;
+ break;
+ case NO_SYNC_OPTION:
+ require_sync = false;
+ break;
+
+ case 'F':
+ /* Accept -F as a synonym for -t for compatibility with Solaris. */
+ case 't':
+ add_fs_type (optarg);
+ break;
+
+ case 'v': /* For SysV compatibility. */
+ /* ignore */
+ break;
+ case 'x':
+ add_excluded_fs_type (optarg);
+ break;
+
+ case OUTPUT_OPTION:
+ if (header_mode == INODES_MODE)
+ {
+ error (0, 0, msg_mut_excl, "-i", "--output");
+ usage (EXIT_FAILURE);
+ }
+ if (posix_format && header_mode == DEFAULT_MODE)
+ {
+ error (0, 0, msg_mut_excl, "-P", "--output");
+ usage (EXIT_FAILURE);
+ }
+ if (print_type)
+ {
+ error (0, 0, msg_mut_excl, "-T", "--output");
+ usage (EXIT_FAILURE);
+ }
+ header_mode = OUTPUT_MODE;
+ if (optarg)
+ decode_output_arg (optarg);
+ break;
+
+ case TOTAL_OPTION:
+ print_grand_total = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
}
if (human_output_opts == -1)
{
if (posix_format)
- {
- human_output_opts = 0;
- output_block_size = (getenv ("POSIXLY_CORRECT") ? 512 : 1024);
- }
+ {
+ human_output_opts = 0;
+ output_block_size = (getenv ("POSIXLY_CORRECT") ? 512 : 1024);
+ }
else
- human_output_opts = human_options (getenv ("DF_BLOCK_SIZE"), false,
- &output_block_size);
+ human_options (getenv ("DF_BLOCK_SIZE"),
+ &human_output_opts, &output_block_size);
}
+ if (header_mode == INODES_MODE || header_mode == OUTPUT_MODE)
+ ;
+ else if (human_output_opts & human_autoscale)
+ header_mode = HUMAN_MODE;
+ else if (posix_format)
+ header_mode = POSIX_MODE;
+
/* 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)
{
- struct fs_type_list *fs_excl;
- for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next)
- {
- if (STREQ (fs_incl->fs_name, fs_excl->fs_name))
- {
- error (0, 0,
- _("file system type %s both selected and excluded"),
- quote (fs_incl->fs_name));
- match = true;
- break;
- }
- }
+ struct fs_type_list *fs_excl;
+ for (fs_excl = fs_exclude_list; fs_excl; fs_excl = fs_excl->fs_next)
+ {
+ if (STREQ (fs_incl->fs_name, fs_excl->fs_name))
+ {
+ error (0, 0,
+ _("file system type %s both selected and excluded"),
+ quote (fs_incl->fs_name));
+ match = true;
+ break;
+ }
+ }
}
if (match)
- exit (EXIT_FAILURE);
+ return EXIT_FAILURE;
}
+ assume (0 < optind);
+
if (optind < argc)
{
int i;
- /* stat all the given entries to make sure they get automounted,
- if necessary, before reading the file system table. */
+ /* Open each of the given entries to make sure any corresponding
+ partition is automounted. This must be done before reading the
+ file system table. */
stats = xnmalloc (argc - optind, sizeof *stats);
for (i = optind; i < argc; ++i)
- {
- if (stat (argv[i], &stats[i - optind]))
- {
- error (0, errno, "%s", quote (argv[i]));
- exit_status = EXIT_FAILURE;
- argv[i] = NULL;
- }
- }
+ {
+ /* Prefer to open with O_NOCTTY and use fstat, but fall back
+ on using "stat", in case the file is unreadable. */
+ int fd = open (argv[i], O_RDONLY | O_NOCTTY);
+ if ((fd < 0 || fstat (fd, &stats[i - optind]))
+ && stat (argv[i], &stats[i - optind]))
+ {
+ error (0, errno, "%s", quotef (argv[i]));
+ exit_status = EXIT_FAILURE;
+ argv[i] = NULL;
+ }
+ if (0 <= fd)
+ close (fd);
+ }
}
mount_list =
read_file_system_list ((fs_select_list != NULL
- || fs_exclude_list != NULL
- || print_type
- || show_local_fs));
+ || fs_exclude_list != NULL
+ || print_type
+ || field_data[FSTYPE_FIELD].used
+ || show_local_fs));
if (mount_list == NULL)
{
/* Couldn't read the table of mounted file systems.
- Fail if df was invoked with no file name arguments;
- Otherwise, merely give a warning and proceed. */
- const char *warning = (optind < argc ? _("Warning: ") : "");
- int status = (optind < argc ? 0 : EXIT_FAILURE);
- error (status, errno,
- _("%scannot read table of mounted file systems"), warning);
+ Fail if df was invoked with no file name arguments,
+ or when either of -a, -l, -t or -x is used with file name
+ arguments. Otherwise, merely give a warning and proceed. */
+ int status = 0;
+ if ( ! (optind < argc)
+ || (show_all_fs
+ || show_local_fs
+ || fs_select_list != NULL
+ || fs_exclude_list != NULL))
+ {
+ status = EXIT_FAILURE;
+ }
+ const char *warning = (status == 0 ? _("Warning: ") : "");
+ error (status, errno, "%s%s", warning,
+ _("cannot read table of mounted file systems"));
}
if (require_sync)
sync ();
+ get_field_list ();
+ get_header ();
+
if (optind < argc)
{
int i;
- /* Display explicitly requested empty file systems. */
+ /* Display explicitly requested empty file systems. */
show_listed_fs = true;
for (i = optind; i < argc; ++i)
- if (argv[i])
- show_entry (argv[i], &stats[i - optind]);
+ if (argv[i])
+ get_entry (argv[i], &stats[i - optind]);
+
+ IF_LINT (free (stats));
+ }
+ else
+ get_all_entries ();
+
+ if (file_systems_processed)
+ {
+ if (print_grand_total)
+ get_dev ("total",
+ (field_data[SOURCE_FIELD].used ? "-" : "total"),
+ NULL, NULL, NULL, false, false, &grand_fsu, false);
+
+ print_table ();
}
else
- show_all_entries ();
+ {
+ /* Print the "no FS processed" diagnostic only if there was no preceding
+ diagnostic, e.g., if all have been excluded. */
+ if (exit_status == EXIT_SUCCESS)
+ error (EXIT_FAILURE, 0, _("no file systems processed"));
+ }
- if (! file_systems_processed)
- error (EXIT_FAILURE, 0, _("no file systems processed"));
+ IF_LINT (free (columns));
- exit (exit_status);
+ return exit_status;
}
diff --git a/src/dircolors.c b/src/dircolors.c
index 82eb1e0..0ada494 100644
--- a/src/dircolors.c
+++ b/src/dircolors.c
@@ -1,11 +1,11 @@
/* dircolors - output commands to set the LS_COLOR environment variable
- Copyright (C) 1996-2007 Free Software Foundation, Inc.
+ Copyright (C) 1996-2016 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
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -13,28 +13,27 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#include <sys/types.h>
+#include <fnmatch.h>
#include <getopt.h>
-#include <stdio.h>
#include "system.h"
#include "dircolors.h"
#include "c-strcase.h"
#include "error.h"
-#include "getline.h"
#include "obstack.h"
#include "quote.h"
+#include "stdio--.h"
#include "xstrndup.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "dircolors"
-#define AUTHORS "H. Peter Anvin"
+#define AUTHORS proper_name ("H. Peter Anvin")
#define obstack_chunk_alloc malloc
#define obstack_chunk_free free
@@ -61,21 +60,21 @@ static struct obstack lsc_obstack;
static const char *const slack_codes[] =
{
- "NORMAL", "NORM", "FILE", "DIR", "LNK", "LINK",
+ "NORMAL", "NORM", "FILE", "RESET", "DIR", "LNK", "LINK",
"SYMLINK", "ORPHAN", "MISSING", "FIFO", "PIPE", "SOCK", "BLK", "BLOCK",
"CHR", "CHAR", "DOOR", "EXEC", "LEFT", "LEFTCODE", "RIGHT", "RIGHTCODE",
"END", "ENDCODE", "SUID", "SETUID", "SGID", "SETGID", "STICKY",
- "OTHER_WRITABLE", "OWR", "STICKY_OTHER_WRITABLE", "OWT", NULL
+ "OTHER_WRITABLE", "OWR", "STICKY_OTHER_WRITABLE", "OWT", "CAPABILITY",
+ "MULTIHARDLINK", "CLRTOEOL", NULL
};
static const char *const ls_codes[] =
{
- "no", "no", "fi", "di", "ln", "ln", "ln", "or", "mi", "pi", "pi",
+ "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", NULL
+ "su", "su", "sg", "sg", "st", "ow", "ow", "tw", "tw", "ca", "mh", "cl", NULL
};
-#define array_len(Array) (sizeof (Array) / sizeof *(Array))
-verify (array_len (slack_codes) == array_len (ls_codes));
+verify (ARRAY_CARDINALITY (slack_codes) == ARRAY_CARDINALITY (ls_codes));
static struct option const long_options[] =
{
@@ -89,14 +88,11 @@ static struct option const long_options[] =
{NULL, 0, NULL, 0}
};
-char *program_name;
-
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("Usage: %s [OPTION]... [FILE]\n"), program_name);
@@ -114,15 +110,15 @@ Determine format of output:\n\
\n\
If FILE is specified, read it to determine which colors to use for which\n\
file types and extensions. Otherwise, a precompiled database is used.\n\
-For details on the format of these files, run `dircolors --print-database'.\n\
+For details on the format of these files, run 'dircolors --print-database'.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
-/* If the SHELL environment variable is set to `csh' or `tcsh,'
+/* If the SHELL environment variable is set to 'csh' or 'tcsh,'
assume C shell. Else Bourne shell. */
static enum Shell_syntax
@@ -202,29 +198,29 @@ append_quoted (const char *str)
while (*str != '\0')
{
switch (*str)
- {
- case '\'':
- APPEND_CHAR ('\'');
- APPEND_CHAR ('\\');
- APPEND_CHAR ('\'');
- need_backslash = true;
- break;
-
- case '\\':
- case '^':
- need_backslash = !need_backslash;
- break;
-
- case ':':
- case '=':
- if (need_backslash)
- APPEND_CHAR ('\\');
- /* Fall through */
-
- default:
- need_backslash = true;
- break;
- }
+ {
+ case '\'':
+ APPEND_CHAR ('\'');
+ APPEND_CHAR ('\\');
+ APPEND_CHAR ('\'');
+ need_backslash = true;
+ break;
+
+ case '\\':
+ case '^':
+ need_backslash = !need_backslash;
+ break;
+
+ case ':':
+ case '=':
+ if (need_backslash)
+ APPEND_CHAR ('\\');
+ /* Fall through */
+
+ default:
+ need_backslash = true;
+ break;
+ }
APPEND_CHAR (*str);
++str;
@@ -232,7 +228,7 @@ append_quoted (const char *str)
}
/* Read the file open on FP (with name FILENAME). First, look for a
- `TERM name' directive where name matches the current terminal type.
+ 'TERM name' directive where name matches the current terminal type.
Once found, translate and accumulate the associated directives onto
the global obstack LSC_OBSTACK. Give a diagnostic
upon failure (unrecognized keyword is the only way to fail here).
@@ -265,106 +261,106 @@ dc_parse_stream (FILE *fp, const char *filename)
++line_number;
if (fp)
- {
- if (getline (&input_line, &input_line_size, fp) <= 0)
- {
- free (input_line);
- break;
- }
- line = input_line;
- }
+ {
+ if (getline (&input_line, &input_line_size, fp) <= 0)
+ {
+ free (input_line);
+ break;
+ }
+ line = input_line;
+ }
else
- {
- if (next_G_line == G_line + sizeof G_line)
- break;
- line = next_G_line;
- next_G_line += strlen (next_G_line) + 1;
- }
+ {
+ if (next_G_line == G_line + sizeof G_line)
+ break;
+ line = next_G_line;
+ next_G_line += strlen (next_G_line) + 1;
+ }
parse_line (line, &keywd, &arg);
if (keywd == NULL)
- continue;
+ continue;
if (arg == NULL)
- {
- error (0, 0, _("%s:%lu: invalid line; missing second token"),
- filename, (unsigned long int) line_number);
- ok = false;
- free (keywd);
- continue;
- }
+ {
+ error (0, 0, _("%s:%lu: invalid line; missing second token"),
+ quotef (filename), (unsigned long int) line_number);
+ ok = false;
+ free (keywd);
+ continue;
+ }
unrecognized = false;
if (c_strcasecmp (keywd, "TERM") == 0)
- {
- if (STREQ (arg, term))
- state = ST_TERMSURE;
- else if (state != ST_TERMSURE)
- state = ST_TERMNO;
- }
+ {
+ if (fnmatch (arg, term, 0) == 0)
+ state = ST_TERMSURE;
+ else if (state != ST_TERMSURE)
+ state = ST_TERMNO;
+ }
else
- {
- if (state == ST_TERMSURE)
- state = ST_TERMYES; /* Another TERM can cancel */
-
- if (state != ST_TERMNO)
- {
- if (keywd[0] == '.')
- {
- APPEND_CHAR ('*');
- append_quoted (keywd);
- APPEND_CHAR ('=');
- append_quoted (arg);
- APPEND_CHAR (':');
- }
- else if (keywd[0] == '*')
- {
- append_quoted (keywd);
- APPEND_CHAR ('=');
- append_quoted (arg);
- APPEND_CHAR (':');
- }
- else if (c_strcasecmp (keywd, "OPTIONS") == 0
- || c_strcasecmp (keywd, "COLOR") == 0
- || c_strcasecmp (keywd, "EIGHTBIT") == 0)
- {
- /* Ignore. */
- }
- else
- {
- int i;
-
- for (i = 0; slack_codes[i] != NULL; ++i)
- if (c_strcasecmp (keywd, slack_codes[i]) == 0)
- break;
-
- if (slack_codes[i] != NULL)
- {
- APPEND_TWO_CHAR_STRING (ls_codes[i]);
- APPEND_CHAR ('=');
- append_quoted (arg);
- APPEND_CHAR (':');
- }
- else
- {
- unrecognized = true;
- }
- }
- }
- else
- {
- unrecognized = true;
- }
- }
+ {
+ if (state == ST_TERMSURE)
+ state = ST_TERMYES; /* Another TERM can cancel */
+
+ if (state != ST_TERMNO)
+ {
+ if (keywd[0] == '.')
+ {
+ APPEND_CHAR ('*');
+ append_quoted (keywd);
+ APPEND_CHAR ('=');
+ append_quoted (arg);
+ APPEND_CHAR (':');
+ }
+ else if (keywd[0] == '*')
+ {
+ append_quoted (keywd);
+ APPEND_CHAR ('=');
+ append_quoted (arg);
+ APPEND_CHAR (':');
+ }
+ else if (c_strcasecmp (keywd, "OPTIONS") == 0
+ || c_strcasecmp (keywd, "COLOR") == 0
+ || c_strcasecmp (keywd, "EIGHTBIT") == 0)
+ {
+ /* Ignore. */
+ }
+ else
+ {
+ int i;
+
+ for (i = 0; slack_codes[i] != NULL; ++i)
+ if (c_strcasecmp (keywd, slack_codes[i]) == 0)
+ break;
+
+ if (slack_codes[i] != NULL)
+ {
+ APPEND_TWO_CHAR_STRING (ls_codes[i]);
+ APPEND_CHAR ('=');
+ append_quoted (arg);
+ APPEND_CHAR (':');
+ }
+ else
+ {
+ unrecognized = true;
+ }
+ }
+ }
+ else
+ {
+ unrecognized = true;
+ }
+ }
if (unrecognized && (state == ST_TERMSURE || state == ST_TERMYES))
- {
- error (0, 0, _("%s:%lu: unrecognized keyword %s"),
- (filename ? quote (filename) : _("<internal>")),
- (unsigned long int) line_number, keywd);
- ok = false;
- }
+ {
+ error (0, 0, _("%s:%lu: unrecognized keyword %s"),
+ (filename ? quotef (filename) : _("<internal>")),
+ (unsigned long int) line_number, keywd);
+ ok = false;
+ }
free (keywd);
free (arg);
@@ -380,7 +376,7 @@ dc_parse_file (const char *filename)
if (! STREQ (filename, "-") && freopen (filename, "r", stdin) == NULL)
{
- error (0, errno, "%s", filename);
+ error (0, errno, "%s", quotef (filename));
return false;
}
@@ -388,7 +384,7 @@ dc_parse_file (const char *filename)
if (fclose (stdin) != 0)
{
- error (0, errno, "%s", quote (filename));
+ error (0, errno, "%s", quotef (filename));
return false;
}
@@ -404,7 +400,7 @@ main (int argc, char **argv)
bool print_database = false;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -415,23 +411,23 @@ main (int argc, char **argv)
switch (optc)
{
case 'b': /* Bourne shell syntax. */
- syntax = SHELL_SYNTAX_BOURNE;
- break;
+ syntax = SHELL_SYNTAX_BOURNE;
+ break;
case 'c': /* C shell syntax. */
- syntax = SHELL_SYNTAX_C;
- break;
+ syntax = SHELL_SYNTAX_C;
+ break;
case 'p':
- print_database = true;
- break;
+ print_database = true;
+ break;
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
- usage (EXIT_FAILURE);
+ usage (EXIT_FAILURE);
}
argc -= optind;
@@ -442,71 +438,71 @@ main (int argc, char **argv)
if (print_database && syntax != SHELL_SYNTAX_UNKNOWN)
{
error (0, 0,
- _("the options to output dircolors' internal database and\n\
-to select a shell syntax are mutually exclusive"));
+ _("the options to output dircolors' internal database and\n"
+ "to select a shell syntax are mutually exclusive"));
usage (EXIT_FAILURE);
}
- if (!print_database < argc)
+ if ((!print_database) < argc)
{
error (0, 0, _("extra operand %s"), quote (argv[!print_database]));
if (print_database)
- fprintf (stderr, "%s\n",
- _("File operands cannot be combined with "
- "--print-database (-p)."));
+ fprintf (stderr, "%s\n",
+ _("file operands cannot be combined with "
+ "--print-database (-p)"));
usage (EXIT_FAILURE);
}
if (print_database)
{
char const *p = G_line;
- while (p < G_line + sizeof G_line)
- {
- puts (p);
- p += strlen (p) + 1;
- }
+ while (p - G_line < sizeof G_line)
+ {
+ puts (p);
+ p += strlen (p) + 1;
+ }
}
else
{
/* If shell syntax was not explicitly specified, try to guess it. */
if (syntax == SHELL_SYNTAX_UNKNOWN)
- {
- syntax = guess_shell_syntax ();
- if (syntax == SHELL_SYNTAX_UNKNOWN)
- {
- error (EXIT_FAILURE, 0,
- _("no SHELL environment variable, and no shell type option given"));
- }
- }
+ {
+ syntax = guess_shell_syntax ();
+ if (syntax == SHELL_SYNTAX_UNKNOWN)
+ {
+ error (EXIT_FAILURE, 0,
+ _("no SHELL environment variable, and no shell type option given"));
+ }
+ }
obstack_init (&lsc_obstack);
if (argc == 0)
- ok = dc_parse_stream (NULL, NULL);
+ ok = dc_parse_stream (NULL, NULL);
else
- ok = dc_parse_file (argv[0]);
+ ok = dc_parse_file (argv[0]);
if (ok)
- {
- size_t len = obstack_object_size (&lsc_obstack);
- char *s = obstack_finish (&lsc_obstack);
- const char *prefix;
- const char *suffix;
-
- if (syntax == SHELL_SYNTAX_BOURNE)
- {
- prefix = "LS_COLORS='";
- suffix = "';\nexport LS_COLORS\n";
- }
- else
- {
- prefix = "setenv LS_COLORS '";
- suffix = "'\n";
- }
- fputs (prefix, stdout);
- fwrite (s, 1, len, stdout);
- fputs (suffix, stdout);
- }
+ {
+ size_t len = obstack_object_size (&lsc_obstack);
+ char *s = obstack_finish (&lsc_obstack);
+ const char *prefix;
+ const char *suffix;
+
+ if (syntax == SHELL_SYNTAX_BOURNE)
+ {
+ prefix = "LS_COLORS='";
+ suffix = "';\nexport LS_COLORS\n";
+ }
+ else
+ {
+ prefix = "setenv LS_COLORS '";
+ suffix = "'\n";
+ }
+ fputs (prefix, stdout);
+ fwrite (s, 1, len, stdout);
+ fputs (suffix, stdout);
+ }
}
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/dircolors.h b/src/dircolors.h
index c74fb53..ec89b2c 100644
--- a/src/dircolors.h
+++ b/src/dircolors.h
@@ -2,51 +2,43 @@ static char const G_line[] =
{
'#',' ','C','o','n','f','i','g','u','r','a','t','i','o','n',' ','f','i','l','e',' ','f','o','r',' ','d','i','r','c','o','l','o','r','s',',',' ','a',' ','u','t','i','l','i','t','y',' ','t','o',' ','h','e','l','p',' ','y','o','u',' ','s','e','t',' ','t','h','e',0,
'#',' ','L','S','_','C','O','L','O','R','S',' ','e','n','v','i','r','o','n','m','e','n','t',' ','v','a','r','i','a','b','l','e',' ','u','s','e','d',' ','b','y',' ','G','N','U',' ','l','s',' ','w','i','t','h',' ','t','h','e',' ','-','-','c','o','l','o','r',' ','o','p','t','i','o','n','.',0,
- '#',' ','C','o','p','y','r','i','g','h','t',' ','(','C',')',' ','1','9','9','6',',',' ','1','9','9','9',',',' ','2','0','0','0',',',' ','2','0','0','1',',',' ','2','0','0','2',',',' ','2','0','0','3',',',' ','2','0','0','4',',',' ','2','0','0','5',',',' ','2','0','0','6',0,
- '#',' ','F','r','e','e',' ','S','o','f','t','w','a','r','e',' ','F','o','u','n','d','a','t','i','o','n',',',' ','I','n','c','.',0,
+ '#',' ','C','o','p','y','r','i','g','h','t',' ','(','C',')',' ','1','9','9','6','-','2','0','1','6',' ','F','r','e','e',' ','S','o','f','t','w','a','r','e',' ','F','o','u','n','d','a','t','i','o','n',',',' ','I','n','c','.',0,
'#',' ','C','o','p','y','i','n','g',' ','a','n','d',' ','d','i','s','t','r','i','b','u','t','i','o','n',' ','o','f',' ','t','h','i','s',' ','f','i','l','e',',',' ','w','i','t','h',' ','o','r',' ','w','i','t','h','o','u','t',' ','m','o','d','i','f','i','c','a','t','i','o','n',',',0,
'#',' ','a','r','e',' ','p','e','r','m','i','t','t','e','d',' ','p','r','o','v','i','d','e','d',' ','t','h','e',' ','c','o','p','y','r','i','g','h','t',' ','n','o','t','i','c','e',' ','a','n','d',' ','t','h','i','s',' ','n','o','t','i','c','e',' ','a','r','e',' ','p','r','e','s','e','r','v','e','d','.',0,
'#',' ','T','h','e',' ','k','e','y','w','o','r','d','s',' ','C','O','L','O','R',',',' ','O','P','T','I','O','N','S',',',' ','a','n','d',' ','E','I','G','H','T','B','I','T',' ','(','h','o','n','o','r','e','d',' ','b','y',' ','t','h','e',0,
'#',' ','s','l','a','c','k','w','a','r','e',' ','v','e','r','s','i','o','n',' ','o','f',' ','d','i','r','c','o','l','o','r','s',')',' ','a','r','e',' ','r','e','c','o','g','n','i','z','e','d',' ','b','u','t',' ','i','g','n','o','r','e','d','.',0,
- '#',' ','B','e','l','o','w',',',' ','t','h','e','r','e',' ','s','h','o','u','l','d',' ','b','e',' ','o','n','e',' ','T','E','R','M',' ','e','n','t','r','y',' ','f','o','r',' ','e','a','c','h',' ','t','e','r','m','t','y','p','e',' ','t','h','a','t',' ','i','s',' ','c','o','l','o','r','i','z','a','b','l','e',0,
+ '#',' ','B','e','l','o','w',' ','a','r','e',' ','T','E','R','M',' ','e','n','t','r','i','e','s',',',' ','w','h','i','c','h',' ','c','a','n',' ','b','e',' ','a',' ','g','l','o','b',' ','p','a','t','t','e','r','n','s',',',' ','t','o',' ','m','a','t','c','h',0,
+ '#',' ','a','g','a','i','n','s','t',' ','t','h','e',' ','T','E','R','M',' ','e','n','v','i','r','o','n','m','e','n','t',' ','v','a','r','i','a','b','l','e',' ','t','o',' ','d','e','t','e','r','m','i','n','e',' ','i','f',' ','i','t',' ','i','s',' ','c','o','l','o','r','i','z','a','b','l','e','.',0,
'T','E','R','M',' ','E','t','e','r','m',0,
'T','E','R','M',' ','a','n','s','i',0,
'T','E','R','M',' ','c','o','l','o','r','-','x','t','e','r','m',0,
- 'T','E','R','M',' ','c','o','n','1','3','2','x','2','5',0,
- 'T','E','R','M',' ','c','o','n','1','3','2','x','3','0',0,
- 'T','E','R','M',' ','c','o','n','1','3','2','x','4','3',0,
- 'T','E','R','M',' ','c','o','n','1','3','2','x','6','0',0,
- 'T','E','R','M',' ','c','o','n','8','0','x','2','5',0,
- 'T','E','R','M',' ','c','o','n','8','0','x','2','8',0,
- 'T','E','R','M',' ','c','o','n','8','0','x','3','0',0,
- 'T','E','R','M',' ','c','o','n','8','0','x','4','3',0,
- 'T','E','R','M',' ','c','o','n','8','0','x','5','0',0,
- 'T','E','R','M',' ','c','o','n','8','0','x','6','0',0,
+ 'T','E','R','M',' ','c','o','n','[','0','-','9',']','*','x','[','0','-','9',']','*',0,
'T','E','R','M',' ','c','o','n','s','2','5',0,
'T','E','R','M',' ','c','o','n','s','o','l','e',0,
'T','E','R','M',' ','c','y','g','w','i','n',0,
'T','E','R','M',' ','d','t','t','e','r','m',0,
+ 'T','E','R','M',' ','e','t','e','r','m','-','c','o','l','o','r',0,
'T','E','R','M',' ','g','n','o','m','e',0,
+ 'T','E','R','M',' ','g','n','o','m','e','-','2','5','6','c','o','l','o','r',0,
+ 'T','E','R','M',' ','h','u','r','d',0,
+ 'T','E','R','M',' ','j','f','b','t','e','r','m',0,
'T','E','R','M',' ','k','o','n','s','o','l','e',0,
'T','E','R','M',' ','k','t','e','r','m',0,
'T','E','R','M',' ','l','i','n','u','x',0,
'T','E','R','M',' ','l','i','n','u','x','-','c',0,
'T','E','R','M',' ','m','a','c','h','-','c','o','l','o','r',0,
+ 'T','E','R','M',' ','m','a','c','h','-','g','n','u','-','c','o','l','o','r',0,
'T','E','R','M',' ','m','l','t','e','r','m',0,
'T','E','R','M',' ','p','u','t','t','y',0,
- 'T','E','R','M',' ','r','x','v','t',0,
- 'T','E','R','M',' ','r','x','v','t','-','c','y','g','w','i','n',0,
- 'T','E','R','M',' ','r','x','v','t','-','c','y','g','w','i','n','-','n','a','t','i','v','e',0,
- 'T','E','R','M',' ','r','x','v','t','-','u','n','i','c','o','d','e',0,
- 'T','E','R','M',' ','s','c','r','e','e','n',0,
- 'T','E','R','M',' ','s','c','r','e','e','n','-','b','c','e',0,
- 'T','E','R','M',' ','s','c','r','e','e','n','-','w',0,
- 'T','E','R','M',' ','s','c','r','e','e','n','.','l','i','n','u','x',0,
+ 'T','E','R','M',' ','p','u','t','t','y','-','2','5','6','c','o','l','o','r',0,
+ 'T','E','R','M',' ','r','x','v','t','*',0,
+ 'T','E','R','M',' ','s','c','r','e','e','n','*',0,
+ 'T','E','R','M',' ','s','t',0,
+ 'T','E','R','M',' ','s','t','-','2','5','6','c','o','l','o','r',0,
+ 'T','E','R','M',' ','t','e','r','m','i','n','a','t','o','r',0,
+ 'T','E','R','M',' ','t','m','u','x','*',0,
'T','E','R','M',' ','v','t','1','0','0',0,
- 'T','E','R','M',' ','x','t','e','r','m',0,
- 'T','E','R','M',' ','x','t','e','r','m','-','2','5','6','c','o','l','o','r',0,
- 'T','E','R','M',' ','x','t','e','r','m','-','c','o','l','o','r',0,
- 'T','E','R','M',' ','x','t','e','r','m','-','d','e','b','i','a','n',0,
+ 'T','E','R','M',' ','x','t','e','r','m','*',0,
'#',' ','B','e','l','o','w',' ','a','r','e',' ','t','h','e',' ','c','o','l','o','r',' ','i','n','i','t',' ','s','t','r','i','n','g','s',' ','f','o','r',' ','t','h','e',' ','b','a','s','i','c',' ','f','i','l','e',' ','t','y','p','e','s','.',' ','A',' ','c','o','l','o','r',' ','i','n','i','t',0,
'#',' ','s','t','r','i','n','g',' ','c','o','n','s','i','s','t','s',' ','o','f',' ','o','n','e',' ','o','r',' ','m','o','r','e',' ','o','f',' ','t','h','e',' ','f','o','l','l','o','w','i','n','g',' ','n','u','m','e','r','i','c',' ','c','o','d','e','s',':',0,
'#',' ','A','t','t','r','i','b','u','t','e',' ','c','o','d','e','s',':',0,
@@ -55,19 +47,23 @@ static char const G_line[] =
'#',' ','3','0','=','b','l','a','c','k',' ','3','1','=','r','e','d',' ','3','2','=','g','r','e','e','n',' ','3','3','=','y','e','l','l','o','w',' ','3','4','=','b','l','u','e',' ','3','5','=','m','a','g','e','n','t','a',' ','3','6','=','c','y','a','n',' ','3','7','=','w','h','i','t','e',0,
'#',' ','B','a','c','k','g','r','o','u','n','d',' ','c','o','l','o','r',' ','c','o','d','e','s',':',0,
'#',' ','4','0','=','b','l','a','c','k',' ','4','1','=','r','e','d',' ','4','2','=','g','r','e','e','n',' ','4','3','=','y','e','l','l','o','w',' ','4','4','=','b','l','u','e',' ','4','5','=','m','a','g','e','n','t','a',' ','4','6','=','c','y','a','n',' ','4','7','=','w','h','i','t','e',0,
- 'N','O','R','M','A','L',' ','0','0',' ','#',' ','g','l','o','b','a','l',' ','d','e','f','a','u','l','t',',',' ','a','l','t','h','o','u','g','h',' ','e','v','e','r','y','t','h','i','n','g',' ','s','h','o','u','l','d',' ','b','e',' ','s','o','m','e','t','h','i','n','g','.',0,
- 'F','I','L','E',' ','0','0',' ','#',' ','n','o','r','m','a','l',' ','f','i','l','e',0,
+ '#','N','O','R','M','A','L',' ','0','0',' ','#',' ','n','o',' ','c','o','l','o','r',' ','c','o','d','e',' ','a','t',' ','a','l','l',0,
+ '#','F','I','L','E',' ','0','0',' ','#',' ','r','e','g','u','l','a','r',' ','f','i','l','e',':',' ','u','s','e',' ','n','o',' ','c','o','l','o','r',' ','a','t',' ','a','l','l',0,
+ 'R','E','S','E','T',' ','0',' ','#',' ','r','e','s','e','t',' ','t','o',' ','"','n','o','r','m','a','l','"',' ','c','o','l','o','r',0,
'D','I','R',' ','0','1',';','3','4',' ','#',' ','d','i','r','e','c','t','o','r','y',0,
'L','I','N','K',' ','0','1',';','3','6',' ','#',' ','s','y','m','b','o','l','i','c',' ','l','i','n','k','.',' ','(','I','f',' ','y','o','u',' ','s','e','t',' ','t','h','i','s',' ','t','o',' ','\'','t','a','r','g','e','t','\'',' ','i','n','s','t','e','a','d',' ','o','f',' ','a',0,
' ','#',' ','n','u','m','e','r','i','c','a','l',' ','v','a','l','u','e',',',' ','t','h','e',' ','c','o','l','o','r',' ','i','s',' ','a','s',' ','f','o','r',' ','t','h','e',' ','f','i','l','e',' ','p','o','i','n','t','e','d',' ','t','o','.',')',0,
+ 'M','U','L','T','I','H','A','R','D','L','I','N','K',' ','0','0',' ','#',' ','r','e','g','u','l','a','r',' ','f','i','l','e',' ','w','i','t','h',' ','m','o','r','e',' ','t','h','a','n',' ','o','n','e',' ','l','i','n','k',0,
'F','I','F','O',' ','4','0',';','3','3',' ','#',' ','p','i','p','e',0,
'S','O','C','K',' ','0','1',';','3','5',' ','#',' ','s','o','c','k','e','t',0,
'D','O','O','R',' ','0','1',';','3','5',' ','#',' ','d','o','o','r',0,
'B','L','K',' ','4','0',';','3','3',';','0','1',' ','#',' ','b','l','o','c','k',' ','d','e','v','i','c','e',' ','d','r','i','v','e','r',0,
'C','H','R',' ','4','0',';','3','3',';','0','1',' ','#',' ','c','h','a','r','a','c','t','e','r',' ','d','e','v','i','c','e',' ','d','r','i','v','e','r',0,
- 'O','R','P','H','A','N',' ','4','0',';','3','1',';','0','1',' ','#',' ','s','y','m','l','i','n','k',' ','t','o',' ','n','o','n','e','x','i','s','t','e','n','t',' ','f','i','l','e',',',' ','o','r',' ','n','o','n','-','s','t','a','t','\'','a','b','l','e',' ','f','i','l','e',0,
+ 'O','R','P','H','A','N',' ','4','0',';','3','1',';','0','1',' ','#',' ','s','y','m','l','i','n','k',' ','t','o',' ','n','o','n','e','x','i','s','t','e','n','t',' ','f','i','l','e',',',' ','o','r',' ','n','o','n','-','s','t','a','t','\'','a','b','l','e',' ','f','i','l','e',' ','.','.','.',0,
+ 'M','I','S','S','I','N','G',' ','0','0',' ','#',' ','.','.','.',' ','a','n','d',' ','t','h','e',' ','f','i','l','e','s',' ','t','h','e','y',' ','p','o','i','n','t',' ','t','o',0,
'S','E','T','U','I','D',' ','3','7',';','4','1',' ','#',' ','f','i','l','e',' ','t','h','a','t',' ','i','s',' ','s','e','t','u','i','d',' ','(','u','+','s',')',0,
'S','E','T','G','I','D',' ','3','0',';','4','3',' ','#',' ','f','i','l','e',' ','t','h','a','t',' ','i','s',' ','s','e','t','g','i','d',' ','(','g','+','s',')',0,
+ 'C','A','P','A','B','I','L','I','T','Y',' ','3','0',';','4','1',' ','#',' ','f','i','l','e',' ','w','i','t','h',' ','c','a','p','a','b','i','l','i','t','y',0,
'S','T','I','C','K','Y','_','O','T','H','E','R','_','W','R','I','T','A','B','L','E',' ','3','0',';','4','2',' ','#',' ','d','i','r',' ','t','h','a','t',' ','i','s',' ','s','t','i','c','k','y',' ','a','n','d',' ','o','t','h','e','r','-','w','r','i','t','a','b','l','e',' ','(','+','t',',','o','+','w',')',0,
'O','T','H','E','R','_','W','R','I','T','A','B','L','E',' ','3','4',';','4','2',' ','#',' ','d','i','r',' ','t','h','a','t',' ','i','s',' ','o','t','h','e','r','-','w','r','i','t','a','b','l','e',' ','(','o','+','w',')',' ','a','n','d',' ','n','o','t',' ','s','t','i','c','k','y',0,
'S','T','I','C','K','Y',' ','3','7',';','4','4',' ','#',' ','d','i','r',' ','w','i','t','h',' ','t','h','e',' ','s','t','i','c','k','y',' ','b','i','t',' ','s','e','t',' ','(','+','t',')',' ','a','n','d',' ','n','o','t',' ','o','t','h','e','r','-','w','r','i','t','a','b','l','e',0,
@@ -89,26 +85,45 @@ static char const G_line[] =
' ','#',' ','a','r','c','h','i','v','e','s',' ','o','r',' ','c','o','m','p','r','e','s','s','e','d',' ','(','b','r','i','g','h','t',' ','r','e','d',')',0,
'.','t','a','r',' ','0','1',';','3','1',0,
'.','t','g','z',' ','0','1',';','3','1',0,
+ '.','a','r','c',' ','0','1',';','3','1',0,
'.','a','r','j',' ','0','1',';','3','1',0,
'.','t','a','z',' ','0','1',';','3','1',0,
+ '.','l','h','a',' ','0','1',';','3','1',0,
+ '.','l','z','4',' ','0','1',';','3','1',0,
'.','l','z','h',' ','0','1',';','3','1',0,
+ '.','l','z','m','a',' ','0','1',';','3','1',0,
+ '.','t','l','z',' ','0','1',';','3','1',0,
+ '.','t','x','z',' ','0','1',';','3','1',0,
+ '.','t','z','o',' ','0','1',';','3','1',0,
+ '.','t','7','z',' ','0','1',';','3','1',0,
'.','z','i','p',' ','0','1',';','3','1',0,
'.','z',' ','0','1',';','3','1',0,
'.','Z',' ','0','1',';','3','1',0,
+ '.','d','z',' ','0','1',';','3','1',0,
'.','g','z',' ','0','1',';','3','1',0,
+ '.','l','r','z',' ','0','1',';','3','1',0,
+ '.','l','z',' ','0','1',';','3','1',0,
+ '.','l','z','o',' ','0','1',';','3','1',0,
+ '.','x','z',' ','0','1',';','3','1',0,
'.','b','z','2',' ','0','1',';','3','1',0,
'.','b','z',' ','0','1',';','3','1',0,
+ '.','t','b','z',' ','0','1',';','3','1',0,
'.','t','b','z','2',' ','0','1',';','3','1',0,
'.','t','z',' ','0','1',';','3','1',0,
'.','d','e','b',' ','0','1',';','3','1',0,
'.','r','p','m',' ','0','1',';','3','1',0,
'.','j','a','r',' ','0','1',';','3','1',0,
+ '.','w','a','r',' ','0','1',';','3','1',0,
+ '.','e','a','r',' ','0','1',';','3','1',0,
+ '.','s','a','r',' ','0','1',';','3','1',0,
'.','r','a','r',' ','0','1',';','3','1',0,
+ '.','a','l','z',' ','0','1',';','3','1',0,
'.','a','c','e',' ','0','1',';','3','1',0,
'.','z','o','o',' ','0','1',';','3','1',0,
'.','c','p','i','o',' ','0','1',';','3','1',0,
'.','7','z',' ','0','1',';','3','1',0,
'.','r','z',' ','0','1',';','3','1',0,
+ '.','c','a','b',' ','0','1',';','3','1',0,
'#',' ','i','m','a','g','e',' ','f','o','r','m','a','t','s',0,
'.','j','p','g',' ','0','1',';','3','5',0,
'.','j','p','e','g',' ','0','1',';','3','5',0,
@@ -123,6 +138,8 @@ static char const G_line[] =
'.','t','i','f',' ','0','1',';','3','5',0,
'.','t','i','f','f',' ','0','1',';','3','5',0,
'.','p','n','g',' ','0','1',';','3','5',0,
+ '.','s','v','g',' ','0','1',';','3','5',0,
+ '.','s','v','g','z',' ','0','1',';','3','5',0,
'.','m','n','g',' ','0','1',';','3','5',0,
'.','p','c','x',' ','0','1',';','3','5',0,
'.','m','o','v',' ','0','1',';','3','5',0,
@@ -130,6 +147,7 @@ static char const G_line[] =
'.','m','p','e','g',' ','0','1',';','3','5',0,
'.','m','2','v',' ','0','1',';','3','5',0,
'.','m','k','v',' ','0','1',';','3','5',0,
+ '.','w','e','b','m',' ','0','1',';','3','5',0,
'.','o','g','m',' ','0','1',';','3','5',0,
'.','m','p','4',' ','0','1',';','3','5',0,
'.','m','4','v',' ','0','1',';','3','5',0,
@@ -144,15 +162,22 @@ static char const G_line[] =
'.','f','l','c',' ','0','1',';','3','5',0,
'.','a','v','i',' ','0','1',';','3','5',0,
'.','f','l','i',' ','0','1',';','3','5',0,
+ '.','f','l','v',' ','0','1',';','3','5',0,
'.','g','l',' ','0','1',';','3','5',0,
'.','d','l',' ','0','1',';','3','5',0,
'.','x','c','f',' ','0','1',';','3','5',0,
'.','x','w','d',' ','0','1',';','3','5',0,
'.','y','u','v',' ','0','1',';','3','5',0,
+ '.','c','g','m',' ','0','1',';','3','5',0,
+ '.','e','m','f',' ','0','1',';','3','5',0,
+ '#',' ','h','t','t','p',':','/','/','w','i','k','i','.','x','i','p','h','.','o','r','g','/','i','n','d','e','x','.','p','h','p','/','M','I','M','E','_','T','y','p','e','s','_','a','n','d','_','F','i','l','e','_','E','x','t','e','n','s','i','o','n','s',0,
+ '.','o','g','v',' ','0','1',';','3','5',0,
+ '.','o','g','x',' ','0','1',';','3','5',0,
'#',' ','a','u','d','i','o',' ','f','o','r','m','a','t','s',0,
'.','a','a','c',' ','0','0',';','3','6',0,
'.','a','u',' ','0','0',';','3','6',0,
'.','f','l','a','c',' ','0','0',';','3','6',0,
+ '.','m','4','a',' ','0','0',';','3','6',0,
'.','m','i','d',' ','0','0',';','3','6',0,
'.','m','i','d','i',' ','0','0',';','3','6',0,
'.','m','k','a',' ','0','0',';','3','6',0,
@@ -161,4 +186,9 @@ static char const G_line[] =
'.','o','g','g',' ','0','0',';','3','6',0,
'.','r','a',' ','0','0',';','3','6',0,
'.','w','a','v',' ','0','0',';','3','6',0,
+ '#',' ','h','t','t','p',':','/','/','w','i','k','i','.','x','i','p','h','.','o','r','g','/','i','n','d','e','x','.','p','h','p','/','M','I','M','E','_','T','y','p','e','s','_','a','n','d','_','F','i','l','e','_','E','x','t','e','n','s','i','o','n','s',0,
+ '.','o','g','a',' ','0','0',';','3','6',0,
+ '.','o','p','u','s',' ','0','0',';','3','6',0,
+ '.','s','p','x',' ','0','0',';','3','6',0,
+ '.','x','s','p','f',' ','0','0',';','3','6',0,
};
diff --git a/src/dircolors.hin b/src/dircolors.hin
index 8d550d1..d2ea453 100644
--- a/src/dircolors.hin
+++ b/src/dircolors.hin
@@ -1,53 +1,45 @@
# 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, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
-# Free Software Foundation, Inc.
+# Copyright (C) 1996-2016 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.
# The keywords COLOR, OPTIONS, and EIGHTBIT (honored by the
# slackware version of dircolors) are recognized but ignored.
-# Below, there should be one TERM entry for each termtype that is colorizable
+# Below are TERM entries, which can be a glob patterns, to match
+# against the TERM environment variable to determine if it is colorizable.
TERM Eterm
TERM ansi
TERM color-xterm
-TERM con132x25
-TERM con132x30
-TERM con132x43
-TERM con132x60
-TERM con80x25
-TERM con80x28
-TERM con80x30
-TERM con80x43
-TERM con80x50
-TERM con80x60
+TERM con[0-9]*x[0-9]*
TERM cons25
TERM console
TERM cygwin
TERM dtterm
+TERM eterm-color
TERM gnome
+TERM gnome-256color
+TERM hurd
+TERM jfbterm
TERM konsole
TERM kterm
TERM linux
TERM linux-c
TERM mach-color
+TERM mach-gnu-color
TERM mlterm
TERM putty
-TERM rxvt
-TERM rxvt-cygwin
-TERM rxvt-cygwin-native
-TERM rxvt-unicode
-TERM screen
-TERM screen-bce
-TERM screen-w
-TERM screen.linux
+TERM putty-256color
+TERM rxvt*
+TERM screen*
+TERM st
+TERM st-256color
+TERM terminator
+TERM tmux*
TERM vt100
-TERM xterm
-TERM xterm-256color
-TERM xterm-color
-TERM xterm-debian
+TERM xterm*
# Below are the color init strings for the basic file types. A color init
# string consists of one or more of the following numeric codes:
@@ -57,19 +49,23 @@ TERM xterm-debian
# 30=black 31=red 32=green 33=yellow 34=blue 35=magenta 36=cyan 37=white
# Background color codes:
# 40=black 41=red 42=green 43=yellow 44=blue 45=magenta 46=cyan 47=white
-NORMAL 00 # global default, although everything should be something.
-FILE 00 # normal file
+#NORMAL 00 # no color code at all
+#FILE 00 # regular file: use no color at all
+RESET 0 # reset to "normal" color
DIR 01;34 # directory
LINK 01;36 # symbolic link. (If you set this to 'target' instead of a
- # numerical value, the color is as for the file pointed to.)
+ # numerical value, the color is as for the file pointed to.)
+MULTIHARDLINK 00 # regular file with more than one link
FIFO 40;33 # pipe
SOCK 01;35 # socket
DOOR 01;35 # door
BLK 40;33;01 # block device driver
CHR 40;33;01 # character device driver
-ORPHAN 40;31;01 # symlink to nonexistent file, or non-stat'able file
+ORPHAN 40;31;01 # symlink to nonexistent file, or non-stat'able file ...
+MISSING 00 # ... and the files they point to
SETUID 37;41 # file that is setuid (u+s)
SETGID 30;43 # file that is setgid (g+s)
+CAPABILITY 30;41 # file with capability
STICKY_OTHER_WRITABLE 30;42 # dir that is sticky and other-writable (+t,o+w)
OTHER_WRITABLE 34;42 # dir that is other-writable (o+w) and not sticky
STICKY 37;44 # dir with the sticky bit set (+t) and not other-writable
@@ -95,26 +91,45 @@ EXEC 01;32
# archives or compressed (bright red)
.tar 01;31
.tgz 01;31
+.arc 01;31
.arj 01;31
.taz 01;31
+.lha 01;31
+.lz4 01;31
.lzh 01;31
+.lzma 01;31
+.tlz 01;31
+.txz 01;31
+.tzo 01;31
+.t7z 01;31
.zip 01;31
.z 01;31
.Z 01;31
+.dz 01;31
.gz 01;31
+.lrz 01;31
+.lz 01;31
+.lzo 01;31
+.xz 01;31
.bz2 01;31
.bz 01;31
+.tbz 01;31
.tbz2 01;31
.tz 01;31
.deb 01;31
.rpm 01;31
.jar 01;31
+.war 01;31
+.ear 01;31
+.sar 01;31
.rar 01;31
+.alz 01;31
.ace 01;31
.zoo 01;31
.cpio 01;31
.7z 01;31
.rz 01;31
+.cab 01;31
# image formats
.jpg 01;35
@@ -130,6 +145,8 @@ EXEC 01;32
.tif 01;35
.tiff 01;35
.png 01;35
+.svg 01;35
+.svgz 01;35
.mng 01;35
.pcx 01;35
.mov 01;35
@@ -137,6 +154,7 @@ EXEC 01;32
.mpeg 01;35
.m2v 01;35
.mkv 01;35
+.webm 01;35
.ogm 01;35
.mp4 01;35
.m4v 01;35
@@ -151,16 +169,24 @@ EXEC 01;32
.flc 01;35
.avi 01;35
.fli 01;35
+.flv 01;35
.gl 01;35
.dl 01;35
.xcf 01;35
.xwd 01;35
.yuv 01;35
+.cgm 01;35
+.emf 01;35
+
+# http://wiki.xiph.org/index.php/MIME_Types_and_File_Extensions
+.ogv 01;35
+.ogx 01;35
# audio formats
.aac 00;36
.au 00;36
.flac 00;36
+.m4a 00;36
.mid 00;36
.midi 00;36
.mka 00;36
@@ -169,3 +195,9 @@ EXEC 01;32
.ogg 00;36
.ra 00;36
.wav 00;36
+
+# http://wiki.xiph.org/index.php/MIME_Types_and_File_Extensions
+.oga 00;36
+.opus 00;36
+.spx 00;36
+.xspf 00;36
diff --git a/src/dirname.c b/src/dirname.c
index 2253391..d3d130d 100644
--- a/src/dirname.c
+++ b/src/dirname.c
@@ -1,12 +1,11 @@
/* dirname -- strip suffix from file name
- Copyright (C) 1990-1997, 1999-2002, 2004, 2005, 2006 Free Software
- Foundation, Inc.
+ Copyright (C) 1990-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -14,8 +13,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by David MacKenzie and Jim Meyering. */
@@ -25,46 +23,53 @@
#include <sys/types.h>
#include "system.h"
-#include "long-options.h"
#include "error.h"
-#include "quote.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "dirname"
-#define AUTHORS "David MacKenzie", "Jim Meyering"
+#define AUTHORS \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Jim Meyering")
-/* The name this program was run with. */
-char *program_name;
+static struct option const longopts[] =
+{
+ {"zero", no_argument, NULL, 'z'},
+ {GETOPT_HELP_OPTION_DECL},
+ {GETOPT_VERSION_OPTION_DECL},
+ {NULL, 0, NULL, 0}
+};
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
-Usage: %s NAME\n\
- or: %s OPTION\n\
+Usage: %s [OPTION] NAME...\n\
"),
- program_name, program_name);
+ program_name);
fputs (_("\
-Print NAME with its trailing /component removed; if NAME contains no /'s,\n\
-output `.' (meaning the current directory).\n\
+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);
printf (_("\
\n\
Examples:\n\
- %s /usr/bin/sort Output \"/usr/bin\".\n\
- %s stdio.h Output \".\".\n\
+ %s /usr/bin/ -> \"/usr\"\n\
+ %s dir1/str dir2/str -> \"dir1\" followed by \"dir2\"\n\
+ %s stdio.h -> \".\"\n\
"),
- program_name, program_name);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ program_name, program_name, program_name);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -73,21 +78,38 @@ int
main (int argc, char **argv)
{
static char const dot = '.';
+ bool use_nuls = false;
char const *result;
size_t len;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
- if (getopt_long (argc, argv, "+", NULL, NULL) != -1)
- usage (EXIT_FAILURE);
+ while (true)
+ {
+ int c = getopt_long (argc, argv, "z", longopts, NULL);
+
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 'z':
+ use_nuls = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
+ }
if (argc < optind + 1)
{
@@ -95,23 +117,20 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
- if (optind + 1 < argc)
+ for (; optind < argc; optind++)
{
- error (0, 0, _("extra operand %s"), quote (argv[optind + 1]));
- usage (EXIT_FAILURE);
- }
+ result = argv[optind];
+ len = dir_len (result);
- result = argv[optind];
- len = dir_len (result);
+ if (! len)
+ {
+ result = &dot;
+ len = 1;
+ }
- if (! len)
- {
- result = &dot;
- len = 1;
+ fwrite (result, 1, len, stdout);
+ putchar (use_nuls ? '\0' :'\n');
}
- fwrite (result, 1, len, stdout);
- putchar ('\n');
-
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/du.c b/src/du.c
index 206d318..45c3703 100644
--- a/src/du.c
+++ b/src/du.c
@@ -1,10 +1,10 @@
/* du -- summarize disk usage
- Copyright (C) 1988-1991, 1995-2007 Free Software Foundation, Inc.
+ Copyright (C) 1988-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Differences from the Unix du:
* Doesn't simply ignore the names of regular files given as arguments
@@ -25,64 +24,62 @@
Rewritten to use nftw, then to use fts by Jim Meyering. */
#include <config.h>
-#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#include <assert.h>
#include "system.h"
#include "argmatch.h"
+#include "argv-iter.h"
+#include "di-set.h"
#include "error.h"
#include "exclude.h"
#include "fprintftime.h"
-#include "hash.h"
#include "human.h"
-#include "inttostr.h"
+#include "mountlist.h"
#include "quote.h"
-#include "quotearg.h"
-#include "readtokens0.h"
-#include "same.h"
+#include "stat-size.h"
#include "stat-time.h"
+#include "stdio--.h"
#include "xfts.h"
#include "xstrtol.h"
extern bool fts_debug;
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "du"
#define AUTHORS \
- "Torbjorn Granlund", "David MacKenzie, Paul Eggert", "Jim Meyering"
+ proper_name_utf8 ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Paul Eggert"), \
+ proper_name ("Jim Meyering")
#if DU_DEBUG
# define FTS_CROSS_CHECK(Fts) fts_cross_check (Fts)
-# define DEBUG_OPT "d"
#else
# define FTS_CROSS_CHECK(Fts)
-# define DEBUG_OPT
#endif
-/* Initial size of the hash table. */
-#define INITIAL_TABLE_SIZE 103
+/* A set of dev/ino pairs to help identify files and directories
+ whose sizes have already been counted. */
+static struct di_set *di_files;
-/* Hash structure for inode and device numbers. The separate entry
- structure makes it easier to rehash "in place". */
+/* A set containing a dev/ino pair for each local mount point directory. */
+static struct di_set *di_mnt;
-struct entry
-{
- ino_t st_ino;
- dev_t st_dev;
-};
-
-/* A set of dev/ino pairs. */
-static Hash_table *htab;
+/* Keep track of the preceding "level" (depth in hierarchy)
+ from one call of process_file to the next. */
+static size_t prev_level;
/* Define a class for collecting directory information. */
-
struct duinfo
{
/* Size of files in directory. */
uintmax_t size;
+ /* Number of inodes in directory. */
+ uintmax_t inodes;
+
/* Latest time stamp found. If tmax.tv_sec == TYPE_MINIMUM (time_t)
&& tmax.tv_nsec < 0, no time stamp has been found. */
struct timespec tmax;
@@ -93,6 +90,7 @@ static inline void
duinfo_init (struct duinfo *a)
{
a->size = 0;
+ a->inodes = 0;
a->tmax.tv_sec = TYPE_MINIMUM (time_t);
a->tmax.tv_nsec = -1;
}
@@ -102,6 +100,7 @@ static inline void
duinfo_set (struct duinfo *a, uintmax_t size, struct timespec tmax)
{
a->size = size;
+ a->inodes = 1;
a->tmax = tmax;
}
@@ -109,7 +108,9 @@ duinfo_set (struct duinfo *a, uintmax_t size, struct timespec tmax)
static inline void
duinfo_add (struct duinfo *a, struct duinfo const *b)
{
- a->size += b->size;
+ uintmax_t sum = a->size + b->size;
+ a->size = a->size <= sum ? sum : UINTMAX_MAX;
+ a->inodes = a->inodes + b->inodes;
if (timespec_cmp (a->tmax, b->tmax) < 0)
a->tmax = b->tmax;
}
@@ -124,9 +125,6 @@ struct dulevel
struct duinfo subdir;
};
-/* Name under which this program was invoked. */
-char *program_name;
-
/* If true, display counts for all files, not just directories. */
static bool opt_all = false;
@@ -137,6 +135,9 @@ static bool apparent_size = false;
/* If true, count each hard link of files with multiple links. */
static bool opt_count_all = false;
+/* If true, hash all files to look for hard links. */
+static bool hash_all;
+
/* If true, output the NUL byte instead of a newline at the end of each line. */
static bool opt_nul_terminate_output = false;
@@ -148,12 +149,19 @@ static bool opt_separate_dirs = false;
/* Show the total for each directory (and file if --all) that is at
most MAX_DEPTH levels down from the root of the hierarchy. The root
- is at level 0, so `du --max-depth=0' is equivalent to `du -s'. */
+ is at level 0, so 'du --max-depth=0' is equivalent to 'du -s'. */
static size_t max_depth = SIZE_MAX;
+/* Only output entries with at least this SIZE if positive,
+ or at most if negative. See --threshold option. */
+static intmax_t opt_threshold = 0;
+
/* Human-readable options for output. */
static int human_output_opts;
+/* Output inodes count instead of blocks used. */
+static bool opt_inodes = false;
+
/* If true, print most recently modified date, using the specified format. */
static bool opt_time = false;
@@ -195,17 +203,10 @@ enum
EXCLUDE_OPTION,
FILES0_FROM_OPTION,
HUMAN_SI_OPTION,
-
- /* FIXME: --kilobytes is deprecated (but not -k); remove in late 2006 */
- KILOBYTES_LONG_OPTION,
-
- MAX_DEPTH_OPTION,
-
- /* FIXME: --megabytes is deprecated (but not -m); remove in late 2006 */
- MEGABYTES_LONG_OPTION,
-
+ FTS_DEBUG,
TIME_OPTION,
- TIME_STYLE_OPTION
+ TIME_STYLE_OPTION,
+ INODES_OPTION
};
static struct option const long_options[] =
@@ -215,22 +216,23 @@ static struct option const long_options[] =
{"block-size", required_argument, NULL, 'B'},
{"bytes", no_argument, NULL, 'b'},
{"count-links", no_argument, NULL, 'l'},
+ /* {"-debug", no_argument, NULL, FTS_DEBUG}, */
{"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},
- {"kilobytes", no_argument, NULL, KILOBYTES_LONG_OPTION},
- {"max-depth", required_argument, NULL, MAX_DEPTH_OPTION},
+ {"max-depth", required_argument, NULL, 'd'},
{"null", no_argument, NULL, '0'},
- {"megabytes", no_argument, NULL, MEGABYTES_LONG_OPTION},
{"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},
@@ -248,8 +250,8 @@ static enum time_type const time_types[] =
};
ARGMATCH_VERIFY (time_args, time_types);
-/* `full-iso' uses full ISO-style dates and times. `long-iso' uses longer
- ISO-style time stamps, though shorter than `full-iso'. `iso' uses shorter
+/* 'full-iso' uses full ISO-style dates and times. 'long-iso' uses longer
+ ISO-style time stamps, though shorter than 'full-iso'. 'iso' uses shorter
ISO-style time stamps. */
enum time_style
{
@@ -272,8 +274,7 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
@@ -281,134 +282,89 @@ Usage: %s [OPTION]... [FILE]...\n\
or: %s [OPTION]... --files0-from=F\n\
"), program_name, program_name);
fputs (_("\
-Summarize disk usage of each FILE, recursively for directories.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
+Summarize disk usage of the set of FILEs, recursively for directories.\n\
"), stdout);
+
+ emit_mandatory_arg_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 disk usage; although\n\
+ --apparent-size print apparent sizes, rather than disk usage; although\
+\n\
the apparent size is usually smaller, it may be\n\
- larger due to holes in (`sparse') files, internal\n\
+ larger due to holes in ('sparse') files, internal\n\
fragmentation, indirect blocks, and the like\n\
"), stdout);
fputs (_("\
- -B, --block-size=SIZE use SIZE-byte blocks\n\
- -b, --bytes equivalent to `--apparent-size --block-size=1'\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\
+ -b, --bytes equivalent to '--apparent-size --block-size=1'\n\
-c, --total produce a grand total\n\
- -D, --dereference-args dereference FILEs that are symbolic links\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 disk usage of the NUL-terminated file\n\
- names specified in file F\n\
- -H like --si, but also evokes a warning; will soon\n\
- change to be equivalent to --dereference-args (-D)\n\
- -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G)\n\
- --si like -h, but use powers of 1000 not 1024\n\
+ --files0-from=F summarize disk 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\
"), stdout);
fputs (_("\
- -L, --dereference dereference all symbolic links\n\
-P, --no-dereference don't follow any symbolic links (this is the default)\n\
- -0, --null end each output line with 0 byte rather than newline\n\
- -S, --separate-dirs do not include size of subdirectories\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 (_("\
- -x, --one-file-system skip directories on different file systems\n\
- -X FILE, --exclude-from=FILE Exclude files that match any pattern in FILE.\n\
- --exclude=PATTERN Exclude files that match PATTERN.\n\
- --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 (_("\
+ -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 STYLE:\n\
- full-iso, long-iso, iso, +FORMAT\n\
- FORMAT is interpreted like `date'\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);
- fputs (_("\n\
-SIZE may be (or may be an integer optionally followed by) one of following:\n\
-kB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
-"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_blocksize_note ("DU");
+ emit_size_note ();
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
-static size_t
-entry_hash (void const *x, size_t table_size)
-{
- struct entry const *p = x;
-
- /* 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;
-}
-
-/* Compare two dev/ino pairs. Return true if they are the same. */
-static bool
-entry_compare (void const *x, void const *y)
-{
- struct entry const *a = x;
- struct entry const *b = y;
- return SAME_INODE (*a, *b) ? true : false;
-}
-
-/* Try to insert the INO/DEV pair into the global table, HTAB.
+/* Try to insert the INO/DEV pair into DI_SET.
Return true if the pair is successfully inserted,
- false if the pair is already in the table. */
+ false if the pair was already there. */
static bool
-hash_ins (ino_t ino, dev_t dev)
+hash_ins (struct di_set *di_set, ino_t ino, dev_t dev)
{
- struct entry *ent;
- struct entry *ent_from_table;
-
- ent = xmalloc (sizeof *ent);
- ent->st_ino = ino;
- ent->st_dev = dev;
-
- ent_from_table = hash_insert (htab, ent);
- if (ent_from_table == NULL)
- {
- /* Insertion failed due to lack of memory. */
- xalloc_die ();
- }
-
- if (ent_from_table == ent)
- {
- /* Insertion succeeded. */
- return true;
- }
-
- /* That pair is already in the table, so ENT was not inserted. Free it. */
- free (ent);
-
- return false;
-}
-
-/* Initialize the hash table. */
-static void
-hash_init (void)
-{
- htab = hash_initialize (INITIAL_TABLE_SIZE, NULL,
- entry_hash, entry_compare, free);
- if (htab == NULL)
+ int inserted = di_set_insert (di_set, dev, ino);
+ if (inserted < 0)
xalloc_die ();
+ return inserted;
}
/* FIXME: this code is nearly identical to code in date.c */
@@ -422,11 +378,9 @@ show_date (const char *format, struct timespec when)
if (! tm)
{
char buf[INT_BUFSIZE_BOUND (intmax_t)];
- error (0, 0, _("time %s is out of range"),
- (TYPE_SIGNED (time_t)
- ? imaxtostr (when.tv_sec, buf)
- : umaxtostr (when.tv_sec, buf)));
- fputs (buf, stdout);
+ char *when_str = timetostr (when.tv_sec, buf);
+ error (0, 0, _("time %s is out of range"), quote (when_str));
+ fputs (when_str, stdout);
return;
}
@@ -439,8 +393,11 @@ static void
print_only_size (uintmax_t n_bytes)
{
char buf[LONGEST_HUMAN_READABLE + 1];
- fputs (human_readable (n_bytes, buf, human_output_opts,
- 1, output_block_size), stdout);
+ fputs ((n_bytes == UINTMAX_MAX
+ ? _("Infinity")
+ : human_readable (n_bytes, buf, human_output_opts,
+ 1, output_block_size)),
+ stdout);
}
/* Print size (and optionally time) indicated by *PDUI, followed by STRING. */
@@ -448,7 +405,10 @@ print_only_size (uintmax_t n_bytes)
static void
print_size (const struct duinfo *pdui, const char *string)
{
- print_only_size (pdui->size);
+ print_only_size (opt_inodes
+ ? pdui->inodes
+ : pdui->size);
+
if (opt_time)
{
putchar ('\t');
@@ -458,6 +418,64 @@ print_size (const struct duinfo *pdui, const char *string)
fflush (stdout);
}
+/* Fill the di_mnt set with local mount point dev/ino pairs. */
+
+static void
+fill_mount_table (void)
+{
+ struct mount_entry *mnt_ent = read_file_system_list (false);
+ while (mnt_ent)
+ {
+ struct mount_entry *mnt_free;
+ if (!mnt_ent->me_remote && !mnt_ent->me_dummy)
+ {
+ struct stat buf;
+ if (!stat (mnt_ent->me_mountdir, &buf))
+ hash_ins (di_mnt, buf.st_ino, buf.st_dev);
+ else
+ {
+ /* Ignore stat failure. False positives are too common.
+ E.g., "Permission denied" on /run/user/<name>/gvfs. */
+ }
+ }
+
+ mnt_free = mnt_ent;
+ mnt_ent = mnt_ent->me_next;
+ free_mount_entry (mnt_free);
+ }
+}
+
+/* This function checks whether any of the directories in the cycle that
+ fts detected is a mount point. */
+
+static bool
+mount_point_in_fts_cycle (FTSENT const *ent)
+{
+ FTSENT const *cycle_ent = ent->fts_cycle;
+
+ if (!di_mnt)
+ {
+ /* Initialize the set of dev,inode pairs. */
+ di_mnt = di_set_alloc ();
+ if (!di_mnt)
+ xalloc_die ();
+
+ fill_mount_table ();
+ }
+
+ while (ent && ent != cycle_ent)
+ {
+ if (di_set_lookup (di_mnt, ent->fts_statp->st_dev,
+ ent->fts_statp->st_ino) > 0)
+ {
+ return true;
+ }
+ ent = ent->fts_parent;
+ }
+
+ return false;
+}
+
/* This function is called once for every file system object that fts
encounters. fts does a depth-first traversal. This function knows
that and accumulates per-directory totals based on changes in
@@ -466,11 +484,10 @@ print_size (const struct duinfo *pdui, const char *string)
static bool
process_file (FTS *fts, FTSENT *ent)
{
- bool ok;
+ bool ok = true;
struct duinfo dui;
struct duinfo dui_to_print;
size_t level;
- static size_t prev_level;
static size_t n_alloc;
/* First element of the structure contains:
The sum of the st_size values of all entries in the single directory
@@ -481,71 +498,98 @@ process_file (FTS *fts, FTSENT *ent)
The sum of the sizes of all entries in the hierarchy at or below the
directory at the specified level. */
static struct dulevel *dulvl;
- bool print = true;
const char *file = ent->fts_path;
const struct stat *sb = ent->fts_statp;
- bool skip;
+ int info = ent->fts_info;
- /* If necessary, set FTS_SKIP before returning. */
- skip = excluded_file_name (exclude, ent->fts_path);
- if (skip)
- fts_set (fts, ent, FTS_SKIP);
-
- switch (ent->fts_info)
+ if (info == FTS_DNR)
{
- case FTS_NS:
- error (0, ent->fts_errno, _("cannot access %s"), quote (file));
- return false;
-
- case FTS_ERR:
- /* if (S_ISDIR (ent->fts_statp->st_mode) && FIXME */
- error (0, ent->fts_errno, _("%s"), quote (file));
- return false;
-
- case FTS_DNR:
- /* Don't return just yet, since although the directory is not readable,
- we were able to stat it, so we do have a size. */
- error (0, ent->fts_errno, _("cannot read directory %s"), quote (file));
+ /* An error occurred, but the size is known, so count it. */
+ error (0, ent->fts_errno, _("cannot read directory %s"), quoteaf (file));
ok = false;
- break;
-
- default:
- ok = true;
- break;
- }
-
- /* If this is the first (pre-order) encounter with a directory,
- or if it's the second encounter for a skipped directory, then
- return right away. */
- if (ent->fts_info == FTS_D || skip)
- return ok;
-
- /* If the file is being excluded or if it has already been counted
- via a hard link, then don't let it contribute to the sums. */
- if (skip
- || (!opt_count_all
- && ! S_ISDIR (sb->st_mode)
- && 1 < sb->st_nlink
- && ! hash_ins (sb->st_ino, sb->st_dev)))
- {
- /* Note that we must not simply return here.
- We still have to update prev_level and maybe propagate
- some sums up the hierarchy. */
- duinfo_init (&dui);
- print = false;
}
- else
+ else if (info != FTS_DP)
{
- duinfo_set (&dui,
- (apparent_size
- ? sb->st_size
- : (uintmax_t) ST_NBLOCKS (*sb) * ST_NBLOCKSIZE),
- (time_type == time_mtime ? get_stat_mtime (sb)
- : time_type == time_atime ? get_stat_atime (sb)
- : get_stat_ctime (sb)));
+ bool excluded = excluded_file_name (exclude, file);
+ if (! excluded)
+ {
+ /* Make the stat buffer *SB valid, or fail noisily. */
+
+ if (info == FTS_NSOK)
+ {
+ fts_set (fts, ent, FTS_AGAIN);
+ FTSENT const *e = fts_read (fts);
+ assert (e == ent);
+ info = ent->fts_info;
+ }
+
+ if (info == FTS_NS || info == FTS_SLNONE)
+ {
+ error (0, ent->fts_errno, _("cannot access %s"), quoteaf (file));
+ return false;
+ }
+
+ /* The --one-file-system (-x) option cannot exclude anything
+ specified on the command-line. By definition, it can exclude
+ a file or directory only when its device number is different
+ from that of its just-processed parent directory, and du does
+ not process the parent of a command-line argument. */
+ if (fts->fts_options & FTS_XDEV
+ && FTS_ROOTLEVEL < ent->fts_level
+ && fts->fts_dev != sb->st_dev)
+ excluded = true;
+ }
+
+ if (excluded
+ || (! opt_count_all
+ && (hash_all || (! S_ISDIR (sb->st_mode) && 1 < sb->st_nlink))
+ && ! hash_ins (di_files, sb->st_ino, sb->st_dev)))
+ {
+ /* If ignoring a directory in preorder, skip its children.
+ Ignore the next fts_read output too, as it's a postorder
+ visit to the same directory. */
+ if (info == FTS_D)
+ {
+ fts_set (fts, ent, FTS_SKIP);
+ FTSENT const *e = fts_read (fts);
+ assert (e == ent);
+ }
+
+ return true;
+ }
+
+ switch (info)
+ {
+ case FTS_D:
+ return true;
+
+ case FTS_ERR:
+ /* An error occurred, but the size is known, so count it. */
+ error (0, ent->fts_errno, "%s", quotef (file));
+ ok = false;
+ break;
+
+ case FTS_DC:
+ /* If not following symlinks and not a (bind) mount point. */
+ if (cycle_warning_required (fts, ent)
+ && ! mount_point_in_fts_cycle (ent))
+ {
+ emit_cycle_warning (file);
+ return false;
+ }
+ return true;
+ }
}
+ duinfo_set (&dui,
+ (apparent_size
+ ? MAX (0, sb->st_size)
+ : (uintmax_t) ST_NBLOCKS (*sb) * ST_NBLOCKSIZE),
+ (time_type == time_mtime ? get_stat_mtime (sb)
+ : time_type == time_atime ? get_stat_atime (sb)
+ : get_stat_ctime (sb)));
+
level = ent->fts_level;
dui_to_print = dui;
@@ -557,71 +601,68 @@ process_file (FTS *fts, FTSENT *ent)
else
{
if (level == prev_level)
- {
- /* This is usually the most common case. Do nothing. */
- }
+ {
+ /* This is usually the most common case. Do nothing. */
+ }
else if (level > prev_level)
- {
- /* Descending the hierarchy.
- Clear the accumulators for *all* levels between prev_level
- and the current one. The depth may change dramatically,
- e.g., from 1 to 10. */
- size_t i;
-
- if (n_alloc <= level)
- {
- dulvl = xnrealloc (dulvl, level, 2 * sizeof *dulvl);
- n_alloc = level * 2;
- }
-
- for (i = prev_level + 1; i <= level; i++)
- {
- duinfo_init (&dulvl[i].ent);
- duinfo_init (&dulvl[i].subdir);
- }
- }
+ {
+ /* Descending the hierarchy.
+ Clear the accumulators for *all* levels between prev_level
+ and the current one. The depth may change dramatically,
+ e.g., from 1 to 10. */
+ size_t i;
+
+ if (n_alloc <= level)
+ {
+ dulvl = xnrealloc (dulvl, level, 2 * sizeof *dulvl);
+ n_alloc = level * 2;
+ }
+
+ for (i = prev_level + 1; i <= level; i++)
+ {
+ duinfo_init (&dulvl[i].ent);
+ duinfo_init (&dulvl[i].subdir);
+ }
+ }
else /* level < prev_level */
- {
- /* Ascending the hierarchy.
- Process a directory only after all entries in that
- directory have been processed. When the depth decreases,
- propagate sums from the children (prev_level) to the parent.
- Here, the current level is always one smaller than the
- previous one. */
- assert (level == prev_level - 1);
- duinfo_add (&dui_to_print, &dulvl[prev_level].ent);
- if (!opt_separate_dirs)
- duinfo_add (&dui_to_print, &dulvl[prev_level].subdir);
- duinfo_add (&dulvl[level].subdir, &dulvl[prev_level].ent);
- duinfo_add (&dulvl[level].subdir, &dulvl[prev_level].subdir);
- }
+ {
+ /* Ascending the hierarchy.
+ Process a directory only after all entries in that
+ directory have been processed. When the depth decreases,
+ propagate sums from the children (prev_level) to the parent.
+ Here, the current level is always one smaller than the
+ previous one. */
+ assert (level == prev_level - 1);
+ duinfo_add (&dui_to_print, &dulvl[prev_level].ent);
+ if (!opt_separate_dirs)
+ duinfo_add (&dui_to_print, &dulvl[prev_level].subdir);
+ duinfo_add (&dulvl[level].subdir, &dulvl[prev_level].ent);
+ duinfo_add (&dulvl[level].subdir, &dulvl[prev_level].subdir);
+ }
}
prev_level = level;
/* Let the size of a directory entry contribute to the total for the
containing directory, unless --separate-dirs (-S) is specified. */
- if ( ! (opt_separate_dirs && IS_DIR_TYPE (ent->fts_info)))
+ if (! (opt_separate_dirs && IS_DIR_TYPE (info)))
duinfo_add (&dulvl[level].ent, &dui);
/* Even if this directory is unreadable or we can't chdir into it,
- do let its size contribute to the total, ... */
+ do let its size contribute to the total. */
duinfo_add (&tot_dui, &dui);
- /* ... but don't print out a total for it, since without the size(s)
- of any potential entries, it could be very misleading. */
- if (ent->fts_info == FTS_DNR)
- return ok;
-
- /* If we're not counting an entry, e.g., because it's a hard link
- to a file we've already counted (and --count-links), then don't
- print a line for it. */
- if (!print)
- return ok;
-
- if ((IS_DIR_TYPE (ent->fts_info) && level <= max_depth)
- || ((opt_all && level <= max_depth) || level == 0))
- print_size (&dui_to_print, file);
+ if ((IS_DIR_TYPE (info) && level <= max_depth)
+ || (opt_all && level <= max_depth)
+ || level == 0)
+ {
+ /* Print or elide this entry according to the --threshold option. */
+ uintmax_t v = opt_inodes ? dui_to_print.inodes : dui_to_print.size;
+ if (opt_threshold < 0
+ ? v <= -opt_threshold
+ : v >= opt_threshold)
+ print_size (&dui_to_print, file);
+ }
return ok;
}
@@ -641,33 +682,36 @@ du_files (char **files, int bit_flags)
FTS *fts = xfts_open (files, bit_flags, NULL);
while (1)
- {
- FTSENT *ent;
-
- ent = fts_read (fts);
- if (ent == NULL)
- {
- if (errno != 0)
- {
- /* FIXME: try to give a better message */
- error (0, errno, _("fts_read failed"));
- ok = false;
- }
- break;
- }
- FTS_CROSS_CHECK (fts);
-
- ok &= process_file (fts, ent);
- }
-
- /* Ignore failure, since the only way it can do so is in failing to
- return to the original directory, and since we're about to exit,
- that doesn't matter. */
- fts_close (fts);
- }
+ {
+ FTSENT *ent;
- if (print_grand_total)
- print_size (&tot_dui, _("total"));
+ ent = fts_read (fts);
+ if (ent == NULL)
+ {
+ if (errno != 0)
+ {
+ error (0, errno, _("fts_read failed: %s"),
+ quotef (fts->fts_path));
+ ok = false;
+ }
+
+ /* When exiting this loop early, be careful to reset the
+ global, prev_level, used in process_file. Otherwise, its
+ (level == prev_level - 1) assertion could fail. */
+ prev_level = 0;
+ break;
+ }
+ FTS_CROSS_CHECK (fts);
+
+ ok &= process_file (fts, ent);
+ }
+
+ if (fts_close (fts) != 0)
+ {
+ error (0, errno, _("fts_close failed"));
+ ok = false;
+ }
+ }
return ok;
}
@@ -675,16 +719,13 @@ du_files (char **files, int bit_flags)
int
main (int argc, char **argv)
{
- int c;
char *cwd_only[2];
bool max_depth_specified = false;
- char **files;
bool ok = true;
char *files_from = NULL;
- struct Tokens tok;
/* Bit flags that control how fts works. */
- int bit_flags = FTS_TIGHT_CYCLE_CHECK;
+ int bit_flags = FTS_NOSTAT;
/* Select one of the three FTS_ options that control if/when
to follow a symlink. */
@@ -693,11 +734,11 @@ main (int argc, char **argv)
/* If true, display only a total for each argument. */
bool opt_summarize_only = false;
- cwd_only[0] = ".";
+ cwd_only[0] = bad_cast (".");
cwd_only[1] = NULL;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -706,167 +747,183 @@ main (int argc, char **argv)
exclude = new_exclude ();
- human_output_opts = human_options (getenv ("DU_BLOCK_SIZE"), false,
- &output_block_size);
+ human_options (getenv ("DU_BLOCK_SIZE"),
+ &human_output_opts, &output_block_size);
- while ((c = getopt_long (argc, argv, DEBUG_OPT "0abchHklmsxB:DLPSX:",
- long_options, NULL)) != -1)
+ while (true)
{
+ int oi = -1;
+ int c = getopt_long (argc, argv, "0abd:chHklmst:xB:DLPSX:",
+ long_options, &oi);
+ if (c == -1)
+ break;
+
switch (c)
- {
+ {
#if DU_DEBUG
- case 'd':
- fts_debug = true;
- break;
+ case FTS_DEBUG:
+ fts_debug = true;
+ break;
#endif
- case '0':
- opt_nul_terminate_output = true;
- break;
-
- case 'a':
- opt_all = true;
- break;
-
- case APPARENT_SIZE_OPTION:
- apparent_size = true;
- break;
-
- case 'b':
- apparent_size = true;
- human_output_opts = 0;
- output_block_size = 1;
- break;
-
- case 'c':
- print_grand_total = true;
- break;
-
- case 'h':
- human_output_opts = human_autoscale | human_SI | human_base_1024;
- output_block_size = 1;
- break;
-
- case 'H': /* FIXME: remove warning and move this "case 'H'" to
- precede --dereference-args in late 2006. */
- error (0, 0, _("WARNING: use --si, not -H; the meaning of the -H\
- option will soon\nchange to be the same as that of --dereference-args (-D)"));
- /* fall through */
- case HUMAN_SI_OPTION:
- human_output_opts = human_autoscale | human_SI;
- output_block_size = 1;
- break;
-
- case KILOBYTES_LONG_OPTION:
- error (0, 0,
- _("the --kilobytes option is deprecated; use -k instead"));
- /* fall through */
- case 'k':
- human_output_opts = 0;
- output_block_size = 1024;
- break;
-
- case MAX_DEPTH_OPTION: /* --max-depth=N */
- {
- unsigned long int tmp_ulong;
- if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) == LONGINT_OK
- && tmp_ulong <= SIZE_MAX)
- {
- max_depth_specified = true;
- max_depth = tmp_ulong;
- }
- else
- {
- error (0, 0, _("invalid maximum depth %s"),
- quote (optarg));
- ok = false;
- }
- }
- break;
-
- case MEGABYTES_LONG_OPTION:
- error (0, 0,
- _("the --megabytes option is deprecated; use -m instead"));
- /* fall through */
- case 'm':
- human_output_opts = 0;
- output_block_size = 1024 * 1024;
- break;
-
- case 'l':
- opt_count_all = true;
- break;
-
- case 's':
- opt_summarize_only = true;
- break;
-
- case 'x':
- bit_flags |= FTS_XDEV;
- break;
-
- case 'B':
- human_output_opts = human_options (optarg, true, &output_block_size);
- break;
-
- case 'D': /* This will eventually be 'H' (-H), too. */
- symlink_deref_bits = FTS_COMFOLLOW | FTS_PHYSICAL;
- break;
-
- case 'L': /* --dereference */
- symlink_deref_bits = FTS_LOGICAL;
- break;
-
- case 'P': /* --no-dereference */
- symlink_deref_bits = FTS_PHYSICAL;
- break;
-
- case 'S':
- opt_separate_dirs = true;
- break;
-
- case 'X':
- if (add_exclude_file (add_exclude, exclude, optarg,
- EXCLUDE_WILDCARDS, '\n'))
- {
- error (0, errno, "%s", quotearg_colon (optarg));
- ok = false;
- }
- break;
-
- case FILES0_FROM_OPTION:
- files_from = optarg;
- break;
-
- case EXCLUDE_OPTION:
- add_exclude (exclude, optarg, EXCLUDE_WILDCARDS);
- break;
-
- case TIME_OPTION:
- opt_time = true;
- time_type =
- (optarg
- ? XARGMATCH ("--time", optarg, time_args, time_types)
- : time_mtime);
- break;
-
- case TIME_STYLE_OPTION:
- time_style = optarg;
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- ok = false;
- }
+ case '0':
+ opt_nul_terminate_output = true;
+ break;
+
+ case 'a':
+ opt_all = true;
+ break;
+
+ case APPARENT_SIZE_OPTION:
+ apparent_size = true;
+ break;
+
+ case 'b':
+ apparent_size = true;
+ human_output_opts = 0;
+ output_block_size = 1;
+ break;
+
+ case 'c':
+ print_grand_total = true;
+ break;
+
+ case 'h':
+ human_output_opts = human_autoscale | human_SI | human_base_1024;
+ output_block_size = 1;
+ break;
+
+ case HUMAN_SI_OPTION:
+ human_output_opts = human_autoscale | human_SI;
+ output_block_size = 1;
+ break;
+
+ case 'k':
+ human_output_opts = 0;
+ output_block_size = 1024;
+ break;
+
+ case 'd': /* --max-depth=N */
+ {
+ unsigned long int tmp_ulong;
+ if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) == LONGINT_OK
+ && tmp_ulong <= SIZE_MAX)
+ {
+ max_depth_specified = true;
+ max_depth = tmp_ulong;
+ }
+ else
+ {
+ error (0, 0, _("invalid maximum depth %s"),
+ quote (optarg));
+ ok = false;
+ }
+ }
+ break;
+
+ case 'm':
+ human_output_opts = 0;
+ output_block_size = 1024 * 1024;
+ break;
+
+ case 'l':
+ opt_count_all = true;
+ break;
+
+ case 's':
+ opt_summarize_only = true;
+ break;
+
+ case 't':
+ {
+ enum strtol_error e;
+ e = xstrtoimax (optarg, NULL, 0, &opt_threshold, "kKmMGTPEZY0");
+ if (e != LONGINT_OK)
+ xstrtol_fatal (e, oi, c, long_options, optarg);
+ if (opt_threshold == 0 && *optarg == '-')
+ {
+ /* Do not allow -0, as this wouldn't make sense anyway. */
+ error (EXIT_FAILURE, 0, _("invalid --threshold argument '-0'"));
+ }
+ }
+ break;
+
+ case 'x':
+ bit_flags |= FTS_XDEV;
+ break;
+
+ case 'B':
+ {
+ enum strtol_error e = human_options (optarg, &human_output_opts,
+ &output_block_size);
+ if (e != LONGINT_OK)
+ xstrtol_fatal (e, oi, c, long_options, optarg);
+ }
+ break;
+
+ case 'H': /* NOTE: before 2008-12, -H was equivalent to --si. */
+ case 'D':
+ symlink_deref_bits = FTS_COMFOLLOW | FTS_PHYSICAL;
+ break;
+
+ case 'L': /* --dereference */
+ symlink_deref_bits = FTS_LOGICAL;
+ break;
+
+ case 'P': /* --no-dereference */
+ symlink_deref_bits = FTS_PHYSICAL;
+ break;
+
+ case 'S':
+ opt_separate_dirs = true;
+ break;
+
+ case 'X':
+ if (add_exclude_file (add_exclude, exclude, optarg,
+ EXCLUDE_WILDCARDS, '\n'))
+ {
+ error (0, errno, "%s", quotef (optarg));
+ ok = false;
+ }
+ break;
+
+ case FILES0_FROM_OPTION:
+ files_from = optarg;
+ break;
+
+ case EXCLUDE_OPTION:
+ add_exclude (exclude, optarg, EXCLUDE_WILDCARDS);
+ break;
+
+ case INODES_OPTION:
+ opt_inodes = true;
+ break;
+
+ case TIME_OPTION:
+ opt_time = true;
+ time_type =
+ (optarg
+ ? XARGMATCH ("--time", optarg, time_args, time_types)
+ : time_mtime);
+ break;
+
+ case TIME_STYLE_OPTION:
+ time_style = optarg;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ ok = false;
+ }
}
if (!ok)
usage (EXIT_FAILURE);
- if (opt_all & opt_summarize_only)
+ if (opt_all && opt_summarize_only)
{
error (0, 0, _("cannot both summarize and show all entries"));
usage (EXIT_FAILURE);
@@ -875,7 +932,7 @@ main (int argc, char **argv)
if (opt_summarize_only && max_depth_specified && max_depth == 0)
{
error (0, 0,
- _("warning: summarizing is the same as using --max-depth=0"));
+ _("warning: summarizing is the same as using --max-depth=0"));
}
if (opt_summarize_only && max_depth_specified && max_depth != 0)
@@ -888,134 +945,192 @@ main (int argc, char **argv)
if (opt_summarize_only)
max_depth = 0;
+ if (opt_inodes)
+ {
+ if (apparent_size)
+ {
+ error (0, 0, _("warning: options --apparent-size and -b are "
+ "ineffective with --inodes"));
+ }
+ output_block_size = 1;
+ }
+
/* Process time style if printing last times. */
if (opt_time)
{
if (! time_style)
- {
- time_style = getenv ("TIME_STYLE");
-
- /* Ignore TIMESTYLE="locale", for compatibility with ls. */
- if (! time_style || STREQ (time_style, "locale"))
- time_style = "long-iso";
- else if (*time_style == '+')
- {
- /* Ignore anything after a newline, for compatibility
- with ls. */
- char *p = strchr (time_style, '\n');
- if (p)
- *p = '\0';
- }
- else
- {
- /* Ignore "posix-" prefix, for compatibility with ls. */
- static char const posix_prefix[] = "posix-";
- while (strncmp (time_style, posix_prefix, sizeof posix_prefix - 1)
- == 0)
- time_style += sizeof posix_prefix - 1;
- }
- }
+ {
+ time_style = getenv ("TIME_STYLE");
+
+ /* Ignore TIMESTYLE="locale", for compatibility with ls. */
+ if (! time_style || STREQ (time_style, "locale"))
+ time_style = "long-iso";
+ else if (*time_style == '+')
+ {
+ /* Ignore anything after a newline, for compatibility
+ with ls. */
+ char *p = strchr (time_style, '\n');
+ if (p)
+ *p = '\0';
+ }
+ else
+ {
+ /* Ignore "posix-" prefix, for compatibility with ls. */
+ static char const posix_prefix[] = "posix-";
+ static const size_t prefix_len = sizeof posix_prefix - 1;
+ while (STREQ_LEN (time_style, posix_prefix, prefix_len))
+ time_style += prefix_len;
+ }
+ }
if (*time_style == '+')
- time_format = time_style + 1;
+ time_format = time_style + 1;
else
{
switch (XARGMATCH ("time style", time_style,
time_style_args, time_style_types))
{
- case full_iso_time_style:
- time_format = "%Y-%m-%d %H:%M:%S.%N %z";
- break;
+ case full_iso_time_style:
+ time_format = "%Y-%m-%d %H:%M:%S.%N %z";
+ break;
- case long_iso_time_style:
- time_format = "%Y-%m-%d %H:%M";
- break;
+ case long_iso_time_style:
+ time_format = "%Y-%m-%d %H:%M";
+ break;
- case iso_time_style:
- time_format = "%Y-%m-%d";
- break;
+ case iso_time_style:
+ time_format = "%Y-%m-%d";
+ break;
}
}
}
+ struct argv_iterator *ai;
if (files_from)
{
/* When using --files0-from=F, you may not specify any files
- on the command-line. */
+ on the command-line. */
if (optind < argc)
- {
- error (0, 0, _("extra operand %s"), quote (argv[optind]));
- fprintf (stderr, "%s\n",
- _("File operands cannot be combined with --files0-from."));
- usage (EXIT_FAILURE);
- }
+ {
+ error (0, 0, _("extra operand %s"), quote (argv[optind]));
+ fprintf (stderr, "%s\n",
+ _("file operands cannot be combined with --files0-from"));
+ usage (EXIT_FAILURE);
+ }
if (! (STREQ (files_from, "-") || freopen (files_from, "r", stdin)))
- error (EXIT_FAILURE, errno, _("cannot open %s for reading"),
- quote (files_from));
-
- readtokens0_init (&tok);
+ error (EXIT_FAILURE, errno, _("cannot open %s for reading"),
+ quoteaf (files_from));
- if (! readtokens0 (stdin, &tok) || fclose (stdin) != 0)
- error (EXIT_FAILURE, 0, _("cannot read file names from %s"),
- quote (files_from));
+ ai = argv_iter_init_stream (stdin);
- files = tok.tok;
+ /* It's not easy here to count the arguments, so assume the
+ worst. */
+ hash_all = true;
}
else
{
- files = (optind < argc ? argv + optind : cwd_only);
+ char **files = (optind < argc ? argv + optind : cwd_only);
+ ai = argv_iter_init_argv (files);
+
+ /* Hash all dev,ino pairs if there are multiple arguments, or if
+ following non-command-line symlinks, because in either case a
+ file with just one hard link might be seen more than once. */
+ hash_all = (optind + 1 < argc || symlink_deref_bits == FTS_LOGICAL);
}
- /* Initialize the hash structure for inode numbers. */
- hash_init ();
+ if (!ai)
+ xalloc_die ();
- /* Report and filter out any empty file names before invoking fts.
- This works around a glitch in fts, which fails immediately
- (without looking at the other file names) when given an empty
- file name. */
- {
- size_t i = 0;
- size_t j;
-
- for (j = 0; ; j++)
- {
- if (i != j)
- files[i] = files[j];
-
- if ( ! files[i])
- break;
-
- if (files[i][0])
- i++;
- else
- {
- if (files_from)
- {
- /* Using the standard `filename:line-number:' prefix here is
- not totally appropriate, since NUL is the separator, not NL,
- but it might be better than nothing. */
- unsigned long int file_number = j + 1;
- error (0, 0, "%s:%lu: %s", quotearg_colon (files_from),
- file_number, _("invalid zero-length file name"));
- }
- else
- error (0, 0, "%s", _("invalid zero-length file name"));
- }
- }
-
- ok = (i == j);
- }
+ /* Initialize the set of dev,inode pairs. */
+ di_files = di_set_alloc ();
+ if (!di_files)
+ xalloc_die ();
+
+ /* If not hashing everything, process_file won't find cycles on its
+ own, so ask fts_read to check for them accurately. */
+ if (opt_count_all || ! hash_all)
+ bit_flags |= FTS_TIGHT_CYCLE_CHECK;
bit_flags |= symlink_deref_bits;
- ok &= du_files (files, bit_flags);
+ static char *temp_argv[] = { NULL, NULL };
- /* This isn't really necessary, but it does ensure we
- exercise this function. */
- if (files_from)
- readtokens0_free (&tok);
+ while (true)
+ {
+ bool skip_file = false;
+ enum argv_iter_err ai_err;
+ char *file_name = argv_iter (ai, &ai_err);
+ if (!file_name)
+ {
+ switch (ai_err)
+ {
+ case AI_ERR_EOF:
+ goto argv_iter_done;
+ case AI_ERR_READ:
+ error (0, errno, _("%s: read error"),
+ quotef (files_from));
+ ok = false;
+ goto argv_iter_done;
+ case AI_ERR_MEM:
+ xalloc_die ();
+ default:
+ assert (!"unexpected error code from argv_iter");
+ }
+ }
+ if (files_from && STREQ (files_from, "-") && STREQ (file_name, "-"))
+ {
+ /* Give a better diagnostic in an unusual case:
+ printf - | du --files0-from=- */
+ error (0, 0, _("when reading file names from stdin, "
+ "no file name of %s allowed"),
+ quoteaf (file_name));
+ skip_file = true;
+ }
+
+ /* Report and skip any empty file names before invoking fts.
+ This works around a glitch in fts, which fails immediately
+ (without looking at the other file names) when given an empty
+ file name. */
+ if (!file_name[0])
+ {
+ /* Diagnose a zero-length file name. When it's one
+ 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 == NULL)
+ error (0, 0, "%s", _("invalid zero-length file name"));
+ else
+ {
+ /* Using the standard 'filename:line-number:' prefix here is
+ not totally appropriate, since NUL is the separator, not NL,
+ but it might be better than nothing. */
+ unsigned long int file_number = argv_iter_n_args (ai);
+ error (0, 0, "%s:%lu: %s", quotef (files_from),
+ file_number, _("invalid zero-length file name"));
+ }
+ skip_file = true;
+ }
+
+ if (skip_file)
+ ok = false;
+ else
+ {
+ temp_argv[0] = file_name;
+ ok &= du_files (temp_argv, bit_flags);
+ }
+ }
+ argv_iter_done:
+
+ argv_iter_free (ai);
+ di_set_free (di_files);
+ if (di_mnt)
+ di_set_free (di_mnt);
- hash_free (htab);
+ if (files_from && (ferror (stdin) || fclose (stdin) != 0) && ok)
+ error (EXIT_FAILURE, 0, _("error reading %s"), quoteaf (files_from));
+
+ if (print_grand_total)
+ print_size (&tot_dui, _("total"));
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/echo.c b/src/echo.c
index 5f8582c..977bd5f 100644
--- a/src/echo.c
+++ b/src/echo.c
@@ -1,10 +1,10 @@
/* echo.c, derived from code echo.c in Bash.
- Copyright (C) 87,89, 1991-1997, 1999-2005 Free Software Foundation, Inc.
+ Copyright (C) 1987-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,90 +12,74 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include "system.h"
-#include "long-options.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "echo"
-#define AUTHORS "FIXME unknown"
-
-/* echo [-neE] [arg ...]
-Output the ARGs. If -n is specified, the trailing newline is
-suppressed. If the -e option is given, interpretation of the
-following backslash-escaped characters is turned on:
- \a alert (bell)
- \b backspace
- \c suppress trailing newline
- \f form feed
- \n new line
- \r carriage return
- \t horizontal tab
- \v vertical tab
- \\ backslash
- \0NNN the character whose ASCII code is NNN (octal).
-
-You can explicitly turn off the interpretation of the above characters
-on System V systems with the -E option.
-*/
+#define AUTHORS \
+ proper_name ("Brian Fox"), \
+ proper_name ("Chet Ramey")
/* If true, interpret backslash escapes by default. */
#ifndef DEFAULT_ECHO_TO_XPG
enum { DEFAULT_ECHO_TO_XPG = false };
#endif
-/* The name this program was run with. */
-char *program_name;
-
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
- printf (_("Usage: %s [OPTION]... [STRING]...\n"), program_name);
+ printf (_("\
+Usage: %s [SHORT-OPTION]... [STRING]...\n\
+ or: %s LONG-OPTION\n\
+"), program_name, program_name);
fputs (_("\
Echo the STRING(s) to standard output.\n\
\n\
-n do not output the trailing newline\n\
"), stdout);
fputs (_(DEFAULT_ECHO_TO_XPG
- ? "\
+ ? N_("\
-e enable interpretation of backslash escapes (default)\n\
- -E disable interpretation of backslash escapes\n"
- : "\
+ -E disable interpretation of backslash escapes\n")
+ : N_("\
-e enable interpretation of backslash escapes\n\
- -E disable interpretation of backslash escapes (default)\n"),
- stdout);
+ -E disable interpretation of backslash escapes (default)\n")),
+ stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
\n\
If -e is in effect, the following sequences are recognized:\n\
\n\
- \\0NNN the character whose ASCII code is NNN (octal)\n\
- \\\\ backslash\n\
- \\a alert (BEL)\n\
- \\b backspace\n\
"), stdout);
fputs (_("\
- \\c suppress trailing newline\n\
- \\f form feed\n\
- \\n new line\n\
- \\r carriage return\n\
- \\t horizontal tab\n\
- \\v vertical tab\n\
+ \\\\ backslash\n\
+ \\a alert (BEL)\n\
+ \\b backspace\n\
+ \\c produce no further output\n\
+ \\e escape\n\
+ \\f form feed\n\
+ \\n new line\n\
+ \\r carriage return\n\
+ \\t horizontal tab\n\
+ \\v vertical tab\n\
+"), stdout);
+ fputs (_("\
+ \\0NNN byte with octal value NNN (1 to 3 digits)\n\
+ \\xHH byte with hexadecimal value HH (1 to 2 digits)\n\
"), stdout);
printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -117,7 +101,7 @@ hextobin (unsigned char c)
}
/* Print the words in LIST to standard output. If the first word is
- `-n', then don't print a trailing newline. We also support the
+ '-n', then don't print a trailing newline. We also support the
echo syntax from Version 9 unix systems. */
int
@@ -134,16 +118,27 @@ main (int argc, char **argv)
bool do_v9 = DEFAULT_ECHO_TO_XPG;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- if (allow_options)
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
+ /* We directly parse options, rather than use parse_long_options, in
+ order to avoid accepting abbreviations. */
+ if (allow_options && argc == 2)
+ {
+ if (STREQ (argv[1], "--help"))
+ usage (EXIT_SUCCESS);
+
+ if (STREQ (argv[1], "--version"))
+ {
+ version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version, AUTHORS,
+ (char *) NULL);
+ return EXIT_SUCCESS;
+ }
+ }
--argc;
++argv;
@@ -151,45 +146,45 @@ main (int argc, char **argv)
if (allow_options)
while (argc > 0 && *argv[0] == '-')
{
- char const *temp = argv[0] + 1;
- size_t i;
-
- /* If it appears that we are handling options, then make sure that
- all of the options specified are actually valid. Otherwise, the
- string should just be echoed. */
-
- for (i = 0; temp[i]; i++)
- switch (temp[i])
- {
- case 'e': case 'E': case 'n':
- break;
- default:
- goto just_echo;
- }
-
- if (i == 0)
- goto just_echo;
-
- /* All of the options in TEMP are valid options to ECHO.
- Handle them. */
- while (*temp)
- switch (*temp++)
- {
- case 'e':
- do_v9 = true;
- break;
-
- case 'E':
- do_v9 = false;
- break;
-
- case 'n':
- display_return = false;
- break;
- }
-
- argc--;
- argv++;
+ char const *temp = argv[0] + 1;
+ size_t i;
+
+ /* If it appears that we are handling options, then make sure that
+ all of the options specified are actually valid. Otherwise, the
+ string should just be echoed. */
+
+ for (i = 0; temp[i]; i++)
+ switch (temp[i])
+ {
+ case 'e': case 'E': case 'n':
+ break;
+ default:
+ goto just_echo;
+ }
+
+ if (i == 0)
+ goto just_echo;
+
+ /* All of the options in TEMP are valid options to ECHO.
+ Handle them. */
+ while (*temp)
+ switch (*temp++)
+ {
+ case 'e':
+ do_v9 = true;
+ break;
+
+ case 'E':
+ do_v9 = false;
+ break;
+
+ case 'n':
+ display_return = false;
+ break;
+ }
+
+ argc--;
+ argv++;
}
just_echo:
@@ -197,80 +192,81 @@ just_echo:
if (do_v9)
{
while (argc > 0)
- {
- char const *s = argv[0];
- unsigned char c;
-
- while ((c = *s++))
- {
- if (c == '\\' && *s)
- {
- switch (c = *s++)
- {
- case 'a': c = '\a'; break;
- case 'b': c = '\b'; break;
- case 'c': exit (EXIT_SUCCESS);
- case 'f': c = '\f'; break;
- case 'n': c = '\n'; break;
- case 'r': c = '\r'; break;
- case 't': c = '\t'; break;
- case 'v': c = '\v'; break;
- case 'x':
- {
- unsigned char ch = *s;
- if (! isxdigit (ch))
- goto not_an_escape;
- s++;
- c = hextobin (ch);
- ch = *s;
- if (isxdigit (ch))
- {
- s++;
- c = c * 16 + hextobin (ch);
- }
- }
- break;
- case '0':
- c = 0;
- if (! ('0' <= *s && *s <= '7'))
- break;
- c = *s++;
- /* Fall through. */
- case '1': case '2': case '3':
- case '4': case '5': case '6': case '7':
- c -= '0';
- if ('0' <= *s && *s <= '7')
- c = c * 8 + (*s++ - '0');
- if ('0' <= *s && *s <= '7')
- c = c * 8 + (*s++ - '0');
- break;
- case '\\': break;
-
- not_an_escape:
- default: putchar ('\\'); break;
- }
- }
- putchar (c);
- }
- argc--;
- argv++;
- if (argc > 0)
- putchar (' ');
- }
+ {
+ char const *s = argv[0];
+ unsigned char c;
+
+ while ((c = *s++))
+ {
+ if (c == '\\' && *s)
+ {
+ switch (c = *s++)
+ {
+ case 'a': c = '\a'; break;
+ case 'b': c = '\b'; break;
+ case 'c': return EXIT_SUCCESS;
+ case 'e': c = '\x1B'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ case 'v': c = '\v'; break;
+ case 'x':
+ {
+ unsigned char ch = *s;
+ if (! isxdigit (ch))
+ goto not_an_escape;
+ s++;
+ c = hextobin (ch);
+ ch = *s;
+ if (isxdigit (ch))
+ {
+ s++;
+ c = c * 16 + hextobin (ch);
+ }
+ }
+ break;
+ case '0':
+ c = 0;
+ if (! ('0' <= *s && *s <= '7'))
+ break;
+ c = *s++;
+ /* Fall through. */
+ case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ c -= '0';
+ if ('0' <= *s && *s <= '7')
+ c = c * 8 + (*s++ - '0');
+ if ('0' <= *s && *s <= '7')
+ c = c * 8 + (*s++ - '0');
+ break;
+ case '\\': break;
+
+ not_an_escape:
+ default: putchar ('\\'); break;
+ }
+ }
+ putchar (c);
+ }
+ argc--;
+ argv++;
+ if (argc > 0)
+ putchar (' ');
+ }
}
else
{
while (argc > 0)
- {
- fputs (argv[0], stdout);
- argc--;
- argv++;
- if (argc > 0)
- putchar (' ');
- }
+ {
+ fputs (argv[0], stdout);
+ argc--;
+ argv++;
+ if (argc > 0)
+ putchar (' ');
+ }
}
if (display_return)
putchar ('\n');
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/env.c b/src/env.c
index d95ebfd..9994b2b 100644
--- a/src/env.c
+++ b/src/env.c
@@ -1,10 +1,10 @@
/* env - run a program in a modified environment
- Copyright (C) 1986, 1991-2005, 2007 Free Software Foundation, Inc.
+ Copyright (C) 1986-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,95 +12,30 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Richard Mlynarik and David MacKenzie */
-/* Options:
- -
- -i
- --ignore-environment
- Construct a new environment from scratch; normally the
- environment is inherited from the parent process, except as
- modified by other options.
-
- -u variable
- --unset=variable
- Unset variable VARIABLE (remove it from the environment).
- If VARIABLE was not set, does nothing.
-
- variable=value (an arg containing a "=" character)
- Set the environment variable VARIABLE to value VALUE. VALUE
- may be of zero length ("variable="). Setting a variable to a
- zero-length value is different from unsetting it.
-
- --
- Indicate that the following argument is the program
- to invoke. This is necessary when the program's name
- begins with "-" or contains a "=".
-
- The first remaining argument specifies a program to invoke;
- it is searched for according to the specification of the PATH
- environment variable. Any arguments following that are
- passed as arguments to that program.
-
- If no command name is specified following the environment
- specifications, the resulting environment is printed.
- This is like specifying a command name of "printenv".
-
- Examples:
-
- If the environment passed to "env" is
- { LOGNAME=rms EDITOR=emacs PATH=.:/gnubin:/hacks }
-
- env - foo
- runs "foo" in a null environment.
-
- env foo
- runs "foo" in the environment
- { LOGNAME=rms EDITOR=emacs PATH=.:/gnubin:/hacks }
-
- env DISPLAY=gnu:0 nemacs
- runs "nemacs" in the environment
- { LOGNAME=rms EDITOR=emacs PATH=.:/gnubin:/hacks DISPLAY=gnu:0 }
-
- env - LOGNAME=foo /hacks/hack bar baz
- runs the "hack" program on arguments "bar" and "baz" in an
- environment in which the only variable is "LOGNAME". Note that
- the "-" option clears out the PATH variable, so one should be
- careful to specify in which directory to find the program to
- call.
-
- env -u EDITOR LOGNAME=foo PATH=/energy -- e=mc2 bar baz
- runs the program "/energy/e=mc2" with environment
- { LOGNAME=foo PATH=/energy }
-*/
-
#include <config.h>
#include <stdio.h>
-#include <getopt.h>
#include <sys/types.h>
#include <getopt.h>
#include "system.h"
#include "error.h"
+#include "quote.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "env"
-#define AUTHORS "Richard Mlynarik", "David MacKenzie"
-
-int putenv ();
-
-extern char **environ;
-
-/* The name by which this program was run. */
-char *program_name;
+#define AUTHORS \
+ proper_name ("Richard Mlynarik"), \
+ proper_name ("David MacKenzie")
static struct option const longopts[] =
{
{"ignore-environment", no_argument, NULL, 'i'},
+ {"null", no_argument, NULL, '0'},
{"unset", required_argument, NULL, 'u'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
@@ -111,18 +46,22 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]\n"),
- program_name);
+ program_name);
fputs (_("\
Set each NAME to VALUE in the environment and run COMMAND.\n\
-\n\
- -i, --ignore-environment start with an empty environment\n\
- -u, --unset=NAME remove variable from the environment\n\
+"), stdout);
+
+ emit_mandatory_arg_note ();
+
+ 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 (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
@@ -130,7 +69,7 @@ Set each NAME to VALUE in the environment and run COMMAND.\n\
\n\
A mere - implies -i. If no COMMAND, print the resulting environment.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -140,30 +79,34 @@ main (int argc, char **argv)
{
int optc;
bool ignore_environment = false;
+ bool opt_nul_terminate_output = false;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
- initialize_exit_failure (EXIT_FAIL);
+ initialize_exit_failure (EXIT_CANCELED);
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "+iu:", longopts, NULL)) != -1)
+ while ((optc = getopt_long (argc, argv, "+iu:0", longopts, NULL)) != -1)
{
switch (optc)
- {
- case 'i':
- ignore_environment = true;
- break;
- case 'u':
- break;
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAIL);
- }
+ {
+ case 'i':
+ ignore_environment = true;
+ break;
+ case 'u':
+ break;
+ case '0':
+ opt_nul_terminate_output = true;
+ break;
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_CANCELED);
+ }
}
if (optind < argc && STREQ (argv[optind], "-"))
@@ -176,30 +119,43 @@ main (int argc, char **argv)
}
optind = 0; /* Force GNU getopt to re-initialize. */
- while ((optc = getopt_long (argc, argv, "+iu:", longopts, NULL)) != -1)
- if (optc == 'u')
- putenv (optarg); /* Requires GNU putenv. */
+ while ((optc = getopt_long (argc, argv, "+iu:0", longopts, NULL)) != -1)
+ if (optc == 'u' && unsetenv (optarg))
+ error (EXIT_CANCELED, errno, _("cannot unset %s"), quote (optarg));
if (optind < argc && STREQ (argv[optind], "-"))
++optind;
- while (optind < argc && strchr (argv[optind], '='))
- putenv (argv[optind++]);
+ char *eq;
+ while (optind < argc && (eq = strchr (argv[optind], '=')))
+ {
+ if (putenv (argv[optind]))
+ {
+ *eq = '\0';
+ error (EXIT_CANCELED, errno, _("cannot set %s"),
+ quote (argv[optind]));
+ }
+ optind++;
+ }
/* If no program is specified, print the environment and exit. */
if (argc <= optind)
{
char *const *e = environ;
while (*e)
- puts (*e++);
- exit (EXIT_SUCCESS);
+ printf ("%s%c", *e++, opt_nul_terminate_output ? '\0' : '\n');
+ return EXIT_SUCCESS;
+ }
+
+ if (opt_nul_terminate_output)
+ {
+ error (0, errno, _("cannot specify --null (-0) with command"));
+ usage (EXIT_CANCELED);
}
execvp (argv[optind], &argv[optind]);
- {
- int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
- error (0, errno, "%s", argv[optind]);
- exit (exit_status);
- }
+ int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
+ error (0, errno, "%s", quote (argv[optind]));
+ return exit_status;
}
diff --git a/src/expand.c b/src/expand.c
index 5645203..e11f1af 100644
--- a/src/expand.c
+++ b/src/expand.c
@@ -1,10 +1,10 @@
/* expand - convert tabs to spaces
- Copyright (C) 89, 91, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* By default, convert all tabs to spaces.
Preserves backspace characters in the output; they decrement the
@@ -24,10 +23,10 @@
--tabs=tab1[,tab2[,...]]
-t tab1[,tab2[,...]]
-tab1[,tab2[,...]] If only one tab stop is given, set the tabs tab1
- columns apart instead of the default 8. Otherwise,
- set the tabs at columns tab1, tab2, etc. (numbered from
- 0); replace any tabs beyond the tab stops given with
- single spaces.
+ columns apart instead of the default 8. Otherwise,
+ set the tabs at columns tab1, tab2, etc. (numbered from
+ 0); replace any tabs beyond the tab stops given with
+ single spaces.
--initial
-i Only convert initial tabs on each line to spaces.
@@ -40,47 +39,41 @@
#include <sys/types.h>
#include "system.h"
#include "error.h"
+#include "fadvise.h"
#include "quote.h"
#include "xstrndup.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "expand"
-#define AUTHORS "David MacKenzie"
-
-/* The number of bytes added at a time to the amount of memory
- allocated for the output line. */
-#define OUTPUT_BLOCK 256
-
-/* The name this program was run with. */
-char *program_name;
+#define AUTHORS proper_name ("David MacKenzie")
/* If true, convert blanks even after nonblank characters have been
read on the line. */
static bool convert_entire_line;
-/* If nonzero, the size of all tab stops. If zero, use `tab_list' instead. */
+/* If nonzero, the size of all tab stops. If zero, use 'tab_list' instead. */
static uintmax_t tab_size;
/* Array of the explicit column numbers of the tab stops;
- after `tab_list' is exhausted, each additional tab is replaced
+ after 'tab_list' is exhausted, each additional tab is replaced
by a space. The first column is column 0. */
static uintmax_t *tab_list;
-/* The number of allocated entries in `tab_list'. */
+/* The number of allocated entries in 'tab_list'. */
static size_t n_tabs_allocated;
-/* The index of the first invalid element of `tab_list',
+/* The index of the first invalid element of 'tab_list',
where the next element can be added. */
static size_t first_free_tab;
/* Null-terminated array of input filenames. */
static char **file_list;
-/* Default for `file_list' if no files are given on the command line. */
+/* Default for 'file_list' if no files are given on the command line. */
static char *stdin_argv[] =
{
- "-", NULL
+ (char *) "-", NULL
};
/* True if we have ever read standard input. */
@@ -104,22 +97,20 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
- program_name);
+ program_name);
fputs (_("\
Convert tabs in each FILE to spaces, writing to standard output.\n\
-With no FILE, or when FILE is -, read standard input.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
+
+ emit_stdin_note ();
+ emit_mandatory_arg_note ();
+
fputs (_("\
-i, --initial do not convert tabs after non blanks\n\
-t, --tabs=NUMBER have tabs NUMBER characters apart, not 8\n\
@@ -129,12 +120,12 @@ Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
-/* Add tab stop TABVAL to the end of `tab_list'. */
+/* Add tab stop TABVAL to the end of 'tab_list'. */
static void
add_tab_stop (uintmax_t tabval)
@@ -151,45 +142,45 @@ static void
parse_tab_stops (char const *stops)
{
bool have_tabval = false;
- uintmax_t tabval IF_LINT (= 0);
- char const *num_start IF_LINT (= NULL);
+ uintmax_t tabval IF_LINT ( = 0);
+ char const *num_start IF_LINT ( = NULL);
bool ok = true;
for (; *stops; stops++)
{
if (*stops == ',' || isblank (to_uchar (*stops)))
- {
- if (have_tabval)
- add_tab_stop (tabval);
- have_tabval = false;
- }
+ {
+ if (have_tabval)
+ add_tab_stop (tabval);
+ have_tabval = false;
+ }
else if (ISDIGIT (*stops))
- {
- if (!have_tabval)
- {
- tabval = 0;
- have_tabval = true;
- num_start = stops;
- }
-
- /* Detect overflow. */
- if (!DECIMAL_DIGIT_ACCUMULATE (tabval, *stops - '0', uintmax_t))
- {
- size_t len = strspn (num_start, "0123456789");
- char *bad_num = xstrndup (num_start, len);
- error (0, 0, _("tab stop is too large %s"), quote (bad_num));
- free (bad_num);
- ok = false;
- stops = num_start + len - 1;
- }
- }
+ {
+ if (!have_tabval)
+ {
+ tabval = 0;
+ have_tabval = true;
+ num_start = stops;
+ }
+
+ /* Detect overflow. */
+ if (!DECIMAL_DIGIT_ACCUMULATE (tabval, *stops - '0', uintmax_t))
+ {
+ size_t len = strspn (num_start, "0123456789");
+ char *bad_num = xstrndup (num_start, len);
+ error (0, 0, _("tab stop is too large %s"), quote (bad_num));
+ free (bad_num);
+ ok = false;
+ stops = num_start + len - 1;
+ }
+ }
else
- {
- error (0, 0, _("tab size contains invalid character(s): %s"),
- quote (stops));
- ok = false;
- break;
- }
+ {
+ error (0, 0, _("tab size contains invalid character(s): %s"),
+ quote (stops));
+ ok = false;
+ break;
+ }
}
if (!ok)
@@ -211,16 +202,16 @@ validate_tab_stops (uintmax_t const *tabs, size_t entries)
for (i = 0; i < entries; i++)
{
if (tabs[i] == 0)
- error (EXIT_FAILURE, 0, _("tab size cannot be 0"));
+ error (EXIT_FAILURE, 0, _("tab size cannot be 0"));
if (tabs[i] <= prev_tab)
- error (EXIT_FAILURE, 0, _("tab sizes must be ascending"));
+ error (EXIT_FAILURE, 0, _("tab sizes must be ascending"));
prev_tab = tabs[i];
}
}
/* 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.
+ Open a filename of '-' as the standard input.
Return NULL if there are no more input files. */
static FILE *
@@ -232,41 +223,42 @@ next_file (FILE *fp)
if (fp)
{
if (ferror (fp))
- {
- error (0, errno, "%s", prev_file);
- exit_status = EXIT_FAILURE;
- }
+ {
+ error (0, errno, "%s", quotef (prev_file));
+ exit_status = EXIT_FAILURE;
+ }
if (STREQ (prev_file, "-"))
- clearerr (fp); /* Also clear EOF. */
+ clearerr (fp); /* Also clear EOF. */
else if (fclose (fp) != 0)
- {
- error (0, errno, "%s", prev_file);
- exit_status = EXIT_FAILURE;
- }
+ {
+ error (0, errno, "%s", quotef (prev_file));
+ exit_status = EXIT_FAILURE;
+ }
}
while ((file = *file_list++) != NULL)
{
if (STREQ (file, "-"))
- {
- have_read_stdin = true;
- prev_file = file;
- return stdin;
- }
- fp = fopen (file, "r");
+ {
+ have_read_stdin = true;
+ fp = stdin;
+ }
+ else
+ fp = fopen (file, "r");
if (fp)
- {
- prev_file = file;
- return fp;
- }
- error (0, errno, "%s", file);
+ {
+ prev_file = file;
+ fadvise (fp, FADVISE_SEQUENTIAL);
+ return fp;
+ }
+ error (0, errno, "%s", quotef (file));
exit_status = EXIT_FAILURE;
}
return NULL;
}
/* Change tabs to spaces, writing to stdout.
- Read each file in `file_list', in order. */
+ Read each file in 'file_list', in order. */
static void
expand (void)
@@ -277,7 +269,7 @@ expand (void)
if (!fp)
return;
- for (;;)
+ while (true)
{
/* Input character, or EOF. */
int c;
@@ -287,7 +279,7 @@ expand (void)
/* The following variables have valid values only when CONVERT
- is true: */
+ is true: */
/* Column of next input character. */
uintmax_t column = 0;
@@ -299,68 +291,68 @@ expand (void)
/* Convert a line of text. */
do
- {
- while ((c = getc (fp)) < 0 && (fp = next_file (fp)))
- continue;
-
- if (convert)
- {
- if (c == '\t')
- {
- /* Column the next input tab stop is on. */
- uintmax_t next_tab_column;
-
- if (tab_size)
- next_tab_column = column + (tab_size - column % tab_size);
- else
- for (;;)
- if (tab_index == first_free_tab)
- {
- next_tab_column = column + 1;
- break;
- }
- else
- {
- uintmax_t tab = tab_list[tab_index++];
- if (column < tab)
- {
- next_tab_column = tab;
- break;
- }
- }
-
- if (next_tab_column < column)
- error (EXIT_FAILURE, 0, _("input line is too long"));
-
- while (++column < next_tab_column)
- if (putchar (' ') < 0)
- error (EXIT_FAILURE, errno, _("write error"));
-
- c = ' ';
- }
- else if (c == '\b')
- {
- /* Go back one column, and force recalculation of the
- next tab stop. */
- column -= !!column;
- tab_index -= !!tab_index;
- }
- else
- {
- column++;
- if (!column)
- error (EXIT_FAILURE, 0, _("input line is too long"));
- }
-
- convert &= convert_entire_line | !! isblank (c);
- }
-
- if (c < 0)
- return;
-
- if (putchar (c) < 0)
- error (EXIT_FAILURE, errno, _("write error"));
- }
+ {
+ while ((c = getc (fp)) < 0 && (fp = next_file (fp)))
+ continue;
+
+ if (convert)
+ {
+ if (c == '\t')
+ {
+ /* Column the next input tab stop is on. */
+ uintmax_t next_tab_column;
+
+ if (tab_size)
+ next_tab_column = column + (tab_size - column % tab_size);
+ else
+ while (true)
+ if (tab_index == first_free_tab)
+ {
+ next_tab_column = column + 1;
+ break;
+ }
+ else
+ {
+ uintmax_t tab = tab_list[tab_index++];
+ if (column < tab)
+ {
+ next_tab_column = tab;
+ break;
+ }
+ }
+
+ if (next_tab_column < column)
+ error (EXIT_FAILURE, 0, _("input line is too long"));
+
+ while (++column < next_tab_column)
+ if (putchar (' ') < 0)
+ error (EXIT_FAILURE, errno, _("write error"));
+
+ c = ' ';
+ }
+ else if (c == '\b')
+ {
+ /* Go back one column, and force recalculation of the
+ next tab stop. */
+ column -= !!column;
+ tab_index -= !!tab_index;
+ }
+ else
+ {
+ column++;
+ if (!column)
+ error (EXIT_FAILURE, 0, _("input line is too long"));
+ }
+
+ convert &= convert_entire_line || !! isblank (c);
+ }
+
+ if (c < 0)
+ return;
+
+ if (putchar (c) < 0)
+ error (EXIT_FAILURE, errno, _("write error"));
+ }
while (c != '\n');
}
}
@@ -371,7 +363,7 @@ main (int argc, char **argv)
int c;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -387,35 +379,35 @@ main (int argc, char **argv)
while ((c = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
{
switch (c)
- {
- case 'i':
- convert_entire_line = false;
- break;
-
- case 't':
- parse_tab_stops (optarg);
- break;
-
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- if (optarg)
- parse_tab_stops (optarg - 1);
- else
- {
- char tab_stop[2];
- tab_stop[0] = c;
- tab_stop[1] = '\0';
- parse_tab_stops (tab_stop);
- }
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'i':
+ convert_entire_line = false;
+ break;
+
+ case 't':
+ parse_tab_stops (optarg);
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (optarg)
+ parse_tab_stops (optarg - 1);
+ else
+ {
+ char tab_stop[2];
+ tab_stop[0] = c;
+ tab_stop[1] = '\0';
+ parse_tab_stops (tab_stop);
+ }
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
}
validate_tab_stops (tab_list, first_free_tab);
@@ -434,5 +426,5 @@ main (int argc, char **argv)
if (have_read_stdin && fclose (stdin) != 0)
error (EXIT_FAILURE, errno, "-");
- exit (exit_status);
+ return exit_status;
}
diff --git a/src/expr.c b/src/expr.c
index 352c80c..86eb136 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -1,10 +1,10 @@
/* expr -- evaluate expressions.
- Copyright (C) 86, 1991-1997, 1999-2006 Free Software Foundation, Inc.
+ Copyright (C) 1986-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,13 +12,13 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Author: Mike Parker.
+ Modified for arbitrary-precision calculation by James Youngman.
This program evaluates expressions. Each token (operator, operand,
- parenthesis) of the expression must be a seperate argument. The
+ parenthesis) of the expression must be a separate argument. The
parser used is a reasonably general one, though any incarnation of
it is language-specific. It is especially nice for expressions.
@@ -34,18 +34,121 @@
#include "system.h"
#include <regex.h>
-#include "long-options.h"
#include "error.h"
-#include "inttostr.h"
-#include "quote.h"
-#include "quotearg.h"
+#include "long-options.h"
#include "strnumcmp.h"
#include "xstrtol.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* Various parts of this code assume size_t fits into unsigned long
+ int, the widest unsigned type that GMP supports. */
+verify (SIZE_MAX <= ULONG_MAX);
+
+#ifndef HAVE_GMP
+# define HAVE_GMP 0
+#endif
+
+#if HAVE_GMP
+# include <gmp.h>
+#else
+static void integer_overflow (char) ATTRIBUTE_NORETURN;
+/* Approximate gmp.h well enough for expr.c's purposes. */
+typedef intmax_t mpz_t[1];
+static void mpz_clear (mpz_t z) { (void) z; }
+static void mpz_init_set_ui (mpz_t z, unsigned long int i) { z[0] = i; }
+static int
+mpz_init_set_str (mpz_t z, char *s, int base)
+{
+ return xstrtoimax (s, NULL, base, z, NULL) == LONGINT_OK ? 0 : -1;
+}
+static void
+mpz_add (mpz_t r, mpz_t a0, mpz_t b0)
+{
+ intmax_t a = a0[0];
+ intmax_t b = b0[0];
+ intmax_t val = a + b;
+ if ((val < a) != (b < 0))
+ integer_overflow ('+');
+ r[0] = val;
+}
+static void
+mpz_sub (mpz_t r, mpz_t a0, mpz_t b0)
+{
+ intmax_t a = a0[0];
+ intmax_t b = b0[0];
+ intmax_t val = a - b;
+ if ((a < val) != (b < 0))
+ integer_overflow ('-');
+ r[0] = val;
+}
+static void
+mpz_mul (mpz_t r, mpz_t a0, mpz_t b0)
+{
+ intmax_t a = a0[0];
+ intmax_t b = b0[0];
+ intmax_t val = a * b;
+ if (! (a == 0 || b == 0
+ || ((val < 0) == ((a < 0) ^ (b < 0)) && val / a == b)))
+ integer_overflow ('*');
+ r[0] = val;
+}
+static void
+mpz_tdiv_q (mpz_t r, mpz_t a0, mpz_t b0)
+{
+ intmax_t a = a0[0];
+ intmax_t b = b0[0];
+
+ /* Some x86-style hosts raise an exception for INT_MIN / -1. */
+ if (a < - INTMAX_MAX && b == -1)
+ integer_overflow ('/');
+ r[0] = a / b;
+}
+static void
+mpz_tdiv_r (mpz_t r, mpz_t a0, mpz_t b0)
+{
+ intmax_t a = a0[0];
+ intmax_t b = b0[0];
+
+ /* Some x86-style hosts raise an exception for INT_MIN % -1. */
+ r[0] = a < - INTMAX_MAX && b == -1 ? 0 : a % b;
+}
+static char *
+mpz_get_str (char const *str, int base, mpz_t z)
+{
+ (void) str; (void) base;
+ char buf[INT_BUFSIZE_BOUND (intmax_t)];
+ return xstrdup (imaxtostr (z[0], buf));
+}
+static int
+mpz_sgn (mpz_t z)
+{
+ return z[0] < 0 ? -1 : 0 < z[0];
+}
+static int
+mpz_fits_ulong_p (mpz_t z)
+{
+ return 0 <= z[0] && z[0] <= ULONG_MAX;
+}
+static unsigned long int
+mpz_get_ui (mpz_t z)
+{
+ return z[0];
+}
+static int
+mpz_out_str (FILE *stream, int base, mpz_t z)
+{
+ (void) base;
+ char buf[INT_BUFSIZE_BOUND (intmax_t)];
+ return fputs (imaxtostr (z[0], buf), stream) != EOF;
+}
+#endif
+
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "expr"
-#define AUTHORS "Mike Parker"
+#define AUTHORS \
+ proper_name ("Mike Parker"), \
+ proper_name ("James Youngman"), \
+ proper_name ("Paul Eggert")
/* Exit statuses. */
enum
@@ -74,7 +177,7 @@ struct valinfo
TYPE type; /* Which kind. */
union
{ /* The value itself. */
- intmax_t i;
+ mpz_t i;
char *s;
} u;
};
@@ -83,9 +186,6 @@ typedef struct valinfo VALUE;
/* The arguments given to the program, minus the program name. */
static char **args;
-/* The name this program was run with. */
-char *program_name;
-
static VALUE *eval (bool);
static bool nomoreargs (void);
static bool null (VALUE *v);
@@ -95,15 +195,14 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s EXPRESSION\n\
or: %s OPTION\n\
"),
- program_name, program_name);
+ program_name, program_name);
putchar ('\n');
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
@@ -130,6 +229,8 @@ separates increasing precedence groups. EXPRESSION may be:\n\
ARG1 + ARG2 arithmetic sum of ARG1 and ARG2\n\
ARG1 - ARG2 arithmetic difference of ARG1 and ARG2\n\
"), stdout);
+ /* Tell xgettext that the "% A" below is not a printf-style
+ format string: xgettext:no-c-format */
fputs (_("\
\n\
ARG1 * ARG2 arithmetic product of ARG1 and ARG2\n\
@@ -147,7 +248,7 @@ separates increasing precedence groups. EXPRESSION may be:\n\
"), stdout);
fputs (_("\
+ TOKEN interpret TOKEN as a string, even if it is a\n\
- keyword like `match' or an operator like `/'\n\
+ keyword like 'match' or an operator like '/'\n\
\n\
( EXPRESSION ) value of EXPRESSION\n\
"), stdout);
@@ -163,7 +264,7 @@ Pattern matches return the string matched between \\( and \\) or null; if\n\
Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null\n\
or 0, 2 if EXPRESSION is syntactically invalid, and 3 if an error occurred.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -175,12 +276,15 @@ syntax_error (void)
error (EXPR_INVALID, 0, _("syntax error"));
}
+#if ! HAVE_GMP
/* Report an integer overflow for operation OP and exit. */
static void
integer_overflow (char op)
{
error (EXPR_FAILURE, ERANGE, "%c", op);
+ abort (); /* notreached */
}
+#endif
int
main (int argc, char **argv)
@@ -188,7 +292,7 @@ main (int argc, char **argv)
VALUE *v;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -196,17 +300,19 @@ main (int argc, char **argv)
initialize_exit_failure (EXPR_FAILURE);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
+ parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, VERSION,
+ usage, AUTHORS, (char const *) NULL);
+
/* The above handles --help and --version.
- Since there is no other invocation of getopt, handle `--' here. */
- if (argc > 1 && STREQ (argv[1], "--"))
+ Since there is no other invocation of getopt, handle '--' here. */
+ unsigned int u_argc = argc;
+ if (1 < u_argc && STREQ (argv[1], "--"))
{
- --argc;
+ --u_argc;
++argv;
}
- if (argc <= 1)
+ if (u_argc <= 1)
{
error (0, 0, _("missing operand"));
usage (EXPR_INVALID);
@@ -219,17 +325,17 @@ main (int argc, char **argv)
syntax_error ();
printv (v);
- exit (null (v));
+ return null (v);
}
/* Return a VALUE for I. */
static VALUE *
-int_value (intmax_t i)
+int_value (unsigned long int i)
{
VALUE *v = xmalloc (sizeof *v);
v->type = integer;
- v->u.i = i;
+ mpz_init_set_ui (v->u.i, i);
return v;
}
@@ -251,6 +357,8 @@ freev (VALUE *v)
{
if (v->type == string)
free (v->u.s);
+ else
+ mpz_clear (v->u.i);
free (v);
}
@@ -259,49 +367,45 @@ freev (VALUE *v)
static void
printv (VALUE *v)
{
- char *p;
- char buf[INT_BUFSIZE_BOUND (intmax_t)];
-
switch (v->type)
{
case integer:
- p = imaxtostr (v->u.i, buf);
+ mpz_out_str (stdout, 10, v->u.i);
+ putchar ('\n');
break;
case string:
- p = v->u.s;
+ puts (v->u.s);
break;
default:
abort ();
}
-
- puts (p);
}
/* Return true if V is a null-string or zero-number. */
-static bool
+static bool _GL_ATTRIBUTE_PURE
null (VALUE *v)
{
switch (v->type)
{
case integer:
- return v->u.i == 0;
+ return mpz_sgn (v->u.i) == 0;
case string:
{
- char const *cp = v->u.s;
- if (*cp == '\0')
- return true;
+ char const *cp = v->u.s;
+ if (*cp == '\0')
+ return true;
- cp += (*cp == '-');
+ cp += (*cp == '-');
- do
- {
- if (*cp != '0')
- return false;
- }
- while (*++cp);
+ do
+ {
+ if (*cp != '0')
+ return false;
+ }
+ while (*++cp);
- return true;
+ return true;
}
default:
abort ();
@@ -310,7 +414,7 @@ null (VALUE *v)
/* Return true if CP takes the form of an integer. */
-static bool
+static bool _GL_ATTRIBUTE_PURE
looks_like_integer (char const *cp)
{
cp += (*cp == '-');
@@ -328,13 +432,15 @@ looks_like_integer (char const *cp)
static void
tostring (VALUE *v)
{
- char buf[INT_BUFSIZE_BOUND (intmax_t)];
-
switch (v->type)
{
case integer:
- v->u.s = xstrdup (imaxtostr (v->u.i, buf));
- v->type = string;
+ {
+ char *s = mpz_get_str (NULL, 10, v->u.i);
+ mpz_clear (v->u.i);
+ v->u.s = s;
+ v->type = string;
+ }
break;
case string:
break;
@@ -354,22 +460,38 @@ toarith (VALUE *v)
return true;
case string:
{
- intmax_t value;
-
- if (! looks_like_integer (v->u.s))
- return false;
- if (xstrtoimax (v->u.s, NULL, 10, &value, NULL) != LONGINT_OK)
- error (EXPR_FAILURE, ERANGE, "%s", v->u.s);
- free (v->u.s);
- v->u.i = value;
- v->type = integer;
- return true;
+ char *s = v->u.s;
+
+ if (! looks_like_integer (s))
+ return false;
+ if (mpz_init_set_str (v->u.i, s, 10) != 0 && !HAVE_GMP)
+ error (EXPR_FAILURE, ERANGE, "%s", (s));
+ free (s);
+ v->type = integer;
+ return true;
}
default:
abort ();
}
}
+/* Extract a size_t value from an integer value I.
+ If the value is negative, return SIZE_MAX.
+ If the value is too large, return SIZE_MAX - 1. */
+static size_t
+getsize (mpz_t i)
+{
+ if (mpz_sgn (i) < 0)
+ return SIZE_MAX;
+ if (mpz_fits_ulong_p (i))
+ {
+ unsigned long int ul = mpz_get_ui (i);
+ if (ul < SIZE_MAX)
+ return ul;
+ }
+ return SIZE_MAX - 1;
+}
+
/* Return true and advance if the next token matches STR exactly.
STR must not be NULL. */
@@ -417,7 +539,7 @@ trace (fxn)
static VALUE *
docolon (VALUE *sv, VALUE *pv)
{
- VALUE *v IF_LINT (= NULL);
+ VALUE *v IF_LINT ( = NULL);
const char *errmsg;
struct re_pattern_buffer re_buffer;
char fastmap[UCHAR_MAX + 1];
@@ -439,7 +561,7 @@ docolon (VALUE *sv, VALUE *pv)
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);
if (errmsg)
- error (EXPR_INVALID, 0, "%s", 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);
@@ -447,25 +569,25 @@ docolon (VALUE *sv, VALUE *pv)
{
/* Were \(...\) used? */
if (re_buffer.re_nsub > 0)
- {
- sv->u.s[re_regs.end[1]] = '\0';
- v = str_value (sv->u.s + re_regs.start[1]);
- }
+ {
+ sv->u.s[re_regs.end[1]] = '\0';
+ v = str_value (sv->u.s + re_regs.start[1]);
+ }
else
- v = int_value (matchlen);
+ v = int_value (matchlen);
}
else if (matchlen == -1)
{
/* Match failed -- return the right kind of null. */
if (re_buffer.re_nsub > 0)
- v = str_value ("");
+ v = str_value ("");
else
- v = int_value (0);
+ v = int_value (0);
}
else
error (EXPR_FAILURE,
- (matchlen == -2 ? errno : EOVERFLOW),
- _("error in regular expression matcher"));
+ (matchlen == -2 ? errno : EOVERFLOW),
+ _("error in regular expression matcher"));
if (0 < re_regs.num_regs)
{
@@ -494,7 +616,7 @@ eval7 (bool evaluate)
{
v = eval (evaluate);
if (!nextarg (")"))
- syntax_error ();
+ syntax_error ();
return v;
}
@@ -521,7 +643,7 @@ eval6 (bool evaluate)
if (nextarg ("+"))
{
if (nomoreargs ())
- syntax_error ();
+ syntax_error ();
return str_value (*args++);
}
else if (nextarg ("length"))
@@ -537,24 +659,25 @@ eval6 (bool evaluate)
l = eval6 (evaluate);
r = eval6 (evaluate);
if (evaluate)
- {
- v = docolon (l, r);
- freev (l);
- }
+ {
+ v = docolon (l, r);
+ freev (l);
+ }
else
- v = l;
+ v = l;
freev (r);
return v;
}
else if (nextarg ("index"))
{
+ size_t pos;
+
l = eval6 (evaluate);
r = eval6 (evaluate);
tostring (l);
tostring (r);
- v = int_value (strcspn (l->u.s, r->u.s) + 1);
- if (v->u.i == strlen (l->u.s) + 1)
- v->u.i = 0;
+ pos = strcspn (l->u.s, r->u.s);
+ v = int_value (l->u.s[pos] ? pos + 1 : 0);
freev (l);
freev (r);
return v;
@@ -567,20 +690,27 @@ eval6 (bool evaluate)
i2 = eval6 (evaluate);
tostring (l);
llen = strlen (l->u.s);
- if (!toarith (i1) || !toarith (i2)
- || llen < i1->u.i
- || i1->u.i <= 0 || i2->u.i <= 0)
- v = str_value ("");
+
+ if (!toarith (i1) || !toarith (i2))
+ v = str_value ("");
else
- {
- size_t vlen = MIN (i2->u.i, llen - i1->u.i + 1);
- char *vlim;
- v = xmalloc (sizeof *v);
- v->type = string;
- v->u.s = xmalloc (vlen + 1);
- vlim = mempcpy (v->u.s, l->u.s + i1->u.i - 1, vlen);
- *vlim = '\0';
- }
+ {
+ size_t pos = getsize (i1->u.i);
+ size_t len = getsize (i2->u.i);
+
+ if (llen < pos || pos == 0 || len == 0 || len == SIZE_MAX)
+ v = str_value ("");
+ else
+ {
+ size_t vlen = MIN (len, llen - pos + 1);
+ char *vlim;
+ v = xmalloc (sizeof *v);
+ v->type = string;
+ v->u.s = xmalloc (vlen + 1);
+ vlim = mempcpy (v->u.s, l->u.s + pos - 1, vlen);
+ *vlim = '\0';
+ }
+ }
freev (l);
freev (i1);
freev (i2);
@@ -607,18 +737,18 @@ eval5 (bool evaluate)
while (1)
{
if (nextarg (":"))
- {
- r = eval6 (evaluate);
- if (evaluate)
- {
- v = docolon (l, r);
- freev (l);
- l = v;
- }
- freev (r);
- }
+ {
+ r = eval6 (evaluate);
+ if (evaluate)
+ {
+ v = docolon (l, r);
+ freev (l);
+ l = v;
+ }
+ freev (r);
+ }
else
- return l;
+ return l;
}
}
@@ -630,7 +760,6 @@ eval4 (bool evaluate)
VALUE *l;
VALUE *r;
enum { multiply, divide, mod } fxn;
- intmax_t val = 0;
#ifdef EVAL_TRACE
trace ("eval4");
@@ -639,46 +768,26 @@ eval4 (bool evaluate)
while (1)
{
if (nextarg ("*"))
- fxn = multiply;
+ fxn = multiply;
else if (nextarg ("/"))
- fxn = divide;
+ fxn = divide;
else if (nextarg ("%"))
- fxn = mod;
+ fxn = mod;
else
- return l;
+ return l;
r = eval5 (evaluate);
if (evaluate)
- {
- if (!toarith (l) || !toarith (r))
- error (EXPR_INVALID, 0, _("non-numeric argument"));
- if (fxn == multiply)
- {
- val = l->u.i * r->u.i;
- if (! (l->u.i == 0 || r->u.i == 0
- || ((val < 0) == ((l->u.i < 0) ^ (r->u.i < 0))
- && val / l->u.i == r->u.i)))
- integer_overflow ('*');
- }
- else
- {
- if (r->u.i == 0)
- error (EXPR_INVALID, 0, _("division by zero"));
- if (l->u.i < - INTMAX_MAX && r->u.i == -1)
- {
- /* Some x86-style hosts raise an exception for
- INT_MIN / -1 and INT_MIN % -1, so handle these
- problematic cases specially. */
- if (fxn == divide)
- integer_overflow ('/');
- val = 0;
- }
- else
- val = fxn == divide ? l->u.i / r->u.i : l->u.i % r->u.i;
- }
- }
- freev (l);
+ {
+ if (!toarith (l) || !toarith (r))
+ error (EXPR_INVALID, 0, _("non-integer argument"));
+ if (fxn != multiply && mpz_sgn (r->u.i) == 0)
+ error (EXPR_INVALID, 0, _("division by zero"));
+ ((fxn == multiply ? mpz_mul
+ : fxn == divide ? mpz_tdiv_q
+ : mpz_tdiv_r)
+ (l->u.i, l->u.i, r->u.i));
+ }
freev (r);
- l = int_value (val);
}
}
@@ -690,7 +799,6 @@ eval3 (bool evaluate)
VALUE *l;
VALUE *r;
enum { plus, minus } fxn;
- intmax_t val = 0;
#ifdef EVAL_TRACE
trace ("eval3");
@@ -699,32 +807,19 @@ eval3 (bool evaluate)
while (1)
{
if (nextarg ("+"))
- fxn = plus;
+ fxn = plus;
else if (nextarg ("-"))
- fxn = minus;
+ fxn = minus;
else
- return l;
+ return l;
r = eval4 (evaluate);
if (evaluate)
- {
- if (!toarith (l) || !toarith (r))
- error (EXPR_INVALID, 0, _("non-numeric argument"));
- if (fxn == plus)
- {
- val = l->u.i + r->u.i;
- if ((val < l->u.i) != (r->u.i < 0))
- integer_overflow ('+');
- }
- else
- {
- val = l->u.i - r->u.i;
- if ((l->u.i < val) != (r->u.i < 0))
- integer_overflow ('-');
- }
- }
- freev (l);
+ {
+ if (!toarith (l) || !toarith (r))
+ error (EXPR_INVALID, 0, _("non-integer argument"));
+ (fxn == plus ? mpz_add : mpz_sub) (l->u.i, l->u.i, r->u.i);
+ }
freev (r);
- l = int_value (val);
}
}
@@ -743,62 +838,62 @@ eval2 (bool evaluate)
{
VALUE *r;
enum
- {
- less_than, less_equal, equal, not_equal, greater_equal, greater_than
- } fxn;
+ {
+ less_than, less_equal, equal, not_equal, greater_equal, greater_than
+ } fxn;
bool val = false;
if (nextarg ("<"))
- fxn = less_than;
+ fxn = less_than;
else if (nextarg ("<="))
- fxn = less_equal;
+ fxn = less_equal;
else if (nextarg ("=") || nextarg ("=="))
- fxn = equal;
+ fxn = equal;
else if (nextarg ("!="))
- fxn = not_equal;
+ fxn = not_equal;
else if (nextarg (">="))
- fxn = greater_equal;
+ fxn = greater_equal;
else if (nextarg (">"))
- fxn = greater_than;
+ fxn = greater_than;
else
- return l;
+ return l;
r = eval3 (evaluate);
if (evaluate)
- {
- int cmp;
- tostring (l);
- tostring (r);
-
- if (looks_like_integer (l->u.s) && looks_like_integer (r->u.s))
- cmp = strintcmp (l->u.s, r->u.s);
- else
- {
- errno = 0;
- cmp = strcoll (l->u.s, r->u.s);
-
- if (errno)
- {
- error (0, errno, _("string comparison failed"));
- error (0, 0, _("Set LC_ALL='C' to work around the problem."));
- error (EXPR_INVALID, 0,
- _("The strings compared were %s and %s."),
- quotearg_n_style (0, locale_quoting_style, l->u.s),
- quotearg_n_style (1, locale_quoting_style, r->u.s));
- }
- }
-
- switch (fxn)
- {
- case less_than: val = (cmp < 0); break;
- case less_equal: val = (cmp <= 0); break;
- case equal: val = (cmp == 0); break;
- case not_equal: val = (cmp != 0); break;
- case greater_equal: val = (cmp >= 0); break;
- case greater_than: val = (cmp > 0); break;
- default: abort ();
- }
- }
+ {
+ int cmp;
+ tostring (l);
+ tostring (r);
+
+ if (looks_like_integer (l->u.s) && looks_like_integer (r->u.s))
+ cmp = strintcmp (l->u.s, r->u.s);
+ else
+ {
+ errno = 0;
+ cmp = strcoll (l->u.s, r->u.s);
+
+ if (errno)
+ {
+ error (0, errno, _("string comparison failed"));
+ error (0, 0, _("set LC_ALL='C' to work around the problem"));
+ error (EXPR_INVALID, 0,
+ _("the strings compared were %s and %s"),
+ quotearg_n_style (0, locale_quoting_style, l->u.s),
+ quotearg_n_style (1, locale_quoting_style, r->u.s));
+ }
+ }
+
+ switch (fxn)
+ {
+ case less_than: val = (cmp < 0); break;
+ case less_equal: val = (cmp <= 0); break;
+ case equal: val = (cmp == 0); break;
+ case not_equal: val = (cmp != 0); break;
+ case greater_equal: val = (cmp >= 0); break;
+ case greater_than: val = (cmp > 0); break;
+ default: abort ();
+ }
+ }
freev (l);
freev (r);
@@ -821,19 +916,19 @@ eval1 (bool evaluate)
while (1)
{
if (nextarg ("&"))
- {
- r = eval2 (evaluate & ~ null (l));
- if (null (l) || null (r))
- {
- freev (l);
- freev (r);
- l = int_value (0);
- }
- else
- freev (r);
- }
+ {
+ r = eval2 (evaluate && !null (l));
+ if (null (l) || null (r))
+ {
+ freev (l);
+ freev (r);
+ l = int_value (0);
+ }
+ else
+ freev (r);
+ }
else
- return l;
+ return l;
}
}
@@ -852,22 +947,22 @@ eval (bool evaluate)
while (1)
{
if (nextarg ("|"))
- {
- r = eval1 (evaluate & null (l));
- if (null (l))
- {
- freev (l);
- l = r;
- if (null (l))
- {
- freev (l);
- l = int_value (0);
- }
- }
- else
- freev (r);
- }
+ {
+ r = eval1 (evaluate && null (l));
+ if (null (l))
+ {
+ freev (l);
+ l = r;
+ if (null (l))
+ {
+ freev (l);
+ l = int_value (0);
+ }
+ }
+ else
+ freev (r);
+ }
else
- return l;
+ return l;
}
}
diff --git a/src/extent-scan.c b/src/extent-scan.c
new file mode 100644
index 0000000..0e3046f
--- /dev/null
+++ b/src/extent-scan.c
@@ -0,0 +1,227 @@
+/* extent-scan.c -- core functions for scanning extents
+ Copyright (C) 2010-2016 Free Software Foundation, Inc.
+
+ This 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/>.
+
+ Written by Jie Liu (jeff.liu@oracle.com). */
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <assert.h>
+
+#include "system.h"
+#include "extent-scan.h"
+#include "fiemap.h"
+#include "xstrtol.h"
+
+
+/* Work around Linux kernel issues on BTRFS and EXT4. */
+static bool
+extent_need_sync (void)
+{
+ /* For now always return true, to be on the safe side.
+ If/when FIEMAP semantics are well defined (before SEEK_HOLE support
+ is usable) and kernels implementing them are in use, we may relax
+ this once again. */
+ return true;
+
+#if FIEMAP_BEHAVIOR_IS_DEFINED_AND_USABLE
+ static int need_sync = -1;
+
+ if (need_sync == -1)
+ {
+ struct utsname name;
+ need_sync = 0; /* No workaround by default. */
+
+# ifdef __linux__
+ if (uname (&name) != -1 && STRNCMP_LIT (name.release, "2.6.") == 0)
+ {
+ unsigned long val;
+ if (xstrtoul (name.release + 4, NULL, 10, &val, NULL) == LONGINT_OK)
+ {
+ if (val < 39)
+ need_sync = 1;
+ }
+ }
+# endif
+ }
+
+ return need_sync;
+#endif
+}
+
+/* Allocate space for struct extent_scan, initialize the entries if
+ necessary and return it as the input argument of extent_scan_read(). */
+extern void
+extent_scan_init (int src_fd, struct extent_scan *scan)
+{
+ scan->fd = src_fd;
+ scan->ei_count = 0;
+ scan->ext_info = NULL;
+ scan->scan_start = 0;
+ scan->initial_scan_failed = false;
+ scan->hit_final_extent = false;
+ scan->fm_flags = extent_need_sync () ? FIEMAP_FLAG_SYNC : 0;
+}
+
+#ifdef __linux__
+# ifndef FS_IOC_FIEMAP
+# define FS_IOC_FIEMAP _IOWR ('f', 11, struct fiemap)
+# endif
+/* Call ioctl(2) with FS_IOC_FIEMAP (available in linux 2.6.27) to
+ obtain a map of file extents excluding holes. */
+extern bool
+extent_scan_read (struct extent_scan *scan)
+{
+ unsigned int si = 0;
+ struct extent_info *last_ei = scan->ext_info;
+
+ while (true)
+ {
+ union { struct fiemap f; char c[4096]; } fiemap_buf;
+ struct fiemap *fiemap = &fiemap_buf.f;
+ struct fiemap_extent *fm_extents = &fiemap->fm_extents[0];
+ enum { count = (sizeof fiemap_buf - sizeof *fiemap)/sizeof *fm_extents };
+ verify (count > 1);
+
+ /* This is required at least to initialize fiemap->fm_start,
+ but also serves (in mid 2010) to appease valgrind, which
+ appears not to know the semantics of the FIEMAP ioctl. */
+ memset (&fiemap_buf, 0, sizeof fiemap_buf);
+
+ fiemap->fm_start = scan->scan_start;
+ fiemap->fm_flags = scan->fm_flags;
+ fiemap->fm_extent_count = count;
+ fiemap->fm_length = FIEMAP_MAX_OFFSET - scan->scan_start;
+
+ /* Fall back to the standard copy if call ioctl(2) failed for
+ the first time. */
+ if (ioctl (scan->fd, FS_IOC_FIEMAP, fiemap) < 0)
+ {
+ if (scan->scan_start == 0)
+ scan->initial_scan_failed = true;
+ return false;
+ }
+
+ /* If 0 extents are returned, then no more scans are needed. */
+ if (fiemap->fm_mapped_extents == 0)
+ {
+ scan->hit_final_extent = true;
+ return scan->scan_start != 0;
+ }
+
+ assert (scan->ei_count <= SIZE_MAX - fiemap->fm_mapped_extents);
+ scan->ei_count += fiemap->fm_mapped_extents;
+ {
+ /* last_ei points into a buffer that may be freed via xnrealloc.
+ Record its offset and adjust after allocation. */
+ size_t prev_idx = last_ei - scan->ext_info;
+ scan->ext_info = xnrealloc (scan->ext_info, scan->ei_count,
+ sizeof (struct extent_info));
+ last_ei = scan->ext_info + prev_idx;
+ }
+
+ unsigned int i = 0;
+ for (i = 0; i < fiemap->fm_mapped_extents; i++)
+ {
+ assert (fm_extents[i].fe_logical
+ <= OFF_T_MAX - fm_extents[i].fe_length);
+
+ verify (sizeof last_ei->ext_flags >= sizeof fm_extents->fe_flags);
+
+ if (si && last_ei->ext_flags
+ == (fm_extents[i].fe_flags & ~FIEMAP_EXTENT_LAST)
+ && (last_ei->ext_logical + last_ei->ext_length
+ == fm_extents[i].fe_logical))
+ {
+ /* Merge previous with last. */
+ last_ei->ext_length += fm_extents[i].fe_length;
+ /* Copy flags in case different. */
+ last_ei->ext_flags = fm_extents[i].fe_flags;
+ }
+ else if ((si == 0 && scan->scan_start > fm_extents[i].fe_logical)
+ || (si && (last_ei->ext_logical + last_ei->ext_length
+ > fm_extents[i].fe_logical)))
+ {
+ /* BTRFS before 2.6.38 could return overlapping extents
+ for sparse files. We adjust the returned extents
+ rather than failing, as otherwise it would be inefficient
+ to detect this on the initial scan. */
+ uint64_t new_logical;
+ uint64_t length_adjust;
+ if (si == 0)
+ new_logical = scan->scan_start;
+ else
+ {
+ /* We could return here if scan->scan_start == 0
+ but don't so as to minimize special cases. */
+ new_logical = last_ei->ext_logical + last_ei->ext_length;
+ }
+ length_adjust = new_logical - fm_extents[i].fe_logical;
+ /* If an extent is contained within the previous one, fail. */
+ if (length_adjust < fm_extents[i].fe_length)
+ {
+ if (scan->scan_start == 0)
+ scan->initial_scan_failed = true;
+ return false;
+ }
+ fm_extents[i].fe_logical = new_logical;
+ fm_extents[i].fe_length -= length_adjust;
+ /* Process the adjusted extent again. */
+ i--;
+ continue;
+ }
+ else
+ {
+ last_ei = scan->ext_info + si;
+ last_ei->ext_logical = fm_extents[i].fe_logical;
+ last_ei->ext_length = fm_extents[i].fe_length;
+ last_ei->ext_flags = fm_extents[i].fe_flags;
+ si++;
+ }
+ }
+
+ if (last_ei->ext_flags & FIEMAP_EXTENT_LAST)
+ scan->hit_final_extent = true;
+
+ /* If we have enough extents, discard the last as it might
+ be merged with one from the next scan. */
+ if (si > count && !scan->hit_final_extent)
+ last_ei = scan->ext_info + --si - 1;
+
+ /* We don't bother reallocating any trailing slots. */
+ scan->ei_count = si;
+
+ if (scan->hit_final_extent)
+ break;
+ else
+ scan->scan_start = last_ei->ext_logical + last_ei->ext_length;
+
+ if (si >= count)
+ break;
+ }
+
+ return true;
+}
+#else
+extern bool
+extent_scan_read (struct extent_scan *scan _GL_UNUSED)
+{
+ scan->initial_scan_failed = true;
+ errno = ENOTSUP;
+ return false;
+}
+#endif
diff --git a/src/extent-scan.h b/src/extent-scan.h
new file mode 100644
index 0000000..747db79
--- /dev/null
+++ b/src/extent-scan.h
@@ -0,0 +1,73 @@
+/* core functions for efficient reading sparse files
+ Copyright (C) 2010-2016 Free Software Foundation, Inc.
+
+ This 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/>.
+
+ Written by Jie Liu (jeff.liu@oracle.com). */
+
+#ifndef EXTENT_SCAN_H
+# define EXTENT_SCAN_H
+
+/* Structure used to store information of each extent. */
+struct extent_info
+{
+ /* Logical offset of an extent. */
+ off_t ext_logical;
+
+ /* Extent length. */
+ off_t ext_length;
+
+ /* Extent flags, use it for FIEMAP only, or set it to zero. */
+ unsigned int ext_flags;
+};
+
+/* Structure used to reserve extent scan information per file. */
+struct extent_scan
+{
+ /* File descriptor of extent scan run against. */
+ int fd;
+
+ /* Next scan start offset. */
+ off_t scan_start;
+
+ /* Flags to use for scan. */
+ unsigned int fm_flags;
+
+ /* How many extent info returned for a scan. */
+ size_t ei_count;
+
+ /* If true, fall back to a normal copy, either set by the
+ failure of ioctl(2) for FIEMAP or lseek(2) with SEEK_DATA. */
+ bool initial_scan_failed;
+
+ /* If true, the total extent scan per file has been finished. */
+ bool hit_final_extent;
+
+ /* Extent information: a malloc'd array of ei_count structs. */
+ struct extent_info *ext_info;
+};
+
+void extent_scan_init (int src_fd, struct extent_scan *scan);
+
+bool extent_scan_read (struct extent_scan *scan);
+
+static inline void
+extent_scan_free (struct extent_scan *scan)
+{
+ free (scan->ext_info);
+ scan->ext_info = NULL;
+ scan->ei_count = 0;
+}
+
+#endif /* EXTENT_SCAN_H */
diff --git a/src/extract-magic b/src/extract-magic
index 2e9e871..73e6d23 100644
--- a/src/extract-magic
+++ b/src/extract-magic
@@ -1,12 +1,12 @@
#!/usr/bin/perl -w
-# Derive #define directives from specially formatted `case ...:' statements.
+# Derive #define directives from specially formatted 'case ...:' statements.
-# Copyright (C) 2003, 2005 Free Software Foundation, Inc.
+# Copyright (C) 2003-2016 Free Software Foundation, Inc.
-# This program is free software; you can redistribute it and/or modify
+# This 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 2, or (at your option)
-# any later version.
+# 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
@@ -14,8 +14,7 @@
# 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, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
use strict;
@@ -55,7 +54,7 @@ sub usage ($)
my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR);
if ($exit_code != 0)
{
- print $STREAM "Try `$ME --help' for more information.\n";
+ print $STREAM "Try '$ME --help' for more information.\n";
}
else
{
@@ -66,10 +65,15 @@ FIXME: describe
OPTIONS:
- Derive #define directives from specially formatted `case ...:' statements.
+ There are two modes of operation, the default, which is to emit
+ #define directives derived from specially formatted 'case' statements,
+ and that with --local, which is to emit a static inline function
+ mapping S_MAGIC_* values to 1, 0, -1, corresponding to known-local,
+ known-remote/distributed/network and unknown, respectively.
- --help display this help and exit
- --version output version information and exit
+ --local emit an is_local_fs_type function
+ --help display this help and exit
+ --version output version information and exit
EOF
}
@@ -77,8 +81,12 @@ EOF
}
{
+ # The default is to print S_MAGIC_* definitions.
+ my $emit_magic = 1;
+
GetOptions
(
+ local => sub { $emit_magic = 0 },
help => sub { usage 0 },
version => sub { print "$ME version $VERSION\n"; exit },
) or usage 1;
@@ -95,40 +103,59 @@ EOF
my $file = $ARGV[0];
open FH, $file
- or die "$ME: can't open `$file' for reading: $!\n";
+ or die "$ME: can't open '$file' for reading: $!\n";
# For each line like this:
# case S_MAGIC_ROMFS: /* 0x7275 */
# emit one like this:
# # define S_MAGIC_ROMFS 0x7275
- # Fail if there is a `case S_MAGIC_.*' line without
+ # Fail if there is a 'case S_MAGIC_.*' line without
# a properly formed comment.
- print <<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. */
+EOF
+ my $magic_comment = <<EOF;
/* Define the magic numbers as given by statfs(2).
Please send additions to bug-coreutils\@gnu.org and meskes\@debian.org.
This file is generated automatically from $file. */
-
-#if defined __linux__
EOF
+ print $emit_magic ? $magic_comment : $map_comment;
+
+ $emit_magic
+ and print "\n#if defined __linux__\n";
+ $emit_magic
+ or print "static inline int\n"
+ . "is_local_fs_type (unsigned long int magic)\n"
+ . "{\n switch (magic)\n {\n";
while (defined (my $line = <FH>))
{
$line =~ /^[ \t]+case S_MAGIC_/
- or next;
- $line =~ m!^[ \t]+case (S_MAGIC_\w+): /\* (0x[0-9A-Fa-f]+) \*/$!
- or (warn "$ME:$file:$.: malformed case S_MAGIC_... line"),
- $fail = 1, next;
+ or next;
+ $line =~
+ m!^[ \t]+case (S_MAGIC_\w+): /\* (0x[0-9A-Fa-f]+) (local|remote) \*/!
+ or (warn "$ME:$file:$.: malformed case S_MAGIC_... line"),
+ $fail = 1, next;
my $name = $1;
- my $value = $2;
- print "# define $name $value\n";
+ my $magic = $2;
+ my $local = $3 eq 'local' ? 1 : 0;
+ print $emit_magic
+ ? "# define $name $magic\n"
+ : " case $name: return $local;\n";
}
- print <<\EOF;
+ $emit_magic
+ and print <<\EOF;
#elif defined __GNU__
# include <hurd/hurd_types.h>
#endif
EOF
+ $emit_magic
+ or printf " default: return -1;\n }\n}\n";
+
close FH;
exit $fail;
diff --git a/src/factor.c b/src/factor.c
index dc8f1cc..8fee759 100644
--- a/src/factor.c
+++ b/src/factor.c
@@ -1,10 +1,10 @@
/* factor -- print prime factors of n.
- Copyright (C) 86, 1995-2005 Free Software Foundation, Inc.
+ Copyright (C) 1986-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,158 +12,2540 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Originally written by Paul Rubin <phr@ocf.berkeley.edu>.
+ Adapted for GNU, fixed to factor UINT_MAX by Jim Meyering.
+ Arbitrary-precision code adapted by James Youngman from Torbjörn
+ Granlund's factorize.c, from GNU MP version 4.2.2.
+ In 2012, the core was rewritten by Torbjörn Granlund and Niels Möller.
+ Contains code from GNU MP. */
+
+/* Efficiently factor numbers that fit in one or two words (word = uintmax_t),
+ or, with GMP, numbers of any size.
+
+ Code organisation:
+
+ There are several variants of many functions, for handling one word, two
+ words, and GMP's mpz_t type. If the one-word variant is called foo, the
+ two-word variant will be foo2, and the one for mpz_t will be mp_foo. In
+ some cases, the plain function variants will handle both one-word and
+ two-word numbers, evidenced by function arguments.
+
+ The factoring code for two words will fall into the code for one word when
+ progress allows that.
+
+ Using GMP is optional. Define HAVE_GMP to make this code include GMP
+ factoring code. The GMP factoring code is based on GMP's demos/factorize.c
+ (last synced 2012-09-07). The GMP-based factoring code will stay in GMP
+ factoring code even if numbers get small enough for using the two-word
+ code.
+
+ Algorithm:
+
+ (1) Perform trial division using a small primes table, but without hardware
+ division since the primes table store inverses modulo the word base.
+ (The GMP variant of this code doesn't make use of the precomputed
+ inverses, but instead relies on GMP for fast divisibility testing.)
+ (2) Check the nature of any non-factored part using Miller-Rabin for
+ detecting composites, and Lucas for detecting primes.
+ (3) Factor any remaining composite part using the Pollard-Brent rho
+ algorithm or if USE_SQUFOF is defined to 1, try that first.
+ Status of found factors are checked again using Miller-Rabin and Lucas.
+
+ We prefer using Hensel norm in the divisions, not the more familiar
+ Euclidian norm, since the former leads to much faster code. In the
+ Pollard-Brent rho code and the prime testing code, we use Montgomery's
+ trick of multiplying all n-residues by the word base, allowing cheap Hensel
+ reductions mod n.
+
+ Improvements:
+
+ * Use modular inverses also for exact division in the Lucas code, and
+ elsewhere. A problem is to locate the inverses not from an index, but
+ from a prime. We might instead compute the inverse on-the-fly.
+
+ * Tune trial division table size (not forgetting that this is a standalone
+ program where the table will be read from disk for each invocation).
+
+ * Implement less naive powm, using k-ary exponentiation for k = 3 or
+ perhaps k = 4.
+
+ * Try to speed trial division code for single uintmax_t numbers, i.e., the
+ code using DIVBLOCK. It currently runs at 2 cycles per prime (Intel SBR,
+ IBR), 3 cycles per prime (AMD Stars) and 5 cycles per prime (AMD BD) when
+ using gcc 4.6 and 4.7. Some software pipelining should help; 1, 2, and 4
+ respectively cycles ought to be possible.
+
+ * The redcify function could be vastly improved by using (plain Euclidian)
+ pre-inversion (such as GMP's invert_limb) and udiv_qrnnd_preinv (from
+ GMP's gmp-impl.h). The redcify2 function could be vastly improved using
+ similar methoods. These functions currently dominate run time when using
+ the -w option.
+*/
+
+/* Whether to recursively factor to prove primality,
+ or run faster probabilistic tests. */
+#ifndef PROVE_PRIMALITY
+# define PROVE_PRIMALITY 1
+#endif
+
+/* Faster for certain ranges but less general. */
+#ifndef USE_SQUFOF
+# define USE_SQUFOF 0
+#endif
+
+/* Output SQUFOF statistics. */
+#ifndef STAT_SQUFOF
+# define STAT_SQUFOF 0
+#endif
-/* Written by Paul Rubin <phr@ocf.berkeley.edu>.
- Adapted for GNU, fixed to factor UINT_MAX by Jim Meyering. */
#include <config.h>
#include <getopt.h>
#include <stdio.h>
-#include <sys/types.h>
+#if HAVE_GMP
+# include <gmp.h>
+# if !HAVE_DECL_MPZ_INITS
+# include <stdarg.h>
+# endif
+#endif
#include <assert.h>
-#define NDEBUG 1
#include "system.h"
#include "error.h"
-#include "inttostr.h"
-#include "long-options.h"
+#include "full-write.h"
#include "quote.h"
#include "readtokens.h"
#include "xstrtol.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "factor"
-#define AUTHORS "Paul Rubin"
+#define AUTHORS \
+ proper_name ("Paul Rubin"), \
+ proper_name_utf8 ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \
+ proper_name_utf8 ("Niels Moller", "Niels M\303\266ller")
/* Token delimiters when reading from a file. */
#define DELIM "\n\t "
-/* The maximum number of factors, including -1, for negative numbers. */
-#define MAX_N_FACTORS (sizeof (uintmax_t) * CHAR_BIT)
+#ifndef USE_LONGLONG_H
+/* With the way we use longlong.h, it's only safe to use
+ when UWtype = UHWtype, as there were various cases
+ (as can be seen in the history for longlong.h) where
+ for example, _LP64 was required to enable W_TYPE_SIZE==64 code,
+ to avoid compile time or run time issues. */
+# if LONG_MAX == INTMAX_MAX
+# define USE_LONGLONG_H 1
+# endif
+#endif
-/* The trial divisor increment wheel. Use it to skip over divisors that
- are composites of 2, 3, 5, 7, or 11. The part from WHEEL_START up to
- WHEEL_END is reused periodically, while the "lead in" is used to test
- for those primes and to jump onto the wheel. For more information, see
- http://www.utm.edu/research/primes/glossary/WheelFactorization.html */
+#if USE_LONGLONG_H
-#include "wheel-size.h" /* For the definition of WHEEL_SIZE. */
-static const unsigned char wheel_tab[] =
- {
-#include "wheel.h"
- };
+/* Make definitions for longlong.h to make it do what it can do for us */
-#define WHEEL_START (wheel_tab + WHEEL_SIZE)
-#define WHEEL_END (wheel_tab + (sizeof wheel_tab / sizeof wheel_tab[0]))
+/* bitcount for uintmax_t */
+# if UINTMAX_MAX == UINT32_MAX
+# define W_TYPE_SIZE 32
+# elif UINTMAX_MAX == UINT64_MAX
+# define W_TYPE_SIZE 64
+# elif UINTMAX_MAX == UINT128_MAX
+# define W_TYPE_SIZE 128
+# endif
-/* The name this program was run with. */
-char *program_name;
+# define UWtype uintmax_t
+# define UHWtype unsigned long int
+# undef UDWtype
+# if HAVE_ATTRIBUTE_MODE
+typedef unsigned int UQItype __attribute__ ((mode (QI)));
+typedef int SItype __attribute__ ((mode (SI)));
+typedef unsigned int USItype __attribute__ ((mode (SI)));
+typedef int DItype __attribute__ ((mode (DI)));
+typedef unsigned int UDItype __attribute__ ((mode (DI)));
+# else
+typedef unsigned char UQItype;
+typedef long SItype;
+typedef unsigned long int USItype;
+# if HAVE_LONG_LONG_INT
+typedef long long int DItype;
+typedef unsigned long long int UDItype;
+# else /* Assume `long' gives us a wide enough type. Needed for hppa2.0w. */
+typedef long int DItype;
+typedef unsigned long int UDItype;
+# endif
+# endif
+# define LONGLONG_STANDALONE /* Don't require GMP's longlong.h mdep files */
+# define ASSERT(x) /* FIXME make longlong.h really standalone */
+# define __GMP_DECLSPEC /* FIXME make longlong.h really standalone */
+# define __clz_tab factor_clz_tab /* Rename to avoid glibc collision */
+# ifndef __GMP_GNUC_PREREQ
+# define __GMP_GNUC_PREREQ(a,b) 1
+# endif
-void
-usage (int status)
+/* These stub macros are only used in longlong.h in certain system compiler
+ combinations, so ensure usage to avoid -Wunused-macros warnings. */
+# if __GMP_GNUC_PREREQ (1,1) && defined __clz_tab
+ASSERT (1)
+__GMP_DECLSPEC
+# endif
+
+# if _ARCH_PPC
+# define HAVE_HOST_CPU_FAMILY_powerpc 1
+# endif
+# include "longlong.h"
+# ifdef COUNT_LEADING_ZEROS_NEED_CLZ_TAB
+const unsigned char factor_clz_tab[129] =
{
- if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ 1,2,3,3,4,4,4,4,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 9
+};
+# endif
+
+#else /* not USE_LONGLONG_H */
+
+# define W_TYPE_SIZE (8 * sizeof (uintmax_t))
+# define __ll_B ((uintmax_t) 1 << (W_TYPE_SIZE / 2))
+# define __ll_lowpart(t) ((uintmax_t) (t) & (__ll_B - 1))
+# define __ll_highpart(t) ((uintmax_t) (t) >> (W_TYPE_SIZE / 2))
+
+#endif
+
+#if !defined __clz_tab && !defined UHWtype
+/* Without this seemingly useless conditional, gcc -Wunused-macros
+ warns that each of the two tested macros is unused on Fedora 18.
+ FIXME: this is just an ugly band-aid. Fix it properly. */
+#endif
+
+/* 2*3*5*7*11...*101 is 128 bits, and has 26 prime factors */
+#define MAX_NFACTS 26
+
+enum
+{
+ DEV_DEBUG_OPTION = CHAR_MAX + 1
+};
+
+static struct option const long_options[] =
+{
+ {"-debug", no_argument, NULL, DEV_DEBUG_OPTION},
+ {GETOPT_HELP_OPTION_DECL},
+ {GETOPT_VERSION_OPTION_DECL},
+ {NULL, 0, NULL, 0}
+};
+
+struct factors
+{
+ uintmax_t plarge[2]; /* Can have a single large factor */
+ uintmax_t p[MAX_NFACTS];
+ unsigned char e[MAX_NFACTS];
+ unsigned char nfactors;
+};
+
+#if HAVE_GMP
+struct mp_factors
+{
+ mpz_t *p;
+ unsigned long int *e;
+ unsigned long int nfactors;
+};
+#endif
+
+static void factor (uintmax_t, uintmax_t, struct factors *);
+
+#ifndef umul_ppmm
+# define umul_ppmm(w1, w0, u, v) \
+ do { \
+ uintmax_t __x0, __x1, __x2, __x3; \
+ unsigned long int __ul, __vl, __uh, __vh; \
+ uintmax_t __u = (u), __v = (v); \
+ \
+ __ul = __ll_lowpart (__u); \
+ __uh = __ll_highpart (__u); \
+ __vl = __ll_lowpart (__v); \
+ __vh = __ll_highpart (__v); \
+ \
+ __x0 = (uintmax_t) __ul * __vl; \
+ __x1 = (uintmax_t) __ul * __vh; \
+ __x2 = (uintmax_t) __uh * __vl; \
+ __x3 = (uintmax_t) __uh * __vh; \
+ \
+ __x1 += __ll_highpart (__x0);/* this can't give carry */ \
+ __x1 += __x2; /* but this indeed can */ \
+ if (__x1 < __x2) /* did we get it? */ \
+ __x3 += __ll_B; /* yes, add it in the proper pos. */ \
+ \
+ (w1) = __x3 + __ll_highpart (__x1); \
+ (w0) = (__x1 << W_TYPE_SIZE / 2) + __ll_lowpart (__x0); \
+ } while (0)
+#endif
+
+#if !defined udiv_qrnnd || defined UDIV_NEEDS_NORMALIZATION
+/* Define our own, not needing normalization. This function is
+ currently not performance critical, so keep it simple. Similar to
+ the mod macro below. */
+# undef udiv_qrnnd
+# define udiv_qrnnd(q, r, n1, n0, d) \
+ do { \
+ uintmax_t __d1, __d0, __q, __r1, __r0; \
+ \
+ assert ((n1) < (d)); \
+ __d1 = (d); __d0 = 0; \
+ __r1 = (n1); __r0 = (n0); \
+ __q = 0; \
+ for (unsigned int __i = W_TYPE_SIZE; __i > 0; __i--) \
+ { \
+ rsh2 (__d1, __d0, __d1, __d0, 1); \
+ __q <<= 1; \
+ if (ge2 (__r1, __r0, __d1, __d0)) \
+ { \
+ __q++; \
+ sub_ddmmss (__r1, __r0, __r1, __r0, __d1, __d0); \
+ } \
+ } \
+ (r) = __r0; \
+ (q) = __q; \
+ } while (0)
+#endif
+
+#if !defined add_ssaaaa
+# define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ do { \
+ uintmax_t _add_x; \
+ _add_x = (al) + (bl); \
+ (sh) = (ah) + (bh) + (_add_x < (al)); \
+ (sl) = _add_x; \
+ } while (0)
+#endif
+
+#define rsh2(rh, rl, ah, al, cnt) \
+ do { \
+ (rl) = ((ah) << (W_TYPE_SIZE - (cnt))) | ((al) >> (cnt)); \
+ (rh) = (ah) >> (cnt); \
+ } while (0)
+
+#define lsh2(rh, rl, ah, al, cnt) \
+ do { \
+ (rh) = ((ah) << cnt) | ((al) >> (W_TYPE_SIZE - (cnt))); \
+ (rl) = (al) << (cnt); \
+ } while (0)
+
+#define ge2(ah, al, bh, bl) \
+ ((ah) > (bh) || ((ah) == (bh) && (al) >= (bl)))
+
+#define gt2(ah, al, bh, bl) \
+ ((ah) > (bh) || ((ah) == (bh) && (al) > (bl)))
+
+#ifndef sub_ddmmss
+# define sub_ddmmss(rh, rl, ah, al, bh, bl) \
+ do { \
+ uintmax_t _cy; \
+ _cy = (al) < (bl); \
+ (rl) = (al) - (bl); \
+ (rh) = (ah) - (bh) - _cy; \
+ } while (0)
+#endif
+
+#ifndef count_leading_zeros
+# define count_leading_zeros(count, x) do { \
+ uintmax_t __clz_x = (x); \
+ unsigned int __clz_c; \
+ for (__clz_c = 0; \
+ (__clz_x & ((uintmax_t) 0xff << (W_TYPE_SIZE - 8))) == 0; \
+ __clz_c += 8) \
+ __clz_x <<= 8; \
+ for (; (intmax_t)__clz_x >= 0; __clz_c++) \
+ __clz_x <<= 1; \
+ (count) = __clz_c; \
+ } while (0)
+#endif
+
+#ifndef count_trailing_zeros
+# define count_trailing_zeros(count, x) do { \
+ uintmax_t __ctz_x = (x); \
+ unsigned int __ctz_c = 0; \
+ while ((__ctz_x & 1) == 0) \
+ { \
+ __ctz_x >>= 1; \
+ __ctz_c++; \
+ } \
+ (count) = __ctz_c; \
+ } while (0)
+#endif
+
+/* Requires that a < n and b <= n */
+#define submod(r,a,b,n) \
+ do { \
+ uintmax_t _t = - (uintmax_t) (a < b); \
+ (r) = ((n) & _t) + (a) - (b); \
+ } while (0)
+
+#define addmod(r,a,b,n) \
+ submod ((r), (a), ((n) - (b)), (n))
+
+/* Modular two-word addition and subtraction. For performance reasons, the
+ most significant bit of n1 must be clear. The destination variables must be
+ distinct from the mod operand. */
+#define addmod2(r1, r0, a1, a0, b1, b0, n1, n0) \
+ do { \
+ add_ssaaaa ((r1), (r0), (a1), (a0), (b1), (b0)); \
+ if (ge2 ((r1), (r0), (n1), (n0))) \
+ sub_ddmmss ((r1), (r0), (r1), (r0), (n1), (n0)); \
+ } while (0)
+#define submod2(r1, r0, a1, a0, b1, b0, n1, n0) \
+ do { \
+ sub_ddmmss ((r1), (r0), (a1), (a0), (b1), (b0)); \
+ if ((intmax_t) (r1) < 0) \
+ add_ssaaaa ((r1), (r0), (r1), (r0), (n1), (n0)); \
+ } while (0)
+
+#define HIGHBIT_TO_MASK(x) \
+ (((intmax_t)-1 >> 1) < 0 \
+ ? (uintmax_t)((intmax_t)(x) >> (W_TYPE_SIZE - 1)) \
+ : ((x) & ((uintmax_t) 1 << (W_TYPE_SIZE - 1)) \
+ ? UINTMAX_MAX : (uintmax_t) 0))
+
+/* Compute r = a mod d, where r = <*t1,retval>, a = <a1,a0>, d = <d1,d0>.
+ Requires that d1 != 0. */
+static uintmax_t
+mod2 (uintmax_t *r1, uintmax_t a1, uintmax_t a0, uintmax_t d1, uintmax_t d0)
+{
+ int cntd, cnta;
+
+ assert (d1 != 0);
+
+ if (a1 == 0)
+ {
+ *r1 = 0;
+ return a0;
+ }
+
+ count_leading_zeros (cntd, d1);
+ count_leading_zeros (cnta, a1);
+ int cnt = cntd - cnta;
+ lsh2 (d1, d0, d1, d0, cnt);
+ for (int i = 0; i < cnt; i++)
+ {
+ if (ge2 (a1, a0, d1, d0))
+ sub_ddmmss (a1, a0, a1, a0, d1, d0);
+ rsh2 (d1, d0, d1, d0, 1);
+ }
+
+ *r1 = a1;
+ return a0;
+}
+
+static uintmax_t _GL_ATTRIBUTE_CONST
+gcd_odd (uintmax_t a, uintmax_t b)
+{
+ if ( (b & 1) == 0)
+ {
+ uintmax_t t = b;
+ b = a;
+ a = t;
+ }
+ if (a == 0)
+ return b;
+
+ /* Take out least significant one bit, to make room for sign */
+ b >>= 1;
+
+ for (;;)
+ {
+ uintmax_t t;
+ uintmax_t bgta;
+
+ while ((a & 1) == 0)
+ a >>= 1;
+ a >>= 1;
+
+ t = a - b;
+ if (t == 0)
+ return (a << 1) + 1;
+
+ bgta = HIGHBIT_TO_MASK (t);
+
+ /* b <-- min (a, b) */
+ b += (bgta & t);
+
+ /* a <-- |a - b| */
+ a = (t ^ bgta) - bgta;
+ }
+}
+
+static uintmax_t
+gcd2_odd (uintmax_t *r1, uintmax_t a1, uintmax_t a0, uintmax_t b1, uintmax_t b0)
+{
+ while ((a0 & 1) == 0)
+ rsh2 (a1, a0, a1, a0, 1);
+ while ((b0 & 1) == 0)
+ rsh2 (b1, b0, b1, b0, 1);
+
+ for (;;)
+ {
+ if ((b1 | a1) == 0)
+ {
+ *r1 = 0;
+ return gcd_odd (b0, a0);
+ }
+
+ if (gt2 (a1, a0, b1, b0))
+ {
+ sub_ddmmss (a1, a0, a1, a0, b1, b0);
+ do
+ rsh2 (a1, a0, a1, a0, 1);
+ while ((a0 & 1) == 0);
+ }
+ else if (gt2 (b1, b0, a1, a0))
+ {
+ sub_ddmmss (b1, b0, b1, b0, a1, a0);
+ do
+ rsh2 (b1, b0, b1, b0, 1);
+ while ((b0 & 1) == 0);
+ }
+ else
+ break;
+ }
+
+ *r1 = a1;
+ return a0;
+}
+
+static void
+factor_insert_multiplicity (struct factors *factors,
+ uintmax_t prime, unsigned int m)
+{
+ unsigned int nfactors = factors->nfactors;
+ uintmax_t *p = factors->p;
+ unsigned char *e = factors->e;
+
+ /* Locate position for insert new or increment e. */
+ int i;
+ for (i = nfactors - 1; i >= 0; i--)
+ {
+ if (p[i] <= prime)
+ break;
+ }
+
+ if (i < 0 || p[i] != prime)
+ {
+ for (int j = nfactors - 1; j > i; j--)
+ {
+ p[j + 1] = p[j];
+ e[j + 1] = e[j];
+ }
+ p[i + 1] = prime;
+ e[i + 1] = m;
+ factors->nfactors = nfactors + 1;
+ }
else
{
- printf (_("\
-Usage: %s [NUMBER]...\n\
- or: %s OPTION\n\
-"),
- program_name, program_name);
- fputs (_("\
-Print the prime factors of each NUMBER.\n\
-\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
- fputs (_("\
-\n\
-Print the prime factors of all specified integer NUMBERs. If no arguments\n\
-are specified on the command line, they are read from standard input.\n\
-"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ e[i] += m;
}
- exit (status);
}
-/* FIXME: comment */
+#define factor_insert(f, p) factor_insert_multiplicity (f, p, 1)
+
+static void
+factor_insert_large (struct factors *factors,
+ uintmax_t p1, uintmax_t p0)
+{
+ if (p1 > 0)
+ {
+ assert (factors->plarge[1] == 0);
+ factors->plarge[0] = p0;
+ factors->plarge[1] = p1;
+ }
+ else
+ factor_insert (factors, p0);
+}
+
+#if HAVE_GMP
+
+# if !HAVE_DECL_MPZ_INITS
+
+# define mpz_inits(...) mpz_va_init (mpz_init, __VA_ARGS__)
+# define mpz_clears(...) mpz_va_init (mpz_clear, __VA_ARGS__)
+
+static void
+mpz_va_init (void (*mpz_single_init)(mpz_t), ...)
+{
+ va_list ap;
+
+ va_start (ap, mpz_single_init);
+
+ mpz_t *mpz;
+ while ((mpz = va_arg (ap, mpz_t *)))
+ mpz_single_init (*mpz);
+
+ va_end (ap);
+}
+# endif
+
+static void mp_factor (mpz_t, struct mp_factors *);
+
+static void
+mp_factor_init (struct mp_factors *factors)
+{
+ factors->p = NULL;
+ factors->e = NULL;
+ factors->nfactors = 0;
+}
+
+static void
+mp_factor_clear (struct mp_factors *factors)
+{
+ for (unsigned int i = 0; i < factors->nfactors; i++)
+ mpz_clear (factors->p[i]);
+
+ free (factors->p);
+ free (factors->e);
+}
+
+static void
+mp_factor_insert (struct mp_factors *factors, mpz_t prime)
+{
+ unsigned long int nfactors = factors->nfactors;
+ mpz_t *p = factors->p;
+ unsigned long int *e = factors->e;
+ long i;
+
+ /* Locate position for insert new or increment e. */
+ for (i = nfactors - 1; i >= 0; i--)
+ {
+ if (mpz_cmp (p[i], prime) <= 0)
+ break;
+ }
+
+ if (i < 0 || mpz_cmp (p[i], prime) != 0)
+ {
+ p = xrealloc (p, (nfactors + 1) * sizeof p[0]);
+ e = xrealloc (e, (nfactors + 1) * sizeof e[0]);
+
+ mpz_init (p[nfactors]);
+ for (long j = nfactors - 1; j > i; j--)
+ {
+ mpz_set (p[j + 1], p[j]);
+ e[j + 1] = e[j];
+ }
+ mpz_set (p[i + 1], prime);
+ e[i + 1] = 1;
+
+ factors->p = p;
+ factors->e = e;
+ factors->nfactors = nfactors + 1;
+ }
+ else
+ {
+ e[i] += 1;
+ }
+}
+
+static void
+mp_factor_insert_ui (struct mp_factors *factors, unsigned long int prime)
+{
+ mpz_t pz;
+
+ mpz_init_set_ui (pz, prime);
+ mp_factor_insert (factors, pz);
+ mpz_clear (pz);
+}
+#endif /* HAVE_GMP */
+
+
+/* Number of bits in an uintmax_t. */
+enum { W = sizeof (uintmax_t) * CHAR_BIT };
+
+/* Verify that uintmax_t does not have holes in its representation. */
+verify (UINTMAX_MAX >> (W - 1) == 1);
+
+#define P(a,b,c,d) a,
+static const unsigned char primes_diff[] = {
+#include "primes.h"
+0,0,0,0,0,0,0 /* 7 sentinels for 8-way loop */
+};
+#undef P
+
+#define PRIMES_PTAB_ENTRIES \
+ (sizeof (primes_diff) / sizeof (primes_diff[0]) - 8 + 1)
+
+#define P(a,b,c,d) b,
+static const unsigned char primes_diff8[] = {
+#include "primes.h"
+0,0,0,0,0,0,0 /* 7 sentinels for 8-way loop */
+};
+#undef P
-static size_t
-factor (uintmax_t n0, size_t max_n_factors, uintmax_t *factors)
+struct primes_dtab
{
- uintmax_t n = n0, d, q;
- size_t n_factors = 0;
- unsigned char const *w = wheel_tab;
+ uintmax_t binv, lim;
+};
+
+#define P(a,b,c,d) {c,d},
+static const struct primes_dtab primes_dtab[] = {
+#include "primes.h"
+{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0} /* 7 sentinels for 8-way loop */
+};
+#undef P
+
+/* Verify that uintmax_t is not wider than
+ the integers used to generate primes.h. */
+verify (W <= WIDE_UINT_BITS);
+
+/* debugging for developers. Enables devmsg().
+ This flag is used only in the GMP code. */
+static bool dev_debug = false;
+
+/* Prove primality or run probabilistic tests. */
+static bool flag_prove_primality = PROVE_PRIMALITY;
+
+/* Number of Miller-Rabin tests to run when not proving primality. */
+#define MR_REPS 25
+
+static void
+factor_insert_refind (struct factors *factors, uintmax_t p, unsigned int i,
+ unsigned int off)
+{
+ for (unsigned int j = 0; j < off; j++)
+ p += primes_diff[i + j];
+ factor_insert (factors, p);
+}
+
+/* Trial division with odd primes uses the following trick.
+
+ Let p be an odd prime, and B = 2^{W_TYPE_SIZE}. For simplicity,
+ consider the case t < B (this is the second loop below).
+
+ From our tables we get
+
+ binv = p^{-1} (mod B)
+ lim = floor ( (B-1) / p ).
+
+ First assume that t is a multiple of p, t = q * p. Then 0 <= q <= lim
+ (and all quotients in this range occur for some t).
+
+ Then t = q * p is true also (mod B), and p is invertible we get
+
+ q = t * binv (mod B).
+
+ Next, assume that t is *not* divisible by p. Since multiplication
+ by binv (mod B) is a one-to-one mapping,
+
+ t * binv (mod B) > lim,
+
+ because all the smaller values are already taken.
+
+ This can be summed up by saying that the function
+
+ q(t) = binv * t (mod B)
+
+ is a permutation of the range 0 <= t < B, with the curious property
+ that it maps the multiples of p onto the range 0 <= q <= lim, in
+ order, and the non-multiples of p onto the range lim < q < B.
+ */
+
+static uintmax_t
+factor_using_division (uintmax_t *t1p, uintmax_t t1, uintmax_t t0,
+ struct factors *factors)
+{
+ if (t0 % 2 == 0)
+ {
+ unsigned int cnt;
+
+ if (t0 == 0)
+ {
+ count_trailing_zeros (cnt, t1);
+ t0 = t1 >> cnt;
+ t1 = 0;
+ cnt += W_TYPE_SIZE;
+ }
+ else
+ {
+ count_trailing_zeros (cnt, t0);
+ rsh2 (t1, t0, t1, t0, cnt);
+ }
+
+ factor_insert_multiplicity (factors, 2, cnt);
+ }
+
+ uintmax_t p = 3;
+ unsigned int i;
+ for (i = 0; t1 > 0 && i < PRIMES_PTAB_ENTRIES; i++)
+ {
+ for (;;)
+ {
+ uintmax_t q1, q0, hi, lo _GL_UNUSED;
+
+ q0 = t0 * primes_dtab[i].binv;
+ umul_ppmm (hi, lo, q0, p);
+ if (hi > t1)
+ break;
+ hi = t1 - hi;
+ q1 = hi * primes_dtab[i].binv;
+ if (LIKELY (q1 > primes_dtab[i].lim))
+ break;
+ t1 = q1; t0 = q0;
+ factor_insert (factors, p);
+ }
+ p += primes_diff[i + 1];
+ }
+ if (t1p)
+ *t1p = t1;
+
+#define DIVBLOCK(I) \
+ do { \
+ for (;;) \
+ { \
+ q = t0 * pd[I].binv; \
+ if (LIKELY (q > pd[I].lim)) \
+ break; \
+ t0 = q; \
+ factor_insert_refind (factors, p, i + 1, I); \
+ } \
+ } while (0)
+
+ for (; i < PRIMES_PTAB_ENTRIES; i += 8)
+ {
+ uintmax_t q;
+ const struct primes_dtab *pd = &primes_dtab[i];
+ DIVBLOCK (0);
+ DIVBLOCK (1);
+ DIVBLOCK (2);
+ DIVBLOCK (3);
+ DIVBLOCK (4);
+ DIVBLOCK (5);
+ DIVBLOCK (6);
+ DIVBLOCK (7);
+
+ p += primes_diff8[i];
+ if (p * p > t0)
+ break;
+ }
+
+ return t0;
+}
+
+#if HAVE_GMP
+static void
+mp_factor_using_division (mpz_t t, struct mp_factors *factors)
+{
+ mpz_t q;
+ unsigned long int p;
+
+ devmsg ("[trial division] ");
+
+ mpz_init (q);
+
+ p = mpz_scan1 (t, 0);
+ mpz_div_2exp (t, t, p);
+ while (p)
+ {
+ mp_factor_insert_ui (factors, 2);
+ --p;
+ }
+
+ p = 3;
+ for (unsigned int i = 1; i <= PRIMES_PTAB_ENTRIES;)
+ {
+ if (! mpz_divisible_ui_p (t, p))
+ {
+ p += primes_diff[i++];
+ if (mpz_cmp_ui (t, p * p) < 0)
+ break;
+ }
+ else
+ {
+ mpz_tdiv_q_ui (t, t, p);
+ mp_factor_insert_ui (factors, p);
+ }
+ }
+
+ mpz_clear (q);
+}
+#endif
+
+/* Entry i contains (2i+1)^(-1) mod 2^8. */
+static const unsigned char binvert_table[128] =
+{
+ 0x01, 0xAB, 0xCD, 0xB7, 0x39, 0xA3, 0xC5, 0xEF,
+ 0xF1, 0x1B, 0x3D, 0xA7, 0x29, 0x13, 0x35, 0xDF,
+ 0xE1, 0x8B, 0xAD, 0x97, 0x19, 0x83, 0xA5, 0xCF,
+ 0xD1, 0xFB, 0x1D, 0x87, 0x09, 0xF3, 0x15, 0xBF,
+ 0xC1, 0x6B, 0x8D, 0x77, 0xF9, 0x63, 0x85, 0xAF,
+ 0xB1, 0xDB, 0xFD, 0x67, 0xE9, 0xD3, 0xF5, 0x9F,
+ 0xA1, 0x4B, 0x6D, 0x57, 0xD9, 0x43, 0x65, 0x8F,
+ 0x91, 0xBB, 0xDD, 0x47, 0xC9, 0xB3, 0xD5, 0x7F,
+ 0x81, 0x2B, 0x4D, 0x37, 0xB9, 0x23, 0x45, 0x6F,
+ 0x71, 0x9B, 0xBD, 0x27, 0xA9, 0x93, 0xB5, 0x5F,
+ 0x61, 0x0B, 0x2D, 0x17, 0x99, 0x03, 0x25, 0x4F,
+ 0x51, 0x7B, 0x9D, 0x07, 0x89, 0x73, 0x95, 0x3F,
+ 0x41, 0xEB, 0x0D, 0xF7, 0x79, 0xE3, 0x05, 0x2F,
+ 0x31, 0x5B, 0x7D, 0xE7, 0x69, 0x53, 0x75, 0x1F,
+ 0x21, 0xCB, 0xED, 0xD7, 0x59, 0xC3, 0xE5, 0x0F,
+ 0x11, 0x3B, 0x5D, 0xC7, 0x49, 0x33, 0x55, 0xFF
+};
+
+/* Compute n^(-1) mod B, using a Newton iteration. */
+#define binv(inv,n) \
+ do { \
+ uintmax_t __n = (n); \
+ uintmax_t __inv; \
+ \
+ __inv = binvert_table[(__n / 2) & 0x7F]; /* 8 */ \
+ if (W_TYPE_SIZE > 8) __inv = 2 * __inv - __inv * __inv * __n; \
+ if (W_TYPE_SIZE > 16) __inv = 2 * __inv - __inv * __inv * __n; \
+ if (W_TYPE_SIZE > 32) __inv = 2 * __inv - __inv * __inv * __n; \
+ \
+ if (W_TYPE_SIZE > 64) \
+ { \
+ int __invbits = 64; \
+ do { \
+ __inv = 2 * __inv - __inv * __inv * __n; \
+ __invbits *= 2; \
+ } while (__invbits < W_TYPE_SIZE); \
+ } \
+ \
+ (inv) = __inv; \
+ } while (0)
+
+/* q = u / d, assuming d|u. */
+#define divexact_21(q1, q0, u1, u0, d) \
+ do { \
+ uintmax_t _di, _q0; \
+ binv (_di, (d)); \
+ _q0 = (u0) * _di; \
+ if ((u1) >= (d)) \
+ { \
+ uintmax_t _p1, _p0 _GL_UNUSED; \
+ umul_ppmm (_p1, _p0, _q0, d); \
+ (q1) = ((u1) - _p1) * _di; \
+ (q0) = _q0; \
+ } \
+ else \
+ { \
+ (q0) = _q0; \
+ (q1) = 0; \
+ } \
+ } while (0)
+
+/* x B (mod n). */
+#define redcify(r_prim, r, n) \
+ do { \
+ uintmax_t _redcify_q _GL_UNUSED; \
+ udiv_qrnnd (_redcify_q, r_prim, r, 0, n); \
+ } while (0)
+
+/* x B^2 (mod n). Requires x > 0, n1 < B/2 */
+#define redcify2(r1, r0, x, n1, n0) \
+ do { \
+ uintmax_t _r1, _r0, _i; \
+ if ((x) < (n1)) \
+ { \
+ _r1 = (x); _r0 = 0; \
+ _i = W_TYPE_SIZE; \
+ } \
+ else \
+ { \
+ _r1 = 0; _r0 = (x); \
+ _i = 2*W_TYPE_SIZE; \
+ } \
+ while (_i-- > 0) \
+ { \
+ lsh2 (_r1, _r0, _r1, _r0, 1); \
+ if (ge2 (_r1, _r0, (n1), (n0))) \
+ sub_ddmmss (_r1, _r0, _r1, _r0, (n1), (n0)); \
+ } \
+ (r1) = _r1; \
+ (r0) = _r0; \
+ } while (0)
+
+/* Modular two-word multiplication, r = a * b mod m, with mi = m^(-1) mod B.
+ Both a and b must be in redc form, the result will be in redc form too. */
+static inline uintmax_t
+mulredc (uintmax_t a, uintmax_t b, uintmax_t m, uintmax_t mi)
+{
+ uintmax_t rh, rl, q, th, tl _GL_UNUSED, xh;
+
+ umul_ppmm (rh, rl, a, b);
+ q = rl * mi;
+ umul_ppmm (th, tl, q, m);
+ xh = rh - th;
+ if (rh < th)
+ xh += m;
+
+ return xh;
+}
+
+/* Modular two-word multiplication, r = a * b mod m, with mi = m^(-1) mod B.
+ Both a and b must be in redc form, the result will be in redc form too.
+ For performance reasons, the most significant bit of m must be clear. */
+static uintmax_t
+mulredc2 (uintmax_t *r1p,
+ uintmax_t a1, uintmax_t a0, uintmax_t b1, uintmax_t b0,
+ uintmax_t m1, uintmax_t m0, uintmax_t mi)
+{
+ uintmax_t r1, r0, q, p1, p0 _GL_UNUSED, t1, t0, s1, s0;
+ mi = -mi;
+ assert ( (a1 >> (W_TYPE_SIZE - 1)) == 0);
+ assert ( (b1 >> (W_TYPE_SIZE - 1)) == 0);
+ assert ( (m1 >> (W_TYPE_SIZE - 1)) == 0);
+
+ /* First compute a0 * <b1, b0> B^{-1}
+ +-----+
+ |a0 b0|
+ +--+--+--+
+ |a0 b1|
+ +--+--+--+
+ |q0 m0|
+ +--+--+--+
+ |q0 m1|
+ -+--+--+--+
+ |r1|r0| 0|
+ +--+--+--+
+ */
+ umul_ppmm (t1, t0, a0, b0);
+ umul_ppmm (r1, r0, a0, b1);
+ q = mi * t0;
+ umul_ppmm (p1, p0, q, m0);
+ umul_ppmm (s1, s0, q, m1);
+ r0 += (t0 != 0); /* Carry */
+ add_ssaaaa (r1, r0, r1, r0, 0, p1);
+ add_ssaaaa (r1, r0, r1, r0, 0, t1);
+ add_ssaaaa (r1, r0, r1, r0, s1, s0);
+
+ /* Next, (a1 * <b1, b0> + <r1, r0> B^{-1}
+ +-----+
+ |a1 b0|
+ +--+--+
+ |r1|r0|
+ +--+--+--+
+ |a1 b1|
+ +--+--+--+
+ |q1 m0|
+ +--+--+--+
+ |q1 m1|
+ -+--+--+--+
+ |r1|r0| 0|
+ +--+--+--+
+ */
+ umul_ppmm (t1, t0, a1, b0);
+ umul_ppmm (s1, s0, a1, b1);
+ add_ssaaaa (t1, t0, t1, t0, 0, r0);
+ q = mi * t0;
+ add_ssaaaa (r1, r0, s1, s0, 0, r1);
+ umul_ppmm (p1, p0, q, m0);
+ umul_ppmm (s1, s0, q, m1);
+ r0 += (t0 != 0); /* Carry */
+ add_ssaaaa (r1, r0, r1, r0, 0, p1);
+ add_ssaaaa (r1, r0, r1, r0, 0, t1);
+ add_ssaaaa (r1, r0, r1, r0, s1, s0);
+
+ if (ge2 (r1, r0, m1, m0))
+ sub_ddmmss (r1, r0, r1, r0, m1, m0);
+
+ *r1p = r1;
+ return r0;
+}
+
+static uintmax_t _GL_ATTRIBUTE_CONST
+powm (uintmax_t b, uintmax_t e, uintmax_t n, uintmax_t ni, uintmax_t one)
+{
+ uintmax_t y = one;
+
+ if (e & 1)
+ y = b;
+
+ while (e != 0)
+ {
+ b = mulredc (b, b, n, ni);
+ e >>= 1;
+
+ if (e & 1)
+ y = mulredc (y, b, n, ni);
+ }
+
+ return y;
+}
+
+static uintmax_t
+powm2 (uintmax_t *r1m,
+ const uintmax_t *bp, const uintmax_t *ep, const uintmax_t *np,
+ uintmax_t ni, const uintmax_t *one)
+{
+ uintmax_t r1, r0, b1, b0, n1, n0;
+ unsigned int i;
+ uintmax_t e;
+
+ b0 = bp[0];
+ b1 = bp[1];
+ n0 = np[0];
+ n1 = np[1];
+
+ r0 = one[0];
+ r1 = one[1];
+
+ for (e = ep[0], i = W_TYPE_SIZE; i > 0; i--, e >>= 1)
+ {
+ if (e & 1)
+ {
+ r0 = mulredc2 (r1m, r1, r0, b1, b0, n1, n0, ni);
+ r1 = *r1m;
+ }
+ b0 = mulredc2 (r1m, b1, b0, b1, b0, n1, n0, ni);
+ b1 = *r1m;
+ }
+ for (e = ep[1]; e > 0; e >>= 1)
+ {
+ if (e & 1)
+ {
+ r0 = mulredc2 (r1m, r1, r0, b1, b0, n1, n0, ni);
+ r1 = *r1m;
+ }
+ b0 = mulredc2 (r1m, b1, b0, b1, b0, n1, n0, ni);
+ b1 = *r1m;
+ }
+ *r1m = r1;
+ return r0;
+}
+
+static bool _GL_ATTRIBUTE_CONST
+millerrabin (uintmax_t n, uintmax_t ni, uintmax_t b, uintmax_t q,
+ unsigned int k, uintmax_t one)
+{
+ uintmax_t y = powm (b, q, n, ni, one);
+
+ uintmax_t nm1 = n - one; /* -1, but in redc representation. */
+
+ if (y == one || y == nm1)
+ return true;
+
+ for (unsigned int i = 1; i < k; i++)
+ {
+ y = mulredc (y, y, n, ni);
+
+ if (y == nm1)
+ return true;
+ if (y == one)
+ return false;
+ }
+ return false;
+}
+
+static bool
+millerrabin2 (const uintmax_t *np, uintmax_t ni, const uintmax_t *bp,
+ const uintmax_t *qp, unsigned int k, const uintmax_t *one)
+{
+ uintmax_t y1, y0, nm1_1, nm1_0, r1m;
+
+ y0 = powm2 (&r1m, bp, qp, np, ni, one);
+ y1 = r1m;
+
+ if (y0 == one[0] && y1 == one[1])
+ return true;
+
+ sub_ddmmss (nm1_1, nm1_0, np[1], np[0], one[1], one[0]);
+
+ if (y0 == nm1_0 && y1 == nm1_1)
+ return true;
+
+ for (unsigned int i = 1; i < k; i++)
+ {
+ y0 = mulredc2 (&r1m, y1, y0, y1, y0, np[1], np[0], ni);
+ y1 = r1m;
+
+ if (y0 == nm1_0 && y1 == nm1_1)
+ return true;
+ if (y0 == one[0] && y1 == one[1])
+ return false;
+ }
+ return false;
+}
+
+#if HAVE_GMP
+static bool
+mp_millerrabin (mpz_srcptr n, mpz_srcptr nm1, mpz_ptr x, mpz_ptr y,
+ mpz_srcptr q, unsigned long int k)
+{
+ mpz_powm (y, x, q, n);
+
+ if (mpz_cmp_ui (y, 1) == 0 || mpz_cmp (y, nm1) == 0)
+ return true;
+
+ for (unsigned long int i = 1; i < k; i++)
+ {
+ mpz_powm_ui (y, y, 2, n);
+ if (mpz_cmp (y, nm1) == 0)
+ return true;
+ if (mpz_cmp_ui (y, 1) == 0)
+ return false;
+ }
+ return false;
+}
+#endif
+
+/* Lucas' prime test. The number of iterations vary greatly, up to a few dozen
+ have been observed. The average seem to be about 2. */
+static bool
+prime_p (uintmax_t n)
+{
+ int k;
+ bool is_prime;
+ uintmax_t a_prim, one, ni;
+ struct factors factors;
if (n <= 1)
- return n_factors;
+ return false;
+
+ /* We have already casted out small primes. */
+ if (n < (uintmax_t) FIRST_OMITTED_PRIME * FIRST_OMITTED_PRIME)
+ return true;
- /* The exit condition in the following loop is correct because
- any time it is tested one of these 3 conditions holds:
- (1) d divides n
- (2) n is prime
- (3) n is composite but has no factors less than d.
- If (1) or (2) obviously the right thing happens.
- If (3), then since n is composite it is >= d^2. */
+ /* Precomputation for Miller-Rabin. */
+ uintmax_t q = n - 1;
+ for (k = 0; (q & 1) == 0; k++)
+ q >>= 1;
- d = 2;
- do
+ uintmax_t a = 2;
+ binv (ni, n); /* ni <- 1/n mod B */
+ redcify (one, 1, n);
+ addmod (a_prim, one, one, n); /* i.e., redcify a = 2 */
+
+ /* Perform a Miller-Rabin test, finds most composites quickly. */
+ if (!millerrabin (n, ni, a_prim, q, k, one))
+ return false;
+
+ if (flag_prove_primality)
{
- q = n / d;
- while (n == q * d)
- {
- assert (n_factors < max_n_factors);
- factors[n_factors++] = d;
- n = q;
- q = n / d;
- }
- d += *(w++);
- if (w == WHEEL_END)
- w = WHEEL_START;
+ /* Factor n-1 for Lucas. */
+ factor (0, n - 1, &factors);
}
- while (d <= q);
- if (n != 1 || n0 == 1)
+ /* Loop until Lucas proves our number prime, or Miller-Rabin proves our
+ number composite. */
+ for (unsigned int r = 0; r < PRIMES_PTAB_ENTRIES; r++)
{
- assert (n_factors < max_n_factors);
- factors[n_factors++] = n;
+ if (flag_prove_primality)
+ {
+ is_prime = true;
+ for (unsigned int i = 0; i < factors.nfactors && is_prime; i++)
+ {
+ is_prime
+ = powm (a_prim, (n - 1) / factors.p[i], n, ni, one) != one;
+ }
+ }
+ else
+ {
+ /* After enough Miller-Rabin runs, be content. */
+ is_prime = (r == MR_REPS - 1);
+ }
+
+ if (is_prime)
+ return true;
+
+ a += primes_diff[r]; /* Establish new base. */
+
+ /* The following is equivalent to redcify (a_prim, a, n). It runs faster
+ on most processors, since it avoids udiv_qrnnd. If we go down the
+ udiv_qrnnd_preinv path, this code should be replaced. */
+ {
+ uintmax_t s1, s0;
+ umul_ppmm (s1, s0, one, a);
+ if (LIKELY (s1 == 0))
+ a_prim = s0 % n;
+ else
+ {
+ uintmax_t dummy _GL_UNUSED;
+ udiv_qrnnd (dummy, a_prim, s1, s0, n);
+ }
+ }
+
+ if (!millerrabin (n, ni, a_prim, q, k, one))
+ return false;
}
- return n_factors;
+ error (0, 0, _("Lucas prime test failure. This should not happen"));
+ abort ();
}
-/* FIXME: comment */
+static bool
+prime2_p (uintmax_t n1, uintmax_t n0)
+{
+ uintmax_t q[2], nm1[2];
+ uintmax_t a_prim[2];
+ uintmax_t one[2];
+ uintmax_t na[2];
+ uintmax_t ni;
+ unsigned int k;
+ struct factors factors;
+
+ if (n1 == 0)
+ return prime_p (n0);
+
+ nm1[1] = n1 - (n0 == 0);
+ nm1[0] = n0 - 1;
+ if (nm1[0] == 0)
+ {
+ count_trailing_zeros (k, nm1[1]);
+
+ q[0] = nm1[1] >> k;
+ q[1] = 0;
+ k += W_TYPE_SIZE;
+ }
+ else
+ {
+ count_trailing_zeros (k, nm1[0]);
+ rsh2 (q[1], q[0], nm1[1], nm1[0], k);
+ }
+
+ uintmax_t a = 2;
+ binv (ni, n0);
+ redcify2 (one[1], one[0], 1, n1, n0);
+ addmod2 (a_prim[1], a_prim[0], one[1], one[0], one[1], one[0], n1, n0);
+
+ /* FIXME: Use scalars or pointers in arguments? Some consistency needed. */
+ na[0] = n0;
+ na[1] = n1;
+
+ if (!millerrabin2 (na, ni, a_prim, q, k, one))
+ return false;
+
+ if (flag_prove_primality)
+ {
+ /* Factor n-1 for Lucas. */
+ factor (nm1[1], nm1[0], &factors);
+ }
+
+ /* Loop until Lucas proves our number prime, or Miller-Rabin proves our
+ number composite. */
+ for (unsigned int r = 0; r < PRIMES_PTAB_ENTRIES; r++)
+ {
+ bool is_prime;
+ uintmax_t e[2], y[2];
+
+ if (flag_prove_primality)
+ {
+ is_prime = true;
+ if (factors.plarge[1])
+ {
+ uintmax_t pi;
+ binv (pi, factors.plarge[0]);
+ e[0] = pi * nm1[0];
+ e[1] = 0;
+ y[0] = powm2 (&y[1], a_prim, e, na, ni, one);
+ is_prime = (y[0] != one[0] || y[1] != one[1]);
+ }
+ for (unsigned int i = 0; i < factors.nfactors && is_prime; i++)
+ {
+ /* FIXME: We always have the factor 2. Do we really need to
+ handle it here? We have done the same powering as part
+ of millerrabin. */
+ if (factors.p[i] == 2)
+ rsh2 (e[1], e[0], nm1[1], nm1[0], 1);
+ else
+ divexact_21 (e[1], e[0], nm1[1], nm1[0], factors.p[i]);
+ y[0] = powm2 (&y[1], a_prim, e, na, ni, one);
+ is_prime = (y[0] != one[0] || y[1] != one[1]);
+ }
+ }
+ else
+ {
+ /* After enough Miller-Rabin runs, be content. */
+ is_prime = (r == MR_REPS - 1);
+ }
+
+ if (is_prime)
+ return true;
+
+ a += primes_diff[r]; /* Establish new base. */
+ redcify2 (a_prim[1], a_prim[0], a, n1, n0);
+
+ if (!millerrabin2 (na, ni, a_prim, q, k, one))
+ return false;
+ }
+
+ error (0, 0, _("Lucas prime test failure. This should not happen"));
+ abort ();
+}
+#if HAVE_GMP
static bool
-print_factors (const char *s)
+mp_prime_p (mpz_t n)
{
- uintmax_t factors[MAX_N_FACTORS];
- uintmax_t n;
- size_t n_factors;
- size_t i;
- char buf[INT_BUFSIZE_BOUND (uintmax_t)];
- strtol_error err;
+ bool is_prime;
+ mpz_t q, a, nm1, tmp;
+ struct mp_factors factors;
+
+ if (mpz_cmp_ui (n, 1) <= 0)
+ return false;
+
+ /* We have already casted out small primes. */
+ if (mpz_cmp_ui (n, (long) FIRST_OMITTED_PRIME * FIRST_OMITTED_PRIME) < 0)
+ return true;
+
+ mpz_inits (q, a, nm1, tmp, NULL);
+
+ /* Precomputation for Miller-Rabin. */
+ mpz_sub_ui (nm1, n, 1);
+
+ /* Find q and k, where q is odd and n = 1 + 2**k * q. */
+ unsigned long int k = mpz_scan1 (nm1, 0);
+ mpz_tdiv_q_2exp (q, nm1, k);
+
+ mpz_set_ui (a, 2);
+
+ /* Perform a Miller-Rabin test, finds most composites quickly. */
+ if (!mp_millerrabin (n, nm1, a, tmp, q, k))
+ {
+ is_prime = false;
+ goto ret2;
+ }
+
+ if (flag_prove_primality)
+ {
+ /* Factor n-1 for Lucas. */
+ mpz_set (tmp, nm1);
+ mp_factor (tmp, &factors);
+ }
+
+ /* Loop until Lucas proves our number prime, or Miller-Rabin proves our
+ number composite. */
+ for (unsigned int r = 0; r < PRIMES_PTAB_ENTRIES; r++)
+ {
+ if (flag_prove_primality)
+ {
+ is_prime = true;
+ for (unsigned long int i = 0; i < factors.nfactors && is_prime; i++)
+ {
+ mpz_divexact (tmp, nm1, factors.p[i]);
+ mpz_powm (tmp, a, tmp, n);
+ is_prime = mpz_cmp_ui (tmp, 1) != 0;
+ }
+ }
+ else
+ {
+ /* After enough Miller-Rabin runs, be content. */
+ is_prime = (r == MR_REPS - 1);
+ }
+
+ if (is_prime)
+ goto ret1;
+
+ mpz_add_ui (a, a, primes_diff[r]); /* Establish new base. */
+
+ if (!mp_millerrabin (n, nm1, a, tmp, q, k))
+ {
+ is_prime = false;
+ goto ret1;
+ }
+ }
+
+ error (0, 0, _("Lucas prime test failure. This should not happen"));
+ abort ();
+
+ ret1:
+ if (flag_prove_primality)
+ mp_factor_clear (&factors);
+ ret2:
+ mpz_clears (q, a, nm1, tmp, NULL);
+
+ return is_prime;
+}
+#endif
+
+static void
+factor_using_pollard_rho (uintmax_t n, unsigned long int a,
+ struct factors *factors)
+{
+ uintmax_t x, z, y, P, t, ni, g;
+
+ unsigned long int k = 1;
+ unsigned long int l = 1;
+
+ redcify (P, 1, n);
+ addmod (x, P, P, n); /* i.e., redcify(2) */
+ y = z = x;
+
+ while (n != 1)
+ {
+ assert (a < n);
+
+ binv (ni, n); /* FIXME: when could we use old 'ni' value? */
+
+ for (;;)
+ {
+ do
+ {
+ x = mulredc (x, x, n, ni);
+ addmod (x, x, a, n);
+
+ submod (t, z, x, n);
+ P = mulredc (P, t, n, ni);
+
+ if (k % 32 == 1)
+ {
+ if (gcd_odd (P, n) != 1)
+ goto factor_found;
+ y = x;
+ }
+ }
+ while (--k != 0);
+
+ z = x;
+ k = l;
+ l = 2 * l;
+ for (unsigned long int i = 0; i < k; i++)
+ {
+ x = mulredc (x, x, n, ni);
+ addmod (x, x, a, n);
+ }
+ y = x;
+ }
+
+ factor_found:
+ do
+ {
+ y = mulredc (y, y, n, ni);
+ addmod (y, y, a, n);
+
+ submod (t, z, y, n);
+ g = gcd_odd (t, n);
+ }
+ while (g == 1);
+
+ n = n / g;
+
+ if (!prime_p (g))
+ factor_using_pollard_rho (g, a + 1, factors);
+ else
+ factor_insert (factors, g);
+
+ if (prime_p (n))
+ {
+ factor_insert (factors, n);
+ break;
+ }
+
+ x = x % n;
+ z = z % n;
+ y = y % n;
+ }
+}
+
+static void
+factor_using_pollard_rho2 (uintmax_t n1, uintmax_t n0, unsigned long int a,
+ struct factors *factors)
+{
+ uintmax_t x1, x0, z1, z0, y1, y0, P1, P0, t1, t0, ni, g1, g0, r1m;
+
+ unsigned long int k = 1;
+ unsigned long int l = 1;
+
+ redcify2 (P1, P0, 1, n1, n0);
+ addmod2 (x1, x0, P1, P0, P1, P0, n1, n0); /* i.e., redcify(2) */
+ y1 = z1 = x1;
+ y0 = z0 = x0;
+
+ while (n1 != 0 || n0 != 1)
+ {
+ binv (ni, n0);
+
+ for (;;)
+ {
+ do
+ {
+ x0 = mulredc2 (&r1m, x1, x0, x1, x0, n1, n0, ni);
+ x1 = r1m;
+ addmod2 (x1, x0, x1, x0, 0, (uintmax_t) a, n1, n0);
+
+ submod2 (t1, t0, z1, z0, x1, x0, n1, n0);
+ P0 = mulredc2 (&r1m, P1, P0, t1, t0, n1, n0, ni);
+ P1 = r1m;
+
+ if (k % 32 == 1)
+ {
+ g0 = gcd2_odd (&g1, P1, P0, n1, n0);
+ if (g1 != 0 || g0 != 1)
+ goto factor_found;
+ y1 = x1; y0 = x0;
+ }
+ }
+ while (--k != 0);
+
+ z1 = x1; z0 = x0;
+ k = l;
+ l = 2 * l;
+ for (unsigned long int i = 0; i < k; i++)
+ {
+ x0 = mulredc2 (&r1m, x1, x0, x1, x0, n1, n0, ni);
+ x1 = r1m;
+ addmod2 (x1, x0, x1, x0, 0, (uintmax_t) a, n1, n0);
+ }
+ y1 = x1; y0 = x0;
+ }
+
+ factor_found:
+ do
+ {
+ y0 = mulredc2 (&r1m, y1, y0, y1, y0, n1, n0, ni);
+ y1 = r1m;
+ addmod2 (y1, y0, y1, y0, 0, (uintmax_t) a, n1, n0);
+
+ submod2 (t1, t0, z1, z0, y1, y0, n1, n0);
+ g0 = gcd2_odd (&g1, t1, t0, n1, n0);
+ }
+ while (g1 == 0 && g0 == 1);
+
+ if (g1 == 0)
+ {
+ /* The found factor is one word. */
+ divexact_21 (n1, n0, n1, n0, g0); /* n = n / g */
+
+ if (!prime_p (g0))
+ factor_using_pollard_rho (g0, a + 1, factors);
+ else
+ factor_insert (factors, g0);
+ }
+ else
+ {
+ /* The found factor is two words. This is highly unlikely, thus hard
+ to trigger. Please be careful before you change this code! */
+ uintmax_t ginv;
+
+ binv (ginv, g0); /* Compute n = n / g. Since the result will */
+ n0 = ginv * n0; /* fit one word, we can compute the quotient */
+ n1 = 0; /* modulo B, ignoring the high divisor word. */
+
+ if (!prime2_p (g1, g0))
+ factor_using_pollard_rho2 (g1, g0, a + 1, factors);
+ else
+ factor_insert_large (factors, g1, g0);
+ }
+
+ if (n1 == 0)
+ {
+ if (prime_p (n0))
+ {
+ factor_insert (factors, n0);
+ break;
+ }
+
+ factor_using_pollard_rho (n0, a, factors);
+ return;
+ }
+
+ if (prime2_p (n1, n0))
+ {
+ factor_insert_large (factors, n1, n0);
+ break;
+ }
+
+ x0 = mod2 (&x1, x1, x0, n1, n0);
+ z0 = mod2 (&z1, z1, z0, n1, n0);
+ y0 = mod2 (&y1, y1, y0, n1, n0);
+ }
+}
+
+#if HAVE_GMP
+static void
+mp_factor_using_pollard_rho (mpz_t n, unsigned long int a,
+ struct mp_factors *factors)
+{
+ mpz_t x, z, y, P;
+ mpz_t t, t2;
+
+ devmsg ("[pollard-rho (%lu)] ", a);
+
+ mpz_inits (t, t2, NULL);
+ mpz_init_set_si (y, 2);
+ mpz_init_set_si (x, 2);
+ mpz_init_set_si (z, 2);
+ mpz_init_set_ui (P, 1);
+
+ unsigned long long int k = 1;
+ unsigned long long int l = 1;
+
+ while (mpz_cmp_ui (n, 1) != 0)
+ {
+ for (;;)
+ {
+ do
+ {
+ mpz_mul (t, x, x);
+ mpz_mod (x, t, n);
+ mpz_add_ui (x, x, a);
+
+ mpz_sub (t, z, x);
+ mpz_mul (t2, P, t);
+ mpz_mod (P, t2, n);
+
+ if (k % 32 == 1)
+ {
+ mpz_gcd (t, P, n);
+ if (mpz_cmp_ui (t, 1) != 0)
+ goto factor_found;
+ mpz_set (y, x);
+ }
+ }
+ while (--k != 0);
+
+ mpz_set (z, x);
+ k = l;
+ l = 2 * l;
+ for (unsigned long long int i = 0; i < k; i++)
+ {
+ mpz_mul (t, x, x);
+ mpz_mod (x, t, n);
+ mpz_add_ui (x, x, a);
+ }
+ mpz_set (y, x);
+ }
+
+ factor_found:
+ do
+ {
+ mpz_mul (t, y, y);
+ mpz_mod (y, t, n);
+ mpz_add_ui (y, y, a);
+
+ mpz_sub (t, z, y);
+ mpz_gcd (t, t, n);
+ }
+ while (mpz_cmp_ui (t, 1) == 0);
+
+ mpz_divexact (n, n, t); /* divide by t, before t is overwritten */
+
+ if (!mp_prime_p (t))
+ {
+ devmsg ("[composite factor--restarting pollard-rho] ");
+ mp_factor_using_pollard_rho (t, a + 1, factors);
+ }
+ else
+ {
+ mp_factor_insert (factors, t);
+ }
+
+ if (mp_prime_p (n))
+ {
+ mp_factor_insert (factors, n);
+ break;
+ }
+
+ mpz_mod (x, x, n);
+ mpz_mod (z, z, n);
+ mpz_mod (y, y, n);
+ }
+
+ mpz_clears (P, t2, t, z, x, y, NULL);
+}
+#endif
+
+#if USE_SQUFOF
+/* FIXME: Maybe better to use an iteration converging to 1/sqrt(n)? If
+ algorithm is replaced, consider also returning the remainder. */
+static uintmax_t _GL_ATTRIBUTE_CONST
+isqrt (uintmax_t n)
+{
+ uintmax_t x;
+ unsigned c;
+ if (n == 0)
+ return 0;
+
+ count_leading_zeros (c, n);
+
+ /* Make x > sqrt(n). This will be invariant through the loop. */
+ x = (uintmax_t) 1 << ((W_TYPE_SIZE + 1 - c) / 2);
+
+ for (;;)
+ {
+ uintmax_t y = (x + n/x) / 2;
+ if (y >= x)
+ return x;
+
+ x = y;
+ }
+}
+
+static uintmax_t _GL_ATTRIBUTE_CONST
+isqrt2 (uintmax_t nh, uintmax_t nl)
+{
+ unsigned int shift;
+ uintmax_t x;
+
+ /* Ensures the remainder fits in an uintmax_t. */
+ assert (nh < ((uintmax_t) 1 << (W_TYPE_SIZE - 2)));
+
+ if (nh == 0)
+ return isqrt (nl);
+
+ count_leading_zeros (shift, nh);
+ shift &= ~1;
+
+ /* Make x > sqrt(n) */
+ x = isqrt ( (nh << shift) + (nl >> (W_TYPE_SIZE - shift))) + 1;
+ x <<= (W_TYPE_SIZE - shift) / 2;
+
+ /* Do we need more than one iteration? */
+ for (;;)
+ {
+ uintmax_t r _GL_UNUSED;
+ uintmax_t q, y;
+ udiv_qrnnd (q, r, nh, nl, x);
+ y = (x + q) / 2;
+
+ if (y >= x)
+ {
+ uintmax_t hi, lo;
+ umul_ppmm (hi, lo, x + 1, x + 1);
+ assert (gt2 (hi, lo, nh, nl));
+
+ umul_ppmm (hi, lo, x, x);
+ assert (ge2 (nh, nl, hi, lo));
+ sub_ddmmss (hi, lo, nh, nl, hi, lo);
+ assert (hi == 0);
+
+ return x;
+ }
+
+ x = y;
+ }
+}
+
+/* MAGIC[N] has a bit i set iff i is a quadratic residue mod N. */
+# define MAGIC64 0x0202021202030213ULL
+# define MAGIC63 0x0402483012450293ULL
+# define MAGIC65 0x218a019866014613ULL
+# define MAGIC11 0x23b
+
+/* Return the square root if the input is a square, otherwise 0. */
+static uintmax_t _GL_ATTRIBUTE_CONST
+is_square (uintmax_t x)
+{
+ /* Uses the tests suggested by Cohen. Excludes 99% of the non-squares before
+ computing the square root. */
+ if (((MAGIC64 >> (x & 63)) & 1)
+ && ((MAGIC63 >> (x % 63)) & 1)
+ /* Both 0 and 64 are squares mod (65) */
+ && ((MAGIC65 >> ((x % 65) & 63)) & 1)
+ && ((MAGIC11 >> (x % 11) & 1)))
+ {
+ uintmax_t r = isqrt (x);
+ if (r*r == x)
+ return r;
+ }
+ return 0;
+}
+
+/* invtab[i] = floor(0x10000 / (0x100 + i) */
+static const unsigned short invtab[0x81] =
+ {
+ 0x200,
+ 0x1fc, 0x1f8, 0x1f4, 0x1f0, 0x1ec, 0x1e9, 0x1e5, 0x1e1,
+ 0x1de, 0x1da, 0x1d7, 0x1d4, 0x1d0, 0x1cd, 0x1ca, 0x1c7,
+ 0x1c3, 0x1c0, 0x1bd, 0x1ba, 0x1b7, 0x1b4, 0x1b2, 0x1af,
+ 0x1ac, 0x1a9, 0x1a6, 0x1a4, 0x1a1, 0x19e, 0x19c, 0x199,
+ 0x197, 0x194, 0x192, 0x18f, 0x18d, 0x18a, 0x188, 0x186,
+ 0x183, 0x181, 0x17f, 0x17d, 0x17a, 0x178, 0x176, 0x174,
+ 0x172, 0x170, 0x16e, 0x16c, 0x16a, 0x168, 0x166, 0x164,
+ 0x162, 0x160, 0x15e, 0x15c, 0x15a, 0x158, 0x157, 0x155,
+ 0x153, 0x151, 0x150, 0x14e, 0x14c, 0x14a, 0x149, 0x147,
+ 0x146, 0x144, 0x142, 0x141, 0x13f, 0x13e, 0x13c, 0x13b,
+ 0x139, 0x138, 0x136, 0x135, 0x133, 0x132, 0x130, 0x12f,
+ 0x12e, 0x12c, 0x12b, 0x129, 0x128, 0x127, 0x125, 0x124,
+ 0x123, 0x121, 0x120, 0x11f, 0x11e, 0x11c, 0x11b, 0x11a,
+ 0x119, 0x118, 0x116, 0x115, 0x114, 0x113, 0x112, 0x111,
+ 0x10f, 0x10e, 0x10d, 0x10c, 0x10b, 0x10a, 0x109, 0x108,
+ 0x107, 0x106, 0x105, 0x104, 0x103, 0x102, 0x101, 0x100,
+ };
+
+/* Compute q = [u/d], r = u mod d. Avoids slow hardware division for the case
+ that q < 0x40; here it instead uses a table of (Euclidian) inverses. */
+# define div_smallq(q, r, u, d) \
+ do { \
+ if ((u) / 0x40 < (d)) \
+ { \
+ int _cnt; \
+ uintmax_t _dinv, _mask, _q, _r; \
+ count_leading_zeros (_cnt, (d)); \
+ _r = (u); \
+ if (UNLIKELY (_cnt > (W_TYPE_SIZE - 8))) \
+ { \
+ _dinv = invtab[((d) << (_cnt + 8 - W_TYPE_SIZE)) - 0x80]; \
+ _q = _dinv * _r >> (8 + W_TYPE_SIZE - _cnt); \
+ } \
+ else \
+ { \
+ _dinv = invtab[((d) >> (W_TYPE_SIZE - 8 - _cnt)) - 0x7f]; \
+ _q = _dinv * (_r >> (W_TYPE_SIZE - 3 - _cnt)) >> 11; \
+ } \
+ _r -= _q*(d); \
+ \
+ _mask = -(uintmax_t) (_r >= (d)); \
+ (r) = _r - (_mask & (d)); \
+ (q) = _q - _mask; \
+ assert ( (q) * (d) + (r) == u); \
+ } \
+ else \
+ { \
+ uintmax_t _q = (u) / (d); \
+ (r) = (u) - _q * (d); \
+ (q) = _q; \
+ } \
+ } while (0)
+
+/* Notes: Example N = 22117019. After first phase we find Q1 = 6314, Q
+ = 3025, P = 1737, representing F_{18} = (-6314, 2* 1737, 3025),
+ with 3025 = 55^2.
+
+ Constructing the square root, we get Q1 = 55, Q = 8653, P = 4652,
+ representing G_0 = (-55, 2*4652, 8653).
+
+ In the notation of the paper:
+
+ S_{-1} = 55, S_0 = 8653, R_0 = 4652
+
+ Put
+
+ t_0 = floor([q_0 + R_0] / S0) = 1
+ R_1 = t_0 * S_0 - R_0 = 4001
+ S_1 = S_{-1} +t_0 (R_0 - R_1) = 706
+*/
+
+/* Multipliers, in order of efficiency:
+ 0.7268 3*5*7*11 = 1155 = 3 (mod 4)
+ 0.7317 3*5*7 = 105 = 1
+ 0.7820 3*5*11 = 165 = 1
+ 0.7872 3*5 = 15 = 3
+ 0.8101 3*7*11 = 231 = 3
+ 0.8155 3*7 = 21 = 1
+ 0.8284 5*7*11 = 385 = 1
+ 0.8339 5*7 = 35 = 3
+ 0.8716 3*11 = 33 = 1
+ 0.8774 3 = 3 = 3
+ 0.8913 5*11 = 55 = 3
+ 0.8972 5 = 5 = 1
+ 0.9233 7*11 = 77 = 1
+ 0.9295 7 = 7 = 3
+ 0.9934 11 = 11 = 3
+*/
+# define QUEUE_SIZE 50
+#endif
+
+#if STAT_SQUFOF
+# define Q_FREQ_SIZE 50
+/* Element 0 keeps the total */
+static unsigned int q_freq[Q_FREQ_SIZE + 1];
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#if USE_SQUFOF
+/* Return true on success. Expected to fail only for numbers
+ >= 2^{2*W_TYPE_SIZE - 2}, or close to that limit. */
+static bool
+factor_using_squfof (uintmax_t n1, uintmax_t n0, struct factors *factors)
+{
+ /* Uses algorithm and notation from
+
+ SQUARE FORM FACTORIZATION
+ JASON E. GOWER AND SAMUEL S. WAGSTAFF, JR.
+
+ http://homes.cerias.purdue.edu/~ssw/squfof.pdf
+ */
+
+ static const unsigned int multipliers_1[] =
+ { /* = 1 (mod 4) */
+ 105, 165, 21, 385, 33, 5, 77, 1, 0
+ };
+ static const unsigned int multipliers_3[] =
+ { /* = 3 (mod 4) */
+ 1155, 15, 231, 35, 3, 55, 7, 11, 0
+ };
+
+ const unsigned int *m;
+
+ struct { uintmax_t Q; uintmax_t P; } queue[QUEUE_SIZE];
+
+ if (n1 >= ((uintmax_t) 1 << (W_TYPE_SIZE - 2)))
+ return false;
+
+ uintmax_t sqrt_n = isqrt2 (n1, n0);
+
+ if (n0 == sqrt_n * sqrt_n)
+ {
+ uintmax_t p1, p0;
+
+ umul_ppmm (p1, p0, sqrt_n, sqrt_n);
+ assert (p0 == n0);
+
+ if (n1 == p1)
+ {
+ if (prime_p (sqrt_n))
+ factor_insert_multiplicity (factors, sqrt_n, 2);
+ else
+ {
+ struct factors f;
+
+ f.nfactors = 0;
+ if (!factor_using_squfof (0, sqrt_n, &f))
+ {
+ /* Try pollard rho instead */
+ factor_using_pollard_rho (sqrt_n, 1, &f);
+ }
+ /* Duplicate the new factors */
+ for (unsigned int i = 0; i < f.nfactors; i++)
+ factor_insert_multiplicity (factors, f.p[i], 2*f.e[i]);
+ }
+ return true;
+ }
+ }
+
+ /* Select multipliers so we always get n * mu = 3 (mod 4) */
+ for (m = (n0 % 4 == 1) ? multipliers_3 : multipliers_1;
+ *m; m++)
+ {
+ uintmax_t S, Dh, Dl, Q1, Q, P, L, L1, B;
+ unsigned int i;
+ unsigned int mu = *m;
+ unsigned int qpos = 0;
+
+ assert (mu * n0 % 4 == 3);
+
+ /* In the notation of the paper, with mu * n == 3 (mod 4), we
+ get \Delta = 4 mu * n, and the paper's \mu is 2 mu. As far as
+ I understand it, the necessary bound is 4 \mu^3 < n, or 32
+ mu^3 < n.
+
+ However, this seems insufficient: With n = 37243139 and mu =
+ 105, we get a trivial factor, from the square 38809 = 197^2,
+ without any corresponding Q earlier in the iteration.
+
+ Requiring 64 mu^3 < n seems sufficient. */
+ if (n1 == 0)
+ {
+ if ((uintmax_t) mu*mu*mu >= n0 / 64)
+ continue;
+ }
+ else
+ {
+ if (n1 > ((uintmax_t) 1 << (W_TYPE_SIZE - 2)) / mu)
+ continue;
+ }
+ umul_ppmm (Dh, Dl, n0, mu);
+ Dh += n1 * mu;
+
+ assert (Dl % 4 != 1);
+ assert (Dh < (uintmax_t) 1 << (W_TYPE_SIZE - 2));
+
+ S = isqrt2 (Dh, Dl);
- if ((err = xstrtoumax (s, NULL, 10, &n, "")) != LONGINT_OK)
+ Q1 = 1;
+ P = S;
+
+ /* Square root remainder fits in one word, so ignore high part. */
+ Q = Dl - P*P;
+ /* FIXME: When can this differ from floor(sqrt(2 sqrt(D)))? */
+ L = isqrt (2*S);
+ B = 2*L;
+ L1 = mu * 2 * L;
+
+ /* The form is (+/- Q1, 2P, -/+ Q), of discriminant 4 (P^2 + Q Q1) =
+ 4 D. */
+
+ for (i = 0; i <= B; i++)
+ {
+ uintmax_t q, P1, t, rem;
+
+ div_smallq (q, rem, S+P, Q);
+ P1 = S - rem; /* P1 = q*Q - P */
+
+ IF_LINT (assert (q > 0 && Q > 0));
+
+# if STAT_SQUFOF
+ q_freq[0]++;
+ q_freq[MIN (q, Q_FREQ_SIZE)]++;
+# endif
+
+ if (Q <= L1)
+ {
+ uintmax_t g = Q;
+
+ if ( (Q & 1) == 0)
+ g /= 2;
+
+ g /= gcd_odd (g, mu);
+
+ if (g <= L)
+ {
+ if (qpos >= QUEUE_SIZE)
+ error (EXIT_FAILURE, 0, _("squfof queue overflow"));
+ queue[qpos].Q = g;
+ queue[qpos].P = P % g;
+ qpos++;
+ }
+ }
+
+ /* I think the difference can be either sign, but mod
+ 2^W_TYPE_SIZE arithmetic should be fine. */
+ t = Q1 + q * (P - P1);
+ Q1 = Q;
+ Q = t;
+ P = P1;
+
+ if ( (i & 1) == 0)
+ {
+ uintmax_t r = is_square (Q);
+ if (r)
+ {
+ for (unsigned int j = 0; j < qpos; j++)
+ {
+ if (queue[j].Q == r)
+ {
+ if (r == 1)
+ /* Traversed entire cycle. */
+ goto next_multiplier;
+
+ /* Need the absolute value for divisibility test. */
+ if (P >= queue[j].P)
+ t = P - queue[j].P;
+ else
+ t = queue[j].P - P;
+ if (t % r == 0)
+ {
+ /* Delete entries up to and including entry
+ j, which matched. */
+ memmove (queue, queue + j + 1,
+ (qpos - j - 1) * sizeof (queue[0]));
+ qpos -= (j + 1);
+ }
+ goto next_i;
+ }
+ }
+
+ /* We have found a square form, which should give a
+ factor. */
+ Q1 = r;
+ assert (S >= P); /* What signs are possible? */
+ P += r * ((S - P) / r);
+
+ /* Note: Paper says (N - P*P) / Q1, that seems incorrect
+ for the case D = 2N. */
+ /* Compute Q = (D - P*P) / Q1, but we need double
+ precision. */
+ uintmax_t hi, lo;
+ umul_ppmm (hi, lo, P, P);
+ sub_ddmmss (hi, lo, Dh, Dl, hi, lo);
+ udiv_qrnnd (Q, rem, hi, lo, Q1);
+ assert (rem == 0);
+
+ for (;;)
+ {
+ /* Note: There appears to by a typo in the paper,
+ Step 4a in the algorithm description says q <--
+ floor([S+P]/\hat Q), but looking at the equations
+ in Sec. 3.1, it should be q <-- floor([S+P] / Q).
+ (In this code, \hat Q is Q1). */
+ div_smallq (q, rem, S+P, Q);
+ P1 = S - rem; /* P1 = q*Q - P */
+
+# if STAT_SQUFOF
+ q_freq[0]++;
+ q_freq[MIN (q, Q_FREQ_SIZE)]++;
+# endif
+ if (P == P1)
+ break;
+ t = Q1 + q * (P - P1);
+ Q1 = Q;
+ Q = t;
+ P = P1;
+ }
+
+ if ( (Q & 1) == 0)
+ Q /= 2;
+ Q /= gcd_odd (Q, mu);
+
+ assert (Q > 1 && (n1 || Q < n0));
+
+ if (prime_p (Q))
+ factor_insert (factors, Q);
+ else if (!factor_using_squfof (0, Q, factors))
+ factor_using_pollard_rho (Q, 2, factors);
+
+ divexact_21 (n1, n0, n1, n0, Q);
+
+ if (prime2_p (n1, n0))
+ factor_insert_large (factors, n1, n0);
+ else
+ {
+ if (!factor_using_squfof (n1, n0, factors))
+ {
+ if (n1 == 0)
+ factor_using_pollard_rho (n0, 1, factors);
+ else
+ factor_using_pollard_rho2 (n1, n0, 1, factors);
+ }
+ }
+
+ return true;
+ }
+ }
+ next_i:;
+ }
+ next_multiplier:;
+ }
+ return false;
+}
+#endif
+
+/* Compute the prime factors of the 128-bit number (T1,T0), and put the
+ results in FACTORS. */
+static void
+factor (uintmax_t t1, uintmax_t t0, struct factors *factors)
+{
+ factors->nfactors = 0;
+ factors->plarge[1] = 0;
+
+ if (t1 == 0 && t0 < 2)
+ return;
+
+ t0 = factor_using_division (&t1, t1, t0, factors);
+
+ if (t1 == 0 && t0 < 2)
+ return;
+
+ if (prime2_p (t1, t0))
+ factor_insert_large (factors, t1, t0);
+ else
+ {
+#if USE_SQUFOF
+ if (factor_using_squfof (t1, t0, factors))
+ return;
+#endif
+
+ if (t1 == 0)
+ factor_using_pollard_rho (t0, 1, factors);
+ else
+ factor_using_pollard_rho2 (t1, t0, 1, factors);
+ }
+}
+
+#if HAVE_GMP
+/* Use Pollard-rho to compute the prime factors of
+ arbitrary-precision T, and put the results in FACTORS. */
+static void
+mp_factor (mpz_t t, struct mp_factors *factors)
+{
+ mp_factor_init (factors);
+
+ if (mpz_sgn (t) != 0)
+ {
+ mp_factor_using_division (t, factors);
+
+ if (mpz_cmp_ui (t, 1) != 0)
+ {
+ devmsg ("[is number prime?] ");
+ if (mp_prime_p (t))
+ mp_factor_insert (factors, t);
+ else
+ mp_factor_using_pollard_rho (t, 1, factors);
+ }
+ }
+}
+#endif
+
+static strtol_error
+strto2uintmax (uintmax_t *hip, uintmax_t *lop, const char *s)
+{
+ unsigned int lo_carry;
+ uintmax_t hi = 0, lo = 0;
+
+ strtol_error err = LONGINT_INVALID;
+
+ /* Skip initial spaces and '+'. */
+ for (;;)
{
- if (err == LONGINT_OVERFLOW)
- error (0, 0, _("%s is too large"), quote (s));
+ char c = *s;
+ if (c == ' ')
+ s++;
+ else if (c == '+')
+ {
+ s++;
+ break;
+ }
else
- error (0, 0, _("%s is not a valid positive integer"), quote (s));
+ break;
+ }
+
+ /* Initial scan for invalid digits. */
+ const char *p = s;
+ for (;;)
+ {
+ unsigned int c = *p++;
+ if (c == 0)
+ break;
+
+ if (UNLIKELY (!ISDIGIT (c)))
+ {
+ err = LONGINT_INVALID;
+ break;
+ }
+
+ err = LONGINT_OK; /* we've seen at least one valid digit */
+ }
+
+ for (;err == LONGINT_OK;)
+ {
+ unsigned int c = *s++;
+ if (c == 0)
+ break;
+
+ c -= '0';
+
+ if (UNLIKELY (hi > ~(uintmax_t)0 / 10))
+ {
+ err = LONGINT_OVERFLOW;
+ break;
+ }
+ hi = 10 * hi;
+
+ lo_carry = (lo >> (W_TYPE_SIZE - 3)) + (lo >> (W_TYPE_SIZE - 1));
+ lo_carry += 10 * lo < 2 * lo;
+
+ lo = 10 * lo;
+ lo += c;
+
+ lo_carry += lo < c;
+ hi += lo_carry;
+ if (UNLIKELY (hi < lo_carry))
+ {
+ err = LONGINT_OVERFLOW;
+ break;
+ }
+ }
+
+ *hip = hi;
+ *lop = lo;
+
+ return err;
+}
+
+/* Structure and routines for buffering and outputting full lines,
+ to support parallel operation efficiently. */
+static struct lbuf_
+{
+ char *buf;
+ char *end;
+} lbuf;
+
+/* 512 is chosen to give good performance,
+ and also is the max guaranteed size that
+ consumers can read atomically through pipes.
+ Also it's big enough to cater for max line length
+ even with 128 bit uintmax_t. */
+#define FACTOR_PIPE_BUF 512
+
+static void
+lbuf_alloc (void)
+{
+ if (lbuf.buf)
+ return;
+
+ /* Double to ensure enough space for
+ previous numbers + next number. */
+ lbuf.buf = xmalloc (FACTOR_PIPE_BUF * 2);
+ lbuf.end = lbuf.buf;
+}
+
+/* Write complete LBUF to standard output. */
+static void
+lbuf_flush (void)
+{
+ size_t size = lbuf.end - lbuf.buf;
+ if (full_write (STDOUT_FILENO, lbuf.buf, size) != size)
+ error (EXIT_FAILURE, errno, "%s", _("write error"));
+ lbuf.end = lbuf.buf;
+}
+
+/* Add a character C to LBUF and if it's a newline
+ and enough bytes are already buffered,
+ then write atomically to standard output. */
+static void
+lbuf_putc (char c)
+{
+ *lbuf.end++ = c;
+
+ if (c == '\n')
+ {
+ size_t buffered = lbuf.end - lbuf.buf;
+
+ if (buffered >= FACTOR_PIPE_BUF)
+ {
+ /* Write output in <= PIPE_BUF chunks
+ so consumers can read atomically. */
+ char const *tend = lbuf.end;
+
+ /* Since a umaxint_t's factors must fit in 512
+ we're guaranteed to find a newline here. */
+ char *tlend = lbuf.buf + FACTOR_PIPE_BUF;
+ while (*--tlend != '\n');
+ tlend++;
+
+ lbuf.end = tlend;
+ lbuf_flush ();
+
+ /* Buffer the remainder. */
+ memcpy (lbuf.buf, tlend, tend - tlend);
+ lbuf.end = lbuf.buf + (tend - tlend);
+ }
+ }
+}
+
+/* Buffer an int to the internal LBUF. */
+static void
+lbuf_putint (uintmax_t i, size_t min_width)
+{
+ char buf[INT_BUFSIZE_BOUND (uintmax_t)];
+ char const *umaxstr = umaxtostr (i, buf);
+ size_t width = sizeof (buf) - (umaxstr - buf) - 1;
+ size_t z = width;
+
+ for (; z < min_width; z++)
+ *lbuf.end++ = '0';
+
+ memcpy (lbuf.end, umaxstr, width);
+ lbuf.end += width;
+}
+
+static void
+print_uintmaxes (uintmax_t t1, uintmax_t t0)
+{
+ uintmax_t q, r;
+
+ if (t1 == 0)
+ lbuf_putint (t0, 0);
+ else
+ {
+ /* Use very plain code here since it seems hard to write fast code
+ without assuming a specific word size. */
+ q = t1 / 1000000000;
+ r = t1 % 1000000000;
+ udiv_qrnnd (t0, r, r, t0, 1000000000);
+ print_uintmaxes (q, t0);
+ lbuf_putint (r, 9);
+ }
+}
+
+/* Single-precision factoring */
+static void
+print_factors_single (uintmax_t t1, uintmax_t t0)
+{
+ struct factors factors;
+
+ print_uintmaxes (t1, t0);
+ lbuf_putc (':');
+
+ factor (t1, t0, &factors);
+
+ for (unsigned int j = 0; j < factors.nfactors; j++)
+ for (unsigned int k = 0; k < factors.e[j]; k++)
+ {
+ lbuf_putc (' ');
+ print_uintmaxes (0, factors.p[j]);
+ }
+
+ if (factors.plarge[1])
+ {
+ lbuf_putc (' ');
+ print_uintmaxes (factors.plarge[1], factors.plarge[0]);
+ }
+
+ lbuf_putc ('\n');
+}
+
+/* Emit the factors of the indicated number. If we have the option of using
+ either algorithm, we select on the basis of the length of the number.
+ For longer numbers, we prefer the MP algorithm even if the native algorithm
+ has enough digits, because the algorithm is better. The turnover point
+ depends on the value. */
+static bool
+print_factors (const char *input)
+{
+ uintmax_t t1, t0;
+
+ /* Try converting the number to one or two words. If it fails, use GMP or
+ print an error message. The 2nd condition checks that the most
+ significant bit of the two-word number is clear, in a typesize neutral
+ way. */
+ strtol_error err = strto2uintmax (&t1, &t0, input);
+
+ switch (err)
+ {
+ case LONGINT_OK:
+ if (((t1 << 1) >> 1) == t1)
+ {
+ devmsg ("[using single-precision arithmetic] ");
+ print_factors_single (t1, t0);
+ return true;
+ }
+ break;
+
+ case LONGINT_OVERFLOW:
+ /* Try GMP. */
+ break;
+
+ default:
+ error (0, 0, _("%s is not a valid positive integer"), quote (input));
return false;
}
- n_factors = factor (n, MAX_N_FACTORS, factors);
- printf ("%s:", umaxtostr (n, buf));
- for (i = 0; i < n_factors; i++)
- printf (" %s", umaxtostr (factors[i], buf));
+
+#if HAVE_GMP
+ devmsg ("[using arbitrary-precision arithmetic] ");
+ mpz_t t;
+ struct mp_factors factors;
+
+ mpz_init_set_str (t, input, 10);
+
+ gmp_printf ("%Zd:", t);
+ mp_factor (t, &factors);
+
+ for (unsigned int j = 0; j < factors.nfactors; j++)
+ for (unsigned int k = 0; k < factors.e[j]; k++)
+ gmp_printf (" %Zd", factors.p[j]);
+
+ mp_factor_clear (&factors);
+ mpz_clear (t);
putchar ('\n');
+ fflush (stdout);
return true;
+#else
+ error (0, 0, _("%s is too large"), quote (input));
+ return false;
+#endif
+}
+
+void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ emit_try_help ();
+ else
+ {
+ printf (_("\
+Usage: %s [NUMBER]...\n\
+ or: %s OPTION\n\
+"),
+ program_name, program_name);
+ fputs (_("\
+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 (HELP_OPTION_DESCRIPTION, stdout);
+ fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ emit_ancillary_info (PROGRAM_NAME);
+ }
+ exit (status);
}
static bool
@@ -174,12 +2556,12 @@ do_stdin (void)
init_tokenbuffer (&tokenbuffer);
- for (;;)
+ while (true)
{
size_t token_length = readtoken (stdin, DELIM, sizeof (DELIM) - 1,
- &tokenbuffer);
+ &tokenbuffer);
if (token_length == (size_t) -1)
- break;
+ break;
ok &= print_factors (tokenbuffer.buffer);
}
free (tokenbuffer.buffer);
@@ -190,31 +2572,63 @@ do_stdin (void)
int
main (int argc, char **argv)
{
- bool ok;
-
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
+ lbuf_alloc ();
atexit (close_stdout);
+ atexit (lbuf_flush);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
- if (getopt_long (argc, argv, "", NULL, NULL) != -1)
- usage (EXIT_FAILURE);
+ int c;
+ while ((c = getopt_long (argc, argv, "", long_options, NULL)) != -1)
+ {
+ switch (c)
+ {
+ case DEV_DEBUG_OPTION:
+ dev_debug = true;
+ break;
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
+ }
+
+#if STAT_SQUFOF
+ memset (q_freq, 0, sizeof (q_freq));
+#endif
+
+ bool ok;
if (argc <= optind)
ok = do_stdin ();
else
{
- int i;
ok = true;
- for (i = optind; i < argc; i++)
- if (! print_factors (argv[i]))
- ok = false;
+ for (int i = optind; i < argc; i++)
+ if (! print_factors (argv[i]))
+ ok = false;
+ }
+
+#if STAT_SQUFOF
+ if (q_freq[0] > 0)
+ {
+ double acc_f;
+ printf ("q freq. cum. freq.(total: %d)\n", q_freq[0]);
+ for (unsigned int i = 1, acc_f = 0.0; i <= Q_FREQ_SIZE; i++)
+ {
+ double f = (double) q_freq[i] / q_freq[0];
+ acc_f += f;
+ printf ("%s%d %.2f%% %.2f%%\n", i == Q_FREQ_SIZE ? ">=" : "", i,
+ 100.0 * f, 100.0 * acc_f);
+ }
}
+#endif
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/fiemap.h b/src/fiemap.h
new file mode 100644
index 0000000..88a9fa6
--- /dev/null
+++ b/src/fiemap.h
@@ -0,0 +1,107 @@
+/* FS_IOC_FIEMAP ioctl infrastructure.
+ Some portions copyright (C) 2007 Cluster File Systems, Inc
+ Authors: Mark Fasheh <mfasheh@suse.com>
+ Kalpak Shah <kalpak.shah@sun.com>
+ Andreas Dilger <adilger@sun.com>. */
+
+/* Copy from kernel, modified to respect GNU code style by Jie Liu. */
+
+#ifndef _LINUX_FIEMAP_H
+# define _LINUX_FIEMAP_H
+
+# include <stdint.h>
+
+struct fiemap_extent
+{
+ /* Logical offset in bytes for the start of the extent
+ from the beginning of the file. */
+ uint64_t fe_logical;
+
+ /* Physical offset in bytes for the start of the extent
+ from the beginning of the disk. */
+ uint64_t fe_physical;
+
+ /* Length in bytes for this extent. */
+ uint64_t fe_length;
+
+ uint64_t fe_reserved64[2];
+
+ /* FIEMAP_EXTENT_* flags for this extent. */
+ uint32_t fe_flags;
+
+ uint32_t fe_reserved[3];
+};
+
+struct fiemap
+{
+ /* Logical offset(inclusive) at which to start mapping(in). */
+ uint64_t fm_start;
+
+ /* Logical length of mapping which userspace wants(in). */
+ uint64_t fm_length;
+
+ /* FIEMAP_FLAG_* flags for request(in/out). */
+ uint32_t fm_flags;
+
+ /* Number of extents that were mapped(out). */
+ uint32_t fm_mapped_extents;
+
+ /* Size of fm_extents array(in). */
+ uint32_t fm_extent_count;
+
+ uint32_t fm_reserved;
+
+ /* Array of mapped extents(out).
+ This is protected by the ifdef because it uses non standard
+ zero length arrays. Note C99 has the equivalent flexible arrays,
+ but we don't use those for maximum portability to older systems. */
+# ifdef __linux__
+ struct fiemap_extent fm_extents[0];
+# endif
+};
+
+/* The maximum offset can be mapped for a file. */
+# define FIEMAP_MAX_OFFSET (~0ULL)
+
+/* Sync file data before map. */
+# define FIEMAP_FLAG_SYNC 0x00000001
+
+/* Map extented attribute tree. */
+# define FIEMAP_FLAG_XATTR 0x00000002
+
+# define FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR)
+
+/* Last extent in file. */
+# define FIEMAP_EXTENT_LAST 0x00000001
+
+/* Data location unknown. */
+# define FIEMAP_EXTENT_UNKNOWN 0x00000002
+
+/* Location still pending, Sets EXTENT_UNKNOWN. */
+# define FIEMAP_EXTENT_DELALLOC 0x00000004
+
+/* Data cannot be read while fs is unmounted. */
+# define FIEMAP_EXTENT_ENCODED 0x00000008
+
+/* Data is encrypted by fs. Sets EXTENT_NO_BYPASS. */
+# define FIEMAP_EXTENT_DATA_ENCRYPTED 0x00000080
+
+/* Extent offsets may not be block aligned. */
+# define FIEMAP_EXTENT_NOT_ALIGNED 0x00000100
+
+/* Data mixed with metadata. Sets EXTENT_NOT_ALIGNED. */
+# define FIEMAP_EXTENT_DATA_INLINE 0x00000200
+
+/* Multiple files in block. Set EXTENT_NOT_ALIGNED. */
+# define FIEMAP_EXTENT_DATA_TAIL 0x00000400
+
+/* Space allocated, but not data (i.e., zero). */
+# define FIEMAP_EXTENT_UNWRITTEN 0x00000800
+
+/* File does not natively support extents. Result merged for efficiency. */
+# define FIEMAP_EXTENT_MERGED 0x00001000
+
+/* Space shared with other files. */
+# define FIEMAP_EXTENT_SHARED 0x00002000
+
+#endif
diff --git a/src/find-mount-point.c b/src/find-mount-point.c
new file mode 100644
index 0000000..b38f8ff
--- /dev/null
+++ b/src/find-mount-point.c
@@ -0,0 +1,112 @@
+/* find-mount-point.c -- find the root mount point for a file.
+ Copyright (C) 2010-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+#include <config.h>
+#include <sys/types.h>
+
+#include "system.h"
+#include "error.h"
+#include "save-cwd.h"
+#include "xgetcwd.h"
+#include "find-mount-point.h"
+
+/* 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 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 = NULL; /* The malloc'd mount point. */
+
+ if (save_cwd (&cwd) != 0)
+ {
+ error (0, errno, _("cannot get current directory"));
+ return NULL;
+ }
+
+ if (S_ISDIR (file_stat->st_mode))
+ /* FILE is a directory, so just chdir there directly. */
+ {
+ last_stat = *file_stat;
+ if (chdir (file) < 0)
+ {
+ error (0, errno, _("cannot change to directory %s"), quoteaf (file));
+ 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);
+
+ if (chdir (dir) < 0)
+ {
+ error (0, errno, _("cannot change to directory %s"), quoteaf (dir));
+ return NULL;
+ }
+
+ if (stat (".", &last_stat) < 0)
+ {
+ error (0, errno, _("cannot stat current directory (now %s)"),
+ quoteaf (dir));
+ goto done;
+ }
+ }
+
+ /* Now walk up FILE's parents until we find another file system or /,
+ chdiring as we go. LAST_STAT holds stat information for the last place
+ we visited. */
+ while (true)
+ {
+ struct stat st;
+ if (stat ("..", &st) < 0)
+ {
+ error (0, errno, _("cannot stat %s"), quoteaf (".."));
+ goto done;
+ }
+ if (st.st_dev != last_stat.st_dev || st.st_ino == last_stat.st_ino)
+ /* cwd is the mount point. */
+ break;
+ if (chdir ("..") < 0)
+ {
+ error (0, errno, _("cannot change to directory %s"), quoteaf (".."));
+ goto done;
+ }
+ last_stat = st;
+ }
+
+ /* Finally reached a mount point, see what it's called. */
+ mp = xgetcwd ();
+
+done:
+ /* Restore the original cwd. */
+ {
+ int save_errno = errno;
+ if (restore_cwd (&cwd) != 0)
+ error (EXIT_FAILURE, errno,
+ _("failed to return to initial working directory"));
+ free_cwd (&cwd);
+ errno = save_errno;
+ }
+
+ return mp;
+}
diff --git a/src/find-mount-point.h b/src/find-mount-point.h
new file mode 100644
index 0000000..9b94df7
--- /dev/null
+++ b/src/find-mount-point.h
@@ -0,0 +1,17 @@
+/* find-mount-point.h -- find the root mount point for a file.
+ Copyright (C) 2010-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+extern char *find_mount_point (char const *, struct stat const *);
diff --git a/src/fmt.c b/src/fmt.c
index 5ccc8c4..78c6994 100644
--- a/src/fmt.c
+++ b/src/fmt.c
@@ -1,10 +1,10 @@
/* GNU fmt -- simple text formatter.
- Copyright (C) 1994-2006 Free Software Foundation, Inc.
+ Copyright (C) 1994-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Ross Paterson <rap@doc.ic.ac.uk>. */
@@ -21,6 +20,7 @@
#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>
+#include <assert.h>
/* Redefine. Otherwise, systems (Unicos for one) with headers that define
it to be a type get syntax errors for the variable declaration below. */
@@ -28,13 +28,13 @@
#include "system.h"
#include "error.h"
-#include "quote.h"
-#include "xstrtol.h"
+#include "fadvise.h"
+#include "xdectoint.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "fmt"
-#define AUTHORS "Ross Paterson"
+#define AUTHORS proper_name ("Ross Paterson")
/* The following parameters represent the program's idea of what is
"best". Adjust to taste, subject to the caveats given. */
@@ -68,7 +68,7 @@ typedef long int COST;
#define SQR(n) ((n) * (n))
#define EQUIV(n) SQR ((COST) (n))
-/* Cost of a filled line n chars longer or shorter than best_width. */
+/* Cost of a filled line n chars longer or shorter than goal_width. */
#define SHORT_COST(n) EQUIV ((n) * 10)
/* Cost of the difference between adjacent filled lines. */
@@ -116,7 +116,7 @@ typedef long int COST;
/* Extra ctype(3)-style macros. */
-#define isopen(c) (strchr ("([`'\"", c) != NULL)
+#define isopen(c) (strchr ("(['`\"", c) != NULL)
#define isclose(c) (strchr (")]'\"", c) != NULL)
#define isperiod(c) (strchr (".?!", c) != NULL)
@@ -168,9 +168,6 @@ static void put_line (WORD *w, int indent);
static void put_word (WORD *w);
static void put_space (int space);
-/* The name this program was run with. */
-const char *program_name;
-
/* Option values. */
/* If true, first 2 lines may have different indent (default false). */
@@ -204,7 +201,7 @@ static int prefix_lead_space;
static int prefix_length;
/* The preferred width of text lines, set to LEEWAY % less than max_width. */
-static int best_width;
+static int goal_width;
/* Dynamic variables. */
@@ -266,45 +263,43 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
- printf (_("Usage: %s [-DIGITS] [OPTION]... [FILE]...\n"), program_name);
+ printf (_("Usage: %s [-WIDTH] [OPTION]... [FILE]...\n"), program_name);
fputs (_("\
Reformat each paragraph in the FILE(s), writing to standard output.\n\
-If no FILE or if FILE is `-', read standard input.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
+The option -WIDTH is an abbreviated form of --width=DIGITS.\n\
"), stdout);
+
+ 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);
+ stdout);
+ /* 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);
- fputs (_("\
-\n\
-With no FILE, or when FILE is -, read standard input.\n"),
- stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
/* Decode options and launch execution. */
-static const struct option long_options[] =
+static struct option const long_options[] =
{
{"crown-margin", no_argument, NULL, 'c'},
{"prefix", required_argument, NULL, 'p'},
@@ -312,6 +307,7 @@ static const struct option long_options[] =
{"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},
{NULL, 0, NULL, 0},
@@ -323,9 +319,10 @@ main (int argc, char **argv)
int optchar;
bool ok = true;
char const *max_width_option = NULL;
+ char const *goal_width_option = NULL;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -348,41 +345,45 @@ main (int argc, char **argv)
argc--;
}
- while ((optchar = getopt_long (argc, argv, "0123456789cstuw:p:",
- long_options, NULL))
- != -1)
+ while ((optchar = getopt_long (argc, argv, "0123456789cstuw:p:g:",
+ long_options, NULL))
+ != -1)
switch (optchar)
{
default:
- if (ISDIGIT (optchar))
- error (0, 0, _("invalid option -- %c; -WIDTH is recognized\
+ if (ISDIGIT (optchar))
+ error (0, 0, _("invalid option -- %c; -WIDTH is recognized\
only when it is the first\noption; use -w N instead"),
- optchar);
- usage (EXIT_FAILURE);
+ optchar);
+ usage (EXIT_FAILURE);
case 'c':
- crown = true;
- break;
+ crown = true;
+ break;
case 's':
- split = true;
- break;
+ split = true;
+ break;
case 't':
- tagged = true;
- break;
+ tagged = true;
+ break;
case 'u':
- uniform = true;
- break;
+ uniform = true;
+ break;
case 'w':
- max_width_option = optarg;
- break;
+ max_width_option = optarg;
+ break;
+
+ case 'g':
+ goal_width_option = optarg;
+ break;
case 'p':
- set_prefix (optarg);
- break;
+ set_prefix (optarg);
+ break;
case_GETOPT_HELP_CHAR;
@@ -393,50 +394,57 @@ main (int argc, char **argv)
if (max_width_option)
{
/* Limit max_width to MAXCHARS / 2; otherwise, the resulting
- output can be quite ugly. */
- unsigned long int tmp;
- if (! (xstrtoul (max_width_option, NULL, 10, &tmp, "") == LONGINT_OK
- && tmp <= MAXCHARS / 2))
- error (EXIT_FAILURE, 0, _("invalid width: %s"),
- quote (max_width_option));
- max_width = tmp;
+ output can be quite ugly. */
+ max_width = xdectoumax (max_width_option, 0, MAXCHARS / 2, "",
+ _("invalid width"), 0);
}
- best_width = max_width * (2 * (100 - LEEWAY) + 1) / 200;
+ if (goal_width_option)
+ {
+ /* Limit goal_width to max_width. */
+ goal_width = xdectoumax (goal_width_option, 0, max_width, "",
+ _("invalid width"), 0);
+ if (max_width_option == NULL)
+ max_width = goal_width + 10;
+ }
+ else
+ {
+ goal_width = max_width * (2 * (100 - LEEWAY) + 1) / 200;
+ }
if (optind == argc)
fmt (stdin);
else
{
for (; optind < argc; optind++)
- {
- char *file = argv[optind];
- if (STREQ (file, "-"))
- fmt (stdin);
- else
- {
- FILE *in_stream;
- in_stream = fopen (file, "r");
- if (in_stream != NULL)
- {
- fmt (in_stream);
- if (fclose (in_stream) == EOF)
- {
- error (0, errno, "%s", file);
- ok = false;
- }
- }
- else
- {
- error (0, errno, _("cannot open %s for reading"),
- quote (file));
- ok = false;
- }
- }
- }
+ {
+ char *file = argv[optind];
+ if (STREQ (file, "-"))
+ fmt (stdin);
+ else
+ {
+ FILE *in_stream;
+ in_stream = fopen (file, "r");
+ if (in_stream != NULL)
+ {
+ fmt (in_stream);
+ if (fclose (in_stream) == EOF)
+ {
+ error (0, errno, "%s", quotef (file));
+ ok = false;
+ }
+ }
+ else
+ {
+ error (0, errno, _("cannot open %s for reading"),
+ quoteaf (file));
+ ok = false;
+ }
+ }
+ }
}
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
/* Trim space from the front and back of the string P, yielding the prefix,
@@ -467,6 +475,7 @@ set_prefix (char *p)
static void
fmt (FILE *f)
{
+ fadvise (f, FADVISE_SEQUENTIAL);
tabs = false;
other_indent = 0;
next_char = get_prefix (f);
@@ -477,7 +486,7 @@ fmt (FILE *f)
}
}
-/* Set the global variable `other_indent' according to SAME_PARAGRAPH
+/* Set the global variable 'other_indent' according to SAME_PARAGRAPH
and other global variables. */
static void
@@ -492,9 +501,9 @@ set_other_indent (bool same_paragraph)
else if (tagged)
{
if (same_paragraph && in_column != first_indent)
- {
- other_indent = in_column;
- }
+ {
+ other_indent = in_column;
+ }
/* Only one line: use the secondary indent from last time if it
splits, or 0 if there have been no multi-line paragraphs in the
@@ -502,7 +511,7 @@ set_other_indent (bool same_paragraph)
pick a new secondary indent. */
else if (other_indent == first_indent)
- other_indent = first_indent == 0 ? DEF_INDENT : 0;
+ other_indent = first_indent == 0 ? DEF_INDENT : 0;
}
else
{
@@ -536,15 +545,15 @@ get_paragraph (FILE *f)
/* Scan (and copy) blank lines, and lines not introduced by the prefix. */
while (c == '\n' || c == EOF
- || next_prefix_indent < prefix_lead_space
- || in_column < next_prefix_indent + prefix_full_length)
+ || next_prefix_indent < prefix_lead_space
+ || in_column < next_prefix_indent + prefix_full_length)
{
c = copy_rest (f, c);
if (c == EOF)
- {
- next_char = EOF;
- return false;
- }
+ {
+ next_char = EOF;
+ return false;
+ }
putchar ('\n');
c = get_prefix (f);
}
@@ -567,30 +576,35 @@ get_paragraph (FILE *f)
else if (crown)
{
if (same_para (c))
- {
- do
- { /* for each line till the end of the para */
- c = get_line (f, c);
- }
- while (same_para (c) && in_column == other_indent);
- }
+ {
+ do
+ { /* for each line till the end of the para */
+ c = get_line (f, c);
+ }
+ while (same_para (c) && in_column == other_indent);
+ }
}
else if (tagged)
{
if (same_para (c) && in_column != first_indent)
- {
- do
- { /* for each line till the end of the para */
- c = get_line (f, c);
- }
- while (same_para (c) && in_column == other_indent);
- }
+ {
+ do
+ { /* for each line till the end of the para */
+ c = get_line (f, c);
+ }
+ while (same_para (c) && in_column == other_indent);
+ }
}
else
{
while (same_para (c) && in_column == other_indent)
- c = get_line (f, c);
+ c = get_line (f, c);
}
+
+ /* Tell static analysis tools that using word_limit[-1] is ok.
+ word_limit is guaranteed to have been incremented by get_line. */
+ assert (word < word_limit);
+
(word_limit - 1)->period = (word_limit - 1)->final = true;
next_char = c;
return true;
@@ -611,11 +625,11 @@ copy_rest (FILE *f, int c)
{
put_space (next_prefix_indent);
for (s = prefix; out_column != in_column && *s; out_column++)
- putchar (*s++);
+ putchar (*s++);
if (c != EOF && c != '\n')
- put_space (in_column - out_column);
+ put_space (in_column - out_column);
if (c == EOF && in_column >= next_prefix_indent + prefix_length)
- putchar ('\n');
+ putchar ('\n');
}
while (c != '\n' && c != EOF)
{
@@ -633,8 +647,8 @@ static bool
same_para (int c)
{
return (next_prefix_indent == prefix_indent
- && in_column >= next_prefix_indent + prefix_full_length
- && c != '\n' && c != EOF);
+ && in_column >= next_prefix_indent + prefix_full_length
+ && c != '\n' && c != EOF);
}
/* Read a line from input file F, given first non-blank character C
@@ -662,15 +676,15 @@ get_line (FILE *f, int c)
word_limit->text = wptr;
do
- {
- if (wptr == end_of_parabuf)
- {
- set_other_indent (true);
- flush_paragraph ();
- }
- *wptr++ = c;
- c = getc (f);
- }
+ {
+ if (wptr == end_of_parabuf)
+ {
+ set_other_indent (true);
+ flush_paragraph ();
+ }
+ *wptr++ = c;
+ c = getc (f);
+ }
while (c != EOF && !isspace (c));
in_column += word_limit->length = wptr - word_limit->text;
check_punctuation (word_limit);
@@ -681,15 +695,15 @@ get_line (FILE *f, int c)
c = get_space (f, c);
word_limit->space = in_column - start;
word_limit->final = (c == EOF
- || (word_limit->period
- && (c == '\n' || word_limit->space > 1)));
+ || (word_limit->period
+ && (c == '\n' || word_limit->space > 1)));
if (c == '\n' || c == EOF || uniform)
- word_limit->space = word_limit->final ? 2 : 1;
+ word_limit->space = word_limit->final ? 2 : 1;
if (word_limit == end_of_word)
- {
- set_other_indent (true);
- flush_paragraph ();
- }
+ {
+ set_other_indent (true);
+ flush_paragraph ();
+ }
word_limit++;
}
while (c != '\n' && c != EOF);
@@ -714,13 +728,13 @@ get_prefix (FILE *f)
const char *p;
next_prefix_indent = in_column;
for (p = prefix; *p != '\0'; p++)
- {
- unsigned char pc = *p;
- if (c != pc)
- return c;
- in_column++;
- c = getc (f);
- }
+ {
+ unsigned char pc = *p;
+ if (c != pc)
+ return c;
+ in_column++;
+ c = getc (f);
+ }
c = get_space (f, c);
}
return c;
@@ -732,17 +746,17 @@ get_prefix (FILE *f)
static int
get_space (FILE *f, int c)
{
- for (;;)
+ while (true)
{
if (c == ' ')
- in_column++;
+ in_column++;
else if (c == '\t')
- {
- tabs = true;
- in_column = (in_column / TABWIDTH + 1) * TABWIDTH;
- }
+ {
+ tabs = true;
+ in_column = (in_column / TABWIDTH + 1) * TABWIDTH;
+ }
else
- return c;
+ return c;
c = getc (f);
}
}
@@ -798,12 +812,12 @@ flush_paragraph (void)
for (w = word->next_break; w != word_limit; w = w->next_break)
{
if (w->best_cost - w->next_break->best_cost < best_break)
- {
- split_point = w;
- best_break = w->best_cost - w->next_break->best_cost;
- }
+ {
+ split_point = w;
+ best_break = w->best_cost - w->next_break->best_cost;
+ }
if (best_break <= MAXCOST - LINE_CREDIT)
- best_break += LINE_CREDIT;
+ best_break += LINE_CREDIT;
}
put_paragraph (split_point);
@@ -852,30 +866,30 @@ fmt_paragraph (void)
w = start;
len += w->length;
do
- {
- w++;
-
- /* Consider breaking before w. */
-
- wcost = line_cost (w, len) + w->best_cost;
- if (start == word && last_line_length > 0)
- wcost += RAGGED_COST (len - last_line_length);
- if (wcost < best)
- {
- best = wcost;
- start->next_break = w;
- start->line_length = len;
- }
-
- /* This is a kludge to keep us from computing `len' as the
- sum of the sentinel length and some non-zero number.
- Since the sentinel w->length may be INT_MAX, adding
- to that would give a negative result. */
- if (w == word_limit)
- break;
-
- len += (w - 1)->space + w->length; /* w > start >= word */
- }
+ {
+ w++;
+
+ /* Consider breaking before w. */
+
+ wcost = line_cost (w, len) + w->best_cost;
+ if (start == word && last_line_length > 0)
+ wcost += RAGGED_COST (len - last_line_length);
+ if (wcost < best)
+ {
+ best = wcost;
+ start->next_break = w;
+ start->line_length = len;
+ }
+
+ /* This is a kludge to keep us from computing 'len' as the
+ sum of the sentinel length and some non-zero number.
+ Since the sentinel w->length may be INT_MAX, adding
+ to that would give a negative result. */
+ if (w == word_limit)
+ break;
+
+ len += (w - 1)->space + w->length; /* w > start >= word */
+ }
while (len < max_width);
start->best_cost = best + base_cost (start);
}
@@ -896,16 +910,16 @@ base_cost (WORD *this)
if (this > word)
{
if ((this - 1)->period)
- {
- if ((this - 1)->final)
- cost -= SENTENCE_BONUS;
- else
- cost += NOBREAK_COST;
- }
+ {
+ if ((this - 1)->final)
+ cost -= SENTENCE_BONUS;
+ else
+ cost += NOBREAK_COST;
+ }
else if ((this - 1)->punct)
- cost -= PUNCT_BONUS;
+ cost -= PUNCT_BONUS;
else if (this > word + 1 && (this - 2)->final)
- cost += WIDOW_COST ((this - 1)->length);
+ cost += WIDOW_COST ((this - 1)->length);
}
if (this->paren)
@@ -927,7 +941,7 @@ line_cost (WORD *next, int len)
if (next == word_limit)
return 0;
- n = best_width - len;
+ n = goal_width - len;
cost = SHORT_COST (n);
if (next->next_break != word_limit)
{
@@ -1001,11 +1015,11 @@ put_space (int space)
{
tab_target = space_target / TABWIDTH * TABWIDTH;
if (out_column + 1 < tab_target)
- while (out_column < tab_target)
- {
- putchar ('\t');
- out_column = (out_column / TABWIDTH + 1) * TABWIDTH;
- }
+ while (out_column < tab_target)
+ {
+ putchar ('\t');
+ out_column = (out_column / TABWIDTH + 1) * TABWIDTH;
+ }
}
while (out_column < space_target)
{
diff --git a/src/fold.c b/src/fold.c
index 0d4ea58..e9bbd8e 100644
--- a/src/fold.c
+++ b/src/fold.c
@@ -1,10 +1,10 @@
/* fold -- wrap each input line to fit in specified width.
- Copyright (C) 91, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1991-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by David MacKenzie, djm@gnu.ai.mit.edu. */
@@ -25,18 +24,15 @@
#include "system.h"
#include "error.h"
-#include "quote.h"
-#include "xstrtol.h"
+#include "fadvise.h"
+#include "xdectoint.h"
#define TAB_WIDTH 8
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "fold"
-#define AUTHORS "David MacKenzie"
-
-/* The name this program was run with. */
-char *program_name;
+#define AUTHORS proper_name ("David MacKenzie")
/* If nonzero, try to break on whitespace. */
static bool break_spaces;
@@ -63,22 +59,20 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
- program_name);
- fputs (_("\
-Wrap input lines in each FILE (standard input by default), writing to\n\
-standard output.\n\
-\n\
-"), stdout);
+ program_name);
fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
+Wrap input lines in each FILE, writing to standard output.\n\
"), stdout);
+
+ emit_stdin_note ();
+ emit_mandatory_arg_note ();
+
fputs (_("\
-b, --bytes count bytes rather than columns\n\
-s, --spaces break at spaces\n\
@@ -86,7 +80,7 @@ Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -101,16 +95,16 @@ adjust_column (size_t column, char c)
if (!count_bytes)
{
if (c == '\b')
- {
- if (column > 0)
- column--;
- }
+ {
+ if (column > 0)
+ column--;
+ }
else if (c == '\r')
- column = 0;
+ column = 0;
else if (c == '\t')
- column += TAB_WIDTH - column % TAB_WIDTH;
+ column += TAB_WIDTH - column % TAB_WIDTH;
else /* if (isprint (c)) */
- column++;
+ column++;
}
else
column++;
@@ -127,7 +121,7 @@ fold_file (char const *filename, size_t width)
FILE *istream;
int c;
size_t column = 0; /* Screen column where next char will go. */
- size_t offset_out = 0; /* Index in `line_out' for next char. */
+ size_t offset_out = 0; /* Index in 'line_out' for next char. */
static char *line_out = NULL;
static size_t allocated_out = 0;
int saved_errno;
@@ -142,78 +136,80 @@ fold_file (char const *filename, size_t width)
if (istream == NULL)
{
- error (0, errno, "%s", filename);
+ error (0, errno, "%s", quotef (filename));
return false;
}
+ fadvise (istream, FADVISE_SEQUENTIAL);
+
while ((c = getc (istream)) != EOF)
{
if (offset_out + 1 >= allocated_out)
- line_out = X2REALLOC (line_out, &allocated_out);
+ line_out = X2REALLOC (line_out, &allocated_out);
if (c == '\n')
- {
- line_out[offset_out++] = c;
- fwrite (line_out, sizeof (char), offset_out, stdout);
- column = offset_out = 0;
- continue;
- }
+ {
+ line_out[offset_out++] = c;
+ fwrite (line_out, sizeof (char), offset_out, stdout);
+ column = offset_out = 0;
+ continue;
+ }
rescan:
column = adjust_column (column, c);
if (column > width)
- {
- /* This character would make the line too long.
- Print the line plus a newline, and make this character
- start the next line. */
- if (break_spaces)
- {
- bool found_blank = false;
- size_t logical_end = offset_out;
-
- /* Look for the last blank. */
- while (logical_end)
- {
- --logical_end;
- if (isblank (to_uchar (line_out[logical_end])))
- {
- found_blank = true;
- break;
- }
- }
-
- if (found_blank)
- {
- size_t i;
-
- /* Found a blank. Don't output the part after it. */
- logical_end++;
- fwrite (line_out, sizeof (char), (size_t) logical_end,
- stdout);
- putchar ('\n');
- /* Move the remainder to the beginning of the next line.
- The areas being copied here might overlap. */
- memmove (line_out, line_out + logical_end,
- offset_out - logical_end);
- offset_out -= logical_end;
- for (column = i = 0; i < offset_out; i++)
- column = adjust_column (column, line_out[i]);
- goto rescan;
- }
- }
-
- if (offset_out == 0)
- {
- line_out[offset_out++] = c;
- continue;
- }
-
- line_out[offset_out++] = '\n';
- fwrite (line_out, sizeof (char), (size_t) offset_out, stdout);
- column = offset_out = 0;
- goto rescan;
- }
+ {
+ /* This character would make the line too long.
+ Print the line plus a newline, and make this character
+ start the next line. */
+ if (break_spaces)
+ {
+ bool found_blank = false;
+ size_t logical_end = offset_out;
+
+ /* Look for the last blank. */
+ while (logical_end)
+ {
+ --logical_end;
+ if (isblank (to_uchar (line_out[logical_end])))
+ {
+ found_blank = true;
+ break;
+ }
+ }
+
+ if (found_blank)
+ {
+ size_t i;
+
+ /* Found a blank. Don't output the part after it. */
+ logical_end++;
+ fwrite (line_out, sizeof (char), (size_t) logical_end,
+ stdout);
+ putchar ('\n');
+ /* Move the remainder to the beginning of the next line.
+ The areas being copied here might overlap. */
+ memmove (line_out, line_out + logical_end,
+ offset_out - logical_end);
+ offset_out -= logical_end;
+ for (column = i = 0; i < offset_out; i++)
+ column = adjust_column (column, line_out[i]);
+ goto rescan;
+ }
+ }
+
+ if (offset_out == 0)
+ {
+ line_out[offset_out++] = c;
+ continue;
+ }
+
+ line_out[offset_out++] = '\n';
+ fwrite (line_out, sizeof (char), (size_t) offset_out, stdout);
+ column = offset_out = 0;
+ goto rescan;
+ }
line_out[offset_out++] = c;
}
@@ -225,14 +221,14 @@ fold_file (char const *filename, size_t width)
if (ferror (istream))
{
- error (0, saved_errno, "%s", filename);
+ error (0, saved_errno, "%s", quotef (filename));
if (!STREQ (filename, "-"))
- fclose (istream);
+ fclose (istream);
return false;
}
if (!STREQ (filename, "-") && fclose (istream) == EOF)
{
- error (0, errno, "%s", filename);
+ error (0, errno, "%s", quotef (filename));
return false;
}
@@ -248,7 +244,7 @@ main (int argc, char **argv)
bool ok;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -262,44 +258,38 @@ main (int argc, char **argv)
char optargbuf[2];
switch (optc)
- {
- case 'b': /* Count bytes rather than columns. */
- count_bytes = true;
- break;
-
- case 's': /* Break at word boundaries. */
- break_spaces = true;
- break;
-
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- if (optarg)
- optarg--;
- else
- {
- optargbuf[0] = optc;
- optargbuf[1] = '\0';
- optarg = optargbuf;
- }
- /* Fall through. */
- case 'w': /* Line width. */
- {
- unsigned long int tmp_ulong;
- if (! (xstrtoul (optarg, NULL, 10, &tmp_ulong, "") == LONGINT_OK
- && 0 < tmp_ulong && tmp_ulong < SIZE_MAX - TAB_WIDTH))
- error (EXIT_FAILURE, 0,
- _("invalid number of columns: %s"), quote (optarg));
- width = tmp_ulong;
- }
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'b': /* Count bytes rather than columns. */
+ count_bytes = true;
+ break;
+
+ case 's': /* Break at word boundaries. */
+ break_spaces = true;
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (optarg)
+ optarg--;
+ else
+ {
+ optargbuf[0] = optc;
+ optargbuf[1] = '\0';
+ optarg = optargbuf;
+ }
+ /* Fall through. */
+ case 'w': /* Line width. */
+ width = xdectoumax (optarg, 1, SIZE_MAX - TAB_WIDTH - 1, "",
+ _("invalid number of columns"), 0);
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
}
if (argc == optind)
@@ -308,11 +298,11 @@ main (int argc, char **argv)
{
ok = true;
for (i = optind; i < argc; i++)
- ok &= fold_file (argv[i], width);
+ ok &= fold_file (argv[i], width);
}
if (have_read_stdin && fclose (stdin) == EOF)
error (EXIT_FAILURE, errno, "-");
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/fs-is-local.h b/src/fs-is-local.h
new file mode 100644
index 0000000..eaadde6
--- /dev/null
+++ b/src/fs-is-local.h
@@ -0,0 +1,121 @@
+/* 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. */
+static inline int
+is_local_fs_type (unsigned long int magic)
+{
+ switch (magic)
+ {
+ case S_MAGIC_ACFS: return 0;
+ case S_MAGIC_ADFS: return 1;
+ case S_MAGIC_AFFS: return 1;
+ case S_MAGIC_AFS: return 0;
+ case S_MAGIC_ANON_INODE_FS: return 1;
+ case S_MAGIC_AUFS: return 0;
+ case S_MAGIC_AUTOFS: return 1;
+ case S_MAGIC_BEFS: return 1;
+ case S_MAGIC_BDEVFS: return 1;
+ case S_MAGIC_BFS: return 1;
+ case S_MAGIC_BPF_FS: return 1;
+ case S_MAGIC_BINFMTFS: return 1;
+ case S_MAGIC_BTRFS: return 1;
+ case S_MAGIC_BTRFS_TEST: return 1;
+ case S_MAGIC_CEPH: return 0;
+ case S_MAGIC_CGROUP: return 1;
+ case S_MAGIC_CIFS: return 0;
+ case S_MAGIC_CODA: return 0;
+ case S_MAGIC_COH: return 1;
+ case S_MAGIC_CONFIGFS: return 1;
+ case S_MAGIC_CRAMFS: return 1;
+ case S_MAGIC_CRAMFS_WEND: return 1;
+ case S_MAGIC_DEBUGFS: return 1;
+ case S_MAGIC_DEVFS: return 1;
+ case S_MAGIC_DEVPTS: return 1;
+ case S_MAGIC_ECRYPTFS: return 1;
+ case S_MAGIC_EFIVARFS: return 1;
+ case S_MAGIC_EFS: return 1;
+ case S_MAGIC_EXOFS: return 1;
+ case S_MAGIC_EXT: return 1;
+ case S_MAGIC_EXT2: return 1;
+ case S_MAGIC_EXT2_OLD: return 1;
+ case S_MAGIC_F2FS: return 1;
+ case S_MAGIC_FAT: return 1;
+ case S_MAGIC_FHGFS: return 0;
+ case S_MAGIC_FUSEBLK: return 0;
+ case S_MAGIC_FUSECTL: return 0;
+ case S_MAGIC_FUTEXFS: return 1;
+ case S_MAGIC_GFS: return 0;
+ case S_MAGIC_GPFS: return 0;
+ case S_MAGIC_HFS: return 1;
+ case S_MAGIC_HFS_PLUS: return 1;
+ case S_MAGIC_HFS_X: return 1;
+ case S_MAGIC_HOSTFS: return 1;
+ case S_MAGIC_HPFS: return 1;
+ case S_MAGIC_HUGETLBFS: return 1;
+ case S_MAGIC_MTD_INODE_FS: return 1;
+ case S_MAGIC_IBRIX: return 0;
+ case S_MAGIC_INOTIFYFS: return 1;
+ case S_MAGIC_ISOFS: return 1;
+ case S_MAGIC_ISOFS_R_WIN: return 1;
+ case S_MAGIC_ISOFS_WIN: return 1;
+ case S_MAGIC_JFFS: return 1;
+ case S_MAGIC_JFFS2: return 1;
+ case S_MAGIC_JFS: return 1;
+ case S_MAGIC_KAFS: return 0;
+ case S_MAGIC_LOGFS: return 1;
+ case S_MAGIC_LUSTRE: return 0;
+ case S_MAGIC_MINIX: return 1;
+ case S_MAGIC_MINIX_30: return 1;
+ case S_MAGIC_MINIX_V2: return 1;
+ case S_MAGIC_MINIX_V2_30: return 1;
+ case S_MAGIC_MINIX_V3: return 1;
+ case S_MAGIC_MQUEUE: return 1;
+ case S_MAGIC_MSDOS: return 1;
+ case S_MAGIC_NCP: return 0;
+ case S_MAGIC_NFS: return 0;
+ case S_MAGIC_NFSD: return 0;
+ case S_MAGIC_NILFS: return 1;
+ case S_MAGIC_NSFS: return 1;
+ case S_MAGIC_NTFS: return 1;
+ case S_MAGIC_OPENPROM: return 1;
+ case S_MAGIC_OCFS2: return 0;
+ case S_MAGIC_OVERLAYFS: return 0;
+ case S_MAGIC_PANFS: return 0;
+ case S_MAGIC_PIPEFS: return 0;
+ case S_MAGIC_PROC: return 1;
+ case S_MAGIC_PSTOREFS: return 1;
+ case S_MAGIC_QNX4: return 1;
+ case S_MAGIC_QNX6: return 1;
+ case S_MAGIC_RAMFS: return 1;
+ case S_MAGIC_REISERFS: return 1;
+ case S_MAGIC_ROMFS: return 1;
+ case S_MAGIC_RPC_PIPEFS: return 1;
+ case S_MAGIC_SECURITYFS: return 1;
+ case S_MAGIC_SELINUX: return 1;
+ case S_MAGIC_SMACK: return 1;
+ case S_MAGIC_SMB: return 0;
+ case S_MAGIC_SNFS: return 0;
+ case S_MAGIC_SOCKFS: return 1;
+ case S_MAGIC_SQUASHFS: return 1;
+ case S_MAGIC_SYSFS: return 1;
+ case S_MAGIC_SYSV2: return 1;
+ case S_MAGIC_SYSV4: return 1;
+ case S_MAGIC_TMPFS: return 1;
+ case S_MAGIC_TRACEFS: return 1;
+ case S_MAGIC_UBIFS: return 1;
+ case S_MAGIC_UDF: return 1;
+ case S_MAGIC_UFS: return 1;
+ case S_MAGIC_UFS_BYTESWAPPED: return 1;
+ case S_MAGIC_USBDEVFS: return 1;
+ case S_MAGIC_V9FS: return 1;
+ case S_MAGIC_VMHGFS: return 0;
+ case S_MAGIC_VXFS: return 0;
+ case S_MAGIC_VZFS: return 1;
+ case S_MAGIC_XENFS: return 1;
+ case S_MAGIC_XENIX: return 1;
+ case S_MAGIC_XFS: return 1;
+ case S_MAGIC_XIAFS: return 1;
+ case S_MAGIC_ZFS: return 1;
+ default: return -1;
+ }
+}
diff --git a/src/fs.h b/src/fs.h
index 5ceea5b..aa67945 100644
--- a/src/fs.h
+++ b/src/fs.h
@@ -1,43 +1,118 @@
/* Define the magic numbers as given by statfs(2).
Please send additions to bug-coreutils@gnu.org and meskes@debian.org.
- This file is generated automatically from ./stat.c. */
+ This file is generated automatically from ./src/stat.c. */
#if defined __linux__
+# define S_MAGIC_ACFS 0x61636673
+# define S_MAGIC_ADFS 0xADF5
# define S_MAGIC_AFFS 0xADFF
+# define S_MAGIC_AFS 0x5346414F
+# define S_MAGIC_ANON_INODE_FS 0x09041934
+# define S_MAGIC_AUFS 0x61756673
+# define S_MAGIC_AUTOFS 0x0187
+# define S_MAGIC_BEFS 0x42465331
+# define S_MAGIC_BDEVFS 0x62646576
+# define S_MAGIC_BFS 0x1BADFACE
+# define S_MAGIC_BPF_FS 0xCAFE4A11
+# define S_MAGIC_BINFMTFS 0x42494E4D
+# define S_MAGIC_BTRFS 0x9123683E
+# define S_MAGIC_BTRFS_TEST 0x73727279
+# define S_MAGIC_CEPH 0x00C36400
+# define S_MAGIC_CGROUP 0x0027E0EB
+# define S_MAGIC_CIFS 0xFF534D42
+# define S_MAGIC_CODA 0x73757245
+# define S_MAGIC_COH 0x012FF7B7
+# define S_MAGIC_CONFIGFS 0x62656570
+# define S_MAGIC_CRAMFS 0x28CD3D45
+# define S_MAGIC_CRAMFS_WEND 0x453DCD28
+# define S_MAGIC_DEBUGFS 0x64626720
+# define S_MAGIC_DEVFS 0x1373
# define S_MAGIC_DEVPTS 0x1CD1
+# define S_MAGIC_ECRYPTFS 0xF15F
+# define S_MAGIC_EFIVARFS 0xDE5E81E4
+# define S_MAGIC_EFS 0x00414A53
+# define S_MAGIC_EXOFS 0x5DF5
# define S_MAGIC_EXT 0x137D
-# define S_MAGIC_EXT2_OLD 0xEF51
# define S_MAGIC_EXT2 0xEF53
-# define S_MAGIC_JFS 0x3153464a
-# define S_MAGIC_XFS 0x58465342
+# define S_MAGIC_EXT2_OLD 0xEF51
+# define S_MAGIC_F2FS 0xF2F52010
+# define S_MAGIC_FAT 0x4006
+# define S_MAGIC_FHGFS 0x19830326
+# define S_MAGIC_FUSEBLK 0x65735546
+# define S_MAGIC_FUSECTL 0x65735543
+# define S_MAGIC_FUTEXFS 0x0BAD1DEA
+# define S_MAGIC_GFS 0x01161970
+# define S_MAGIC_GPFS 0x47504653
+# define S_MAGIC_HFS 0x4244
+# define S_MAGIC_HFS_PLUS 0x482B
+# define S_MAGIC_HFS_X 0x4858
+# define S_MAGIC_HOSTFS 0x00C0FFEE
# define S_MAGIC_HPFS 0xF995E849
+# define S_MAGIC_HUGETLBFS 0x958458F6
+# define S_MAGIC_MTD_INODE_FS 0x11307854
+# define S_MAGIC_IBRIX 0x013111A8
+# define S_MAGIC_INOTIFYFS 0x2BAD1DEA
# define S_MAGIC_ISOFS 0x9660
-# define S_MAGIC_ISOFS_WIN 0x4000
# define S_MAGIC_ISOFS_R_WIN 0x4004
+# define S_MAGIC_ISOFS_WIN 0x4000
+# define S_MAGIC_JFFS 0x07C0
+# define S_MAGIC_JFFS2 0x72B6
+# define S_MAGIC_JFS 0x3153464A
+# define S_MAGIC_KAFS 0x6B414653
+# define S_MAGIC_LOGFS 0xC97E8168
+# define S_MAGIC_LUSTRE 0x0BD00BD0
# define S_MAGIC_MINIX 0x137F
# define S_MAGIC_MINIX_30 0x138F
# define S_MAGIC_MINIX_V2 0x2468
# define S_MAGIC_MINIX_V2_30 0x2478
-# define S_MAGIC_MSDOS 0x4d44
-# define S_MAGIC_FAT 0x4006
-# define S_MAGIC_NCP 0x564c
+# define S_MAGIC_MINIX_V3 0x4D5A
+# define S_MAGIC_MQUEUE 0x19800202
+# define S_MAGIC_MSDOS 0x4D44
+# define S_MAGIC_NCP 0x564C
# define S_MAGIC_NFS 0x6969
-# define S_MAGIC_PROC 0x9fa0
-# define S_MAGIC_SMB 0x517B
-# define S_MAGIC_XENIX 0x012FF7B4
-# define S_MAGIC_SYSV4 0x012FF7B5
-# define S_MAGIC_SYSV2 0x012FF7B6
-# define S_MAGIC_COH 0x012FF7B7
-# define S_MAGIC_UFS 0x00011954
-# define S_MAGIC_XIAFS 0x012FD16D
-# define S_MAGIC_NTFS 0x5346544e
-# define S_MAGIC_TMPFS 0x1021994
+# define S_MAGIC_NFSD 0x6E667364
+# define S_MAGIC_NILFS 0x3434
+# define S_MAGIC_NSFS 0x6E736673
+# define S_MAGIC_NTFS 0x5346544E
+# define S_MAGIC_OPENPROM 0x9FA1
+# define S_MAGIC_OCFS2 0x7461636F
+# define S_MAGIC_OVERLAYFS 0x794C7630
+# define S_MAGIC_PANFS 0xAAD7AAEA
+# define S_MAGIC_PIPEFS 0x50495045
+# define S_MAGIC_PROC 0x9FA0
+# define S_MAGIC_PSTOREFS 0x6165676C
+# define S_MAGIC_QNX4 0x002F
+# define S_MAGIC_QNX6 0x68191122
+# define S_MAGIC_RAMFS 0x858458F6
# define S_MAGIC_REISERFS 0x52654973
-# define S_MAGIC_CRAMFS 0x28cd3d45
# define S_MAGIC_ROMFS 0x7275
-# define S_MAGIC_RAMFS 0x858458f6
+# define S_MAGIC_RPC_PIPEFS 0x67596969
+# define S_MAGIC_SECURITYFS 0x73636673
+# define S_MAGIC_SELINUX 0xF97CFF8C
+# define S_MAGIC_SMACK 0x43415D53
+# define S_MAGIC_SMB 0x517B
+# define S_MAGIC_SNFS 0xBEEFDEAD
+# define S_MAGIC_SOCKFS 0x534F434B
# define S_MAGIC_SQUASHFS 0x73717368
# define S_MAGIC_SYSFS 0x62656572
+# define S_MAGIC_SYSV2 0x012FF7B6
+# define S_MAGIC_SYSV4 0x012FF7B5
+# define S_MAGIC_TMPFS 0x01021994
+# define S_MAGIC_TRACEFS 0x74726163
+# define S_MAGIC_UBIFS 0x24051905
+# define S_MAGIC_UDF 0x15013346
+# define S_MAGIC_UFS 0x00011954
+# define S_MAGIC_UFS_BYTESWAPPED 0x54190100
+# define S_MAGIC_USBDEVFS 0x9FA2
+# define S_MAGIC_V9FS 0x01021997
+# define S_MAGIC_VMHGFS 0xBACBACBC
+# define S_MAGIC_VXFS 0xA501FCF5
+# define S_MAGIC_VZFS 0x565A4653
+# define S_MAGIC_XENFS 0xABBA1974
+# define S_MAGIC_XENIX 0x012FF7B4
+# define S_MAGIC_XFS 0x58465342
+# define S_MAGIC_XIAFS 0x012FD16D
+# define S_MAGIC_ZFS 0x2FC12FC1
#elif defined __GNU__
# include <hurd/hurd_types.h>
#endif
diff --git a/src/getlimits.c b/src/getlimits.c
new file mode 100644
index 0000000..e49a658
--- /dev/null
+++ b/src/getlimits.c
@@ -0,0 +1,172 @@
+/* getlimits - print various platform dependent limits.
+ Copyright (C) 2008-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Written by Pádraig Brady */
+
+#include <config.h> /* sets _FILE_OFFSET_BITS=64 etc. */
+#include <stdio.h>
+#include <sys/types.h>
+#include <float.h>
+
+#include "ftoastr.h"
+#include "system.h"
+#include "long-options.h"
+
+#define PROGRAM_NAME "getlimits"
+
+#define AUTHORS proper_name_utf8 ("Padraig Brady", "P\303\241draig Brady")
+
+#ifndef TIME_T_MAX
+# define TIME_T_MAX TYPE_MAXIMUM (time_t)
+#endif
+
+#ifndef TIME_T_MIN
+# define TIME_T_MIN TYPE_MINIMUM (time_t)
+#endif
+
+#ifndef SSIZE_MIN
+# define SSIZE_MIN TYPE_MINIMUM (ssize_t)
+#endif
+
+#ifndef PID_T_MIN
+# define PID_T_MIN TYPE_MINIMUM (pid_t)
+#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. */
+#define SIZE_MIN 0
+#define UCHAR_MIN 0
+#define UINT_MIN 0
+#define ULONG_MIN 0
+#define UINTMAX_MIN 0
+#define UID_T_MIN 0
+#define GID_T_MIN 0
+
+void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ emit_try_help ();
+ else
+ {
+ printf (_("\
+Usage: %s\n\
+"), program_name);
+
+ fputs (_("\
+Output platform dependent limits in a format useful for shell scripts.\n\
+\n\
+"), stdout);
+ fputs (HELP_OPTION_DESCRIPTION, stdout);
+ fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ emit_ancillary_info (PROGRAM_NAME);
+ }
+ exit (status);
+}
+
+/* Add one to the absolute value of the number whose textual
+ representation is BUF + 1. Do this in-place, in the buffer.
+ Return a pointer to the result, which is normally BUF + 1, but is
+ BUF if the representation grew in size. */
+static char const *
+decimal_absval_add_one (char *buf)
+{
+ bool negative = (buf[1] == '-');
+ char *absnum = buf + 1 + negative;
+ char *p = absnum + strlen (absnum);
+ absnum[-1] = '0';
+ while (*--p == '9')
+ *p = '0';
+ ++*p;
+ char *result = MIN (absnum, p);
+ if (negative)
+ *--result = '-';
+ return result;
+}
+
+#define PRINT_FLOATTYPE(N, T, FTOASTR, BUFSIZE) \
+static void \
+N (T x) \
+{ \
+ char buf[BUFSIZE]; \
+ FTOASTR (buf, sizeof buf, FTOASTR_LEFT_JUSTIFY, 0, x); \
+ puts (buf); \
+}
+
+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)
+
+int
+main (int argc, char **argv)
+{
+ char limit[1 + MAX (INT_BUFSIZE_BOUND (intmax_t),
+ INT_BUFSIZE_BOUND (uintmax_t))];
+
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ initialize_exit_failure (EXIT_FAILURE);
+ atexit (close_stdout);
+
+ parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, VERSION,
+ usage, AUTHORS, (char const *) NULL);
+
+#define print_int(TYPE) \
+ sprintf (limit + 1, "%"PRIuMAX, (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, "%"PRIdMAX, (intmax_t) TYPE##_MIN); \
+ printf (#TYPE"_MIN=%s\n", limit + 1); \
+ printf (#TYPE"_UFLOW=%s\n", decimal_absval_add_one (limit)); \
+ }
+
+#define print_float(TYPE) \
+ printf (#TYPE"_MIN="); print_##TYPE (TYPE##_MIN); \
+ printf (#TYPE"_MAX="); print_##TYPE (TYPE##_MAX);
+
+ /* Variable sized ints */
+ print_int (CHAR);
+ print_int (SCHAR);
+ print_int (UCHAR);
+ print_int (SHRT);
+ print_int (INT);
+ print_int (UINT);
+ print_int (LONG);
+ print_int (ULONG);
+ print_int (SIZE);
+ print_int (SSIZE);
+ print_int (TIME_T);
+ print_int (UID_T);
+ print_int (GID_T);
+ print_int (PID_T);
+ print_int (OFF_T);
+ print_int (INTMAX);
+ print_int (UINTMAX);
+
+ /* Variable sized floats */
+ print_float (FLT);
+ print_float (DBL);
+ print_float (LDBL);
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/group-list.c b/src/group-list.c
new file mode 100644
index 0000000..8d9128e
--- /dev/null
+++ b/src/group-list.c
@@ -0,0 +1,123 @@
+/* group-list.c --Print a list of group IDs or names.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Written by Arnold Robbins.
+ Major rewrite by David MacKenzie, djm@gnu.ai.mit.edu.
+ Extracted from id.c by James Youngman. */
+
+#include <config.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "system.h"
+#include "error.h"
+#include "mgetgroups.h"
+#include "quote.h"
+#include "group-list.h"
+
+
+/* Print all of the distinct groups the user is in. */
+extern bool
+print_group_list (const char *username,
+ uid_t ruid, gid_t rgid, gid_t egid,
+ bool use_names, char delim)
+{
+ bool ok = true;
+ struct passwd *pwd = NULL;
+
+ if (username)
+ {
+ pwd = getpwuid (ruid);
+ if (pwd == NULL)
+ ok = false;
+ }
+
+ if (!print_group (rgid, use_names))
+ ok = false;
+
+ if (egid != rgid)
+ {
+ putchar (delim);
+ if (!print_group (egid, use_names))
+ ok = false;
+ }
+
+ {
+ gid_t *groups;
+ int i;
+
+ int n_groups = xgetgroups (username, (pwd ? pwd->pw_gid : egid), &groups);
+ if (n_groups < 0)
+ {
+ if (username)
+ {
+ error (0, errno, _("failed to get groups for user %s"),
+ quote (username));
+ }
+ else
+ {
+ error (0, errno, _("failed to get groups for the current process"));
+ }
+ return false;
+ }
+
+ for (i = 0; i < n_groups; i++)
+ if (groups[i] != rgid && groups[i] != egid)
+ {
+ putchar (delim);
+ if (!print_group (groups[i], use_names))
+ ok = false;
+ }
+ free (groups);
+ }
+ return ok;
+}
+
+/* Convert a gid_t to string. Do not use this function directly.
+ Instead, use it via the gidtostr macro.
+ Beware that it returns a pointer to static storage. */
+static char *
+gidtostr_ptr (gid_t const *gid)
+{
+ static char buf[INT_BUFSIZE_BOUND (uintmax_t)];
+ return umaxtostr (*gid, buf);
+}
+#define gidtostr(g) gidtostr_ptr (&(g))
+
+/* Print the name or value of group ID GID. */
+extern bool
+print_group (gid_t gid, bool use_name)
+{
+ struct group *grp = NULL;
+ bool ok = true;
+
+ if (use_name)
+ {
+ grp = getgrgid (gid);
+ if (grp == NULL)
+ {
+ error (0, 0, _("cannot find name for group ID %lu"),
+ (unsigned long int) gid);
+ ok = false;
+ }
+ }
+
+ char *s = grp ? grp->gr_name : gidtostr (gid);
+ fputs (s, stdout);
+ return ok;
+}
diff --git a/src/group-list.h b/src/group-list.h
new file mode 100644
index 0000000..0b95371
--- /dev/null
+++ b/src/group-list.h
@@ -0,0 +1,19 @@
+/* group-list.h -- prototypes shared by id and groups.
+
+ Copyright (C) 2008-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+bool print_group (gid_t, bool);
+bool print_group_list (const char *, uid_t, gid_t, gid_t, bool, char);
diff --git a/src/groups.c b/src/groups.c
new file mode 100644
index 0000000..c66d141
--- /dev/null
+++ b/src/groups.c
@@ -0,0 +1,142 @@
+/* groups -- print the groups a user is in
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Written by James Youngman based on id.c and groups.sh,
+ which were written by Arnold Robbins and David MacKenzie. */
+
+#include <config.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <getopt.h>
+
+#include "system.h"
+#include "error.h"
+#include "group-list.h"
+#include "quote.h"
+
+/* The official name of this program (e.g., no 'g' prefix). */
+#define PROGRAM_NAME "groups"
+
+#define AUTHORS \
+ proper_name ("David MacKenzie"), \
+ proper_name ("James Youngman")
+
+
+static struct option const longopts[] =
+{
+ {GETOPT_HELP_OPTION_DECL},
+ {GETOPT_VERSION_OPTION_DECL},
+ {NULL, 0, NULL, 0}
+};
+
+void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ emit_try_help ();
+ else
+ {
+ printf (_("Usage: %s [OPTION]... [USERNAME]...\n"), program_name);
+ fputs (_("\
+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);
+ emit_ancillary_info (PROGRAM_NAME);
+ }
+ exit (status);
+}
+
+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, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ 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, NULL)) != -1)
+ {
+ switch (optc)
+ {
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
+ }
+
+ if (optind == argc)
+ {
+ /* No arguments. Divulge the details of the current process. */
+ uid_t NO_UID = -1;
+ gid_t NO_GID = -1;
+
+ errno = 0;
+ ruid = getuid ();
+ if (ruid == NO_UID && errno)
+ error (EXIT_FAILURE, errno, _("cannot get real UID"));
+
+ errno = 0;
+ egid = getegid ();
+ if (egid == NO_GID && errno)
+ error (EXIT_FAILURE, errno, _("cannot get effective GID"));
+
+ errno = 0;
+ rgid = getgid ();
+ if (rgid == NO_GID && errno)
+ error (EXIT_FAILURE, errno, _("cannot get real GID"));
+
+ if (!print_group_list (NULL, ruid, rgid, egid, true, ' '))
+ ok = false;
+ putchar ('\n');
+ }
+ else
+ {
+ /* At least one argument. Divulge the details of the specified users. */
+ while (optind < argc)
+ {
+ struct passwd *pwd = getpwnam (argv[optind]);
+ if (pwd == NULL)
+ error (EXIT_FAILURE, 0, _("%s: no such user"),
+ quote (argv[optind]));
+ ruid = pwd->pw_uid;
+ rgid = egid = pwd->pw_gid;
+
+ printf ("%s : ", argv[optind]);
+ if (!print_group_list (argv[optind++], ruid, rgid, egid, true, ' '))
+ ok = false;
+ putchar ('\n');
+ }
+ }
+
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/groups.sh b/src/groups.sh
deleted file mode 100755
index dd32c63..0000000
--- a/src/groups.sh
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/bin/sh
-# groups -- print the groups a user is in
-# Copyright (C) 1991, 1997, 2000, 2002, 2004-2007 Free Software Foundation, Inc.
-
-# This 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 2, 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, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-# Written by David MacKenzie <djm@gnu.ai.mit.edu>.
-
-# Make sure we get GNU id, if possible; also allow
-# it to be somewhere else in PATH if not installed yet.
-PATH=@bindir@:$PATH
-
-usage="Usage: $0 [OPTION]... [USERNAME]...
-
- --help display this help and exit
- --version output version information and exit
-
-Same as id -Gn. If no USERNAME, use current process.
-
-Report bugs to <@PACKAGE_BUGREPORT@>."
-
-version='groups (@GNU_PACKAGE@) @VERSION@
-Copyright (C) @RELEASE_YEAR@ Free Software Foundation, Inc.
-This is free software. You may redistribute copies of it under the terms of
-the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.
-There is NO WARRANTY, to the extent permitted by law.
-
-Written by David MacKenzie.'
-
-
-for arg
-do
- case $arg in
- --help | --hel | --he | --h)
- exec echo "$usage" ;;
- --version | --versio | --versi | --vers | --ver | --ve | --v)
- exec echo "$version" ;;
- --)
- shift
- break ;;
- -*)
- echo "$0: invalid option: $arg" >&2
- exit 1 ;;
- esac
-done
-
-# With fewer than two arguments, simply exec "id".
-case $# in
- 0|1) exec id -Gn -- "$@" ;;
-esac
-
-# With more, we need a loop, and be sure to exit nonzero upon failure.
-status=0
-write_error=0
-
-for name
-do
- if groups=`id -Gn -- "$name"`; then
- echo "$name : $groups" || {
- status=$?
- if test $write_error = 0; then
- echo "$0: write error" >&2
- write_error=1
- fi
- }
- else
- status=$?
- fi
-done
-
-exit $status
diff --git a/src/head.c b/src/head.c
index 4038722..282c2ea 100644
--- a/src/head.c
+++ b/src/head.c
@@ -1,10 +1,10 @@
/* head -- output first part of file(s)
- Copyright (C) 89, 90, 91, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,12 +12,11 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Options: (see usage)
Reads from standard input if no files are given or when a filename of
- ``-'' is encountered.
+ ''-'' is encountered.
By default, filename headers are printed only if more than one file
is given.
By default, prints the first 10 lines (head -n 10).
@@ -33,17 +32,19 @@
#include "system.h"
#include "error.h"
-#include "full-write.h"
#include "full-read.h"
-#include "inttostr.h"
#include "quote.h"
#include "safe-read.h"
-#include "xstrtol.h"
+#include "stat-size.h"
+#include "xfreopen.h"
+#include "xdectoint.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "head"
-#define AUTHORS "David MacKenzie", "Jim Meyering"
+#define AUTHORS \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Jim Meyering")
/* Number of lines/chars/blocks to head. */
#define DEFAULT_NUMBER 10
@@ -57,15 +58,15 @@ static bool presume_input_pipe;
/* If true, print filename headers. */
static bool print_headers;
+/* Character to split lines by. */
+static char line_end;
+
/* When to print the filename banners. */
enum header_mode
{
multiple_files, always, never
};
-/* The name this program was run with. */
-char *program_name;
-
/* Have we ever read standard input? */
static bool have_read_stdin;
@@ -73,7 +74,6 @@ enum Copy_fd_status
{
COPY_FD_OK = 0,
COPY_FD_READ_ERROR,
- COPY_FD_WRITE_ERROR,
COPY_FD_UNEXPECTED_EOF
};
@@ -93,6 +93,7 @@ static struct option const long_options[] =
{"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},
{NULL, 0, NULL, 0}
@@ -102,42 +103,45 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
- program_name);
- fputs (_("\
-Print the first 10 lines of each FILE to standard output.\n\
+ program_name);
+ printf (_("\
+Print the first %d lines of each FILE to standard output.\n\
With more than one FILE, precede each with a header giving the file name.\n\
-With no FILE, or when FILE is -, read standard input.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
-"), stdout);
- fputs (_("\
- -c, --bytes=[-]N print the first N bytes of each file;\n\
- with the leading `-', print all but the last\n\
- N bytes of each file\n\
- -n, --lines=[-]N print the first N lines instead of the first 10;\n\
- with the leading `-', print all but the last\n\
- N lines of each file\n\
-"), stdout);
+"), DEFAULT_NUMBER);
+
+ 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\
+"), 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);
fputs (_("\
\n\
-N may have a multiplier suffix: b 512, k 1024, m 1024*1024.\n\
+NUM may have a multiplier suffix:\n\
+b 512, kB 1000, K 1024, MB 1000*1000, M 1024*1024,\n\
+GB 1000*1000*1000, G 1024*1024*1024, and so on for T, P, E, Z, Y.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -148,13 +152,10 @@ diagnose_copy_fd_failure (enum Copy_fd_status err, char const *filename)
switch (err)
{
case COPY_FD_READ_ERROR:
- error (0, errno, _("error reading %s"), quote (filename));
- break;
- case COPY_FD_WRITE_ERROR:
- error (0, errno, _("error writing %s"), quote (filename));
+ error (0, errno, _("error reading %s"), quoteaf (filename));
break;
case COPY_FD_UNEXPECTED_EOF:
- error (0, errno, _("%s: file has shrunk too much"), quote (filename));
+ error (0, errno, _("%s: file has shrunk too much"), quotef (filename));
break;
default:
abort ();
@@ -170,11 +171,25 @@ write_header (const char *filename)
first_file = false;
}
-/* Copy no more than N_BYTES from file descriptor SRC_FD to O_STREAM.
- Return an appropriate indication of success or failure. */
+/* Write N_BYTES from BUFFER to stdout.
+ Exit immediately on error with a single diagnostic. */
+
+static void
+xwrite_stdout (char const *buffer, size_t n_bytes)
+{
+ if (n_bytes > 0 && fwrite (buffer, 1, n_bytes, stdout) < n_bytes)
+ {
+ clearerr (stdout); /* To avoid redundant close_stdout diagnostic. */
+ error (EXIT_FAILURE, errno, _("error writing %s"),
+ quoteaf ("standard output"));
+ }
+}
+
+/* Copy no more than N_BYTES from file descriptor SRC_FD to stdout.
+ Return an appropriate indication of success or read failure. */
static enum Copy_fd_status
-copy_fd (int src_fd, FILE *o_stream, uintmax_t n_bytes)
+copy_fd (int src_fd, uintmax_t n_bytes)
{
char buf[BUFSIZ];
const size_t buf_size = sizeof (buf);
@@ -185,27 +200,55 @@ copy_fd (int src_fd, FILE *o_stream, uintmax_t n_bytes)
size_t n_to_read = MIN (buf_size, n_bytes);
size_t n_read = safe_read (src_fd, buf, n_to_read);
if (n_read == SAFE_READ_ERROR)
- return COPY_FD_READ_ERROR;
+ return COPY_FD_READ_ERROR;
n_bytes -= n_read;
if (n_read == 0 && n_bytes != 0)
- return COPY_FD_UNEXPECTED_EOF;
+ return COPY_FD_UNEXPECTED_EOF;
- if (fwrite (buf, 1, n_read, o_stream) < n_read)
- return COPY_FD_WRITE_ERROR;
+ xwrite_stdout (buf, n_read);
}
return COPY_FD_OK;
}
-/* Print all but the last N_ELIDE lines from the input available via
- the non-seekable file descriptor FD. Return true upon success.
+/* Call lseek (FD, OFFSET, WHENCE), where file descriptor FD
+ corresponds to the file FILENAME. WHENCE must be SEEK_SET or
+ SEEK_CUR. Return the resulting offset. Give a diagnostic and
+ return -1 if lseek fails. */
+
+static off_t
+elseek (int fd, off_t offset, int whence, char const *filename)
+{
+ off_t new_offset = lseek (fd, offset, whence);
+ char buf[INT_BUFSIZE_BOUND (offset)];
+
+ if (new_offset < 0)
+ error (0, errno,
+ _(whence == SEEK_SET
+ ? N_("%s: cannot seek to offset %s")
+ : N_("%s: cannot seek to relative offset %s")),
+ quotef (filename),
+ offtostr (offset, buf));
+
+ return new_offset;
+}
+
+/* For an input file with name FILENAME and descriptor FD,
+ output all but the last N_ELIDE_0 bytes.
+ If CURRENT_POS is nonnegative, assume that the input file is
+ positioned at CURRENT_POS and that it should be repositioned to
+ just before the elided bytes before returning.
+ Return true upon success.
Give a diagnostic and return false upon error. */
static bool
-elide_tail_bytes_pipe (const char *filename, int fd, uintmax_t n_elide_0)
+elide_tail_bytes_pipe (const char *filename, int fd, uintmax_t n_elide_0,
+ off_t current_pos)
{
size_t n_elide = n_elide_0;
+ uintmax_t desired_pos = current_pos;
+ bool ok = true;
#ifndef HEAD_TAIL_PIPE_READ_BUFSIZE
# define HEAD_TAIL_PIPE_READ_BUFSIZE BUFSIZ
@@ -226,9 +269,9 @@ elide_tail_bytes_pipe (const char *filename, int fd, uintmax_t n_elide_0)
if (SIZE_MAX < n_elide_0 + READ_BUFSIZE)
{
- char umax_buf[INT_BUFSIZE_BOUND (uintmax_t)];
+ char umax_buf[INT_BUFSIZE_BOUND (n_elide_0)];
error (EXIT_FAILURE, 0, _("%s: number of bytes is too large"),
- umaxtostr (n_elide_0, umax_buf));
+ umaxtostr (n_elide_0, umax_buf));
}
/* Two cases to consider...
@@ -244,7 +287,6 @@ elide_tail_bytes_pipe (const char *filename, int fd, uintmax_t n_elide_0)
if (n_elide <= HEAD_TAIL_PIPE_BYTECOUNT_THRESHOLD)
{
- bool ok = true;
bool first = true;
bool eof = false;
size_t n_to_read = READ_BUFSIZE + n_elide;
@@ -254,213 +296,205 @@ elide_tail_bytes_pipe (const char *filename, int fd, uintmax_t n_elide_0)
b[1] = b[0] + n_to_read;
for (i = false; ! eof ; i = !i)
- {
- size_t n_read = full_read (fd, b[i], n_to_read);
- size_t delta = 0;
- if (n_read < n_to_read)
- {
- if (errno != 0)
- {
- error (0, errno, _("error reading %s"), quote (filename));
- ok = false;
- break;
- }
-
- /* reached EOF */
- if (n_read <= n_elide)
- {
- if (first)
- {
- /* The input is no larger than the number of bytes
- to elide. So there's nothing to output, and
- we're done. */
- }
- else
- {
- delta = n_elide - n_read;
- }
- }
- eof = true;
- }
-
- /* Output any (but maybe just part of the) elided data from
- the previous round. */
- if ( ! first)
- {
- /* Don't bother checking for errors here.
- If there's a failure, the test of the following
- fwrite or in close_stdout will catch it. */
- fwrite (b[!i] + READ_BUFSIZE, 1, n_elide - delta, stdout);
- }
- first = false;
-
- if (n_elide < n_read
- && fwrite (b[i], 1, n_read - n_elide, stdout) < n_read - n_elide)
- {
- error (0, errno, _("write error"));
- ok = false;
- break;
- }
- }
+ {
+ size_t n_read = full_read (fd, b[i], n_to_read);
+ size_t delta = 0;
+ if (n_read < n_to_read)
+ {
+ if (errno != 0)
+ {
+ error (0, errno, _("error reading %s"), quoteaf (filename));
+ ok = false;
+ break;
+ }
+
+ /* reached EOF */
+ if (n_read <= n_elide)
+ {
+ if (first)
+ {
+ /* The input is no larger than the number of bytes
+ to elide. So there's nothing to output, and
+ we're done. */
+ }
+ else
+ {
+ delta = n_elide - n_read;
+ }
+ }
+ eof = true;
+ }
+
+ /* Output any (but maybe just part of the) elided data from
+ the previous round. */
+ if (! first)
+ {
+ desired_pos += n_elide - delta;
+ xwrite_stdout (b[!i] + READ_BUFSIZE, n_elide - delta);
+ }
+ first = false;
+
+ if (n_elide < n_read)
+ {
+ desired_pos += n_read - n_elide;
+ xwrite_stdout (b[i], n_read - n_elide);
+ }
+ }
free (b[0]);
- return ok;
}
else
{
/* Read blocks of size READ_BUFSIZE, until we've read at least n_elide
- bytes. Then, for each new buffer we read, also write an old one. */
+ bytes. Then, for each new buffer we read, also write an old one. */
- bool ok = true;
bool eof = false;
size_t n_read;
bool buffered_enough;
size_t i, i_next;
- char **b;
+ char **b = NULL;
/* Round n_elide up to a multiple of READ_BUFSIZE. */
size_t rem = READ_BUFSIZE - (n_elide % READ_BUFSIZE);
size_t n_elide_round = n_elide + rem;
size_t n_bufs = n_elide_round / READ_BUFSIZE + 1;
- b = xcalloc (n_bufs, sizeof *b);
+ size_t n_alloc = 0;
+ size_t n_array_alloc = 0;
buffered_enough = false;
for (i = 0, i_next = 1; !eof; i = i_next, i_next = (i_next + 1) % n_bufs)
- {
- if (b[i] == NULL)
- b[i] = xmalloc (READ_BUFSIZE);
- n_read = full_read (fd, b[i], READ_BUFSIZE);
- if (n_read < READ_BUFSIZE)
- {
- if (errno != 0)
- {
- error (0, errno, _("error reading %s"), quote (filename));
- ok = false;
- goto free_mem;
- }
- eof = true;
- }
-
- if (i + 1 == n_bufs)
- buffered_enough = true;
-
- if (buffered_enough)
- {
- if (fwrite (b[i_next], 1, n_read, stdout) < n_read)
- {
- error (0, errno, _("write error"));
- ok = false;
- goto free_mem;
- }
- }
- }
+ {
+ if (n_array_alloc == i)
+ {
+ /* reallocate between 16 and n_bufs entries. */
+ if (n_array_alloc == 0)
+ n_array_alloc = MIN (n_bufs, 16);
+ else if (n_array_alloc <= n_bufs / 2)
+ n_array_alloc *= 2;
+ else
+ n_array_alloc = n_bufs;
+ b = xnrealloc (b, n_array_alloc, sizeof *b);
+ }
+
+ if (! buffered_enough)
+ {
+ b[i] = xmalloc (READ_BUFSIZE);
+ n_alloc = i + 1;
+ }
+ n_read = full_read (fd, b[i], READ_BUFSIZE);
+ if (n_read < READ_BUFSIZE)
+ {
+ if (errno != 0)
+ {
+ error (0, errno, _("error reading %s"), quoteaf (filename));
+ ok = false;
+ goto free_mem;
+ }
+ eof = true;
+ }
+
+ if (i + 1 == n_bufs)
+ buffered_enough = true;
+
+ if (buffered_enough)
+ {
+ desired_pos += n_read;
+ xwrite_stdout (b[i_next], n_read);
+ }
+ }
/* Output any remainder: rem bytes from b[i] + n_read. */
if (rem)
- {
- if (buffered_enough)
- {
- size_t n_bytes_left_in_b_i = READ_BUFSIZE - n_read;
- if (rem < n_bytes_left_in_b_i)
- {
- fwrite (b[i] + n_read, 1, rem, stdout);
- }
- else
- {
- fwrite (b[i] + n_read, 1, n_bytes_left_in_b_i, stdout);
- fwrite (b[i_next], 1, rem - n_bytes_left_in_b_i, stdout);
- }
- }
- else if (i + 1 == n_bufs)
- {
- /* This happens when n_elide < file_size < n_elide_round.
-
- |READ_BUF.|
- | | rem |
- |---------!---------!---------!---------|
- |---- n_elide ---------|
- | | x |
- | |y |
- |---- file size -----------|
- | |n_read|
- |---- n_elide_round ----------|
- */
- size_t y = READ_BUFSIZE - rem;
- size_t x = n_read - y;
- fwrite (b[i_next], 1, x, stdout);
- }
- }
-
- free_mem:;
- for (i = 0; i < n_bufs; i++)
- free (b[i]);
+ {
+ if (buffered_enough)
+ {
+ size_t n_bytes_left_in_b_i = READ_BUFSIZE - n_read;
+ desired_pos += rem;
+ if (rem < n_bytes_left_in_b_i)
+ {
+ xwrite_stdout (b[i] + n_read, rem);
+ }
+ else
+ {
+ xwrite_stdout (b[i] + n_read, n_bytes_left_in_b_i);
+ xwrite_stdout (b[i_next], rem - n_bytes_left_in_b_i);
+ }
+ }
+ else if (i + 1 == n_bufs)
+ {
+ /* This happens when n_elide < file_size < n_elide_round.
+
+ |READ_BUF.|
+ | | rem |
+ |---------!---------!---------!---------|
+ |---- n_elide ---------|
+ | | x |
+ | |y |
+ |---- file size -----------|
+ | |n_read|
+ |---- n_elide_round ----------|
+ */
+ size_t y = READ_BUFSIZE - rem;
+ size_t x = n_read - y;
+ desired_pos += x;
+ xwrite_stdout (b[i_next], x);
+ }
+ }
+
+ free_mem:
+ for (i = 0; i < n_alloc; i++)
+ free (b[i]);
free (b);
-
- return ok;
}
+
+ if (0 <= current_pos && elseek (fd, desired_pos, SEEK_SET, filename) < 0)
+ ok = false;
+ return ok;
}
-/* Print all but the last N_ELIDE lines from the input available
- via file descriptor FD. Return true upon success.
+/* For the file FILENAME with descriptor FD, output all but the last N_ELIDE
+ bytes. If SIZE is nonnegative, this is a regular file positioned
+ at CURRENT_POS with SIZE bytes. Return true on success.
Give a diagnostic and return false upon error. */
/* NOTE: if the input file shrinks by more than N_ELIDE bytes between
the length determination and the actual reading, then head fails. */
static bool
-elide_tail_bytes_file (const char *filename, int fd, uintmax_t n_elide)
+elide_tail_bytes_file (const char *filename, int fd, uintmax_t n_elide,
+ struct stat const *st, off_t current_pos)
{
- struct stat stats;
-
- if (presume_input_pipe || fstat (fd, &stats) || ! S_ISREG (stats.st_mode))
- {
- return elide_tail_bytes_pipe (filename, fd, n_elide);
- }
+ off_t size = st->st_size;
+ if (presume_input_pipe || size <= ST_BLKSIZE (*st))
+ return elide_tail_bytes_pipe (filename, fd, n_elide, current_pos);
else
{
- off_t current_pos, end_pos;
- uintmax_t bytes_remaining;
- off_t diff;
- enum Copy_fd_status err;
-
- if ((current_pos = lseek (fd, (off_t) 0, SEEK_CUR)) == -1
- || (end_pos = lseek (fd, (off_t) 0, SEEK_END)) == -1)
- {
- error (0, errno, _("cannot lseek %s"), quote (filename));
- return false;
- }
-
/* Be careful here. The current position may actually be
- beyond the end of the file. */
- bytes_remaining = (diff = end_pos - current_pos) < 0 ? 0 : diff;
+ beyond the end of the file. */
+ off_t diff = size - current_pos;
+ off_t bytes_remaining = diff < 0 ? 0 : diff;
if (bytes_remaining <= n_elide)
- return true;
-
- /* Seek back to `current' position, then copy the required
- number of bytes from fd. */
- if (lseek (fd, (off_t) 0, current_pos) == -1)
- {
- error (0, errno, _("%s: cannot lseek back to original position"),
- quote (filename));
- return false;
- }
-
- err = copy_fd (fd, stdout, bytes_remaining - n_elide);
+ return true;
+
+ enum Copy_fd_status err = copy_fd (fd, bytes_remaining - n_elide);
if (err == COPY_FD_OK)
- return true;
+ return true;
diagnose_copy_fd_failure (err, filename);
return false;
}
}
-/* Print all but the last N_ELIDE lines from the input stream
- open for reading via file descriptor FD.
+/* For an input file with name FILENAME and descriptor FD,
+ output all but the last N_ELIDE_0 bytes.
+ If CURRENT_POS is nonnegative, the input file is positioned there
+ and should be repositioned to just before the elided bytes.
Buffer the specified number of lines as a linked list of LBUFFERs,
adding them as needed. Return true if successful. */
static bool
-elide_tail_lines_pipe (const char *filename, int fd, uintmax_t n_elide)
+elide_tail_lines_pipe (const char *filename, int fd, uintmax_t n_elide,
+ off_t current_pos)
{
struct linebuffer
{
@@ -469,6 +503,7 @@ elide_tail_lines_pipe (const char *filename, int fd, uintmax_t n_elide)
size_t nlines;
struct linebuffer *next;
};
+ uintmax_t desired_pos = current_pos;
typedef struct linebuffer LBUFFER;
LBUFFER *first, *last, *tmp;
size_t total_lines = 0; /* Total number of newlines in all buffers. */
@@ -487,64 +522,73 @@ elide_tail_lines_pipe (const char *filename, int fd, uintmax_t n_elide)
{
n_read = safe_read (fd, tmp->buffer, BUFSIZ);
if (n_read == 0 || n_read == SAFE_READ_ERROR)
- break;
+ break;
+
+ if (! n_elide)
+ {
+ desired_pos += n_read;
+ xwrite_stdout (tmp->buffer, n_read);
+ continue;
+ }
+
tmp->nbytes = n_read;
tmp->nlines = 0;
tmp->next = NULL;
/* Count the number of newlines just read. */
{
- char const *buffer_end = tmp->buffer + n_read;
- char const *p = tmp->buffer;
- while ((p = memchr (p, '\n', buffer_end - p)))
- {
- ++p;
- ++tmp->nlines;
- }
+ char const *buffer_end = tmp->buffer + n_read;
+ char const *p = tmp->buffer;
+ while ((p = memchr (p, line_end, buffer_end - p)))
+ {
+ ++p;
+ ++tmp->nlines;
+ }
}
total_lines += tmp->nlines;
/* If there is enough room in the last buffer read, just append the new
- one to it. This is because when reading from a pipe, `n_read' can
+ one to it. This is because when reading from a pipe, 'n_read' can
often be very small. */
if (tmp->nbytes + last->nbytes < BUFSIZ)
- {
- memcpy (&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
- last->nbytes += tmp->nbytes;
- last->nlines += tmp->nlines;
- }
+ {
+ memcpy (&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
+ last->nbytes += tmp->nbytes;
+ last->nlines += tmp->nlines;
+ }
else
- {
- /* If there's not enough room, link the new buffer onto the end of
- the list, then either free up the oldest buffer for the next
- read if that would leave enough lines, or else malloc a new one.
- Some compaction mechanism is possible but probably not
- worthwhile. */
- last = last->next = tmp;
- if (n_elide < total_lines - first->nlines)
- {
- fwrite (first->buffer, 1, first->nbytes, stdout);
- tmp = first;
- total_lines -= first->nlines;
- first = first->next;
- }
- else
- tmp = xmalloc (sizeof (LBUFFER));
- }
+ {
+ /* If there's not enough room, link the new buffer onto the end of
+ the list, then either free up the oldest buffer for the next
+ read if that would leave enough lines, or else malloc a new one.
+ Some compaction mechanism is possible but probably not
+ worthwhile. */
+ last = last->next = tmp;
+ if (n_elide < total_lines - first->nlines)
+ {
+ desired_pos += first->nbytes;
+ xwrite_stdout (first->buffer, first->nbytes);
+ tmp = first;
+ total_lines -= first->nlines;
+ first = first->next;
+ }
+ else
+ tmp = xmalloc (sizeof (LBUFFER));
+ }
}
free (tmp);
if (n_read == SAFE_READ_ERROR)
{
- error (0, errno, _("error reading %s"), quote (filename));
+ error (0, errno, _("error reading %s"), quoteaf (filename));
ok = false;
goto free_lbuffers;
}
/* If we read any bytes at all, count the incomplete line
on files that don't end with a newline. */
- if (last->nbytes && last->buffer[last->nbytes - 1] != '\n')
+ if (last->nbytes && last->buffer[last->nbytes - 1] != line_end)
{
++last->nlines;
++total_lines;
@@ -552,23 +596,25 @@ elide_tail_lines_pipe (const char *filename, int fd, uintmax_t n_elide)
for (tmp = first; n_elide < total_lines - tmp->nlines; tmp = tmp->next)
{
- fwrite (tmp->buffer, 1, tmp->nbytes, stdout);
+ desired_pos += tmp->nbytes;
+ xwrite_stdout (tmp->buffer, tmp->nbytes);
total_lines -= tmp->nlines;
}
- /* Print the first `total_lines - n_elide' lines of tmp->buffer. */
+ /* Print the first 'total_lines - n_elide' lines of tmp->buffer. */
if (n_elide < total_lines)
{
size_t n = total_lines - n_elide;
char const *buffer_end = tmp->buffer + tmp->nbytes;
char const *p = tmp->buffer;
- while (n && (p = memchr (p, '\n', buffer_end - p)))
- {
- ++p;
- ++tmp->nlines;
- --n;
- }
- fwrite (tmp->buffer, 1, p - tmp->buffer, stdout);
+ while (n && (p = memchr (p, line_end, buffer_end - p)))
+ {
+ ++p;
+ ++tmp->nlines;
+ --n;
+ }
+ desired_pos += p - tmp->buffer;
+ xwrite_stdout (tmp->buffer, p - tmp->buffer);
}
free_lbuffers:
@@ -578,14 +624,17 @@ free_lbuffers:
free (first);
first = tmp;
}
+
+ if (0 <= current_pos && elseek (fd, desired_pos, SEEK_SET, filename) < 0)
+ ok = false;
return ok;
}
/* Output all but the last N_LINES lines of the input stream defined by
- FD, START_POS, and END_POS.
+ FD, START_POS, and SIZE.
START_POS is the starting position of the read pointer for the file
associated with FD (may be nonzero).
- END_POS is the file offset of EOF (one larger than offset of last byte).
+ SIZE is the file size in bytes.
Return true upon success.
Give a diagnostic and return false upon error.
@@ -594,37 +643,35 @@ free_lbuffers:
in a less efficient implementation or a messy interface. */
static bool
elide_tail_lines_seekable (const char *pretty_filename, int fd,
- uintmax_t n_lines,
- off_t start_pos, off_t end_pos)
+ uintmax_t n_lines,
+ off_t start_pos, off_t size)
{
char buffer[BUFSIZ];
size_t bytes_read;
- off_t pos = end_pos;
+ off_t pos = size;
- /* Set `bytes_read' to the size of the last, probably partial, buffer;
- 0 < `bytes_read' <= `BUFSIZ'. */
+ /* Set 'bytes_read' to the size of the last, probably partial, buffer;
+ 0 < 'bytes_read' <= 'BUFSIZ'. */
bytes_read = (pos - start_pos) % BUFSIZ;
if (bytes_read == 0)
bytes_read = BUFSIZ;
- /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
+ /* Make 'pos' a multiple of 'BUFSIZ' (0 if the file is short), so that all
reads will be on block boundaries, which might increase efficiency. */
pos -= bytes_read;
- if (lseek (fd, pos, SEEK_SET) < 0)
- {
- char offset_buf[INT_BUFSIZE_BOUND (off_t)];
- error (0, errno, _("%s: cannot seek to offset %s"),
- pretty_filename, offtostr (pos, offset_buf));
- return false;
- }
+ if (elseek (fd, pos, SEEK_SET, pretty_filename) < 0)
+ return false;
bytes_read = safe_read (fd, buffer, bytes_read);
if (bytes_read == SAFE_READ_ERROR)
{
- error (0, errno, _("error reading %s"), quote (pretty_filename));
+ error (0, errno, _("error reading %s"), quoteaf (pretty_filename));
return false;
}
+ /* n_lines == 0 case needs special treatment. */
+ const bool all_lines = !n_lines;
+
/* Count the incomplete line on files that don't end with a newline. */
- if (bytes_read && buffer[bytes_read - 1] != '\n')
+ if (n_lines && bytes_read && buffer[bytes_read - 1] != line_end)
--n_lines;
while (1)
@@ -633,106 +680,92 @@ elide_tail_lines_seekable (const char *pretty_filename, int fd,
size_t n = bytes_read;
while (n)
- {
- char const *nl;
- nl = memrchr (buffer, '\n', n);
- if (nl == NULL)
- break;
- n = nl - buffer;
- if (n_lines-- == 0)
- {
- /* Found it. */
- /* If necessary, restore the file pointer and copy
- input to output up to position, POS. */
- if (start_pos < pos)
- {
- enum Copy_fd_status err;
- if (lseek (fd, start_pos, SEEK_SET) < 0)
- {
- /* Failed to reposition file pointer. */
- error (0, errno,
- "%s: unable to restore file pointer to initial offset",
- quote (pretty_filename));
- return false;
- }
-
- err = copy_fd (fd, stdout, pos - start_pos);
- if (err != COPY_FD_OK)
- {
- diagnose_copy_fd_failure (err, pretty_filename);
- return false;
- }
- }
-
- /* Output the initial portion of the buffer
- in which we found the desired newline byte.
- Don't bother testing for failure for such a small amount.
- Any failure will be detected upon close. */
- fwrite (buffer, 1, n + 1, stdout);
- return true;
- }
- }
+ {
+ if (all_lines)
+ n -= 1;
+ else
+ {
+ char const *nl;
+ nl = memrchr (buffer, line_end, n);
+ if (nl == NULL)
+ break;
+ n = nl - buffer;
+ }
+ if (n_lines-- == 0)
+ {
+ /* Found it. */
+ /* If necessary, restore the file pointer and copy
+ input to output up to position, POS. */
+ if (start_pos < pos)
+ {
+ enum Copy_fd_status err;
+ if (elseek (fd, start_pos, SEEK_SET, pretty_filename) < 0)
+ return false;
+
+ err = copy_fd (fd, pos - start_pos);
+ if (err != COPY_FD_OK)
+ {
+ diagnose_copy_fd_failure (err, pretty_filename);
+ return false;
+ }
+ }
+
+ /* Output the initial portion of the buffer
+ in which we found the desired newline byte. */
+ xwrite_stdout (buffer, n + 1);
+
+ /* Set file pointer to the byte after what we've output. */
+ return 0 <= elseek (fd, pos + n + 1, SEEK_SET, pretty_filename);
+ }
+ }
/* Not enough newlines in that bufferfull. */
if (pos == start_pos)
- {
- /* Not enough lines in the file. */
- return true;
- }
+ {
+ /* Not enough lines in the file. */
+ return true;
+ }
pos -= BUFSIZ;
- if (lseek (fd, pos, SEEK_SET) < 0)
- {
- char offset_buf[INT_BUFSIZE_BOUND (off_t)];
- error (0, errno, _("%s: cannot seek to offset %s"),
- pretty_filename, offtostr (pos, offset_buf));
- return false;
- }
+ if (elseek (fd, pos, SEEK_SET, pretty_filename) < 0)
+ return false;
bytes_read = safe_read (fd, buffer, BUFSIZ);
if (bytes_read == SAFE_READ_ERROR)
- {
- error (0, errno, _("error reading %s"), quote (pretty_filename));
- return false;
- }
+ {
+ error (0, errno, _("error reading %s"), quoteaf (pretty_filename));
+ return false;
+ }
/* FIXME: is this dead code?
- Consider the test, pos == start_pos, above. */
+ Consider the test, pos == start_pos, above. */
if (bytes_read == 0)
- return true;
+ return true;
}
}
-/* Print all but the last N_ELIDE lines from the input available
- via file descriptor FD. Return true upon success.
+/* For the file FILENAME with descriptor FD, output all but the last N_ELIDE
+ lines. If SIZE is nonnegative, this is a regular file positioned
+ at START_POS with SIZE bytes. Return true on success.
Give a diagnostic and return nonzero upon error. */
static bool
-elide_tail_lines_file (const char *filename, int fd, uintmax_t n_elide)
+elide_tail_lines_file (const char *filename, int fd, uintmax_t n_elide,
+ struct stat const *st, off_t current_pos)
{
- if (!presume_input_pipe)
+ off_t size = st->st_size;
+ if (presume_input_pipe || size <= ST_BLKSIZE (*st))
+ return elide_tail_lines_pipe (filename, fd, n_elide, current_pos);
+ else
{
/* Find the offset, OFF, of the Nth newline from the end,
- but not counting the last byte of the file.
- If found, write from current position to OFF, inclusive.
- Otherwise, just return true. */
-
- off_t start_pos = lseek (fd, (off_t) 0, SEEK_CUR);
- off_t end_pos = lseek (fd, (off_t) 0, SEEK_END);
- if (0 <= start_pos && start_pos < end_pos)
- {
- /* If the file is empty, we're done. */
- if (end_pos == 0)
- return true;
-
- return elide_tail_lines_seekable (filename, fd, n_elide,
- start_pos, end_pos);
- }
-
- /* lseek failed or the end offset precedes start.
- Fall through. */
- }
+ but not counting the last byte of the file.
+ If found, write from current position to OFF, inclusive.
+ Otherwise, just return true. */
- return elide_tail_lines_pipe (filename, fd, n_elide);
+ return (size <= current_pos
+ || elide_tail_lines_seekable (filename, fd, n_elide,
+ current_pos, size));
+ }
}
static bool
@@ -745,17 +778,16 @@ head_bytes (const char *filename, int fd, uintmax_t bytes_to_write)
{
size_t bytes_read;
if (bytes_to_write < bytes_to_read)
- bytes_to_read = bytes_to_write;
+ bytes_to_read = bytes_to_write;
bytes_read = safe_read (fd, buffer, bytes_to_read);
if (bytes_read == SAFE_READ_ERROR)
- {
- error (0, errno, _("error reading %s"), quote (filename));
- return false;
- }
+ {
+ error (0, errno, _("error reading %s"), quoteaf (filename));
+ return false;
+ }
if (bytes_read == 0)
- break;
- if (fwrite (buffer, 1, bytes_read, stdout) < bytes_read)
- error (EXIT_FAILURE, errno, _("write error"));
+ break;
+ xwrite_stdout (buffer, bytes_read);
bytes_to_write -= bytes_read;
}
return true;
@@ -772,31 +804,28 @@ head_lines (const char *filename, int fd, uintmax_t lines_to_write)
size_t bytes_to_write = 0;
if (bytes_read == SAFE_READ_ERROR)
- {
- error (0, errno, _("error reading %s"), quote (filename));
- return false;
- }
+ {
+ error (0, errno, _("error reading %s"), quoteaf (filename));
+ return false;
+ }
if (bytes_read == 0)
- break;
+ break;
while (bytes_to_write < bytes_read)
- if (buffer[bytes_to_write++] == '\n' && --lines_to_write == 0)
- {
- off_t n_bytes_past_EOL = bytes_read - bytes_to_write;
- /* If we have read more data than that on the specified number
- of lines, try to seek back to the position we would have
- gotten to had we been reading one byte at a time. */
- if (lseek (fd, -n_bytes_past_EOL, SEEK_CUR) < 0)
- {
- int e = errno;
- struct stat st;
- if (fstat (fd, &st) != 0 || S_ISREG (st.st_mode))
- error (0, e, _("cannot reposition file pointer for %s"),
- quote (filename));
- }
- break;
- }
- if (fwrite (buffer, 1, bytes_to_write, stdout) < bytes_to_write)
- error (EXIT_FAILURE, errno, _("write error"));
+ if (buffer[bytes_to_write++] == line_end && --lines_to_write == 0)
+ {
+ off_t n_bytes_past_EOL = bytes_read - bytes_to_write;
+ /* If we have read more data than that on the specified number
+ of lines, try to seek back to the position we would have
+ gotten to had we been reading one byte at a time. */
+ if (lseek (fd, -n_bytes_past_EOL, SEEK_CUR) < 0)
+ {
+ struct stat st;
+ if (fstat (fd, &st) != 0 || S_ISREG (st.st_mode))
+ elseek (fd, -n_bytes_past_EOL, SEEK_CUR, filename);
+ }
+ break;
+ }
+ xwrite_stdout (buffer, bytes_to_write);
}
return true;
}
@@ -810,14 +839,24 @@ head (const char *filename, int fd, uintmax_t n_units, bool count_lines,
if (elide_from_end)
{
+ off_t current_pos = -1;
+ struct stat st;
+ if (fstat (fd, &st) != 0)
+ {
+ error (0, errno, _("cannot fstat %s"),
+ quoteaf (filename));
+ return false;
+ }
+ if (! presume_input_pipe && usable_st_size (&st))
+ {
+ current_pos = elseek (fd, 0, SEEK_CUR, filename);
+ if (current_pos < 0)
+ return false;
+ }
if (count_lines)
- {
- return elide_tail_lines_file (filename, fd, n_units);
- }
+ return elide_tail_lines_file (filename, fd, n_units, &st, current_pos);
else
- {
- return elide_tail_bytes_file (filename, fd, n_units);
- }
+ return elide_tail_bytes_file (filename, fd, n_units, &st, current_pos);
}
if (count_lines)
return head_lines (filename, fd, n_units);
@@ -827,7 +866,7 @@ head (const char *filename, int fd, uintmax_t n_units, bool count_lines,
static bool
head_file (const char *filename, uintmax_t n_units, bool count_lines,
- bool elide_from_end)
+ bool elide_from_end)
{
int fd;
bool ok;
@@ -839,29 +878,29 @@ head_file (const char *filename, uintmax_t n_units, bool count_lines,
fd = STDIN_FILENO;
filename = _("standard input");
if (O_BINARY && ! isatty (STDIN_FILENO))
- freopen (NULL, "rb", stdin);
+ xfreopen (NULL, "rb", stdin);
}
else
{
fd = open (filename, O_RDONLY | O_BINARY);
if (fd < 0)
- {
- error (0, errno, _("cannot open %s for reading"), quote (filename));
- return false;
- }
+ {
+ error (0, errno, _("cannot open %s for reading"), quoteaf (filename));
+ return false;
+ }
}
ok = head (filename, fd, n_units, count_lines, elide_from_end);
if (!is_stdin && close (fd) != 0)
{
- error (0, errno, _("closing %s"), quote (filename));
+ error (0, errno, _("failed to close %s"), quoteaf (filename));
return false;
}
return ok;
}
-/* Convert a string of decimal digits, N_STRING, with a single, optional suffix
- character (b, k, or m) to an integral value. Upon successful conversion,
+/* Convert a string of decimal digits, N_STRING, with an optional suffix
+ to an integral value. Upon successful conversion,
return that value. If it cannot be converted, give a diagnostic and exit.
COUNT_LINES indicates whether N_STRING is a number of bytes or a number
of lines. It is used solely to give a more specific diagnostic. */
@@ -869,27 +908,9 @@ head_file (const char *filename, uintmax_t n_units, bool count_lines,
static uintmax_t
string_to_integer (bool count_lines, const char *n_string)
{
- strtol_error s_err;
- uintmax_t n;
-
- s_err = xstrtoumax (n_string, NULL, 10, &n, "bkm");
-
- if (s_err == LONGINT_OVERFLOW)
- {
- error (EXIT_FAILURE, 0,
- _("%s: %s is so large that it is not representable"), n_string,
- count_lines ? _("number of lines") : _("number of bytes"));
- }
-
- if (s_err != LONGINT_OK)
- {
- error (EXIT_FAILURE, 0, "%s: %s", n_string,
- (count_lines
- ? _("invalid number of lines")
- : _("invalid number of bytes")));
- }
-
- return n;
+ return xdectoumax (n_string, 0, UINTMAX_MAX, "bkKmMGTPEZY0",
+ count_lines ? _("invalid number of lines")
+ : _("invalid number of bytes"), 0);
}
int
@@ -917,7 +938,7 @@ main (int argc, char **argv)
char const *const *file_list;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -928,6 +949,8 @@ main (int argc, char **argv)
print_headers = false;
+ line_end = '\n';
+
if (1 < argc && argv[1][0] == '-' && ISDIGIT (argv[1][1]))
{
char *a = argv[1];
@@ -936,7 +959,7 @@ main (int argc, char **argv)
char multiplier_char = 0;
/* Old option syntax; a dash, one or more digits, and one or
- more option letters. Move past the number. */
+ more option letters. Move past the number. */
do ++a;
while (ISDIGIT (*a));
@@ -945,44 +968,48 @@ main (int argc, char **argv)
/* Parse any appended option letters. */
for (; *a; a++)
- {
- switch (*a)
- {
- case 'c':
- count_lines = false;
- multiplier_char = 0;
- break;
-
- case 'b':
- case 'k':
- case 'm':
- count_lines = false;
- multiplier_char = *a;
- break;
-
- case 'l':
- count_lines = true;
- break;
-
- case 'q':
- header_mode = never;
- break;
-
- case 'v':
- header_mode = always;
- break;
-
- default:
- error (0, 0, _("invalid trailing option -- %c"), *a);
- usage (EXIT_FAILURE);
- }
- }
+ {
+ switch (*a)
+ {
+ case 'c':
+ count_lines = false;
+ multiplier_char = 0;
+ break;
+
+ case 'b':
+ case 'k':
+ case 'm':
+ count_lines = false;
+ multiplier_char = *a;
+ break;
+
+ case 'l':
+ count_lines = true;
+ break;
+
+ case 'q':
+ header_mode = never;
+ break;
+
+ case 'v':
+ header_mode = always;
+ break;
+
+ case 'z':
+ line_end = '\0';
+ break;
+
+ default:
+ error (0, 0, _("invalid trailing option -- %c"), *a);
+ usage (EXIT_FAILURE);
+ }
+ }
/* Append the multiplier character (if any) onto the end of
- the digit string. Then add NUL byte if necessary. */
+ the digit string. Then add NUL byte if necessary. */
*end_n_string = multiplier_char;
if (multiplier_char)
- *(++end_n_string) = 0;
+ *(++end_n_string) = 0;
n_units = string_to_integer (count_lines, n_string);
@@ -992,48 +1019,52 @@ main (int argc, char **argv)
argc--;
}
- while ((c = getopt_long (argc, argv, "c:n:qv0123456789", long_options, NULL))
- != -1)
+ while ((c = getopt_long (argc, argv, "c:n:qvz0123456789", long_options, NULL))
+ != -1)
{
switch (c)
- {
- case PRESUME_INPUT_PIPE_OPTION:
- presume_input_pipe = true;
- break;
-
- case 'c':
- count_lines = false;
- elide_from_end = (*optarg == '-');
- if (elide_from_end)
- ++optarg;
- n_units = string_to_integer (count_lines, optarg);
- break;
-
- case 'n':
- count_lines = true;
- elide_from_end = (*optarg == '-');
- if (elide_from_end)
- ++optarg;
- n_units = string_to_integer (count_lines, optarg);
- break;
-
- case 'q':
- header_mode = never;
- break;
-
- case 'v':
- header_mode = always;
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- if (ISDIGIT (c))
- error (0, 0, _("invalid trailing option -- %c"), c);
- usage (EXIT_FAILURE);
- }
+ {
+ case PRESUME_INPUT_PIPE_OPTION:
+ presume_input_pipe = true;
+ break;
+
+ case 'c':
+ count_lines = false;
+ elide_from_end = (*optarg == '-');
+ if (elide_from_end)
+ ++optarg;
+ n_units = string_to_integer (count_lines, optarg);
+ break;
+
+ case 'n':
+ count_lines = true;
+ elide_from_end = (*optarg == '-');
+ if (elide_from_end)
+ ++optarg;
+ n_units = string_to_integer (count_lines, optarg);
+ break;
+
+ case 'q':
+ header_mode = never;
+ break;
+
+ case 'v':
+ header_mode = always;
+ break;
+
+ case 'z':
+ line_end = '\0';
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ if (ISDIGIT (c))
+ error (0, 0, _("invalid trailing option -- %c"), c);
+ usage (EXIT_FAILURE);
+ }
}
if (header_mode == always
@@ -1042,17 +1073,17 @@ main (int argc, char **argv)
if ( ! count_lines && elide_from_end && OFF_T_MAX < n_units)
{
- char umax_buf[INT_BUFSIZE_BOUND (uintmax_t)];
- error (EXIT_FAILURE, 0, _("%s: number of bytes is too large"),
- umaxtostr (n_units, umax_buf));
+ char umax_buf[INT_BUFSIZE_BOUND (n_units)];
+ error (EXIT_FAILURE, EOVERFLOW, "%s: %s", _("invalid number of bytes"),
+ quote (umaxtostr (n_units, umax_buf)));
}
file_list = (optind < argc
- ? (char const *const *) &argv[optind]
- : default_file_list);
+ ? (char const *const *) &argv[optind]
+ : default_file_list);
if (O_BINARY && ! isatty (STDOUT_FILENO))
- freopen (NULL, "wb", stdout);
+ xfreopen (NULL, "wb", stdout);
for (i = 0; file_list[i]; ++i)
ok &= head_file (file_list[i], n_units, count_lines, elide_from_end);
@@ -1060,5 +1091,5 @@ main (int argc, char **argv)
if (have_read_stdin && close (STDIN_FILENO) < 0)
error (EXIT_FAILURE, errno, "-");
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/hostid.c b/src/hostid.c
index 090b8a0..67e3e33 100644
--- a/src/hostid.c
+++ b/src/hostid.c
@@ -1,12 +1,11 @@
/* print the hexadecimal identifier for the current host
- Copyright (C) 1997, 1999, 2000, 2001, 2002, 2003, 2004 Free
- Software Foundation, Inc.
+ Copyright (C) 1997-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -14,8 +13,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Jim Meyering. */
@@ -29,32 +27,26 @@
#include "error.h"
#include "quote.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "hostid"
-#define AUTHORS "Jim Meyering"
-
-/* The name this program was run with, for error messages. */
-char *program_name;
+#define AUTHORS proper_name ("Jim Meyering")
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
-Usage: %s\n\
- or: %s OPTION\n\
+Usage: %s [OPTION]\n\
Print the numeric identifier (in hexadecimal) for the current host.\n\
\n\
-"),
- program_name, program_name);
+"), program_name);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -65,15 +57,15 @@ main (int argc, char **argv)
unsigned int id;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
+ parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, Version,
+ usage, AUTHORS, (char const *) NULL);
if (getopt_long (argc, argv, "", NULL, NULL) != -1)
usage (EXIT_FAILURE);
@@ -92,5 +84,5 @@ main (int argc, char **argv)
printf ("%08x\n", id);
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/hostname.c b/src/hostname.c
index f6748a6..11f3032 100644
--- a/src/hostname.c
+++ b/src/hostname.c
@@ -1,10 +1,10 @@
/* hostname - set or print the name of current host system
- Copyright (C) 1994-1997, 1999-2005 Free Software Foundation, Inc.
+ Copyright (C) 1994-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Jim Meyering. */
@@ -28,20 +27,16 @@
#include "quote.h"
#include "xgethostname.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "hostname"
-#define AUTHORS "Jim Meyering"
-
-#if HAVE_SETHOSTNAME && !defined sethostname
-int sethostname ();
-#endif
+#define AUTHORS proper_name ("Jim Meyering")
#if !defined HAVE_SETHOSTNAME && defined HAVE_SYSINFO && \
defined HAVE_SYS_SYSTEMINFO_H
# include <sys/systeminfo.h>
-int
+static int
sethostname (char *name, size_t namelen)
{
/* Using sysinfo() is the SVR4 mechanism to set a hostname. */
@@ -51,15 +46,11 @@ sethostname (char *name, size_t namelen)
# define HAVE_SETHOSTNAME 1 /* Now we have it... */
#endif
-/* The name this program was run with. */
-char *program_name;
-
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
@@ -71,7 +62,7 @@ Print or set the hostname of the current system.\n\
program_name, program_name);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -82,15 +73,15 @@ main (int argc, char **argv)
char *hostname;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
+ parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, Version,
+ usage, AUTHORS, (char const *) NULL);
if (getopt_long (argc, argv, "", NULL, NULL) != -1)
usage (EXIT_FAILURE);
@@ -100,10 +91,11 @@ main (int argc, char **argv)
/* Set hostname to operand. */
char const *name = argv[optind];
if (sethostname (name, strlen (name)) != 0)
- error (EXIT_FAILURE, errno, _("cannot set name to %s"), quote (name));
+ error (EXIT_FAILURE, errno, _("cannot set name to %s"),
+ quote (name));
#else
error (EXIT_FAILURE, 0,
- _("cannot set hostname; this system lacks the functionality"));
+ _("cannot set hostname; this system lacks the functionality"));
#endif
}
@@ -111,7 +103,7 @@ main (int argc, char **argv)
{
hostname = xgethostname ();
if (hostname == NULL)
- error (EXIT_FAILURE, errno, _("cannot determine hostname"));
+ error (EXIT_FAILURE, errno, _("cannot determine hostname"));
printf ("%s\n", hostname);
}
@@ -121,5 +113,5 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/id.c b/src/id.c
index 7abb3e5..38844af 100644
--- a/src/id.c
+++ b/src/id.c
@@ -1,10 +1,10 @@
/* id -- print real and effective UIDs and GIDs
- Copyright (C) 1989-2005 Free Software Foundation, Inc.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,39 +12,40 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Arnold Robbins.
Major rewrite by David MacKenzie, djm@gnu.ai.mit.edu. */
#include <config.h>
#include <stdio.h>
-#include <getopt.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <getopt.h>
+#include <selinux/selinux.h>
#include "system.h"
#include "error.h"
+#include "mgetgroups.h"
#include "quote.h"
+#include "group-list.h"
+#include "smack.h"
+#include "userspec.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "id"
-#define AUTHORS "Arnold Robbins", "David MacKenzie"
+#define AUTHORS \
+ proper_name ("Arnold Robbins"), \
+ proper_name ("David MacKenzie")
-int getugroups ();
+/* If nonzero, output only the SELinux context. */
+static bool just_context = 0;
static void print_user (uid_t uid);
-static void print_group (gid_t gid);
-static void print_group_list (const char *username);
static void print_full_info (const char *username);
-/* The name this program was run with. */
-char *program_name;
-
/* If true, output user/group name instead of ID number. -n */
static bool use_name = false;
@@ -55,13 +56,19 @@ static gid_t rgid, egid;
/* True unless errors have been encountered. */
static bool ok = true;
+/* 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 = NULL;
+
static struct option const longopts[] =
{
+ {"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},
{NULL, 0, NULL, 0}
@@ -71,20 +78,25 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
- printf (_("Usage: %s [OPTION]... [USERNAME]\n"), program_name);
+ printf (_("Usage: %s [OPTION]... [USER]\n"), program_name);
fputs (_("\
-Print information for USERNAME, or the current user.\n\
-\n\
- -a ignore, for compatibility with other versions\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 -ugG\n\
- -r, --real print the real ID instead of the effective ID, with -ugG\n\
- -u, --user print only the effective user ID\n\
+Print user and group information for the specified USER,\n\
+or (when USER omitted) for the current user.\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 -ugG\n\
+ -r, --real print the real ID instead of the effective ID, with -ugG\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);
@@ -92,7 +104,7 @@ Print information for USERNAME, or the current user.\n\
\n\
Without any OPTION, print some useful set of identified information.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -101,6 +113,10 @@ int
main (int argc, char **argv)
{
int optc;
+ int selinux_enabled = (is_selinux_enabled () > 0);
+ bool smack_enabled = is_smack_enabled ();
+ bool opt_zero = false;
+ char *pw_name = NULL;
/* If true, output the list of all group IDs. -G */
bool just_group_list = false;
@@ -112,216 +128,237 @@ main (int argc, char **argv)
bool just_user = false;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "agnruG", longopts, NULL)) != -1)
+ while ((optc = getopt_long (argc, argv, "agnruzGZ", longopts, NULL)) != -1)
{
switch (optc)
- {
- case 'a':
- /* Ignore -a, for compatibility with SVR4. */
- break;
- case 'g':
- just_group = true;
- break;
- case 'n':
- use_name = true;
- break;
- case 'r':
- use_real = true;
- break;
- case 'u':
- just_user = true;
- break;
- case 'G':
- just_group_list = true;
- break;
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'a':
+ /* Ignore -a, for compatibility with SVR4. */
+ break;
+
+ case 'Z':
+ /* politely decline if we're not on a SELinux/SMACK-enabled kernel. */
+#ifdef HAVE_SMACK
+ if (!selinux_enabled && !smack_enabled)
+ error (EXIT_FAILURE, 0,
+ _("--context (-Z) works only on "
+ "an SELinux/SMACK-enabled kernel"));
+#else
+ if (!selinux_enabled)
+ error (EXIT_FAILURE, 0,
+ _("--context (-Z) works only on an SELinux-enabled kernel"));
+#endif
+ just_context = true;
+ break;
+
+ case 'g':
+ just_group = true;
+ break;
+ case 'n':
+ use_name = true;
+ break;
+ case 'r':
+ use_real = true;
+ break;
+ case 'u':
+ just_user = true;
+ break;
+ case 'z':
+ opt_zero = true;
+ break;
+ case 'G':
+ just_group_list = true;
+ break;
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
}
- if (just_user + just_group + just_group_list > 1)
- error (EXIT_FAILURE, 0, _("cannot print only user and only group"));
+ size_t n_ids = argc - optind;
+ if (1 < n_ids)
+ {
+ error (0, 0, _("extra operand %s"), quote (argv[optind + 1]));
+ usage (EXIT_FAILURE);
+ }
- if (just_user + just_group + just_group_list == 0 && (use_real | use_name))
+ if (n_ids && just_context)
error (EXIT_FAILURE, 0,
- _("cannot print only names or real IDs in default format"));
+ _("cannot print security context when user specified"));
+
+ if (just_user + just_group + just_group_list + just_context > 1)
+ error (EXIT_FAILURE, 0, _("cannot print \"only\" of more than one choice"));
+
+ bool default_format = (just_user + just_group + just_group_list
+ + just_context == 0);
- if (argc - optind > 1)
+ if (default_format && (use_real || use_name))
+ error (EXIT_FAILURE, 0,
+ _("cannot print only names or real IDs in default format"));
+
+ if (default_format && opt_zero)
+ error (EXIT_FAILURE, 0,
+ _("option --zero not permitted in default format"));
+
+ /* If we are on a SELinux/SMACK-enabled kernel, no user is specified, and
+ either --context is specified or none of (-u,-g,-G) is specified,
+ and we're not in POSIXLY_CORRECT mode, get our context. Otherwise,
+ leave the context variable alone - it has been initialized to an
+ invalid value that will be not displayed in print_full_info(). */
+ if (n_ids == 0
+ && (just_context
+ || (default_format && ! getenv ("POSIXLY_CORRECT"))))
{
- error (0, 0, _("extra operand %s"), quote (argv[optind + 1]));
- usage (EXIT_FAILURE);
+ /* Report failure only if --context (-Z) was explicitly requested. */
+ if ((selinux_enabled && getcon (&context) && just_context)
+ || (smack_enabled
+ && smack_new_label_from_self (&context) < 0
+ && just_context))
+ error (EXIT_FAILURE, 0, _("can't get process context"));
}
- if (argc - optind == 1)
+ if (n_ids == 1)
{
- struct passwd *pwd = getpwnam (argv[optind]);
+ struct passwd *pwd = NULL;
+ const char *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, NULL, NULL, NULL) == NULL)
+ {
+ /* parse_user_spec will only extract a numeric spec,
+ so we lookup that here to verify and also retrieve
+ the PW_NAME used subsequently in group lookup. */
+ pwd = getpwuid (euid);
+ }
+ }
if (pwd == NULL)
- error (EXIT_FAILURE, 0, _("%s: No such user"), argv[optind]);
+ error (EXIT_FAILURE, 0, _("%s: no such user"), quote (spec));
+ pw_name = xstrdup (pwd->pw_name);
ruid = euid = pwd->pw_uid;
rgid = egid = pwd->pw_gid;
}
else
{
- euid = geteuid ();
- ruid = getuid ();
- egid = getegid ();
- rgid = getgid ();
+ /* POSIX says identification functions (getuid, getgid, and
+ others) cannot fail, but they can fail under GNU/Hurd and a
+ few other systems. Test for failure by checking errno. */
+ uid_t NO_UID = -1;
+ gid_t NO_GID = -1;
+
+ if (just_user ? !use_real
+ : !just_group && !just_group_list && !just_context)
+ {
+ errno = 0;
+ euid = geteuid ();
+ if (euid == NO_UID && errno)
+ error (EXIT_FAILURE, errno, _("cannot get effective UID"));
+ }
+
+ if (just_user ? use_real
+ : !just_group && (just_group_list || !just_context))
+ {
+ errno = 0;
+ ruid = getuid ();
+ if (ruid == NO_UID && errno)
+ error (EXIT_FAILURE, errno, _("cannot get real UID"));
+ }
+
+ if (!just_user && (just_group || just_group_list || !just_context))
+ {
+ errno = 0;
+ egid = getegid ();
+ if (egid == NO_GID && errno)
+ error (EXIT_FAILURE, errno, _("cannot get effective GID"));
+
+ errno = 0;
+ rgid = getgid ();
+ if (rgid == NO_GID && errno)
+ error (EXIT_FAILURE, errno, _("cannot get real GID"));
+ }
}
if (just_user)
- print_user (use_real ? ruid : euid);
- else if (just_group)
- print_group (use_real ? rgid : egid);
- else if (just_group_list)
- print_group_list (argv[optind]);
- else
- print_full_info (argv[optind]);
- putchar ('\n');
-
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
-}
-
-/* Print the name or value of user ID UID. */
-
-static void
-print_user (uid_t uid)
-{
- struct passwd *pwd = NULL;
-
- if (use_name)
{
- pwd = getpwuid (uid);
- if (pwd == NULL)
- {
- error (0, 0, _("cannot find name for user ID %lu"),
- (unsigned long int) uid);
- ok = false;
- }
+ print_user (use_real ? ruid : euid);
}
-
- if (pwd == NULL)
- printf ("%lu", (unsigned long int) uid);
- else
- printf ("%s", pwd->pw_name);
-}
-
-/* Print the name or value of group ID GID. */
-
-static void
-print_group (gid_t gid)
-{
- struct group *grp = NULL;
-
- if (use_name)
+ else if (just_group)
{
- grp = getgrgid (gid);
- if (grp == NULL)
- {
- error (0, 0, _("cannot find name for group ID %lu"),
- (unsigned long int) gid);
- ok = false;
- }
+ if (!print_group (use_real ? rgid : egid, use_name))
+ ok = false;
}
-
- if (grp == NULL)
- printf ("%lu", (unsigned long int) gid);
- else
- printf ("%s", grp->gr_name);
-}
-
-#if HAVE_GETGROUPS
-
-/* FIXME: document */
-
-static bool
-xgetgroups (const char *username, gid_t gid, int *n_groups,
- GETGROUPS_T **groups)
-{
- int max_n_groups;
- int ng;
- GETGROUPS_T *g = NULL;
-
- if (!username)
- max_n_groups = getgroups (0, NULL);
- else
- max_n_groups = getugroups (0, NULL, username, gid);
-
- if (max_n_groups < 0)
- ng = -1;
- else
+ else if (just_group_list)
{
- g = xnmalloc (max_n_groups, sizeof *g);
- if (!username)
- ng = getgroups (max_n_groups, g);
- else
- ng = getugroups (max_n_groups, g, username, gid);
+ if (!print_group_list (pw_name, ruid, rgid, egid, use_name,
+ opt_zero ? '\0' : ' '))
+ ok = false;
}
-
- if (ng < 0)
+ else if (just_context)
{
- error (0, errno, _("cannot get supplemental group list"));
- free (g);
- return false;
+ fputs (context, stdout);
}
else
{
- *n_groups = ng;
- *groups = g;
- return true;
+ print_full_info (pw_name);
}
+ putchar (opt_zero ? '\0' : '\n');
+
+ IF_LINT (free (pw_name));
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+/* Convert a gid_t to string. Do not use this function directly.
+ Instead, use it via the gidtostr macro.
+ Beware that it returns a pointer to static storage. */
+static char *
+gidtostr_ptr (gid_t const *gid)
+{
+ static char buf[INT_BUFSIZE_BOUND (uintmax_t)];
+ return umaxtostr (*gid, buf);
}
+#define gidtostr(g) gidtostr_ptr (&(g))
-#endif /* HAVE_GETGROUPS */
+/* Convert a uid_t to string. Do not use this function directly.
+ Instead, use it via the uidtostr macro.
+ Beware that it returns a pointer to static storage. */
+static char *
+uidtostr_ptr (uid_t const *uid)
+{
+ static char buf[INT_BUFSIZE_BOUND (uintmax_t)];
+ return umaxtostr (*uid, buf);
+}
+#define uidtostr(u) uidtostr_ptr (&(u))
-/* Print all of the distinct groups the user is in. */
+/* Print the name or value of user ID UID. */
static void
-print_group_list (const char *username)
+print_user (uid_t uid)
{
- struct passwd *pwd;
-
- pwd = getpwuid (ruid);
- if (pwd == NULL)
- ok = false;
+ struct passwd *pwd = NULL;
- print_group (rgid);
- if (egid != rgid)
+ if (use_name)
{
- putchar (' ');
- print_group (egid);
+ pwd = getpwuid (uid);
+ if (pwd == NULL)
+ {
+ error (0, 0, _("cannot find name for user ID %s"),
+ uidtostr (uid));
+ ok = false;
+ }
}
-#if HAVE_GETGROUPS
- {
- int n_groups;
- GETGROUPS_T *groups;
- int i;
-
- if (! xgetgroups (username, (pwd ? pwd->pw_gid : (gid_t) -1),
- &n_groups, &groups))
- {
- ok = false;
- return;
- }
-
- for (i = 0; i < n_groups; i++)
- if (groups[i] != rgid && groups[i] != egid)
- {
- putchar (' ');
- print_group (groups[i]);
- }
- free (groups);
- }
-#endif /* HAVE_GETGROUPS */
+ char *s = pwd ? pwd->pw_name : uidtostr (uid);
+ fputs (s, stdout);
}
/* Print all of the info about the user's user and group IDs. */
@@ -332,57 +369,70 @@ print_full_info (const char *username)
struct passwd *pwd;
struct group *grp;
- printf ("uid=%lu", (unsigned long int) ruid);
+ printf (_("uid=%s"), uidtostr (ruid));
pwd = getpwuid (ruid);
if (pwd)
printf ("(%s)", pwd->pw_name);
- printf (" gid=%lu", (unsigned long int) rgid);
+ printf (_(" gid=%s"), gidtostr (rgid));
grp = getgrgid (rgid);
if (grp)
printf ("(%s)", grp->gr_name);
if (euid != ruid)
{
- printf (" euid=%lu", (unsigned long int) euid);
+ printf (_(" euid=%s"), uidtostr (euid));
pwd = getpwuid (euid);
if (pwd)
- printf ("(%s)", pwd->pw_name);
+ printf ("(%s)", pwd->pw_name);
}
if (egid != rgid)
{
- printf (" egid=%lu", (unsigned long int) egid);
+ printf (_(" egid=%s"), gidtostr (egid));
grp = getgrgid (egid);
if (grp)
- printf ("(%s)", grp->gr_name);
+ printf ("(%s)", grp->gr_name);
}
-#if HAVE_GETGROUPS
{
- int n_groups;
- GETGROUPS_T *groups;
+ gid_t *groups;
int i;
- if (! xgetgroups (username, (pwd ? pwd->pw_gid : (gid_t) -1),
- &n_groups, &groups))
+ gid_t primary_group;
+ if (username)
+ primary_group = pwd ? pwd->pw_gid : -1;
+ else
+ primary_group = egid;
+
+ int n_groups = xgetgroups (username, primary_group, &groups);
+ if (n_groups < 0)
{
- ok = false;
- return;
+ if (username)
+ error (0, errno, _("failed to get groups for user %s"),
+ quote (username));
+ else
+ error (0, errno, _("failed to get groups for the current process"));
+ ok = false;
+ return;
}
if (n_groups > 0)
fputs (_(" groups="), stdout);
for (i = 0; i < n_groups; i++)
{
- if (i > 0)
- putchar (',');
- printf ("%lu", (unsigned long int) groups[i]);
- grp = getgrgid (groups[i]);
- if (grp)
- printf ("(%s)", grp->gr_name);
+ if (i > 0)
+ putchar (',');
+ fputs (gidtostr (groups[i]), stdout);
+ grp = getgrgid (groups[i]);
+ if (grp)
+ printf ("(%s)", grp->gr_name);
}
free (groups);
}
-#endif /* HAVE_GETGROUPS */
+
+ /* POSIX mandates the precise output format, and that it not include
+ any context=... part, so skip that if POSIXLY_CORRECT is set. */
+ if (context)
+ printf (_(" context=%s"), context);
}
diff --git a/src/install.c b/src/install.c
index 0457751..2ff279c 100644
--- a/src/install.c
+++ b/src/install.c
@@ -1,10 +1,10 @@
/* install - copy files and set attributes
- Copyright (C) 89, 90, 91, 1995-2007 Free Software Foundation, Inc.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by David MacKenzie <djm@gnu.ai.mit.edu> */
@@ -24,6 +23,8 @@
#include <signal.h>
#include <pwd.h>
#include <grp.h>
+#include <selinux/selinux.h>
+#include <sys/wait.h>
#include "system.h"
#include "backupfile.h"
@@ -31,23 +32,24 @@
#include "cp-hash.h"
#include "copy.h"
#include "filenamecat.h"
+#include "full-read.h"
#include "mkancesdirs.h"
#include "mkdir-p.h"
#include "modechange.h"
+#include "prog-fprintf.h"
#include "quote.h"
#include "savewd.h"
#include "stat-time.h"
#include "utimens.h"
#include "xstrtol.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "install"
-#define AUTHORS "David MacKenzie"
+#define AUTHORS proper_name ("David MacKenzie")
-#if HAVE_SYS_WAIT_H
-# include <sys/wait.h>
-#endif
+static int selinux_enabled = 0;
+static bool use_default_selinux_context = true;
#if ! HAVE_ENDGRENT
# define endgrent() ((void) 0)
@@ -57,47 +59,26 @@
# define endpwent() ((void) 0)
#endif
-/* Initial number of entries in each hash table entry's table of inodes. */
-#define INITIAL_HASH_MODULE 100
-
-/* Initial number of entries in the inode hash table. */
-#define INITIAL_ENTRY_TAB_SIZE 70
-
-/* Number of bytes of a file to copy at a time. */
-#define READ_SIZE (32 * 1024)
-
-static bool change_timestamps (struct stat const *from_sb, char const *to);
-static bool change_attributes (char const *name);
-static bool copy_file (const char *from, const char *to,
- const struct cp_options *x);
-static bool install_file_in_file_parents (char const *from, char *to,
- struct cp_options *x);
-static bool install_file_in_dir (const char *from, const char *to_dir,
- const struct cp_options *x);
-static bool install_file_in_file (const char *from, const char *to,
- const struct cp_options *x);
-static void get_ids (void);
-static void strip (char const *name);
-static void announce_mkdir (char const *dir, void *options);
-static int make_ancestor (char const *dir, char const *component,
- void *options);
-void usage (int status);
-
-/* The name this program was run with, for error messages. */
-char *program_name;
+#if ! HAVE_LCHOWN
+# define lchown(name, uid, gid) chown (name, uid, gid)
+#endif
+
+#if ! HAVE_MATCHPATHCON_INIT_PREFIX
+# define matchpathcon_init_prefix(a, p) /* empty */
+#endif
/* 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'. */
+/* The user ID corresponding to 'owner_name'. */
static uid_t owner_id;
/* The group name that will own the files, or NULL to make the group
the current group ID. */
static char *group_name;
-/* The group ID corresponding to `group_name'. */
+/* The group ID corresponding to 'group_name'. */
static gid_t group_id;
#define DEFAULT_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
@@ -115,22 +96,40 @@ static mode_t dir_mode = DEFAULT_MODE;
or S_ISGID bits. */
static mode_t dir_mode_bits = CHMOD_MODE_BITS;
+/* Compare files before installing (-C) */
+static bool copy_only_if_needed;
+
/* If true, strip executable files after copying them. */
static bool strip_files;
/* If true, install a directory instead of a regular file. */
static bool dir_arg;
+/* Program used to strip binaries, "strip" is default */
+static char const *strip_program = "strip";
+
+/* For long options that have no equivalent short option, use a
+ non-character as a pseudo short option, starting with CHAR_MAX + 1. */
+enum
+{
+ PRESERVE_CONTEXT_OPTION = CHAR_MAX + 1,
+ STRIP_PROGRAM_OPTION
+};
+
static struct option const long_options[] =
{
{"backup", optional_argument, NULL, 'b'},
+ {"compare", no_argument, NULL, 'C'},
+ {GETOPT_SELINUX_CONTEXT_OPTION_DECL},
{"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'},
@@ -139,23 +138,147 @@ static struct option const long_options[] =
{NULL, 0, NULL, 0}
};
+/* Compare content of opened files using file descriptors A_FD and B_FD. Return
+ true if files are equal. */
+static bool
+have_same_content (int a_fd, int b_fd)
+{
+ enum { CMP_BLOCK_SIZE = 4096 };
+ static char a_buff[CMP_BLOCK_SIZE];
+ static char b_buff[CMP_BLOCK_SIZE];
+
+ size_t size;
+ while (0 < (size = full_read (a_fd, a_buff, sizeof a_buff))) {
+ if (size != full_read (b_fd, b_buff, sizeof b_buff))
+ return false;
+
+ if (memcmp (a_buff, b_buff, size) != 0)
+ return false;
+ }
+
+ return size == 0;
+}
+
+/* Return true for mode with non-permission bits. */
+static bool
+extra_mode (mode_t input)
+{
+ mode_t mask = S_IRWXUGO | S_IFMT;
+ return !! (input & ~ mask);
+}
+
+/* Return true if copy of file SRC_NAME to file DEST_NAME is necessary. */
+static bool
+need_copy (const char *src_name, const char *dest_name,
+ 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 */
+ if (lstat (src_name, &src_sb) != 0)
+ return true;
+
+ if (lstat (dest_name, &dest_sb) != 0)
+ return true;
+
+ if (!S_ISREG (src_sb.st_mode) || !S_ISREG (dest_sb.st_mode)
+ || extra_mode (src_sb.st_mode) || extra_mode (dest_sb.st_mode))
+ return true;
+
+ if (src_sb.st_size != dest_sb.st_size
+ || (dest_sb.st_mode & CHMOD_MODE_BITS) != mode)
+ return true;
+
+ if (owner_id == (uid_t) -1)
+ {
+ errno = 0;
+ uid_t ruid = getuid ();
+ if ((ruid == (uid_t) -1 && errno) || dest_sb.st_uid != ruid)
+ return true;
+ }
+ else if (dest_sb.st_uid != owner_id)
+ return true;
+
+ if (group_id == (uid_t) -1)
+ {
+ errno = 0;
+ gid_t rgid = getgid ();
+ if ((rgid == (uid_t) -1 && errno) || dest_sb.st_gid != rgid)
+ return true;
+ }
+ else if (dest_sb.st_gid != group_id)
+ return true;
+
+ /* compare SELinux context if preserving */
+ if (selinux_enabled && x->preserve_security_context)
+ {
+ char *file_scontext = NULL;
+ char *to_scontext = NULL;
+ bool scontext_match;
+
+ if (getfilecon (src_name, &file_scontext) == -1)
+ return true;
+
+ if (getfilecon (dest_name, &to_scontext) == -1)
+ {
+ freecon (file_scontext);
+ return true;
+ }
+
+ scontext_match = STREQ (file_scontext, to_scontext);
+
+ freecon (file_scontext);
+ freecon (to_scontext);
+ if (!scontext_match)
+ return true;
+ }
+
+ /* compare files content */
+ src_fd = open (src_name, O_RDONLY | O_BINARY);
+ if (src_fd < 0)
+ return true;
+
+ dest_fd = open (dest_name, O_RDONLY | O_BINARY);
+ if (dest_fd < 0)
+ {
+ close (src_fd);
+ return true;
+ }
+
+ content_match = have_same_content (src_fd, dest_fd);
+
+ close (src_fd);
+ close (dest_fd);
+ return !content_match;
+}
+
static void
cp_option_init (struct cp_options *x)
{
+ cp_options_default (x);
x->copy_as_regular = true;
+ x->reflink_mode = REFLINK_NEVER;
x->dereference = DEREF_ALWAYS;
x->unlink_dest_before_opening = true;
x->unlink_dest_after_failed_open = false;
x->hard_link = false;
x->interactive = I_UNSPECIFIED;
x->move_mode = false;
- x->chown_privileges = chown_privileges ();
x->one_file_system = false;
x->preserve_ownership = false;
x->preserve_links = false;
x->preserve_mode = false;
x->preserve_timestamps = false;
+ x->explicit_no_preserve_mode = false;
+ x->reduce_diagnostics=false;
+ x->data_copy_required = true;
x->require_preserve = false;
+ x->require_preserve_xattr = false;
x->recursive = false;
x->sparse_mode = SPARSE_AUTO;
x->symbolic_link = false;
@@ -168,12 +291,101 @@ cp_option_init (struct cp_options *x)
x->mode = S_IRUSR | S_IWUSR;
x->stdin_tty = false;
+ x->open_dangling_dest_symlink = false;
x->update = false;
+ 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 = false; /* Whether to set sys default context. */
+ x->preserve_xattr = false;
x->verbose = false;
x->dest_info = NULL;
x->src_info = NULL;
}
+#ifdef ENABLE_MATCHPATHCON
+/* Modify file context to match the specified policy.
+ If an error occurs the file will remain with the default directory
+ context. Note this sets the context to that returned by matchpathcon,
+ and thus discards MLS levels and user identity of the FILE. */
+static void
+setdefaultfilecon (char const *file)
+{
+ struct stat st;
+ char *scontext = NULL;
+ static bool first_call = true;
+
+ if (selinux_enabled != 1)
+ {
+ /* Indicate no context found. */
+ return;
+ }
+ if (lstat (file, &st) != 0)
+ return;
+
+ if (first_call && IS_ABSOLUTE_FILE_NAME (file))
+ {
+ /* Calling matchpathcon_init_prefix (NULL, "/first_component/")
+ is an optimization to minimize the expense of the following
+ matchpathcon call. Do it only once, just before the first
+ matchpathcon call. We *could* call matchpathcon_fini after
+ the final matchpathcon call, but that's not necessary, since
+ by then we're about to exit, and besides, the buffers it
+ would free are still reachable. */
+ char const *p0;
+ char const *p = file + 1;
+ while (ISSLASH (*p))
+ ++p;
+
+ /* Record final leading slash, for when FILE starts with two or more. */
+ p0 = p - 1;
+
+ if (*p)
+ {
+ char *prefix;
+ do
+ {
+ ++p;
+ }
+ while (*p && !ISSLASH (*p));
+
+ prefix = malloc (p - p0 + 2);
+ if (prefix)
+ {
+ stpcpy (stpncpy (prefix, p0, p - p0), "/");
+ matchpathcon_init_prefix (NULL, prefix);
+ free (prefix);
+ }
+ }
+ }
+ first_call = false;
+
+ /* If there's an error determining the context, or it has none,
+ return to allow default context. Note the "<<none>>" check
+ is only needed for libselinux < 1.20 (2005-01-04). */
+ if ((matchpathcon (file, st.st_mode, &scontext) != 0)
+ || STREQ (scontext, "<<none>>"))
+ {
+ if (scontext != NULL)
+ freecon (scontext);
+ return;
+ }
+
+ if (lsetfilecon (file, scontext) < 0 && errno != ENOTSUP)
+ error (0, errno,
+ _("warning: %s: failed to change context to %s"),
+ quotef_n (0, file), quote_n (1, scontext));
+
+ freecon (scontext);
+ return;
+}
+#else
+static void
+setdefaultfilecon (char const *file)
+{
+ (void) file;
+}
+#endif
+
/* FILE is the last operand of this command. Return true if FILE is a
directory. But report an error there is a problem accessing FILE,
or if FILE does not exist but would have to refer to an existing
@@ -189,295 +401,44 @@ target_directory_operand (char const *file)
int err = (stat (file, &st) == 0 ? 0 : errno);
bool is_a_dir = !err && S_ISDIR (st.st_mode);
if (err && err != ENOENT)
- error (EXIT_FAILURE, err, _("accessing %s"), quote (file));
+ error (EXIT_FAILURE, err, _("failed to access %s"), quoteaf (file));
if (is_a_dir < looks_like_a_dir)
- error (EXIT_FAILURE, err, _("target %s is not a directory"), quote (file));
+ error (EXIT_FAILURE, err, _("target %s is not a directory"),
+ quoteaf (file));
return is_a_dir;
}
-/* Process a command-line file name, for the -d option. */
-static int
-process_dir (char *dir, struct savewd *wd, void *options)
-{
- return (make_dir_parents (dir, wd,
- make_ancestor, options,
- dir_mode, announce_mkdir,
- dir_mode_bits, owner_id, group_id, false)
- ? EXIT_SUCCESS
- : EXIT_FAILURE);
-}
-
-int
-main (int argc, char **argv)
-{
- int optc;
- int exit_status = EXIT_SUCCESS;
- const char *specified_mode = NULL;
- bool make_backups = false;
- char *backup_suffix_string;
- char *version_control_string = NULL;
- bool mkdir_and_install = false;
- struct cp_options x;
- char const *target_directory = NULL;
- bool no_target_directory = false;
- int n_files;
- char **file;
-
- initialize_main (&argc, &argv);
- program_name = argv[0];
- setlocale (LC_ALL, "");
- bindtextdomain (PACKAGE, LOCALEDIR);
- textdomain (PACKAGE);
-
- atexit (close_stdout);
-
- cp_option_init (&x);
-
- owner_name = NULL;
- group_name = NULL;
- strip_files = false;
- dir_arg = false;
- umask (0);
-
- /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
- we'll actually use backup_suffix_string. */
- backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
-
- while ((optc = getopt_long (argc, argv, "bcsDdg:m:o:pt:TvS:", long_options,
- NULL)) != -1)
- {
- switch (optc)
- {
- case 'b':
- make_backups = true;
- if (optarg)
- version_control_string = optarg;
- break;
- case 'c':
- break;
- case 's':
- strip_files = true;
-#ifdef SIGCHLD
- /* System V fork+wait does not work if SIGCHLD is ignored. */
- signal (SIGCHLD, SIG_DFL);
-#endif
- break;
- case 'd':
- dir_arg = true;
- break;
- case 'D':
- mkdir_and_install = true;
- break;
- case 'v':
- x.verbose = true;
- break;
- case 'g':
- group_name = optarg;
- break;
- case 'm':
- specified_mode = optarg;
- break;
- case 'o':
- owner_name = optarg;
- break;
- case 'p':
- x.preserve_timestamps = true;
- break;
- case 'S':
- make_backups = true;
- backup_suffix_string = optarg;
- break;
- case 't':
- if (target_directory)
- error (EXIT_FAILURE, 0,
- _("multiple target directories specified"));
- else
- {
- struct stat st;
- if (stat (optarg, &st) != 0)
- error (EXIT_FAILURE, errno, _("accessing %s"), quote (optarg));
- if (! S_ISDIR (st.st_mode))
- error (EXIT_FAILURE, 0, _("target %s is not a directory"),
- quote (optarg));
- }
- target_directory = optarg;
- break;
- case 'T':
- no_target_directory = true;
- break;
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- }
- }
-
- /* Check for invalid combinations of arguments. */
- if (dir_arg & strip_files)
- error (EXIT_FAILURE, 0,
- _("the strip option may not be used when installing a directory"));
- if (dir_arg && target_directory)
- error (EXIT_FAILURE, 0,
- _("target directory not allowed when installing a directory"));
-
- if (backup_suffix_string)
- simple_backup_suffix = xstrdup (backup_suffix_string);
-
- x.backup_type = (make_backups
- ? xget_version (_("backup type"),
- version_control_string)
- : no_backups);
-
- n_files = argc - optind;
- file = argv + optind;
-
- if (n_files <= ! (dir_arg || target_directory))
- {
- if (n_files <= 0)
- error (0, 0, _("missing file operand"));
- else
- error (0, 0, _("missing destination file operand after %s"),
- quote (file[0]));
- usage (EXIT_FAILURE);
- }
-
- if (no_target_directory)
- {
- if (target_directory)
- error (EXIT_FAILURE, 0,
- _("Cannot combine --target-directory (-t) "
- "and --no-target-directory (-T)"));
- if (2 < n_files)
- {
- error (0, 0, _("extra operand %s"), quote (file[2]));
- usage (EXIT_FAILURE);
- }
- }
- else if (! (dir_arg || target_directory))
- {
- if (2 <= n_files && target_directory_operand (file[n_files - 1]))
- target_directory = file[--n_files];
- else if (2 < n_files)
- error (EXIT_FAILURE, 0, _("target %s is not a directory"),
- quote (file[n_files - 1]));
- }
-
- if (specified_mode)
- {
- 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, NULL);
- dir_mode = mode_adjust (0, true, 0, change, &dir_mode_bits);
- free (change);
- }
-
- get_ids ();
-
- if (dir_arg)
- exit_status = savewd_process_files (n_files, file, process_dir, &x);
- else
- {
- /* FIXME: it's a little gross that this initialization is
- required by copy.c::copy. */
- hash_init ();
-
- if (!target_directory)
- {
- if (! (mkdir_and_install
- ? install_file_in_file_parents (file[0], file[1], &x)
- : install_file_in_file (file[0], file[1], &x)))
- exit_status = EXIT_FAILURE;
- }
- else
- {
- int i;
- dest_info_init (&x);
- for (i = 0; i < n_files; i++)
- if (! install_file_in_dir (file[i], target_directory, &x))
- exit_status = EXIT_FAILURE;
- }
- }
-
- exit (exit_status);
-}
-
-/* Copy file FROM onto file TO, creating any missing parent directories of TO.
- Return true if successful. */
-
-static bool
-install_file_in_file_parents (char const *from, char *to,
- struct cp_options *x)
+/* Report that directory DIR was made, if OPTIONS requests this. */
+static void
+announce_mkdir (char const *dir, void *options)
{
- bool save_working_directory =
- ! (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);
-
- if (mkancesdirs (to, &wd, make_ancestor, x) == -1)
- {
- error (0, errno, _("cannot create directory %s"), to);
- status = EXIT_FAILURE;
- }
-
- if (save_working_directory)
- {
- int restore_result = savewd_restore (&wd, status);
- int restore_errno = errno;
- savewd_finish (&wd);
- if (EXIT_SUCCESS < restore_result)
- return false;
- if (restore_result < 0 && status == EXIT_SUCCESS)
- {
- error (0, restore_errno, _("cannot create directory %s"), to);
- return false;
- }
- }
-
- return (status == EXIT_SUCCESS && install_file_in_file (from, to, x));
+ struct cp_options const *x = options;
+ if (x->verbose)
+ prog_fprintf (stdout, _("creating directory %s"), quoteaf (dir));
}
-/* Copy file FROM onto file TO and give TO the appropriate
- attributes.
- Return true if successful. */
-
-static bool
-install_file_in_file (const char *from, const char *to,
- const struct cp_options *x)
+/* Make ancestor directory DIR, whose last file name component is
+ COMPONENT, with options OPTIONS. Assume the working directory is
+ COMPONENT's parent. */
+static int
+make_ancestor (char const *dir, char const *component, void *options)
{
- struct stat from_sb;
- if (x->preserve_timestamps && stat (from, &from_sb) != 0)
- {
- error (0, errno, _("cannot stat %s"), quote (from));
- return false;
- }
- if (! copy_file (from, to, x))
- return false;
- if (strip_files)
- strip (to);
- if (x->preserve_timestamps && (strip_files || ! S_ISREG (from_sb.st_mode))
- && ! change_timestamps (&from_sb, to))
- return false;
- return change_attributes (to);
+ int r = mkdir (component, DEFAULT_MODE);
+ if (r == 0)
+ announce_mkdir (dir, options);
+ return r;
}
-/* Copy file FROM into directory TO_DIR, keeping its same name,
- and give the copy the appropriate attributes.
- Return true if successful. */
-
-static bool
-install_file_in_dir (const char *from, const char *to_dir,
- const struct cp_options *x)
+/* Process a command-line file name, for the -d option. */
+static int
+process_dir (char *dir, struct savewd *wd, void *options)
{
- const char *from_base = last_component (from);
- char *to = file_name_concat (to_dir, from_base, NULL);
- bool ret = install_file_in_file (from, to, x);
- free (to);
- return ret;
+ return (make_dir_parents (dir, wd,
+ make_ancestor, options,
+ dir_mode, announce_mkdir,
+ dir_mode_bits, owner_id, group_id, false)
+ ? EXIT_SUCCESS
+ : EXIT_FAILURE);
}
/* Copy file FROM onto file TO, creating TO if necessary.
@@ -488,6 +449,9 @@ copy_file (const char *from, const char *to, const struct cp_options *x)
{
bool copy_into_self;
+ if (copy_only_if_needed && !need_copy (from, to, x))
+ return true;
+
/* Allow installing from non-regular files like /dev/null.
Charles Karney reported that some Sun version of install allows that
and that sendmail's installation process relies on the behavior.
@@ -503,6 +467,7 @@ copy_file (const char *from, const char *to, const struct cp_options *x)
static bool
change_attributes (char const *name)
{
+ bool ok = false;
/* chown must precede chmod because on some systems,
chown clears the set[ug]id bits for non-superusers,
resulting in incorrect permissions.
@@ -516,29 +481,32 @@ change_attributes (char const *name)
want to know. */
if (! (owner_id == (uid_t) -1 && group_id == (gid_t) -1)
- && chown (name, owner_id, group_id) != 0)
- error (0, errno, _("cannot change ownership of %s"), quote (name));
+ && lchown (name, owner_id, group_id) != 0)
+ error (0, errno, _("cannot change ownership of %s"), quoteaf (name));
else if (chmod (name, mode) != 0)
- error (0, errno, _("cannot change permissions of %s"), quote (name));
+ error (0, errno, _("cannot change permissions of %s"), quoteaf (name));
else
- return true;
+ ok = true;
- return false;
+ if (use_default_selinux_context)
+ setdefaultfilecon (name);
+
+ return ok;
}
-/* Set the timestamps of file TO to match those of file FROM.
+/* Set the timestamps of file DEST to match those of SRC_SB.
Return true if successful. */
static bool
-change_timestamps (struct stat const *from_sb, char const *to)
+change_timestamps (struct stat const *src_sb, char const *dest)
{
struct timespec timespec[2];
- timespec[0] = get_stat_atime (from_sb);
- timespec[1] = get_stat_mtime (from_sb);
+ timespec[0] = get_stat_atime (src_sb);
+ timespec[1] = get_stat_mtime (src_sb);
- if (utimens (to, timespec))
+ if (utimens (dest, timespec))
{
- error (0, errno, _("cannot set time stamps for %s"), quote (to));
+ error (0, errno, _("cannot set time stamps for %s"), quoteaf (dest));
return false;
}
return true;
@@ -550,28 +518,32 @@ change_timestamps (struct stat const *from_sb, char const *to)
magic numbers vary so much from system to system that making
it portable would be very difficult. Not worth the effort. */
-static void
+static bool
strip (char const *name)
{
int status;
+ bool ok = false;
pid_t pid = fork ();
switch (pid)
{
case -1:
- error (EXIT_FAILURE, errno, _("fork system call failed"));
+ error (0, errno, _("fork system call failed"));
break;
case 0: /* Child. */
- execlp ("strip", "strip", name, NULL);
- error (EXIT_FAILURE, errno, _("cannot run strip"));
+ execlp (strip_program, strip_program, name, NULL);
+ error (EXIT_FAILURE, errno, _("cannot run %s"), quoteaf (strip_program));
break;
default: /* Parent. */
if (waitpid (pid, &status, 0) < 0)
- error (EXIT_FAILURE, errno, _("waiting for strip"));
+ error (0, errno, _("waiting for strip"));
else if (! WIFEXITED (status) || WEXITSTATUS (status))
- error (EXIT_FAILURE, 0, _("strip process terminated abnormally"));
+ error (0, 0, _("strip process terminated abnormally"));
+ else
+ ok = true; /* strip succeeded */
break;
}
+ return ok;
}
/* Initialize the user and group ownership of the files to install. */
@@ -586,15 +558,16 @@ get_ids (void)
{
pw = getpwnam (owner_name);
if (pw == NULL)
- {
- unsigned long int tmp;
- if (xstrtoul (owner_name, NULL, 0, &tmp, NULL) != LONGINT_OK
- || UID_T_MAX < tmp)
- error (EXIT_FAILURE, 0, _("invalid user %s"), quote (owner_name));
- owner_id = tmp;
- }
+ {
+ unsigned long int tmp;
+ if (xstrtoul (owner_name, NULL, 0, &tmp, NULL) != LONGINT_OK
+ || UID_T_MAX < tmp)
+ error (EXIT_FAILURE, 0, _("invalid user %s"),
+ quote (owner_name));
+ owner_id = tmp;
+ }
else
- owner_id = pw->pw_uid;
+ owner_id = pw->pw_uid;
endpwent ();
}
else
@@ -604,48 +577,27 @@ get_ids (void)
{
gr = getgrnam (group_name);
if (gr == NULL)
- {
- unsigned long int tmp;
- if (xstrtoul (group_name, NULL, 0, &tmp, NULL) != LONGINT_OK
- || GID_T_MAX < tmp)
- error (EXIT_FAILURE, 0, _("invalid group %s"), quote (group_name));
- group_id = tmp;
- }
+ {
+ unsigned long int tmp;
+ if (xstrtoul (group_name, NULL, 0, &tmp, NULL) != LONGINT_OK
+ || GID_T_MAX < tmp)
+ error (EXIT_FAILURE, 0, _("invalid group %s"),
+ quote (group_name));
+ group_id = tmp;
+ }
else
- group_id = gr->gr_gid;
+ group_id = gr->gr_gid;
endgrent ();
}
else
group_id = (gid_t) -1;
}
-/* Report that directory DIR was made, if OPTIONS requests this. */
-static void
-announce_mkdir (char const *dir, void *options)
-{
- struct cp_options const *x = options;
- if (x->verbose)
- error (0, 0, _("creating directory %s"), quote (dir));
-}
-
-/* Make ancestor directory DIR, whose last file name component is
- COMPONENT, with options OPTIONS. Assume the working directory is
- COMPONENT's parent. */
-static int
-make_ancestor (char const *dir, char const *component, void *options)
-{
- int r = mkdir (component, DEFAULT_MODE);
- if (r == 0)
- announce_mkdir (dir, options);
- return r;
-}
-
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
@@ -654,25 +606,33 @@ Usage: %s [OPTION]... [-T] SOURCE DEST\n\
or: %s [OPTION]... -t DIRECTORY SOURCE...\n\
or: %s [OPTION]... -d DIRECTORY...\n\
"),
- program_name, program_name, program_name, program_name);
+ program_name, program_name, program_name, program_name);
fputs (_("\
+\n\
+This install program copies files (often just compiled) into destination\n\
+locations you choose. If you want to download and install a ready-to-use\n\
+package on a GNU/Linux system, you should instead be using a package manager\n\
+like yum(1) or apt-get(1).\n\
+\n\
In the first three forms, copy SOURCE to DEST or multiple SOURCE(s) to\n\
the existing DIRECTORY, while setting permission modes and owner/group.\n\
In the 4th form, create all components of the given DIRECTORY(ies).\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
+
+ 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 each pair of source and destination files, and\n\
+ in some cases, 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\
-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\
@@ -682,16 +642,25 @@ Mandatory arguments to long options are mandatory for short options too.\n\
-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\
-v, --verbose print the name of each directory as it is created\n\
"), stdout);
+ fputs (_("\
+ --preserve-context preserve SELinux security context\n\
+ -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);
fputs (_("\
\n\
-The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
+The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
The version control method may be selected via the --backup option or through\n\
the VERSION_CONTROL environment variable. Here are the values:\n\
\n\
@@ -702,7 +671,383 @@ the VERSION_CONTROL environment variable. Here are the values:\n\
existing, nil numbered if numbered backups exist, simple otherwise\n\
simple, never always make simple backups\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
+
+/* Copy file FROM onto file TO and give TO the appropriate
+ attributes.
+ Return true if successful. */
+
+static bool
+install_file_in_file (const char *from, const char *to,
+ const struct cp_options *x)
+{
+ struct stat from_sb;
+ if (x->preserve_timestamps && stat (from, &from_sb) != 0)
+ {
+ error (0, errno, _("cannot stat %s"), quoteaf (from));
+ return false;
+ }
+ if (! copy_file (from, to, x))
+ return false;
+ if (strip_files)
+ if (! strip (to))
+ {
+ if (unlink (to) != 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))
+ && ! change_timestamps (&from_sb, to))
+ return false;
+ return change_attributes (to);
+}
+
+/* Create any missing parent directories of TO,
+ while maintaining the current Working Directory.
+ Return true if successful. */
+
+static bool
+mkancesdirs_safe_wd (char const *from, char *to, struct cp_options *x,
+ bool save_always)
+{
+ 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);
+
+ if (mkancesdirs (to, &wd, make_ancestor, x) == -1)
+ {
+ error (0, errno, _("cannot create directory %s"), quoteaf (to));
+ status = EXIT_FAILURE;
+ }
+
+ if (save_working_directory)
+ {
+ int restore_result = savewd_restore (&wd, status);
+ int restore_errno = errno;
+ savewd_finish (&wd);
+ if (EXIT_SUCCESS < restore_result)
+ return false;
+ if (restore_result < 0 && status == EXIT_SUCCESS)
+ {
+ error (0, restore_errno, _("cannot create directory %s"),
+ quoteaf (to));
+ return false;
+ }
+ }
+ return status == EXIT_SUCCESS;
+}
+
+/* Copy file FROM onto file TO, creating any missing parent directories of TO.
+ Return true if successful. */
+
+static bool
+install_file_in_file_parents (char const *from, char *to,
+ const struct cp_options *x)
+{
+ return (mkancesdirs_safe_wd (from, to, (struct cp_options *)x, false)
+ && install_file_in_file (from, to, x));
+}
+
+/* Copy file FROM into directory TO_DIR, keeping its same name,
+ and give the copy the appropriate attributes.
+ Return true if successful. */
+
+static bool
+install_file_in_dir (const char *from, const char *to_dir,
+ const struct cp_options *x, bool mkdir_and_install)
+{
+ const char *from_base = last_component (from);
+ char *to = file_name_concat (to_dir, from_base, NULL);
+ bool ret = true;
+
+ if (mkdir_and_install)
+ ret = mkancesdirs_safe_wd (from, to, (struct cp_options *)x, true);
+
+ ret = ret && install_file_in_file (from, to, x);
+ free (to);
+ return ret;
+}
+
+int
+main (int argc, char **argv)
+{
+ int optc;
+ int exit_status = EXIT_SUCCESS;
+ const char *specified_mode = NULL;
+ bool make_backups = false;
+ char *backup_suffix_string;
+ char *version_control_string = NULL;
+ bool mkdir_and_install = false;
+ struct cp_options x;
+ char const *target_directory = NULL;
+ bool no_target_directory = false;
+ int n_files;
+ char **file;
+ bool strip_program_specified = false;
+ char const *scontext = NULL;
+ /* set iff kernel has extra selinux system calls */
+ selinux_enabled = (0 < is_selinux_enabled ());
+
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ atexit (close_stdin);
+
+ cp_option_init (&x);
+
+ owner_name = NULL;
+ group_name = NULL;
+ strip_files = false;
+ dir_arg = false;
+ umask (0);
+
+ /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
+ we'll actually use backup_suffix_string. */
+ backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
+
+ while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pt:TvS:Z", long_options,
+ NULL)) != -1)
+ {
+ switch (optc)
+ {
+ case 'b':
+ make_backups = true;
+ if (optarg)
+ version_control_string = optarg;
+ break;
+ case 'c':
+ break;
+ case 'C':
+ copy_only_if_needed = true;
+ break;
+ case 's':
+ strip_files = true;
+#ifdef SIGCHLD
+ /* System V fork+wait does not work if SIGCHLD is ignored. */
+ signal (SIGCHLD, SIG_DFL);
+#endif
+ break;
+ case STRIP_PROGRAM_OPTION:
+ strip_program = xstrdup (optarg);
+ strip_program_specified = true;
+ break;
+ case 'd':
+ dir_arg = true;
+ break;
+ case 'D':
+ mkdir_and_install = true;
+ break;
+ case 'v':
+ x.verbose = true;
+ break;
+ case 'g':
+ group_name = optarg;
+ break;
+ case 'm':
+ specified_mode = optarg;
+ break;
+ case 'o':
+ owner_name = optarg;
+ break;
+ case 'p':
+ x.preserve_timestamps = true;
+ break;
+ case 'S':
+ make_backups = true;
+ backup_suffix_string = optarg;
+ break;
+ case 't':
+ if (target_directory)
+ error (EXIT_FAILURE, 0,
+ _("multiple target directories specified"));
+ target_directory = optarg;
+ break;
+ case 'T':
+ no_target_directory = true;
+ break;
+
+ case PRESERVE_CONTEXT_OPTION:
+ if (! selinux_enabled)
+ {
+ error (0, 0, _("WARNING: ignoring --preserve-context; "
+ "this kernel is not SELinux-enabled"));
+ break;
+ }
+ x.preserve_security_context = true;
+ use_default_selinux_context = false;
+ break;
+ case 'Z':
+ if (selinux_enabled)
+ {
+ /* Disable use of the install(1) specific setdefaultfilecon().
+ Note setdefaultfilecon() is different from the newer and more
+ generic restorecon() in that the former sets the context of
+ the dest files to that returned by matchpathcon directly,
+ thus discarding MLS level and user identity of the file.
+ TODO: consider removing setdefaultfilecon() in future. */
+ use_default_selinux_context = false;
+
+ if (optarg)
+ scontext = optarg;
+ else
+ x.set_security_context = true;
+ }
+ else if (optarg)
+ {
+ error (0, 0,
+ _("warning: ignoring --context; "
+ "it requires an SELinux-enabled kernel"));
+ }
+ break;
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
+ }
+
+ /* Check for invalid combinations of arguments. */
+ if (dir_arg && strip_files)
+ error (EXIT_FAILURE, 0,
+ _("the strip option may not be used when installing a directory"));
+ if (dir_arg && target_directory)
+ error (EXIT_FAILURE, 0,
+ _("target directory not allowed when installing a directory"));
+
+ if (target_directory)
+ {
+ struct stat st;
+ bool stat_success = stat (target_directory, &st) == 0 ? true : false;
+ if (! mkdir_and_install && ! stat_success)
+ error (EXIT_FAILURE, errno, _("failed to access %s"),
+ quoteaf (target_directory));
+ if (stat_success && ! S_ISDIR (st.st_mode))
+ error (EXIT_FAILURE, 0, _("target %s is not a directory"),
+ quoteaf (target_directory));
+ }
+
+ if (backup_suffix_string)
+ simple_backup_suffix = xstrdup (backup_suffix_string);
+
+ x.backup_type = (make_backups
+ ? xget_version (_("backup type"),
+ version_control_string)
+ : no_backups);
+
+ if (x.preserve_security_context && (x.set_security_context || scontext))
+ error (EXIT_FAILURE, 0,
+ _("cannot set target context and preserve it"));
+
+ if (scontext && setfscreatecon (se_const (scontext)) < 0)
+ error (EXIT_FAILURE, errno,
+ _("failed to set default file creation context to %s"),
+ quote (scontext));
+
+ n_files = argc - optind;
+ file = argv + optind;
+
+ if (n_files <= ! (dir_arg || target_directory))
+ {
+ if (n_files <= 0)
+ error (0, 0, _("missing file operand"));
+ else
+ error (0, 0, _("missing destination file operand after %s"),
+ quoteaf (file[0]));
+ usage (EXIT_FAILURE);
+ }
+
+ if (no_target_directory)
+ {
+ if (target_directory)
+ error (EXIT_FAILURE, 0,
+ _("cannot combine --target-directory (-t) "
+ "and --no-target-directory (-T)"));
+ if (2 < n_files)
+ {
+ error (0, 0, _("extra operand %s"), quoteaf (file[2]));
+ usage (EXIT_FAILURE);
+ }
+ }
+ else if (! (dir_arg || target_directory))
+ {
+ if (2 <= n_files && target_directory_operand (file[n_files - 1]))
+ target_directory = file[--n_files];
+ else if (2 < n_files)
+ error (EXIT_FAILURE, 0, _("target %s is not a directory"),
+ quoteaf (file[n_files - 1]));
+ }
+
+ if (specified_mode)
+ {
+ 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, NULL);
+ dir_mode = mode_adjust (0, true, 0, change, &dir_mode_bits);
+ free (change);
+ }
+
+ if (strip_program_specified && !strip_files)
+ 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 "
+ "exclusive"));
+ usage (EXIT_FAILURE);
+ }
+
+ if (copy_only_if_needed && extra_mode (mode))
+ error (0, 0, _("the --compare (-C) option is ignored when you"
+ " specify a mode with non-permission bits"));
+
+ get_ids ();
+
+ if (dir_arg)
+ exit_status = savewd_process_files (n_files, file, process_dir, &x);
+ else
+ {
+ /* FIXME: it's a little gross that this initialization is
+ required by copy.c::copy. */
+ hash_init ();
+
+ if (!target_directory)
+ {
+ if (! (mkdir_and_install
+ ? install_file_in_file_parents (file[0], file[1], &x)
+ : install_file_in_file (file[0], file[1], &x)))
+ exit_status = EXIT_FAILURE;
+ }
+ else
+ {
+ int i;
+ dest_info_init (&x);
+ for (i = 0; i < n_files; i++)
+ if (! install_file_in_dir (file[i], target_directory, &x,
+ i == 0 && mkdir_and_install))
+ exit_status = EXIT_FAILURE;
+ }
+ }
+
+ return exit_status;
+}
diff --git a/src/ioblksize.h b/src/ioblksize.h
new file mode 100644
index 0000000..266c209
--- /dev/null
+++ b/src/ioblksize.h
@@ -0,0 +1,78 @@
+/* I/O block size definitions for coreutils
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Include this file _after_ system headers if possible. */
+
+/* sys/stat.h will already have been included by system.h. */
+#include "stat-size.h"
+
+
+/* As of May 2014, 128KiB is determined to be the minimium
+ blksize to best minimize system call overhead.
+ This can be tested with this script:
+
+ for i in $(seq 0 10); do
+ bs=$((1024*2**$i))
+ printf "%7s=" $bs
+ timeout --foreground -sINT 2 \
+ dd bs=$bs if=/dev/zero of=/dev/null 2>&1 \
+ | sed -n 's/.* \([0-9.]* [GM]B\/s\)/\1/p'
+ done
+
+ With the results shown for these systems:
+ system #1: 1.7GHz pentium-m with 400MHz DDR2 RAM, arch=i686
+ system #2: 2.1GHz i3-2310M with 1333MHz DDR3 RAM, arch=x86_64
+ system #3: 3.2GHz i7-970 with 1333MHz DDR3, arch=x86_64
+ system #4: 2.20GHz Xeon E5-2660 with 1333MHz DDR3, arch=x86_64
+ system #5: 2.30GHz i7-3615QM with 1600MHz DDR3, arch=x86_64
+ system #6: 1.30GHz i5-4250U with 1-channel 1600MHz DDR3, arch=x86_64
+ system #7: 3.55GHz IBM,8231-E2B with 1066MHz DDR3, POWER7 revision 2.1
+
+ per-system transfer rate (GB/s)
+ blksize #1 #2 #3 #4 #5 #6 #7
+ ------------------------------------------------------------------------
+ 1024 .73 1.7 2.6 .64 1.0 2.5 1.3
+ 2048 1.3 3.0 4.4 1.2 2.0 4.4 2.5
+ 4096 2.4 5.1 6.5 2.3 3.7 7.4 4.8
+ 8192 3.5 7.3 8.5 4.0 6.0 10.4 9.2
+ 16384 3.9 9.4 10.1 6.3 8.3 13.3 16.8
+ 32768 5.2 9.9 11.1 8.1 10.7 13.2 28.0
+ 65536 5.3 11.2 12.0 10.6 12.8 16.1 41.4
+ 131072 5.5 11.8 12.3 12.1 14.0 16.7 54.8
+ 262144 5.7 11.6 12.5 12.3 14.7 16.4 40.0
+ 524288 5.7 11.4 12.5 12.1 14.7 15.5 34.5
+ 1048576 5.8 11.4 12.6 12.2 14.9 15.7 36.5
+
+
+ Note that this is to minimize system call overhead.
+ Other values may be appropriate to minimize file system
+ or disk overhead. For example on my current GNU/Linux system
+ the readahead setting is 128KiB which was read using:
+
+ file="."
+ device=$(df --output=source --local "$file" | tail -n1)
+ echo $(( $(blockdev --getra $device) * 512 ))
+
+ However there isn't a portable way to get the above.
+ In the future we could use the above method if available
+ and default to io_blksize() if not.
+ */
+enum { IO_BUFSIZE = 128*1024 };
+static inline size_t
+io_blksize (struct stat sb)
+{
+ return MAX (IO_BUFSIZE, ST_BLKSIZE (sb));
+}
diff --git a/src/join.c b/src/join.c
index b113c54..9b25da6 100644
--- a/src/join.c
+++ b/src/join.c
@@ -1,10 +1,10 @@
/* join - join lines of two files on a common field
- Copyright (C) 91, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1991-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
Written by Mike Haertel, mike@gnu.ai.mit.edu. */
@@ -25,6 +24,7 @@
#include "system.h"
#include "error.h"
+#include "fadvise.h"
#include "hard-locale.h"
#include "linebuffer.h"
#include "memcasecmp.h"
@@ -32,14 +32,21 @@
#include "stdio--.h"
#include "xmemcoll.h"
#include "xstrtol.h"
+#include "argmatch.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "join"
-#define AUTHORS "Mike Haertel"
+#define AUTHORS proper_name ("Mike Haertel")
#define join system_join
+#define SWAPLINES(a, b) do { \
+ struct line *tmp = a; \
+ a = b; \
+ b = tmp; \
+} while (0);
+
/* An element of the list identifying which fields to print for each
output line. */
struct outlist
@@ -65,8 +72,8 @@ struct field
struct line
{
struct linebuffer buf; /* The line itself. */
- size_t nfields; /* Number of elements in `fields'. */
- size_t nfields_allocated; /* Number of elements allocated for `fields'. */
+ size_t nfields; /* Number of elements in 'fields'. */
+ size_t nfields_allocated; /* Number of elements allocated for 'fields'. */
struct field *fields;
};
@@ -74,13 +81,24 @@ struct line
same join field value. */
struct seq
{
- size_t count; /* Elements used in `lines'. */
- size_t alloc; /* Elements allocated in `lines'. */
- struct line *lines;
+ size_t count; /* Elements used in 'lines'. */
+ size_t alloc; /* Elements allocated in 'lines'. */
+ struct line **lines;
};
-/* The name this program was run with. */
-char *program_name;
+/* The previous line read from each file. */
+static struct line *prevline[2] = {NULL, NULL};
+
+/* The number of lines read from each file. */
+static uintmax_t line_no[2] = {0, 0};
+
+/* The input file names. */
+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] = {NULL, NULL};
/* True if the LC_COLLATE locale is hard. */
static bool hard_LC_COLLATE;
@@ -91,9 +109,22 @@ static bool print_unpairables_1, print_unpairables_2;
/* If nonzero, print pairable lines. */
static bool print_pairables;
+/* If nonzero, we have seen at least one unpairable line. */
+static bool seen_unpairable;
+
+/* If nonzero, we have warned about disorder in that file. */
+static bool issued_disorder_warning[2];
+
/* Empty output field filler. */
static char const *empty_filler;
+/* Whether to ensure the same number of fields are output from each line. */
+static bool autoformat;
+/* The number of fields to output for each line.
+ Only significant when autoformat is true. */
+static size_t autocount_1;
+static size_t autocount_2;
+
/* Field to join on; SIZE_MAX means they haven't been determined yet. */
static size_t join_field_1 = SIZE_MAX;
static size_t join_field_2 = SIZE_MAX;
@@ -101,7 +132,7 @@ static size_t join_field_2 = SIZE_MAX;
/* List of fields to print. */
static struct outlist outlist_head;
-/* Last element in `outlist', where a new element can be added. */
+/* Last element in 'outlist', where a new element can be added. */
static struct outlist *outlist_end = &outlist_head;
/* Tab character separating fields. If negative, fields are separated
@@ -109,9 +140,29 @@ static struct outlist *outlist_end = &outlist_head;
tab character whose value (when cast to unsigned char) equals TAB. */
static int tab = -1;
+/* If nonzero, check that the input is correctly ordered. */
+static enum
+ {
+ CHECK_ORDER_DEFAULT,
+ CHECK_ORDER_ENABLED,
+ CHECK_ORDER_DISABLED
+ } check_input_order;
+
+enum
+{
+ CHECK_ORDER_OPTION = CHAR_MAX + 1,
+ NOCHECK_ORDER_OPTION,
+ HEADER_LINE_OPTION
+};
+
+
static struct option const longopts[] =
{
{"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},
{NULL, 0, NULL, 0}
@@ -123,30 +174,42 @@ static struct line uni_blank;
/* If nonzero, ignore case when comparing join fields. */
static bool ignore_case;
+/* If nonzero, treat the first line of each file as column headers --
+ join them without checking for ordering */
+static bool join_header_lines;
+
+/* The character marking end of line. Default to \n. */
+static char eolchar = '\n';
+
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... FILE1 FILE2\n\
"),
- program_name);
+ program_name);
fputs (_("\
For each pair of input lines with identical join fields, write a line to\n\
-standard output. The default join field is the first, delimited\n\
-by whitespace. When FILE1 or FILE2 (not both) is -, read standard input.\n\
+standard output. The default join field is the first, delimited by blanks.\
\n\
- -a FILENUM print unpairable lines coming from file FILENUM, where\n\
+"), stdout);
+ fputs (_("\
+\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\
-e EMPTY replace missing input fields with EMPTY\n\
"), stdout);
fputs (_("\
-i, --ignore-case ignore differences in case when comparing fields\n\
- -j FIELD equivalent to `-1 FIELD -2 FIELD'\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);
@@ -154,6 +217,14 @@ by whitespace. When FILE1 or FILE2 (not both) is -, read standard input.\n\
-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);
@@ -162,14 +233,19 @@ by whitespace. When FILE1 or FILE2 (not both) is -, read standard input.\n\
Unless -t CHAR is given, leading blanks separate fields and are ignored,\n\
else fields are separated by CHAR. Any FIELD is a field number counted\n\
from 1. FORMAT is one or more comma or blank separated specifications,\n\
-each being `FILENUM.FIELD' or `0'. Default FORMAT outputs the join field,\n\
+each being 'FILENUM.FIELD' or '0'. Default FORMAT outputs the join field,\n\
the remaining fields from FILE1, the remaining fields from FILE2, all\n\
-separated by CHAR.\n\
+separated by CHAR. If FORMAT is the keyword 'auto', then the first\n\
+line of each file determines the number of fields output for each line.\n\
\n\
Important: FILE1 and FILE2 must be sorted on the join fields.\n\
-E.g., use `sort -k 1b,1' if `join' has no options.\n\
+E.g., use \"sort -k 1b,1\" if 'join' has no options,\n\
+or use \"join -t ''\" if 'sort' has no options.\n\
+Note, comparisons honor the rules specified by 'LC_COLLATE'.\n\
+If the input is not sorted and some lines cannot be joined, a\n\
+warning message will be given.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -188,7 +264,7 @@ extract_field (struct line *line, char *field, size_t len)
++(line->nfields);
}
-/* Fill in the `fields' structure in LINE. */
+/* Fill in the 'fields' structure in LINE. */
static void
xfields (struct line *line)
@@ -199,108 +275,55 @@ xfields (struct line *line)
if (ptr == lim)
return;
- if (0 <= tab)
+ if (0 <= tab && tab != '\n')
{
char *sep;
for (; (sep = memchr (ptr, tab, lim - ptr)) != NULL; ptr = sep + 1)
- extract_field (line, ptr, sep - ptr);
+ extract_field (line, ptr, sep - ptr);
}
- else
+ else if (tab < 0)
{
/* Skip leading blanks before the first field. */
- while (isblank (to_uchar (*ptr)))
- if (++ptr == lim)
- return;
+ while (field_sep (*ptr))
+ if (++ptr == lim)
+ return;
do
- {
- char *sep;
- for (sep = ptr + 1; sep != lim && ! isblank (to_uchar (*sep)); sep++)
- continue;
- extract_field (line, ptr, sep - ptr);
- if (sep == lim)
- return;
- for (ptr = sep + 1; ptr != lim && isblank (to_uchar (*ptr)); ptr++)
- continue;
- }
+ {
+ char *sep;
+ for (sep = ptr + 1; sep != lim && ! field_sep (*sep); sep++)
+ continue;
+ extract_field (line, ptr, sep - ptr);
+ if (sep == lim)
+ return;
+ for (ptr = sep + 1; ptr != lim && field_sep (*ptr); ptr++)
+ continue;
+ }
while (ptr != lim);
}
extract_field (line, ptr, lim - ptr);
}
-/* Read a line from FP into LINE and split it into fields.
- Return true if successful. */
-
-static bool
-get_line (FILE *fp, struct line *line)
-{
- initbuffer (&line->buf);
-
- if (! readlinebuffer (&line->buf, fp))
- {
- if (ferror (fp))
- error (EXIT_FAILURE, errno, _("read error"));
- free (line->buf.buffer);
- line->buf.buffer = NULL;
- return false;
- }
-
- line->nfields_allocated = 0;
- line->nfields = 0;
- line->fields = NULL;
- xfields (line);
- return true;
-}
-
static void
freeline (struct line *line)
{
+ if (line == NULL)
+ return;
free (line->fields);
+ line->fields = NULL;
free (line->buf.buffer);
line->buf.buffer = NULL;
}
-static void
-initseq (struct seq *seq)
-{
- seq->count = 0;
- seq->alloc = 0;
- seq->lines = NULL;
-}
-
-/* Read a line from FP and add it to SEQ. Return true if successful. */
-
-static bool
-getseq (FILE *fp, struct seq *seq)
-{
- if (seq->count == seq->alloc)
- seq->lines = X2NREALLOC (seq->lines, &seq->alloc);
-
- if (get_line (fp, &seq->lines[seq->count]))
- {
- ++seq->count;
- return true;
- }
- return false;
-}
-
-static void
-delseq (struct seq *seq)
-{
- size_t i;
- for (i = 0; i < seq->count; i++)
- if (seq->lines[i].buf.buffer)
- freeline (&seq->lines[i]);
- free (seq->lines);
-}
-
/* Return <0 if the join field in LINE1 compares less than the one in LINE2;
>0 if it compares greater; 0 if it compares equal.
- Report an error and exit if the comparison fails. */
+ Report an error and exit if the comparison fails.
+ Use join fields JF_1 and JF_2 respectively. */
static int
-keycmp (struct line const *line1, struct line const *line2)
+keycmp (struct line const *line1, struct line const *line2,
+ size_t jf_1, size_t jf_2)
{
/* Start of field to compare in each file. */
char *beg1;
@@ -310,10 +333,10 @@ keycmp (struct line const *line1, struct line const *line2)
size_t len2; /* Length of fields to compare. */
int diff;
- if (join_field_1 < line1->nfields)
+ if (jf_1 < line1->nfields)
{
- beg1 = line1->fields[join_field_1].beg;
- len1 = line1->fields[join_field_1].len;
+ beg1 = line1->fields[jf_1].beg;
+ len1 = line1->fields[jf_1].len;
}
else
{
@@ -321,10 +344,10 @@ keycmp (struct line const *line1, struct line const *line2)
len1 = 0;
}
- if (join_field_2 < line2->nfields)
+ if (jf_2 < line2->nfields)
{
- beg2 = line2->fields[join_field_2].beg;
- len2 = line2->fields[join_field_2].len;
+ beg2 = line2->fields[jf_2].beg;
+ len2 = line2->fields[jf_2].len;
}
else
{
@@ -346,7 +369,7 @@ keycmp (struct line const *line1, struct line const *line2)
else
{
if (hard_LC_COLLATE)
- return xmemcoll (beg1, len1, beg2, len2);
+ return xmemcoll (beg1, len1, beg2, len2);
diff = memcmp (beg1, beg2, MIN (len1, len2));
}
@@ -355,8 +378,174 @@ keycmp (struct line const *line1, struct line const *line2)
return len1 < len2 ? -1 : len1 != len2;
}
+/* Check that successive input lines PREV and CURRENT from input file
+ WHATFILE are presented in order, unless the user may be relying on
+ the GNU extension that input lines may be out of order if no input
+ lines are unpairable.
+
+ If the user specified --nocheck-order, the check is not made.
+ If the user specified --check-order, the problem is fatal.
+ Otherwise (the default), the message is simply a warning.
+
+ A message is printed at most once per input file. */
+
+static void
+check_order (const struct line *prev,
+ const struct line *current,
+ int whatfile)
+{
+ if (check_input_order != CHECK_ORDER_DISABLED
+ && ((check_input_order == CHECK_ORDER_ENABLED) || seen_unpairable))
+ {
+ if (!issued_disorder_warning[whatfile-1])
+ {
+ size_t join_field = whatfile == 1 ? join_field_1 : join_field_2;
+ if (keycmp (prev, current, join_field, join_field) > 0)
+ {
+ /* Exclude any trailing newline. */
+ size_t len = current->buf.length;
+ if (0 < len && current->buf.buffer[len - 1] == '\n')
+ --len;
+
+ /* If the offending line is longer than INT_MAX, output
+ only the first INT_MAX bytes in this diagnostic. */
+ len = MIN (INT_MAX, len);
+
+ error ((check_input_order == CHECK_ORDER_ENABLED
+ ? EXIT_FAILURE : 0),
+ 0, _("%s:%"PRIuMAX": is not sorted: %.*s"),
+ g_names[whatfile - 1], line_no[whatfile - 1],
+ (int) len, current->buf.buffer);
+
+ /* If we get to here, the message was merely a warning.
+ Arrange to issue it only once per file. */
+ issued_disorder_warning[whatfile-1] = true;
+ }
+ }
+ }
+}
+
+static inline void
+reset_line (struct line *line)
+{
+ line->nfields = 0;
+}
+
+static struct line *
+init_linep (struct line **linep)
+{
+ struct line *line = xcalloc (1, sizeof *line);
+ *linep = line;
+ return line;
+}
+
+/* Read a line from FP into LINE and split it into fields.
+ Return true if successful. */
+
+static bool
+get_line (FILE *fp, struct line **linep, int which)
+{
+ struct line *line = *linep;
+
+ if (line == prevline[which - 1])
+ {
+ SWAPLINES (line, spareline[which - 1]);
+ *linep = line;
+ }
+
+ if (line)
+ reset_line (line);
+ else
+ line = init_linep (linep);
+
+ if (! readlinebuffer_delim (&line->buf, fp, eolchar))
+ {
+ if (ferror (fp))
+ error (EXIT_FAILURE, errno, _("read error"));
+ freeline (line);
+ return false;
+ }
+ ++line_no[which - 1];
+
+ xfields (line);
+
+ if (prevline[which - 1])
+ check_order (prevline[which - 1], line, which);
+
+ prevline[which - 1] = line;
+ return true;
+}
+
+static void
+free_spareline (void)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_CARDINALITY (spareline); i++)
+ {
+ if (spareline[i])
+ {
+ freeline (spareline[i]);
+ free (spareline[i]);
+ }
+ }
+}
+
+static void
+initseq (struct seq *seq)
+{
+ seq->count = 0;
+ seq->alloc = 0;
+ seq->lines = NULL;
+}
+
+/* Read a line from FP and add it to SEQ. Return true if successful. */
+
+static bool
+getseq (FILE *fp, struct seq *seq, int whichfile)
+{
+ if (seq->count == seq->alloc)
+ {
+ size_t i;
+ seq->lines = X2NREALLOC (seq->lines, &seq->alloc);
+ for (i = seq->count; i < seq->alloc; i++)
+ seq->lines[i] = NULL;
+ }
+
+ if (get_line (fp, &seq->lines[seq->count], whichfile))
+ {
+ ++seq->count;
+ return true;
+ }
+ return false;
+}
+
+/* Read a line from FP and add it to SEQ, as the first item if FIRST is
+ true, else as the next. */
+static bool
+advance_seq (FILE *fp, struct seq *seq, bool first, int whichfile)
+{
+ if (first)
+ seq->count = 0;
+
+ return getseq (fp, seq, whichfile);
+}
+
+static void
+delseq (struct seq *seq)
+{
+ size_t i;
+ for (i = 0; i < seq->alloc; i++)
+ {
+ freeline (seq->lines[i]);
+ free (seq->lines[i]);
+ }
+ free (seq->lines);
+}
+
+
/* Print field N of LINE if it exists and is nonempty, otherwise
- `empty_filler' if it is nonempty. */
+ 'empty_filler' if it is nonempty. */
static void
prfield (size_t n, struct line const *line)
@@ -367,14 +556,35 @@ prfield (size_t n, struct line const *line)
{
len = line->fields[n].len;
if (len)
- fwrite (line->fields[n].beg, 1, len, stdout);
+ fwrite (line->fields[n].beg, 1, len, stdout);
else if (empty_filler)
- fputs (empty_filler, stdout);
+ fputs (empty_filler, stdout);
}
else if (empty_filler)
fputs (empty_filler, stdout);
}
+/* Output all the fields in line, other than the join field. */
+
+static void
+prfields (struct line const *line, size_t join_field, size_t autocount)
+{
+ size_t i;
+ size_t nfields = autoformat ? autocount : line->nfields;
+ char output_separator = tab < 0 ? ' ' : tab;
+
+ for (i = 0; i < join_field && i < nfields; ++i)
+ {
+ putchar (output_separator);
+ prfield (i, line);
+ }
+ for (i = join_field + 1; i < nfields; ++i)
+ {
+ putchar (output_separator);
+ prfield (i, line);
+ }
+}
+
/* Print the join of LINE1 and LINE2. */
static void
@@ -382,6 +592,8 @@ prjoin (struct line const *line1, struct line const *line2)
{
const struct outlist *outlist;
char output_separator = tab < 0 ? ' ' : tab;
+ size_t field;
+ struct line const *line;
outlist = outlist_head.next;
if (outlist)
@@ -390,70 +602,54 @@ prjoin (struct line const *line1, struct line const *line2)
o = outlist;
while (1)
- {
- size_t field;
- struct line const *line;
-
- if (o->file == 0)
- {
- if (line1 == &uni_blank)
- {
- line = line2;
- field = join_field_2;
- }
- else
- {
- line = line1;
- field = join_field_1;
- }
- }
- else
- {
- line = (o->file == 1 ? line1 : line2);
- field = o->field;
- }
- prfield (field, line);
- o = o->next;
- if (o == NULL)
- break;
- putchar (output_separator);
- }
- putchar ('\n');
+ {
+ if (o->file == 0)
+ {
+ if (line1 == &uni_blank)
+ {
+ line = line2;
+ field = join_field_2;
+ }
+ else
+ {
+ line = line1;
+ field = join_field_1;
+ }
+ }
+ else
+ {
+ line = (o->file == 1 ? line1 : line2);
+ field = o->field;
+ }
+ prfield (field, line);
+ o = o->next;
+ if (o == NULL)
+ break;
+ putchar (output_separator);
+ }
+ putchar (eolchar);
}
else
{
- size_t i;
-
if (line1 == &uni_blank)
- {
- struct line const *t;
- t = line1;
- line1 = line2;
- line2 = t;
- }
- prfield (join_field_1, line1);
- for (i = 0; i < join_field_1 && i < line1->nfields; ++i)
- {
- putchar (output_separator);
- prfield (i, line1);
- }
- for (i = join_field_1 + 1; i < line1->nfields; ++i)
- {
- putchar (output_separator);
- prfield (i, line1);
- }
-
- for (i = 0; i < join_field_2 && i < line2->nfields; ++i)
- {
- putchar (output_separator);
- prfield (i, line2);
- }
- for (i = join_field_2 + 1; i < line2->nfields; ++i)
- {
- putchar (output_separator);
- prfield (i, line2);
- }
- putchar ('\n');
+ {
+ line = line2;
+ field = join_field_2;
+ }
+ else
+ {
+ line = line1;
+ field = join_field_1;
+ }
+
+ /* Output the join field. */
+ prfield (field, line);
+
+ /* Output other fields. */
+ prfields (line1, join_field_1, autocount_1);
+ prfields (line2, join_field_2, autocount_2);
+
+ putchar (eolchar);
}
}
@@ -463,121 +659,161 @@ static void
join (FILE *fp1, FILE *fp2)
{
struct seq seq1, seq2;
- struct line line;
int diff;
bool eof1, eof2;
+ fadvise (fp1, FADVISE_SEQUENTIAL);
+ fadvise (fp2, FADVISE_SEQUENTIAL);
+
/* Read the first line of each file. */
initseq (&seq1);
- getseq (fp1, &seq1);
+ getseq (fp1, &seq1, 1);
initseq (&seq2);
- getseq (fp2, &seq2);
+ getseq (fp2, &seq2, 2);
+
+ if (autoformat)
+ {
+ autocount_1 = seq1.count ? seq1.lines[0]->nfields : 0;
+ autocount_2 = seq2.count ? seq2.lines[0]->nfields : 0;
+ }
+
+ if (join_header_lines && (seq1.count || seq2.count))
+ {
+ 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] = NULL;
+ prevline[1] = NULL;
+ if (seq1.count)
+ advance_seq (fp1, &seq1, true, 1);
+ if (seq2.count)
+ advance_seq (fp2, &seq2, true, 2);
+ }
while (seq1.count && seq2.count)
{
size_t i;
- diff = keycmp (&seq1.lines[0], &seq2.lines[0]);
+ diff = keycmp (seq1.lines[0], seq2.lines[0],
+ join_field_1, join_field_2);
if (diff < 0)
- {
- if (print_unpairables_1)
- prjoin (&seq1.lines[0], &uni_blank);
- freeline (&seq1.lines[0]);
- seq1.count = 0;
- getseq (fp1, &seq1);
- continue;
- }
+ {
+ if (print_unpairables_1)
+ prjoin (seq1.lines[0], &uni_blank);
+ advance_seq (fp1, &seq1, true, 1);
+ seen_unpairable = true;
+ continue;
+ }
if (diff > 0)
- {
- if (print_unpairables_2)
- prjoin (&uni_blank, &seq2.lines[0]);
- freeline (&seq2.lines[0]);
- seq2.count = 0;
- getseq (fp2, &seq2);
- continue;
- }
+ {
+ if (print_unpairables_2)
+ prjoin (&uni_blank, seq2.lines[0]);
+ advance_seq (fp2, &seq2, true, 2);
+ seen_unpairable = true;
+ continue;
+ }
/* Keep reading lines from file1 as long as they continue to
match the current line from file2. */
eof1 = false;
do
- if (!getseq (fp1, &seq1))
- {
- eof1 = true;
- ++seq1.count;
- break;
- }
- while (!keycmp (&seq1.lines[seq1.count - 1], &seq2.lines[0]));
+ if (!advance_seq (fp1, &seq1, false, 1))
+ {
+ eof1 = true;
+ ++seq1.count;
+ break;
+ }
+ while (!keycmp (seq1.lines[seq1.count - 1], seq2.lines[0],
+ join_field_1, join_field_2));
/* Keep reading lines from file2 as long as they continue to
match the current line from file1. */
eof2 = false;
do
- if (!getseq (fp2, &seq2))
- {
- eof2 = true;
- ++seq2.count;
- break;
- }
- while (!keycmp (&seq1.lines[0], &seq2.lines[seq2.count - 1]));
+ if (!advance_seq (fp2, &seq2, false, 2))
+ {
+ eof2 = true;
+ ++seq2.count;
+ break;
+ }
+ while (!keycmp (seq1.lines[0], seq2.lines[seq2.count - 1],
+ join_field_1, join_field_2));
if (print_pairables)
- {
- for (i = 0; i < seq1.count - 1; ++i)
- {
- size_t j;
- for (j = 0; j < seq2.count - 1; ++j)
- prjoin (&seq1.lines[i], &seq2.lines[j]);
- }
- }
-
- for (i = 0; i < seq1.count - 1; ++i)
- freeline (&seq1.lines[i]);
+ {
+ for (i = 0; i < seq1.count - 1; ++i)
+ {
+ size_t j;
+ for (j = 0; j < seq2.count - 1; ++j)
+ prjoin (seq1.lines[i], seq2.lines[j]);
+ }
+ }
+
if (!eof1)
- {
- seq1.lines[0] = seq1.lines[seq1.count - 1];
- seq1.count = 1;
- }
+ {
+ SWAPLINES (seq1.lines[0], seq1.lines[seq1.count - 1]);
+ seq1.count = 1;
+ }
else
- seq1.count = 0;
+ seq1.count = 0;
- for (i = 0; i < seq2.count - 1; ++i)
- freeline (&seq2.lines[i]);
if (!eof2)
- {
- seq2.lines[0] = seq2.lines[seq2.count - 1];
- seq2.count = 1;
- }
+ {
+ SWAPLINES (seq2.lines[0], seq2.lines[seq2.count - 1]);
+ seq2.count = 1;
+ }
else
- seq2.count = 0;
+ seq2.count = 0;
}
- if (print_unpairables_1 && seq1.count)
+ /* If the user did not specify --nocheck-order, then we read the
+ 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 = NULL;
+ bool checktail = false;
+
+ if (check_input_order != CHECK_ORDER_DISABLED
+ && !(issued_disorder_warning[0] && issued_disorder_warning[1]))
+ checktail = true;
+
+ if ((print_unpairables_1 || checktail) && seq1.count)
{
- prjoin (&seq1.lines[0], &uni_blank);
- freeline (&seq1.lines[0]);
- while (get_line (fp1, &line))
- {
- prjoin (&line, &uni_blank);
- freeline (&line);
- }
+ if (print_unpairables_1)
+ prjoin (seq1.lines[0], &uni_blank);
+ if (seq2.count)
+ seen_unpairable = true;
+ while (get_line (fp1, &line, 1))
+ {
+ if (print_unpairables_1)
+ prjoin (line, &uni_blank);
+ if (issued_disorder_warning[0] && !print_unpairables_1)
+ break;
+ }
}
- if (print_unpairables_2 && seq2.count)
+ if ((print_unpairables_2 || checktail) && seq2.count)
{
- prjoin (&uni_blank, &seq2.lines[0]);
- freeline (&seq2.lines[0]);
- while (get_line (fp2, &line))
- {
- prjoin (&uni_blank, &line);
- freeline (&line);
- }
+ if (print_unpairables_2)
+ prjoin (&uni_blank, seq2.lines[0]);
+ if (seq1.count)
+ seen_unpairable = true;
+ while (get_line (fp2, &line, 2))
+ {
+ if (print_unpairables_2)
+ prjoin (&uni_blank, line);
+ if (issued_disorder_warning[1] && !print_unpairables_2)
+ break;
+ }
}
+ freeline (line);
+ free (line);
+
delseq (&seq1);
delseq (&seq2);
}
-/* Add a field spec for field FIELD of file FILE to `outlist'. */
+/* Add a field spec for field FIELD of file FILE to 'outlist'. */
static void
add_field (int file, size_t field)
@@ -634,9 +870,9 @@ decode_field_spec (const char *s, int *file_index, size_t *field_index)
case '0':
if (s[1])
{
- /* `0' must be all alone -- no `.FIELD'. */
- error (EXIT_FAILURE, 0, _("invalid field specifier: %s"), quote (s));
- }
+ /* '0' must be all alone -- no '.FIELD'. */
+ error (EXIT_FAILURE, 0, _("invalid field specifier: %s"), quote (s));
+ }
*file_index = 0;
*field_index = 0;
break;
@@ -644,25 +880,25 @@ decode_field_spec (const char *s, int *file_index, size_t *field_index)
case '1':
case '2':
if (s[1] != '.')
- error (EXIT_FAILURE, 0, _("invalid field specifier: %s"), quote (s));
+ error (EXIT_FAILURE, 0, _("invalid field specifier: %s"), quote (s));
*file_index = s[0] - '0';
*field_index = string_to_join_field (s + 2);
break;
default:
error (EXIT_FAILURE, 0,
- _("invalid file number in field spec: %s"), quote (s));
+ _("invalid file number in field spec: %s"), quote (s));
/* Tell gcc -W -Wall that we can't get beyond this point.
- This avoids a warning (otherwise legit) that the caller's copies
- of *file_index and *field_index might be used uninitialized. */
+ This avoids a warning (otherwise legit) that the caller's copies
+ of *file_index and *field_index might be used uninitialized. */
abort ();
break;
}
}
-/* Add the comma or blank separated field spec(s) in STR to `outlist'. */
+/* Add the comma or blank separated field spec(s) in STR to 'outlist'. */
static void
add_field_list (char *str)
@@ -695,7 +931,7 @@ set_join_field (size_t *var, size_t val)
unsigned long int var1 = *var + 1;
unsigned long int val1 = val + 1;
error (EXIT_FAILURE, 0, _("incompatible join fields %lu, %lu"),
- var1, val1);
+ var1, val1);
}
*var = val;
}
@@ -723,8 +959,8 @@ enum operand_status
static void
add_file_name (char *name, char *names[2],
- int operand_status[2], int joption_count[2], int *nfiles,
- int *prev_optc_status, int *optc_status)
+ int operand_status[2], int joption_count[2], int *nfiles,
+ int *prev_optc_status, int *optc_status)
{
int n = *nfiles;
@@ -733,30 +969,30 @@ add_file_name (char *name, char *names[2],
bool op0 = (operand_status[0] == MUST_BE_OPERAND);
char *arg = names[op0];
switch (operand_status[op0])
- {
- case MUST_BE_OPERAND:
- error (0, 0, _("extra operand %s"), quote (name));
- usage (EXIT_FAILURE);
-
- case MIGHT_BE_J1_ARG:
- joption_count[0]--;
- set_join_field (&join_field_1, string_to_join_field (arg));
- break;
-
- case MIGHT_BE_J2_ARG:
- joption_count[1]--;
- set_join_field (&join_field_2, string_to_join_field (arg));
- break;
-
- case MIGHT_BE_O_ARG:
- add_field_list (arg);
- break;
- }
+ {
+ case MUST_BE_OPERAND:
+ error (0, 0, _("extra operand %s"), quoteaf (name));
+ usage (EXIT_FAILURE);
+
+ case MIGHT_BE_J1_ARG:
+ joption_count[0]--;
+ set_join_field (&join_field_1, string_to_join_field (arg));
+ break;
+
+ case MIGHT_BE_J2_ARG:
+ joption_count[1]--;
+ set_join_field (&join_field_2, string_to_join_field (arg));
+ break;
+
+ case MIGHT_BE_O_ARG:
+ add_field_list (arg);
+ break;
+ }
if (!op0)
- {
- operand_status[0] = operand_status[1];
- names[0] = names[1];
- }
+ {
+ operand_status[0] = operand_status[1];
+ names[0] = names[1];
+ }
n = 1;
}
@@ -774,120 +1010,144 @@ main (int argc, char **argv)
int prev_optc_status = MUST_BE_OPERAND;
int operand_status[2];
int joption_count[2] = { 0, 0 };
- char *names[2];
FILE *fp1, *fp2;
int optc;
int nfiles = 0;
int i;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
hard_LC_COLLATE = hard_locale (LC_COLLATE);
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:",
- longopts, NULL))
- != -1)
+ while ((optc = getopt_long (argc, argv, "-a:e:i1:2:j:o:t:v:z",
+ longopts, NULL))
+ != -1)
{
optc_status = MUST_BE_OPERAND;
switch (optc)
- {
- case 'v':
- print_pairables = false;
- /* Fall through. */
-
- case 'a':
- {
- unsigned long int val;
- if (xstrtoul (optarg, NULL, 10, &val, "") != LONGINT_OK
- || (val != 1 && val != 2))
- error (EXIT_FAILURE, 0,
- _("invalid field number: %s"), quote (optarg));
- if (val == 1)
- print_unpairables_1 = true;
- else
- print_unpairables_2 = true;
- }
- break;
-
- case 'e':
- if (empty_filler && ! STREQ (empty_filler, optarg))
- error (EXIT_FAILURE, 0,
- _("conflicting empty-field replacement strings"));
- empty_filler = optarg;
- break;
-
- case 'i':
- ignore_case = true;
- break;
-
- case '1':
- set_join_field (&join_field_1, string_to_join_field (optarg));
- break;
-
- case '2':
- set_join_field (&join_field_2, string_to_join_field (optarg));
- break;
-
- case 'j':
- if ((optarg[0] == '1' || optarg[0] == '2') && !optarg[1]
- && optarg == argv[optind - 1] + 2)
- {
- /* The argument was either "-j1" or "-j2". */
- bool is_j2 = (optarg[0] == '2');
- joption_count[is_j2]++;
- optc_status = MIGHT_BE_J1_ARG + is_j2;
- }
- else
- {
- set_join_field (&join_field_1, string_to_join_field (optarg));
- set_join_field (&join_field_2, join_field_1);
- }
- break;
-
- case 'o':
- add_field_list (optarg);
- optc_status = MIGHT_BE_O_ARG;
- break;
-
- case 't':
- {
- unsigned char newtab = optarg[0];
- if (! newtab)
- error (EXIT_FAILURE, 0, _("empty tab"));
- if (optarg[1])
- {
- if (STREQ (optarg, "\\0"))
- newtab = '\0';
- else
- error (EXIT_FAILURE, 0, _("multi-character tab %s"),
- quote (optarg));
- }
- if (0 <= tab && tab != newtab)
- error (EXIT_FAILURE, 0, _("incompatible tabs"));
- tab = newtab;
- }
- break;
-
- case 1: /* Non-option argument. */
- add_file_name (optarg, names, operand_status, joption_count,
- &nfiles, &prev_optc_status, &optc_status);
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'v':
+ print_pairables = false;
+ /* Fall through. */
+
+ case 'a':
+ {
+ unsigned long int val;
+ if (xstrtoul (optarg, NULL, 10, &val, "") != LONGINT_OK
+ || (val != 1 && val != 2))
+ error (EXIT_FAILURE, 0,
+ _("invalid field number: %s"), quote (optarg));
+ if (val == 1)
+ print_unpairables_1 = true;
+ else
+ print_unpairables_2 = true;
+ }
+ break;
+
+ case 'e':
+ if (empty_filler && ! STREQ (empty_filler, optarg))
+ error (EXIT_FAILURE, 0,
+ _("conflicting empty-field replacement strings"));
+ empty_filler = optarg;
+ break;
+
+ case 'i':
+ ignore_case = true;
+ break;
+
+ case '1':
+ set_join_field (&join_field_1, string_to_join_field (optarg));
+ break;
+
+ case '2':
+ set_join_field (&join_field_2, string_to_join_field (optarg));
+ break;
+
+ case 'j':
+ if ((optarg[0] == '1' || optarg[0] == '2') && !optarg[1]
+ && optarg == argv[optind - 1] + 2)
+ {
+ /* The argument was either "-j1" or "-j2". */
+ bool is_j2 = (optarg[0] == '2');
+ joption_count[is_j2]++;
+ optc_status = MIGHT_BE_J1_ARG + is_j2;
+ }
+ else
+ {
+ set_join_field (&join_field_1, string_to_join_field (optarg));
+ set_join_field (&join_field_2, join_field_1);
+ }
+ break;
+
+ case 'o':
+ if (STREQ (optarg, "auto"))
+ autoformat = true;
+ else
+ {
+ add_field_list (optarg);
+ optc_status = MIGHT_BE_O_ARG;
+ }
+ break;
+
+ case 't':
+ {
+ unsigned char newtab = optarg[0];
+ if (! newtab)
+ newtab = '\n'; /* '' => process the whole line. */
+ else if (optarg[1])
+ {
+ if (STREQ (optarg, "\\0"))
+ newtab = '\0';
+ else
+ error (EXIT_FAILURE, 0, _("multi-character tab %s"),
+ quote (optarg));
+ }
+ if (0 <= tab && tab != newtab)
+ error (EXIT_FAILURE, 0, _("incompatible tabs"));
+ tab = newtab;
+ }
+ break;
+
+ case 'z':
+ eolchar = 0;
+ break;
+
+ case NOCHECK_ORDER_OPTION:
+ check_input_order = CHECK_ORDER_DISABLED;
+ break;
+
+ case CHECK_ORDER_OPTION:
+ check_input_order = CHECK_ORDER_ENABLED;
+ break;
+
+ case 1: /* Non-option argument. */
+ add_file_name (optarg, g_names, operand_status, joption_count,
+ &nfiles, &prev_optc_status, &optc_status);
+ break;
+
+ case HEADER_LINE_OPTION:
+ join_header_lines = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
prev_optc_status = optc_status;
}
@@ -895,15 +1155,15 @@ main (int argc, char **argv)
/* Process any operands after "--". */
prev_optc_status = MUST_BE_OPERAND;
while (optind < argc)
- add_file_name (argv[optind++], names, operand_status, joption_count,
- &nfiles, &prev_optc_status, &optc_status);
+ add_file_name (argv[optind++], g_names, operand_status, joption_count,
+ &nfiles, &prev_optc_status, &optc_status);
if (nfiles != 2)
{
if (nfiles == 0)
- error (0, 0, _("missing operand"));
+ error (0, 0, _("missing operand"));
else
- error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
+ error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
usage (EXIT_FAILURE);
}
@@ -912,8 +1172,8 @@ main (int argc, char **argv)
for (i = 0; i < 2; i++)
if (joption_count[i] != 0)
{
- set_join_field (&join_field_1, i);
- set_join_field (&join_field_2, i);
+ set_join_field (&join_field_1, i);
+ set_join_field (&join_field_2, i);
}
if (join_field_1 == SIZE_MAX)
@@ -921,20 +1181,23 @@ main (int argc, char **argv)
if (join_field_2 == SIZE_MAX)
join_field_2 = 0;
- fp1 = STREQ (names[0], "-") ? stdin : fopen (names[0], "r");
+ fp1 = STREQ (g_names[0], "-") ? stdin : fopen (g_names[0], "r");
if (!fp1)
- error (EXIT_FAILURE, errno, "%s", names[0]);
- fp2 = STREQ (names[1], "-") ? stdin : fopen (names[1], "r");
+ error (EXIT_FAILURE, errno, "%s", quotef (g_names[0]));
+ fp2 = STREQ (g_names[1], "-") ? stdin : fopen (g_names[1], "r");
if (!fp2)
- error (EXIT_FAILURE, errno, "%s", names[1]);
+ error (EXIT_FAILURE, errno, "%s", quotef (g_names[1]));
if (fp1 == fp2)
error (EXIT_FAILURE, errno, _("both files cannot be standard input"));
join (fp1, fp2);
if (fclose (fp1) != 0)
- error (EXIT_FAILURE, errno, "%s", names[0]);
+ error (EXIT_FAILURE, errno, "%s", quotef (g_names[0]));
if (fclose (fp2) != 0)
- error (EXIT_FAILURE, errno, "%s", names[1]);
+ error (EXIT_FAILURE, errno, "%s", quotef (g_names[1]));
- exit (EXIT_SUCCESS);
+ if (issued_disorder_warning[0] || issued_disorder_warning[1])
+ return EXIT_FAILURE;
+ else
+ return EXIT_SUCCESS;
}
diff --git a/src/kill.c b/src/kill.c
index c59025f..cb7798c 100644
--- a/src/kill.c
+++ b/src/kill.c
@@ -1,10 +1,10 @@
/* kill -- send a signal to a process
- Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 2002-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Paul Eggert. */
@@ -23,25 +22,17 @@
#include <sys/types.h>
#include <signal.h>
-#if HAVE_SYS_WAIT_H
-# include <sys/wait.h>
-#endif
-#ifndef WIFSIGNALED
-# define WIFSIGNALED(s) (((s) & 0xFFFF) - 1 < (unsigned int) 0xFF)
-#endif
-#ifndef WTERMSIG
-# define WTERMSIG(s) ((s) & 0x7F)
-#endif
-
#include "system.h"
#include "error.h"
#include "sig2str.h"
+#include "operand2sig.h"
+#include "quote.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "kill"
-#define AUTHORS "Paul Eggert"
-
+#define AUTHORS proper_name ("Paul Eggert")
+
#if ! (HAVE_DECL_STRSIGNAL || defined strsignal)
# if ! (HAVE_DECL_SYS_SIGLIST || defined sys_siglist)
# if HAVE_DECL__SYS_SIGLIST || defined _sys_siglist
@@ -52,22 +43,19 @@
# endif
# if HAVE_DECL_SYS_SIGLIST || defined sys_siglist
# define strsignal(signum) (0 <= (signum) && (signum) <= SIGNUM_BOUND \
- ? sys_siglist[signum] \
- : 0)
+ ? sys_siglist[signum] \
+ : 0)
# endif
# ifndef strsignal
# define strsignal(signum) 0
# endif
#endif
-
-/* The name this program was run with, for error messages. */
-char *program_name;
static char const short_options[] =
"0::1::2::3::4::5::6::7::8::9::"
- "A::B::C::D::E::F::G::H::I::J::K::L::M::"
+ "A::B::C::D::E::F::G::H::I::J::K::M::"
"N::O::P::Q::R::S::T::U::V::W::X::Y::Z::"
- "ln:s:t";
+ "Lln:s:t";
static struct option const long_options[] =
{
@@ -83,8 +71,7 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
@@ -92,14 +79,13 @@ Usage: %s [-s SIGNAL | -SIGNAL] PID...\n\
or: %s -l [SIGNAL]...\n\
or: %s -t [SIGNAL]...\n\
"),
- program_name, program_name, program_name);
+ program_name, program_name, program_name);
fputs (_("\
Send signals to processes, or list signals.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
+
+ emit_mandatory_arg_note ();
+
fputs (_("\
-s, --signal=SIGNAL, -SIGNAL\n\
specify the name or number of the signal to be sent\n\
@@ -109,73 +95,27 @@ Mandatory arguments to long options are mandatory for short options too.\n\
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\n\
-SIGNAL may be a signal name like `HUP', or a signal number like `1',\n\
-or an exit status of a process terminated by a signal.\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\
PID is an integer; if negative it identifies a process group.\n\
"), stdout);
printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
-
-/* Convert OPERAND to a signal number with printable representation SIGNAME.
- Return the signal number, or -1 if unsuccessful. */
-static int
-operand2sig (char const *operand, char *signame)
-{
- int signum;
-
- if (ISDIGIT (*operand))
- {
- char *endp;
- long int l = (errno = 0, strtol (operand, &endp, 10));
- int i = l;
- signum = (operand == endp || *endp || errno || i != l ? -1
- : WIFSIGNALED (i) ? WTERMSIG (i)
- : i);
- }
- else
- {
- /* 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++)
- if (strchr ("abcdefghijklmnopqrstuvwxyz", *p))
- *p += 'A' - 'a';
-
- /* Look for the signal name, possibly prefixed by "SIG",
- and possibly lowercased. */
- if (! (str2sig (upcased, &signum) == 0
- || (upcased[0] == 'S' && upcased[1] == 'I' && upcased[2] == 'G'
- && str2sig (upcased + 3, &signum) == 0)))
- signum = -1;
-
- free (upcased);
- }
-
- if (signum < 0 || sig2str (signum, signame) != 0)
- {
- error (0, 0, _("%s: invalid signal"), operand);
- return -1;
- }
-
- return signum;
-}
-
-/* Print a row of `kill -t' output. NUM_WIDTH is the maximum signal
+/* Print a row of 'kill -t' output. NUM_WIDTH is the maximum signal
number width, and SIGNUM is the signal number to print. The
maximum name width is NAME_WIDTH, and SIGNAME is the name to print. */
static void
-print_table_row (unsigned int num_width, int signum,
- unsigned int name_width, char const *signame)
+print_table_row (int num_width, int signum,
+ int name_width, char const *signame)
{
char const *description = strsignal (signum);
printf ("%*d %-*s %s\n", num_width, signum, name_width, signame,
- description ? description : "?");
+ description ? description : "?");
}
/* Print a list of signal names. If TABLE, print a table.
@@ -196,56 +136,56 @@ list_signals (bool table, char *const *argv)
/* Compute the maximum width of a signal number. */
unsigned int num_width = 1;
for (signum = 1; signum <= SIGNUM_BOUND / 10; signum *= 10)
- num_width++;
+ num_width++;
/* Compute the maximum width of a signal name. */
for (signum = 1; signum <= SIGNUM_BOUND; signum++)
- if (sig2str (signum, signame) == 0)
- {
- size_t len = strlen (signame);
- if (name_width < len)
- name_width = len;
- }
+ if (sig2str (signum, signame) == 0)
+ {
+ size_t len = strlen (signame);
+ if (name_width < len)
+ name_width = len;
+ }
if (argv)
- for (; *argv; argv++)
- {
- signum = operand2sig (*argv, signame);
- if (signum < 0)
- status = EXIT_FAILURE;
- else
- print_table_row (num_width, signum, name_width, signame);
- }
+ for (; *argv; argv++)
+ {
+ signum = operand2sig (*argv, signame);
+ if (signum < 0)
+ status = EXIT_FAILURE;
+ else
+ print_table_row (num_width, signum, name_width, signame);
+ }
else
- for (signum = 1; signum <= SIGNUM_BOUND; signum++)
- if (sig2str (signum, signame) == 0)
- print_table_row (num_width, signum, name_width, signame);
+ for (signum = 1; signum <= SIGNUM_BOUND; signum++)
+ if (sig2str (signum, signame) == 0)
+ print_table_row (num_width, signum, name_width, signame);
}
else
{
if (argv)
- for (; *argv; argv++)
- {
- signum = operand2sig (*argv, signame);
- if (signum < 0)
- status = EXIT_FAILURE;
- else
- {
- if (ISDIGIT (**argv))
- puts (signame);
- else
- printf ("%d\n", signum);
- }
- }
+ for (; *argv; argv++)
+ {
+ signum = operand2sig (*argv, signame);
+ if (signum < 0)
+ status = EXIT_FAILURE;
+ else
+ {
+ if (ISDIGIT (**argv))
+ puts (signame);
+ else
+ printf ("%d\n", signum);
+ }
+ }
else
- for (signum = 1; signum <= SIGNUM_BOUND; signum++)
- if (sig2str (signum, signame) == 0)
- puts (signame);
+ for (signum = 1; signum <= SIGNUM_BOUND; signum++)
+ if (sig2str (signum, signame) == 0)
+ puts (signame);
}
return status;
}
-
+
/* Send signal SIGNUM to all the processes or process groups specified
by ARGV. Return a suitable exit status. */
@@ -262,21 +202,21 @@ send_signals (int signum, char *const *argv)
pid_t pid = n;
if (errno == ERANGE || pid != n || arg == endp || *endp)
- {
- error (0, 0, _("%s: invalid process id"), arg);
- status = EXIT_FAILURE;
- }
+ {
+ error (0, 0, _("%s: invalid process id"), quote (arg));
+ status = EXIT_FAILURE;
+ }
else if (kill (pid, signum) != 0)
- {
- error (0, errno, "%s", arg);
- status = EXIT_FAILURE;
- }
+ {
+ error (0, errno, "%s", quote (arg));
+ status = EXIT_FAILURE;
+ }
}
while ((arg = *++argv));
return status;
}
-
+
int
main (int argc, char **argv)
{
@@ -287,7 +227,7 @@ main (int argc, char **argv)
char signame[SIG2STR_MAX];
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -295,63 +235,64 @@ main (int argc, char **argv)
atexit (close_stdout);
while ((optc = getopt_long (argc, argv, short_options, long_options, NULL))
- != -1)
+ != -1)
switch (optc)
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
- if (optind != 2)
- {
- /* This option is actually a process-id. */
- optind--;
- goto no_more_options;
- }
- /* Fall through. */
+ if (optind != 2)
+ {
+ /* This option is actually a process-id. */
+ optind--;
+ goto no_more_options;
+ }
+ /* Fall through. */
case 'A': case 'B': case 'C': case 'D': case 'E':
case 'F': case 'G': case 'H': case 'I': case 'J':
- case 'K': case 'L': case 'M': case 'N': case 'O':
+ case 'K': /*case 'L':*/ case 'M': case 'N': case 'O':
case 'P': case 'Q': case 'R': case 'S': case 'T':
case 'U': case 'V': case 'W': case 'X': case 'Y':
case 'Z':
- if (! optarg)
- optarg = argv[optind - 1] + strlen (argv[optind - 1]);
- if (optarg != argv[optind - 1] + 2)
- {
- error (0, 0, _("invalid option -- %c"), optc);
- usage (EXIT_FAILURE);
- }
- optarg--;
- /* Fall through. */
+ if (! optarg)
+ optarg = argv[optind - 1] + strlen (argv[optind - 1]);
+ if (optarg != argv[optind - 1] + 2)
+ {
+ error (0, 0, _("invalid option -- %c"), optc);
+ usage (EXIT_FAILURE);
+ }
+ optarg--;
+ /* Fall through. */
case 'n': /* -n is not documented, but is for Bash compatibility. */
case 's':
- if (0 <= signum)
- {
- error (0, 0, _("%s: multiple signals specified"), optarg);
- usage (EXIT_FAILURE);
- }
- signum = operand2sig (optarg, signame);
- if (signum < 0)
- usage (EXIT_FAILURE);
- break;
-
+ if (0 <= signum)
+ {
+ error (0, 0, _("%s: multiple signals specified"), quote (optarg));
+ usage (EXIT_FAILURE);
+ }
+ signum = operand2sig (optarg, signame);
+ if (signum < 0)
+ usage (EXIT_FAILURE);
+ break;
+
+ case 'L': /* -L is not documented, but is for procps compatibility. */
case 't':
- table = true;
- /* Fall through. */
+ table = true;
+ /* Fall through. */
case 'l':
- if (list)
- {
- error (0, 0, _("multiple -l or -t options specified"));
- usage (EXIT_FAILURE);
- }
- list = true;
- break;
+ if (list)
+ {
+ error (0, 0, _("multiple -l or -t options specified"));
+ usage (EXIT_FAILURE);
+ }
+ list = true;
+ break;
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
- usage (EXIT_FAILURE);
+ usage (EXIT_FAILURE);
}
- no_more_options:;
+ no_more_options:
if (signum < 0)
signum = SIGTERM;
@@ -368,6 +309,6 @@ main (int argc, char **argv)
}
return (list
- ? list_signals (table, optind < argc ? argv + optind : NULL)
- : send_signals (signum, argv + optind));
+ ? list_signals (table, optind < argc ? argv + optind : NULL)
+ : send_signals (signum, argv + optind));
}
diff --git a/src/libstdbuf.c b/src/libstdbuf.c
new file mode 100644
index 0000000..885377a
--- /dev/null
+++ b/src/libstdbuf.c
@@ -0,0 +1,141 @@
+/* libstdbuf -- a shared lib to preload to setup stdio buffering for a command
+ Copyright (C) 2009-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Written by Pádraig Brady. LD_PRELOAD idea from Brian Dessent. */
+
+#include <config.h>
+#include <stdio.h>
+#include "system.h"
+
+/* Note currently for glibc (2.3.5) the following call does not change
+ the buffer size, and more problematically does not give any indication
+ that the new size request was ignored:
+
+ setvbuf (stdout, (char*)NULL, _IOFBF, 8192);
+
+ The ISO C99 standard section 7.19.5.6 on the setvbuf function says:
+
+ ... If buf is not a null pointer, the array it points to _may_ be used
+ instead of a buffer allocated by the setvbuf function and the argument
+ size specifies the size of the array; otherwise, size _may_ determine
+ the size of a buffer allocated by the setvbuf function. ...
+
+ Obviously some interpret the above to mean setvbuf(....,size)
+ is only a hint from the application which I don't agree with.
+
+ FreeBSD's libc seems more sensible in this regard. From the man page:
+
+ The size argument may be given as zero to obtain deferred optimal-size
+ buffer allocation as usual. If it is not zero, then except for
+ unbuffered files, the buf argument should point to a buffer at least size
+ bytes long; this buffer will be used instead of the current buffer. (If
+ the size argument is not zero but buf is NULL, a buffer of the given size
+ will be allocated immediately, and released on close. This is an extension
+ to ANSI C; portable code should use a size of 0 with any NULL buffer.)
+ --------------------
+ Another issue is that on glibc-2.7 the following doesn't buffer
+ the first write if it's greater than 1 byte.
+
+ setvbuf(stdout,buf,_IOFBF,127);
+
+ Now the POSIX standard says that "allocating a buffer of size bytes does
+ not necessarily imply that all of size bytes are used for the buffer area".
+ However I think it's just a buggy implementation due to the various
+ inconsistencies with write sizes and subsequent writes. */
+
+static const char *
+fileno_to_name (const int fd)
+{
+ const char *ret = NULL;
+
+ switch (fd)
+ {
+ case 0:
+ ret = "stdin";
+ break;
+ case 1:
+ ret = "stdout";
+ break;
+ case 2:
+ ret = "stderr";
+ break;
+ default:
+ ret = "unknown";
+ break;
+ }
+
+ return ret;
+}
+
+static void
+apply_mode (FILE *stream, const char *mode)
+{
+ char *buf = NULL;
+ int setvbuf_mode;
+ size_t size = 0;
+
+ if (*mode == '0')
+ setvbuf_mode = _IONBF;
+ else if (*mode == 'L')
+ setvbuf_mode = _IOLBF; /* FIXME: should we allow 1ML */
+ else
+ {
+ setvbuf_mode = _IOFBF;
+ verify (SIZE_MAX <= ULONG_MAX);
+ size = strtoul (mode, NULL, 10);
+ if (size > 0)
+ {
+ if (!(buf = malloc (size))) /* will be freed by fclose() */
+ {
+ /* We could defer the allocation to libc, however since
+ glibc currently ignores the combination of NULL buffer
+ with non zero size, we'll fail here. */
+ fprintf (stderr,
+ _("failed to allocate a %" PRIuMAX
+ " byte stdio buffer\n"), (uintmax_t) size);
+ return;
+ }
+ }
+ else
+ {
+ fprintf (stderr, _("invalid buffering mode %s for %s\n"),
+ mode, fileno_to_name (fileno (stream)));
+ return;
+ }
+ }
+
+ if (setvbuf (stream, buf, setvbuf_mode, size) != 0)
+ {
+ fprintf (stderr, _("could not set buffering of %s to mode %s\n"),
+ fileno_to_name (fileno (stream)), mode);
+ free (buf);
+ }
+}
+
+/* Use __attribute to avoid elision of __attribute__ on SUNPRO_C etc. */
+static void __attribute ((constructor))
+stdbuf (void)
+{
+ char *e_mode = getenv ("_STDBUF_E");
+ char *i_mode = getenv ("_STDBUF_I");
+ char *o_mode = getenv ("_STDBUF_O");
+ if (e_mode) /* Do first so can write errors to stderr */
+ apply_mode (stderr, e_mode);
+ if (i_mode)
+ apply_mode (stdin, i_mode);
+ if (o_mode)
+ apply_mode (stdout, o_mode);
+}
diff --git a/src/link.c b/src/link.c
index 277cf23..8f19a60 100644
--- a/src/link.c
+++ b/src/link.c
@@ -1,10 +1,10 @@
/* link utility for GNU.
- Copyright (C) 2001, 2002, 2004 Free Software Foundation, Inc.
+ Copyright (C) 2001-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Michael Stone */
@@ -31,20 +30,16 @@
#include "long-options.h"
#include "quote.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "link"
-#define AUTHORS "Michael Stone"
-
-/* Name this program was run with. */
-char *program_name;
+#define AUTHORS proper_name ("Michael Stone")
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
@@ -52,10 +47,10 @@ Usage: %s FILE1 FILE2\n\
or: %s OPTION\n"), program_name, program_name);
fputs (_("Call the link function to create a link named FILE2\
to an existing FILE1.\n\n"),
- stdout);
+ stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -64,24 +59,24 @@ int
main (int argc, char **argv)
{
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
+ parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, Version,
+ usage, AUTHORS, (char const *) NULL);
if (getopt_long (argc, argv, "", NULL, NULL) != -1)
usage (EXIT_FAILURE);
if (argc < optind + 2)
{
if (argc < optind + 1)
- error (0, 0, _("missing operand"));
+ error (0, 0, _("missing operand"));
else
- error (0, 0, _("missing operand after %s"), quote (argv[optind]));
+ error (0, 0, _("missing operand after %s"), quote (argv[optind]));
usage (EXIT_FAILURE);
}
@@ -93,7 +88,7 @@ main (int argc, char **argv)
if (link (argv[optind], argv[optind + 1]) != 0)
error (EXIT_FAILURE, errno, _("cannot create link %s to %s"),
- quote_n (0, argv[optind + 1]), quote_n (1, argv[optind]));
+ quoteaf_n (0, argv[optind + 1]), quoteaf_n (1, argv[optind]));
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/ln.c b/src/ln.c
index fae3708..dda5ba2 100644
--- a/src/ln.c
+++ b/src/ln.c
@@ -1,10 +1,10 @@
-/* `ln' program to create links between files.
- Copyright (C) 1986, 1989-1991, 1995-2006 Free Software Foundation, Inc.
+/* 'ln' program to create links between files.
+ Copyright (C) 1986-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,9 +12,8 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
-
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
/* Written by Mike Parker and David MacKenzie. */
#include <config.h>
@@ -23,38 +22,23 @@
#include <getopt.h>
#include "system.h"
-#include "same.h"
#include "backupfile.h"
#include "error.h"
#include "filenamecat.h"
-#include "quote.h"
+#include "file-set.h"
+#include "hash.h"
+#include "hash-triple.h"
+#include "relpath.h"
+#include "same.h"
#include "yesno.h"
+#include "canonicalize.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "ln"
-#define AUTHORS "Mike Parker", "David MacKenzie"
-
-#ifndef ENABLE_HARD_LINK_TO_SYMLINK_WARNING
-# define ENABLE_HARD_LINK_TO_SYMLINK_WARNING 0
-#endif
-
-/* In being careful not even to try to make hard links to directories,
- we have to know whether link(2) follows symlinks. If it does, then
- we have to *stat* the `source' to see if the resulting link would be
- to a directory. Otherwise, we have to use *lstat* so that we allow
- users to make hard links to symlinks-that-point-to-directories. */
-
-#if LINK_FOLLOWS_SYMLINKS
-# define STAT_LIKE_LINK(File, Stat_buf) \
- stat (File, Stat_buf)
-#else
-# define STAT_LIKE_LINK(File, Stat_buf) \
- lstat (File, Stat_buf)
-#endif
-
-/* The name by which the program was run, for error messages. */
-char *program_name;
+#define AUTHORS \
+ proper_name ("Mike Parker"), \
+ proper_name ("David MacKenzie")
/* FIXME: document */
static enum backup_type backup_type;
@@ -62,6 +46,12 @@ static enum backup_type backup_type;
/* If true, make symbolic links; otherwise, make hard links. */
static bool symbolic_link;
+/* If true, make symbolic links relative */
+static bool relative;
+
+/* If true, hard links are logical rather than physical. */
+static bool logical = !!LINK_FOLLOWS_SYMLINKS;
+
/* If true, ask the user before removing existing files. */
static bool interactive;
@@ -74,15 +64,24 @@ static bool verbose;
/* If true, allow the superuser to *attempt* to make hard links
to directories. However, it appears that this option is not useful
in practice, since even the superuser is prohibited from hard-linking
- directories on most (all?) existing systems. */
+ directories on most existing systems (Solaris being an exception). */
static bool hard_dir_link;
/* If nonzero, and the specified destination is a symbolic link to a
directory, treat it just as if it were a directory. Otherwise, the
- command `ln --force --no-dereference file symlink-to-dir' deletes
+ command 'ln --force --no-dereference file symlink-to-dir' deletes
symlink-to-dir before creating the new link. */
static bool dereference_dest_dir_symlinks = true;
+/* This is a set of destination name/inode/dev triples for hard links
+ created by ln. Use this data structure to avoid data loss via a
+ sequence of commands like this:
+ rm -rf a b c; mkdir a b c; touch a/f b/f; ln -f a/f b/f c && rm -r a b */
+static Hash_table *dest_set;
+
+/* Initial size of the dest_set hash table. */
+enum { DEST_INFO_INITIAL_CAPACITY = 61 };
+
static struct option const long_options[] =
{
{"backup", optional_argument, NULL, 'b'},
@@ -93,6 +92,9 @@ static struct option const long_options[] =
{"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},
@@ -100,8 +102,18 @@ static struct option const long_options[] =
{NULL, 0, NULL, 0}
};
+/* Return true when the passed ERR implies
+ that a file does not or could not exist. */
+
+static bool
+errno_nonexisting (int err)
+{
+ return err == ENOENT || err == ENAMETOOLONG || err == ENOTDIR || err == ELOOP;
+}
+
+
/* FILE is the last operand of this command. Return true if FILE is a
- directory. But report an error there is a problem accessing FILE,
+ directory. But report an error if there is a problem accessing FILE,
or if FILE does not exist but would have to refer to an existing
directory if it referred to anything at all. */
@@ -116,13 +128,47 @@ target_directory_operand (char const *file)
(dereference_dest_dir_symlinks ? stat (file, &st) : lstat (file, &st));
int err = (stat_result == 0 ? 0 : errno);
bool is_a_dir = !err && S_ISDIR (st.st_mode);
- if (err && err != ENOENT)
- error (EXIT_FAILURE, err, _("accessing %s"), quote (file));
+ if (err && ! errno_nonexisting (errno))
+ error (EXIT_FAILURE, err, _("failed to access %s"), quoteaf (file));
if (is_a_dir < looks_like_a_dir)
- error (EXIT_FAILURE, err, _("target %s is not a directory"), quote (file));
+ error (EXIT_FAILURE, err, _("target %s is not a directory"),
+ quoteaf (file));
return is_a_dir;
}
+/* Return FROM represented as relative to the dir of TARGET.
+ The result is malloced. */
+
+static char *
+convert_abs_rel (const char *from, const char *target)
+{
+ /* Get dirname to generate paths relative to. We don't resolve
+ the full TARGET as the last component could be an existing symlink. */
+ char *targetdir = dir_name (target);
+
+ char *realdest = canonicalize_filename_mode (targetdir, CAN_MISSING);
+ char *realfrom = canonicalize_filename_mode (from, CAN_MISSING);
+
+ char *relative_from = NULL;
+ if (realdest && realfrom)
+ {
+ /* Write to a PATH_MAX buffer. */
+ relative_from = xmalloc (PATH_MAX);
+
+ if (!relpath (realfrom, realdest, relative_from, PATH_MAX))
+ {
+ free (relative_from);
+ relative_from = NULL;
+ }
+ }
+
+ free (targetdir);
+ free (realdest);
+ free (realfrom);
+
+ return relative_from ? relative_from : xstrdup (from);
+}
+
/* Make a link DEST to the (usually) existing file SOURCE.
Symbolic links to nonexistent files are allowed.
Return true if successful. */
@@ -133,50 +179,57 @@ do_link (const char *source, const char *dest)
struct stat source_stats;
struct stat dest_stats;
char *dest_backup = NULL;
+ char *rel_source = NULL;
bool dest_lstat_ok = false;
bool source_is_dir = false;
bool ok;
- /* Use stat here instead of lstat.
- On SVR4, link does not follow symlinks, so this check disallows
- making hard links to symlinks that point to directories. Big deal.
- On other systems, link follows symlinks, so this check is right. */
if (!symbolic_link)
{
- if (STAT_LIKE_LINK (source, &source_stats) != 0)
- {
- error (0, errno, _("accessing %s"), quote (source));
- return false;
- }
-
- if (ENABLE_HARD_LINK_TO_SYMLINK_WARNING
- && S_ISLNK (source_stats.st_mode))
- {
- error (0, 0, _("%s: warning: making a hard link to a symbolic link\
- is not portable"),
- quote (source));
- }
+ /* Which stat to use depends on whether linkat will follow the
+ symlink. We can't use the shorter
+ (logical?stat:lstat) (source, &source_stats)
+ since stat might be a function-like macro. */
+ if ((logical ? stat (source, &source_stats)
+ : lstat (source, &source_stats))
+ != 0)
+ {
+ error (0, errno, _("failed to access %s"), quoteaf (source));
+ return false;
+ }
if (S_ISDIR (source_stats.st_mode))
- {
- source_is_dir = true;
- if (! hard_dir_link)
- {
- error (0, 0, _("%s: hard link not allowed for directory"),
- quote (source));
- return false;
- }
- }
+ {
+ source_is_dir = true;
+ if (! hard_dir_link)
+ {
+ error (0, 0, _("%s: hard link not allowed for directory"),
+ quotef (source));
+ return false;
+ }
+ }
}
if (remove_existing_files || interactive || backup_type != no_backups)
{
dest_lstat_ok = (lstat (dest, &dest_stats) == 0);
if (!dest_lstat_ok && errno != ENOENT)
- {
- error (0, errno, _("accessing %s"), quote (dest));
- return false;
- }
+ {
+ error (0, errno, _("failed to access %s"), quoteaf (dest));
+ return false;
+ }
+ }
+
+ /* If the current target was created as a hard link to another
+ source file, then refuse to unlink it. */
+ if (dest_lstat_ok
+ && dest_set != NULL
+ && seen_file (dest_set, dest, &dest_stats))
+ {
+ error (0, 0,
+ _("will not overwrite just-created %s with %s"),
+ quoteaf_n (0, dest), quoteaf_n (1, source));
+ return false;
}
/* If --force (-f) has been specified without --backup, then before
@@ -186,77 +239,84 @@ do_link (const char *source, const char *dest)
anything and fail right here. */
if ((remove_existing_files
/* Ensure that "ln --backup f f" fails here, with the
- "... same file" diagnostic, below. Otherwise, subsequent
- code would give a misleading "file not found" diagnostic.
- This case is different than the others handled here, since
- the command in question doesn't use --force. */
+ "... same file" diagnostic, below. Otherwise, subsequent
+ code would give a misleading "file not found" diagnostic.
+ This case is different than the others handled here, since
+ the command in question doesn't use --force. */
|| (!symbolic_link && backup_type != no_backups))
&& dest_lstat_ok
- /* Allow `ln -sf --backup k k' to succeed in creating the
- self-referential symlink, but don't allow the hard-linking
- equivalent: `ln -f k k' (with or without --backup) to get
- beyond this point, because the error message you'd get is
- misleading. */
+ /* Allow 'ln -sf --backup k k' to succeed in creating the
+ self-referential symlink, but don't allow the hard-linking
+ equivalent: 'ln -f k k' (with or without --backup) to get
+ beyond this point, because the error message you'd get is
+ misleading. */
&& (backup_type == no_backups || !symbolic_link)
&& (!symbolic_link || stat (source, &source_stats) == 0)
&& SAME_INODE (source_stats, dest_stats)
/* The following detects whether removing DEST will also remove
- SOURCE. If the file has only one link then both are surely
- the same link. Otherwise check whether they point to the same
- name in the same directory. */
+ SOURCE. If the file has only one link then both are surely
+ the same link. Otherwise check whether they point to the same
+ name in the same directory. */
&& (source_stats.st_nlink == 1 || same_name (source, dest)))
{
error (0, 0, _("%s and %s are the same file"),
- quote_n (0, source), quote_n (1, dest));
+ quoteaf_n (0, source), quoteaf_n (1, dest));
return false;
}
if (dest_lstat_ok)
{
if (S_ISDIR (dest_stats.st_mode))
- {
- error (0, 0, _("%s: cannot overwrite directory"), quote (dest));
- return false;
- }
+ {
+ error (0, 0, _("%s: cannot overwrite directory"), quotef (dest));
+ return false;
+ }
if (interactive)
- {
- fprintf (stderr, _("%s: replace %s? "), program_name, quote (dest));
- if (!yesno ())
- return true;
- remove_existing_files = true;
- }
+ {
+ fprintf (stderr, _("%s: replace %s? "), program_name, quoteaf (dest));
+ if (!yesno ())
+ return true;
+ remove_existing_files = true;
+ }
if (backup_type != no_backups)
- {
- dest_backup = find_backup_file_name (dest, backup_type);
- if (rename (dest, dest_backup) != 0)
- {
- int rename_errno = errno;
- free (dest_backup);
- dest_backup = NULL;
- if (rename_errno != ENOENT)
- {
- error (0, rename_errno, _("cannot backup %s"), quote (dest));
- return false;
- }
- }
- }
+ {
+ dest_backup = find_backup_file_name (dest, backup_type);
+ if (rename (dest, dest_backup) != 0)
+ {
+ int rename_errno = errno;
+ free (dest_backup);
+ dest_backup = NULL;
+ if (rename_errno != ENOENT)
+ {
+ error (0, rename_errno, _("cannot backup %s"),
+ quoteaf (dest));
+ return false;
+ }
+ }
+ }
}
- ok = ((symbolic_link ? symlink (source, dest) : link (source, dest))
- == 0);
+ if (relative)
+ source = rel_source = convert_abs_rel (source, dest);
+
+ ok = ((symbolic_link ? symlink (source, dest)
+ : linkat (AT_FDCWD, source, AT_FDCWD, dest,
+ logical ? AT_SYMLINK_FOLLOW : 0))
+ == 0);
/* If the attempt to create a link failed and we are removing or
backing up destinations, unlink the destination and try again.
- POSIX 1003.1-2004 requires that ln -f A B must unlink B even on
- failure (e.g., when A does not exist). This is counterintuitive,
- and we submitted a defect report
- <http://www.opengroup.org/austin/mailarchives/ag-review/msg01794.html>
- (2004-06-24). If the committee does not fix the standard we'll
- have to change the behavior of ln -f, at least if POSIXLY_CORRECT
- is set. In the meantime ln -f A B will not unlink B unless the
- attempt to link A to B failed because B already existed.
+ On the surface, POSIX describes an algorithm that states that
+ 'ln -f A B' will call unlink() on B before ever attempting
+ link() on A. But strictly following this has the counterintuitive
+ effect of losing the contents of B, if A does not exist.
+ Fortunately, POSIX 2008 clarified that an application is free
+ to fail early if it can prove that continuing onwards cannot
+ succeed, so we are justified in trying link() before blindly
+ removing B, thus sometimes calling link() a second time during
+ a successful 'ln -f A B'.
Try to unlink DEST even if we may have backed it up successfully.
In some unusual cases (when DEST and DEST_BACKUP are hard-links
@@ -267,49 +327,58 @@ do_link (const char *source, const char *dest)
if (!ok && errno == EEXIST && (remove_existing_files || dest_backup))
{
if (unlink (dest) != 0)
- {
- error (0, errno, _("cannot remove %s"), quote (dest));
- free (dest_backup);
- return false;
- }
-
- ok = ((symbolic_link ? symlink (source, dest) : link (source, dest))
- == 0);
+ {
+ error (0, errno, _("cannot remove %s"), quoteaf (dest));
+ free (dest_backup);
+ free (rel_source);
+ return false;
+ }
+
+ ok = ((symbolic_link ? symlink (source, dest)
+ : linkat (AT_FDCWD, source, AT_FDCWD, dest,
+ logical ? AT_SYMLINK_FOLLOW : 0))
+ == 0);
}
if (ok)
{
+ /* Right after creating a hard link, do this: (note dest name and
+ source_stats, which are also the just-linked-destinations stats) */
+ if (! symbolic_link)
+ record_file (dest_set, dest, &source_stats);
+
if (verbose)
- {
- if (dest_backup)
- printf ("%s ~ ", quote (dest_backup));
- printf ("%s %c> %s\n", quote_n (0, dest), (symbolic_link ? '-' : '='),
- quote_n (1, source));
- }
+ {
+ if (dest_backup)
+ printf ("%s ~ ", quoteaf (dest_backup));
+ printf ("%s %c> %s\n", quoteaf_n (0, dest),
+ (symbolic_link ? '-' : '='), quoteaf_n (1, source));
+ }
}
else
{
error (0, errno,
- (symbolic_link
- ? (errno != ENAMETOOLONG && *source
- ? _("creating symbolic link %s")
- : _("creating symbolic link %s -> %s"))
- : (errno == EMLINK && !source_is_dir
- ? _("creating hard link to %.0s%s")
- : (errno == EDQUOT || errno == EEXIST || errno == ENOSPC
- || errno == EROFS)
- ? _("creating hard link %s")
- : _("creating hard link %s => %s"))),
- quote_n (0, dest), quote_n (1, source));
+ (symbolic_link
+ ? (errno != ENAMETOOLONG && *source
+ ? _("failed to create symbolic link %s")
+ : _("failed to create symbolic link %s -> %s"))
+ : (errno == EMLINK && !source_is_dir
+ ? _("failed to create hard link to %.0s%s")
+ : (errno == EDQUOT || errno == EEXIST || errno == ENOSPC
+ || errno == EROFS)
+ ? _("failed to create hard link %s")
+ : _("failed to create hard link %s => %s"))),
+ quoteaf_n (0, dest), quoteaf_n (1, source));
if (dest_backup)
- {
- if (rename (dest_backup, dest) != 0)
- error (0, errno, _("cannot un-backup %s"), quote (dest));
- }
+ {
+ if (rename (dest_backup, dest) != 0)
+ error (0, errno, _("cannot un-backup %s"), quoteaf (dest));
+ }
}
free (dest_backup);
+ free (rel_source);
return ok;
}
@@ -317,8 +386,7 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
@@ -327,18 +395,20 @@ Usage: %s [OPTION]... [-T] TARGET LINK_NAME (1st form)\n\
or: %s [OPTION]... TARGET... DIRECTORY (3rd form)\n\
or: %s [OPTION]... -t DIRECTORY TARGET... (4th form)\n\
"),
- program_name, program_name, program_name, program_name);
+ program_name, program_name, program_name, program_name);
fputs (_("\
In the 1st form, create a link to TARGET with the name LINK_NAME.\n\
In the 2nd form, create a link to TARGET in the current directory.\n\
In the 3rd and 4th forms, create links to each TARGET in DIRECTORY.\n\
Create hard links by default, symbolic links with --symbolic.\n\
-When creating hard links, each TARGET must exist.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
+By default, each destination (name of new link) should not already exist.\n\
+When creating hard links, each TARGET must exist. Symbolic links\n\
+can hold arbitrary text; if later resolved, a relative link is\n\
+interpreted in relation to its parent directory.\n\
"), stdout);
+
+ 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\
@@ -348,23 +418,26 @@ Mandatory arguments to long options are mandatory for short options too.\n\
-f, --force remove existing destination files\n\
"), stdout);
fputs (_("\
- -n, --no-dereference treat destination that is a symlink to a\n\
- directory as if it were a normal file\n\
-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 create symbolic 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\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);
fputs (_("\
\n\
-The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
+The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
The version control method may be selected via the --backup option or through\n\
the VERSION_CONTROL environment variable. Here are the values:\n\
\n\
@@ -375,7 +448,12 @@ the VERSION_CONTROL environment variable. Here are the values:\n\
existing, nil numbered if numbered backups exist, simple otherwise\n\
simple, never always make simple backups\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ printf (_("\
+\n\
+Using -s ignores -L and -P. Otherwise, the last option specified controls\n\
+behavior when a TARGET is a symbolic link, defaulting to %s.\n\
+"), LINK_FOLLOWS_SYMLINKS ? "-L" : "-P");
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -394,12 +472,12 @@ main (int argc, char **argv)
char **file;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
- atexit (close_stdout);
+ atexit (close_stdin);
/* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
we'll actually use backup_suffix_string. */
@@ -408,64 +486,74 @@ main (int argc, char **argv)
symbolic_link = remove_existing_files = interactive = verbose
= hard_dir_link = false;
- while ((c = getopt_long (argc, argv, "bdfinst:vFS:T", long_options, NULL))
- != -1)
+ while ((c = getopt_long (argc, argv, "bdfinrst:vFLPS:T", long_options, NULL))
+ != -1)
{
switch (c)
- {
- case 'b':
- make_backups = true;
- if (optarg)
- version_control_string = optarg;
- break;
- case 'd':
- case 'F':
- hard_dir_link = true;
- break;
- case 'f':
- remove_existing_files = true;
- interactive = false;
- break;
- case 'i':
- remove_existing_files = false;
- interactive = true;
- break;
- case 'n':
- dereference_dest_dir_symlinks = false;
- break;
- case 's':
- symbolic_link = true;
- break;
- case 't':
- if (target_directory)
- error (EXIT_FAILURE, 0, _("multiple target directories specified"));
- else
- {
- struct stat st;
- if (stat (optarg, &st) != 0)
- error (EXIT_FAILURE, errno, _("accessing %s"), quote (optarg));
- if (! S_ISDIR (st.st_mode))
- error (EXIT_FAILURE, 0, _("target %s is not a directory"),
- quote (optarg));
- }
- target_directory = optarg;
- break;
- case 'T':
- no_target_directory = true;
- break;
- case 'v':
- verbose = true;
- break;
- case 'S':
- make_backups = true;
- backup_suffix_string = optarg;
- break;
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- break;
- }
+ {
+ case 'b':
+ make_backups = true;
+ if (optarg)
+ version_control_string = optarg;
+ break;
+ case 'd':
+ case 'F':
+ hard_dir_link = true;
+ break;
+ case 'f':
+ remove_existing_files = true;
+ interactive = false;
+ break;
+ case 'i':
+ remove_existing_files = false;
+ interactive = true;
+ break;
+ case 'L':
+ logical = true;
+ break;
+ case 'n':
+ dereference_dest_dir_symlinks = false;
+ break;
+ case 'P':
+ logical = false;
+ break;
+ case 'r':
+ relative = true;
+ break;
+ case 's':
+ symbolic_link = true;
+ break;
+ case 't':
+ if (target_directory)
+ error (EXIT_FAILURE, 0, _("multiple target directories specified"));
+ else
+ {
+ struct stat st;
+ if (stat (optarg, &st) != 0)
+ error (EXIT_FAILURE, errno, _("failed to access %s"),
+ quoteaf (optarg));
+ if (! S_ISDIR (st.st_mode))
+ error (EXIT_FAILURE, 0, _("target %s is not a directory"),
+ quoteaf (optarg));
+ }
+ target_directory = optarg;
+ break;
+ case 'T':
+ no_target_directory = true;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ case 'S':
+ make_backups = true;
+ backup_suffix_string = optarg;
+ break;
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ break;
+ }
}
n_files = argc - optind;
@@ -480,55 +568,85 @@ main (int argc, char **argv)
if (no_target_directory)
{
if (target_directory)
- error (EXIT_FAILURE, 0,
- _("Cannot combine --target-directory "
- "and --no-target-directory"));
+ error (EXIT_FAILURE, 0,
+ _("cannot combine --target-directory "
+ "and --no-target-directory"));
if (n_files != 2)
- {
- if (n_files < 2)
- error (0, 0,
- _("missing destination file operand after %s"),
- quote (file[0]));
- else
- error (0, 0, _("extra operand %s"), quote (file[2]));
- usage (EXIT_FAILURE);
- }
+ {
+ if (n_files < 2)
+ error (0, 0,
+ _("missing destination file operand after %s"),
+ quoteaf (file[0]));
+ else
+ error (0, 0, _("extra operand %s"), quoteaf (file[2]));
+ usage (EXIT_FAILURE);
+ }
}
else if (!target_directory)
{
if (n_files < 2)
- target_directory = ".";
+ target_directory = ".";
else if (2 <= n_files && target_directory_operand (file[n_files - 1]))
- target_directory = file[--n_files];
+ target_directory = file[--n_files];
else if (2 < n_files)
- error (EXIT_FAILURE, 0, _("target %s is not a directory"),
- quote (file[n_files - 1]));
+ error (EXIT_FAILURE, 0, _("target %s is not a directory"),
+ quoteaf (file[n_files - 1]));
}
if (backup_suffix_string)
simple_backup_suffix = xstrdup (backup_suffix_string);
backup_type = (make_backups
- ? xget_version (_("backup type"), version_control_string)
- : no_backups);
+ ? xget_version (_("backup type"), version_control_string)
+ : no_backups);
+
+ if (relative && !symbolic_link)
+ {
+ error (EXIT_FAILURE, 0,
+ _("cannot do --relative without --symbolic"));
+ }
+
if (target_directory)
{
int i;
+
+ /* Create the data structure we'll use to record which hard links we
+ create. Used to ensure that ln detects an obscure corner case that
+ might result in user data loss. Create it only if needed. */
+ if (2 <= n_files
+ && remove_existing_files
+ /* Don't bother trying to protect symlinks, since ln clobbering
+ a just-created symlink won't ever lead to real data loss. */
+ && ! symbolic_link
+ /* No destination hard link can be clobbered when making
+ numbered backups. */
+ && backup_type != numbered_backups)
+
+ {
+ dest_set = hash_initialize (DEST_INFO_INITIAL_CAPACITY,
+ NULL,
+ triple_hash,
+ triple_compare,
+ triple_free);
+ if (dest_set == NULL)
+ xalloc_die ();
+ }
+
ok = true;
for (i = 0; i < n_files; ++i)
- {
- char *dest_base;
- char *dest = file_name_concat (target_directory,
- last_component (file[i]),
- &dest_base);
- strip_trailing_slashes (dest_base);
- ok &= do_link (file[i], dest);
- free (dest);
- }
+ {
+ char *dest_base;
+ char *dest = file_name_concat (target_directory,
+ last_component (file[i]),
+ &dest_base);
+ strip_trailing_slashes (dest_base);
+ ok &= do_link (file[i], dest);
+ free (dest);
+ }
}
else
ok = do_link (file[0], file[1]);
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/local.mk b/src/local.mk
new file mode 100644
index 0000000..12a0d55
--- /dev/null
+++ b/src/local.mk
@@ -0,0 +1,651 @@
+# Make coreutils programs. -*-Makefile-*-
+# This is included by the top-level Makefile.am.
+
+## Copyright (C) 1990-2016 Free Software Foundation, Inc.
+
+## This 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/>.
+
+# FIXME: once lib/ and gnulib-tests/ are also converted, hoist to Makefile.am
+AM_CFLAGS = $(WERROR_CFLAGS)
+
+# The list of all programs (separated in different variables to express
+# the how and when they should be installed) is defined in this makefile
+# fragment, autogenerated by the 'gen-lists-of-programs.sh' auxiliary
+# script.
+include $(srcdir)/src/cu-progs.mk
+
+EXTRA_PROGRAMS = \
+ $(no_install__progs) \
+ $(build_if_possible__progs) \
+ $(default__progs)
+
+# The user can tweak these lists at configure time.
+bin_PROGRAMS = @bin_PROGRAMS@
+pkglibexec_PROGRAMS = @pkglibexec_PROGRAMS@
+
+# Needed by the testsuite.
+noinst_PROGRAMS = \
+ src/getlimits \
+ src/make-prime-list
+
+noinst_HEADERS = \
+ src/chown-core.h \
+ src/copy.h \
+ src/cp-hash.h \
+ src/dircolors.h \
+ src/fiemap.h \
+ src/find-mount-point.h \
+ src/fs.h \
+ src/fs-is-local.h \
+ src/group-list.h \
+ src/ioblksize.h \
+ src/longlong.h \
+ src/ls.h \
+ src/operand2sig.h \
+ src/prog-fprintf.h \
+ src/remove.h \
+ src/set-fields.h \
+ src/system.h \
+ src/uname.h
+
+EXTRA_DIST += \
+ src/dcgen \
+ src/dircolors.hin \
+ src/primes.h \
+ src/tac-pipe.c \
+ src/extract-magic
+
+CLEANFILES += $(SCRIPTS)
+
+# Also remove these sometimes-built programs.
+# For example, even when excluded, they're built via 'sc_check-AUTHORS'
+# or 'dist'.
+CLEANFILES += $(no_install__progs)
+
+noinst_LIBRARIES += src/libver.a
+nodist_src_libver_a_SOURCES = src/version.c src/version.h
+
+# Tell the linker to omit references to unused shared libraries.
+AM_LDFLAGS = $(IGNORE_UNUSED_LIBRARIES_CFLAGS)
+
+# Extra libraries needed by more than one program. Will be updated later.
+copy_ldadd =
+remove_ldadd =
+
+# Sometimes, the expansion of $(LIBINTL) includes -lc which may
+# include modules defining variables like 'optind', so libcoreutils.a
+# must precede $(LIBINTL) in order to ensure we use GNU getopt.
+# But libcoreutils.a must also follow $(LIBINTL), since libintl uses
+# replacement functions defined in libcoreutils.a.
+LDADD = src/libver.a lib/libcoreutils.a $(LIBINTL) lib/libcoreutils.a
+
+# First, list all programs, to make listing per-program libraries easier.
+# See [ below.
+src_arch_LDADD = $(LDADD)
+src_base64_LDADD = $(LDADD)
+src_base32_LDADD = $(LDADD)
+src_basename_LDADD = $(LDADD)
+src_cat_LDADD = $(LDADD)
+src_chcon_LDADD = $(LDADD)
+src_chgrp_LDADD = $(LDADD)
+src_chmod_LDADD = $(LDADD)
+src_chown_LDADD = $(LDADD)
+src_chroot_LDADD = $(LDADD)
+src_cksum_LDADD = $(LDADD)
+src_comm_LDADD = $(LDADD)
+src_nproc_LDADD = $(LDADD)
+src_cp_LDADD = $(LDADD)
+if !SINGLE_BINARY
+src_coreutils_LDADD = $(LDADD)
+endif
+src_csplit_LDADD = $(LDADD)
+src_cut_LDADD = $(LDADD)
+src_date_LDADD = $(LDADD)
+src_dd_LDADD = $(LDADD)
+src_df_LDADD = $(LDADD)
+# See dir_LDADD below
+src_dircolors_LDADD = $(LDADD)
+src_dirname_LDADD = $(LDADD)
+src_du_LDADD = $(LDADD)
+src_echo_LDADD = $(LDADD)
+src_env_LDADD = $(LDADD)
+src_expand_LDADD = $(LDADD)
+src_expr_LDADD = $(LDADD)
+src_factor_LDADD = $(LDADD)
+src_false_LDADD = $(LDADD)
+src_fmt_LDADD = $(LDADD)
+src_fold_LDADD = $(LDADD)
+src_getlimits_LDADD = $(LDADD)
+src_ginstall_LDADD = $(LDADD)
+src_groups_LDADD = $(LDADD)
+src_head_LDADD = $(LDADD)
+src_hostid_LDADD = $(LDADD)
+src_hostname_LDADD = $(LDADD)
+src_id_LDADD = $(LDADD)
+src_join_LDADD = $(LDADD)
+src_kill_LDADD = $(LDADD)
+src_link_LDADD = $(LDADD)
+src_ln_LDADD = $(LDADD)
+src_logname_LDADD = $(LDADD)
+src_ls_LDADD = $(LDADD)
+
+# This must *not* depend on anything in lib/, since it is used to generate
+# src/primes.h. If it depended on libcoreutils.a, that would pull all lib/*.c
+# into BUILT_SOURCES.
+src_make_prime_list_LDADD =
+
+src_md5sum_LDADD = $(LDADD)
+src_mkdir_LDADD = $(LDADD)
+src_mkfifo_LDADD = $(LDADD)
+src_mknod_LDADD = $(LDADD)
+src_mktemp_LDADD = $(LDADD)
+src_mv_LDADD = $(LDADD)
+src_nice_LDADD = $(LDADD)
+src_nl_LDADD = $(LDADD)
+src_nohup_LDADD = $(LDADD)
+src_numfmt_LDADD = $(LDADD)
+src_od_LDADD = $(LDADD)
+src_paste_LDADD = $(LDADD)
+src_pathchk_LDADD = $(LDADD)
+src_pinky_LDADD = $(LDADD)
+src_pr_LDADD = $(LDADD)
+src_printenv_LDADD = $(LDADD)
+src_printf_LDADD = $(LDADD)
+src_ptx_LDADD = $(LDADD)
+src_pwd_LDADD = $(LDADD)
+src_readlink_LDADD = $(LDADD)
+src_realpath_LDADD = $(LDADD)
+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)
+src_shred_LDADD = $(LDADD)
+src_shuf_LDADD = $(LDADD)
+src_sleep_LDADD = $(LDADD)
+src_sort_LDADD = $(LDADD)
+src_split_LDADD = $(LDADD)
+src_stat_LDADD = $(LDADD)
+src_stdbuf_LDADD = $(LDADD)
+src_stty_LDADD = $(LDADD)
+src_sum_LDADD = $(LDADD)
+src_sync_LDADD = $(LDADD)
+src_tac_LDADD = $(LDADD)
+src_tail_LDADD = $(LDADD)
+src_tee_LDADD = $(LDADD)
+src_test_LDADD = $(LDADD)
+src_timeout_LDADD = $(LDADD)
+src_touch_LDADD = $(LDADD)
+src_tr_LDADD = $(LDADD)
+src_true_LDADD = $(LDADD)
+src_truncate_LDADD = $(LDADD)
+src_tsort_LDADD = $(LDADD)
+src_tty_LDADD = $(LDADD)
+src_uname_LDADD = $(LDADD)
+src_unexpand_LDADD = $(LDADD)
+src_uniq_LDADD = $(LDADD)
+src_unlink_LDADD = $(LDADD)
+src_uptime_LDADD = $(LDADD)
+src_users_LDADD = $(LDADD)
+# See vdir_LDADD below
+src_wc_LDADD = $(LDADD)
+src_who_LDADD = $(LDADD)
+src_whoami_LDADD = $(LDADD)
+src_yes_LDADD = $(LDADD)
+
+# Synonyms. Recall that Automake transliterates '[' and '/' to '_'.
+src___LDADD = $(src_test_LDADD)
+src_dir_LDADD = $(src_ls_LDADD)
+src_vdir_LDADD = $(src_ls_LDADD)
+
+src_cp_LDADD += $(copy_ldadd)
+src_ginstall_LDADD += $(copy_ldadd)
+src_mv_LDADD += $(copy_ldadd)
+
+src_mv_LDADD += $(remove_ldadd)
+src_rm_LDADD += $(remove_ldadd)
+
+# for eaccess, euidaccess
+copy_ldadd += $(LIB_EACCESS)
+remove_ldadd += $(LIB_EACCESS)
+src_sort_LDADD += $(LIB_EACCESS)
+src_test_LDADD += $(LIB_EACCESS)
+
+# for selinux use
+copy_ldadd += $(LIB_SELINUX)
+src_chcon_LDADD += $(LIB_SELINUX)
+src_ginstall_LDADD += $(LIB_SELINUX)
+src_id_LDADD += $(LIB_SELINUX)
+src_id_LDADD += $(LIB_SMACK)
+src_ls_LDADD += $(LIB_SELINUX)
+src_ls_LDADD += $(LIB_SMACK)
+src_mkdir_LDADD += $(LIB_SELINUX)
+src_mkdir_LDADD += $(LIB_SMACK)
+src_mkfifo_LDADD += $(LIB_SELINUX)
+src_mkfifo_LDADD += $(LIB_SMACK)
+src_mknod_LDADD += $(LIB_SELINUX)
+src_mknod_LDADD += $(LIB_SMACK)
+src_runcon_LDADD += $(LIB_SELINUX)
+src_stat_LDADD += $(LIB_SELINUX)
+
+# for nvlist_lookup_uint64_array
+src_stat_LDADD += $(LIB_NVPAIR)
+
+# for gettime, settime, utimecmp, utimens
+copy_ldadd += $(LIB_CLOCK_GETTIME)
+src_date_LDADD += $(LIB_CLOCK_GETTIME)
+src_ginstall_LDADD += $(LIB_CLOCK_GETTIME)
+src_ls_LDADD += $(LIB_CLOCK_GETTIME)
+src_pr_LDADD += $(LIB_CLOCK_GETTIME)
+src_timeout_LDADD += $(LIB_TIMER_TIME)
+src_touch_LDADD += $(LIB_CLOCK_GETTIME)
+
+# for gethrxtime
+src_dd_LDADD += $(LIB_GETHRXTIME)
+
+# for cap_get_file
+src_ls_LDADD += $(LIB_CAP)
+
+# for fdatasync
+src_dd_LDADD += $(LIB_FDATASYNC)
+src_shred_LDADD += $(LIB_FDATASYNC)
+src_sync_LDADD += $(LIB_FDATASYNC)
+
+# for xnanosleep
+src_sleep_LDADD += $(LIB_NANOSLEEP)
+src_sort_LDADD += $(LIB_NANOSLEEP)
+src_tail_LDADD += $(LIB_NANOSLEEP)
+
+# for various GMP functions
+src_expr_LDADD += $(LIB_GMP)
+src_factor_LDADD += $(LIB_GMP)
+
+# for getloadavg
+src_uptime_LDADD += $(GETLOADAVG_LIBS)
+
+# for various ACL functions
+copy_ldadd += $(LIB_ACL)
+src_ls_LDADD += $(LIB_HAS_ACL)
+
+# for various xattr functions
+copy_ldadd += $(LIB_XATTR)
+
+# for print_unicode_char, proper_name_utf8
+src_cat_LDADD += $(LIBICONV)
+src_cp_LDADD += $(LIBICONV)
+src_df_LDADD += $(LIBICONV)
+src_du_LDADD += $(LIBICONV)
+src_factor_LDADD += $(LIBICONV)
+src_getlimits_LDADD += $(LIBICONV)
+src_printf_LDADD += $(LIBICONV)
+src_ptx_LDADD += $(LIBICONV)
+src_realpath_LDADD += $(LIBICONV)
+src_split_LDADD += $(LIBICONV)
+src_stdbuf_LDADD += $(LIBICONV)
+src_timeout_LDADD += $(LIBICONV)
+src_truncate_LDADD += $(LIBICONV)
+
+# for libcrypto hash routines
+src_md5sum_LDADD += $(LIB_CRYPTO)
+src_sort_LDADD += $(LIB_CRYPTO)
+src_sha1sum_LDADD += $(LIB_CRYPTO)
+src_sha224sum_LDADD += $(LIB_CRYPTO)
+src_sha256sum_LDADD += $(LIB_CRYPTO)
+src_sha384sum_LDADD += $(LIB_CRYPTO)
+src_sha512sum_LDADD += $(LIB_CRYPTO)
+
+# for canon_host
+src_pinky_LDADD += $(GETADDRINFO_LIB)
+src_who_LDADD += $(GETADDRINFO_LIB)
+
+# for gethostname, uname
+src_hostname_LDADD += $(GETHOSTNAME_LIB)
+src_uname_LDADD += $(GETHOSTNAME_LIB)
+
+# for strsignal
+src_kill_LDADD += $(LIBTHREAD)
+
+# for pthread
+src_sort_LDADD += $(LIB_PTHREAD)
+
+# 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' \
+ $(top_srcdir)/lib/version-etc.c`
+
+selinux_sources = \
+ src/selinux.c \
+ src/selinux.h
+
+copy_sources = \
+ src/copy.c \
+ src/cp-hash.c \
+ src/extent-scan.c \
+ src/extent-scan.h
+
+# Use 'ginstall' in the definition of PROGRAMS and in dependencies to avoid
+# confusion with the 'install' target. The install rule transforms 'ginstall'
+# to install before applying any user-specified name transformations.
+
+# Don't apply prefix transformations to libstdbuf shared lib
+# as that's not generally needed, and we need to reference the
+# name directly in LD_PRELOAD etc. In general it's surprising
+# that $(transform) is applied to libexec at all given that is
+# for internal package naming, not privy to $(transform).
+
+transform = s/ginstall/install/;/libstdbuf/!$(program_transform_name)
+
+src_ginstall_SOURCES = src/install.c src/prog-fprintf.c $(copy_sources) \
+ $(selinux_sources)
+
+# This is for the '[' program. Automake transliterates '[' and '/' to '_'.
+src___SOURCES = src/lbracket.c
+
+nodist_src_coreutils_SOURCES = src/coreutils.h
+src_coreutils_SOURCES = src/coreutils.c
+
+src_cp_SOURCES = src/cp.c $(copy_sources) $(selinux_sources)
+src_dir_SOURCES = src/ls.c src/ls-dir.c
+src_vdir_SOURCES = src/ls.c src/ls-vdir.c
+src_id_SOURCES = src/id.c src/group-list.c
+src_groups_SOURCES = src/groups.c src/group-list.c
+src_ls_SOURCES = src/ls.c src/ls-ls.c
+src_ln_SOURCES = src/ln.c src/relpath.c src/relpath.h
+src_chown_SOURCES = src/chown.c src/chown-core.c
+src_chgrp_SOURCES = src/chgrp.c src/chown-core.c
+src_kill_SOURCES = src/kill.c src/operand2sig.c
+src_realpath_SOURCES = src/realpath.c src/relpath.c src/relpath.h
+src_timeout_SOURCES = src/timeout.c src/operand2sig.c
+
+src_mv_SOURCES = src/mv.c src/remove.c $(copy_sources) $(selinux_sources)
+src_rm_SOURCES = src/rm.c src/remove.c
+
+src_mkdir_SOURCES = src/mkdir.c src/prog-fprintf.c $(selinux_sources)
+src_rmdir_SOURCES = src/rmdir.c src/prog-fprintf.c
+
+src_mkfifo_SOURCES = src/mkfifo.c $(selinux_sources)
+src_mknod_SOURCES = src/mknod.c $(selinux_sources)
+
+src_df_SOURCES = src/df.c src/find-mount-point.c
+src_stat_SOURCES = src/stat.c src/find-mount-point.c
+
+src_uname_SOURCES = src/uname.c src/uname-uname.c
+src_arch_SOURCES = src/uname.c src/uname-arch.c
+
+src_cut_SOURCES = src/cut.c src/set-fields.c
+src_numfmt_SOURCES = src/numfmt.c src/set-fields.c
+
+src_md5sum_CPPFLAGS = -DHASH_ALGO_MD5=1 $(AM_CPPFLAGS)
+src_sha1sum_SOURCES = src/md5sum.c
+src_sha1sum_CPPFLAGS = -DHASH_ALGO_SHA1=1 $(AM_CPPFLAGS)
+src_sha224sum_SOURCES = src/md5sum.c
+src_sha224sum_CPPFLAGS = -DHASH_ALGO_SHA224=1 $(AM_CPPFLAGS)
+src_sha256sum_SOURCES = src/md5sum.c
+src_sha256sum_CPPFLAGS = -DHASH_ALGO_SHA256=1 $(AM_CPPFLAGS)
+src_sha384sum_SOURCES = src/md5sum.c
+src_sha384sum_CPPFLAGS = -DHASH_ALGO_SHA384=1 $(AM_CPPFLAGS)
+src_sha512sum_SOURCES = src/md5sum.c
+src_sha512sum_CPPFLAGS = -DHASH_ALGO_SHA512=1 $(AM_CPPFLAGS)
+
+src_base64_CPPFLAGS = -DBASE_TYPE=64 $(AM_CPPFLAGS)
+src_base32_SOURCES = src/base64.c
+src_base32_CPPFLAGS = -DBASE_TYPE=32 $(AM_CPPFLAGS)
+
+src_ginstall_CPPFLAGS = -DENABLE_MATCHPATHCON=1 $(AM_CPPFLAGS)
+
+# Ensure we don't link against libcoreutils.a as that lib is
+# not compiled with -fPIC which causes issues on 64 bit at least
+src_libstdbuf_so_LDADD = $(LIBINTL)
+
+# Note libstdbuf is only compiled if GCC is available
+# (as per the check in configure.ac), so these flags should be available.
+# libtool is probably required to relax this dependency.
+src_libstdbuf_so_LDFLAGS = -shared
+src_libstdbuf_so_CFLAGS = -fPIC $(AM_CFLAGS)
+
+BUILT_SOURCES += src/coreutils.h
+if SINGLE_BINARY
+# Single binary dependencies
+src_coreutils_CFLAGS = -DSINGLE_BINARY $(AM_CFLAGS)
+#src_coreutils_LDFLAGS = $(AM_LDFLAGS)
+src_coreutils_LDADD = $(single_binary_deps) $(LDADD) $(single_binary_libs)
+src_coreutils_DEPENDENCIES = $(LDADD) $(single_binary_deps)
+
+include $(top_srcdir)/src/single-binary.mk
+
+# Creates symlinks or shebangs to the installed programs when building
+# coreutils single binary.
+EXTRA_src_coreutils_DEPENDENCIES = src/coreutils_$(single_binary_install_type)
+endif SINGLE_BINARY
+
+CLEANFILES += src/coreutils_symlinks
+src/coreutils_symlinks: Makefile
+ $(AM_V_GEN)touch $@
+ $(AM_V_at)${MKDIR_P} src
+ $(AM_V_at)for i in x $(single_binary_progs); do \
+ test $$i = x && continue; \
+ rm -f src/$$i$(EXEEXT) || exit $$?; \
+ $(LN_S) -s coreutils$(EXEEXT) src/$$i$(EXEEXT) || exit $$?; \
+ done
+
+CLEANFILES += src/coreutils_shebangs
+src/coreutils_shebangs: Makefile
+ $(AM_V_GEN)touch $@
+ $(AM_V_at)${MKDIR_P} src
+ $(AM_V_at)for i in x $(single_binary_progs); do \
+ test $$i = x && continue; \
+ rm -f src/$$i$(EXEEXT) || exit $$?; \
+ printf '#!%s --coreutils-prog-shebang=%s\n' \
+ $(abs_top_builddir)/src/coreutils$(EXEEXT) $$i \
+ >src/$$i$(EXEEXT) || exit $$?; \
+ chmod a+x,a-w src/$$i$(EXEEXT) || exit $$?; \
+ done
+
+clean-local:
+ $(AM_V_at)for i in x $(single_binary_progs); do \
+ test $$i = x && continue; \
+ rm -f src/$$i$(EXEEXT) || exit $$?; \
+ done
+
+
+BUILT_SOURCES += src/dircolors.h
+src/dircolors.h: src/dcgen src/dircolors.hin
+ $(AM_V_GEN)rm -f $@ $@-t
+ $(AM_V_at)${MKDIR_P} src
+ $(AM_V_at)$(PERL) -w -- $(srcdir)/src/dcgen \
+ $(srcdir)/src/dircolors.hin > $@-t
+ $(AM_V_at)chmod a-w $@-t
+ $(AM_V_at)mv $@-t $@
+
+# This file is built by maintainers. It's architecture-independent,
+# and it needs to be built on a widest-known-int architecture, so it's
+# built only if absent. It is not cleaned because we don't want to
+# insist that maintainers must build on hosts that support the widest
+# known ints (currently 128-bit).
+BUILT_SOURCES += $(top_srcdir)/src/primes.h
+$(top_srcdir)/src/primes.h:
+ $(AM_V_at)${MKDIR_P} src
+ $(MAKE) src/make-prime-list$(EXEEXT)
+ $(AM_V_GEN)rm -f $@ $@-t
+ $(AM_V_at)src/make-prime-list$(EXEEXT) 5000 > $@-t
+ $(AM_V_at)chmod a-w $@-t
+ $(AM_V_at)mv $@-t $@
+
+# false exits nonzero even with --help or --version.
+# test doesn't support --help or --version.
+# Tell automake to exempt then from that installcheck test.
+AM_INSTALLCHECK_STD_OPTIONS_EXEMPT = src/false src/test
+
+# Compare fs.h with the list of file system names/magic-numbers in the
+# Linux statfs man page. This target prints any new name/number pairs.
+# Also compare against /usr/include/linux/magic.h
+.PHONY: src/fs-magic-compare
+src/fs-magic-compare: src/fs-magic src/fs-kernel-magic src/fs-def
+ @join -v1 -t@ src/fs-magic src/fs-def
+ @join -v1 -t@ src/fs-kernel-magic src/fs-def
+
+CLEANFILES += src/fs-def
+src/fs-def: src/fs.h
+ grep '^# *define ' src/fs.h | $(ASSORT) > $@-t && mv $@-t $@
+
+# Massage bits of the statfs man page and definitions from
+# /usr/include/linux/magic.h to be in a form consistent with what's in fs.h.
+fs_normalize_perl_subst = \
+ -e 's/MINIX_SUPER_MAGIC\b/MINIX/;' \
+ -e 's/MINIX_SUPER_MAGIC2\b/MINIX_30/;' \
+ -e 's/MINIX2_SUPER_MAGIC\b/MINIX_V2/;' \
+ -e 's/MINIX2_SUPER_MAGIC2\b/MINIX_V2_30/;' \
+ -e 's/MINIX3_SUPER_MAGIC\b/MINIX_V3/;' \
+ -e 's/CIFS_MAGIC_NUMBER/CIFS/;' \
+ -e 's/(_SUPER)?_MAGIC//;' \
+ -e 's/\s+0x(\S+)/" 0x" . uc $$1/e;' \
+ -e 's/(\s+0x)(\X{3})\b/$${1}0$$2/;' \
+ -e 's/(\s+0x)(\X{6})\b/$${1}00$$2/;' \
+ -e 's/(\s+0x)(\X{7})\b/$${1}0$$2/;' \
+ -e 's/^\s+//;' \
+ -e 's/^\043define\s+//;' \
+ -e 's/^_(XIAFS)/$$1/;' \
+ -e 's/^USBDEVICE/USBDEVFS/;' \
+ -e 's/NTFS_SB/NTFS/;' \
+ -e 's/^/\043 define S_MAGIC_/;' \
+ -e 's,\s*/\* .*? \*/,,;'
+
+CLEANFILES += src/fs-magic
+src/fs-magic: Makefile
+ @MANPAGER= man statfs \
+ |perl -ne '/File system types:/.../Nobody kno/ and print' \
+ |grep 0x | perl -p \
+ $(fs_normalize_perl_subst) \
+ | grep -Ev 'S_MAGIC_EXT[34]|STACK_END' \
+ | $(ASSORT) \
+ > $@-t && mv $@-t $@
+
+DISTCLEANFILES += src/fs-latest-magic.h
+# This rule currently gets the latest header, but probably isn't general
+# enough to enable by default.
+# @kgit='https://git.kernel.org/cgit/linux/kernel/git'; \
+# wget -q $$kgit/torvalds/linux.git/plain/include/uapi/linux/magic.h \
+# -O $@
+src/fs-latest-magic.h:
+ @touch $@
+
+CLEANFILES += src/fs-kernel-magic
+src/fs-kernel-magic: Makefile src/fs-latest-magic.h
+ @perl -ne '/^#define.*0x/ and print' \
+ /usr/include/linux/magic.h src/fs-latest-magic.h \
+ | perl -p \
+ $(fs_normalize_perl_subst) \
+ | grep -Ev 'S_MAGIC_EXT[34]|STACK_END' \
+ | $(ASSORT) -u \
+ > $@-t && mv $@-t $@
+
+BUILT_SOURCES += src/fs-is-local.h
+src/fs-is-local.h: src/stat.c src/extract-magic
+ $(AM_V_GEN)rm -f $@
+ $(AM_V_at)${MKDIR_P} src
+ $(AM_V_at)$(PERL) $(srcdir)/src/extract-magic \
+ --local $(srcdir)/src/stat.c > $@t
+ $(AM_V_at)chmod a-w $@t
+ $(AM_V_at)mv $@t $@
+
+BUILT_SOURCES += src/fs.h
+src/fs.h: src/stat.c src/extract-magic
+ $(AM_V_GEN)rm -f $@
+ $(AM_V_at)${MKDIR_P} src
+ $(AM_V_at)$(PERL) $(srcdir)/src/extract-magic \
+ $(srcdir)/src/stat.c > $@t
+ $(AM_V_at)chmod a-w $@t
+ $(AM_V_at)mv $@t $@
+
+BUILT_SOURCES += src/version.c
+src/version.c: Makefile
+ $(AM_V_GEN)rm -f $@
+ $(AM_V_at)${MKDIR_P} src
+ $(AM_V_at)printf '#include <config.h>\n' > $@t
+ $(AM_V_at)printf 'char const *Version = "$(PACKAGE_VERSION)";\n' >> $@t
+ $(AM_V_at)chmod a-w $@t
+ $(AM_V_at)mv $@t $@
+
+BUILT_SOURCES += src/version.h
+src/version.h: Makefile
+ $(AM_V_GEN)rm -f $@
+ $(AM_V_at)${MKDIR_P} src
+ $(AM_V_at)printf 'extern char const *Version;\n' > $@t
+ $(AM_V_at)chmod a-w $@t
+ $(AM_V_at)mv $@t $@
+
+# Generates a list of macro invocations like:
+# SINGLE_BINARY_PROGRAM(program_name_str, main_name)
+# once for each program list on $(single_binary_progs). Note that
+# for [ the macro invocation is:
+# SINGLE_BINARY_PROGRAM("[", _)
+DISTCLEANFILES += src/coreutils.h
+src/coreutils.h: Makefile
+ $(AM_V_GEN)rm -f $@
+ $(AM_V_at)${MKDIR_P} src
+ $(AM_V_at)for prog in x $(single_binary_progs); do \
+ test $$prog = x && continue; \
+ prog=`basename $$prog`; \
+ main=`echo $$prog | tr '[' '_'`; \
+ echo "SINGLE_BINARY_PROGRAM(\"$$prog\", $$main)"; \
+ done | sort > $@t
+ $(AM_V_at)chmod a-w $@t
+ $(AM_V_at)mv $@t $@
+
+DISTCLEANFILES += src/version.c src/version.h
+MAINTAINERCLEANFILES += $(BUILT_SOURCES)
+
+all_programs = \
+ $(bin_PROGRAMS) \
+ $(bin_SCRIPTS) \
+ $(EXTRA_PROGRAMS)
+
+pm = progs-makefile
+pr = progs-readme
+# Ensure that the list of programs in README matches the list
+# of programs we can build.
+check-local: check-README check-duplicate-no-install
+.PHONY: check-README
+check-README:
+ $(AM_V_GEN)rm -rf $(pr) $(pm)
+ $(AM_V_at)echo $(all_programs) \
+ | tr -s ' ' '\n' \
+ | sed -e 's,$(EXEEXT)$$,,' \
+ -e 's,^src/,,' \
+ -e 's/^ginstall$$/install/' \
+ | sed /libstdbuf/d \
+ | $(ASSORT) -u > $(pm) && \
+ sed -n '/^The programs .* are:/,/^[a-zA-Z]/p' $(top_srcdir)/README \
+ | sed -n '/^ */s///p' | tr -s ' ' '\n' > $(pr)
+ $(AM_V_at)diff $(pm) $(pr) && rm -rf $(pr) $(pm)
+
+# Ensure that a by-default-not-installed program (listed in
+# $(no_install__progs) is not also listed as another $(EXTRA_PROGRAMS)
+# entry, because if that were to happen, it *would* be installed
+# by default.
+.PHONY: check-duplicate-no-install
+check-duplicate-no-install: src/tr
+ $(AM_V_GEN)test -z "`echo '$(EXTRA_PROGRAMS)' | tr ' ' '\n' | uniq -d`"
+
+# Use the just-built 'ginstall', when not cross-compiling.
+if CROSS_COMPILING
+cu_install_program = @INSTALL_PROGRAM@
+else
+cu_install_program = src/ginstall
+endif
+INSTALL_PROGRAM = $(cu_install_program)
diff --git a/src/logname.c b/src/logname.c
index dc82967..6e8bf30 100644
--- a/src/logname.c
+++ b/src/logname.c
@@ -1,10 +1,10 @@
/* logname -- print user's login name
- Copyright (C) 1990-1997, 1999-2005 Free Software Foundation, Inc.
+ Copyright (C) 1990-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#include <stdio.h>
@@ -25,20 +24,16 @@
#include "long-options.h"
#include "quote.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "logname"
-#define AUTHORS "FIXME: unknown"
-
-/* The name this program was run with. */
-char *program_name;
+#define AUTHORS proper_name ("FIXME: unknown")
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("Usage: %s [OPTION]\n"), program_name);
@@ -48,7 +43,7 @@ Print the name of the current user.\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -59,15 +54,15 @@ main (int argc, char **argv)
char *cp;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
+ parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, Version,
+ usage, AUTHORS, (char const *) NULL);
if (getopt_long (argc, argv, "", NULL, NULL) != -1)
usage (EXIT_FAILURE);
@@ -77,15 +72,12 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
- /* POSIX requires using getlogin (or equivalent code). */
+ /* POSIX requires using getlogin (or equivalent code) and prohibits
+ using a fallback technique. */
cp = getlogin ();
- if (cp)
- {
- puts (cp);
- exit (EXIT_SUCCESS);
- }
- /* POSIX prohibits using a fallback technique. */
+ if (! cp)
+ error (EXIT_FAILURE, 0, _("no login name"));
- error (0, 0, _("no login name"));
- exit (EXIT_FAILURE);
+ puts (cp);
+ return EXIT_SUCCESS;
}
diff --git a/src/longlong.h b/src/longlong.h
new file mode 100644
index 0000000..29564a7
--- /dev/null
+++ b/src/longlong.h
@@ -0,0 +1,2201 @@
+/* longlong.h -- definitions for mixed size 32/64 bit arithmetic.
+
+Copyright 1991-2016 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 published by the Free
+Software Foundation; either version 3 of the License, or (at your option) any
+later version.
+
+This file 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 Lesser General Public License for more
+details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this file. If not, see http://www.gnu.org/licenses/. */
+
+/* You have to define the following before including this file:
+
+ UWtype -- An unsigned type, default type for operations (typically a "word")
+ UHWtype -- An unsigned type, at least half the size of UWtype
+ UDWtype -- An unsigned type, at least twice as large a UWtype
+ W_TYPE_SIZE -- size in bits of UWtype
+
+ SItype, USItype -- Signed and unsigned 32 bit types
+ DItype, UDItype -- Signed and unsigned 64 bit types
+
+ On a 32 bit machine UWtype should typically be USItype;
+ on a 64 bit machine, UWtype should typically be UDItype.
+
+ Optionally, define:
+
+ LONGLONG_STANDALONE -- Avoid code that needs machine-dependent support files
+ NO_ASM -- Disable inline asm
+
+
+ CAUTION! Using this version of longlong.h outside of GMP is not safe. You
+ need to include gmp.h and gmp-impl.h, or certain things might not work as
+ expected.
+*/
+
+#define __BITS4 (W_TYPE_SIZE / 4)
+#define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2))
+#define __ll_lowpart(t) ((UWtype) (t) & (__ll_B - 1))
+#define __ll_highpart(t) ((UWtype) (t) >> (W_TYPE_SIZE / 2))
+
+/* This is used to make sure no undesirable sharing between different libraries
+ that use this file takes place. */
+#ifndef __MPN
+#define __MPN(x) __##x
+#endif
+
+/* Define auxiliary asm macros.
+
+ 1) umul_ppmm(high_prod, low_prod, multiplier, multiplicand) multiplies two
+ UWtype integers MULTIPLIER and MULTIPLICAND, and generates a two UWtype
+ word product in HIGH_PROD and LOW_PROD.
+
+ 2) __umulsidi3(a,b) multiplies two UWtype integers A and B, and returns a
+ UDWtype product. This is just a variant of umul_ppmm.
+
+ 3) udiv_qrnnd(quotient, remainder, high_numerator, low_numerator,
+ denominator) divides a UDWtype, composed by the UWtype integers
+ HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and places the quotient
+ in QUOTIENT and the remainder in REMAINDER. HIGH_NUMERATOR must be less
+ than DENOMINATOR for correct operation. If, in addition, the most
+ significant bit of DENOMINATOR must be 1, then the pre-processor symbol
+ UDIV_NEEDS_NORMALIZATION is defined to 1.
+
+ 4) sdiv_qrnnd(quotient, remainder, high_numerator, low_numerator,
+ denominator). Like udiv_qrnnd but the numbers are signed. The quotient
+ is rounded towards 0.
+
+ 5) count_leading_zeros(count, x) counts the number of zero-bits from the
+ msb to the first non-zero bit in the UWtype X. This is the number of
+ steps X needs to be shifted left to set the msb. Undefined for X == 0,
+ unless the symbol COUNT_LEADING_ZEROS_0 is defined to some value.
+
+ 6) count_trailing_zeros(count, x) like count_leading_zeros, but counts
+ from the least significant end.
+
+ 7) add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1,
+ high_addend_2, low_addend_2) adds two UWtype integers, composed by
+ HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and LOW_ADDEND_2
+ respectively. The result is placed in HIGH_SUM and LOW_SUM. Overflow
+ (i.e. carry out) is not stored anywhere, and is lost.
+
+ 8) sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend,
+ high_subtrahend, low_subtrahend) subtracts two two-word UWtype integers,
+ composed by HIGH_MINUEND_1 and LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and
+ LOW_SUBTRAHEND_2 respectively. The result is placed in HIGH_DIFFERENCE
+ and LOW_DIFFERENCE. Overflow (i.e. carry out) is not stored anywhere,
+ and is lost.
+
+ If any of these macros are left undefined for a particular CPU,
+ C macros are used.
+
+
+ Notes:
+
+ For add_ssaaaa the two high and two low addends can both commute, but
+ unfortunately gcc only supports one "%" commutative in each asm block.
+ This has always been so but is only documented in recent versions
+ (eg. pre-release 3.3). Having two or more "%"s can cause an internal
+ compiler error in certain rare circumstances.
+
+ Apparently it was only the last "%" that was ever actually respected, so
+ the code has been updated to leave just that. Clearly there's a free
+ choice whether high or low should get it, if there's a reason to favour
+ one over the other. Also obviously when the constraints on the two
+ operands are identical there's no benefit to the reloader in any "%" at
+ all.
+
+ */
+
+/* The CPUs come in alphabetical order below.
+
+ Please add support for more CPUs here, or improve the current support
+ for the CPUs below! */
+
+
+/* count_leading_zeros_gcc_clz is count_leading_zeros implemented with gcc
+ 3.4 __builtin_clzl or __builtin_clzll, according to our limb size.
+ Similarly count_trailing_zeros_gcc_ctz using __builtin_ctzl or
+ __builtin_ctzll.
+
+ These builtins are only used when we check what code comes out, on some
+ chips they're merely libgcc calls, where we will instead want an inline
+ in that case (either asm or generic C).
+
+ These builtins are better than an asm block of the same insn, since an
+ asm block doesn't give gcc any information about scheduling or resource
+ usage. We keep an asm block for use on prior versions of gcc though.
+
+ For reference, __builtin_ffs existed in gcc prior to __builtin_clz, but
+ it's not used (for count_leading_zeros) because it generally gives extra
+ code to ensure the result is 0 when the input is 0, which we don't need
+ or want. */
+
+#ifdef _LONG_LONG_LIMB
+#define count_leading_zeros_gcc_clz(count,x) \
+ do { \
+ ASSERT ((x) != 0); \
+ (count) = __builtin_clzll (x); \
+ } while (0)
+#else
+#define count_leading_zeros_gcc_clz(count,x) \
+ do { \
+ ASSERT ((x) != 0); \
+ (count) = __builtin_clzl (x); \
+ } while (0)
+#endif
+
+#ifdef _LONG_LONG_LIMB
+#define count_trailing_zeros_gcc_ctz(count,x) \
+ do { \
+ ASSERT ((x) != 0); \
+ (count) = __builtin_ctzll (x); \
+ } while (0)
+#else
+#define count_trailing_zeros_gcc_ctz(count,x) \
+ do { \
+ ASSERT ((x) != 0); \
+ (count) = __builtin_ctzl (x); \
+ } while (0)
+#endif
+
+
+/* FIXME: The macros using external routines like __MPN(count_leading_zeros)
+ don't need to be under !NO_ASM */
+#if ! defined (NO_ASM)
+
+#if defined (__alpha) && W_TYPE_SIZE == 64
+/* Most alpha-based machines, except Cray systems. */
+#if defined (__GNUC__)
+#if __GMP_GNUC_PREREQ (3,3)
+#define umul_ppmm(ph, pl, m0, m1) \
+ do { \
+ UDItype __m0 = (m0), __m1 = (m1); \
+ (ph) = __builtin_alpha_umulh (__m0, __m1); \
+ (pl) = __m0 * __m1; \
+ } while (0)
+#else
+#define umul_ppmm(ph, pl, m0, m1) \
+ do { \
+ UDItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("umulh %r1,%2,%0" \
+ : "=r" (ph) \
+ : "%rJ" (__m0), "rI" (__m1)); \
+ (pl) = __m0 * __m1; \
+ } while (0)
+#endif
+#define UMUL_TIME 18
+#else /* ! __GNUC__ */
+#include <machine/builtins.h>
+#define umul_ppmm(ph, pl, m0, m1) \
+ do { \
+ UDItype __m0 = (m0), __m1 = (m1); \
+ (ph) = __UMULH (__m0, __m1); \
+ (pl) = __m0 * __m1; \
+ } while (0)
+#endif
+#ifndef LONGLONG_STANDALONE
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { UWtype __di; \
+ __di = __MPN(invert_limb) (d); \
+ udiv_qrnnd_preinv (q, r, n1, n0, d, __di); \
+ } while (0)
+#define UDIV_PREINV_ALWAYS 1
+#define UDIV_NEEDS_NORMALIZATION 1
+#define UDIV_TIME 220
+#endif /* LONGLONG_STANDALONE */
+
+/* clz_tab is required in all configurations, since mpn/alpha/cntlz.asm
+ always goes into libgmp.so, even when not actually used. */
+#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB
+
+#if defined (__GNUC__) && HAVE_HOST_CPU_alpha_CIX
+#define count_leading_zeros(COUNT,X) \
+ __asm__("ctlz %1,%0" : "=r"(COUNT) : "r"(X))
+#define count_trailing_zeros(COUNT,X) \
+ __asm__("cttz %1,%0" : "=r"(COUNT) : "r"(X))
+#endif /* clz/ctz using cix */
+
+#if ! defined (count_leading_zeros) \
+ && defined (__GNUC__) && ! defined (LONGLONG_STANDALONE)
+/* ALPHA_CMPBGE_0 gives "cmpbge $31,src,dst", ie. test src bytes == 0.
+ "$31" is written explicitly in the asm, since an "r" constraint won't
+ select reg 31. There seems no need to worry about "r31" syntax for cray,
+ since gcc itself (pre-release 3.4) emits just $31 in various places. */
+#define ALPHA_CMPBGE_0(dst, src) \
+ do { asm ("cmpbge $31, %1, %0" : "=r" (dst) : "r" (src)); } while (0)
+/* Zero bytes are turned into bits with cmpbge, a __clz_tab lookup counts
+ them, locating the highest non-zero byte. A second __clz_tab lookup
+ counts the leading zero bits in that byte, giving the result. */
+#define count_leading_zeros(count, x) \
+ do { \
+ UWtype __clz__b, __clz__c, __clz__x = (x); \
+ ALPHA_CMPBGE_0 (__clz__b, __clz__x); /* zero bytes */ \
+ __clz__b = __clz_tab [(__clz__b >> 1) ^ 0x7F]; /* 8 to 1 byte */ \
+ __clz__b = __clz__b * 8 - 7; /* 57 to 1 shift */ \
+ __clz__x >>= __clz__b; \
+ __clz__c = __clz_tab [__clz__x]; /* 8 to 1 bit */ \
+ __clz__b = 65 - __clz__b; \
+ (count) = __clz__b - __clz__c; \
+ } while (0)
+#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB
+#endif /* clz using cmpbge */
+
+#if ! defined (count_leading_zeros) && ! defined (LONGLONG_STANDALONE)
+#if HAVE_ATTRIBUTE_CONST
+long __MPN(count_leading_zeros) (UDItype) __attribute__ ((const));
+#else
+long __MPN(count_leading_zeros) (UDItype);
+#endif
+#define count_leading_zeros(count, x) \
+ ((count) = __MPN(count_leading_zeros) (x))
+#endif /* clz using mpn */
+#endif /* __alpha */
+
+#if defined (__AVR) && W_TYPE_SIZE == 8
+#define umul_ppmm(ph, pl, m0, m1) \
+ do { \
+ unsigned short __p = (unsigned short) (m0) * (m1); \
+ (ph) = __p >> 8; \
+ (pl) = __p; \
+ } while (0)
+#endif /* AVR */
+
+#if defined (_CRAY) && W_TYPE_SIZE == 64
+#include <intrinsics.h>
+#define UDIV_PREINV_ALWAYS 1
+#define UDIV_NEEDS_NORMALIZATION 1
+#define UDIV_TIME 220
+long __MPN(count_leading_zeros) (UDItype);
+#define count_leading_zeros(count, x) \
+ ((count) = _leadz ((UWtype) (x)))
+#if defined (_CRAYIEEE) /* I.e., Cray T90/ieee, T3D, and T3E */
+#define umul_ppmm(ph, pl, m0, m1) \
+ do { \
+ UDItype __m0 = (m0), __m1 = (m1); \
+ (ph) = _int_mult_upper (__m0, __m1); \
+ (pl) = __m0 * __m1; \
+ } while (0)
+#ifndef LONGLONG_STANDALONE
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { UWtype __di; \
+ __di = __MPN(invert_limb) (d); \
+ udiv_qrnnd_preinv (q, r, n1, n0, d, __di); \
+ } while (0)
+#endif /* LONGLONG_STANDALONE */
+#endif /* _CRAYIEEE */
+#endif /* _CRAY */
+
+#if defined (__ia64) && W_TYPE_SIZE == 64
+/* This form encourages gcc (pre-release 3.4 at least) to emit predicated
+ "sub r=r,r" and "sub r=r,r,1", giving a 2 cycle latency. The generic
+ code using "al<bl" arithmetically comes out making an actual 0 or 1 in a
+ register, which takes an extra cycle. */
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ do { \
+ UWtype __x; \
+ __x = (al) - (bl); \
+ if ((al) < (bl)) \
+ (sh) = (ah) - (bh) - 1; \
+ else \
+ (sh) = (ah) - (bh); \
+ (sl) = __x; \
+ } while (0)
+#if defined (__GNUC__) && ! defined (__INTEL_COMPILER)
+/* Do both product parts in assembly, since that gives better code with
+ all gcc versions. Some callers will just use the upper part, and in
+ that situation we waste an instruction, but not any cycles. */
+#define umul_ppmm(ph, pl, m0, m1) \
+ __asm__ ("xma.hu %0 = %2, %3, f0\n\txma.l %1 = %2, %3, f0" \
+ : "=&f" (ph), "=f" (pl) \
+ : "f" (m0), "f" (m1))
+#define UMUL_TIME 14
+#define count_leading_zeros(count, x) \
+ do { \
+ UWtype _x = (x), _y, _a, _c; \
+ __asm__ ("mux1 %0 = %1, @rev" : "=r" (_y) : "r" (_x)); \
+ __asm__ ("czx1.l %0 = %1" : "=r" (_a) : "r" (-_y | _y)); \
+ _c = (_a - 1) << 3; \
+ _x >>= _c; \
+ if (_x >= 1 << 4) \
+ _x >>= 4, _c += 4; \
+ if (_x >= 1 << 2) \
+ _x >>= 2, _c += 2; \
+ _c += _x >> 1; \
+ (count) = W_TYPE_SIZE - 1 - _c; \
+ } while (0)
+/* similar to what gcc does for __builtin_ffs, but 0 based rather than 1
+ based, and we don't need a special case for x==0 here */
+#define count_trailing_zeros(count, x) \
+ do { \
+ UWtype __ctz_x = (x); \
+ __asm__ ("popcnt %0 = %1" \
+ : "=r" (count) \
+ : "r" ((__ctz_x-1) & ~__ctz_x)); \
+ } while (0)
+#endif
+#if defined (__INTEL_COMPILER)
+#include <ia64intrin.h>
+#define umul_ppmm(ph, pl, m0, m1) \
+ do { \
+ UWtype __m0 = (m0), __m1 = (m1); \
+ ph = _m64_xmahu (__m0, __m1, 0); \
+ pl = __m0 * __m1; \
+ } while (0)
+#endif
+#ifndef LONGLONG_STANDALONE
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { UWtype __di; \
+ __di = __MPN(invert_limb) (d); \
+ udiv_qrnnd_preinv (q, r, n1, n0, d, __di); \
+ } while (0)
+#define UDIV_PREINV_ALWAYS 1
+#define UDIV_NEEDS_NORMALIZATION 1
+#endif
+#define UDIV_TIME 220
+#endif
+
+
+#if defined (__GNUC__)
+
+/* We sometimes need to clobber "cc" with gcc2, but that would not be
+ understood by gcc1. Use cpp to avoid major code duplication. */
+#if __GNUC__ < 2
+#define __CLOBBER_CC
+#define __AND_CLOBBER_CC
+#else /* __GNUC__ >= 2 */
+#define __CLOBBER_CC : "cc"
+#define __AND_CLOBBER_CC , "cc"
+#endif /* __GNUC__ < 2 */
+
+#if (defined (__a29k__) || defined (_AM29K)) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add %1,%4,%5\n\taddc %0,%2,%3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" (ah), "rI" (bh), "%r" (al), "rI" (bl))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub %1,%4,%5\n\tsubc %0,%2,%3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" (ah), "rI" (bh), "r" (al), "rI" (bl))
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("multiplu %0,%1,%2" \
+ : "=r" (xl) \
+ : "r" (__m0), "r" (__m1)); \
+ __asm__ ("multmu %0,%1,%2" \
+ : "=r" (xh) \
+ : "r" (__m0), "r" (__m1)); \
+ } while (0)
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("dividu %0,%3,%4" \
+ : "=r" (q), "=q" (r) \
+ : "1" (n1), "r" (n0), "r" (d))
+#define count_leading_zeros(count, x) \
+ __asm__ ("clz %0,%1" \
+ : "=r" (count) \
+ : "r" (x))
+#define COUNT_LEADING_ZEROS_0 32
+#endif /* __a29k__ */
+
+#if defined (__arc__)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add.f\t%1, %4, %5\n\tadc\t%0, %2, %3" \
+ : "=r" (sh), \
+ "=&r" (sl) \
+ : "r" ((USItype) (ah)), \
+ "rIJ" ((USItype) (bh)), \
+ "%r" ((USItype) (al)), \
+ "rIJ" ((USItype) (bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub.f\t%1, %4, %5\n\tsbc\t%0, %2, %3" \
+ : "=r" (sh), \
+ "=&r" (sl) \
+ : "r" ((USItype) (ah)), \
+ "rIJ" ((USItype) (bh)), \
+ "r" ((USItype) (al)), \
+ "rIJ" ((USItype) (bl)))
+#endif
+
+#if defined (__arm__) && (defined (__thumb2__) || !defined (__thumb__)) \
+ && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("adds\t%1, %4, %5\n\tadc\t%0, %2, %3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" (ah), "rI" (bh), "%r" (al), "rI" (bl) __CLOBBER_CC)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ do { \
+ if (__builtin_constant_p (al)) \
+ { \
+ if (__builtin_constant_p (ah)) \
+ __asm__ ("rsbs\t%1, %5, %4\n\trsc\t%0, %3, %2" \
+ : "=r" (sh), "=&r" (sl) \
+ : "rI" (ah), "r" (bh), "rI" (al), "r" (bl) __CLOBBER_CC); \
+ else \
+ __asm__ ("rsbs\t%1, %5, %4\n\tsbc\t%0, %2, %3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" (ah), "rI" (bh), "rI" (al), "r" (bl) __CLOBBER_CC); \
+ } \
+ else if (__builtin_constant_p (ah)) \
+ { \
+ if (__builtin_constant_p (bl)) \
+ __asm__ ("subs\t%1, %4, %5\n\trsc\t%0, %3, %2" \
+ : "=r" (sh), "=&r" (sl) \
+ : "rI" (ah), "r" (bh), "r" (al), "rI" (bl) __CLOBBER_CC); \
+ else \
+ __asm__ ("rsbs\t%1, %5, %4\n\trsc\t%0, %3, %2" \
+ : "=r" (sh), "=&r" (sl) \
+ : "rI" (ah), "r" (bh), "rI" (al), "r" (bl) __CLOBBER_CC); \
+ } \
+ else if (__builtin_constant_p (bl)) \
+ { \
+ if (__builtin_constant_p (bh)) \
+ __asm__ ("subs\t%1, %4, %5\n\tsbc\t%0, %2, %3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" (ah), "rI" (bh), "r" (al), "rI" (bl) __CLOBBER_CC); \
+ else \
+ __asm__ ("subs\t%1, %4, %5\n\trsc\t%0, %3, %2" \
+ : "=r" (sh), "=&r" (sl) \
+ : "rI" (ah), "r" (bh), "r" (al), "rI" (bl) __CLOBBER_CC); \
+ } \
+ else /* only bh might be a constant */ \
+ __asm__ ("subs\t%1, %4, %5\n\tsbc\t%0, %2, %3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" (ah), "rI" (bh), "r" (al), "rI" (bl) __CLOBBER_CC);\
+ } while (0)
+#if defined (__ARM_ARCH_2__) || defined (__ARM_ARCH_2A__) \
+ || defined (__ARM_ARCH_3__)
+#define umul_ppmm(xh, xl, a, b) \
+ do { \
+ register USItype __t0, __t1, __t2; \
+ __asm__ ("%@ Inlined umul_ppmm\n" \
+ " mov %2, %5, lsr #16\n" \
+ " mov %0, %6, lsr #16\n" \
+ " bic %3, %5, %2, lsl #16\n" \
+ " bic %4, %6, %0, lsl #16\n" \
+ " mul %1, %3, %4\n" \
+ " mul %4, %2, %4\n" \
+ " mul %3, %0, %3\n" \
+ " mul %0, %2, %0\n" \
+ " adds %3, %4, %3\n" \
+ " addcs %0, %0, #65536\n" \
+ " adds %1, %1, %3, lsl #16\n" \
+ " adc %0, %0, %3, lsr #16" \
+ : "=&r" ((USItype) (xh)), "=r" ((USItype) (xl)), \
+ "=&r" (__t0), "=&r" (__t1), "=r" (__t2) \
+ : "r" ((USItype) (a)), "r" ((USItype) (b)) __CLOBBER_CC); \
+ } while (0)
+#define UMUL_TIME 20
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { UWtype __r; \
+ (q) = __MPN(udiv_qrnnd) (&__r, (n1), (n0), (d)); \
+ (r) = __r; \
+ } while (0)
+extern UWtype __MPN(udiv_qrnnd) (UWtype *, UWtype, UWtype, UWtype);
+#define UDIV_TIME 200
+#else /* ARMv4 or newer */
+#define umul_ppmm(xh, xl, a, b) \
+ __asm__ ("umull %0,%1,%2,%3" : "=&r" (xl), "=&r" (xh) : "r" (a), "r" (b))
+#define UMUL_TIME 5
+#define smul_ppmm(xh, xl, a, b) \
+ __asm__ ("smull %0,%1,%2,%3" : "=&r" (xl), "=&r" (xh) : "r" (a), "r" (b))
+#ifndef LONGLONG_STANDALONE
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { UWtype __di; \
+ __di = __MPN(invert_limb) (d); \
+ udiv_qrnnd_preinv (q, r, n1, n0, d, __di); \
+ } while (0)
+#define UDIV_PREINV_ALWAYS 1
+#define UDIV_NEEDS_NORMALIZATION 1
+#define UDIV_TIME 70
+#endif /* LONGLONG_STANDALONE */
+#endif /* defined(__ARM_ARCH_2__) ... */
+#define count_leading_zeros(count, x) count_leading_zeros_gcc_clz(count, x)
+#define count_trailing_zeros(count, x) count_trailing_zeros_gcc_ctz(count, x)
+#define COUNT_LEADING_ZEROS_0 32
+#endif /* __arm__ */
+
+#if defined (__aarch64__) && W_TYPE_SIZE == 64
+/* FIXME: Extend the immediate range for the low word by using both
+ ADDS and SUBS, since they set carry in the same way. */
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("adds\t%1, %x4, %5\n\tadc\t%0, %x2, %x3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "rZ" ((UDItype)(ah)), "rZ" ((UDItype)(bh)), \
+ "%r" ((UDItype)(al)), "rI" ((UDItype)(bl)) __CLOBBER_CC)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subs\t%1, %x4, %5\n\tsbc\t%0, %x2, %x3" \
+ : "=r,r" (sh), "=&r,&r" (sl) \
+ : "rZ,rZ" ((UDItype)(ah)), "rZ,rZ" ((UDItype)(bh)), \
+ "r,Z" ((UDItype)(al)), "rI,r" ((UDItype)(bl)) __CLOBBER_CC)
+#define umul_ppmm(ph, pl, m0, m1) \
+ do { \
+ UDItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("umulh\t%0, %1, %2" : "=r" (ph) : "r" (__m0), "r" (__m1)); \
+ (pl) = __m0 * __m1; \
+ } while (0)
+#define count_leading_zeros(count, x) count_leading_zeros_gcc_clz(count, x)
+#define count_trailing_zeros(count, x) count_trailing_zeros_gcc_ctz(count, x)
+#define COUNT_LEADING_ZEROS_0 64
+#endif /* __aarch64__ */
+
+#if defined (__clipper__) && W_TYPE_SIZE == 32
+#define umul_ppmm(w1, w0, u, v) \
+ ({union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __x; \
+ __asm__ ("mulwux %2,%0" \
+ : "=r" (__x.__ll) \
+ : "%0" ((USItype)(u)), "r" ((USItype)(v))); \
+ (w1) = __x.__i.__h; (w0) = __x.__i.__l;})
+#define smul_ppmm(w1, w0, u, v) \
+ ({union {DItype __ll; \
+ struct {SItype __l, __h;} __i; \
+ } __x; \
+ __asm__ ("mulwx %2,%0" \
+ : "=r" (__x.__ll) \
+ : "%0" ((SItype)(u)), "r" ((SItype)(v))); \
+ (w1) = __x.__i.__h; (w0) = __x.__i.__l;})
+#define __umulsidi3(u, v) \
+ ({UDItype __w; \
+ __asm__ ("mulwux %2,%0" \
+ : "=r" (__w) : "%0" ((USItype)(u)), "r" ((USItype)(v))); \
+ __w; })
+#endif /* __clipper__ */
+
+/* Fujitsu vector computers. */
+#if defined (__uxp__) && W_TYPE_SIZE == 32
+#define umul_ppmm(ph, pl, u, v) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __x; \
+ __asm__ ("mult.lu %1,%2,%0" : "=r" (__x.__ll) : "%r" (u), "rK" (v));\
+ (ph) = __x.__i.__h; \
+ (pl) = __x.__i.__l; \
+ } while (0)
+#define smul_ppmm(ph, pl, u, v) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __x; \
+ __asm__ ("mult.l %1,%2,%0" : "=r" (__x.__ll) : "%r" (u), "rK" (v)); \
+ (ph) = __x.__i.__h; \
+ (pl) = __x.__i.__l; \
+ } while (0)
+#endif
+
+#if defined (__gmicro__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add.w %5,%1\n\taddx %3,%0" \
+ : "=g" (sh), "=&g" (sl) \
+ : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub.w %5,%1\n\tsubx %3,%0" \
+ : "=g" (sh), "=&g" (sl) \
+ : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \
+ "1" ((USItype)(al)), "g" ((USItype)(bl)))
+#define umul_ppmm(ph, pl, m0, m1) \
+ __asm__ ("mulx %3,%0,%1" \
+ : "=g" (ph), "=r" (pl) \
+ : "%0" ((USItype)(m0)), "g" ((USItype)(m1)))
+#define udiv_qrnnd(q, r, nh, nl, d) \
+ __asm__ ("divx %4,%0,%1" \
+ : "=g" (q), "=r" (r) \
+ : "1" ((USItype)(nh)), "0" ((USItype)(nl)), "g" ((USItype)(d)))
+#define count_leading_zeros(count, x) \
+ __asm__ ("bsch/1 %1,%0" \
+ : "=g" (count) : "g" ((USItype)(x)), "0" ((USItype)0))
+#endif
+
+#if defined (__hppa) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add%I5 %5,%r4,%1\n\taddc %r2,%r3,%0" \
+ : "=r" (sh), "=&r" (sl) \
+ : "rM" (ah), "rM" (bh), "%rM" (al), "rI" (bl))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub%I4 %4,%r5,%1\n\tsubb %r2,%r3,%0" \
+ : "=r" (sh), "=&r" (sl) \
+ : "rM" (ah), "rM" (bh), "rI" (al), "rM" (bl))
+#if defined (_PA_RISC1_1)
+#define umul_ppmm(wh, wl, u, v) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __x; \
+ __asm__ ("xmpyu %1,%2,%0" : "=*f" (__x.__ll) : "*f" (u), "*f" (v)); \
+ (wh) = __x.__i.__h; \
+ (wl) = __x.__i.__l; \
+ } while (0)
+#define UMUL_TIME 8
+#define UDIV_TIME 60
+#else
+#define UMUL_TIME 40
+#define UDIV_TIME 80
+#endif
+#define count_leading_zeros(count, x) \
+ do { \
+ USItype __tmp; \
+ __asm__ ( \
+ "ldi 1,%0\n" \
+" extru,= %1,15,16,%%r0 ; Bits 31..16 zero?\n" \
+" extru,tr %1,15,16,%1 ; No. Shift down, skip add.\n" \
+" ldo 16(%0),%0 ; Yes. Perform add.\n" \
+" extru,= %1,23,8,%%r0 ; Bits 15..8 zero?\n" \
+" extru,tr %1,23,8,%1 ; No. Shift down, skip add.\n" \
+" ldo 8(%0),%0 ; Yes. Perform add.\n" \
+" extru,= %1,27,4,%%r0 ; Bits 7..4 zero?\n" \
+" extru,tr %1,27,4,%1 ; No. Shift down, skip add.\n" \
+" ldo 4(%0),%0 ; Yes. Perform add.\n" \
+" extru,= %1,29,2,%%r0 ; Bits 3..2 zero?\n" \
+" extru,tr %1,29,2,%1 ; No. Shift down, skip add.\n" \
+" ldo 2(%0),%0 ; Yes. Perform add.\n" \
+" extru %1,30,1,%1 ; Extract bit 1.\n" \
+" sub %0,%1,%0 ; Subtract it.\n" \
+ : "=r" (count), "=r" (__tmp) : "1" (x)); \
+ } while (0)
+#endif /* hppa */
+
+/* These macros are for ABI=2.0w. In ABI=2.0n they can't be used, since GCC
+ (3.2) puts longlong into two adjacent 32-bit registers. Presumably this
+ is just a case of no direct support for 2.0n but treating it like 1.0. */
+#if defined (__hppa) && W_TYPE_SIZE == 64 && ! defined (_LONG_LONG_LIMB)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add%I5 %5,%r4,%1\n\tadd,dc %r2,%r3,%0" \
+ : "=r" (sh), "=&r" (sl) \
+ : "rM" (ah), "rM" (bh), "%rM" (al), "rI" (bl))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub%I4 %4,%r5,%1\n\tsub,db %r2,%r3,%0" \
+ : "=r" (sh), "=&r" (sl) \
+ : "rM" (ah), "rM" (bh), "rI" (al), "rM" (bl))
+#endif /* hppa */
+
+#if (defined (__i370__) || defined (__s390__) || defined (__mvs__)) && W_TYPE_SIZE == 32
+#if defined (__zarch__) || defined (HAVE_HOST_CPU_s390_zarch)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ do { \
+/* if (__builtin_constant_p (bl)) \
+ __asm__ ("alfi\t%1,%o5\n\talcr\t%0,%3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "0" (ah), "r" (bh), "%1" (al), "n" (bl) __CLOBBER_CC);\
+ else \
+*/ __asm__ ("alr\t%1,%5\n\talcr\t%0,%3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "0" (ah), "r" (bh), "%1" (al), "r" (bl)__CLOBBER_CC); \
+ } while (0)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ do { \
+/* if (__builtin_constant_p (bl)) \
+ __asm__ ("slfi\t%1,%o5\n\tslbr\t%0,%3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "0" (ah), "r" (bh), "1" (al), "n" (bl) __CLOBBER_CC); \
+ else \
+*/ __asm__ ("slr\t%1,%5\n\tslbr\t%0,%3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "0" (ah), "r" (bh), "1" (al), "r" (bl) __CLOBBER_CC); \
+ } while (0)
+#if __GMP_GNUC_PREREQ (4,5)
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __x; \
+ __x.__ll = (UDItype) (m0) * (UDItype) (m1); \
+ (xh) = __x.__i.__h; (xl) = __x.__i.__l; \
+ } while (0)
+#else
+#if 0
+/* FIXME: this fails if gcc knows about the 64-bit registers. Use only
+ with a new enough processor pretending we have 32-bit registers. */
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __x; \
+ __asm__ ("mlr\t%0,%2" \
+ : "=r" (__x.__ll) \
+ : "%0" (m0), "r" (m1)); \
+ (xh) = __x.__i.__h; (xl) = __x.__i.__l; \
+ } while (0)
+#else
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ /* When we have 64-bit regs and gcc is aware of that, we cannot simply use
+ DImode for the product, since that would be allocated to a single 64-bit
+ register, whereas mlr uses the low 32-bits of an even-odd register pair.
+ */ \
+ register USItype __r0 __asm__ ("0"); \
+ register USItype __r1 __asm__ ("1") = (m0); \
+ __asm__ ("mlr\t%0,%3" \
+ : "=r" (__r0), "=r" (__r1) \
+ : "r" (__r1), "r" (m1)); \
+ (xh) = __r0; (xl) = __r1; \
+ } while (0)
+#endif /* if 0 */
+#endif
+#if 0
+/* FIXME: this fails if gcc knows about the 64-bit registers. Use only
+ with a new enough processor pretending we have 32-bit registers. */
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __x; \
+ __x.__i.__h = n1; __x.__i.__l = n0; \
+ __asm__ ("dlr\t%0,%2" \
+ : "=r" (__x.__ll) \
+ : "0" (__x.__ll), "r" (d)); \
+ (q) = __x.__i.__l; (r) = __x.__i.__h; \
+ } while (0)
+#else
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { \
+ register USItype __r0 __asm__ ("0") = (n1); \
+ register USItype __r1 __asm__ ("1") = (n0); \
+ __asm__ ("dlr\t%0,%4" \
+ : "=r" (__r0), "=r" (__r1) \
+ : "r" (__r0), "r" (__r1), "r" (d)); \
+ (q) = __r1; (r) = __r0; \
+ } while (0)
+#endif /* if 0 */
+#else /* if __zarch__ */
+/* FIXME: this fails if gcc knows about the 64-bit registers. */
+#define smul_ppmm(xh, xl, m0, m1) \
+ do { \
+ union {DItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __x; \
+ __asm__ ("mr\t%0,%2" \
+ : "=r" (__x.__ll) \
+ : "%0" (m0), "r" (m1)); \
+ (xh) = __x.__i.__h; (xl) = __x.__i.__l; \
+ } while (0)
+/* FIXME: this fails if gcc knows about the 64-bit registers. */
+#define sdiv_qrnnd(q, r, n1, n0, d) \
+ do { \
+ union {DItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __x; \
+ __x.__i.__h = n1; __x.__i.__l = n0; \
+ __asm__ ("dr\t%0,%2" \
+ : "=r" (__x.__ll) \
+ : "0" (__x.__ll), "r" (d)); \
+ (q) = __x.__i.__l; (r) = __x.__i.__h; \
+ } while (0)
+#endif /* if __zarch__ */
+#endif
+
+#if defined (__s390x__) && W_TYPE_SIZE == 64
+/* We need to cast operands with register constraints, otherwise their types
+ will be assumed to be SImode by gcc. For these machines, such operations
+ will insert a value into the low 32 bits, and leave the high 32 bits with
+ garbage. */
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ do { \
+ __asm__ ("algr\t%1,%5\n\talcgr\t%0,%3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "0" ((UDItype)(ah)), "r" ((UDItype)(bh)), \
+ "%1" ((UDItype)(al)), "r" ((UDItype)(bl)) __CLOBBER_CC); \
+ } while (0)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ do { \
+ __asm__ ("slgr\t%1,%5\n\tslbgr\t%0,%3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "0" ((UDItype)(ah)), "r" ((UDItype)(bh)), \
+ "1" ((UDItype)(al)), "r" ((UDItype)(bl)) __CLOBBER_CC); \
+ } while (0)
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ union {unsigned int __attribute__ ((mode(TI))) __ll; \
+ struct {UDItype __h, __l;} __i; \
+ } __x; \
+ __asm__ ("mlgr\t%0,%2" \
+ : "=r" (__x.__ll) \
+ : "%0" ((UDItype)(m0)), "r" ((UDItype)(m1))); \
+ (xh) = __x.__i.__h; (xl) = __x.__i.__l; \
+ } while (0)
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { \
+ union {unsigned int __attribute__ ((mode(TI))) __ll; \
+ struct {UDItype __h, __l;} __i; \
+ } __x; \
+ __x.__i.__h = n1; __x.__i.__l = n0; \
+ __asm__ ("dlgr\t%0,%2" \
+ : "=r" (__x.__ll) \
+ : "0" (__x.__ll), "r" ((UDItype)(d))); \
+ (q) = __x.__i.__l; (r) = __x.__i.__h; \
+ } while (0)
+#if 0 /* FIXME: Enable for z10 (?) */
+#define count_leading_zeros(cnt, x) \
+ do { \
+ union {unsigned int __attribute__ ((mode(TI))) __ll; \
+ struct {UDItype __h, __l;} __i; \
+ } __clr_cnt; \
+ __asm__ ("flogr\t%0,%1" \
+ : "=r" (__clr_cnt.__ll) \
+ : "r" (x) __CLOBBER_CC); \
+ (cnt) = __clr_cnt.__i.__h; \
+ } while (0)
+#endif
+#endif
+
+/* On x86 and x86_64, every asm implicitly clobbers "flags" and "fpsr",
+ so we don't need __CLOBBER_CC. */
+#if (defined (__i386__) || defined (__i486__)) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addl %5,%k1\n\tadcl %3,%k0" \
+ : "=r" (sh), "=&r" (sl) \
+ : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subl %5,%k1\n\tsbbl %3,%k0" \
+ : "=r" (sh), "=&r" (sl) \
+ : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \
+ "1" ((USItype)(al)), "g" ((USItype)(bl)))
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("mull %3" \
+ : "=a" (w0), "=d" (w1) \
+ : "%0" ((USItype)(u)), "rm" ((USItype)(v)))
+#define udiv_qrnnd(q, r, n1, n0, dx) /* d renamed to dx avoiding "=d" */\
+ __asm__ ("divl %4" /* stringification in K&R C */ \
+ : "=a" (q), "=d" (r) \
+ : "0" ((USItype)(n0)), "1" ((USItype)(n1)), "rm" ((USItype)(dx)))
+
+#if HAVE_HOST_CPU_i586 || HAVE_HOST_CPU_pentium || HAVE_HOST_CPU_pentiummmx
+/* Pentium bsrl takes between 10 and 72 cycles depending where the most
+ significant 1 bit is, hence the use of the following alternatives. bsfl
+ is slow too, between 18 and 42 depending where the least significant 1
+ bit is, so let the generic count_trailing_zeros below make use of the
+ count_leading_zeros here too. */
+
+#if HAVE_HOST_CPU_pentiummmx && ! defined (LONGLONG_STANDALONE)
+/* The following should be a fixed 14 or 15 cycles, but possibly plus an L1
+ cache miss reading from __clz_tab. For P55 it's favoured over the float
+ below so as to avoid mixing MMX and x87, since the penalty for switching
+ between the two is about 100 cycles.
+
+ The asm block sets __shift to -3 if the high 24 bits are clear, -2 for
+ 16, -1 for 8, or 0 otherwise. This could be written equivalently as
+ follows, but as of gcc 2.95.2 it results in conditional jumps.
+
+ __shift = -(__n < 0x1000000);
+ __shift -= (__n < 0x10000);
+ __shift -= (__n < 0x100);
+
+ The middle two sbbl and cmpl's pair, and with luck something gcc
+ generates might pair with the first cmpl and the last sbbl. The "32+1"
+ constant could be folded into __clz_tab[], but it doesn't seem worth
+ making a different table just for that. */
+
+#define count_leading_zeros(c,n) \
+ do { \
+ USItype __n = (n); \
+ USItype __shift; \
+ __asm__ ("cmpl $0x1000000, %1\n" \
+ "sbbl %0, %0\n" \
+ "cmpl $0x10000, %1\n" \
+ "sbbl $0, %0\n" \
+ "cmpl $0x100, %1\n" \
+ "sbbl $0, %0\n" \
+ : "=&r" (__shift) : "r" (__n)); \
+ __shift = __shift*8 + 24 + 1; \
+ (c) = 32 + 1 - __shift - __clz_tab[__n >> __shift]; \
+ } while (0)
+#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB
+#define COUNT_LEADING_ZEROS_0 31 /* n==0 indistinguishable from n==1 */
+
+#else /* ! pentiummmx || LONGLONG_STANDALONE */
+/* The following should be a fixed 14 cycles or so. Some scheduling
+ opportunities should be available between the float load/store too. This
+ sort of code is used in gcc 3 for __builtin_ffs (with "n&-n") and is
+ apparently suggested by the Intel optimizing manual (don't know exactly
+ where). gcc 2.95 or up will be best for this, so the "double" is
+ correctly aligned on the stack. */
+#define count_leading_zeros(c,n) \
+ do { \
+ union { \
+ double d; \
+ unsigned a[2]; \
+ } __u; \
+ ASSERT ((n) != 0); \
+ __u.d = (UWtype) (n); \
+ (c) = 0x3FF + 31 - (__u.a[1] >> 20); \
+ } while (0)
+#define COUNT_LEADING_ZEROS_0 (0x3FF + 31)
+#endif /* pentiummx */
+
+#else /* ! pentium */
+
+#if __GMP_GNUC_PREREQ (3,4) /* using bsrl */
+#define count_leading_zeros(count,x) count_leading_zeros_gcc_clz(count,x)
+#endif /* gcc clz */
+
+/* On P6, gcc prior to 3.0 generates a partial register stall for
+ __cbtmp^31, due to using "xorb $31" instead of "xorl $31", the former
+ being 1 code byte smaller. "31-__cbtmp" is a workaround, probably at the
+ cost of one extra instruction. Do this for "i386" too, since that means
+ generic x86. */
+#if ! defined (count_leading_zeros) && __GNUC__ < 3 \
+ && (HAVE_HOST_CPU_i386 \
+ || HAVE_HOST_CPU_i686 \
+ || HAVE_HOST_CPU_pentiumpro \
+ || HAVE_HOST_CPU_pentium2 \
+ || HAVE_HOST_CPU_pentium3)
+#define count_leading_zeros(count, x) \
+ do { \
+ USItype __cbtmp; \
+ ASSERT ((x) != 0); \
+ __asm__ ("bsrl %1,%0" : "=r" (__cbtmp) : "rm" ((USItype)(x))); \
+ (count) = 31 - __cbtmp; \
+ } while (0)
+#endif /* gcc<3 asm bsrl */
+
+#ifndef count_leading_zeros
+#define count_leading_zeros(count, x) \
+ do { \
+ USItype __cbtmp; \
+ ASSERT ((x) != 0); \
+ __asm__ ("bsrl %1,%0" : "=r" (__cbtmp) : "rm" ((USItype)(x))); \
+ (count) = __cbtmp ^ 31; \
+ } while (0)
+#endif /* asm bsrl */
+
+#if __GMP_GNUC_PREREQ (3,4) /* using bsfl */
+#define count_trailing_zeros(count,x) count_trailing_zeros_gcc_ctz(count,x)
+#endif /* gcc ctz */
+
+#ifndef count_trailing_zeros
+#define count_trailing_zeros(count, x) \
+ do { \
+ ASSERT ((x) != 0); \
+ __asm__ ("bsfl %1,%k0" : "=r" (count) : "rm" ((USItype)(x))); \
+ } while (0)
+#endif /* asm bsfl */
+
+#endif /* ! pentium */
+
+#ifndef UMUL_TIME
+#define UMUL_TIME 10
+#endif
+#ifndef UDIV_TIME
+#define UDIV_TIME 40
+#endif
+#endif /* 80x86 */
+
+#if defined (__amd64__) && W_TYPE_SIZE == 64
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addq %5,%q1\n\tadcq %3,%q0" \
+ : "=r" (sh), "=&r" (sl) \
+ : "0" ((UDItype)(ah)), "rme" ((UDItype)(bh)), \
+ "%1" ((UDItype)(al)), "rme" ((UDItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subq %5,%q1\n\tsbbq %3,%q0" \
+ : "=r" (sh), "=&r" (sl) \
+ : "0" ((UDItype)(ah)), "rme" ((UDItype)(bh)), \
+ "1" ((UDItype)(al)), "rme" ((UDItype)(bl)))
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("mulq %3" \
+ : "=a" (w0), "=d" (w1) \
+ : "%0" ((UDItype)(u)), "rm" ((UDItype)(v)))
+#define udiv_qrnnd(q, r, n1, n0, dx) /* d renamed to dx avoiding "=d" */\
+ __asm__ ("divq %4" /* stringification in K&R C */ \
+ : "=a" (q), "=d" (r) \
+ : "0" ((UDItype)(n0)), "1" ((UDItype)(n1)), "rm" ((UDItype)(dx)))
+/* bsrq destination must be a 64-bit register, hence UDItype for __cbtmp. */
+#define count_leading_zeros(count, x) \
+ do { \
+ UDItype __cbtmp; \
+ ASSERT ((x) != 0); \
+ __asm__ ("bsrq %1,%0" : "=r" (__cbtmp) : "rm" ((UDItype)(x))); \
+ (count) = __cbtmp ^ 63; \
+ } while (0)
+/* bsfq destination must be a 64-bit register, "%q0" forces this in case
+ count is only an int. */
+#define count_trailing_zeros(count, x) \
+ do { \
+ ASSERT ((x) != 0); \
+ __asm__ ("bsfq %1,%q0" : "=r" (count) : "rm" ((UDItype)(x))); \
+ } while (0)
+#endif /* __amd64__ */
+
+#if defined (__i860__) && W_TYPE_SIZE == 32
+#define rshift_rhlc(r,h,l,c) \
+ __asm__ ("shr %3,r0,r0\;shrd %1,%2,%0" \
+ "=r" (r) : "r" (h), "r" (l), "rn" (c))
+#endif /* i860 */
+
+#if defined (__i960__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("cmpo 1,0\;addc %5,%4,%1\;addc %3,%2,%0" \
+ : "=r" (sh), "=&r" (sl) \
+ : "dI" (ah), "dI" (bh), "%dI" (al), "dI" (bl))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("cmpo 0,0\;subc %5,%4,%1\;subc %3,%2,%0" \
+ : "=r" (sh), "=&r" (sl) \
+ : "dI" (ah), "dI" (bh), "dI" (al), "dI" (bl))
+#define umul_ppmm(w1, w0, u, v) \
+ ({union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __x; \
+ __asm__ ("emul %2,%1,%0" \
+ : "=d" (__x.__ll) : "%dI" (u), "dI" (v)); \
+ (w1) = __x.__i.__h; (w0) = __x.__i.__l;})
+#define __umulsidi3(u, v) \
+ ({UDItype __w; \
+ __asm__ ("emul %2,%1,%0" : "=d" (__w) : "%dI" (u), "dI" (v)); \
+ __w; })
+#define udiv_qrnnd(q, r, nh, nl, d) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __nn; \
+ __nn.__i.__h = (nh); __nn.__i.__l = (nl); \
+ __asm__ ("ediv %d,%n,%0" \
+ : "=d" (__rq.__ll) : "dI" (__nn.__ll), "dI" (d)); \
+ (r) = __rq.__i.__l; (q) = __rq.__i.__h; \
+ } while (0)
+#define count_leading_zeros(count, x) \
+ do { \
+ USItype __cbtmp; \
+ __asm__ ("scanbit %1,%0" : "=r" (__cbtmp) : "r" (x)); \
+ (count) = __cbtmp ^ 31; \
+ } while (0)
+#define COUNT_LEADING_ZEROS_0 (-32) /* sic */
+#if defined (__i960mx) /* what is the proper symbol to test??? */
+#define rshift_rhlc(r,h,l,c) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __nn; \
+ __nn.__i.__h = (h); __nn.__i.__l = (l); \
+ __asm__ ("shre %2,%1,%0" : "=d" (r) : "dI" (__nn.__ll), "dI" (c)); \
+ }
+#endif /* i960mx */
+#endif /* i960 */
+
+#if (defined (__mc68000__) || defined (__mc68020__) || defined(mc68020) \
+ || defined (__m68k__) || defined (__mc5200__) || defined (__mc5206e__) \
+ || defined (__mc5307__)) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add%.l %5,%1\n\taddx%.l %3,%0" \
+ : "=d" (sh), "=&d" (sl) \
+ : "0" ((USItype)(ah)), "d" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub%.l %5,%1\n\tsubx%.l %3,%0" \
+ : "=d" (sh), "=&d" (sl) \
+ : "0" ((USItype)(ah)), "d" ((USItype)(bh)), \
+ "1" ((USItype)(al)), "g" ((USItype)(bl)))
+/* The '020, '030, '040 and CPU32 have 32x32->64 and 64/32->32q-32r. */
+#if defined (__mc68020__) || defined(mc68020) \
+ || defined (__mc68030__) || defined (mc68030) \
+ || defined (__mc68040__) || defined (mc68040) \
+ || defined (__mcpu32__) || defined (mcpu32) \
+ || defined (__NeXT__)
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("mulu%.l %3,%1:%0" \
+ : "=d" (w0), "=d" (w1) \
+ : "%0" ((USItype)(u)), "dmi" ((USItype)(v)))
+#define UMUL_TIME 45
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("divu%.l %4,%1:%0" \
+ : "=d" (q), "=d" (r) \
+ : "0" ((USItype)(n0)), "1" ((USItype)(n1)), "dmi" ((USItype)(d)))
+#define UDIV_TIME 90
+#define sdiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("divs%.l %4,%1:%0" \
+ : "=d" (q), "=d" (r) \
+ : "0" ((USItype)(n0)), "1" ((USItype)(n1)), "dmi" ((USItype)(d)))
+#else /* for other 68k family members use 16x16->32 multiplication */
+#define umul_ppmm(xh, xl, a, b) \
+ do { USItype __umul_tmp1, __umul_tmp2; \
+ __asm__ ("| Inlined umul_ppmm\n" \
+" move%.l %5,%3\n" \
+" move%.l %2,%0\n" \
+" move%.w %3,%1\n" \
+" swap %3\n" \
+" swap %0\n" \
+" mulu%.w %2,%1\n" \
+" mulu%.w %3,%0\n" \
+" mulu%.w %2,%3\n" \
+" swap %2\n" \
+" mulu%.w %5,%2\n" \
+" add%.l %3,%2\n" \
+" jcc 1f\n" \
+" add%.l %#0x10000,%0\n" \
+"1: move%.l %2,%3\n" \
+" clr%.w %2\n" \
+" swap %2\n" \
+" swap %3\n" \
+" clr%.w %3\n" \
+" add%.l %3,%1\n" \
+" addx%.l %2,%0\n" \
+" | End inlined umul_ppmm" \
+ : "=&d" (xh), "=&d" (xl), \
+ "=d" (__umul_tmp1), "=&d" (__umul_tmp2) \
+ : "%2" ((USItype)(a)), "d" ((USItype)(b))); \
+ } while (0)
+#define UMUL_TIME 100
+#define UDIV_TIME 400
+#endif /* not mc68020 */
+/* The '020, '030, '040 and '060 have bitfield insns.
+ GCC 3.4 defines __mc68020__ when in CPU32 mode, check for __mcpu32__ to
+ exclude bfffo on that chip (bitfield insns not available). */
+#if (defined (__mc68020__) || defined (mc68020) \
+ || defined (__mc68030__) || defined (mc68030) \
+ || defined (__mc68040__) || defined (mc68040) \
+ || defined (__mc68060__) || defined (mc68060) \
+ || defined (__NeXT__)) \
+ && ! defined (__mcpu32__)
+#define count_leading_zeros(count, x) \
+ __asm__ ("bfffo %1{%b2:%b2},%0" \
+ : "=d" (count) \
+ : "od" ((USItype) (x)), "n" (0))
+#define COUNT_LEADING_ZEROS_0 32
+#endif
+#endif /* mc68000 */
+
+#if defined (__m88000__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addu.co %1,%r4,%r5\n\taddu.ci %0,%r2,%r3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "rJ" (ah), "rJ" (bh), "%rJ" (al), "rJ" (bl))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subu.co %1,%r4,%r5\n\tsubu.ci %0,%r2,%r3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "rJ" (ah), "rJ" (bh), "rJ" (al), "rJ" (bl))
+#define count_leading_zeros(count, x) \
+ do { \
+ USItype __cbtmp; \
+ __asm__ ("ff1 %0,%1" : "=r" (__cbtmp) : "r" (x)); \
+ (count) = __cbtmp ^ 31; \
+ } while (0)
+#define COUNT_LEADING_ZEROS_0 63 /* sic */
+#if defined (__m88110__)
+#define umul_ppmm(wh, wl, u, v) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __x; \
+ __asm__ ("mulu.d %0,%1,%2" : "=r" (__x.__ll) : "r" (u), "r" (v)); \
+ (wh) = __x.__i.__h; \
+ (wl) = __x.__i.__l; \
+ } while (0)
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ ({union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __x, __q; \
+ __x.__i.__h = (n1); __x.__i.__l = (n0); \
+ __asm__ ("divu.d %0,%1,%2" \
+ : "=r" (__q.__ll) : "r" (__x.__ll), "r" (d)); \
+ (r) = (n0) - __q.__l * (d); (q) = __q.__l; })
+#define UMUL_TIME 5
+#define UDIV_TIME 25
+#else
+#define UMUL_TIME 17
+#define UDIV_TIME 150
+#endif /* __m88110__ */
+#endif /* __m88000__ */
+
+#if defined (__mips) && W_TYPE_SIZE == 32
+#if __GMP_GNUC_PREREQ (4,4)
+#define umul_ppmm(w1, w0, u, v) \
+ do { \
+ UDItype __ll = (UDItype)(u) * (v); \
+ w1 = __ll >> 32; \
+ w0 = __ll; \
+ } while (0)
+#endif
+#if !defined (umul_ppmm) && __GMP_GNUC_PREREQ (2,7) && !defined (__clang__)
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("multu %2,%3" : "=l" (w0), "=h" (w1) : "d" (u), "d" (v))
+#endif
+#if !defined (umul_ppmm)
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("multu %2,%3\n\tmflo %0\n\tmfhi %1" \
+ : "=d" (w0), "=d" (w1) : "d" (u), "d" (v))
+#endif
+#define UMUL_TIME 10
+#define UDIV_TIME 100
+#endif /* __mips */
+
+#if (defined (__mips) && __mips >= 3) && W_TYPE_SIZE == 64
+#if __GMP_GNUC_PREREQ (4,4)
+#define umul_ppmm(w1, w0, u, v) \
+ do { \
+ typedef unsigned int __ll_UTItype __attribute__((mode(TI))); \
+ __ll_UTItype __ll = (__ll_UTItype)(u) * (v); \
+ w1 = __ll >> 64; \
+ w0 = __ll; \
+ } while (0)
+#endif
+#if !defined (umul_ppmm) && __GMP_GNUC_PREREQ (2,7) && !defined (__clang__)
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("dmultu %2,%3" \
+ : "=l" (w0), "=h" (w1) \
+ : "d" ((UDItype)(u)), "d" ((UDItype)(v)))
+#endif
+#if !defined (umul_ppmm)
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("dmultu %2,%3\n\tmflo %0\n\tmfhi %1" \
+ : "=d" (w0), "=d" (w1) \
+ : "d" ((UDItype)(u)), "d" ((UDItype)(v)))
+#endif
+#define UMUL_TIME 20
+#define UDIV_TIME 140
+#endif /* __mips */
+
+#if defined (__mmix__) && W_TYPE_SIZE == 64
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("MULU %0,%2,%3" : "=r" (w0), "=z" (w1) : "r" (u), "r" (v))
+#endif
+
+#if defined (__ns32000__) && W_TYPE_SIZE == 32
+#define umul_ppmm(w1, w0, u, v) \
+ ({union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __x; \
+ __asm__ ("meid %2,%0" \
+ : "=g" (__x.__ll) \
+ : "%0" ((USItype)(u)), "g" ((USItype)(v))); \
+ (w1) = __x.__i.__h; (w0) = __x.__i.__l;})
+#define __umulsidi3(u, v) \
+ ({UDItype __w; \
+ __asm__ ("meid %2,%0" \
+ : "=g" (__w) \
+ : "%0" ((USItype)(u)), "g" ((USItype)(v))); \
+ __w; })
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ ({union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __x; \
+ __x.__i.__h = (n1); __x.__i.__l = (n0); \
+ __asm__ ("deid %2,%0" \
+ : "=g" (__x.__ll) \
+ : "0" (__x.__ll), "g" ((USItype)(d))); \
+ (r) = __x.__i.__l; (q) = __x.__i.__h; })
+#define count_trailing_zeros(count,x) \
+ do { \
+ __asm__ ("ffsd %2,%0" \
+ : "=r" (count) \
+ : "0" ((USItype) 0), "r" ((USItype) (x))); \
+ } while (0)
+#endif /* __ns32000__ */
+
+/* In the past we had a block of various #defines tested
+ _ARCH_PPC - AIX
+ _ARCH_PWR - AIX
+ __powerpc__ - gcc
+ __POWERPC__ - BEOS
+ __ppc__ - Darwin
+ PPC - old gcc, GNU/Linux, SysV
+ The plain PPC test was not good for vxWorks, since PPC is defined on all
+ CPUs there (eg. m68k too), as a constant one is expected to compare
+ CPU_FAMILY against.
+
+ At any rate, this was pretty unattractive and a bit fragile. The use of
+ HAVE_HOST_CPU_FAMILY is designed to cut through it all and be sure of
+ getting the desired effect.
+
+ ENHANCE-ME: We should test _IBMR2 here when we add assembly support for
+ the system vendor compilers. (Is that vendor compilers with inline asm,
+ or what?) */
+
+#if (HAVE_HOST_CPU_FAMILY_power || HAVE_HOST_CPU_FAMILY_powerpc) \
+ && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ do { \
+ if (__builtin_constant_p (bh) && (bh) == 0) \
+ __asm__ ("add%I4c %1,%3,%4\n\taddze %0,%2" \
+ : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl)); \
+ else if (__builtin_constant_p (bh) && (bh) == ~(USItype) 0) \
+ __asm__ ("add%I4c %1,%3,%4\n\taddme %0,%2" \
+ : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl)); \
+ else \
+ __asm__ ("add%I5c %1,%4,%5\n\tadde %0,%2,%3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" (ah), "r" (bh), "%r" (al), "rI" (bl)); \
+ } while (0)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ do { \
+ if (__builtin_constant_p (ah) && (ah) == 0) \
+ __asm__ ("subf%I3c %1,%4,%3\n\tsubfze %0,%2" \
+ : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\
+ else if (__builtin_constant_p (ah) && (ah) == ~(USItype) 0) \
+ __asm__ ("subf%I3c %1,%4,%3\n\tsubfme %0,%2" \
+ : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\
+ else if (__builtin_constant_p (bh) && (bh) == 0) \
+ __asm__ ("subf%I3c %1,%4,%3\n\taddme %0,%2" \
+ : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\
+ else if (__builtin_constant_p (bh) && (bh) == ~(USItype) 0) \
+ __asm__ ("subf%I3c %1,%4,%3\n\taddze %0,%2" \
+ : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\
+ else \
+ __asm__ ("subf%I4c %1,%5,%4\n\tsubfe %0,%3,%2" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" (ah), "r" (bh), "rI" (al), "r" (bl)); \
+ } while (0)
+#define count_leading_zeros(count, x) \
+ __asm__ ("cntlzw %0,%1" : "=r" (count) : "r" (x))
+#define COUNT_LEADING_ZEROS_0 32
+#if HAVE_HOST_CPU_FAMILY_powerpc
+#if __GMP_GNUC_PREREQ (4,4)
+#define umul_ppmm(w1, w0, u, v) \
+ do { \
+ UDItype __ll = (UDItype)(u) * (v); \
+ w1 = __ll >> 32; \
+ w0 = __ll; \
+ } while (0)
+#endif
+#if !defined (umul_ppmm)
+#define umul_ppmm(ph, pl, m0, m1) \
+ do { \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("mulhwu %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \
+ (pl) = __m0 * __m1; \
+ } while (0)
+#endif
+#define UMUL_TIME 15
+#define smul_ppmm(ph, pl, m0, m1) \
+ do { \
+ SItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("mulhw %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \
+ (pl) = __m0 * __m1; \
+ } while (0)
+#define SMUL_TIME 14
+#define UDIV_TIME 120
+#else
+#define UMUL_TIME 8
+#define smul_ppmm(xh, xl, m0, m1) \
+ __asm__ ("mul %0,%2,%3" : "=r" (xh), "=q" (xl) : "r" (m0), "r" (m1))
+#define SMUL_TIME 4
+#define sdiv_qrnnd(q, r, nh, nl, d) \
+ __asm__ ("div %0,%2,%4" : "=r" (q), "=q" (r) : "r" (nh), "1" (nl), "r" (d))
+#define UDIV_TIME 100
+#endif
+#endif /* 32-bit POWER architecture variants. */
+
+/* We should test _IBMR2 here when we add assembly support for the system
+ vendor compilers. */
+#if HAVE_HOST_CPU_FAMILY_powerpc && W_TYPE_SIZE == 64
+#if !defined (_LONG_LONG_LIMB)
+/* _LONG_LONG_LIMB is ABI=mode32 where adde operates on 32-bit values. So
+ use adde etc only when not _LONG_LONG_LIMB. */
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ do { \
+ if (__builtin_constant_p (bh) && (bh) == 0) \
+ __asm__ ("add%I4c %1,%3,%4\n\taddze %0,%2" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" ((UDItype)(ah)), \
+ "%r" ((UDItype)(al)), "rI" ((UDItype)(bl))); \
+ else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0) \
+ __asm__ ("add%I4c %1,%3,%4\n\taddme %0,%2" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" ((UDItype)(ah)), \
+ "%r" ((UDItype)(al)), "rI" ((UDItype)(bl))); \
+ else \
+ __asm__ ("add%I5c %1,%4,%5\n\tadde %0,%2,%3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" ((UDItype)(ah)), "r" ((UDItype)(bh)), \
+ "%r" ((UDItype)(al)), "rI" ((UDItype)(bl))); \
+ } while (0)
+/* We use "*rI" for the constant operand here, since with just "I", gcc barfs.
+ This might seem strange, but gcc folds away the dead code late. */
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ do { \
+ if (__builtin_constant_p (bl) && bl > -0x8000 && bl <= 0x8000) { \
+ if (__builtin_constant_p (ah) && (ah) == 0) \
+ __asm__ ("addic %1,%3,%4\n\tsubfze %0,%2" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" ((UDItype)(bh)), \
+ "rI" ((UDItype)(al)), "*rI" (-((UDItype)(bl)))); \
+ else if (__builtin_constant_p (ah) && (ah) == ~(UDItype) 0) \
+ __asm__ ("addic %1,%3,%4\n\tsubfme %0,%2" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" ((UDItype)(bh)), \
+ "rI" ((UDItype)(al)), "*rI" (-((UDItype)(bl)))); \
+ else if (__builtin_constant_p (bh) && (bh) == 0) \
+ __asm__ ("addic %1,%3,%4\n\taddme %0,%2" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" ((UDItype)(ah)), \
+ "rI" ((UDItype)(al)), "*rI" (-((UDItype)(bl)))); \
+ else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0) \
+ __asm__ ("addic %1,%3,%4\n\taddze %0,%2" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" ((UDItype)(ah)), \
+ "rI" ((UDItype)(al)), "*rI" (-((UDItype)(bl)))); \
+ else \
+ __asm__ ("addic %1,%4,%5\n\tsubfe %0,%3,%2" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" ((UDItype)(ah)), "r" ((UDItype)(bh)), \
+ "rI" ((UDItype)(al)), "*rI" (-((UDItype)(bl)))); \
+ } else { \
+ if (__builtin_constant_p (ah) && (ah) == 0) \
+ __asm__ ("subf%I3c %1,%4,%3\n\tsubfze %0,%2" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" ((UDItype)(bh)), \
+ "rI" ((UDItype)(al)), "r" ((UDItype)(bl))); \
+ else if (__builtin_constant_p (ah) && (ah) == ~(UDItype) 0) \
+ __asm__ ("subf%I3c %1,%4,%3\n\tsubfme %0,%2" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" ((UDItype)(bh)), \
+ "rI" ((UDItype)(al)), "r" ((UDItype)(bl))); \
+ else if (__builtin_constant_p (bh) && (bh) == 0) \
+ __asm__ ("subf%I3c %1,%4,%3\n\taddme %0,%2" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" ((UDItype)(ah)), \
+ "rI" ((UDItype)(al)), "r" ((UDItype)(bl))); \
+ else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0) \
+ __asm__ ("subf%I3c %1,%4,%3\n\taddze %0,%2" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" ((UDItype)(ah)), \
+ "rI" ((UDItype)(al)), "r" ((UDItype)(bl))); \
+ else \
+ __asm__ ("subf%I4c %1,%5,%4\n\tsubfe %0,%3,%2" \
+ : "=r" (sh), "=&r" (sl) \
+ : "r" ((UDItype)(ah)), "r" ((UDItype)(bh)), \
+ "rI" ((UDItype)(al)), "r" ((UDItype)(bl))); \
+ } \
+ } while (0)
+#endif /* ! _LONG_LONG_LIMB */
+#define count_leading_zeros(count, x) \
+ __asm__ ("cntlzd %0,%1" : "=r" (count) : "r" (x))
+#define COUNT_LEADING_ZEROS_0 64
+#if 0 && __GMP_GNUC_PREREQ (4,4) /* Disable, this results in libcalls! */
+#define umul_ppmm(w1, w0, u, v) \
+ do { \
+ typedef unsigned int __ll_UTItype __attribute__((mode(TI))); \
+ __ll_UTItype __ll = (__ll_UTItype)(u) * (v); \
+ w1 = __ll >> 64; \
+ w0 = __ll; \
+ } while (0)
+#endif
+#if !defined (umul_ppmm)
+#define umul_ppmm(ph, pl, m0, m1) \
+ do { \
+ UDItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("mulhdu %0,%1,%2" : "=r" (ph) : "%r" (__m0), "r" (__m1)); \
+ (pl) = __m0 * __m1; \
+ } while (0)
+#endif
+#define UMUL_TIME 15
+#define smul_ppmm(ph, pl, m0, m1) \
+ do { \
+ DItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("mulhd %0,%1,%2" : "=r" (ph) : "%r" (__m0), "r" (__m1)); \
+ (pl) = __m0 * __m1; \
+ } while (0)
+#define SMUL_TIME 14 /* ??? */
+#define UDIV_TIME 120 /* ??? */
+#endif /* 64-bit PowerPC. */
+
+#if defined (__pyr__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addw %5,%1\n\taddwc %3,%0" \
+ : "=r" (sh), "=&r" (sl) \
+ : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subw %5,%1\n\tsubwb %3,%0" \
+ : "=r" (sh), "=&r" (sl) \
+ : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \
+ "1" ((USItype)(al)), "g" ((USItype)(bl)))
+/* This insn works on Pyramids with AP, XP, or MI CPUs, but not with SP. */
+#define umul_ppmm(w1, w0, u, v) \
+ ({union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __x; \
+ __asm__ ("movw %1,%R0\n\tuemul %2,%0" \
+ : "=&r" (__x.__ll) \
+ : "g" ((USItype) (u)), "g" ((USItype)(v))); \
+ (w1) = __x.__i.__h; (w0) = __x.__i.__l;})
+#endif /* __pyr__ */
+
+#if defined (__ibm032__) /* RT/ROMP */ && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("a %1,%5\n\tae %0,%3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "0" ((USItype)(ah)), "r" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), "r" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("s %1,%5\n\tse %0,%3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "0" ((USItype)(ah)), "r" ((USItype)(bh)), \
+ "1" ((USItype)(al)), "r" ((USItype)(bl)))
+#define smul_ppmm(ph, pl, m0, m1) \
+ __asm__ ( \
+ "s r2,r2\n" \
+" mts r10,%2\n" \
+" m r2,%3\n" \
+" m r2,%3\n" \
+" m r2,%3\n" \
+" m r2,%3\n" \
+" m r2,%3\n" \
+" m r2,%3\n" \
+" m r2,%3\n" \
+" m r2,%3\n" \
+" m r2,%3\n" \
+" m r2,%3\n" \
+" m r2,%3\n" \
+" m r2,%3\n" \
+" m r2,%3\n" \
+" m r2,%3\n" \
+" m r2,%3\n" \
+" m r2,%3\n" \
+" cas %0,r2,r0\n" \
+" mfs r10,%1" \
+ : "=r" (ph), "=r" (pl) \
+ : "%r" ((USItype)(m0)), "r" ((USItype)(m1)) \
+ : "r2")
+#define UMUL_TIME 20
+#define UDIV_TIME 200
+#define count_leading_zeros(count, x) \
+ do { \
+ if ((x) >= 0x10000) \
+ __asm__ ("clz %0,%1" \
+ : "=r" (count) : "r" ((USItype)(x) >> 16)); \
+ else \
+ { \
+ __asm__ ("clz %0,%1" \
+ : "=r" (count) : "r" ((USItype)(x))); \
+ (count) += 16; \
+ } \
+ } while (0)
+#endif /* RT/ROMP */
+
+#if (defined (__SH2__) || defined (__SH3__) || defined (__SH4__)) && W_TYPE_SIZE == 32
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("dmulu.l %2,%3\n\tsts macl,%1\n\tsts mach,%0" \
+ : "=r" (w1), "=r" (w0) : "r" (u), "r" (v) : "macl", "mach")
+#define UMUL_TIME 5
+#endif
+
+#if defined (__sparc__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addcc %r4,%5,%1\n\taddx %r2,%3,%0" \
+ : "=r" (sh), "=&r" (sl) \
+ : "rJ" (ah), "rI" (bh),"%rJ" (al), "rI" (bl) \
+ __CLOBBER_CC)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subcc %r4,%5,%1\n\tsubx %r2,%3,%0" \
+ : "=r" (sh), "=&r" (sl) \
+ : "rJ" (ah), "rI" (bh), "rJ" (al), "rI" (bl) \
+ __CLOBBER_CC)
+/* FIXME: When gcc -mcpu=v9 is used on solaris, gcc/config/sol2-sld-64.h
+ doesn't define anything to indicate that to us, it only sets __sparcv8. */
+#if defined (__sparc_v9__) || defined (__sparcv9)
+/* Perhaps we should use floating-point operations here? */
+#if 0
+/* Triggers a bug making mpz/tests/t-gcd.c fail.
+ Perhaps we simply need explicitly zero-extend the inputs? */
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("mulx %2,%3,%%g1; srl %%g1,0,%1; srlx %%g1,32,%0" : \
+ "=r" (w1), "=r" (w0) : "r" (u), "r" (v) : "g1")
+#else
+/* Use v8 umul until above bug is fixed. */
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("umul %2,%3,%1;rd %%y,%0" : "=r" (w1), "=r" (w0) : "r" (u), "r" (v))
+#endif
+/* Use a plain v8 divide for v9. */
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { \
+ USItype __q; \
+ __asm__ ("mov %1,%%y;nop;nop;nop;udiv %2,%3,%0" \
+ : "=r" (__q) : "r" (n1), "r" (n0), "r" (d)); \
+ (r) = (n0) - __q * (d); \
+ (q) = __q; \
+ } while (0)
+#else
+#if defined (__sparc_v8__) /* gcc normal */ \
+ || defined (__sparcv8) /* gcc solaris */ \
+ || HAVE_HOST_CPU_supersparc
+/* Don't match immediate range because, 1) it is not often useful,
+ 2) the 'I' flag thinks of the range as a 13 bit signed interval,
+ while we want to match a 13 bit interval, sign extended to 32 bits,
+ but INTERPRETED AS UNSIGNED. */
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("umul %2,%3,%1;rd %%y,%0" : "=r" (w1), "=r" (w0) : "r" (u), "r" (v))
+#define UMUL_TIME 5
+
+#if HAVE_HOST_CPU_supersparc
+#define UDIV_TIME 60 /* SuperSPARC timing */
+#else
+/* Don't use this on SuperSPARC because its udiv only handles 53 bit
+ dividends and will trap to the kernel for the rest. */
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { \
+ USItype __q; \
+ __asm__ ("mov %1,%%y;nop;nop;nop;udiv %2,%3,%0" \
+ : "=r" (__q) : "r" (n1), "r" (n0), "r" (d)); \
+ (r) = (n0) - __q * (d); \
+ (q) = __q; \
+ } while (0)
+#define UDIV_TIME 25
+#endif /* HAVE_HOST_CPU_supersparc */
+
+#else /* ! __sparc_v8__ */
+#if defined (__sparclite__)
+/* This has hardware multiply but not divide. It also has two additional
+ instructions scan (ffs from high bit) and divscc. */
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("umul %2,%3,%1;rd %%y,%0" : "=r" (w1), "=r" (w0) : "r" (u), "r" (v))
+#define UMUL_TIME 5
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("! Inlined udiv_qrnnd\n" \
+" wr %%g0,%2,%%y ! Not a delayed write for sparclite\n" \
+" tst %%g0\n" \
+" divscc %3,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%%g1\n" \
+" divscc %%g1,%4,%0\n" \
+" rd %%y,%1\n" \
+" bl,a 1f\n" \
+" add %1,%4,%1\n" \
+"1: ! End of inline udiv_qrnnd" \
+ : "=r" (q), "=r" (r) : "r" (n1), "r" (n0), "rI" (d) \
+ : "%g1" __AND_CLOBBER_CC)
+#define UDIV_TIME 37
+#define count_leading_zeros(count, x) \
+ __asm__ ("scan %1,1,%0" : "=r" (count) : "r" (x))
+/* Early sparclites return 63 for an argument of 0, but they warn that future
+ implementations might change this. Therefore, leave COUNT_LEADING_ZEROS_0
+ undefined. */
+#endif /* __sparclite__ */
+#endif /* __sparc_v8__ */
+#endif /* __sparc_v9__ */
+/* Default to sparc v7 versions of umul_ppmm and udiv_qrnnd. */
+#ifndef umul_ppmm
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("! Inlined umul_ppmm\n" \
+" wr %%g0,%2,%%y ! SPARC has 0-3 delay insn after a wr\n" \
+" sra %3,31,%%g2 ! Don't move this insn\n" \
+" and %2,%%g2,%%g2 ! Don't move this insn\n" \
+" andcc %%g0,0,%%g1 ! Don't move this insn\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,%3,%%g1\n" \
+" mulscc %%g1,0,%%g1\n" \
+" add %%g1,%%g2,%0\n" \
+" rd %%y,%1" \
+ : "=r" (w1), "=r" (w0) : "%rI" (u), "r" (v) \
+ : "%g1", "%g2" __AND_CLOBBER_CC)
+#define UMUL_TIME 39 /* 39 instructions */
+#endif
+#ifndef udiv_qrnnd
+#ifndef LONGLONG_STANDALONE
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { UWtype __r; \
+ (q) = __MPN(udiv_qrnnd) (&__r, (n1), (n0), (d)); \
+ (r) = __r; \
+ } while (0)
+extern UWtype __MPN(udiv_qrnnd) (UWtype *, UWtype, UWtype, UWtype);
+#ifndef UDIV_TIME
+#define UDIV_TIME 140
+#endif
+#endif /* LONGLONG_STANDALONE */
+#endif /* udiv_qrnnd */
+#endif /* __sparc__ */
+
+#if defined (__sparc__) && W_TYPE_SIZE == 64
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ( \
+ "addcc %r4,%5,%1\n" \
+ " addccc %r6,%7,%%g0\n" \
+ " addc %r2,%3,%0" \
+ : "=r" (sh), "=&r" (sl) \
+ : "rJ" ((UDItype)(ah)), "rI" ((UDItype)(bh)), \
+ "%rJ" ((UDItype)(al)), "rI" ((UDItype)(bl)), \
+ "%rJ" ((UDItype)(al) >> 32), "rI" ((UDItype)(bl) >> 32) \
+ __CLOBBER_CC)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ( \
+ "subcc %r4,%5,%1\n" \
+ " subccc %r6,%7,%%g0\n" \
+ " subc %r2,%3,%0" \
+ : "=r" (sh), "=&r" (sl) \
+ : "rJ" ((UDItype)(ah)), "rI" ((UDItype)(bh)), \
+ "rJ" ((UDItype)(al)), "rI" ((UDItype)(bl)), \
+ "rJ" ((UDItype)(al) >> 32), "rI" ((UDItype)(bl) >> 32) \
+ __CLOBBER_CC)
+#if __VIS__ >= 0x300
+#undef add_ssaaaa
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ( \
+ "addcc %r4, %5, %1\n" \
+ " addxc %r2, %r3, %0" \
+ : "=r" (sh), "=&r" (sl) \
+ : "rJ" ((UDItype)(ah)), "rJ" ((UDItype)(bh)), \
+ "%rJ" ((UDItype)(al)), "rI" ((UDItype)(bl)) __CLOBBER_CC)
+#define umul_ppmm(ph, pl, m0, m1) \
+ do { \
+ UDItype __m0 = (m0), __m1 = (m1); \
+ (pl) = __m0 * __m1; \
+ __asm__ ("umulxhi\t%2, %1, %0" \
+ : "=r" (ph) \
+ : "%r" (__m0), "r" (__m1)); \
+ } while (0)
+#define count_leading_zeros(count, x) \
+ __asm__ ("lzd\t%1,%0" : "=r" (count) : "r" (x))
+/* Needed by count_leading_zeros_32 in sparc64.h. */
+#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB
+#endif
+#endif
+
+#if (defined (__vax) || defined (__vax__)) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addl2 %5,%1\n\tadwc %3,%0" \
+ : "=g" (sh), "=&g" (sl) \
+ : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subl2 %5,%1\n\tsbwc %3,%0" \
+ : "=g" (sh), "=&g" (sl) \
+ : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \
+ "1" ((USItype)(al)), "g" ((USItype)(bl)))
+#define smul_ppmm(xh, xl, m0, m1) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __x; \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("emul %1,%2,$0,%0" \
+ : "=g" (__x.__ll) : "g" (__m0), "g" (__m1)); \
+ (xh) = __x.__i.__h; (xl) = __x.__i.__l; \
+ } while (0)
+#define sdiv_qrnnd(q, r, n1, n0, d) \
+ do { \
+ union {DItype __ll; \
+ struct {SItype __l, __h;} __i; \
+ } __x; \
+ __x.__i.__h = n1; __x.__i.__l = n0; \
+ __asm__ ("ediv %3,%2,%0,%1" \
+ : "=g" (q), "=g" (r) : "g" (__x.__ll), "g" (d)); \
+ } while (0)
+#if 0
+/* FIXME: This instruction appears to be unimplemented on some systems (vax
+ 8800 maybe). */
+#define count_trailing_zeros(count,x) \
+ do { \
+ __asm__ ("ffs 0, 31, %1, %0" \
+ : "=g" (count) \
+ : "g" ((USItype) (x))); \
+ } while (0)
+#endif
+#endif /* vax */
+
+#if defined (__z8000__) && W_TYPE_SIZE == 16
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add %H1,%H5\n\tadc %H0,%H3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "0" ((unsigned int)(ah)), "r" ((unsigned int)(bh)), \
+ "%1" ((unsigned int)(al)), "rQR" ((unsigned int)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub %H1,%H5\n\tsbc %H0,%H3" \
+ : "=r" (sh), "=&r" (sl) \
+ : "0" ((unsigned int)(ah)), "r" ((unsigned int)(bh)), \
+ "1" ((unsigned int)(al)), "rQR" ((unsigned int)(bl)))
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ union {long int __ll; \
+ struct {unsigned int __h, __l;} __i; \
+ } __x; \
+ unsigned int __m0 = (m0), __m1 = (m1); \
+ __asm__ ("mult %S0,%H3" \
+ : "=r" (__x.__i.__h), "=r" (__x.__i.__l) \
+ : "%1" (m0), "rQR" (m1)); \
+ (xh) = __x.__i.__h; (xl) = __x.__i.__l; \
+ (xh) += ((((signed int) __m0 >> 15) & __m1) \
+ + (((signed int) __m1 >> 15) & __m0)); \
+ } while (0)
+#endif /* __z8000__ */
+
+#endif /* __GNUC__ */
+
+#endif /* NO_ASM */
+
+
+/* FIXME: "sidi" here is highly doubtful, should sometimes be "diti". */
+#if !defined (umul_ppmm) && defined (__umulsidi3)
+#define umul_ppmm(ph, pl, m0, m1) \
+ { \
+ UDWtype __ll = __umulsidi3 (m0, m1); \
+ ph = (UWtype) (__ll >> W_TYPE_SIZE); \
+ pl = (UWtype) __ll; \
+ }
+#endif
+
+#if !defined (__umulsidi3)
+#define __umulsidi3(u, v) \
+ ({UWtype __hi, __lo; \
+ umul_ppmm (__hi, __lo, u, v); \
+ ((UDWtype) __hi << W_TYPE_SIZE) | __lo; })
+#endif
+
+
+#if defined (__cplusplus)
+#define __longlong_h_C "C"
+#else
+#define __longlong_h_C
+#endif
+
+/* Use mpn_umul_ppmm or mpn_udiv_qrnnd functions, if they exist. The "_r"
+ forms have "reversed" arguments, meaning the pointer is last, which
+ sometimes allows better parameter passing, in particular on 64-bit
+ hppa. */
+
+#define mpn_umul_ppmm __MPN(umul_ppmm)
+extern __longlong_h_C UWtype mpn_umul_ppmm (UWtype *, UWtype, UWtype);
+
+#if ! defined (umul_ppmm) && HAVE_NATIVE_mpn_umul_ppmm \
+ && ! defined (LONGLONG_STANDALONE)
+#define umul_ppmm(wh, wl, u, v) \
+ do { \
+ UWtype __umul_ppmm__p0; \
+ (wh) = mpn_umul_ppmm (&__umul_ppmm__p0, (UWtype) (u), (UWtype) (v));\
+ (wl) = __umul_ppmm__p0; \
+ } while (0)
+#endif
+
+#define mpn_umul_ppmm_r __MPN(umul_ppmm_r)
+extern __longlong_h_C UWtype mpn_umul_ppmm_r (UWtype, UWtype, UWtype *);
+
+#if ! defined (umul_ppmm) && HAVE_NATIVE_mpn_umul_ppmm_r \
+ && ! defined (LONGLONG_STANDALONE)
+#define umul_ppmm(wh, wl, u, v) \
+ do { \
+ UWtype __umul_p0; \
+ (wh) = mpn_umul_ppmm_r ((UWtype) (u), (UWtype) (v), &__umul_p0); \
+ (wl) = __umul_p0; \
+ } while (0)
+#endif
+
+#define mpn_udiv_qrnnd __MPN(udiv_qrnnd)
+extern __longlong_h_C UWtype mpn_udiv_qrnnd (UWtype *, UWtype, UWtype, UWtype);
+
+#if ! defined (udiv_qrnnd) && HAVE_NATIVE_mpn_udiv_qrnnd \
+ && ! defined (LONGLONG_STANDALONE)
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { \
+ UWtype __udiv_qrnnd_r; \
+ (q) = mpn_udiv_qrnnd (&__udiv_qrnnd_r, \
+ (UWtype) (n1), (UWtype) (n0), (UWtype) d); \
+ (r) = __udiv_qrnnd_r; \
+ } while (0)
+#endif
+
+#define mpn_udiv_qrnnd_r __MPN(udiv_qrnnd_r)
+extern __longlong_h_C UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *);
+
+#if ! defined (udiv_qrnnd) && HAVE_NATIVE_mpn_udiv_qrnnd_r \
+ && ! defined (LONGLONG_STANDALONE)
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { \
+ UWtype __udiv_qrnnd_r; \
+ (q) = mpn_udiv_qrnnd_r ((UWtype) (n1), (UWtype) (n0), (UWtype) d, \
+ &__udiv_qrnnd_r); \
+ (r) = __udiv_qrnnd_r; \
+ } while (0)
+#endif
+
+
+/* If this machine has no inline assembler, use C macros. */
+
+#if !defined (add_ssaaaa)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ do { \
+ UWtype __x; \
+ __x = (al) + (bl); \
+ (sh) = (ah) + (bh) + (__x < (al)); \
+ (sl) = __x; \
+ } while (0)
+#endif
+
+#if !defined (sub_ddmmss)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ do { \
+ UWtype __x; \
+ __x = (al) - (bl); \
+ (sh) = (ah) - (bh) - ((al) < (bl)); \
+ (sl) = __x; \
+ } while (0)
+#endif
+
+/* If we lack umul_ppmm but have smul_ppmm, define umul_ppmm in terms of
+ smul_ppmm. */
+#if !defined (umul_ppmm) && defined (smul_ppmm)
+#define umul_ppmm(w1, w0, u, v) \
+ do { \
+ UWtype __w1; \
+ UWtype __xm0 = (u), __xm1 = (v); \
+ smul_ppmm (__w1, w0, __xm0, __xm1); \
+ (w1) = __w1 + (-(__xm0 >> (W_TYPE_SIZE - 1)) & __xm1) \
+ + (-(__xm1 >> (W_TYPE_SIZE - 1)) & __xm0); \
+ } while (0)
+#endif
+
+/* If we still don't have umul_ppmm, define it using plain C.
+
+ For reference, when this code is used for squaring (ie. u and v identical
+ expressions), gcc recognises __x1 and __x2 are the same and generates 3
+ multiplies, not 4. The subsequent additions could be optimized a bit,
+ but the only place GMP currently uses such a square is mpn_sqr_basecase,
+ and chips obliged to use this generic C umul will have plenty of worse
+ performance problems than a couple of extra instructions on the diagonal
+ of sqr_basecase. */
+
+#if !defined (umul_ppmm)
+#define umul_ppmm(w1, w0, u, v) \
+ do { \
+ UWtype __x0, __x1, __x2, __x3; \
+ UHWtype __ul, __vl, __uh, __vh; \
+ UWtype __u = (u), __v = (v); \
+ \
+ __ul = __ll_lowpart (__u); \
+ __uh = __ll_highpart (__u); \
+ __vl = __ll_lowpart (__v); \
+ __vh = __ll_highpart (__v); \
+ \
+ __x0 = (UWtype) __ul * __vl; \
+ __x1 = (UWtype) __ul * __vh; \
+ __x2 = (UWtype) __uh * __vl; \
+ __x3 = (UWtype) __uh * __vh; \
+ \
+ __x1 += __ll_highpart (__x0);/* this can't give carry */ \
+ __x1 += __x2; /* but this indeed can */ \
+ if (__x1 < __x2) /* did we get it? */ \
+ __x3 += __ll_B; /* yes, add it in the proper pos. */ \
+ \
+ (w1) = __x3 + __ll_highpart (__x1); \
+ (w0) = (__x1 << W_TYPE_SIZE/2) + __ll_lowpart (__x0); \
+ } while (0)
+#endif
+
+/* If we don't have smul_ppmm, define it using umul_ppmm (which surely will
+ exist in one form or another. */
+#if !defined (smul_ppmm)
+#define smul_ppmm(w1, w0, u, v) \
+ do { \
+ UWtype __w1; \
+ UWtype __xm0 = (u), __xm1 = (v); \
+ umul_ppmm (__w1, w0, __xm0, __xm1); \
+ (w1) = __w1 - (-(__xm0 >> (W_TYPE_SIZE - 1)) & __xm1) \
+ - (-(__xm1 >> (W_TYPE_SIZE - 1)) & __xm0); \
+ } while (0)
+#endif
+
+/* Define this unconditionally, so it can be used for debugging. */
+#define __udiv_qrnnd_c(q, r, n1, n0, d) \
+ do { \
+ UWtype __d1, __d0, __q1, __q0, __r1, __r0, __m; \
+ \
+ ASSERT ((d) != 0); \
+ ASSERT ((n1) < (d)); \
+ \
+ __d1 = __ll_highpart (d); \
+ __d0 = __ll_lowpart (d); \
+ \
+ __q1 = (n1) / __d1; \
+ __r1 = (n1) - __q1 * __d1; \
+ __m = __q1 * __d0; \
+ __r1 = __r1 * __ll_B | __ll_highpart (n0); \
+ if (__r1 < __m) \
+ { \
+ __q1--, __r1 += (d); \
+ if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\
+ if (__r1 < __m) \
+ __q1--, __r1 += (d); \
+ } \
+ __r1 -= __m; \
+ \
+ __q0 = __r1 / __d1; \
+ __r0 = __r1 - __q0 * __d1; \
+ __m = __q0 * __d0; \
+ __r0 = __r0 * __ll_B | __ll_lowpart (n0); \
+ if (__r0 < __m) \
+ { \
+ __q0--, __r0 += (d); \
+ if (__r0 >= (d)) \
+ if (__r0 < __m) \
+ __q0--, __r0 += (d); \
+ } \
+ __r0 -= __m; \
+ \
+ (q) = __q1 * __ll_B | __q0; \
+ (r) = __r0; \
+ } while (0)
+
+/* If the processor has no udiv_qrnnd but sdiv_qrnnd, go through
+ __udiv_w_sdiv (defined in libgcc or elsewhere). */
+#if !defined (udiv_qrnnd) && defined (sdiv_qrnnd)
+#define udiv_qrnnd(q, r, nh, nl, d) \
+ do { \
+ UWtype __r; \
+ (q) = __MPN(udiv_w_sdiv) (&__r, nh, nl, d); \
+ (r) = __r; \
+ } while (0)
+__GMP_DECLSPEC UWtype __MPN(udiv_w_sdiv) (UWtype *, UWtype, UWtype, UWtype);
+#endif
+
+/* If udiv_qrnnd was not defined for this processor, use __udiv_qrnnd_c. */
+#if !defined (udiv_qrnnd)
+#define UDIV_NEEDS_NORMALIZATION 1
+#define udiv_qrnnd __udiv_qrnnd_c
+#endif
+
+#if !defined (count_leading_zeros)
+#define count_leading_zeros(count, x) \
+ do { \
+ UWtype __xr = (x); \
+ UWtype __a; \
+ \
+ if (W_TYPE_SIZE == 32) \
+ { \
+ __a = __xr < ((UWtype) 1 << 2*__BITS4) \
+ ? (__xr < ((UWtype) 1 << __BITS4) ? 1 : __BITS4 + 1) \
+ : (__xr < ((UWtype) 1 << 3*__BITS4) ? 2*__BITS4 + 1 \
+ : 3*__BITS4 + 1); \
+ } \
+ else \
+ { \
+ for (__a = W_TYPE_SIZE - 8; __a > 0; __a -= 8) \
+ if (((__xr >> __a) & 0xff) != 0) \
+ break; \
+ ++__a; \
+ } \
+ \
+ (count) = W_TYPE_SIZE + 1 - __a - __clz_tab[__xr >> __a]; \
+ } while (0)
+/* This version gives a well-defined value for zero. */
+#define COUNT_LEADING_ZEROS_0 (W_TYPE_SIZE - 1)
+#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB
+#define COUNT_LEADING_ZEROS_SLOW
+#endif
+
+/* clz_tab needed by mpn/x86/pentium/mod_1.asm in a fat binary */
+#if HAVE_HOST_CPU_FAMILY_x86 && WANT_FAT_BINARY
+#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB
+#endif
+
+#ifdef COUNT_LEADING_ZEROS_NEED_CLZ_TAB
+extern const unsigned char __GMP_DECLSPEC __clz_tab[129];
+#endif
+
+#if !defined (count_trailing_zeros)
+#if !defined (COUNT_LEADING_ZEROS_SLOW)
+/* Define count_trailing_zeros using an asm count_leading_zeros. */
+#define count_trailing_zeros(count, x) \
+ do { \
+ UWtype __ctz_x = (x); \
+ UWtype __ctz_c; \
+ ASSERT (__ctz_x != 0); \
+ count_leading_zeros (__ctz_c, __ctz_x & -__ctz_x); \
+ (count) = W_TYPE_SIZE - 1 - __ctz_c; \
+ } while (0)
+#else
+/* Define count_trailing_zeros in plain C, assuming small counts are common.
+ We use clz_tab without ado, since the C count_leading_zeros above will have
+ pulled it in. */
+#define count_trailing_zeros(count, x) \
+ do { \
+ UWtype __ctz_x = (x); \
+ int __ctz_c; \
+ \
+ if (LIKELY ((__ctz_x & 0xff) != 0)) \
+ (count) = __clz_tab[__ctz_x & -__ctz_x] - 2; \
+ else \
+ { \
+ for (__ctz_c = 8 - 2; __ctz_c < W_TYPE_SIZE - 2; __ctz_c += 8) \
+ { \
+ __ctz_x >>= 8; \
+ if (LIKELY ((__ctz_x & 0xff) != 0)) \
+ break; \
+ } \
+ \
+ (count) = __ctz_c + __clz_tab[__ctz_x & -__ctz_x]; \
+ } \
+ } while (0)
+#endif
+#endif
+
+#ifndef UDIV_NEEDS_NORMALIZATION
+#define UDIV_NEEDS_NORMALIZATION 0
+#endif
+
+/* Whether udiv_qrnnd is actually implemented with udiv_qrnnd_preinv, and
+ that hence the latter should always be used. */
+#ifndef UDIV_PREINV_ALWAYS
+#define UDIV_PREINV_ALWAYS 0
+#endif
+
+/* Give defaults for UMUL_TIME and UDIV_TIME. */
+#ifndef UMUL_TIME
+#define UMUL_TIME 1
+#endif
+
+#ifndef UDIV_TIME
+#define UDIV_TIME UMUL_TIME
+#endif
diff --git a/src/ls.c b/src/ls.c
index 3d48900..d976036 100644
--- a/src/ls.c
+++ b/src/ls.c
@@ -1,10 +1,10 @@
-/* `dir', `vdir' and `ls' directory listing programs for GNU.
- Copyright (C) 85, 88, 90, 91, 1995-2007 Free Software Foundation, Inc.
+/* 'dir', 'vdir' and 'ls' directory listing programs for GNU.
+ Copyright (C) 1985-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,23 +12,22 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* If ls_mode is LS_MULTI_COL,
the multi-column format is the default regardless
of the type of output device.
- This is for the `dir' program.
+ This is for the 'dir' program.
If ls_mode is LS_LONG_FORMAT,
the long format is the default regardless of the
type of output device.
- This is for the `vdir' program.
+ This is for the 'vdir' program.
If ls_mode is LS_LS,
the output format depends on whether the output
device is a terminal.
- This is for the `ls' program. */
+ This is for the 'ls' program. */
/* Written by Richard Stallman and David MacKenzie. */
@@ -39,15 +38,11 @@
#include <config.h>
#include <sys/types.h>
-#if HAVE_TERMIOS_H
-# include <termios.h>
-#endif
+#include <termios.h>
#if HAVE_STROPTS_H
# include <stropts.h>
#endif
-#if HAVE_SYS_IOCTL_H
-# include <sys/ioctl.h>
-#endif
+#include <sys/ioctl.h>
#ifdef WINSIZE_IN_PTEM
# include <sys/stream.h>
@@ -57,10 +52,15 @@
#include <stdio.h>
#include <assert.h>
#include <setjmp.h>
-#include <grp.h>
#include <pwd.h>
#include <getopt.h>
#include <signal.h>
+#include <selinux/selinux.h>
+#include <wchar.h>
+
+#if HAVE_LANGINFO_CODESET
+# include <langinfo.h>
+#endif
/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
present. */
@@ -72,6 +72,11 @@
# 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
+ that's the best we can do on such a platform. */
#ifndef SA_RESTART
# define SA_RESTART 0
#endif
@@ -82,34 +87,43 @@
#include "acl.h"
#include "argmatch.h"
#include "dev-ino.h"
-#include "dirfd.h"
#include "error.h"
#include "filenamecat.h"
#include "hard-locale.h"
#include "hash.h"
#include "human.h"
#include "filemode.h"
-#include "inttostr.h"
+#include "filevercmp.h"
+#include "idcache.h"
#include "ls.h"
-#include "lstat.h"
#include "mbswidth.h"
#include "mpsort.h"
#include "obstack.h"
#include "quote.h"
-#include "quotearg.h"
-#include "same.h"
+#include "smack.h"
+#include "stat-size.h"
#include "stat-time.h"
#include "strftime.h"
-#include "strverscmp.h"
-#include "wcwidth.h"
+#include "xdectoint.h"
#include "xstrtol.h"
-#include "xreadlink.h"
+#include "areadlink.h"
+#include "mbsalign.h"
+#include "dircolors.h"
+
+/* Include <sys/capability.h> last to avoid a clash of <sys/types.h>
+ include guards with some premature versions of libcap.
+ For more details, see <http://bugzilla.redhat.com/483548>. */
+#ifdef HAVE_CAP
+# include <sys/capability.h>
+#endif
#define PROGRAM_NAME (ls_mode == LS_LS ? "ls" \
- : (ls_mode == LS_MULTI_COL \
- ? "dir" : "vdir"))
+ : (ls_mode == LS_MULTI_COL \
+ ? "dir" : "vdir"))
-#define AUTHORS "Richard Stallman", "David MacKenzie"
+#define AUTHORS \
+ proper_name ("Richard M. Stallman"), \
+ proper_name ("David MacKenzie")
#define obstack_chunk_alloc malloc
#define obstack_chunk_free free
@@ -118,6 +132,26 @@
Subtracting doesn't always work, due to overflow. */
#define longdiff(a, b) ((a) < (b) ? -1 : (a) > (b))
+/* Unix-based readdir implementations have historically returned a dirent.d_ino
+ value that is sometimes not equal to the stat-obtained st_ino value for
+ that same entry. This error occurs for a readdir entry that refers
+ to a mount point. readdir's error is to return the inode number of
+ the underlying directory -- one that typically cannot be stat'ed, as
+ long as a file system is mounted on that directory. RELIABLE_D_INO
+ encapsulates whether we can use the more efficient approach of relying
+ on readdir-supplied d_ino values, or whether we must incur the cost of
+ calling stat or lstat to obtain each guaranteed-valid inode number. */
+
+#ifndef READDIR_LIES_ABOUT_MOUNTPOINT_D_INO
+# define READDIR_LIES_ABOUT_MOUNTPOINT_D_INO 1
+#endif
+
+#if READDIR_LIES_ABOUT_MOUNTPOINT_D_INO
+# define RELIABLE_D_INO(dp) NOT_AN_INODE_NUMBER
+#else
+# define RELIABLE_D_INO(dp) D_INO (dp)
+#endif
+
#if ! HAVE_STRUCT_STAT_ST_AUTHOR
# define st_author st_uid
#endif
@@ -150,6 +184,12 @@ verify (sizeof filetype_letter - 1 == arg_directory + 1);
C_LINK, C_SOCK, C_FILE, C_DIR \
}
+enum acl_type
+ {
+ ACL_T_NONE,
+ ACL_T_LSM_CONTEXT_ONLY,
+ ACL_T_YES
+ };
struct fileinfo
{
@@ -167,23 +207,22 @@ struct fileinfo
zero. */
mode_t linkmode;
+ /* security context. */
+ char *scontext;
+
bool stat_ok;
/* For symbolic link and color printing, true if linked-to file
exists, otherwise false. */
bool linkok;
-#if USE_ACL
- /* For long listings, true if the file has an access control list. */
- bool have_acl;
-#endif
- };
+ /* For long listings, true if the file has an access control list,
+ or a security context. */
+ enum acl_type acl_type;
-#if USE_ACL
-# define FILE_HAS_ACL(F) ((F)->have_acl)
-#else
-# define FILE_HAS_ACL(F) 0
-#endif
+ /* For color listings, true if a regular file has capability info. */
+ bool has_capability;
+ };
#define LEN_STR_PAIR(s) sizeof (s) - 1, s
@@ -197,65 +236,61 @@ struct bin_str
const char *string; /* Pointer to the same */
};
-char *getgroup ();
-char *getuser ();
-
#if ! HAVE_TCGETPGRP
# define tcgetpgrp(Fd) 0
#endif
static size_t quote_name (FILE *out, const char *name,
- struct quoting_options const *options,
- size_t *width);
+ struct quoting_options const *options,
+ size_t *width);
static char *make_link_name (char const *name, char const *linkname);
static int decode_switches (int argc, char **argv);
static bool file_ignored (char const *name);
static uintmax_t gobble_file (char const *name, enum filetype type,
- ino_t inode, bool command_line_arg,
- char const *dirname);
-static void print_color_indicator (const char *name, mode_t mode, int linkok,
- bool stat_ok, enum filetype type);
+ ino_t inode, bool command_line_arg,
+ char const *dirname);
+static bool print_color_indicator (const struct fileinfo *f,
+ bool symlink_target);
static void put_indicator (const struct bin_str *ind);
static void add_ignore_pattern (const char *pattern);
static void attach (char *dest, const char *dirname, const char *name);
static void clear_files (void);
static void extract_dirs_from_files (char const *dirname,
- bool command_line_arg);
+ bool command_line_arg);
static void get_link_name (char const *filename, struct fileinfo *f,
- bool command_line_arg);
+ bool command_line_arg);
static void indent (size_t from, size_t to);
static size_t calculate_columns (bool by_columns);
static void print_current_files (void);
static void print_dir (char const *name, char const *realname,
- bool command_line_arg);
-static void print_file_name_and_frills (const struct fileinfo *f);
+ bool command_line_arg);
+static size_t print_file_name_and_frills (const struct fileinfo *f,
+ size_t start_col);
static void print_horizontal (void);
static int format_user_width (uid_t u);
static int format_group_width (gid_t g);
static void print_long_format (const struct fileinfo *f);
static void print_many_per_line (void);
-static void print_name_with_quoting (const char *p, mode_t mode,
- int linkok, bool stat_ok,
- enum filetype type,
- struct obstack *stack);
+static size_t print_name_with_quoting (const struct fileinfo *f,
+ bool symlink_target,
+ struct obstack *stack,
+ size_t start_col);
static void prep_non_filename_text (void);
-static void print_type_indicator (bool stat_ok, mode_t mode,
- enum filetype type);
-static void print_with_commas (void);
+static bool print_type_indicator (bool stat_ok, mode_t mode,
+ enum filetype type);
+static void print_with_separator (char sep);
static void queue_directory (char const *name, char const *realname,
- bool command_line_arg);
+ bool command_line_arg);
static void sort_files (void);
static void parse_ls_color (void);
-void usage (int status);
-/* The name this program was run with. */
-char *program_name;
+static void getenv_quoting_style (void);
/* Initial size of hash table.
Most hierarchies are likely to be shallower than this. */
#define INITIAL_TABLE_SIZE 30
-/* The set of `active' directories, from the current command-line argument
+/* The set of 'active' directories, from the current command-line argument
to the level in the hierarchy at which files are being listed.
A directory is represented by its device and inode numbers (struct dev_ino).
A directory is added to this set when ls begins listing it or its
@@ -268,17 +303,17 @@ static Hash_table *active_dir_set;
/* The table of files in the current directory:
- `cwd_file' points to a vector of `struct fileinfo', one per file.
- `cwd_n_alloc' is the number of elements space has been allocated for.
- `cwd_n_used' is the number actually in use. */
+ 'cwd_file' points to a vector of 'struct fileinfo', one per file.
+ 'cwd_n_alloc' is the number of elements space has been allocated for.
+ 'cwd_n_used' is the number actually in use. */
/* Address of block containing the files that are described. */
static struct fileinfo *cwd_file;
-/* Length of block that `cwd_file' points to, measured in files. */
+/* Length of block that 'cwd_file' points to, measured in files. */
static size_t cwd_n_alloc;
-/* Index of first unused slot in `cwd_file'. */
+/* Index of first unused slot in 'cwd_file'. */
static size_t cwd_n_used;
/* Vector of pointers to files, in proper sorted order, and the number
@@ -287,15 +322,15 @@ static void **sorted_file;
static size_t sorted_file_alloc;
/* When true, in a color listing, color each symlink name according to the
- type of file it points to. Otherwise, color them according to the `ln'
+ type of file it points to. Otherwise, color them according to the 'ln'
directive in LS_COLORS. Dangling (orphan) symlinks are treated specially,
- regardless. This is set when `ln=target' appears in LS_COLORS. */
+ regardless. This is set when 'ln=target' appears in LS_COLORS. */
static bool color_symlink_as_referent;
/* mode of appropriate file for colorization */
#define FILE_OR_LINK_MODE(File) \
- ((color_symlink_as_referent & (File)->linkok) \
+ ((color_symlink_as_referent && (File)->linkok) \
? (File)->linkmode : (File)->stat.st_mode)
@@ -305,7 +340,7 @@ struct pending
{
char *name;
/* If the directory is actually the file pointed to by a symbolic link we
- were told to list, `realname' will contain the name of the symbolic
+ were told to list, 'realname' will contain the name of the symbolic
link, otherwise zero. */
char *realname;
bool command_line_arg;
@@ -317,17 +352,15 @@ static struct pending *pending_dirs;
/* Current time in seconds and nanoseconds since 1970, updated as
needed when deciding whether a file is recent. */
-static time_t current_time = TYPE_MINIMUM (time_t);
-static int current_time_ns = -1;
+static struct timespec current_time;
+
+static bool print_scontext;
+static char UNKNOWN_SECURITY_CONTEXT[] = "?";
/* Whether any of the files has an ACL. This affects the width of the
mode column. */
-#if USE_ACL
static bool any_has_acl;
-#else
-enum { any_has_acl = false };
-#endif
/* The number of columns to use for columns containing inode numbers,
block sizes, link counts, owners, groups, authors, major device
@@ -336,6 +369,7 @@ enum { any_has_acl = false };
static int inode_number_width;
static int block_size_width;
static int nlink_width;
+static int scontext_width;
static int owner_width;
static int group_width;
static int author_width;
@@ -365,9 +399,9 @@ enum format
static enum format format;
-/* `full-iso' uses full ISO-style dates and times. `long-iso' uses longer
- ISO-style time stamps, though shorter than `full-iso'. `iso' uses shorter
- ISO-style time stamps. `locale' uses locale-dependent time stamps. */
+/* 'full-iso' uses full ISO-style dates and times. 'long-iso' uses longer
+ ISO-style time stamps, though shorter than 'full-iso'. 'iso' uses shorter
+ ISO-style time stamps. 'locale' uses locale-dependent time stamps. */
enum time_style
{
full_iso_time_style, /* --time-style=full-iso */
@@ -447,13 +481,14 @@ static bool numeric_ids;
static bool print_block_size;
-/* Human-readable options for output. */
+/* Human-readable options for output, when printing block counts. */
static int human_output_opts;
-/* The units to use when printing sizes other than file sizes. */
+/* The units to use when printing block counts. */
static uintmax_t output_block_size;
/* Likewise, but for file sizes. */
+static int file_human_output_opts;
static uintmax_t file_output_block_size = 1;
/* Follow the output with a special string. Using this format,
@@ -461,10 +496,10 @@ static uintmax_t file_output_block_size = 1;
strange characters in file names. */
static bool dired;
-/* `none' means don't mention the type of files.
- `slash' means mention directories only, with a '/'.
- `file_type' means mention file types.
- `classify' means mention file types and mark executables.
+/* 'none' means don't mention the type of files.
+ 'slash' means mention directories only, with a '/'.
+ 'file_type' means mention file types.
+ 'classify' means mention file types and mark executables.
Controlled by -F, -p, and --indicator-style. */
@@ -495,6 +530,12 @@ ARGMATCH_VERIFY (indicator_style_args, indicator_style_types);
static bool print_with_color;
+/* Whether we used any colors in the output so far. If so, we will
+ need to restore the default color later. If not, we will need to
+ call prep_non_filename_text before using color for the first time. */
+
+static bool used_color = false;
+
enum color_type
{
color_never, /* 0: default or --color=never */
@@ -513,16 +554,18 @@ enum Dereference_symlink
enum indicator_no
{
- C_LEFT, C_RIGHT, C_END, C_NORM, C_FILE, C_DIR, C_LINK, C_FIFO, C_SOCK,
+ C_LEFT, C_RIGHT, C_END, C_RESET, C_NORM, C_FILE, C_DIR, C_LINK,
+ C_FIFO, C_SOCK,
C_BLK, C_CHR, C_MISSING, C_ORPHAN, C_EXEC, C_DOOR, C_SETUID, C_SETGID,
- C_STICKY, C_OTHER_WRITABLE, C_STICKY_OTHER_WRITABLE
+ C_STICKY, C_OTHER_WRITABLE, C_STICKY_OTHER_WRITABLE, C_CAP, C_MULTIHARDLINK,
+ C_CLR_TO_EOL
};
static const char *const indicator_name[]=
{
- "lc", "rc", "ec", "no", "fi", "di", "ln", "pi", "so",
+ "lc", "rc", "ec", "rs", "no", "fi", "di", "ln", "pi", "so",
"bd", "cd", "mi", "or", "ex", "do", "su", "sg", "st",
- "ow", "tw", NULL
+ "ow", "tw", "ca", "mh", "cl", NULL
};
struct color_ext_type
@@ -536,9 +579,10 @@ static struct bin_str color_indicator[] =
{
{ LEN_STR_PAIR ("\033[") }, /* lc: Left of color sequence */
{ LEN_STR_PAIR ("m") }, /* rc: Right of color sequence */
- { 0, NULL }, /* ec: End color (replaces lc+no+rc) */
- { LEN_STR_PAIR ("0") }, /* no: Normal */
- { LEN_STR_PAIR ("0") }, /* fi: File: default */
+ { 0, NULL }, /* ec: End color (replaces lc+rs+rc) */
+ { LEN_STR_PAIR ("0") }, /* rs: Reset to ordinary colors */
+ { 0, NULL }, /* no: Normal */
+ { 0, NULL }, /* fi: File: default */
{ LEN_STR_PAIR ("01;34") }, /* di: Directory: bright blue */
{ LEN_STR_PAIR ("01;36") }, /* ln: Symlink: bright cyan */
{ LEN_STR_PAIR ("33") }, /* pi: Pipe: yellow/brown */
@@ -554,6 +598,9 @@ static struct bin_str color_indicator[] =
{ LEN_STR_PAIR ("37;44") }, /* st: sticky: black on blue */
{ LEN_STR_PAIR ("34;42") }, /* ow: other-writable: blue on green */
{ LEN_STR_PAIR ("30;42") }, /* tw: ow w/ sticky: black on green */
+ { LEN_STR_PAIR ("30;41") }, /* ca: black on red */
+ { 0, NULL }, /* mh: disabled by default */
+ { LEN_STR_PAIR ("\033[K") }, /* cl: clear to end of line */
};
/* FIXME: comment */
@@ -594,11 +641,11 @@ static bool directories_first;
static enum
{
- /* Ignore files whose names start with `.', and files specified by
+ /* Ignore files whose names start with '.', and files specified by
--hide and --ignore. */
IGNORE_DEFAULT,
- /* Ignore `.', `..', and files specified by --ignore. */
+ /* Ignore '.', '..', and files specified by --ignore. */
IGNORE_DOT_AND_DOTDOT,
/* Ignore only files specified by --ignore. */
@@ -608,7 +655,7 @@ static enum
/* A linked list of shell-style globbing patterns. If a non-argument
file name matches any of these patterns, it is ignored.
Controlled by -I. Multiple -I options accumulate.
- The -B option adds `*~' and `.*~' to this list. */
+ The -B option adds '*~' and '.*~' to this list. */
struct ignore_pattern
{
@@ -622,12 +669,12 @@ static struct ignore_pattern *ignore_patterns;
variable itself to be ignored. */
static struct ignore_pattern *hide_patterns;
-/* True means output nongraphic chars in file names as `?'.
+/* True means output nongraphic chars in file names as '?'.
(-q, --hide-control-chars)
qmark_funny_chars and the quoting style (-Q, --quoting-style=WORD) are
independent. The algorithm is: first, obey the quoting style to get a
string representing the file name; then, if qmark_funny_chars is set,
- replace all nonprintable chars in that string with `?'. It's necessary
+ replace all nonprintable chars in that string with '?'. It's necessary
to replace nonprintable chars even in quoted strings, because we don't
want to mess up the terminal if control chars get sent to it, and some
quoting methods pass through control chars as-is. */
@@ -651,12 +698,16 @@ static bool print_dir_name;
static size_t line_length;
+/* The local time zone rules, as per the TZ environment variable. */
+
+static timezone_t localtz;
+
/* If true, the file listing format requires that stat be called on
each file. */
static bool format_needs_stat;
-/* Similar to `format_needs_stat', but set if only the file type is
+/* Similar to 'format_needs_stat', but set if only the file type is
needed. */
static bool format_needs_type;
@@ -681,6 +732,11 @@ static char const *long_time_format[2] =
screen columns small, because many people work in windows with
only 80 columns. But make this as wide as the other string
below, for recent files. */
+ /* TRANSLATORS: ls output needs to be aligned for ease of reading,
+ so be wary of using variable width fields from the locale.
+ Note %b is handled specially by ls and aligned correctly.
+ Note also that specifying a width as in %5b is erroneous as strftime
+ will count bytes rather than characters in multibyte locales. */
N_("%b %e %Y"),
/* strftime format for recent files (younger than 6 months), in -l
output. This should contain the month, day and time (at
@@ -689,6 +745,11 @@ static char const *long_time_format[2] =
screen columns small, because many people work in windows with
only 80 columns. But make this as wide as the other string
above, for non-recent files. */
+ /* TRANSLATORS: ls output needs to be aligned for ease of reading,
+ so be wary of using variable width fields from the locale.
+ Note %b is handled specially by ls and aligned correctly.
+ Note also that specifying a width as in %5b is erroneous as strftime
+ will count bytes rather than characters in multibyte locales. */
N_("%b %e %H:%M")
};
@@ -711,11 +772,14 @@ static int exit_status;
/* Exit statuses. */
enum
{
- /* "ls" had a minor problem (e.g., it could not stat a directory
- entry). */
+ /* "ls" had a minor problem. E.g., while processing a directory,
+ ls obtained the name of an entry via readdir, yet was later
+ unable to stat that name. This happens when listing a directory
+ in which entries are actively being removed or renamed. */
LS_MINOR_PROBLEM = 1,
- /* "ls" had more serious trouble. */
+ /* "ls" had more serious trouble (e.g., memory exhausted, invalid
+ option or failure to stat a command line argument. */
LS_FAILURE = 2
};
@@ -733,10 +797,6 @@ enum
GROUP_DIRECTORIES_FIRST_OPTION,
HIDE_OPTION,
INDICATOR_STYLE_OPTION,
-
- /* FIXME: --kilobytes is deprecated (but not -k); remove in late 2006 */
- KILOBYTES_LONG_OPTION,
-
QUOTING_STYLE_OPTION,
SHOW_CONTROL_CHARS_OPTION,
SI_OPTION,
@@ -756,7 +816,7 @@ static struct option const long_options[] =
GROUP_DIRECTORIES_FIRST_OPTION},
{"human-readable", no_argument, NULL, 'h'},
{"inode", no_argument, NULL, 'i'},
- {"kilobytes", no_argument, NULL, KILOBYTES_LONG_OPTION},
+ {"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'},
@@ -787,6 +847,7 @@ static struct option const long_options[] =
{"time-style", required_argument, NULL, TIME_STYLE_OPTION},
{"color", optional_argument, NULL, COLOR_OPTION},
{"block-size", required_argument, NULL, BLOCK_SIZE_OPTION},
+ {"context", no_argument, 0, 'Z'},
{"author", no_argument, NULL, AUTHOR_OPTION},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
@@ -879,16 +940,16 @@ static size_t dired_pos;
#define DIRED_INDENT() \
do \
{ \
- if (dired) \
- DIRED_FPUTS_LITERAL (" ", stdout); \
+ if (dired) \
+ DIRED_FPUTS_LITERAL (" ", stdout); \
} \
while (0)
-/* With --dired, store pairs of beginning and ending indices of filenames. */
+/* With --dired, store pairs of beginning and ending indices of file names. */
static struct obstack dired_obstack;
/* With --dired, store pairs of beginning and ending indices of any
- directory names that appear as headers (just before `total' line)
+ directory names that appear as headers (just before 'total' line)
for lists of directory entries. Such directory names are seen when
listing hierarchies using -R and when a directory is listed with at
least one other command line argument. */
@@ -899,7 +960,7 @@ static struct obstack subdired_obstack;
do \
{ \
if (dired) \
- obstack_grow (obs, &dired_pos, sizeof (dired_pos)); \
+ obstack_grow (obs, &dired_pos, sizeof (dired_pos)); \
} \
while (0)
@@ -909,27 +970,36 @@ static struct obstack subdired_obstack;
static struct obstack dev_ino_obstack;
/* Push a pair onto the device/inode stack. */
-#define DEV_INO_PUSH(Dev, Ino) \
- do \
- { \
- struct dev_ino *di; \
- obstack_blank (&dev_ino_obstack, sizeof (struct dev_ino)); \
- di = -1 + (struct dev_ino *) obstack_next_free (&dev_ino_obstack); \
- di->st_dev = (Dev); \
- di->st_ino = (Ino); \
- } \
- while (0)
+static void
+dev_ino_push (dev_t dev, ino_t ino)
+{
+ void *vdi;
+ struct dev_ino *di;
+ int dev_ino_size = sizeof *di;
+ obstack_blank (&dev_ino_obstack, dev_ino_size);
+ vdi = obstack_next_free (&dev_ino_obstack);
+ di = vdi;
+ di--;
+ di->st_dev = dev;
+ di->st_ino = ino;
+}
/* Pop a dev/ino struct off the global dev_ino_obstack
and return that struct. */
static struct dev_ino
dev_ino_pop (void)
{
- assert (sizeof (struct dev_ino) <= obstack_object_size (&dev_ino_obstack));
- obstack_blank (&dev_ino_obstack, -(int) (sizeof (struct dev_ino)));
- return *(struct dev_ino *) obstack_next_free (&dev_ino_obstack);
+ void *vdi;
+ struct dev_ino *di;
+ int dev_ino_size = sizeof *di;
+ assert (dev_ino_size <= obstack_object_size (&dev_ino_obstack));
+ obstack_blank_fast (&dev_ino_obstack, -dev_ino_size);
+ vdi = obstack_next_free (&dev_ino_obstack);
+ di = vdi;
+ return *di;
}
+/* Note the use commented out below:
#define ASSERT_MATCHING_DEV_INO(Name, Di) \
do \
{ \
@@ -940,7 +1010,7 @@ dev_ino_pop (void)
assert (sb.st_ino == Di.st_ino); \
} \
while (0)
-
+*/
/* Write to standard output PREFIX, followed by the quoting style and
a space-separated list of the integers stored in OS all on one line. */
@@ -959,11 +1029,61 @@ dired_dump_obstack (const char *prefix, struct obstack *os)
pos = (size_t *) obstack_finish (os);
fputs (prefix, stdout);
for (i = 0; i < n_pos; i++)
- printf (" %lu", (unsigned long int) pos[i]);
+ printf (" %lu", (unsigned long int) pos[i]);
putchar ('\n');
}
}
+/* Read the abbreviated month names from the locale, to align them
+ and to determine the max width of the field and to truncate names
+ greater than our max allowed.
+ Note even though this handles multibyte locales correctly
+ it's not restricted to them as single byte locales can have
+ variable width abbreviated months and also precomputing/caching
+ the names was seen to increase the performance of ls significantly. */
+
+/* max number of display cells to use */
+enum { MAX_MON_WIDTH = 5 };
+/* In the unlikely event that the abmon[] storage is not big enough
+ an error message will be displayed, and we revert to using
+ unmodified abbreviated month names from the locale database. */
+static char abmon[12][MAX_MON_WIDTH * 2 * MB_LEN_MAX + 1];
+/* minimum width needed to align %b, 0 => don't use precomputed values. */
+static size_t required_mon_width;
+
+static size_t
+abmon_init (void)
+{
+#ifdef HAVE_NL_LANGINFO
+ required_mon_width = MAX_MON_WIDTH;
+ size_t curr_max_width;
+ do
+ {
+ curr_max_width = required_mon_width;
+ required_mon_width = 0;
+ for (int i = 0; i < 12; i++)
+ {
+ size_t width = curr_max_width;
+
+ size_t req = mbsalign (nl_langinfo (ABMON_1 + i),
+ abmon[i], sizeof (abmon[i]),
+ &width, MBS_ALIGN_LEFT, 0);
+
+ if (req == (size_t) -1 || req >= sizeof (abmon[i]))
+ {
+ required_mon_width = 0; /* ignore precomputed strings. */
+ return required_mon_width;
+ }
+
+ required_mon_width = MAX (required_mon_width, width);
+ }
+ }
+ while (curr_max_width > required_mon_width);
+#endif
+
+ return required_mon_width;
+}
+
static size_t
dev_ino_hash (void const *x, size_t table_size)
{
@@ -1034,8 +1154,8 @@ is_colored (enum indicator_no type)
size_t len = color_indicator[type].len;
char const *s = color_indicator[type].string;
return ! (len == 0
- || (len == 1 && strncmp (s, "0", 1) == 0)
- || (len == 2 && strncmp (s, "00", 2) == 0));
+ || (len == 1 && STRNCMP_LIT (s, "0") == 0)
+ || (len == 2 && STRNCMP_LIT (s, "00") == 0));
}
static void
@@ -1045,6 +1165,17 @@ restore_default_color (void)
put_indicator (&color_indicator[C_RIGHT]);
}
+static void
+set_normal_color (void)
+{
+ if (print_with_color && is_colored (C_NORM))
+ {
+ put_indicator (&color_indicator[C_LEFT]);
+ put_indicator (&color_indicator[C_NORM]);
+ put_indicator (&color_indicator[C_RIGHT]);
+ }
+}
+
/* An ordinary signal was received; arrange for the program to exit. */
static void
@@ -1076,39 +1207,40 @@ stophandler (int sig)
static void
process_signals (void)
{
- while (interrupt_signal | stop_signal_count)
+ while (interrupt_signal || stop_signal_count)
{
int sig;
int stops;
sigset_t oldset;
- restore_default_color ();
+ if (used_color)
+ restore_default_color ();
fflush (stdout);
sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
/* Reload interrupt_signal and stop_signal_count, in case a new
- signal was handled before sigprocmask took effect. */
+ signal was handled before sigprocmask took effect. */
sig = interrupt_signal;
stops = stop_signal_count;
/* SIGTSTP is special, since the application can receive that signal
- more than once. In this case, don't set the signal handler to the
- default. Instead, just raise the uncatchable SIGSTOP. */
+ more than once. In this case, don't set the signal handler to the
+ default. Instead, just raise the uncatchable SIGSTOP. */
if (stops)
- {
- stop_signal_count = stops - 1;
- sig = SIGSTOP;
- }
+ {
+ stop_signal_count = stops - 1;
+ sig = SIGSTOP;
+ }
else
- signal (sig, SIG_DFL);
+ signal (sig, SIG_DFL);
/* Exit or suspend the program. */
raise (sig);
sigprocmask (SIG_SETMASK, &oldset, NULL);
/* If execution reaches here, then the program has been
- continued (after being suspended). */
+ continued (after being suspended). */
}
}
@@ -1143,14 +1275,14 @@ main (int argc, char **argv)
SIGXFSZ,
#endif
};
- enum { nsigs = sizeof sig / sizeof sig[0] };
+ enum { nsigs = ARRAY_CARDINALITY (sig) };
#if ! SA_NOCLDSTOP
bool caught_sig[nsigs];
#endif
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -1158,13 +1290,16 @@ main (int argc, char **argv)
initialize_exit_failure (LS_FAILURE);
atexit (close_stdout);
-#define N_ENTRIES(Array) (sizeof Array / sizeof *(Array))
- assert (N_ENTRIES (color_indicator) + 1 == N_ENTRIES (indicator_name));
+ assert (ARRAY_CARDINALITY (color_indicator) + 1
+ == ARRAY_CARDINALITY (indicator_name));
exit_status = EXIT_SUCCESS;
print_dir_name = true;
pending_dirs = NULL;
+ current_time.tv_sec = TYPE_MINIMUM (time_t);
+ current_time.tv_nsec = -1;
+
i = decode_switches (argc, argv);
if (print_with_color)
@@ -1176,82 +1311,83 @@ main (int argc, char **argv)
{
/* Avoid following symbolic links when possible. */
if (is_colored (C_ORPHAN)
- || is_colored (C_EXEC)
- || (is_colored (C_MISSING) && format == long_format))
- check_symlink_color = true;
+ || (is_colored (C_EXEC) && color_symlink_as_referent)
+ || (is_colored (C_MISSING) && format == long_format))
+ check_symlink_color = true;
/* If the standard output is a controlling terminal, watch out
for signals, so that the colors can be restored to the
default state if "ls" is suspended or interrupted. */
if (0 <= tcgetpgrp (STDOUT_FILENO))
- {
- int j;
+ {
+ int j;
#if SA_NOCLDSTOP
- struct sigaction act;
-
- sigemptyset (&caught_signals);
- for (j = 0; j < nsigs; j++)
- {
- sigaction (sig[j], NULL, &act);
- if (act.sa_handler != SIG_IGN)
- sigaddset (&caught_signals, sig[j]);
- }
-
- 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, NULL);
- }
+ struct sigaction act;
+
+ sigemptyset (&caught_signals);
+ for (j = 0; j < nsigs; j++)
+ {
+ sigaction (sig[j], NULL, &act);
+ if (act.sa_handler != SIG_IGN)
+ sigaddset (&caught_signals, sig[j]);
+ }
+
+ 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, NULL);
+ }
#else
- for (j = 0; j < nsigs; j++)
- {
- caught_sig[j] = (signal (sig[j], SIG_IGN) != SIG_IGN);
- if (caught_sig[j])
- {
- signal (sig[j], sig[j] == SIGTSTP ? stophandler : sighandler);
- siginterrupt (sig[j], 0);
- }
- }
+ for (j = 0; j < nsigs; j++)
+ {
+ caught_sig[j] = (signal (sig[j], SIG_IGN) != SIG_IGN);
+ if (caught_sig[j])
+ {
+ signal (sig[j], sig[j] == SIGTSTP ? stophandler : sighandler);
+ siginterrupt (sig[j], 0);
+ }
+ }
#endif
- }
-
- prep_non_filename_text ();
+ }
}
if (dereference == DEREF_UNDEFINED)
dereference = ((immediate_dirs
- || indicator_style == classify
- || format == long_format)
- ? DEREF_NEVER
- : DEREF_COMMAND_LINE_SYMLINK_TO_DIR);
+ || indicator_style == classify
+ || format == long_format)
+ ? DEREF_NEVER
+ : DEREF_COMMAND_LINE_SYMLINK_TO_DIR);
/* When using -R, initialize a data structure we'll use to
detect any directory cycles. */
if (recursive)
{
active_dir_set = hash_initialize (INITIAL_TABLE_SIZE, NULL,
- dev_ino_hash,
- dev_ino_compare,
- dev_ino_free);
+ dev_ino_hash,
+ dev_ino_compare,
+ dev_ino_free);
if (active_dir_set == NULL)
- xalloc_die ();
+ xalloc_die ();
obstack_init (&dev_ino_obstack);
}
+ localtz = tzalloc (getenv ("TZ"));
+
format_needs_stat = sort_type == sort_time || sort_type == sort_size
|| format == long_format
+ || print_scontext
|| print_block_size;
format_needs_type = (! format_needs_stat
- && (recursive
- || print_with_color
- || indicator_style != none
- || directories_first));
+ && (recursive
+ || print_with_color
+ || indicator_style != none
+ || directories_first));
if (dired)
{
@@ -1270,9 +1406,9 @@ main (int argc, char **argv)
if (n_files <= 0)
{
if (immediate_dirs)
- gobble_file (".", directory, NOT_AN_INODE_NUMBER, true, "");
+ gobble_file (".", directory, NOT_AN_INODE_NUMBER, true, "");
else
- queue_directory (".", NULL, true);
+ queue_directory (".", NULL, true);
}
else
do
@@ -1283,11 +1419,11 @@ main (int argc, char **argv)
{
sort_files ();
if (!immediate_dirs)
- extract_dirs_from_files (NULL, true);
- /* `cwd_n_used' might be zero now. */
+ extract_dirs_from_files (NULL, true);
+ /* 'cwd_n_used' might be zero now. */
}
- /* In the following if/else blocks, it is sufficient to test `pending_dirs'
+ /* In the following if/else blocks, it is sufficient to test 'pending_dirs'
(and not pending_dirs->name) because there may be no markers in the queue
at this point. A marker may be enqueued when extract_dirs_from_files is
called with a non-empty string or via print_dir. */
@@ -1295,7 +1431,7 @@ main (int argc, char **argv)
{
print_current_files ();
if (pending_dirs)
- DIRED_PUTCHAR ('\n');
+ DIRED_PUTCHAR ('\n');
}
else if (n_files <= 1 && pending_dirs && pending_dirs->next == 0)
print_dir_name = false;
@@ -1306,25 +1442,25 @@ main (int argc, char **argv)
pending_dirs = pending_dirs->next;
if (LOOP_DETECT)
- {
- if (thispend->name == NULL)
- {
- /* 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. */
- struct dev_ino di = dev_ino_pop ();
- struct dev_ino *found = hash_delete (active_dir_set, &di);
- /* ASSERT_MATCHING_DEV_INO (thispend->realname, di); */
- assert (found);
- dev_ino_free (found);
- free_pending_ent (thispend);
- continue;
- }
- }
+ {
+ if (thispend->name == NULL)
+ {
+ /* 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. */
+ struct dev_ino di = dev_ino_pop ();
+ struct dev_ino *found = hash_delete (active_dir_set, &di);
+ /* ASSERT_MATCHING_DEV_INO (thispend->realname, di); */
+ assert (found);
+ dev_ino_free (found);
+ free_pending_ent (thispend);
+ continue;
+ }
+ }
print_dir (thispend->name, thispend->realname,
- thispend->command_line_arg);
+ thispend->command_line_arg);
free_pending_ent (thispend);
print_dir_name = true;
@@ -1334,29 +1470,38 @@ main (int argc, char **argv)
{
int j;
- restore_default_color ();
+ if (used_color)
+ {
+ /* Skip the restore when it would be a no-op, i.e.,
+ when left is "\033[" and right is "m". */
+ if (!(color_indicator[C_LEFT].len == 2
+ && memcmp (color_indicator[C_LEFT].string, "\033[", 2) == 0
+ && color_indicator[C_RIGHT].len == 1
+ && color_indicator[C_RIGHT].string[0] == 'm'))
+ restore_default_color ();
+ }
fflush (stdout);
/* Restore the default signal handling. */
#if SA_NOCLDSTOP
for (j = 0; j < nsigs; j++)
- if (sigismember (&caught_signals, sig[j]))
- signal (sig[j], SIG_DFL);
+ 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);
+ if (caught_sig[j])
+ signal (sig[j], SIG_DFL);
#endif
/* Act on any signals that arrived before the default was restored.
- 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. */
+ 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--)
- raise (SIGSTOP);
+ raise (SIGSTOP);
j = interrupt_signal;
if (j)
- raise (j);
+ raise (j);
}
if (dired)
@@ -1365,7 +1510,7 @@ main (int argc, char **argv)
dired_dump_obstack ("//DIRED//", &dired_obstack);
dired_dump_obstack ("//SUBDIRED//", &subdired_obstack);
printf ("//DIRED-OPTIONS// --quoting-style=%s\n",
- quoting_style_args[get_quoting_style (filename_quoting_options)]);
+ quoting_style_args[get_quoting_style (filename_quoting_options)]);
}
if (LOOP_DETECT)
@@ -1374,7 +1519,32 @@ main (int argc, char **argv)
hash_free (active_dir_set);
}
- exit (exit_status);
+ return exit_status;
+}
+
+/* Set the line length to the value given by SPEC. Return true if
+ successful. 0 means no limit on line length. */
+
+static bool
+set_line_length (char const *spec)
+{
+ uintmax_t val;
+
+ /* Treat too-large values as if they were SIZE_MAX, which is
+ effectively infinity. */
+ switch (xstrtoumax (spec, NULL, 0, &val, ""))
+ {
+ case LONGINT_OK:
+ line_length = MIN (val, SIZE_MAX);
+ return true;
+
+ case LONGINT_OVERFLOW:
+ line_length = SIZE_MAX;
+ return true;
+
+ default:
+ return false;
+ }
}
/* Set all the option flags according to the switches specified.
@@ -1383,11 +1553,10 @@ main (int argc, char **argv)
static int
decode_switches (int argc, char **argv)
{
- int c;
char *time_style_option = NULL;
- /* Record whether there is an option specifying sort type. */
bool sort_type_specified = false;
+ bool kibibytes_specified = false;
qmark_funny_chars = false;
@@ -1396,30 +1565,31 @@ decode_switches (int argc, char **argv)
switch (ls_mode)
{
case LS_MULTI_COL:
- /* This is for the `dir' program. */
+ /* This is for the 'dir' program. */
format = many_per_line;
set_quoting_style (NULL, escape_quoting_style);
break;
case LS_LONG_FORMAT:
- /* This is for the `vdir' program. */
+ /* This is for the 'vdir' program. */
format = long_format;
set_quoting_style (NULL, escape_quoting_style);
break;
case LS_LS:
- /* This is for the `ls' program. */
+ /* This is for the 'ls' program. */
if (isatty (STDOUT_FILENO))
- {
- format = many_per_line;
- /* See description of qmark_funny_chars, above. */
- qmark_funny_chars = true;
- }
+ {
+ format = many_per_line;
+ set_quoting_style (NULL, shell_escape_quoting_style);
+ /* See description of qmark_funny_chars, above. */
+ qmark_funny_chars = true;
+ }
else
- {
- format = one_per_line;
- qmark_funny_chars = false;
- }
+ {
+ format = one_per_line;
+ qmark_funny_chars = false;
+ }
break;
default:
@@ -1439,48 +1609,17 @@ decode_switches (int argc, char **argv)
ignore_mode = IGNORE_DEFAULT;
ignore_patterns = NULL;
hide_patterns = NULL;
+ print_scontext = false;
- /* FIXME: put this in a function. */
- {
- char const *q_style = getenv ("QUOTING_STYLE");
- if (q_style)
- {
- int i = ARGMATCH (q_style, quoting_style_args, quoting_style_vals);
- if (0 <= i)
- set_quoting_style (NULL, quoting_style_vals[i]);
- else
- error (0, 0,
- _("ignoring invalid value of environment variable QUOTING_STYLE: %s"),
- quotearg (q_style));
- }
- }
-
- {
- char const *ls_block_size = getenv ("LS_BLOCK_SIZE");
- human_output_opts = human_options (ls_block_size, false,
- &output_block_size);
- if (ls_block_size || getenv ("BLOCK_SIZE"))
- file_output_block_size = output_block_size;
- }
+ getenv_quoting_style ();
line_length = 80;
{
char const *p = getenv ("COLUMNS");
- if (p && *p)
- {
- unsigned long int tmp_ulong;
- if (xstrtoul (p, NULL, 0, &tmp_ulong, NULL) == LONGINT_OK
- && 0 < tmp_ulong && tmp_ulong <= SIZE_MAX)
- {
- line_length = tmp_ulong;
- }
- else
- {
- error (0, 0,
- _("ignoring invalid width in environment variable COLUMNS: %s"),
- quotearg (p));
- }
- }
+ if (p && *p && ! set_line_length (p))
+ error (0, 0,
+ _("ignoring invalid width in environment variable COLUMNS: %s"),
+ quote (p));
}
#ifdef TIOCGWINSZ
@@ -1488,7 +1627,7 @@ decode_switches (int argc, char **argv)
struct winsize ws;
if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &ws) != -1
- && 0 < ws.ws_col && ws.ws_col == (size_t) ws.ws_col)
+ && 0 < ws.ws_col && ws.ws_col == (size_t) ws.ws_col)
line_length = ws.ws_col;
}
#endif
@@ -1498,331 +1637,353 @@ decode_switches (int argc, char **argv)
tabsize = 8;
if (p)
{
- unsigned long int tmp_ulong;
- if (xstrtoul (p, NULL, 0, &tmp_ulong, NULL) == LONGINT_OK
- && tmp_ulong <= SIZE_MAX)
- {
- tabsize = tmp_ulong;
- }
- else
- {
- error (0, 0,
- _("ignoring invalid tab size in environment variable TABSIZE: %s"),
- quotearg (p));
- }
+ unsigned long int tmp_ulong;
+ if (xstrtoul (p, NULL, 0, &tmp_ulong, NULL) == LONGINT_OK
+ && tmp_ulong <= SIZE_MAX)
+ {
+ tabsize = tmp_ulong;
+ }
+ else
+ {
+ error (0, 0,
+ _("ignoring invalid tab size in environment variable TABSIZE: %s"),
+ quote (p));
+ }
}
}
- while ((c = getopt_long (argc, argv,
- "abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UX1",
- long_options, NULL)) != -1)
+ while (true)
{
+ int oi = -1;
+ int c = getopt_long (argc, argv,
+ "abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UXZ1",
+ long_options, &oi);
+ if (c == -1)
+ break;
+
switch (c)
- {
- case 'a':
- ignore_mode = IGNORE_MINIMAL;
- break;
-
- case 'b':
- set_quoting_style (NULL, escape_quoting_style);
- break;
-
- case 'c':
- time_type = time_ctime;
- break;
-
- case 'd':
- immediate_dirs = true;
- break;
-
- case 'f':
- /* Same as enabling -a -U and disabling -l -s. */
- ignore_mode = IGNORE_MINIMAL;
- sort_type = sort_none;
- sort_type_specified = true;
- /* disable -l */
- if (format == long_format)
- format = (isatty (STDOUT_FILENO) ? many_per_line : one_per_line);
- print_block_size = false; /* disable -s */
- print_with_color = false; /* disable --color */
- break;
-
- case FILE_TYPE_INDICATOR_OPTION: /* --file-type */
- indicator_style = file_type;
- break;
-
- case 'g':
- format = long_format;
- print_owner = false;
- break;
-
- case 'h':
- human_output_opts = human_autoscale | human_SI | human_base_1024;
- file_output_block_size = output_block_size = 1;
- break;
-
- case 'i':
- print_inode = true;
- break;
-
- case KILOBYTES_LONG_OPTION:
- error (0, 0,
- _("the --kilobytes option is deprecated; use -k instead"));
- /* fall through */
- case 'k':
- human_output_opts = 0;
- file_output_block_size = output_block_size = 1024;
- break;
-
- case 'l':
- format = long_format;
- break;
-
- case 'm':
- format = with_commas;
- break;
-
- case 'n':
- numeric_ids = true;
- format = long_format;
- break;
-
- case 'o': /* Just like -l, but don't display group info. */
- format = long_format;
- print_group = false;
- break;
-
- case 'p':
- indicator_style = slash;
- break;
-
- case 'q':
- qmark_funny_chars = true;
- break;
-
- case 'r':
- sort_reverse = true;
- break;
-
- case 's':
- print_block_size = true;
- break;
-
- case 't':
- sort_type = sort_time;
- sort_type_specified = true;
- break;
-
- case 'u':
- time_type = time_atime;
- break;
-
- case 'v':
- sort_type = sort_version;
- sort_type_specified = true;
- break;
-
- case 'w':
- {
- unsigned long int tmp_ulong;
- if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
- || ! (0 < tmp_ulong && tmp_ulong <= SIZE_MAX))
- error (LS_FAILURE, 0, _("invalid line width: %s"),
- quotearg (optarg));
- line_length = tmp_ulong;
- break;
- }
-
- case 'x':
- format = horizontal;
- break;
-
- case 'A':
- if (ignore_mode == IGNORE_DEFAULT)
- ignore_mode = IGNORE_DOT_AND_DOTDOT;
- break;
-
- case 'B':
- add_ignore_pattern ("*~");
- add_ignore_pattern (".*~");
- break;
-
- case 'C':
- format = many_per_line;
- break;
-
- case 'D':
- dired = true;
- break;
-
- case 'F':
- indicator_style = classify;
- break;
-
- case 'G': /* inhibit display of group info */
- print_group = false;
- break;
-
- case 'H':
- dereference = DEREF_COMMAND_LINE_ARGUMENTS;
- break;
-
- case DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR_OPTION:
- dereference = DEREF_COMMAND_LINE_SYMLINK_TO_DIR;
- break;
-
- case 'I':
- add_ignore_pattern (optarg);
- break;
-
- case 'L':
- dereference = DEREF_ALWAYS;
- break;
-
- case 'N':
- set_quoting_style (NULL, literal_quoting_style);
- break;
-
- case 'Q':
- set_quoting_style (NULL, c_quoting_style);
- break;
-
- case 'R':
- recursive = true;
- break;
-
- case 'S':
- sort_type = sort_size;
- sort_type_specified = true;
- break;
-
- case 'T':
- {
- unsigned long int tmp_ulong;
- if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
- || SIZE_MAX < tmp_ulong)
- error (LS_FAILURE, 0, _("invalid tab size: %s"),
- quotearg (optarg));
- tabsize = tmp_ulong;
- break;
- }
-
- case 'U':
- sort_type = sort_none;
- sort_type_specified = true;
- break;
-
- case 'X':
- sort_type = sort_extension;
- sort_type_specified = true;
- break;
-
- case '1':
- /* -1 has no effect after -l. */
- if (format != long_format)
- format = one_per_line;
- break;
+ {
+ case 'a':
+ ignore_mode = IGNORE_MINIMAL;
+ break;
+
+ case 'b':
+ set_quoting_style (NULL, escape_quoting_style);
+ break;
+
+ case 'c':
+ time_type = time_ctime;
+ break;
+
+ case 'd':
+ immediate_dirs = true;
+ break;
+
+ case 'f':
+ /* Same as enabling -a -U and disabling -l -s. */
+ ignore_mode = IGNORE_MINIMAL;
+ sort_type = sort_none;
+ sort_type_specified = true;
+ /* disable -l */
+ if (format == long_format)
+ format = (isatty (STDOUT_FILENO) ? many_per_line : one_per_line);
+ print_block_size = false; /* disable -s */
+ print_with_color = false; /* disable --color */
+ break;
+
+ case FILE_TYPE_INDICATOR_OPTION: /* --file-type */
+ indicator_style = file_type;
+ break;
+
+ case 'g':
+ format = long_format;
+ print_owner = false;
+ break;
+
+ case 'h':
+ file_human_output_opts = human_output_opts =
+ human_autoscale | human_SI | human_base_1024;
+ file_output_block_size = output_block_size = 1;
+ break;
+
+ case 'i':
+ print_inode = true;
+ break;
+
+ case 'k':
+ kibibytes_specified = true;
+ break;
+
+ case 'l':
+ format = long_format;
+ break;
+
+ case 'm':
+ format = with_commas;
+ break;
+
+ case 'n':
+ numeric_ids = true;
+ format = long_format;
+ break;
+
+ case 'o': /* Just like -l, but don't display group info. */
+ format = long_format;
+ print_group = false;
+ break;
+
+ case 'p':
+ indicator_style = slash;
+ break;
+
+ case 'q':
+ qmark_funny_chars = true;
+ break;
+
+ case 'r':
+ sort_reverse = true;
+ break;
+
+ case 's':
+ print_block_size = true;
+ break;
+
+ case 't':
+ sort_type = sort_time;
+ sort_type_specified = true;
+ break;
+
+ case 'u':
+ time_type = time_atime;
+ break;
+
+ case 'v':
+ sort_type = sort_version;
+ sort_type_specified = true;
+ break;
+
+ case 'w':
+ if (! set_line_length (optarg))
+ error (LS_FAILURE, 0, "%s: %s", _("invalid line width"),
+ quote (optarg));
+ break;
+
+ case 'x':
+ format = horizontal;
+ break;
+
+ case 'A':
+ if (ignore_mode == IGNORE_DEFAULT)
+ ignore_mode = IGNORE_DOT_AND_DOTDOT;
+ break;
+
+ case 'B':
+ add_ignore_pattern ("*~");
+ add_ignore_pattern (".*~");
+ break;
+
+ case 'C':
+ format = many_per_line;
+ break;
+
+ case 'D':
+ dired = true;
+ break;
+
+ case 'F':
+ indicator_style = classify;
+ break;
+
+ case 'G': /* inhibit display of group info */
+ print_group = false;
+ break;
+
+ case 'H':
+ dereference = DEREF_COMMAND_LINE_ARGUMENTS;
+ break;
+
+ case DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR_OPTION:
+ dereference = DEREF_COMMAND_LINE_SYMLINK_TO_DIR;
+ break;
+
+ case 'I':
+ add_ignore_pattern (optarg);
+ break;
+
+ case 'L':
+ dereference = DEREF_ALWAYS;
+ break;
+
+ case 'N':
+ set_quoting_style (NULL, literal_quoting_style);
+ break;
+
+ case 'Q':
+ set_quoting_style (NULL, c_quoting_style);
+ break;
+
+ case 'R':
+ recursive = true;
+ break;
+
+ case 'S':
+ sort_type = sort_size;
+ sort_type_specified = true;
+ break;
+
+ case 'T':
+ tabsize = xnumtoumax (optarg, 0, 0, SIZE_MAX, "",
+ _("invalid tab size"), LS_FAILURE);
+ break;
+
+ case 'U':
+ sort_type = sort_none;
+ sort_type_specified = true;
+ break;
+
+ case 'X':
+ sort_type = sort_extension;
+ sort_type_specified = true;
+ break;
+
+ case '1':
+ /* -1 has no effect after -l. */
+ if (format != long_format)
+ format = one_per_line;
+ break;
case AUTHOR_OPTION:
print_author = true;
break;
- case HIDE_OPTION:
- {
- struct ignore_pattern *hide = xmalloc (sizeof *hide);
- hide->pattern = optarg;
- hide->next = hide_patterns;
- hide_patterns = hide;
- }
- break;
-
- case SORT_OPTION:
- sort_type = XARGMATCH ("--sort", optarg, sort_args, sort_types);
- sort_type_specified = true;
- break;
-
- case GROUP_DIRECTORIES_FIRST_OPTION:
- directories_first = true;
- break;
-
- case TIME_OPTION:
- time_type = XARGMATCH ("--time", optarg, time_args, time_types);
- break;
-
- case FORMAT_OPTION:
- format = XARGMATCH ("--format", optarg, format_args, format_types);
- break;
-
- case FULL_TIME_OPTION:
- format = long_format;
- time_style_option = "full-iso";
- break;
-
- case COLOR_OPTION:
- {
- int i;
- if (optarg)
- i = XARGMATCH ("--color", optarg, color_args, color_types);
- else
- /* Using --color with no argument is equivalent to using
- --color=always. */
- i = color_always;
-
- print_with_color = (i == color_always
- || (i == color_if_tty
- && isatty (STDOUT_FILENO)));
-
- if (print_with_color)
- {
- /* Don't use TAB characters in output. Some terminal
- emulators can't handle the combination of tabs and
- color codes on the same line. */
- tabsize = 0;
- }
- break;
- }
-
- case INDICATOR_STYLE_OPTION:
- indicator_style = XARGMATCH ("--indicator-style", optarg,
- indicator_style_args,
- indicator_style_types);
- break;
-
- case QUOTING_STYLE_OPTION:
- set_quoting_style (NULL,
- XARGMATCH ("--quoting-style", optarg,
- quoting_style_args,
- quoting_style_vals));
- break;
-
- case TIME_STYLE_OPTION:
- time_style_option = optarg;
- break;
-
- case SHOW_CONTROL_CHARS_OPTION:
- qmark_funny_chars = false;
- break;
-
- case BLOCK_SIZE_OPTION:
- human_output_opts = human_options (optarg, true, &output_block_size);
- file_output_block_size = output_block_size;
- break;
-
- case SI_OPTION:
- human_output_opts = human_autoscale | human_SI;
- file_output_block_size = output_block_size = 1;
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (LS_FAILURE);
- }
+ case HIDE_OPTION:
+ {
+ struct ignore_pattern *hide = xmalloc (sizeof *hide);
+ hide->pattern = optarg;
+ hide->next = hide_patterns;
+ hide_patterns = hide;
+ }
+ break;
+
+ case SORT_OPTION:
+ sort_type = XARGMATCH ("--sort", optarg, sort_args, sort_types);
+ sort_type_specified = true;
+ break;
+
+ case GROUP_DIRECTORIES_FIRST_OPTION:
+ directories_first = true;
+ break;
+
+ case TIME_OPTION:
+ time_type = XARGMATCH ("--time", optarg, time_args, time_types);
+ break;
+
+ case FORMAT_OPTION:
+ format = XARGMATCH ("--format", optarg, format_args, format_types);
+ break;
+
+ case FULL_TIME_OPTION:
+ format = long_format;
+ time_style_option = bad_cast ("full-iso");
+ break;
+
+ case COLOR_OPTION:
+ {
+ int i;
+ if (optarg)
+ i = XARGMATCH ("--color", optarg, color_args, color_types);
+ else
+ /* Using --color with no argument is equivalent to using
+ --color=always. */
+ i = color_always;
+
+ print_with_color = (i == color_always
+ || (i == color_if_tty
+ && isatty (STDOUT_FILENO)));
+
+ if (print_with_color)
+ {
+ /* Don't use TAB characters in output. Some terminal
+ emulators can't handle the combination of tabs and
+ color codes on the same line. */
+ tabsize = 0;
+ }
+ break;
+ }
+
+ case INDICATOR_STYLE_OPTION:
+ indicator_style = XARGMATCH ("--indicator-style", optarg,
+ indicator_style_args,
+ indicator_style_types);
+ break;
+
+ case QUOTING_STYLE_OPTION:
+ set_quoting_style (NULL,
+ XARGMATCH ("--quoting-style", optarg,
+ quoting_style_args,
+ quoting_style_vals));
+ break;
+
+ case TIME_STYLE_OPTION:
+ time_style_option = optarg;
+ break;
+
+ case SHOW_CONTROL_CHARS_OPTION:
+ qmark_funny_chars = false;
+ break;
+
+ case BLOCK_SIZE_OPTION:
+ {
+ enum strtol_error e = human_options (optarg, &human_output_opts,
+ &output_block_size);
+ if (e != LONGINT_OK)
+ xstrtol_fatal (e, oi, 0, long_options, optarg);
+ file_human_output_opts = human_output_opts;
+ file_output_block_size = output_block_size;
+ }
+ break;
+
+ case SI_OPTION:
+ file_human_output_opts = human_output_opts =
+ human_autoscale | human_SI;
+ file_output_block_size = output_block_size = 1;
+ break;
+
+ case 'Z':
+ print_scontext = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (LS_FAILURE);
+ }
}
- max_idx = MAX (1, line_length / MIN_COLUMN_WIDTH);
+ if (! output_block_size)
+ {
+ char const *ls_block_size = getenv ("LS_BLOCK_SIZE");
+ human_options (ls_block_size,
+ &human_output_opts, &output_block_size);
+ if (ls_block_size || getenv ("BLOCK_SIZE"))
+ {
+ file_human_output_opts = human_output_opts;
+ file_output_block_size = output_block_size;
+ }
+ if (kibibytes_specified)
+ {
+ human_output_opts = 0;
+ output_block_size = 1024;
+ }
+ }
+
+ /* Determine the max possible number of display columns. */
+ max_idx = line_length / MIN_COLUMN_WIDTH;
+ /* Account for first display column not having a separator,
+ or line_lengths shorter than MIN_COLUMN_WIDTH. */
+ max_idx += line_length % MIN_COLUMN_WIDTH != 0;
filename_quoting_options = clone_quoting_options (NULL);
if (get_quoting_style (filename_quoting_options) == escape_quoting_style)
@@ -1830,8 +1991,8 @@ decode_switches (int argc, char **argv)
if (file_type <= indicator_style)
{
char const *p;
- for (p = "*=>@|" + indicator_style - file_type; *p; p++)
- set_char_quoting (filename_quoting_options, *p, 1);
+ for (p = &"*=>@|"[indicator_style - file_type]; *p; p++)
+ set_char_quoting (filename_quoting_options, *p, 1);
}
dirname_quoting_options = clone_quoting_options (NULL);
@@ -1846,7 +2007,7 @@ decode_switches (int argc, char **argv)
/* If -c or -u is specified and not -l (or any other option that implies -l),
and no sort-type was specified, then sort by the ctime (-c) or atime (-u).
The behavior of ls when using either -c or -u but with neither -l nor -t
- appears to be unspecified by POSIX. So, with GNU ls, `-u' alone means
+ appears to be unspecified by POSIX. So, with GNU ls, '-u' alone means
sort by atime (this is the one that's not specified by the POSIX spec),
-lu means show atime and sort by name, -lut means show atime and sort
by atime. */
@@ -1863,68 +2024,89 @@ decode_switches (int argc, char **argv)
static char const posix_prefix[] = "posix-";
if (! style)
- if (! (style = getenv ("TIME_STYLE")))
- style = "locale";
+ if (! (style = getenv ("TIME_STYLE")))
+ style = bad_cast ("locale");
- while (strncmp (style, posix_prefix, sizeof posix_prefix - 1) == 0)
- {
- if (! hard_locale (LC_TIME))
- return optind;
- style += sizeof posix_prefix - 1;
- }
+ 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 *p0 = style + 1;
- char *p1 = strchr (p0, '\n');
- if (! p1)
- p1 = p0;
- else
- {
- if (strchr (p1 + 1, '\n'))
- error (LS_FAILURE, 0, _("invalid time style format %s"),
- quote (p0));
- *p1++ = '\0';
- }
- long_time_format[0] = p0;
- long_time_format[1] = p1;
- }
+ {
+ char *p0 = style + 1;
+ char *p1 = strchr (p0, '\n');
+ if (! p1)
+ p1 = p0;
+ else
+ {
+ if (strchr (p1 + 1, '\n'))
+ error (LS_FAILURE, 0, _("invalid time style format %s"),
+ quote (p0));
+ *p1++ = '\0';
+ }
+ long_time_format[0] = p0;
+ long_time_format[1] = p1;
+ }
else
- switch (XARGMATCH ("time style", style,
- time_style_args,
- time_style_types))
- {
- case full_iso_time_style:
- long_time_format[0] = long_time_format[1] =
- "%Y-%m-%d %H:%M:%S.%N %z";
- break;
-
- case long_iso_time_style:
- case_long_iso_time_style:
- long_time_format[0] = long_time_format[1] = "%Y-%m-%d %H:%M";
- break;
-
- case iso_time_style:
- long_time_format[0] = "%Y-%m-%d ";
- long_time_format[1] = "%m-%d %H:%M";
- break;
-
- case locale_time_style:
- if (hard_locale (LC_TIME))
- {
- /* Ensure that the locale has translations for both
- formats. If not, fall back on long-iso format. */
- int i;
- for (i = 0; i < 2; i++)
- {
- char const *locale_format =
- dcgettext (NULL, long_time_format[i], LC_TIME);
- if (locale_format == long_time_format[i])
- goto case_long_iso_time_style;
- long_time_format[i] = locale_format;
- }
- }
- }
+ {
+ ptrdiff_t res = argmatch (style, time_style_args,
+ (char const *) time_style_types,
+ sizeof (*time_style_types));
+ if (res < 0)
+ {
+ /* This whole block used to be a simple use of XARGMATCH.
+ but that didn't print the "posix-"-prefixed variants or
+ the "+"-prefixed format string option upon failure. */
+ argmatch_invalid ("time style", style, res);
+
+ /* The following is a manual expansion of argmatch_valid,
+ but with the added "+ ..." description and the [posix-]
+ prefixes prepended. Note that this simplification works
+ only because all four existing time_style_types values
+ are distinct. */
+ fputs (_("Valid arguments are:\n"), stderr);
+ char const *const *p = time_style_args;
+ while (*p)
+ fprintf (stderr, " - [posix-]%s\n", *p++);
+ fputs (_(" - +FORMAT (e.g., +%H:%M) for a 'date'-style"
+ " format\n"), stderr);
+ usage (LS_FAILURE);
+ }
+ switch (res)
+ {
+ case full_iso_time_style:
+ long_time_format[0] = long_time_format[1] =
+ "%Y-%m-%d %H:%M:%S.%N %z";
+ break;
+
+ case long_iso_time_style:
+ long_time_format[0] = long_time_format[1] = "%Y-%m-%d %H:%M";
+ break;
+
+ case iso_time_style:
+ long_time_format[0] = "%Y-%m-%d ";
+ long_time_format[1] = "%m-%d %H:%M";
+ break;
+
+ case locale_time_style:
+ if (hard_locale (LC_TIME))
+ {
+ int i;
+ for (i = 0; i < 2; i++)
+ long_time_format[i] =
+ dcgettext (NULL, long_time_format[i], LC_TIME);
+ }
+ }
+ }
+
+ /* Note we leave %5b etc. alone so user widths/flags are honored. */
+ if (strstr (long_time_format[0], "%b")
+ || strstr (long_time_format[1], "%b"))
+ if (!abmon_init ())
+ error (0, 0, _("error initializing month strings"));
}
return optind;
@@ -1945,7 +2127,7 @@ decode_switches (int argc, char **argv)
static bool
get_funky_string (char **dest, const char **src, bool equals_end,
- size_t *output_count)
+ size_t *output_count)
{
char num; /* For numerical codes */
size_t count; /* Something to count with */
@@ -1965,170 +2147,170 @@ get_funky_string (char **dest, const char **src, bool equals_end,
while (state < ST_END)
{
switch (state)
- {
- case ST_GND: /* Ground state (no escapes) */
- switch (*p)
- {
- case ':':
- case '\0':
- state = ST_END; /* End of string */
- break;
- case '\\':
- state = ST_BACKSLASH; /* Backslash scape sequence */
- ++p;
- break;
- case '^':
- state = ST_CARET; /* Caret escape */
- ++p;
- break;
- case '=':
- if (equals_end)
- {
- state = ST_END; /* End */
- break;
- }
- /* else fall through */
- default:
- *(q++) = *(p++);
- ++count;
- break;
- }
- break;
-
- case ST_BACKSLASH: /* Backslash escaped character */
- switch (*p)
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- state = ST_OCTAL; /* Octal sequence */
- num = *p - '0';
- break;
- case 'x':
- case 'X':
- state = ST_HEX; /* Hex sequence */
- num = 0;
- break;
- case 'a': /* Bell */
- num = '\a';
- break;
- case 'b': /* Backspace */
- num = '\b';
- break;
- case 'e': /* Escape */
- num = 27;
- break;
- case 'f': /* Form feed */
- num = '\f';
- break;
- case 'n': /* Newline */
- num = '\n';
- break;
- case 'r': /* Carriage return */
- num = '\r';
- break;
- case 't': /* Tab */
- num = '\t';
- break;
- case 'v': /* Vtab */
- num = '\v';
- break;
- case '?': /* Delete */
+ {
+ case ST_GND: /* Ground state (no escapes) */
+ switch (*p)
+ {
+ case ':':
+ case '\0':
+ state = ST_END; /* End of string */
+ break;
+ case '\\':
+ state = ST_BACKSLASH; /* Backslash escape sequence */
+ ++p;
+ break;
+ case '^':
+ state = ST_CARET; /* Caret escape */
+ ++p;
+ break;
+ case '=':
+ if (equals_end)
+ {
+ state = ST_END; /* End */
+ break;
+ }
+ /* else fall through */
+ default:
+ *(q++) = *(p++);
+ ++count;
+ break;
+ }
+ break;
+
+ case ST_BACKSLASH: /* Backslash escaped character */
+ switch (*p)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ state = ST_OCTAL; /* Octal sequence */
+ num = *p - '0';
+ break;
+ case 'x':
+ case 'X':
+ state = ST_HEX; /* Hex sequence */
+ num = 0;
+ break;
+ case 'a': /* Bell */
+ num = '\a';
+ break;
+ case 'b': /* Backspace */
+ num = '\b';
+ break;
+ case 'e': /* Escape */
+ num = 27;
+ break;
+ case 'f': /* Form feed */
+ num = '\f';
+ break;
+ case 'n': /* Newline */
+ num = '\n';
+ break;
+ case 'r': /* Carriage return */
+ num = '\r';
+ break;
+ case 't': /* Tab */
+ num = '\t';
+ break;
+ case 'v': /* Vtab */
+ num = '\v';
+ break;
+ case '?': /* Delete */
num = 127;
- break;
- case '_': /* Space */
- num = ' ';
- break;
- case '\0': /* End of string */
- state = ST_ERROR; /* Error! */
- break;
- default: /* Escaped character like \ ^ : = */
- num = *p;
- break;
- }
- if (state == ST_BACKSLASH)
- {
- *(q++) = num;
- ++count;
- state = ST_GND;
- }
- ++p;
- break;
-
- case ST_OCTAL: /* Octal sequence */
- if (*p < '0' || *p > '7')
- {
- *(q++) = num;
- ++count;
- state = ST_GND;
- }
- else
- num = (num << 3) + (*(p++) - '0');
- break;
-
- case ST_HEX: /* Hex sequence */
- switch (*p)
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- num = (num << 4) + (*(p++) - '0');
- break;
- case 'a':
- case 'b':
- case 'c':
- case 'd':
- case 'e':
- case 'f':
- num = (num << 4) + (*(p++) - 'a') + 10;
- break;
- case 'A':
- case 'B':
- case 'C':
- case 'D':
- case 'E':
- case 'F':
- num = (num << 4) + (*(p++) - 'A') + 10;
- break;
- default:
- *(q++) = num;
- ++count;
- state = ST_GND;
- break;
- }
- break;
-
- case ST_CARET: /* Caret escape */
- state = ST_GND; /* Should be the next state... */
- if (*p >= '@' && *p <= '~')
- {
- *(q++) = *(p++) & 037;
- ++count;
- }
- else if (*p == '?')
- {
- *(q++) = 127;
- ++count;
- }
- else
- state = ST_ERROR;
- break;
-
- default:
- abort ();
- }
+ break;
+ case '_': /* Space */
+ num = ' ';
+ break;
+ case '\0': /* End of string */
+ state = ST_ERROR; /* Error! */
+ break;
+ default: /* Escaped character like \ ^ : = */
+ num = *p;
+ break;
+ }
+ if (state == ST_BACKSLASH)
+ {
+ *(q++) = num;
+ ++count;
+ state = ST_GND;
+ }
+ ++p;
+ break;
+
+ case ST_OCTAL: /* Octal sequence */
+ if (*p < '0' || *p > '7')
+ {
+ *(q++) = num;
+ ++count;
+ state = ST_GND;
+ }
+ else
+ num = (num << 3) + (*(p++) - '0');
+ break;
+
+ case ST_HEX: /* Hex sequence */
+ switch (*p)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ num = (num << 4) + (*(p++) - '0');
+ break;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ num = (num << 4) + (*(p++) - 'a') + 10;
+ break;
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ num = (num << 4) + (*(p++) - 'A') + 10;
+ break;
+ default:
+ *(q++) = num;
+ ++count;
+ state = ST_GND;
+ break;
+ }
+ break;
+
+ case ST_CARET: /* Caret escape */
+ state = ST_GND; /* Should be the next state... */
+ if (*p >= '@' && *p <= '~')
+ {
+ *(q++) = *(p++) & 037;
+ ++count;
+ }
+ else if (*p == '?')
+ {
+ *(q++) = 127;
+ ++count;
+ }
+ else
+ state = ST_ERROR;
+ break;
+
+ default:
+ abort ();
+ }
}
*dest = q;
@@ -2138,18 +2320,60 @@ get_funky_string (char **dest, const char **src, bool equals_end,
return state != ST_ERROR;
}
+enum parse_state
+ {
+ PS_START = 1,
+ PS_2,
+ PS_3,
+ PS_4,
+ PS_DONE,
+ PS_FAIL
+ };
+
+
+/* Check if the content of TERM is a valid name in dircolors. */
+
+static bool
+known_term_type (void)
+{
+ char const *term = getenv ("TERM");
+ if (! term || ! *term)
+ return false;
+
+ char const *line = G_line;
+ while (line - G_line < sizeof (G_line))
+ {
+ if (STRNCMP_LIT (line, "TERM ") == 0)
+ {
+ if (fnmatch (line + 5, term, 0) == 0)
+ return true;
+ }
+ line += strlen (line) + 1;
+ }
+
+ return false;
+}
+
static void
parse_ls_color (void)
{
const char *p; /* Pointer to character being parsed */
char *buf; /* color_buf buffer pointer */
- int state; /* State of parser */
int ind_no; /* Indicator number */
char label[3]; /* Indicator label */
struct color_ext_type *ext; /* Extension we are working on */
if ((p = getenv ("LS_COLORS")) == NULL || *p == '\0')
- return;
+ {
+ /* LS_COLORS takes precedence, but if that's not set then
+ honor the COLORTERM and TERM env variables so that
+ we only go with the internal ANSI color codes if the
+ former is non empty or the latter is set to a known value. */
+ char const *colorterm = getenv ("COLORTERM");
+ if (! (colorterm && *colorterm) && ! known_term_type ())
+ print_with_color = false;
+ return;
+ }
ext = NULL;
strcpy (label, "??");
@@ -2160,111 +2384,137 @@ parse_ls_color (void)
advance. */
buf = color_buf = xstrdup (p);
- state = 1;
- while (state > 0)
+ enum parse_state state = PS_START;
+ while (true)
{
switch (state)
- {
- case 1: /* First label character */
- switch (*p)
- {
- case ':':
- ++p;
- break;
-
- case '*':
- /* Allocate new extension block and add to head of
- linked list (this way a later definition will
- override an earlier one, which can be useful for
- having terminal-specific defs override global). */
-
- ext = xmalloc (sizeof *ext);
- ext->next = color_ext_list;
- color_ext_list = ext;
-
- ++p;
- ext->ext.string = buf;
-
- state = (get_funky_string (&buf, &p, true, &ext->ext.len)
- ? 4 : -1);
- break;
-
- case '\0':
- state = 0; /* Done! */
- break;
-
- default: /* Assume it is file type label */
- label[0] = *(p++);
- state = 2;
- break;
- }
- break;
-
- case 2: /* Second label character */
- if (*p)
- {
- label[1] = *(p++);
- state = 3;
- }
- else
- state = -1; /* Error */
- break;
-
- case 3: /* Equal sign after indicator label */
- state = -1; /* Assume failure... */
- if (*(p++) == '=')/* It *should* be... */
- {
- for (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no)
- {
- if (STREQ (label, indicator_name[ind_no]))
- {
- color_indicator[ind_no].string = buf;
- state = (get_funky_string (&buf, &p, false,
- &color_indicator[ind_no].len)
- ? 1 : -1);
- break;
- }
- }
- if (state == -1)
- error (0, 0, _("unrecognized prefix: %s"), quotearg (label));
- }
- break;
-
- case 4: /* Equal sign after *.ext */
- if (*(p++) == '=')
- {
- ext->seq.string = buf;
- state = (get_funky_string (&buf, &p, false, &ext->seq.len)
- ? 1 : -1);
- }
- else
- state = -1;
- break;
- }
+ {
+ case PS_START: /* First label character */
+ switch (*p)
+ {
+ case ':':
+ ++p;
+ break;
+
+ case '*':
+ /* Allocate new extension block and add to head of
+ linked list (this way a later definition will
+ override an earlier one, which can be useful for
+ having terminal-specific defs override global). */
+
+ ext = xmalloc (sizeof *ext);
+ ext->next = color_ext_list;
+ color_ext_list = ext;
+
+ ++p;
+ ext->ext.string = buf;
+
+ state = (get_funky_string (&buf, &p, true, &ext->ext.len)
+ ? PS_4 : PS_FAIL);
+ break;
+
+ case '\0':
+ state = PS_DONE; /* Done! */
+ goto done;
+
+ default: /* Assume it is file type label */
+ label[0] = *(p++);
+ state = PS_2;
+ break;
+ }
+ break;
+
+ case PS_2: /* Second label character */
+ if (*p)
+ {
+ label[1] = *(p++);
+ state = PS_3;
+ }
+ else
+ state = PS_FAIL; /* Error */
+ break;
+
+ case PS_3: /* Equal sign after indicator label */
+ state = PS_FAIL; /* Assume failure... */
+ if (*(p++) == '=')/* It *should* be... */
+ {
+ for (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no)
+ {
+ if (STREQ (label, indicator_name[ind_no]))
+ {
+ color_indicator[ind_no].string = buf;
+ state = (get_funky_string (&buf, &p, false,
+ &color_indicator[ind_no].len)
+ ? PS_START : PS_FAIL);
+ break;
+ }
+ }
+ if (state == PS_FAIL)
+ error (0, 0, _("unrecognized prefix: %s"), quote (label));
+ }
+ break;
+
+ case PS_4: /* Equal sign after *.ext */
+ if (*(p++) == '=')
+ {
+ ext->seq.string = buf;
+ state = (get_funky_string (&buf, &p, false, &ext->seq.len)
+ ? PS_START : PS_FAIL);
+ }
+ else
+ state = PS_FAIL;
+ break;
+
+ case PS_FAIL:
+ goto done;
+
+ default:
+ abort ();
+ }
}
+ done:
- if (state < 0)
+ if (state == PS_FAIL)
{
struct color_ext_type *e;
struct color_ext_type *e2;
error (0, 0,
- _("unparsable value for LS_COLORS environment variable"));
+ _("unparsable value for LS_COLORS environment variable"));
free (color_buf);
for (e = color_ext_list; e != NULL; /* empty */)
- {
- e2 = e;
- e = e->next;
- free (e2);
- }
+ {
+ e2 = e;
+ e = e->next;
+ free (e2);
+ }
print_with_color = false;
}
if (color_indicator[C_LINK].len == 6
- && !strncmp (color_indicator[C_LINK].string, "target", 6))
+ && !STRNCMP_LIT (color_indicator[C_LINK].string, "target"))
color_symlink_as_referent = true;
}
+/* Set the quoting style default if the environment variable
+ QUOTING_STYLE is set. */
+
+static void
+getenv_quoting_style (void)
+{
+ char const *q_style = getenv ("QUOTING_STYLE");
+ if (q_style)
+ {
+ int i = ARGMATCH (q_style, quoting_style_args, quoting_style_vals);
+ if (0 <= i)
+ set_quoting_style (NULL, quoting_style_vals[i]);
+ else
+ error (0, 0,
+ _("ignoring invalid value of environment variable QUOTING_STYLE: %s"),
+ quote (q_style));
+ }
+}
+
/* Set the exit status to report a failure. If SERIOUS, it is a
serious failure; otherwise, it is merely a minor problem. */
@@ -2284,7 +2534,7 @@ set_exit_status (bool serious)
static void
file_failure (bool serious, char const *message, char const *file)
{
- error (0, errno, message, quotearg_colon (file));
+ error (0, errno, message, quoteaf (file));
set_exit_status (serious);
}
@@ -2337,29 +2587,43 @@ print_dir (char const *name, char const *realname, bool command_line_arg)
/* If dirfd failed, endure the overhead of using stat. */
if ((0 <= fd
- ? fstat (fd, &dir_stat)
- : stat (name, &dir_stat)) < 0)
- {
- file_failure (command_line_arg,
- _("cannot determine device and inode of %s"), name);
- closedir (dirp);
- return;
- }
+ ? fstat (fd, &dir_stat)
+ : stat (name, &dir_stat)) < 0)
+ {
+ file_failure (command_line_arg,
+ _("cannot determine device and inode of %s"), name);
+ closedir (dirp);
+ return;
+ }
/* If we've already visited this dev/inode pair, warn that
- we've found a loop, and do not process this directory. */
+ we've found a loop, and do not process this directory. */
if (visit_dir (dir_stat.st_dev, dir_stat.st_ino))
- {
- error (0, 0, _("%s: not listing already-listed directory"),
- quotearg_colon (name));
- closedir (dirp);
- return;
- }
-
- DEV_INO_PUSH (dir_stat.st_dev, dir_stat.st_ino);
+ {
+ error (0, 0, _("%s: not listing already-listed directory"),
+ quotef (name));
+ closedir (dirp);
+ set_exit_status (true);
+ return;
+ }
+
+ dev_ino_push (dir_stat.st_dev, dir_stat.st_ino);
}
- /* Read the directory entries, and insert the subfiles into the `cwd_file'
+ if (recursive || print_dir_name)
+ {
+ if (!first)
+ DIRED_PUTCHAR ('\n');
+ first = false;
+ DIRED_INDENT ();
+ PUSH_CURRENT_DIRED_POS (&subdired_obstack);
+ dired_pos += quote_name (stdout, realname ? realname : name,
+ dirname_quoting_options, NULL);
+ PUSH_CURRENT_DIRED_POS (&subdired_obstack);
+ DIRED_FPUTS_LITERAL (":\n", stdout);
+ }
+
+ /* Read the directory entries, and insert the subfiles into the 'cwd_file'
table. */
clear_files ();
@@ -2367,42 +2631,63 @@ print_dir (char const *name, char const *realname, bool command_line_arg)
while (1)
{
/* Set errno to zero so we can distinguish between a readdir failure
- and when readdir simply finds that there are no more entries. */
+ and when readdir simply finds that there are no more entries. */
errno = 0;
next = readdir (dirp);
if (next)
- {
- if (! file_ignored (next->d_name))
- {
- enum filetype type = unknown;
+ {
+ if (! file_ignored (next->d_name))
+ {
+ enum filetype type = unknown;
#if HAVE_STRUCT_DIRENT_D_TYPE
- switch (next->d_type)
- {
- case DT_BLK: type = blockdev; break;
- case DT_CHR: type = chardev; break;
- case DT_DIR: type = directory; break;
- case DT_FIFO: type = fifo; break;
- case DT_LNK: type = symbolic_link; break;
- case DT_REG: type = normal; break;
- case DT_SOCK: type = sock; break;
+ switch (next->d_type)
+ {
+ case DT_BLK: type = blockdev; break;
+ case DT_CHR: type = chardev; break;
+ case DT_DIR: type = directory; break;
+ case DT_FIFO: type = fifo; break;
+ case DT_LNK: type = symbolic_link; break;
+ case DT_REG: type = normal; break;
+ case DT_SOCK: type = sock; break;
# ifdef DT_WHT
- case DT_WHT: type = whiteout; break;
+ case DT_WHT: type = whiteout; break;
# endif
- }
+ }
#endif
- total_blocks += gobble_file (next->d_name, type, D_INO (next),
- false, name);
- }
- }
+ total_blocks += gobble_file (next->d_name, type,
+ RELIABLE_D_INO (next),
+ false, name);
+
+ /* In this narrow case, print out each name right away, so
+ ls uses constant memory while processing the entries of
+ this directory. Useful when there are many (millions)
+ of entries in a directory. */
+ if (format == one_per_line && sort_type == sort_none
+ && !print_block_size && !recursive)
+ {
+ /* We must call sort_files in spite of
+ "sort_type == sort_none" for its initialization
+ of the sorted_file vector. */
+ sort_files ();
+ print_current_files ();
+ clear_files ();
+ }
+ }
+ }
else if (errno != 0)
- {
- file_failure (command_line_arg, _("reading directory %s"), name);
- if (errno != EOVERFLOW)
- break;
- }
+ {
+ file_failure (command_line_arg, _("reading directory %s"), name);
+ if (errno != EOVERFLOW)
+ break;
+ }
else
- break;
+ break;
+
+ /* When processing a very large directory, and since we've inhibited
+ interrupts, this loop would take so long that ls would be annoyingly
+ uninterruptible. This ensures that it handles signals promptly. */
+ process_signals ();
}
if (closedir (dirp) != 0)
@@ -2418,20 +2703,7 @@ print_dir (char const *name, char const *realname, bool command_line_arg)
contents listed rather than being mentioned here as files. */
if (recursive)
- extract_dirs_from_files (name, command_line_arg);
-
- if (recursive | print_dir_name)
- {
- if (!first)
- DIRED_PUTCHAR ('\n');
- first = false;
- DIRED_INDENT ();
- PUSH_CURRENT_DIRED_POS (&subdired_obstack);
- dired_pos += quote_name (stdout, realname ? realname : name,
- dirname_quoting_options, NULL);
- PUSH_CURRENT_DIRED_POS (&subdired_obstack);
- DIRED_FPUTS_LITERAL (":\n", stdout);
- }
+ extract_dirs_from_files (name, false);
if (format == long_format || print_block_size)
{
@@ -2443,7 +2715,7 @@ print_dir (char const *name, char const *realname, bool command_line_arg)
DIRED_FPUTS (p, stdout, strlen (p));
DIRED_PUTCHAR (' ');
p = human_readable (total_blocks, buf, human_output_opts,
- ST_NBLOCKSIZE, output_block_size);
+ ST_NBLOCKSIZE, output_block_size);
DIRED_FPUTS (p, stdout, strlen (p));
DIRED_PUTCHAR ('\n');
}
@@ -2452,7 +2724,7 @@ print_dir (char const *name, char const *realname, bool command_line_arg)
print_current_files ();
}
-/* Add `pattern' to the list of patterns for which files that match are
+/* Add 'pattern' to the list of patterns for which files that match are
not listed. */
static void
@@ -2485,11 +2757,11 @@ static bool
file_ignored (char const *name)
{
return ((ignore_mode != IGNORE_MINIMAL
- && name[0] == '.'
- && (ignore_mode == IGNORE_DEFAULT || ! name[1 + (name[1] == '.')]))
- || (ignore_mode == IGNORE_DEFAULT
- && patterns_match (hide_patterns, name))
- || patterns_match (ignore_patterns, name));
+ && name[0] == '.'
+ && (ignore_mode == IGNORE_DEFAULT || ! name[1 + (name[1] == '.')]))
+ || (ignore_mode == IGNORE_DEFAULT
+ && patterns_match (hide_patterns, name))
+ || patterns_match (ignore_patterns, name));
}
/* POSIX requires that a file size be printed without a sign, even
@@ -2502,10 +2774,55 @@ unsigned_file_size (off_t size)
return size + (size < 0) * ((uintmax_t) OFF_T_MAX - OFF_T_MIN + 1);
}
-/* Enter and remove entries in the table `cwd_file'. */
+#ifdef HAVE_CAP
+/* Return true if NAME has a capability (see linux/capability.h) */
+static bool
+has_capability (char const *name)
+{
+ char *result;
+ bool has_cap;
-/* Empty the table of files. */
+ cap_t cap_d = cap_get_file (name);
+ if (cap_d == NULL)
+ return false;
+
+ result = cap_to_text (cap_d, NULL);
+ cap_free (cap_d);
+ if (!result)
+ return false;
+ /* check if human-readable capability string is empty */
+ has_cap = !!*result;
+
+ cap_free (result);
+ return has_cap;
+}
+#else
+static bool
+has_capability (char const *name _GL_UNUSED)
+{
+ errno = ENOTSUP;
+ return false;
+}
+#endif
+
+/* Enter and remove entries in the table 'cwd_file'. */
+
+static void
+free_ent (struct fileinfo *f)
+{
+ free (f->name);
+ free (f->linkname);
+ if (f->scontext != UNKNOWN_SECURITY_CONTEXT)
+ {
+ if (is_smack_enabled ())
+ free (f->scontext);
+ else
+ freecon (f->scontext);
+ }
+}
+
+/* Empty the table of files. */
static void
clear_files (void)
{
@@ -2514,32 +2831,114 @@ clear_files (void)
for (i = 0; i < cwd_n_used; i++)
{
struct fileinfo *f = sorted_file[i];
- free (f->name);
- free (f->linkname);
+ free_ent (f);
}
cwd_n_used = 0;
-#if USE_ACL
any_has_acl = false;
-#endif
inode_number_width = 0;
block_size_width = 0;
nlink_width = 0;
owner_width = 0;
group_width = 0;
author_width = 0;
+ scontext_width = 0;
major_device_number_width = 0;
minor_device_number_width = 0;
file_size_width = 0;
}
+/* Return true if ERR implies lack-of-support failure by a
+ getxattr-calling function like getfilecon or file_has_acl. */
+static bool
+errno_unsupported (int err)
+{
+ return (err == EINVAL || err == ENOSYS || is_ENOTSUP (err));
+}
+
+/* Cache *getfilecon failure, when it's trivial to do so.
+ Like getfilecon/lgetfilecon, but when F's st_dev says it's doesn't
+ support getting the security context, fail with ENOTSUP immediately. */
+static int
+getfilecon_cache (char const *file, struct fileinfo *f, bool deref)
+{
+ /* st_dev of the most recently processed device for which we've
+ found that [l]getfilecon fails indicating lack of support. */
+ static dev_t unsupported_device;
+
+ if (f->stat.st_dev == unsupported_device)
+ {
+ errno = ENOTSUP;
+ return -1;
+ }
+ int r = 0;
+#ifdef HAVE_SMACK
+ if (is_smack_enabled ())
+ r = smack_new_label_from_path (file, "security.SMACK64", deref,
+ &f->scontext);
+ else
+#endif
+ r = (deref
+ ? getfilecon (file, &f->scontext)
+ : lgetfilecon (file, &f->scontext));
+ if (r < 0 && errno_unsupported (errno))
+ unsupported_device = f->stat.st_dev;
+ return r;
+}
+
+/* Cache file_has_acl failure, when it's trivial to do.
+ Like file_has_acl, but when F's st_dev says it's on a file
+ system lacking ACL support, return 0 with ENOTSUP immediately. */
+static int
+file_has_acl_cache (char const *file, struct fileinfo *f)
+{
+ /* st_dev of the most recently processed device for which we've
+ found that file_has_acl fails indicating lack of support. */
+ static dev_t unsupported_device;
+
+ if (f->stat.st_dev == unsupported_device)
+ {
+ errno = ENOTSUP;
+ return 0;
+ }
+
+ /* Zero errno so that we can distinguish between two 0-returning cases:
+ "has-ACL-support, but only a default ACL" and "no ACL support". */
+ errno = 0;
+ int n = file_has_acl (file, &f->stat);
+ if (n <= 0 && errno_unsupported (errno))
+ unsupported_device = f->stat.st_dev;
+ return n;
+}
+
+/* Cache has_capability failure, when it's trivial to do.
+ Like has_capability, but when F's st_dev says it's on a file
+ system lacking capability support, return 0 with ENOTSUP immediately. */
+static bool
+has_capability_cache (char const *file, struct fileinfo *f)
+{
+ /* st_dev of the most recently processed device for which we've
+ found that has_capability fails indicating lack of support. */
+ static dev_t unsupported_device;
+
+ if (f->stat.st_dev == unsupported_device)
+ {
+ errno = ENOTSUP;
+ return 0;
+ }
+
+ bool b = has_capability (file);
+ if ( !b && errno_unsupported (errno))
+ unsupported_device = f->stat.st_dev;
+ return b;
+}
+
/* Add a file to the current table of files.
Verify that the file exists, and print an error message if it does not.
Return the number of blocks that the file occupies. */
-
static uintmax_t
gobble_file (char const *name, enum filetype type, ino_t inode,
- bool command_line_arg, char const *dirname)
+ bool command_line_arg, char const *dirname)
{
uintmax_t blocks = 0;
struct fileinfo *f;
@@ -2562,212 +2961,273 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
if (command_line_arg
|| format_needs_stat
/* When coloring a directory (we may know the type from
- direct.d_type), we have to stat it in order to indicate
- sticky and/or other-writable attributes. */
- || (type == directory && print_with_color)
+ direct.d_type), we have to stat it in order to indicate
+ sticky and/or other-writable attributes. */
+ || (type == directory && print_with_color
+ && (is_colored (C_OTHER_WRITABLE)
+ || is_colored (C_STICKY)
+ || is_colored (C_STICKY_OTHER_WRITABLE)))
/* When dereferencing symlinks, the inode and type must come from
- stat, but readdir provides the inode and type of lstat. */
+ stat, but readdir provides the inode and type of lstat. */
|| ((print_inode || format_needs_type)
- && (type == symbolic_link || type == unknown)
- && (dereference == DEREF_ALWAYS
- || (command_line_arg && dereference != DEREF_NEVER)))
+ && (type == symbolic_link || type == unknown)
+ && (dereference == DEREF_ALWAYS
+ || color_symlink_as_referent || check_symlink_color))
/* Command line dereferences are already taken care of by the above
- assertion that the inode number is not yet known. */
+ assertion that the inode number is not yet known. */
|| (print_inode && inode == NOT_AN_INODE_NUMBER)
|| (format_needs_type
- && (type == unknown || command_line_arg
- /* --indicator-style=classify (aka -F)
- requires that we stat each regular file
- to see if it's executable. */
- || (type == normal && (indicator_style == classify
- /* This is so that --color ends up
- highlighting files with the executable
- bit set even when options like -F are
- not specified. */
- || (print_with_color
- && is_colored (C_EXEC))
- )))))
+ && (type == unknown || command_line_arg
+ /* --indicator-style=classify (aka -F)
+ requires that we stat each regular file
+ to see if it's executable. */
+ || (type == normal && (indicator_style == classify
+ /* This is so that --color ends up
+ highlighting files with these mode
+ bits set even when options like -F are
+ not specified. Note we do a redundant
+ stat in the very unlikely case where
+ C_CAP is set but not the others. */
+ || (print_with_color
+ && (is_colored (C_EXEC)
+ || is_colored (C_SETUID)
+ || is_colored (C_SETGID)
+ || is_colored (C_CAP)))
+ )))))
{
/* Absolute name of this file. */
char *absolute_name;
-
+ bool do_deref;
int err;
if (name[0] == '/' || dirname[0] == 0)
- absolute_name = (char *) name;
+ absolute_name = (char *) name;
else
- {
- absolute_name = alloca (strlen (name) + strlen (dirname) + 2);
- attach (absolute_name, dirname, name);
- }
+ {
+ absolute_name = alloca (strlen (name) + strlen (dirname) + 2);
+ attach (absolute_name, dirname, name);
+ }
switch (dereference)
- {
- case DEREF_ALWAYS:
- err = stat (absolute_name, &f->stat);
- break;
-
- case DEREF_COMMAND_LINE_ARGUMENTS:
- case DEREF_COMMAND_LINE_SYMLINK_TO_DIR:
- if (command_line_arg)
- {
- bool need_lstat;
- err = stat (absolute_name, &f->stat);
-
- if (dereference == DEREF_COMMAND_LINE_ARGUMENTS)
- break;
-
- need_lstat = (err < 0
- ? errno == ENOENT
- : ! S_ISDIR (f->stat.st_mode));
- if (!need_lstat)
- break;
-
- /* stat failed because of ENOENT, maybe indicating a dangling
- symlink. Or stat succeeded, ABSOLUTE_NAME does not refer to a
- directory, and --dereference-command-line-symlink-to-dir is
- in effect. Fall through so that we call lstat instead. */
- }
-
- default: /* DEREF_NEVER */
- err = lstat (absolute_name, &f->stat);
- break;
- }
+ {
+ case DEREF_ALWAYS:
+ err = stat (absolute_name, &f->stat);
+ do_deref = true;
+ break;
+
+ case DEREF_COMMAND_LINE_ARGUMENTS:
+ case DEREF_COMMAND_LINE_SYMLINK_TO_DIR:
+ if (command_line_arg)
+ {
+ bool need_lstat;
+ err = stat (absolute_name, &f->stat);
+ do_deref = true;
+
+ if (dereference == DEREF_COMMAND_LINE_ARGUMENTS)
+ break;
+
+ need_lstat = (err < 0
+ ? errno == ENOENT
+ : ! S_ISDIR (f->stat.st_mode));
+ if (!need_lstat)
+ break;
+
+ /* stat failed because of ENOENT, maybe indicating a dangling
+ symlink. Or stat succeeded, ABSOLUTE_NAME does not refer to a
+ directory, and --dereference-command-line-symlink-to-dir is
+ in effect. Fall through so that we call lstat instead. */
+ }
+
+ default: /* DEREF_NEVER */
+ err = lstat (absolute_name, &f->stat);
+ do_deref = false;
+ break;
+ }
if (err != 0)
- {
- /* Failure to stat a command line argument leads to
- an exit status of 2. For other files, stat failure
- provokes an exit status of 1. */
- file_failure (command_line_arg,
- _("cannot access %s"), absolute_name);
- if (command_line_arg)
- return 0;
+ {
+ /* Failure to stat a command line argument leads to
+ an exit status of 2. For other files, stat failure
+ provokes an exit status of 1. */
+ file_failure (command_line_arg,
+ _("cannot access %s"), absolute_name);
+ if (command_line_arg)
+ return 0;
- f->name = xstrdup (name);
- cwd_n_used++;
+ f->name = xstrdup (name);
+ cwd_n_used++;
- return 0;
- }
+ return 0;
+ }
f->stat_ok = true;
-#if USE_ACL
- if (format == long_format)
- {
- int n = file_has_acl (absolute_name, &f->stat);
- f->have_acl = (0 < n);
- any_has_acl |= f->have_acl;
- if (n < 0)
- error (0, errno, "%s", quotearg_colon (absolute_name));
- }
-#endif
+ /* Note has_capability() adds around 30% runtime to 'ls --color' */
+ if ((type == normal || S_ISREG (f->stat.st_mode))
+ && print_with_color && is_colored (C_CAP))
+ f->has_capability = has_capability_cache (absolute_name, f);
+
+ if (format == long_format || print_scontext)
+ {
+ bool have_scontext = false;
+ bool have_acl = false;
+ int attr_len = getfilecon_cache (absolute_name, f, do_deref);
+ err = (attr_len < 0);
+
+ if (err == 0)
+ {
+ if (is_smack_enabled ())
+ have_scontext = ! STREQ ("_", f->scontext);
+ else
+ have_scontext = ! STREQ ("unlabeled", f->scontext);
+ }
+ else
+ {
+ f->scontext = UNKNOWN_SECURITY_CONTEXT;
+
+ /* When requesting security context information, don't make
+ ls fail just because the file (even a command line argument)
+ isn't on the right type of file system. I.e., a getfilecon
+ failure isn't in the same class as a stat failure. */
+ if (is_ENOTSUP (errno) || errno == ENODATA)
+ err = 0;
+ }
+
+ if (err == 0 && format == long_format)
+ {
+ int n = file_has_acl_cache (absolute_name, f);
+ err = (n < 0);
+ have_acl = (0 < n);
+ }
+
+ f->acl_type = (!have_scontext && !have_acl
+ ? ACL_T_NONE
+ : (have_scontext && !have_acl
+ ? ACL_T_LSM_CONTEXT_ONLY
+ : ACL_T_YES));
+ any_has_acl |= f->acl_type != ACL_T_NONE;
+
+ if (err)
+ error (0, errno, "%s", quotef (absolute_name));
+ }
if (S_ISLNK (f->stat.st_mode)
- && (format == long_format || check_symlink_color))
- {
- char *linkname;
- struct stat linkstats;
-
- get_link_name (absolute_name, f, command_line_arg);
- linkname = make_link_name (absolute_name, f->linkname);
-
- /* Avoid following symbolic links when possible, ie, when
- they won't be traced and when no indicator is needed. */
- if (linkname
- && (file_type <= indicator_style || check_symlink_color)
- && stat (linkname, &linkstats) == 0)
- {
- f->linkok = true;
-
- /* Symbolic links to directories that are mentioned on the
- command line are automatically traced if not being
- listed as files. */
- if (!command_line_arg || format == long_format
- || !S_ISDIR (linkstats.st_mode))
- {
- /* Get the linked-to file's mode for the filetype indicator
- in long listings. */
- f->linkmode = linkstats.st_mode;
- }
- }
- free (linkname);
- }
+ && (format == long_format || check_symlink_color))
+ {
+ struct stat linkstats;
+
+ get_link_name (absolute_name, f, command_line_arg);
+ char *linkname = make_link_name (absolute_name, f->linkname);
+
+ /* Avoid following symbolic links when possible, ie, when
+ they won't be traced and when no indicator is needed. */
+ if (linkname
+ && (file_type <= indicator_style || check_symlink_color)
+ && stat (linkname, &linkstats) == 0)
+ {
+ f->linkok = true;
+
+ /* Symbolic links to directories that are mentioned on the
+ command line are automatically traced if not being
+ listed as files. */
+ if (!command_line_arg || format == long_format
+ || !S_ISDIR (linkstats.st_mode))
+ {
+ /* Get the linked-to file's mode for the filetype indicator
+ in long listings. */
+ f->linkmode = linkstats.st_mode;
+ }
+ }
+ free (linkname);
+ }
if (S_ISLNK (f->stat.st_mode))
- f->filetype = symbolic_link;
+ f->filetype = symbolic_link;
else if (S_ISDIR (f->stat.st_mode))
- {
- if (command_line_arg & !immediate_dirs)
- f->filetype = arg_directory;
- else
- f->filetype = directory;
- }
+ {
+ if (command_line_arg && !immediate_dirs)
+ f->filetype = arg_directory;
+ else
+ f->filetype = directory;
+ }
else
- f->filetype = normal;
+ f->filetype = normal;
blocks = ST_NBLOCKS (f->stat);
- {
- char buf[LONGEST_HUMAN_READABLE + 1];
- int len = mbswidth (human_readable (blocks, buf, human_output_opts,
- ST_NBLOCKSIZE, output_block_size),
- 0);
- if (block_size_width < len)
- block_size_width = len;
- }
-
- if (print_owner)
- {
- int len = format_user_width (f->stat.st_uid);
- if (owner_width < len)
- owner_width = len;
- }
+ if (format == long_format || print_block_size)
+ {
+ char buf[LONGEST_HUMAN_READABLE + 1];
+ int len = mbswidth (human_readable (blocks, buf, human_output_opts,
+ ST_NBLOCKSIZE, output_block_size),
+ 0);
+ if (block_size_width < len)
+ block_size_width = len;
+ }
- if (print_group)
- {
- int len = format_group_width (f->stat.st_gid);
- if (group_width < len)
- group_width = len;
- }
-
- if (print_author)
- {
- int len = format_user_width (f->stat.st_author);
- if (author_width < len)
- author_width = len;
- }
-
- {
- char buf[INT_BUFSIZE_BOUND (uintmax_t)];
- int len = strlen (umaxtostr (f->stat.st_nlink, buf));
- if (nlink_width < len)
- nlink_width = len;
- }
+ if (format == long_format)
+ {
+ if (print_owner)
+ {
+ int len = format_user_width (f->stat.st_uid);
+ if (owner_width < len)
+ owner_width = len;
+ }
+
+ if (print_group)
+ {
+ int len = format_group_width (f->stat.st_gid);
+ if (group_width < len)
+ group_width = len;
+ }
+
+ if (print_author)
+ {
+ int len = format_user_width (f->stat.st_author);
+ if (author_width < len)
+ author_width = len;
+ }
+ }
+
+ if (print_scontext)
+ {
+ int len = strlen (f->scontext);
+ if (scontext_width < len)
+ scontext_width = len;
+ }
- if (S_ISCHR (f->stat.st_mode) || S_ISBLK (f->stat.st_mode))
- {
- char buf[INT_BUFSIZE_BOUND (uintmax_t)];
- int len = strlen (umaxtostr (major (f->stat.st_rdev), buf));
- if (major_device_number_width < len)
- major_device_number_width = len;
- len = strlen (umaxtostr (minor (f->stat.st_rdev), buf));
- if (minor_device_number_width < len)
- minor_device_number_width = len;
- len = major_device_number_width + 2 + minor_device_number_width;
- if (file_size_width < len)
- file_size_width = len;
- }
- else
- {
- char buf[LONGEST_HUMAN_READABLE + 1];
- uintmax_t size = unsigned_file_size (f->stat.st_size);
- int len = mbswidth (human_readable (size, buf, human_output_opts,
- 1, file_output_block_size),
- 0);
- if (file_size_width < len)
- file_size_width = len;
- }
+ if (format == long_format)
+ {
+ char b[INT_BUFSIZE_BOUND (uintmax_t)];
+ int b_len = strlen (umaxtostr (f->stat.st_nlink, b));
+ if (nlink_width < b_len)
+ nlink_width = b_len;
+
+ if (S_ISCHR (f->stat.st_mode) || S_ISBLK (f->stat.st_mode))
+ {
+ char buf[INT_BUFSIZE_BOUND (uintmax_t)];
+ int len = strlen (umaxtostr (major (f->stat.st_rdev), buf));
+ if (major_device_number_width < len)
+ major_device_number_width = len;
+ len = strlen (umaxtostr (minor (f->stat.st_rdev), buf));
+ if (minor_device_number_width < len)
+ minor_device_number_width = len;
+ len = major_device_number_width + 2 + minor_device_number_width;
+ if (file_size_width < len)
+ file_size_width = len;
+ }
+ else
+ {
+ char buf[LONGEST_HUMAN_READABLE + 1];
+ uintmax_t size = unsigned_file_size (f->stat.st_size);
+ int len = mbswidth (human_readable (size, buf,
+ file_human_output_opts,
+ 1, file_output_block_size),
+ 0);
+ if (file_size_width < len)
+ file_size_width = len;
+ }
+ }
}
if (print_inode)
@@ -2775,7 +3235,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode,
char buf[INT_BUFSIZE_BOUND (uintmax_t)];
int len = strlen (umaxtostr (f->stat.st_ino, buf));
if (inode_number_width < len)
- inode_number_width = len;
+ inode_number_width = len;
}
f->name = xstrdup (name);
@@ -2792,50 +3252,52 @@ is_directory (const struct fileinfo *f)
}
/* Put the name of the file that FILENAME is a symbolic link to
- into the LINKNAME field of `f'. COMMAND_LINE_ARG indicates whether
+ into the LINKNAME field of 'f'. COMMAND_LINE_ARG indicates whether
FILENAME is a command-line argument. */
static void
get_link_name (char const *filename, struct fileinfo *f, bool command_line_arg)
{
- f->linkname = xreadlink_with_size (filename, f->stat.st_size);
+ f->linkname = areadlink_with_size (filename, f->stat.st_size);
if (f->linkname == NULL)
file_failure (command_line_arg, _("cannot read symbolic link %s"),
- filename);
+ filename);
}
-/* If `linkname' is a relative name and `name' contains one or more
- leading directories, return `linkname' with those directories
- prepended; otherwise, return a copy of `linkname'.
- If `linkname' is zero, return zero. */
+/* If LINKNAME is a relative name and NAME contains one or more
+ leading directories, return LINKNAME with those directories
+ prepended; otherwise, return a copy of LINKNAME.
+ If LINKNAME is NULL, return NULL. */
static char *
make_link_name (char const *name, char const *linkname)
{
- char *linkbuf;
- size_t bufsiz;
-
if (!linkname)
return NULL;
- if (*linkname == '/')
+ if (IS_ABSOLUTE_FILE_NAME (linkname))
return xstrdup (linkname);
/* The link is to a relative name. Prepend any leading directory
- in `name' to the link name. */
- linkbuf = strrchr (name, '/');
- if (linkbuf == 0)
+ in 'name' to the link name. */
+ size_t prefix_len = dir_len (name);
+ if (prefix_len == 0)
return xstrdup (linkname);
- bufsiz = linkbuf - name + 1;
- linkbuf = xmalloc (bufsiz + strlen (linkname) + 1);
- strncpy (linkbuf, name, bufsiz);
- strcpy (linkbuf + bufsiz, linkname);
- return linkbuf;
+ char *p = xmalloc (prefix_len + 1 + strlen (linkname) + 1);
+
+ /* PREFIX_LEN usually specifies a string not ending in slash.
+ In that case, extend it by one, since the next byte *is* a slash.
+ Otherwise, the prefix is "/", so leave the length unchanged. */
+ if ( ! ISSLASH (name[prefix_len - 1]))
+ ++prefix_len;
+
+ stpcpy (stpncpy (p, name, prefix_len), linkname);
+ return p;
}
-/* Return true if the last component of NAME is `.' or `..'
- This is so we don't try to recurse on `././././. ...' */
+/* Return true if the last component of NAME is '.' or '..'
+ This is so we don't try to recurse on '././././. ...' */
static bool
basename_is_dot_or_dotdot (const char *name)
@@ -2862,8 +3324,8 @@ extract_dirs_from_files (char const *dirname, bool command_line_arg)
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. */
+ we'll know that DIRNAME has been processed and may be removed
+ from the set of active directories. */
queue_directory (NULL, dirname, false);
}
@@ -2874,20 +3336,20 @@ extract_dirs_from_files (char const *dirname, bool command_line_arg)
struct fileinfo *f = sorted_file[i];
if (is_directory (f)
- && (! ignore_dot_and_dot_dot
- || ! basename_is_dot_or_dotdot (f->name)))
- {
- if (!dirname || f->name[0] == '/')
- queue_directory (f->name, f->linkname, command_line_arg);
- else
- {
- char *name = file_name_concat (dirname, f->name, NULL);
- queue_directory (name, f->linkname, command_line_arg);
- free (name);
- }
- if (f->filetype == arg_directory)
- free (f->name);
- }
+ && (! ignore_dot_and_dot_dot
+ || ! basename_is_dot_or_dotdot (f->name)))
+ {
+ if (!dirname || f->name[0] == '/')
+ queue_directory (f->name, f->linkname, command_line_arg);
+ else
+ {
+ char *name = file_name_concat (dirname, f->name, NULL);
+ queue_directory (name, f->linkname, command_line_arg);
+ free (name);
+ }
+ if (f->filetype == arg_directory)
+ free_ent (f);
+ }
}
/* Now delete the directories from the table, compacting all the remaining
@@ -2916,7 +3378,7 @@ xstrcoll (char const *a, char const *b)
if (errno)
{
error (0, errno, _("cannot compare file names %s and %s"),
- quote_n (0, a), quote_n (1, b));
+ quote_n (0, a), quote_n (1, b));
set_exit_status (false);
longjmp (failed_strcoll, 1);
}
@@ -2937,9 +3399,9 @@ typedef int (*qsortFunc)(V a, V b);
bool a_is_dir = is_directory ((struct fileinfo const *) a); \
bool b_is_dir = is_directory ((struct fileinfo const *) b); \
if (a_is_dir && !b_is_dir) \
- return -1; /* a goes before b */ \
+ return -1; /* a goes before b */ \
if (!a_is_dir && b_is_dir) \
- return 1; /* b goes before a */ \
+ return 1; /* b goes before a */ \
} \
while (0)
@@ -2954,19 +3416,19 @@ typedef int (*qsortFunc)(V a, V b);
{ return key_cmp_func (a, b, xstrcoll); } \
static int strcmp_##key_name (V a, V b) \
{ return key_cmp_func (a, b, strcmp); } \
- \
+ \
/* reverse, non-dirfirst versions */ \
static int rev_xstrcoll_##key_name (V a, V b) \
{ return key_cmp_func (b, a, xstrcoll); } \
static int rev_strcmp_##key_name (V a, V b) \
{ return key_cmp_func (b, a, strcmp); } \
- \
+ \
/* direct, dirfirst versions */ \
static int xstrcoll_df_##key_name (V a, V b) \
{ DIRFIRST_CHECK (a, b); return key_cmp_func (a, b, xstrcoll); } \
static int strcmp_df_##key_name (V a, V b) \
{ DIRFIRST_CHECK (a, b); return key_cmp_func (a, b, strcmp); } \
- \
+ \
/* reverse, dirfirst versions */ \
static int rev_xstrcoll_df_##key_name (V a, V b) \
{ DIRFIRST_CHECK (a, b); return key_cmp_func (b, a, xstrcoll); } \
@@ -2975,34 +3437,34 @@ typedef int (*qsortFunc)(V a, V b);
static inline int
cmp_ctime (struct fileinfo const *a, struct fileinfo const *b,
- int (*cmp) (char const *, char const *))
+ int (*cmp) (char const *, char const *))
{
int diff = timespec_cmp (get_stat_ctime (&b->stat),
- get_stat_ctime (&a->stat));
+ get_stat_ctime (&a->stat));
return diff ? diff : cmp (a->name, b->name);
}
static inline int
cmp_mtime (struct fileinfo const *a, struct fileinfo const *b,
- int (*cmp) (char const *, char const *))
+ int (*cmp) (char const *, char const *))
{
int diff = timespec_cmp (get_stat_mtime (&b->stat),
- get_stat_mtime (&a->stat));
+ get_stat_mtime (&a->stat));
return diff ? diff : cmp (a->name, b->name);
}
static inline int
cmp_atime (struct fileinfo const *a, struct fileinfo const *b,
- int (*cmp) (char const *, char const *))
+ int (*cmp) (char const *, char const *))
{
int diff = timespec_cmp (get_stat_atime (&b->stat),
- get_stat_atime (&a->stat));
+ get_stat_atime (&a->stat));
return diff ? diff : cmp (a->name, b->name);
}
static inline int
cmp_size (struct fileinfo const *a, struct fileinfo const *b,
- int (*cmp) (char const *, char const *))
+ int (*cmp) (char const *, char const *))
{
int diff = longdiff (b->stat.st_size, a->stat.st_size);
return diff ? diff : cmp (a->name, b->name);
@@ -3010,17 +3472,17 @@ cmp_size (struct fileinfo const *a, struct fileinfo const *b,
static inline int
cmp_name (struct fileinfo const *a, struct fileinfo const *b,
- int (*cmp) (char const *, char const *))
+ int (*cmp) (char const *, char const *))
{
return cmp (a->name, b->name);
}
-/* Compare file extensions. Files with no extension are `smallest'.
- If extensions are the same, compare by filenames instead. */
+/* Compare file extensions. Files with no extension are 'smallest'.
+ If extensions are the same, compare by file names instead. */
static inline int
cmp_extension (struct fileinfo const *a, struct fileinfo const *b,
- int (*cmp) (char const *, char const *))
+ int (*cmp) (char const *, char const *))
{
char const *base1 = strrchr (a->name, '.');
char const *base2 = strrchr (b->name, '.');
@@ -3037,17 +3499,17 @@ DEFINE_SORT_FUNCTIONS (extension, cmp_extension)
/* Compare file versions.
Unlike all other compare functions above, cmp_version depends only
- on strverscmp, which does not fail (even for locale reasons), and does not
- need a secondary sort key.
+ on filevercmp, which does not fail (even for locale reasons), and does not
+ need a secondary sort key. See lib/filevercmp.h for function description.
+
All the other sort options, in fact, need xstrcoll and strcmp variants,
because they all use a string comparison (either as the primary or secondary
sort key), and xstrcoll has the ability to do a longjmp if strcoll fails for
- locale reasons. Last, strverscmp is ALWAYS available in coreutils,
- thanks to the gnulib library. */
+ locale reasons. Lastly, filevercmp is ALWAYS available with gnulib. */
static inline int
cmp_version (struct fileinfo const *a, struct fileinfo const *b)
{
- return strverscmp (a->name, b->name);
+ return filevercmp (a->name, b->name);
}
static int xstrcoll_version (V a, V b)
@@ -3060,13 +3522,13 @@ static int rev_xstrcoll_df_version (V a, V b)
{ DIRFIRST_CHECK (a, b); return cmp_version (b, a); }
-/* We have 2^3 different variants for each sortkey function
+/* We have 2^3 different variants for each sort-key function
(for 3 independent sort modes).
The function pointers stored in this array must be dereferenced as:
sort_variants[sort_key][use_strcmp][reverse][dirs_first]
- Note that the order in which sortkeys are listed in the function pointer
+ Note that the order in which sort keys are listed in the function pointer
array below is defined by the order of the elements in the time_type and
sort_type enums! */
@@ -3082,7 +3544,7 @@ static int rev_xstrcoll_df_version (V a, V b)
} \
}
-static qsortFunc sort_functions[][2][2][2] =
+static qsortFunc const sort_functions[][2][2][2] =
{
LIST_SORTFUNCTION_VARIANTS (name),
LIST_SORTFUNCTION_VARIANTS (extension),
@@ -3110,16 +3572,16 @@ static qsortFunc sort_functions[][2][2][2] =
LIST_SORTFUNCTION_VARIANTS (atime)
};
-/* The number of sortkeys is calculated as
- the number of elements in the sort_type enum (i.e. sort_numtypes) +
- the number of elements in the time_type enum (i.e. time_numtypes) - 1
+/* The number of sort keys is calculated as the sum of
+ the number of elements in the sort_type enum (i.e., sort_numtypes)
+ the number of elements in the time_type enum (i.e., time_numtypes) - 1
This is because when sort_type==sort_time, we have up to
- time_numtypes possible sortkeys.
+ time_numtypes possible sort keys.
This line verifies at compile-time that the array of sort functions has been
- initialized for all possible sortkeys. */
+ initialized for all possible sort keys. */
verify (ARRAY_CARDINALITY (sort_functions)
- == sort_numtypes + time_numtypes - 1 );
+ == sort_numtypes + time_numtypes - 1 );
/* Set up SORTED_FILE to point to the in-use entries in CWD_FILE, in order. */
@@ -3166,9 +3628,9 @@ sort_files (void)
/* When sort_type == sort_time, use time_type as subindex. */
mpsort ((void const **) sorted_file, cwd_n_used,
- sort_functions[sort_type + (sort_type == sort_time ? time_type : 0)]
- [use_strcmp][sort_reverse]
- [directories_first]);
+ sort_functions[sort_type + (sort_type == sort_time ? time_type : 0)]
+ [use_strcmp][sort_reverse]
+ [directories_first]);
}
/* List all the files now in the table. */
@@ -3182,34 +3644,71 @@ print_current_files (void)
{
case one_per_line:
for (i = 0; i < cwd_n_used; i++)
- {
- print_file_name_and_frills (sorted_file[i]);
- putchar ('\n');
- }
+ {
+ print_file_name_and_frills (sorted_file[i], 0);
+ putchar ('\n');
+ }
break;
case many_per_line:
- print_many_per_line ();
+ if (! line_length)
+ print_with_separator (' ');
+ else
+ print_many_per_line ();
break;
case horizontal:
- print_horizontal ();
+ if (! line_length)
+ print_with_separator (' ');
+ else
+ print_horizontal ();
break;
case with_commas:
- print_with_commas ();
+ print_with_separator (',');
break;
case long_format:
for (i = 0; i < cwd_n_used; i++)
- {
- print_long_format (sorted_file[i]);
- DIRED_PUTCHAR ('\n');
- }
+ {
+ set_normal_color ();
+ print_long_format (sorted_file[i]);
+ DIRED_PUTCHAR ('\n');
+ }
break;
}
}
+/* Replace the first %b with precomputed aligned month names.
+ Note on glibc-2.7 at least, this speeds up the whole 'ls -lU'
+ process by around 17%, compared to letting strftime() handle the %b. */
+
+static size_t
+align_nstrftime (char *buf, size_t size, char const *fmt, struct tm const *tm,
+ timezone_t tz, int ns)
+{
+ const char *nfmt = fmt;
+ /* In the unlikely event that rpl_fmt below is not large enough,
+ the replacement is not done. A malloc here slows ls down by 2% */
+ char rpl_fmt[sizeof (abmon[0]) + 100];
+ const char *pb;
+ if (required_mon_width && (pb = strstr (fmt, "%b"))
+ && 0 <= tm->tm_mon && tm->tm_mon <= 11)
+ {
+ if (strlen (fmt) < (sizeof (rpl_fmt) - sizeof (abmon[0]) + 2))
+ {
+ char *pfmt = rpl_fmt;
+ nfmt = rpl_fmt;
+
+ pfmt = mempcpy (pfmt, fmt, pb - fmt);
+ pfmt = stpcpy (pfmt, abmon[tm->tm_mon]);
+ strcpy (pfmt, pb + 2);
+ }
+ }
+ size_t ret = nstrftime (buf, size, nfmt, tm, tz, ns);
+ return ret;
+}
+
/* Return the expected number of columns in a long-format time stamp,
or zero if it cannot be calculated. */
@@ -3225,63 +3724,28 @@ long_time_expected_width (void)
char buf[TIME_STAMP_LEN_MAXIMUM + 1];
/* In case you're wondering if localtime can fail with an input time_t
- value of 0, let's just say it's very unlikely, but not inconceivable.
- The TZ environment variable would have to specify a time zone that
- is 2**31-1900 years or more ahead of UTC. This could happen only on
- a 64-bit system that blindly accepts e.g., TZ=UTC+20000000000000.
- However, this is not possible with Solaris 10 or glibc-2.3.5, since
- their implementations limit the offset to 167:59 and 24:00, resp. */
+ value of 0, let's just say it's very unlikely, but not inconceivable.
+ The TZ environment variable would have to specify a time zone that
+ is 2**31-1900 years or more ahead of UTC. This could happen only on
+ a 64-bit system that blindly accepts e.g., TZ=UTC+20000000000000.
+ However, this is not possible with Solaris 10 or glibc-2.3.5, since
+ their implementations limit the offset to 167:59 and 24:00, resp. */
if (tm)
- {
- size_t len =
- nstrftime (buf, sizeof buf, long_time_format[0], tm, 0, 0);
- if (len != 0)
- width = mbsnwidth (buf, len, 0);
- }
+ {
+ size_t len =
+ align_nstrftime (buf, sizeof buf, long_time_format[0], tm,
+ localtz, 0);
+ if (len != 0)
+ width = mbsnwidth (buf, len, 0);
+ }
if (width < 0)
- width = 0;
+ width = 0;
}
return width;
}
-/* Get the current time. */
-
-static void
-get_current_time (void)
-{
-#if HAVE_CLOCK_GETTIME && defined CLOCK_REALTIME
- {
- struct timespec timespec;
- if (clock_gettime (CLOCK_REALTIME, &timespec) == 0)
- {
- current_time = timespec.tv_sec;
- current_time_ns = timespec.tv_nsec;
- return;
- }
- }
-#endif
-
- /* The clock does not have nanosecond resolution, so get the maximum
- possible value for the current time that is consistent with the
- reported clock. That way, files are not considered to be in the
- future merely because their time stamps have higher resolution
- than the clock resolution. */
-
-#if HAVE_GETTIMEOFDAY
- {
- struct timeval timeval;
- gettimeofday (&timeval, NULL);
- current_time = timeval.tv_sec;
- current_time_ns = timeval.tv_usec * 1000 + 999;
- }
-#else
- current_time = time (NULL);
- current_time_ns = 999999999;
-#endif
-}
-
/* Print the user or group name NAME, with numeric id ID, using a
print width of WIDTH columns. */
@@ -3298,7 +3762,7 @@ format_user_or_group (char const *name, unsigned long int id, int width)
len = strlen (name) + pad;
do
- putchar (' ');
+ putchar (' ');
while (pad--);
}
else
@@ -3317,7 +3781,7 @@ static void
format_user (uid_t u, int width, bool stat_ok)
{
format_user_or_group (! stat_ok ? "?" :
- (numeric_ids ? NULL : getuser (u)), u, width);
+ (numeric_ids ? NULL : getuser (u)), u, width);
}
/* Likewise, for groups. */
@@ -3326,7 +3790,7 @@ static void
format_group (gid_t g, int width, bool stat_ok)
{
format_user_or_group (! stat_ok ? "?" :
- (numeric_ids ? NULL : getgroup (g)), g, width);
+ (numeric_ids ? NULL : getgroup (g)), g, width);
}
/* Return the number of columns that format_user_or_group will print. */
@@ -3341,7 +3805,7 @@ format_user_or_group_width (char const *name, unsigned long int id)
}
else
{
- char buf[INT_BUFSIZE_BOUND (unsigned long int)];
+ char buf[INT_BUFSIZE_BOUND (id)];
sprintf (buf, "%lu", id);
return strlen (buf);
}
@@ -3363,9 +3827,19 @@ format_group_width (gid_t 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,
+ possibly using buffer, BUF, of length BUFLEN, which must be at least
+ INT_BUFSIZE_BOUND (uintmax_t) bytes. */
+static char *
+format_inode (char *buf, size_t buflen, const struct fileinfo *f)
+{
+ assert (INT_BUFSIZE_BOUND (uintmax_t) <= buflen);
+ return (f->stat_ok && f->stat.st_ino != NOT_AN_INODE_NUMBER
+ ? umaxtostr (f->stat.st_ino, buf)
+ : (char *) "?");
+}
/* Print information about F in long format. */
-
static void
print_long_format (const struct fileinfo *f)
{
@@ -3381,13 +3855,11 @@ print_long_format (const struct fileinfo *f)
];
size_t s;
char *p;
- time_t when;
- int when_ns;
struct timespec when_timespec;
struct tm *when_local;
/* Compute the mode string, except remove the trailing space if no
- files in this directory have ACLs. */
+ file in this directory has an ACL or security context. */
if (f->stat_ok)
filemodestring (&f->stat, modebuf);
else
@@ -3398,7 +3870,9 @@ print_long_format (const struct fileinfo *f)
}
if (! any_has_acl)
modebuf[10] = '\0';
- else if (FILE_HAS_ACL (f))
+ else if (f->acl_type == ACL_T_LSM_CONTEXT_ONLY)
+ modebuf[10] = '.';
+ else if (f->acl_type == ACL_T_YES)
modebuf[10] = '+';
switch (time_type)
@@ -3416,20 +3890,15 @@ print_long_format (const struct fileinfo *f)
abort ();
}
- when = when_timespec.tv_sec;
- when_ns = when_timespec.tv_nsec;
-
p = buf;
if (print_inode)
{
char hbuf[INT_BUFSIZE_BOUND (uintmax_t)];
sprintf (p, "%*s ", inode_number_width,
- (f->stat.st_ino == NOT_AN_INODE_NUMBER
- ? "?"
- : umaxtostr (f->stat.st_ino, hbuf)));
+ format_inode (hbuf, sizeof hbuf, f));
/* Increment by strlen (p) here, rather than by inode_number_width + 1.
- The latter is wrong when inode_number_width is zero. */
+ The latter is wrong when inode_number_width is zero. */
p += strlen (p);
}
@@ -3437,15 +3906,15 @@ print_long_format (const struct fileinfo *f)
{
char hbuf[LONGEST_HUMAN_READABLE + 1];
char const *blocks =
- (! f->stat_ok
- ? "?"
- : human_readable (ST_NBLOCKS (f->stat), hbuf, human_output_opts,
- ST_NBLOCKSIZE, output_block_size));
+ (! f->stat_ok
+ ? "?"
+ : human_readable (ST_NBLOCKS (f->stat), hbuf, human_output_opts,
+ ST_NBLOCKSIZE, output_block_size));
int pad;
for (pad = block_size_width - mbswidth (blocks, 0); 0 < pad; pad--)
- *p++ = ' ';
+ *p++ = ' ';
while ((*p++ = *blocks++))
- continue;
+ continue;
p[-1] = ' ';
}
@@ -3454,7 +3923,7 @@ print_long_format (const struct fileinfo *f)
{
char hbuf[INT_BUFSIZE_BOUND (uintmax_t)];
sprintf (p, "%s %*s ", modebuf, nlink_width,
- ! f->stat_ok ? "?" : umaxtostr (f->stat.st_nlink, hbuf));
+ ! f->stat_ok ? "?" : umaxtostr (f->stat.st_nlink, hbuf));
}
/* Increment by strlen (p) here, rather than by, e.g.,
sizeof modebuf - 2 + any_has_acl + 1 + nlink_width + 1.
@@ -3463,18 +3932,21 @@ print_long_format (const struct fileinfo *f)
DIRED_INDENT ();
- if (print_owner | print_group | print_author)
+ if (print_owner || print_group || print_author || print_scontext)
{
DIRED_FPUTS (buf, stdout, p - buf);
if (print_owner)
- format_user (f->stat.st_uid, owner_width, f->stat_ok);
+ format_user (f->stat.st_uid, owner_width, f->stat_ok);
if (print_group)
- format_group (f->stat.st_gid, group_width, f->stat_ok);
+ format_group (f->stat.st_gid, group_width, f->stat_ok);
if (print_author)
- format_user (f->stat.st_author, author_width, f->stat_ok);
+ format_user (f->stat.st_author, author_width, f->stat_ok);
+
+ if (print_scontext)
+ format_user_or_group (f->scontext, 0, scontext_width);
p = buf;
}
@@ -3485,28 +3957,29 @@ print_long_format (const struct fileinfo *f)
char majorbuf[INT_BUFSIZE_BOUND (uintmax_t)];
char minorbuf[INT_BUFSIZE_BOUND (uintmax_t)];
int blanks_width = (file_size_width
- - (major_device_number_width + 2
- + minor_device_number_width));
+ - (major_device_number_width + 2
+ + minor_device_number_width));
sprintf (p, "%*s, %*s ",
- major_device_number_width + MAX (0, blanks_width),
- umaxtostr (major (f->stat.st_rdev), majorbuf),
- minor_device_number_width,
- umaxtostr (minor (f->stat.st_rdev), minorbuf));
+ major_device_number_width + MAX (0, blanks_width),
+ umaxtostr (major (f->stat.st_rdev), majorbuf),
+ minor_device_number_width,
+ umaxtostr (minor (f->stat.st_rdev), minorbuf));
p += file_size_width + 1;
}
else
{
char hbuf[LONGEST_HUMAN_READABLE + 1];
char const *size =
- (! f->stat_ok
- ? "?"
- : human_readable (unsigned_file_size (f->stat.st_size),
- hbuf, human_output_opts, 1, file_output_block_size));
+ (! f->stat_ok
+ ? "?"
+ : human_readable (unsigned_file_size (f->stat.st_size),
+ hbuf, file_human_output_opts, 1,
+ file_output_block_size));
int pad;
for (pad = file_size_width - mbswidth (size, 0); 0 < pad; pad--)
- *p++ = ' ';
+ *p++ = ' ';
while ((*p++ = *size++))
- continue;
+ continue;
p[-1] = ' ';
}
@@ -3516,35 +3989,37 @@ print_long_format (const struct fileinfo *f)
if (f->stat_ok && when_local)
{
- time_t six_months_ago;
+ struct timespec six_months_ago;
bool recent;
char const *fmt;
/* If the file appears to be in the future, update the current
- time, in case the file happens to have been modified since
- the last time we checked the clock. */
- if (current_time < when
- || (current_time == when && current_time_ns < when_ns))
- {
- /* Note that get_current_time calls gettimeofday which, on some non-
- compliant systems, clobbers the buffer used for localtime's result.
- But it's ok here, because we use a gettimeofday wrapper that
- saves and restores the buffer around the gettimeofday call. */
- get_current_time ();
- }
-
- /* Consider a time to be recent if it is within the past six
- months. A Gregorian year has 365.2425 * 24 * 60 * 60 ==
- 31556952 seconds on the average. Write this value as an
- integer constant to avoid floating point hassles. */
- six_months_ago = current_time - 31556952 / 2;
- recent = (six_months_ago <= when
- && (when < current_time
- || (when == current_time && when_ns <= current_time_ns)));
+ time, in case the file happens to have been modified since
+ the last time we checked the clock. */
+ if (timespec_cmp (current_time, when_timespec) < 0)
+ {
+ /* Note that gettime may call gettimeofday which, on some non-
+ compliant systems, clobbers the buffer used for localtime's result.
+ But it's ok here, because we use a gettimeofday wrapper that
+ saves and restores the buffer around the gettimeofday call. */
+ gettime (&current_time);
+ }
+
+ /* Consider a time to be recent if it is within the past six months.
+ A Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds
+ on the average. Write this value as an integer constant to
+ avoid floating point hassles. */
+ six_months_ago.tv_sec = current_time.tv_sec - 31556952 / 2;
+ six_months_ago.tv_nsec = current_time.tv_nsec;
+
+ recent = (timespec_cmp (six_months_ago, when_timespec) < 0
+ && (timespec_cmp (when_timespec, current_time) < 0));
fmt = long_time_format[recent];
- s = nstrftime (p, TIME_STAMP_LEN_MAXIMUM + 1, fmt,
- when_local, 0, when_ns);
+ /* We assume here that all time zones are offset from UTC by a
+ whole number of seconds. */
+ s = align_nstrftime (p, TIME_STAMP_LEN_MAXIMUM + 1, fmt,
+ when_local, localtz, when_timespec.tv_nsec);
}
if (s || !*p)
@@ -3558,31 +4033,28 @@ print_long_format (const struct fileinfo *f)
else
{
/* The time cannot be converted using the desired format, so
- print it as a huge integer number of seconds. */
+ print it as a huge integer number of seconds. */
char hbuf[INT_BUFSIZE_BOUND (intmax_t)];
sprintf (p, "%*s ", long_time_expected_width (),
- (! f->stat_ok
- ? "?"
- : (TYPE_SIGNED (time_t)
- ? imaxtostr (when, hbuf)
- : umaxtostr (when, hbuf))));
+ (! f->stat_ok
+ ? "?"
+ : timetostr (when_timespec.tv_sec, hbuf)));
+ /* FIXME: (maybe) We discarded when_timespec.tv_nsec. */
p += strlen (p);
}
DIRED_FPUTS (buf, stdout, p - buf);
- print_name_with_quoting (f->name, FILE_OR_LINK_MODE (f), f->linkok,
- f->stat_ok, f->filetype, &dired_obstack);
+ size_t w = print_name_with_quoting (f, false, &dired_obstack, p - buf);
if (f->filetype == symbolic_link)
{
if (f->linkname)
- {
- DIRED_FPUTS_LITERAL (" -> ", stdout);
- print_name_with_quoting (f->linkname, f->linkmode, f->linkok - 1,
- f->stat_ok, f->filetype, NULL);
- if (indicator_style != none)
- print_type_indicator (true, f->linkmode, unknown);
- }
+ {
+ DIRED_FPUTS_LITERAL (" -> ", stdout);
+ print_name_with_quoting (f, true, NULL, (p - buf) + w + 4);
+ if (indicator_style != none)
+ print_type_indicator (true, f->linkmode, unknown);
+ }
}
else if (indicator_style != none)
print_type_indicator (f->stat_ok, f->stat.st_mode, f->filetype);
@@ -3596,12 +4068,12 @@ print_long_format (const struct fileinfo *f)
static size_t
quote_name (FILE *out, const char *name, struct quoting_options const *options,
- size_t *width)
+ size_t *width)
{
char smallbuf[BUFSIZ];
size_t len = quotearg_buffer (smallbuf, sizeof smallbuf, name, -1, options);
char *buf;
- size_t displayed_width IF_LINT (= 0);
+ size_t displayed_width IF_LINT ( = 0);
if (len < sizeof smallbuf)
buf = smallbuf;
@@ -3611,144 +4083,144 @@ quote_name (FILE *out, const char *name, struct quoting_options const *options,
quotearg_buffer (buf, len + 1, name, -1, options);
}
- if (qmark_funny_chars)
+ enum quoting_style qs = get_quoting_style (options);
+
+ if (qmark_funny_chars
+ && (qs == shell_quoting_style || qs == shell_always_quoting_style
+ || qs == literal_quoting_style))
{
-#if HAVE_MBRTOWC
if (MB_CUR_MAX > 1)
- {
- char const *p = buf;
- char const *plimit = buf + len;
- char *q = buf;
- displayed_width = 0;
-
- while (p < plimit)
- switch (*p)
- {
- case ' ': case '!': case '"': case '#': case '%':
- case '&': case '\'': case '(': case ')': case '*':
- case '+': case ',': case '-': case '.': case '/':
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- case ':': case ';': case '<': case '=': case '>':
- case '?':
- case 'A': case 'B': case 'C': case 'D': case 'E':
- case 'F': case 'G': case 'H': case 'I': case 'J':
- case 'K': case 'L': case 'M': case 'N': case 'O':
- case 'P': case 'Q': case 'R': case 'S': case 'T':
- case 'U': case 'V': case 'W': case 'X': case 'Y':
- case 'Z':
- case '[': case '\\': case ']': case '^': case '_':
- case 'a': case 'b': case 'c': case 'd': case 'e':
- case 'f': case 'g': case 'h': case 'i': case 'j':
- case 'k': case 'l': case 'm': case 'n': case 'o':
- case 'p': case 'q': case 'r': case 's': case 't':
- case 'u': case 'v': case 'w': case 'x': case 'y':
- case 'z': case '{': case '|': case '}': case '~':
- /* These characters are printable ASCII characters. */
- *q++ = *p++;
- displayed_width += 1;
- break;
- default:
- /* If we have a multibyte sequence, copy it until we
- reach its end, replacing each non-printable multibyte
- character with a single question mark. */
- {
- mbstate_t mbstate = { 0, };
- do
- {
- wchar_t wc;
- size_t bytes;
- int w;
-
- bytes = mbrtowc (&wc, p, plimit - p, &mbstate);
-
- if (bytes == (size_t) -1)
- {
- /* An invalid multibyte sequence was
- encountered. Skip one input byte, and
- put a question mark. */
- p++;
- *q++ = '?';
- displayed_width += 1;
- break;
- }
-
- if (bytes == (size_t) -2)
- {
- /* An incomplete multibyte character
- at the end. Replace it entirely with
- a question mark. */
- p = plimit;
- *q++ = '?';
- displayed_width += 1;
- break;
- }
-
- if (bytes == 0)
- /* A null wide character was encountered. */
- bytes = 1;
-
- w = wcwidth (wc);
- if (w >= 0)
- {
- /* A printable multibyte character.
- Keep it. */
- for (; bytes > 0; --bytes)
- *q++ = *p++;
- displayed_width += w;
- }
- else
- {
- /* An unprintable multibyte character.
- Replace it entirely with a question
- mark. */
- p += bytes;
- *q++ = '?';
- displayed_width += 1;
- }
- }
- while (! mbsinit (&mbstate));
- }
- break;
- }
-
- /* The buffer may have shrunk. */
- len = q - buf;
- }
+ {
+ char const *p = buf;
+ char const *plimit = buf + len;
+ char *q = buf;
+ displayed_width = 0;
+
+ while (p < plimit)
+ switch (*p)
+ {
+ case ' ': case '!': case '"': case '#': case '%':
+ case '&': case '\'': case '(': case ')': case '*':
+ case '+': case ',': case '-': case '.': case '/':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case ':': case ';': case '<': case '=': case '>':
+ case '?':
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F': case 'G': case 'H': case 'I': case 'J':
+ case 'K': case 'L': case 'M': case 'N': case 'O':
+ case 'P': case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X': case 'Y':
+ case 'Z':
+ case '[': case '\\': case ']': case '^': case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f': case 'g': case 'h': case 'i': case 'j':
+ case 'k': case 'l': case 'm': case 'n': case 'o':
+ case 'p': case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x': case 'y':
+ case 'z': case '{': case '|': case '}': case '~':
+ /* These characters are printable ASCII characters. */
+ *q++ = *p++;
+ displayed_width += 1;
+ break;
+ default:
+ /* If we have a multibyte sequence, copy it until we
+ reach its end, replacing each non-printable multibyte
+ character with a single question mark. */
+ {
+ mbstate_t mbstate = { 0, };
+ do
+ {
+ wchar_t wc;
+ size_t bytes;
+ int w;
+
+ bytes = mbrtowc (&wc, p, plimit - p, &mbstate);
+
+ if (bytes == (size_t) -1)
+ {
+ /* An invalid multibyte sequence was
+ encountered. Skip one input byte, and
+ put a question mark. */
+ p++;
+ *q++ = '?';
+ displayed_width += 1;
+ break;
+ }
+
+ if (bytes == (size_t) -2)
+ {
+ /* An incomplete multibyte character
+ at the end. Replace it entirely with
+ a question mark. */
+ p = plimit;
+ *q++ = '?';
+ displayed_width += 1;
+ break;
+ }
+
+ if (bytes == 0)
+ /* A null wide character was encountered. */
+ bytes = 1;
+
+ w = wcwidth (wc);
+ if (w >= 0)
+ {
+ /* A printable multibyte character.
+ Keep it. */
+ for (; bytes > 0; --bytes)
+ *q++ = *p++;
+ displayed_width += w;
+ }
+ else
+ {
+ /* An unprintable multibyte character.
+ Replace it entirely with a question
+ mark. */
+ p += bytes;
+ *q++ = '?';
+ displayed_width += 1;
+ }
+ }
+ while (! mbsinit (&mbstate));
+ }
+ break;
+ }
+
+ /* The buffer may have shrunk. */
+ len = q - buf;
+ }
else
-#endif
- {
- char *p = buf;
- char const *plimit = buf + len;
-
- while (p < plimit)
- {
- if (! isprint (to_uchar (*p)))
- *p = '?';
- p++;
- }
- displayed_width = len;
- }
+ {
+ char *p = buf;
+ char const *plimit = buf + len;
+
+ while (p < plimit)
+ {
+ if (! isprint (to_uchar (*p)))
+ *p = '?';
+ p++;
+ }
+ displayed_width = len;
+ }
}
else if (width != NULL)
{
-#if HAVE_MBRTOWC
if (MB_CUR_MAX > 1)
- displayed_width = mbsnwidth (buf, len, 0);
+ displayed_width = mbsnwidth (buf, len, 0);
else
-#endif
- {
- char const *p = buf;
- char const *plimit = buf + len;
-
- displayed_width = 0;
- while (p < plimit)
- {
- if (isprint (to_uchar (*p)))
- displayed_width++;
- p++;
- }
- }
+ {
+ char const *p = buf;
+ char const *plimit = buf + len;
+
+ displayed_width = 0;
+ while (p < plimit)
+ {
+ if (isprint (to_uchar (*p)))
+ displayed_width++;
+ p++;
+ }
+ }
}
if (out != NULL)
@@ -3758,27 +4230,38 @@ quote_name (FILE *out, const char *name, struct quoting_options const *options,
return len;
}
-static void
-print_name_with_quoting (const char *p, mode_t mode, int linkok,
- bool stat_ok, enum filetype type,
- struct obstack *stack)
+static size_t
+print_name_with_quoting (const struct fileinfo *f,
+ bool symlink_target,
+ struct obstack *stack,
+ size_t start_col)
{
- if (print_with_color)
- print_color_indicator (p, mode, linkok, stat_ok, type);
+ const char* name = symlink_target ? f->linkname : f->name;
+
+ bool used_color_this_time
+ = (print_with_color
+ && (print_color_indicator (f, symlink_target)
+ || is_colored (C_NORM)));
if (stack)
PUSH_CURRENT_DIRED_POS (stack);
- dired_pos += quote_name (stdout, p, filename_quoting_options, NULL);
+ size_t width = quote_name (stdout, name, filename_quoting_options, NULL);
+ dired_pos += width;
if (stack)
PUSH_CURRENT_DIRED_POS (stack);
- if (print_with_color)
+ process_signals ();
+ if (used_color_this_time)
{
- process_signals ();
prep_non_filename_text ();
+ if (line_length
+ && (start_col / line_length != (start_col + width - 1) / line_length))
+ put_indicator (&color_indicator[C_CLR_TO_EOL]);
}
+
+ return width;
}
static void
@@ -3789,34 +4272,41 @@ prep_non_filename_text (void)
else
{
put_indicator (&color_indicator[C_LEFT]);
- put_indicator (&color_indicator[C_NORM]);
+ put_indicator (&color_indicator[C_RESET]);
put_indicator (&color_indicator[C_RIGHT]);
}
}
-/* Print the file name of `f' with appropriate quoting.
+/* Print the file name of 'f' with appropriate quoting.
Also print file size, inode number, and filetype indicator character,
as requested by switches. */
-static void
-print_file_name_and_frills (const struct fileinfo *f)
+static size_t
+print_file_name_and_frills (const struct fileinfo *f, size_t start_col)
{
char buf[MAX (LONGEST_HUMAN_READABLE + 1, INT_BUFSIZE_BOUND (uintmax_t))];
+ set_normal_color ();
+
if (print_inode)
printf ("%*s ", format == with_commas ? 0 : inode_number_width,
- umaxtostr (f->stat.st_ino, buf));
+ format_inode (buf, sizeof buf, f));
if (print_block_size)
printf ("%*s ", format == with_commas ? 0 : block_size_width,
- human_readable (ST_NBLOCKS (f->stat), buf, human_output_opts,
- ST_NBLOCKSIZE, output_block_size));
+ ! f->stat_ok ? "?"
+ : human_readable (ST_NBLOCKS (f->stat), buf, human_output_opts,
+ ST_NBLOCKSIZE, output_block_size));
+
+ if (print_scontext)
+ printf ("%*s ", format == with_commas ? 0 : scontext_width, f->scontext);
- print_name_with_quoting (f->name, FILE_OR_LINK_MODE (f), f->linkok,
- f->stat_ok, f->filetype, NULL);
+ size_t width = print_name_with_quoting (f, false, NULL, start_col);
if (indicator_style != none)
- print_type_indicator (f->stat_ok, f->stat.st_mode, f->filetype);
+ width += print_type_indicator (f->stat_ok, f->stat.st_mode, f->filetype);
+
+ return width;
}
/* Given these arguments describing a file, return the single-byte
@@ -3829,96 +4319,118 @@ get_type_indicator (bool stat_ok, mode_t mode, enum filetype type)
if (stat_ok ? S_ISREG (mode) : type == normal)
{
if (stat_ok && indicator_style == classify && (mode & S_IXUGO))
- c = '*';
+ c = '*';
else
- c = 0;
+ c = 0;
}
else
{
if (stat_ok ? S_ISDIR (mode) : type == directory || type == arg_directory)
- c = '/';
+ c = '/';
else if (indicator_style == slash)
- c = 0;
+ c = 0;
else if (stat_ok ? S_ISLNK (mode) : type == symbolic_link)
- c = '@';
+ c = '@';
else if (stat_ok ? S_ISFIFO (mode) : type == fifo)
- c = '|';
+ c = '|';
else if (stat_ok ? S_ISSOCK (mode) : type == sock)
- c = '=';
+ c = '=';
else if (stat_ok && S_ISDOOR (mode))
- c = '>';
+ c = '>';
else
- c = 0;
+ c = 0;
}
return c;
}
-static void
+static bool
print_type_indicator (bool stat_ok, mode_t mode, enum filetype type)
{
char c = get_type_indicator (stat_ok, mode, type);
if (c)
DIRED_PUTCHAR (c);
+ return !!c;
}
-static void
-print_color_indicator (const char *name, mode_t mode, int linkok,
- bool stat_ok, enum filetype filetype)
+/* Returns whether any color sequence was printed. */
+static bool
+print_color_indicator (const struct fileinfo *f, bool symlink_target)
{
- int type;
+ enum indicator_no type;
struct color_ext_type *ext; /* Color extension */
size_t len; /* Length of name */
+ const char* name;
+ mode_t mode;
+ int linkok;
+ if (symlink_target)
+ {
+ name = f->linkname;
+ mode = f->linkmode;
+ linkok = f->linkok ? 0 : -1;
+ }
+ else
+ {
+ name = f->name;
+ mode = FILE_OR_LINK_MODE (f);
+ linkok = f->linkok;
+ }
+
/* Is this a nonexistent file? If so, linkok == -1. */
- if (linkok == -1 && color_indicator[C_MISSING].string != NULL)
+ if (linkok == -1 && is_colored (C_MISSING))
type = C_MISSING;
- else if (! stat_ok)
+ else if (!f->stat_ok)
{
static enum indicator_no filetype_indicator[] = FILETYPE_INDICATORS;
- type = filetype_indicator[filetype];
+ type = filetype_indicator[f->filetype];
}
else
{
if (S_ISREG (mode))
- {
- type = C_FILE;
- if ((mode & S_ISUID) != 0)
- type = C_SETUID;
- else if ((mode & S_ISGID) != 0)
- type = C_SETGID;
- else if ((mode & S_IXUGO) != 0)
- type = C_EXEC;
- }
+ {
+ type = C_FILE;
+
+ if ((mode & S_ISUID) != 0 && is_colored (C_SETUID))
+ type = C_SETUID;
+ else if ((mode & S_ISGID) != 0 && is_colored (C_SETGID))
+ type = C_SETGID;
+ else if (is_colored (C_CAP) && f->has_capability)
+ type = C_CAP;
+ else if ((mode & S_IXUGO) != 0 && is_colored (C_EXEC))
+ type = C_EXEC;
+ else if ((1 < f->stat.st_nlink) && is_colored (C_MULTIHARDLINK))
+ type = C_MULTIHARDLINK;
+ }
else if (S_ISDIR (mode))
- {
- if ((mode & S_ISVTX) && (mode & S_IWOTH))
- type = C_STICKY_OTHER_WRITABLE;
- else if ((mode & S_IWOTH) != 0)
- type = C_OTHER_WRITABLE;
- else if ((mode & S_ISVTX) != 0)
- type = C_STICKY;
- else
- type = C_DIR;
- }
+ {
+ type = C_DIR;
+
+ if ((mode & S_ISVTX) && (mode & S_IWOTH)
+ && is_colored (C_STICKY_OTHER_WRITABLE))
+ type = C_STICKY_OTHER_WRITABLE;
+ else if ((mode & S_IWOTH) != 0 && is_colored (C_OTHER_WRITABLE))
+ type = C_OTHER_WRITABLE;
+ else if ((mode & S_ISVTX) != 0 && is_colored (C_STICKY))
+ type = C_STICKY;
+ }
else if (S_ISLNK (mode))
- type = ((!linkok && color_indicator[C_ORPHAN].string)
- ? C_ORPHAN : C_LINK);
+ type = C_LINK;
else if (S_ISFIFO (mode))
- type = C_FIFO;
+ type = C_FIFO;
else if (S_ISSOCK (mode))
- type = C_SOCK;
+ type = C_SOCK;
else if (S_ISBLK (mode))
- type = C_BLK;
+ type = C_BLK;
else if (S_ISCHR (mode))
- type = C_CHR;
+ type = C_CHR;
else if (S_ISDOOR (mode))
- type = C_DOOR;
+ type = C_DOOR;
else
- {
- /* Classify a file of some other type as C_ORPHAN. */
- type = C_ORPHAN;
- }
+ {
+ /* Classify a file of some other type as C_ORPHAN. */
+ type = C_ORPHAN;
+ }
}
/* Check the file's suffix only if still classified as C_FILE. */
@@ -3930,30 +4442,50 @@ print_color_indicator (const char *name, mode_t mode, int linkok,
len = strlen (name);
name += len; /* Pointer to final \0. */
for (ext = color_ext_list; ext != NULL; ext = ext->next)
- {
- if (ext->ext.len <= len
- && strncmp (name - ext->ext.len, ext->ext.string,
- ext->ext.len) == 0)
- break;
- }
+ {
+ if (ext->ext.len <= len
+ && STREQ_LEN (name - ext->ext.len, ext->ext.string,
+ ext->ext.len))
+ break;
+ }
}
- put_indicator (&color_indicator[C_LEFT]);
- put_indicator (ext ? &(ext->seq) : &color_indicator[type]);
- put_indicator (&color_indicator[C_RIGHT]);
+ /* Adjust the color for orphaned symlinks. */
+ if (type == C_LINK && !linkok)
+ {
+ if (color_symlink_as_referent || is_colored (C_ORPHAN))
+ type = C_ORPHAN;
+ }
+
+ {
+ const struct bin_str *const s
+ = ext ? &(ext->seq) : &color_indicator[type];
+ if (s->string != NULL)
+ {
+ /* Need to reset so not dealing with attribute combinations */
+ if (is_colored (C_NORM))
+ restore_default_color ();
+ put_indicator (&color_indicator[C_LEFT]);
+ put_indicator (s);
+ put_indicator (&color_indicator[C_RIGHT]);
+ return true;
+ }
+ else
+ return false;
+ }
}
/* Output a color indicator (which may contain nulls). */
static void
put_indicator (const struct bin_str *ind)
{
- size_t i;
- const char *p;
-
- p = ind->string;
+ if (! used_color)
+ {
+ used_color = true;
+ prep_non_filename_text ();
+ }
- for (i = ind->len; i != 0; --i)
- putchar (*(p++));
+ fwrite (ind->string, ind->len, 1, stdout);
}
static size_t
@@ -3965,15 +4497,19 @@ length_of_file_name_and_frills (const struct fileinfo *f)
if (print_inode)
len += 1 + (format == with_commas
- ? strlen (umaxtostr (f->stat.st_ino, buf))
- : inode_number_width);
+ ? strlen (umaxtostr (f->stat.st_ino, buf))
+ : inode_number_width);
if (print_block_size)
len += 1 + (format == with_commas
- ? strlen (human_readable (ST_NBLOCKS (f->stat), buf,
- human_output_opts, ST_NBLOCKSIZE,
- output_block_size))
- : block_size_width);
+ ? strlen (! f->stat_ok ? "?"
+ : human_readable (ST_NBLOCKS (f->stat), buf,
+ human_output_opts, ST_NBLOCKSIZE,
+ output_block_size))
+ : block_size_width);
+
+ if (print_scontext)
+ len += 1 + (format == with_commas ? strlen (f->scontext) : scontext_width);
quote_name (NULL, f->name, filename_quoting_options, &name_width);
len += name_width;
@@ -4006,19 +4542,19 @@ print_many_per_line (void)
/* Print the next row. */
while (1)
- {
- struct fileinfo const *f = sorted_file[filesno];
- size_t name_length = length_of_file_name_and_frills (f);
- size_t max_name_length = line_fmt->col_arr[col++];
- print_file_name_and_frills (f);
-
- filesno += rows;
- if (filesno >= cwd_n_used)
- break;
-
- indent (pos + name_length, pos + max_name_length);
- pos += max_name_length;
- }
+ {
+ struct fileinfo const *f = sorted_file[filesno];
+ size_t name_length = length_of_file_name_and_frills (f);
+ size_t max_name_length = line_fmt->col_arr[col++];
+ print_file_name_and_frills (f, pos);
+
+ filesno += rows;
+ if (filesno >= cwd_n_used)
+ break;
+
+ indent (pos + name_length, pos + max_name_length);
+ pos += max_name_length;
+ }
putchar ('\n');
}
}
@@ -4030,31 +4566,31 @@ print_horizontal (void)
size_t pos = 0;
size_t cols = calculate_columns (false);
struct column_info const *line_fmt = &column_info[cols - 1];
- size_t name_length = length_of_file_name_and_frills (cwd_file);
+ struct fileinfo const *f = sorted_file[0];
+ size_t name_length = length_of_file_name_and_frills (f);
size_t max_name_length = line_fmt->col_arr[0];
/* Print first entry. */
- print_file_name_and_frills (cwd_file);
+ print_file_name_and_frills (f, 0);
/* Now the rest. */
for (filesno = 1; filesno < cwd_n_used; ++filesno)
{
- struct fileinfo const *f;
size_t col = filesno % cols;
if (col == 0)
- {
- putchar ('\n');
- pos = 0;
- }
+ {
+ putchar ('\n');
+ pos = 0;
+ }
else
- {
- indent (pos + name_length, pos + max_name_length);
- pos += max_name_length;
- }
+ {
+ indent (pos + name_length, pos + max_name_length);
+ pos += max_name_length;
+ }
f = sorted_file[filesno];
- print_file_name_and_frills (f);
+ print_file_name_and_frills (f, pos);
name_length = length_of_file_name_and_frills (f);
max_name_length = line_fmt->col_arr[col];
@@ -4062,8 +4598,10 @@ print_horizontal (void)
putchar ('\n');
}
+/* Output name + SEP + ' '. */
+
static void
-print_with_commas (void)
+print_with_separator (char sep)
{
size_t filesno;
size_t pos = 0;
@@ -4071,28 +4609,30 @@ print_with_commas (void)
for (filesno = 0; filesno < cwd_n_used; filesno++)
{
struct fileinfo const *f = sorted_file[filesno];
- size_t len = length_of_file_name_and_frills (f);
+ size_t len = line_length ? length_of_file_name_and_frills (f) : 0;
if (filesno != 0)
- {
- char separator;
-
- if (pos + len + 2 < line_length)
- {
- pos += 2;
- separator = ' ';
- }
- else
- {
- pos = 0;
- separator = '\n';
- }
-
- putchar (',');
- putchar (separator);
- }
-
- print_file_name_and_frills (f);
+ {
+ char separator;
+
+ if (! line_length
+ || ((pos + len + 2 < line_length)
+ && (pos <= SIZE_MAX - len - 2)))
+ {
+ pos += 2;
+ separator = ' ';
+ }
+ else
+ {
+ pos = 0;
+ separator = '\n';
+ }
+
+ putchar (sep);
+ putchar (separator);
+ }
+
+ print_file_name_and_frills (f, pos);
pos += len;
}
putchar ('\n');
@@ -4107,19 +4647,19 @@ indent (size_t from, size_t to)
while (from < to)
{
if (tabsize != 0 && to / tabsize > (from + 1) / tabsize)
- {
- putchar ('\t');
- from += tabsize - from % tabsize;
- }
+ {
+ putchar ('\t');
+ from += tabsize - from % tabsize;
+ }
else
- {
- putchar (' ');
- from++;
- }
+ {
+ putchar (' ');
+ from++;
+ }
}
}
-/* Put DIRNAME/NAME into DEST, handling `.' and `/' properly. */
+/* Put DIRNAME/NAME into DEST, handling '.' and '/' properly. */
/* FIXME: maybe remove this function someday. See about using a
non-malloc'ing version of file_name_concat. */
@@ -4132,10 +4672,10 @@ attach (char *dest, const char *dirname, const char *name)
if (dirname[0] != '.' || dirname[1] != 0)
{
while (*dirnamep)
- *dest++ = *dirnamep++;
- /* Add '/' if `dirname' doesn't already end with it. */
+ *dest++ = *dirnamep++;
+ /* Add '/' if 'dirname' doesn't already end with it. */
if (dirnamep > dirname && dirnamep[-1] != '/')
- *dest++ = '/';
+ *dest++ = '/';
}
while (*name)
*dest++ = *name++;
@@ -4161,41 +4701,41 @@ init_column_info (void)
size_t *p;
if (max_cols < max_idx / 2)
- {
- /* The number of columns is far less than the display width
- allows. Grow the allocation, but only so that it's
- double the current requirements. If the display is
- extremely wide, this avoids allocating a lot of memory
- that is never needed. */
- column_info = xnrealloc (column_info, max_cols,
- 2 * sizeof *column_info);
- new_column_info_alloc = 2 * max_cols;
- }
+ {
+ /* The number of columns is far less than the display width
+ allows. Grow the allocation, but only so that it's
+ double the current requirements. If the display is
+ extremely wide, this avoids allocating a lot of memory
+ that is never needed. */
+ column_info = xnrealloc (column_info, max_cols,
+ 2 * sizeof *column_info);
+ new_column_info_alloc = 2 * max_cols;
+ }
else
- {
- column_info = xnrealloc (column_info, max_idx, sizeof *column_info);
- new_column_info_alloc = max_idx;
- }
+ {
+ column_info = xnrealloc (column_info, max_idx, sizeof *column_info);
+ new_column_info_alloc = max_idx;
+ }
/* Allocate the new size_t objects by computing the triangle
- formula n * (n + 1) / 2, except that we don't need to
- allocate the part of the triangle that we've already
- allocated. Check for address arithmetic overflow. */
+ formula n * (n + 1) / 2, except that we don't need to
+ allocate the part of the triangle that we've already
+ allocated. Check for address arithmetic overflow. */
{
- size_t column_info_growth = new_column_info_alloc - column_info_alloc;
- size_t s = column_info_alloc + 1 + new_column_info_alloc;
- size_t t = s * column_info_growth;
- if (s < new_column_info_alloc || t / column_info_growth != s)
- xalloc_die ();
- p = xnmalloc (t / 2, sizeof *p);
+ size_t column_info_growth = new_column_info_alloc - column_info_alloc;
+ size_t s = column_info_alloc + 1 + new_column_info_alloc;
+ size_t t = s * column_info_growth;
+ if (s < new_column_info_alloc || t / column_info_growth != s)
+ xalloc_die ();
+ p = xnmalloc (t / 2, sizeof *p);
}
/* Grow the triangle by parceling out the cells just allocated. */
for (i = column_info_alloc; i < new_column_info_alloc; i++)
- {
- column_info[i].col_arr = p;
- p += i + 1;
- }
+ {
+ column_info[i].col_arr = p;
+ p += i + 1;
+ }
column_info_alloc = new_column_info_alloc;
}
@@ -4207,7 +4747,7 @@ init_column_info (void)
column_info[i].valid_len = true;
column_info[i].line_len = (i + 1) * MIN_COLUMN_WIDTH;
for (j = 0; j <= i; ++j)
- column_info[i].col_arr[j] = MIN_COLUMN_WIDTH;
+ column_info[i].col_arr[j] = MIN_COLUMN_WIDTH;
}
}
@@ -4235,31 +4775,31 @@ calculate_columns (bool by_columns)
size_t i;
for (i = 0; i < max_cols; ++i)
- {
- if (column_info[i].valid_len)
- {
- size_t idx = (by_columns
- ? filesno / ((cwd_n_used + i) / (i + 1))
- : filesno % (i + 1));
- size_t real_length = name_length + (idx == i ? 0 : 2);
-
- if (column_info[i].col_arr[idx] < real_length)
- {
- column_info[i].line_len += (real_length
- - 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);
- }
- }
- }
+ {
+ if (column_info[i].valid_len)
+ {
+ size_t idx = (by_columns
+ ? filesno / ((cwd_n_used + i) / (i + 1))
+ : filesno % (i + 1));
+ size_t real_length = name_length + (idx == i ? 0 : 2);
+
+ if (column_info[i].col_arr[idx] < real_length)
+ {
+ column_info[i].line_len += (real_length
+ - 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);
+ }
+ }
+ }
}
/* Find maximum allowed columns. */
for (cols = max_cols; 1 < cols; --cols)
{
if (column_info[cols - 1].valid_len)
- break;
+ break;
}
return cols;
@@ -4269,45 +4809,46 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
fputs (_("\
List information about the FILEs (the current directory by default).\n\
-Sort entries alphabetically if none of -cftuvSUX nor --sort.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
+Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.\n\
"), stdout);
+
+ 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 octal escapes for nongraphic characters\n\
+ -b, --escape print C-style escapes for nongraphic characters\n\
"), stdout);
fputs (_("\
- --block-size=SIZE use SIZE-byte blocks\n\
+ --block-size=SIZE scale sizes by SIZE before printing them; e.g.,\n\
+ '--block-size=M' prints sizes in units of\n\
+ 1,048,576 bytes; see SIZE format below\n\
-B, --ignore-backups do not list implied entries ending with ~\n\
-c with -lt: sort by, and show, ctime (time of last\n\
- modification of file status information)\n\
- with -l: show ctime and sort by name\n\
- otherwise: sort by ctime\n\
+ modification of file status information);\n\
+ with -l: show ctime and sort by name;\n\
+ otherwise: sort by ctime, newest first\n\
"), stdout);
fputs (_("\
-C list entries by columns\n\
- --color[=WHEN] control whether color is used to distinguish file\n\
- types. WHEN may be `never', `always', or `auto'\n\
- -d, --directory list directory entries instead of contents,\n\
- and do not dereference symbolic links\n\
+ --color[=WHEN] colorize the output; WHEN can be 'always' (default\
+\n\
+ if omitted), 'auto', or 'never'; 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 do not sort, enable -aU, disable -ls --color\n\
-F, --classify append indicator (one of */=>@|) to entries\n\
- --file-type likewise, except do not append `*'\n\
+ --file-type likewise, except do not append '*'\n\
--format=WORD across -x, commas -m, horizontal -x, long -l,\n\
single-column -1, verbose -l, vertical -C\n\
--full-time like -l --time-style=full-iso\n\
@@ -4317,11 +4858,13 @@ Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
fputs (_("\
--group-directories-first\n\
- group directories before files\n\
+ group directories before files;\n\
+ can be augmented with a --sort option, but any\n\
+ use of --sort=none (-U) disables grouping\n\
"), stdout);
fputs (_("\
-G, --no-group in a long listing, don't print group names\n\
- -h, --human-readable with -l, print sizes in human readable format\n\
+ -h, --human-readable with -l and/or -s, print human readable sizes\n\
(e.g., 1K 234M 2G)\n\
--si likewise, but use powers of 1000 not 1024\n\
"), stdout);
@@ -4330,24 +4873,28 @@ Mandatory arguments to long options are mandatory for short options too.\n\
follow symbolic links listed on the command line\n\
--dereference-command-line-symlink-to-dir\n\
follow each command line symbolic link\n\
- that points to a directory\n\
- --hide=PATTERN do not list implied entries matching shell PATTERN\n\
+ that points to a directory\n\
+ --hide=PATTERN do not list implied entries matching shell PATTERN\
+\n\
(overridden by -a or -A)\n\
"), stdout);
fputs (_("\
- --indicator-style=WORD append indicator with style WORD to entry names:\n\
+ --indicator-style=WORD append indicator with style WORD to entry names:\
+\n\
none (default), slash (-p),\n\
file-type (--file-type), classify (-F)\n\
-i, --inode print the index number of each file\n\
- -I, --ignore=PATTERN do not list implied entries matching shell PATTERN\n\
- -k like --block-size=1K\n\
+ -I, --ignore=PATTERN do not list implied entries matching shell PATTERN\
+\n\
+ -k, --kibibytes default to 1024-byte blocks for disk usage\n\
"), stdout);
fputs (_("\
-l use a long listing format\n\
-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\
- -m fill width with a comma separated list of entries\n\
+ -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\
@@ -4358,73 +4905,80 @@ Mandatory arguments to long options are mandatory for short options too.\n\
append / indicator to directories\n\
"), stdout);
fputs (_("\
- -q, --hide-control-chars print ? instead of non graphic characters\n\
- --show-control-chars show non graphic characters as-is (default\n\
- unless program is `ls' and output is a terminal)\n\
+ -q, --hide-control-chars print ? instead of nongraphic characters\n\
+ --show-control-chars show nongraphic characters as-is (the default,\n\
+ unless program is 'ls' and output is a terminal)\
+\n\
-Q, --quote-name enclose entry names in double quotes\n\
--quoting-style=WORD use quoting style WORD for entry names:\n\
- literal, locale, shell, shell-always, c, escape\n\
+ literal, locale, shell, shell-always,\n\
+ shell-escape, shell-escape-always, c, escape\n\
"), stdout);
fputs (_("\
-r, --reverse reverse order while sorting\n\
-R, --recursive list subdirectories recursively\n\
- -s, --size print the size of each file, in blocks\n\
+ -s, --size print the allocated size of each file, in blocks\n\
"), stdout);
fputs (_("\
- -S sort by file size\n\
- --sort=WORD sort by WORD instead of name: none -U,\n\
- extension -X, size -S, time -t, version -v\n\
- --time=WORD with -l, show time as WORD instead of modification\n\
- time: atime -u, access -u, use -u, ctime -c,\n\
- or status -c; use specified time as sort key\n\
- if --sort=time\n\
+ -S sort by file size, largest first\n\
+ --sort=WORD sort by WORD instead of name: none (-U), size (-S)\
+,\n\
+ time (-t), version (-v), extension (-X)\n\
+ --time=WORD with -l, show time as WORD instead of default\n\
+ modification time: atime or access or use (-u);\
+\n\
+ ctime or status (-c); also use specified time\n\
+ as sort key if --sort=time (newest first)\n\
"), stdout);
fputs (_("\
--time-style=STYLE with -l, show times using style STYLE:\n\
- full-iso, long-iso, iso, locale, +FORMAT.\n\
- FORMAT is interpreted like `date'; if FORMAT is\n\
- FORMAT1<newline>FORMAT2, FORMAT1 applies to\n\
- non-recent files and FORMAT2 to recent files;\n\
- if STYLE is prefixed with `posix-', STYLE\n\
- takes effect only outside the POSIX locale\n\
+ full-iso, long-iso, iso, locale, or +FORMAT;\n\
+ FORMAT is interpreted like in 'date'; if FORMAT\
+\n\
+ is FORMAT1<newline>FORMAT2, then FORMAT1 applies\
+\n\
+ to non-recent files and FORMAT2 to recent files;\
+\n\
+ if STYLE is prefixed with 'posix-', STYLE\n\
+ takes effect only outside the POSIX locale\n\
"), stdout);
fputs (_("\
- -t sort by modification time\n\
+ -t sort by modification time, newest first\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\n\
+ -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\
-U do not sort; list entries in directory order\n\
- -v sort by version\n\
+ -v natural sort of (version) numbers within text\n\
"), stdout);
fputs (_("\
- -w, --width=COLS assume screen width instead of current value\n\
+ -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\
- -1 list one file per line\n\
+ -Z, --context print any security context of each file\n\
+ -1 list one file per line. Avoid '\\n' with -q or -b\
+\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- fputs (_("\n\
-SIZE may be (or may be an integer optionally followed by) one of following:\n\
-kB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
-"), stdout);
+ emit_size_note ();
fputs (_("\
\n\
-By default, color is not used to distinguish types of files. That is\n\
-equivalent to using --color=none. Using the --color option without the\n\
-optional WHEN argument is equivalent to using --color=always. With\n\
---color=auto, color codes are output only if standard output is connected\n\
-to a terminal (tty). The environment variable LS_COLORS can influence the\n\
-colors, and can be set easily by the dircolors command.\n\
+Using color to distinguish file types is disabled both by default and\n\
+with --color=never. With --color=auto, ls emits color codes only when\n\
+standard output is connected to a terminal. The LS_COLORS environment\n\
+variable can change the settings. Use the dircolors command to set it.\n\
"), stdout);
fputs (_("\
\n\
-Exit status is 0 if OK, 1 if minor problems, 2 if serious trouble.\n\
+Exit status:\n\
+ 0 if OK,\n\
+ 1 if minor problems (e.g., cannot access subdirectory),\n\
+ 2 if serious trouble (e.g., cannot access command-line argument).\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
diff --git a/src/ls.h b/src/ls.h
index 028271f..b358211 100644
--- a/src/ls.h
+++ b/src/ls.h
@@ -1,10 +1,10 @@
-/* This is for the `ls' program. */
+/* This is for the 'ls' program. */
#define LS_LS 1
-/* This is for the `dir' program. */
+/* This is for the 'dir' program. */
#define LS_MULTI_COL 2
-/* This is for the `vdir' program. */
+/* This is for the 'vdir' program. */
#define LS_LONG_FORMAT 3
extern int ls_mode;
diff --git a/src/make-prime-list.c b/src/make-prime-list.c
new file mode 100644
index 0000000..27e534d
--- /dev/null
+++ b/src/make-prime-list.c
@@ -0,0 +1,230 @@
+/* Factoring of uintmax_t numbers. Generation of needed tables.
+
+ Contributed to the GNU project by Torbjörn Granlund and Niels Möller
+ Contains code from GNU MP.
+
+Copyright 2012-2016 Free Software Foundation, Inc.
+
+This 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/. */
+
+#include <config.h>
+
+#include <limits.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+/* Deactivate config.h's "rpl_"-prefixed definitions of these symbols. */
+#undef fclose
+#undef malloc
+#undef strerror
+
+/* An unsigned type that is no narrower than 32 bits and no narrower
+ than unsigned int. It's best to make it as wide as possible.
+ For GCC 4.6 and later, use a heuristic to guess whether unsigned
+ __int128 works on your platform. If this heuristic does not work
+ for you, please report a bug; in the meantime compile with, e.g.,
+ -Dwide_uint='unsigned __int128' to override the heuristic. */
+#ifndef wide_uint
+# if 4 < __GNUC__ + (6 <= __GNUC_MINOR__) && ULONG_MAX >> 31 >> 31 >> 1 != 0
+typedef unsigned __int128 wide_uint;
+# else
+typedef uintmax_t wide_uint;
+# endif
+#endif
+
+struct prime
+{
+ unsigned p;
+ wide_uint pinv; /* Inverse mod b = 2^{bitsize of wide_uint} */
+ wide_uint lim; /* floor ((wide_uint) -1 / p) */
+};
+
+static wide_uint _GL_ATTRIBUTE_CONST
+binvert (wide_uint a)
+{
+ wide_uint x = 0xf5397db1 >> (4*((a/2) & 0x7));
+ for (;;)
+ {
+ wide_uint y = 2*x - x*x*a;
+ if (y == x)
+ return x;
+ x = y;
+ }
+}
+
+static void
+process_prime (struct prime *info, unsigned p)
+{
+ wide_uint max = -1;
+ info->p = p;
+ info->pinv = binvert (p);
+ info->lim = max / p;
+}
+
+static void
+print_wide_uint (wide_uint n, int nesting, unsigned wide_uint_bits)
+{
+ /* Number of bits per integer literal. 8 is too many, because
+ uintmax_t is 32 bits on some machines so we cannot shift by 32 bits.
+ So use 7. */
+ int hex_digits_per_literal = 7;
+ int bits_per_literal = hex_digits_per_literal * 4;
+
+ unsigned remainder = n & ((1 << bits_per_literal) - 1);
+
+ if (n != remainder)
+ {
+ int needs_parentheses = n >> bits_per_literal >> bits_per_literal != 0;
+ if (needs_parentheses)
+ printf ("(");
+ print_wide_uint (n >> bits_per_literal, nesting + 1, wide_uint_bits);
+ if (needs_parentheses)
+ printf (")\n%*s", nesting + 3, "");
+ printf (" << %d | ", bits_per_literal);
+ }
+ else if (nesting)
+ {
+ printf ("(uintmax_t) ");
+ hex_digits_per_literal
+ = ((wide_uint_bits - 1) % bits_per_literal) % 4 + 1;
+ }
+
+ printf ("0x%0*xU", hex_digits_per_literal, remainder);
+}
+
+static void
+output_primes (const struct prime *primes, unsigned nprimes)
+{
+ unsigned i;
+ unsigned p;
+ int is_prime;
+
+ /* Compute wide_uint_bits by repeated shifting, rather than by
+ multiplying sizeof by CHAR_BIT, as this works even if the
+ wide_uint representation has holes. */
+ unsigned wide_uint_bits = 0;
+ wide_uint mask = -1;
+ for (wide_uint_bits = 0; mask; wide_uint_bits++)
+ mask >>= 1;
+
+ puts ("/* Generated file -- DO NOT EDIT */\n");
+ printf ("#define WIDE_UINT_BITS %u\n", wide_uint_bits);
+
+ for (i = 0, p = 2; i < nprimes; i++)
+ {
+ unsigned int d8 = i + 8 < nprimes ? primes[i + 8].p - primes[i].p : 0xff;
+ if (255 < d8) /* this happens at 668221 */
+ abort ();
+ printf ("P (%u, %u,\n (", primes[i].p - p, d8);
+ print_wide_uint (primes[i].pinv, 0, wide_uint_bits);
+ printf ("),\n UINTMAX_MAX / %u)\n", primes[i].p);
+ p = primes[i].p;
+ }
+
+ printf ("\n#undef FIRST_OMITTED_PRIME\n");
+
+ /* Find next prime */
+ do
+ {
+ p += 2;
+ for (i = 0, is_prime = 1; is_prime; i++)
+ {
+ if (primes[i].p * primes[i].p > p)
+ break;
+ if (p * primes[i].pinv <= primes[i].lim)
+ {
+ is_prime = 0;
+ break;
+ }
+ }
+ }
+ while (!is_prime);
+
+ printf ("#define FIRST_OMITTED_PRIME %u\n", p);
+}
+
+static void *
+xalloc (size_t s)
+{
+ void *p = malloc (s);
+ if (p)
+ return p;
+
+ fprintf (stderr, "Virtual memory exhausted.\n");
+ exit (EXIT_FAILURE);
+}
+
+int
+main (int argc, char **argv)
+{
+ int limit;
+
+ char *sieve;
+ size_t size, i;
+
+ struct prime *prime_list;
+ unsigned nprimes;
+
+ if (argc != 2)
+ {
+ fprintf (stderr, "Usage: %s LIMIT\n"
+ "Produces a list of odd primes <= LIMIT\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ limit = atoi (argv[1]);
+ if (limit < 3)
+ return EXIT_SUCCESS;
+
+ /* Make limit odd */
+ if ( !(limit & 1))
+ limit--;
+
+ size = (limit-1)/2;
+ /* sieve[i] represents 3+2*i */
+ sieve = xalloc (size);
+ memset (sieve, 1, size);
+
+ prime_list = xalloc (size * sizeof (*prime_list));
+ nprimes = 0;
+
+ for (i = 0; i < size;)
+ {
+ unsigned p = 3+2*i;
+ unsigned j;
+
+ process_prime (&prime_list[nprimes++], p);
+
+ for (j = (p*p - 3)/2; j < size; j+= p)
+ sieve[j] = 0;
+
+ while (++i < size && sieve[i] == 0)
+ ;
+ }
+
+ output_primes (prime_list, nprimes);
+
+ free (sieve);
+ free (prime_list);
+
+ if (ferror (stdout) + fclose (stdout))
+ {
+ fprintf (stderr, "write error: %s\n", strerror (errno));
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/md5sum.c b/src/md5sum.c
index a8ce1cf..39132e3 100644
--- a/src/md5sum.c
+++ b/src/md5sum.c
@@ -1,10 +1,10 @@
-/* Compute MD5, SHA1, SHA224, SHA256, SHA384 or SHA512 checksum of files or strings
- Copyright (C) 1995-2006 Free Software Foundation, Inc.
+/* Compute checksums of files or strings.
+ Copyright (C) 1995-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>. */
@@ -36,12 +35,12 @@
#if HASH_ALGO_SHA512 || HASH_ALGO_SHA384
# include "sha512.h"
#endif
-#include "getline.h"
#include "error.h"
-#include "quote.h"
+#include "fadvise.h"
#include "stdio--.h"
+#include "xfreopen.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#if HASH_ALGO_MD5
# define PROGRAM_NAME "md5sum"
# define DIGEST_TYPE_STRING "MD5"
@@ -91,13 +90,16 @@
#define DIGEST_HEX_BYTES (DIGEST_BITS / 4)
#define DIGEST_BIN_BYTES (DIGEST_BITS / 8)
-#define AUTHORS "Ulrich Drepper", "Scott Miller", "David Madore"
+#define AUTHORS \
+ proper_name ("Ulrich Drepper"), \
+ proper_name ("Scott Miller"), \
+ proper_name ("David Madore")
/* The minimum length of a valid digest line. This length does
not include any newline character at the end of a line. */
#define MIN_DIGEST_LINE_LENGTH \
(DIGEST_HEX_BYTES /* length of hexadecimal message digest */ \
- + 2 /* blank and binary indicator */ \
+ + 1 /* blank */ \
+ 1 /* minimum filename length */ )
/* True if any of the files read were the standard input. */
@@ -117,23 +119,41 @@ static bool status_only = false;
improperly formatted checksum line. */
static bool warn = false;
-/* The name this program was run with. */
-char *program_name;
+/* 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;
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
{
- STATUS_OPTION = CHAR_MAX + 1
+ IGNORE_MISSING_OPTION = CHAR_MAX + 1,
+ STATUS_OPTION,
+ QUIET_OPTION,
+ STRICT_OPTION,
+ TAG_OPTION
};
-static const struct option long_options[] =
+static struct option const long_options[] =
{
{ "binary", no_argument, NULL, 'b' },
{ "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 },
{ "text", no_argument, NULL, 't' },
{ "warn", no_argument, NULL, 'w' },
+ { "strict", no_argument, NULL, STRICT_OPTION },
+ { "tag", no_argument, NULL, TAG_OPTION },
{ GETOPT_HELP_OPTION_DECL },
{ GETOPT_VERSION_OPTION_DECL },
{ NULL, 0, NULL, 0 }
@@ -143,43 +163,51 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
-Usage: %s [OPTION] [FILE]...\n\
+Usage: %s [OPTION]... [FILE]...\n\
Print or check %s (%d-bit) checksums.\n\
-With no FILE, or when FILE is -, read standard input.\n\
-\n\
"),
- program_name,
- DIGEST_TYPE_STRING,
- DIGEST_BITS);
+ program_name,
+ DIGEST_TYPE_STRING,
+ DIGEST_BITS);
+
+ emit_stdin_note ();
+
if (O_BINARY)
- fputs (_("\
- -b, --binary read in binary mode (default unless reading tty stdin)\n\
+ fputs (_("\
+\n\
+ -b, --binary read in binary mode (default unless reading tty stdin)\n\
"), stdout);
else
- fputs (_("\
- -b, --binary read in binary mode\n\
+ fputs (_("\
+\n\
+ -b, --binary read in binary mode\n\
"), stdout);
printf (_("\
- -c, --check read %s sums from the FILEs and check them\n"),
- DIGEST_TYPE_STRING);
+ -c, --check read %s sums from the FILEs and check them\n"),
+ DIGEST_TYPE_STRING);
+ fputs (_("\
+ --tag create a BSD-style checksum\n\
+"), stdout);
if (O_BINARY)
- fputs (_("\
- -t, --text read in text mode (default if reading tty stdin)\n\
+ fputs (_("\
+ -t, --text read in text mode (default if reading tty stdin)\n\
"), stdout);
else
- fputs (_("\
- -t, --text read in text mode (default)\n\
+ fputs (_("\
+ -t, --text read in text mode (default)\n\
"), stdout);
fputs (_("\
\n\
-The following two options are useful only when verifying checksums:\n\
- --status don't output anything, status code shows success\n\
- -w, --warn warn about improperly formatted checksum lines\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);
fputs (HELP_OPTION_DESCRIPTION, stdout);
@@ -187,11 +215,11 @@ The following two options are useful only when verifying checksums:\n\
printf (_("\
\n\
The sums are computed as described in %s. When checking, the input\n\
-should be a former output of this program. The default mode is to print\n\
-a line with checksum, a character indicating type (`*' for binary, ` ' for\n\
-text), and name for each FILE.\n"),
- DIGEST_REFERENCE);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+should be a former output of this program. The default mode is to print a\n\
+line with checksum, a space, a character indicating input mode ('*' for binary,\
+\n' ' for text or where binary is insignificant), and name for each FILE.\n"),
+ DIGEST_REFERENCE);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -199,19 +227,73 @@ text), and name for each FILE.\n"),
#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/\\\\/\\/g' i.e., replacing each "\\n" string with a newline
+ 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, 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 NULL;
+ }
+ ++i;
+ switch (s[i])
+ {
+ case 'n':
+ *dst++ = '\n';
+ break;
+ case '\\':
+ *dst++ = '\\';
+ break;
+ default:
+ /* Only '\' or 'n' 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';
+
+ return s;
+}
+
/* 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. Return true if successful. */
static bool
-bsd_split_3 (char *s, size_t s_len, unsigned char **hex_digest, char **file_name)
+bsd_split_3 (char *s, size_t s_len, unsigned char **hex_digest,
+ char **file_name, bool escaped_filename)
{
size_t i;
- *file_name = s;
+ if (s_len == 0)
+ return false;
- /* Find end of filename. The BSD 'md5' and 'sha1' commands do not escape
- filenames, so search backwards for the last ')'. */
+ /* Find end of filename. */
i = s_len - 1;
while (i && s[i] != ')')
i--;
@@ -219,6 +301,11 @@ bsd_split_3 (char *s, size_t s_len, unsigned char **hex_digest, char **file_name
if (s[i] != ')')
return false;
+ *file_name = s;
+
+ if (escaped_filename && filename_unescape (s, i) == NULL)
+ return false;
+
s[i++] = '\0';
while (ISWHITE (s[i]))
@@ -242,41 +329,44 @@ bsd_split_3 (char *s, size_t s_len, unsigned char **hex_digest, char **file_name
static bool
split_3 (char *s, size_t s_len,
- unsigned char **hex_digest, int *binary, char **file_name)
+ unsigned char **hex_digest, int *binary, char **file_name)
{
- size_t i;
bool escaped_filename = false;
size_t algo_name_len;
- i = 0;
+ size_t i = 0;
while (ISWHITE (s[i]))
++i;
+ if (s[i] == '\\')
+ {
+ ++i;
+ escaped_filename = true;
+ }
+
/* Check for BSD-style checksum line. */
+
algo_name_len = strlen (DIGEST_TYPE_STRING);
- if (strncmp (s + i, DIGEST_TYPE_STRING, algo_name_len) == 0)
+ if (STREQ_LEN (s + i, DIGEST_TYPE_STRING, algo_name_len))
{
- if (strncmp (s + i + algo_name_len, " (", 2) == 0)
- {
- *binary = 0;
- return bsd_split_3 (s + i + algo_name_len + 2,
- s_len - (i + algo_name_len + 2),
- hex_digest, file_name);
- }
+ if (s[i + algo_name_len] == ' ')
+ ++i;
+ if (s[i + algo_name_len] == '(')
+ {
+ *binary = 0;
+ return bsd_split_3 (s + i + algo_name_len + 1,
+ s_len - (i + algo_name_len + 1),
+ hex_digest, file_name, escaped_filename);
+ }
}
/* Ignore this line if it is too short.
- Each line must have at least `min_digest_line_length - 1' (or one more, if
+ 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;
- if (s[i] == '\\')
- {
- ++i;
- escaped_filename = true;
- }
*hex_digest = (unsigned char *) &s[i];
/* The first field has to be the n-character hexadecimal
@@ -288,71 +378,79 @@ split_3 (char *s, size_t s_len,
s[i++] = '\0';
- if (s[i] != ' ' && s[i] != '*')
- return false;
- *binary = (s[i++] == '*');
+ /* 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)
- {
- /* Translate each `\n' string in the file name to a NEWLINE,
- and each `\\' string to a backslash. */
-
- char *dst = &s[i];
-
- while (i < s_len)
- {
- switch (s[i])
- {
- case '\\':
- if (i == s_len - 1)
- {
- /* A valid line does not end with a backslash. */
- return false;
- }
- ++i;
- switch (s[i++])
- {
- case 'n':
- *dst++ = '\n';
- break;
- case '\\':
- *dst++ = '\\';
- break;
- default:
- /* Only `\' or `n' may follow a backslash. */
- return false;
- }
- break;
-
- case '\0':
- /* The file name may not contain a NUL. */
- return false;
- break;
-
- default:
- *dst++ = s[i++];
- break;
- }
- }
- *dst = '\0';
- }
+ return filename_unescape (&s[i], s_len - i) != NULL;
+
return true;
}
-static bool
+/* Return true if S is a NUL-terminated string of DIGEST_HEX_BYTES hex digits.
+ Otherwise, return false. */
+static bool _GL_ATTRIBUTE_PURE
hex_digits (unsigned char const *s)
{
- while (*s)
+ unsigned int i;
+ for (i = 0; i < digest_hex_bytes; i++)
{
if (!isxdigit (*s))
return false;
++s;
}
- return true;
+ return *s == '\0';
+}
+
+/* If ESCAPE is true, then translate each NEWLINE byte to the string, "\\n",
+ 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 '\\':
+ fputs ("\\\\", stdout);
+ break;
+
+ default:
+ putchar (*file);
+ break;
+ }
+ file++;
+ }
}
/* An interface to the function, DIGEST_STREAM.
@@ -378,35 +476,42 @@ digest_file (const char *filename, int *binary, unsigned char *bin_result)
have_read_stdin = true;
fp = stdin;
if (O_BINARY && *binary)
- {
- if (*binary < 0)
- *binary = ! isatty (STDIN_FILENO);
- if (*binary)
- freopen (NULL, "rb", stdin);
- }
+ {
+ if (*binary < 0)
+ *binary = ! isatty (STDIN_FILENO);
+ if (*binary)
+ xfreopen (NULL, "rb", stdin);
+ }
}
else
{
fp = fopen (filename, (O_BINARY && *binary ? "rb" : "r"));
if (fp == NULL)
- {
- error (0, errno, "%s", filename);
- return false;
- }
+ {
+ if (ignore_missing && errno == ENOENT)
+ {
+ *bin_result = '\0';
+ return true;
+ }
+ error (0, errno, "%s", quotef (filename));
+ return false;
+ }
}
+ fadvise (fp, FADVISE_SEQUENTIAL);
+
err = DIGEST_STREAM (fp, bin_result);
if (err)
{
- error (0, errno, "%s", filename);
+ error (0, errno, "%s", quotef (filename));
if (fp != stdin)
- fclose (fp);
+ fclose (fp);
return false;
}
if (!is_stdin && fclose (fp) != 0)
{
- error (0, errno, "%s", filename);
+ error (0, errno, "%s", quotef (filename));
return false;
}
@@ -417,9 +522,12 @@ static bool
digest_check (const char *checkfile_name)
{
FILE *checkfile_stream;
- uintmax_t n_properly_formatted_lines = 0;
+ uintmax_t n_misformatted_lines = 0;
+ uintmax_t n_improperly_formatted_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);
@@ -438,10 +546,10 @@ digest_check (const char *checkfile_name)
{
checkfile_stream = fopen (checkfile_name, "r");
if (checkfile_stream == NULL)
- {
- error (0, errno, "%s", checkfile_name);
- return false;
- }
+ {
+ error (0, errno, "%s", quotef (checkfile_name));
+ return false;
+ }
}
line_number = 0;
@@ -449,87 +557,114 @@ digest_check (const char *checkfile_name)
line_chars_allocated = 0;
do
{
- char *filename;
+ char *filename IF_LINT ( = NULL);
int binary;
- unsigned char *hex_digest IF_LINT (= NULL);
+ unsigned char *hex_digest IF_LINT ( = NULL);
ssize_t line_length;
++line_number;
if (line_number == 0)
- error (EXIT_FAILURE, 0, _("%s: too many checksum lines"),
- checkfile_name);
+ 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;
+ break;
/* Ignore comment lines, which begin with a '#' character. */
if (line[0] == '#')
- continue;
+ continue;
/* Remove any trailing newline. */
if (line[line_length - 1] == '\n')
- line[--line_length] = '\0';
+ line[--line_length] = '\0';
if (! (split_3 (line, line_length, &hex_digest, &binary, &filename)
- && ! (is_stdin && STREQ (filename, "-"))
- && hex_digits (hex_digest)))
- {
- if (warn)
- {
- error (0, 0,
- _("%s: %" PRIuMAX
- ": improperly formatted %s checksum line"),
- checkfile_name, line_number,
- DIGEST_TYPE_STRING);
- }
- }
+ && ! (is_stdin && STREQ (filename, "-"))
+ && hex_digits (hex_digest)))
+ {
+ ++n_misformatted_lines;
+
+ if (warn)
+ {
+ error (0, 0,
+ _("%s: %" PRIuMAX
+ ": improperly formatted %s checksum line"),
+ quotef (checkfile_name), line_number,
+ DIGEST_TYPE_STRING);
+ }
+
+ ++n_improperly_formatted_lines;
+ }
else
- {
- static const char bin2hex[] = { '0', '1', '2', '3',
- '4', '5', '6', '7',
- '8', '9', 'a', 'b',
- 'c', 'd', 'e', 'f' };
- bool ok;
-
- ++n_properly_formatted_lines;
-
- ok = digest_file (filename, &binary, bin_buffer);
-
- if (!ok)
- {
- ++n_open_or_read_failures;
- if (!status_only)
- {
- printf (_("%s: FAILED open or read\n"), filename);
- fflush (stdout);
- }
- }
- else
- {
- size_t digest_bin_bytes = digest_hex_bytes / 2;
- size_t cnt;
- /* Compare generated binary number with text representation
- in check file. Ignore case of hex digits. */
- for (cnt = 0; cnt < digest_bin_bytes; ++cnt)
- {
- if (tolower (hex_digest[2 * cnt])
- != bin2hex[bin_buffer[cnt] >> 4]
- || (tolower (hex_digest[2 * cnt + 1])
- != (bin2hex[bin_buffer[cnt] & 0xf])))
- break;
- }
- if (cnt != digest_bin_bytes)
- ++n_mismatched_checksums;
-
- if (!status_only)
- {
- printf ("%s: %s\n", filename,
- (cnt != digest_bin_bytes ? _("FAILED") : _("OK")));
- fflush (stdout);
- }
- }
- }
+ {
+ static const char bin2hex[] = { '0', '1', '2', '3',
+ '4', '5', '6', '7',
+ '8', '9', 'a', 'b',
+ 'c', 'd', 'e', 'f' };
+ bool ok;
+ /* Only escape in the edge case producing multiple lines,
+ to ease automatic processing of status output. */
+ bool needs_escape = ! status_only && strchr (filename, '\n');
+
+ properly_formatted_lines = true;
+
+ *bin_buffer = '\1'; /* flag set to 0 for ignored missing files. */
+ ok = digest_file (filename, &binary, bin_buffer);
+
+ 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 && ! *bin_buffer)
+ {
+ /* Treat an empty buffer as meaning a missing file,
+ which is ignored with --ignore-missing. */
+ ;
+ }
+ else
+ {
+ size_t digest_bin_bytes = digest_hex_bytes / 2;
+ size_t cnt;
+
+ /* Compare generated binary number with text representation
+ in check file. Ignore case of hex digits. */
+ for (cnt = 0; cnt < digest_bin_bytes; ++cnt)
+ {
+ if (tolower (hex_digest[2 * cnt])
+ != bin2hex[bin_buffer[cnt] >> 4]
+ || (tolower (hex_digest[2 * cnt + 1])
+ != (bin2hex[bin_buffer[cnt] & 0xf])))
+ break;
+ }
+ if (cnt != digest_bin_bytes)
+ ++n_mismatched_checksums;
+ else
+ matched_checksums = true;
+
+ if (!status_only)
+ {
+ if (cnt != digest_bin_bytes || ! quiet)
+ {
+ if (needs_escape)
+ putchar ('\\');
+ print_filename (filename, needs_escape);
+ }
+
+ if (cnt != digest_bin_bytes)
+ printf (": %s\n", _("FAILED"));
+ else if (!quiet)
+ printf (": %s\n", _("OK"));
+ }
+ }
+ }
}
while (!feof (checkfile_stream) && !ferror (checkfile_stream));
@@ -537,53 +672,61 @@ digest_check (const char *checkfile_name)
if (ferror (checkfile_stream))
{
- error (0, 0, _("%s: read error"), checkfile_name);
+ error (0, 0, _("%s: read error"), quotef (checkfile_name));
return false;
}
if (!is_stdin && fclose (checkfile_stream) != 0)
{
- error (0, errno, "%s", checkfile_name);
+ error (0, errno, "%s", quotef (checkfile_name));
return false;
}
- if (n_properly_formatted_lines == 0)
+ if (! properly_formatted_lines)
{
/* Warn if no tests are found. */
error (0, 0, _("%s: no properly formatted %s checksum lines found"),
- checkfile_name, DIGEST_TYPE_STRING);
+ quotef (checkfile_name), DIGEST_TYPE_STRING);
}
else
{
if (!status_only)
- {
- if (n_open_or_read_failures != 0)
- error (0, 0,
- ngettext ("WARNING: %" PRIuMAX " of %" PRIuMAX
- " listed file could not be read",
- "WARNING: %" PRIuMAX " of %" PRIuMAX
- " listed files could not be read",
- select_plural (n_properly_formatted_lines)),
- n_open_or_read_failures, n_properly_formatted_lines);
-
- if (n_mismatched_checksums != 0)
- {
- uintmax_t n_computed_checksums =
- (n_properly_formatted_lines - n_open_or_read_failures);
- error (0, 0,
- ngettext ("WARNING: %" PRIuMAX " of %" PRIuMAX
- " computed checksum did NOT match",
- "WARNING: %" PRIuMAX " of %" PRIuMAX
- " computed checksums did NOT match",
- select_plural (n_computed_checksums)),
- n_mismatched_checksums, n_computed_checksums);
- }
- }
+ {
+ if (n_misformatted_lines != 0)
+ error (0, 0,
+ (ngettext
+ ("WARNING: %" PRIuMAX " line is improperly formatted",
+ "WARNING: %" PRIuMAX " lines are improperly formatted",
+ select_plural (n_misformatted_lines))),
+ n_misformatted_lines);
+
+ if (n_open_or_read_failures != 0)
+ error (0, 0,
+ (ngettext
+ ("WARNING: %" PRIuMAX " listed file could not be read",
+ "WARNING: %" PRIuMAX " 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: %" PRIuMAX " computed checksum did NOT match",
+ "WARNING: %" PRIuMAX " 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 (n_properly_formatted_lines != 0
- && n_mismatched_checksums == 0
- && n_open_or_read_failures == 0);
+ return (properly_formatted_lines
+ && matched_checksums
+ && n_mismatched_checksums == 0
+ && n_open_or_read_failures == 0
+ && (!strict || n_improperly_formatted_lines == 0));
}
int
@@ -596,128 +739,194 @@ main (int argc, char **argv)
int opt;
bool ok = true;
int binary = -1;
+ bool prefix_tag = false;
/* Setting values of global variables. */
initialize_main (&argc, &argv);
- program_name = argv[0];
+ 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);
+
while ((opt = getopt_long (argc, argv, "bctw", long_options, NULL)) != -1)
switch (opt)
{
case 'b':
- binary = 1;
- break;
+ binary = 1;
+ break;
case 'c':
- do_check = true;
- break;
+ do_check = true;
+ break;
case STATUS_OPTION:
- status_only = true;
- warn = false;
- break;
+ status_only = true;
+ warn = false;
+ quiet = false;
+ break;
case 't':
- binary = 0;
- break;
+ binary = 0;
+ break;
case 'w':
- status_only = false;
- warn = true;
- break;
+ 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;
+ case TAG_OPTION:
+ prefix_tag = true;
+ binary = 1;
+ break;
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
- usage (EXIT_FAILURE);
+ usage (EXIT_FAILURE);
}
min_digest_line_length = MIN_DIGEST_LINE_LENGTH;
digest_hex_bytes = DIGEST_HEX_BYTES;
+ 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. */
+ error (0, 0, _("--tag does not support --text mode"));
+ usage (EXIT_FAILURE);
+ }
+
+ if (prefix_tag && do_check)
+ {
+ error (0, 0, _("the --tag option is meaningless when "
+ "verifying checksums"));
+ usage (EXIT_FAILURE);
+ }
+
if (0 <= binary && do_check)
{
error (0, 0, _("the --binary and --text options are meaningless when "
- "verifying checksums"));
+ "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)
+ if (status_only && !do_check)
{
error (0, 0,
_("the --status option is meaningful only when verifying checksums"));
usage (EXIT_FAILURE);
}
- if (warn & !do_check)
+ 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;
if (optind == argc)
- argv[argc++] = "-";
+ argv[argc++] = bad_cast ("-");
for (; optind < argc; ++optind)
{
char *file = argv[optind];
if (do_check)
- ok &= digest_check (file);
+ ok &= digest_check (file);
else
- {
- int file_is_binary = binary;
-
- if (! digest_file (file, &file_is_binary, bin_buffer))
- ok = false;
- else
- {
- size_t i;
-
- /* Output a leading backslash if the file name contains
- a newline or backslash. */
- if (strchr (file, '\n') || strchr (file, '\\'))
- putchar ('\\');
-
- for (i = 0; i < (digest_hex_bytes / 2); ++i)
- printf ("%02x", bin_buffer[i]);
-
- putchar (' ');
- if (file_is_binary)
- putchar ('*');
- else
- putchar (' ');
-
- /* Translate each NEWLINE byte to the string, "\\n",
- and each backslash to "\\\\". */
- for (i = 0; i < strlen (file); ++i)
- {
- switch (file[i])
- {
- case '\n':
- fputs ("\\n", stdout);
- break;
-
- case '\\':
- fputs ("\\\\", stdout);
- break;
-
- default:
- putchar (file[i]);
- break;
- }
- }
- putchar ('\n');
- }
- }
+ {
+ int file_is_binary = binary;
+
+ if (! digest_file (file, &file_is_binary, bin_buffer))
+ ok = false;
+ else
+ {
+ /* We don't really need to escape, and hence detect, the '\\'
+ char, and not doing so should be both forwards and backwards
+ compatible, since only escaped lines would have a '\\' char at
+ the start. However just in case users are directly comparing
+ against old (hashed) outputs, in the presence of files
+ containing '\\' characters, we decided to not simplify the
+ output in this case. */
+ bool needs_escape = strchr (file, '\\') || strchr (file, '\n');
+
+ if (prefix_tag)
+ {
+ if (needs_escape)
+ putchar ('\\');
+
+ fputs (DIGEST_TYPE_STRING, stdout);
+ fputs (" (", stdout);
+ print_filename (file, needs_escape);
+ fputs (") = ", stdout);
+ }
+
+ size_t i;
+
+ /* Output a leading backslash if the file name contains
+ a newline or backslash. */
+ if (!prefix_tag && needs_escape)
+ putchar ('\\');
+
+ for (i = 0; i < (digest_hex_bytes / 2); ++i)
+ printf ("%02x", bin_buffer[i]);
+
+ if (!prefix_tag)
+ {
+ putchar (' ');
+
+ putchar (file_is_binary ? '*' : ' ');
+
+ print_filename (file, needs_escape);
+ }
+
+ putchar ('\n');
+ }
+ }
}
if (have_read_stdin && fclose (stdin) == EOF)
error (EXIT_FAILURE, errno, _("standard input"));
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/mkdir.c b/src/mkdir.c
index 6fa0ac2..60fc08a 100644
--- a/src/mkdir.c
+++ b/src/mkdir.c
@@ -1,10 +1,10 @@
/* mkdir -- make directories
- Copyright (C) 90, 1995-2002, 2004, 2005, 2006 Free Software Foundation, Inc.
+ Copyright (C) 1990-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* David MacKenzie <djm@ai.mit.edu> */
@@ -21,25 +20,26 @@
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
+#include <selinux/selinux.h>
#include "system.h"
#include "error.h"
-#include "lchmod.h"
#include "mkdir-p.h"
#include "modechange.h"
+#include "prog-fprintf.h"
#include "quote.h"
#include "savewd.h"
+#include "selinux.h"
+#include "smack.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "mkdir"
-#define AUTHORS "David MacKenzie"
-
-/* The name this program was run with. */
-char *program_name;
+#define AUTHORS proper_name ("David MacKenzie")
static struct option const longopts[] =
{
+ {GETOPT_SELINUX_CONTEXT_OPTION_DECL},
{"mode", required_argument, NULL, 'm'},
{"parents", no_argument, NULL, 'p'},
{"verbose", no_argument, NULL, 'v'},
@@ -52,26 +52,30 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
- printf (_("Usage: %s [OPTION] DIRECTORY...\n"), program_name);
+ printf (_("Usage: %s [OPTION]... DIRECTORY...\n"), program_name);
fputs (_("\
Create the DIRECTORY(ies), if they do not already exist.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
+
+ 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\
-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);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -83,8 +87,8 @@ struct mkdir_options
made. */
int (*make_ancestor_function) (char const *, char const *, void *);
- /* Mode for ancestor directory. */
- mode_t ancestor_mode;
+ /* Umask value in effect. */
+ mode_t umask_value;
/* Mode for directory itself. */
mode_t mode;
@@ -92,6 +96,9 @@ struct mkdir_options
/* File mode bits affected by MODE. */
mode_t mode_bits;
+ /* Set the SELinux File Context. */
+ bool set_security_context;
+
/* If not null, format to use when reporting newly made directories. */
char const *created_directory_format;
};
@@ -102,7 +109,7 @@ announce_mkdir (char const *dir, void *options)
{
struct mkdir_options const *o = options;
if (o->created_directory_format)
- error (0, 0, o->created_directory_format, quote (dir));
+ prog_fprintf (stdout, o->created_directory_format, quoteaf (dir));
}
/* Make ancestor directory DIR, whose last component is COMPONENT,
@@ -114,10 +121,26 @@ static int
make_ancestor (char const *dir, char const *component, void *options)
{
struct mkdir_options const *o = options;
- int r = mkdir (component, o->ancestor_mode);
+
+ if (o->set_security_context && defaultcon (dir, S_IFDIR) < 0
+ && ! ignorable_ctx_err (errno))
+ error (0, errno, _("failed to set default creation context for %s"),
+ quoteaf (dir));
+
+ mode_t user_wx = S_IWUSR | S_IXUSR;
+ bool self_denying_umask = (o->umask_value & user_wx) != 0;
+ if (self_denying_umask)
+ umask (o->umask_value & ~user_wx);
+ int r = mkdir (component, S_IRWXUGO);
+ if (self_denying_umask)
+ {
+ int mkdir_errno = errno;
+ umask (o->umask_value);
+ errno = mkdir_errno;
+ }
if (r == 0)
{
- r = ! (o->ancestor_mode & S_IRUSR);
+ r = (o->umask_value & S_IRUSR) != 0;
announce_mkdir (dir, options);
}
return r;
@@ -128,11 +151,37 @@ static int
process_dir (char *dir, struct savewd *wd, void *options)
{
struct mkdir_options const *o = options;
- return (make_dir_parents (dir, wd, o->make_ancestor_function, options,
- o->mode, announce_mkdir,
- o->mode_bits, (uid_t) -1, (gid_t) -1, true)
- ? EXIT_SUCCESS
- : EXIT_FAILURE);
+
+ /* If possible set context before DIR created. */
+ if (o->set_security_context)
+ {
+ if (! o->make_ancestor_function && defaultcon (dir, S_IFDIR) < 0
+ && ! ignorable_ctx_err (errno))
+ error (0, errno, _("failed to set default creation context for %s"),
+ quoteaf (dir));
+ }
+
+ int ret = (make_dir_parents (dir, wd, o->make_ancestor_function, options,
+ o->mode, announce_mkdir,
+ o->mode_bits, (uid_t) -1, (gid_t) -1, true)
+ ? EXIT_SUCCESS
+ : EXIT_FAILURE);
+
+ /* FIXME: Due to the current structure of make_dir_parents()
+ we don't have the facility to call defaultcon() before the
+ final component of DIR is created. So for now, create the
+ final component with the context from previous component
+ and here we set the context for the final component. */
+ if (ret == EXIT_SUCCESS && o->set_security_context
+ && o->make_ancestor_function)
+ {
+ if (! restorecon (last_component (dir), false, false)
+ && ! ignorable_ctx_err (errno))
+ error (0, errno, _("failed to restore context for %s"),
+ quoteaf (dir));
+ }
+
+ return ret;
}
int
@@ -140,38 +189,61 @@ main (int argc, char **argv)
{
const char *specified_mode = NULL;
int optc;
+ char const *scontext = NULL;
struct mkdir_options options;
+
options.make_ancestor_function = NULL;
options.mode = S_IRWXUGO;
options.mode_bits = 0;
options.created_directory_format = NULL;
+ options.set_security_context = false;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "pm:v", longopts, NULL)) != -1)
+ while ((optc = getopt_long (argc, argv, "pm:vZ", longopts, NULL)) != -1)
{
switch (optc)
- {
- case 'p':
- options.make_ancestor_function = make_ancestor;
- break;
- case 'm':
- specified_mode = optarg;
- break;
- case 'v': /* --verbose */
- options.created_directory_format = _("created directory %s");
- break;
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'p':
+ options.make_ancestor_function = make_ancestor;
+ break;
+ case 'm':
+ specified_mode = optarg;
+ break;
+ case 'v': /* --verbose */
+ options.created_directory_format = _("created directory %s");
+ break;
+ case 'Z':
+ if (is_smack_enabled ())
+ {
+ /* We don't yet support -Z to restore context with SMACK. */
+ scontext = optarg;
+ }
+ else if (is_selinux_enabled () > 0)
+ {
+ if (optarg)
+ scontext = optarg;
+ else
+ options.set_security_context = true;
+ }
+ else if (optarg)
+ {
+ error (0, 0,
+ _("warning: ignoring --context; "
+ "it requires an SELinux/SMACK-enabled kernel"));
+ }
+ break;
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
}
if (optind == argc)
@@ -180,26 +252,44 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
+ /* FIXME: This assumes mkdir() is done in the same process.
+ If that's not always the case we would need to call this
+ like we do when options.set_security_context == true. */
+ if (scontext)
+ {
+ int ret = 0;
+ if (is_smack_enabled ())
+ ret = smack_set_label_for_self (scontext);
+ else
+ ret = setfscreatecon (se_const (scontext));
+
+ if (ret < 0)
+ error (EXIT_FAILURE, errno,
+ _("failed to set default file creation context to %s"),
+ quote (scontext));
+ }
+
+
if (options.make_ancestor_function || specified_mode)
{
mode_t umask_value = umask (0);
-
- options.ancestor_mode = (S_IRWXUGO & ~umask_value) | (S_IWUSR | S_IXUSR);
+ umask (umask_value);
+ options.umask_value = umask_value;
if (specified_mode)
- {
- struct mode_change *change = mode_compile (specified_mode);
- if (!change)
- error (EXIT_FAILURE, 0, _("invalid mode %s"),
- quote (specified_mode));
- options.mode = mode_adjust (S_IRWXUGO, true, umask_value, change,
- &options.mode_bits);
- free (change);
- }
+ {
+ struct mode_change *change = mode_compile (specified_mode);
+ if (!change)
+ error (EXIT_FAILURE, 0, _("invalid mode %s"),
+ quote (specified_mode));
+ options.mode = mode_adjust (S_IRWXUGO, true, umask_value, change,
+ &options.mode_bits);
+ free (change);
+ }
else
- options.mode = S_IRWXUGO & ~umask_value;
+ options.mode = S_IRWXUGO;
}
- exit (savewd_process_files (argc - optind, argv + optind,
- process_dir, &options));
+ return savewd_process_files (argc - optind, argv + optind,
+ process_dir, &options);
}
diff --git a/src/mkfifo.c b/src/mkfifo.c
index d329b79..5136173 100644
--- a/src/mkfifo.c
+++ b/src/mkfifo.c
@@ -1,10 +1,10 @@
/* mkfifo -- make fifo's (named pipes)
- Copyright (C) 90, 91, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1990-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* David MacKenzie <djm@ai.mit.edu> */
@@ -21,22 +20,23 @@
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
+#include <selinux/selinux.h>
#include "system.h"
#include "error.h"
#include "modechange.h"
#include "quote.h"
+#include "selinux.h"
+#include "smack.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "mkfifo"
-#define AUTHORS "David MacKenzie"
-
-/* The name this program was run with. */
-char *program_name;
+#define AUTHORS proper_name ("David MacKenzie")
static struct option const longopts[] =
{
+ {GETOPT_SELINUX_CONTEXT_OPTION_DECL},
{"mode", required_argument, NULL, 'm'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
@@ -47,24 +47,27 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
- printf (_("Usage: %s [OPTION] NAME...\n"), program_name);
+ printf (_("Usage: %s [OPTION]... NAME...\n"), program_name);
fputs (_("\
Create named pipes (FIFOs) with the given NAMEs.\n\
-\n\
"), stdout);
+
+ emit_mandatory_arg_note ();
+
fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
+ -m, --mode=MODE set file permission bits to MODE, not a=rw - umask\n\
"), stdout);
fputs (_("\
- -m, --mode=MODE set file permission bits to MODE, not a=rw - umask\n\
+ -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);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -76,27 +79,49 @@ main (int argc, char **argv)
char const *specified_mode = NULL;
int exit_status = EXIT_SUCCESS;
int optc;
+ char const *scontext = NULL;
+ bool set_security_context = false;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "m:", longopts, NULL)) != -1)
+ while ((optc = getopt_long (argc, argv, "m:Z", longopts, NULL)) != -1)
{
switch (optc)
- {
- case 'm':
- specified_mode = optarg;
- break;
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'm':
+ specified_mode = optarg;
+ break;
+ case 'Z':
+ if (is_smack_enabled ())
+ {
+ /* We don't yet support -Z to restore context with SMACK. */
+ scontext = optarg;
+ }
+ else if (is_selinux_enabled () > 0)
+ {
+ if (optarg)
+ scontext = optarg;
+ else
+ set_security_context = true;
+ }
+ else if (optarg)
+ {
+ error (0, 0,
+ _("warning: ignoring --context; "
+ "it requires an SELinux/SMACK-enabled kernel"));
+ }
+ break;
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
}
if (optind == argc)
@@ -105,25 +130,52 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
- newmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+ if (scontext)
+ {
+ int ret = 0;
+ if (is_smack_enabled ())
+ ret = smack_set_label_for_self (scontext);
+ else
+ ret = setfscreatecon (se_const (scontext));
+
+ if (ret < 0)
+ error (EXIT_FAILURE, errno,
+ _("failed to set default file creation context to %s"),
+ quote (scontext));
+ }
+
+ 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"));
- newmode = mode_adjust (newmode, false, umask (0), change, NULL);
+ error (EXIT_FAILURE, 0, _("invalid mode"));
+ umask_value = umask (0);
+ umask (umask_value);
+ 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"));
+ error (EXIT_FAILURE, 0,
+ _("mode must specify only file permission bits"));
}
for (; optind < argc; ++optind)
- if (mkfifo (argv[optind], newmode) != 0)
- {
- error (0, errno, _("cannot create fifo %s"), quote (argv[optind]));
- exit_status = EXIT_FAILURE;
- }
+ {
+ if (set_security_context)
+ defaultcon (argv[optind], S_IFIFO);
+ if (mkfifo (argv[optind], newmode) != 0)
+ {
+ error (0, errno, _("cannot create fifo %s"), quoteaf (argv[optind]));
+ exit_status = EXIT_FAILURE;
+ }
+ else if (specified_mode && lchmod (argv[optind], newmode) != 0)
+ {
+ error (0, errno, _("cannot set permissions of %s"),
+ quoteaf (argv[optind]));
+ exit_status = EXIT_FAILURE;
+ }
+ }
- exit (exit_status);
+ return exit_status;
}
diff --git a/src/mknod.c b/src/mknod.c
index 43a1b9d..42bdf90 100644
--- a/src/mknod.c
+++ b/src/mknod.c
@@ -1,10 +1,10 @@
/* mknod -- make special files
- Copyright (C) 90, 91, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1990-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by David MacKenzie <djm@ai.mit.edu> */
@@ -21,23 +20,24 @@
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
+#include <selinux/selinux.h>
#include "system.h"
#include "error.h"
#include "modechange.h"
#include "quote.h"
+#include "selinux.h"
+#include "smack.h"
#include "xstrtol.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "mknod"
-#define AUTHORS "David MacKenzie"
-
-/* The name this program was run with. */
-char *program_name;
+#define AUTHORS proper_name ("David MacKenzie")
static struct option const longopts[] =
{
+ {GETOPT_SELINUX_CONTEXT_OPTION_DECL},
{"mode", required_argument, NULL, 'm'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
@@ -48,21 +48,24 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("Usage: %s [OPTION]... NAME TYPE [MAJOR MINOR]\n"),
- program_name);
+ program_name);
fputs (_("\
Create the special file NAME of the given TYPE.\n\
-\n\
"), stdout);
+
+ emit_mandatory_arg_note ();
+
fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
+ -m, --mode=MODE set file permission bits to MODE, not a=rw - umask\n\
"), stdout);
fputs (_("\
- -m, --mode=MODE set file permission bits to MODE, not a=rw - umask\n\
+ -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);
@@ -79,7 +82,8 @@ otherwise, as decimal. TYPE may be:\n\
c, u create a character (unbuffered) special file\n\
p create a FIFO\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -90,79 +94,118 @@ main (int argc, char **argv)
mode_t newmode;
char const *specified_mode = NULL;
int optc;
- int expected_operands;
+ size_t expected_operands;
mode_t node_type;
+ char const *scontext = NULL;
+ bool set_security_context = false;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "m:", longopts, NULL)) != -1)
+ while ((optc = getopt_long (argc, argv, "m:Z", longopts, NULL)) != -1)
{
switch (optc)
- {
- case 'm':
- specified_mode = optarg;
- break;
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'm':
+ specified_mode = optarg;
+ break;
+ case 'Z':
+ if (is_smack_enabled ())
+ {
+ /* We don't yet support -Z to restore context with SMACK. */
+ scontext = optarg;
+ }
+ else if (is_selinux_enabled () > 0)
+ {
+ if (optarg)
+ scontext = optarg;
+ else
+ set_security_context = true;
+ }
+ else if (optarg)
+ {
+ error (0, 0,
+ _("warning: ignoring --context; "
+ "it requires an SELinux/SMACK-enabled kernel"));
+ }
+ break;
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
}
- newmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+ 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"));
- newmode = mode_adjust (newmode, false, umask (0), change, NULL);
+ error (EXIT_FAILURE, 0, _("invalid mode"));
+ umask_value = umask (0);
+ umask (umask_value);
+ 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"));
+ error (EXIT_FAILURE, 0,
+ _("mode must specify only file permission bits"));
}
/* 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
+ 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);
+ || (optind + 1 < argc && argv[optind + 1][0] == 'p')
+ ? 2 : 4);
if (argc - optind < expected_operands)
{
if (argc <= optind)
- error (0, 0, _("missing operand"));
+ error (0, 0, _("missing operand"));
else
- error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
+ error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
if (expected_operands == 4 && argc - optind == 2)
- fprintf (stderr, "%s\n",
- _("Special files require major and minor device numbers."));
+ fprintf (stderr, "%s\n",
+ _("Special files require major and minor device numbers."));
usage (EXIT_FAILURE);
}
if (expected_operands < argc - optind)
{
error (0, 0, _("extra operand %s"),
- quote (argv[optind + expected_operands]));
+ quote (argv[optind + expected_operands]));
if (expected_operands == 2 && argc - optind == 4)
- fprintf (stderr, "%s\n",
- _("Fifos do not have major and minor device numbers."));
+ fprintf (stderr, "%s\n",
+ _("Fifos do not have major and minor device numbers."));
usage (EXIT_FAILURE);
}
+ if (scontext)
+ {
+ int ret = 0;
+ if (is_smack_enabled ())
+ ret = smack_set_label_for_self (scontext);
+ else
+ ret = setfscreatecon (se_const (scontext));
+
+ if (ret < 0)
+ error (EXIT_FAILURE, errno,
+ _("failed to set default file creation context to %s"),
+ quote (scontext));
+ }
+
/* Only check the first character, to allow mnemonic usage like
- `mknod /dev/rst0 character 18 0'. */
+ 'mknod /dev/rst0 character 18 0'. */
switch (argv[optind + 1][0])
{
- case 'b': /* `block' or `buffered' */
+ case 'b': /* 'block' or 'buffered' */
#ifndef S_IFBLK
error (EXIT_FAILURE, 0, _("block special files not supported"));
#else
@@ -170,8 +213,8 @@ main (int argc, char **argv)
#endif
goto block_or_character;
- case 'c': /* `character' */
- case 'u': /* `unbuffered' */
+ case 'c': /* 'character' */
+ case 'u': /* 'unbuffered' */
#ifndef S_IFCHR
error (EXIT_FAILURE, 0, _("character special files not supported"));
#else
@@ -181,35 +224,41 @@ 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, NULL, 0, &i_major, NULL) != LONGINT_OK
- || i_major != (major_t) i_major)
- error (EXIT_FAILURE, 0,
- _("invalid major device number %s"), quote (s_major));
-
- if (xstrtoumax (s_minor, NULL, 0, &i_minor, NULL) != 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);
+ 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, NULL, 0, &i_major, NULL) != LONGINT_OK
+ || i_major != (major_t) i_major)
+ error (EXIT_FAILURE, 0,
+ _("invalid major device number %s"), quote (s_major));
+
+ if (xstrtoumax (s_minor, NULL, 0, &i_minor, NULL) != 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);
#ifdef NODEV
- if (device == NODEV)
- error (EXIT_FAILURE, 0, _("invalid device %s %s"), s_major, s_minor);
+ if (device == NODEV)
+ error (EXIT_FAILURE, 0, _("invalid device %s %s"),
+ s_major, s_minor);
#endif
- if (mknod (argv[optind], newmode | node_type, device) != 0)
- error (EXIT_FAILURE, errno, "%s", quote (argv[optind]));
+ if (set_security_context)
+ defaultcon (argv[optind], node_type);
+
+ if (mknod (argv[optind], newmode | node_type, device) != 0)
+ error (EXIT_FAILURE, errno, "%s", quotef (argv[optind]));
}
break;
- case 'p': /* `pipe' */
+ case 'p': /* 'pipe' */
+ if (set_security_context)
+ defaultcon (argv[optind], S_IFIFO);
if (mkfifo (argv[optind], newmode) != 0)
- error (EXIT_FAILURE, errno, "%s", quote (argv[optind]));
+ error (EXIT_FAILURE, errno, "%s", quotef (argv[optind]));
break;
default:
@@ -217,5 +266,9 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
- exit (EXIT_SUCCESS);
+ if (specified_mode && lchmod (argv[optind], newmode) != 0)
+ error (EXIT_FAILURE, errno, _("cannot set permissions of %s"),
+ quoteaf (argv[optind]));
+
+ return EXIT_SUCCESS;
}
diff --git a/src/mktemp.c b/src/mktemp.c
new file mode 100644
index 0000000..d2e126d
--- /dev/null
+++ b/src/mktemp.c
@@ -0,0 +1,349 @@
+/* Create a temporary file or directory, safely.
+ Copyright (C) 2007-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Written by Jim Meyering and Eric Blake. */
+
+#include <config.h>
+#include <sys/types.h>
+#include <getopt.h>
+
+#include "system.h"
+
+#include "close-stream.h"
+#include "error.h"
+#include "filenamecat.h"
+#include "quote.h"
+#include "tempname.h"
+
+/* The official name of this program (e.g., no 'g' prefix). */
+#define PROGRAM_NAME "mktemp"
+
+#define AUTHORS \
+ proper_name ("Jim Meyering"), \
+ proper_name ("Eric Blake")
+
+static const char *default_template = "tmp.XXXXXXXXXX";
+
+/* For long options that have no equivalent short option, use a
+ non-character as a pseudo short option, starting with CHAR_MAX + 1. */
+enum
+{
+ SUFFIX_OPTION = CHAR_MAX + 1,
+};
+
+static struct option const longopts[] =
+{
+ {"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},
+ {NULL, 0, NULL, 0}
+};
+
+void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ emit_try_help ();
+ else
+ {
+ printf (_("Usage: %s [OPTION]... [TEMPLATE]\n"), program_name);
+ fputs (_("\
+Create a temporary file or directory, safely, and print its name.\n\
+TEMPLATE must contain at least 3 consecutive 'X's in last component.\n\
+If TEMPLATE is not specified, use tmp.XXXXXXXXXX, and --tmpdir is implied.\n\
+"), stdout);
+ fputs (_("\
+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);
+ emit_ancillary_info (PROGRAM_NAME);
+ }
+
+ exit (status);
+}
+
+static size_t
+count_consecutive_X_s (const char *s, size_t len)
+{
+ size_t n = 0;
+ for ( ; len && s[len-1] == 'X'; len--)
+ ++n;
+ return n;
+}
+
+static int
+mkstemp_len (char *tmpl, size_t suff_len, size_t x_len, bool dry_run)
+{
+ return gen_tempname_len (tmpl, suff_len, 0, dry_run ? GT_NOCREATE : GT_FILE,
+ x_len);
+}
+
+static int
+mkdtemp_len (char *tmpl, size_t suff_len, size_t x_len, bool dry_run)
+{
+ return gen_tempname_len (tmpl, suff_len, 0, dry_run ? GT_NOCREATE : GT_DIR,
+ x_len);
+}
+
+/* True if we have already closed standard output. */
+static bool stdout_closed;
+
+/* Avoid closing stdout twice. Since we conditionally call
+ close_stream (stdout) in order to decide whether to clean up a
+ temporary file, the exit hook needs to know whether to do all of
+ close_stdout or just the stderr half. */
+static void
+maybe_close_stdout (void)
+{
+ if (!stdout_closed)
+ close_stdout ();
+ else if (close_stream (stderr) != 0)
+ _exit (EXIT_FAILURE);
+}
+
+int
+main (int argc, char **argv)
+{
+ char const *dest_dir;
+ char const *dest_dir_arg = NULL;
+ bool suppress_file_err = false;
+ int c;
+ unsigned int n_args;
+ char *template;
+ char *suffix = NULL;
+ bool use_dest_dir = false;
+ bool deprecated_t_option = false;
+ bool create_directory = false;
+ bool dry_run = false;
+ int status = EXIT_SUCCESS;
+ size_t x_count;
+ size_t suffix_len;
+ char *dest_name;
+
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ atexit (maybe_close_stdout);
+
+ while ((c = getopt_long (argc, argv, "dp:qtuV", longopts, NULL)) != -1)
+ {
+ switch (c)
+ {
+ case 'd':
+ create_directory = true;
+ break;
+ case 'p':
+ dest_dir_arg = optarg;
+ use_dest_dir = true;
+ break;
+ case 'q':
+ suppress_file_err = true;
+ break;
+ case 't':
+ use_dest_dir = true;
+ deprecated_t_option = true;
+ break;
+ case 'u':
+ dry_run = true;
+ break;
+
+ case SUFFIX_OPTION:
+ suffix = optarg;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case 'V': /* Undocumented alias, for compatibility with the original
+ mktemp program. */
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
+ }
+
+ n_args = argc - optind;
+ if (2 <= n_args)
+ {
+ error (0, 0, _("too many templates"));
+ usage (EXIT_FAILURE);
+ }
+
+ if (n_args == 0)
+ {
+ use_dest_dir = true;
+ template = (char *) default_template;
+ }
+ else
+ {
+ template = argv[optind];
+ }
+
+ if (suffix)
+ {
+ size_t len = strlen (template);
+ if (!len || template[len - 1] != 'X')
+ {
+ error (EXIT_FAILURE, 0,
+ _("with --suffix, template %s must end in X"),
+ quote (template));
+ }
+ suffix_len = strlen (suffix);
+ dest_name = xcharalloc (len + suffix_len + 1);
+ memcpy (dest_name, template, len);
+ memcpy (dest_name + len, suffix, suffix_len + 1);
+ template = dest_name;
+ suffix = dest_name + len;
+ }
+ else
+ {
+ template = xstrdup (template);
+ suffix = strrchr (template, 'X');
+ if (!suffix)
+ suffix = strchr (template, '\0');
+ else
+ suffix++;
+ suffix_len = strlen (suffix);
+ }
+
+ /* At this point, template is malloc'd, and suffix points into template. */
+ if (suffix_len && last_component (suffix) != suffix)
+ {
+ error (EXIT_FAILURE, 0,
+ _("invalid suffix %s, contains directory separator"),
+ quote (suffix));
+ }
+ x_count = count_consecutive_X_s (template, suffix - template);
+ if (x_count < 3)
+ error (EXIT_FAILURE, 0, _("too few X's in template %s"), quote (template));
+
+ if (use_dest_dir)
+ {
+ if (deprecated_t_option)
+ {
+ char *env = getenv ("TMPDIR");
+ if (env && *env)
+ dest_dir = env;
+ else if (dest_dir_arg && *dest_dir_arg)
+ dest_dir = dest_dir_arg;
+ else
+ dest_dir = "/tmp";
+
+ if (last_component (template) != template)
+ error (EXIT_FAILURE, 0,
+ _("invalid template, %s, contains directory separator"),
+ quote (template));
+ }
+ else
+ {
+ if (dest_dir_arg && *dest_dir_arg)
+ dest_dir = dest_dir_arg;
+ else
+ {
+ char *env = getenv ("TMPDIR");
+ dest_dir = (env && *env ? env : "/tmp");
+ }
+ if (IS_ABSOLUTE_FILE_NAME (template))
+ error (EXIT_FAILURE, 0,
+ _("invalid template, %s; with --tmpdir,"
+ " it may not be absolute"),
+ quote (template));
+ }
+
+ dest_name = file_name_concat (dest_dir, template, NULL);
+ free (template);
+ template = dest_name;
+ /* Note that suffix is now invalid. */
+ }
+
+ /* Make a copy to be used in case of diagnostic, since failing
+ mkstemp may leave the buffer in an undefined state. */
+ dest_name = xstrdup (template);
+
+ if (create_directory)
+ {
+ int err = mkdtemp_len (dest_name, suffix_len, x_count, dry_run);
+ if (err != 0)
+ {
+ if (!suppress_file_err)
+ error (0, errno, _("failed to create directory via template %s"),
+ quote (template));
+ status = EXIT_FAILURE;
+ }
+ }
+ else
+ {
+ int fd = mkstemp_len (dest_name, suffix_len, x_count, dry_run);
+ if (fd < 0 || (!dry_run && close (fd) != 0))
+ {
+ if (!suppress_file_err)
+ error (0, errno, _("failed to create file via template %s"),
+ quote (template));
+ status = EXIT_FAILURE;
+ }
+ }
+
+ if (status == EXIT_SUCCESS)
+ {
+ puts (dest_name);
+ /* If we created a file, but then failed to output the file
+ name, we should clean up the mess before failing. */
+ if (!dry_run && ((stdout_closed = true), close_stream (stdout) != 0))
+ {
+ int saved_errno = errno;
+ remove (dest_name);
+ if (!suppress_file_err)
+ error (0, saved_errno, _("write error"));
+ status = EXIT_FAILURE;
+ }
+ }
+
+#ifdef lint
+ free (dest_name);
+ free (template);
+#endif
+
+ return status;
+}
diff --git a/src/mv.c b/src/mv.c
index 1d1ddda..9539b41 100644
--- a/src/mv.c
+++ b/src/mv.c
@@ -1,10 +1,10 @@
/* mv -- move or rename files
- Copyright (C) 86, 89, 90, 91, 1995-2007 Free Software Foundation, Inc.
+ Copyright (C) 1986-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Mike Parker, David MacKenzie, and Jim Meyering */
@@ -22,62 +21,44 @@
#include <getopt.h>
#include <sys/types.h>
#include <assert.h>
+#include <selinux/selinux.h>
#include "system.h"
-#include "argmatch.h"
#include "backupfile.h"
#include "copy.h"
#include "cp-hash.h"
#include "error.h"
#include "filenamecat.h"
-#include "quote.h"
#include "remove.h"
+#include "root-dev-ino.h"
+#include "priv-set.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "mv"
-#define AUTHORS "Mike Parker", "David MacKenzie", "Jim Meyering"
-
-/* Initial number of entries in each hash table entry's table of inodes. */
-#define INITIAL_HASH_MODULE 100
-
-/* Initial number of entries in the inode hash table. */
-#define INITIAL_ENTRY_TAB_SIZE 70
+#define AUTHORS \
+ proper_name ("Mike Parker"), \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Jim Meyering")
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
{
- REPLY_OPTION = CHAR_MAX + 1,
- STRIP_TRAILING_SLASHES_OPTION
+ STRIP_TRAILING_SLASHES_OPTION = CHAR_MAX + 1
};
-/* The name this program was run with. */
-char *program_name;
-
/* Remove any trailing slashes from each SOURCE argument. */
static bool remove_trailing_slashes;
-/* Valid arguments to the `--reply' option. */
-static char const* const reply_args[] =
-{
- "yes", "no", "query", NULL
-};
-
-/* The values that correspond to the above strings. */
-static int const reply_vals[] =
-{
- I_ALWAYS_YES, I_ALWAYS_NO, I_ASK_USER
-};
-
static struct option const long_options[] =
{
{"backup", optional_argument, NULL, 'b'},
+ {"context", no_argument, NULL, 'Z'},
{"force", no_argument, NULL, 'f'},
{"interactive", no_argument, NULL, 'i'},
+ {"no-clobber", no_argument, NULL, 'n'},
{"no-target-directory", no_argument, NULL, 'T'},
- {"reply", required_argument, NULL, REPLY_OPTION}, /* Deprecated 2005-07-03,
- remove in 2008. */
{"strip-trailing-slashes", no_argument, NULL, STRIP_TRAILING_SLASHES_OPTION},
{"suffix", required_argument, NULL, 'S'},
{"target-directory", required_argument, NULL, 't'},
@@ -92,11 +73,11 @@ static void
rm_option_init (struct rm_options *x)
{
x->ignore_missing_files = false;
- x->root_dev_ino = NULL;
+ x->remove_empty_directories = true;
x->recursive = true;
x->one_file_system = false;
- /* Should we prompt for removal, too? No. Prompting for the `move'
+ /* Should we prompt for removal, too? No. Prompting for the 'move'
part is enough. It implies removal. */
x->interactive = RMI_NEVER;
x->stdin_tty = false;
@@ -104,29 +85,48 @@ rm_option_init (struct rm_options *x)
x->verbose = false;
/* Since this program may well have to process additional command
- line arguments after any call to `rm', that function must preserve
+ line arguments after any call to 'rm', that function must preserve
the initial working directory, in case one of those is a
- `.'-relative name. */
+ '.'-relative name. */
x->require_restore_cwd = true;
+
+ {
+ static struct dev_ino dev_ino_buf;
+ x->root_dev_ino = get_root_dev_ino (&dev_ino_buf);
+ if (x->root_dev_ino == NULL)
+ error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
+ quoteaf ("/"));
+ }
}
static void
cp_option_init (struct cp_options *x)
{
+ bool selinux_enabled = (0 < is_selinux_enabled ());
+
+ cp_options_default (x);
x->copy_as_regular = false; /* FIXME: maybe make this an option */
+ x->reflink_mode = REFLINK_AUTO;
x->dereference = DEREF_NEVER;
x->unlink_dest_before_opening = false;
x->unlink_dest_after_failed_open = false;
x->hard_link = false;
x->interactive = I_UNSPECIFIED;
x->move_mode = true;
- x->chown_privileges = chown_privileges ();
x->one_file_system = false;
x->preserve_ownership = true;
x->preserve_links = true;
x->preserve_mode = true;
x->preserve_timestamps = true;
+ x->explicit_no_preserve_mode= false;
+ x->preserve_security_context = selinux_enabled;
+ x->set_security_context = false;
+ x->reduce_diagnostics = false;
+ x->data_copy_required = true;
x->require_preserve = false; /* FIXME: maybe make this an option */
+ x->require_preserve_context = false;
+ x->preserve_xattr = true;
+ x->require_preserve_xattr = false;
x->recursive = true;
x->sparse_mode = SPARSE_AUTO; /* FIXME: maybe make this an option */
x->symbolic_link = false;
@@ -134,6 +134,7 @@ cp_option_init (struct cp_options *x)
x->mode = 0;
x->stdin_tty = isatty (STDIN_FILENO);
+ x->open_dangling_dest_symlink = false;
x->update = false;
x->verbose = false;
x->dest_info = NULL;
@@ -151,7 +152,7 @@ target_directory_operand (char const *file)
int err = (stat (file, &st) == 0 ? 0 : errno);
bool is_a_dir = !err && S_ISDIR (st.st_mode);
if (err && err != ENOENT)
- error (EXIT_FAILURE, err, _("accessing %s"), quote (file));
+ error (EXIT_FAILURE, err, _("failed to access %s"), quoteaf (file));
return is_a_dir;
}
@@ -170,67 +171,70 @@ do_move (const char *source, const char *dest, const struct cp_options *x)
{
char const *dir_to_remove;
if (copy_into_self)
- {
- /* In general, when copy returns with copy_into_self set, SOURCE is
- the same as, or a parent of DEST. In this case we know it's a
- parent. It doesn't make sense to move a directory into itself, and
- besides in some situations doing so would give highly nonintuitive
- results. Run this `mkdir b; touch a c; mv * b' in an empty
- directory. Here's the result of running echo `find b -print`:
- b b/a b/b b/b/a b/c. Notice that only file `a' was copied
- into b/b. Handle this by giving a diagnostic, removing the
- copied-into-self directory, DEST (`b/b' in the example),
- and failing. */
-
- dir_to_remove = NULL;
- ok = false;
- }
+ {
+ /* In general, when copy returns with copy_into_self set, SOURCE is
+ the same as, or a parent of DEST. In this case we know it's a
+ parent. It doesn't make sense to move a directory into itself, and
+ besides in some situations doing so would give highly nonintuitive
+ results. Run this 'mkdir b; touch a c; mv * b' in an empty
+ directory. Here's the result of running echo $(find b -print):
+ b b/a b/b b/b/a b/c. Notice that only file 'a' was copied
+ into b/b. Handle this by giving a diagnostic, removing the
+ copied-into-self directory, DEST ('b/b' in the example),
+ and failing. */
+
+ 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 = NULL;
- }
+ {
+ /* No need to remove anything. SOURCE was successfully
+ renamed to DEST. Or the user declined to rename a file. */
+ dir_to_remove = NULL;
+ }
else
- {
- /* This may mean SOURCE and DEST referred to different devices.
- It may also conceivably mean that even though they referred
- to the same device, rename wasn't implemented for that device.
+ {
+ /* This may mean SOURCE and DEST referred to different devices.
+ It may also conceivably mean that even though they referred
+ to the same device, rename wasn't implemented for that device.
- E.g., (from Joel N. Weber),
- [...] there might someday be cases where you can't rename
- but you can copy where the device name is the same, especially
- on Hurd. Consider an ftpfs with a primitive ftp server that
- supports uploading, downloading and deleting, but not renaming.
+ E.g., (from Joel N. Weber),
+ [...] there might someday be cases where you can't rename
+ but you can copy where the device name is the same, especially
+ on Hurd. Consider an ftpfs with a primitive ftp server that
+ supports uploading, downloading and deleting, but not renaming.
- Also, note that comparing device numbers is not a reliable
- check for `can-rename'. Some systems can be set up so that
- files from many different physical devices all have the same
- st_dev field. This is a feature of some NFS mounting
- configurations.
+ Also, note that comparing device numbers is not a reliable
+ check for 'can-rename'. Some systems can be set up so that
+ files from many different physical devices all have the same
+ st_dev field. This is a feature of some NFS mounting
+ configurations.
- We reach this point if SOURCE has been successfully copied
- to DEST. Now we have to remove SOURCE.
+ We reach this point if SOURCE has been successfully copied
+ to DEST. Now we have to remove SOURCE.
- This function used to resort to copying only when rename
- failed and set errno to EXDEV. */
+ This function used to resort to copying only when rename
+ failed and set errno to EXDEV. */
- dir_to_remove = source;
- }
+ dir_to_remove = source;
+ }
if (dir_to_remove != NULL)
- {
- struct rm_options rm_options;
- enum RM_status status;
-
- rm_option_init (&rm_options);
- rm_options.verbose = x->verbose;
-
- status = rm (1, &dir_to_remove, &rm_options);
- assert (VALID_STATUS (status));
- if (status == RM_ERROR)
- ok = false;
- }
+ {
+ 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] = NULL;
+
+ status = rm ((void*) dir, &rm_options);
+ assert (VALID_STATUS (status));
+ if (status == RM_ERROR)
+ ok = false;
+ }
}
return ok;
@@ -242,15 +246,15 @@ do_move (const char *source, const char *dest, const struct cp_options *x)
static bool
movefile (char *source, char *dest, bool dest_is_dir,
- const struct cp_options *x)
+ const struct cp_options *x)
{
bool ok;
/* This code was introduced to handle the ambiguity in the semantics
of mv that is induced by the varying semantics of the rename function.
- Some systems (e.g., Linux) have a rename function that honors a
+ Some systems (e.g., GNU/Linux) have a rename function that honors a
trailing slash, while others (like Solaris 5,6,7) have a rename
- function that ignores a trailing slash. I believe the Linux
+ function that ignores a trailing slash. I believe the GNU/Linux
rename semantics are POSIX and susv2 compliant. */
if (remove_trailing_slashes)
@@ -277,8 +281,7 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
@@ -286,19 +289,21 @@ Usage: %s [OPTION]... [-T] SOURCE DEST\n\
or: %s [OPTION]... SOURCE... DIRECTORY\n\
or: %s [OPTION]... -t DIRECTORY SOURCE...\n\
"),
- program_name, program_name, program_name);
+ program_name, program_name, program_name);
fputs (_("\
Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
+
+ emit_mandatory_arg_note ();
+
fputs (_("\
- --backup[=CONTROL] make a backup of each existing destination file\n\
+ --backup[=CONTROL] make a backup of each existing destination file\
+\n\
-b like --backup but does not accept an argument\n\
-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 (_("\
--strip-trailing-slashes remove any trailing slashes from each SOURCE\n\
@@ -312,12 +317,14 @@ Mandatory arguments to long options are mandatory for short options too.\n\
than the destination file or when the\n\
destination file is missing\n\
-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);
fputs (_("\
\n\
-The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
+The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
The version control method may be selected via the --backup option or through\n\
the VERSION_CONTROL environment variable. Here are the values:\n\
\n\
@@ -328,7 +335,7 @@ the VERSION_CONTROL environment variable. Here are the values:\n\
existing, nil numbered if numbered backups exist, simple otherwise\n\
simple, never always make simple backups\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -346,78 +353,89 @@ main (int argc, char **argv)
bool no_target_directory = false;
int n_files;
char **file;
+ bool selinux_enabled = (0 < is_selinux_enabled ());
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
- atexit (close_stdout);
+ atexit (close_stdin);
cp_option_init (&x);
+ /* Try to disable the ability to unlink a directory. */
+ priv_set_remove_linkdir ();
+
/* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
we'll actually use backup_suffix_string. */
backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
- while ((c = getopt_long (argc, argv, "bfit:uvS:T", long_options, NULL))
- != -1)
+ while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, NULL))
+ != -1)
{
switch (c)
- {
- case 'b':
- make_backups = true;
- if (optarg)
- version_control_string = optarg;
- break;
- case 'f':
- x.interactive = I_ALWAYS_YES;
- break;
- case 'i':
- x.interactive = I_ASK_USER;
- break;
- case REPLY_OPTION: /* Deprecated */
- x.interactive = XARGMATCH ("--reply", optarg,
- reply_args, reply_vals);
- error (0, 0,
- _("the --reply option is deprecated; use -i or -f instead"));
- break;
- case STRIP_TRAILING_SLASHES_OPTION:
- remove_trailing_slashes = true;
- break;
- case 't':
- if (target_directory)
- error (EXIT_FAILURE, 0, _("multiple target directories specified"));
- else
- {
- struct stat st;
- if (stat (optarg, &st) != 0)
- error (EXIT_FAILURE, errno, _("accessing %s"), quote (optarg));
- if (! S_ISDIR (st.st_mode))
- error (EXIT_FAILURE, 0, _("target %s is not a directory"),
- quote (optarg));
- }
- target_directory = optarg;
- break;
- case 'T':
- no_target_directory = true;
- break;
- case 'u':
- x.update = true;
- break;
- case 'v':
- x.verbose = true;
- break;
- case 'S':
- make_backups = true;
- backup_suffix_string = optarg;
- break;
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'b':
+ make_backups = true;
+ if (optarg)
+ version_control_string = optarg;
+ break;
+ case 'f':
+ x.interactive = I_ALWAYS_YES;
+ break;
+ case 'i':
+ x.interactive = I_ASK_USER;
+ break;
+ case 'n':
+ x.interactive = I_ALWAYS_NO;
+ break;
+ case STRIP_TRAILING_SLASHES_OPTION:
+ remove_trailing_slashes = true;
+ break;
+ case 't':
+ if (target_directory)
+ error (EXIT_FAILURE, 0, _("multiple target directories specified"));
+ else
+ {
+ struct stat st;
+ if (stat (optarg, &st) != 0)
+ error (EXIT_FAILURE, errno, _("failed to access %s"),
+ quoteaf (optarg));
+ if (! S_ISDIR (st.st_mode))
+ error (EXIT_FAILURE, 0, _("target %s is not a directory"),
+ quoteaf (optarg));
+ }
+ target_directory = optarg;
+ break;
+ case 'T':
+ no_target_directory = true;
+ break;
+ case 'u':
+ x.update = true;
+ break;
+ case 'v':
+ x.verbose = true;
+ break;
+ case 'S':
+ make_backups = true;
+ backup_suffix_string = optarg;
+ break;
+ case 'Z':
+ /* As a performance enhancement, don't even bother trying
+ to "restorecon" when not on an selinux-enabled kernel. */
+ if (selinux_enabled)
+ {
+ x.preserve_security_context = false;
+ x.set_security_context = true;
+ }
+ break;
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
}
n_files = argc - optind;
@@ -426,42 +444,49 @@ main (int argc, char **argv)
if (n_files <= !target_directory)
{
if (n_files <= 0)
- error (0, 0, _("missing file operand"));
+ error (0, 0, _("missing file operand"));
else
- error (0, 0, _("missing destination file operand after %s"),
- quote (file[0]));
+ error (0, 0, _("missing destination file operand after %s"),
+ quoteaf (file[0]));
usage (EXIT_FAILURE);
}
if (no_target_directory)
{
if (target_directory)
- error (EXIT_FAILURE, 0,
- _("Cannot combine --target-directory (-t) "
- "and --no-target-directory (-T)"));
+ error (EXIT_FAILURE, 0,
+ _("cannot combine --target-directory (-t) "
+ "and --no-target-directory (-T)"));
if (2 < n_files)
- {
- error (0, 0, _("extra operand %s"), quote (file[2]));
- usage (EXIT_FAILURE);
- }
+ {
+ error (0, 0, _("extra operand %s"), quoteaf (file[2]));
+ usage (EXIT_FAILURE);
+ }
}
else if (!target_directory)
{
assert (2 <= n_files);
if (target_directory_operand (file[n_files - 1]))
- target_directory = file[--n_files];
+ target_directory = file[--n_files];
else if (2 < n_files)
- error (EXIT_FAILURE, 0, _("target %s is not a directory"),
- quote (file[n_files - 1]));
+ error (EXIT_FAILURE, 0, _("target %s is not a directory"),
+ quoteaf (file[n_files - 1]));
+ }
+
+ if (make_backups && x.interactive == I_ALWAYS_NO)
+ {
+ error (0, 0,
+ _("options --backup and --no-clobber are mutually exclusive"));
+ usage (EXIT_FAILURE);
}
if (backup_suffix_string)
simple_backup_suffix = xstrdup (backup_suffix_string);
x.backup_type = (make_backups
- ? xget_version (_("backup type"),
- version_control_string)
- : no_backups);
+ ? xget_version (_("backup type"),
+ version_control_string)
+ : no_backups);
hash_init ();
@@ -470,17 +495,17 @@ main (int argc, char **argv)
int i;
/* Initialize the hash table only if we'll need it.
- The problem it is used to detect can arise only if there are
- two or more files to move. */
+ The problem it is used to detect can arise only if there are
+ two or more files to move. */
if (2 <= n_files)
- dest_info_init (&x);
+ dest_info_init (&x);
ok = true;
for (i = 0; i < n_files; ++i)
- ok &= movefile (file[i], target_directory, true, &x);
+ ok &= movefile (file[i], target_directory, true, &x);
}
else
ok = movefile (file[0], file[1], false, &x);
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/nice.c b/src/nice.c
index 19b38bc..a145f53 100644
--- a/src/nice.c
+++ b/src/nice.c
@@ -1,10 +1,10 @@
/* nice -- run a program with modified niceness
- Copyright (C) 1990-2005 Free Software Foundation, Inc.
+ Copyright (C) 1990-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* David MacKenzie <djm@gnu.ai.mit.edu> */
@@ -31,14 +30,13 @@
#endif
#include "error.h"
-#include "long-options.h"
#include "quote.h"
#include "xstrtol.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "nice"
-#define AUTHORS "David MacKenzie"
+#define AUTHORS proper_name ("David MacKenzie")
#if HAVE_NICE
# define GET_NICENESS() nice (0)
@@ -56,12 +54,11 @@
# define NZERO 20
#endif
-/* The name this program was run with. */
-char *program_name;
-
static struct option const longopts[] =
{
{"adjustment", required_argument, NULL, 'n'},
+ {GETOPT_HELP_OPTION_DECL},
+ {GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
};
@@ -69,27 +66,36 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("Usage: %s [OPTION] [COMMAND [ARG]...]\n"), program_name);
printf (_("\
Run COMMAND with an adjusted niceness, which affects process scheduling.\n\
-With no COMMAND, print the current niceness. Nicenesses range from\n\
-%d (most favorable scheduling) to %d (least favorable).\n\
-\n\
- -n, --adjustment=N add integer N to the niceness (default 10)\n\
+With no COMMAND, print the current niceness. Niceness values range from\n\
+%d (most favorable to the process) to %d (least favorable to the process).\n\
"),
- - NZERO, NZERO - 1);
+ - NZERO, NZERO - 1);
+
+ 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);
printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
+static bool
+perm_related_errno (int err)
+{
+ return err == EACCES || err == EPERM;
+}
+
int
main (int argc, char **argv)
{
@@ -100,77 +106,88 @@ main (int argc, char **argv)
int i;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
- initialize_exit_failure (EXIT_FAIL);
+ initialize_exit_failure (EXIT_CANCELED);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
-
for (i = 1; i < argc; /* empty */)
{
char const *s = argv[i];
if (s[0] == '-' && ISDIGIT (s[1 + (s[1] == '-' || s[1] == '+')]))
- {
- adjustment_given = s + 1;
- ++i;
- }
+ {
+ adjustment_given = s + 1;
+ ++i;
+ }
else
- {
- int optc;
- int fake_argc = argc - (i - 1);
- char **fake_argv = argv + (i - 1);
-
- /* Ensure that any getopt diagnostics use the right name. */
- fake_argv[0] = program_name;
-
- /* Initialize getopt_long's internal state. */
- optind = 0;
-
- optc = getopt_long (fake_argc, fake_argv, "+n:", longopts, NULL);
- i += optind - 1;
-
- if (optc == '?')
- usage (EXIT_FAIL);
- else if (optc == 'n')
- adjustment_given = optarg;
- else /* optc == -1 */
- break;
- }
+ {
+ int c;
+ int fake_argc = argc - (i - 1);
+ char **fake_argv = argv + (i - 1);
+
+ /* Ensure that any getopt diagnostics use the right name. */
+ fake_argv[0] = argv[0];
+
+ /* Initialize getopt_long's internal state. */
+ optind = 0;
+
+ c = getopt_long (fake_argc, fake_argv, "+n:", longopts, NULL);
+ i += optind - 1;
+
+ switch (c)
+ {
+ case 'n':
+ adjustment_given = optarg;
+ break;
+
+ case -1:
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_CANCELED);
+ break;
+ }
+
+ if (c == -1)
+ break;
+ }
}
if (adjustment_given)
{
/* If the requested adjustment is outside the valid range,
- silently bring it to just within range; this mimics what
- "setpriority" and "nice" do. */
+ silently bring it to just within range; this mimics what
+ "setpriority" and "nice" do. */
enum { MIN_ADJUSTMENT = 1 - 2 * NZERO, MAX_ADJUSTMENT = 2 * NZERO - 1 };
long int tmp;
if (LONGINT_OVERFLOW < xstrtol (adjustment_given, NULL, 10, &tmp, ""))
- error (EXIT_FAIL, 0, _("invalid adjustment %s"),
- quote (adjustment_given));
+ error (EXIT_CANCELED, 0, _("invalid adjustment %s"),
+ quote (adjustment_given));
adjustment = MAX (MIN_ADJUSTMENT, MIN (tmp, MAX_ADJUSTMENT));
}
if (i == argc)
{
if (adjustment_given)
- {
- error (0, 0, _("a command must be given with an adjustment"));
- usage (EXIT_FAIL);
- }
+ {
+ error (0, 0, _("a command must be given with an adjustment"));
+ usage (EXIT_CANCELED);
+ }
/* No command given; print the niceness. */
errno = 0;
current_niceness = GET_NICENESS ();
if (current_niceness == -1 && errno != 0)
- error (EXIT_FAIL, errno, _("cannot get niceness"));
+ error (EXIT_CANCELED, errno, _("cannot get niceness"));
printf ("%d\n", current_niceness);
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
errno = 0;
@@ -179,17 +196,25 @@ main (int argc, char **argv)
#else
current_niceness = GET_NICENESS ();
if (current_niceness == -1 && errno != 0)
- error (EXIT_FAIL, errno, _("cannot get niceness"));
+ error (EXIT_CANCELED, errno, _("cannot get niceness"));
ok = (setpriority (PRIO_PROCESS, 0, current_niceness + adjustment) == 0);
#endif
if (!ok)
- error (errno == EPERM ? 0 : EXIT_FAIL, errno, _("cannot set niceness"));
+ {
+ error (perm_related_errno (errno) ? 0
+ : EXIT_CANCELED, errno, _("cannot set niceness"));
+ /* error() flushes stderr, but does not check for write failure.
+ Normally, we would catch this via our atexit() hook of
+ close_stdout, but execvp() gets in the way. If stderr
+ encountered a write failure, there is no need to try calling
+ error() again. */
+ if (ferror (stderr))
+ return EXIT_CANCELED;
+ }
execvp (argv[i], &argv[i]);
- {
- int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
- error (0, errno, "%s", argv[i]);
- exit (exit_status);
- }
+ int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
+ error (0, errno, "%s", quote (argv[i]));
+ return exit_status;
}
diff --git a/src/nl.c b/src/nl.c
index 04c8118..a4a48bc 100644
--- a/src/nl.c
+++ b/src/nl.c
@@ -1,10 +1,10 @@
/* nl -- number lines of files
- Copyright (C) 89, 92, 1995-2007 Free Software Foundation, Inc.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,9 +12,8 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
-
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
/* Written by Scott Bartram (nancy!scott@uunet.uu.net)
Revised by David MacKenzie (djm@gnu.ai.mit.edu) */
@@ -29,14 +28,17 @@
#include <regex.h>
#include "error.h"
+#include "fadvise.h"
#include "linebuffer.h"
#include "quote.h"
-#include "xstrtol.h"
+#include "xdectoint.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "nl"
-#define AUTHORS "Scott Bartram", "David MacKenzie"
+#define AUTHORS \
+ proper_name ("Scott Bartram"), \
+ proper_name ("David MacKenzie")
/* Line-number formats. They are given an int width, an intmax_t
value, and a string separator. */
@@ -60,9 +62,6 @@ enum section
Header, Body, Footer, Text
};
-/* The name this program was run with. */
-char *program_name;
-
/* Format of body lines (-b). */
static char const *body_type = "t";
@@ -152,7 +151,7 @@ static struct option const longopts[] =
{"body-numbering", required_argument, NULL, 'b'},
{"footer-numbering", required_argument, NULL, 'f'},
{"starting-line-number", required_argument, NULL, 'v'},
- {"page-increment", required_argument, NULL, 'i'},
+ {"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'},
@@ -170,22 +169,20 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
- program_name);
+ program_name);
fputs (_("\
Write each FILE to standard output, with line numbers added.\n\
-With no FILE, or when FILE is -, read standard input.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
+
+ emit_stdin_note ();
+ emit_mandatory_arg_note ();
+
fputs (_("\
-b, --body-numbering=STYLE use STYLE for numbering body lines\n\
-d, --section-delimiter=CC use CC for separating logical pages\n\
@@ -193,14 +190,14 @@ Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
fputs (_("\
-h, --header-numbering=STYLE use STYLE for numbering header lines\n\
- -i, --page-increment=NUMBER line number increment at each line\n\
+ -i, --line-increment=NUMBER line number increment at each line\n\
-l, --join-blank-lines=NUMBER group of NUMBER empty lines counted as one\n\
-n, --number-format=FORMAT insert line numbers according to FORMAT\n\
-p, --no-renumber do not reset line numbers at logical pages\n\
-s, --number-separator=STRING add STRING after (possible) line number\n\
"), stdout);
fputs (_("\
- -v, --first-page=NUMBER first line number on each logical page\n\
+ -v, --starting-line-number=NUMBER first line number on each logical page\n\
-w, --number-width=NUMBER use NUMBER columns for line numbers\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
@@ -226,17 +223,17 @@ FORMAT is one of:\n\
rz right justified, leading zeros\n\
\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
/* Set the command line flag TYPEP and possibly the regex pointer REGEXP,
- according to `optarg'. */
+ according to 'optarg'. */
static bool
build_type_arg (char const **typep,
- struct re_pattern_buffer *regexp, char *fastmap)
+ struct re_pattern_buffer *regexp, char *fastmap)
{
char const *errmsg;
bool rval = true;
@@ -255,10 +252,10 @@ build_type_arg (char const **typep,
regexp->fastmap = fastmap;
regexp->translate = NULL;
re_syntax_options =
- RE_SYNTAX_POSIX_BASIC & ~RE_CONTEXT_INVALID_DUP & ~RE_NO_EMPTY_RANGES;
+ RE_SYNTAX_POSIX_BASIC & ~RE_CONTEXT_INVALID_DUP & ~RE_NO_EMPTY_RANGES;
errmsg = re_compile_pattern (optarg, strlen (optarg), regexp);
if (errmsg)
- error (EXIT_FAILURE, 0, "%s", errmsg);
+ error (EXIT_FAILURE, 0, "%s", (errmsg));
break;
default:
rval = false;
@@ -314,7 +311,7 @@ proc_footer (void)
putchar ('\n');
}
-/* Process a regular text line in `line_buf'. */
+/* Process a regular text line in 'line_buf'. */
static void
proc_text (void)
@@ -325,47 +322,47 @@ proc_text (void)
{
case 'a':
if (blank_join > 1)
- {
- if (1 < line_buf.length || ++blank_lines == blank_join)
- {
- print_lineno ();
- blank_lines = 0;
- }
- else
- fputs (print_no_line_fmt, stdout);
- }
+ {
+ if (1 < line_buf.length || ++blank_lines == blank_join)
+ {
+ print_lineno ();
+ blank_lines = 0;
+ }
+ else
+ fputs (print_no_line_fmt, stdout);
+ }
else
- print_lineno ();
+ print_lineno ();
break;
case 't':
if (1 < line_buf.length)
- print_lineno ();
+ print_lineno ();
else
- fputs (print_no_line_fmt, stdout);
+ fputs (print_no_line_fmt, stdout);
break;
case 'n':
fputs (print_no_line_fmt, stdout);
break;
case 'p':
switch (re_search (current_regex, line_buf.buffer, line_buf.length - 1,
- 0, line_buf.length - 1, NULL))
- {
- case -2:
- error (EXIT_FAILURE, errno, _("error in regular expression search"));
-
- case -1:
- fputs (print_no_line_fmt, stdout);
- break;
-
- default:
- print_lineno ();
- break;
- }
+ 0, line_buf.length - 1, NULL))
+ {
+ case -2:
+ error (EXIT_FAILURE, errno, _("error in regular expression search"));
+
+ case -1:
+ fputs (print_no_line_fmt, stdout);
+ break;
+
+ default:
+ print_lineno ();
+ break;
+ }
}
fwrite (line_buf.buffer, sizeof (char), line_buf.length, stdout);
}
-/* Return the type of line in `line_buf'. */
+/* Return the type of line in 'line_buf'. */
static enum section
check_section (void)
@@ -394,20 +391,20 @@ process_file (FILE *fp)
while (readlinebuffer (&line_buf, fp))
{
switch (check_section ())
- {
- case Header:
- proc_header ();
- break;
- case Body:
- proc_body ();
- break;
- case Footer:
- proc_footer ();
- break;
- case Text:
- proc_text ();
- break;
- }
+ {
+ case Header:
+ proc_header ();
+ break;
+ case Body:
+ proc_body ();
+ break;
+ case Footer:
+ proc_footer ();
+ break;
+ case Text:
+ proc_text ();
+ break;
+ }
}
}
@@ -428,24 +425,26 @@ nl_file (char const *file)
{
stream = fopen (file, "r");
if (stream == NULL)
- {
- error (0, errno, "%s", file);
- return false;
- }
+ {
+ error (0, errno, "%s", quotef (file));
+ return false;
+ }
}
+ fadvise (stream, FADVISE_SEQUENTIAL);
+
process_file (stream);
if (ferror (stream))
{
- error (0, errno, "%s", file);
+ error (0, errno, "%s", quotef (file));
return false;
}
if (STREQ (file, "-"))
clearerr (stream); /* Also clear EOF. */
else if (fclose (stream) == EOF)
{
- error (0, errno, "%s", file);
+ error (0, errno, "%s", quotef (file));
return false;
}
return true;
@@ -459,7 +458,7 @@ main (int argc, char **argv)
bool ok = true;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -469,106 +468,80 @@ main (int argc, char **argv)
have_read_stdin = false;
while ((c = getopt_long (argc, argv, "h:b:f:v:i:pl:s:w:n:d:", longopts,
- NULL)) != -1)
+ NULL)) != -1)
{
switch (c)
- {
- case 'h':
- if (! build_type_arg (&header_type, &header_regex, header_fastmap))
- {
- error (0, 0, _("invalid header numbering style: %s"),
- quote (optarg));
- ok = false;
- }
- break;
- case 'b':
- if (! build_type_arg (&body_type, &body_regex, body_fastmap))
- {
- error (0, 0, _("invalid body numbering style: %s"),
- quote (optarg));
- ok = false;
- }
- break;
- case 'f':
- if (! build_type_arg (&footer_type, &footer_regex, footer_fastmap))
- {
- error (0, 0, _("invalid footer numbering style: %s"),
- quote (optarg));
- ok = false;
- }
- break;
- case 'v':
- if (xstrtoimax (optarg, NULL, 10, &starting_line_number, "")
- != LONGINT_OK)
- {
- error (0, 0, _("invalid starting line number: %s"),
- quote (optarg));
- ok = false;
- }
- break;
- case 'i':
- if (! (xstrtoimax (optarg, NULL, 10, &page_incr, "") == LONGINT_OK
- && 0 < page_incr))
- {
- error (0, 0, _("invalid line number increment: %s"),
- quote (optarg));
- ok = false;
- }
- break;
- case 'p':
- reset_numbers = false;
- break;
- case 'l':
- if (! (xstrtoimax (optarg, NULL, 10, &blank_join, "") == LONGINT_OK
- && 0 < blank_join))
- {
- error (0, 0, _("invalid number of blank lines: %s"),
- quote (optarg));
- ok = false;
- }
- break;
- case 's':
- separator_str = optarg;
- break;
- case 'w':
- {
- long int tmp_long;
- if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
- || tmp_long <= 0 || tmp_long > INT_MAX)
- {
- error (0, 0, _("invalid line number field width: %s"),
- quote (optarg));
- ok = false;
- }
- else
- {
- lineno_width = tmp_long;
- }
- }
- break;
- case 'n':
- if (STREQ (optarg, "ln"))
- lineno_format = FORMAT_LEFT;
- else if (STREQ (optarg, "rn"))
- lineno_format = FORMAT_RIGHT_NOLZ;
- else if (STREQ (optarg, "rz"))
- lineno_format = FORMAT_RIGHT_LZ;
- else
- {
- error (0, 0, _("invalid line numbering format: %s"),
- quote (optarg));
- ok = false;
- }
- break;
- case 'd':
- section_del = optarg;
- break;
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- ok = false;
- break;
- }
+ {
+ case 'h':
+ if (! build_type_arg (&header_type, &header_regex, header_fastmap))
+ {
+ error (0, 0, _("invalid header numbering style: %s"),
+ quote (optarg));
+ ok = false;
+ }
+ break;
+ case 'b':
+ if (! build_type_arg (&body_type, &body_regex, body_fastmap))
+ {
+ error (0, 0, _("invalid body numbering style: %s"),
+ quote (optarg));
+ ok = false;
+ }
+ break;
+ case 'f':
+ if (! build_type_arg (&footer_type, &footer_regex, footer_fastmap))
+ {
+ error (0, 0, _("invalid footer numbering style: %s"),
+ quote (optarg));
+ ok = false;
+ }
+ break;
+ case 'v':
+ starting_line_number = xdectoimax (optarg, INTMAX_MIN, INTMAX_MAX, "",
+ _("invalid starting line number"),
+ 0);
+ break;
+ case 'i':
+ page_incr = xdectoimax (optarg, 1, INTMAX_MAX, "",
+ _("invalid line number increment"), 0);
+ break;
+ case 'p':
+ reset_numbers = false;
+ break;
+ case 'l':
+ blank_join = xdectoimax (optarg, 1, INTMAX_MAX, "",
+ _("invalid line number of blank lines"), 0);
+ break;
+ case 's':
+ separator_str = optarg;
+ break;
+ case 'w':
+ lineno_width = xdectoimax (optarg, 1, INT_MAX, "",
+ _("invalid line number field width"), 0);
+ break;
+ case 'n':
+ if (STREQ (optarg, "ln"))
+ lineno_format = FORMAT_LEFT;
+ else if (STREQ (optarg, "rn"))
+ lineno_format = FORMAT_RIGHT_NOLZ;
+ else if (STREQ (optarg, "rz"))
+ lineno_format = FORMAT_RIGHT_LZ;
+ else
+ {
+ error (0, 0, _("invalid line numbering format: %s"),
+ quote (optarg));
+ ok = false;
+ }
+ break;
+ case 'd':
+ section_del = optarg;
+ break;
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ ok = false;
+ break;
+ }
}
if (!ok)
@@ -579,15 +552,15 @@ main (int argc, char **argv)
header_del_len = len * 3;
header_del = xmalloc (header_del_len + 1);
- strcat (strcat (strcpy (header_del, section_del), section_del), section_del);
+ stpcpy (stpcpy (stpcpy (header_del, section_del), section_del), section_del);
body_del_len = len * 2;
body_del = xmalloc (body_del_len + 1);
- strcat (strcpy (body_del, section_del), section_del);
+ stpcpy (stpcpy (body_del, section_del), section_del);
footer_del_len = len;
footer_del = xmalloc (footer_del_len + 1);
- strcpy (footer_del, section_del);
+ stpcpy (footer_del, section_del);
/* Initialize the input buffer. */
initbuffer (&line_buf);
@@ -613,5 +586,5 @@ main (int argc, char **argv)
if (have_read_stdin && fclose (stdin) == EOF)
error (EXIT_FAILURE, errno, "-");
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/nohup.c b/src/nohup.c
index 1f8e62b..11ccf3f 100644
--- a/src/nohup.c
+++ b/src/nohup.c
@@ -1,10 +1,10 @@
/* nohup -- run a command immune to hangups, with output to a non-tty
- Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 2003-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Jim Meyering */
@@ -30,35 +29,31 @@
#include "filenamecat.h"
#include "fd-reopen.h"
#include "long-options.h"
-#include "quote.h"
#include "unistd--.h"
#define PROGRAM_NAME "nohup"
-#define AUTHORS "Jim Meyering"
+#define AUTHORS proper_name ("Jim Meyering")
/* Exit statuses. */
enum
{
- /* `nohup' itself failed. */
- NOHUP_FAILURE = 127
+ /* 'nohup' itself failed. */
+ POSIX_NOHUP_FAILURE = 127
};
-char *program_name;
-
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s COMMAND [ARG]...\n\
or: %s OPTION\n\
"),
- program_name, program_name);
+ program_name, program_name);
fputs (_("\
Run COMMAND, ignoring hangup signals.\n\
@@ -66,8 +61,15 @@ Run COMMAND, ignoring hangup signals.\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ 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\
+'$HOME/nohup.out' otherwise.\n\
+If standard error is a terminal, redirect it to standard output.\n\
+To save output to FILE, use '%s COMMAND > FILE'.\n"),
+ program_name);
printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -81,25 +83,32 @@ main (int argc, char **argv)
bool redirecting_stdout;
bool stdout_is_closed;
bool redirecting_stderr;
+ int exit_internal_failure;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
- initialize_exit_failure (NOHUP_FAILURE);
+ /* POSIX 2008 requires that internal failure give status 127; unlike
+ 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);
+ initialize_exit_failure (exit_internal_failure);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
+ parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, Version,
+ usage, AUTHORS, (char const *) NULL);
if (getopt_long (argc, argv, "+", NULL, NULL) != -1)
- usage (NOHUP_FAILURE);
+ usage (exit_internal_failure);
if (argc <= optind)
{
error (0, 0, _("missing operand"));
- usage (NOHUP_FAILURE);
+ usage (exit_internal_failure);
}
ignoring_input = isatty (STDIN_FILENO);
@@ -112,9 +121,11 @@ main (int argc, char **argv)
to ensure any read evokes an error. */
if (ignoring_input)
{
- fd_reopen (STDIN_FILENO, "/dev/null", O_WRONLY, 0);
+ if (fd_reopen (STDIN_FILENO, "/dev/null", O_WRONLY, 0) < 0)
+ error (exit_internal_failure, errno,
+ _("failed to render standard input unusable"));
if (!redirecting_stdout && !redirecting_stderr)
- error (0, 0, _("ignoring input"));
+ error (0, 0, _("ignoring input"));
}
/* If standard output is a tty, redirect it (appending) to a file.
@@ -129,38 +140,38 @@ main (int argc, char **argv)
mode_t mode = S_IRUSR | S_IWUSR;
mode_t umask_value = umask (~mode);
out_fd = (redirecting_stdout
- ? fd_reopen (STDOUT_FILENO, file, flags, mode)
- : open (file, flags, mode));
+ ? fd_reopen (STDOUT_FILENO, file, flags, mode)
+ : open (file, flags, mode));
if (out_fd < 0)
- {
- int saved_errno = errno;
- char const *home = getenv ("HOME");
- if (home)
- {
- 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));
- }
- if (out_fd < 0)
- {
- int saved_errno2 = errno;
- error (0, saved_errno, _("failed to open %s"), quote (file));
- if (in_home)
- error (0, saved_errno2, _("failed to open %s"),
- quote (in_home));
- exit (NOHUP_FAILURE);
- }
- file = in_home;
- }
+ {
+ int saved_errno = errno;
+ char const *home = getenv ("HOME");
+ if (home)
+ {
+ 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));
+ }
+ if (out_fd < 0)
+ {
+ int saved_errno2 = errno;
+ error (0, saved_errno, _("failed to open %s"), quoteaf (file));
+ if (in_home)
+ error (0, saved_errno2, _("failed to open %s"),
+ quoteaf (in_home));
+ return exit_internal_failure;
+ }
+ file = in_home;
+ }
umask (umask_value);
error (0, 0,
- _(ignoring_input
- ? "ignoring input and appending output to %s"
- : "appending output to %s"),
- quote (file));
+ _(ignoring_input
+ ? N_("ignoring input and appending output to %s")
+ : N_("appending output to %s")),
+ quoteaf (file));
free (in_home);
}
@@ -168,49 +179,54 @@ main (int argc, char **argv)
if (redirecting_stderr)
{
/* Save a copy of stderr before redirecting, so we can use the original
- if execve fails. It's no big deal if this dup fails. It might
- not change anything, and at worst, it'll lead to suppression of
- the post-failed-execve diagnostic. */
+ if execve fails. It's no big deal if this dup fails. It might
+ not change anything, and at worst, it'll lead to suppression of
+ the post-failed-execve diagnostic. */
saved_stderr_fd = dup (STDERR_FILENO);
if (0 <= saved_stderr_fd
- && set_cloexec_flag (saved_stderr_fd, true) != 0)
- error (NOHUP_FAILURE, errno,
- _("failed to set the copy of stderr to close on exec"));
+ && set_cloexec_flag (saved_stderr_fd, true) != 0)
+ error (exit_internal_failure, errno,
+ _("failed to set the copy of stderr to close on exec"));
if (!redirecting_stdout)
- error (0, 0,
- _(ignoring_input
- ? "ignoring input and redirecting stderr to stdout"
- : "redirecting stderr to stdout"));
+ error (0, 0,
+ _(ignoring_input
+ ? N_("ignoring input and redirecting stderr to stdout")
+ : N_("redirecting stderr to stdout")));
if (dup2 (out_fd, STDERR_FILENO) < 0)
- error (NOHUP_FAILURE, errno, _("failed to redirect standard error"));
+ error (exit_internal_failure, errno,
+ _("failed to redirect standard error"));
if (stdout_is_closed)
- close (out_fd);
+ close (out_fd);
}
+ /* error() flushes stderr, but does not check for write failure.
+ Normally, we would catch this via our atexit() hook of
+ close_stdout, but execvp() gets in the way. If stderr
+ encountered a write failure, there is no need to try calling
+ error() again, particularly since we may have just changed the
+ underlying fd out from under stderr. */
+ if (ferror (stderr))
+ return exit_internal_failure;
+
signal (SIGHUP, SIG_IGN);
- {
- int exit_status;
- int saved_errno;
- char **cmd = argv + optind;
-
- execvp (*cmd, cmd);
- exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
- saved_errno = errno;
-
- /* The execve failed. Output a diagnostic to stderr only if:
- - stderr was initially redirected to a non-tty, or
- - stderr was initially directed to a tty, and we
- can dup2 it to point back to that same tty.
- In other words, output the diagnostic if possible, but only if
- it will go to the original stderr. */
- if (dup2 (saved_stderr_fd, STDERR_FILENO) == STDERR_FILENO)
- error (0, saved_errno, _("cannot run command %s"), quote (*cmd));
-
- exit (exit_status);
- }
+ char **cmd = argv + optind;
+ execvp (*cmd, cmd);
+ int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
+ int saved_errno = errno;
+
+ /* The execve failed. Output a diagnostic to stderr only if:
+ - stderr was initially redirected to a non-tty, or
+ - stderr was initially directed to a tty, and we
+ can dup2 it to point back to that same tty.
+ In other words, output the diagnostic if possible, but only if
+ it will go to the original stderr. */
+ if (dup2 (saved_stderr_fd, STDERR_FILENO) == STDERR_FILENO)
+ error (0, saved_errno, _("failed to run command %s"), quoteaf (*cmd));
+
+ return exit_status;
}
diff --git a/src/nproc.c b/src/nproc.c
new file mode 100644
index 0000000..ddf3660
--- /dev/null
+++ b/src/nproc.c
@@ -0,0 +1,129 @@
+/* nproc - print the number of processors.
+ Copyright (C) 2009-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Written by Giuseppe Scrivano. */
+
+#include <config.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "system.h"
+#include "error.h"
+#include "nproc.h"
+#include "quote.h"
+#include "xdectoint.h"
+
+/* The official name of this program (e.g., no 'g' prefix). */
+#define PROGRAM_NAME "nproc"
+
+#define AUTHORS proper_name ("Giuseppe Scrivano")
+
+enum
+{
+ ALL_OPTION = CHAR_MAX + 1,
+ IGNORE_OPTION
+};
+
+static struct option const longopts[] =
+{
+ {"all", no_argument, NULL, ALL_OPTION},
+ {"ignore", required_argument, NULL, IGNORE_OPTION},
+ {GETOPT_HELP_OPTION_DECL},
+ {GETOPT_VERSION_OPTION_DECL},
+ {NULL, 0, NULL, 0}
+};
+
+void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ emit_try_help ();
+ else
+ {
+ 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\
+\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);
+ emit_ancillary_info (PROGRAM_NAME);
+ }
+ exit (status);
+}
+
+int
+main (int argc, char **argv)
+{
+ unsigned long nproc, ignore = 0;
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ atexit (close_stdout);
+
+ enum nproc_query mode = NPROC_CURRENT_OVERRIDABLE;
+
+ while (1)
+ {
+ int c = getopt_long (argc, argv, "", longopts, NULL);
+ if (c == -1)
+ break;
+ switch (c)
+ {
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ case ALL_OPTION:
+ mode = NPROC_ALL;
+ break;
+
+ case IGNORE_OPTION:
+ ignore = xdectoumax (optarg, 0, ULONG_MAX, "", _("invalid number"),0);
+ break;
+
+ default:
+ usage (EXIT_FAILURE);
+ }
+ }
+
+ if (argc != optind)
+ {
+ error (0, 0, _("extra operand %s"), quote (argv[optind]));
+ usage (EXIT_FAILURE);
+ }
+
+ nproc = num_processors (mode);
+
+ if (ignore < nproc)
+ nproc -= ignore;
+ else
+ nproc = 1;
+
+ printf ("%lu\n", nproc);
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/numfmt.c b/src/numfmt.c
new file mode 100644
index 0000000..223f2a2
--- /dev/null
+++ b/src/numfmt.c
@@ -0,0 +1,1650 @@
+/* Reformat numbers like 11505426432 to the more human-readable 11G
+ Copyright (C) 2012-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+#include <config.h>
+#include <float.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <langinfo.h>
+
+#include "mbsalign.h"
+#include "argmatch.h"
+#include "c-ctype.h"
+#include "error.h"
+#include "quote.h"
+#include "system.h"
+#include "xstrtol.h"
+#include "xstrndup.h"
+
+#include "set-fields.h"
+
+#if HAVE_FPSETPREC
+# include <ieeefp.h>
+#endif
+
+/* The official name of this program (e.g., no 'g' prefix). */
+#define PROGRAM_NAME "numfmt"
+
+#define AUTHORS proper_name ("Assaf Gordon")
+
+/* Exit code when some numbers fail to convert. */
+enum { EXIT_CONVERSION_WARNINGS = 2 };
+
+enum
+{
+ FROM_OPTION = CHAR_MAX + 1,
+ FROM_UNIT_OPTION,
+ TO_OPTION,
+ TO_UNIT_OPTION,
+ ROUND_OPTION,
+ SUFFIX_OPTION,
+ GROUPING_OPTION,
+ PADDING_OPTION,
+ FIELD_OPTION,
+ DEBUG_OPTION,
+ DEV_DEBUG_OPTION,
+ HEADER_OPTION,
+ FORMAT_OPTION,
+ INVALID_OPTION
+};
+
+enum scale_type
+{
+ scale_none, /* the default: no scaling. */
+ scale_auto, /* --from only. */
+ scale_SI,
+ scale_IEC,
+ scale_IEC_I /* 'i' suffix is required. */
+};
+
+static char const *const scale_from_args[] =
+{
+ "none", "auto", "si", "iec", "iec-i", NULL
+};
+
+static enum scale_type const scale_from_types[] =
+{
+ scale_none, scale_auto, scale_SI, scale_IEC, scale_IEC_I
+};
+
+static char const *const scale_to_args[] =
+{
+ "none", "si", "iec", "iec-i", NULL
+};
+
+static enum scale_type const scale_to_types[] =
+{
+ scale_none, scale_SI, scale_IEC, scale_IEC_I
+};
+
+
+enum round_type
+{
+ round_ceiling,
+ round_floor,
+ round_from_zero,
+ round_to_zero,
+ round_nearest,
+};
+
+static char const *const round_args[] =
+{
+ "up", "down", "from-zero", "towards-zero", "nearest", NULL
+};
+
+static enum round_type const round_types[] =
+{
+ round_ceiling, round_floor, round_from_zero, round_to_zero, round_nearest
+};
+
+
+enum inval_type
+{
+ inval_abort,
+ inval_fail,
+ inval_warn,
+ inval_ignore
+};
+
+static char const *const inval_args[] =
+{
+ "abort", "fail", "warn", "ignore", NULL
+};
+
+static enum inval_type const inval_types[] =
+{
+ inval_abort, inval_fail, inval_warn, inval_ignore
+};
+
+static struct option const longopts[] =
+{
+ {"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},
+ {"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},
+ {NULL, 0, NULL, 0}
+};
+
+/* If delimiter has this value, blanks separate fields. */
+enum { DELIMITER_DEFAULT = CHAR_MAX + 1 };
+
+/* Maximum number of digits we can safely handle
+ without precision loss, if scaling is 'none'. */
+enum { MAX_UNSCALED_DIGITS = LDBL_DIG };
+
+/* Maximum number of digits we can work with.
+ This is equivalent to 999Y.
+ NOTE: 'long double' can handle more than that, but there's
+ no official suffix assigned beyond Yotta (1000^8). */
+enum { MAX_ACCEPTABLE_DIGITS = 27 };
+
+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 const char *suffix = NULL;
+static uintmax_t from_unit_size = 1;
+static uintmax_t to_unit_size = 1;
+static int grouping = 0;
+static char *padding_buffer = NULL;
+static size_t padding_buffer_size = 0;
+static long int padding_width = 0;
+static long int zero_padding_width = 0;
+static long int user_precision = -1;
+static const char *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;
+
+
+/* auto-pad each line based on skipped whitespace. */
+static int auto_padding = 0;
+static mbs_align_t padding_alignment = MBS_ALIGN_RIGHT;
+
+/* field delimiter */
+static int delimiter = DELIMITER_DEFAULT;
+
+/* line delimiter. */
+static unsigned char line_delim = '\n';
+
+/* if non-zero, the first 'header' lines from STDIN are skipped. */
+static uintmax_t header = 0;
+
+/* Debug for users: print warnings to STDERR about possible
+ error (similar to sort's debug). */
+static bool debug;
+
+/* will be set according to the current locale. */
+static const char *decimal_point;
+static int decimal_point_length;
+
+/* debugging for developers. Enables devmsg(). */
+static bool dev_debug = false;
+
+
+static inline int
+default_scale_base (enum scale_type scale)
+{
+ switch (scale)
+ {
+ case scale_IEC:
+ case scale_IEC_I:
+ return 1024;
+
+ case scale_none:
+ case scale_auto:
+ case scale_SI:
+ default:
+ return 1000;
+ }
+}
+
+static inline int
+valid_suffix (const char suf)
+{
+ static const char *valid_suffixes = "KMGTPEZY";
+ return (strchr (valid_suffixes, suf) != NULL);
+}
+
+static inline int
+suffix_power (const char suf)
+{
+ switch (suf)
+ {
+ case 'K': /* kilo or kibi. */
+ return 1;
+
+ case 'M': /* mega or mebi. */
+ return 2;
+
+ case 'G': /* giga or gibi. */
+ return 3;
+
+ case 'T': /* tera or tebi. */
+ return 4;
+
+ case 'P': /* peta or pebi. */
+ return 5;
+
+ case 'E': /* exa or exbi. */
+ return 6;
+
+ case 'Z': /* zetta or 2**70. */
+ return 7;
+
+ case 'Y': /* yotta or 2**80. */
+ return 8;
+
+ default: /* should never happen. assert? */
+ return 0;
+ }
+}
+
+static inline const char *
+suffix_power_char (unsigned int power)
+{
+ switch (power)
+ {
+ case 0:
+ return "";
+
+ case 1:
+ return "K";
+
+ case 2:
+ return "M";
+
+ case 3:
+ return "G";
+
+ case 4:
+ return "T";
+
+ case 5:
+ return "P";
+
+ case 6:
+ return "E";
+
+ case 7:
+ return "Z";
+
+ case 8:
+ return "Y";
+
+ default:
+ return "(error)";
+ }
+}
+
+/* Similar to 'powl(3)' but without requiring 'libm'. */
+static long double
+powerld (long double base, unsigned int x)
+{
+ long double result = base;
+ if (x == 0)
+ return 1; /* note for test coverage: this is never
+ reached, as 'powerld' won't be called if
+ there's no suffix, hence, no "power". */
+
+ /* TODO: check for overflow, inf? */
+ while (--x)
+ result *= base;
+ return result;
+}
+
+/* Similar to 'fabs(3)' but without requiring 'libm'. */
+static inline long double
+absld (long double val)
+{
+ return val < 0 ? -val : val;
+}
+
+/* Scale down 'val', returns 'updated val' and 'x', such that
+ val*base^X = original val
+ Similar to "frexpl(3)" but without requiring 'libm',
+ allowing only integer scale, limited functionality and error checking. */
+static long double
+expld (long double val, unsigned int base, unsigned int /*output */ *x)
+{
+ unsigned int power = 0;
+
+ if (val >= -LDBL_MAX && val <= LDBL_MAX)
+ {
+ while (absld (val) >= base)
+ {
+ ++power;
+ val /= base;
+ }
+ }
+ if (x)
+ *x = power;
+ return val;
+}
+
+/* EXTREMELY limited 'ceil' - without 'libm'.
+ Assumes values that fit in intmax_t. */
+static inline intmax_t
+simple_round_ceiling (long double val)
+{
+ intmax_t intval = val;
+ if (intval < val)
+ intval++;
+ return intval;
+}
+
+/* EXTREMELY limited 'floor' - without 'libm'.
+ Assumes values that fit in intmax_t. */
+static inline intmax_t
+simple_round_floor (long double val)
+{
+ return -simple_round_ceiling (-val);
+}
+
+/* EXTREMELY limited 'round away from zero'.
+ Assumes values that fit in intmax_t. */
+static inline intmax_t
+simple_round_from_zero (long double val)
+{
+ return val < 0 ? simple_round_floor (val) : simple_round_ceiling (val);
+}
+
+/* EXTREMELY limited 'round away to zero'.
+ Assumes values that fit in intmax_t. */
+static inline intmax_t
+simple_round_to_zero (long double val)
+{
+ return val;
+}
+
+/* EXTREMELY limited 'round' - without 'libm'.
+ Assumes values that fit in intmax_t. */
+static inline intmax_t
+simple_round_nearest (long double val)
+{
+ return val < 0 ? val - 0.5 : val + 0.5;
+}
+
+static inline long double _GL_ATTRIBUTE_CONST
+simple_round (long double val, enum round_type t)
+{
+ intmax_t rval;
+ intmax_t intmax_mul = val / INTMAX_MAX;
+ val -= (long double) INTMAX_MAX * intmax_mul;
+
+ switch (t)
+ {
+ case round_ceiling:
+ rval = simple_round_ceiling (val);
+ break;
+
+ case round_floor:
+ rval = simple_round_floor (val);
+ break;
+
+ case round_from_zero:
+ rval = simple_round_from_zero (val);
+ break;
+
+ case round_to_zero:
+ rval = simple_round_to_zero (val);
+ break;
+
+ case round_nearest:
+ rval = simple_round_nearest (val);
+ break;
+
+ default:
+ /* to silence the compiler - this should never happen. */
+ return 0;
+ }
+
+ return (long double) INTMAX_MAX * intmax_mul + rval;
+}
+
+enum simple_strtod_error
+{
+ SSE_OK = 0,
+ SSE_OK_PRECISION_LOSS,
+ SSE_OVERFLOW,
+ SSE_INVALID_NUMBER,
+
+ /* the following are returned by 'simple_strtod_human'. */
+ SSE_VALID_BUT_FORBIDDEN_SUFFIX,
+ SSE_INVALID_SUFFIX,
+ SSE_MISSING_I_SUFFIX
+};
+
+/* Read an *integer* INPUT_STR,
+ but return the integer value in a 'long double' VALUE
+ hence, no UINTMAX_MAX limitation.
+ NEGATIVE is updated, and is stored separately from the VALUE
+ so that signbit() isn't required to determine the sign of -0..
+ ENDPTR is required (unlike strtod) and is used to store a pointer
+ to the character after the last character used in the conversion.
+
+ Note locale'd grouping is not supported,
+ nor is skipping of white-space supported.
+
+ Returns:
+ SSE_OK - valid number.
+ SSE_OK_PRECISION_LOSS - if more than 18 digits were used.
+ SSE_OVERFLOW - if more than 27 digits (999Y) were used.
+ SSE_INVALID_NUMBER - if no digits were found. */
+static enum simple_strtod_error
+simple_strtod_int (const char *input_str,
+ char **endptr, long double *value, bool *negative)
+{
+ enum simple_strtod_error e = SSE_OK;
+
+ long double val = 0;
+ unsigned int digits = 0;
+ bool found_digit = false;
+
+ if (*input_str == '-')
+ {
+ input_str++;
+ *negative = true;
+ }
+ else
+ *negative = false;
+
+ *endptr = (char *) input_str;
+ while (*endptr && c_isdigit (**endptr))
+ {
+ int digit = (**endptr) - '0';
+
+ found_digit = true;
+
+ if (val || digit)
+ digits++;
+
+ if (digits > MAX_UNSCALED_DIGITS)
+ e = SSE_OK_PRECISION_LOSS;
+
+ if (digits > MAX_ACCEPTABLE_DIGITS)
+ return SSE_OVERFLOW;
+
+ val *= 10;
+ val += digit;
+
+ ++(*endptr);
+ }
+ if (! found_digit
+ && ! STREQ_LEN (*endptr, decimal_point, decimal_point_length))
+ return SSE_INVALID_NUMBER;
+ if (*negative)
+ val = -val;
+
+ if (value)
+ *value = val;
+
+ return e;
+}
+
+/* Read a floating-point INPUT_STR represented as "NNNN[.NNNNN]",
+ and return the value in a 'long double' VALUE.
+ ENDPTR is required (unlike strtod) and is used to store a pointer
+ to the character after the last character used in the conversion.
+ PRECISION is optional and used to indicate fractions are present.
+
+ Note locale'd grouping is not supported,
+ nor is skipping of white-space supported.
+
+ Returns:
+ SSE_OK - valid number.
+ SSE_OK_PRECISION_LOSS - if more than 18 digits were used.
+ SSE_OVERFLOW - if more than 27 digits (999Y) were used.
+ SSE_INVALID_NUMBER - if no digits were found. */
+static enum simple_strtod_error
+simple_strtod_float (const char *input_str,
+ char **endptr,
+ long double *value,
+ size_t *precision)
+{
+ bool negative;
+ enum simple_strtod_error e = SSE_OK;
+
+ if (precision)
+ *precision = 0;
+
+ /* TODO: accept locale'd grouped values for the integral part. */
+ e = simple_strtod_int (input_str, endptr, value, &negative);
+ if (e != SSE_OK && e != SSE_OK_PRECISION_LOSS)
+ return e;
+
+ /* optional decimal point + fraction. */
+ if (STREQ_LEN (*endptr, decimal_point, decimal_point_length))
+ {
+ char *ptr2;
+ long double val_frac = 0;
+ bool neg_frac;
+
+ (*endptr) += decimal_point_length;
+ enum simple_strtod_error e2 =
+ simple_strtod_int (*endptr, &ptr2, &val_frac, &neg_frac);
+ if (e2 != SSE_OK && e2 != SSE_OK_PRECISION_LOSS)
+ return e2;
+ if (e2 == SSE_OK_PRECISION_LOSS)
+ e = e2; /* propagate warning. */
+ if (neg_frac)
+ return SSE_INVALID_NUMBER;
+
+ /* number of digits in the fractions. */
+ size_t exponent = ptr2 - *endptr;
+
+ val_frac = ((long double) val_frac) / powerld (10, exponent);
+
+ /* TODO: detect loss of precision (only really 18 digits
+ of precision across all digits (before and after '.')). */
+ if (value)
+ {
+ if (negative)
+ *value -= val_frac;
+ else
+ *value += val_frac;
+ }
+
+ if (precision)
+ *precision = exponent;
+
+ *endptr = ptr2;
+ }
+ return e;
+}
+
+/* Read a 'human' INPUT_STR represented as "NNNN[.NNNNN] + suffix",
+ and return the value in a 'long double' VALUE,
+ with the precision of the input returned in PRECISION.
+ ENDPTR is required (unlike strtod) and is used to store a pointer
+ to the character after the last character used in the conversion.
+ ALLOWED_SCALING determines the scaling supported.
+
+ TODO:
+ support locale'd grouping
+ accept scentific and hex floats (probably use strtold directly)
+
+ Returns:
+ SSE_OK - valid number.
+ SSE_OK_PRECISION_LOSS - if more than LDBL_DIG digits were used.
+ SSE_OVERFLOW - if more than 27 digits (999Y) were used.
+ SSE_INVALID_NUMBER - if no digits were found.
+ SSE_VALID_BUT_FORBIDDEN_SUFFIX
+ SSE_INVALID_SUFFIX
+ SSE_MISSING_I_SUFFIX */
+static enum simple_strtod_error
+simple_strtod_human (const char *input_str,
+ char **endptr, long double *value, size_t *precision,
+ enum scale_type allowed_scaling)
+{
+ int power = 0;
+ /* 'scale_auto' is checked below. */
+ int scale_base = default_scale_base (allowed_scaling);
+
+ devmsg ("simple_strtod_human:\n input string: %s\n"
+ " locale decimal-point: %s\n"
+ " MAX_UNSCALED_DIGITS: %d\n",
+ quote_n (0, input_str),
+ quote_n (1, decimal_point),
+ MAX_UNSCALED_DIGITS);
+
+ enum simple_strtod_error e =
+ simple_strtod_float (input_str, endptr, value, precision);
+ if (e != SSE_OK && e != SSE_OK_PRECISION_LOSS)
+ return e;
+
+ devmsg (" parsed numeric value: %Lf\n"
+ " input precision = %d\n", *value, (int)*precision);
+
+ if (**endptr != '\0')
+ {
+ /* process suffix. */
+
+ /* Skip any blanks between the number and suffix. */
+ while (isblank (to_uchar (**endptr)))
+ (*endptr)++;
+
+ if (!valid_suffix (**endptr))
+ return SSE_INVALID_SUFFIX;
+
+ if (allowed_scaling == scale_none)
+ return SSE_VALID_BUT_FORBIDDEN_SUFFIX;
+
+ power = suffix_power (**endptr);
+ (*endptr)++; /* skip first suffix character. */
+
+ if (allowed_scaling == scale_auto && **endptr == 'i')
+ {
+ /* auto-scaling enabled, and the first suffix character
+ is followed by an 'i' (e.g. Ki, Mi, Gi). */
+ scale_base = 1024;
+ (*endptr)++; /* skip second ('i') suffix character. */
+ devmsg (" Auto-scaling, found 'i', switching to base %d\n",
+ scale_base);
+ }
+
+ *precision = 0; /* Reset, to select precision based on scale. */
+ }
+
+ if (allowed_scaling == scale_IEC_I)
+ {
+ if (**endptr == 'i')
+ (*endptr)++;
+ else
+ return SSE_MISSING_I_SUFFIX;
+ }
+
+ long double multiplier = powerld (scale_base, power);
+
+ devmsg (" suffix power=%d^%d = %Lf\n", scale_base, power, multiplier);
+
+ /* TODO: detect loss of precision and overflows. */
+ (*value) = (*value) * multiplier;
+
+ devmsg (" returning value: %Lf (%LG)\n", *value, *value);
+
+ return e;
+}
+
+
+static void
+simple_strtod_fatal (enum simple_strtod_error err, char const *input_str)
+{
+ char const *msgid = NULL;
+
+ switch (err)
+ {
+ case SSE_OK_PRECISION_LOSS:
+ case SSE_OK:
+ /* should never happen - this function isn't called when OK. */
+ abort ();
+
+ case SSE_OVERFLOW:
+ msgid = N_("value too large to be converted: %s");
+ break;
+
+ case SSE_INVALID_NUMBER:
+ msgid = N_("invalid number: %s");
+ break;
+
+ case SSE_VALID_BUT_FORBIDDEN_SUFFIX:
+ msgid = N_("rejecting suffix in input: %s (consider using --from)");
+ break;
+
+ case SSE_INVALID_SUFFIX:
+ msgid = N_("invalid suffix in input: %s");
+ break;
+
+ case SSE_MISSING_I_SUFFIX:
+ msgid = N_("missing 'i' suffix in input: %s (e.g Ki/Mi/Gi)");
+ break;
+
+ }
+
+ if (inval_style != inval_ignore)
+ error (conv_exit_code, 0, gettext (msgid), quote (input_str));
+}
+
+/* Convert VAL to a human format string in BUF. */
+static void
+double_to_human (long double val, int precision,
+ char *buf, size_t buf_size,
+ enum scale_type scale, int group, enum round_type round)
+{
+ int num_size;
+ char fmt[64];
+ verify (sizeof (fmt) > (INT_BUFSIZE_BOUND (zero_padding_width)
+ + INT_BUFSIZE_BOUND (precision)
+ + 10 /* for %.Lf etc. */));
+
+ char *pfmt = fmt;
+ *pfmt++ = '%';
+
+ if (group)
+ *pfmt++ = '\'';
+
+ if (zero_padding_width)
+ pfmt += snprintf (pfmt, sizeof (fmt) - 2, "0%ld", zero_padding_width);
+
+ devmsg ("double_to_human:\n");
+
+ if (scale == scale_none)
+ {
+ val *= powerld (10, precision);
+ val = simple_round (val, round);
+ val /= powerld (10, precision);
+
+ devmsg ((group) ?
+ " no scaling, returning (grouped) value: %'.*Lf\n" :
+ " no scaling, returning value: %.*Lf\n", precision, val);
+
+ stpcpy (pfmt, ".*Lf");
+
+ num_size = snprintf (buf, buf_size, fmt, precision, val);
+ if (num_size < 0 || num_size >= (int) buf_size)
+ error (EXIT_FAILURE, 0,
+ _("failed to prepare value '%Lf' for printing"), val);
+ return;
+ }
+
+ /* Scaling requested by user. */
+ double scale_base = default_scale_base (scale);
+
+ /* Normalize val to scale. */
+ unsigned int power = 0;
+ val = expld (val, scale_base, &power);
+ devmsg (" scaled value to %Lf * %0.f ^ %u\n", val, scale_base, power);
+
+ /* Perform rounding. */
+ unsigned int power_adjust = 0;
+ if (user_precision != -1)
+ power_adjust = MIN (power * 3, user_precision);
+ else if (absld (val) < 10)
+ {
+ /* for values less than 10, we allow one decimal-point digit,
+ so adjust before rounding. */
+ power_adjust = 1;
+ }
+
+ val *= powerld (10, power_adjust);
+ val = simple_round (val, round);
+ val /= powerld (10, power_adjust);
+
+ /* two special cases after rounding:
+ 1. a "999.99" can turn into 1000 - so scale down
+ 2. a "9.99" can turn into 10 - so don't display decimal-point. */
+ if (absld (val) >= scale_base)
+ {
+ val /= scale_base;
+ power++;
+ }
+
+ /* should "7.0" be printed as "7" ?
+ if removing the ".0" is preferred, enable the fourth condition. */
+ int show_decimal_point = (val != 0) && (absld (val) < 10) && (power > 0);
+ /* && (absld (val) > simple_round_floor (val))) */
+
+ devmsg (" after rounding, value=%Lf * %0.f ^ %u\n", val, scale_base, power);
+
+ stpcpy (pfmt, ".*Lf%s");
+
+ int prec = user_precision == -1 ? show_decimal_point : user_precision;
+
+ /* buf_size - 1 used here to ensure place for possible scale_IEC_I suffix. */
+ num_size = snprintf (buf, buf_size - 1, fmt, prec, val,
+ suffix_power_char (power));
+ if (num_size < 0 || num_size >= (int) buf_size - 1)
+ error (EXIT_FAILURE, 0,
+ _("failed to prepare value '%Lf' for printing"), val);
+
+ if (scale == scale_IEC_I && power > 0)
+ strncat (buf, "i", buf_size - num_size - 1);
+
+ devmsg (" returning value: %s\n", quote (buf));
+
+ return;
+}
+
+/* Convert a string of decimal digits, N_STRING, with an optional suffix
+ to an integral value. Suffixes are handled as with --from=auto.
+ Upon successful conversion, return that value.
+ If it cannot be converted, give a diagnostic and exit. */
+static uintmax_t
+unit_to_umax (const char *n_string)
+{
+ strtol_error s_err;
+ const char *c_string = n_string;
+ char *t_string = NULL;
+ size_t n_len = strlen (n_string);
+ char *end = NULL;
+ uintmax_t n;
+ const char *suffixes = "KMGTPEZY";
+
+ /* Adjust suffixes so K=1000, Ki=1024, KiB=invalid. */
+ if (n_len && ! c_isdigit (n_string[n_len - 1]))
+ {
+ t_string = xmalloc (n_len + 2);
+ end = t_string + n_len - 1;
+ memcpy (t_string, n_string, n_len);
+
+ if (*end == 'i' && 2 <= n_len && ! c_isdigit (*(end - 1)))
+ *end = '\0';
+ else
+ {
+ *++end = 'B';
+ *++end = '\0';
+ suffixes = "KMGTPEZY0";
+ }
+
+ c_string = t_string;
+ }
+
+ s_err = xstrtoumax (c_string, &end, 10, &n, suffixes);
+
+ if (s_err != LONGINT_OK || *end || n == 0)
+ {
+ free (t_string);
+ error (EXIT_FAILURE, 0, _("invalid unit size: %s"), quote (n_string));
+ }
+
+ free (t_string);
+
+ return n;
+}
+
+
+static void
+setup_padding_buffer (size_t min_size)
+{
+ if (padding_buffer_size > min_size)
+ return;
+
+ padding_buffer_size = min_size + 1;
+ padding_buffer = xrealloc (padding_buffer, padding_buffer_size);
+}
+
+void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ emit_try_help ();
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION]... [NUMBER]...\n\
+"), program_name);
+ fputs (_("\
+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 (_("\
+ --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);
+
+ fputs (_("\
+\n\
+UNIT options:\n"), stdout);
+ fputs (_("\
+ none no auto-scaling is done; suffixes will trigger an error\n\
+"), stdout);
+ fputs (_("\
+ auto accept optional single/two letter suffix:\n\
+ 1K = 1000,\n\
+ 1Ki = 1024,\n\
+ 1M = 1000000,\n\
+ 1Mi = 1048576,\n"), stdout);
+ fputs (_("\
+ si accept optional single letter suffix:\n\
+ 1K = 1000,\n\
+ 1M = 1000000,\n\
+ ...\n"), stdout);
+ fputs (_("\
+ iec accept optional single letter suffix:\n\
+ 1K = 1024,\n\
+ 1M = 1048576,\n\
+ ...\n"), stdout);
+ fputs (_("\
+ iec-i accept optional two-letter suffix:\n\
+ 1Ki = 1024,\n\
+ 1Mi = 1048576,\n\
+ ...\n"), stdout);
+
+ fputs (_("\n\
+FIELDS supports cut(1) style field ranges:\n\
+ N N'th field, counted from 1\n\
+ N- from N'th field, to end of line\n\
+ N-M from N'th to M'th field (inclusive)\n\
+ -M from first to M'th field (inclusive)\n\
+ - all fields\n\
+Multiple fields/ranges can be separated with commas\n\
+"), stdout);
+
+ fputs (_("\n\
+FORMAT must be suitable for printing one floating-point argument '%f'.\n\
+Optional quote (%'f) will enable --grouping (if supported by current locale).\n\
+Optional width value (%10f) will pad output. Optional zero (%010f) width\n\
+will zero pad the number. Optional negative values (%-10f) will left align.\n\
+Optional precision (%.1f) will override the input determined precision.\n\
+"), stdout);
+
+ printf (_("\n\
+Exit status is 0 if all input numbers were successfully converted.\n\
+By default, %s will stop at the first conversion error with exit status 2.\n\
+With --invalid='fail' a warning is printed for each conversion error\n\
+and the exit status is 2. With --invalid='warn' each conversion error is\n\
+diagnosed, but the exit status is 0. With --invalid='ignore' conversion\n\
+errors are not diagnosed and the exit status is 0.\n\
+"), program_name);
+
+ printf (_("\n\
+Examples:\n\
+ $ %s --to=si 1000\n\
+ -> \"1.0K\"\n\
+ $ %s --to=iec 2048\n\
+ -> \"2.0K\"\n\
+ $ %s --to=iec-i 4096\n\
+ -> \"4.0Ki\"\n\
+ $ echo 1K | %s --from=si\n\
+ -> \"1000\"\n\
+ $ echo 1K | %s --from=iec\n\
+ -> \"1024\"\n\
+ $ df -B1 | %s --header --field 2-4 --to=si\n\
+ $ ls -l | %s --header --field 5 --to=iec\n\
+ $ ls -lh | %s --header --field 5 --from=iec --padding=10\n\
+ $ ls -lh | %s --header --field 5 --from=iec --format %%10f\n"),
+ program_name, program_name, program_name,
+ program_name, program_name, program_name,
+ program_name, program_name, program_name);
+ emit_ancillary_info (PROGRAM_NAME);
+ }
+ exit (status);
+}
+
+/* Given 'fmt' (a printf(3) compatible format string), extracts the following:
+ 1. padding (e.g. %20f)
+ 2. alignment (e.g. %-20f)
+ 3. grouping (e.g. %'f)
+
+ Only a limited subset of printf(3) syntax is supported.
+
+ TODO:
+ support %e %g etc. rather than just %f
+
+ NOTES:
+ 1. This function sets the global variables:
+ padding_width, padding_alignment, grouping,
+ format_str_prefix, format_str_suffix
+ 2. The function aborts on any errors. */
+static void
+parse_format_string (char const *fmt)
+{
+ size_t i;
+ size_t prefix_len = 0;
+ size_t suffix_pos;
+ long int pad = 0;
+ char *endptr = NULL;
+ bool zero_padding = false;
+
+ for (i = 0; !(fmt[i] == '%' && fmt[i + 1] != '%'); i += (fmt[i] == '%') + 1)
+ {
+ if (!fmt[i])
+ error (EXIT_FAILURE, 0,
+ _("format %s has no %% directive"), quote (fmt));
+ prefix_len++;
+ }
+
+ i++;
+ while (true)
+ {
+ size_t skip = strspn (fmt + i, " ");
+ i += skip;
+ if (fmt[i] == '\'')
+ {
+ grouping = 1;
+ i++;
+ }
+ else if (fmt[i] == '0')
+ {
+ zero_padding = true;
+ i++;
+ }
+ else if (! skip)
+ break;
+ }
+
+ errno = 0;
+ pad = strtol (fmt + i, &endptr, 10);
+ if (errno == ERANGE)
+ error (EXIT_FAILURE, 0,
+ _("invalid format %s (width overflow)"), quote (fmt));
+
+ if (endptr != (fmt + i) && pad != 0)
+ {
+ if (debug && padding_width && !(zero_padding && pad > 0))
+ error (0, 0, _("--format padding overriding --padding"));
+
+ if (pad < 0)
+ {
+ padding_alignment = MBS_ALIGN_LEFT;
+ padding_width = -pad;
+ }
+ else
+ {
+ if (zero_padding)
+ zero_padding_width = pad;
+ else
+ padding_width = pad;
+ }
+
+ }
+ i = endptr - fmt;
+
+ if (fmt[i] == '\0')
+ error (EXIT_FAILURE, 0, _("format %s ends in %%"), quote (fmt));
+
+ if (fmt[i] == '.')
+ {
+ i++;
+ errno = 0;
+ user_precision = strtol (fmt + i, &endptr, 10);
+ if (errno == ERANGE || user_precision < 0 || SIZE_MAX < user_precision
+ || isblank (fmt[i]) || fmt[i] == '+')
+ {
+ /* Note we disallow negative user_precision to be
+ consistent with printf(1). POSIX states that
+ negative precision is only supported (and ignored)
+ when used with '.*f'. glibc at least will malform
+ output when passed a direct negative precision. */
+ error (EXIT_FAILURE, 0,
+ _("invalid precision in format %s"), quote (fmt));
+ }
+ i = endptr - fmt;
+ }
+
+ if (fmt[i] != 'f')
+ error (EXIT_FAILURE, 0, _("invalid format %s,"
+ " directive must be %%[0]['][-][N][.][N]f"),
+ quote (fmt));
+ i++;
+ suffix_pos = i;
+
+ for (; fmt[i] != '\0'; i += (fmt[i] == '%') + 1)
+ if (fmt[i] == '%' && fmt[i + 1] != '%')
+ error (EXIT_FAILURE, 0, _("format %s has too many %% directives"),
+ quote (fmt));
+
+ if (prefix_len)
+ format_str_prefix = xstrndup (fmt, prefix_len);
+ if (fmt[suffix_pos] != '\0')
+ format_str_suffix = xstrdup (fmt + suffix_pos);
+
+ devmsg ("format String:\n input: %s\n grouping: %s\n"
+ " padding width: %ld\n alignment: %s\n"
+ " prefix: %s\n suffix: %s\n",
+ quote_n (0, fmt), (grouping) ? "yes" : "no",
+ padding_width,
+ (padding_alignment == MBS_ALIGN_LEFT) ? "Left" : "Right",
+ quote_n (1, format_str_prefix ? format_str_prefix : ""),
+ quote_n (2, format_str_suffix ? format_str_suffix : ""));
+}
+
+/* Parse a numeric value (with optional suffix) from a string.
+ Returns a long double value, with input precision.
+
+ If there's an error converting the string to value - exits with
+ an error.
+
+ If there are any trailing characters after the number
+ (besides a valid suffix) - exits with an error. */
+static enum simple_strtod_error
+parse_human_number (const char *str, long double /*output */ *value,
+ size_t *precision)
+{
+ char *ptr = NULL;
+
+ enum simple_strtod_error e =
+ simple_strtod_human (str, &ptr, value, precision, scale_from);
+ if (e != SSE_OK && e != SSE_OK_PRECISION_LOSS)
+ {
+ simple_strtod_fatal (e, str);
+ return e;
+ }
+
+ if (ptr && *ptr != '\0')
+ {
+ if (inval_style != inval_ignore)
+ error (conv_exit_code, 0, _("invalid suffix in input %s: %s"),
+ quote_n (0, str), quote_n (1, ptr));
+ e = SSE_INVALID_SUFFIX;
+ }
+ return e;
+}
+
+
+/* Print the given VAL, using the requested representation.
+ The number is printed to STDOUT, with padding and alignment. */
+static int
+prepare_padded_number (const long double val, size_t precision)
+{
+ /* Generate Output. */
+ char buf[128];
+
+ size_t precision_used = user_precision == -1 ? precision : user_precision;
+
+ /* Can't reliably print too-large values without auto-scaling. */
+ unsigned int x;
+ expld (val, 10, &x);
+
+ if (scale_to == scale_none
+ && x + precision_used > MAX_UNSCALED_DIGITS)
+ {
+ if (inval_style != inval_ignore)
+ {
+ if (precision_used)
+ error (conv_exit_code, 0,
+ _("value/precision too large to be printed: '%Lg/%"PRIuMAX"'"
+ " (consider using --to)"), val, (uintmax_t)precision_used);
+ else
+ error (conv_exit_code, 0,
+ _("value too large to be printed: '%Lg'"
+ " (consider using --to)"), val);
+ }
+ return 0;
+ }
+
+ if (x > MAX_ACCEPTABLE_DIGITS - 1)
+ {
+ if (inval_style != inval_ignore)
+ error (conv_exit_code, 0, _("value too large to be printed: '%Lg'"
+ " (cannot handle values > 999Y)"), val);
+ return 0;
+ }
+
+ double_to_human (val, precision_used, buf, sizeof (buf),
+ scale_to, grouping, round_style);
+ if (suffix)
+ strncat (buf, suffix, sizeof (buf) - strlen (buf) -1);
+
+ devmsg ("formatting output:\n value: %Lf\n humanized: %s\n",
+ val, quote (buf));
+
+ if (padding_width && strlen (buf) < padding_width)
+ {
+ size_t w = padding_width;
+ mbsalign (buf, padding_buffer, padding_buffer_size, &w,
+ padding_alignment, MBA_UNIBYTE_ONLY);
+
+ devmsg (" After padding: %s\n", quote (padding_buffer));
+ }
+ else
+ {
+ setup_padding_buffer (strlen (buf) + 1);
+ strcpy (padding_buffer, buf);
+ }
+
+ return 1;
+}
+
+static void
+print_padded_number (void)
+{
+ if (format_str_prefix)
+ fputs (format_str_prefix, stdout);
+
+ fputs (padding_buffer, stdout);
+
+ if (format_str_suffix)
+ fputs (format_str_suffix, stdout);
+}
+
+/* Converts the TEXT number string to the requested representation,
+ and handles automatic suffix addition. */
+static int
+process_suffixed_number (char *text, long double *result,
+ size_t *precision, long int field)
+{
+ if (suffix && strlen (text) > strlen (suffix))
+ {
+ char *possible_suffix = text + strlen (text) - strlen (suffix);
+
+ if (STREQ (suffix, possible_suffix))
+ {
+ /* trim suffix, ONLY if it's at the end of the text. */
+ *possible_suffix = '\0';
+ devmsg ("trimming suffix %s\n", quote (suffix));
+ }
+ else
+ devmsg ("no valid suffix found\n");
+ }
+
+ /* Skip white space - always. */
+ char *p = text;
+ while (*p && isblank (to_uchar (*p)))
+ ++p;
+ const unsigned int skip_count = text - p;
+
+ /* setup auto-padding. */
+ if (auto_padding)
+ {
+ if (skip_count > 0 || field > 1)
+ {
+ padding_width = strlen (text);
+ setup_padding_buffer (padding_width);
+ }
+ else
+ {
+ padding_width = 0;
+ }
+ devmsg ("setting Auto-Padding to %ld characters\n", padding_width);
+ }
+
+ long double val = 0;
+ enum simple_strtod_error e = parse_human_number (p, &val, precision);
+ if (e == SSE_OK_PRECISION_LOSS && debug)
+ error (0, 0, _("large input value %s: possible precision loss"),
+ quote (p));
+
+ if (from_unit_size != 1 || to_unit_size != 1)
+ val = (val * from_unit_size) / to_unit_size;
+
+ *result = val;
+
+ return (e == SSE_OK || e == SSE_OK_PRECISION_LOSS);
+}
+
+/* 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*
+next_field (char **line)
+{
+ char *field_start = *line;
+ char *field_end = field_start;
+
+ if (delimiter != DELIMITER_DEFAULT)
+ {
+ if (*field_start != delimiter)
+ {
+ while (*field_end && *field_end != delimiter)
+ ++field_end;
+ }
+ /* else empty field */
+ }
+ else
+ {
+ /* keep any space prefix in the returned field */
+ while (*field_end && field_sep (*field_end))
+ ++field_end;
+
+ while (*field_end && ! field_sep (*field_end))
+ ++field_end;
+ }
+
+ *line = field_end;
+ return field_start;
+}
+
+static bool _GL_ATTRIBUTE_PURE
+include_field (size_t field)
+{
+ struct field_range_pair *p = frp;
+ if (!p)
+ return field == 1;
+
+ while (p->lo != SIZE_MAX)
+ {
+ if (p->lo <= field && p->hi >= field)
+ return true;
+ ++p;
+ }
+ return false;
+}
+
+/* Convert and output the given field. If it is not included in the set
+ of fields to process just output the original */
+static bool
+process_field (char *text, size_t field)
+{
+ long double val = 0;
+ size_t precision = 0;
+ bool valid_number = true;
+
+ if (include_field (field))
+ {
+ valid_number =
+ process_suffixed_number (text, &val, &precision, field);
+
+ if (valid_number)
+ valid_number = prepare_padded_number (val, precision);
+
+ if (valid_number)
+ print_padded_number ();
+ else
+ fputs (text, stdout);
+ }
+ else
+ fputs (text, stdout);
+
+ return valid_number;
+}
+
+/* Convert number in a given line of text.
+ NEWLINE specifies whether to output a '\n' for this "line". */
+static int
+process_line (char *line, bool newline)
+{
+ char *next;
+ size_t field = 0;
+ bool valid_number = true;
+
+ while (true) {
+ ++field;
+ next = next_field (&line);
+
+ if (*line != '\0')
+ {
+ /* nul terminate the current field string and process */
+ *line = '\0';
+
+ if (! process_field (next, field))
+ valid_number = false;
+
+ fputc ((delimiter == DELIMITER_DEFAULT) ?
+ ' ' : delimiter, stdout);
+ ++line;
+ }
+ else
+ {
+ /* end of the line, process the last field and finish */
+ if (! process_field (next, field))
+ valid_number = false;
+
+ break;
+ }
+ }
+
+ if (newline)
+ putchar (line_delim);
+
+ return valid_number;
+}
+
+int
+main (int argc, char **argv)
+{
+ int valid_numbers = 1;
+ bool locale_ok;
+
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ locale_ok = setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+#if HAVE_FPSETPREC
+ /* Enabled extended precision if needed. */
+ fpsetprec (FP_PE);
+#endif
+
+ decimal_point = nl_langinfo (RADIXCHAR);
+ if (decimal_point == NULL || strlen (decimal_point) == 0)
+ decimal_point = ".";
+ decimal_point_length = strlen (decimal_point);
+
+ atexit (close_stdout);
+
+ while (true)
+ {
+ int c = getopt_long (argc, argv, "d:z", longopts, NULL);
+
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case FROM_OPTION:
+ scale_from = XARGMATCH ("--from", optarg,
+ scale_from_args, scale_from_types);
+ break;
+
+ case FROM_UNIT_OPTION:
+ from_unit_size = unit_to_umax (optarg);
+ break;
+
+ case TO_OPTION:
+ scale_to =
+ XARGMATCH ("--to", optarg, scale_to_args, scale_to_types);
+ break;
+
+ case TO_UNIT_OPTION:
+ to_unit_size = unit_to_umax (optarg);
+ break;
+
+ case ROUND_OPTION:
+ round_style = XARGMATCH ("--round", optarg, round_args, round_types);
+ break;
+
+ case GROUPING_OPTION:
+ grouping = 1;
+ break;
+
+ case PADDING_OPTION:
+ if (xstrtol (optarg, NULL, 10, &padding_width, "") != LONGINT_OK
+ || padding_width == 0)
+ error (EXIT_FAILURE, 0, _("invalid padding value %s"),
+ quote (optarg));
+ if (padding_width < 0)
+ {
+ padding_alignment = MBS_ALIGN_LEFT;
+ padding_width = -padding_width;
+ }
+ /* TODO: We probably want to apply a specific --padding
+ to --header lines too. */
+ break;
+
+ case FIELD_OPTION:
+ if (n_frp)
+ error (EXIT_FAILURE, 0, _("multiple field specifications"));
+ set_fields (optarg, SETFLD_ALLOW_DASH);
+ break;
+
+ case 'd':
+ /* Interpret -d '' to mean 'use the NUL byte as the delimiter.' */
+ if (optarg[0] != '\0' && optarg[1] != '\0')
+ error (EXIT_FAILURE, 0,
+ _("the delimiter must be a single character"));
+ delimiter = optarg[0];
+ break;
+
+ case 'z':
+ line_delim = '\0';
+ break;
+
+ case SUFFIX_OPTION:
+ suffix = optarg;
+ break;
+
+ case DEBUG_OPTION:
+ debug = true;
+ break;
+
+ case DEV_DEBUG_OPTION:
+ dev_debug = true;
+ debug = true;
+ break;
+
+ case HEADER_OPTION:
+ if (optarg)
+ {
+ if (xstrtoumax (optarg, NULL, 10, &header, "") != LONGINT_OK
+ || header == 0)
+ error (EXIT_FAILURE, 0, _("invalid header value %s"),
+ quote (optarg));
+ }
+ else
+ {
+ header = 1;
+ }
+ break;
+
+ case FORMAT_OPTION:
+ format_str = optarg;
+ break;
+
+ case INVALID_OPTION:
+ inval_style = XARGMATCH ("--invalid", optarg,
+ inval_args, inval_types);
+ break;
+
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
+ }
+
+ if (format_str != NULL && grouping)
+ error (EXIT_FAILURE, 0, _("--grouping cannot be combined with --format"));
+
+ if (debug && ! locale_ok)
+ error (0, 0, _("failed to set locale"));
+
+ /* Warn about no-op. */
+ if (debug && scale_from == scale_none && scale_to == scale_none
+ && !grouping && (padding_width == 0) && (format_str == NULL))
+ error (0, 0, _("no conversion option specified"));
+
+ if (format_str)
+ parse_format_string (format_str);
+
+ if (grouping)
+ {
+ if (scale_to != scale_none)
+ error (EXIT_FAILURE, 0, _("grouping cannot be combined with --to"));
+ if (debug && (strlen (nl_langinfo (THOUSEP)) == 0))
+ error (0, 0, _("grouping has no effect in this locale"));
+ }
+
+
+ setup_padding_buffer (padding_width);
+ auto_padding = (padding_width == 0 && delimiter == DELIMITER_DEFAULT);
+
+ if (inval_style != inval_abort)
+ conv_exit_code = 0;
+
+ if (argc > optind)
+ {
+ if (debug && header)
+ error (0, 0, _("--header ignored with command-line input"));
+
+ for (; optind < argc; optind++)
+ valid_numbers &= process_line (argv[optind], true);
+ }
+ else
+ {
+ char *line = NULL;
+ size_t line_allocated = 0;
+ ssize_t len;
+
+ while (header-- && getdelim (&line, &line_allocated,
+ line_delim, stdin) > 0)
+ fputs (line, stdout);
+
+ while ((len = getdelim (&line, &line_allocated,
+ line_delim, stdin)) > 0)
+ {
+ bool newline = line[len - 1] == line_delim;
+ if (newline)
+ line[len - 1] = '\0';
+ valid_numbers &= process_line (line, newline);
+ }
+
+ IF_LINT (free (line));
+
+ if (ferror (stdin))
+ error (0, errno, _("error reading input"));
+ }
+
+#ifdef lint
+ free (padding_buffer);
+ free (format_str_prefix);
+ free (format_str_suffix);
+ reset_fields ();
+#endif
+
+ if (debug && !valid_numbers)
+ error (0, 0, _("failed to convert some of the input numbers"));
+
+ int exit_status = EXIT_SUCCESS;
+ if (!valid_numbers
+ && inval_style != inval_warn && inval_style != inval_ignore)
+ exit_status = EXIT_CONVERSION_WARNINGS;
+
+ return exit_status;
+}
diff --git a/src/od.c b/src/od.c
index 706a469..74f523c 100644
--- a/src/od.c
+++ b/src/od.c
@@ -1,10 +1,10 @@
/* od -- dump files in octal and other formats
- Copyright (C) 92, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1992-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Jim Meyering. */
@@ -24,45 +23,27 @@
#include <getopt.h>
#include <sys/types.h>
#include "system.h"
+#include "argmatch.h"
#include "error.h"
+#include "ftoastr.h"
#include "quote.h"
+#include "stat-size.h"
+#include "xfreopen.h"
+#include "xprintf.h"
#include "xstrtol.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "od"
-#define AUTHORS "Jim Meyering"
-
-#include <float.h>
-
-#ifdef HAVE_LONG_DOUBLE
-typedef long double LONG_DOUBLE;
-#else
-typedef double LONG_DOUBLE;
-#endif
+#define AUTHORS proper_name ("Jim Meyering")
/* The default number of input bytes per output line. */
#define DEFAULT_BYTES_PER_BLOCK 16
-/* The number of decimal digits of precision in a float. */
-#ifndef FLT_DIG
-# define FLT_DIG 7
-#endif
-
-/* The number of decimal digits of precision in a double. */
-#ifndef DBL_DIG
-# define DBL_DIG 15
-#endif
-
-/* The number of decimal digits of precision in a long double. */
-#ifndef LDBL_DIG
-# define LDBL_DIG DBL_DIG
-#endif
-
#if HAVE_UNSIGNED_LONG_LONG_INT
typedef unsigned long long int unsigned_long_long_int;
#else
-/* This is just a place-holder to avoid a few `#if' directives.
+/* This is just a place-holder to avoid a few '#if' directives.
In this case, the type isn't actually used. */
typedef unsigned long int unsigned_long_long_int;
#endif
@@ -93,35 +74,44 @@ enum output_format
CHARACTER
};
-/* The maximum number of bytes needed for a format string,
- including the trailing null. */
+#define MAX_INTEGRAL_TYPE_SIZE sizeof (unsigned_long_long_int)
+
+/* The maximum number of bytes needed for a format string, including
+ the trailing nul. Each format string expects a variable amount of
+ padding (guaranteed to be at least 1 plus the field width), then an
+ element that will be formatted in the field. */
enum
{
FMT_BYTES_ALLOCATED =
- MAX ((sizeof " %0" - 1 + INT_STRLEN_BOUND (int)
- + MAX (sizeof "ld",
- MAX (sizeof PRIdMAX,
- MAX (sizeof PRIoMAX,
- MAX (sizeof PRIuMAX,
- sizeof PRIxMAX))))),
- sizeof " %.Le" + 2 * INT_STRLEN_BOUND (int))
+ (sizeof "%*.99" - 1
+ + MAX (sizeof "ld",
+ MAX (sizeof PRIdMAX,
+ MAX (sizeof PRIoMAX,
+ MAX (sizeof PRIuMAX,
+ sizeof PRIxMAX)))))
};
-/* Each output format specification (from `-t spec' or from
+/* Ensure that our choice for FMT_BYTES_ALLOCATED is reasonable. */
+verify (MAX_INTEGRAL_TYPE_SIZE * CHAR_BIT / 3 <= 99);
+
+/* Each output format specification (from '-t spec' or from
old-style options) is represented by one of these structures. */
struct tspec
{
enum output_format fmt;
- enum size_spec size;
- void (*print_function) (size_t, void const *, char const *);
- char fmt_string[FMT_BYTES_ALLOCATED];
+ enum size_spec size; /* Type of input object. */
+ /* FIELDS is the number of fields per line, BLANK is the number of
+ fields to leave blank. WIDTH is width of one field, excluding
+ leading space, and PAD is total pad to divide among FIELDS.
+ PAD is at least as large as FIELDS. */
+ void (*print_function) (size_t fields, size_t blank, void const *data,
+ char const *fmt, int width, int pad);
+ char fmt_string[FMT_BYTES_ALLOCATED]; /* Of the style "%*d". */
bool hexl_mode_trailer;
- int field_width;
+ int field_width; /* Minimum width of a field, excluding leading space. */
+ int pad_width; /* Total padding to be divided among fields. */
};
-/* The name this program was run with. */
-char *program_name;
-
/* Convert the number of 8-bit bytes of a binary representation to
the number of characters (digits + sign if the type is signed)
required to represent the same quantity in the specified base/type.
@@ -144,13 +134,10 @@ static unsigned int const bytes_to_unsigned_dec_digits[] =
static unsigned int const bytes_to_hex_digits[] =
{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32};
-#define MAX_INTEGRAL_TYPE_SIZE sizeof (unsigned_long_long_int)
-
/* It'll be a while before we see integral types wider than 16 bytes,
but if/when it happens, this check will catch it. Without this check,
a wider type would provoke a buffer overrun. */
-verify (MAX_INTEGRAL_TYPE_SIZE
- < sizeof bytes_to_hex_digits / sizeof *bytes_to_hex_digits);
+verify (MAX_INTEGRAL_TYPE_SIZE < ARRAY_CARDINALITY (bytes_to_hex_digits));
/* Make sure the other arrays have the same length. */
verify (sizeof bytes_to_oct_digits == sizeof bytes_to_signed_dec_digits);
@@ -168,15 +155,15 @@ static const int width_bytes[] =
sizeof (unsigned_long_long_int),
sizeof (float),
sizeof (double),
- sizeof (LONG_DOUBLE)
+ sizeof (long double)
};
-/* Ensure that for each member of `enum size_spec' there is an
+/* Ensure that for each member of 'enum size_spec' there is an
initializer in the width_bytes array. */
-verify (sizeof width_bytes / sizeof width_bytes[0] == N_SIZE_SPECS);
+verify (ARRAY_CARDINALITY (width_bytes) == N_SIZE_SPECS);
/* Names for some non-printing characters. */
-static const char *const charname[33] =
+static char const charname[33][4] =
{
"nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
"bs", "ht", "nl", "vt", "ff", "cr", "so", "si",
@@ -196,7 +183,10 @@ static int address_base;
/* Width of a normal address. */
static int address_pad_len;
+/* Minimum length when detecting --strings. */
static size_t string_min;
+
+/* True when in --strings mode. */
static bool flag_dump_strings;
/* True if we should recognize the older non-option arguments
@@ -204,7 +194,7 @@ static bool flag_dump_strings;
offset and pseudo-start address. */
static bool traditional;
-/* True if an old-style `pseudo-address' was specified. */
+/* True if an old-style 'pseudo-address' was specified. */
static bool flag_pseudo_start;
/* The difference between the old-style pseudo starting address and
@@ -268,16 +258,40 @@ static bool have_read_stdin;
/* Map the size in bytes to a type identifier. */
static enum size_spec integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1];
-#define MAX_FP_TYPE_SIZE sizeof (LONG_DOUBLE)
+#define MAX_FP_TYPE_SIZE sizeof (long double)
static enum size_spec fp_type_size[MAX_FP_TYPE_SIZE + 1];
+#ifndef WORDS_BIGENDIAN
+# define WORDS_BIGENDIAN 0
+#endif
+
+/* Use native endianess by default. */
+static bool input_swap;
+
static char const short_options[] = "A:aBbcDdeFfHhIij:LlN:OoS:st:vw::Xx";
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
{
- TRADITIONAL_OPTION = CHAR_MAX + 1
+ TRADITIONAL_OPTION = CHAR_MAX + 1,
+ ENDIAN_OPTION,
+};
+
+enum endian_type
+{
+ endian_little,
+ endian_big
+};
+
+static char const *const endian_args[] =
+{
+ "little", "big", NULL
+};
+
+static enum endian_type const endian_types[] =
+{
+ endian_little, endian_big
};
static struct option const long_options[] =
@@ -290,6 +304,7 @@ static struct option const long_options[] =
{"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},
@@ -300,8 +315,7 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
@@ -309,37 +323,53 @@ Usage: %s [OPTION]... [FILE]...\n\
or: %s [-abcdfilosx]... [FILE] [[+]OFFSET[.][b]]\n\
or: %s --traditional [OPTION]... [FILE] [[+]OFFSET[.][b] [+][LABEL][.][b]]\n\
"),
- program_name, program_name, program_name);
+ program_name, program_name, program_name);
fputs (_("\n\
Write an unambiguous representation, octal bytes by default,\n\
of FILE to standard output. With more than one FILE argument,\n\
concatenate them in the listed order to form the input.\n\
-With no FILE, or when FILE is -, read standard input.\n\
-\n\
"), stdout);
+
+ emit_stdin_note ();
+
fputs (_("\
-All arguments to long options are mandatory for short options.\n\
+\n\
+If first and second call formats both apply, the second format is assumed\n\
+if the last operand begins with + or (if there are 2 operands) a digit.\n\
+An OFFSET operand means -j OFFSET. LABEL is the pseudo-address\n\
+at first byte printed, incremented when dump is progressing.\n\
+For OFFSET and LABEL, a 0x or 0X prefix indicates hexadecimal;\n\
+suffixes may be . for octal and b for multiply by 512.\n\
"), stdout);
+
+ emit_mandatory_arg_note ();
+
fputs (_("\
- -A, --address-radix=RADIX decide how file offsets are printed\n\
+ -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, --strings[=BYTES] output strings of at least BYTES graphic chars\n\
+ -S BYTES, --strings[=BYTES] output strings of at least BYTES graphic chars;\
+\n\
+ 3 is implied when BYTES is not specified\n\
-t, --format=TYPE select output format or formats\n\
-v, --output-duplicates do not use * to mark line suppression\n\
- -w, --width[=BYTES] output BYTES bytes per output line\n\
- --traditional accept arguments in traditional form\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);
fputs (_("\
\n\
+\n\
Traditional format specifications may be intermixed; they accumulate:\n\
-a same as -t a, select named characters, ignoring high-order bit\n\
-b same as -t o1, select octal bytes\n\
- -c same as -t c, select ASCII characters or backslash escapes\n\
+ -c same as -t c, select printable characters or backslash escapes\n\
-d same as -t u2, select unsigned decimal 2-byte units\n\
"), stdout);
fputs (_("\
@@ -352,19 +382,10 @@ Traditional format specifications may be intermixed; they accumulate:\n\
"), stdout);
fputs (_("\
\n\
-If first and second call formats both apply, the second format is assumed\n\
-if the last operand begins with + or (if there are 2 operands) a digit.\n\
-An OFFSET operand means -j OFFSET. LABEL is the pseudo-address\n\
-at first byte printed, incremented when dump is progressing.\n\
-For OFFSET and LABEL, a 0x or 0X prefix indicates hexadecimal;\n\
-suffixes may be . for octal and b for multiply by 512.\n\
-"), stdout);
- fputs (_("\
\n\
TYPE is made up of one or more of these specifications:\n\
-\n\
a named character, ignoring high-order bit\n\
- c ASCII character or backslash escape\n\
+ c printable character or backslash escape\n\
"), stdout);
fputs (_("\
d[SIZE] signed decimal, SIZE bytes per integer\n\
@@ -375,121 +396,89 @@ TYPE is made up of one or more of these specifications:\n\
"), stdout);
fputs (_("\
\n\
-SIZE is a number. For TYPE in doux, SIZE may also be C for\n\
+SIZE is a number. For TYPE in [doux], SIZE may also be C for\n\
sizeof(char), S for sizeof(short), I for sizeof(int) or L for\n\
sizeof(long). If TYPE is f, SIZE may also be F for sizeof(float), D\n\
for sizeof(double) or L for sizeof(long double).\n\
"), stdout);
fputs (_("\
\n\
-RADIX is d for decimal, o for octal, x for hexadecimal or n for none.\n\
-BYTES is hexadecimal with 0x or 0X prefix, it is multiplied by 512\n\
-with b suffix, by 1024 with k and by 1048576 with m. Adding a z suffix to\n\
-any type adds a display of printable characters to the end of each line\n\
-of output. \
+Adding a z suffix to any type displays printable characters at the end of\n\
+each output line.\n\
"), stdout);
fputs (_("\
---string without a number implies 3. --width without a number\n\
-implies 32. By default, od uses -A o -t d2 -w16.\n\
+\n\
+\n\
+BYTES is hex with 0x or 0X prefix, and may have a multiplier suffix:\n\
+ b 512\n\
+ KB 1000\n\
+ K 1024\n\
+ MB 1000*1000\n\
+ M 1024*1024\n\
+and so on for G, T, P, E, Z, Y.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
/* Define the print functions. */
-static void
-print_s_char (size_t n_bytes, void const *block, char const *fmt_string)
-{
- signed char const *p = block;
- size_t i;
- for (i = n_bytes / sizeof *p; i != 0; i--)
- printf (fmt_string, *p++);
-}
-
-static void
-print_char (size_t n_bytes, void const *block, char const *fmt_string)
-{
- unsigned char const *p = block;
- size_t i;
- for (i = n_bytes / sizeof *p; i != 0; i--)
- printf (fmt_string, *p++);
-}
-
-static void
-print_s_short (size_t n_bytes, void const *block, char const *fmt_string)
-{
- short int const *p = block;
- size_t i;
- for (i = n_bytes / sizeof *p; i != 0; i--)
- printf (fmt_string, *p++);
-}
-
-static void
-print_short (size_t n_bytes, void const *block, char const *fmt_string)
-{
- unsigned short int const *p = block;
- size_t i;
- for (i = n_bytes / sizeof *p; i != 0; i--)
- printf (fmt_string, *p++);
-}
-
-static void
-print_int (size_t n_bytes, void const *block, char const *fmt_string)
-{
- unsigned int const *p = block;
- size_t i;
- for (i = n_bytes / sizeof *p; i != 0; i--)
- printf (fmt_string, *p++);
+#define PRINT_FIELDS(N, T, FMT_STRING, ACTION) \
+static void \
+N (size_t fields, size_t blank, void const *block, \
+ char const *FMT_STRING, int width, int pad) \
+{ \
+ T const *p = block; \
+ uintmax_t i; \
+ int pad_remaining = pad; \
+ for (i = fields; blank < i; i--) \
+ { \
+ int next_pad = pad * (i - 1) / fields; \
+ int adjusted_width = pad_remaining - next_pad + width; \
+ T x; \
+ if (input_swap && sizeof (T) > 1) \
+ { \
+ size_t j; \
+ union { \
+ T x; \
+ char b[sizeof (T)]; \
+ } u; \
+ for (j = 0; j < sizeof (T); j++) \
+ u.b[j] = ((const char *) p)[sizeof (T) - 1 - j]; \
+ x = u.x; \
+ } \
+ else \
+ x = *p; \
+ p++; \
+ ACTION; \
+ pad_remaining = next_pad; \
+ } \
}
-static void
-print_long (size_t n_bytes, void const *block, char const *fmt_string)
-{
- unsigned long int const *p = block;
- size_t i;
- for (i = n_bytes / sizeof *p; i != 0; i--)
- printf (fmt_string, *p++);
-}
+#define PRINT_TYPE(N, T) \
+ PRINT_FIELDS (N, T, fmt_string, xprintf (fmt_string, adjusted_width, x))
-static void
-print_long_long (size_t n_bytes, void const *block, char const *fmt_string)
-{
- unsigned_long_long_int const *p = block;
- size_t i;
- for (i = n_bytes / sizeof *p; i != 0; i--)
- printf (fmt_string, *p++);
-}
+#define PRINT_FLOATTYPE(N, T, FTOASTR, BUFSIZE) \
+ PRINT_FIELDS (N, T, fmt_string _GL_UNUSED, \
+ char buf[BUFSIZE]; \
+ FTOASTR (buf, sizeof buf, 0, 0, x); \
+ xprintf ("%*s", adjusted_width, buf))
-static void
-print_float (size_t n_bytes, void const *block, char const *fmt_string)
-{
- float const *p = block;
- size_t i;
- for (i = n_bytes / sizeof *p; i != 0; i--)
- printf (fmt_string, *p++);
-}
+PRINT_TYPE (print_s_char, signed char)
+PRINT_TYPE (print_char, unsigned char)
+PRINT_TYPE (print_s_short, short int)
+PRINT_TYPE (print_short, unsigned short int)
+PRINT_TYPE (print_int, unsigned int)
+PRINT_TYPE (print_long, unsigned long int)
+PRINT_TYPE (print_long_long, unsigned_long_long_int)
-static void
-print_double (size_t n_bytes, void const *block, char const *fmt_string)
-{
- double const *p = block;
- size_t i;
- for (i = n_bytes / sizeof *p; i != 0; i--)
- printf (fmt_string, *p++);
-}
+PRINT_FLOATTYPE (print_float, float, ftoastr, FLT_BUFSIZE_BOUND)
+PRINT_FLOATTYPE (print_double, double, dtoastr, DBL_BUFSIZE_BOUND)
+PRINT_FLOATTYPE (print_long_double, long double, ldtoastr, LDBL_BUFSIZE_BOUND)
-#ifdef HAVE_LONG_DOUBLE
-static void
-print_long_double (size_t n_bytes, void const *block, char const *fmt_string)
-{
- long double const *p = block;
- size_t i;
- for (i = n_bytes / sizeof *p; i != 0; i--)
- printf (fmt_string, *p++);
-}
-#endif
+#undef PRINT_TYPE
+#undef PRINT_FLOATTYPE
static void
dump_hexl_mode_trailer (size_t n_bytes, const char *block)
@@ -506,83 +495,92 @@ dump_hexl_mode_trailer (size_t n_bytes, const char *block)
}
static void
-print_named_ascii (size_t n_bytes, void const *block,
- const char *unused_fmt_string ATTRIBUTE_UNUSED)
+print_named_ascii (size_t fields, size_t blank, void const *block,
+ const char *unused_fmt_string _GL_UNUSED,
+ int width, int pad)
{
unsigned char const *p = block;
- size_t i;
- for (i = n_bytes; i > 0; i--)
+ uintmax_t i;
+ int pad_remaining = pad;
+ for (i = fields; blank < i; i--)
{
+ int next_pad = pad * (i - 1) / fields;
int masked_c = *p++ & 0x7f;
const char *s;
- char buf[5];
+ char buf[2];
if (masked_c == 127)
- s = "del";
+ s = "del";
else if (masked_c <= 040)
- s = charname[masked_c];
+ s = charname[masked_c];
else
- {
- sprintf (buf, " %c", masked_c);
- s = buf;
- }
-
- printf (" %3s", s);
+ {
+ buf[0] = masked_c;
+ buf[1] = 0;
+ s = buf;
+ }
+
+ xprintf ("%*s", pad_remaining - next_pad + width, s);
+ pad_remaining = next_pad;
}
}
static void
-print_ascii (size_t n_bytes, void const *block,
- const char *unused_fmt_string ATTRIBUTE_UNUSED)
+print_ascii (size_t fields, size_t blank, void const *block,
+ const char *unused_fmt_string _GL_UNUSED, int width,
+ int pad)
{
unsigned char const *p = block;
- size_t i;
- for (i = n_bytes; i > 0; i--)
+ uintmax_t i;
+ int pad_remaining = pad;
+ for (i = fields; blank < i; i--)
{
+ int next_pad = pad * (i - 1) / fields;
unsigned char c = *p++;
const char *s;
- char buf[5];
+ char buf[4];
switch (c)
- {
- case '\0':
- s = " \\0";
- break;
-
- case '\a':
- s = " \\a";
- break;
-
- case '\b':
- s = " \\b";
- break;
-
- case '\f':
- s = " \\f";
- break;
-
- case '\n':
- s = " \\n";
- break;
-
- case '\r':
- s = " \\r";
- break;
-
- case '\t':
- s = " \\t";
- break;
-
- case '\v':
- s = " \\v";
- break;
-
- default:
- sprintf (buf, (isprint (c) ? " %c" : "%03o"), c);
- s = buf;
- }
-
- printf (" %3s", s);
+ {
+ case '\0':
+ s = "\\0";
+ break;
+
+ case '\a':
+ s = "\\a";
+ break;
+
+ case '\b':
+ s = "\\b";
+ break;
+
+ case '\f':
+ s = "\\f";
+ break;
+
+ case '\n':
+ s = "\\n";
+ break;
+
+ case '\r':
+ s = "\\r";
+ break;
+
+ case '\t':
+ s = "\\t";
+ break;
+
+ case '\v':
+ s = "\\v";
+ break;
+
+ default:
+ sprintf (buf, (isprint (c) ? "%c" : "%03o"), c);
+ s = buf;
+ }
+
+ xprintf ("%*s", pad_remaining - next_pad + width, s);
+ pad_remaining = next_pad;
}
}
@@ -604,7 +602,7 @@ simple_strtoul (const char *s, const char **p, unsigned long int *val)
{
int c = *s++ - '0';
if (sum > (ULONG_MAX - c) / 10)
- return false;
+ return false;
sum = sum * 10 + c;
}
*p = s;
@@ -622,25 +620,27 @@ simple_strtoul (const char *s, const char **p, unsigned long int *val)
fmt = SIGNED_DECIMAL;
size = INT or LONG; (whichever integral_type_size[4] resolves to)
print_function = print_int; (assuming size == INT)
- fmt_string = "%011d%c";
+ field_width = 11;
+ fmt_string = "%*d";
}
+ pad_width is determined later, but is at least as large as the
+ number of fields printed per row.
S_ORIG is solely for reporting errors. It should be the full format
string argument.
*/
static bool
decode_one_format (const char *s_orig, const char *s, const char **next,
- struct tspec *tspec)
+ struct tspec *tspec)
{
enum size_spec size_spec;
unsigned long int size;
enum output_format fmt;
- const char *pre_fmt_string;
- void (*print_function) (size_t, void const *, char const *);
+ void (*print_function) (size_t, size_t, void const *, char const *,
+ int, int);
const char *p;
char c;
int field_width;
- int precision;
assert (tspec != NULL);
@@ -653,51 +653,52 @@ decode_one_format (const char *s_orig, const char *s, const char **next,
c = *s;
++s;
switch (*s)
- {
- case 'C':
- ++s;
- size = sizeof (char);
- break;
-
- case 'S':
- ++s;
- size = sizeof (short int);
- break;
-
- case 'I':
- ++s;
- size = sizeof (int);
- break;
-
- case 'L':
- ++s;
- size = sizeof (long int);
- break;
-
- default:
- if (! simple_strtoul (s, &p, &size))
- {
- /* The integer at P in S would overflow an unsigned long int.
- A digit string that long is sufficiently odd looking
- that the following diagnostic is sufficient. */
- error (0, 0, _("invalid type string %s"), quote (s_orig));
- return false;
- }
- if (p == s)
- size = sizeof (int);
- else
- {
- if (MAX_INTEGRAL_TYPE_SIZE < size
- || integral_type_size[size] == NO_SIZE)
- {
- error (0, 0, _("invalid type string %s;\n\
-this system doesn't provide a %lu-byte integral type"), quote (s_orig), size);
- return false;
- }
- s = p;
- }
- break;
- }
+ {
+ case 'C':
+ ++s;
+ size = sizeof (char);
+ break;
+
+ case 'S':
+ ++s;
+ size = sizeof (short int);
+ break;
+
+ case 'I':
+ ++s;
+ size = sizeof (int);
+ break;
+
+ case 'L':
+ ++s;
+ size = sizeof (long int);
+ break;
+
+ default:
+ if (! simple_strtoul (s, &p, &size))
+ {
+ /* The integer at P in S would overflow an unsigned long int.
+ A digit string that long is sufficiently odd looking
+ that the following diagnostic is sufficient. */
+ error (0, 0, _("invalid type string %s"), quote (s_orig));
+ return false;
+ }
+ if (p == s)
+ size = sizeof (int);
+ else
+ {
+ if (MAX_INTEGRAL_TYPE_SIZE < size
+ || integral_type_size[size] == NO_SIZE)
+ {
+ error (0, 0, _("invalid type string %s;\nthis system"
+ " doesn't provide a %lu-byte integral type"),
+ quote (s_orig), size);
+ return false;
+ }
+ s = p;
+ }
+ break;
+ }
#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \
((Spec) == LONG_LONG ? (Max_format) \
@@ -707,149 +708,149 @@ this system doesn't provide a %lu-byte integral type"), quote (s_orig), size);
size_spec = integral_type_size[size];
switch (c)
- {
- case 'd':
- fmt = SIGNED_DECIMAL;
- sprintf (tspec->fmt_string, " %%%d%s",
- (field_width = bytes_to_signed_dec_digits[size]),
- ISPEC_TO_FORMAT (size_spec, "d", "ld", PRIdMAX));
- break;
-
- case 'o':
- fmt = OCTAL;
- sprintf (tspec->fmt_string, " %%0%d%s",
- (field_width = bytes_to_oct_digits[size]),
- ISPEC_TO_FORMAT (size_spec, "o", "lo", PRIoMAX));
- break;
-
- case 'u':
- fmt = UNSIGNED_DECIMAL;
- sprintf (tspec->fmt_string, " %%%d%s",
- (field_width = bytes_to_unsigned_dec_digits[size]),
- ISPEC_TO_FORMAT (size_spec, "u", "lu", PRIuMAX));
- break;
-
- case 'x':
- fmt = HEXADECIMAL;
- sprintf (tspec->fmt_string, " %%0%d%s",
- (field_width = bytes_to_hex_digits[size]),
- ISPEC_TO_FORMAT (size_spec, "x", "lx", PRIxMAX));
- break;
-
- default:
- abort ();
- }
+ {
+ case 'd':
+ fmt = SIGNED_DECIMAL;
+ field_width = bytes_to_signed_dec_digits[size];
+ sprintf (tspec->fmt_string, "%%*%s",
+ ISPEC_TO_FORMAT (size_spec, "d", "ld", PRIdMAX));
+ break;
+
+ case 'o':
+ fmt = OCTAL;
+ sprintf (tspec->fmt_string, "%%*.%d%s",
+ (field_width = bytes_to_oct_digits[size]),
+ ISPEC_TO_FORMAT (size_spec, "o", "lo", PRIoMAX));
+ break;
+
+ case 'u':
+ fmt = UNSIGNED_DECIMAL;
+ field_width = bytes_to_unsigned_dec_digits[size];
+ sprintf (tspec->fmt_string, "%%*%s",
+ ISPEC_TO_FORMAT (size_spec, "u", "lu", PRIuMAX));
+ break;
+
+ case 'x':
+ fmt = HEXADECIMAL;
+ sprintf (tspec->fmt_string, "%%*.%d%s",
+ (field_width = bytes_to_hex_digits[size]),
+ ISPEC_TO_FORMAT (size_spec, "x", "lx", PRIxMAX));
+ break;
+
+ default:
+ abort ();
+ }
assert (strlen (tspec->fmt_string) < FMT_BYTES_ALLOCATED);
switch (size_spec)
- {
- case CHAR:
- print_function = (fmt == SIGNED_DECIMAL
- ? print_s_char
- : print_char);
- break;
-
- case SHORT:
- print_function = (fmt == SIGNED_DECIMAL
- ? print_s_short
- : print_short);
- break;
-
- case INT:
- print_function = print_int;
- break;
-
- case LONG:
- print_function = print_long;
- break;
-
- case LONG_LONG:
- print_function = print_long_long;
- break;
-
- default:
- abort ();
- }
+ {
+ case CHAR:
+ print_function = (fmt == SIGNED_DECIMAL
+ ? print_s_char
+ : print_char);
+ break;
+
+ case SHORT:
+ print_function = (fmt == SIGNED_DECIMAL
+ ? print_s_short
+ : print_short);
+ break;
+
+ case INT:
+ print_function = print_int;
+ break;
+
+ case LONG:
+ print_function = print_long;
+ break;
+
+ case LONG_LONG:
+ print_function = print_long_long;
+ break;
+
+ default:
+ abort ();
+ }
break;
case 'f':
fmt = FLOATING_POINT;
++s;
switch (*s)
- {
- case 'F':
- ++s;
- size = sizeof (float);
- break;
-
- case 'D':
- ++s;
- size = sizeof (double);
- break;
-
- case 'L':
- ++s;
- size = sizeof (LONG_DOUBLE);
- break;
-
- default:
- if (! simple_strtoul (s, &p, &size))
- {
- /* The integer at P in S would overflow an unsigned long int.
- A digit string that long is sufficiently odd looking
- that the following diagnostic is sufficient. */
- error (0, 0, _("invalid type string %s"), quote (s_orig));
- return false;
- }
- if (p == s)
- size = sizeof (double);
- else
- {
- if (size > MAX_FP_TYPE_SIZE
- || fp_type_size[size] == NO_SIZE)
- {
- error (0, 0, _("invalid type string %s;\n\
-this system doesn't provide a %lu-byte floating point type"),
- quote (s_orig), size);
- return false;
- }
- s = p;
- }
- break;
- }
+ {
+ case 'F':
+ ++s;
+ size = sizeof (float);
+ break;
+
+ case 'D':
+ ++s;
+ size = sizeof (double);
+ break;
+
+ case 'L':
+ ++s;
+ size = sizeof (long double);
+ break;
+
+ default:
+ if (! simple_strtoul (s, &p, &size))
+ {
+ /* The integer at P in S would overflow an unsigned long int.
+ A digit string that long is sufficiently odd looking
+ that the following diagnostic is sufficient. */
+ error (0, 0, _("invalid type string %s"), quote (s_orig));
+ return false;
+ }
+ if (p == s)
+ size = sizeof (double);
+ else
+ {
+ if (size > MAX_FP_TYPE_SIZE
+ || fp_type_size[size] == NO_SIZE)
+ {
+ error (0, 0,
+ _("invalid type string %s;\n"
+ "this system doesn't provide a %lu-byte"
+ " floating point type"),
+ quote (s_orig), size);
+ return false;
+ }
+ s = p;
+ }
+ break;
+ }
size_spec = fp_type_size[size];
- switch (size_spec)
- {
- case FLOAT_SINGLE:
- print_function = print_float;
- /* Don't use %#e; not all systems support it. */
- pre_fmt_string = " %%%d.%de";
- precision = FLT_DIG;
- break;
-
- case FLOAT_DOUBLE:
- print_function = print_double;
- pre_fmt_string = " %%%d.%de";
- precision = DBL_DIG;
- break;
-
-#ifdef HAVE_LONG_DOUBLE
- case FLOAT_LONG_DOUBLE:
- print_function = print_long_double;
- pre_fmt_string = " %%%d.%dLe";
- precision = LDBL_DIG;
- break;
-#endif
-
- default:
- abort ();
- }
-
- field_width = precision + 8;
- sprintf (tspec->fmt_string, pre_fmt_string, field_width, precision);
- break;
+ {
+ struct lconv const *locale = localeconv ();
+ size_t decimal_point_len =
+ (locale->decimal_point[0] ? strlen (locale->decimal_point) : 1);
+
+ switch (size_spec)
+ {
+ case FLOAT_SINGLE:
+ print_function = print_float;
+ field_width = FLT_STRLEN_BOUND_L (decimal_point_len);
+ break;
+
+ case FLOAT_DOUBLE:
+ print_function = print_double;
+ field_width = DBL_STRLEN_BOUND_L (decimal_point_len);
+ break;
+
+ case FLOAT_LONG_DOUBLE:
+ print_function = print_long_double;
+ field_width = LDBL_STRLEN_BOUND_L (decimal_point_len);
+ break;
+
+ default:
+ abort ();
+ }
+
+ break;
+ }
case 'a':
++s;
@@ -868,8 +869,8 @@ this system doesn't provide a %lu-byte floating point type"),
break;
default:
- error (0, 0, _("invalid character `%c' in type string %s"),
- *s, quote (s_orig));
+ error (0, 0, _("invalid character '%c' in type string %s"),
+ *s, quote (s_orig));
return false;
}
@@ -904,30 +905,30 @@ open_next_file (void)
{
input_filename = *file_list;
if (input_filename == NULL)
- return ok;
+ return ok;
++file_list;
if (STREQ (input_filename, "-"))
- {
- input_filename = _("standard input");
- in_stream = stdin;
- have_read_stdin = true;
- if (O_BINARY && ! isatty (STDIN_FILENO))
- freopen (NULL, "rb", stdin);
- }
+ {
+ input_filename = _("standard input");
+ in_stream = stdin;
+ have_read_stdin = true;
+ if (O_BINARY && ! isatty (STDIN_FILENO))
+ xfreopen (NULL, "rb", stdin);
+ }
else
- {
- in_stream = fopen (input_filename, (O_BINARY ? "rb" : "r"));
- if (in_stream == NULL)
- {
- error (0, errno, "%s", input_filename);
- ok = false;
- }
- }
+ {
+ in_stream = fopen (input_filename, (O_BINARY ? "rb" : "r"));
+ if (in_stream == NULL)
+ {
+ error (0, errno, "%s", quotef (input_filename));
+ ok = false;
+ }
+ }
}
while (in_stream == NULL);
- if (limit_bytes_to_format & !flag_dump_strings)
+ if (limit_bytes_to_format && !flag_dump_strings)
setvbuf (in_stream, NULL, _IONBF, 0);
return ok;
@@ -948,17 +949,17 @@ check_and_close (int in_errno)
if (in_stream != NULL)
{
if (ferror (in_stream))
- {
- error (0, in_errno, _("%s: read error"), input_filename);
- if (! STREQ (file_list[-1], "-"))
- fclose (in_stream);
- ok = false;
- }
+ {
+ error (0, in_errno, _("%s: read error"), quotef (input_filename));
+ if (! STREQ (file_list[-1], "-"))
+ fclose (in_stream);
+ ok = false;
+ }
else if (! STREQ (file_list[-1], "-") && fclose (in_stream) != 0)
- {
- error (0, errno, "%s", input_filename);
- ok = false;
- }
+ {
+ error (0, errno, "%s", quotef (input_filename));
+ ok = false;
+ }
in_stream = NULL;
}
@@ -987,10 +988,10 @@ decode_format_string (const char *s)
const char *next;
if (n_specs_allocated <= n_specs)
- spec = X2NREALLOC (spec, &n_specs_allocated);
+ spec = X2NREALLOC (spec, &n_specs_allocated);
if (! decode_one_format (s_orig, s, &next, &spec[n_specs]))
- return false;
+ return false;
assert (s != next);
s = next;
@@ -1021,71 +1022,79 @@ skip (uintmax_t n_skip)
struct stat file_stats;
/* First try seeking. For large offsets, this extra work is
- worthwhile. If the offset is below some threshold it may be
- more efficient to move the pointer by reading. There are two
- issues when trying to seek:
- - the file must be seekable.
- - before seeking to the specified position, make sure
- that the new position is in the current file.
- Try to do that by getting file's size using fstat.
- But that will work only for regular files. */
+ worthwhile. If the offset is below some threshold it may be
+ more efficient to move the pointer by reading. There are two
+ issues when trying to seek:
+ - the file must be seekable.
+ - before seeking to the specified position, make sure
+ that the new position is in the current file.
+ Try to do that by getting file's size using fstat.
+ But that will work only for regular files. */
if (fstat (fileno (in_stream), &file_stats) == 0)
- {
- /* The st_size field is valid only for regular files
- (and for symbolic links, which cannot occur here).
- If the number of bytes left to skip is at least
- as large as the size of the current file, we can
- decrement n_skip and go on to the next file. */
-
- if (S_ISREG (file_stats.st_mode) && 0 <= file_stats.st_size)
- {
- if ((uintmax_t) file_stats.st_size <= n_skip)
- n_skip -= file_stats.st_size;
- else
- {
- if (fseeko (in_stream, n_skip, SEEK_CUR) != 0)
- {
- in_errno = errno;
- ok = false;
- }
- n_skip = 0;
- }
- }
-
- /* If it's not a regular file with nonnegative size,
- position the file pointer by reading. */
-
- else
- {
- char buf[BUFSIZ];
- size_t n_bytes_read, n_bytes_to_read = BUFSIZ;
-
- while (0 < n_skip)
- {
- if (n_skip < n_bytes_to_read)
- n_bytes_to_read = n_skip;
- n_bytes_read = fread (buf, 1, n_bytes_to_read, in_stream);
- n_skip -= n_bytes_read;
- if (n_bytes_read != n_bytes_to_read)
- {
- in_errno = errno;
- ok = false;
- n_skip = 0;
- break;
- }
- }
- }
-
- if (n_skip == 0)
- break;
- }
+ {
+ /* The st_size field is valid for regular files.
+ If the number of bytes left to skip is larger than
+ the size of the current file, we can decrement n_skip
+ and go on to the next file. Skip this optimization also
+ when st_size is no greater than the block size, because
+ some kernels report nonsense small file sizes for
+ proc-like file systems. */
+ if (usable_st_size (&file_stats)
+ && ST_BLKSIZE (file_stats) < file_stats.st_size)
+ {
+ if ((uintmax_t) file_stats.st_size < n_skip)
+ n_skip -= file_stats.st_size;
+ else
+ {
+ if (fseeko (in_stream, n_skip, SEEK_CUR) != 0)
+ {
+ in_errno = errno;
+ ok = false;
+ }
+ n_skip = 0;
+ }
+ }
+
+ /* If it's not a regular file with nonnegative size,
+ or if it's so small that it might be in a proc-like file system,
+ position the file pointer by reading. */
+
+ else
+ {
+ char buf[BUFSIZ];
+ size_t n_bytes_read, n_bytes_to_read = BUFSIZ;
+
+ while (0 < n_skip)
+ {
+ if (n_skip < n_bytes_to_read)
+ n_bytes_to_read = n_skip;
+ n_bytes_read = fread (buf, 1, n_bytes_to_read, in_stream);
+ n_skip -= n_bytes_read;
+ if (n_bytes_read != n_bytes_to_read)
+ {
+ if (ferror (in_stream))
+ {
+ in_errno = errno;
+ ok = false;
+ n_skip = 0;
+ break;
+ }
+ if (feof (in_stream))
+ break;
+ }
+ }
+ }
+
+ if (n_skip == 0)
+ break;
+ }
else /* cannot fstat() file */
- {
- error (0, errno, "%s", input_filename);
- ok = false;
- }
+ {
+ error (0, errno, "%s", quotef (input_filename));
+ ok = false;
+ }
ok &= check_and_close (in_errno);
@@ -1099,7 +1108,8 @@ skip (uintmax_t n_skip)
}
static void
-format_address_none (uintmax_t address ATTRIBUTE_UNUSED, char c ATTRIBUTE_UNUSED)
+format_address_none (uintmax_t address _GL_UNUSED,
+ char c _GL_UNUSED)
{
}
@@ -1120,19 +1130,19 @@ format_address_std (uintmax_t address, char c)
{
case 8:
do
- *--p = '0' + (address & 7);
+ *--p = '0' + (address & 7);
while ((address >>= 3) != 0);
break;
case 10:
do
- *--p = '0' + (address % 10);
+ *--p = '0' + (address % 10);
while ((address /= 10) != 0);
break;
case 16:
do
- *--p = "0123456789abcdef"[address & 15];
+ *--p = "0123456789abcdef"[address & 15];
while ((address >>= 4) != 0);
break;
}
@@ -1167,12 +1177,11 @@ format_address_label (uintmax_t address, char c)
for a sequence of identical input blocks is the output for the first
block followed by an asterisk alone on a line. It is valid to compare
the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
- That condition may be false only for the last input block -- and then
- only when it has not been padded to length BYTES_PER_BLOCK. */
+ That condition may be false only for the last input block. */
static void
write_block (uintmax_t current_offset, size_t n_bytes,
- const char *prev_block, const char *curr_block)
+ const char *prev_block, const char *curr_block)
{
static bool first = true;
static bool prev_pair_equal = false;
@@ -1184,15 +1193,15 @@ write_block (uintmax_t current_offset, size_t n_bytes,
&& EQUAL_BLOCKS (prev_block, curr_block))
{
if (prev_pair_equal)
- {
- /* The two preceding blocks were equal, and the current
- block is the same as the last one, so print nothing. */
- }
+ {
+ /* The two preceding blocks were equal, and the current
+ block is the same as the last one, so print nothing. */
+ }
else
- {
- printf ("*\n");
- prev_pair_equal = true;
- }
+ {
+ printf ("*\n");
+ prev_pair_equal = true;
+ }
}
else
{
@@ -1200,23 +1209,28 @@ write_block (uintmax_t current_offset, size_t n_bytes,
prev_pair_equal = false;
for (i = 0; i < n_specs; i++)
- {
- if (i == 0)
- format_address (current_offset, '\0');
- else
- printf ("%*s", address_pad_len, "");
- (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
- if (spec[i].hexl_mode_trailer)
- {
- /* space-pad out to full line width, then dump the trailer */
- int datum_width = width_bytes[spec[i].size];
- int blank_fields = (bytes_per_block - n_bytes) / datum_width;
- int field_width = spec[i].field_width + 1;
- printf ("%*s", blank_fields * field_width, "");
- dump_hexl_mode_trailer (n_bytes, curr_block);
- }
- putchar ('\n');
- }
+ {
+ int datum_width = width_bytes[spec[i].size];
+ int fields_per_block = bytes_per_block / datum_width;
+ int blank_fields = (bytes_per_block - n_bytes) / datum_width;
+ if (i == 0)
+ format_address (current_offset, '\0');
+ else
+ printf ("%*s", address_pad_len, "");
+ (*spec[i].print_function) (fields_per_block, blank_fields,
+ curr_block, spec[i].fmt_string,
+ spec[i].field_width, spec[i].pad_width);
+ if (spec[i].hexl_mode_trailer)
+ {
+ /* space-pad out to full line width, then dump the trailer */
+ int field_width = spec[i].field_width;
+ int pad_width = (spec[i].pad_width * blank_fields
+ / fields_per_block);
+ printf ("%*s", blank_fields * field_width + pad_width, "");
+ dump_hexl_mode_trailer (n_bytes, curr_block);
+ }
+ putchar ('\n');
+ }
}
first = false;
}
@@ -1244,7 +1258,7 @@ read_char (int *c)
*c = fgetc (in_stream);
if (*c != EOF)
- break;
+ break;
ok &= check_and_close (errno);
@@ -1277,9 +1291,6 @@ read_block (size_t n, char *block, size_t *n_bytes_in_buffer)
*n_bytes_in_buffer = 0;
- if (n == 0)
- return true;
-
while (in_stream != NULL) /* EOF. */
{
size_t n_needed;
@@ -1291,7 +1302,7 @@ read_block (size_t n, char *block, size_t *n_bytes_in_buffer)
*n_bytes_in_buffer += n_read;
if (n_read == n_needed)
- break;
+ break;
ok &= check_and_close (errno);
@@ -1304,7 +1315,7 @@ read_block (size_t n, char *block, size_t *n_bytes_in_buffer)
/* Return the least common multiple of the sizes associated
with the format specs. */
-static int
+static int _GL_ATTRIBUTE_PURE
get_lcm (void)
{
size_t i;
@@ -1330,17 +1341,17 @@ parse_old_offset (const char *s, uintmax_t *offset)
if (s[0] == '+')
++s;
- /* Determine the radix we'll use to interpret S. If there is a `.',
- it's decimal, otherwise, if the string begins with `0X'or `0x',
+ /* Determine the radix we'll use to interpret S. If there is a '.',
+ it's decimal, otherwise, if the string begins with '0X'or '0x',
it's hexadecimal, else octal. */
if (strchr (s, '.') != NULL)
radix = 10;
else
{
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
- radix = 16;
+ radix = 16;
else
- radix = 8;
+ radix = 8;
}
return xstrtoumax (s, NULL, radix, offset, "Bb") == LONGINT_OK;
@@ -1375,38 +1386,38 @@ dump (void)
if (limit_bytes_to_format)
{
while (1)
- {
- size_t n_needed;
- if (current_offset >= end_offset)
- {
- n_bytes_read = 0;
- break;
- }
- n_needed = MIN (end_offset - current_offset,
- (uintmax_t) bytes_per_block);
- ok &= read_block (n_needed, block[idx], &n_bytes_read);
- if (n_bytes_read < bytes_per_block)
- break;
- assert (n_bytes_read == bytes_per_block);
- write_block (current_offset, n_bytes_read,
- block[!idx], block[idx]);
- current_offset += n_bytes_read;
- idx = !idx;
- }
+ {
+ size_t n_needed;
+ if (current_offset >= end_offset)
+ {
+ n_bytes_read = 0;
+ break;
+ }
+ n_needed = MIN (end_offset - current_offset,
+ (uintmax_t) bytes_per_block);
+ ok &= read_block (n_needed, block[idx], &n_bytes_read);
+ if (n_bytes_read < bytes_per_block)
+ break;
+ assert (n_bytes_read == bytes_per_block);
+ write_block (current_offset, n_bytes_read,
+ block[!idx], block[idx]);
+ current_offset += n_bytes_read;
+ idx = !idx;
+ }
}
else
{
while (1)
- {
- ok &= read_block (bytes_per_block, block[idx], &n_bytes_read);
- if (n_bytes_read < bytes_per_block)
- break;
- assert (n_bytes_read == bytes_per_block);
- write_block (current_offset, n_bytes_read,
- block[!idx], block[idx]);
- current_offset += n_bytes_read;
- idx = !idx;
- }
+ {
+ ok &= read_block (bytes_per_block, block[idx], &n_bytes_read);
+ if (n_bytes_read < bytes_per_block)
+ break;
+ assert (n_bytes_read == bytes_per_block);
+ write_block (current_offset, n_bytes_read,
+ block[!idx], block[idx]);
+ current_offset += n_bytes_read;
+ idx = !idx;
+ }
}
if (n_bytes_read > 0)
@@ -1416,13 +1427,12 @@ dump (void)
l_c_m = get_lcm ();
- /* Make bytes_to_write the smallest multiple of l_c_m that
- is at least as large as n_bytes_read. */
+ /* Ensure zero-byte padding up to the smallest multiple of l_c_m that
+ is at least as large as n_bytes_read. */
bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);
memset (block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
- write_block (current_offset, bytes_to_write,
- block[!idx], block[idx]);
+ write_block (current_offset, n_bytes_read, block[!idx], block[idx]);
current_offset += n_bytes_read;
}
@@ -1437,7 +1447,7 @@ dump (void)
}
/* STRINGS mode. Find each "string constant" in the input.
- A string constant is a run of at least `string_min' ASCII
+ A string constant is a run of at least 'string_min' ASCII
graphic (or formatting) characters terminated by a null.
Based on a function written by Richard Stallman for a
traditional version of od. Return true if successful. */
@@ -1455,91 +1465,91 @@ dump_strings (void)
size_t i;
int c;
- /* See if the next `string_min' chars are all printing chars. */
+ /* See if the next 'string_min' chars are all printing chars. */
tryline:
if (limit_bytes_to_format
- && (end_offset < string_min || end_offset - string_min <= address))
- break;
+ && (end_offset < string_min || end_offset - string_min <= address))
+ break;
for (i = 0; i < string_min; i++)
- {
- ok &= read_char (&c);
- address++;
- if (c < 0)
- {
- free (buf);
- return ok;
- }
- if (! isprint (c))
- /* Found a non-printing. Try again starting with next char. */
- goto tryline;
- buf[i] = c;
- }
-
- /* We found a run of `string_min' printable characters.
- Now see if it is terminated with a null byte. */
+ {
+ ok &= read_char (&c);
+ address++;
+ if (c < 0)
+ {
+ free (buf);
+ return ok;
+ }
+ if (! isprint (c))
+ /* Found a non-printing. Try again starting with next char. */
+ goto tryline;
+ buf[i] = c;
+ }
+
+ /* We found a run of 'string_min' printable characters.
+ Now see if it is terminated with a null byte. */
while (!limit_bytes_to_format || address < end_offset)
- {
- if (i == bufsize)
- {
- buf = X2REALLOC (buf, &bufsize);
- }
- ok &= read_char (&c);
- address++;
- if (c < 0)
- {
- free (buf);
- return ok;
- }
- if (c == '\0')
- break; /* It is; print this string. */
- if (! isprint (c))
- goto tryline; /* It isn't; give up on this string. */
- buf[i++] = c; /* String continues; store it all. */
- }
+ {
+ if (i == bufsize)
+ {
+ buf = X2REALLOC (buf, &bufsize);
+ }
+ ok &= read_char (&c);
+ address++;
+ if (c < 0)
+ {
+ free (buf);
+ return ok;
+ }
+ if (c == '\0')
+ break; /* It is; print this string. */
+ if (! isprint (c))
+ goto tryline; /* It isn't; give up on this string. */
+ buf[i++] = c; /* String continues; store it all. */
+ }
/* If we get here, the string is all printable and null-terminated,
- so print it. It is all in `buf' and `i' is its length. */
+ so print it. It is all in 'buf' and 'i' is its length. */
buf[i] = 0;
format_address (address - i - 1, ' ');
for (i = 0; (c = buf[i]); i++)
- {
- switch (c)
- {
- case '\a':
- fputs ("\\a", stdout);
- break;
-
- case '\b':
- fputs ("\\b", stdout);
- break;
-
- case '\f':
- fputs ("\\f", stdout);
- break;
-
- case '\n':
- fputs ("\\n", stdout);
- break;
-
- case '\r':
- fputs ("\\r", stdout);
- break;
-
- case '\t':
- fputs ("\\t", stdout);
- break;
-
- case '\v':
- fputs ("\\v", stdout);
- break;
-
- default:
- putc (c, stdout);
- }
- }
+ {
+ switch (c)
+ {
+ case '\a':
+ fputs ("\\a", stdout);
+ break;
+
+ case '\b':
+ fputs ("\\b", stdout);
+ break;
+
+ case '\f':
+ fputs ("\\f", stdout);
+ break;
+
+ case '\n':
+ fputs ("\\n", stdout);
+ break;
+
+ case '\r':
+ fputs ("\\r", stdout);
+ break;
+
+ case '\t':
+ fputs ("\\t", stdout);
+ break;
+
+ case '\v':
+ fputs ("\\v", stdout);
+ break;
+
+ default:
+ putc (c, stdout);
+ }
+ }
putchar ('\n');
}
@@ -1555,21 +1565,22 @@ dump_strings (void)
int
main (int argc, char **argv)
{
- int c;
int n_files;
size_t i;
int l_c_m;
- size_t desired_width IF_LINT (= 0);
+ size_t desired_width IF_LINT ( = 0);
bool modern = false;
bool width_specified = false;
bool ok = true;
+ size_t width_per_block = 0;
+ static char const multipliers[] = "bEGKkMmPTYZ0";
- /* The old-style `pseudo starting address' to be printed in parentheses
+ /* The old-style 'pseudo starting address' to be printed in parentheses
after any true address. */
- uintmax_t pseudo_start IF_LINT (= 0);
+ uintmax_t pseudo_start IF_LINT ( = 0);
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -1584,8 +1595,8 @@ main (int argc, char **argv)
integral_type_size[sizeof (int)] = INT;
integral_type_size[sizeof (long int)] = LONG;
#if HAVE_UNSIGNED_LONG_LONG_INT
- /* If `long int' and `long long int' have the same size, it's fine
- to overwrite the entry for `long' with this one. */
+ /* If 'long int' and 'long long int' have the same size, it's fine
+ to overwrite the entry for 'long' with this one. */
integral_type_size[sizeof (unsigned_long_long_int)] = LONG_LONG;
#endif
@@ -1593,10 +1604,10 @@ main (int argc, char **argv)
fp_type_size[i] = NO_SIZE;
fp_type_size[sizeof (float)] = FLOAT_SINGLE;
- /* The array entry for `double' is filled in after that for LONG_DOUBLE
- so that if `long double' is the same type or if long double isn't
- supported FLOAT_LONG_DOUBLE will never be used. */
- fp_type_size[sizeof (LONG_DOUBLE)] = FLOAT_LONG_DOUBLE;
+ /* The array entry for 'double' is filled in after that for 'long double'
+ so that if they are the same size, we avoid any overhead of
+ long double computation in libc. */
+ fp_type_size[sizeof (long double)] = FLOAT_LONG_DOUBLE;
fp_type_size[sizeof (double)] = FLOAT_DOUBLE;
n_specs = 0;
@@ -1608,261 +1619,277 @@ main (int argc, char **argv)
address_pad_len = 7;
flag_dump_strings = false;
- while ((c = getopt_long (argc, argv, short_options, long_options, NULL))
- != -1)
+ while (true)
{
uintmax_t tmp;
enum strtol_error s_err;
+ int oi = -1;
+ int c = getopt_long (argc, argv, short_options, long_options, &oi);
+ if (c == -1)
+ break;
switch (c)
- {
- case 'A':
- modern = true;
- switch (optarg[0])
- {
- case 'd':
- format_address = format_address_std;
- address_base = 10;
- address_pad_len = 7;
- break;
- case 'o':
- format_address = format_address_std;
- address_base = 8;
- address_pad_len = 7;
- break;
- case 'x':
- format_address = format_address_std;
- address_base = 16;
- address_pad_len = 6;
- break;
- case 'n':
- format_address = format_address_none;
- address_pad_len = 0;
- break;
- default:
- error (EXIT_FAILURE, 0,
- _("invalid output address radix `%c'; \
-it must be one character from [doxn]"),
- optarg[0]);
- break;
- }
- break;
-
- case 'j':
- modern = true;
- s_err = xstrtoumax (optarg, NULL, 0, &n_bytes_to_skip, "bkm");
- if (s_err != LONGINT_OK)
- STRTOL_FATAL_ERROR (optarg, _("skip argument"), s_err);
- break;
-
- case 'N':
- modern = true;
- limit_bytes_to_format = true;
-
- s_err = xstrtoumax (optarg, NULL, 0, &max_bytes_to_format, "bkm");
- if (s_err != LONGINT_OK)
- STRTOL_FATAL_ERROR (optarg, _("limit argument"), s_err);
- break;
-
- case 'S':
- modern = true;
- if (optarg == NULL)
- string_min = 3;
- else
- {
- s_err = xstrtoumax (optarg, NULL, 0, &tmp, "bkm");
- if (s_err != LONGINT_OK)
- STRTOL_FATAL_ERROR (optarg, _("minimum string length"), s_err);
-
- /* The minimum string length may be no larger than SIZE_MAX,
- since we may allocate a buffer of this size. */
- if (SIZE_MAX < tmp)
- error (EXIT_FAILURE, 0, _("%s is too large"), optarg);
-
- string_min = tmp;
- }
- flag_dump_strings = true;
- break;
-
- case 't':
- modern = true;
- ok &= decode_format_string (optarg);
- break;
-
- case 'v':
- modern = true;
- abbreviate_duplicate_blocks = false;
- break;
-
- case TRADITIONAL_OPTION:
- traditional = true;
- break;
-
- /* The next several cases map the traditional format
- specification options to the corresponding modern format
- specs. GNU od accepts any combination of old- and
- new-style options. Format specification options accumulate.
- The obsolescent and undocumented formats are compatible
- with FreeBSD 4.10 od. */
+ {
+ case 'A':
+ modern = true;
+ switch (optarg[0])
+ {
+ case 'd':
+ format_address = format_address_std;
+ address_base = 10;
+ address_pad_len = 7;
+ break;
+ case 'o':
+ format_address = format_address_std;
+ address_base = 8;
+ address_pad_len = 7;
+ break;
+ case 'x':
+ format_address = format_address_std;
+ address_base = 16;
+ address_pad_len = 6;
+ break;
+ case 'n':
+ format_address = format_address_none;
+ address_pad_len = 0;
+ break;
+ default:
+ error (EXIT_FAILURE, 0,
+ _("invalid output address radix '%c';\
+ it must be one character from [doxn]"),
+ optarg[0]);
+ break;
+ }
+ break;
+
+ case 'j':
+ modern = true;
+ s_err = xstrtoumax (optarg, NULL, 0, &n_bytes_to_skip, multipliers);
+ if (s_err != LONGINT_OK)
+ xstrtol_fatal (s_err, oi, c, long_options, optarg);
+ break;
+
+ case 'N':
+ modern = true;
+ limit_bytes_to_format = true;
+
+ s_err = xstrtoumax (optarg, NULL, 0, &max_bytes_to_format,
+ multipliers);
+ if (s_err != LONGINT_OK)
+ xstrtol_fatal (s_err, oi, c, long_options, optarg);
+ break;
+
+ case 'S':
+ modern = true;
+ if (optarg == NULL)
+ string_min = 3;
+ else
+ {
+ s_err = xstrtoumax (optarg, NULL, 0, &tmp, multipliers);
+ if (s_err != LONGINT_OK)
+ xstrtol_fatal (s_err, oi, c, long_options, optarg);
+
+ /* The minimum string length may be no larger than SIZE_MAX,
+ since we may allocate a buffer of this size. */
+ if (SIZE_MAX < tmp)
+ error (EXIT_FAILURE, 0, _("%s is too large"), quote (optarg));
+
+ string_min = tmp;
+ }
+ flag_dump_strings = true;
+ break;
+
+ case 't':
+ modern = true;
+ ok &= decode_format_string (optarg);
+ break;
+
+ case 'v':
+ modern = true;
+ abbreviate_duplicate_blocks = false;
+ break;
+
+ case TRADITIONAL_OPTION:
+ traditional = true;
+ break;
+
+ case ENDIAN_OPTION:
+ switch (XARGMATCH ("--endian", optarg, endian_args, endian_types))
+ {
+ case endian_big:
+ input_swap = ! WORDS_BIGENDIAN;
+ break;
+ case endian_little:
+ input_swap = WORDS_BIGENDIAN;
+ break;
+ }
+ break;
+
+ /* The next several cases map the traditional format
+ specification options to the corresponding modern format
+ specs. GNU od accepts any combination of old- and
+ new-style options. Format specification options accumulate.
+ The obsolescent and undocumented formats are compatible
+ with FreeBSD 4.10 od. */
#define CASE_OLD_ARG(old_char,new_string) \
- case old_char: \
- ok &= decode_format_string (new_string); \
- break
-
- CASE_OLD_ARG ('a', "a");
- CASE_OLD_ARG ('b', "o1");
- CASE_OLD_ARG ('c', "c");
- CASE_OLD_ARG ('D', "u4"); /* obsolescent and undocumented */
- CASE_OLD_ARG ('d', "u2");
- case 'F': /* obsolescent and undocumented alias */
- CASE_OLD_ARG ('e', "fD"); /* obsolescent and undocumented */
- CASE_OLD_ARG ('f', "fF");
- case 'X': /* obsolescent and undocumented alias */
- CASE_OLD_ARG ('H', "x4"); /* obsolescent and undocumented */
- CASE_OLD_ARG ('i', "dI");
- case 'I': case 'L': /* obsolescent and undocumented aliases */
- CASE_OLD_ARG ('l', "dL");
- CASE_OLD_ARG ('O', "o4"); /* obsolesent and undocumented */
- case 'B': /* obsolescent and undocumented alias */
- CASE_OLD_ARG ('o', "o2");
- CASE_OLD_ARG ('s', "d2");
- case 'h': /* obsolescent and undocumented alias */
- CASE_OLD_ARG ('x', "x2");
+ case old_char: \
+ ok &= decode_format_string (new_string); \
+ break
+
+ CASE_OLD_ARG ('a', "a");
+ CASE_OLD_ARG ('b', "o1");
+ CASE_OLD_ARG ('c', "c");
+ CASE_OLD_ARG ('D', "u4"); /* obsolescent and undocumented */
+ CASE_OLD_ARG ('d', "u2");
+ case 'F': /* obsolescent and undocumented alias */
+ CASE_OLD_ARG ('e', "fD"); /* obsolescent and undocumented */
+ CASE_OLD_ARG ('f', "fF");
+ case 'X': /* obsolescent and undocumented alias */
+ CASE_OLD_ARG ('H', "x4"); /* obsolescent and undocumented */
+ CASE_OLD_ARG ('i', "dI");
+ case 'I': case 'L': /* obsolescent and undocumented aliases */
+ CASE_OLD_ARG ('l', "dL");
+ CASE_OLD_ARG ('O', "o4"); /* obsolesent and undocumented */
+ case 'B': /* obsolescent and undocumented alias */
+ CASE_OLD_ARG ('o', "o2");
+ CASE_OLD_ARG ('s', "d2");
+ case 'h': /* obsolescent and undocumented alias */
+ CASE_OLD_ARG ('x', "x2");
#undef CASE_OLD_ARG
- case 'w':
- modern = true;
- width_specified = true;
- if (optarg == NULL)
- {
- desired_width = 32;
- }
- else
- {
- uintmax_t w_tmp;
- s_err = xstrtoumax (optarg, NULL, 10, &w_tmp, "");
- if (s_err != LONGINT_OK)
- STRTOL_FATAL_ERROR (optarg, _("width specification"), s_err);
- if (SIZE_MAX < w_tmp)
- error (EXIT_FAILURE, 0, _("%s is too large"), optarg);
- desired_width = w_tmp;
- }
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (EXIT_FAILURE);
- break;
- }
+ case 'w':
+ modern = true;
+ width_specified = true;
+ if (optarg == NULL)
+ {
+ desired_width = 32;
+ }
+ else
+ {
+ uintmax_t w_tmp;
+ s_err = xstrtoumax (optarg, NULL, 10, &w_tmp, "");
+ if (s_err != LONGINT_OK)
+ xstrtol_fatal (s_err, oi, c, long_options, optarg);
+ if (SIZE_MAX < w_tmp)
+ error (EXIT_FAILURE, 0, _("%s is too large"), quote (optarg));
+ desired_width = w_tmp;
+ }
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ break;
+ }
}
if (!ok)
- exit (EXIT_FAILURE);
+ return EXIT_FAILURE;
if (flag_dump_strings && n_specs > 0)
error (EXIT_FAILURE, 0,
- _("no type may be specified when dumping strings"));
+ _("no type may be specified when dumping strings"));
n_files = argc - optind;
/* If the --traditional option is used, there may be from
0 to 3 remaining command line arguments; handle each case
separately.
- od [file] [[+]offset[.][b] [[+]label[.][b]]]
+ od [file] [[+]offset[.][b] [[+]label[.][b]]]
The offset and label have the same syntax.
If --traditional is not given, and if no modern options are
given, and if the offset begins with + or (if there are two
operands) a digit, accept only this form, as per POSIX:
- od [file] [[+]offset[.][b]]
+ od [file] [[+]offset[.][b]]
*/
- if (!modern | traditional)
+ if (!modern || traditional)
{
uintmax_t o1;
uintmax_t o2;
switch (n_files)
- {
- case 1:
- if ((traditional || argv[optind][0] == '+')
- && parse_old_offset (argv[optind], &o1))
- {
- n_bytes_to_skip = o1;
- --n_files;
- ++argv;
- }
- break;
-
- case 2:
- if ((traditional || argv[optind + 1][0] == '+'
- || ISDIGIT (argv[optind + 1][0]))
- && parse_old_offset (argv[optind + 1], &o2))
- {
- if (traditional && parse_old_offset (argv[optind], &o1))
- {
- n_bytes_to_skip = o1;
- flag_pseudo_start = true;
- pseudo_start = o2;
- argv += 2;
- n_files -= 2;
- }
- else
- {
- n_bytes_to_skip = o2;
- --n_files;
- argv[optind + 1] = argv[optind];
- ++argv;
- }
- }
- break;
-
- case 3:
- if (traditional
- && parse_old_offset (argv[optind + 1], &o1)
- && parse_old_offset (argv[optind + 2], &o2))
- {
- n_bytes_to_skip = o1;
- flag_pseudo_start = true;
- pseudo_start = o2;
- argv[optind + 2] = argv[optind];
- argv += 2;
- n_files -= 2;
- }
- break;
- }
+ {
+ case 1:
+ if ((traditional || argv[optind][0] == '+')
+ && parse_old_offset (argv[optind], &o1))
+ {
+ n_bytes_to_skip = o1;
+ --n_files;
+ ++argv;
+ }
+ break;
+
+ case 2:
+ if ((traditional || argv[optind + 1][0] == '+'
+ || ISDIGIT (argv[optind + 1][0]))
+ && parse_old_offset (argv[optind + 1], &o2))
+ {
+ if (traditional && parse_old_offset (argv[optind], &o1))
+ {
+ n_bytes_to_skip = o1;
+ flag_pseudo_start = true;
+ pseudo_start = o2;
+ argv += 2;
+ n_files -= 2;
+ }
+ else
+ {
+ n_bytes_to_skip = o2;
+ --n_files;
+ argv[optind + 1] = argv[optind];
+ ++argv;
+ }
+ }
+ break;
+
+ case 3:
+ if (traditional
+ && parse_old_offset (argv[optind + 1], &o1)
+ && parse_old_offset (argv[optind + 2], &o2))
+ {
+ n_bytes_to_skip = o1;
+ flag_pseudo_start = true;
+ pseudo_start = o2;
+ argv[optind + 2] = argv[optind];
+ argv += 2;
+ n_files -= 2;
+ }
+ break;
+ }
if (traditional && 1 < n_files)
- {
- error (0, 0, _("extra operand %s"), quote (argv[optind + 1]));
- error (0, 0, "%s\n",
- _("Compatibility mode supports at most one file."));
- usage (EXIT_FAILURE);
- }
+ {
+ error (0, 0, _("extra operand %s"), quote (argv[optind + 1]));
+ error (0, 0, "%s",
+ _("compatibility mode supports at most one file"));
+ usage (EXIT_FAILURE);
+ }
}
if (flag_pseudo_start)
{
if (format_address == format_address_none)
- {
- address_base = 8;
- address_pad_len = 7;
- format_address = format_address_paren;
- }
+ {
+ address_base = 8;
+ address_pad_len = 7;
+ format_address = format_address_paren;
+ }
else
- format_address = format_address_label;
+ format_address = format_address_label;
}
if (limit_bytes_to_format)
{
end_offset = n_bytes_to_skip + max_bytes_to_format;
if (end_offset < n_bytes_to_skip)
- error (EXIT_FAILURE, 0, _("skip-bytes + read-bytes is too large"));
+ error (EXIT_FAILURE, 0, _("skip-bytes + read-bytes is too large"));
}
if (n_specs == 0)
@@ -1871,15 +1898,15 @@ it must be one character from [doxn]"),
if (n_files > 0)
{
/* Set the global pointer FILE_LIST so that it
- references the first file-argument on the command-line. */
+ references the first file-argument on the command-line. */
file_list = (char const *const *) &argv[optind];
}
else
{
/* No files were listed on the command line.
- Set the global pointer FILE_LIST so that it
- references the null-terminated list of one name: "-". */
+ Set the global pointer FILE_LIST so that it
+ references the null-terminated list of one name: "-". */
file_list = default_file_list;
}
@@ -1902,36 +1929,57 @@ it must be one character from [doxn]"),
if (width_specified)
{
if (desired_width != 0 && desired_width % l_c_m == 0)
- bytes_per_block = desired_width;
+ bytes_per_block = desired_width;
else
- {
- error (0, 0, _("warning: invalid width %lu; using %d instead"),
- (unsigned long int) desired_width, l_c_m);
- bytes_per_block = l_c_m;
- }
+ {
+ error (0, 0, _("warning: invalid width %lu; using %d instead"),
+ (unsigned long int) desired_width, l_c_m);
+ bytes_per_block = l_c_m;
+ }
}
else
{
if (l_c_m < DEFAULT_BYTES_PER_BLOCK)
- bytes_per_block = l_c_m * (DEFAULT_BYTES_PER_BLOCK / l_c_m);
+ bytes_per_block = l_c_m * (DEFAULT_BYTES_PER_BLOCK / l_c_m);
else
- bytes_per_block = l_c_m;
+ bytes_per_block = l_c_m;
+ }
+
+ /* Compute padding necessary to align output block. */
+ for (i = 0; i < n_specs; i++)
+ {
+ int fields_per_block = bytes_per_block / width_bytes[spec[i].size];
+ int block_width = (spec[i].field_width + 1) * fields_per_block;
+ if (width_per_block < block_width)
+ width_per_block = block_width;
+ }
+ for (i = 0; i < n_specs; i++)
+ {
+ int fields_per_block = bytes_per_block / width_bytes[spec[i].size];
+ int block_width = spec[i].field_width * fields_per_block;
+ spec[i].pad_width = width_per_block - block_width;
}
#ifdef DEBUG
+ printf ("lcm=%d, width_per_block=%"PRIuMAX"\n", l_c_m,
+ (uintmax_t) width_per_block);
for (i = 0; i < n_specs; i++)
{
- printf (_("%d: fmt=\"%s\" width=%d\n"),
- i, spec[i].fmt_string, width_bytes[spec[i].size]);
+ int fields_per_block = bytes_per_block / width_bytes[spec[i].size];
+ assert (bytes_per_block % width_bytes[spec[i].size] == 0);
+ assert (1 <= spec[i].pad_width / fields_per_block);
+ printf ("%d: fmt=\"%s\" in_width=%d out_width=%d pad=%d\n",
+ i, spec[i].fmt_string, width_bytes[spec[i].size],
+ spec[i].field_width, spec[i].pad_width);
}
#endif
ok &= (flag_dump_strings ? dump_strings () : dump ());
-cleanup:;
+cleanup:
if (have_read_stdin && fclose (stdin) == EOF)
error (EXIT_FAILURE, errno, _("standard input"));
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/operand2sig.c b/src/operand2sig.c
new file mode 100644
index 0000000..62039db
--- /dev/null
+++ b/src/operand2sig.c
@@ -0,0 +1,86 @@
+/* operand2sig.c -- common function for parsing signal specifications
+ Copyright (C) 2008-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Extracted from kill.c/timeout.c by Pádraig Brady.
+ FIXME: Move this to gnulib/str2sig.c */
+
+
+/* Convert OPERAND to a signal number with printable representation SIGNAME.
+ Return the signal number, or -1 if unsuccessful. */
+
+#include <config.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "system.h"
+#include "error.h"
+#include "quote.h"
+#include "sig2str.h"
+#include "operand2sig.h"
+
+extern int
+operand2sig (char const *operand, char *signame)
+{
+ int signum;
+
+ if (ISDIGIT (*operand))
+ {
+ /* Note we don't put a limit on the maximum value passed,
+ because we're checking shell $? values here, and ksh for
+ example will add 256 to the signal value, thus being wider
+ than the number of WEXITSTATUS bits.
+ We could validate that values were not above say
+ ((WEXITSTATUS (~0) << 1) + 1), which would cater for ksh.
+ But some shells may use other adjustments in future to be
+ (forward) compatible with systems that support
+ wider exit status values as discussed at
+ http://austingroupbugs.net/view.php?id=947 */
+
+ char *endp;
+ long int l = (errno = 0, strtol (operand, &endp, 10));
+ int i = l;
+ signum = (operand == endp || *endp || errno || i != l ? -1
+ : WIFSIGNALED (i) ? WTERMSIG (i) : i);
+ }
+ else
+ {
+ /* 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++)
+ if (strchr ("abcdefghijklmnopqrstuvwxyz", *p))
+ *p += 'A' - 'a';
+
+ /* Look for the signal name, possibly prefixed by "SIG",
+ and possibly lowercased. */
+ if (!(str2sig (upcased, &signum) == 0
+ || (upcased[0] == 'S' && upcased[1] == 'I' && upcased[2] == 'G'
+ && str2sig (upcased + 3, &signum) == 0)))
+ signum = -1;
+
+ free (upcased);
+ }
+
+ if (signum < 0 || sig2str (signum, signame) != 0)
+ {
+ error (0, 0, _("%s: invalid signal"), quote (operand));
+ return -1;
+ }
+
+ return signum;
+}
diff --git a/src/operand2sig.h b/src/operand2sig.h
new file mode 100644
index 0000000..968b5d1
--- /dev/null
+++ b/src/operand2sig.h
@@ -0,0 +1,18 @@
+/* operand2sig.h -- prototype for signal specification function
+
+ Copyright (C) 2008-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+extern int operand2sig (char const *operand, char *signame);
diff --git a/src/paste.c b/src/paste.c
index 414fb88..bf99fe0 100644
--- a/src/paste.c
+++ b/src/paste.c
@@ -1,11 +1,11 @@
/* paste - merge lines of files
- Copyright (C) 1997-2005 Free Software Foundation, Inc.
+ Copyright (C) 1997-2016 Free Software Foundation, Inc.
Copyright (C) 1984 David M. Ihnat
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -13,8 +13,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by David Ihnat. */
@@ -27,13 +26,13 @@
Options:
--serial
-s Paste one file at a time rather than
- one line from each file.
+ one line from each file.
--delimiters=delim-list
-d delim-list Consecutively use the characters in
- DELIM-LIST instead of tab to separate
- merged lines. When DELIM-LIST is exhausted,
- start again at its beginning.
- A FILE of `-' means standard input.
+ DELIM-LIST instead of tab to separate
+ merged lines. When DELIM-LIST is exhausted,
+ start again at its beginning.
+ A FILE of '-' means standard input.
If no FILEs are given, standard input is used. */
#include <config.h>
@@ -43,18 +42,18 @@
#include <sys/types.h>
#include "system.h"
#include "error.h"
+#include "fadvise.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "paste"
-#define AUTHORS "David M. Ihnat", "David MacKenzie"
+#define AUTHORS \
+ proper_name ("David M. Ihnat"), \
+ proper_name ("David MacKenzie")
/* Indicates that no delimiter should be added in the current position. */
#define EMPTY_DELIM '\0'
-/* Name this program was run with. */
-char *program_name;
-
/* If nonzero, we have read standard input at some point. */
static bool have_read_stdin;
@@ -62,16 +61,19 @@ static bool have_read_stdin;
corresponding lines from each file in parallel. */
static bool serial_merge;
-/* The delimeters between lines of input files (used cyclically). */
+/* The delimiters between lines of input files (used cyclically). */
static char *delims;
-/* A pointer to the character after the end of `delims'. */
+/* A pointer to the character after the end of 'delims'. */
static char const *delim_end;
+static unsigned char line_delim = '\n';
+
static struct option const longopts[] =
{
{"serial", no_argument, NULL, 's'},
{"delimiters", required_argument, NULL, 'd'},
+ {"zero-terminated", no_argument, NULL, 'z'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
@@ -80,58 +82,75 @@ static struct option const longopts[] =
/* 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. */
+ 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. */
-static void
+static int
collapse_escapes (char const *strptr)
{
char *strout = xstrdup (strptr);
+ bool backslash_at_end = false;
+
delims = strout;
while (*strptr)
{
if (*strptr != '\\') /* Is it an escape character? */
- *strout++ = *strptr++; /* No, just transfer it. */
+ *strout++ = *strptr++; /* No, just transfer it. */
else
- {
- switch (*++strptr)
- {
- case '0':
- *strout++ = EMPTY_DELIM;
- break;
-
- 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;
-
- default:
- *strout++ = *strptr;
- break;
- }
- strptr++;
- }
+ {
+ switch (*++strptr)
+ {
+ case '0':
+ *strout++ = EMPTY_DELIM;
+ break;
+
+ 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;
+
+ case '\0':
+ backslash_at_end = true;
+ goto done;
+
+ default:
+ *strout++ = *strptr;
+ break;
+ }
+ strptr++;
+ }
}
+
+ done:
+
delim_end = strout;
+ return backslash_at_end ? 1 : 0;
}
/* Report a write error and exit. */
@@ -184,18 +203,19 @@ paste_parallel (size_t nfiles, char **fnamptr)
for (files_open = 0; files_open < nfiles; ++files_open)
{
if (STREQ (fnamptr[files_open], "-"))
- {
- have_read_stdin = true;
- fileptr[files_open] = stdin;
- }
+ {
+ have_read_stdin = true;
+ fileptr[files_open] = stdin;
+ }
else
- {
- fileptr[files_open] = fopen (fnamptr[files_open], "r");
- if (fileptr[files_open] == NULL)
- error (EXIT_FAILURE, errno, "%s", fnamptr[files_open]);
- else if (fileno (fileptr[files_open]) == STDIN_FILENO)
- opened_stdin = true;
- }
+ {
+ fileptr[files_open] = fopen (fnamptr[files_open], "r");
+ 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;
+ fadvise (fileptr[files_open], FADVISE_SEQUENTIAL);
+ }
}
if (opened_stdin && have_read_stdin)
@@ -210,111 +230,111 @@ 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'. */
+ size_t delims_saved = 0; /* Number of delims saved in 'delbuf'. */
size_t i;
for (i = 0; i < nfiles && files_open; i++)
- {
- int chr IF_LINT (= 0); /* Input character. */
- int err IF_LINT (= 0); /* Input errno value. */
- size_t line_length = 0; /* Number of chars in line. */
-
- if (fileptr[i])
- {
- chr = getc (fileptr[i]);
- err = errno;
- if (chr != EOF && delims_saved)
- {
- if (fwrite (delbuf, 1, delims_saved, stdout) != delims_saved)
- write_error ();
- delims_saved = 0;
- }
-
- while (chr != EOF)
- {
- line_length++;
- if (chr == '\n')
- break;
- xputchar (chr);
- chr = getc (fileptr[i]);
- err = errno;
- }
- }
-
- if (line_length == 0)
- {
- /* EOF, read error, or closed file.
- If an EOF or error, close the file. */
- if (fileptr[i])
- {
- if (ferror (fileptr[i]))
- {
- error (0, err, "%s", fnamptr[i]);
- ok = false;
- }
- if (fileptr[i] == stdin)
- clearerr (fileptr[i]); /* Also clear EOF. */
- else if (fclose (fileptr[i]) == EOF)
- {
- error (0, errno, "%s", fnamptr[i]);
- ok = false;
- }
-
- fileptr[i] = NULL;
- files_open--;
- }
-
- if (i + 1 == nfiles)
- {
- /* End of this output line.
- Is this the end of the whole thing? */
- if (somedone)
- {
- /* No. Some files were not closed for this line. */
- if (delims_saved)
- {
- if (fwrite (delbuf, 1, delims_saved, stdout)
- != delims_saved)
- write_error ();
- delims_saved = 0;
- }
- xputchar ('\n');
- }
- continue; /* Next read of files, or exit. */
- }
- else
- {
- /* Closed file; add delimiter to `delbuf'. */
- if (*delimptr != EMPTY_DELIM)
- delbuf[delims_saved++] = *delimptr;
- if (++delimptr == delim_end)
- delimptr = delims;
- }
- }
- else
- {
- /* Some data read. */
- somedone = true;
-
- /* Except for last file, replace last newline with delim. */
- if (i + 1 != nfiles)
- {
- if (chr != '\n' && chr != EOF)
- xputchar (chr);
- if (*delimptr != EMPTY_DELIM)
- xputchar (*delimptr);
- if (++delimptr == delim_end)
- delimptr = delims;
- }
- else
- {
- /* If the last line of the last file lacks a newline,
- print one anyhow. POSIX requires this. */
- char c = (chr == EOF ? '\n' : chr);
- xputchar (c);
- }
- }
- }
+ {
+ int chr IF_LINT ( = 0); /* Input character. */
+ int err IF_LINT ( = 0); /* Input errno value. */
+ bool sometodo = false; /* Input chars to process. */
+
+ if (fileptr[i])
+ {
+ chr = getc (fileptr[i]);
+ err = errno;
+ if (chr != EOF && delims_saved)
+ {
+ if (fwrite (delbuf, 1, delims_saved, stdout) != delims_saved)
+ write_error ();
+ delims_saved = 0;
+ }
+
+ while (chr != EOF)
+ {
+ sometodo = true;
+ if (chr == line_delim)
+ break;
+ xputchar (chr);
+ chr = getc (fileptr[i]);
+ err = errno;
+ }
+ }
+
+ if (! sometodo)
+ {
+ /* EOF, read error, or closed file.
+ If an EOF or error, close the file. */
+ if (fileptr[i])
+ {
+ if (ferror (fileptr[i]))
+ {
+ error (0, err, "%s", quotef (fnamptr[i]));
+ ok = false;
+ }
+ if (fileptr[i] == stdin)
+ clearerr (fileptr[i]); /* Also clear EOF. */
+ else if (fclose (fileptr[i]) == EOF)
+ {
+ error (0, errno, "%s", quotef (fnamptr[i]));
+ ok = false;
+ }
+
+ fileptr[i] = NULL;
+ files_open--;
+ }
+
+ if (i + 1 == nfiles)
+ {
+ /* End of this output line.
+ Is this the end of the whole thing? */
+ if (somedone)
+ {
+ /* No. Some files were not closed for this line. */
+ if (delims_saved)
+ {
+ if (fwrite (delbuf, 1, delims_saved, stdout)
+ != delims_saved)
+ write_error ();
+ delims_saved = 0;
+ }
+ xputchar (line_delim);
+ }
+ continue; /* Next read of files, or exit. */
+ }
+ else
+ {
+ /* Closed file; add delimiter to 'delbuf'. */
+ if (*delimptr != EMPTY_DELIM)
+ delbuf[delims_saved++] = *delimptr;
+ if (++delimptr == delim_end)
+ delimptr = delims;
+ }
+ }
+ else
+ {
+ /* Some data read. */
+ somedone = true;
+
+ /* Except for last file, replace last newline with delim. */
+ if (i + 1 != nfiles)
+ {
+ if (chr != line_delim && chr != EOF)
+ xputchar (chr);
+ if (*delimptr != EMPTY_DELIM)
+ xputchar (*delimptr);
+ if (++delimptr == delim_end)
+ delimptr = delims;
+ }
+ else
+ {
+ /* If the last line of the last file lacks a newline,
+ print one anyhow. POSIX requires this. */
+ char c = (chr == EOF ? line_delim : chr);
+ xputchar (c);
+ }
+ }
+ }
}
free (fileptr);
free (delbuf);
@@ -338,70 +358,71 @@ paste_serial (size_t nfiles, char **fnamptr)
int saved_errno;
bool is_stdin = STREQ (*fnamptr, "-");
if (is_stdin)
- {
- have_read_stdin = true;
- fileptr = stdin;
- }
+ {
+ have_read_stdin = true;
+ fileptr = stdin;
+ }
else
- {
- fileptr = fopen (*fnamptr, "r");
- if (fileptr == NULL)
- {
- error (0, errno, "%s", *fnamptr);
- ok = false;
- continue;
- }
- }
+ {
+ fileptr = fopen (*fnamptr, "r");
+ if (fileptr == NULL)
+ {
+ error (0, errno, "%s", quotef (*fnamptr));
+ ok = false;
+ continue;
+ }
+ fadvise (fileptr, FADVISE_SEQUENTIAL);
+ }
delimptr = delims; /* Set up for delimiter string. */
charold = getc (fileptr);
saved_errno = errno;
if (charold != EOF)
- {
- /* `charold' is set up. Hit it!
- Keep reading characters, stashing them in `charnew';
- output `charold', converting to the appropriate delimiter
- character if needed. After the EOF, output `charold'
- if it's a newline; otherwise, output it and then a newline. */
-
- while ((charnew = getc (fileptr)) != EOF)
- {
- /* Process the old character. */
- if (charold == '\n')
- {
- if (*delimptr != EMPTY_DELIM)
- xputchar (*delimptr);
-
- if (++delimptr == delim_end)
- delimptr = delims;
- }
- else
- xputchar (charold);
-
- charold = charnew;
- }
- saved_errno = errno;
-
- /* Hit EOF. Process that last character. */
- xputchar (charold);
- }
-
- if (charold != '\n')
- xputchar ('\n');
+ {
+ /* 'charold' is set up. Hit it!
+ Keep reading characters, stashing them in 'charnew';
+ output 'charold', converting to the appropriate delimiter
+ character if needed. After the EOF, output 'charold'
+ if it's a newline; otherwise, output it and then a newline. */
+
+ while ((charnew = getc (fileptr)) != EOF)
+ {
+ /* Process the old character. */
+ if (charold == line_delim)
+ {
+ if (*delimptr != EMPTY_DELIM)
+ xputchar (*delimptr);
+
+ if (++delimptr == delim_end)
+ delimptr = delims;
+ }
+ else
+ xputchar (charold);
+
+ charold = charnew;
+ }
+ saved_errno = errno;
+
+ /* Hit EOF. Process that last character. */
+ xputchar (charold);
+ }
+
+ if (charold != line_delim)
+ xputchar (line_delim);
if (ferror (fileptr))
- {
- error (0, saved_errno, "%s", *fnamptr);
- ok = false;
- }
+ {
+ error (0, saved_errno, "%s", quotef (*fnamptr));
+ ok = false;
+ }
if (is_stdin)
- clearerr (fileptr); /* Also clear EOF. */
+ clearerr (fileptr); /* Also clear EOF. */
else if (fclose (fileptr) == EOF)
- {
- error (0, errno, "%s", *fnamptr);
- ok = false;
- }
+ {
+ error (0, errno, "%s", quotef (*fnamptr));
+ ok = false;
+ }
}
return ok;
}
@@ -410,31 +431,32 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
- program_name);
+ program_name);
fputs (_("\
Write lines consisting of the sequentially corresponding lines from\n\
each FILE, separated by TABs, to standard output.\n\
-With no FILE, or when FILE is -, read standard input.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\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);
/* FIXME: add a couple of examples. */
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -447,7 +469,7 @@ main (int argc, char **argv)
char const *delim_arg = "\t";
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -457,32 +479,43 @@ main (int argc, char **argv)
have_read_stdin = false;
serial_merge = false;
- while ((optc = getopt_long (argc, argv, "d:s", longopts, NULL)) != -1)
+ while ((optc = getopt_long (argc, argv, "d:sz", longopts, NULL)) != -1)
{
switch (optc)
- {
- case 'd':
- /* Delimiter character(s). */
- delim_arg = (optarg[0] == '\0' ? "\\0" : optarg);
- break;
+ {
+ case 'd':
+ /* Delimiter character(s). */
+ delim_arg = (optarg[0] == '\0' ? "\\0" : optarg);
+ break;
- case 's':
- serial_merge = true;
- break;
+ case 's':
+ serial_merge = true;
+ break;
- case_GETOPT_HELP_CHAR;
+ case 'z':
+ line_delim = '\0';
+ break;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ case_GETOPT_HELP_CHAR;
- default:
- usage (EXIT_FAILURE);
- }
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
}
if (optind == argc)
- argv[argc++] = "-";
+ argv[argc++] = bad_cast ("-");
- collapse_escapes (delim_arg);
+ if (collapse_escapes (delim_arg))
+ {
+ /* Don't use the quote() quoting style, because that would double the
+ number of displayed backslashes, making the diagnostic look bogus. */
+ error (EXIT_FAILURE, 0,
+ _("delimiter list ends with an unescaped backslash: %s"),
+ quotearg_n_style_colon (0, c_maybe_quoting_style, delim_arg));
+ }
if (!serial_merge)
ok = paste_parallel (argc - optind, &argv[optind]);
@@ -493,5 +526,5 @@ main (int argc, char **argv)
if (have_read_stdin && fclose (stdin) == EOF)
error (EXIT_FAILURE, errno, "-");
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/pathchk.c b/src/pathchk.c
index 2fc55d3..bd4254a 100644
--- a/src/pathchk.c
+++ b/src/pathchk.c
@@ -1,10 +1,10 @@
/* pathchk -- check whether file names are valid or portable
- Copyright (C) 1991-2006 Free Software Foundation, Inc.
+ Copyright (C) 1991-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,32 +12,25 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
-#if HAVE_WCHAR_H
-# include <wchar.h>
-#endif
+#include <wchar.h>
#include "system.h"
#include "error.h"
-#include "euidaccess.h"
#include "quote.h"
-#include "quotearg.h"
-
-#if ! (HAVE_MBRLEN && HAVE_MBSTATE_T)
-# define mbrlen(s, n, ps) 1
-# define mbstate_t int
-#endif
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "pathchk"
-#define AUTHORS "Paul Eggert", "David MacKenzie", "Jim Meyering"
+#define AUTHORS \
+ proper_name ("Paul Eggert"), \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Jim Meyering")
#ifndef _POSIX_PATH_MAX
# define _POSIX_PATH_MAX 256
@@ -70,9 +63,6 @@
static bool validate_file_name (char *, bool, bool);
-/* The name this program was run with. */
-char *program_name;
-
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
@@ -92,13 +82,12 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("Usage: %s [OPTION]... NAME...\n"), program_name);
fputs (_("\
-Diagnose unportable constructs in NAME.\n\
+Diagnose invalid or unportable file names.\n\
\n\
-p check for most POSIX systems\n\
-P check for empty names and leading \"-\"\n\
@@ -106,7 +95,7 @@ Diagnose unportable constructs in NAME.\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -120,7 +109,7 @@ main (int argc, char **argv)
int optc;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -130,27 +119,27 @@ main (int argc, char **argv)
while ((optc = getopt_long (argc, argv, "+pP", longopts, NULL)) != -1)
{
switch (optc)
- {
- case PORTABILITY_OPTION:
- check_basic_portability = true;
- check_extra_portability = true;
- break;
+ {
+ case PORTABILITY_OPTION:
+ check_basic_portability = true;
+ check_extra_portability = true;
+ break;
- case 'p':
- check_basic_portability = true;
- break;
+ case 'p':
+ check_basic_portability = true;
+ break;
- case 'P':
- check_extra_portability = true;
- break;
+ case 'P':
+ check_extra_portability = true;
+ break;
- case_GETOPT_HELP_CHAR;
+ case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- }
+ default:
+ usage (EXIT_FAILURE);
+ }
}
if (optind == argc)
@@ -161,9 +150,9 @@ main (int argc, char **argv)
for (; optind < argc; ++optind)
ok &= validate_file_name (argv[optind],
- check_basic_portability, check_extra_portability);
+ check_basic_portability, check_extra_portability);
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
/* If FILE contains a component with a leading "-", report an error
@@ -177,9 +166,9 @@ no_leading_hyphen (char const *file)
for (p = file; (p = strchr (p, '-')); p++)
if (p == file || p[-1] == '/')
{
- error (0, 0, _("leading `-' in a component of file name %s"),
- quote (file));
- return false;
+ error (0, 0, _("leading '-' in a component of file name %s"),
+ quoteaf (file));
+ return false;
}
return true;
@@ -192,10 +181,10 @@ static bool
portable_chars_only (char const *file, size_t filelen)
{
size_t validlen = strspn (file,
- ("/"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz"
- "0123456789._-"));
+ ("/"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789._-"));
char const *invalid = file + validlen;
if (*invalid)
@@ -203,10 +192,10 @@ portable_chars_only (char const *file, size_t filelen)
mbstate_t mbstate = { 0, };
size_t charlen = mbrlen (invalid, filelen - validlen, &mbstate);
error (0, 0,
- _("nonportable character %s in file name %s"),
- quotearg_n_style_mem (1, locale_quoting_style, invalid,
- (charlen <= MB_LEN_MAX ? charlen : 1)),
- quote_n (0, file));
+ _("nonportable character %s in file name %s"),
+ quotearg_n_style_mem (1, locale_quoting_style, invalid,
+ (charlen <= MB_LEN_MAX ? charlen : 1)),
+ quoteaf_n (0, file));
return false;
}
@@ -215,7 +204,7 @@ portable_chars_only (char const *file, size_t filelen)
/* Return the address of the start of the next file name component in F. */
-static char *
+static char * _GL_ATTRIBUTE_PURE
component_start (char *f)
{
while (*f == '/')
@@ -225,7 +214,7 @@ component_start (char *f)
/* Return the size of the file name component F. F must be nonempty. */
-static size_t
+static size_t _GL_ATTRIBUTE_PURE
component_len (char const *f)
{
size_t len;
@@ -256,7 +245,7 @@ component_len (char const *f)
static bool
validate_file_name (char *file, bool check_basic_portability,
- bool check_extra_portability)
+ bool check_extra_portability)
{
size_t filelen = strlen (file);
@@ -272,13 +261,13 @@ validate_file_name (char *file, bool check_basic_portability,
if (check_extra_portability && ! no_leading_hyphen (file))
return false;
- if ((check_basic_portability | check_extra_portability)
+ if ((check_basic_portability || check_extra_portability)
&& filelen == 0)
{
/* Fail, since empty names are not portable. As of
- 2005-01-06 POSIX does not address whether "pathchk -p ''"
- should (or is allowed to) fail, so this is not a
- conformance violation. */
+ 2005-01-06 POSIX does not address whether "pathchk -p ''"
+ should (or is allowed to) fail, so this is not a
+ conformance violation. */
error (0, 0, _("empty file name"));
return false;
}
@@ -286,23 +275,23 @@ validate_file_name (char *file, bool check_basic_portability,
if (check_basic_portability)
{
if (! portable_chars_only (file, filelen))
- return false;
+ return false;
}
else
{
/* Check whether a file name component is in a directory that
- is not searchable, or has some other serious problem.
- POSIX does not allow "" as a file name, but some non-POSIX
- hosts do (as an alias for "."), so allow "" if lstat does. */
+ is not searchable, or has some other serious problem.
+ POSIX does not allow "" as a file name, but some non-POSIX
+ hosts do (as an alias for "."), so allow "" if lstat does. */
struct stat st;
if (lstat (file, &st) == 0)
- file_exists = true;
+ file_exists = true;
else if (errno != ENOENT || filelen == 0)
- {
- error (0, errno, "%s", file);
- return false;
- }
+ {
+ error (0, errno, "%s", quotef (file));
+ return false;
+ }
}
if (check_basic_portability
@@ -311,31 +300,31 @@ validate_file_name (char *file, bool check_basic_portability,
size_t maxsize;
if (check_basic_portability)
- maxsize = _POSIX_PATH_MAX;
+ maxsize = _POSIX_PATH_MAX;
else
- {
- long int size;
- char const *dir = (*file == '/' ? "/" : ".");
- errno = 0;
- size = pathconf (dir, _PC_PATH_MAX);
- if (size < 0 && errno != 0)
- {
- error (0, errno,
- _("%s: unable to determine maximum file name length"),
- dir);
- return false;
- }
- maxsize = MIN (size, SIZE_MAX);
- }
+ {
+ long int size;
+ char const *dir = (*file == '/' ? "/" : ".");
+ errno = 0;
+ size = pathconf (dir, _PC_PATH_MAX);
+ if (size < 0 && errno != 0)
+ {
+ error (0, errno,
+ _("%s: unable to determine maximum file name length"),
+ dir);
+ return false;
+ }
+ maxsize = MIN (size, SSIZE_MAX);
+ }
if (maxsize <= filelen)
- {
- unsigned long int len = filelen;
- unsigned long int maxlen = maxsize - 1;
- error (0, 0, _("limit %lu exceeded by length %lu of file name %s"),
- maxlen, len, quote (file));
- return false;
- }
+ {
+ unsigned long int len = filelen;
+ unsigned long int maxlen = maxsize - 1;
+ error (0, 0, _("limit %lu exceeded by length %lu of file name %s"),
+ maxlen, len, quoteaf (file));
+ return false;
+ }
}
/* Check whether pathconf (..., _PC_NAME_MAX) can be avoided, i.e.,
@@ -347,17 +336,17 @@ validate_file_name (char *file, bool check_basic_portability,
if (! check_component_lengths && ! file_exists)
{
for (start = file; *(start = component_start (start)); )
- {
- size_t length = component_len (start);
+ {
+ size_t length = component_len (start);
- if (NAME_MAX_MINIMUM < length)
- {
- check_component_lengths = true;
- break;
- }
+ if (NAME_MAX_MINIMUM < length)
+ {
+ check_component_lengths = true;
+ break;
+ }
- start += length;
- }
+ start += length;
+ }
}
if (check_component_lengths)
@@ -372,61 +361,61 @@ validate_file_name (char *file, bool check_basic_portability,
size_t known_name_max = (check_basic_portability ? _POSIX_NAME_MAX : 0);
for (start = file; *(start = component_start (start)); )
- {
- size_t length;
-
- if (known_name_max)
- name_max = known_name_max;
- else
- {
- long int len;
- char const *dir = (start == file ? "." : file);
- char c = *start;
- errno = 0;
- *start = '\0';
- len = pathconf (dir, _PC_NAME_MAX);
- *start = c;
- if (0 <= len)
- name_max = MIN (len, SIZE_MAX);
- else
- switch (errno)
- {
- case 0:
- /* There is no limit. */
- name_max = SIZE_MAX;
- break;
-
- case ENOENT:
- /* DIR does not exist; use its parent's maximum. */
- known_name_max = name_max;
- break;
-
- default:
- *start = '\0';
- error (0, errno, "%s", dir);
- *start = c;
- return false;
- }
- }
-
- length = component_len (start);
-
- if (name_max < length)
- {
- unsigned long int len = length;
- unsigned long int maxlen = name_max;
- char c = start[len];
- start[len] = '\0';
- error (0, 0,
- _("limit %lu exceeded by length %lu "
- "of file name component %s"),
- maxlen, len, quote (start));
- start[len] = c;
- return false;
- }
-
- start += length;
- }
+ {
+ size_t length;
+
+ if (known_name_max)
+ name_max = known_name_max;
+ else
+ {
+ long int len;
+ char const *dir = (start == file ? "." : file);
+ char c = *start;
+ errno = 0;
+ *start = '\0';
+ len = pathconf (dir, _PC_NAME_MAX);
+ *start = c;
+ if (0 <= len)
+ name_max = MIN (len, SSIZE_MAX);
+ else
+ switch (errno)
+ {
+ case 0:
+ /* There is no limit. */
+ name_max = SIZE_MAX;
+ break;
+
+ case ENOENT:
+ /* DIR does not exist; use its parent's maximum. */
+ known_name_max = name_max;
+ break;
+
+ default:
+ *start = '\0';
+ error (0, errno, "%s", quotef (dir));
+ *start = c;
+ return false;
+ }
+ }
+
+ length = component_len (start);
+
+ if (name_max < length)
+ {
+ unsigned long int len = length;
+ unsigned long int maxlen = name_max;
+ char c = start[len];
+ start[len] = '\0';
+ error (0, 0,
+ _("limit %lu exceeded by length %lu "
+ "of file name component %s"),
+ maxlen, len, quote (start));
+ start[len] = c;
+ return false;
+ }
+
+ start += length;
+ }
}
return true;
diff --git a/src/pinky.c b/src/pinky.c
index 885012b..68566fd 100644
--- a/src/pinky.c
+++ b/src/pinky.c
@@ -1,10 +1,10 @@
/* GNU's pinky.
- Copyright (C) 1992-1997, 1999-2006 Free Software Foundation, Inc.
+ Copyright (C) 1992-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Created by hacking who.c by Kaveh Ghazi ghazi@caip.rutgers.edu */
@@ -28,22 +27,17 @@
#include "canon-host.h"
#include "error.h"
#include "hard-locale.h"
-#include "inttostr.h"
#include "readutmp.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "pinky"
-#define AUTHORS "Joseph Arceneaux", "David MacKenzie", "Kaveh Ghazi"
+#define AUTHORS \
+ proper_name ("Joseph Arceneaux"), \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Kaveh Ghazi")
-#ifndef MAXHOSTNAMELEN
-# define MAXHOSTNAMELEN 64
-#endif
-
-char *ttyname ();
-
-/* The name this program was run with. */
-const char *program_name;
+char *ttyname (int);
/* If true, display the hours:minutes since each user has touched
the keyboard, or blank if within the last minute, or days followed
@@ -88,14 +82,14 @@ static struct option const longopts[] =
/* Count and return the number of ampersands in STR. */
-static size_t
+static size_t _GL_ATTRIBUTE_PURE
count_ampersands (const char *str)
{
size_t count = 0;
do
{
if (*str == '&')
- count++;
+ count++;
} while (*str++);
return count;
}
@@ -103,7 +97,7 @@ count_ampersands (const char *str)
/* Create a string (via xmalloc) which contains a full name by substituting
for each ampersand in GECOS_NAME the USER_NAME string with its first
character capitalized. The caller must ensure that GECOS_NAME contains
- no `,'s. The caller also is responsible for free'ing the return value of
+ no ','s. The caller also is responsible for free'ing the return value of
this function. */
static char *
@@ -120,7 +114,7 @@ create_fullname (const char *gecos_name, const char *user_name)
size_t product = ampersands * ulen;
rsize += product - ampersands;
if (xalloc_oversized (ulen, ampersands) || rsize < product)
- xalloc_die ();
+ xalloc_die ();
}
r = result = xmalloc (rsize);
@@ -128,17 +122,17 @@ create_fullname (const char *gecos_name, const char *user_name)
while (*gecos_name)
{
if (*gecos_name == '&')
- {
- const char *uname = user_name;
- if (islower (to_uchar (*uname)))
- *r++ = toupper (to_uchar (*uname++));
- while (*uname)
- *r++ = *uname++;
- }
+ {
+ const char *uname = user_name;
+ if (islower (to_uchar (*uname)))
+ *r++ = toupper (to_uchar (*uname++));
+ while (*uname)
+ *r++ = *uname++;
+ }
else
- {
- *r++ = *gecos_name;
- }
+ {
+ *r++ = *gecos_name;
+ }
gecos_name++;
}
@@ -185,10 +179,10 @@ time_string (const STRUCT_UTMP *utmp_ent)
/* Don't take the address of UT_TIME_MEMBER directly.
Ulrich Drepper wrote:
- ``... GNU libc (and perhaps other libcs as well) have extended
+ "... GNU libc (and perhaps other libcs as well) have extended
utmp file formats which do not use a simple time_t ut_time field.
In glibc, ut_time is a macro which selects for backward compatibility
- the tv_sec member of a struct timeval value.'' */
+ the tv_sec member of a struct timeval value." */
time_t t = UT_TIME_MEMBER (utmp_ent);
struct tm *tmp = localtime (&t);
@@ -198,7 +192,7 @@ time_string (const STRUCT_UTMP *utmp_ent)
return buf;
}
else
- return TYPE_SIGNED (time_t) ? imaxtostr (t, buf) : umaxtostr (t, buf);
+ return timetostr (t, buf);
}
/* Display a line of information about UTMP_ENT. */
@@ -214,21 +208,14 @@ print_entry (const STRUCT_UTMP *utmp_ent)
#define DEV_DIR_LEN (sizeof (DEV_DIR_WITH_TRAILING_SLASH) - 1)
char line[sizeof (utmp_ent->ut_line) + DEV_DIR_LEN + 1];
+ char *p = line;
- /* Copy ut_line into LINE, prepending `/dev/' if ut_line is not
+ /* Copy ut_line into LINE, prepending '/dev/' if ut_line is not
already an absolute file name. Some system may put the full,
absolute file name in ut_line. */
- if (utmp_ent->ut_line[0] == '/')
- {
- strncpy (line, utmp_ent->ut_line, sizeof (utmp_ent->ut_line));
- line[sizeof (utmp_ent->ut_line)] = '\0';
- }
- else
- {
- strcpy (line, DEV_DIR_WITH_TRAILING_SLASH);
- strncpy (line + DEV_DIR_LEN, utmp_ent->ut_line, sizeof (utmp_ent->ut_line));
- line[DEV_DIR_LEN + sizeof (utmp_ent->ut_line)] = '\0';
- }
+ if ( ! IS_ABSOLUTE_FILE_NAME (utmp_ent->ut_line))
+ p = stpcpy (p, DEV_DIR_WITH_TRAILING_SLASH);
+ stzncpy (p, utmp_ent->ut_line, sizeof (utmp_ent->ut_line));
if (stat (line, &stats) == 0)
{
@@ -248,34 +235,35 @@ print_entry (const STRUCT_UTMP *utmp_ent)
struct passwd *pw;
char name[UT_USER_SIZE + 1];
- strncpy (name, UT_USER (utmp_ent), UT_USER_SIZE);
- name[UT_USER_SIZE] = '\0';
+ stzncpy (name, UT_USER (utmp_ent), UT_USER_SIZE);
pw = getpwnam (name);
if (pw == NULL)
- printf (" %19s", " ???");
+ /* TRANSLATORS: Real name is unknown; at most 19 characters. */
+ printf (" %19s", _(" ???"));
else
- {
- char *const comma = strchr (pw->pw_gecos, ',');
- char *result;
+ {
+ char *const comma = strchr (pw->pw_gecos, ',');
+ char *result;
- if (comma)
- *comma = '\0';
+ if (comma)
+ *comma = '\0';
- result = create_fullname (pw->pw_gecos, pw->pw_name);
- printf (" %-19.19s", result);
- free (result);
- }
+ result = create_fullname (pw->pw_gecos, pw->pw_name);
+ printf (" %-19.19s", result);
+ free (result);
+ }
}
printf (" %c%-8.*s",
- mesg, (int) sizeof (utmp_ent->ut_line), utmp_ent->ut_line);
+ mesg, (int) sizeof (utmp_ent->ut_line), utmp_ent->ut_line);
if (include_idle)
{
if (last_change)
- printf (" %-6s", idle_string (last_change));
+ printf (" %-6s", idle_string (last_change));
else
- printf (" %-6s", "???");
+ /* TRANSLATORS: Idle time is unknown; at most 5 characters. */
+ printf (" %-6s", _("?????"));
}
printf (" %s", time_string (utmp_ent));
@@ -288,27 +276,26 @@ print_entry (const STRUCT_UTMP *utmp_ent)
char *display = NULL;
/* Copy the host name into UT_HOST, and ensure it's nul terminated. */
- strncpy (ut_host, utmp_ent->ut_host, (int) sizeof (utmp_ent->ut_host));
- ut_host[sizeof (utmp_ent->ut_host)] = '\0';
+ stzncpy (ut_host, utmp_ent->ut_host, sizeof (utmp_ent->ut_host));
/* Look for an X display. */
display = strchr (ut_host, ':');
if (display)
- *display++ = '\0';
+ *display++ = '\0';
if (*ut_host)
- /* See if we can canonicalize it. */
- host = canon_host (ut_host);
+ /* See if we can canonicalize it. */
+ host = canon_host (ut_host);
if ( ! host)
- host = ut_host;
+ host = ut_host;
if (display)
- printf (" %s:%s", host, display);
+ printf (" %s:%s", host, display);
else
- printf (" %s", host);
+ printf (" %s", host);
if (host != ut_host)
- free (host);
+ free (host);
}
#endif
@@ -330,6 +317,7 @@ print_long_entry (const char name[])
printf (_("In real life: "));
if (pw == NULL)
{
+ /* TRANSLATORS: Real name is unknown; no hard limit. */
printf (" %s", _("???\n"));
return;
}
@@ -339,7 +327,7 @@ print_long_entry (const char name[])
char *result;
if (comma)
- *comma = '\0';
+ *comma = '\0';
result = create_fullname (pw->pw_gecos, pw->pw_name);
printf (" %s", result);
@@ -363,22 +351,20 @@ print_long_entry (const char name[])
char buf[1024];
const char *const baseproject = "/.project";
char *const project =
- xmalloc (strlen (pw->pw_dir) + strlen (baseproject) + 1);
-
- strcpy (project, pw->pw_dir);
- strcat (project, baseproject);
+ xmalloc (strlen (pw->pw_dir) + strlen (baseproject) + 1);
+ stpcpy (stpcpy (project, pw->pw_dir), baseproject);
stream = fopen (project, "r");
if (stream)
- {
- size_t bytes;
+ {
+ size_t bytes;
- printf (_("Project: "));
+ printf (_("Project: "));
- while ((bytes = fread (buf, 1, sizeof (buf), stream)) > 0)
- fwrite (buf, 1, bytes, stdout);
- fclose (stream);
- }
+ while ((bytes = fread (buf, 1, sizeof (buf), stream)) > 0)
+ fwrite (buf, 1, bytes, stdout);
+ fclose (stream);
+ }
free (project);
}
@@ -389,22 +375,20 @@ print_long_entry (const char name[])
char buf[1024];
const char *const baseplan = "/.plan";
char *const plan =
- xmalloc (strlen (pw->pw_dir) + strlen (baseplan) + 1);
-
- strcpy (plan, pw->pw_dir);
- strcat (plan, baseplan);
+ xmalloc (strlen (pw->pw_dir) + strlen (baseplan) + 1);
+ stpcpy (stpcpy (plan, pw->pw_dir), baseplan);
stream = fopen (plan, "r");
if (stream)
- {
- size_t bytes;
+ {
+ size_t bytes;
- printf (_("Plan:\n"));
+ printf (_("Plan:\n"));
- while ((bytes = fread (buf, 1, sizeof (buf), stream)) > 0)
- fwrite (buf, 1, bytes, stdout);
- fclose (stream);
- }
+ while ((bytes = fread (buf, 1, sizeof (buf), stream)) > 0)
+ fwrite (buf, 1, bytes, stdout);
+ fclose (stream);
+ }
free (plan);
}
@@ -436,7 +420,7 @@ print_heading (void)
static void
scan_entries (size_t n, const STRUCT_UTMP *utmp_buf,
- const int argc_names, char *const argv_names[])
+ const int argc_names, char *const argv_names[])
{
if (hard_locale (LC_TIME))
{
@@ -455,22 +439,21 @@ scan_entries (size_t n, const STRUCT_UTMP *utmp_buf,
while (n--)
{
if (IS_USER_PROCESS (utmp_buf))
- {
- if (argc_names)
- {
- int i;
-
- for (i = 0; i < argc_names; i++)
- if (strncmp (UT_USER (utmp_buf), argv_names[i], UT_USER_SIZE)
- == 0)
- {
- print_entry (utmp_buf);
- break;
- }
- }
- else
- print_entry (utmp_buf);
- }
+ {
+ if (argc_names)
+ {
+ int i;
+
+ for (i = 0; i < argc_names; i++)
+ if (STREQ_LEN (UT_USER (utmp_buf), argv_names[i], UT_USER_SIZE))
+ {
+ print_entry (utmp_buf);
+ break;
+ }
+ }
+ else
+ print_entry (utmp_buf);
+ }
utmp_buf++;
}
}
@@ -479,15 +462,17 @@ scan_entries (size_t n, const STRUCT_UTMP *utmp_buf,
static void
short_pinky (const char *filename,
- const int argc_names, char *const argv_names[])
+ const int argc_names, char *const argv_names[])
{
size_t n_users;
- STRUCT_UTMP *utmp_buf;
+ STRUCT_UTMP *utmp_buf = NULL;
if (read_utmp (filename, &n_users, &utmp_buf, 0) != 0)
- error (EXIT_FAILURE, errno, "%s", filename);
+ error (EXIT_FAILURE, errno, "%s", quotef (filename));
scan_entries (n_users, utmp_buf, argc_names, argv_names);
+
+ IF_LINT (free (utmp_buf));
}
static void
@@ -503,8 +488,7 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("Usage: %s [OPTION]... [USER]...\n"), program_name);
@@ -527,10 +511,10 @@ usage (int status)
fputs (VERSION_OPTION_DESCRIPTION, stdout);
printf (_("\
\n\
-A lightweight `finger' program; print user information.\n\
+A lightweight 'finger' program; print user information.\n\
The utmp file will be %s.\n\
"), UTMP_FILE);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -542,7 +526,7 @@ main (int argc, char **argv)
int n_users;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -552,57 +536,57 @@ main (int argc, char **argv)
while ((optc = getopt_long (argc, argv, "sfwiqbhlp", longopts, NULL)) != -1)
{
switch (optc)
- {
- case 's':
- do_short_format = true;
- break;
+ {
+ case 's':
+ do_short_format = true;
+ break;
- case 'l':
- do_short_format = false;
- break;
+ case 'l':
+ do_short_format = false;
+ break;
- case 'f':
- include_heading = false;
- break;
+ case 'f':
+ include_heading = false;
+ break;
- case 'w':
- include_fullname = false;
- break;
+ case 'w':
+ include_fullname = false;
+ break;
- case 'i':
- include_fullname = false;
+ case 'i':
+ include_fullname = false;
#ifdef HAVE_UT_HOST
- include_where = false;
+ include_where = false;
#endif
- break;
+ break;
- case 'q':
- include_fullname = false;
+ case 'q':
+ include_fullname = false;
#ifdef HAVE_UT_HOST
- include_where = false;
+ include_where = false;
#endif
- include_idle = false;
- break;
+ include_idle = false;
+ break;
- case 'h':
- include_project = false;
- break;
+ case 'h':
+ include_project = false;
+ break;
- case 'p':
- include_plan = false;
- break;
+ case 'p':
+ include_plan = false;
+ break;
- case 'b':
- include_home_and_shell = false;
- break;
+ case 'b':
+ include_home_and_shell = false;
+ break;
- case_GETOPT_HELP_CHAR;
+ case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- }
+ default:
+ usage (EXIT_FAILURE);
+ }
}
n_users = argc - optind;
@@ -619,5 +603,5 @@ main (int argc, char **argv)
else
long_pinky (n_users, argv + optind);
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/pr.c b/src/pr.c
index e0aea22..8885fff 100644
--- a/src/pr.c
+++ b/src/pr.c
@@ -1,10 +1,10 @@
/* pr -- convert text files for printing.
- Copyright (C) 88, 91, 1995-2007 Free Software Foundation, Inc.
+ Copyright (C) 1988-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,11 +12,10 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* By Pete TerMaat, with considerable refinement by Roland Huebner. */
-
+
/* Things to watch: Sys V screws up on ...
pr -n -3 -s: /usr/dict/words
pr -m -o10 -n /usr/dict/words{,,,}
@@ -35,26 +34,26 @@
Concept:
If the input_tab_char differs from the default value TAB
- (`-e[CHAR[...]]' is used), any input text tab is expanded to the
+ ('-e[CHAR[...]]' is used), any input text tab is expanded to the
default width of 8 spaces (compare char_to_clump). - Same as SunOS
does.
The treatment of the number_separator (compare add_line_number):
- The default value TAB of the number_separator (`-n[SEP[...]]') doesn't
- be thought to be an input character. An optional `-e'-input has no
+ The default value TAB of the number_separator ('-n[SEP[...]]') doesn't
+ be thought to be an input character. An optional '-e'-input has no
effect.
- With single column output
only one POSIX requirement has to be met:
The default n-separator should be a TAB. The consequence is a
different width between the number and the text if the output position
- of the separator changes, i.e. it depends upon the left margin used.
+ of the separator changes, i.e., it depends upon the left margin used.
That's not nice but easy-to-use together with the defaults of other
utilities, e.g. sort or cut. - Same as SunOS does.
- With multicolumn output
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'.
+ 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'.
(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
@@ -73,9 +72,9 @@
PAGE_WIDTH may occur.
The interference of the POSIX-compliant small letter options -w and -s:
- (`interference' means `setting a _separator_ with -s switches off the
- column sturctur and the default - not generally - page_width,
- acts on -w option')
+ ("interference" means "setting a _separator_ with -s switches off the
+ column structure and the default - not generally - page_width,
+ acts on -w option")
options: text form / separator: equivalent new options:
-w l -s[x]
--------------------------------------------------------------------
@@ -93,7 +92,7 @@
Options:
Including version 1.22i:
- Some SMALL LETTER options has been redefined with the object of a
+ Some SMALL LETTER options have been redefined with the object of a
better POSIX compliance. The output of some further cases has been
adapted to other UNIXes. A violation of downward compatibility has to
be accepted.
@@ -113,201 +112,200 @@
form feeds produce empty pages.
+FIRST_PAGE[:LAST_PAGE], --pages=FIRST_PAGE[:LAST_PAGE]
- begin [stop] printing with page FIRST_[LAST_]PAGE
+ begin [stop] printing with page FIRST_[LAST_]PAGE
-COLUMN, --columns=COLUMN
- Produce output that is COLUMN columns wide and
- print columns down, unless -a is used. Balance number of
- lines in the columns on each page.
+ Produce output that is COLUMN 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
- one
- two
- three
- four
- will be printed with `-a -3' as
- one two three
- four
+ together with -COLUMN. The input
+ one
+ two
+ three
+ four
+ will be printed with '-a -3' as
+ one two three
+ four
-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
- 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
- printing columns down is equivalent to that on the last
- page. So we need a balancing.
-
- Keeping -b as an underground option guarantees some
- downward compatibility. Utilities using pr with -b
- (a most frequently used form) still work as usual.
+ -b is no longer an independent option. It's always used
+ together with -COLUMN (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
+ printing columns down is equivalent to that on the last
+ page. So we need a balancing.
+
+ Keeping -b as an underground option guarantees some
+ downward compatibility. Utilities using pr with -b
+ (a most frequently used form) still work as usual.
-c, --show-control-chars
- Print unprintable characters as control prefixes.
- Control-g is printed as ^G (use hat notation) and
- octal backslash notation.
+ Print unprintable characters as control prefixes.
+ Control-g is printed as ^G (use hat notation) and
+ octal backslash notation.
-d, --double-space Double space the output.
-D FORMAT, --date-format=FORMAT Use FORMAT for the header date.
-e[CHAR[WIDTH]], --expand-tabs[=CHAR[WIDTH]]
- Expand tabs to spaces on input. Optional argument CHAR
- is the input TAB character. (Default is TAB). Optional
- argument WIDTH is the input TAB character's width.
- (Default is 8.)
+ Expand tabs to spaces on input. Optional argument CHAR
+ is the input TAB character. (Default is TAB). Optional
+ argument WIDTH is the input TAB character's width.
+ (Default is 8.)
-F, -f, --form-feed Use formfeeds instead of newlines to separate
- pages. A three line HEADER is used, no TRAILER with -F,
- without -F both HEADER and TRAILER are made of five lines.
+ pages. A three line HEADER is used, no TRAILER with -F,
+ without -F both HEADER and TRAILER are made of five lines.
-h HEADER, --header=HEADER
- Replace the filename in the header with the string HEADER.
- A centered header is used.
+ Replace the filename in the header with the string HEADER.
+ A centered header is used.
-i[CHAR[WIDTH]], --output-tabs[=CHAR[WIDTH]]
- Replace spaces with tabs on output. Optional argument
- CHAR is the output TAB character. (Default is TAB).
- Optional argument WIDTH is the output TAB character's
- width. (Default is 8)
+ Replace spaces with tabs on output. Optional argument
+ CHAR is the output TAB character. (Default is TAB).
+ Optional argument WIDTH is the output TAB character's
+ width. (Default is 8)
-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).
- -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.
+ line truncation, no column alignment, --sep-string[=STRING]
+ sets separators, works with all column options
+ (-COLUMN | -a -COLUMN | -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.
-l PAGE_LENGTH, --length=PAGE_LENGTH
- Set the page length to PAGE_LENGTH lines. Default is 66,
- including 5 lines of HEADER and 5 lines of TRAILER
- without -F, but only 3 lines of HEADER and no TRAILER
- with -F (i.e the number of text lines defaults to 56 or
- 63 respectively).
+ Set the page length to PAGE_LENGTH lines. Default is 66,
+ including 5 lines of HEADER and 5 lines of TRAILER
+ without -F, but only 3 lines of HEADER and no TRAILER
+ with -F (i.e the number of text lines defaults to 56 or
+ 63 respectively).
-m, --merge Print files in parallel; pad_across_to align
- columns; truncate lines and print separator strings;
- Do it also with empty columns to get a continuous line
- numbering and column marking by separators throughout
- the whole merged file.
+ columns; truncate lines and print separator strings;
+ Do it also with empty columns to get a continuous line
+ numbering and column marking by separators throughout
+ the whole merged file.
- Empty pages in some input files produce empty columns
- [marked by separators] in the merged pages. Completely
- empty merged pages show no column separators at all.
+ Empty pages in some input files produce empty columns
+ [marked by separators] in the merged pages. Completely
+ empty merged pages show no column separators at all.
- The layout of a merged page is ruled by the largest form
- feed distance of the single pages at that page. Shorter
- columns will be filled up with empty lines.
+ The layout of a merged page is ruled by the largest form
+ feed distance of the single pages at that page. Shorter
+ columns will be filled up with empty lines.
- Together with -J option join lines of full length and
- set separators when -S option is used.
+ Together with -J option join lines of full length and
+ set separators when -S option is used.
-n[SEP[DIGITS]], --number-lines[=SEP[DIGITS]]
- Provide DIGITS digit line numbering (default for DIGITS
- is 5). With multicolumn output the number occupies the
- first DIGITS column positions of each text column or only
- each line of -m output.
- With single column output the number precedes each line
- just as -m output.
- Optional argument SEP is the character appended to the
- line number to separate it from the text followed.
- The default separator is a TAB. In a strict sense a TAB
- is always printed with single column output only. The
- TAB-width varies with the TAB-position, e.g. with the
- left margin specified by -o option.
- With multicolumn output priority is given to `equal width
- of output columns' (a POSIX specification). The TAB-width
- is fixed to the value of the 1st column and does not
- change with different values of left margin. That means a
- fixed number of spaces is always printed in the place of
- a TAB. The tabification depends upon the output
- position.
-
- Default counting of the line numbers starts with 1st
- line of the input file (not the 1st line printed,
- compare the --page option and -N option).
+ Provide DIGITS digit line numbering (default for DIGITS
+ is 5). With multicolumn output the number occupies the
+ first DIGITS column positions of each text column or only
+ each line of -m output.
+ With single column output the number precedes each line
+ just as -m output.
+ Optional argument SEP is the character appended to the
+ line number to separate it from the text followed.
+ The default separator is a TAB. In a strict sense a TAB
+ is always printed with single column output only. The
+ TAB-width varies with the TAB-position, e.g. with the
+ left margin specified by -o option.
+ With multicolumn output priority is given to "equal width
+ of output columns" (a POSIX specification). The TAB-width
+ is fixed to the value of the 1st column and does not
+ change with different values of left margin. That means a
+ fixed number of spaces is always printed in the place of
+ a TAB. The tabification depends upon the output
+ position.
+
+ Default counting of the line numbers starts with 1st
+ line of the input file (not the 1st line printed,
+ compare the --page option and -N option).
-N NUMBER, --first-line-number=NUMBER
- Start line counting with the number NUMBER at the 1st
- line of first page printed (mostly not the 1st line of
- the input file).
+ Start line counting with the number NUMBER at the 1st
+ line of first page printed (mostly not the 1st line of
+ the input file).
-o MARGIN, --indent=MARGIN
- Offset each line with a margin MARGIN spaces wide.
- Total page width is the size of the margin plus the
- PAGE_WIDTH set with -W/-w option.
+ Offset each line with a margin MARGIN spaces wide.
+ Total page width is the size of the margin plus the
+ PAGE_WIDTH set with -W/-w option.
-r, --no-file-warnings
- Omit warning when a file cannot be opened.
+ Omit warning when a file cannot be opened.
-s[CHAR], --separator[=CHAR]
- Separate columns by a single character CHAR, default for
- 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
- compliant formulation. The source code translates -s into
- the new options -S and -J, also -W if required.
-
- -S STRING, --sep-string[=STRING]
- Separate columns by any string STRING. The -S option
- doesn't react upon the -W/-w option (unlike -s option
- does). It defines a separator nothing else.
- Without -S: Default separator TAB is used with -J and
- `space' otherwise (same as -S" ").
- With -S "": No separator is used.
- Quotes should be used with blanks and some shell active
- characters.
- -S is problematic because in its obsolete form you
- cannot use -S "STRING", but in its standard form you
- must use -S "STRING" if STRING is empty. Use
- --sep-string to avoid the ambiguity.
+ Separate columns by a single character CHAR, default for
+ 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
+ compliant formulation. The source code translates -s into
+ the new options -S and -J, also -W if required.
+
+ -S[STRING], --sep-string[=STRING]
+ Separate columns by any string STRING. The -S option
+ doesn't react upon the -W/-w option (unlike -s option
+ does). It defines a separator nothing else.
+ Without -S: Default separator TAB is used with -J and
+ 'space' otherwise (same as -S" ").
+ With -S "": No separator is used.
+ Quotes should be used with blanks and some shell active
+ characters.
+ -S is problematic because in its obsolete form you
+ cannot use -S "STRING", but in its standard form you
+ must use -S "STRING" if STRING is empty. Use
+ --sep-string to avoid the ambiguity.
-t, --omit-header Do not print headers or footers but retain form
- feeds set in the input files.
+ feeds set in the input files.
-T, --omit-pagination
- Do not print headers or footers, eliminate any pagination
- by form feeds set in the input files.
+ Do not print headers or footers, eliminate any pagination
+ by form feeds set in the input files.
-v, --show-nonprinting
- Print unprintable characters as escape sequences. Use
- octal backslash notation. Control-G becomes \007.
+ Print unprintable characters as escape sequences. Use
+ octal backslash notation. Control-G becomes \007.
-w PAGE_WIDTH, --width=PAGE_WIDTH
- Set page width to PAGE_WIDTH characters for multiple
- text-column output only (default for PAGE_WIDTH is 72).
- -s[CHAR] turns off the default page width and any line
- truncation. Lines of full length will be merged,
- regardless of the column options set. A POSIX compliant
- formulation.
+ Set page width to PAGE_WIDTH characters for multiple
+ text-column output only (default for PAGE_WIDTH is 72).
+ -s[CHAR] turns off the default page width and any line
+ truncation. Lines of full length will be merged,
+ regardless of the column options set. A POSIX compliant
+ formulation.
-W PAGE_WIDTH, --page-width=PAGE_WIDTH
- 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
- is always used.
- Default is 72 characters.
- Without -W PAGE_WIDTH
- - but with one of the column options default truncation of
- 72 characters is used (to keep downward compatibility
- and to simplify most frequently met column tasks).
- Column alignment and column separators are used.
- - and without any of the column options NO line truncation
- is used (to keep downward compatibility and to meet most
- frequent tasks). That's equivalent to -W 72 -J .
-
- With/without -W PAGE_WIDTH the header line is always
- truncated to avoid line overflow.
-
- (In pr versions newer than 1.14 -S option does no longer
- affect -W option.)
+ 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
+ is always used.
+ Default is 72 characters.
+ Without -W PAGE_WIDTH
+ - but with one of the column options default truncation of
+ 72 characters is used (to keep downward compatibility
+ and to simplify most frequently met column tasks).
+ Column alignment and column separators are used.
+ - and without any of the column options NO line truncation
+ is used (to keep downward compatibility and to meet most
+ frequent tasks). That's equivalent to -W 72 -J .
+
+ With/without -W PAGE_WIDTH the header line is always
+ truncated to avoid line overflow.
+
+ (In pr versions newer than 1.14 -S option does no longer
+ affect -W option.)
*/
-
#include <config.h>
@@ -315,19 +313,22 @@
#include <sys/types.h>
#include "system.h"
#include "error.h"
+#include "fadvise.h"
#include "hard-locale.h"
-#include "inttostr.h"
#include "mbswidth.h"
#include "quote.h"
#include "stat-time.h"
#include "stdio--.h"
#include "strftime.h"
#include "xstrtol.h"
+#include "xdectoint.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "pr"
-#define AUTHORS "Pete TerMaat", "Roland Huebner"
+#define AUTHORS \
+ proper_name ("Pete TerMaat"), \
+ proper_name ("Roland Huebner")
/* Used with start_position in the struct COLUMN described below.
If start_position == ANYWHERE, we aren't truncating columns and
@@ -349,27 +350,27 @@
fit the same printing loop.
print_func Function used to print lines in this column.
- If we're storing this column it will be
- print_stored(), Otherwise it will be read_line().
+ If we're storing this column it will be
+ print_stored(), Otherwise it will be read_line().
char_func Function used to process characters in this column.
- If we're storing this column it will be store_char(),
- otherwise it will be print_char().
+ If we're storing this column it will be store_char(),
+ otherwise it will be print_char().
current_line Index of the current entry in line_vector, which
- contains the index of the first character of the
- current line in buff[].
+ contains the index of the first character of the
+ current line in buff[].
lines_stored Number of lines in this column which are stored in
- buff.
+ buff.
lines_to_print If we're storing this column, lines_to_print is
- the number of stored_lines which remain to be
- printed. Otherwise it is the number of lines
- we can print without exceeding lines_per_body.
+ the number of stored_lines which remain to be
+ printed. Otherwise it is the number of lines
+ we can print without exceeding lines_per_body.
start_position The horizontal position we want to be in before we
- print the first character in this column.
+ print the first character in this column.
numbered True means precede this column with a line number. */
@@ -385,11 +386,11 @@ struct COLUMN
char const *name; /* File name. */
enum
{
- OPEN,
- FF_FOUND, /* used with -b option, set with \f, changed
- to ON_HOLD after print_header */
- ON_HOLD, /* Hit a form feed. */
- CLOSED
+ OPEN,
+ FF_FOUND, /* used with -b option, set with \f, changed
+ to ON_HOLD after print_header */
+ ON_HOLD, /* Hit a form feed. */
+ CLOSED
}
status; /* Status of the file pointer. */
@@ -414,8 +415,6 @@ struct COLUMN
typedef struct COLUMN COLUMN;
-#define NULLCOL (COLUMN *)0
-
static int char_to_clump (char c);
static bool read_line (COLUMN *p);
static bool print_page (void);
@@ -425,9 +424,10 @@ static bool skip_to_page (uintmax_t page);
static void print_header (void);
static void pad_across_to (int position);
static void add_line_number (COLUMN *p);
+static void getoptnum (const char *n_str, int min, int *num,
+ const char *errfmt);
static void getoptarg (char *arg, char switch_char, char *character,
- int *number);
-void usage (int status);
+ int *number);
static void print_files (int number_of_files, char **av);
static void init_parameters (int number_of_files);
static void init_header (char const *filename, int desc);
@@ -437,7 +437,7 @@ static void init_store_cols (void);
static void store_columns (void);
static void balance (int total_stored);
static void store_char (char c);
-static void pad_down (int lines);
+static void pad_down (unsigned int lines);
static void read_rest_of_line (COLUMN *p);
static void skip_read (COLUMN *p, int column_number);
static void print_char (char c);
@@ -445,9 +445,6 @@ static void cleanup (void);
static void print_sep_string (void);
static void separator_string (const char *optarg_S);
-/* The name under which this program was invoked. */
-char *program_name;
-
/* All of the columns to print. */
static COLUMN *column_vector;
@@ -459,7 +456,7 @@ static char *buff;
/* Index of the position in buff where the next character
will be stored. */
-static int buff_current;
+static unsigned int buff_current;
/* The number of characters in buff.
Used for allocation of buff and to detect overflow of buff. */
@@ -471,7 +468,7 @@ static size_t buff_allocated;
we do column balancing on the last page. */
static int *line_vector;
-/* Array of horizonal positions.
+/* Array of horizontal positions.
For each line in line_vector, end_vector[line] is the horizontal
position we are in after printing that line. We keep track of this
so that we know how much we need to pad to prepare for the next
@@ -536,9 +533,9 @@ static int lines_per_page = 66;
/* Number of lines in the header and footer can be reset to 0 using
the -t flag. */
-static int lines_per_header = 5;
+enum { lines_per_header = 5 };
static int lines_per_body;
-static int lines_per_footer = 5;
+enum { lines_per_footer = 5 };
/* (-w|-W) Width in characters of the page. Does not include the width of
the margin. */
@@ -635,10 +632,6 @@ static uintmax_t page_number;
2 moo 4 hoo 6 zoo */
static int line_number;
-/* With line_number overflow, we use power_10 to cut off the higher-order
- digits of the line_number */
-static int power_10;
-
/* (-n) True means lines should be preceded by numbers. */
static bool numbered_lines = false;
@@ -693,11 +686,11 @@ static bool use_col_separator = false;
/* String used to separate columns if the -S option has been specified.
Default without -S but together with one of the column options
- -a|COLUMN|-m is a `space' and with the -J option a `tab'. */
-static char *col_sep_string = "";
+ -a|COLUMN|-m is a 'space' and with the -J option a 'tab'. */
+static char *col_sep_string = (char *) "";
static int col_sep_length = 0;
-static char *column_separator = " ";
-static char *line_separator = "\t";
+static char *column_separator = (char *) " ";
+static char *line_separator = (char *) "\t";
/* Number of separator characters waiting to be printed as soon as we
know that we have any input remaining to be printed. */
@@ -777,18 +770,18 @@ static struct option const long_options[] =
/* Return the number of columns that have either an open file or
stored lines. */
-static int
+static unsigned int _GL_ATTRIBUTE_PURE
cols_ready_to_print (void)
{
COLUMN *q;
- int i;
- int n;
+ unsigned int i;
+ unsigned int n;
n = 0;
for (q = column_vector, i = 0; i < columns; ++q, ++i)
- if (q->status == OPEN ||
- q->status == FF_FOUND || /* With -b: To print a header only */
- (storing_columns && q->lines_stored > 0 && q->lines_to_print > 0))
+ if (q->status == OPEN
+ || q->status == FF_FOUND /* With -b: To print a header only */
+ || (storing_columns && q->lines_stored > 0 && q->lines_to_print > 0))
++n;
return n;
}
@@ -797,14 +790,14 @@ cols_ready_to_print (void)
using option +FIRST_PAGE:LAST_PAGE */
static bool
-first_last_page (char const *pages)
+first_last_page (int oi, char c, char const *pages)
{
char *p;
uintmax_t first;
uintmax_t last = UINTMAX_MAX;
strtol_error err = xstrtoumax (pages, &p, 10, &first, "");
if (err != LONGINT_OK && err != LONGINT_INVALID_SUFFIX_CHAR)
- _STRTOL_ERROR (EXIT_FAILURE, pages, _("page range"), err);
+ xstrtol_fatal (err, oi, c, long_options, pages);
if (p == pages || !first)
return false;
@@ -814,9 +807,9 @@ first_last_page (char const *pages)
char const *p1 = p + 1;
err = xstrtoumax (p1, &p, 10, &last, "");
if (err != LONGINT_OK)
- _STRTOL_ERROR (EXIT_FAILURE, pages, _("page range"), err);
+ xstrtol_fatal (err, oi, c, long_options, pages);
if (p1 == p || last < first)
- return false;
+ return false;
}
if (*p)
@@ -828,19 +821,13 @@ first_last_page (char const *pages)
}
/* Parse column count string S, and if it's valid (1 or larger and
- within range of the type of `columns') set the global variables
- columns and explicit_columns and return true.
- Otherwise, exit with a diagnostic. */
+ within range of the type of 'columns') set the global variables
+ columns and explicit_columns. Otherwise, exit with a diagnostic. */
+
static void
parse_column_count (char const *s)
{
- long int tmp_long;
- if (xstrtol (s, NULL, 10, &tmp_long, "") != LONGINT_OK
- || !(1 <= tmp_long && tmp_long <= INT_MAX))
- error (EXIT_FAILURE, 0,
- _("invalid number of columns: %s"), quote (s));
-
- columns = tmp_long;
+ getoptnum (s, 1, &columns, _("invalid number of columns"));
explicit_columns = true;
}
@@ -857,7 +844,6 @@ separator_string (const char *optarg_S)
int
main (int argc, char **argv)
{
- int c;
int n_files;
bool old_options = false;
bool old_w = false;
@@ -870,7 +856,7 @@ main (int argc, char **argv)
size_t n_alloc = 0;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -879,207 +865,177 @@ main (int argc, char **argv)
n_files = 0;
file_names = (argc > 1
- ? xmalloc ((argc - 1) * sizeof (char *))
- : NULL);
+ ? xmalloc ((argc - 1) * sizeof (char *))
+ : NULL);
- while ((c = getopt_long (argc, argv, short_options, long_options, NULL))
- != -1)
+ while (true)
{
+ int oi = -1;
+ int c = getopt_long (argc, argv, short_options, long_options, &oi);
+ if (c == -1)
+ break;
+
if (ISDIGIT (c))
- {
- /* Accumulate column-count digits specified via old-style options. */
- if (n_digits + 1 >= n_alloc)
- column_count_string
- = X2REALLOC (column_count_string, &n_alloc);
- column_count_string[n_digits++] = c;
- column_count_string[n_digits] = '\0';
- continue;
- }
+ {
+ /* Accumulate column-count digits specified via old-style options. */
+ if (n_digits + 1 >= n_alloc)
+ column_count_string
+ = X2REALLOC (column_count_string, &n_alloc);
+ column_count_string[n_digits++] = c;
+ column_count_string[n_digits] = '\0';
+ continue;
+ }
n_digits = 0;
switch (c)
- {
- case 1: /* Non-option argument. */
- /* long option --page dominates old `+FIRST_PAGE ...'. */
- if (! (first_page_number == 0
- && *optarg == '+' && first_last_page (optarg + 1)))
- file_names[n_files++] = optarg;
- break;
-
- case PAGES_OPTION: /* --pages=FIRST_PAGE[:LAST_PAGE] */
- { /* dominates old opt +... */
- if (! optarg)
- error (EXIT_FAILURE, 0,
- _("`--pages=FIRST_PAGE[:LAST_PAGE]' missing argument"));
- else if (! first_last_page (optarg))
- error (EXIT_FAILURE, 0, _("Invalid page range %s"),
- quote (optarg));
- break;
- }
-
- case COLUMNS_OPTION: /* --columns=COLUMN */
- {
- parse_column_count (optarg);
-
- /* If there was a prior column count specified via the
- short-named option syntax, e.g., -9, ensure that this
- long-name-specified value overrides it. */
- free (column_count_string);
- column_count_string = NULL;
- n_alloc = 0;
- break;
- }
-
- case 'a':
- print_across_flag = true;
- storing_columns = false;
- break;
- case 'b':
- balance_columns = true;
- break;
- case 'c':
- use_cntrl_prefix = true;
- break;
- case 'd':
- double_space = true;
- break;
- case 'D':
- date_format = optarg;
- break;
- case 'e':
- if (optarg)
- getoptarg (optarg, 'e', &input_tab_char,
- &chars_per_input_tab);
- /* Could check tab width > 0. */
- untabify_input = true;
- break;
- case 'f':
- case 'F':
- use_form_feed = true;
- break;
- case 'h':
- custom_header = optarg;
- break;
- case 'i':
- if (optarg)
- getoptarg (optarg, 'i', &output_tab_char,
- &chars_per_output_tab);
- /* Could check tab width > 0. */
- tabify_output = true;
- break;
- case 'J':
- join_lines = true;
- break;
- case 'l':
- {
- long int tmp_long;
- if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
- || tmp_long <= 0 || tmp_long > INT_MAX)
- {
- error (EXIT_FAILURE, 0,
- _("`-l PAGE_LENGTH' invalid number of lines: %s"),
- quote (optarg));
- }
- lines_per_page = tmp_long;
- break;
- }
- case 'm':
- parallel_files = true;
- storing_columns = false;
- break;
- case 'n':
- numbered_lines = true;
- if (optarg)
- getoptarg (optarg, 'n', &number_separator,
- &chars_per_number);
- break;
- case 'N':
- skip_count = false;
- {
- long int tmp_long;
- if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
- || tmp_long > INT_MAX)
- {
- error (EXIT_FAILURE, 0,
- _("`-N NUMBER' invalid starting line number: %s"),
- quote (optarg));
- }
- start_line_num = tmp_long;
- break;
- }
- case 'o':
- {
- long int tmp_long;
- if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
- || tmp_long < 0 || tmp_long > INT_MAX)
- error (EXIT_FAILURE, 0,
- _("`-o MARGIN' invalid line offset: %s"), quote (optarg));
- chars_per_margin = tmp_long;
- break;
- }
- case 'r':
- ignore_failed_opens = true;
- break;
- case 's':
- old_options = true;
- old_s = true;
- if (!use_col_separator && optarg)
- separator_string (optarg);
- break;
- case 'S':
- old_s = false;
- /* Reset an additional input of -s, -S dominates -s */
- col_sep_string = "";
- col_sep_length = 0;
- use_col_separator = true;
- if (optarg)
- separator_string (optarg);
- break;
- case 't':
- extremities = false;
- keep_FF = true;
- break;
- case 'T':
- extremities = false;
- keep_FF = false;
- break;
- case 'v':
- use_esc_sequence = true;
- break;
- case 'w':
- old_options = true;
- old_w = true;
- {
- long int tmp_long;
- if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
- || tmp_long <= 0 || tmp_long > INT_MAX)
- error (EXIT_FAILURE, 0,
- _("`-w PAGE_WIDTH' invalid number of characters: %s"),
- quote (optarg));
- if (!truncate_lines)
- chars_per_line = tmp_long;
- break;
- }
- case 'W':
- old_w = false; /* dominates -w */
- truncate_lines = true;
- {
- long int tmp_long;
- if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
- || tmp_long <= 0 || tmp_long > INT_MAX)
- error (EXIT_FAILURE, 0,
- _("`-W PAGE_WIDTH' invalid number of characters: %s"),
- quote (optarg));
- chars_per_line = tmp_long;
- break;
- }
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- break;
- }
+ {
+ case 1: /* Non-option argument. */
+ /* long option --page dominates old '+FIRST_PAGE ...'. */
+ if (! (first_page_number == 0
+ && *optarg == '+' && first_last_page (-2, '+', optarg + 1)))
+ file_names[n_files++] = optarg;
+ break;
+
+ case PAGES_OPTION: /* --pages=FIRST_PAGE[:LAST_PAGE] */
+ { /* dominates old opt +... */
+ if (! optarg)
+ error (EXIT_FAILURE, 0,
+ _("'--pages=FIRST_PAGE[:LAST_PAGE]' missing argument"));
+ else if (! first_last_page (oi, 0, optarg))
+ error (EXIT_FAILURE, 0, _("invalid page range %s"),
+ quote (optarg));
+ break;
+ }
+
+ case COLUMNS_OPTION: /* --columns=COLUMN */
+ {
+ parse_column_count (optarg);
+
+ /* If there was a prior column count specified via the
+ short-named option syntax, e.g., -9, ensure that this
+ long-name-specified value overrides it. */
+ free (column_count_string);
+ column_count_string = NULL;
+ n_alloc = 0;
+ break;
+ }
+
+ case 'a':
+ print_across_flag = true;
+ storing_columns = false;
+ break;
+ case 'b':
+ balance_columns = true;
+ break;
+ case 'c':
+ use_cntrl_prefix = true;
+ break;
+ case 'd':
+ double_space = true;
+ break;
+ case 'D':
+ date_format = optarg;
+ break;
+ case 'e':
+ if (optarg)
+ getoptarg (optarg, 'e', &input_tab_char,
+ &chars_per_input_tab);
+ /* Could check tab width > 0. */
+ untabify_input = true;
+ break;
+ case 'f':
+ case 'F':
+ use_form_feed = true;
+ break;
+ case 'h':
+ custom_header = optarg;
+ break;
+ case 'i':
+ if (optarg)
+ getoptarg (optarg, 'i', &output_tab_char,
+ &chars_per_output_tab);
+ /* Could check tab width > 0. */
+ tabify_output = true;
+ break;
+ case 'J':
+ join_lines = true;
+ break;
+ case 'l':
+ getoptnum (optarg, 1, &lines_per_page,
+ _("'-l PAGE_LENGTH' invalid number of lines"));
+ break;
+ case 'm':
+ parallel_files = true;
+ storing_columns = false;
+ break;
+ case 'n':
+ numbered_lines = true;
+ if (optarg)
+ getoptarg (optarg, 'n', &number_separator,
+ &chars_per_number);
+ break;
+ case 'N':
+ skip_count = false;
+ getoptnum (optarg, INT_MIN, &start_line_num,
+ _("'-N NUMBER' invalid starting line number"));
+ break;
+ case 'o':
+ getoptnum (optarg, 0, &chars_per_margin,
+ _("'-o MARGIN' invalid line offset"));
+ break;
+ case 'r':
+ ignore_failed_opens = true;
+ break;
+ case 's':
+ old_options = true;
+ old_s = true;
+ if (!use_col_separator && optarg)
+ separator_string (optarg);
+ break;
+ case 'S':
+ old_s = false;
+ /* Reset an additional input of -s, -S dominates -s */
+ col_sep_string = bad_cast ("");
+ col_sep_length = 0;
+ use_col_separator = true;
+ if (optarg)
+ separator_string (optarg);
+ break;
+ case 't':
+ extremities = false;
+ keep_FF = true;
+ break;
+ case 'T':
+ extremities = false;
+ keep_FF = false;
+ break;
+ case 'v':
+ use_esc_sequence = true;
+ break;
+ case 'w':
+ old_options = true;
+ old_w = true;
+ {
+ int tmp_cpl;
+ getoptnum (optarg, 1, &tmp_cpl,
+ _("'-w PAGE_WIDTH' invalid number of characters"));
+ if (! truncate_lines)
+ chars_per_line = tmp_cpl;
+ }
+ break;
+ case 'W':
+ old_w = false; /* dominates -w */
+ truncate_lines = true;
+ getoptnum (optarg, 1, &chars_per_line,
+ _("'-W PAGE_WIDTH' invalid number of characters"));
+ break;
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ break;
+ }
}
if (column_count_string)
@@ -1090,20 +1046,20 @@ main (int argc, char **argv)
if (! date_format)
date_format = (getenv ("POSIXLY_CORRECT") && !hard_locale (LC_TIME)
- ? "%b %e %H:%M %Y"
- : "%Y-%m-%d %H:%M");
+ ? "%b %e %H:%M %Y"
+ : "%Y-%m-%d %H:%M");
/* Now we can set a reasonable initial value: */
if (first_page_number == 0)
first_page_number = 1;
- if (parallel_files & explicit_columns)
+ if (parallel_files && explicit_columns)
error (EXIT_FAILURE, 0,
- _("Cannot specify number of columns when printing in parallel."));
+ _("cannot specify number of columns when printing in parallel"));
- if (parallel_files & print_across_flag)
+ if (parallel_files && print_across_flag)
error (EXIT_FAILURE, 0,
- _("Cannot specify both printing across and printing in parallel."));
+ _("cannot specify both printing across and printing in parallel"));
/* Translate some old short options to new/long options.
To meet downward compatibility with other UNIX pr utilities
@@ -1113,41 +1069,41 @@ main (int argc, char **argv)
{
if (old_w)
{
- if (parallel_files | explicit_columns)
- {
- /* activate -W */
- truncate_lines = true;
- if (old_s)
- /* adapt HP-UX and SunOS: -s = no separator;
- activate -S */
- use_col_separator = true;
- }
- else
- /* old -w sets width with columns only
- activate -J */
- join_lines = true;
- }
+ if (parallel_files || explicit_columns)
+ {
+ /* activate -W */
+ truncate_lines = true;
+ if (old_s)
+ /* adapt HP-UX and SunOS: -s = no separator;
+ activate -S */
+ use_col_separator = true;
+ }
+ else
+ /* old -w sets width with columns only
+ activate -J */
+ join_lines = true;
+ }
else if (!use_col_separator)
{
- /* No -S option read */
- if (old_s & (parallel_files | explicit_columns))
- {
- if (!truncate_lines)
- {
- /* old -s (without -w and -W) annuls column alignment,
- uses fields, activate -J */
- join_lines = true;
- if (col_sep_length > 0)
- /* activate -S */
- use_col_separator = true;
- }
- else
- /* with -W */
- /* adapt HP-UX and SunOS: -s = no separator;
- activate -S */
- use_col_separator = true;
- }
- }
+ /* No -S option read */
+ if (old_s && (parallel_files || explicit_columns))
+ {
+ if (!truncate_lines)
+ {
+ /* old -s (without -w and -W) annuls column alignment,
+ uses fields, activate -J */
+ join_lines = true;
+ if (col_sep_length > 0)
+ /* activate -S */
+ use_col_separator = true;
+ }
+ else
+ /* with -W */
+ /* adapt HP-UX and SunOS: -s = no separator;
+ activate -S */
+ use_col_separator = true;
+ }
+ }
}
for (; optind < argc; optind++)
@@ -1163,22 +1119,30 @@ main (int argc, char **argv)
else
{
if (parallel_files)
- print_files (n_files, file_names);
+ print_files (n_files, file_names);
else
- {
- int i;
- for (i = 0; i < n_files; i++)
- print_files (1, &file_names[i]);
- }
+ {
+ unsigned int i;
+ for (i = 0; i < n_files; i++)
+ print_files (1, &file_names[i]);
+ }
}
cleanup ();
+ IF_LINT (free (file_names));
if (have_read_stdin && fclose (stdin) == EOF)
error (EXIT_FAILURE, errno, _("standard input"));
- if (failed_opens)
- exit (EXIT_FAILURE);
- exit (EXIT_SUCCESS);
+ return failed_opens ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+/* Parse numeric arguments, ensuring MIN <= number <= INT_MAX. */
+
+static void
+getoptnum (const char *n_str, int min, int *num, const char *err)
+{
+ intmax_t tnum = xdectoimax (n_str, min, INT_MAX, "", err, 0);
+ *num = tnum;
}
/* Parse options of the form -scNNN.
@@ -1196,17 +1160,17 @@ getoptarg (char *arg, char switch_char, char *character, int *number)
{
long int tmp_long;
if (xstrtol (arg, NULL, 10, &tmp_long, "") != LONGINT_OK
- || tmp_long <= 0 || tmp_long > INT_MAX)
- {
- error (0, 0,
- _("`-%c' extra characters or invalid number in the argument: %s"),
- switch_char, quote (arg));
- usage (EXIT_FAILURE);
- }
+ || tmp_long <= 0 || INT_MAX < tmp_long)
+ {
+ error (0, INT_MAX < tmp_long ? EOVERFLOW : errno,
+ _("'-%c' extra characters or invalid number in the argument: %s"),
+ switch_char, quote (arg));
+ usage (EXIT_FAILURE);
+ }
*number = tmp_long;
}
}
-
+
/* Set parameters related to formatting. */
static void
@@ -1214,12 +1178,6 @@ init_parameters (int number_of_files)
{
int chars_used_by_number = 0;
- if (use_form_feed)
- {
- lines_per_header = 3;
- lines_per_footer = 0;
- }
-
lines_per_body = lines_per_page - lines_per_header - lines_per_footer;
if (lines_per_body <= 0)
{
@@ -1249,16 +1207,16 @@ init_parameters (int number_of_files)
if (columns > 1)
{
if (!use_col_separator)
- {
- /* Use default separator */
- if (join_lines)
- col_sep_string = line_separator;
- else
- col_sep_string = column_separator;
-
- col_sep_length = 1;
- use_col_separator = true;
- }
+ {
+ /* Use default separator */
+ if (join_lines)
+ col_sep_string = line_separator;
+ else
+ col_sep_string = column_separator;
+
+ col_sep_length = 1;
+ use_col_separator = true;
+ }
/* It's rather pointless to define a TAB separator with column
alignment */
else if (!join_lines && *col_sep_string == '\t')
@@ -1276,37 +1234,30 @@ init_parameters (int number_of_files)
if (numbered_lines)
{
- int tmp_i;
int chars_per_default_tab = 8;
line_count = start_line_num;
/* To allow input tab-expansion (-e sensitive) use:
- if (number_separator == input_tab_char)
- number_width = chars_per_number +
- TAB_WIDTH (chars_per_input_tab, chars_per_number); */
+ if (number_separator == input_tab_char)
+ number_width = chars_per_number
+ + TAB_WIDTH (chars_per_input_tab, chars_per_number); */
/* Estimate chars_per_text without any margin and keep it constant. */
if (number_separator == '\t')
- number_width = chars_per_number +
- TAB_WIDTH (chars_per_default_tab, chars_per_number);
+ number_width = (chars_per_number
+ + TAB_WIDTH (chars_per_default_tab, chars_per_number));
else
- number_width = chars_per_number + 1;
+ number_width = chars_per_number + 1;
/* The number is part of the column width unless we are
- printing files in parallel. */
+ printing files in parallel. */
if (parallel_files)
- chars_used_by_number = number_width;
-
- /* We use power_10 to cut off the higher-order digits of the
- line_number in function add_line_number */
- tmp_i = chars_per_number;
- for (power_10 = 1; tmp_i > 0; --tmp_i)
- power_10 = 10 * power_10;
+ chars_used_by_number = number_width;
}
- chars_per_column = (chars_per_line - chars_used_by_number -
- (columns - 1) * col_sep_length) / columns;
+ chars_per_column = (chars_per_line - chars_used_by_number
+ - (columns - 1) * col_sep_length) / columns;
if (chars_per_column < 1)
error (EXIT_FAILURE, 0, _("page width too narrow"));
@@ -1314,7 +1265,8 @@ init_parameters (int number_of_files)
if (numbered_lines)
{
free (number_buff);
- number_buff = xmalloc (2 * chars_per_number);
+ number_buff = xmalloc (MAX (chars_per_number,
+ INT_STRLEN_BOUND (line_number)) + 1);
}
/* Pick the maximum between the tab width and the width of an
@@ -1325,7 +1277,7 @@ init_parameters (int number_of_files)
free (clump_buff);
clump_buff = xmalloc (MAX (8, chars_per_input_tab));
}
-
+
/* Open the necessary files,
maintaining a COLUMN structure for each column.
@@ -1354,54 +1306,54 @@ init_fps (int number_of_files, char **av)
{
files_left = number_of_files;
for (p = column_vector; files_left--; ++p, ++av)
- {
- if (! open_file (*av, p))
- {
- --p;
- --columns;
- }
- }
+ {
+ if (! open_file (*av, p))
+ {
+ --p;
+ --columns;
+ }
+ }
if (columns == 0)
- return false;
+ return false;
init_header ("", -1);
}
else
{
p = column_vector;
if (number_of_files > 0)
- {
- if (! open_file (*av, p))
- return false;
- init_header (*av, fileno (p->fp));
- p->lines_stored = 0;
- }
+ {
+ if (! open_file (*av, p))
+ return false;
+ init_header (*av, fileno (p->fp));
+ p->lines_stored = 0;
+ }
else
- {
- p->name = _("standard input");
- p->fp = stdin;
- have_read_stdin = true;
- p->status = OPEN;
- p->full_page_printed = false;
- ++total_files;
- init_header ("", -1);
- p->lines_stored = 0;
- }
+ {
+ p->name = _("standard input");
+ p->fp = stdin;
+ have_read_stdin = true;
+ p->status = OPEN;
+ p->full_page_printed = false;
+ ++total_files;
+ init_header ("", -1);
+ p->lines_stored = 0;
+ }
firstname = p->name;
firstfp = p->fp;
for (i = columns - 1, ++p; i; --i, ++p)
- {
- p->name = firstname;
- p->fp = firstfp;
- p->status = OPEN;
- p->full_page_printed = false;
- p->lines_stored = 0;
- }
+ {
+ p->name = firstname;
+ p->fp = firstfp;
+ p->status = OPEN;
+ p->full_page_printed = false;
+ p->lines_stored = 0;
+ }
}
files_ready_to_read = total_files;
return true;
}
-
+
/* Determine print_func and char_func, the functions
used by each column for printing and/or storing.
@@ -1421,12 +1373,12 @@ init_funcs (void)
else
{
/* When numbering lines of parallel files, we enlarge the
- first column to accomodate the number. Looks better than
+ first column to accommodate the number. Looks better than
the Sys V approach. */
- if (parallel_files & numbered_lines)
- h_next = h + chars_per_column + number_width;
+ if (parallel_files && numbered_lines)
+ h_next = h + chars_per_column + number_width;
else
- h_next = h + chars_per_column;
+ h_next = h + chars_per_column;
}
/* Enlarge p->start_position of first column to use the same form of
@@ -1438,16 +1390,16 @@ init_funcs (void)
for (p = column_vector, i = 1; i < columns; ++p, ++i)
{
if (storing_columns) /* One file, multi columns down. */
- {
- p->char_func = store_char;
- p->print_func = print_stored;
- }
+ {
+ p->char_func = store_char;
+ p->print_func = print_stored;
+ }
else
- /* One file, multi columns across; or parallel files. */
- {
- p->char_func = print_char;
- p->print_func = read_line;
- }
+ /* One file, multi columns across; or parallel files. */
+ {
+ p->char_func = print_char;
+ p->print_func = read_line;
+ }
/* Number only the first column when printing files in
parallel. */
@@ -1459,22 +1411,22 @@ init_funcs (void)
using a margin. */
if (!truncate_lines)
- {
- h = ANYWHERE;
- h_next = ANYWHERE;
- }
+ {
+ h = ANYWHERE;
+ h_next = ANYWHERE;
+ }
else
- {
- h = h_next + col_sep_length;
- h_next = h + chars_per_column;
- }
+ {
+ h = h_next + col_sep_length;
+ h_next = h + chars_per_column;
+ }
}
/* The rightmost column.
Doesn't need to be stored unless we intend to balance
columns on the last page. */
- if (storing_columns & balance_columns)
+ if (storing_columns && balance_columns)
{
p->char_func = store_char;
p->print_func = print_stored;
@@ -1488,7 +1440,7 @@ init_funcs (void)
p->numbered = numbered_lines && (!parallel_files || i == 1);
p->start_position = h;
}
-
+
/* Open a file. Return true if successful.
With each file p, p->full_page_printed is initialized,
@@ -1512,9 +1464,10 @@ open_file (char *name, COLUMN *p)
{
failed_opens = true;
if (!ignore_failed_opens)
- error (0, errno, "%s", name);
+ error (0, errno, "%s", quotef (name));
return false;
}
+ fadvise (p->fp, FADVISE_SEQUENTIAL);
p->status = OPEN;
p->full_page_printed = false;
++total_files;
@@ -1535,20 +1488,20 @@ close_file (COLUMN *p)
if (p->status == CLOSED)
return;
if (ferror (p->fp))
- error (EXIT_FAILURE, errno, "%s", p->name);
+ error (EXIT_FAILURE, errno, "%s", quotef (p->name));
if (fileno (p->fp) != STDIN_FILENO && fclose (p->fp) != 0)
- error (EXIT_FAILURE, errno, "%s", p->name);
+ error (EXIT_FAILURE, errno, "%s", quotef (p->name));
if (!parallel_files)
{
for (q = column_vector, i = columns; i; ++q, --i)
- {
- q->status = CLOSED;
- if (q->lines_stored == 0)
- {
- q->lines_to_print = 0;
- }
- }
+ {
+ q->status = CLOSED;
+ if (q->lines_stored == 0)
+ {
+ q->lines_to_print = 0;
+ }
+ }
}
else
{
@@ -1574,10 +1527,10 @@ hold_file (COLUMN *p)
if (!parallel_files)
for (q = column_vector, i = columns; i; ++q, --i)
{
- if (storing_columns)
- q->status = FF_FOUND;
- else
- q->status = ON_HOLD;
+ if (storing_columns)
+ q->status = FF_FOUND;
+ else
+ q->status = ON_HOLD;
}
else
p->status = ON_HOLD;
@@ -1598,8 +1551,8 @@ reset_status (void)
for (p = column_vector; i; --i, ++p)
if (p->status == ON_HOLD)
{
- p->status = OPEN;
- files_ready_to_read++;
+ p->status = OPEN;
+ files_ready_to_read++;
}
if (storing_columns)
@@ -1611,7 +1564,7 @@ reset_status (void)
files_ready_to_read = 1;
}
}
-
+
/* Print a single file, or multiple files in parallel.
Set up the list of columns, opening the necessary files.
@@ -1633,9 +1586,9 @@ print_files (int number_of_files, char **av)
if (first_page_number > 1)
{
if (!skip_to_page (first_page_number))
- return;
+ return;
else
- page_number = first_page_number;
+ page_number = first_page_number;
}
else
page_number = 1;
@@ -1646,7 +1599,7 @@ print_files (int number_of_files, char **av)
while (print_page ())
;
}
-
+
/* Initialize header information.
If DESC is non-negative, it is a file descriptor open to
FILENAME for reading. */
@@ -1669,7 +1622,7 @@ init_header (char const *filename, int desc)
{
static struct timespec timespec;
if (! timespec.tv_sec)
- gettime (&timespec);
+ gettime (&timespec);
t = timespec;
}
@@ -1678,7 +1631,7 @@ init_header (char const *filename, int desc)
if (tm == NULL)
{
buf = xmalloc (INT_BUFSIZE_BOUND (long int)
- + MAX (10, INT_BUFSIZE_BOUND (int)));
+ + MAX (10, INT_BUFSIZE_BOUND (int)));
sprintf (buf, "%ld.%09d", (long int) t.tv_sec, ns);
}
else
@@ -1692,10 +1645,10 @@ init_header (char const *filename, int desc)
date_text = buf;
file_text = custom_header ? custom_header : desc < 0 ? "" : filename;
header_width_available = (chars_per_line
- - mbswidth (date_text, 0)
- - mbswidth (file_text, 0));
+ - mbswidth (date_text, 0)
+ - mbswidth (file_text, 0));
}
-
+
/* Set things up for printing a page
Scan through the columns ...
@@ -1716,35 +1669,35 @@ init_page (void)
{
store_columns ();
for (j = columns - 1, p = column_vector; j; --j, ++p)
- {
- p->lines_to_print = p->lines_stored;
- }
+ {
+ p->lines_to_print = p->lines_stored;
+ }
/* Last column. */
if (balance_columns)
- {
- p->lines_to_print = p->lines_stored;
- }
+ {
+ p->lines_to_print = p->lines_stored;
+ }
/* Since we're not balancing columns, we don't need to store
the rightmost column. Read it straight from the file. */
else
- {
- if (p->status == OPEN)
- {
- p->lines_to_print = lines_per_body;
- }
- else
- p->lines_to_print = 0;
- }
+ {
+ if (p->status == OPEN)
+ {
+ p->lines_to_print = lines_per_body;
+ }
+ else
+ p->lines_to_print = 0;
+ }
}
else
for (j = columns, p = column_vector; j; --j, ++p)
if (p->status == OPEN)
- {
- p->lines_to_print = lines_per_body;
- }
+ {
+ p->lines_to_print = lines_per_body;
+ }
else
- p->lines_to_print = 0;
+ p->lines_to_print = 0;
}
/* Align empty columns and print separators.
@@ -1821,86 +1774,86 @@ print_page (void)
empty_line = true;
for (j = 1, p = column_vector; j <= columns; ++j, ++p)
- {
- input_position = 0;
- if (p->lines_to_print > 0 || p->status == FF_FOUND)
- {
- FF_only = false;
- padding_not_printed = p->start_position;
- if (!(p->print_func) (p))
- read_rest_of_line (p);
- pv |= pad_vertically;
-
- --p->lines_to_print;
- if (p->lines_to_print <= 0)
- {
- if (cols_ready_to_print () <= 0)
- break;
- }
-
- /* File p changed its status to ON_HOLD or CLOSED */
- if (parallel_files && p->status != OPEN)
- {
- if (empty_line)
- align_empty_cols = true;
- else if (p->status == CLOSED ||
- (p->status == ON_HOLD && FF_only))
- align_column (p);
- }
- }
- else if (parallel_files)
- {
- /* File status ON_HOLD or CLOSED */
- if (empty_line)
- align_empty_cols = true;
- else
- align_column (p);
- }
-
- /* We need it also with an empty column */
- if (use_col_separator)
- ++separators_not_printed;
- }
+ {
+ input_position = 0;
+ if (p->lines_to_print > 0 || p->status == FF_FOUND)
+ {
+ FF_only = false;
+ padding_not_printed = p->start_position;
+ if (!(p->print_func) (p))
+ read_rest_of_line (p);
+ pv |= pad_vertically;
+
+ --p->lines_to_print;
+ if (p->lines_to_print <= 0)
+ {
+ if (cols_ready_to_print () == 0)
+ break;
+ }
+
+ /* File p changed its status to ON_HOLD or CLOSED */
+ if (parallel_files && p->status != OPEN)
+ {
+ if (empty_line)
+ align_empty_cols = true;
+ else if (p->status == CLOSED
+ || (p->status == ON_HOLD && FF_only))
+ align_column (p);
+ }
+ }
+ else if (parallel_files)
+ {
+ /* File status ON_HOLD or CLOSED */
+ if (empty_line)
+ align_empty_cols = true;
+ else
+ align_column (p);
+ }
+
+ /* We need it also with an empty column */
+ if (use_col_separator)
+ ++separators_not_printed;
+ }
if (pad_vertically)
- {
- putchar ('\n');
- --lines_left_on_page;
- }
-
- if (cols_ready_to_print () <= 0 && !extremities)
- break;
-
- if (double_space & pv)
- {
- putchar ('\n');
- --lines_left_on_page;
- }
+ {
+ putchar ('\n');
+ --lines_left_on_page;
+ }
+
+ if (cols_ready_to_print () == 0 && !extremities)
+ break;
+
+ if (double_space && pv)
+ {
+ putchar ('\n');
+ --lines_left_on_page;
+ }
}
if (lines_left_on_page == 0)
for (j = 1, p = column_vector; j <= columns; ++j, ++p)
if (p->status == OPEN)
- p->full_page_printed = true;
+ p->full_page_printed = true;
pad_vertically = pv;
- if (pad_vertically & extremities)
+ if (pad_vertically && extremities)
pad_down (lines_left_on_page + lines_per_footer);
- else if (keep_FF & print_a_FF)
+ else if (keep_FF && print_a_FF)
{
putchar ('\f');
print_a_FF = false;
}
- if (last_page_number < page_number)
+ if (last_page_number < ++page_number)
return false; /* Stop printing with LAST_PAGE */
reset_status (); /* Change ON_HOLD to OPEN. */
return true; /* More pages to go. */
}
-
+
/* Allocate space for storing columns.
This is necessary when printing multiple columns from a single file.
@@ -1923,15 +1876,15 @@ init_store_cols (void)
free (line_vector);
/* FIXME: here's where it was allocated. */
- line_vector = xmalloc ((total_lines + 1) * sizeof (int *));
+ line_vector = xmalloc ((total_lines + 1) * sizeof *line_vector);
free (end_vector);
- end_vector = xmalloc (total_lines * sizeof (int *));
+ end_vector = xmalloc (total_lines * sizeof *end_vector);
free (buff);
buff_allocated = (use_col_separator
- ? 2 * chars_if_truncate
- : chars_if_truncate); /* Tune this. */
+ ? 2 * chars_if_truncate
+ : chars_if_truncate); /* Tune this. */
buff = xmalloc (buff_allocated);
}
@@ -1951,8 +1904,8 @@ static void
store_columns (void)
{
int i, j;
- int line = 0;
- int buff_start;
+ unsigned int line = 0;
+ unsigned int buff_start;
int last_col; /* The rightmost column which will be saved in buff */
COLUMN *p;
@@ -1973,22 +1926,22 @@ store_columns (void)
p->current_line = line;
for (j = lines_per_body; j && files_ready_to_read; --j)
- if (p->status == OPEN) /* Redundant. Clean up. */
- {
- input_position = 0;
-
- if (!read_line (p))
- read_rest_of_line (p);
-
- if (p->status == OPEN
- || buff_start != buff_current)
- {
- ++p->lines_stored;
- line_vector[line] = buff_start;
- end_vector[line++] = input_position;
- buff_start = buff_current;
- }
- }
+ if (p->status == OPEN) /* Redundant. Clean up. */
+ {
+ input_position = 0;
+
+ if (!read_line (p))
+ read_rest_of_line (p);
+
+ if (p->status == OPEN
+ || buff_start != buff_current)
+ {
+ ++p->lines_stored;
+ line_vector[line] = buff_start;
+ end_vector[line++] = input_position;
+ buff_start = buff_current;
+ }
+ }
}
/* Keep track of the location of the last char in buff. */
@@ -2009,7 +1962,7 @@ balance (int total_stored)
{
lines = total_stored / columns;
if (i <= total_stored % columns)
- ++lines;
+ ++lines;
p->lines_stored = lines;
p->current_line = first_line;
@@ -2036,32 +1989,26 @@ add_line_number (COLUMN *p)
{
int i;
char *s;
- int left_cut;
+ int num_width;
/* Cutting off the higher-order digits is more informative than
- lower-order cut off*/
- if (line_number < power_10)
- sprintf (number_buff, "%*d", chars_per_number, line_number);
- else
- {
- left_cut = line_number % power_10;
- sprintf (number_buff, "%0*d", chars_per_number, left_cut);
- }
+ lower-order cut off. */
+ num_width = sprintf (number_buff, "%*d", chars_per_number, line_number);
line_number++;
- s = number_buff;
+ s = number_buff + (num_width - chars_per_number);
for (i = chars_per_number; i > 0; i--)
(p->char_func) (*s++);
if (columns > 1)
{
/* Tabification is assumed for multiple columns, also for n-separators,
- but `default n-separator = TAB' hasn't been given priority over
- equal column_width also specified by POSIX. */
+ but 'default n-separator = TAB' hasn't been given priority over
+ equal column_width also specified by POSIX. */
if (number_separator == '\t')
{
i = number_width - chars_per_number;
while (i-- > 0)
- (p->char_func) (' ');
+ (p->char_func) (' ');
}
else
(p->char_func) (number_separator);
@@ -2074,13 +2021,13 @@ add_line_number (COLUMN *p)
(p->char_func) (number_separator);
if (number_separator == '\t')
output_position = POS_AFTER_TAB (chars_per_output_tab,
- output_position);
+ output_position);
}
- if (truncate_lines & !parallel_files)
+ if (truncate_lines && !parallel_files)
input_position += number_width;
}
-
+
/* Print (or store) padding until the current horizontal position
is position. */
@@ -2094,7 +2041,7 @@ pad_across_to (int position)
else
{
while (++h <= position)
- putchar (' ');
+ putchar (' ');
output_position = position;
}
}
@@ -2105,9 +2052,9 @@ pad_across_to (int position)
Otherwise, use newlines. */
static void
-pad_down (int lines)
+pad_down (unsigned int lines)
{
- int i;
+ unsigned int i;
if (use_form_feed)
putchar ('\f');
@@ -2131,19 +2078,19 @@ read_rest_of_line (COLUMN *p)
while ((c = getc (f)) != '\n')
{
if (c == '\f')
- {
- if ((c = getc (f)) != '\n')
- ungetc (c, f);
- if (keep_FF)
- print_a_FF = true;
- hold_file (p);
- break;
- }
+ {
+ if ((c = getc (f)) != '\n')
+ ungetc (c, f);
+ if (keep_FF)
+ print_a_FF = true;
+ hold_file (p);
+ break;
+ }
else if (c == EOF)
- {
- close_file (p);
- break;
- }
+ {
+ close_file (p);
+ break;
+ }
}
}
@@ -2187,28 +2134,28 @@ skip_read (COLUMN *p, int column_number)
while (c != '\n')
{
if (c == '\f')
- {
- /* No FF-coincidence possible,
- no catching up of a FF-coincidence with next page */
- if (last_line)
- {
- if (!parallel_files)
- for (q = column_vector, i = columns; i; ++q, --i)
- q->full_page_printed = false;
- else
- p->full_page_printed = false;
- }
-
- if ((c = getc (f)) != '\n')
- ungetc (c, f);
- hold_file (p);
- break;
- }
+ {
+ /* No FF-coincidence possible,
+ no catching up of a FF-coincidence with next page */
+ if (last_line)
+ {
+ if (!parallel_files)
+ for (q = column_vector, i = columns; i; ++q, --i)
+ q->full_page_printed = false;
+ else
+ p->full_page_printed = false;
+ }
+
+ if ((c = getc (f)) != '\n')
+ ungetc (c, f);
+ hold_file (p);
+ break;
+ }
else if (c == EOF)
- {
- close_file (p);
- break;
- }
+ {
+ close_file (p);
+ break;
+ }
c = getc (f);
}
@@ -2216,7 +2163,7 @@ skip_read (COLUMN *p, int column_number)
if ((!parallel_files || column_number == 1) && !single_ff)
++line_count;
}
-
+
/* If we're tabifying output,
When print_char encounters white space it keeps track
@@ -2231,7 +2178,7 @@ print_white_space (void)
int goal = h_old + spaces_not_printed;
while (goal - h_old > 1
- && (h_new = POS_AFTER_TAB (chars_per_output_tab, h_old)) <= goal)
+ && (h_new = POS_AFTER_TAB (chars_per_output_tab, h_old)) <= goal)
{
putchar (output_tab_char);
h_old = h_new;
@@ -2260,35 +2207,35 @@ print_sep_string (void)
{
/* We'll be starting a line with chars_per_margin, anything else? */
if (spaces_not_printed > 0)
- print_white_space ();
+ print_white_space ();
}
else
{
for (; separators_not_printed > 0; --separators_not_printed)
- {
- while (l-- > 0)
- {
- /* 3 types of sep_strings: spaces only, spaces and chars,
- chars only */
- if (*s == ' ')
- {
- /* We're tabifying output; consecutive spaces in
- sep_string may have to be converted to tabs */
- s++;
- ++spaces_not_printed;
- }
- else
- {
- if (spaces_not_printed > 0)
- print_white_space ();
- putchar (*s++);
- ++output_position;
- }
- }
+ {
+ while (l-- > 0)
+ {
+ /* 3 types of sep_strings: spaces only, spaces and chars,
+ chars only */
+ if (*s == ' ')
+ {
+ /* We're tabifying output; consecutive spaces in
+ sep_string may have to be converted to tabs */
+ s++;
+ ++spaces_not_printed;
+ }
+ else
+ {
+ if (spaces_not_printed > 0)
+ print_white_space ();
+ putchar (*s++);
+ ++output_position;
+ }
+ }
/* sep_string ends with some spaces */
- if (spaces_not_printed > 0)
- print_white_space ();
- }
+ if (spaces_not_printed > 0)
+ print_white_space ();
+ }
}
}
@@ -2317,21 +2264,21 @@ print_char (char c)
if (tabify_output)
{
if (c == ' ')
- {
- ++spaces_not_printed;
- return;
- }
+ {
+ ++spaces_not_printed;
+ return;
+ }
else if (spaces_not_printed > 0)
- print_white_space ();
+ print_white_space ();
/* Nonprintables are assumed to have width 0, except '\b'. */
if (! isprint (to_uchar (c)))
- {
- if (c == '\b')
- --output_position;
- }
+ {
+ if (c == '\b')
+ --output_position;
+ }
else
- ++output_position;
+ ++output_position;
}
putchar (c);
}
@@ -2350,34 +2297,34 @@ skip_to_page (uintmax_t page)
for (n = 1; n < page; ++n)
{
for (i = 1; i < lines_per_body; ++i)
- {
- for (j = 1, p = column_vector; j <= columns; ++j, ++p)
- if (p->status == OPEN)
- skip_read (p, j);
- }
+ {
+ for (j = 1, p = column_vector; j <= columns; ++j, ++p)
+ if (p->status == OPEN)
+ skip_read (p, j);
+ }
last_line = true;
for (j = 1, p = column_vector; j <= columns; ++j, ++p)
- if (p->status == OPEN)
- skip_read (p, j);
+ if (p->status == OPEN)
+ skip_read (p, j);
if (storing_columns) /* change FF_FOUND to ON_HOLD */
- for (j = 1, p = column_vector; j <= columns; ++j, ++p)
- if (p->status != CLOSED)
- p->status = ON_HOLD;
+ for (j = 1, p = column_vector; j <= columns; ++j, ++p)
+ if (p->status != CLOSED)
+ p->status = ON_HOLD;
reset_status ();
last_line = false;
if (files_ready_to_read < 1)
{
- /* It's very helpful, normally the total number of pages is
- not known in advance. */
- error (0, 0,
- _("starting page number %"PRIuMAX
- " exceeds page count %"PRIuMAX),
- page, n);
+ /* It's very helpful, normally the total number of pages is
+ not known in advance. */
+ error (0, 0,
+ _("starting page number %"PRIuMAX
+ " exceeds page count %"PRIuMAX),
+ page, n);
break;
- }
+ }
}
return files_ready_to_read > 0;
}
@@ -2395,27 +2342,26 @@ print_header (void)
int lhs_spaces;
int rhs_spaces;
- if (!use_form_feed)
- printf ("\n\n");
-
output_position = 0;
pad_across_to (chars_per_margin);
print_white_space ();
if (page_number == 0)
- error (EXIT_FAILURE, 0, _("Page number overflow"));
+ error (EXIT_FAILURE, 0, _("page number overflow"));
/* The translator must ensure that formatting the translation of
"Page %"PRIuMAX does not generate more than (sizeof page_text - 1)
bytes. */
- sprintf (page_text, _("Page %"PRIuMAX), page_number++);
+ sprintf (page_text, _("Page %"PRIuMAX), page_number);
available_width = header_width_available - mbswidth (page_text, 0);
available_width = MAX (0, available_width);
lhs_spaces = available_width >> 1;
rhs_spaces = available_width - lhs_spaces;
- printf ("%s%*s%s%*s%s\n\n\n",
- date_text, lhs_spaces, " ", file_text, rhs_spaces, " ", page_text);
+ printf ("\n\n%*s%s%*s%s%*s%s\n\n\n",
+ chars_per_margin, "",
+ date_text, lhs_spaces, " ",
+ file_text, rhs_spaces, " ", page_text);
print_a_header = false;
output_position = 0;
@@ -2446,7 +2392,7 @@ static bool
read_line (COLUMN *p)
{
int c;
- int chars IF_LINT (= 0);
+ int chars IF_LINT ( = 0);
int last_input_position;
int j, k;
COLUMN *q;
@@ -2465,15 +2411,15 @@ read_line (COLUMN *p)
{
case '\f':
if ((c = getc (p->fp)) != '\n')
- ungetc (c, p->fp);
+ ungetc (c, p->fp);
FF_only = true;
- if (print_a_header & !storing_columns)
- {
- pad_vertically = true;
- print_header ();
- }
+ if (print_a_header && !storing_columns)
+ {
+ pad_vertically = true;
+ print_header ();
+ }
else if (keep_FF)
- print_a_FF = true;
+ print_a_FF = true;
hold_file (p);
return true;
case EOF:
@@ -2495,35 +2441,35 @@ read_line (COLUMN *p)
{
pad_vertically = true;
- if (print_a_header & !storing_columns)
- print_header ();
-
- if (parallel_files & align_empty_cols)
- {
- /* We have to align empty columns at the beginning of a line. */
- k = separators_not_printed;
- separators_not_printed = 0;
- for (j = 1, q = column_vector; j <= k; ++j, ++q)
- {
- align_column (q);
- separators_not_printed += 1;
- }
- padding_not_printed = p->start_position;
- if (truncate_lines)
- spaces_not_printed = chars_per_column;
- else
- spaces_not_printed = 0;
- align_empty_cols = false;
- }
+ if (print_a_header && !storing_columns)
+ print_header ();
+
+ if (parallel_files && align_empty_cols)
+ {
+ /* We have to align empty columns at the beginning of a line. */
+ k = separators_not_printed;
+ separators_not_printed = 0;
+ for (j = 1, q = column_vector; j <= k; ++j, ++q)
+ {
+ align_column (q);
+ separators_not_printed += 1;
+ }
+ padding_not_printed = p->start_position;
+ if (truncate_lines)
+ spaces_not_printed = chars_per_column;
+ else
+ spaces_not_printed = 0;
+ align_empty_cols = false;
+ }
if (padding_not_printed - col_sep_length > 0)
- {
- pad_across_to (padding_not_printed - col_sep_length);
- padding_not_printed = ANYWHERE;
- }
+ {
+ pad_across_to (padding_not_printed - col_sep_length);
+ padding_not_printed = ANYWHERE;
+ }
if (use_col_separator)
- print_sep_string ();
+ print_sep_string ();
}
if (p->numbered)
@@ -2535,33 +2481,33 @@ read_line (COLUMN *p)
print_clump (p, chars, clump_buff);
- for (;;)
+ while (true)
{
c = getc (p->fp);
switch (c)
- {
- case '\n':
- return true;
- case '\f':
- if ((c = getc (p->fp)) != '\n')
- ungetc (c, p->fp);
- if (keep_FF)
- print_a_FF = true;
- hold_file (p);
- return true;
- case EOF:
- close_file (p);
- return true;
- }
+ {
+ case '\n':
+ return true;
+ case '\f':
+ if ((c = getc (p->fp)) != '\n')
+ ungetc (c, p->fp);
+ if (keep_FF)
+ print_a_FF = true;
+ hold_file (p);
+ return true;
+ case EOF:
+ close_file (p);
+ return true;
+ }
last_input_position = input_position;
chars = char_to_clump (c);
if (truncate_lines && input_position > chars_per_column)
- {
- input_position = last_input_position;
- return false;
- }
+ {
+ input_position = last_input_position;
+ return false;
+ }
print_clump (p, chars, clump_buff);
}
@@ -2610,13 +2556,13 @@ print_stored (COLUMN *p)
if (p->status == FF_FOUND)
{
for (i = 1, q = column_vector; i <= columns; ++i, ++q)
- q->status = ON_HOLD;
+ q->status = ON_HOLD;
if (column_vector->lines_to_print <= 0)
- {
- if (!extremities)
- pad_vertically = false;
- return true; /* print a header only */
- }
+ {
+ if (!extremities)
+ pad_vertically = false;
+ return true; /* print a header only */
+ }
}
if (padding_not_printed - col_sep_length > 0)
@@ -2635,7 +2581,7 @@ print_stored (COLUMN *p)
{
output_position = p->start_position + end_vector[line];
if (p->start_position - col_sep_length == chars_per_margin)
- output_position -= col_sep_length;
+ output_position -= col_sep_length;
}
return true;
@@ -2672,60 +2618,60 @@ char_to_clump (char c)
width = TAB_WIDTH (chars_per_c, input_position);
if (untabify_input)
- {
- for (i = width; i; --i)
- *s++ = ' ';
- chars = width;
- }
+ {
+ for (i = width; i; --i)
+ *s++ = ' ';
+ chars = width;
+ }
else
- {
- *s = c;
- chars = 1;
- }
+ {
+ *s = c;
+ chars = 1;
+ }
}
else if (! isprint (uc))
{
if (use_esc_sequence)
- {
- width = 4;
- chars = 4;
- *s++ = '\\';
- sprintf (esc_buff, "%03o", uc);
- for (i = 0; i <= 2; ++i)
- *s++ = esc_buff[i];
- }
+ {
+ width = 4;
+ chars = 4;
+ *s++ = '\\';
+ sprintf (esc_buff, "%03o", uc);
+ for (i = 0; i <= 2; ++i)
+ *s++ = esc_buff[i];
+ }
else if (use_cntrl_prefix)
- {
- if (uc < 0200)
- {
- width = 2;
- chars = 2;
- *s++ = '^';
- *s++ = c ^ 0100;
- }
- else
- {
- width = 4;
- chars = 4;
- *s++ = '\\';
- sprintf (esc_buff, "%03o", uc);
- for (i = 0; i <= 2; ++i)
- *s++ = esc_buff[i];
- }
- }
+ {
+ if (uc < 0200)
+ {
+ width = 2;
+ chars = 2;
+ *s++ = '^';
+ *s = c ^ 0100;
+ }
+ else
+ {
+ width = 4;
+ chars = 4;
+ *s++ = '\\';
+ sprintf (esc_buff, "%03o", uc);
+ for (i = 0; i <= 2; ++i)
+ *s++ = esc_buff[i];
+ }
+ }
else if (c == '\b')
- {
- width = -1;
- chars = 1;
- *s = c;
- }
+ {
+ width = -1;
+ chars = 1;
+ *s = c;
+ }
else
- {
- width = 0;
- chars = 1;
- *s = c;
- }
+ {
+ width = 0;
+ chars = 1;
+ *s = c;
+ }
}
else
{
@@ -2734,14 +2680,24 @@ char_to_clump (char c)
*s = c;
}
- input_position += width;
+ /* Too many backspaces must put us in position 0 -- never negative. */
+ if (width < 0 && input_position == 0)
+ {
+ chars = 0;
+ input_position = 0;
+ }
+ else if (width < 0 && input_position <= -width)
+ input_position = 0;
+ else
+ input_position += width;
+
return chars;
}
/* We've just printed some files and need to clean up things before
looking for more options and printing the next batch of files.
- Free everything we've xmalloc'ed, except `header'. */
+ Free everything we've xmalloc'ed, except 'header'. */
static void
cleanup (void)
@@ -2753,36 +2709,35 @@ cleanup (void)
free (end_vector);
free (buff);
}
-
+
/* Complain, print a usage message, and die. */
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
- program_name);
+ program_name);
fputs (_("\
Paginate or columnate FILE(s) for printing.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
+
+ emit_stdin_note ();
+ emit_mandatory_arg_note ();
+
fputs (_("\
+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\
+ columns on each page\n\
"), stdout);
fputs (_("\
-a, --across print columns across rather than down, used together\n\
@@ -2803,7 +2758,7 @@ Mandatory arguments to long options are mandatory for short options too.\n\
and trailer without -F)\n\
"), stdout);
fputs (_("\
- -h HEADER, --header=HEADER\n\
+ -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\
-i[CHAR[WIDTH]], --output-tabs[=CHAR[WIDTH]]\n\
@@ -2812,9 +2767,12 @@ Mandatory arguments to long options are mandatory for short options too.\n\
alignment, --sep-string[=STRING] sets separators\n\
"), stdout);
fputs (_("\
- -l PAGE_LENGTH, --length=PAGE_LENGTH\n\
+ -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\
+ (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);
@@ -2822,32 +2780,34 @@ Mandatory arguments to long options are mandatory for short options too.\n\
-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\
- -N NUMBER, --first-line-number=NUMBER\n\
+ -N, --first-line-number=NUMBER\n\
start counting with NUMBER at 1st line of first\n\
page printed (see +FIRST_PAGE)\n\
"), stdout);
fputs (_("\
- -o MARGIN, --indent=MARGIN\n\
+ -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\
-r, --no-file-warnings\n\
omit warning when a file cannot be opened\n\
"), stdout);
fputs (_("\
- -s[CHAR],--separator[=CHAR]\n\
+ -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\
+ 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 (_("\
- -SSTRING, --sep-string[=STRING]\n\
-"), stdout);
- fputs (_("\
+ -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\
- -t, --omit-header omit page headers and trailers\n\
+"), stdout);
+ fputs (_("\
+ -t, --omit-header omit page headers and trailers;\n\
+ implied if PAGE_LENGTH <= 10\n\
"), stdout);
fputs (_("\
-T, --omit-pagination\n\
@@ -2855,24 +2815,19 @@ Mandatory arguments to long options are mandatory for short options too.\n\
by form feeds set in input files\n\
-v, --show-nonprinting\n\
use octal backslash notation\n\
- -w PAGE_WIDTH, --width=PAGE_WIDTH\n\
+ -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 (_("\
- -W PAGE_WIDTH, --page-width=PAGE_WIDTH\n\
+ -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);
- fputs (_("\
-\n\
--T implied by -l nn when nn <= 10 or <= 3 with -F. With no FILE, or when\n\
-FILE is -, read standard input.\n\
-"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
diff --git a/src/primes.h b/src/primes.h
new file mode 100644
index 0000000..f608966
--- /dev/null
+++ b/src/primes.h
@@ -0,0 +1,4014 @@
+/* Generated file -- DO NOT EDIT */
+
+#define WIDE_UINT_BITS 128
+P (1, 26,
+ (((((uintmax_t) 0xaaaaU << 28 | 0xaaaaaaaU)
+ << 28 | 0xaaaaaaaU)
+ << 28 | 0xaaaaaaaU)
+ << 28 | 0xaaaaaabU),
+ UINTMAX_MAX / 3)
+P (2, 26,
+ (((((uintmax_t) 0xccccU << 28 | 0xcccccccU)
+ << 28 | 0xcccccccU)
+ << 28 | 0xcccccccU)
+ << 28 | 0xccccccdU),
+ UINTMAX_MAX / 5)
+P (2, 30,
+ (((((uintmax_t) 0xb6dbU << 28 | 0x6db6db6U)
+ << 28 | 0xdb6db6dU)
+ << 28 | 0xb6db6dbU)
+ << 28 | 0x6db6db7U),
+ UINTMAX_MAX / 7)
+P (4, 30,
+ (((((uintmax_t) 0xa2e8U << 28 | 0xba2e8baU)
+ << 28 | 0x2e8ba2eU)
+ << 28 | 0x8ba2e8bU)
+ << 28 | 0xa2e8ba3U),
+ UINTMAX_MAX / 11)
+P (2, 30,
+ (((((uintmax_t) 0xc4ecU << 28 | 0x4ec4ec4U)
+ << 28 | 0xec4ec4eU)
+ << 28 | 0xc4ec4ecU)
+ << 28 | 0x4ec4ec5U),
+ UINTMAX_MAX / 13)
+P (4, 30,
+ (((((uintmax_t) 0xf0f0U << 28 | 0xf0f0f0fU)
+ << 28 | 0x0f0f0f0U)
+ << 28 | 0xf0f0f0fU)
+ << 28 | 0x0f0f0f1U),
+ UINTMAX_MAX / 17)
+P (2, 34,
+ (((((uintmax_t) 0xbca1U << 28 | 0xaf286bcU)
+ << 28 | 0xa1af286U)
+ << 28 | 0xbca1af2U)
+ << 28 | 0x86bca1bU),
+ UINTMAX_MAX / 19)
+P (4, 36,
+ (((((uintmax_t) 0x4de9U << 28 | 0xbd37a6fU)
+ << 28 | 0x4de9bd3U)
+ << 28 | 0x7a6f4deU)
+ << 28 | 0x9bd37a7U),
+ UINTMAX_MAX / 23)
+P (6, 32,
+ (((((uintmax_t) 0xc234U << 28 | 0xf72c234U)
+ << 28 | 0xf72c234U)
+ << 28 | 0xf72c234U)
+ << 28 | 0xf72c235U),
+ UINTMAX_MAX / 29)
+P (2, 36,
+ (((((uintmax_t) 0xdef7U << 28 | 0xbdef7bdU)
+ << 28 | 0xef7bdefU)
+ << 28 | 0x7bdef7bU)
+ << 28 | 0xdef7bdfU),
+ UINTMAX_MAX / 31)
+P (6, 34,
+ (((((uintmax_t) 0xc1baU << 28 | 0xcf914c1U)
+ << 28 | 0xbacf914U)
+ << 28 | 0xc1bacf9U)
+ << 28 | 0x14c1badU),
+ UINTMAX_MAX / 37)
+P (4, 32,
+ (((((uintmax_t) 0x18f9U << 28 | 0xc18f9c1U)
+ << 28 | 0x8f9c18fU)
+ << 28 | 0x9c18f9cU)
+ << 28 | 0x18f9c19U),
+ UINTMAX_MAX / 41)
+P (2, 36,
+ (((((uintmax_t) 0xbe82U << 28 | 0xfa0be82U)
+ << 28 | 0xfa0be82U)
+ << 28 | 0xfa0be82U)
+ << 28 | 0xfa0be83U),
+ UINTMAX_MAX / 43)
+P (4, 36,
+ (((((uintmax_t) 0x3677U << 28 | 0xd46cefaU)
+ << 28 | 0x8d9df51U)
+ << 28 | 0xb3bea36U)
+ << 28 | 0x77d46cfU),
+ UINTMAX_MAX / 47)
+P (6, 36,
+ (((((uintmax_t) 0x1352U << 28 | 0x1cfb2b7U)
+ << 28 | 0x8c13521U)
+ << 28 | 0xcfb2b78U)
+ << 28 | 0xc13521dU),
+ UINTMAX_MAX / 53)
+P (6, 38,
+ (((((uintmax_t) 0x8f2fU << 28 | 0xba93868U)
+ << 28 | 0x22b63cbU)
+ << 28 | 0xeea4e1aU)
+ << 28 | 0x08ad8f3U),
+ UINTMAX_MAX / 59)
+P (2, 40,
+ (((((uintmax_t) 0x14fbU << 28 | 0xcda3ac1U)
+ << 28 | 0x0c9714fU)
+ << 28 | 0xbcda3acU)
+ << 28 | 0x10c9715U),
+ UINTMAX_MAX / 61)
+P (6, 36,
+ (((((uintmax_t) 0xc2ddU << 28 | 0x9ca81e9U)
+ << 28 | 0x131abf0U)
+ << 28 | 0xb7672a0U)
+ << 28 | 0x7a44c6bU),
+ UINTMAX_MAX / 67)
+P (4, 36,
+ (((((uintmax_t) 0x4f52U << 28 | 0xedf8c9eU)
+ << 28 | 0xa5dbf19U)
+ << 28 | 0x3d4bb7eU)
+ << 28 | 0x327a977U),
+ UINTMAX_MAX / 71)
+P (2, 36,
+ (((((uintmax_t) 0x3f1fU << 28 | 0x8fc7e3fU)
+ << 28 | 0x1f8fc7eU)
+ << 28 | 0x3f1f8fcU)
+ << 28 | 0x7e3f1f9U),
+ UINTMAX_MAX / 73)
+P (6, 34,
+ (((((uintmax_t) 0xd5dfU << 28 | 0x984dc5aU)
+ << 28 | 0xbbf309bU)
+ << 28 | 0x8b577e6U)
+ << 28 | 0x13716afU),
+ UINTMAX_MAX / 79)
+P (4, 44,
+ (((((uintmax_t) 0x2818U << 28 | 0xacb90f6U)
+ << 28 | 0xbf3a9a3U)
+ << 28 | 0x784a062U)
+ << 28 | 0xb2e43dbU),
+ UINTMAX_MAX / 83)
+P (6, 42,
+ (((((uintmax_t) 0xd1faU << 28 | 0x3f47e8fU)
+ << 28 | 0xd1fa3f4U)
+ << 28 | 0x7e8fd1fU)
+ << 28 | 0xa3f47e9U),
+ UINTMAX_MAX / 89)
+P (8, 40,
+ (((((uintmax_t) 0x5f02U << 28 | 0xa3a0fd5U)
+ << 28 | 0xc5f02a3U)
+ << 28 | 0xa0fd5c5U)
+ << 28 | 0xf02a3a1U),
+ UINTMAX_MAX / 97)
+P (4, 38,
+ (((((uintmax_t) 0xc32bU << 28 | 0x16cfd77U)
+ << 28 | 0x20f353aU)
+ << 28 | 0x4c0a237U)
+ << 28 | 0xc32b16dU),
+ UINTMAX_MAX / 101)
+P (2, 46,
+ (((((uintmax_t) 0xd0c6U << 28 | 0xd5bf60eU)
+ << 28 | 0xe9a18daU)
+ << 28 | 0xb7ec1ddU)
+ << 28 | 0x3431b57U),
+ UINTMAX_MAX / 103)
+P (4, 44,
+ (((((uintmax_t) 0xa2b1U << 28 | 0x0bf66e0U)
+ << 28 | 0xe5aea77U)
+ << 28 | 0xa04c8f8U)
+ << 28 | 0xd28ac43U),
+ UINTMAX_MAX / 107)
+P (2, 48,
+ (((((uintmax_t) 0xc096U << 28 | 0x4fda6c0U)
+ << 28 | 0x964fda6U)
+ << 28 | 0xc0964fdU)
+ << 28 | 0xa6c0965U),
+ UINTMAX_MAX / 109)
+P (4, 50,
+ (((((uintmax_t) 0xc090U << 28 | 0xfdbc090U)
+ << 28 | 0xfdbc090U)
+ << 28 | 0xfdbc090U)
+ << 28 | 0xfdbc091U),
+ UINTMAX_MAX / 113)
+P (14, 40,
+ (((((uintmax_t) 0xbf7eU << 28 | 0xfdfbf7eU)
+ << 28 | 0xfdfbf7eU)
+ << 28 | 0xfdfbf7eU)
+ << 28 | 0xfdfbf7fU),
+ UINTMAX_MAX / 127)
+P (4, 42,
+ (((((uintmax_t) 0xf82eU << 28 | 0xe6986d6U)
+ << 28 | 0xf63aa03U)
+ << 28 | 0xe88cb3cU)
+ << 28 | 0x9484e2bU),
+ UINTMAX_MAX / 131)
+P (6, 42,
+ (((((uintmax_t) 0x21a2U << 28 | 0x91c0779U)
+ << 28 | 0x75b8fe2U)
+ << 28 | 0x1a291c0U)
+ << 28 | 0x77975b9U),
+ UINTMAX_MAX / 137)
+P (2, 42,
+ (((((uintmax_t) 0xa212U << 28 | 0x6ad1f4fU)
+ << 28 | 0x31ba03aU)
+ << 28 | 0xef6ca97U)
+ << 28 | 0x0586723U),
+ UINTMAX_MAX / 139)
+P (10, 42,
+ (((((uintmax_t) 0x93c2U << 28 | 0x25cc74dU)
+ << 28 | 0x50c06dfU)
+ << 28 | 0x5b0f768U)
+ << 28 | 0xce2cabdU),
+ UINTMAX_MAX / 149)
+P (2, 42,
+ (((((uintmax_t) 0x26feU << 28 | 0x4dfc9bfU)
+ << 28 | 0x937f26fU)
+ << 28 | 0xe4dfc9bU)
+ << 28 | 0xf937f27U),
+ UINTMAX_MAX / 151)
+P (6, 40,
+ (((((uintmax_t) 0x0685U << 28 | 0xb4fe5e9U)
+ << 28 | 0x2c0685bU)
+ << 28 | 0x4fe5e92U)
+ << 28 | 0xc0685b5U),
+ UINTMAX_MAX / 157)
+P (6, 36,
+ (((((uintmax_t) 0x8bc7U << 28 | 0x75ca99eU)
+ << 28 | 0xa03241fU)
+ << 28 | 0x693a1c4U)
+ << 28 | 0x51ab30bU),
+ UINTMAX_MAX / 163)
+P (4, 44,
+ (((((uintmax_t) 0x513eU << 28 | 0xd9ad38bU)
+ << 28 | 0x7f3bc8dU)
+ << 28 | 0x07aa27dU)
+ << 28 | 0xb35a717U),
+ UINTMAX_MAX / 167)
+P (6, 50,
+ (((((uintmax_t) 0x133cU << 28 | 0xaba736cU)
+ << 28 | 0x05eb488U)
+ << 28 | 0x2383b30U)
+ << 28 | 0xd516325U),
+ UINTMAX_MAX / 173)
+P (6, 48,
+ (((((uintmax_t) 0x0e4dU << 28 | 0x3aa30a0U)
+ << 28 | 0x2dc3eedU)
+ << 28 | 0x6866f8dU)
+ << 28 | 0x962ae7bU),
+ UINTMAX_MAX / 179)
+P (2, 48,
+ (((((uintmax_t) 0x6fbcU << 28 | 0x1c498c0U)
+ << 28 | 0x5a84f34U)
+ << 28 | 0x54dca41U)
+ << 28 | 0x0f8ed9dU),
+ UINTMAX_MAX / 181)
+P (10, 42,
+ (((((uintmax_t) 0x7749U << 28 | 0xb79f7f5U)
+ << 28 | 0x470961dU)
+ << 28 | 0x7ca632eU)
+ << 28 | 0xe936f3fU),
+ UINTMAX_MAX / 191)
+P (2, 46,
+ (((((uintmax_t) 0x9094U << 28 | 0x8f40feaU)
+ << 28 | 0xc6f6b70U)
+ << 28 | 0xbf01539U)
+ << 28 | 0x0948f41U),
+ UINTMAX_MAX / 193)
+P (4, 44,
+ (((((uintmax_t) 0x0bb2U << 28 | 0x07cc053U)
+ << 28 | 0x2ae21c9U)
+ << 28 | 0x6bdb9d3U)
+ << 28 | 0xd137e0dU),
+ UINTMAX_MAX / 197)
+P (2, 52,
+ (((((uintmax_t) 0x7a36U << 28 | 0x07b7f5bU)
+ << 28 | 0x5630e26U)
+ << 28 | 0x97cc8aeU)
+ << 28 | 0xf46c0f7U),
+ UINTMAX_MAX / 199)
+P (12, 46,
+ (((((uintmax_t) 0x2f51U << 28 | 0x4a026d3U)
+ << 28 | 0x1be7bc0U)
+ << 28 | 0xe8f2a76U)
+ << 28 | 0xe68575bU),
+ UINTMAX_MAX / 211)
+P (12, 40,
+ (((((uintmax_t) 0xdd8fU << 28 | 0x7f6d0eeU)
+ << 28 | 0xc7bfb68U)
+ << 28 | 0x7763dfdU)
+ << 28 | 0xb43bb1fU),
+ UINTMAX_MAX / 223)
+P (4, 42,
+ (((((uintmax_t) 0x766aU << 28 | 0x024168eU)
+ << 28 | 0x18cf81bU)
+ << 28 | 0x10ea929U)
+ << 28 | 0xba144cbU),
+ UINTMAX_MAX / 227)
+P (2, 42,
+ (((((uintmax_t) 0x0c4cU << 28 | 0x0478bbcU)
+ << 28 | 0xecfee1dU)
+ << 28 | 0x10c4c04U)
+ << 28 | 0x78bbcedU),
+ UINTMAX_MAX / 229)
+P (4, 44,
+ (((((uintmax_t) 0x758fU << 28 | 0xee6bac7U)
+ << 28 | 0xf735d63U)
+ << 28 | 0xfb9aeb1U)
+ << 28 | 0xfdcd759U),
+ UINTMAX_MAX / 233)
+P (6, 42,
+ (((((uintmax_t) 0x077fU << 28 | 0x76e538cU)
+ << 28 | 0x5167e64U)
+ << 28 | 0xafaa4f4U)
+ << 28 | 0x37b2e0fU),
+ UINTMAX_MAX / 239)
+P (2, 42,
+ (((((uintmax_t) 0x10feU << 28 | 0xf010fefU)
+ << 28 | 0x010fef0U)
+ << 28 | 0x10fef01U)
+ << 28 | 0x0fef011U),
+ UINTMAX_MAX / 241)
+P (10, 42,
+ (((((uintmax_t) 0xa020U << 28 | 0xa32fefaU)
+ << 28 | 0xe680828U)
+ << 28 | 0xcbfbeb9U)
+ << 28 | 0xa020a33U),
+ UINTMAX_MAX / 251)
+P (6, 50,
+ (((((uintmax_t) 0xff00U << 28 | 0xff00ff0U)
+ << 28 | 0x0ff00ffU)
+ << 28 | 0x00ff00fU)
+ << 28 | 0xf00ff01U),
+ UINTMAX_MAX / 257)
+P (6, 48,
+ (((((uintmax_t) 0xf836U << 28 | 0x826ef73U)
+ << 28 | 0xd52bcd6U)
+ << 28 | 0x24fd147U)
+ << 28 | 0x0e99cb7U),
+ UINTMAX_MAX / 263)
+P (6, 44,
+ (((((uintmax_t) 0x3ce8U << 28 | 0x354b2eaU)
+ << 28 | 0x1c8cd8fU)
+ << 28 | 0xb3ddbd6U)
+ << 28 | 0x205b5c5U),
+ UINTMAX_MAX / 269)
+P (2, 46,
+ (((((uintmax_t) 0x8715U << 28 | 0xba188f9U)
+ << 28 | 0x63302d5U)
+ << 28 | 0x7da36caU)
+ << 28 | 0x27acdefU),
+ UINTMAX_MAX / 271)
+P (6, 54,
+ (((((uintmax_t) 0xb25eU << 28 | 0x4463cffU)
+ << 28 | 0x13686eeU)
+ << 28 | 0x70c03b2U)
+ << 28 | 0x5e4463dU),
+ UINTMAX_MAX / 277)
+P (4, 56,
+ (((((uintmax_t) 0x6c69U << 28 | 0xae01d27U)
+ << 28 | 0x2ca3fc5U)
+ << 28 | 0xb1a6b80U)
+ << 28 | 0x749cb29U),
+ UINTMAX_MAX / 281)
+P (2, 64,
+ (((((uintmax_t) 0xf26eU << 28 | 0x5c44bfcU)
+ << 28 | 0x61b2347U)
+ << 28 | 0x768073cU)
+ << 28 | 0x9b97113U),
+ UINTMAX_MAX / 283)
+P (10, 56,
+ (((((uintmax_t) 0xb07dU << 28 | 0xd0d1b15U)
+ << 28 | 0xd7cf125U)
+ << 28 | 0x91e9488U)
+ << 28 | 0x4ce32adU),
+ UINTMAX_MAX / 293)
+P (14, 46,
+ (((((uintmax_t) 0xd2f8U << 28 | 0x7ebfcaaU)
+ << 28 | 0x1c5a0f0U)
+ << 28 | 0x2806abcU)
+ << 28 | 0x74be1fbU),
+ UINTMAX_MAX / 307)
+P (4, 48,
+ (((((uintmax_t) 0xbe25U << 28 | 0xdd6d7aaU)
+ << 28 | 0x646ca7eU)
+ << 28 | 0xc3e8f3aU)
+ << 28 | 0x7198487U),
+ UINTMAX_MAX / 311)
+P (2, 54,
+ (((((uintmax_t) 0xbc1dU << 28 | 0x71afd8bU)
+ << 28 | 0xdc03458U)
+ << 28 | 0x550f8a3U)
+ << 28 | 0x9409d09U),
+ UINTMAX_MAX / 313)
+P (4, 56,
+ (((((uintmax_t) 0x2ed6U << 28 | 0xd05a72aU)
+ << 28 | 0xcd1f7ecU)
+ << 28 | 0x9e48ae6U)
+ << 28 | 0xf71de15U),
+ UINTMAX_MAX / 317)
+P (14, 48,
+ (((((uintmax_t) 0x62ffU << 28 | 0x3a018bfU)
+ << 28 | 0xce8062fU)
+ << 28 | 0xf3a018bU)
+ << 28 | 0xfce8063U),
+ UINTMAX_MAX / 331)
+P (6, 46,
+ (((((uintmax_t) 0x3fcfU << 28 | 0x61fe7b0U)
+ << 28 | 0xff3d87fU)
+ << 28 | 0x9ec3fcfU)
+ << 28 | 0x61fe7b1U),
+ UINTMAX_MAX / 337)
+P (10, 42,
+ (((((uintmax_t) 0x398bU << 28 | 0x6f668c2U)
+ << 28 | 0xc43df89U)
+ << 28 | 0xf5abe57U)
+ << 28 | 0x0e046d3U),
+ UINTMAX_MAX / 347)
+P (2, 48,
+ (((((uintmax_t) 0x8c1aU << 28 | 0x682913cU)
+ << 28 | 0xe1ecedaU)
+ << 28 | 0x971b23fU)
+ << 28 | 0x1545af5U),
+ UINTMAX_MAX / 349)
+P (4, 48,
+ (((((uintmax_t) 0x0b9aU << 28 | 0x7862a0fU)
+ << 28 | 0xf465879U)
+ << 28 | 0xd5f00b9U)
+ << 28 | 0xa7862a1U),
+ UINTMAX_MAX / 353)
+P (6, 50,
+ (((((uintmax_t) 0xe7c1U << 28 | 0x3f77161U)
+ << 28 | 0xb18f54dU)
+ << 28 | 0xba1df32U)
+ << 28 | 0xa128a57U),
+ UINTMAX_MAX / 359)
+P (8, 52,
+ (((((uintmax_t) 0x7318U << 28 | 0x6a06f9bU)
+ << 28 | 0x8d9a287U)
+ << 28 | 0x530217bU)
+ << 28 | 0x7747d8fU),
+ UINTMAX_MAX / 367)
+P (6, 48,
+ (((((uintmax_t) 0x7c39U << 28 | 0xa6c708eU)
+ << 28 | 0xc18b530U)
+ << 28 | 0xbaae53bU)
+ << 28 | 0xb5e06ddU),
+ UINTMAX_MAX / 373)
+P (6, 52,
+ (((((uintmax_t) 0x3763U << 28 | 0x4af9ebbU)
+ << 28 | 0xc742deeU)
+ << 28 | 0x70206c1U)
+ << 28 | 0x2e9b5b3U),
+ UINTMAX_MAX / 379)
+P (4, 50,
+ (((((uintmax_t) 0x5035U << 28 | 0x78fb523U)
+ << 28 | 0x6cf34cdU)
+ << 28 | 0xde9462eU)
+ << 28 | 0xc9dbe7fU),
+ UINTMAX_MAX / 383)
+P (6, 50,
+ (((((uintmax_t) 0xbcdfU << 28 | 0xc0d2975U)
+ << 28 | 0xccab1afU)
+ << 28 | 0xb64b05eU)
+ << 28 | 0xc41cf4dU),
+ UINTMAX_MAX / 389)
+P (8, 46,
+ (((((uintmax_t) 0xf5aeU << 28 | 0xc02944fU)
+ << 28 | 0xf5aec02U)
+ << 28 | 0x944ff5aU)
+ << 28 | 0xec02945U),
+ UINTMAX_MAX / 397)
+P (4, 48,
+ (((((uintmax_t) 0xc7d2U << 28 | 0x08f00a3U)
+ << 28 | 0x6e71a2cU)
+ << 28 | 0xb033128U)
+ << 28 | 0x382df71U),
+ UINTMAX_MAX / 401)
+P (8, 48,
+ (((((uintmax_t) 0xd38fU << 28 | 0x55c0280U)
+ << 28 | 0xf05a21cU)
+ << 28 | 0xcacc0c8U)
+ << 28 | 0x4b1c2a9U),
+ UINTMAX_MAX / 409)
+P (10, 42,
+ (((((uintmax_t) 0xca3bU << 28 | 0xe03aa76U)
+ << 28 | 0x87a3219U)
+ << 28 | 0xa93db57U)
+ << 28 | 0x5eb3a0bU),
+ UINTMAX_MAX / 419)
+P (2, 42,
+ (((((uintmax_t) 0x6a69U << 28 | 0xce2344bU)
+ << 28 | 0x66c3cceU)
+ << 28 | 0xbeef94fU)
+ << 28 | 0xa86fe2dU),
+ UINTMAX_MAX / 421)
+P (10, 36,
+ (((((uintmax_t) 0xfecfU << 28 | 0xe37d53bU)
+ << 28 | 0xfd9fc6fU)
+ << 28 | 0xaa77fb3U)
+ << 28 | 0xf8df54fU),
+ UINTMAX_MAX / 431)
+P (2, 46,
+ (((((uintmax_t) 0xa58aU << 28 | 0xf00975aU)
+ << 28 | 0x750ff68U)
+ << 28 | 0xa58af00U)
+ << 28 | 0x975a751U),
+ UINTMAX_MAX / 433)
+P (6, 48,
+ (((((uintmax_t) 0xdc6dU << 28 | 0xa187df5U)
+ << 28 | 0x80dfed5U)
+ << 28 | 0x6e36d0cU)
+ << 28 | 0x3efac07U),
+ UINTMAX_MAX / 439)
+P (4, 48,
+ (((((uintmax_t) 0x8fe4U << 28 | 0x4308ab0U)
+ << 28 | 0xd4a8bd8U)
+ << 28 | 0xb44c47aU)
+ << 28 | 0x8299b73U),
+ UINTMAX_MAX / 443)
+P (6, 50,
+ (((((uintmax_t) 0xf1bfU << 28 | 0x0091f5bU)
+ << 28 | 0xcb8bb02U)
+ << 28 | 0xd9ccaf9U)
+ << 28 | 0xba70e41U),
+ UINTMAX_MAX / 449)
+P (8, 46,
+ (((((uintmax_t) 0x5e1cU << 28 | 0x023d9e8U)
+ << 28 | 0x78ff709U)
+ << 28 | 0x85e1c02U)
+ << 28 | 0x3d9e879U),
+ UINTMAX_MAX / 457)
+P (4, 48,
+ (((((uintmax_t) 0x7880U << 28 | 0xd53da3dU)
+ << 28 | 0x15a842aU)
+ << 28 | 0x343316cU)
+ << 28 | 0x494d305U),
+ UINTMAX_MAX / 461)
+P (2, 58,
+ (((((uintmax_t) 0x1ddbU << 28 | 0x81ef699U)
+ << 28 | 0xb5e8c70U)
+ << 28 | 0xcb7916aU)
+ << 28 | 0xb67652fU),
+ UINTMAX_MAX / 463)
+P (4, 56,
+ (((((uintmax_t) 0xf364U << 28 | 0x5121706U)
+ << 28 | 0x07acad3U)
+ << 28 | 0x98f132fU)
+ << 28 | 0xb10fe5bU),
+ UINTMAX_MAX / 467)
+P (12, 62,
+ (((((uintmax_t) 0xadb1U << 28 | 0xf8848afU)
+ << 28 | 0x4c6d06fU)
+ << 28 | 0x2a38a6bU)
+ << 28 | 0xf54fa1fU),
+ UINTMAX_MAX / 479)
+P (8, 60,
+ (((((uintmax_t) 0xd9a0U << 28 | 0x541b55aU)
+ << 28 | 0xf0c1721U)
+ << 28 | 0x1df689bU)
+ << 28 | 0x98f81d7U),
+ UINTMAX_MAX / 487)
+P (4, 66,
+ (((((uintmax_t) 0x673bU << 28 | 0xf592825U)
+ << 28 | 0x8a2ac0eU)
+ << 28 | 0x994983eU)
+ << 28 | 0x90f1ec3U),
+ UINTMAX_MAX / 491)
+P (8, 64,
+ (((((uintmax_t) 0x0ddaU << 28 | 0x093c062U)
+ << 28 | 0x8041aadU)
+ << 28 | 0x671e44bU)
+ << 28 | 0xed87f3bU),
+ UINTMAX_MAX / 499)
+P (4, 66,
+ (((((uintmax_t) 0xa9fcU << 28 | 0xf24229bU)
+ << 28 | 0xbcd1af9U)
+ << 28 | 0x623a051U)
+ << 28 | 0x6e70fc7U),
+ UINTMAX_MAX / 503)
+P (6, 62,
+ (((((uintmax_t) 0xcbb1U << 28 | 0x8a4f773U)
+ << 28 | 0x2cc324bU)
+ << 28 | 0x7129be9U)
+ << 28 | 0xdece355U),
+ UINTMAX_MAX / 509)
+P (12, 56,
+ (((((uintmax_t) 0x01f7U << 28 | 0x27cce5fU)
+ << 28 | 0x530a519U)
+ << 28 | 0x0f3b747U)
+ << 28 | 0x3f62c39U),
+ UINTMAX_MAX / 521)
+P (2, 64,
+ (((((uintmax_t) 0x6da4U << 28 | 0xf4bdeb7U)
+ << 28 | 0x1121c63U)
+ << 28 | 0xdacc9aaU)
+ << 28 | 0xd46f9a3U),
+ UINTMAX_MAX / 523)
+P (18, 52,
+ (((((uintmax_t) 0x4d9aU << 28 | 0xbc552cfU)
+ << 28 | 0x42b88c1U)
+ << 28 | 0x108fda2U)
+ << 28 | 0x4e8d035U),
+ UINTMAX_MAX / 541)
+P (6, 52,
+ (((((uintmax_t) 0x141fU << 28 | 0xd312409U)
+ << 28 | 0x5c328b7U)
+ << 28 | 0x7578472U)
+ << 28 | 0x319bd8bU),
+ UINTMAX_MAX / 547)
+P (10, 44,
+ (((((uintmax_t) 0xddfdU << 28 | 0x3e0bf32U)
+ << 28 | 0x18d1947U)
+ << 28 | 0x3d20a1cU)
+ << 28 | 0x7ed9da5U),
+ UINTMAX_MAX / 557)
+P (6, 44,
+ (((((uintmax_t) 0xdb2bU << 28 | 0x3278f3bU)
+ << 28 | 0x910d2fbU)
+ << 28 | 0xe85af0fU)
+ << 28 | 0xea2c8fbU),
+ UINTMAX_MAX / 563)
+P (6, 44,
+ (((((uintmax_t) 0xcb5cU << 28 | 0x3b636e3U)
+ << 28 | 0xa7d1358U)
+ << 28 | 0xa1f7e6cU)
+ << 28 | 0xe0f4c09U),
+ UINTMAX_MAX / 569)
+P (2, 46,
+ (((((uintmax_t) 0x1bcbU << 28 | 0xfe34e75U)
+ << 28 | 0x76cf21aU)
+ << 28 | 0x00e58c5U)
+ << 28 | 0x44986f3U),
+ UINTMAX_MAX / 571)
+P (6, 42,
+ (((((uintmax_t) 0x6b5eU << 28 | 0x80aa5efU)
+ << 28 | 0x23f0071U)
+ << 28 | 0x94a17f5U)
+ << 28 | 0x5a10dc1U),
+ UINTMAX_MAX / 577)
+P (10, 44,
+ (((((uintmax_t) 0x9a62U << 28 | 0x8feb110U)
+ << 28 | 0x22e3a70U)
+ << 28 | 0x8494478U)
+ << 28 | 0x5e33763U),
+ UINTMAX_MAX / 587)
+P (6, 48,
+ (((((uintmax_t) 0xbe61U << 28 | 0x909eddeU)
+ << 28 | 0x53c01baU)
+ << 28 | 0x10679bdU)
+ << 28 | 0x84886b1U),
+ UINTMAX_MAX / 593)
+P (6, 44,
+ (((((uintmax_t) 0x4febU << 28 | 0x7c5e05fU)
+ << 28 | 0xbb9e8ebU)
+ << 28 | 0xe9c6bb3U)
+ << 28 | 0x1260967U),
+ UINTMAX_MAX / 599)
+P (2, 46,
+ (((((uintmax_t) 0x1ff2U << 28 | 0x5e8ff92U)
+ << 28 | 0xf47fc97U)
+ << 28 | 0xa3fe4bdU)
+ << 28 | 0x1ff25e9U),
+ UINTMAX_MAX / 601)
+P (6, 46,
+ (((((uintmax_t) 0x3014U << 28 | 0x3e6b1faU)
+ << 28 | 0x187616cU)
+ << 28 | 0x6388395U)
+ << 28 | 0xb84d99fU),
+ UINTMAX_MAX / 607)
+P (6, 46,
+ (((((uintmax_t) 0xd491U << 28 | 0x54c6c94U)
+ << 28 | 0xac0f08cU)
+ << 28 | 0x51da6a1U)
+ << 28 | 0x335df6dU),
+ UINTMAX_MAX / 613)
+P (4, 44,
+ (((((uintmax_t) 0x9b97U << 28 | 0x71454a4U)
+ << 28 | 0x4e00d46U)
+ << 28 | 0xf323447U)
+ << 28 | 0x5d5add9U),
+ UINTMAX_MAX / 617)
+P (2, 54,
+ (((((uintmax_t) 0x3abaU << 28 | 0x1b4baefU)
+ << 28 | 0x0b2a990U)
+ << 28 | 0x5605ca3U)
+ << 28 | 0xc619a43U),
+ UINTMAX_MAX / 619)
+P (12, 46,
+ (((((uintmax_t) 0xcc11U << 28 | 0xd9dd1bfU)
+ << 28 | 0xe608eceU)
+ << 28 | 0xe8dff30U)
+ << 28 | 0x4767747U),
+ UINTMAX_MAX / 631)
+P (10, 42,
+ (((((uintmax_t) 0xff99U << 28 | 0xc27f006U)
+ << 28 | 0x63d80ffU)
+ << 28 | 0x99c27f0U)
+ << 28 | 0x0663d81U),
+ UINTMAX_MAX / 641)
+P (2, 48,
+ (((((uintmax_t) 0x111eU << 28 | 0xa8032f6U)
+ << 28 | 0x0bf1aacU)
+ << 28 | 0xca407f6U)
+ << 28 | 0x71ddc2bU),
+ UINTMAX_MAX / 643)
+P (4, 54,
+ (((((uintmax_t) 0xdd93U << 28 | 0x95f5b66U)
+ << 28 | 0x7aa88e7U)
+ << 28 | 0x1298bacU)
+ << 28 | 0x1e12337U),
+ UINTMAX_MAX / 647)
+P (6, 56,
+ (((((uintmax_t) 0xa7caU << 28 | 0xaed9303U)
+ << 28 | 0x8740afaU)
+ << 28 | 0x1e94309U)
+ << 28 | 0xcd09045U),
+ UINTMAX_MAX / 653)
+P (6, 60,
+ (((((uintmax_t) 0x2be5U << 28 | 0x958f582U)
+ << 28 | 0xe9db7beU)
+ << 28 | 0xbccb8e9U)
+ << 28 | 0x1496b9bU),
+ UINTMAX_MAX / 659)
+P (2, 66,
+ (((((uintmax_t) 0x995eU << 28 | 0x1ca8dbfU)
+ << 28 | 0xb5a3d31U)
+ << 28 | 0x2fa30ccU)
+ << 28 | 0x7d7b8bdU),
+ UINTMAX_MAX / 661)
+P (12, 60,
+ (((((uintmax_t) 0x9f00U << 28 | 0x6160ff9U)
+ << 28 | 0xe9f0061U)
+ << 28 | 0x60ff9e9U)
+ << 28 | 0xf006161U),
+ UINTMAX_MAX / 673)
+P (4, 62,
+ (((((uintmax_t) 0xb33cU << 28 | 0xe15ee9bU)
+ << 28 | 0x097416bU)
+ << 28 | 0x03673b5U)
+ << 28 | 0xe28152dU),
+ UINTMAX_MAX / 677)
+P (6, 60,
+ (((((uintmax_t) 0xfa00U << 28 | 0xbfe802fU)
+ << 28 | 0xfa00bfeU)
+ << 28 | 0x802ffa0U)
+ << 28 | 0x0bfe803U),
+ UINTMAX_MAX / 683)
+P (8, 60,
+ (((((uintmax_t) 0x1c28U << 28 | 0x02f6bcfU)
+ << 28 | 0x18d26e6U)
+ << 28 | 0x6fe25c9U)
+ << 28 | 0xe907c7bU),
+ UINTMAX_MAX / 691)
+P (10, 56,
+ (((((uintmax_t) 0xcf6dU << 28 | 0xec4793eU)
+ << 28 | 0x72aba3fU)
+ << 28 | 0x8b236c7U)
+ << 28 | 0x6528895U),
+ UINTMAX_MAX / 701)
+P (8, 52,
+ (((((uintmax_t) 0x1e54U << 28 | 0x7da72d2U)
+ << 28 | 0x24d44f6U)
+ << 28 | 0xf923bf0U)
+ << 28 | 0x1ce2c0dU),
+ UINTMAX_MAX / 709)
+P (10, 50,
+ (((((uintmax_t) 0x7746U << 28 | 0xda9d5fcU)
+ << 28 | 0x708306cU)
+ << 28 | 0x3d3d98bU)
+ << 28 | 0xed7c42fU),
+ UINTMAX_MAX / 719)
+P (8, 46,
+ (((((uintmax_t) 0xcdffU << 28 | 0x4bb5591U)
+ << 28 | 0x6e37a30U)
+ << 28 | 0x981efcdU)
+ << 28 | 0x4b010e7U),
+ UINTMAX_MAX / 727)
+P (6, 54,
+ (((((uintmax_t) 0x2c01U << 28 | 0x65a1b3dU)
+ << 28 | 0xd13356fU)
+ << 28 | 0x691fc81U)
+ << 28 | 0xebbe575U),
+ UINTMAX_MAX / 733)
+P (6, 58,
+ (((((uintmax_t) 0xa802U << 28 | 0xc574bddU)
+ << 28 | 0x5bccbb1U)
+ << 28 | 0x0480ddbU)
+ << 28 | 0x47b52cbU),
+ UINTMAX_MAX / 739)
+P (4, 66,
+ (((((uintmax_t) 0x5411U << 28 | 0xeaa350fU)
+ << 28 | 0x8134b74U)
+ << 28 | 0xcd59ed6U)
+ << 28 | 0x4f3f0d7U),
+ UINTMAX_MAX / 743)
+P (8, 60,
+ (((((uintmax_t) 0xfceeU << 28 | 0x9d7c6bbU)
+ << 28 | 0x7bbd301U)
+ << 28 | 0x05cb813U)
+ << 28 | 0x16d6c0fU),
+ UINTMAX_MAX / 751)
+P (6, 64,
+ (((((uintmax_t) 0x4248U << 28 | 0x5eb0874U)
+ << 28 | 0x553879bU)
+ << 28 | 0xe64c6d9U)
+ << 28 | 0x1c1195dU),
+ UINTMAX_MAX / 757)
+P (4, 62,
+ (((((uintmax_t) 0xe060U << 28 | 0xe20f797U)
+ << 28 | 0x0b19e71U)
+ << 28 | 0xb3f945aU)
+ << 28 | 0x27b1f49U),
+ UINTMAX_MAX / 761)
+P (8, 58,
+ (((((uintmax_t) 0x782dU << 28 | 0x463deb5U)
+ << 28 | 0xc369877U)
+ << 28 | 0xd80d50eU)
+ << 28 | 0x508fd01U),
+ UINTMAX_MAX / 769)
+P (4, 56,
+ (((((uintmax_t) 0x4a2fU << 28 | 0x06f468aU)
+ << 28 | 0x6e9cfa5U)
+ << 28 | 0xeb778e1U)
+ << 28 | 0x33551cdU),
+ UINTMAX_MAX / 773)
+P (14, 52,
+ (((((uintmax_t) 0xda44U << 28 | 0x4f5ea87U)
+ << 28 | 0xf831718U)
+ << 28 | 0x657d3c2U)
+ << 28 | 0xd8a3f1bU),
+ UINTMAX_MAX / 787)
+P (10, 56,
+ (((((uintmax_t) 0xfb80U << 28 | 0xcd9225eU)
+ << 28 | 0x6f2302eU)
+ << 28 | 0x40e220cU)
+ << 28 | 0x34ad735U),
+ UINTMAX_MAX / 797)
+P (12, 48,
+ (((((uintmax_t) 0x1719U << 28 | 0xa1b36beU)
+ << 28 | 0x7f357a7U)
+ << 28 | 0x6593c70U)
+ << 28 | 0xa714919U),
+ UINTMAX_MAX / 809)
+P (2, 48,
+ (((((uintmax_t) 0x2867U << 28 | 0x894fdcaU)
+ << 28 | 0x567da1eU)
+ << 28 | 0xef45212U)
+ << 28 | 0x4eea383U),
+ UINTMAX_MAX / 811)
+P (10, 42,
+ (((((uintmax_t) 0x8932U << 28 | 0xd36914eU)
+ << 28 | 0x43f9c38U)
+ << 28 | 0x206dc24U)
+ << 28 | 0x2ba771dU),
+ UINTMAX_MAX / 821)
+P (2, 54,
+ (((((uintmax_t) 0xdeb7U << 28 | 0x8610cc0U)
+ << 28 | 0xdafbf4cU)
+ << 28 | 0xd4c3580U)
+ << 28 | 0x7772287U),
+ UINTMAX_MAX / 823)
+P (4, 54,
+ (((((uintmax_t) 0x8fa1U << 28 | 0xe560e3dU)
+ << 28 | 0x4a9a283U)
+ << 28 | 0xde917d5U)
+ << 28 | 0xe69ddf3U),
+ UINTMAX_MAX / 827)
+P (2, 54,
+ (((((uintmax_t) 0x6724U << 28 | 0x2159dccU)
+ << 28 | 0xbcfd388U)
+ << 28 | 0x2ef0403U)
+ << 28 | 0xb4a6c15U),
+ UINTMAX_MAX / 829)
+P (10, 48,
+ (((((uintmax_t) 0x5e96U << 28 | 0xbb58ca9U)
+ << 28 | 0xa64b0f8U)
+ << 28 | 0xfb6c51cU)
+ << 28 | 0x606b677U),
+ UINTMAX_MAX / 839)
+P (14, 54,
+ (((((uintmax_t) 0x2450U << 28 | 0x6e7171bU)
+ << 28 | 0xe930eb4U)
+ << 28 | 0xabaac44U)
+ << 28 | 0x6d3e1fdU),
+ UINTMAX_MAX / 853)
+P (4, 54,
+ (((((uintmax_t) 0x3743U << 28 | 0x3611535U)
+ << 28 | 0x7861fa9U)
+ << 28 | 0xf83bbe4U)
+ << 28 | 0x84a14e9U),
+ UINTMAX_MAX / 857)
+P (2, 60,
+ (((((uintmax_t) 0x232aU << 28 | 0x9df37baU)
+ << 28 | 0xdbf080bU)
+ << 28 | 0xebbc0d1U)
+ << 28 | 0xce874d3U),
+ UINTMAX_MAX / 859)
+P (4, 66,
+ (((((uintmax_t) 0x569eU << 28 | 0x67d2e92U)
+ << 28 | 0x8a3bebdU)
+ << 28 | 0x418eaf0U)
+ << 28 | 0x473189fU),
+ UINTMAX_MAX / 863)
+P (14, 60,
+ (((((uintmax_t) 0x7e1aU << 28 | 0x457923eU)
+ << 28 | 0x77ae444U)
+ << 28 | 0xe3af6f3U)
+ << 28 | 0x72b7e65U),
+ UINTMAX_MAX / 877)
+P (4, 60,
+ (((((uintmax_t) 0x9764U << 28 | 0x3fed672U)
+ << 28 | 0x7cf2ec8U)
+ << 28 | 0x7fdace4U)
+ << 28 | 0xf9e5d91U),
+ UINTMAX_MAX / 881)
+P (2, 64,
+ (((((uintmax_t) 0xea8bU << 28 | 0xbde5e83U)
+ << 28 | 0x9fbf0ecU)
+ << 28 | 0x93479c4U)
+ << 28 | 0x46bd9bbU),
+ UINTMAX_MAX / 883)
+P (4, 66,
+ (((((uintmax_t) 0x3d2fU << 28 | 0x9f06a35U)
+ << 28 | 0xae9c6daU)
+ << 28 | 0xc4d592eU)
+ << 28 | 0x777c647U),
+ UINTMAX_MAX / 887)
+P (20, 60,
+ (((((uintmax_t) 0x81d5U << 28 | 0xa9a1ba9U)
+ << 28 | 0x11379a6U)
+ << 28 | 0x3ea8c8fU)
+ << 28 | 0x61f0c23U),
+ UINTMAX_MAX / 907)
+P (4, 60,
+ (((((uintmax_t) 0x752eU << 28 | 0x5ddb77fU)
+ << 28 | 0xdc07de4U)
+ << 28 | 0x76062eaU)
+ << 28 | 0x5cbbb6fU),
+ UINTMAX_MAX / 911)
+P (8, 58,
+ (((((uintmax_t) 0x1abdU << 28 | 0xfafc60fU)
+ << 28 | 0x0add2dfU)
+ << 28 | 0x68761c6U)
+ << 28 | 0x9daac27U),
+ UINTMAX_MAX / 919)
+P (10, 54,
+ (((((uintmax_t) 0xac3aU << 28 | 0x6b786c0U)
+ << 28 | 0x582e4b8U)
+ << 28 | 0x13d7376U)
+ << 28 | 0x37aa061U),
+ UINTMAX_MAX / 929)
+P (8, 54,
+ (((((uintmax_t) 0x131fU << 28 | 0xf741d81U)
+ << 28 | 0xc6a01a3U)
+ << 28 | 0xa77aac1U)
+ << 28 | 0xfb15099U),
+ UINTMAX_MAX / 937)
+P (4, 56,
+ (((((uintmax_t) 0xc53cU << 28 | 0xaad918cU)
+ << 28 | 0x1b34817U)
+ << 28 | 0xf0c3e07U)
+ << 28 | 0x12c5825U),
+ UINTMAX_MAX / 941)
+P (6, 62,
+ (((((uintmax_t) 0xea1aU << 28 | 0x7df8f8bU)
+ << 28 | 0x37f52fdU)
+ << 28 | 0x912a70fU)
+ << 28 | 0xf30637bU),
+ UINTMAX_MAX / 947)
+P (6, 60,
+ (((((uintmax_t) 0xbb3bU << 28 | 0x5dc0113U)
+ << 28 | 0x1288ffbU)
+ << 28 | 0xb3b5dc0U)
+ << 28 | 0x1131289U),
+ UINTMAX_MAX / 953)
+P (14, 52,
+ (((((uintmax_t) 0x50beU << 28 | 0x9c31c53U)
+ << 28 | 0xa81b885U)
+ << 28 | 0x6d560a0U)
+ << 28 | 0xf5acdf7U),
+ UINTMAX_MAX / 967)
+P (4, 50,
+ (((((uintmax_t) 0x6580U << 28 | 0xec3a008U)
+ << 28 | 0x6fc9296U)
+ << 28 | 0x472f314U)
+ << 28 | 0xd3f89e3U),
+ UINTMAX_MAX / 971)
+P (6, 54,
+ (((((uintmax_t) 0x1108U << 28 | 0x1f71752U)
+ << 28 | 0x03ab1a7U)
+ << 28 | 0x6f5c7edU)
+ << 28 | 0x2253531U),
+ UINTMAX_MAX / 977)
+P (6, 50,
+ (((((uintmax_t) 0xb81fU << 28 | 0x4053563U)
+ << 28 | 0x3908981U)
+ << 28 | 0x6eae7c7U)
+ << 28 | 0xbf69fe7U),
+ UINTMAX_MAX / 983)
+P (8, 48,
+ (((((uintmax_t) 0x9c8bU << 28 | 0x7ed668eU)
+ << 28 | 0x14263b6U)
+ << 28 | 0xa2bea4cU)
+ << 28 | 0xfb1781fU),
+ UINTMAX_MAX / 991)
+P (6, 52,
+ (((((uintmax_t) 0x0291U << 28 | 0x54fdb06U)
+ << 28 | 0x6b547a3U)
+ << 28 | 0x900c533U)
+ << 28 | 0x18e81edU),
+ UINTMAX_MAX / 997)
+P (12, 42,
+ (((((uintmax_t) 0x2240U << 28 | 0x71aa3e6U)
+ << 28 | 0xa0db360U)
+ << 28 | 0xaa7f5d9U)
+ << 28 | 0xf148d11U),
+ UINTMAX_MAX / 1009)
+P (4, 48,
+ (((((uintmax_t) 0x02c7U << 28 | 0xa505cffU)
+ << 28 | 0xbf4e16bU)
+ << 28 | 0xe8c0102U)
+ << 28 | 0xc7a505dU),
+ UINTMAX_MAX / 1013)
+P (6, 44,
+ (((((uintmax_t) 0xcafdU << 28 | 0xbd2c779U)
+ << 28 | 0x57ad98fU)
+ << 28 | 0xf3f0ed2U)
+ << 28 | 0x8728f33U),
+ UINTMAX_MAX / 1019)
+P (2, 48,
+ (((((uintmax_t) 0x513cU << 28 | 0xedb245bU)
+ << 28 | 0x4473568U)
+ << 28 | 0x0e0a87eU)
+ << 28 | 0x5ec7155U),
+ UINTMAX_MAX / 1021)
+P (10, 56,
+ (((((uintmax_t) 0x2e6eU << 28 | 0xbe33267U)
+ << 28 | 0xca5ddbbU)
+ << 28 | 0xf70fa49U)
+ << 28 | 0xfe829b7U),
+ UINTMAX_MAX / 1031)
+P (2, 58,
+ (((((uintmax_t) 0x007eU << 28 | 0xe2825abU)
+ << 28 | 0x3eb2ed6U)
+ << 28 | 0x9d1e7b6U)
+ << 28 | 0xa50ca39U),
+ UINTMAX_MAX / 1033)
+P (6, 54,
+ (((((uintmax_t) 0x2f8dU << 28 | 0xacb84cdU)
+ << 28 | 0xfb90a1aU)
+ << 28 | 0x1e0f46bU)
+ << 28 | 0x6d26aefU),
+ UINTMAX_MAX / 1039)
+P (10, 48,
+ (((((uintmax_t) 0x01f3U << 28 | 0xcc435b0U)
+ << 28 | 0x713c474U)
+ << 28 | 0x29f9a7aU)
+ << 28 | 0x8251829U),
+ UINTMAX_MAX / 1049)
+P (2, 52,
+ (((((uintmax_t) 0x8c0eU << 28 | 0x9d59e14U)
+ << 28 | 0xf29a6d9U)
+ << 28 | 0xc2219d1U)
+ << 28 | 0xb863613U),
+ UINTMAX_MAX / 1051)
+P (10, 48,
+ (((((uintmax_t) 0x6e81U << 28 | 0xcf42d5cU)
+ << 28 | 0x6932e91U)
+ << 28 | 0x406c182U)
+ << 28 | 0x0d077adU),
+ UINTMAX_MAX / 1061)
+P (2, 54,
+ (((((uintmax_t) 0x9c4cU << 28 | 0x1a02688U)
+ << 28 | 0x4efdd52U)
+ << 28 | 0x1f4ec02U)
+ << 28 | 0xe3d2b97U),
+ UINTMAX_MAX / 1063)
+P (6, 54,
+ (((((uintmax_t) 0x7bcfU << 28 | 0x2599067U)
+ << 28 | 0x74255bbU)
+ << 28 | 0x8283b63U)
+ << 28 | 0xdc8eba5U),
+ UINTMAX_MAX / 1069)
+P (18, 42,
+ (((((uintmax_t) 0x46a7U << 28 | 0x3667275U)
+ << 28 | 0x48c5d43U)
+ << 28 | 0x1eda153U)
+ << 28 | 0x229ebbfU),
+ UINTMAX_MAX / 1087)
+P (4, 60,
+ (((((uintmax_t) 0xe720U << 28 | 0x9daecfeU)
+ << 28 | 0x5b832afU)
+ << 28 | 0x0bf78d7U)
+ << 28 | 0xe01686bU),
+ UINTMAX_MAX / 1091)
+P (2, 60,
+ (((((uintmax_t) 0x194bU << 28 | 0xa6ff4c1U)
+ << 28 | 0xeeaafa9U)
+ << 28 | 0xced0742U)
+ << 28 | 0xc086e8dU),
+ UINTMAX_MAX / 1093)
+P (4, 66,
+ (((((uintmax_t) 0x777bU << 28 | 0x730c5e4U)
+ << 28 | 0x768c7c2U)
+ << 28 | 0x6458ad9U)
+ << 28 | 0xf632df9U),
+ UINTMAX_MAX / 1097)
+P (6, 68,
+ (((((uintmax_t) 0x2aefU << 28 | 0xfc49577U)
+ << 28 | 0xfe24abbU)
+ << 28 | 0xff1255dU)
+ << 28 | 0xff892afU),
+ UINTMAX_MAX / 1103)
+P (6, 72,
+ (((((uintmax_t) 0xf1b0U << 28 | 0x213da24U)
+ << 28 | 0x78f59cbU)
+ << 28 | 0xd49a333U)
+ << 28 | 0xf04d8fdU),
+ UINTMAX_MAX / 1109)
+P (8, 70,
+ (((((uintmax_t) 0x8822U << 28 | 0xd60f205U)
+ << 28 | 0x0ac58ecU)
+ << 28 | 0x84ed6f9U)
+ << 28 | 0xcfdeff5U),
+ UINTMAX_MAX / 1117)
+P (6, 70,
+ (((((uintmax_t) 0x3606U << 28 | 0xd6bd351U)
+ << 28 | 0xd682d97U)
+ << 28 | 0x980cc40U)
+ << 28 | 0xbda9d4bU),
+ UINTMAX_MAX / 1123)
+P (6, 72,
+ (((((uintmax_t) 0x0122U << 28 | 0x3d38ea0U)
+ << 28 | 0x15c4977U)
+ << 28 | 0x7f34d52U)
+ << 28 | 0x4f5cbd9U),
+ UINTMAX_MAX / 1129)
+P (22, 62,
+ (((((uintmax_t) 0x78feU << 28 | 0x716e8a5U)
+ << 28 | 0x7a1b227U)
+ << 28 | 0x97051d9U)
+ << 28 | 0x4cbbb7fU),
+ UINTMAX_MAX / 1151)
+P (2, 64,
+ (((((uintmax_t) 0xd6ecU << 28 | 0xaef5908U)
+ << 28 | 0xa8be0eaU)
+ << 28 | 0x769051bU)
+ << 28 | 0x4f43b81U),
+ UINTMAX_MAX / 1153)
+P (10, 60,
+ (((((uintmax_t) 0x7867U << 28 | 0xe595e6eU)
+ << 28 | 0x801c2ceU)
+ << 28 | 0x7910f30U)
+ << 28 | 0x34d4323U),
+ UINTMAX_MAX / 1163)
+P (8, 58,
+ (((((uintmax_t) 0xa705U << 28 | 0xe713e4eU)
+ << 28 | 0x43c5692U)
+ << 28 | 0x791d137U)
+ << 28 | 0x4f5b99bU),
+ UINTMAX_MAX / 1171)
+P (10, 50,
+ (((((uintmax_t) 0x92c0U << 28 | 0x0ddf7c3U)
+ << 28 | 0x4e40989U)
+ << 28 | 0xa5645ccU)
+ << 28 | 0x68ea1b5U),
+ UINTMAX_MAX / 1181)
+P (6, 50,
+ (((((uintmax_t) 0xab06U << 28 | 0xaf8e205U)
+ << 28 | 0x9b7f75fU)
+ << 28 | 0x8aacf79U)
+ << 28 | 0x6c0cf0bU),
+ UINTMAX_MAX / 1187)
+P (6, 56,
+ (((((uintmax_t) 0xe187U << 28 | 0x673725fU)
+ << 28 | 0xb4774f2U)
+ << 28 | 0xe90a15eU)
+ << 28 | 0x33edf99U),
+ UINTMAX_MAX / 1193)
+P (8, 58,
+ (((((uintmax_t) 0x57d1U << 28 | 0xf5579b6U)
+ << 28 | 0x3f8538eU)
+ << 28 | 0x99e5febU)
+ << 28 | 0x897c451U),
+ UINTMAX_MAX / 1201)
+P (12, 64,
+ (((((uintmax_t) 0x5f64U << 28 | 0xab5ec29U)
+ << 28 | 0x5d7e6acU)
+ << 28 | 0xa2eda38U)
+ << 28 | 0xfb91695U),
+ UINTMAX_MAX / 1213)
+P (4, 62,
+ (((((uintmax_t) 0x48c8U << 28 | 0x41a1574U)
+ << 28 | 0xbf0035dU)
+ << 28 | 0x9b737beU)
+ << 28 | 0x5ea8b41U),
+ UINTMAX_MAX / 1217)
+P (6, 60,
+ (((((uintmax_t) 0x348aU << 28 | 0x26ef0b8U)
+ << 28 | 0x33e964aU)
+ << 28 | 0xefe1db9U)
+ << 28 | 0x3fd7cf7U),
+ UINTMAX_MAX / 1223)
+P (6, 60,
+ (((((uintmax_t) 0x5247U << 28 | 0x3d081faU)
+ << 28 | 0x958f1a0U)
+ << 28 | 0x994ef20U)
+ << 28 | 0xb3f8805U),
+ UINTMAX_MAX / 1229)
+P (2, 60,
+ (((((uintmax_t) 0x0ec3U << 28 | 0xe6367c5U)
+ << 28 | 0xc55ae10U)
+ << 28 | 0x3890bdaU)
+ << 28 | 0x912822fU),
+ UINTMAX_MAX / 1231)
+P (6, 60,
+ (((((uintmax_t) 0xb57fU << 28 | 0x46921bbU)
+ << 28 | 0xb4ab5b4U)
+ << 28 | 0x41659d1U)
+ << 28 | 0x3a9147dU),
+ UINTMAX_MAX / 1237)
+P (12, 52,
+ (((((uintmax_t) 0xb2eeU << 28 | 0xfcecf03U)
+ << 28 | 0x7c00d1eU)
+ << 28 | 0x2134440U)
+ << 28 | 0xc4c3f21U),
+ UINTMAX_MAX / 1249)
+P (10, 44,
+ (((((uintmax_t) 0xed4bU << 28 | 0x07ee1b3U)
+ << 28 | 0xf3ccc26U)
+ << 28 | 0x3a27727U)
+ << 28 | 0xa6883c3U),
+ UINTMAX_MAX / 1259)
+P (18, 30,
+ (((((uintmax_t) 0x435bU << 28 | 0x9d5e6bdU)
+ << 28 | 0xa4fc978U)
+ << 28 | 0xe221472U)
+ << 28 | 0xab33855U),
+ UINTMAX_MAX / 1277)
+P (2, 40,
+ (((((uintmax_t) 0x6013U << 28 | 0x370b023U)
+ << 28 | 0x3a3ed95U)
+ << 28 | 0xeac88e8U)
+ << 28 | 0x2e6faffU),
+ UINTMAX_MAX / 1279)
+P (4, 38,
+ (((((uintmax_t) 0x3447U << 28 | 0x089473bU)
+ << 28 | 0xa900ff6U)
+ << 28 | 0x6c25831U)
+ << 28 | 0x7be8dabU),
+ UINTMAX_MAX / 1283)
+P (6, 38,
+ (((((uintmax_t) 0x0f7dU << 28 | 0xb74fa3dU)
+ << 28 | 0x912de09U)
+ << 28 | 0xee202c7U)
+ << 28 | 0xcb91939U),
+ UINTMAX_MAX / 1289)
+P (2, 70,
+ (((((uintmax_t) 0x5316U << 28 | 0x02c6b14U)
+ << 28 | 0x6caa88dU)
+ << 28 | 0x2fca104U)
+ << 28 | 0x2a09ea3U),
+ UINTMAX_MAX / 1291)
+P (6, 70,
+ (((((uintmax_t) 0x2128U << 28 | 0xdb7c26aU)
+ << 28 | 0xfaabb82U)
+ << 28 | 0x779c856U)
+ << 28 | 0xd8b8bf1U),
+ UINTMAX_MAX / 1297)
+P (4, 72,
+ (((((uintmax_t) 0xb01cU << 28 | 0x55cadf2U)
+ << 28 | 0x39d9d38U)
+ << 28 | 0x79361cbU)
+ << 28 | 0xa8a223dU),
+ UINTMAX_MAX / 1301)
+P (2, 78,
+ (((((uintmax_t) 0x3d4cU << 28 | 0x6d3cb58U)
+ << 28 | 0x9b9a9f2U)
+ << 28 | 0x3f43639U)
+ << 28 | 0xc3182a7U),
+ UINTMAX_MAX / 1303)
+P (4, 92,
+ (((((uintmax_t) 0x0bc0U << 28 | 0x89e42fcU)
+ << 28 | 0xab94aa0U)
+ << 28 | 0x3868fc4U)
+ << 28 | 0x74bcd13U),
+ UINTMAX_MAX / 1307)
+P (12, 90,
+ (((((uintmax_t) 0x34fcU << 28 | 0x4ff6af1U)
+ << 28 | 0x0e2b165U)
+ << 28 | 0x1e78b8cU)
+ << 28 | 0x5311a97U),
+ UINTMAX_MAX / 1319)
+P (2, 102,
+ (((((uintmax_t) 0x18ffU << 28 | 0xce639c0U)
+ << 28 | 0x0c6718fU)
+ << 28 | 0xfce639cU)
+ << 28 | 0x00c6719U),
+ UINTMAX_MAX / 1321)
+P (6, 100,
+ (((((uintmax_t) 0x9b4cU << 28 | 0x33b39aeU)
+ << 28 | 0x96dc4f7U)
+ << 28 | 0xb460754U)
+ << 28 | 0xb0b61cfU),
+ UINTMAX_MAX / 1327)
+P (34, 68,
+ (((((uintmax_t) 0xbbe8U << 28 | 0xad0c9a3U)
+ << 28 | 0xd51d27bU)
+ << 28 | 0x03f3359U)
+ << 28 | 0xb8e63b1U),
+ UINTMAX_MAX / 1361)
+P (6, 66,
+ (((((uintmax_t) 0xa28dU << 28 | 0x33dfca1U)
+ << 28 | 0x0dabba5U)
+ << 28 | 0x5c53260U)
+ << 28 | 0x41eb667U),
+ UINTMAX_MAX / 1367)
+P (6, 66,
+ (((((uintmax_t) 0x677bU << 28 | 0x3ed5acdU)
+ << 28 | 0x78a2964U)
+ << 28 | 0x7f88ab8U)
+ << 28 | 0x96a76f5U),
+ UINTMAX_MAX / 1373)
+P (8, 66,
+ (((((uintmax_t) 0xf4e0U << 28 | 0xac06ac6U)
+ << 28 | 0x595988fU)
+ << 28 | 0xd971434U)
+ << 28 | 0xa55a46dU),
+ UINTMAX_MAX / 1381)
+P (18, 52,
+ (((((uintmax_t) 0x3ba7U << 28 | 0x6f12d90U)
+ << 28 | 0x609e19fU)
+ << 28 | 0xbf96995U)
+ << 28 | 0x8046447U),
+ UINTMAX_MAX / 1399)
+P (10, 44,
+ (((((uintmax_t) 0x3d69U << 28 | 0x32b0f71U)
+ << 28 | 0x8e43399U)
+ << 28 | 0x86feba6U)
+ << 28 | 0x9be3a81U),
+ UINTMAX_MAX / 1409)
+P (14, 36,
+ (((((uintmax_t) 0xb7adU << 28 | 0xf701426U)
+ << 28 | 0x239eda6U)
+ << 28 | 0x68b3e6dU)
+ << 28 | 0x053796fU),
+ UINTMAX_MAX / 1423)
+P (4, 44,
+ (((((uintmax_t) 0xd0d1U << 28 | 0x893d2caU)
+ << 28 | 0xb80fc97U)
+ << 28 | 0x694e658U)
+ << 28 | 0x9f4e09bU),
+ UINTMAX_MAX / 1427)
+P (2, 52,
+ (((((uintmax_t) 0xc00bU << 28 | 0x7721dbcU)
+ << 28 | 0xffd2237U)
+ << 28 | 0x890c00bU)
+ << 28 | 0x7721dbdU),
+ UINTMAX_MAX / 1429)
+P (4, 50,
+ (((((uintmax_t) 0xe9d9U << 28 | 0x0e1cf0dU)
+ << 28 | 0x0a8a45aU)
+ << 28 | 0xc094a23U)
+ << 28 | 0x5f37ea9U),
+ UINTMAX_MAX / 1433)
+P (6, 48,
+ (((((uintmax_t) 0x8489U << 28 | 0x56fe661U)
+ << 28 | 0xd881831U)
+ << 28 | 0xcff775fU)
+ << 28 | 0x2d5d65fU),
+ UINTMAX_MAX / 1439)
+P (8, 42,
+ (((((uintmax_t) 0xfd85U << 28 | 0xed3f28dU)
+ << 28 | 0xe356dddU)
+ << 28 | 0xad8e6b3U)
+ << 28 | 0x6505217U),
+ UINTMAX_MAX / 1447)
+P (4, 42,
+ (((((uintmax_t) 0x0a68U << 28 | 0xcca8aacU)
+ << 28 | 0x8c7035aU)
+ << 28 | 0x27df897U)
+ << 28 | 0x062cd03U),
+ UINTMAX_MAX / 1451)
+P (2, 46,
+ (((((uintmax_t) 0x57eaU << 28 | 0xdb877ceU)
+ << 28 | 0xaae6ce2U)
+ << 28 | 0x396fe0fU)
+ << 28 | 0xdb5a625U),
+ UINTMAX_MAX / 1453)
+P (6, 52,
+ (((((uintmax_t) 0x1c12U << 28 | 0xf330f43U)
+ << 28 | 0xe76f6b3U)
+ << 28 | 0x52a4957U)
+ << 28 | 0xe82317bU),
+ UINTMAX_MAX / 1459)
+P (12, 52,
+ (((((uintmax_t) 0x472dU << 28 | 0xc52d6c1U)
+ << 28 | 0x2cb9dd8U)
+ << 28 | 0xab3f2c6U)
+ << 28 | 0x0c2ea3fU),
+ UINTMAX_MAX / 1471)
+P (10, 50,
+ (((((uintmax_t) 0xda51U << 28 | 0x3e0e2c9U)
+ << 28 | 0x8ce0b68U)
+ << 28 | 0x93f702fU)
+ << 28 | 0x0452479U),
+ UINTMAX_MAX / 1481)
+P (2, 60,
+ (((((uintmax_t) 0x442fU << 28 | 0xa4dae2dU)
+ << 28 | 0x3a2c896U)
+ << 28 | 0x86fdc18U)
+ << 28 | 0x2acf7e3U),
+ UINTMAX_MAX / 1483)
+P (4, 62,
+ (((((uintmax_t) 0x091fU << 28 | 0xd96fbb2U)
+ << 28 | 0x2f2be68U)
+ << 28 | 0x5403717U)
+ << 28 | 0x3dce12fU),
+ UINTMAX_MAX / 1487)
+P (2, 64,
+ (((((uintmax_t) 0x3accU << 28 | 0x97fbdfaU)
+ << 28 | 0xd798d7fU)
+ << 28 | 0x0ded168U)
+ << 28 | 0x5c27331U),
+ UINTMAX_MAX / 1489)
+P (4, 66,
+ (((((uintmax_t) 0x4d54U << 28 | 0xe047548U)
+ << 28 | 0x87cd3eeU)
+ << 28 | 0xda72e1fU)
+ << 28 | 0xe490b7dU),
+ UINTMAX_MAX / 1493)
+P (6, 68,
+ (((((uintmax_t) 0x7e8cU << 28 | 0x61afbbbU)
+ << 28 | 0x013209eU)
+ << 28 | 0x7bfc959U)
+ << 28 | 0xa8e6e53U),
+ UINTMAX_MAX / 1499)
+P (12, 60,
+ (((((uintmax_t) 0xc4b3U << 28 | 0x96f4fccU)
+ << 28 | 0x7ebab49U)
+ << 28 | 0xb314d6dU)
+ << 28 | 0x4753dd7U),
+ UINTMAX_MAX / 1511)
+P (12, 56,
+ (((((uintmax_t) 0x9eadU << 28 | 0x21c933fU)
+ << 28 | 0x089292eU)
+ << 28 | 0x8f8c5acU)
+ << 28 | 0x4aa1b3bU),
+ UINTMAX_MAX / 1523)
+P (8, 52,
+ (((((uintmax_t) 0x0584U << 28 | 0x992a4deU)
+ << 28 | 0xb99aab8U)
+ << 28 | 0xef72348U)
+ << 28 | 0x1163d33U),
+ UINTMAX_MAX / 1531)
+P (12, 54,
+ (((((uintmax_t) 0x8b08U << 28 | 0x7620d9aU)
+ << 28 | 0xcb6806aU)
+ << 28 | 0x2ec96a5U)
+ << 28 | 0x94287b7U),
+ UINTMAX_MAX / 1543)
+P (6, 52,
+ (((((uintmax_t) 0xc108U << 28 | 0x6dbce6bU)
+ << 28 | 0x6c94bdbU)
+ << 28 | 0xa41c6d1U)
+ << 28 | 0x3aab8c5U),
+ UINTMAX_MAX / 1549)
+P (4, 54,
+ (((((uintmax_t) 0xe478U << 28 | 0xaa1e005U)
+ << 28 | 0x46633c2U)
+ << 28 | 0xadbe648U)
+ << 28 | 0xdc3aaf1U),
+ UINTMAX_MAX / 1553)
+P (6, 50,
+ (((((uintmax_t) 0x5cf1U << 28 | 0x0e9d4faU)
+ << 28 | 0x40b2a87U)
+ << 28 | 0xa2bade5U)
+ << 28 | 0x65f91a7U),
+ UINTMAX_MAX / 1559)
+P (8, 46,
+ (((((uintmax_t) 0x9ecbU << 28 | 0x8ef2c45U)
+ << 28 | 0xec11a4dU)
+ << 28 | 0x6fe8798U)
+ << 28 | 0xc01f5dfU),
+ UINTMAX_MAX / 1567)
+P (4, 48,
+ (((((uintmax_t) 0xfb99U << 28 | 0xaa49543U)
+ << 28 | 0xf39d937U)
+ << 28 | 0x91310c8U)
+ << 28 | 0xc23d98bU),
+ UINTMAX_MAX / 1571)
+P (8, 42,
+ (((((uintmax_t) 0x7abbU << 28 | 0x187b379U)
+ << 28 | 0xc2112f8U)
+ << 28 | 0x0e446b0U)
+ << 28 | 0x1228883U),
+ UINTMAX_MAX / 1579)
+P (4, 44,
+ (((((uintmax_t) 0x3cceU << 28 | 0x5a3d212U)
+ << 28 | 0x6f95e9aU)
+ << 28 | 0xed1436fU)
+ << 28 | 0xbf500cfU),
+ UINTMAX_MAX / 1583)
+P (14, 40,
+ (((((uintmax_t) 0xc6eeU << 28 | 0xd90c05cU)
+ << 28 | 0x5547a78U)
+ << 28 | 0x39b54ccU)
+ << 28 | 0x8b24115U),
+ UINTMAX_MAX / 1597)
+P (4, 56,
+ (((((uintmax_t) 0x8798U << 28 | 0x627f99aU)
+ << 28 | 0x9f948c1U)
+ << 28 | 0x28c646aU)
+ << 28 | 0xd0309c1U),
+ UINTMAX_MAX / 1601)
+P (6, 56,
+ (((((uintmax_t) 0x5233U << 28 | 0x4bab403U)
+ << 28 | 0x2fa1b14U)
+ << 28 | 0xde63162U)
+ << 28 | 0x4a3c377U),
+ UINTMAX_MAX / 1607)
+P (2, 58,
+ (((((uintmax_t) 0x0e51U << 28 | 0xc7ad43fU)
+ << 28 | 0x016e93fU)
+ << 28 | 0x7b9fe68U)
+ << 28 | 0xb0ecbf9U),
+ UINTMAX_MAX / 1609)
+P (4, 56,
+ (((((uintmax_t) 0x00a2U << 28 | 0x84ffd75U)
+ << 28 | 0xec00a28U)
+ << 28 | 0x4ffd75eU)
+ << 28 | 0xc00a285U),
+ UINTMAX_MAX / 1613)
+P (6, 74,
+ (((((uintmax_t) 0xe72cU << 28 | 0xbfa4ebeU)
+ << 28 | 0xb20bb37U)
+ << 28 | 0x803cb80U)
+ << 28 | 0xdea2ddbU),
+ UINTMAX_MAX / 1619)
+P (2, 76,
+ (((((uintmax_t) 0x22beU << 28 | 0x75d04e5U)
+ << 28 | 0x4f6ff86U)
+ << 28 | 0xb63f7c9U)
+ << 28 | 0xac4c6fdU),
+ UINTMAX_MAX / 1621)
+P (6, 72,
+ (((((uintmax_t) 0x84f4U << 28 | 0xd419cdfU)
+ << 28 | 0x6dfbe8bU)
+ << 28 | 0x6851d1bU)
+ << 28 | 0xd99b9d3U),
+ UINTMAX_MAX / 1627)
+P (10, 72,
+ (((((uintmax_t) 0xe83aU << 28 | 0xccdcd04U)
+ << 28 | 0xd90f7b6U)
+ << 28 | 0x2fda77cU)
+ << 28 | 0xa343b6dU),
+ UINTMAX_MAX / 1637)
+P (20, 64,
+ (((((uintmax_t) 0x9e34U << 28 | 0x383c8ffU)
+ << 28 | 0xd872f1fU)
+ << 28 | 0x0dc009eU)
+ << 28 | 0x34383c9U),
+ UINTMAX_MAX / 1657)
+P (6, 60,
+ (((((uintmax_t) 0x2e7dU << 28 | 0x4e5ad2eU)
+ << 28 | 0x55e5d49U)
+ << 28 | 0x6dc21ddU)
+ << 28 | 0xd35b97fU),
+ UINTMAX_MAX / 1663)
+P (4, 66,
+ (((((uintmax_t) 0xe596U << 28 | 0x098573aU)
+ << 28 | 0x33e80b0U)
+ << 28 | 0xe96ce17U)
+ << 28 | 0x090f82bU),
+ UINTMAX_MAX / 1667)
+P (2, 72,
+ (((((uintmax_t) 0x7181U << 28 | 0x4dc42e0U)
+ << 28 | 0x3fceeaaU)
+ << 28 | 0xdf05acdU)
+ << 28 | 0xd7d024dU),
+ UINTMAX_MAX / 1669)
+P (24, 54,
+ (((((uintmax_t) 0xa4abU << 28 | 0x2bb32f5U)
+ << 28 | 0x43975cbU)
+ << 28 | 0x1381967U)
+ << 28 | 0x46eafb5U),
+ UINTMAX_MAX / 1693)
+P (4, 56,
+ (((((uintmax_t) 0xa2ecU << 28 | 0x3cf1f87U)
+ << 28 | 0x5102434U)
+ << 28 | 0x7f52373U)
+ << 28 | 0x6755d61U),
+ UINTMAX_MAX / 1697)
+P (2, 60,
+ (((((uintmax_t) 0x6ff3U << 28 | 0xf223422U)
+ << 28 | 0x5ab51d1U)
+ << 28 | 0x4a48a05U)
+ << 28 | 0x1f7dd0bU),
+ UINTMAX_MAX / 1699)
+P (10, 68,
+ (((((uintmax_t) 0x6c00U << 28 | 0x9963e9dU)
+ << 28 | 0x48f3447U)
+ << 28 | 0x4d71b1cU)
+ << 28 | 0xe914d25U),
+ UINTMAX_MAX / 1709)
+P (12, 62,
+ (((((uintmax_t) 0x894cU << 28 | 0x02f99a8U)
+ << 28 | 0xd502d38U)
+ << 28 | 0x6063f5eU)
+ << 28 | 0x28c1f89U),
+ UINTMAX_MAX / 1721)
+P (2, 64,
+ (((((uintmax_t) 0xc8e0U << 28 | 0xa6684d4U)
+ << 28 | 0x2b6281dU)
+ << 28 | 0xb7325e3U)
+ << 28 | 0x2d04e73U),
+ UINTMAX_MAX / 1723)
+P (10, 56,
+ (((((uintmax_t) 0xf8c2U << 28 | 0xfdc8c0aU)
+ << 28 | 0x0b85afeU)
+ << 28 | 0xf748d38U)
+ << 28 | 0x93b880dU),
+ UINTMAX_MAX / 1733)
+P (8, 60,
+ (((((uintmax_t) 0xd0a7U << 28 | 0x0a25594U)
+ << 28 | 0x123bb2fU)
+ << 28 | 0x3351506U)
+ << 28 | 0xe935605U),
+ UINTMAX_MAX / 1741)
+P (6, 64,
+ (((((uintmax_t) 0xdb5dU << 28 | 0xa31878bU)
+ << 28 | 0xf158a7aU)
+ << 28 | 0x3637fa2U)
+ << 28 | 0x376415bU),
+ UINTMAX_MAX / 1747)
+P (6, 70,
+ (((((uintmax_t) 0x75b4U << 28 | 0x5a8abbcU)
+ << 28 | 0xd2e004aU)
+ << 28 | 0xc525d2bU)
+ << 28 | 0xaa21969U),
+ UINTMAX_MAX / 1753)
+P (6, 72,
+ (((((uintmax_t) 0x7e53U << 28 | 0x89d2e22U)
+ << 28 | 0xa34af3aU)
+ << 28 | 0x11c16b4U)
+ << 28 | 0x2cd351fU),
+ UINTMAX_MAX / 1759)
+P (18, 70,
+ (((((uintmax_t) 0xeaf7U << 28 | 0x801270aU)
+ << 28 | 0x843ff6cU)
+ << 28 | 0x7abde00U)
+ << 28 | 0x49c2a11U),
+ UINTMAX_MAX / 1777)
+P (6, 78,
+ (((((uintmax_t) 0x1ad9U << 28 | 0x60a0cecU)
+ << 28 | 0x0ae9754U)
+ << 28 | 0xdad0303U)
+ << 28 | 0xe069ac7U),
+ UINTMAX_MAX / 1783)
+P (4, 80,
+ (((((uintmax_t) 0x082aU << 28 | 0x676e737U)
+ << 28 | 0x70be3ebU)
+ << 28 | 0xf1ac9fdU)
+ << 28 | 0xfe91433U),
+ UINTMAX_MAX / 1787)
+P (2, 82,
+ (((((uintmax_t) 0x50b4U << 28 | 0xdfcda14U)
+ << 28 | 0x51d9efaU)
+ << 28 | 0xfdda823U)
+ << 28 | 0x7cec655U),
+ UINTMAX_MAX / 1789)
+P (12, 72,
+ (((((uintmax_t) 0x1ffbU << 28 | 0x738ffdbU)
+ << 28 | 0x9c7fedcU)
+ << 28 | 0xe3ff6e7U)
+ << 28 | 0x1ffb739U),
+ UINTMAX_MAX / 1801)
+P (10, 66,
+ (((((uintmax_t) 0xa660U << 28 | 0xf8ca6cdU)
+ << 28 | 0x88f9ebeU)
+ << 28 | 0xd5737d6U)
+ << 28 | 0x286db1bU),
+ UINTMAX_MAX / 1811)
+P (12, 56,
+ (((((uintmax_t) 0xed52U << 28 | 0xb6467eaU)
+ << 28 | 0xa7abbe4U)
+ << 28 | 0x79e431fU)
+ << 28 | 0xe08b4dfU),
+ UINTMAX_MAX / 1823)
+P (8, 58,
+ (((((uintmax_t) 0xdaf2U << 28 | 0xff4d09aU)
+ << 28 | 0x5ae119dU)
+ << 28 | 0xd9b0dd7U)
+ << 28 | 0x742f897U),
+ UINTMAX_MAX / 1831)
+P (16, 54,
+ (((((uintmax_t) 0x6054U << 28 | 0x454d33bU)
+ << 28 | 0x2efc88fU)
+ << 28 | 0x09d7402U)
+ << 28 | 0xc5a5e87U),
+ UINTMAX_MAX / 1847)
+P (14, 46,
+ (((((uintmax_t) 0xf545U << 28 | 0x31625b1U)
+ << 28 | 0x0a51292U)
+ << 28 | 0x16d5c4dU)
+ << 28 | 0x958738dU),
+ UINTMAX_MAX / 1861)
+P (6, 46,
+ (((((uintmax_t) 0x6df8U << 28 | 0x0c1100aU)
+ << 28 | 0xf82f2b3U)
+ << 28 | 0x139ba11U)
+ << 28 | 0xd34ca63U),
+ UINTMAX_MAX / 1867)
+P (4, 60,
+ (((((uintmax_t) 0xaf8bU << 28 | 0xf8e2952U)
+ << 28 | 0x3b61d47U)
+ << 28 | 0xd54f7edU)
+ << 28 | 0x644afafU),
+ UINTMAX_MAX / 1871)
+P (2, 60,
+ (((((uintmax_t) 0x4d5cU << 28 | 0x4227171U)
+ << 28 | 0x9491f92U)
+ << 28 | 0xa81d85cU)
+ << 28 | 0xf11a1b1U),
+ UINTMAX_MAX / 1873)
+P (4, 72,
+ (((((uintmax_t) 0xf78bU << 28 | 0x4082eeaU)
+ << 28 | 0xdc21475U)
+ << 28 | 0x4b26533U)
+ << 28 | 0x253bdfdU),
+ UINTMAX_MAX / 1877)
+P (2, 72,
+ (((((uintmax_t) 0xf354U << 28 | 0x558f76aU)
+ << 28 | 0xad92bbbU)
+ << 28 | 0xe0efc98U)
+ << 28 | 0x0bfd467U),
+ UINTMAX_MAX / 1879)
+P (10, 84,
+ (((((uintmax_t) 0x0ab4U << 28 | 0xc91d231U)
+ << 28 | 0x99d11c0U)
+ << 28 | 0xd8d594fU)
+ << 28 | 0x024dca1U),
+ UINTMAX_MAX / 1889)
+P (12, 78,
+ (((((uintmax_t) 0x1b56U << 28 | 0x52256feU)
+ << 28 | 0x84c7d82U)
+ << 28 | 0x38d43bcU)
+ << 28 | 0xaac1a65U),
+ UINTMAX_MAX / 1901)
+P (6, 80,
+ (((((uintmax_t) 0xaca2U << 28 | 0xb39dbc1U)
+ << 28 | 0x2cb3e27U)
+ << 28 | 0x779c1faU)
+ << 28 | 0xe6175bbU),
+ UINTMAX_MAX / 1907)
+P (6, 80,
+ (((((uintmax_t) 0x3856U << 28 | 0xb755c78U)
+ << 28 | 0x7068ea7U)
+ << 28 | 0x46ca9afU)
+ << 28 | 0x708b2c9U),
+ UINTMAX_MAX / 1913)
+P (18, 66,
+ (((((uintmax_t) 0x052bU << 28 | 0x9de5385U)
+ << 28 | 0x8076c93U)
+ << 28 | 0xf3cd9f3U)
+ << 28 | 0x89be823U),
+ UINTMAX_MAX / 1931)
+P (2, 66,
+ (((((uintmax_t) 0x820dU << 28 | 0x822f698U)
+ << 28 | 0xd4f545cU)
+ << 28 | 0xb4a4c04U)
+ << 28 | 0xc489345U),
+ UINTMAX_MAX / 1933)
+P (16, 54,
+ (((((uintmax_t) 0xcd09U << 28 | 0x536828fU)
+ << 28 | 0xb23dbbfU)
+ << 28 | 0x6047743U)
+ << 28 | 0xe85b6b5U),
+ UINTMAX_MAX / 1949)
+P (2, 60,
+ (((((uintmax_t) 0x8486U << 28 | 0xe386c1eU)
+ << 28 | 0xf778961U)
+ << 28 | 0xc147831U)
+ << 28 | 0x563545fU),
+ UINTMAX_MAX / 1951)
+P (22, 44,
+ (((((uintmax_t) 0xec68U << 28 | 0x5200c74U)
+ << 28 | 0xc6c78edU)
+ << 28 | 0xb47c0aeU)
+ << 28 | 0x62dee9dU),
+ UINTMAX_MAX / 1973)
+P (6, 48,
+ (((((uintmax_t) 0xd8acU << 28 | 0xd298624U)
+ << 28 | 0xff1830aU)
+ << 28 | 0x3824386U)
+ << 28 | 0x673a573U),
+ UINTMAX_MAX / 1979)
+P (8, 42,
+ (((((uintmax_t) 0x03ddU << 28 | 0x78b87ecU)
+ << 28 | 0x6aad6a4U)
+ << 28 | 0xa77d19eU)
+ << 28 | 0x575a0ebU),
+ UINTMAX_MAX / 1987)
+P (6, 46,
+ (((((uintmax_t) 0x8950U << 28 | 0x062a636U)
+ << 28 | 0xb8325a2U)
+ << 28 | 0xbee045eU)
+ << 28 | 0x066c279U),
+ UINTMAX_MAX / 1993)
+P (4, 56,
+ (((((uintmax_t) 0xa9daU << 28 | 0xd301275U)
+ << 28 | 0xae369c2U)
+ << 28 | 0x3618de8U)
+ << 28 | 0xab43d05U),
+ UINTMAX_MAX / 1997)
+P (2, 64,
+ (((((uintmax_t) 0xfa3cU << 28 | 0xb3cd496U)
+ << 28 | 0x174ec26U)
+ << 28 | 0x6b51521U)
+ << 28 | 0x6cb9f2fU),
+ UINTMAX_MAX / 1999)
+P (4, 66,
+ (((((uintmax_t) 0x5c05U << 28 | 0x9fa1eedU)
+ << 28 | 0xfaa1ce2U)
+ << 28 | 0x79edd9eU)
+ << 28 | 0x9c2e85bU),
+ UINTMAX_MAX / 2003)
+P (8, 70,
+ (((((uintmax_t) 0x8e52U << 28 | 0x3c5712bU)
+ << 28 | 0x68c48d0U)
+ << 28 | 0xc591c22U)
+ << 28 | 0x1dc9c53U),
+ UINTMAX_MAX / 2011)
+P (6, 66,
+ (((((uintmax_t) 0x8de5U << 28 | 0xdaaf67bU)
+ << 28 | 0x1d10a06U)
+ << 28 | 0xda8ee9cU)
+ << 28 | 0x9ee7c21U),
+ UINTMAX_MAX / 2017)
+P (10, 60,
+ (((((uintmax_t) 0xec2bU << 28 | 0xf35ed8fU)
+ << 28 | 0x98f179dU)
+ << 28 | 0xfebcaf4U)
+ << 28 | 0xc27e8c3U),
+ UINTMAX_MAX / 2027)
+P (2, 60,
+ (((((uintmax_t) 0xe8c8U << 28 | 0xdd0cfedU)
+ << 28 | 0xd4d9849U)
+ << 28 | 0xaeff9f1U)
+ << 28 | 0x9dd6de5U),
+ UINTMAX_MAX / 2029)
+P (10, 60,
+ (((((uintmax_t) 0x65f2U << 28 | 0xb107280U)
+ << 28 | 0xd0eb086U)
+ << 28 | 0x976a57aU)
+ << 28 | 0x296e9c7U),
+ UINTMAX_MAX / 2039)
+P (14, 58,
+ (((((uintmax_t) 0x44b5U << 28 | 0x0ed6b9cU)
+ << 28 | 0xbe093a3U)
+ << 28 | 0xb9abf48U)
+ << 28 | 0x72b84cdU),
+ UINTMAX_MAX / 2053)
+P (10, 50,
+ (((((uintmax_t) 0x9e96U << 28 | 0xa5899dfU)
+ << 28 | 0x7cf5b34U)
+ << 28 | 0xfca6483U)
+ << 28 | 0x895e6efU),
+ UINTMAX_MAX / 2063)
+P (6, 60,
+ (((((uintmax_t) 0x49beU << 28 | 0x6c24212U)
+ << 28 | 0x8f47e34U)
+ << 28 | 0xb5a3339U)
+ << 28 | 0x88f873dU),
+ UINTMAX_MAX / 2069)
+P (12, 50,
+ (((((uintmax_t) 0xd1fdU << 28 | 0xc922526U)
+ << 28 | 0xc0275d9U)
+ << 28 | 0xdd4f19bU)
+ << 28 | 0x5f17be1U),
+ UINTMAX_MAX / 2081)
+P (2, 54,
+ (((((uintmax_t) 0xb8d7U << 28 | 0x51f95d0U)
+ << 28 | 0x8f8bfb9U)
+ << 28 | 0x35b507fU)
+ << 28 | 0xd0ce78bU),
+ UINTMAX_MAX / 2083)
+P (4, 54,
+ (((((uintmax_t) 0x971fU << 28 | 0x47835f8U)
+ << 28 | 0xe2aeeb4U)
+ << 28 | 0x50f5540U)
+ << 28 | 0x660e797U),
+ UINTMAX_MAX / 2087)
+P (2, 54,
+ (((((uintmax_t) 0x418fU << 28 | 0xfe0a0c7U)
+ << 28 | 0xff05063U)
+ << 28 | 0xff82831U)
+ << 28 | 0xffc1419U),
+ UINTMAX_MAX / 2089)
+P (10, 54,
+ (((((uintmax_t) 0xd06fU << 28 | 0x3ae8760U)
+ << 28 | 0xf5e0889U)
+ << 28 | 0x92f718cU)
+ << 28 | 0x22a32fbU),
+ UINTMAX_MAX / 2099)
+P (12, 50,
+ (((((uintmax_t) 0x16adU << 28 | 0x6a5a779U)
+ << 28 | 0x25f515fU)
+ << 28 | 0x3253ad0U)
+ << 28 | 0xd37e7bfU),
+ UINTMAX_MAX / 2111)
+P (2, 66,
+ (((((uintmax_t) 0xfe0fU << 28 | 0xc007c0fU)
+ << 28 | 0xfe0fc00U)
+ << 28 | 0x7c0ffe0U)
+ << 28 | 0xfc007c1U),
+ UINTMAX_MAX / 2113)
+P (16, 74,
+ (((((uintmax_t) 0x9763U << 28 | 0x3395b43U)
+ << 28 | 0xf020b4dU)
+ << 28 | 0x8ebadc0U)
+ << 28 | 0xc0640b1U),
+ UINTMAX_MAX / 2129)
+P (2, 76,
+ (((((uintmax_t) 0x9a20U << 28 | 0xea7f195U)
+ << 28 | 0x90471e2U)
+ << 28 | 0x729af83U)
+ << 28 | 0x1037bdbU),
+ UINTMAX_MAX / 2131)
+P (6, 76,
+ (((((uintmax_t) 0x7285U << 28 | 0xee07e80U)
+ << 28 | 0xa8ab8b8U)
+ << 28 | 0xf64bf30U)
+ << 28 | 0xfeebfe9U),
+ UINTMAX_MAX / 2137)
+P (4, 80,
+ (((((uintmax_t) 0x3dd1U << 28 | 0x5e1a10fU)
+ << 28 | 0xa9e8cdaU)
+ << 28 | 0x93124b5U)
+ << 28 | 0x44c0bf5U),
+ UINTMAX_MAX / 2141)
+P (2, 94,
+ (((((uintmax_t) 0x4f14U << 28 | 0xe7bff85U)
+ << 28 | 0xac9e29cU)
+ << 28 | 0xf7ff0b5U)
+ << 28 | 0x93c539fU),
+ UINTMAX_MAX / 2143)
+P (10, 86,
+ (((((uintmax_t) 0x12e7U << 28 | 0xdccdf10U)
+ << 28 | 0x4a322d6U)
+ << 28 | 0xbd8861fU)
+ << 28 | 0xa0e07d9U),
+ UINTMAX_MAX / 2153)
+P (8, 82,
+ (((((uintmax_t) 0xd7b8U << 28 | 0xebfac9aU)
+ << 28 | 0x00b5f5cU)
+ << 28 | 0xfe75c0bU)
+ << 28 | 0xd8ab891U),
+ UINTMAX_MAX / 2161)
+P (18, 72,
+ (((((uintmax_t) 0xae1cU << 28 | 0xe6bd9efU)
+ << 28 | 0x512ea43U)
+ << 28 | 0xe808757U)
+ << 28 | 0xc2e862bU),
+ UINTMAX_MAX / 2179)
+P (24, 64,
+ (((((uintmax_t) 0x459bU << 28 | 0x5dc70f3U)
+ << 28 | 0x90e8690U)
+ << 28 | 0xcaa96d5U)
+ << 28 | 0x95c9d93U),
+ UINTMAX_MAX / 2203)
+P (4, 62,
+ (((((uintmax_t) 0x4ec2U << 28 | 0xa38d65bU)
+ << 28 | 0xa2bd88fU)
+ << 28 | 0xd550625U)
+ << 28 | 0xd07135fU),
+ UINTMAX_MAX / 2207)
+P (6, 60,
+ (((((uintmax_t) 0x525dU << 28 | 0x3cf6a14U)
+ << 28 | 0x20da676U)
+ << 28 | 0xb010a86U)
+ << 28 | 0xe209f2dU),
+ UINTMAX_MAX / 2213)
+P (8, 60,
+ (((((uintmax_t) 0x716bU << 28 | 0x4f6a9e5U)
+ << 28 | 0xf3522ecU)
+ << 28 | 0xc042644U)
+ << 28 | 0x7769b25U),
+ UINTMAX_MAX / 2221)
+P (16, 50,
+ (((((uintmax_t) 0x48abU << 28 | 0x336212fU)
+ << 28 | 0xf32ece3U)
+ << 28 | 0x81339caU)
+ << 28 | 0xabe3295U),
+ UINTMAX_MAX / 2237)
+P (2, 54,
+ (((((uintmax_t) 0xbde9U << 28 | 0xd1944b7U)
+ << 28 | 0x656aad1U)
+ << 28 | 0xb190a2dU)
+ << 28 | 0x0c7673fU),
+ UINTMAX_MAX / 2239)
+P (4, 54,
+ (((((uintmax_t) 0xb595U << 28 | 0xdb3fccdU)
+ << 28 | 0xe54afc3U)
+ << 28 | 0xbce3cf2U)
+ << 28 | 0x6b0e7ebU),
+ UINTMAX_MAX / 2243)
+P (8, 58,
+ (((((uintmax_t) 0x8a10U << 28 | 0x9aab45fU)
+ << 28 | 0x137285fU)
+ << 28 | 0x87e76f5U)
+ << 28 | 0x6c61ce3U),
+ UINTMAX_MAX / 2251)
+P (16, 44,
+ (((((uintmax_t) 0x2e69U << 28 | 0x78b763bU)
+ << 28 | 0x65f88c0U)
+ << 28 | 0x6c6857aU)
+ << 28 | 0x124b353U),
+ UINTMAX_MAX / 2267)
+P (2, 64,
+ (((((uintmax_t) 0x7e40U << 28 | 0x4f6dc75U)
+ << 28 | 0xca11d38U)
+ << 28 | 0xc040fcbU)
+ << 28 | 0xa630f75U),
+ UINTMAX_MAX / 2269)
+P (4, 66,
+ (((((uintmax_t) 0xa706U << 28 | 0x6b72173U)
+ << 28 | 0x37865d0U)
+ << 28 | 0x78bc4fbU)
+ << 28 | 0xd533b21U),
+ UINTMAX_MAX / 2273)
+P (8, 60,
+ (((((uintmax_t) 0x1165U << 28 | 0x5853800U)
+ << 28 | 0xe5d99deU)
+ << 28 | 0x8e15c5dU)
+ << 28 | 0xd354f59U),
+ UINTMAX_MAX / 2281)
+P (6, 60,
+ (((((uintmax_t) 0xad0dU << 28 | 0xfdfc31bU)
+ << 28 | 0x33610caU)
+ << 28 | 0x61d53d7U)
+ << 28 | 0x414260fU),
+ UINTMAX_MAX / 2287)
+P (6, 58,
+ (((((uintmax_t) 0x65b5U << 28 | 0x32cc4f0U)
+ << 28 | 0xb46abb5U)
+ << 28 | 0x6bf5ba8U)
+ << 28 | 0xeae635dU),
+ UINTMAX_MAX / 2293)
+P (4, 60,
+ (((((uintmax_t) 0xcdbcU << 28 | 0x7622fecU)
+ << 28 | 0x6285844U)
+ << 28 | 0xa72cb0fU)
+ << 28 | 0xb6e3949U),
+ UINTMAX_MAX / 2297)
+P (12, 62,
+ (((((uintmax_t) 0x37c4U << 28 | 0x92cae49U)
+ << 28 | 0xd6fa587U)
+ << 28 | 0x9839a71U)
+ << 28 | 0x4f45bcdU),
+ UINTMAX_MAX / 2309)
+P (2, 66,
+ (((((uintmax_t) 0xc031U << 28 | 0xa083283U)
+ << 28 | 0x60ed802U)
+ << 28 | 0xa8994fdU)
+ << 28 | 0xe5314b7U),
+ UINTMAX_MAX / 2311)
+P (22, 48,
+ (((((uintmax_t) 0xc841U << 28 | 0xd685a6aU)
+ << 28 | 0xe081eb9U)
+ << 28 | 0x71920cfU)
+ << 28 | 0x2b90135U),
+ UINTMAX_MAX / 2333)
+P (6, 44,
+ (((((uintmax_t) 0xc4c9U << 28 | 0xd2b0364U)
+ << 28 | 0x9549a8aU)
+ << 28 | 0x8fd0b7dU)
+ << 28 | 0xf9a6e8bU),
+ UINTMAX_MAX / 2339)
+P (2, 48,
+ (((((uintmax_t) 0xe3c9U << 28 | 0x5290213U)
+ << 28 | 0xe7112b3U)
+ << 28 | 0x1f9a84cU)
+ << 28 | 0x1c6eaadU),
+ UINTMAX_MAX / 2341)
+P (6, 46,
+ (((((uintmax_t) 0xf02fU << 28 | 0x1ede4bbU)
+ << 28 | 0x2c64c92U)
+ << 28 | 0x293b028U)
+ << 28 | 0x23c6d83U),
+ UINTMAX_MAX / 2347)
+P (4, 48,
+ (((((uintmax_t) 0x83f9U << 28 | 0x7773bffU)
+ << 28 | 0x907f2eeU)
+ << 28 | 0xe77ff20U)
+ << 28 | 0xfe5ddcfU),
+ UINTMAX_MAX / 2351)
+P (6, 54,
+ (((((uintmax_t) 0xd472U << 28 | 0x42b02b7U)
+ << 28 | 0x1ef460eU)
+ << 28 | 0x1ea0f6cU)
+ << 28 | 0x496c11dU),
+ UINTMAX_MAX / 2357)
+P (14, 46,
+ (((((uintmax_t) 0xd905U << 28 | 0xb8f4727U)
+ << 28 | 0x318f0fdU)
+ << 28 | 0xf2d3d6fU)
+ << 28 | 0x88ccb6bU),
+ UINTMAX_MAX / 2371)
+P (6, 46,
+ (((((uintmax_t) 0xf2c0U << 28 | 0xc7e3914U)
+ << 28 | 0x920a1faU)
+ << 28 | 0x9d74a34U)
+ << 28 | 0x57738f9U),
+ UINTMAX_MAX / 2377)
+P (4, 56,
+ (((((uintmax_t) 0x6c7cU << 28 | 0x4a67008U)
+ << 28 | 0x99f72efU)
+ << 28 | 0xc3ca3dbU)
+ << 28 | 0x71a5785U),
+ UINTMAX_MAX / 2381)
+P (2, 58,
+ (((((uintmax_t) 0x7e55U << 28 | 0xba2c0b9U)
+ << 28 | 0xa289b8eU)
+ << 28 | 0x2071718U)
+ << 28 | 0xd0d6dafU),
+ UINTMAX_MAX / 2383)
+P (6, 58,
+ (((((uintmax_t) 0xbf46U << 28 | 0xd4d0be4U)
+ << 28 | 0xff091bcU)
+ << 28 | 0x0fdbfebU)
+ << 28 | 0x6cfabfdU),
+ UINTMAX_MAX / 2389)
+P (4, 66,
+ (((((uintmax_t) 0x1908U << 28 | 0x738977bU)
+ << 28 | 0x58af71eU)
+ << 28 | 0xeab613eU)
+ << 28 | 0x5e5aee9U),
+ UINTMAX_MAX / 2393)
+P (6, 68,
+ (((((uintmax_t) 0x6a48U << 28 | 0xc6e8d7fU)
+ << 28 | 0xbbb472dU)
+ << 28 | 0x2388e90U)
+ << 28 | 0xe9e929fU),
+ UINTMAX_MAX / 2399)
+P (12, 62,
+ (((((uintmax_t) 0x9f7bU << 28 | 0x7cc2f24U)
+ << 28 | 0xd82eb81U)
+ << 28 | 0xdbafba5U)
+ << 28 | 0x88ddb43U),
+ UINTMAX_MAX / 2411)
+P (6, 60,
+ (((((uintmax_t) 0x57ceU << 28 | 0x01e8101U)
+ << 28 | 0x96b8152U)
+ << 28 | 0xeebc51cU)
+ << 28 | 0x4799791U),
+ UINTMAX_MAX / 2417)
+P (6, 80,
+ (((((uintmax_t) 0x22c2U << 28 | 0x9d6cb7dU)
+ << 28 | 0x695651cU)
+ << 28 | 0x6bc4693U)
+ << 28 | 0xb45a047U),
+ UINTMAX_MAX / 2423)
+P (14, 84,
+ (((((uintmax_t) 0x366aU << 28 | 0x190050aU)
+ << 28 | 0xd1e2606U)
+ << 28 | 0xeee0974U)
+ << 28 | 0x498874dU),
+ UINTMAX_MAX / 2437)
+P (4, 90,
+ (((((uintmax_t) 0x7708U << 28 | 0x7eb0665U)
+ << 28 | 0xba929d8U)
+ << 28 | 0x5b7377aU)
+ << 28 | 0x9953cb9U),
+ UINTMAX_MAX / 2441)
+P (6, 92,
+ (((((uintmax_t) 0x8f53U << 28 | 0x96f6b06U)
+ << 28 | 0x2c2614bU)
+ << 28 | 0x6df412dU)
+ << 28 | 0x4caf56fU),
+ UINTMAX_MAX / 2447)
+P (12, 84,
+ (((((uintmax_t) 0x0c2eU << 28 | 0x394250fU)
+ << 28 | 0xedad56bU)
+ << 28 | 0x8afbbb4U)
+ << 28 | 0xa053493U),
+ UINTMAX_MAX / 2459)
+P (8, 82,
+ (((((uintmax_t) 0x78afU << 28 | 0x29d1b7fU)
+ << 28 | 0xbd965ccU)
+ << 28 | 0x5299c96U)
+ << 28 | 0xac7720bU),
+ UINTMAX_MAX / 2467)
+P (6, 78,
+ (((((uintmax_t) 0x1287U << 28 | 0x9bcb69bU)
+ << 28 | 0x11e89adU)
+ << 28 | 0xce84b5cU)
+ << 28 | 0x710aa99U),
+ UINTMAX_MAX / 2473)
+P (4, 80,
+ (((((uintmax_t) 0x92c2U << 28 | 0x17c54bfU)
+ << 28 | 0x67de19dU)
+ << 28 | 0x673f5aaU)
+ << 28 | 0x3804225U),
+ UINTMAX_MAX / 2477)
+P (26, 76,
+ (((((uintmax_t) 0xd46eU << 28 | 0x0ce30e3U)
+ << 28 | 0x76f2ce6U)
+ << 28 | 0x541268eU)
+ << 28 | 0xfbce7f7U),
+ UINTMAX_MAX / 2503)
+P (18, 70,
+ (((((uintmax_t) 0xa49bU << 28 | 0x91ec4ccU)
+ << 28 | 0x5004dfcU)
+ << 28 | 0xf41e76cU)
+ << 28 | 0xf5be669U),
+ UINTMAX_MAX / 2521)
+P (10, 62,
+ (((((uintmax_t) 0x6098U << 28 | 0x1f8eb77U)
+ << 28 | 0xa7cd05cU)
+ << 28 | 0x3eb5dc3U)
+ << 28 | 0x1c383cbU),
+ UINTMAX_MAX / 2531)
+P (8, 70,
+ (((((uintmax_t) 0x62e9U << 28 | 0x505bf44U)
+ << 28 | 0xdd6a930U)
+ << 28 | 0x1832d11U)
+ << 28 | 0xd8ad6c3U),
+ UINTMAX_MAX / 2539)
+P (4, 74,
+ (((((uintmax_t) 0xb3cbU << 28 | 0x3fecabfU)
+ << 28 | 0x119df2eU)
+ << 28 | 0x9c0942fU)
+ << 28 | 0x1ce450fU),
+ UINTMAX_MAX / 2543)
+P (6, 72,
+ (((((uintmax_t) 0xef3aU << 28 | 0x59c92a1U)
+ << 28 | 0x4b05b97U)
+ << 28 | 0xf3f2be3U)
+ << 28 | 0x7a39a5dU),
+ UINTMAX_MAX / 2549)
+P (2, 82,
+ (((((uintmax_t) 0xe69cU << 28 | 0x5983c36U)
+ << 28 | 0x30c57e8U)
+ << 28 | 0xb7d8a96U)
+ << 28 | 0x54187c7U),
+ UINTMAX_MAX / 2551)
+P (6, 90,
+ (((((uintmax_t) 0x437aU << 28 | 0xa4cb09bU)
+ << 28 | 0x61d08b5U)
+ << 28 | 0xd024d7dU)
+ << 28 | 0xa5b1b55U),
+ UINTMAX_MAX / 2557)
+P (22, 78,
+ (((((uintmax_t) 0x1b65U << 28 | 0x8bdca98U)
+ << 28 | 0xaabb9b8U)
+ << 28 | 0xba9d6e7U)
+ << 28 | 0xae3501bU),
+ UINTMAX_MAX / 2579)
+P (12, 68,
+ (((((uintmax_t) 0x3ea4U << 28 | 0x3624f3dU)
+ << 28 | 0x8dfb0f5U)
+ << 28 | 0x0865f71U)
+ << 28 | 0xb90f1dfU),
+ UINTMAX_MAX / 2591)
+P (2, 70,
+ (((((uintmax_t) 0x2d05U << 28 | 0x08fbf3cU)
+ << 28 | 0x1ffcd73U)
+ << 28 | 0x9c16828U)
+ << 28 | 0x47df9e1U),
+ UINTMAX_MAX / 2593)
+P (16, 62,
+ (((((uintmax_t) 0xc716U << 28 | 0xdcc634cU)
+ << 28 | 0xa218ec4U)
+ << 28 | 0x70a4d84U)
+ << 28 | 0x2b90ed1U),
+ UINTMAX_MAX / 2609)
+P (8, 60,
+ (((((uintmax_t) 0xe30bU << 28 | 0x71f669dU)
+ << 28 | 0x7e49c1fU)
+ << 28 | 0xb1be116U)
+ << 28 | 0x98cc409U),
+ UINTMAX_MAX / 2617)
+P (4, 62,
+ (((((uintmax_t) 0xa624U << 28 | 0x238d871U)
+ << 28 | 0x4cde4d8U)
+ << 28 | 0xd5512a7U)
+ << 28 | 0xcd35d15U),
+ UINTMAX_MAX / 2621)
+P (12, 54,
+ (((((uintmax_t) 0x6488U << 28 | 0x81e55c1U)
+ << 28 | 0x30e7ca5U)
+ << 28 | 0x4968217U)
+ << 28 | 0x23e07f9U),
+ UINTMAX_MAX / 2633)
+P (14, 42,
+ (((((uintmax_t) 0x8513U << 28 | 0xd3830beU)
+ << 28 | 0x54ea0bcU)
+ << 28 | 0xc8c6d7aU)
+ << 28 | 0xbaa8167U),
+ UINTMAX_MAX / 2647)
+P (10, 36,
+ (((((uintmax_t) 0x49b5U << 28 | 0x0a4f32fU)
+ << 28 | 0x800c552U)
+ << 28 | 0xc396c95U)
+ << 28 | 0xeb619a1U),
+ UINTMAX_MAX / 2657)
+P (2, 40,
+ (((((uintmax_t) 0xa1f0U << 28 | 0x049f0c9U)
+ << 28 | 0xcbd166eU)
+ << 28 | 0xb7e3808U)
+ << 28 | 0x78ec74bU),
+ UINTMAX_MAX / 2659)
+P (4, 44,
+ (((((uintmax_t) 0x25f8U << 28 | 0xe2df380U)
+ << 28 | 0xb892e3dU)
+ << 28 | 0x5513b50U)
+ << 28 | 0x4537157U),
+ UINTMAX_MAX / 2663)
+P (8, 40,
+ (((((uintmax_t) 0x1654U << 28 | 0xeb02967U)
+ << 28 | 0x9b8e231U)
+ << 28 | 0x4391f88U)
+ << 28 | 0x62e948fU),
+ UINTMAX_MAX / 2671)
+P (6, 36,
+ (((((uintmax_t) 0x304aU << 28 | 0xf935d6eU)
+ << 28 | 0x11c97dcU)
+ << 28 | 0x0b17cfcU)
+ << 28 | 0xd81f5ddU),
+ UINTMAX_MAX / 2677)
+P (6, 36,
+ (((((uintmax_t) 0xef7eU << 28 | 0x3c1c9feU)
+ << 28 | 0xaa07d2fU)
+ << 28 | 0x6bea3ecU)
+ << 28 | 0x89044b3U),
+ UINTMAX_MAX / 2683)
+P (4, 42,
+ (((((uintmax_t) 0xd02cU << 28 | 0x34f8dabU)
+ << 28 | 0xf7ff3ceU)
+ << 28 | 0x13a0586U)
+ << 28 | 0x9f1b57fU),
+ UINTMAX_MAX / 2687)
+P (2, 42,
+ (((((uintmax_t) 0xca7fU << 28 | 0x00185f3U)
+ << 28 | 0x3e2ad75U)
+ << 28 | 0x93474e8U)
+ << 28 | 0xace3581U),
+ UINTMAX_MAX / 2689)
+P (4, 48,
+ (((((uintmax_t) 0x613fU << 28 | 0x67e6e76U)
+ << 28 | 0x10ebc07U)
+ << 28 | 0xfc32929U)
+ << 28 | 0x5a05e4dU),
+ UINTMAX_MAX / 2693)
+P (6, 50,
+ (((((uintmax_t) 0x91e1U << 28 | 0x1433fa4U)
+ << 28 | 0xf1ad7b0U)
+ << 28 | 0x5377cbaU)
+ << 28 | 0x4908d23U),
+ UINTMAX_MAX / 2699)
+P (8, 46,
+ (((((uintmax_t) 0x99c5U << 28 | 0x2d7ced2U)
+ << 28 | 0xe3e9ae7U)
+ << 28 | 0xb2131a6U)
+ << 28 | 0x28aa39bU),
+ UINTMAX_MAX / 2707)
+P (4, 56,
+ (((((uintmax_t) 0xe699U << 28 | 0x2a662c6U)
+ << 28 | 0x1d45f90U)
+ << 28 | 0x31dbed7U)
+ << 28 | 0xde01527U),
+ UINTMAX_MAX / 2711)
+P (2, 64,
+ (((((uintmax_t) 0x86efU << 28 | 0x7ca673aU)
+ << 28 | 0xf9ad876U)
+ << 28 | 0x844b1c6U)
+ << 28 | 0x70aa9a9U),
+ UINTMAX_MAX / 2713)
+P (6, 70,
+ (((((uintmax_t) 0xb29bU << 28 | 0x59ea585U)
+ << 28 | 0x098266aU)
+ << 28 | 0x03f4533U)
+ << 28 | 0xb08915fU),
+ UINTMAX_MAX / 2719)
+P (10, 62,
+ (((((uintmax_t) 0x2d67U << 28 | 0x181bc45U)
+ << 28 | 0x6ad8b1dU)
+ << 28 | 0xbca579dU)
+ << 28 | 0xb0a3999U),
+ UINTMAX_MAX / 2729)
+P (2, 66,
+ (((((uintmax_t) 0xffa0U << 28 | 0x02ffe80U)
+ << 28 | 0x0bffa00U)
+ << 28 | 0x2ffe800U)
+ << 28 | 0xbffa003U),
+ UINTMAX_MAX / 2731)
+P (10, 60,
+ (((((uintmax_t) 0xef00U << 28 | 0x778c303U)
+ << 28 | 0x1503a47U)
+ << 28 | 0x8ab1a3eU)
+ << 28 | 0x936139dU),
+ UINTMAX_MAX / 2741)
+P (8, 54,
+ (((((uintmax_t) 0xd453U << 28 | 0x113a63aU)
+ << 28 | 0x4bcdb66U)
+ << 28 | 0xe722bc4U)
+ << 28 | 0xc5cc095U),
+ UINTMAX_MAX / 2749)
+P (4, 66,
+ (((((uintmax_t) 0x01c4U << 28 | 0x4cfeca8U)
+ << 28 | 0x7f35a7aU)
+ << 28 | 0x8f63c71U)
+ << 28 | 0x7278541U),
+ UINTMAX_MAX / 2753)
+P (14, 66,
+ (((((uintmax_t) 0x3887U << 28 | 0x72a189cU)
+ << 28 | 0x2c09fdfU)
+ << 28 | 0x6eee24dU)
+ << 28 | 0x292bc2fU),
+ UINTMAX_MAX / 2767)
+P (10, 60,
+ (((((uintmax_t) 0x835dU << 28 | 0x625cbd2U)
+ << 28 | 0xa50339fU)
+ << 28 | 0xc20d172U)
+ << 28 | 0x37dd569U),
+ UINTMAX_MAX / 2777)
+P (12, 54,
+ (((((uintmax_t) 0x8052U << 28 | 0x3e3ba9bU)
+ << 28 | 0x7da8ccdU)
+ << 28 | 0xf993235U)
+ << 28 | 0x6bda2edU),
+ UINTMAX_MAX / 2789)
+P (2, 60,
+ (((((uintmax_t) 0xced6U << 28 | 0x1518ac7U)
+ << 28 | 0x0a2e697U)
+ << 28 | 0xb5e332eU)
+ << 28 | 0x80f68d7U),
+ UINTMAX_MAX / 2791)
+P (6, 60,
+ (((((uintmax_t) 0x42d0U << 28 | 0x7f67b31U)
+ << 28 | 0xe1cbd46U)
+ << 28 | 0xeee26fdU)
+ << 28 | 0x875e2e5U),
+ UINTMAX_MAX / 2797)
+P (4, 60,
+ (((((uintmax_t) 0xa787U << 28 | 0x5b7cc16U)
+ << 28 | 0x4cf4935U)
+ << 28 | 0x48a8e65U)
+ << 28 | 0x157a611U),
+ UINTMAX_MAX / 2801)
+P (2, 76,
+ (((((uintmax_t) 0x69abU << 28 | 0x6d816a6U)
+ << 28 | 0x6791ac2U)
+ << 28 | 0x88d03beU)
+ << 28 | 0x9b71e3bU),
+ UINTMAX_MAX / 2803)
+P (16, 68,
+ (((((uintmax_t) 0xace8U << 28 | 0x1dc954bU)
+ << 28 | 0xa58d081U)
+ << 28 | 0x51186dbU)
+ << 28 | 0x38937abU),
+ UINTMAX_MAX / 2819)
+P (14, 64,
+ (((((uintmax_t) 0x7c3fU << 28 | 0xfa377bbU)
+ << 28 | 0x52dd078U)
+ << 28 | 0x00b9108U)
+ << 28 | 0x95a45f1U),
+ UINTMAX_MAX / 2833)
+P (4, 66,
+ (((((uintmax_t) 0x1f0aU << 28 | 0x8ec0eccU)
+ << 28 | 0x79a36aeU)
+ << 28 | 0xe0b0241U)
+ << 28 | 0x82eec3dU),
+ UINTMAX_MAX / 2837)
+P (6, 66,
+ (((((uintmax_t) 0x609eU << 28 | 0x7b00a15U)
+ << 28 | 0xca83496U)
+ << 28 | 0x323eda1U)
+ << 28 | 0x73b5713U),
+ UINTMAX_MAX / 2843)
+P (8, 66,
+ (((((uintmax_t) 0x7362U << 28 | 0x52ca08cU)
+ << 28 | 0xcba690eU)
+ << 28 | 0xd0dbd03U)
+ << 28 | 0xae77c8bU),
+ UINTMAX_MAX / 2851)
+P (6, 70,
+ (((((uintmax_t) 0xa370U << 28 | 0x463ffa4U)
+ << 28 | 0x3eb91f7U)
+ << 28 | 0x3800b78U)
+ << 28 | 0x28dc119U),
+ UINTMAX_MAX / 2857)
+P (4, 78,
+ (((((uintmax_t) 0x4586U << 28 | 0x7cbbe80U)
+ << 28 | 0x502c61bU)
+ << 28 | 0x61715ecU)
+ << 28 | 0x22b7ca5U),
+ UINTMAX_MAX / 2861)
+P (18, 74,
+ (((((uintmax_t) 0x508fU << 28 | 0xb1c027dU)
+ << 28 | 0x607a5a8U)
+ << 28 | 0x533a991U)
+ << 28 | 0xead64bfU),
+ UINTMAX_MAX / 2879)
+P (8, 70,
+ (((((uintmax_t) 0xbc40U << 28 | 0xe8adccbU)
+ << 28 | 0xf2e057fU)
+ << 28 | 0x6c7290eU)
+ << 28 | 0x46c2e77U),
+ UINTMAX_MAX / 2887)
+P (10, 66,
+ (((((uintmax_t) 0x73d9U << 28 | 0x78cc4e1U)
+ << 28 | 0xdde3e63U)
+ << 28 | 0x25e8d90U)
+ << 28 | 0x7b01db1U),
+ UINTMAX_MAX / 2897)
+P (6, 66,
+ (((((uintmax_t) 0x1c21U << 28 | 0x8299f86U)
+ << 28 | 0xa86ec28U)
+ << 28 | 0x909f701U)
+ << 28 | 0x52a1067U),
+ UINTMAX_MAX / 2903)
+P (6, 62,
+ (((((uintmax_t) 0x5da2U << 28 | 0x8a842e1U)
+ << 28 | 0xd0a78eaU)
+ << 28 | 0x7077af0U)
+ << 28 | 0x997a0f5U),
+ UINTMAX_MAX / 2909)
+P (8, 82,
+ (((((uintmax_t) 0x21f6U << 28 | 0xb281b61U)
+ << 28 | 0xadae07eU)
+ << 28 | 0x605cad1U)
+ << 28 | 0x0c32e6dU),
+ UINTMAX_MAX / 2917)
+P (10, 74,
+ (((((uintmax_t) 0x2e9dU << 28 | 0xf4a1477U)
+ << 28 | 0x4c2dd47U)
+ << 28 | 0x1b33570U)
+ << 28 | 0x635b38fU),
+ UINTMAX_MAX / 2927)
+P (12, 72,
+ (((((uintmax_t) 0x891aU << 28 | 0x37ebcabU)
+ << 28 | 0x12ba3abU)
+ << 28 | 0x559fa99U)
+ << 28 | 0x7a61bb3U),
+ UINTMAX_MAX / 2939)
+P (14, 66,
+ (((((uintmax_t) 0xccadU << 28 | 0xbad1f78U)
+ << 28 | 0x11569adU)
+ << 28 | 0x4bdae56U)
+ << 28 | 0x2bddab9U),
+ UINTMAX_MAX / 2953)
+P (4, 66,
+ (((((uintmax_t) 0xb335U << 28 | 0x6a92a82U)
+ << 28 | 0x08d4a05U)
+ << 28 | 0x5e1b2f2U)
+ << 28 | 0xed62f45U),
+ UINTMAX_MAX / 2957)
+P (6, 74,
+ (((((uintmax_t) 0x58bbU << 28 | 0x5017802U)
+ << 28 | 0x12d5c03U)
+ << 28 | 0xcd328b1U)
+ << 28 | 0xa2dca9bU),
+ UINTMAX_MAX / 2963)
+P (6, 72,
+ (((((uintmax_t) 0x7501U << 28 | 0xa365242U)
+ << 28 | 0x0c3e6d2U)
+ << 28 | 0x8f4e087U)
+ << 28 | 0x33218a9U),
+ UINTMAX_MAX / 2969)
+P (2, 78,
+ (((((uintmax_t) 0x18a4U << 28 | 0xbffa7c4U)
+ << 28 | 0x073ceb6U)
+ << 28 | 0x800b077U)
+ << 28 | 0xf186293U),
+ UINTMAX_MAX / 2971)
+P (28, 62,
+ (((((uintmax_t) 0xa633U << 28 | 0x0bdd838U)
+ << 28 | 0xae2356fU)
+ << 28 | 0xbd138c3U)
+ << 28 | 0xfd9c207U),
+ UINTMAX_MAX / 2999)
+P (2, 66,
+ (((((uintmax_t) 0xe2ffU << 28 | 0x0fc80a3U)
+ << 28 | 0xc9104b1U)
+ << 28 | 0x17ccd12U)
+ << 28 | 0xae88a89U),
+ UINTMAX_MAX / 3001)
+P (10, 68,
+ (((((uintmax_t) 0x1183U << 28 | 0xb2cce6eU)
+ << 28 | 0xb2b722fU)
+ << 28 | 0x1a1a044U)
+ << 28 | 0x046bcebU),
+ UINTMAX_MAX / 3011)
+P (8, 64,
+ (((((uintmax_t) 0xbfb9U << 28 | 0x73118d8U)
+ << 28 | 0x666f154U)
+ << 28 | 0x8aba0b0U)
+ << 28 | 0x60541e3U),
+ UINTMAX_MAX / 3019)
+P (4, 66,
+ (((((uintmax_t) 0xa152U << 28 | 0xbc81bc6U)
+ << 28 | 0xc0e90cfU)
+ << 28 | 0x4e808ceU)
+ << 28 | 0xa111b2fU),
+ UINTMAX_MAX / 3023)
+P (14, 72,
+ (((((uintmax_t) 0xaebdU << 28 | 0xa92d6f2U)
+ << 28 | 0xef39bdbU)
+ << 28 | 0xec1b4faU)
+ << 28 | 0x855a475U),
+ UINTMAX_MAX / 3037)
+P (4, 78,
+ (((((uintmax_t) 0x890cU << 28 | 0xb62bf18U)
+ << 28 | 0x542ece3U)
+ << 28 | 0xf794eb6U)
+ << 28 | 0x00d7821U),
+ UINTMAX_MAX / 3041)
+P (8, 72,
+ (((((uintmax_t) 0x699fU << 28 | 0xc793db6U)
+ << 28 | 0x480a134U)
+ << 28 | 0xfae0d9aU)
+ << 28 | 0x11f7c59U),
+ UINTMAX_MAX / 3049)
+P (12, 76,
+ (((((uintmax_t) 0x14fdU << 28 | 0xe8c0055U)
+ << 28 | 0xa3d62f0U)
+ << 28 | 0x06b0ccbU)
+ << 28 | 0xbac085dU),
+ UINTMAX_MAX / 3061)
+P (6, 96,
+ (((((uintmax_t) 0xa99cU << 28 | 0x01006adU)
+ << 28 | 0x72efe3fU)
+ << 28 | 0x45076dcU)
+ << 28 | 0x3114733U),
+ UINTMAX_MAX / 3067)
+P (12, 88,
+ (((((uintmax_t) 0x59e0U << 28 | 0xe778f96U)
+ << 28 | 0xe7f8aeeU)
+ << 28 | 0xf49bfa5U)
+ << 28 | 0x8a1a1b7U),
+ UINTMAX_MAX / 3079)
+P (4, 86,
+ (((((uintmax_t) 0x6edaU << 28 | 0x627b0f3U)
+ << 28 | 0x2121a12U)
+ << 28 | 0xc4218beU)
+ << 28 | 0xa691fa3U),
+ UINTMAX_MAX / 3083)
+P (6, 92,
+ (((((uintmax_t) 0xf88aU << 28 | 0x9107df8U)
+ << 28 | 0x35b3ebcU)
+ << 28 | 0x7504e3bU)
+ << 28 | 0xd5e64f1U),
+ UINTMAX_MAX / 3089)
+P (20, 78,
+ (((((uintmax_t) 0xcddaU << 28 | 0x9dee60fU)
+ << 28 | 0xf969a4eU)
+ << 28 | 0xe21c292U)
+ << 28 | 0xbb92fadU),
+ UINTMAX_MAX / 3109)
+P (10, 72,
+ (((((uintmax_t) 0x4ff1U << 28 | 0x8de982bU)
+ << 28 | 0xfe5bc34U)
+ << 28 | 0x338b732U)
+ << 28 | 0x7a4bacfU),
+ UINTMAX_MAX / 3119)
+P (2, 82,
+ (((((uintmax_t) 0x8fdfU << 28 | 0x30a40ccU)
+ << 28 | 0xbc0053fU)
+ << 28 | 0xe5c0833U)
+ << 28 | 0xd6fccd1U),
+ UINTMAX_MAX / 3121)
+P (16, 72,
+ (((((uintmax_t) 0x0ca6U << 28 | 0x26ae799U)
+ << 28 | 0x8087cb1U)
+ << 28 | 0xe707435U)
+ << 28 | 0x35203c1U),
+ UINTMAX_MAX / 3137)
+P (26, 54,
+ (((((uintmax_t) 0x3a1cU << 28 | 0xa6ba507U)
+ << 28 | 0x340aaefU)
+ << 28 | 0xbb5dcdfU)
+ << 28 | 0xb4e43d3U),
+ UINTMAX_MAX / 3163)
+P (4, 54,
+ (((((uintmax_t) 0x340eU << 28 | 0x8ccfe76U)
+ << 28 | 0xd34c8caU)
+ << 28 | 0x68467caU)
+ << 28 | 0x5394f9fU),
+ UINTMAX_MAX / 3167)
+P (2, 60,
+ (((((uintmax_t) 0xe94cU << 28 | 0xd3010cdU)
+ << 28 | 0x82c978cU)
+ << 28 | 0x51c0814U)
+ << 28 | 0x08b97a1U),
+ UINTMAX_MAX / 3169)
+P (12, 70,
+ (((((uintmax_t) 0x69d4U << 28 | 0x0f213ccU)
+ << 28 | 0x2c1a132U)
+ << 28 | 0x75a899dU)
+ << 28 | 0xfa5dd65U),
+ UINTMAX_MAX / 3181)
+P (6, 66,
+ (((((uintmax_t) 0xcc45U << 28 | 0x14a4d46U)
+ << 28 | 0x1ff849eU)
+ << 28 | 0x674cb62U)
+ << 28 | 0xe1b78bbU),
+ UINTMAX_MAX / 3187)
+P (4, 66,
+ (((((uintmax_t) 0x6351U << 28 | 0xbffadd9U)
+ << 28 | 0x54cc6a3U)
+ << 28 | 0x7ff5bb2U)
+ << 28 | 0xa998d47U),
+ UINTMAX_MAX / 3191)
+P (12, 56,
+ (((((uintmax_t) 0x77baU << 28 | 0x4e2aae1U)
+ << 28 | 0x3a95c79U)
+ << 28 | 0x2a999dbU)
+ << 28 | 0x131a22bU),
+ UINTMAX_MAX / 3203)
+P (6, 62,
+ (((((uintmax_t) 0x8d1fU << 28 | 0x82e96c6U)
+ << 28 | 0xa42da1bU)
+ << 28 | 0x48841bcU)
+ << 28 | 0x30d29b9U),
+ UINTMAX_MAX / 3209)
+P (8, 82,
+ (((((uintmax_t) 0x0ef5U << 28 | 0xe4c8da5U)
+ << 28 | 0xc2683f0U)
+ << 28 | 0x6721d20U)
+ << 28 | 0x11d3471U),
+ UINTMAX_MAX / 3217)
+P (4, 80,
+ (((((uintmax_t) 0x9ccfU << 28 | 0x98fef77U)
+ << 28 | 0xeed5293U)
+ << 28 | 0xfd2386dU)
+ << 28 | 0xff85ebdU),
+ UINTMAX_MAX / 3221)
+P (8, 78,
+ (((((uintmax_t) 0x9c06U << 28 | 0xa8de9f5U)
+ << 28 | 0xb182e4cU)
+ << 28 | 0xe72f54cU)
+ << 28 | 0x07ed9b5U),
+ UINTMAX_MAX / 3229)
+P (22, 62,
+ (((((uintmax_t) 0xdcf5U << 28 | 0x5e929f8U)
+ << 28 | 0x99148d6U)
+ << 28 | 0xd0fd3e7U)
+ << 28 | 0x1dd827bU),
+ UINTMAX_MAX / 3251)
+P (2, 66,
+ (((((uintmax_t) 0xcebcU << 28 | 0x664e397U)
+ << 28 | 0x2d17d85U)
+ << 28 | 0x6405fb1U)
+ << 28 | 0xeed819dU),
+ UINTMAX_MAX / 3253)
+P (4, 66,
+ (((((uintmax_t) 0x921eU << 28 | 0x0671f84U)
+ << 28 | 0xc15b18eU)
+ << 28 | 0xa8aceb7U)
+ << 28 | 0xc443989U),
+ UINTMAX_MAX / 3257)
+P (2, 70,
+ (((((uintmax_t) 0x4223U << 28 | 0xfa07b2bU)
+ << 28 | 0x4830634U)
+ << 28 | 0xa13026fU)
+ << 28 | 0x62e5873U),
+ UINTMAX_MAX / 3259)
+P (12, 60,
+ (((((uintmax_t) 0x4ceeU << 28 | 0xdc3bcb1U)
+ << 28 | 0x806e31eU)
+ << 28 | 0xea0208eU)
+ << 28 | 0xc0af4f7U),
+ UINTMAX_MAX / 3271)
+P (28, 44,
+ (((((uintmax_t) 0x969eU << 28 | 0xc4a2f55U)
+ << 28 | 0xe703563U)
+ << 28 | 0x679853cU)
+ << 28 | 0xea598cbU),
+ UINTMAX_MAX / 3299)
+P (2, 46,
+ (((((uintmax_t) 0xd886U << 28 | 0xa176bb8U)
+ << 28 | 0x577a9c3U)
+ << 28 | 0x0b3ebd6U)
+ << 28 | 0x1f2d0edU),
+ UINTMAX_MAX / 3301)
+P (6, 52,
+ (((((uintmax_t) 0xaaecU << 28 | 0xb97a633U)
+ << 28 | 0xdda117eU)
+ << 28 | 0xb9037bcU)
+ << 28 | 0x7f43bc3U),
+ UINTMAX_MAX / 3307)
+P (6, 48,
+ (((((uintmax_t) 0x1a59U << 28 | 0x7af0505U)
+ << 28 | 0xcb9c2a5U)
+ << 28 | 0x83e6f6cU)
+ << 28 | 0xe016411U),
+ UINTMAX_MAX / 3313)
+P (6, 52,
+ (((((uintmax_t) 0x76c8U << 28 | 0x6358785U)
+ << 28 | 0x34d5cf1U)
+ << 28 | 0x938d895U)
+ << 28 | 0xf1a74c7U),
+ UINTMAX_MAX / 3319)
+P (4, 50,
+ (((((uintmax_t) 0xb781U << 28 | 0xa8058bfU)
+ << 28 | 0xac2e880U)
+ << 28 | 0xcf1491cU)
+ << 28 | 0x1e81e33U),
+ UINTMAX_MAX / 3323)
+P (6, 60,
+ (((((uintmax_t) 0xc604U << 28 | 0x75cf8d9U)
+ << 28 | 0x2a5f33cU)
+ << 28 | 0x0f12886U)
+ << 28 | 0xba8f301U),
+ UINTMAX_MAX / 3329)
+P (2, 60,
+ (((((uintmax_t) 0x9d2aU << 28 | 0x8009d65U)
+ << 28 | 0x861c20eU)
+ << 28 | 0x4b786e0U)
+ << 28 | 0xdfcc5abU),
+ UINTMAX_MAX / 3331)
+P (12, 64,
+ (((((uintmax_t) 0x4053U << 28 | 0x511894dU)
+ << 28 | 0xe137367U)
+ << 28 | 0x2684c93U)
+ << 28 | 0xf2d41efU),
+ UINTMAX_MAX / 3343)
+P (4, 66,
+ (((((uintmax_t) 0xcbfdU << 28 | 0x3f19edcU)
+ << 28 | 0xbd615e0U)
+ << 28 | 0x0757badU)
+ << 28 | 0xb35c51bU),
+ UINTMAX_MAX / 3347)
+P (12, 74,
+ (((((uintmax_t) 0x303eU << 28 | 0x309fbe2U)
+ << 28 | 0x6de63d6U)
+ << 28 | 0xd84afe6U)
+ << 28 | 0x6472edfU),
+ UINTMAX_MAX / 3359)
+P (2, 88,
+ (((((uintmax_t) 0x1123U << 28 | 0x440491fU)
+ << 28 | 0x00137fbU)
+ << 28 | 0xbc0eedcU)
+ << 28 | 0xbbfb6e1U),
+ UINTMAX_MAX / 3361)
+P (10, 86,
+ (((((uintmax_t) 0x5ae7U << 28 | 0x03df7f3U)
+ << 28 | 0x3de4825U)
+ << 28 | 0x0f43aa0U)
+ << 28 | 0x8a84983U),
+ UINTMAX_MAX / 3371)
+P (2, 88,
+ (((((uintmax_t) 0x11fcU << 28 | 0xcff5122U)
+ << 28 | 0x3abe804U)
+ << 28 | 0x400e927U)
+ << 28 | 0xb1acaa5U),
+ UINTMAX_MAX / 3373)
+P (16, 74,
+ (((((uintmax_t) 0x80cbU << 28 | 0x0c29652U)
+ << 28 | 0x5643d56U)
+ << 28 | 0x572be34U)
+ << 28 | 0xb9d3215U),
+ UINTMAX_MAX / 3389)
+P (2, 76,
+ (((((uintmax_t) 0xc57dU << 28 | 0xffd958dU)
+ << 28 | 0xb3c0487U)
+ << 28 | 0x964ef77U)
+ << 28 | 0x81c62bfU),
+ UINTMAX_MAX / 3391)
+P (16, 62,
+ (((((uintmax_t) 0x9c4aU << 28 | 0x3cdce8eU)
+ << 28 | 0xea48e29U)
+ << 28 | 0xed84051U)
+ << 28 | 0xc06e9afU),
+ UINTMAX_MAX / 3407)
+P (6, 78,
+ (((((uintmax_t) 0x0cf9U << 28 | 0xeca5ea8U)
+ << 28 | 0xc4381b0U)
+ << 28 | 0x0acd11eU)
+ << 28 | 0xd3f87fdU),
+ UINTMAX_MAX / 3413)
+P (20, 66,
+ (((((uintmax_t) 0xfe48U << 28 | 0xee074edU)
+ << 28 | 0x223a506U)
+ << 28 | 0x3078817U)
+ << 28 | 0x44152d9U),
+ UINTMAX_MAX / 3433)
+P (16, 62,
+ (((((uintmax_t) 0xa409U << 28 | 0x342e04eU)
+ << 28 | 0x6187e7aU)
+ << 28 | 0x786459fU)
+ << 28 | 0x5c1ccc9U),
+ UINTMAX_MAX / 3449)
+P (8, 60,
+ (((((uintmax_t) 0xe4e5U << 28 | 0x902e357U)
+ << 28 | 0x74c7f13U)
+ << 28 | 0x08125d7U)
+ << 28 | 0x4563281U),
+ UINTMAX_MAX / 3457)
+P (4, 66,
+ (((((uintmax_t) 0x7588U << 28 | 0x9dfe5f6U)
+ << 28 | 0xae1e539U)
+ << 28 | 0x5310a48U)
+ << 28 | 0x0b3e34dU),
+ UINTMAX_MAX / 3461)
+P (2, 66,
+ (((((uintmax_t) 0x3784U << 28 | 0x6603fdeU)
+ << 28 | 0xe1c3d35U)
+ << 28 | 0x985baa8U)
+ << 28 | 0xb202837U),
+ UINTMAX_MAX / 3463)
+P (4, 66,
+ (((((uintmax_t) 0xb450U << 28 | 0xa1daeecU)
+ << 28 | 0xba5ea96U)
+ << 28 | 0x304a6e0U)
+ << 28 | 0x52b3223U),
+ UINTMAX_MAX / 3467)
+P (2, 70,
+ (((((uintmax_t) 0xfbf0U << 28 | 0xf20d6e5U)
+ << 28 | 0x363d8bdU)
+ << 28 | 0x8265fc9U)
+ << 28 | 0xaf8fd45U),
+ UINTMAX_MAX / 3469)
+P (22, 50,
+ (((((uintmax_t) 0xeeb1U << 28 | 0x9bd44b6U)
+ << 28 | 0x27bee1bU)
+ << 28 | 0x6d0b383U)
+ << 28 | 0xec58e0bU),
+ UINTMAX_MAX / 3491)
+P (8, 48,
+ (((((uintmax_t) 0x7386U << 28 | 0x8c53fdfU)
+ << 28 | 0x38fe9c2U)
+ << 28 | 0x1a7c3b6U)
+ << 28 | 0x8b28503U),
+ UINTMAX_MAX / 3499)
+P (12, 46,
+ (((((uintmax_t) 0xba13U << 28 | 0x65219cfU)
+ << 28 | 0xbb2b623U)
+ << 28 | 0x6fa180fU)
+ << 28 | 0xbfd6007U),
+ UINTMAX_MAX / 3511)
+P (6, 42,
+ (((((uintmax_t) 0xe16dU << 28 | 0xb1887adU)
+ << 28 | 0xe4c6dc4U)
+ << 28 | 0x2accd44U)
+ << 28 | 0x0ed9595U),
+ UINTMAX_MAX / 3517)
+P (10, 44,
+ (((((uintmax_t) 0x4cf0U << 28 | 0x1ab5e49U)
+ << 28 | 0x04b7c7aU)
+ << 28 | 0xcf71282U)
+ << 28 | 0x36ba3f7U),
+ UINTMAX_MAX / 3527)
+P (2, 52,
+ (((((uintmax_t) 0x6374U << 28 | 0x6df92e5U)
+ << 28 | 0xaad5ff9U)
+ << 28 | 0x09367a9U)
+ << 28 | 0x87b9c79U),
+ UINTMAX_MAX / 3529)
+P (4, 50,
+ (((((uintmax_t) 0x3fc3U << 28 | 0xb6abbabU)
+ << 28 | 0xa82dcb6U)
+ << 28 | 0x4efb252U)
+ << 28 | 0xbfba705U),
+ UINTMAX_MAX / 3533)
+P (6, 54,
+ (((((uintmax_t) 0x82b6U << 28 | 0x6ef6f53U)
+ << 28 | 0x8c8ce98U)
+ << 28 | 0x0d4f5a7U)
+ << 28 | 0xe4cd25bU),
+ UINTMAX_MAX / 3539)
+P (2, 66,
+ (((((uintmax_t) 0x20c0U << 28 | 0x04a07f3U)
+ << 28 | 0xdab1fe1U)
+ << 28 | 0xecc4ef2U)
+ << 28 | 0x7b0c37dU),
+ UINTMAX_MAX / 3541)
+P (6, 66,
+ (((((uintmax_t) 0xfb2aU << 28 | 0x13c68cbU)
+ << 28 | 0xd185291U)
+ << 28 | 0x11aebb8U)
+ << 28 | 0x1d72653U),
+ UINTMAX_MAX / 3547)
+P (10, 60,
+ (((((uintmax_t) 0x8908U << 28 | 0x46d1b90U)
+ << 28 | 0x96d9c89U)
+ << 28 | 0x51f985cU)
+ << 28 | 0xb2c67edU),
+ UINTMAX_MAX / 3557)
+P (2, 64,
+ (((((uintmax_t) 0xf7baU << 28 | 0x5f17856U)
+ << 28 | 0xe44e8c4U)
+ << 28 | 0x39d4fc5U)
+ << 28 | 0x4e0b5d7U),
+ UINTMAX_MAX / 3559)
+P (12, 60,
+ (((((uintmax_t) 0x811cU << 28 | 0x75db26eU)
+ << 28 | 0xd4a0de8U)
+ << 28 | 0x57bf318U)
+ << 28 | 0x96d533bU),
+ UINTMAX_MAX / 3571)
+P (10, 56,
+ (((((uintmax_t) 0x6fbcU << 28 | 0x83d31afU)
+ << 28 | 0x37d51b6U)
+ << 28 | 0x14bb4cbU)
+ << 28 | 0x5023755U),
+ UINTMAX_MAX / 3581)
+P (2, 60,
+ (((((uintmax_t) 0xdf7dU << 28 | 0xad8c657U)
+ << 28 | 0x4f61193U)
+ << 28 | 0x8a89e54U)
+ << 28 | 0x73bf1ffU),
+ UINTMAX_MAX / 3583)
+P (10, 66,
+ (((((uintmax_t) 0x48beU << 28 | 0xf2f618aU)
+ << 28 | 0x70259eaU)
+ << 28 | 0xc481acaU)
+ << 28 | 0x34de039U),
+ UINTMAX_MAX / 3593)
+P (14, 64,
+ (((((uintmax_t) 0x5c8cU << 28 | 0x86d951dU)
+ << 28 | 0x4fd8414U)
+ << 28 | 0xb961badU)
+ << 28 | 0xf4809a7U),
+ UINTMAX_MAX / 3607)
+P (6, 60,
+ (((((uintmax_t) 0x3e35U << 28 | 0xfddfd4eU)
+ << 28 | 0xb85d876U)
+ << 28 | 0x784fecbU)
+ << 28 | 0xa352435U),
+ UINTMAX_MAX / 3613)
+P (4, 60,
+ (((((uintmax_t) 0x3f46U << 28 | 0x480d05dU)
+ << 28 | 0xfde06efU)
+ << 28 | 0xa689bb5U)
+ << 28 | 0x8aef5e1U),
+ UINTMAX_MAX / 3617)
+P (6, 68,
+ (((((uintmax_t) 0xa7f5U << 28 | 0x427da20U)
+ << 28 | 0x5cb49b2U)
+ << 28 | 0xb2c4db9U)
+ << 28 | 0xc3a8197U),
+ UINTMAX_MAX / 3623)
+P (8, 66,
+ (((((uintmax_t) 0x1756U << 28 | 0x39f44bdU)
+ << 28 | 0xcbf7d25U)
+ << 28 | 0x03bc992U)
+ << 28 | 0x279f8cfU),
+ UINTMAX_MAX / 3631)
+P (6, 64,
+ (((((uintmax_t) 0xf7b1U << 28 | 0xba9905dU)
+ << 28 | 0x798f3d2U)
+ << 28 | 0xab9aec5U)
+ << 28 | 0xca1541dU),
+ UINTMAX_MAX / 3637)
+P (6, 66,
+ (((((uintmax_t) 0x0ec1U << 28 | 0xcf3b3d3U)
+ << 28 | 0x4ea253eU)
+ << 28 | 0x78ba146U)
+ << 28 | 0x0f99af3U),
+ UINTMAX_MAX / 3643)
+P (16, 60,
+ (((((uintmax_t) 0x694bU << 28 | 0xe954ddeU)
+ << 28 | 0xd63b30aU)
+ << 28 | 0x0142657U)
+ << 28 | 0x2cfcb63U),
+ UINTMAX_MAX / 3659)
+P (12, 56,
+ (((((uintmax_t) 0xd628U << 28 | 0x9612455U)
+ << 28 | 0x13dfebeU)
+ << 28 | 0xa857968U)
+ << 28 | 0xf3cbd67U),
+ UINTMAX_MAX / 3671)
+P (2, 60,
+ (((((uintmax_t) 0x63bcU << 28 | 0xcfb30dbU)
+ << 28 | 0xaffca78U)
+ << 28 | 0xdb213eeU)
+ << 28 | 0xfe659e9U),
+ UINTMAX_MAX / 3673)
+P (4, 62,
+ (((((uintmax_t) 0x7cf8U << 28 | 0xb08fb32U)
+ << 28 | 0x328ba96U)
+ << 28 | 0x3e8541aU)
+ << 28 | 0x74d35f5U),
+ UINTMAX_MAX / 3677)
+P (14, 70,
+ (((((uintmax_t) 0x99e7U << 28 | 0xb98849cU)
+ << 28 | 0xbfb489eU)
+ << 28 | 0x22d1527U)
+ << 28 | 0x76f2e43U),
+ UINTMAX_MAX / 3691)
+P (6, 70,
+ (((((uintmax_t) 0x1767U << 28 | 0xa90721dU)
+ << 28 | 0xc686c05U)
+ << 28 | 0xd10d39dU)
+ << 28 | 0x1e1f291U),
+ UINTMAX_MAX / 3697)
+P (4, 68,
+ (((((uintmax_t) 0x817cU << 28 | 0xb6e3047U)
+ << 28 | 0xeff3d37U)
+ << 28 | 0x4468dccU)
+ << 28 | 0xaced1ddU),
+ UINTMAX_MAX / 3701)
+P (8, 70,
+ (((((uintmax_t) 0x916dU << 28 | 0x896be15U)
+ << 28 | 0xac3548dU)
+ << 28 | 0x145c7d1U)
+ << 28 | 0x10c5ad5U),
+ UINTMAX_MAX / 3709)
+P (10, 74,
+ (((((uintmax_t) 0x50e1U << 28 | 0xc7f7bd5U)
+ << 28 | 0xdf5f332U)
+ << 28 | 0x51a39f5U)
+ << 28 | 0xacb5737U),
+ UINTMAX_MAX / 3719)
+P (8, 70,
+ (((((uintmax_t) 0xc1e7U << 28 | 0xf58f36eU)
+ << 28 | 0x1b567a6U)
+ << 28 | 0x6e50171U)
+ << 28 | 0x443506fU),
+ UINTMAX_MAX / 3727)
+P (6, 70,
+ (((((uintmax_t) 0xe72cU << 28 | 0xc7f8de3U)
+ << 28 | 0x0f6e112U)
+ << 28 | 0x4f69ad9U)
+ << 28 | 0x1dd4cbdU),
+ UINTMAX_MAX / 3733)
+P (6, 82,
+ (((((uintmax_t) 0x81e2U << 28 | 0x02e029aU)
+ << 28 | 0x0d485ecU)
+ << 28 | 0x24f8f2aU)
+ << 28 | 0x61a2793U),
+ UINTMAX_MAX / 3739)
+P (22, 62,
+ (((((uintmax_t) 0x66a5U << 28 | 0x216bc00U)
+ << 28 | 0x45b35b4U)
+ << 28 | 0x72148e6U)
+ << 28 | 0x56b7a51U),
+ UINTMAX_MAX / 3761)
+P (6, 66,
+ (((((uintmax_t) 0x3442U << 28 | 0x9973536U)
+ << 28 | 0x29ba00aU)
+ << 28 | 0xdf9570eU)
+ << 28 | 0x1142f07U),
+ UINTMAX_MAX / 3767)
+P (2, 78,
+ (((((uintmax_t) 0xc952U << 28 | 0x869f58aU)
+ << 28 | 0x38eb489U)
+ << 28 | 0xbf33b06U)
+ << 28 | 0x5119789U),
+ UINTMAX_MAX / 3769)
+P (10, 72,
+ (((((uintmax_t) 0xc462U << 28 | 0xe78b7b7U)
+ << 28 | 0xebf2b8fU)
+ << 28 | 0x0149803U)
+ << 28 | 0xcb291ebU),
+ UINTMAX_MAX / 3779)
+P (14, 60,
+ (((((uintmax_t) 0xa7b8U << 28 | 0x300e09dU)
+ << 28 | 0xa9be883U)
+ << 28 | 0x34b63afU)
+ << 28 | 0xd190a31U),
+ UINTMAX_MAX / 3793)
+P (4, 66,
+ (((((uintmax_t) 0x678fU << 28 | 0x45607afU)
+ << 28 | 0xa226292U)
+ << 28 | 0x0908d50U)
+ << 28 | 0xd6aba7dU),
+ UINTMAX_MAX / 3797)
+P (6, 74,
+ (((((uintmax_t) 0x3066U << 28 | 0x51b882dU)
+ << 28 | 0xc63e557U)
+ << 28 | 0xd8b018cU)
+ << 28 | 0x5a33d53U),
+ UINTMAX_MAX / 3803)
+P (18, 60,
+ (((((uintmax_t) 0x03f3U << 28 | 0xf0b9737U)
+ << 28 | 0x01682eaU)
+ << 28 | 0x1773092U)
+ << 28 | 0xdc27ee5U),
+ UINTMAX_MAX / 3821)
+P (2, 66,
+ (((((uintmax_t) 0x824fU << 28 | 0x6b12f35U)
+ << 28 | 0x80e76caU)
+ << 28 | 0xe5f38b7U)
+ << 28 | 0xbf2e00fU),
+ UINTMAX_MAX / 3823)
+P (10, 74,
+ (((((uintmax_t) 0xba8aU << 28 | 0x4084821U)
+ << 28 | 0xa94f02bU)
+ << 28 | 0xd02df34U)
+ << 28 | 0xf695349U),
+ UINTMAX_MAX / 3833)
+P (14, 64,
+ (((((uintmax_t) 0x1f9bU << 28 | 0xea70762U)
+ << 28 | 0xf3f48ddU)
+ << 28 | 0xfecd5beU)
+ << 28 | 0x62e2eb7U),
+ UINTMAX_MAX / 3847)
+P (4, 66,
+ (((((uintmax_t) 0xb7acU << 28 | 0x817ee73U)
+ << 28 | 0x45119dbU)
+ << 28 | 0xf849ebeU)
+ << 28 | 0xc96c4a3U),
+ UINTMAX_MAX / 3851)
+P (2, 66,
+ (((((uintmax_t) 0xf8c2U << 28 | 0x0286585U)
+ << 28 | 0xe14dcdaU)
+ << 28 | 0x31d4d01U)
+ << 28 | 0x87357c5U),
+ UINTMAX_MAX / 3853)
+P (10, 60,
+ (((((uintmax_t) 0x7727U << 28 | 0x2a58ab3U)
+ << 28 | 0xdb276e3U)
+ << 28 | 0x4e21cc2U)
+ << 28 | 0xd5418a7U),
+ UINTMAX_MAX / 3863)
+P (14, 52,
+ (((((uintmax_t) 0x61caU << 28 | 0x83edc68U)
+ << 28 | 0xdb38968U)
+ << 28 | 0xca5137aU)
+ << 28 | 0x9e574adU),
+ UINTMAX_MAX / 3877)
+P (4, 50,
+ (((((uintmax_t) 0x74f3U << 28 | 0x8879e60U)
+ << 28 | 0x2c53a3eU)
+ << 28 | 0xaa0d0f8U)
+ << 28 | 0x04bfd19U),
+ UINTMAX_MAX / 3881)
+P (8, 54,
+ (((((uintmax_t) 0x1c6fU << 28 | 0xe7c6996U)
+ << 28 | 0x04df055U)
+ << 28 | 0x4fb753cU)
+ << 28 | 0xc20e9d1U),
+ UINTMAX_MAX / 3889)
+P (18, 40,
+ (((((uintmax_t) 0x374dU << 28 | 0x408a62aU)
+ << 28 | 0xda31679U)
+ << 28 | 0x7afcca1U)
+ << 28 | 0x300756bU),
+ UINTMAX_MAX / 3907)
+P (4, 56,
+ (((((uintmax_t) 0xc8e2U << 28 | 0xbdb1524U)
+ << 28 | 0x758f48bU)
+ << 28 | 0x8d950b5U)
+ << 28 | 0x2eeea77U),
+ UINTMAX_MAX / 3911)
+P (6, 72,
+ (((((uintmax_t) 0xbfc1U << 28 | 0x421336fU)
+ << 28 | 0x6ea5dfbU)
+ << 28 | 0x6cd166aU)
+ << 28 | 0xcabc185U),
+ UINTMAX_MAX / 3917)
+P (2, 82,
+ (((((uintmax_t) 0x7daeU << 28 | 0x58b5560U)
+ << 28 | 0x7b5454eU)
+ << 28 | 0xb6c5ed9U)
+ << 28 | 0x437a7afU),
+ UINTMAX_MAX / 3919)
+P (4, 80,
+ (((((uintmax_t) 0xf1f8U << 28 | 0x4cbdc3dU)
+ << 28 | 0x573f5d1U)
+ << 28 | 0xeddbd91U)
+ << 28 | 0xb790cdbU),
+ UINTMAX_MAX / 3923)
+P (6, 78,
+ (((((uintmax_t) 0xa6abU << 28 | 0x9f4ec63U)
+ << 28 | 0x4c6db93U)
+ << 28 | 0xd714ea4U)
+ << 28 | 0xd8948e9U),
+ UINTMAX_MAX / 3929)
+P (2, 82,
+ (((((uintmax_t) 0x8198U << 28 | 0x742e1b7U)
+ << 28 | 0xb68a73cU)
+ << 28 | 0xa13ed81U)
+ << 28 | 0x45188d3U),
+ UINTMAX_MAX / 3931)
+P (12, 76,
+ (((((uintmax_t) 0x5ab3U << 28 | 0x52c7947U)
+ << 28 | 0xbe09382U)
+ << 28 | 0x9086016U)
+ << 28 | 0xda89c57U),
+ UINTMAX_MAX / 3943)
+P (4, 74,
+ (((((uintmax_t) 0xec69U << 28 | 0x9751239U)
+ << 28 | 0xb9900d7U)
+ << 28 | 0xda1f432U)
+ << 28 | 0x124a543U),
+ UINTMAX_MAX / 3947)
+P (20, 60,
+ (((((uintmax_t) 0xa4e1U << 28 | 0x58dc715U)
+ << 28 | 0x1a22b7eU)
+ << 28 | 0xad55816U)
+ << 28 | 0x32fb07fU),
+ UINTMAX_MAX / 3967)
+P (22, 60,
+ (((((uintmax_t) 0x4cd1U << 28 | 0xba8fa08U)
+ << 28 | 0x1613a35U)
+ << 28 | 0x443837fU)
+ << 28 | 0x63ec3bdU),
+ UINTMAX_MAX / 3989)
+P (12, 50,
+ (((((uintmax_t) 0x48afU << 28 | 0x92759a4U)
+ << 28 | 0x3f37589U)
+ << 28 | 0xe2b200eU)
+ << 28 | 0x5519461U),
+ UINTMAX_MAX / 4001)
+P (2, 54,
+ (((((uintmax_t) 0x9293U << 28 | 0xfc29b25U)
+ << 28 | 0xcbafee9U)
+ << 28 | 0xae44f0bU)
+ << 28 | 0x7289c0bU),
+ UINTMAX_MAX / 4003)
+P (4, 66,
+ (((((uintmax_t) 0xc02cU << 28 | 0xfa2fa91U)
+ << 28 | 0xcaf9094U)
+ << 28 | 0x387a277U)
+ << 28 | 0xb9fa817U),
+ UINTMAX_MAX / 4007)
+P (6, 66,
+ (((((uintmax_t) 0x15c0U << 28 | 0xd8627efU)
+ << 28 | 0x28a2cc8U)
+ << 28 | 0x4f1a58aU)
+ << 28 | 0xbfc2c25U),
+ UINTMAX_MAX / 4013)
+P (6, 72,
+ (((((uintmax_t) 0x1143U << 28 | 0x12ca6e3U)
+ << 28 | 0x2522b71U)
+ << 28 | 0x101d8e3U)
+ << 28 | 0xc83377bU),
+ UINTMAX_MAX / 4019)
+P (2, 72,
+ (((((uintmax_t) 0xcfadU << 28 | 0x7d3b04aU)
+ << 28 | 0x5c91ec0U)
+ << 28 | 0x24abe5cU)
+ << 28 | 0x50ba69dU),
+ UINTMAX_MAX / 4021)
+P (6, 72,
+ (((((uintmax_t) 0x9d46U << 28 | 0x3eef687U)
+ << 28 | 0x26d7815U)
+ << 28 | 0xde4eb36U)
+ << 28 | 0x5a65d73U),
+ UINTMAX_MAX / 4027)
+P (22, 62,
+ (((((uintmax_t) 0xe98eU << 28 | 0x1152e37U)
+ << 28 | 0xc3cf309U)
+ << 28 | 0xed28a76U)
+ << 28 | 0xbcca931U),
+ UINTMAX_MAX / 4049)
+P (2, 76,
+ (((((uintmax_t) 0xa002U << 28 | 0x05affefU)
+ << 28 | 0xd280081U)
+ << 28 | 0x6bffbf4U)
+ << 28 | 0xa00205bU),
+ UINTMAX_MAX / 4051)
+P (6, 72,
+ (((((uintmax_t) 0x1d87U << 28 | 0xfb74ed0U)
+ << 28 | 0x1b4271fU)
+ << 28 | 0x5c71543U)
+ << 28 | 0xd558069U),
+ UINTMAX_MAX / 4057)
+P (16, 60,
+ (((((uintmax_t) 0x7051U << 28 | 0x751852fU)
+ << 28 | 0x74370f2U)
+ << 28 | 0x5c64d0eU)
+ << 28 | 0xc53b859U),
+ UINTMAX_MAX / 4073)
+P (6, 60,
+ (((((uintmax_t) 0x88e1U << 28 | 0x6f867eeU)
+ << 28 | 0x6d54296U)
+ << 28 | 0xc02c2efU)
+ << 28 | 0x1e0ff0fU),
+ UINTMAX_MAX / 4079)
+P (12, 62,
+ (((((uintmax_t) 0xe8e8U << 28 | 0xc8bebb9U)
+ << 28 | 0xaa05219U)
+ << 28 | 0xa804816U)
+ << 28 | 0x870a333U),
+ UINTMAX_MAX / 4091)
+P (2, 64,
+ (((((uintmax_t) 0xc605U << 28 | 0x20f62e2U)
+ << 28 | 0x8a79f6dU)
+ << 28 | 0xe49add0U)
+ << 28 | 0x971c555U),
+ UINTMAX_MAX / 4093)
+P (6, 60,
+ (((((uintmax_t) 0x46c2U << 28 | 0xbb7cd89U)
+ << 28 | 0x7639d52U)
+ << 28 | 0x8087e68U)
+ << 28 | 0x4c71aabU),
+ UINTMAX_MAX / 4099)
+P (12, 66,
+ (((((uintmax_t) 0xfc73U << 28 | 0x53e15cbU)
+ << 28 | 0x9127ea9U)
+ << 28 | 0x4152c26U)
+ << 28 | 0x9bcdeefU),
+ UINTMAX_MAX / 4111)
+P (16, 74,
+ (((((uintmax_t) 0x3d78U << 28 | 0xe5c2d68U)
+ << 28 | 0x0673803U)
+ << 28 | 0x79450a3U)
+ << 28 | 0xc2b6bdfU),
+ UINTMAX_MAX / 4127)
+P (2, 82,
+ (((((uintmax_t) 0x4a66U << 28 | 0x8c7e3baU)
+ << 28 | 0x4fbb8d2U)
+ << 28 | 0xcd38bafU)
+ << 28 | 0xe5373e1U),
+ UINTMAX_MAX / 4129)
+P (4, 84,
+ (((((uintmax_t) 0x616eU << 28 | 0xb008eb5U)
+ << 28 | 0xfb2b2c2U)
+ << 28 | 0x9df2beaU)
+ << 28 | 0x71d8badU),
+ UINTMAX_MAX / 4133)
+P (6, 80,
+ (((((uintmax_t) 0x12bdU << 28 | 0xa25ba9aU)
+ << 28 | 0x80c5ec1U)
+ << 28 | 0x5862775U)
+ << 28 | 0xf302e83U),
+ UINTMAX_MAX / 4139)
+P (14, 76,
+ (((((uintmax_t) 0x98dfU << 28 | 0x642b264U)
+ << 28 | 0x7a0d310U)
+ << 28 | 0x16af2feU)
+ << 28 | 0x55ede09U),
+ UINTMAX_MAX / 4153)
+P (4, 74,
+ (((((uintmax_t) 0xcc45U << 28 | 0x381a1c7U)
+ << 28 | 0x3878b3dU)
+ << 28 | 0x26dbd9dU)
+ << 28 | 0x1910715U),
+ UINTMAX_MAX / 4157)
+P (2, 82,
+ (((((uintmax_t) 0x1344U << 28 | 0x23b36d8U)
+ << 28 | 0x0d4ba62U)
+ << 28 | 0x1dab2dfU)
+ << 28 | 0xaf3dfbfU),
+ UINTMAX_MAX / 4159)
+P (18, 66,
+ (((((uintmax_t) 0xd614U << 28 | 0x399c587U)
+ << 28 | 0xff827b6U)
+ << 28 | 0xf1d7ac2U)
+ << 28 | 0x87338b1U),
+ UINTMAX_MAX / 4177)
+P (24, 52,
+ (((((uintmax_t) 0x5c04U << 28 | 0x24ce751U)
+ << 28 | 0xf620c8dU)
+ << 28 | 0x9e9f0c3U)
+ << 28 | 0xf9e7fd9U),
+ UINTMAX_MAX / 4201)
+P (10, 48,
+ (((((uintmax_t) 0xa4cfU << 28 | 0x6d1fac5U)
+ << 28 | 0x93e8e60U)
+ << 28 | 0xa93f876U)
+ << 28 | 0x2e914bbU),
+ UINTMAX_MAX / 4211)
+P (6, 44,
+ (((((uintmax_t) 0x16b4U << 28 | 0x4c7d8a9U)
+ << 28 | 0x7e358b1U)
+ << 28 | 0x4371f24U)
+ << 28 | 0x7c159c9U),
+ UINTMAX_MAX / 4217)
+P (2, 52,
+ (((((uintmax_t) 0x7d2dU << 28 | 0xb0c132cU)
+ << 28 | 0x9926a6dU)
+ << 28 | 0xd3b4844U)
+ << 28 | 0x71d4eb3U),
+ UINTMAX_MAX / 4219)
+P (10, 44,
+ (((((uintmax_t) 0xc12aU << 28 | 0x5044c45U)
+ << 28 | 0xfa4f4cdU)
+ << 28 | 0x172f470U)
+ << 28 | 0x1c1684dU),
+ UINTMAX_MAX / 4229)
+P (2, 52,
+ (((((uintmax_t) 0x3b6aU << 28 | 0xabf51beU)
+ << 28 | 0x4a6c103U)
+ << 28 | 0x72e686eU)
+ << 28 | 0xd8bb537U),
+ UINTMAX_MAX / 4231)
+P (10, 48,
+ (((((uintmax_t) 0x0b0bU << 28 | 0xe43ba38U)
+ << 28 | 0x61105bcU)
+ << 28 | 0x07f7ca6U)
+ << 28 | 0x5c5b071U),
+ UINTMAX_MAX / 4241)
+P (2, 54,
+ (((((uintmax_t) 0x1841U << 28 | 0x2954499U)
+ << 28 | 0xbb949abU)
+ << 28 | 0x2b6170cU)
+ << 28 | 0x3f78d9bU),
+ UINTMAX_MAX / 4243)
+P (10, 74,
+ (((((uintmax_t) 0x67e4U << 28 | 0x8d552c3U)
+ << 28 | 0xde0d1f3U)
+ << 28 | 0xd74f461U)
+ << 28 | 0xfe6f5b5U),
+ UINTMAX_MAX / 4253)
+P (6, 78,
+ (((((uintmax_t) 0xa030U << 28 | 0x161ea7bU)
+ << 28 | 0x38ae8dbU)
+ << 28 | 0xc13f4b3U)
+ << 28 | 0x1f3230bU),
+ UINTMAX_MAX / 4259)
+P (2, 78,
+ (((((uintmax_t) 0xf2a9U << 28 | 0x8b90bb7U)
+ << 28 | 0x2eec1d1U)
+ << 28 | 0x420716eU)
+ << 28 | 0x3f1572dU),
+ UINTMAX_MAX / 4261)
+P (10, 78,
+ (((((uintmax_t) 0xa0c1U << 28 | 0xb926e68U)
+ << 28 | 0x69f8ed5U)
+ << 28 | 0xbe2fd4dU)
+ << 28 | 0x805464fU),
+ UINTMAX_MAX / 4271)
+P (2, 84,
+ (((((uintmax_t) 0xc4edU << 28 | 0x7ccb753U)
+ << 28 | 0xef76ec6U)
+ << 28 | 0x8b97c13U)
+ << 28 | 0x6943851U),
+ UINTMAX_MAX / 4273)
+P (10, 80,
+ (((((uintmax_t) 0x5305U << 28 | 0xada2a32U)
+ << 28 | 0xce35e9eU)
+ << 28 | 0x27918afU)
+ << 28 | 0x7cfb473U),
+ UINTMAX_MAX / 4283)
+P (6, 84,
+ (((((uintmax_t) 0x0b38U << 28 | 0xa4bcd9fU)
+ << 28 | 0xaa0cc5eU)
+ << 28 | 0xc8ab6c3U)
+ << 28 | 0x6ac7f41U),
+ UINTMAX_MAX / 4289)
+P (8, 94,
+ (((((uintmax_t) 0xc8f3U << 28 | 0x8c6bf3dU)
+ << 28 | 0x8adf696U)
+ << 28 | 0x4076331U)
+ << 28 | 0xdd90979U),
+ UINTMAX_MAX / 4297)
+P (30, 70,
+ (((((uintmax_t) 0x3ed4U << 28 | 0xdeb0e60U)
+ << 28 | 0x6fb3530U)
+ << 28 | 0x198eff7U)
+ << 28 | 0x7b002d7U),
+ UINTMAX_MAX / 4327)
+P (10, 72,
+ (((((uintmax_t) 0xe304U << 28 | 0x8b8a2eaU)
+ << 28 | 0x19da93aU)
+ << 28 | 0xf7cb958U)
+ << 28 | 0x3ece011U),
+ UINTMAX_MAX / 4337)
+P (2, 82,
+ (((((uintmax_t) 0x63b5U << 28 | 0xa908ca7U)
+ << 28 | 0xcb9bb34U)
+ << 28 | 0xce06f64U)
+ << 28 | 0x3d9883bU),
+ UINTMAX_MAX / 4339)
+P (10, 74,
+ (((((uintmax_t) 0xd58fU << 28 | 0x1940b11U)
+ << 28 | 0x0300879U)
+ << 28 | 0xf767e52U)
+ << 28 | 0x8708c55U),
+ UINTMAX_MAX / 4349)
+P (8, 84,
+ (((((uintmax_t) 0xa973U << 28 | 0xcee1454U)
+ << 28 | 0x5fa7a18U)
+ << 28 | 0x5332d2eU)
+ << 28 | 0xf2313cdU),
+ UINTMAX_MAX / 4357)
+P (6, 84,
+ (((((uintmax_t) 0xc544U << 28 | 0x1f37189U)
+ << 28 | 0x5bd3a43U)
+ << 28 | 0xb611b84U)
+ << 28 | 0xc8332a3U),
+ UINTMAX_MAX / 4363)
+P (10, 78,
+ (((((uintmax_t) 0xc201U << 28 | 0x49b4038U)
+ << 28 | 0x330c3c2U)
+ << 28 | 0xe215e4fU)
+ << 28 | 0x43bb63dU),
+ UINTMAX_MAX / 4373)
+P (18, 66,
+ (((((uintmax_t) 0xfcf7U << 28 | 0xe56a2a8U)
+ << 28 | 0xf4dd4f9U)
+ << 28 | 0x4b9dd22U)
+ << 28 | 0xce44e97U),
+ UINTMAX_MAX / 4391)
+P (6, 66,
+ (((((uintmax_t) 0xc364U << 28 | 0x3300862U)
+ << 28 | 0x47258d8U)
+ << 28 | 0x95834a1U)
+ << 28 | 0xdb166a5U),
+ UINTMAX_MAX / 4397)
+P (12, 72,
+ (((((uintmax_t) 0xa5f1U << 28 | 0xb76bd2bU)
+ << 28 | 0x5f83834U)
+ << 28 | 0x7d2f16dU)
+ << 28 | 0x19b8d09U),
+ UINTMAX_MAX / 4409)
+P (12, 62,
+ (((((uintmax_t) 0x9b97U << 28 | 0x89df750U)
+ << 28 | 0x6e4081bU)
+ << 28 | 0x54d4dc4U)
+ << 28 | 0x5b7d98dU),
+ UINTMAX_MAX / 4421)
+P (2, 70,
+ (((((uintmax_t) 0x612dU << 28 | 0xe5f44efU)
+ << 28 | 0x2839e11U)
+ << 28 | 0x7ac30d9U)
+ << 28 | 0xa044877U),
+ UINTMAX_MAX / 4423)
+P (18, 66,
+ (((((uintmax_t) 0x9811U << 28 | 0x1015369U)
+ << 28 | 0x6e9ec0eU)
+ << 28 | 0x10b78a6U)
+ << 28 | 0x7a526e9U),
+ UINTMAX_MAX / 4441)
+P (6, 66,
+ (((((uintmax_t) 0xa197U << 28 | 0x1cf4c64U)
+ << 28 | 0x2a99792U)
+ << 28 | 0xda68a81U)
+ << 28 | 0x8688a9fU),
+ UINTMAX_MAX / 4447)
+P (4, 66,
+ (((((uintmax_t) 0x0f02U << 28 | 0xeeeb01cU)
+ << 28 | 0x870bacfU)
+ << 28 | 0x2b6c87fU)
+ << 28 | 0x741f84bU),
+ UINTMAX_MAX / 4451)
+P (6, 62,
+ (((((uintmax_t) 0x8d2eU << 28 | 0x94fe559U)
+ << 28 | 0x50d09d2U)
+ << 28 | 0x64f9bd4U)
+ << 28 | 0x1e18ed9U),
+ UINTMAX_MAX / 4457)
+P (6, 60,
+ (((((uintmax_t) 0xa84bU << 28 | 0xb74450fU)
+ << 28 | 0xe38c973U)
+ << 28 | 0x3cbeaa9U)
+ << 28 | 0x7166d8fU),
+ UINTMAX_MAX / 4463)
+P (18, 66,
+ (((((uintmax_t) 0x495aU << 28 | 0xe4dcfaaU)
+ << 28 | 0xfd8b1c9U)
+ << 28 | 0xf475b02U)
+ << 28 | 0x1d22e81U),
+ UINTMAX_MAX / 4481)
+P (2, 66,
+ (((((uintmax_t) 0x6837U << 28 | 0x46fb256U)
+ << 28 | 0x74d6073U)
+ << 28 | 0x1f76f2eU)
+ << 28 | 0xc4c852bU),
+ UINTMAX_MAX / 4483)
+P (10, 68,
+ (((((uintmax_t) 0xf6ffU << 28 | 0x5f8d222U)
+ << 28 | 0x12931daU)
+ << 28 | 0xf6f0c97U)
+ << 28 | 0x8f69945U),
+ UINTMAX_MAX / 4493)
+P (14, 60,
+ (((((uintmax_t) 0xd49aU << 28 | 0xb982b2bU)
+ << 28 | 0x1c92174U)
+ << 28 | 0x9c8ad20U)
+ << 28 | 0xc61ec93U),
+ UINTMAX_MAX / 4507)
+P (6, 70,
+ (((((uintmax_t) 0x2f4fU << 28 | 0x04983ffU)
+ << 28 | 0xc5e9e09U)
+ << 28 | 0x307ff8bU)
+ << 28 | 0xd3c1261U),
+ UINTMAX_MAX / 4513)
+P (4, 74,
+ (((((uintmax_t) 0xadefU << 28 | 0x566dd5fU)
+ << 28 | 0x282eb33U)
+ << 28 | 0x4a69fb5U)
+ << 28 | 0xa486e2dU),
+ UINTMAX_MAX / 4517)
+P (2, 78,
+ (((((uintmax_t) 0xd118U << 28 | 0x137ccc9U)
+ << 28 | 0xe647f1fU)
+ << 28 | 0x36c7bf3U)
+ << 28 | 0x1578617U),
+ UINTMAX_MAX / 4519)
+P (4, 80,
+ (((((uintmax_t) 0x01cfU << 28 | 0xa9f7f67U)
+ << 28 | 0xdc3aa31U)
+ << 28 | 0xebbcc27U)
+ << 28 | 0x9ea6103U),
+ UINTMAX_MAX / 4523)
+P (24, 74,
+ (((((uintmax_t) 0x9c1fU << 28 | 0x4da38ddU)
+ << 28 | 0x2657442U)
+ << 28 | 0xe2aad11U)
+ << 28 | 0x9f466ebU),
+ UINTMAX_MAX / 4547)
+P (2, 88,
+ (((((uintmax_t) 0x41acU << 28 | 0x994bcdcU)
+ << 28 | 0xd3d2c10U)
+ << 28 | 0x6ec05a0U)
+ << 28 | 0xab1450dU),
+ UINTMAX_MAX / 4549)
+P (12, 78,
+ (((((uintmax_t) 0x556dU << 28 | 0x480324aU)
+ << 28 | 0x6d002b1U)
+ << 28 | 0xb38db92U)
+ << 28 | 0xa99e731U),
+ UINTMAX_MAX / 4561)
+P (6, 76,
+ (((((uintmax_t) 0x9c39U << 28 | 0x2ce6456U)
+ << 28 | 0x52d9278U)
+ << 28 | 0x4ae377eU)
+ << 28 | 0x67071e7U),
+ UINTMAX_MAX / 4567)
+P (16, 66,
+ (((((uintmax_t) 0xcdc8U << 28 | 0x79fec56U)
+ << 28 | 0x781893eU)
+ << 28 | 0x9e1471bU)
+ << 28 | 0xa6671d7U),
+ UINTMAX_MAX / 4583)
+P (8, 60,
+ (((((uintmax_t) 0x375eU << 28 | 0xf621586U)
+ << 28 | 0x1b19982U)
+ << 28 | 0xc29b59dU)
+ << 28 | 0x4d73d0fU),
+ UINTMAX_MAX / 4591)
+P (6, 60,
+ (((((uintmax_t) 0x75c7U << 28 | 0xfa35597U)
+ << 28 | 0xdcce0c2U)
+ << 28 | 0x3dd0712U)
+ << 28 | 0x8b5525dU),
+ UINTMAX_MAX / 4597)
+P (6, 60,
+ (((((uintmax_t) 0x4083U << 28 | 0xb2ce1ccU)
+ << 28 | 0xf1d164dU)
+ << 28 | 0x4e5ce0eU)
+ << 28 | 0x9245133U),
+ UINTMAX_MAX / 4603)
+P (18, 52,
+ (((((uintmax_t) 0x9d9cU << 28 | 0x64622aeU)
+ << 28 | 0x10824c8U)
+ << 28 | 0xfd1057cU)
+ << 28 | 0x09f8cc5U),
+ UINTMAX_MAX / 4621)
+P (16, 42,
+ (((((uintmax_t) 0x02b4U << 28 | 0x87cfdbcU)
+ << 28 | 0x89230eaU)
+ << 28 | 0x1516e94U)
+ << 28 | 0xf394035U),
+ UINTMAX_MAX / 4637)
+P (2, 52,
+ (((((uintmax_t) 0x32e1U << 28 | 0x4328c7fU)
+ << 28 | 0xce8e0b5U)
+ << 28 | 0xe3319c5U)
+ << 28 | 0x64ee9dfU),
+ UINTMAX_MAX / 4639)
+P (4, 60,
+ (((((uintmax_t) 0xf929U << 28 | 0xbd10602U)
+ << 28 | 0x894a612U)
+ << 28 | 0x6a69f90U)
+ << 28 | 0xd822d8bU),
+ UINTMAX_MAX / 4643)
+P (6, 72,
+ (((((uintmax_t) 0xa0bcU << 28 | 0x8b6d15cU)
+ << 28 | 0x03be950U)
+ << 28 | 0x1ed6348U)
+ << 28 | 0x857aa19U),
+ UINTMAX_MAX / 4649)
+P (2, 72,
+ (((((uintmax_t) 0xf169U << 28 | 0xf4a94f1U)
+ << 28 | 0x86231deU)
+ << 28 | 0x344a324U)
+ << 28 | 0xeee1c83U),
+ UINTMAX_MAX / 4651)
+P (6, 72,
+ (((((uintmax_t) 0xafdaU << 28 | 0x2e10d23U)
+ << 28 | 0x58ab11dU)
+ << 28 | 0xd9690cbU)
+ << 28 | 0x2c406d1U),
+ UINTMAX_MAX / 4657)
+P (6, 70,
+ (((((uintmax_t) 0x70eeU << 28 | 0x0c3017bU)
+ << 28 | 0x7881908U)
+ << 28 | 0xd6c5178U)
+ << 28 | 0xd5e4387U),
+ UINTMAX_MAX / 4663)
+P (10, 78,
+ (((((uintmax_t) 0x2b47U << 28 | 0x45bd0e3U)
+ << 28 | 0x051844cU)
+ << 28 | 0xea4050aU)
+ << 28 | 0x3e8fdc1U),
+ UINTMAX_MAX / 4673)
+P (6, 80,
+ (((((uintmax_t) 0x5aa8U << 28 | 0x9fc2b8dU)
+ << 28 | 0x1a891c1U)
+ << 28 | 0x14a06acU)
+ << 28 | 0xc83f777U),
+ UINTMAX_MAX / 4679)
+P (12, 92,
+ (((((uintmax_t) 0x834dU << 28 | 0x385f9c7U)
+ << 28 | 0x5a89320U)
+ << 28 | 0xb060ebcU)
+ << 28 | 0x0ea01dbU),
+ UINTMAX_MAX / 4691)
+P (12, 84,
+ (((((uintmax_t) 0xcbb0U << 28 | 0x86fea3aU)
+ << 28 | 0x06a40feU)
+ << 28 | 0x50045acU)
+ << 28 | 0xb78c99fU),
+ UINTMAX_MAX / 4703)
+P (18, 68,
+ (((((uintmax_t) 0x4bceU << 28 | 0xc35242bU)
+ << 28 | 0x29eaa29U)
+ << 28 | 0x1a68705U)
+ << 28 | 0xb196e91U),
+ UINTMAX_MAX / 4721)
+P (2, 70,
+ (((((uintmax_t) 0x1cf1U << 28 | 0xbea1a20U)
+ << 28 | 0x324cdc1U)
+ << 28 | 0x042c724U)
+ << 28 | 0x273e2bbU),
+ UINTMAX_MAX / 4723)
+P (6, 70,
+ (((((uintmax_t) 0x530aU << 28 | 0xaa16d83U)
+ << 28 | 0x622522cU)
+ << 28 | 0xee680bbU)
+ << 28 | 0x165b7c9U),
+ UINTMAX_MAX / 4729)
+P (4, 68,
+ (((((uintmax_t) 0x6dbeU << 28 | 0xc4fd598U)
+ << 28 | 0x42343fdU)
+ << 28 | 0x2ff9f12U)
+ << 28 | 0xe0776d5U),
+ UINTMAX_MAX / 4733)
+P (18, 62,
+ (((((uintmax_t) 0x9327U << 28 | 0xd1e0357U)
+ << 28 | 0x3cba016U)
+ << 28 | 0x6a5da63U)
+ << 28 | 0xaf2cc6fU),
+ UINTMAX_MAX / 4751)
+P (8, 58,
+ (((((uintmax_t) 0xfe7eU << 28 | 0x69c1b53U)
+ << 28 | 0xa5d7dedU)
+ << 28 | 0xd16a593U)
+ << 28 | 0x0408d27U),
+ UINTMAX_MAX / 4759)
+P (24, 48,
+ (((((uintmax_t) 0xdba8U << 28 | 0x6fc17c3U)
+ << 28 | 0xa04d12aU)
+ << 28 | 0xdf30c26U)
+ << 28 | 0x528844fU),
+ UINTMAX_MAX / 4783)
+P (4, 74,
+ (((((uintmax_t) 0x4928U << 28 | 0x7ba43b4U)
+ << 28 | 0x0f9d99aU)
+ << 28 | 0x48d6572U)
+ << 28 | 0xb5eec7bU),
+ UINTMAX_MAX / 4787)
+P (2, 82,
+ (((((uintmax_t) 0xfd7cU << 28 | 0xd1c2bd5U)
+ << 28 | 0x72fbc6eU)
+ << 28 | 0x8bf2877U)
+ << 28 | 0x503cb9dU),
+ UINTMAX_MAX / 4789)
+P (4, 84,
+ (((((uintmax_t) 0x1951U << 28 | 0x21b3d5eU)
+ << 28 | 0x975e0eaU)
+ << 28 | 0x27a191aU)
+ << 28 | 0x7045389U),
+ UINTMAX_MAX / 4793)
+P (6, 90,
+ (((((uintmax_t) 0xced1U << 28 | 0x00e827bU)
+ << 28 | 0x0325b6eU)
+ << 28 | 0xb091f34U)
+ << 28 | 0xdd45d3fU),
+ UINTMAX_MAX / 4799)
+P (2, 102,
+ (((((uintmax_t) 0xe394U << 28 | 0x4a02e12U)
+ << 28 | 0x05dd8dcU)
+ << 28 | 0x8a6cabbU)
+ << 28 | 0x2937d41U),
+ UINTMAX_MAX / 4801)
+P (12, 96,
+ (((((uintmax_t) 0x3e2dU << 28 | 0xa2eb33fU)
+ << 28 | 0x746e6bcU)
+ << 28 | 0x2f04f25U)
+ << 28 | 0x4922a05U),
+ UINTMAX_MAX / 4813)
+P (4, 102,
+ (((((uintmax_t) 0xf205U << 28 | 0xd890fadU)
+ << 28 | 0x84cf441U)
+ << 28 | 0x431f4d6U)
+ << 28 | 0xeb38631U),
+ UINTMAX_MAX / 4817)
+P (14, 100,
+ (((((uintmax_t) 0x7974U << 28 | 0xa2271b8U)
+ << 28 | 0x09c017bU)
+ << 28 | 0xd717435U)
+ << 28 | 0xa08291fU),
+ UINTMAX_MAX / 4831)
+P (30, 72,
+ (((((uintmax_t) 0xf434U << 28 | 0x0837312U)
+ << 28 | 0x2b4a342U)
+ << 28 | 0x32df9c9U)
+ << 28 | 0x1fc1a55U),
+ UINTMAX_MAX / 4861)
+P (10, 66,
+ (((((uintmax_t) 0x4c78U << 28 | 0x09ab985U)
+ << 28 | 0xc13f8a4U)
+ << 28 | 0x651e1d5U)
+ << 28 | 0x382eab7U),
+ UINTMAX_MAX / 4871)
+P (6, 66,
+ (((((uintmax_t) 0x9273U << 28 | 0x60376e4U)
+ << 28 | 0x8c0bf7cU)
+ << 28 | 0xfb5409dU)
+ << 28 | 0xe4cf3c5U),
+ UINTMAX_MAX / 4877)
+P (12, 62,
+ (((((uintmax_t) 0x47a1U << 28 | 0xbf627e6U)
+ << 28 | 0x7276dcdU)
+ << 28 | 0xd636fb0U)
+ << 28 | 0x68b9929U),
+ UINTMAX_MAX / 4889)
+P (14, 54,
+ (((((uintmax_t) 0x3f55U << 28 | 0x93b5db8U)
+ << 28 | 0xe2d01eeU)
+ << 28 | 0x8f95e74U)
+ << 28 | 0x0462c97U),
+ UINTMAX_MAX / 4903)
+P (6, 58,
+ (((((uintmax_t) 0x29aaU << 28 | 0xc9d12b8U)
+ << 28 | 0xb650349U)
+ << 28 | 0x0f97b3aU)
+ << 28 | 0x758b4a5U),
+ UINTMAX_MAX / 4909)
+P (10, 50,
+ (((((uintmax_t) 0x3c51U << 28 | 0x65394caU)
+ << 28 | 0x8d3eb64U)
+ << 28 | 0x1431563U)
+ << 28 | 0xc441287U),
+ UINTMAX_MAX / 4919)
+P (12, 42,
+ (((((uintmax_t) 0xf258U << 28 | 0x91c808bU)
+ << 28 | 0x8d292b7U)
+ << 28 | 0x43dad3eU)
+ << 28 | 0xc45916bU),
+ UINTMAX_MAX / 4931)
+P (2, 54,
+ (((((uintmax_t) 0x708fU << 28 | 0xa57e92aU)
+ << 28 | 0x8098c7bU)
+ << 28 | 0x188be8fU)
+ << 28 | 0x55c878dU),
+ UINTMAX_MAX / 4933)
+P (4, 56,
+ (((((uintmax_t) 0x983dU << 28 | 0xcf2775dU)
+ << 28 | 0xcd7ead8U)
+ << 28 | 0x05648b2U)
+ << 28 | 0xca54ef9U),
+ UINTMAX_MAX / 4937)
+P (6, 56,
+ (((((uintmax_t) 0x729cU << 28 | 0xb7c09bcU)
+ << 28 | 0x91a2776U)
+ << 28 | 0xdbe6eefU)
+ << 28 | 0x60123afU),
+ UINTMAX_MAX / 4943)
+P (8, 255,
+ (((((uintmax_t) 0xe8f0U << 28 | 0x5536727U)
+ << 28 | 0xa8b8137U)
+ << 28 | 0x11525e6U)
+ << 28 | 0xa9e8867U),
+ UINTMAX_MAX / 4951)
+P (6, 255,
+ (((((uintmax_t) 0xbdf2U << 28 | 0x781fd01U)
+ << 28 | 0x3014a85U)
+ << 28 | 0xc2215cbU)
+ << 28 | 0x383d8f5U),
+ UINTMAX_MAX / 4957)
+P (10, 255,
+ (((((uintmax_t) 0x0439U << 28 | 0xee5f8e3U)
+ << 28 | 0x30656e5U)
+ << 28 | 0x8f554c8U)
+ << 28 | 0x9825857U),
+ UINTMAX_MAX / 4967)
+P (2, 255,
+ (((((uintmax_t) 0x77adU << 28 | 0xfb283c9U)
+ << 28 | 0x63b0a8fU)
+ << 28 | 0xbd3b17cU)
+ << 28 | 0x01dacd9U),
+ UINTMAX_MAX / 4969)
+P (4, 255,
+ (((((uintmax_t) 0x5d7bU << 28 | 0xe851f3fU)
+ << 28 | 0x443554cU)
+ << 28 | 0x8c39dc7U)
+ << 28 | 0xaedee65U),
+ UINTMAX_MAX / 4973)
+P (14, 255,
+ (((((uintmax_t) 0x373cU << 28 | 0x1c8a99bU)
+ << 28 | 0x1412465U)
+ << 28 | 0x3ac6ddaU)
+ << 28 | 0x86cd3b3U),
+ UINTMAX_MAX / 4987)
+P (6, 255,
+ (((((uintmax_t) 0x5b50U << 28 | 0xa687decU)
+ << 28 | 0x6a07b0dU)
+ << 28 | 0x61c6791U)
+ << 28 | 0xa9c2c81U),
+ UINTMAX_MAX / 4993)
+P (6, 255,
+ (((((uintmax_t) 0x0b44U << 28 | 0x292c4bfU)
+ << 28 | 0xef9cdb6U)
+ << 28 | 0x27a3009U)
+ << 28 | 0x0354237U),
+ UINTMAX_MAX / 4999)
+
+#undef FIRST_OMITTED_PRIME
+#define FIRST_OMITTED_PRIME 5003
diff --git a/src/printenv.c b/src/printenv.c
index e06b704..e351d3a 100644
--- a/src/printenv.c
+++ b/src/printenv.c
@@ -1,10 +1,10 @@
/* printenv -- print all or part of environment
- Copyright (C) 1989-1997, 1999-2005 Free Software Foundation, Inc.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Usage: printenv [variable...]
@@ -34,41 +33,46 @@
#include <getopt.h>
#include "system.h"
-#include "error.h"
-#include "long-options.h"
/* Exit status for syntax errors, etc. */
enum { PRINTENV_FAILURE = 2 };
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "printenv"
-#define AUTHORS "David MacKenzie", "Richard Mlynarik"
+#define AUTHORS \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Richard Mlynarik")
-/* The name this program was run with. */
-char *program_name;
-
-extern char **environ;
+static struct option const longopts[] =
+{
+ {"null", no_argument, NULL, '0'},
+ {GETOPT_HELP_OPTION_DECL},
+ {GETOPT_VERSION_OPTION_DECL},
+ {NULL, 0, NULL, 0}
+};
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
-Usage: %s [VARIABLE]...\n\
- or: %s OPTION\n\
-If no environment VARIABLE specified, print them all.\n\
+Usage: %s [OPTION]... [VARIABLE]...\n\
+Print the values of the specified environment VARIABLE(s).\n\
+If no VARIABLE is specified, print name and value pairs for them all.\n\
\n\
"),
- program_name, program_name);
+ 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);
printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -80,9 +84,11 @@ main (int argc, char **argv)
char *ep, *ap;
int i;
bool ok;
+ int optc;
+ bool opt_nul_terminate_output = false;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -90,15 +96,24 @@ main (int argc, char **argv)
initialize_exit_failure (PRINTENV_FAILURE);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
- if (getopt_long (argc, argv, "+", NULL, NULL) != -1)
- usage (PRINTENV_FAILURE);
+ while ((optc = getopt_long (argc, argv, "+iu:0", longopts, NULL)) != -1)
+ {
+ switch (optc)
+ {
+ case '0':
+ opt_nul_terminate_output = true;
+ break;
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (PRINTENV_FAILURE);
+ }
+ }
if (optind >= argc)
{
for (env = environ; *env != NULL; ++env)
- puts (*env);
+ printf ("%s%c", *env, opt_nul_terminate_output ? '\0' : '\n');
ok = true;
}
else
@@ -106,29 +121,34 @@ main (int argc, char **argv)
int matches = 0;
for (i = optind; i < argc; ++i)
- {
- bool matched = false;
-
- for (env = environ; *env; ++env)
- {
- ep = *env;
- ap = argv[i];
- while (*ep != '\0' && *ap != '\0' && *ep++ == *ap++)
- {
- if (*ep == '=' && *ap == '\0')
- {
- puts (ep + 1);
- matched = true;
- break;
- }
- }
- }
-
- matches += matched;
- }
+ {
+ bool matched = false;
+
+ /* 'printenv a=b' is silent, even if 'a=b=c' is in environ. */
+ if (strchr (argv[i], '='))
+ continue;
+
+ for (env = environ; *env; ++env)
+ {
+ ep = *env;
+ 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');
+ matched = true;
+ break;
+ }
+ }
+ }
+
+ matches += matched;
+ }
ok = (matches == argc - optind);
}
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/printf.c b/src/printf.c
index 6205153..0885f8f 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -1,10 +1,10 @@
/* printf - format and print data
- Copyright (C) 1990-2007 Free Software Foundation, Inc.
+ Copyright (C) 1990-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Usage: printf format [argument...]
@@ -26,6 +25,7 @@
\a = alert (bell)
\b = backspace
\c = produce no further output
+ \e = escape
\f = form feed
\n = new line
\r = carriage return
@@ -41,7 +41,11 @@
%b = print an argument string, interpreting backslash escapes,
except that octal escapes are of the form \0 or \0ooo.
- The `format' argument is re-used as many times as necessary
+ %q = print an argument string in a format that can be
+ reused as shell input. Escaped characters used the proposed
+ POSIX $'' syntax supported by most shells.
+
+ The 'format' argument is re-used as many times as necessary
to convert all of the given arguments.
David MacKenzie <djm@gnu.ai.mit.edu> */
@@ -49,23 +53,22 @@
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
-#include <getopt.h>
#include "system.h"
#include "c-strtod.h"
#include "error.h"
-#include "long-options.h"
#include "quote.h"
#include "unicodeio.h"
+#include "xprintf.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "printf"
-#define AUTHORS "David MacKenzie"
+#define AUTHORS proper_name ("David MacKenzie")
#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')
+ (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
#define octtobin(c) ((c) - '0')
/* The value to return to the calling program. */
@@ -79,24 +82,20 @@ static bool posixly_correct;
static char const *const cfcc_msg =
N_("warning: %s: character(s) following character constant have been ignored");
-/* The name this program was run with. */
-char *program_name;
-
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s FORMAT [ARGUMENT]...\n\
or: %s OPTION\n\
"),
- program_name, program_name);
+ program_name, program_name);
fputs (_("\
-Print ARGUMENT(s) according to FORMAT.\n\
+Print ARGUMENT(s) according to FORMAT, or execute according to OPTION:\n\
\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
@@ -106,36 +105,37 @@ Print ARGUMENT(s) according to FORMAT.\n\
FORMAT controls the output as in C printf. Interpreted sequences are:\n\
\n\
\\\" double quote\n\
- \\NNN character with octal value NNN (1 to 3 digits)\n\
- \\\\ backslash\n\
"), stdout);
fputs (_("\
+ \\\\ backslash\n\
\\a alert (BEL)\n\
\\b backspace\n\
\\c produce no further output\n\
+ \\e escape\n\
\\f form feed\n\
-"), stdout);
- fputs (_("\
\\n new line\n\
\\r carriage return\n\
\\t horizontal tab\n\
\\v vertical tab\n\
"), stdout);
fputs (_("\
+ \\NNN byte with octal value NNN (1 to 3 digits)\n\
\\xHH byte with hexadecimal value HH (1 to 2 digits)\n\
\\uHHHH Unicode (ISO/IEC 10646) character with hex value HHHH (4 digits)\n\
\\UHHHHHHHH Unicode character with hex value HHHHHHHH (8 digits)\n\
"), stdout);
fputs (_("\
%% a single %\n\
- %b ARGUMENT as a string with `\\' escapes interpreted,\n\
+ %b ARGUMENT as a string with '\\' escapes interpreted,\n\
except that octal escapes are of the form \\0 or \\0NNN\n\
-\n\
+ %q ARGUMENT is printed in a format that can be reused as shell input,\n\
+ escaping non-printable characters with the proposed POSIX $'' syntax.\
+\n\n\
and all C format specifications ending with one of diouxXfeEgGcs, with\n\
ARGUMENTs converted to proper type first. Variable widths are handled.\n\
"), stdout);
printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -145,15 +145,15 @@ verify_numeric (const char *s, const char *end)
{
if (errno)
{
- error (0, errno, "%s", s);
+ error (0, errno, "%s", quote (s));
exit_status = EXIT_FAILURE;
}
else if (*end)
{
if (s == end)
- error (0, 0, _("%s: expected a numeric value"), s);
+ error (0, 0, _("%s: expected a numeric value"), quote (s));
else
- error (0, 0, _("%s: value not completely converted"), s);
+ error (0, 0, _("%s: value not completely converted"), quote (s));
exit_status = EXIT_FAILURE;
}
}
@@ -164,17 +164,17 @@ FUNC_NAME (char const *s) \
{ \
char *end; \
TYPE val; \
- \
- if (*s == '\"' || *s == '\'') \
+ \
+ if ((*s == '\"' || *s == '\'') && *(s + 1)) \
{ \
unsigned char ch = *++s; \
val = ch; \
/* If POSIXLY_CORRECT is not set, then give a warning that there \
- are characters following the character constant and that GNU \
- printf is ignoring those characters. If POSIXLY_CORRECT *is* \
- set, then don't give the warning. */ \
+ are characters following the character constant and that GNU \
+ printf is ignoring those characters. If POSIXLY_CORRECT *is* \
+ set, then don't give the warning. */ \
if (*++s != 0 && !posixly_correct) \
- error (0, 0, _(cfcc_msg), s); \
+ error (0, 0, _(cfcc_msg), s); \
} \
else \
{ \
@@ -205,6 +205,9 @@ print_esc_char (char c)
case 'c': /* Cancel the rest of the output. */
exit (EXIT_SUCCESS);
break;
+ case 'e': /* Escape. */
+ putchar ('\x1B');
+ break;
case 'f': /* Form feed. */
putchar ('\f');
break;
@@ -243,11 +246,11 @@ print_esc (const char *escstart, bool octal_0)
{
/* A hexadecimal \xhh escape sequence must have 1 or 2 hex. digits. */
for (esc_length = 0, ++p;
- esc_length < 2 && isxdigit (to_uchar (*p));
- ++esc_length, ++p)
- esc_value = esc_value * 16 + hextobin (*p);
+ esc_length < 2 && isxdigit (to_uchar (*p));
+ ++esc_length, ++p)
+ esc_value = esc_value * 16 + hextobin (*p);
if (esc_length == 0)
- error (EXIT_FAILURE, 0, _("missing hexadecimal number in escape"));
+ error (EXIT_FAILURE, 0, _("missing hexadecimal number in escape"));
putchar (esc_value);
}
else if (isodigit (*p))
@@ -256,12 +259,12 @@ print_esc (const char *escstart, bool octal_0)
Allow \ooo if octal_0 && *p != '0'; this is an undocumented
extension to POSIX that is compatible with Bash 2.05b. */
for (esc_length = 0, p += octal_0 && *p == '0';
- esc_length < 3 && isodigit (*p);
- ++esc_length, ++p)
- esc_value = esc_value * 8 + octtobin (*p);
+ esc_length < 3 && isodigit (*p);
+ ++esc_length, ++p)
+ esc_value = esc_value * 8 + octtobin (*p);
putchar (esc_value);
}
- else if (*p && strchr ("\"\\abcfnrtv", *p))
+ else if (*p && strchr ("\"\\abcefnrtv", *p))
print_esc_char (*p++);
else if (*p == 'u' || *p == 'U')
{
@@ -270,24 +273,24 @@ print_esc (const char *escstart, bool octal_0)
uni_value = 0;
for (esc_length = (esc_char == 'u' ? 4 : 8), ++p;
- esc_length > 0;
- --esc_length, ++p)
- {
- if (! isxdigit (to_uchar (*p)))
- error (EXIT_FAILURE, 0, _("missing hexadecimal number in escape"));
- uni_value = uni_value * 16 + hextobin (*p);
- }
+ esc_length > 0;
+ --esc_length, ++p)
+ {
+ if (! isxdigit (to_uchar (*p)))
+ error (EXIT_FAILURE, 0, _("missing hexadecimal number in escape"));
+ uni_value = uni_value * 16 + hextobin (*p);
+ }
/* A universal character name shall not specify a character short
- identifier in the range 00000000 through 00000020, 0000007F through
- 0000009F, or 0000D800 through 0000DFFF inclusive. A universal
- character name shall not designate a character in the required
- character set. */
+ identifier in the range 00000000 through 00000020, 0000007F through
+ 0000009F, or 0000D800 through 0000DFFF inclusive. A universal
+ character name shall not designate a character in the required
+ character set. */
if ((uni_value <= 0x9f
- && uni_value != 0x24 && uni_value != 0x40 && uni_value != 0x60)
- || (uni_value >= 0xd800 && uni_value <= 0xdfff))
- error (EXIT_FAILURE, 0, _("invalid universal character name \\%c%0*x"),
- esc_char, (esc_char == 'u' ? 4 : 8), uni_value);
+ && uni_value != 0x24 && uni_value != 0x40 && uni_value != 0x60)
+ || (uni_value >= 0xd800 && uni_value <= 0xdfff))
+ error (EXIT_FAILURE, 0, _("invalid universal character name \\%c%0*x"),
+ esc_char, (esc_char == 'u' ? 4 : 8), uni_value);
print_unicode_char (stdout, uni_value, 0);
}
@@ -295,10 +298,10 @@ print_esc (const char *escstart, bool octal_0)
{
putchar ('\\');
if (*p)
- {
- putchar (*p);
- p++;
- }
+ {
+ putchar (*p);
+ p++;
+ }
}
return p - escstart - 1;
}
@@ -325,9 +328,9 @@ print_esc_string (const char *str)
static void
print_direc (const char *start, size_t length, char conversion,
- bool have_field_width, int field_width,
- bool have_precision, int precision,
- char const *argument)
+ bool have_field_width, int field_width,
+ bool have_precision, int precision,
+ char const *argument)
{
char *p; /* Null-terminated copy of % directive. */
@@ -342,20 +345,20 @@ print_direc (const char *start, size_t length, char conversion,
switch (conversion)
{
case 'd': case 'i': case 'o': case 'u': case 'x': case 'X':
- length_modifier = PRIdMAX;
- length_modifier_len = sizeof PRIdMAX - 2;
- break;
+ length_modifier = PRIdMAX;
+ length_modifier_len = sizeof PRIdMAX - 2;
+ break;
case 'a': case 'e': case 'f': case 'g':
case 'A': case 'E': case 'F': case 'G':
- length_modifier = "L";
- length_modifier_len = 1;
- break;
+ length_modifier = "L";
+ length_modifier_len = 1;
+ break;
default:
- length_modifier = start; /* Any valid pointer will do. */
- length_modifier_len = 0;
- break;
+ length_modifier = start; /* Any valid pointer will do. */
+ length_modifier_len = 0;
+ break;
}
p = xmalloc (length + length_modifier_len + 2);
@@ -370,21 +373,21 @@ print_direc (const char *start, size_t length, char conversion,
case 'd':
case 'i':
{
- intmax_t arg = vstrtoimax (argument);
- if (!have_field_width)
- {
- if (!have_precision)
- printf (p, arg);
- else
- printf (p, precision, arg);
- }
- else
- {
- if (!have_precision)
- printf (p, field_width, arg);
- else
- printf (p, field_width, precision, arg);
- }
+ intmax_t arg = vstrtoimax (argument);
+ if (!have_field_width)
+ {
+ if (!have_precision)
+ xprintf (p, arg);
+ else
+ xprintf (p, precision, arg);
+ }
+ else
+ {
+ if (!have_precision)
+ xprintf (p, field_width, arg);
+ else
+ xprintf (p, field_width, precision, arg);
+ }
}
break;
@@ -393,21 +396,21 @@ print_direc (const char *start, size_t length, char conversion,
case 'x':
case 'X':
{
- uintmax_t arg = vstrtoumax (argument);
- if (!have_field_width)
- {
- if (!have_precision)
- printf (p, arg);
- else
- printf (p, precision, arg);
- }
- else
- {
- if (!have_precision)
- printf (p, field_width, arg);
- else
- printf (p, field_width, precision, arg);
- }
+ uintmax_t arg = vstrtoumax (argument);
+ if (!have_field_width)
+ {
+ if (!have_precision)
+ xprintf (p, arg);
+ else
+ xprintf (p, precision, arg);
+ }
+ else
+ {
+ if (!have_precision)
+ xprintf (p, field_width, arg);
+ else
+ xprintf (p, field_width, precision, arg);
+ }
}
break;
@@ -420,46 +423,46 @@ print_direc (const char *start, size_t length, char conversion,
case 'g':
case 'G':
{
- long double arg = vstrtold (argument);
- if (!have_field_width)
- {
- if (!have_precision)
- printf (p, arg);
- else
- printf (p, precision, arg);
- }
- else
- {
- if (!have_precision)
- printf (p, field_width, arg);
- else
- printf (p, field_width, precision, arg);
- }
+ long double arg = vstrtold (argument);
+ if (!have_field_width)
+ {
+ if (!have_precision)
+ xprintf (p, arg);
+ else
+ xprintf (p, precision, arg);
+ }
+ else
+ {
+ if (!have_precision)
+ xprintf (p, field_width, arg);
+ else
+ xprintf (p, field_width, precision, arg);
+ }
}
break;
case 'c':
if (!have_field_width)
- printf (p, *argument);
+ xprintf (p, *argument);
else
- printf (p, field_width, *argument);
+ xprintf (p, field_width, *argument);
break;
case 's':
if (!have_field_width)
- {
- if (!have_precision)
- printf (p, argument);
- else
- printf (p, precision, argument);
- }
+ {
+ if (!have_precision)
+ xprintf (p, argument);
+ else
+ xprintf (p, precision, argument);
+ }
else
- {
- if (!have_precision)
- printf (p, field_width, argument);
- else
- printf (p, field_width, precision, argument);
- }
+ {
+ if (!have_precision)
+ xprintf (p, field_width, argument);
+ else
+ xprintf (p, field_width, precision, argument);
+ }
break;
}
@@ -467,14 +470,14 @@ print_direc (const char *start, size_t length, char conversion,
}
/* Print the text in FORMAT, using ARGV (with ARGC elements) for
- arguments to any `%' directives.
+ arguments to any '%' directives.
Return the number of elements of ARGV used. */
static int
print_formatted (const char *format, int argc, char **argv)
{
int save_argc = argc; /* Preserve original value. */
- const char *f; /* Pointer into `format'. */
+ const char *f; /* Pointer into 'format'. */
const char *direc_start; /* Start of % directive. */
size_t direc_length; /* Length of % directive. */
bool have_field_width; /* True if FIELD_WIDTH is valid. */
@@ -486,146 +489,158 @@ print_formatted (const char *format, int argc, char **argv)
for (f = format; *f; ++f)
{
switch (*f)
- {
- case '%':
- direc_start = f++;
- direc_length = 1;
- have_field_width = have_precision = false;
- if (*f == '%')
- {
- putchar ('%');
- break;
- }
- if (*f == 'b')
- {
- /* FIXME: Field width and precision are not supported
- for %b, even though POSIX requires it. */
- if (argc > 0)
- {
- print_esc_string (*argv);
- ++argv;
- --argc;
- }
- break;
- }
-
- memset (ok, 0, sizeof ok);
- ok['a'] = ok['A'] = ok['c'] = ok['d'] = ok['e'] = ok['E'] =
- ok['f'] = ok['F'] = ok['g'] = ok['G'] = ok['i'] = ok['o'] =
- ok['s'] = ok['u'] = ok['x'] = ok['X'] = 1;
-
- for (;; f++, direc_length++)
- switch (*f)
- {
+ {
+ case '%':
+ direc_start = f++;
+ direc_length = 1;
+ have_field_width = have_precision = false;
+ if (*f == '%')
+ {
+ putchar ('%');
+ break;
+ }
+ if (*f == 'b')
+ {
+ /* FIXME: Field width and precision are not supported
+ for %b, even though POSIX requires it. */
+ if (argc > 0)
+ {
+ print_esc_string (*argv);
+ ++argv;
+ --argc;
+ }
+ break;
+ }
+
+ if (*f == 'q')
+ {
+ if (argc > 0)
+ {
+ fputs (quotearg_style (shell_escape_quoting_style, *argv),
+ stdout);
+ ++argv;
+ --argc;
+ }
+ break;
+ }
+
+ memset (ok, 0, sizeof ok);
+ ok['a'] = ok['A'] = ok['c'] = ok['d'] = ok['e'] = ok['E'] =
+ ok['f'] = ok['F'] = ok['g'] = ok['G'] = ok['i'] = ok['o'] =
+ ok['s'] = ok['u'] = ok['x'] = ok['X'] = 1;
+
+ for (;; f++, direc_length++)
+ switch (*f)
+ {
#if (__GLIBC__ == 2 && 2 <= __GLIBC_MINOR__) || 3 <= __GLIBC__
- case 'I':
+ case 'I':
#endif
- case '\'':
- ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E'] =
- ok['o'] = ok['s'] = ok['x'] = ok['X'] = 0;
- break;
- case '-': case '+': case ' ':
- break;
- case '#':
- ok['c'] = ok['d'] = ok['i'] = ok['s'] = ok['u'] = 0;
- break;
- case '0':
- ok['c'] = ok['s'] = 0;
- break;
- default:
- goto no_more_flag_characters;
- }
- no_more_flag_characters:;
-
- if (*f == '*')
- {
- ++f;
- ++direc_length;
- if (argc > 0)
- {
- intmax_t width = vstrtoimax (*argv);
- if (INT_MIN <= width && width <= INT_MAX)
- field_width = width;
- else
- error (EXIT_FAILURE, 0, _("invalid field width: %s"),
- *argv);
- ++argv;
- --argc;
- }
- else
- field_width = 0;
- have_field_width = true;
- }
- else
- while (ISDIGIT (*f))
- {
- ++f;
- ++direc_length;
- }
- if (*f == '.')
- {
- ++f;
- ++direc_length;
- ok['c'] = 0;
- if (*f == '*')
- {
- ++f;
- ++direc_length;
- if (argc > 0)
- {
- intmax_t prec = vstrtoimax (*argv);
- if (prec < 0)
- {
- /* A negative precision is taken as if the
- precision were omitted, so -1 is safe
- here even if prec < INT_MIN. */
- precision = -1;
- }
- else if (INT_MAX < prec)
- error (EXIT_FAILURE, 0, _("invalid precision: %s"),
- *argv);
- else
- precision = prec;
- ++argv;
- --argc;
- }
- else
- precision = 0;
- have_precision = true;
- }
- else
- while (ISDIGIT (*f))
- {
- ++f;
- ++direc_length;
- }
- }
-
- while (*f == 'l' || *f == 'L' || *f == 'h'
- || *f == 'j' || *f == 't' || *f == 'z')
- ++f;
-
- {
- unsigned char conversion = *f;
- if (! ok[conversion])
- error (EXIT_FAILURE, 0,
- _("%.*s: invalid conversion specification"),
- (int) (f + 1 - direc_start), direc_start);
- }
-
- print_direc (direc_start, direc_length, *f,
- have_field_width, field_width,
- have_precision, precision,
- (argc <= 0 ? "" : (argc--, *argv++)));
- break;
-
- case '\\':
- f += print_esc (f, false);
- break;
-
- default:
- putchar (*f);
- }
+ case '\'':
+ ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E'] =
+ ok['o'] = ok['s'] = ok['x'] = ok['X'] = 0;
+ break;
+ case '-': case '+': case ' ':
+ break;
+ case '#':
+ ok['c'] = ok['d'] = ok['i'] = ok['s'] = ok['u'] = 0;
+ break;
+ case '0':
+ ok['c'] = ok['s'] = 0;
+ break;
+ default:
+ goto no_more_flag_characters;
+ }
+ no_more_flag_characters:
+
+ if (*f == '*')
+ {
+ ++f;
+ ++direc_length;
+ if (argc > 0)
+ {
+ intmax_t width = vstrtoimax (*argv);
+ if (INT_MIN <= width && width <= INT_MAX)
+ field_width = width;
+ else
+ error (EXIT_FAILURE, 0, _("invalid field width: %s"),
+ quote (*argv));
+ ++argv;
+ --argc;
+ }
+ else
+ field_width = 0;
+ have_field_width = true;
+ }
+ else
+ while (ISDIGIT (*f))
+ {
+ ++f;
+ ++direc_length;
+ }
+ if (*f == '.')
+ {
+ ++f;
+ ++direc_length;
+ ok['c'] = 0;
+ if (*f == '*')
+ {
+ ++f;
+ ++direc_length;
+ if (argc > 0)
+ {
+ intmax_t prec = vstrtoimax (*argv);
+ if (prec < 0)
+ {
+ /* A negative precision is taken as if the
+ precision were omitted, so -1 is safe
+ here even if prec < INT_MIN. */
+ precision = -1;
+ }
+ else if (INT_MAX < prec)
+ error (EXIT_FAILURE, 0, _("invalid precision: %s"),
+ quote (*argv));
+ else
+ precision = prec;
+ ++argv;
+ --argc;
+ }
+ else
+ precision = 0;
+ have_precision = true;
+ }
+ else
+ while (ISDIGIT (*f))
+ {
+ ++f;
+ ++direc_length;
+ }
+ }
+
+ while (*f == 'l' || *f == 'L' || *f == 'h'
+ || *f == 'j' || *f == 't' || *f == 'z')
+ ++f;
+
+ {
+ unsigned char conversion = *f;
+ if (! ok[conversion])
+ error (EXIT_FAILURE, 0,
+ _("%.*s: invalid conversion specification"),
+ (int) (f + 1 - direc_start), direc_start);
+ }
+
+ print_direc (direc_start, direc_length, *f,
+ have_field_width, field_width,
+ have_precision, precision,
+ (argc <= 0 ? "" : (argc--, *argv++)));
+ break;
+
+ case '\\':
+ f += print_esc (f, false);
+ break;
+
+ default:
+ putchar (*f);
+ }
}
return save_argc - argc;
@@ -638,7 +653,7 @@ main (int argc, char **argv)
int args_used;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -649,11 +664,23 @@ main (int argc, char **argv)
posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
+ /* We directly parse options, rather than use parse_long_options, in
+ order to avoid accepting abbreviations. */
+ if (argc == 2)
+ {
+ if (STREQ (argv[1], "--help"))
+ usage (EXIT_SUCCESS);
+
+ if (STREQ (argv[1], "--version"))
+ {
+ version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version, AUTHORS,
+ (char *) NULL);
+ return EXIT_SUCCESS;
+ }
+ }
/* The above handles --help and --version.
- Since there is no other invocation of getopt, handle `--' here. */
+ Since there is no other invocation of getopt, handle '--' here. */
if (1 < argc && STREQ (argv[1], "--"))
{
--argc;
@@ -680,8 +707,8 @@ main (int argc, char **argv)
if (argc > 0)
error (0, 0,
- _("warning: ignoring excess arguments, starting with %s"),
- quote (argv[0]));
+ _("warning: ignoring excess arguments, starting with %s"),
+ quote (argv[0]));
- exit (exit_status);
+ return exit_status;
}
diff --git a/src/prog-fprintf.c b/src/prog-fprintf.c
new file mode 100644
index 0000000..9fffec9
--- /dev/null
+++ b/src/prog-fprintf.c
@@ -0,0 +1,37 @@
+/* prog-fprintf.c - common formating output functions and definitions
+ Copyright (C) 2008-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+#include <config.h>
+#include <stdarg.h>
+#include <sys/types.h>
+
+#include "system.h"
+
+#include "prog-fprintf.h"
+
+/* Display program name followed by variable list.
+ Used for e.g. verbose output */
+void
+prog_fprintf (FILE *fp, char const *fmt, ...)
+{
+ va_list ap;
+ fputs (program_name, fp);
+ fputs (": ", fp);
+ va_start (ap, fmt);
+ vfprintf (fp, fmt, ap);
+ va_end (ap);
+ fputc ('\n', fp);
+}
diff --git a/src/prog-fprintf.h b/src/prog-fprintf.h
new file mode 100644
index 0000000..a3221e0
--- /dev/null
+++ b/src/prog-fprintf.h
@@ -0,0 +1,25 @@
+/* prog-fprintf.h - common formating output functions and definitions
+ Copyright (C) 2008-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+#ifndef PROG_FPRINTF_H
+# define PROG_FPRINTF_H
+
+# include <stdio.h>
+
+extern void prog_fprintf (FILE *fp, char const *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 2, 3)));
+
+#endif
diff --git a/src/ptx.c b/src/ptx.c
index 9c35596..d19c66e 100644
--- a/src/ptx.c
+++ b/src/ptx.c
@@ -1,51 +1,51 @@
/* Permuted index for GNU, with keywords in their context.
- Copyright (C) 1990, 1991, 1993, 1998-2006 Free Software Foundation, Inc.
+ Copyright (C) 1990-2016 Free Software Foundation, Inc.
François Pinard <pinard@iro.umontreal.ca>, 1988.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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.
+ 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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
François Pinard <pinard@iro.umontreal.ca> */
#include <config.h>
-#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#include "system.h"
+#include <regex.h>
#include "argmatch.h"
#include "diacrit.h"
#include "error.h"
+#include "fadvise.h"
#include "quote.h"
-#include "quotearg.h"
-#include "regex.h"
+#include "read-file.h"
+#include "stdio--.h"
#include "xstrtol.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "ptx"
-/* Note to translator: Please translate "F. Pinard" to "François
- Pinard" if "ç" (c-with-cedilla) is available in the
- translation's character set and encoding. */
-#define AUTHORS _("F. Pinard")
+/* TRANSLATORS: Please translate "F. Pinard" to "François Pinard"
+ if "ç" (c-with-cedilla) is available in the translation's character
+ set and encoding. */
+#define AUTHORS proper_name_utf8 ("F. Pinard", "Fran\xc3\xa7ois Pinard")
/* 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')
+ : (C) >= 'A' && (C) <= 'F' ? (C)-'A'+10 : (C)-'0')
#define OCTTOBIN(C) ((C) - '0')
/* Debugging the memory allocator. */
@@ -54,7 +54,7 @@
# define MALLOC_FUNC_CHECK 1
# include <dmalloc.h>
#endif
-
+
/* Global definitions. */
/* FIXME: There are many unchecked integer overflows in this file,
@@ -62,42 +62,32 @@
options. Many of the "int" values below should be "size_t" or
something else like that. */
-/* Reallocation step when swallowing non regular files. The value is not
- the actual reallocation step, but its base two logarithm. */
-#define SWALLOW_REALLOC_LOG 12
-
-/* Imported from "regex.c". */
-#define Sword 1
-
-/* The name this program was run with. */
-char *program_name;
-
/* Program options. */
enum Format
{
UNKNOWN_FORMAT, /* output format still unknown */
DUMB_FORMAT, /* output for a dumb terminal */
- ROFF_FORMAT, /* output for `troff' or `nroff' */
- TEX_FORMAT /* output for `TeX' or `LaTeX' */
+ ROFF_FORMAT, /* output for 'troff' or 'nroff' */
+ TEX_FORMAT /* output for 'TeX' or 'LaTeX' */
};
static bool gnu_extensions = true; /* trigger all GNU extensions */
-static bool auto_reference = false; /* refs are `file_name:line_number:' */
+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 int line_width = 72; /* output line width in characters */
static int gap_size = 3; /* number of spaces between output fields */
static const char *truncation_string = "/";
- /* string used to mark line truncations */
+ /* string used to mark line truncations */
static const char *macro_name = "xx"; /* macro name for roff or TeX output */
static enum Format output_format = UNKNOWN_FORMAT;
- /* output format */
+ /* output format */
static bool ignore_case = false; /* fold lower to upper for sorting */
-static const char *break_file = NULL; /* name of the `Break characters' file */
-static const char *only_file = NULL; /* name of the `Only words' file */
-static const char *ignore_file = NULL; /* name of the `Ignore words' file */
+static const char *break_file = NULL; /* name of the 'Break chars' file */
+static const char *only_file = NULL; /* name of the 'Only words' file */
+static const char *ignore_file = NULL; /* name of the 'Ignore words' file */
/* Options that use regular expressions. */
struct regex_data
@@ -173,9 +163,9 @@ static WORD_TABLE only_table; /* table of words to select */
static int number_input_files; /* number of text input files */
static int total_line_count; /* total number of lines seen so far */
static const char **input_file_name; /* array of text input file names */
-static int *file_line_count; /* array of `total_line_count' values at end */
+static int *file_line_count; /* array of 'total_line_count' values at end */
-static BLOCK text_buffer; /* file to study */
+static BLOCK *text_buffers; /* files to study */
/* SKIP_NON_WHITE used only for getting or skipping the reference. */
@@ -208,14 +198,14 @@ static BLOCK text_buffer; /* file to study */
/* Occurrences table.
- The `keyword' pointer provides the central word, which is surrounded
- by a left context and a right context. The `keyword' and `length'
+ The 'keyword' pointer provides the central word, which is surrounded
+ by a left context and a right context. The 'keyword' and 'length'
field allow full 8-bit characters keys, even including NULs. At other
- places in this program, the name `keyafter' refers to the keyword
+ places in this program, the name 'keyafter' refers to the keyword
followed by its right context.
The left context does not extend, towards the beginning of the file,
- further than a distance given by the `left' value. This value is
+ further than a distance given by the 'left' value. This value is
relative to the keyword beginning, it is usually negative. This
insures that, except for white space, we will never have to backward
scan the source text, when it is time to generate the final output
@@ -223,12 +213,12 @@ static BLOCK text_buffer; /* file to study */
The right context, indirectly attainable through the keyword end, does
not extend, towards the end of the file, further than a distance given
- by the `right' value. This value is relative to the keyword
+ by the 'right' value. This value is relative to the keyword
beginning, it is usually positive.
- When automatic references are used, the `reference' value is the
+ When automatic references are used, the 'reference' value is the
overall line number in all input files read so far, in this case, it
- is of type (int). When input references are used, the `reference'
+ is of type (int). When input references are used, the 'reference'
value indicates the distance between the keyword beginning and the
start of the reference field, it is of type (DELTA) and usually
negative. */
@@ -241,6 +231,7 @@ typedef struct
DELTA left; /* distance to left context start */
DELTA right; /* distance to right context end */
int reference; /* reference descriptor */
+ size_t file_index; /* corresponding file */
}
OCCURS;
@@ -260,11 +251,11 @@ static char edited_flag[CHAR_SET_SIZE];
static int half_line_width; /* half of line width, reference excluded */
static int before_max_width; /* maximum width of before field */
static int keyafter_max_width; /* maximum width of keyword-and-after field */
-static int truncation_string_length;/* length of string used to flag truncation */
+static int truncation_string_length;/* length of string that flags truncation */
/* When context is limited by lines, wraparound may happen on final output:
- the `head' pointer gives access to some supplementary left context which
- will be seen at the end of the output line, the `tail' pointer gives
+ the 'head' pointer gives access to some supplementary left context which
+ will be seen at the end of the output line, the 'tail' pointer gives
access to some supplementary right context which will be seen at the
beginning of the output line. */
@@ -281,7 +272,7 @@ static BLOCK head; /* head field */
static int head_truncation; /* flag truncation before the head field */
static BLOCK reference; /* reference field for input reference mode */
-
+
/* Miscellaneous routines. */
/* Diagnose an error in the regular expression matcher. Then exit. */
@@ -311,91 +302,97 @@ copy_unescaped_string (const char *string)
cursor = result;
while (*string)
- if (*string == '\\')
- {
- string++;
- switch (*string)
- {
- case 'x': /* \xhhh escape, 3 chars maximum */
- value = 0;
- for (length = 0, string++;
- length < 3 && isxdigit (to_uchar (*string));
- length++, string++)
- value = value * 16 + HEXTOBIN (*string);
- if (length == 0)
- {
- *cursor++ = '\\';
- *cursor++ = 'x';
- }
- else
- *cursor++ = value;
- break;
-
- case '0': /* \0ooo escape, 3 chars maximum */
- value = 0;
- for (length = 0, string++;
- length < 3 && ISODIGIT (*string);
- length++, string++)
- value = value * 8 + OCTTOBIN (*string);
- *cursor++ = value;
- break;
-
- case 'a': /* alert */
+ {
+ if (*string == '\\')
+ {
+ string++;
+ switch (*string)
+ {
+ case 'x': /* \xhhh escape, 3 chars maximum */
+ value = 0;
+ for (length = 0, string++;
+ length < 3 && isxdigit (to_uchar (*string));
+ length++, string++)
+ value = value * 16 + HEXTOBIN (*string);
+ if (length == 0)
+ {
+ *cursor++ = '\\';
+ *cursor++ = 'x';
+ }
+ else
+ *cursor++ = value;
+ break;
+
+ case '0': /* \0ooo escape, 3 chars maximum */
+ value = 0;
+ for (length = 0, string++;
+ length < 3 && ISODIGIT (*string);
+ length++, string++)
+ value = value * 8 + OCTTOBIN (*string);
+ *cursor++ = value;
+ break;
+
+ case 'a': /* alert */
#if __STDC__
- *cursor++ = '\a';
+ *cursor++ = '\a';
#else
- *cursor++ = 7;
+ *cursor++ = 7;
#endif
- string++;
- break;
-
- case 'b': /* backspace */
- *cursor++ = '\b';
- string++;
- break;
-
- case 'c': /* cancel the rest of the output */
- while (*string)
- string++;
- break;
-
- case 'f': /* form feed */
- *cursor++ = '\f';
- string++;
- break;
-
- case 'n': /* new line */
- *cursor++ = '\n';
- string++;
- break;
-
- case 'r': /* carriage return */
- *cursor++ = '\r';
- string++;
- break;
-
- case 't': /* horizontal tab */
- *cursor++ = '\t';
- string++;
- break;
-
- case 'v': /* vertical tab */
+ string++;
+ break;
+
+ case 'b': /* backspace */
+ *cursor++ = '\b';
+ string++;
+ break;
+
+ case 'c': /* cancel the rest of the output */
+ while (*string)
+ string++;
+ break;
+
+ case 'f': /* form feed */
+ *cursor++ = '\f';
+ string++;
+ break;
+
+ case 'n': /* new line */
+ *cursor++ = '\n';
+ string++;
+ break;
+
+ case 'r': /* carriage return */
+ *cursor++ = '\r';
+ string++;
+ break;
+
+ case 't': /* horizontal tab */
+ *cursor++ = '\t';
+ string++;
+ break;
+
+ case 'v': /* vertical tab */
#if __STDC__
- *cursor++ = '\v';
+ *cursor++ = '\v';
#else
- *cursor++ = 11;
+ *cursor++ = 11;
#endif
- string++;
- break;
-
- default:
- *cursor++ = '\\';
- *cursor++ = *string++;
- break;
- }
- }
- else
- *cursor++ = *string++;
+ string++;
+ break;
+
+ case '\0': /* lone backslash at end of string */
+ /* ignore it */
+ break;
+
+ default:
+ *cursor++ = '\\';
+ *cursor++ = *string++;
+ break;
+ }
+ }
+ else
+ *cursor++ = *string++;
+ }
*cursor = '\0';
return result;
@@ -421,8 +418,8 @@ compile_regex (struct regex_data *regex)
if (message)
error (EXIT_FAILURE, 0, _("%s (for regexp %s)"), message, quote (string));
- /* The fastmap should be compiled before `re_match'. The following
- call is not mandatory, because `re_search' is always called sooner,
+ /* The fastmap should be compiled before 're_match'. The following
+ call is not mandatory, because 're_search' is always called sooner,
and it compiles the fastmap if this has not been done yet. */
re_compile_fastmap (pattern);
@@ -454,9 +451,9 @@ initialize_regex (void)
if (context_regex.string)
{
if (!*context_regex.string)
- context_regex.string = NULL;
+ context_regex.string = NULL;
}
- else if (gnu_extensions & !input_reference)
+ else if (gnu_extensions && !input_reference)
context_regex.string = "[.?!][]\"')}]*\\($\\|\t\\| \\)[ \t\n]*";
else
context_regex.string = "\n";
@@ -477,23 +474,23 @@ initialize_regex (void)
else if (!break_file)
{
if (gnu_extensions)
- {
+ {
- /* Simulate \w+. */
+ /* Simulate \w+. */
- for (character = 0; character < CHAR_SET_SIZE; character++)
- word_fastmap[character] = !! isalpha (character);
- }
+ for (character = 0; character < CHAR_SET_SIZE; character++)
+ word_fastmap[character] = !! isalpha (character);
+ }
else
- {
+ {
- /* Simulate [^ \t\n]+. */
+ /* Simulate [^ \t\n]+. */
- memset (word_fastmap, 1, CHAR_SET_SIZE);
- word_fastmap[' '] = 0;
- word_fastmap['\t'] = 0;
- word_fastmap['\n'] = 0;
- }
+ memset (word_fastmap, 1, CHAR_SET_SIZE);
+ word_fastmap[' '] = 0;
+ word_fastmap['\t'] = 0;
+ word_fastmap['\n'] = 0;
+ }
}
}
@@ -511,88 +508,23 @@ initialize_regex (void)
static void
swallow_file_in_memory (const char *file_name, BLOCK *block)
{
- int file_handle; /* file descriptor number */
- struct stat stat_block; /* stat block for file */
- size_t allocated_length; /* allocated length of memory buffer */
size_t used_length; /* used length in memory buffer */
- int read_length; /* number of character gotten on last read */
/* As special cases, a file name which is NULL or "-" indicates standard
input, which is already opened. In all other cases, open the file from
its name. */
bool using_stdin = !file_name || !*file_name || STREQ (file_name, "-");
if (using_stdin)
- file_handle = STDIN_FILENO;
- else
- if ((file_handle = open (file_name, O_RDONLY)) < 0)
- error (EXIT_FAILURE, errno, "%s", file_name);
-
- /* If the file is a plain, regular file, allocate the memory buffer all at
- once and swallow the file in one blow. In other cases, read the file
- repeatedly in smaller chunks until we have it all, reallocating memory
- once in a while, as we go. */
-
- if (fstat (file_handle, &stat_block) < 0)
- error (EXIT_FAILURE, errno, "%s", file_name);
-
- if (S_ISREG (stat_block.st_mode))
- {
- size_t in_memory_size;
-
- block->start = xmalloc ((size_t) stat_block.st_size);
-
- if ((in_memory_size = read (file_handle,
- block->start, (size_t) stat_block.st_size))
- != stat_block.st_size)
- {
-#if MSDOS
- /* On MSDOS, in memory size may be smaller than the file
- size, because of end of line conversions. But it can
- never be smaller than half the file size, because the
- minimum is when all lines are empty and terminated by
- CR+LF. */
- if (in_memory_size != (size_t)-1
- && in_memory_size >= stat_block.st_size / 2)
- block->start = xrealloc (block->start, in_memory_size);
- else
-#endif /* not MSDOS */
-
- error (EXIT_FAILURE, errno, "%s", file_name);
- }
- block->end = block->start + in_memory_size;
- }
+ block->start = fread_file (stdin, &used_length);
else
- {
- block->start = xmalloc ((size_t) 1 << SWALLOW_REALLOC_LOG);
- used_length = 0;
- allocated_length = (1 << SWALLOW_REALLOC_LOG);
-
- while (read_length = read (file_handle,
- block->start + used_length,
- allocated_length - used_length),
- read_length > 0)
- {
- used_length += read_length;
- if (used_length == allocated_length)
- {
- allocated_length += (1 << SWALLOW_REALLOC_LOG);
- block->start
- = xrealloc (block->start, allocated_length);
- }
- }
-
- if (read_length < 0)
- error (EXIT_FAILURE, errno, "%s", file_name);
-
- block->end = block->start + used_length;
- }
+ block->start = read_file (file_name, &used_length);
- /* Close the file, but only if it was not the standard input. */
+ if (!block->start)
+ error (EXIT_FAILURE, errno, "%s", quotef (using_stdin ? "-" : file_name));
- if (! using_stdin && close (file_handle) != 0)
- error (EXIT_FAILURE, errno, "%s", file_name);
+ block->end = block->start + used_length;
}
-
+
/* Sort and search routines. */
/*--------------------------------------------------------------------------.
@@ -617,22 +549,22 @@ compare_words (const void *void_first, const void *void_second)
if (ignore_case)
{
for (counter = 0; counter < length; counter++)
- {
- value = (folded_chars [to_uchar (first->start[counter])]
- - folded_chars [to_uchar (second->start[counter])]);
- if (value != 0)
- return value;
- }
+ {
+ value = (folded_chars [to_uchar (first->start[counter])]
+ - folded_chars [to_uchar (second->start[counter])]);
+ if (value != 0)
+ return value;
+ }
}
else
{
for (counter = 0; counter < length; counter++)
- {
- value = (to_uchar (first->start[counter])
- - to_uchar (second->start[counter]));
- if (value != 0)
- return value;
- }
+ {
+ value = (to_uchar (first->start[counter])
+ - to_uchar (second->start[counter]));
+ if (value != 0)
+ return value;
+ }
}
return first->size - second->size;
@@ -663,7 +595,7 @@ compare_occurs (const void *void_first, const void *void_second)
| Return !0 if WORD appears in TABLE. Uses a binary search. |
`------------------------------------------------------------*/
-static int
+static int _GL_ATTRIBUTE_PURE
search_table (WORD *word, WORD_TABLE *table)
{
int lowest; /* current lowest possible index */
@@ -678,17 +610,17 @@ search_table (WORD *word, WORD_TABLE *table)
middle = (lowest + highest) / 2;
value = compare_words (word, table->start + middle);
if (value < 0)
- highest = middle - 1;
+ highest = middle - 1;
else if (value > 0)
- lowest = middle + 1;
+ lowest = middle + 1;
else
- return 1;
+ return 1;
}
return 0;
}
/*---------------------------------------------------------------------.
-| Sort the whole occurs table in memory. Presumably, `qsort' does not |
+| Sort the whole occurs table in memory. Presumably, 'qsort' does not |
| take intermediate copies or table elements, so the sort will be |
| stabilized throughout the comparison routine. |
`---------------------------------------------------------------------*/
@@ -698,11 +630,11 @@ sort_found_occurs (void)
{
/* Only one language for the time being. */
-
- qsort (occurs_table[0], number_of_occurs[0], sizeof **occurs_table,
- compare_occurs);
+ if (number_of_occurs[0])
+ qsort (occurs_table[0], number_of_occurs[0], sizeof **occurs_table,
+ compare_occurs);
}
-
+
/* Parameter files reading routines. */
/*----------------------------------------------------------------------.
@@ -729,10 +661,10 @@ digest_break_file (const char *file_name)
{
/* If GNU extensions are enabled, the only way to avoid newline as
- a break character is to write all the break characters in the
- file with no newline at all, not even at the end of the file.
- If disabled, spaces, tabs and newlines are always considered as
- break characters even if not included in the break file. */
+ a break character is to write all the break characters in the
+ file with no newline at all, not even at the end of the file.
+ If disabled, spaces, tabs and newlines are always considered as
+ break characters even if not included in the break file. */
word_fastmap[' '] = 0;
word_fastmap['\t'] = 0;
@@ -774,37 +706,37 @@ digest_word_file (const char *file_name, WORD_TABLE *table)
word_start = cursor;
while (cursor < file_contents.end && *cursor != '\n')
- cursor++;
+ cursor++;
/* Record the word in table if it is not empty. */
if (cursor > word_start)
- {
- if (table->length == table->alloc)
- {
- if ((SIZE_MAX / sizeof *table->start - 1) / 2 < table->alloc)
- xalloc_die ();
- table->alloc = table->alloc * 2 + 1;
- table->start = xrealloc (table->start,
- table->alloc * sizeof *table->start);
- }
-
- table->start[table->length].start = word_start;
- table->start[table->length].size = cursor - word_start;
- table->length++;
- }
+ {
+ if (table->length == table->alloc)
+ {
+ if ((SIZE_MAX / sizeof *table->start - 1) / 2 < table->alloc)
+ xalloc_die ();
+ table->alloc = table->alloc * 2 + 1;
+ table->start = xrealloc (table->start,
+ table->alloc * sizeof *table->start);
+ }
+
+ table->start[table->length].start = word_start;
+ table->start[table->length].size = cursor - word_start;
+ table->length++;
+ }
/* This test allows for an incomplete line at end of file. */
if (cursor < file_contents.end)
- cursor++;
+ cursor++;
}
/* Finally, sort all the words read. */
qsort (table->start, table->length, sizeof table->start[0], compare_words);
}
-
+
/* Keyword recognition and selection. */
/*----------------------------------------------------------------------.
@@ -812,7 +744,7 @@ digest_word_file (const char *file_name, WORD_TABLE *table)
`----------------------------------------------------------------------*/
static void
-find_occurs_in_text (void)
+find_occurs_in_text (size_t file_index)
{
char *cursor; /* for scanning the source text */
char *scan; /* for scanning the source text also */
@@ -828,7 +760,9 @@ find_occurs_in_text (void)
char *word_end; /* end of word */
char *next_context_start; /* next start of left context */
- /* reference_length is always used within `if (input_reference)'.
+ const BLOCK *text_buffer = &text_buffers[file_index];
+
+ /* reference_length is always used within 'if (input_reference)'.
However, GNU C diagnoses that it may be used uninitialized. The
following assignment is merely to shut it up. */
@@ -843,243 +777,245 @@ find_occurs_in_text (void)
found inside it. Also, unconditionally assigning these variable has
the happy effect of shutting up lint. */
- line_start = text_buffer.start;
+ line_start = text_buffer->start;
line_scan = line_start;
if (input_reference)
{
- SKIP_NON_WHITE (line_scan, text_buffer.end);
+ SKIP_NON_WHITE (line_scan, text_buffer->end);
reference_length = line_scan - line_start;
- SKIP_WHITE (line_scan, text_buffer.end);
+ SKIP_WHITE (line_scan, text_buffer->end);
}
/* Process the whole buffer, one line or one sentence at a time. */
- for (cursor = text_buffer.start;
- cursor < text_buffer.end;
+ for (cursor = text_buffer->start;
+ cursor < text_buffer->end;
cursor = next_context_start)
{
- /* `context_start' gets initialized before the processing of each
- line, or once for the whole buffer if no end of line or sentence
- sequence separator. */
+ /* 'context_start' gets initialized before the processing of each
+ line, or once for the whole buffer if no end of line or sentence
+ sequence separator. */
context_start = cursor;
- /* If a end of line or end of sentence sequence is defined and
- non-empty, `next_context_start' will be recomputed to be the end of
- each line or sentence, before each one is processed. If no such
- sequence, then `next_context_start' is set at the end of the whole
- buffer, which is then considered to be a single line or sentence.
- This test also accounts for the case of an incomplete line or
- sentence at the end of the buffer. */
+ /* If an end of line or end of sentence sequence is defined and
+ non-empty, 'next_context_start' will be recomputed to be the end of
+ each line or sentence, before each one is processed. If no such
+ sequence, then 'next_context_start' is set at the end of the whole
+ buffer, which is then considered to be a single line or sentence.
+ This test also accounts for the case of an incomplete line or
+ sentence at the end of the buffer. */
- next_context_start = text_buffer.end;
+ next_context_start = text_buffer->end;
if (context_regex.string)
- switch (re_search (&context_regex.pattern, cursor,
- text_buffer.end - cursor,
- 0, text_buffer.end - cursor, &context_regs))
- {
- case -2:
- matcher_error ();
+ switch (re_search (&context_regex.pattern, cursor,
+ text_buffer->end - cursor,
+ 0, text_buffer->end - cursor, &context_regs))
+ {
+ case -2:
+ matcher_error ();
- case -1:
- break;
+ case -1:
+ break;
- default:
- next_context_start = cursor + context_regs.end[0];
- break;
- }
+ default:
+ next_context_start = cursor + context_regs.end[0];
+ break;
+ }
/* Include the separator into the right context, but not any suffix
- white space in this separator; this insures it will be seen in
- output and will not take more space than necessary. */
+ white space in this separator; this insures it will be seen in
+ output and will not take more space than necessary. */
context_end = next_context_start;
SKIP_WHITE_BACKWARDS (context_end, context_start);
/* Read and process a single input line or sentence, one word at a
- time. */
+ time. */
while (1)
- {
- if (word_regex.string)
-
- /* If a word regexp has been compiled, use it to skip at the
- beginning of the next word. If there is no such word, exit
- the loop. */
-
- {
- regoff_t r = re_search (&word_regex.pattern, cursor,
- context_end - cursor,
- 0, context_end - cursor, &word_regs);
- if (r == -2)
- matcher_error ();
- if (r == -1)
- break;
- word_start = cursor + word_regs.start[0];
- word_end = cursor + word_regs.end[0];
- }
- else
-
- /* Avoid re_search and use the fastmap to skip to the
- beginning of the next word. If there is no more word in
- the buffer, exit the loop. */
-
- {
- scan = cursor;
- while (scan < context_end
- && !word_fastmap[to_uchar (*scan)])
- scan++;
-
- if (scan == context_end)
- break;
-
- word_start = scan;
-
- while (scan < context_end
- && word_fastmap[to_uchar (*scan)])
- scan++;
-
- word_end = scan;
- }
-
- /* Skip right to the beginning of the found word. */
-
- cursor = word_start;
-
- /* Skip any zero length word. Just advance a single position,
- then go fetch the next word. */
-
- if (word_end == word_start)
- {
- cursor++;
- continue;
- }
-
- /* This is a genuine, non empty word, so save it as a possible
- key. Then skip over it. Also, maintain the maximum length of
- all words read so far. It is mandatory to take the maximum
- length of all words in the file, without considering if they
- are actually kept or rejected, because backward jumps at output
- generation time may fall in *any* word. */
-
- possible_key.start = cursor;
- possible_key.size = word_end - word_start;
- cursor += possible_key.size;
-
- if (possible_key.size > maximum_word_length)
- maximum_word_length = possible_key.size;
-
- /* In input reference mode, update `line_start' from its previous
- value. Count the lines just in case auto reference mode is
- also selected. If it happens that the word just matched is
- indeed part of a reference; just ignore it. */
-
- if (input_reference)
- {
- while (line_scan < possible_key.start)
- if (*line_scan == '\n')
- {
- total_line_count++;
- line_scan++;
- line_start = line_scan;
- SKIP_NON_WHITE (line_scan, text_buffer.end);
- reference_length = line_scan - line_start;
- }
- else
- line_scan++;
- if (line_scan > possible_key.start)
- continue;
- }
-
- /* Ignore the word if an `Ignore words' table exists and if it is
- part of it. Also ignore the word if an `Only words' table and
- if it is *not* part of it.
-
- It is allowed that both tables be used at once, even if this
- may look strange for now. Just ignore a word that would appear
- in both. If regexps are eventually implemented for these
- tables, the Ignore table could then reject words that would
- have been previously accepted by the Only table. */
-
- if (ignore_file && search_table (&possible_key, &ignore_table))
- continue;
- if (only_file && !search_table (&possible_key, &only_table))
- continue;
-
- /* A non-empty word has been found. First of all, insure
- proper allocation of the next OCCURS, and make a pointer to
- where it will be constructed. */
-
- if (number_of_occurs[0] == occurs_alloc[0])
- {
- if ((SIZE_MAX / sizeof *occurs_table[0] - 1) / 2
- < occurs_alloc[0])
- xalloc_die ();
- occurs_alloc[0] = occurs_alloc[0] * 2 + 1;
- occurs_table[0] = xrealloc (occurs_table[0],
- occurs_alloc[0] * sizeof *occurs_table[0]);
- }
-
- occurs_cursor = occurs_table[0] + number_of_occurs[0];
-
- /* Define the refence field, if any. */
-
- if (auto_reference)
- {
-
- /* While auto referencing, update `line_start' from its
- previous value, counting lines as we go. If input
- referencing at the same time, `line_start' has been
- advanced earlier, and the following loop is never really
- executed. */
-
- while (line_scan < possible_key.start)
- if (*line_scan == '\n')
- {
- total_line_count++;
- line_scan++;
- line_start = line_scan;
- SKIP_NON_WHITE (line_scan, text_buffer.end);
- }
- else
- line_scan++;
-
- occurs_cursor->reference = total_line_count;
- }
- else if (input_reference)
- {
-
- /* If only input referencing, `line_start' has been computed
- earlier to detect the case the word matched would be part
- of the reference. The reference position is simply the
- value of `line_start'. */
-
- occurs_cursor->reference
- = (DELTA) (line_start - possible_key.start);
- if (reference_length > reference_max_width)
- reference_max_width = reference_length;
- }
-
- /* Exclude the reference from the context in simple cases. */
-
- if (input_reference && line_start == context_start)
- {
- SKIP_NON_WHITE (context_start, context_end);
- SKIP_WHITE (context_start, context_end);
- }
-
- /* Completes the OCCURS structure. */
-
- occurs_cursor->key = possible_key;
- occurs_cursor->left = context_start - possible_key.start;
- occurs_cursor->right = context_end - possible_key.start;
-
- number_of_occurs[0]++;
- }
+ {
+ if (word_regex.string)
+
+ /* If a word regexp has been compiled, use it to skip at the
+ beginning of the next word. If there is no such word, exit
+ the loop. */
+
+ {
+ regoff_t r = re_search (&word_regex.pattern, cursor,
+ context_end - cursor,
+ 0, context_end - cursor, &word_regs);
+ if (r == -2)
+ matcher_error ();
+ if (r == -1)
+ break;
+ word_start = cursor + word_regs.start[0];
+ word_end = cursor + word_regs.end[0];
+ }
+ else
+
+ /* Avoid re_search and use the fastmap to skip to the
+ beginning of the next word. If there is no more word in
+ the buffer, exit the loop. */
+
+ {
+ scan = cursor;
+ while (scan < context_end
+ && !word_fastmap[to_uchar (*scan)])
+ scan++;
+
+ if (scan == context_end)
+ break;
+
+ word_start = scan;
+
+ while (scan < context_end
+ && word_fastmap[to_uchar (*scan)])
+ scan++;
+
+ word_end = scan;
+ }
+
+ /* Skip right to the beginning of the found word. */
+
+ cursor = word_start;
+
+ /* Skip any zero length word. Just advance a single position,
+ then go fetch the next word. */
+
+ if (word_end == word_start)
+ {
+ cursor++;
+ continue;
+ }
+
+ /* This is a genuine, non empty word, so save it as a possible
+ key. Then skip over it. Also, maintain the maximum length of
+ all words read so far. It is mandatory to take the maximum
+ length of all words in the file, without considering if they
+ are actually kept or rejected, because backward jumps at output
+ generation time may fall in *any* word. */
+
+ possible_key.start = cursor;
+ possible_key.size = word_end - word_start;
+ cursor += possible_key.size;
+
+ if (possible_key.size > maximum_word_length)
+ maximum_word_length = possible_key.size;
+
+ /* In input reference mode, update 'line_start' from its previous
+ value. Count the lines just in case auto reference mode is
+ also selected. If it happens that the word just matched is
+ indeed part of a reference; just ignore it. */
+
+ if (input_reference)
+ {
+ while (line_scan < possible_key.start)
+ if (*line_scan == '\n')
+ {
+ total_line_count++;
+ line_scan++;
+ line_start = line_scan;
+ SKIP_NON_WHITE (line_scan, text_buffer->end);
+ reference_length = line_scan - line_start;
+ }
+ else
+ line_scan++;
+ if (line_scan > possible_key.start)
+ continue;
+ }
+
+ /* Ignore the word if an 'Ignore words' table exists and if it is
+ part of it. Also ignore the word if an 'Only words' table and
+ if it is *not* part of it.
+
+ It is allowed that both tables be used at once, even if this
+ may look strange for now. Just ignore a word that would appear
+ in both. If regexps are eventually implemented for these
+ tables, the Ignore table could then reject words that would
+ have been previously accepted by the Only table. */
+
+ if (ignore_file && search_table (&possible_key, &ignore_table))
+ continue;
+ if (only_file && !search_table (&possible_key, &only_table))
+ continue;
+
+ /* A non-empty word has been found. First of all, insure
+ proper allocation of the next OCCURS, and make a pointer to
+ where it will be constructed. */
+
+ if (number_of_occurs[0] == occurs_alloc[0])
+ {
+ if ((SIZE_MAX / sizeof *occurs_table[0] - 1) / 2
+ < occurs_alloc[0])
+ xalloc_die ();
+ occurs_alloc[0] = occurs_alloc[0] * 2 + 1;
+ occurs_table[0] =
+ xrealloc (occurs_table[0],
+ occurs_alloc[0] * sizeof *occurs_table[0]);
+ }
+
+ occurs_cursor = occurs_table[0] + number_of_occurs[0];
+
+ /* Define the reference field, if any. */
+
+ if (auto_reference)
+ {
+
+ /* While auto referencing, update 'line_start' from its
+ previous value, counting lines as we go. If input
+ referencing at the same time, 'line_start' has been
+ advanced earlier, and the following loop is never really
+ executed. */
+
+ while (line_scan < possible_key.start)
+ if (*line_scan == '\n')
+ {
+ total_line_count++;
+ line_scan++;
+ line_start = line_scan;
+ SKIP_NON_WHITE (line_scan, text_buffer->end);
+ }
+ else
+ line_scan++;
+
+ occurs_cursor->reference = total_line_count;
+ }
+ else if (input_reference)
+ {
+
+ /* If only input referencing, 'line_start' has been computed
+ earlier to detect the case the word matched would be part
+ of the reference. The reference position is simply the
+ value of 'line_start'. */
+
+ occurs_cursor->reference
+ = (DELTA) (line_start - possible_key.start);
+ if (reference_length > reference_max_width)
+ reference_max_width = reference_length;
+ }
+
+ /* Exclude the reference from the context in simple cases. */
+
+ if (input_reference && line_start == context_start)
+ {
+ SKIP_NON_WHITE (context_start, context_end);
+ SKIP_WHITE (context_start, context_end);
+ }
+
+ /* Completes the OCCURS structure. */
+
+ occurs_cursor->key = possible_key;
+ occurs_cursor->left = context_start - possible_key.start;
+ occurs_cursor->right = context_end - possible_key.start;
+ occurs_cursor->file_index = file_index;
+
+ number_of_occurs[0]++;
+ }
}
}
-
+
/* Formatting and actual output - service routines. */
/*-----------------------------------------.
@@ -1113,147 +1049,147 @@ print_field (BLOCK field)
{
unsigned char character = *cursor;
if (edited_flag[character])
- {
-
- /* First check if this is a diacriticized character.
-
- This works only for TeX. I do not know how diacriticized
- letters work with `roff'. Please someone explain it to me! */
-
- diacritic = todiac (character);
- if (diacritic != 0 && output_format == TEX_FORMAT)
- {
- base = tobase (character);
- switch (diacritic)
- {
-
- case 1: /* Latin diphthongs */
- switch (base)
- {
- case 'o':
- fputs ("\\oe{}", stdout);
- break;
-
- case 'O':
- fputs ("\\OE{}", stdout);
- break;
-
- case 'a':
- fputs ("\\ae{}", stdout);
- break;
-
- case 'A':
- fputs ("\\AE{}", stdout);
- break;
-
- default:
- putchar (' ');
- }
- break;
-
- case 2: /* Acute accent */
- printf ("\\'%s%c", (base == 'i' ? "\\" : ""), base);
- break;
-
- case 3: /* Grave accent */
- printf ("\\`%s%c", (base == 'i' ? "\\" : ""), base);
- break;
-
- case 4: /* Circumflex accent */
- printf ("\\^%s%c", (base == 'i' ? "\\" : ""), base);
- break;
-
- case 5: /* Diaeresis */
- printf ("\\\"%s%c", (base == 'i' ? "\\" : ""), base);
- break;
-
- case 6: /* Tilde accent */
- printf ("\\~%s%c", (base == 'i' ? "\\" : ""), base);
- break;
-
- case 7: /* Cedilla */
- printf ("\\c{%c}", base);
- break;
-
- case 8: /* Small circle beneath */
- switch (base)
- {
- case 'a':
- fputs ("\\aa{}", stdout);
- break;
-
- case 'A':
- fputs ("\\AA{}", stdout);
- break;
-
- default:
- putchar (' ');
- }
- break;
-
- case 9: /* Strike through */
- switch (base)
- {
- case 'o':
- fputs ("\\o{}", stdout);
- break;
-
- case 'O':
- fputs ("\\O{}", stdout);
- break;
-
- default:
- putchar (' ');
- }
- break;
- }
- }
- else
-
- /* This is not a diacritic character, so handle cases which are
- really specific to `roff' or TeX. All white space processing
- is done as the default case of this switch. */
-
- switch (character)
- {
- case '"':
- /* In roff output format, double any quote. */
- putchar ('"');
- putchar ('"');
- break;
-
- case '$':
- case '%':
- case '&':
- case '#':
- case '_':
- /* In TeX output format, precede these with a backslash. */
- putchar ('\\');
- putchar (character);
- break;
-
- case '{':
- case '}':
- /* In TeX output format, precede these with a backslash and
- force mathematical mode. */
- printf ("$\\%c$", character);
- break;
-
- case '\\':
- /* In TeX output mode, request production of a backslash. */
- fputs ("\\backslash{}", stdout);
- break;
-
- default:
- /* Any other flagged character produces a single space. */
- putchar (' ');
- }
- }
+ {
+
+ /* First check if this is a diacriticized character.
+
+ This works only for TeX. I do not know how diacriticized
+ letters work with 'roff'. Please someone explain it to me! */
+
+ diacritic = todiac (character);
+ if (diacritic != 0 && output_format == TEX_FORMAT)
+ {
+ base = tobase (character);
+ switch (diacritic)
+ {
+
+ case 1: /* Latin diphthongs */
+ switch (base)
+ {
+ case 'o':
+ fputs ("\\oe{}", stdout);
+ break;
+
+ case 'O':
+ fputs ("\\OE{}", stdout);
+ break;
+
+ case 'a':
+ fputs ("\\ae{}", stdout);
+ break;
+
+ case 'A':
+ fputs ("\\AE{}", stdout);
+ break;
+
+ default:
+ putchar (' ');
+ }
+ break;
+
+ case 2: /* Acute accent */
+ printf ("\\'%s%c", (base == 'i' ? "\\" : ""), base);
+ break;
+
+ case 3: /* Grave accent */
+ printf ("\\'%s%c", (base == 'i' ? "\\" : ""), base);
+ break;
+
+ case 4: /* Circumflex accent */
+ printf ("\\^%s%c", (base == 'i' ? "\\" : ""), base);
+ break;
+
+ case 5: /* Diaeresis */
+ printf ("\\\"%s%c", (base == 'i' ? "\\" : ""), base);
+ break;
+
+ case 6: /* Tilde accent */
+ printf ("\\~%s%c", (base == 'i' ? "\\" : ""), base);
+ break;
+
+ case 7: /* Cedilla */
+ printf ("\\c{%c}", base);
+ break;
+
+ case 8: /* Small circle beneath */
+ switch (base)
+ {
+ case 'a':
+ fputs ("\\aa{}", stdout);
+ break;
+
+ case 'A':
+ fputs ("\\AA{}", stdout);
+ break;
+
+ default:
+ putchar (' ');
+ }
+ break;
+
+ case 9: /* Strike through */
+ switch (base)
+ {
+ case 'o':
+ fputs ("\\o{}", stdout);
+ break;
+
+ case 'O':
+ fputs ("\\O{}", stdout);
+ break;
+
+ default:
+ putchar (' ');
+ }
+ break;
+ }
+ }
+ else
+
+ /* This is not a diacritic character, so handle cases which are
+ really specific to 'roff' or TeX. All white space processing
+ is done as the default case of this switch. */
+
+ switch (character)
+ {
+ case '"':
+ /* In roff output format, double any quote. */
+ putchar ('"');
+ putchar ('"');
+ break;
+
+ case '$':
+ case '%':
+ case '&':
+ case '#':
+ case '_':
+ /* In TeX output format, precede these with a backslash. */
+ putchar ('\\');
+ putchar (character);
+ break;
+
+ case '{':
+ case '}':
+ /* In TeX output format, precede these with a backslash and
+ force mathematical mode. */
+ printf ("$\\%c$", character);
+ break;
+
+ case '\\':
+ /* In TeX output mode, request production of a backslash. */
+ fputs ("\\backslash{}", stdout);
+ break;
+
+ default:
+ /* Any other flagged character produces a single space. */
+ putchar (' ');
+ }
+ }
else
- putchar (*cursor);
+ putchar (*cursor);
}
}
-
+
/* Formatting and actual output - planning routines. */
/*--------------------------------------------------------------------.
@@ -1279,17 +1215,17 @@ fix_output_parameters (void)
{
reference_max_width = 0;
for (file_index = 0; file_index < number_input_files; file_index++)
- {
- line_ordinal = file_line_count[file_index] + 1;
- if (file_index > 0)
- line_ordinal -= file_line_count[file_index - 1];
- sprintf (ordinal_string, "%d", line_ordinal);
- reference_width = strlen (ordinal_string);
- if (input_file_name[file_index])
- reference_width += strlen (input_file_name[file_index]);
- if (reference_width > reference_max_width)
- reference_max_width = reference_width;
- }
+ {
+ line_ordinal = file_line_count[file_index] + 1;
+ if (file_index > 0)
+ line_ordinal -= file_line_count[file_index - 1];
+ sprintf (ordinal_string, "%d", line_ordinal);
+ reference_width = strlen (ordinal_string);
+ if (input_file_name[file_index])
+ reference_width += strlen (input_file_name[file_index]);
+ if (reference_width > reference_max_width)
+ reference_max_width = reference_width;
+ }
reference_max_width++;
reference.start = xmalloc ((size_t) reference_max_width + 1);
}
@@ -1297,7 +1233,7 @@ fix_output_parameters (void)
/* If the reference appears to the left of the output line, reserve some
space for it right away, including one gap size. */
- if ((auto_reference | input_reference) & !right_reference)
+ if ((auto_reference || input_reference) && !right_reference)
line_width -= reference_max_width + gap_size;
/* The output lines, minimally, will contain from left to right a left
@@ -1329,38 +1265,40 @@ fix_output_parameters (void)
{
/* When flagging truncation at the left of the keyword, the
- truncation mark goes at the beginning of the before field,
- unless there is a head field, in which case the mark goes at the
- left of the head field. When flagging truncation at the right
- of the keyword, the mark goes at the end of the keyafter field,
- unless there is a tail field, in which case the mark goes at the
- end of the tail field. Only eight combination cases could arise
- for truncation marks:
-
- . None.
- . One beginning the before field.
- . One beginning the head field.
- . One ending the keyafter field.
- . One ending the tail field.
- . One beginning the before field, another ending the keyafter field.
- . One ending the tail field, another beginning the before field.
- . One ending the keyafter field, another beginning the head field.
-
- So, there is at most two truncation marks, which could appear both
- on the left side of the center of the output line, both on the
- right side, or one on either side. */
+ truncation mark goes at the beginning of the before field,
+ unless there is a head field, in which case the mark goes at the
+ left of the head field. When flagging truncation at the right
+ of the keyword, the mark goes at the end of the keyafter field,
+ unless there is a tail field, in which case the mark goes at the
+ end of the tail field. Only eight combination cases could arise
+ for truncation marks:
+
+ . None.
+ . One beginning the before field.
+ . One beginning the head field.
+ . One ending the keyafter field.
+ . One ending the tail field.
+ . One beginning the before field, another ending the keyafter field.
+ . One ending the tail field, another beginning the before field.
+ . One ending the keyafter field, another beginning the head field.
+
+ So, there is at most two truncation marks, which could appear both
+ on the left side of the center of the output line, both on the
+ right side, or one on either side. */
before_max_width -= 2 * truncation_string_length;
+ if (before_max_width < 0)
+ before_max_width = 0;
keyafter_max_width -= 2 * truncation_string_length;
}
else
{
/* I never figured out exactly how UNIX' ptx plans the output width
- of its various fields. If GNU extensions are disabled, do not
- try computing the field widths correctly; instead, use the
- following formula, which does not completely imitate UNIX' ptx,
- but almost. */
+ of its various fields. If GNU extensions are disabled, do not
+ try computing the field widths correctly; instead, use the
+ following formula, which does not completely imitate UNIX' ptx,
+ but almost. */
keyafter_max_width -= 2 * truncation_string_length + 1;
}
@@ -1386,7 +1324,7 @@ fix_output_parameters (void)
case ROFF_FORMAT:
- /* `Quote' characters should be doubled. */
+ /* 'Quote' characters should be doubled. */
edited_flag['"'] = 1;
break;
@@ -1396,13 +1334,13 @@ fix_output_parameters (void)
/* Various characters need special processing. */
for (cursor = "$%&#_{}\\"; *cursor; cursor++)
- edited_flag[to_uchar (*cursor)] = 1;
+ edited_flag[to_uchar (*cursor)] = 1;
/* Any character with 8th bit set will print to a single space, unless
- it is diacriticized. */
+ it is diacriticized. */
for (character = 0200; character < CHAR_SET_SIZE; character++)
- edited_flag[character] = todiac (character) != 0;
+ edited_flag[character] = todiac (character) != 0;
break;
}
}
@@ -1420,15 +1358,16 @@ define_all_fields (OCCURS *occurs)
char *cursor; /* running cursor in source text */
char *left_context_start; /* start of left context */
char *right_context_end; /* end of right context */
- char *left_field_start; /* conservative start for `head'/`before' */
- int file_index; /* index in text input file arrays */
+ char *left_field_start; /* conservative start for 'head'/'before' */
const char *file_name; /* file name for reference */
int line_ordinal; /* line ordinal for reference */
+ const char *buffer_start; /* start of buffered file for this occurs */
+ const char *buffer_end; /* end of buffered file for this occurs */
- /* Define `keyafter', start of left context and end of right context.
- `keyafter' starts at the saved position for keyword and extend to the
+ /* Define 'keyafter', start of left context and end of right context.
+ 'keyafter' starts at the saved position for keyword and extend to the
right from the end of the keyword, eating separators or full words, but
- not beyond maximum allowed width for `keyafter' field or limit for the
+ not beyond maximum allowed width for 'keyafter' field or limit for the
right context. Suffix spaces will be removed afterwards. */
keyafter.start = occurs->key.start;
@@ -1436,9 +1375,12 @@ define_all_fields (OCCURS *occurs)
left_context_start = keyafter.start + occurs->left;
right_context_end = keyafter.start + occurs->right;
+ buffer_start = text_buffers[occurs->file_index].start;
+ buffer_end = text_buffers[occurs->file_index].end;
+
cursor = keyafter.end;
while (cursor < right_context_end
- && cursor <= keyafter.start + keyafter_max_width)
+ && cursor <= keyafter.start + keyafter_max_width)
{
keyafter.end = cursor;
SKIP_SOMETHING (cursor, right_context_end);
@@ -1451,7 +1393,7 @@ define_all_fields (OCCURS *occurs)
SKIP_WHITE_BACKWARDS (keyafter.end, keyafter.start);
/* When the left context is wide, it might take some time to catch up from
- the left context boundary to the beginning of the `head' or `before'
+ the left context boundary to the beginning of the 'head' or 'before'
fields. So, in this case, to speed the catchup, we jump back from the
keyword, using some secure distance, possibly falling in the middle of
a word. A secure backward jump would be at least half the maximum
@@ -1459,22 +1401,22 @@ define_all_fields (OCCURS *occurs)
input. We conclude this backward jump by a skip forward of at least
one word. In this manner, we should not inadvertently accept only part
of a word. From the reached point, when it will be time to fix the
- beginning of `head' or `before' fields, we will skip forward words or
+ beginning of 'head' or 'before' fields, we will skip forward words or
delimiters until we get sufficiently near. */
if (-occurs->left > half_line_width + maximum_word_length)
{
left_field_start
- = keyafter.start - (half_line_width + maximum_word_length);
+ = keyafter.start - (half_line_width + maximum_word_length);
SKIP_SOMETHING (left_field_start, keyafter.start);
}
else
left_field_start = keyafter.start + occurs->left;
- /* `before' certainly ends at the keyword, but not including separating
+ /* 'before' certainly ends at the keyword, but not including separating
spaces. It starts after than the saved value for the left context, by
advancing it until it falls inside the maximum allowed width for the
- before field. There will be no prefix spaces either. `before' only
+ before field. There will be no prefix spaces either. 'before' only
advances by skipping single separators or whole words. */
before.start = left_field_start;
@@ -1487,13 +1429,13 @@ define_all_fields (OCCURS *occurs)
if (truncation_string)
{
cursor = before.start;
- SKIP_WHITE_BACKWARDS (cursor, text_buffer.start);
+ SKIP_WHITE_BACKWARDS (cursor, buffer_start);
before_truncation = cursor > left_context_start;
}
else
before_truncation = 0;
- SKIP_WHITE (before.start, text_buffer.end);
+ SKIP_WHITE (before.start, buffer_end);
/* The tail could not take more columns than what has been left in the
left context field, and a gap is mandatory. It starts after the
@@ -1508,27 +1450,27 @@ define_all_fields (OCCURS *occurs)
if (tail_max_width > 0)
{
tail.start = keyafter.end;
- SKIP_WHITE (tail.start, text_buffer.end);
+ SKIP_WHITE (tail.start, buffer_end);
tail.end = tail.start;
cursor = tail.end;
while (cursor < right_context_end
- && cursor < tail.start + tail_max_width)
- {
- tail.end = cursor;
- SKIP_SOMETHING (cursor, right_context_end);
- }
+ && cursor < tail.start + tail_max_width)
+ {
+ tail.end = cursor;
+ SKIP_SOMETHING (cursor, right_context_end);
+ }
if (cursor < tail.start + tail_max_width)
- tail.end = cursor;
+ tail.end = cursor;
if (tail.end > tail.start)
- {
- keyafter_truncation = 0;
- tail_truncation = truncation_string && tail.end < right_context_end;
- }
+ {
+ keyafter_truncation = 0;
+ tail_truncation = truncation_string && tail.end < right_context_end;
+ }
else
- tail_truncation = 0;
+ tail_truncation = 0;
SKIP_WHITE_BACKWARDS (tail.end, tail.start);
}
@@ -1542,7 +1484,7 @@ define_all_fields (OCCURS *occurs)
tail_truncation = 0;
}
- /* `head' could not take more columns than what has been left in the right
+ /* 'head' could not take more columns than what has been left in the right
context field, and a gap is mandatory. It ends before the left
context, and does not contain suffixed spaces. Its pointer is advanced
until the head field has shrunk to its allowed width. It cannot
@@ -1554,20 +1496,20 @@ define_all_fields (OCCURS *occurs)
if (head_max_width > 0)
{
head.end = before.start;
- SKIP_WHITE_BACKWARDS (head.end, text_buffer.start);
+ SKIP_WHITE_BACKWARDS (head.end, buffer_start);
head.start = left_field_start;
while (head.start + head_max_width < head.end)
- SKIP_SOMETHING (head.start, head.end);
+ SKIP_SOMETHING (head.start, head.end);
if (head.end > head.start)
- {
- before_truncation = 0;
- head_truncation = (truncation_string
- && head.start > left_context_start);
- }
+ {
+ before_truncation = 0;
+ head_truncation = (truncation_string
+ && head.start > left_context_start);
+ }
else
- head_truncation = 0;
+ head_truncation = 0;
SKIP_WHITE (head.start, head.end);
}
@@ -1585,21 +1527,16 @@ define_all_fields (OCCURS *occurs)
{
/* Construct the reference text in preallocated space from the file
- name and the line number. Find out in which file the reference
- occurred. Standard input yields an empty file name. Insure line
- numbers are one based, even if they are computed zero based. */
-
- file_index = 0;
- while (file_line_count[file_index] < occurs->reference)
- file_index++;
+ name and the line number. Standard input yields an empty file name.
+ Ensure line numbers are 1 based, even if they are computed 0 based. */
- file_name = input_file_name[file_index];
+ file_name = input_file_name[occurs->file_index];
if (!file_name)
- file_name = "";
+ file_name = "";
line_ordinal = occurs->reference + 1;
- if (file_index > 0)
- line_ordinal -= file_line_count[file_index - 1];
+ if (occurs->file_index > 0)
+ line_ordinal -= file_line_count[occurs->file_index - 1];
sprintf (reference.start, "%s:%d", file_name, line_ordinal);
reference.end = reference.start + strlen (reference.start);
@@ -1608,24 +1545,24 @@ define_all_fields (OCCURS *occurs)
{
/* Reference starts at saved position for reference and extends right
- until some white space is met. */
+ until some white space is met. */
reference.start = keyafter.start + (DELTA) occurs->reference;
reference.end = reference.start;
SKIP_NON_WHITE (reference.end, right_context_end);
}
}
-
+
/* Formatting and actual output - control routines. */
/*----------------------------------------------------------------------.
-| Output the current output fields as one line for `troff' or `nroff'. |
+| Output the current output fields as one line for 'troff' or 'nroff'. |
`----------------------------------------------------------------------*/
static void
output_one_roff_line (void)
{
- /* Output the `tail' field. */
+ /* Output the 'tail' field. */
printf (".%s \"", macro_name);
print_field (tail);
@@ -1633,7 +1570,7 @@ output_one_roff_line (void)
fputs (truncation_string, stdout);
putchar ('"');
- /* Output the `before' field. */
+ /* Output the 'before' field. */
fputs (" \"", stdout);
if (before_truncation)
@@ -1641,7 +1578,7 @@ output_one_roff_line (void)
print_field (before);
putchar ('"');
- /* Output the `keyafter' field. */
+ /* Output the 'keyafter' field. */
fputs (" \"", stdout);
print_field (keyafter);
@@ -1649,7 +1586,7 @@ output_one_roff_line (void)
fputs (truncation_string, stdout);
putchar ('"');
- /* Output the `head' field. */
+ /* Output the 'head' field. */
fputs (" \"", stdout);
if (head_truncation)
@@ -1657,9 +1594,9 @@ output_one_roff_line (void)
print_field (head);
putchar ('"');
- /* Conditionally output the `reference' field. */
+ /* Conditionally output the 'reference' field. */
- if (auto_reference | input_reference)
+ if (auto_reference || input_reference)
{
fputs (" \"", stdout);
print_field (reference);
@@ -1670,7 +1607,7 @@ output_one_roff_line (void)
}
/*---------------------------------------------------------.
-| Output the current output fields as one line for `TeX'. |
+| Output the current output fields as one line for 'TeX'. |
`---------------------------------------------------------*/
static void
@@ -1698,7 +1635,7 @@ output_one_tex_line (void)
fputs ("}{", stdout);
print_field (head);
putchar ('}');
- if (auto_reference | input_reference)
+ if (auto_reference || input_reference)
{
putchar ('{');
print_field (reference);
@@ -1717,51 +1654,51 @@ output_one_dumb_line (void)
if (!right_reference)
{
if (auto_reference)
- {
-
- /* Output the `reference' field, in such a way that GNU emacs
- next-error will handle it. The ending colon is taken from the
- gap which follows. */
-
- print_field (reference);
- putchar (':');
- print_spaces (reference_max_width
- + gap_size
- - (reference.end - reference.start)
- - 1);
- }
+ {
+
+ /* Output the 'reference' field, in such a way that GNU emacs
+ next-error will handle it. The ending colon is taken from the
+ gap which follows. */
+
+ print_field (reference);
+ putchar (':');
+ print_spaces (reference_max_width
+ + gap_size
+ - (reference.end - reference.start)
+ - 1);
+ }
else
- {
+ {
- /* Output the `reference' field and its following gap. */
+ /* Output the 'reference' field and its following gap. */
- print_field (reference);
- print_spaces (reference_max_width
- + gap_size
- - (reference.end - reference.start));
- }
+ print_field (reference);
+ print_spaces (reference_max_width
+ + gap_size
+ - (reference.end - reference.start));
+ }
}
if (tail.start < tail.end)
{
- /* Output the `tail' field. */
+ /* Output the 'tail' field. */
print_field (tail);
if (tail_truncation)
- fputs (truncation_string, stdout);
+ fputs (truncation_string, stdout);
print_spaces (half_line_width - gap_size
- - (before.end - before.start)
- - (before_truncation ? truncation_string_length : 0)
- - (tail.end - tail.start)
- - (tail_truncation ? truncation_string_length : 0));
+ - (before.end - before.start)
+ - (before_truncation ? truncation_string_length : 0)
+ - (tail.end - tail.start)
+ - (tail_truncation ? truncation_string_length : 0));
}
else
print_spaces (half_line_width - gap_size
- - (before.end - before.start)
- - (before_truncation ? truncation_string_length : 0));
+ - (before.end - before.start)
+ - (before_truncation ? truncation_string_length : 0));
- /* Output the `before' field. */
+ /* Output the 'before' field. */
if (before_truncation)
fputs (truncation_string, stdout);
@@ -1769,7 +1706,7 @@ output_one_dumb_line (void)
print_spaces (gap_size);
- /* Output the `keyafter' field. */
+ /* Output the 'keyafter' field. */
print_field (keyafter);
if (keyafter_truncation)
@@ -1777,27 +1714,27 @@ output_one_dumb_line (void)
if (head.start < head.end)
{
- /* Output the `head' field. */
+ /* Output the 'head' field. */
print_spaces (half_line_width
- - (keyafter.end - keyafter.start)
- - (keyafter_truncation ? truncation_string_length : 0)
- - (head.end - head.start)
- - (head_truncation ? truncation_string_length : 0));
+ - (keyafter.end - keyafter.start)
+ - (keyafter_truncation ? truncation_string_length : 0)
+ - (head.end - head.start)
+ - (head_truncation ? truncation_string_length : 0));
if (head_truncation)
- fputs (truncation_string, stdout);
+ fputs (truncation_string, stdout);
print_field (head);
}
else
- if ((auto_reference | input_reference) & right_reference)
+ if ((auto_reference || input_reference) && right_reference)
print_spaces (half_line_width
- - (keyafter.end - keyafter.start)
- - (keyafter_truncation ? truncation_string_length : 0));
+ - (keyafter.end - keyafter.start)
+ - (keyafter_truncation ? truncation_string_length : 0));
- if ((auto_reference | input_reference) & right_reference)
+ if ((auto_reference || input_reference) && right_reference)
{
- /* Output the `reference' field. */
+ /* Output the 'reference' field. */
print_spaces (gap_size);
print_field (reference);
@@ -1836,36 +1773,36 @@ generate_all_output (void)
for (occurs_index = 0; occurs_index < number_of_occurs[0]; occurs_index++)
{
/* Compute the exact size of every field and whenever truncation flags
- are present or not. */
+ are present or not. */
define_all_fields (occurs_cursor);
/* Produce one output line according to selected format. */
switch (output_format)
- {
- case UNKNOWN_FORMAT:
- /* Should never happen. */
+ {
+ case UNKNOWN_FORMAT:
+ /* Should never happen. */
- case DUMB_FORMAT:
- output_one_dumb_line ();
- break;
+ case DUMB_FORMAT:
+ output_one_dumb_line ();
+ break;
- case ROFF_FORMAT:
- output_one_roff_line ();
- break;
+ case ROFF_FORMAT:
+ output_one_roff_line ();
+ break;
- case TEX_FORMAT:
- output_one_tex_line ();
- break;
- }
+ case TEX_FORMAT:
+ output_one_tex_line ();
+ break;
+ }
/* Advance the cursor into the occurs table. */
occurs_cursor++;
}
}
-
+
/* Option decoding and main program. */
/*------------------------------------------------------.
@@ -1876,28 +1813,30 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... [INPUT]... (without -G)\n\
or: %s -G [OPTION]... [INPUT [OUTPUT]]\n"),
- program_name, program_name);
+ program_name, program_name);
fputs (_("\
Output a permuted index, including context, of the words in the input files.\n\
-\n\
"), stdout);
+
+ emit_stdin_note ();
+ emit_mandatory_arg_note ();
+
fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
+ -A, --auto-reference output automatically generated references\n\
+ -G, --traditional behave more like System V 'ptx'\n\
"), stdout);
fputs (_("\
- -A, --auto-reference output automatically generated references\n\
- -G, --traditional behave more like System V `ptx'\n\
- -F, --flag-truncation=STRING use STRING for flagging line truncations\n\
+ -F, --flag-truncation=STRING use STRING for flagging line truncations.\n\
+ The default is '/'\n\
"), stdout);
fputs (_("\
- -M, --macro-name=STRING macro name to use instead of `xx'\n\
+ -M, --macro-name=STRING macro name to use instead of 'xx'\n\
-O, --format=roff generate output as roff directives\n\
-R, --right-side-refs put references at right, not counted in -w\n\
-S, --sentence-regexp=REGEXP for end of lines or end of sentences\n\
@@ -1918,11 +1857,7 @@ Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- fputs (_("\
-\n\
-With no FILE or if FILE is -, read Standard Input. `-F /' by default.\n\
-"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -1933,11 +1868,10 @@ With no FILE or if FILE is -, read Standard Input. `-F /' by default.\n\
`----------------------------------------------------------------------*/
/* Long options equivalences. */
-static const struct option long_options[] =
+static struct option const long_options[] =
{
{"auto-reference", no_argument, NULL, 'A'},
{"break-file", required_argument, NULL, 'b'},
- {"copyright", no_argument, NULL, 'C'}, /* Deprecated, remove in 2007. */
{"flag-truncation", required_argument, NULL, 'F'},
{"ignore-case", no_argument, NULL, 'f'},
{"gap-size", required_argument, NULL, 'g'},
@@ -1976,7 +1910,7 @@ main (int argc, char **argv)
/* Decode program options. */
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -1987,111 +1921,108 @@ main (int argc, char **argv)
setchrclass (NULL);
#endif
- while (optchar = getopt_long (argc, argv, "ACF:GM:ORS:TW:b:i:fg:o:trw:",
- long_options, NULL),
- optchar != EOF)
+ while (optchar = getopt_long (argc, argv, "AF:GM:ORS:TW:b:i:fg:o:trw:",
+ long_options, NULL),
+ optchar != EOF)
{
switch (optchar)
- {
- default:
- usage (EXIT_FAILURE);
-
- case 'G':
- gnu_extensions = false;
- break;
-
- case 'b':
- break_file = optarg;
- break;
-
- case 'f':
- ignore_case = true;
- break;
-
- case 'g':
- {
- unsigned long int tmp_ulong;
- if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
- || ! (0 < tmp_ulong && tmp_ulong <= INT_MAX))
- error (EXIT_FAILURE, 0, _("invalid gap width: %s"),
- quotearg (optarg));
- gap_size = tmp_ulong;
- break;
- }
-
- case 'i':
- ignore_file = optarg;
- break;
-
- case 'o':
- only_file = optarg;
- break;
-
- case 'r':
- input_reference = true;
- break;
-
- case 't':
- /* Yet to understand... */
- break;
-
- case 'w':
- {
- unsigned long int tmp_ulong;
- if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
- || ! (0 < tmp_ulong && tmp_ulong <= INT_MAX))
- error (EXIT_FAILURE, 0, _("invalid line width: %s"),
- quotearg (optarg));
- line_width = tmp_ulong;
- break;
- }
-
- case 'A':
- auto_reference = true;
- break;
-
- case 'F':
- truncation_string = copy_unescaped_string (optarg);
- break;
-
- case 'M':
- macro_name = optarg;
- break;
-
- case 'O':
- output_format = ROFF_FORMAT;
- break;
-
- case 'R':
- right_reference = true;
- break;
-
- case 'S':
- context_regex.string = copy_unescaped_string (optarg);
- break;
-
- case 'T':
- output_format = TEX_FORMAT;
- break;
-
- case 'W':
- word_regex.string = copy_unescaped_string (optarg);
- if (!*word_regex.string)
- word_regex.string = NULL;
- break;
-
- case 10:
- output_format = XARGMATCH ("--format", optarg,
- format_args, format_vals);
- case_GETOPT_HELP_CHAR;
-
- case 'C':
- error (0, 0, _("\
-the --copyright option is deprecated; use --version instead"));
- /* fallthrough */
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- }
+ {
+ default:
+ usage (EXIT_FAILURE);
+
+ case 'G':
+ gnu_extensions = false;
+ break;
+
+ case 'b':
+ break_file = optarg;
+ break;
+
+ case 'f':
+ ignore_case = true;
+ break;
+
+ case 'g':
+ {
+ unsigned long int tmp_ulong;
+ if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
+ || ! (0 < tmp_ulong && tmp_ulong <= INT_MAX))
+ error (EXIT_FAILURE, 0, _("invalid gap width: %s"),
+ quote (optarg));
+ gap_size = tmp_ulong;
+ break;
+ }
+
+ case 'i':
+ ignore_file = optarg;
+ break;
+
+ case 'o':
+ only_file = optarg;
+ break;
+
+ case 'r':
+ input_reference = true;
+ break;
+
+ case 't':
+ /* Yet to understand... */
+ break;
+
+ case 'w':
+ {
+ unsigned long int tmp_ulong;
+ if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
+ || ! (0 < tmp_ulong && tmp_ulong <= INT_MAX))
+ error (EXIT_FAILURE, 0, _("invalid line width: %s"),
+ quote (optarg));
+ line_width = tmp_ulong;
+ break;
+ }
+
+ case 'A':
+ auto_reference = true;
+ break;
+
+ case 'F':
+ truncation_string = copy_unescaped_string (optarg);
+ break;
+
+ case 'M':
+ macro_name = optarg;
+ break;
+
+ case 'O':
+ output_format = ROFF_FORMAT;
+ break;
+
+ case 'R':
+ right_reference = true;
+ break;
+
+ case 'S':
+ context_regex.string = copy_unescaped_string (optarg);
+ break;
+
+ case 'T':
+ output_format = TEX_FORMAT;
+ break;
+
+ case 'W':
+ word_regex.string = copy_unescaped_string (optarg);
+ if (!*word_regex.string)
+ word_regex.string = NULL;
+ break;
+
+ case 10:
+ output_format = XARGMATCH ("--format", optarg,
+ format_args, format_vals);
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ }
}
/* Process remaining arguments. If GNU extensions are enabled, process
@@ -2105,6 +2036,7 @@ the --copyright option is deprecated; use --version instead"));
input_file_name = xmalloc (sizeof *input_file_name);
file_line_count = xmalloc (sizeof *file_line_count);
+ text_buffers = xmalloc (sizeof *text_buffers);
number_input_files = 1;
input_file_name[0] = NULL;
}
@@ -2113,16 +2045,16 @@ the --copyright option is deprecated; use --version instead"));
number_input_files = argc - optind;
input_file_name = xmalloc (number_input_files * sizeof *input_file_name);
file_line_count = xmalloc (number_input_files * sizeof *file_line_count);
+ text_buffers = xmalloc (number_input_files * sizeof *text_buffers);
for (file_index = 0; file_index < number_input_files; file_index++)
- {
- input_file_name[file_index] = argv[optind];
- if (!*argv[optind] || STREQ (argv[optind], "-"))
- input_file_name[0] = NULL;
- else
- input_file_name[0] = argv[optind];
- optind++;
- }
+ {
+ if (!*argv[optind] || STREQ (argv[optind], "-"))
+ input_file_name[file_index] = NULL;
+ else
+ input_file_name[file_index] = argv[optind];
+ optind++;
+ }
}
else
{
@@ -2132,32 +2064,33 @@ the --copyright option is deprecated; use --version instead"));
number_input_files = 1;
input_file_name = xmalloc (sizeof *input_file_name);
file_line_count = xmalloc (sizeof *file_line_count);
+ text_buffers = xmalloc (sizeof *text_buffers);
if (!*argv[optind] || STREQ (argv[optind], "-"))
- input_file_name[0] = NULL;
+ input_file_name[0] = NULL;
else
- input_file_name[0] = argv[optind];
+ input_file_name[0] = argv[optind];
optind++;
/* Redirect standard output, only if requested. */
if (optind < argc)
- {
- if (! freopen (argv[optind], "w", stdout))
- error (EXIT_FAILURE, errno, "%s", argv[optind]);
- optind++;
- }
+ {
+ if (! freopen (argv[optind], "w", stdout))
+ error (EXIT_FAILURE, errno, "%s", quotef (argv[optind]));
+ optind++;
+ }
/* Diagnose any other argument as an error. */
if (optind < argc)
- {
- error (0, 0, _("extra operand %s"), quote (argv[optind]));
- usage (EXIT_FAILURE);
- }
+ {
+ error (0, 0, _("extra operand %s"), quote (argv[optind]));
+ usage (EXIT_FAILURE);
+ }
}
/* If the output format has not been explicitly selected, choose dumb
- terminal format if GNU extensions are enabled, else `roff' format. */
+ terminal format if GNU extensions are enabled, else 'roff' format. */
if (output_format == UNKNOWN_FORMAT)
output_format = gnu_extensions ? DUMB_FORMAT : ROFF_FORMAT;
@@ -2166,12 +2099,12 @@ the --copyright option is deprecated; use --version instead"));
initialize_regex ();
- /* Read `Break character' file, if any. */
+ /* Read 'Break character' file, if any. */
if (break_file)
digest_break_file (break_file);
- /* Read `Ignore words' file and `Only words' files, if any. If any of
+ /* Read 'Ignore words' file and 'Only words' files, if any. If any of
these files is empty, reset the name of the file to NULL, to avoid
unnecessary calls to search_table. */
@@ -2179,14 +2112,14 @@ the --copyright option is deprecated; use --version instead"));
{
digest_word_file (ignore_file, &ignore_table);
if (ignore_table.length == 0)
- ignore_file = NULL;
+ ignore_file = NULL;
}
if (only_file)
{
digest_word_file (only_file, &only_table);
if (only_table.length == 0)
- only_file = NULL;
+ only_file = NULL;
}
/* Prepare to study all the input files. */
@@ -2198,15 +2131,16 @@ the --copyright option is deprecated; use --version instead"));
for (file_index = 0; file_index < number_input_files; file_index++)
{
+ BLOCK *text_buffer = text_buffers + file_index;
- /* Read the file in core, than study it. */
+ /* Read the file in core, then study it. */
- swallow_file_in_memory (input_file_name[file_index], &text_buffer);
- find_occurs_in_text ();
+ swallow_file_in_memory (input_file_name[file_index], text_buffer);
+ find_occurs_in_text (file_index);
/* Maintain for each file how many lines has been read so far when its
- end is reached. Incrementing the count first is a simple kludge to
- handle a possible incomplete line at end of file. */
+ end is reached. Incrementing the count first is a simple kludge to
+ handle a possible incomplete line at end of file. */
total_line_count++;
file_line_count[file_index] = total_line_count;
@@ -2220,5 +2154,5 @@ the --copyright option is deprecated; use --version instead"));
/* All done. */
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/pwd.c b/src/pwd.c
index 4f16b73..e004098 100644
--- a/src/pwd.c
+++ b/src/pwd.c
@@ -1,10 +1,10 @@
/* pwd - print current directory
- Copyright (C) 1994-1997, 1999-2006 Free Software Foundation, Inc.
+ Copyright (C) 1994-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#include <getopt.h>
@@ -21,17 +20,15 @@
#include <sys/types.h>
#include "system.h"
-#include "dirfd.h"
#include "error.h"
-#include "long-options.h"
#include "quote.h"
#include "root-dev-ino.h"
#include "xgetcwd.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "pwd"
-#define AUTHORS "Jim Meyering"
+#define AUTHORS proper_name ("Jim Meyering")
struct file_name
{
@@ -40,26 +37,38 @@ struct file_name
char *start;
};
-/* The name this program was run with. */
-char *program_name;
+static struct option const longopts[] =
+{
+ {"logical", no_argument, NULL, 'L'},
+ {"physical", no_argument, NULL, 'P'},
+ {GETOPT_HELP_OPTION_DECL},
+ {GETOPT_VERSION_OPTION_DECL},
+ {NULL, 0, NULL, 0}
+};
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
- printf (_("Usage: %s [OPTION]\n"), program_name);
+ printf (_("Usage: %s [OPTION]...\n"), program_name);
fputs (_("\
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\
+ -P, --physical avoid all symlinks\n\
+"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ fputs (_("\n\
+If no option is specified, -P is assumed.\n\
+"), stdout);
printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -95,9 +104,9 @@ file_name_prepend (struct file_name *p, char const *s, size_t s_len)
{
size_t half = p->n_alloc + 1 + s_len;
/* Use xnmalloc+free rather than xnrealloc, 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. With xnmalloc, we
- copy it only once. */
+ we'd end up copying the data twice: once via realloc, then again
+ to align it with the end of the new buffer. With xnmalloc, we
+ copy it only once. */
char *q = xnmalloc (2, half);
size_t n_used = p->n_alloc - n_free;
p->start = q + 2 * half - n_used;
@@ -112,7 +121,7 @@ file_name_prepend (struct file_name *p, char const *s, size_t s_len)
memcpy (p->start + 1, s, s_len);
}
-/* Return a string (malloc'd) consisting of N `/'-separated ".." components. */
+/* Return a string (malloc'd) consisting of N '/'-separated ".." components. */
static char *
nth_parent (size_t n)
{
@@ -131,18 +140,18 @@ nth_parent (size_t n)
/* Determine the basename of the current directory, where DOT_SB is the
result of lstat'ing "." and prepend that to the file name in *FILE_NAME.
- Find the directory entry in `..' that matches the dev/i-node of DOT_SB.
- Upon success, update *DOT_SB with stat information of `..', chdir to `..',
+ Find the directory entry in '..' that matches the dev/i-node of DOT_SB.
+ Upon success, update *DOT_SB with stat information of '..', chdir to '..',
and prepend "/basename" to FILE_NAME.
Otherwise, exit with a diagnostic.
- PARENT_HEIGHT is the number of levels `..' is above the starting directory.
+ PARENT_HEIGHT is the number of levels '..' is above the starting directory.
The first time this function is called (from the initial directory),
PARENT_HEIGHT is 1. This is solely for diagnostics.
Exit nonzero upon error. */
static void
find_dir_entry (struct stat *dot_sb, struct file_name *file_name,
- size_t parent_height)
+ size_t parent_height)
{
DIR *dirp;
int fd;
@@ -153,16 +162,16 @@ find_dir_entry (struct stat *dot_sb, struct file_name *file_name,
dirp = opendir ("..");
if (dirp == NULL)
error (EXIT_FAILURE, errno, _("cannot open directory %s"),
- quote (nth_parent (parent_height)));
+ quote (nth_parent (parent_height)));
fd = dirfd (dirp);
if ((0 <= fd ? fchdir (fd) : chdir ("..")) < 0)
error (EXIT_FAILURE, errno, _("failed to chdir to %s"),
- quote (nth_parent (parent_height)));
+ quote (nth_parent (parent_height)));
if ((0 <= fd ? fstat (fd, &parent_sb) : stat (".", &parent_sb)) < 0)
error (EXIT_FAILURE, errno, _("failed to stat %s"),
- quote (nth_parent (parent_height)));
+ 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. */
@@ -177,57 +186,57 @@ find_dir_entry (struct stat *dot_sb, struct file_name *file_name,
errno = 0;
if ((dp = readdir_ignoring_dot_and_dotdot (dirp)) == NULL)
- {
- if (errno)
- {
- /* Save/restore errno across closedir call. */
- int e = errno;
- closedir (dirp);
- errno = e;
-
- /* Arrange to give a diagnostic after exiting this loop. */
- dirp = NULL;
- }
- break;
- }
+ {
+ if (errno)
+ {
+ /* Save/restore errno across closedir call. */
+ int e = errno;
+ closedir (dirp);
+ errno = e;
+
+ /* Arrange to give a diagnostic after exiting this loop. */
+ dirp = NULL;
+ }
+ break;
+ }
ino = D_INO (dp);
if (ino == NOT_AN_INODE_NUMBER || use_lstat)
- {
- if (lstat (dp->d_name, &ent_sb) < 0)
- {
- /* Skip any entry we can't stat. */
- continue;
- }
- ino = ent_sb.st_ino;
- }
+ {
+ if (lstat (dp->d_name, &ent_sb) < 0)
+ {
+ /* Skip any entry we can't stat. */
+ continue;
+ }
+ ino = ent_sb.st_ino;
+ }
if (ino != dot_sb->st_ino)
- continue;
+ continue;
/* If we're not crossing a device boundary, then a simple i-node
- match is enough. */
+ match is enough. */
if ( ! use_lstat || ent_sb.st_dev == dot_sb->st_dev)
- {
- file_name_prepend (file_name, dp->d_name, _D_EXACT_NAMLEN (dp));
- found = true;
- break;
- }
+ {
+ file_name_prepend (file_name, dp->d_name, _D_EXACT_NAMLEN (dp));
+ found = true;
+ break;
+ }
}
if (dirp == NULL || closedir (dirp) != 0)
{
/* Note that this diagnostic serves for both readdir
- and closedir failures. */
+ and closedir failures. */
error (EXIT_FAILURE, errno, _("reading directory %s"),
- quote (nth_parent (parent_height)));
+ quote (nth_parent (parent_height)));
}
if ( ! found)
error (EXIT_FAILURE, 0,
- _("couldn't find directory entry in %s with matching i-node"),
- quote (nth_parent (parent_height)));
+ _("couldn't find directory entry in %s with matching i-node"),
+ quote (nth_parent (parent_height)));
*dot_sb = parent_sb;
}
@@ -237,7 +246,7 @@ find_dir_entry (struct stat *dot_sb, struct file_name *file_name,
The getcwd function performs nearly the same task, but is typically
unable to handle names longer than PATH_MAX. This function has
no such limitation. However, this function *can* fail due to
- permission problems or a lack of memory, while Linux's getcwd
+ permission problems or a lack of memory, while GNU/Linux's getcwd
function works regardless of restricted permissions on parent
directories. Upon failure, give a diagnostic and exit nonzero.
@@ -248,7 +257,7 @@ find_dir_entry (struct stat *dot_sb, struct file_name *file_name,
the information the caller would require in order to produce good
diagnostics, it doesn't seem worth the added complexity.
In any case, any getcwd replacement must *not* exceed the PATH_MAX
- limitation. Otherwise, functions like `chdir' would fail with
+ limitation. Otherwise, functions like 'chdir' would fail with
ENAMETOOLONG.
FIXME-maybe: if find_dir_entry fails due to permissions, try getcwd,
@@ -265,16 +274,16 @@ robust_getcwd (struct file_name *file_name)
if (root_dev_ino == NULL)
error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
- quote ("/"));
+ quoteaf ("/"));
if (stat (".", &dot_sb) < 0)
- error (EXIT_FAILURE, errno, _("failed to stat %s"), quote ("."));
+ error (EXIT_FAILURE, errno, _("failed to stat %s"), quoteaf ("."));
while (1)
{
/* If we've reached the root, we're done. */
if (SAME_INODE (dot_sb, *root_dev_ino))
- break;
+ break;
find_dir_entry (&dot_sb, file_name, height++);
}
@@ -284,27 +293,89 @@ robust_getcwd (struct file_name *file_name)
file_name_prepend (file_name, "", 0);
}
+
+/* Return PWD from the environment if it is acceptable for 'pwd -L'
+ output, otherwise NULL. */
+static char *
+logical_getcwd (void)
+{
+ struct stat st1;
+ struct stat st2;
+ char *wd = getenv ("PWD");
+ char *p;
+
+ /* Textual validation first. */
+ if (!wd || wd[0] != '/')
+ return NULL;
+ p = wd;
+ while ((p = strstr (p, "/.")))
+ {
+ if (!p[2] || p[2] == '/'
+ || (p[2] == '.' && (!p[3] || p[3] == '/')))
+ return NULL;
+ p++;
+ }
+
+ /* System call validation. */
+ if (stat (wd, &st1) == 0 && stat (".", &st2) == 0 && SAME_INODE (st1, st2))
+ return wd;
+ 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") != NULL);
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
- if (getopt_long (argc, argv, "", NULL, NULL) != -1)
- usage (EXIT_FAILURE);
+ while (1)
+ {
+ int c = getopt_long (argc, argv, "LP", longopts, NULL);
+ if (c == -1)
+ break;
+ switch (c)
+ {
+ case 'L':
+ logical = true;
+ break;
+ case 'P':
+ logical = false;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
+ }
if (optind < argc)
error (0, 0, _("ignoring non-option arguments"));
+ if (logical)
+ {
+ wd = logical_getcwd ();
+ if (wd)
+ {
+ puts (wd);
+ return EXIT_SUCCESS;
+ }
+ }
+
wd = xgetcwd ();
if (wd != NULL)
{
@@ -319,5 +390,5 @@ main (int argc, char **argv)
file_name_free (file_name);
}
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/readlink.c b/src/readlink.c
index 121c7ff..2444f86 100644
--- a/src/readlink.c
+++ b/src/readlink.c
@@ -1,10 +1,10 @@
/* readlink -- display value of a symbolic link.
- Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+ Copyright (C) 2002-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Dmitry V. Levin */
@@ -25,16 +24,12 @@
#include "system.h"
#include "canonicalize.h"
#include "error.h"
-#include "xreadlink.h"
-#include "quote.h"
+#include "areadlink.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "readlink"
-#define AUTHORS "Dmitry V. Levin"
-
-/* Name this program was run with. */
-char *program_name;
+#define AUTHORS proper_name ("Dmitry V. Levin")
/* If true, do not output the trailing newline. */
static bool no_newline;
@@ -51,6 +46,7 @@ static struct option const longopts[] =
{"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},
{NULL, 0, NULL, 0}
@@ -60,33 +56,36 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
- printf (_("Usage: %s [OPTION]... FILE\n"), program_name);
- fputs (_("Display value of a symbolic link on standard output.\n\n"),
- stdout);
+ 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\
+ 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\
+ 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\
+ every component of the given name recursively,\
+\n\
without requirements on components existence\n\
- -n, --no-newline do not output the trailing newline\n\
+ -n, --no-newline do not output the trailing delimiter\n\
-q, --quiet,\n\
-s, --silent suppress most error messages\n\
-v, --verbose report error messages\n\
+ -z, --zero end each output line with NUL, not newline\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -96,51 +95,49 @@ main (int argc, char **argv)
{
/* If not -1, use this method to canonicalize. */
int can_mode = -1;
-
- /* File name to canonicalize. */
- const char *fname;
-
- /* Result of canonicalize. */
- char *value;
-
+ int status = EXIT_SUCCESS;
int optc;
+ bool use_nuls = false;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "efmnqsv", longopts, NULL)) != -1)
+ while ((optc = getopt_long (argc, argv, "efmnqsvz", longopts, NULL)) != -1)
{
switch (optc)
- {
- case 'e':
- can_mode = CAN_EXISTING;
- break;
- case 'f':
- can_mode = CAN_ALL_BUT_LAST;
- break;
- case 'm':
- can_mode = CAN_MISSING;
- break;
- case 'n':
- no_newline = true;
- break;
- case 'q':
- case 's':
- verbose = false;
- break;
- case 'v':
- verbose = true;
- break;
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'e':
+ can_mode = CAN_EXISTING;
+ break;
+ case 'f':
+ can_mode = CAN_ALL_BUT_LAST;
+ break;
+ case 'm':
+ can_mode = CAN_MISSING;
+ break;
+ case 'n':
+ no_newline = true;
+ break;
+ case 'q':
+ case 's':
+ verbose = false;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ case 'z':
+ use_nuls = true;
+ break;
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
}
if (optind >= argc)
@@ -149,26 +146,33 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
- fname = argv[optind++];
-
- if (optind < argc)
+ if (argc - optind > 1)
{
- error (0, 0, _("extra operand %s"), quote (argv[optind]));
- usage (EXIT_FAILURE);
+ if (no_newline)
+ error (0, 0, _("ignoring --no-newline with multiple arguments"));
+ no_newline = false;
}
- value = (can_mode != -1
- ? canonicalize_filename_mode (fname, can_mode)
- : xreadlink (fname));
- if (value)
+ for (; optind < argc; ++optind)
{
- printf ("%s%s", value, (no_newline ? "" : "\n"));
- free (value);
- return EXIT_SUCCESS;
+ const char *fname = argv[optind];
+ char *value = (can_mode != -1
+ ? canonicalize_filename_mode (fname, can_mode)
+ : areadlink_with_size (fname, 63));
+ if (value)
+ {
+ fputs (value, stdout);
+ if (! no_newline)
+ putchar (use_nuls ? '\0' : '\n');
+ free (value);
+ }
+ else
+ {
+ status = EXIT_FAILURE;
+ if (verbose)
+ error (0, errno, "%s", quotef (fname));
+ }
}
- if (verbose)
- error (EXIT_FAILURE, errno, "%s", fname);
-
- return EXIT_FAILURE;
+ return status;
}
diff --git a/src/realpath.c b/src/realpath.c
new file mode 100644
index 0000000..6b6c2c0
--- /dev/null
+++ b/src/realpath.c
@@ -0,0 +1,277 @@
+/* realpath - print the resolved path
+ Copyright (C) 2011-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Written by Pádraig Brady. */
+
+#include <config.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "system.h"
+#include "canonicalize.h"
+#include "error.h"
+#include "relpath.h"
+
+/* The official name of this program (e.g., no 'g' prefix). */
+#define PROGRAM_NAME "realpath"
+
+#define AUTHORS proper_name_utf8 ("Padraig Brady", "P\303\241draig Brady")
+
+enum
+{
+ RELATIVE_TO_OPTION = CHAR_MAX + 1,
+ RELATIVE_BASE_OPTION
+};
+
+static bool verbose = true;
+static bool logical;
+static bool use_nuls;
+static const char *can_relative_to;
+static const char *can_relative_base;
+
+static struct option const longopts[] =
+{
+ {"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},
+ {NULL, 0, NULL, 0}
+};
+
+void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ emit_try_help ();
+ else
+ {
+ printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
+ fputs (_("\
+Print the resolved absolute file name;\n\
+all but the last component must exist\n\
+\n\
+"), stdout);
+ fputs (_("\
+ -e, --canonicalize-existing all components of the path must exist\n\
+ -m, --canonicalize-missing no path components need exist or be a directory\
+\n\
+ -L, --logical resolve '..' components before symlinks\n\
+ -P, --physical resolve symlinks as encountered (default)\n\
+ -q, --quiet suppress most error messages\n\
+ --relative-to=FILE print the resolved path relative to FILE\n\
+ --relative-base=FILE print absolute paths unless paths below FILE\n\
+ -s, --strip, --no-symlinks don't expand symlinks\n\
+ -z, --zero end each output line with NUL, not newline\n\
+\n\
+"), stdout);
+ fputs (HELP_OPTION_DESCRIPTION, stdout);
+ fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ emit_ancillary_info (PROGRAM_NAME);
+ }
+ exit (status);
+}
+
+/* A wrapper around canonicalize_filename_mode(),
+ to call it twice when in LOGICAL mode. */
+static char *
+realpath_canon (const char *fname, int can_mode)
+{
+ char *can_fname = canonicalize_filename_mode (fname, can_mode);
+ if (logical && can_fname) /* canonicalize again to resolve symlinks. */
+ {
+ can_mode &= ~CAN_NOLINKS;
+ char *can_fname2 = canonicalize_filename_mode (can_fname, can_mode);
+ free (can_fname);
+ return can_fname2;
+ }
+ return can_fname;
+}
+
+/* Test whether canonical prefix is parent or match of path. */
+static bool _GL_ATTRIBUTE_PURE
+path_prefix (const char *prefix, const char *path)
+{
+ /* We already know prefix[0] and path[0] are '/'. */
+ prefix++;
+ path++;
+
+ /* '/' is the prefix of everything except '//' (since we know '//'
+ is only present after canonicalization if it is distinct). */
+ if (!*prefix)
+ return *path != '/';
+
+ /* Likewise, '//' is a prefix of any double-slash path. */
+ if (*prefix == '/' && !prefix[1])
+ return *path == '/';
+
+ /* Any other prefix has a non-slash portion. */
+ while (*prefix && *path)
+ {
+ if (*prefix != *path)
+ break;
+ prefix++;
+ path++;
+ }
+ return (!*prefix && (*path == '/' || !*path));
+}
+
+static bool
+isdir (const char *path)
+{
+ struct stat sb;
+ if (stat (path, &sb) != 0)
+ error (EXIT_FAILURE, errno, _("cannot stat %s"), quoteaf (path));
+ return S_ISDIR (sb.st_mode);
+}
+
+static bool
+process_path (const char *fname, int can_mode)
+{
+ char *can_fname = realpath_canon (fname, can_mode);
+ if (!can_fname)
+ {
+ if (verbose)
+ error (0, errno, "%s", quotef (fname));
+ return false;
+ }
+
+ if (!can_relative_to
+ || (can_relative_base && !path_prefix (can_relative_base, can_fname))
+ || (can_relative_to && !relpath (can_fname, can_relative_to, NULL, 0)))
+ fputs (can_fname, stdout);
+
+ putchar (use_nuls ? '\0' : '\n');
+
+ free (can_fname);
+
+ return true;
+}
+
+int
+main (int argc, char **argv)
+{
+ bool ok = true;
+ int can_mode = CAN_ALL_BUT_LAST;
+ const char *relative_to = NULL;
+ const char *relative_base = NULL;
+
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ atexit (close_stdout);
+
+ while (1)
+ {
+ int c = getopt_long (argc, argv, "eLmPqsz", longopts, NULL);
+ if (c == -1)
+ break;
+ switch (c)
+ {
+ case 'e':
+ can_mode &= ~CAN_MODE_MASK;
+ can_mode |= CAN_EXISTING;
+ break;
+ case 'm':
+ can_mode &= ~CAN_MODE_MASK;
+ can_mode |= CAN_MISSING;
+ break;
+ case 'L':
+ can_mode |= CAN_NOLINKS;
+ logical = true;
+ break;
+ case 's':
+ can_mode |= CAN_NOLINKS;
+ logical = false;
+ break;
+ case 'P':
+ can_mode &= ~CAN_NOLINKS;
+ logical = false;
+ break;
+ case 'q':
+ verbose = false;
+ break;
+ case 'z':
+ use_nuls = true;
+ break;
+ case RELATIVE_TO_OPTION:
+ relative_to = optarg;
+ break;
+ case RELATIVE_BASE_OPTION:
+ relative_base = optarg;
+ break;
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
+ }
+
+ if (optind >= argc)
+ {
+ error (0, 0, _("missing operand"));
+ usage (EXIT_FAILURE);
+ }
+
+ if (relative_base && !relative_to)
+ relative_to = relative_base;
+
+ bool need_dir = (can_mode & CAN_MODE_MASK) == CAN_EXISTING;
+ if (relative_to)
+ {
+ can_relative_to = realpath_canon (relative_to, can_mode);
+ if (!can_relative_to)
+ error (EXIT_FAILURE, errno, "%s", quotef (relative_to));
+ if (need_dir && !isdir (can_relative_to))
+ error (EXIT_FAILURE, ENOTDIR, "%s", quotef (relative_to));
+ }
+ if (relative_base == relative_to)
+ can_relative_base = can_relative_to;
+ else if (relative_base)
+ {
+ char *base = realpath_canon (relative_base, can_mode);
+ if (!base)
+ error (EXIT_FAILURE, errno, "%s", quotef (relative_base));
+ if (need_dir && !isdir (base))
+ error (EXIT_FAILURE, ENOTDIR, "%s", quotef (relative_base));
+ /* --relative-to is a no-op if it does not have --relative-base
+ as a prefix */
+ if (path_prefix (base, can_relative_to))
+ can_relative_base = base;
+ else
+ {
+ free (base);
+ can_relative_base = can_relative_to;
+ can_relative_to = NULL;
+ }
+ }
+
+ for (; optind < argc; ++optind)
+ ok &= process_path (argv[optind], can_mode);
+
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/relpath.c b/src/relpath.c
new file mode 100644
index 0000000..75516c0
--- /dev/null
+++ b/src/relpath.c
@@ -0,0 +1,133 @@
+/* relpath - print the relative path
+ Copyright (C) 2012-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Written by Pádraig Brady. */
+
+#include <config.h>
+
+#include "error.h"
+#include "system.h"
+#include "relpath.h"
+
+
+/* Return the length of the longest common prefix
+ of canonical PATH1 and PATH2, ensuring only full path components
+ are matched. Return 0 on no match. */
+static int _GL_ATTRIBUTE_PURE
+path_common_prefix (const char *path1, const char *path2)
+{
+ int i = 0;
+ int ret = 0;
+
+ /* We already know path1[0] and path2[0] are '/'. Special case
+ '//', which is only present in a canonical name on platforms
+ where it is distinct. */
+ if ((path1[1] == '/') != (path2[1] == '/'))
+ return 0;
+
+ while (*path1 && *path2)
+ {
+ if (*path1 != *path2)
+ break;
+ if (*path1 == '/')
+ ret = i + 1;
+ path1++;
+ path2++;
+ i++;
+ }
+
+ if ((!*path1 && !*path2)
+ || (!*path1 && *path2 == '/')
+ || (!*path2 && *path1 == '/'))
+ ret = i;
+
+ return ret;
+}
+
+/* Either output STR to stdout or
+ if *PBUF is not NULL then append STR to *PBUF
+ and update *PBUF to point to the end of the buffer
+ and adjust *PLEN to reflect the remaining space.
+ Return TRUE on failure. */
+static bool
+buffer_or_output (const char* str, char **pbuf, size_t *plen)
+{
+ if (*pbuf)
+ {
+ size_t slen = strlen (str);
+ if (slen >= *plen)
+ return true;
+ memcpy (*pbuf, str, slen + 1);
+ *pbuf += slen;
+ *plen -= slen;
+ }
+ else
+ {
+ fputs (str, stdout);
+ }
+
+ return false;
+}
+
+/* Output the relative representation if possible.
+ If BUF is non-NULL, write to that buffer rather than to stdout. */
+bool
+relpath (const char *can_fname, const char *can_reldir, char *buf, size_t len)
+{
+ bool buf_err = false;
+
+ /* Skip the prefix common to --relative-to and path. */
+ int common_index = path_common_prefix (can_reldir, can_fname);
+ if (!common_index)
+ return false;
+
+ const char *relto_suffix = can_reldir + common_index;
+ const char *fname_suffix = can_fname + common_index;
+
+ /* Skip over extraneous '/'. */
+ if (*relto_suffix == '/')
+ relto_suffix++;
+ if (*fname_suffix == '/')
+ fname_suffix++;
+
+ /* Replace remaining components of --relative-to with '..', to get
+ to a common directory. Then output the remainder of fname. */
+ if (*relto_suffix)
+ {
+ buf_err |= buffer_or_output ("..", &buf, &len);
+ for (; *relto_suffix; ++relto_suffix)
+ {
+ if (*relto_suffix == '/')
+ buf_err |= buffer_or_output ("/..", &buf, &len);
+ }
+
+ if (*fname_suffix)
+ {
+ buf_err |= buffer_or_output ("/", &buf, &len);
+ buf_err |= buffer_or_output (fname_suffix, &buf, &len);
+ }
+ }
+ else
+ {
+ buf_err |= buffer_or_output (*fname_suffix ? fname_suffix : ".",
+ &buf, &len);
+ }
+
+ if (buf_err)
+ error (0, ENAMETOOLONG, "%s", _("generating relative path"));
+
+ return !buf_err;
+}
diff --git a/src/relpath.h b/src/relpath.h
new file mode 100644
index 0000000..34402d7
--- /dev/null
+++ b/src/relpath.h
@@ -0,0 +1,25 @@
+/* relpath - print the relative path
+ Copyright (C) 2012-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Written by Pádraig Brady. */
+
+#ifndef _RELPATH_H
+# define _RELPATH_H
+
+extern bool
+relpath (const char *can_fname, const char *can_reldir, char *buf, size_t len);
+
+#endif
diff --git a/src/remove.c b/src/remove.c
index 59ee9e5..7309f03 100644
--- a/src/remove.c
+++ b/src/remove.c
@@ -1,10 +1,10 @@
/* remove.c -- core functions for removing files and directories
- Copyright (C) 88, 90, 91, 1994-2007 Free Software Foundation, Inc.
+ Copyright (C) 1988-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,64 +12,25 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
-/* Extracted from rm.c and librarified, then rewritten by Jim Meyering. */
+/* Extracted from rm.c, librarified, then rewritten twice by Jim Meyering. */
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
-#include <setjmp.h>
#include <assert.h>
#include "system.h"
-#include "cycle-check.h"
-#include "dirfd.h"
#include "error.h"
-#include "euidaccess.h"
-#include "euidaccess-stat.h"
#include "file-type.h"
-#include "hash.h"
-#include "hash-pjw.h"
-#include "lstat.h"
-#include "obstack.h"
-#include "openat.h"
-#include "quote.h"
+#include "ignore-value.h"
#include "remove.h"
#include "root-dev-ino.h"
-#include "unlinkdir.h"
+#include "write-any-file.h"
+#include "xfts.h"
#include "yesno.h"
-/* Avoid shadowing warnings because these are functions declared
- in dirname.h as well as locals used below. */
-#define dir_name rm_dir_name
-#define dir_len rm_dir_len
-
-#define obstack_chunk_alloc malloc
-#define obstack_chunk_free free
-
-/* This is the maximum number of consecutive readdir/unlink calls that
- can be made (with no intervening rewinddir or closedir/opendir) before
- triggering a bug that makes readdir return NULL even though some
- directory entries have not been processed. The bug afflicts SunOS's
- readdir when applied to ufs file systems and Darwin 6.5's (and OSX
- v.10.3.8's) HFS+. This maximum is conservative in that demonstrating
- the problem requires a directory containing at least 16 deletable
- entries (which doesn't count . and ..).
- This problem also affects Darwin 7.9.0 (aka MacOS X 10.3.9) on HFS+
- and NFS-mounted file systems, but not vfat ones. */
-enum
- {
- CONSECUTIVE_READDIR_UNLINK_THRESHOLD = 10
- };
-
-/* FIXME: in 2009, or whenever Darwin 7.9.0 (aka MacOS X 10.3.9) is no
- longer relevant, remove this work-around code. Then, there will be
- no need to perform the extra rewinddir call, ever. */
-#define NEED_REWIND(readdir_unlink_count) \
- (CONSECUTIVE_READDIR_UNLINK_THRESHOLD <= (readdir_unlink_count))
-
enum Ternary
{
T_UNKNOWN = 2,
@@ -87,79 +48,34 @@ enum Prompt_action
PA_REMOVE_DIR
};
-/* Initial capacity of per-directory hash table of entries that have
- been processed but not been deleted. */
-enum { HT_UNREMOVABLE_INITIAL_CAPACITY = 13 };
-
-/* An entry in the active directory stack.
- Each entry corresponds to an `active' directory. */
-struct AD_ent
-{
- /* For a given active directory, this is the set of names of
- entries in that directory that could/should not be removed.
- For example, `.' and `..', as well as files/dirs for which
- unlink/rmdir failed e.g., due to access restrictions. */
- Hash_table *unremovable;
-
- /* Record the status for a given active directory; we need to know
- whether an entry was not removed, either because of an error or
- because the user declined. */
- enum RM_status status;
-
- /* The directory's dev/ino. Used to ensure that a malicious user does
- not replace a directory we're about to process with a symlink to
- some other directory. */
- struct dev_ino dev_ino;
-};
-
-extern char *program_name;
-
-struct dirstack_state
-{
- /* The name of the directory (starting with and relative to a command
- line argument) being processed. When a subdirectory is entered, a new
- component is appended (pushed). Remove (pop) the top component
- upon chdir'ing out of a directory. This is used to form the full
- name of the current directory or a file therein, when necessary. */
- struct obstack dir_stack;
-
- /* Stack of lengths of directory names (including trailing slash)
- appended to dir_stack. We have to have a separate stack of lengths
- (rather than just popping back to previous slash) because the first
- element pushed onto the dir stack may contain slashes. */
- struct obstack len_stack;
-
- /* Stack of active directory entries.
- The first `active' directory is the initial working directory.
- Additional active dirs are pushed onto the stack as we `chdir'
- into each directory to be processed. When finished with the
- hierarchy under a directory, pop the active dir stack. */
- struct obstack Active_dir;
-
- /* Used to detect cycles. */
- struct cycle_check_state cycle_check_state;
-
- /* Target of a longjmp in case rm has to stop processing the current
- command-line argument. This happens 1) when rm detects a directory
- cycle or 2) when it has processed one or more directories, but then
- is unable to return to the initial working directory to process
- additional `.'-relative command-line arguments. */
- jmp_buf current_arg_jumpbuf;
-};
-typedef struct dirstack_state Dirstack_state;
+/* D_TYPE(D) is the type of directory entry D if known, DT_UNKNOWN
+ otherwise. */
+#if ! HAVE_STRUCT_DIRENT_D_TYPE
+/* Any int values will do here, so long as they're distinct.
+ Undef any existing macros out of the way. */
+# undef DT_UNKNOWN
+# undef DT_DIR
+# undef DT_LNK
+# define DT_UNKNOWN 0
+# define DT_DIR 1
+# define DT_LNK 2
+#endif
/* Like fstatat, but cache the result. If ST->st_size is -1, the
status has not been gotten yet. If less than -1, fstatat failed
- with errno == -1 - ST->st_size. Otherwise, the status has already
+ with errno == ST->st_ino. Otherwise, the status has already
been gotten, so return 0. */
static int
cache_fstatat (int fd, char const *file, struct stat *st, int flag)
{
if (st->st_size == -1 && fstatat (fd, file, st, flag) != 0)
- st->st_size = -1 - errno;
+ {
+ st->st_size = -2;
+ st->st_ino = errno;
+ }
if (0 <= st->st_size)
return 0;
- errno = -1 - st->st_size;
+ errno = (int) st->st_ino;
return -1;
}
@@ -171,552 +87,19 @@ cache_stat_init (struct stat *st)
return st;
}
-/* Return true if *ST has been statted. */
-static inline bool
-cache_statted (struct stat *st)
-{
- return (st->st_size != -1);
-}
-
-/* Return true if *ST has been statted successfully. */
-static inline bool
-cache_stat_ok (struct stat *st)
-{
- return (0 <= st->st_size);
-}
-
-
-static void
-hash_freer (void *x)
-{
- free (x);
-}
-
-static bool
-hash_compare_strings (void const *x, void const *y)
-{
- return STREQ (x, y) ? true : false;
-}
-
-static inline void
-push_dir (Dirstack_state *ds, const char *dir_name)
-{
- size_t len = strlen (dir_name);
-
- /* Append the string onto the stack. */
- obstack_grow (&ds->dir_stack, dir_name, len);
-
- /* Append a trailing slash. */
- obstack_1grow (&ds->dir_stack, '/');
-
- /* Add one for the slash. */
- ++len;
-
- /* Push the length (including slash) onto its stack. */
- obstack_grow (&ds->len_stack, &len, sizeof (len));
-}
-
-/* Return the entry name of the directory on the top of the stack
- in malloc'd storage. */
-static inline char *
-top_dir (Dirstack_state const *ds)
-{
- size_t n_lengths = obstack_object_size (&ds->len_stack) / sizeof (size_t);
- size_t *length = obstack_base (&ds->len_stack);
- size_t top_len = length[n_lengths - 1];
- char const *p = obstack_next_free (&ds->dir_stack) - top_len;
- char *q = xmalloc (top_len);
- memcpy (q, p, top_len - 1);
- q[top_len - 1] = 0;
- return q;
-}
-
-static inline void
-pop_dir (Dirstack_state *ds)
-{
- size_t n_lengths = obstack_object_size (&ds->len_stack) / sizeof (size_t);
- size_t *length = obstack_base (&ds->len_stack);
-
- assert (n_lengths > 0);
- size_t top_len = length[n_lengths - 1];
- assert (top_len >= 2);
-
- /* Pop the specified length of file name. */
- assert (obstack_object_size (&ds->dir_stack) >= top_len);
- obstack_blank (&ds->dir_stack, -top_len);
-
- /* Pop the length stack, too. */
- assert (obstack_object_size (&ds->len_stack) >= sizeof (size_t));
- obstack_blank (&ds->len_stack, -(int) sizeof (size_t));
-}
-
-/* Copy the SRC_LEN bytes of data beginning at SRC into the DST_LEN-byte
- buffer, DST, so that the last source byte is at the end of the destination
- buffer. If SRC_LEN is longer than DST_LEN, then set *TRUNCATED.
- Set *RESULT to point to the beginning of (the portion of) the source data
- in DST. Return the number of bytes remaining in the destination buffer. */
-
-static size_t
-right_justify (char *dst, size_t dst_len, const char *src, size_t src_len,
- char **result, bool *truncated)
-{
- const char *sp;
- char *dp;
-
- if (src_len <= dst_len)
- {
- sp = src;
- dp = dst + (dst_len - src_len);
- *truncated = false;
- }
- else
- {
- sp = src + (src_len - dst_len);
- dp = dst;
- src_len = dst_len;
- *truncated = true;
- }
-
- *result = memcpy (dp, sp, src_len);
- return dst_len - src_len;
-}
-
-/* Using the global directory name obstack, create the full name FILENAME.
- Return it in sometimes-realloc'd space that should not be freed by the
- caller. Realloc as necessary. If realloc fails, use a static buffer
- and put as long a suffix in that buffer as possible. */
-
-#define full_filename(Filename) full_filename_ (ds, Filename)
-static char *
-full_filename_ (Dirstack_state const *ds, const char *filename)
-{
- static char *buf = NULL;
- static size_t n_allocated = 0;
-
- size_t dir_len = obstack_object_size (&ds->dir_stack);
- char *dir_name = obstack_base (&ds->dir_stack);
- size_t n_bytes_needed;
- size_t filename_len;
-
- filename_len = strlen (filename);
- n_bytes_needed = dir_len + filename_len + 1;
-
- if (n_allocated < n_bytes_needed)
- {
- /* This code requires that realloc accept NULL as the first arg.
- This function must not use xrealloc. Otherwise, an out-of-memory
- error involving a file name to be expanded here wouldn't ever
- be issued. Use realloc and fall back on using a static buffer
- if memory allocation fails. */
- char *new_buf = realloc (buf, n_bytes_needed);
- n_allocated = n_bytes_needed;
-
- if (new_buf == NULL)
- {
-#define SBUF_SIZE 512
-#define ELLIPSES_PREFIX "[...]"
- static char static_buf[SBUF_SIZE];
- bool truncated;
- size_t len;
- char *p;
-
- free (buf);
- len = right_justify (static_buf, SBUF_SIZE, filename,
- filename_len + 1, &p, &truncated);
- right_justify (static_buf, len, dir_name, dir_len, &p, &truncated);
- if (truncated)
- {
- memcpy (static_buf, ELLIPSES_PREFIX,
- sizeof (ELLIPSES_PREFIX) - 1);
- }
- return p;
- }
-
- buf = new_buf;
- }
-
- if (filename_len == 1 && *filename == '.' && dir_len)
- {
- /* FILENAME is just `.' and dir_len is nonzero.
- Copy the directory part, omitting the trailing slash,
- and append a trailing zero byte. */
- char *p = mempcpy (buf, dir_name, dir_len - 1);
- *p = 0;
- }
- else
- {
- /* Copy the directory part, including trailing slash, and then
- append the filename part, including a trailing zero byte. */
- memcpy (mempcpy (buf, dir_name, dir_len), filename, filename_len + 1);
- assert (strlen (buf) + 1 == n_bytes_needed);
- }
-
- return buf;
-}
-
-static inline size_t
-AD_stack_height (Dirstack_state const *ds)
-{
- return obstack_object_size (&ds->Active_dir) / sizeof (struct AD_ent);
-}
-
-static inline struct AD_ent *
-AD_stack_top (Dirstack_state const *ds)
-{
- return (struct AD_ent *)
- ((char *) obstack_next_free (&ds->Active_dir) - sizeof (struct AD_ent));
-}
-
-static void
-AD_stack_pop (Dirstack_state *ds)
-{
- assert (0 < AD_stack_height (ds));
-
- /* operate on Active_dir. pop and free top entry */
- struct AD_ent *top = AD_stack_top (ds);
- if (top->unremovable)
- hash_free (top->unremovable);
- obstack_blank (&ds->Active_dir, -(int) sizeof (struct AD_ent));
-}
-
-static void
-AD_stack_clear (Dirstack_state *ds)
-{
- while (0 < AD_stack_height (ds))
- {
- AD_stack_pop (ds);
- }
-}
-
-static Dirstack_state *
-ds_init (void)
-{
- Dirstack_state *ds = xmalloc (sizeof *ds);
- obstack_init (&ds->dir_stack);
- obstack_init (&ds->len_stack);
- obstack_init (&ds->Active_dir);
- return ds;
-}
-
-static void
-ds_clear (Dirstack_state *ds)
-{
- obstack_free (&ds->dir_stack, obstack_finish (&ds->dir_stack));
- obstack_free (&ds->len_stack, obstack_finish (&ds->len_stack));
- while (0 < AD_stack_height (ds))
- AD_stack_pop (ds);
- obstack_free (&ds->Active_dir, obstack_finish (&ds->Active_dir));
-}
-
-static void
-ds_free (Dirstack_state *ds)
-{
- obstack_free (&ds->dir_stack, NULL);
- obstack_free (&ds->len_stack, NULL);
- obstack_free (&ds->Active_dir, NULL);
- free (ds);
-}
-
-/* Pop the active directory (AD) stack and prepare to move `up' one level,
- safely. Moving `up' usually means opening `..', but when we've just
- finished recursively processing a command-line directory argument,
- there's nothing left on the stack, so set *FDP to AT_FDCWD in that case.
- The idea is to return with *FDP opened on the parent directory,
- assuming there are entries in that directory that we need to remove.
-
- Note that we must not call opendir (or fdopendir) just yet, since
- the caller must first remove the directory we're coming from.
- That is because some file system implementations cache readdir
- results at opendir time; so calling opendir, rmdir, readdir would
- return an entry for the just-removed directory.
-
- Whenever using chdir '..' (virtually, now, via openat), verify
- that the post-chdir dev/ino numbers for `.' match the saved ones.
- If any system call fails or if dev/ino don't match, then give a
- diagnostic and longjump out.
- Return the name (in malloc'd storage) of the
- directory (usually now empty) from which we're coming, and which
- corresponds to the input value of DIRP.
-
- Finally, note that while this function's name is no longer as
- accurate as it once was (it no longer calls chdir), it does open
- the destination directory. */
-static char *
-AD_pop_and_chdir (DIR *dirp, int *fdp, Dirstack_state *ds)
-{
- struct AD_ent *leaf_dir_ent = AD_stack_top(ds);
- struct dev_ino leaf_dev_ino = leaf_dir_ent->dev_ino;
- enum RM_status old_status = leaf_dir_ent->status;
- struct AD_ent *top;
-
- /* Get the name of the current (but soon to be `previous') directory
- from the top of the stack. */
- char *prev_dir = top_dir (ds);
-
- AD_stack_pop (ds);
- pop_dir (ds);
- top = AD_stack_top (ds);
-
- /* If the directory we're about to leave (and try to rmdir)
- is the one whose dev_ino is being used to detect a cycle,
- reset cycle_check_state.dev_ino to that of the parent.
- Otherwise, once that directory is removed, its dev_ino
- could be reused in the creation (by some other process)
- of a directory that this rm process would encounter,
- which would result in a false-positive cycle indication. */
- CYCLE_CHECK_REFLECT_CHDIR_UP (&ds->cycle_check_state,
- top->dev_ino, leaf_dev_ino);
-
- /* Propagate any failure to parent. */
- UPDATE_STATUS (top->status, old_status);
-
- assert (AD_stack_height (ds));
-
- if (1 < AD_stack_height (ds))
- {
- struct stat sb;
- int fd = openat (dirfd (dirp), "..", O_RDONLY);
- if (closedir (dirp) != 0)
- {
- error (0, errno, _("FATAL: failed to close directory %s"),
- quote (full_filename (prev_dir)));
- goto next_cmdline_arg;
- }
-
- /* The above fails with EACCES when DIRP is readable but not
- searchable, when using Solaris' openat. Without this openat
- call, tests/rm2 would fail to remove directories a/2 and a/3. */
- if (fd < 0)
- fd = openat (AT_FDCWD, full_filename ("."), O_RDONLY);
-
- if (fd < 0)
- {
- error (0, errno, _("FATAL: cannot open .. from %s"),
- quote (full_filename (prev_dir)));
- goto next_cmdline_arg;
- }
-
- if (fstat (fd, &sb))
- {
- error (0, errno,
- _("FATAL: cannot ensure %s (returned to via ..) is safe"),
- quote (full_filename (".")));
- goto close_and_next;
- }
-
- /* Ensure that post-chdir dev/ino match the stored ones. */
- if ( ! SAME_INODE (sb, top->dev_ino))
- {
- error (0, 0, _("FATAL: directory %s changed dev/ino"),
- quote (full_filename (".")));
- close_and_next:;
- close (fd);
-
- next_cmdline_arg:;
- free (prev_dir);
- longjmp (ds->current_arg_jumpbuf, 1);
- }
- *fdp = fd;
- }
- else
- {
- if (closedir (dirp) != 0)
- {
- error (0, errno, _("FATAL: failed to close directory %s"),
- quote (full_filename (prev_dir)));
- goto next_cmdline_arg;
- }
- *fdp = AT_FDCWD;
- }
-
- return prev_dir;
-}
-
-/* Initialize *HT if it is NULL. Return *HT. */
-static Hash_table *
-AD_ensure_initialized (Hash_table **ht)
-{
- if (*ht == NULL)
- {
- *ht = hash_initialize (HT_UNREMOVABLE_INITIAL_CAPACITY, NULL, hash_pjw,
- hash_compare_strings, hash_freer);
- if (*ht == NULL)
- xalloc_die ();
- }
-
- return *ht;
-}
-
-/* Initialize *HT if it is NULL.
- Insert FILENAME into HT. */
-static void
-AD_mark_helper (Hash_table **ht, char *filename)
-{
- void *ent = hash_insert (AD_ensure_initialized (ht), filename);
- if (ent == NULL)
- xalloc_die ();
- else
- {
- if (ent != filename)
- free (filename);
- }
-}
-
-/* Mark FILENAME (in current directory) as unremovable. */
-static void
-AD_mark_as_unremovable (Dirstack_state *ds, char const *filename)
-{
- AD_mark_helper (&AD_stack_top(ds)->unremovable, xstrdup (filename));
-}
-
-/* Mark the current directory as unremovable. I.e., mark the entry
- in the parent directory corresponding to `.'.
- This happens e.g., when an opendir fails and the only name
- the caller has conveniently at hand is `.'. */
-static void
-AD_mark_current_as_unremovable (Dirstack_state *ds)
-{
- struct AD_ent *top = AD_stack_top (ds);
- char *curr = top_dir (ds);
-
- assert (1 < AD_stack_height (ds));
-
- --top;
- AD_mark_helper (&top->unremovable, curr);
-}
-
-/* Push an initial dummy entry onto the stack.
- This will always be the bottommost entry on the stack. */
-static void
-AD_push_initial (Dirstack_state *ds)
-{
- struct AD_ent *top;
-
- /* Extend the stack. */
- obstack_blank (&ds->Active_dir, sizeof (struct AD_ent));
-
- /* Fill in the new values. */
- top = AD_stack_top (ds);
- top->unremovable = NULL;
-
- /* These should never be used.
- Give them values that might look suspicious
- in a debugger or in a diagnostic. */
- top->dev_ino.st_dev = TYPE_MAXIMUM (dev_t);
- top->dev_ino.st_ino = TYPE_MAXIMUM (ino_t);
-}
-
-/* Push info about the current working directory (".") onto the
- active directory stack. DIR is the ./-relative name through
- which we've just `chdir'd to this directory. DIR_SB_FROM_PARENT
- is the result of calling lstat on DIR from the parent of DIR.
- Longjump out (skipping the entire command line argument we're
- dealing with) if `fstat (FD_CWD, ...' fails or if someone has
- replaced DIR with e.g., a symlink to some other directory. */
-static void
-AD_push (int fd_cwd, Dirstack_state *ds, char const *dir,
- struct stat const *dir_sb_from_parent)
-{
- struct AD_ent *top;
-
- push_dir (ds, dir);
-
- /* If our uses of openat are guaranteed not to
- follow a symlink, then we can skip this check. */
- if (! HAVE_WORKING_O_NOFOLLOW)
- {
- struct stat sb;
- if (fstat (fd_cwd, &sb) != 0)
- {
- error (0, errno, _("FATAL: cannot enter directory %s"),
- quote (full_filename (".")));
- longjmp (ds->current_arg_jumpbuf, 1);
- }
-
- if ( ! SAME_INODE (sb, *dir_sb_from_parent))
- {
- error (0, 0,
- _("FATAL: just-changed-to directory %s changed dev/ino"),
- quote (full_filename (".")));
- longjmp (ds->current_arg_jumpbuf, 1);
- }
- }
-
- if (cycle_check (&ds->cycle_check_state, dir_sb_from_parent))
- {
- error (0, 0, _("\
-WARNING: Circular directory structure.\n\
-This almost certainly means that you have a corrupted file system.\n\
-NOTIFY YOUR SYSTEM MANAGER.\n\
-The following directory is part of the cycle:\n %s\n"),
- quote (full_filename (".")));
- longjmp (ds->current_arg_jumpbuf, 1);
- }
-
- /* Extend the stack. */
- obstack_blank (&ds->Active_dir, sizeof (struct AD_ent));
-
- /* The active directory stack must be one larger than the length stack. */
- assert (AD_stack_height (ds) ==
- 1 + obstack_object_size (&ds->len_stack) / sizeof (size_t));
-
- /* Fill in the new values. */
- top = AD_stack_top (ds);
- top->dev_ino.st_dev = dir_sb_from_parent->st_dev;
- top->dev_ino.st_ino = dir_sb_from_parent->st_ino;
- top->unremovable = NULL;
-}
-
-static inline bool
-AD_is_removable (Dirstack_state const *ds, char const *file)
-{
- struct AD_ent *top = AD_stack_top (ds);
- return ! (top->unremovable && hash_lookup (top->unremovable, file));
-}
-
-/* Return true if DIR is determined to be an empty directory. */
-static bool
-is_empty_dir (int fd_cwd, char const *dir)
-{
- DIR *dirp;
- struct dirent const *dp;
- int saved_errno;
- int fd = openat (fd_cwd, dir,
- (O_RDONLY | O_DIRECTORY
- | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK));
-
- if (fd < 0)
- return false;
-
- dirp = fdopendir (fd);
- if (dirp == NULL)
- {
- close (fd);
- return false;
- }
-
- errno = 0;
- dp = readdir_ignoring_dot_and_dotdot (dirp);
- saved_errno = errno;
- closedir (dirp);
- if (dp != NULL)
- return false;
- return saved_errno == 0 ? true : false;
-}
-
-/* Return -1 if FILE is an unwritable non-symlink,
+/* Return 1 if FILE is an unwritable non-symlink,
0 if it is writable or some other type of file,
- a positive error number if there is some problem in determining the answer.
- Set *BUF to the file status.
- This is to avoid calling euidaccess when FILE is a symlink. */
+ -1 and set errno if there is some problem in determining the answer.
+ Set *BUF to the file status. */
static int
write_protected_non_symlink (int fd_cwd,
- char const *file,
- Dirstack_state const *ds,
- struct stat *buf)
+ char const *file,
+ struct stat *buf)
{
+ if (can_write_any_file ())
+ return 0;
if (cache_fstatat (fd_cwd, file, buf, AT_SYMLINK_NOFOLLOW) != 0)
- return errno;
+ return -1;
if (S_ISLNK (buf->st_mode))
return 0;
/* Here, we know FILE is not a symbolic link. */
@@ -730,211 +113,191 @@ write_protected_non_symlink (int fd_cwd,
/* In the absence of a native eaccessat function, here are some of
the implementation choices [#4 and #5 were suggested by Paul Eggert]:
1) call openat with O_WRONLY|O_NOCTTY
- Disadvantage: may create the file and doesn't work for directory,
- may mistakenly report `unwritable' for EROFS or ACLs even though
- perm bits say the file is writable.
+ Disadvantage: may create the file and doesn't work for directory,
+ may mistakenly report 'unwritable' for EROFS or ACLs even though
+ perm bits say the file is writable.
2) fake eaccessat (save_cwd, fchdir, call euidaccess, restore_cwd)
- Disadvantage: changes working directory (not reentrant) and can't
- work if save_cwd fails.
+ Disadvantage: changes working directory (not reentrant) and can't
+ work if save_cwd fails.
- 3) if (euidaccess (full_filename (file), W_OK) == 0)
- Disadvantage: doesn't work if full_filename is too long.
- Inefficient for very deep trees (O(Depth^2)).
+ 3) if (euidaccess (full_name, W_OK) == 0)
+ Disadvantage: doesn't work if full_name is too long.
+ Inefficient for very deep trees (O(Depth^2)).
4) If the full pathname is sufficiently short (say, less than
- PATH_MAX or 8192 bytes, whichever is shorter):
- use method (3) (i.e., euidaccess (full_filename (file), W_OK));
- Otherwise: vfork, fchdir in the child, run euidaccess in the
- child, then the child exits with a status that tells the parent
- whether euidaccess succeeded.
-
- This avoids the O(N**2) algorithm of method (3), and it also avoids
- the failure-due-to-too-long-file-names of method (3), but it's fast
- in the normal shallow case. It also avoids the lack-of-reentrancy
- and the save_cwd problems.
- Disadvantage; it uses a process slot for very-long file names,
- and would be very slow for hierarchies with many such files.
+ PATH_MAX or 8192 bytes, whichever is shorter):
+ use method (3) (i.e., euidaccess (full_name, W_OK));
+ Otherwise: vfork, fchdir in the child, run euidaccess in the
+ child, then the child exits with a status that tells the parent
+ whether euidaccess succeeded.
+
+ This avoids the O(N**2) algorithm of method (3), and it also avoids
+ the failure-due-to-too-long-file-names of method (3), but it's fast
+ in the normal shallow case. It also avoids the lack-of-reentrancy
+ and the save_cwd problems.
+ Disadvantage; it uses a process slot for very-long file names,
+ and would be very slow for hierarchies with many such files.
5) If the full file name is sufficiently short (say, less than
- PATH_MAX or 8192 bytes, whichever is shorter):
- use method (3) (i.e., euidaccess (full_filename (file), W_OK));
- Otherwise: look just at the file bits. Perhaps issue a warning
- the first time this occurs.
+ PATH_MAX or 8192 bytes, whichever is shorter):
+ use method (3) (i.e., euidaccess (full_name, W_OK));
+ Otherwise: look just at the file bits. Perhaps issue a warning
+ the first time this occurs.
- This is like (4), except for the "Otherwise" case where it isn't as
- "perfect" as (4) but is considerably faster. It conforms to current
- POSIX, and is uniformly better than what Solaris and FreeBSD do (they
- mess up with long file names). */
+ This is like (4), except for the "Otherwise" case where it isn't as
+ "perfect" as (4) but is considerably faster. It conforms to current
+ POSIX, and is uniformly better than what Solaris and FreeBSD do (they
+ mess up with long file names). */
{
- /* This implements #5: */
- size_t file_name_len
- = obstack_object_size (&ds->dir_stack) + strlen (file);
-
- if (MIN (PATH_MAX, 8192) <= file_name_len)
- return - euidaccess_stat (buf, W_OK);
- if (euidaccess (full_filename (file), W_OK) == 0)
+ if (faccessat (fd_cwd, file, W_OK, AT_EACCESS) == 0)
return 0;
- if (errno == EACCES)
- return -1;
- /* Perhaps some other process has removed the file, or perhaps this
- is a buggy NFS client. */
- return errno;
+ return errno == EACCES ? 1 : -1;
}
}
-/* Prompt whether to remove FILENAME, if required via a combination of
+/* Prompt whether to remove FILENAME (ent->, if required via a combination of
the options specified by X and/or file attributes. If the file may
be removed, return RM_OK. If the user declines to remove the file,
return RM_USER_DECLINED. If not ignoring missing files and we
cannot lstat FILENAME, then return RM_ERROR.
- Depending on MODE, ask whether to `descend into' or to `remove' the
+ IS_DIR is true if ENT designates a directory, false otherwise.
+
+ Depending on MODE, ask whether to 'descend into' or to 'remove' the
directory FILENAME. MODE is ignored when FILENAME is not a directory.
- Set *IS_EMPTY to T_YES if FILENAME is an empty directory, and it is
+ Set *IS_EMPTY_P to T_YES if FILENAME is an empty directory, and it is
appropriate to try to remove it with rmdir (e.g. recursive mode).
- Don't even try to set *IS_EMPTY when MODE == PA_REMOVE_DIR. */
+ Don't even try to set *IS_EMPTY_P when MODE == PA_REMOVE_DIR. */
static enum RM_status
-prompt (int fd_cwd, Dirstack_state const *ds, char const *filename,
- struct stat *sbuf,
- struct rm_options const *x, enum Prompt_action mode,
- Ternary *is_empty)
+prompt (FTS const *fts, FTSENT const *ent, bool is_dir,
+ struct rm_options const *x, enum Prompt_action mode,
+ Ternary *is_empty_p)
{
+ int fd_cwd = fts->fts_cwd_fd;
+ char const *full_name = ent->fts_path;
+ char const *filename = ent->fts_accpath;
+ if (is_empty_p)
+ *is_empty_p = T_UNKNOWN;
+
+ struct stat st;
+ struct stat *sbuf = &st;
+ cache_stat_init (sbuf);
+
+ int dirent_type = is_dir ? DT_DIR : DT_UNKNOWN;
int write_protected = 0;
- *is_empty = T_UNKNOWN;
+ bool is_empty = false;
+ if (is_empty_p)
+ {
+ is_empty = is_empty_dir (fd_cwd, filename);
+ *is_empty_p = is_empty ? T_YES : T_NO;
+ }
+
+ /* When nonzero, this indicates that we failed to remove a child entry,
+ either because the user declined an interactive prompt, or due to
+ some other failure, like permissions. */
+ if (ent->fts_number)
+ return RM_USER_DECLINED;
if (x->interactive == RMI_NEVER)
return RM_OK;
+ int wp_errno = 0;
if (!x->ignore_missing_files
- & ((x->interactive == RMI_ALWAYS) | x->stdin_tty))
- write_protected = write_protected_non_symlink (fd_cwd, filename, ds, sbuf);
+ && ((x->interactive == RMI_ALWAYS) || x->stdin_tty)
+ && dirent_type != DT_LNK)
+ {
+ write_protected = write_protected_non_symlink (fd_cwd, filename, sbuf);
+ wp_errno = errno;
+ }
if (write_protected || x->interactive == RMI_ALWAYS)
{
- if (write_protected <= 0
- && cache_fstatat (fd_cwd, filename, sbuf, AT_SYMLINK_NOFOLLOW) != 0)
- {
- /* This happens, e.g., with `rm '''. */
- write_protected = errno;
- }
-
- if (write_protected <= 0)
- {
- /* Using permissions doesn't make sense for symlinks. */
- if (S_ISLNK (sbuf->st_mode) && x->interactive != RMI_ALWAYS)
- return RM_OK;
-
- if (S_ISDIR (sbuf->st_mode) && !x->recursive)
- write_protected = EISDIR;
- }
-
- char const *quoted_name = quote (full_filename (filename));
-
- if (0 < write_protected)
- {
- error (0, write_protected, _("cannot remove %s"), quoted_name);
- return RM_ERROR;
- }
+ if (0 <= write_protected && dirent_type == DT_UNKNOWN)
+ {
+ if (cache_fstatat (fd_cwd, filename, sbuf, AT_SYMLINK_NOFOLLOW) == 0)
+ {
+ if (S_ISLNK (sbuf->st_mode))
+ dirent_type = DT_LNK;
+ else if (S_ISDIR (sbuf->st_mode))
+ dirent_type = DT_DIR;
+ /* Otherwise it doesn't matter, so leave it DT_UNKNOWN. */
+ }
+ else
+ {
+ /* This happens, e.g., with 'rm '''. */
+ write_protected = -1;
+ wp_errno = errno;
+ }
+ }
+
+ if (0 <= write_protected)
+ switch (dirent_type)
+ {
+ case DT_LNK:
+ /* Using permissions doesn't make sense for symlinks. */
+ if (x->interactive != RMI_ALWAYS)
+ return RM_OK;
+ break;
+
+ case DT_DIR:
+ /* Unless we're either deleting directories or deleting
+ recursively, we want to raise an EISDIR error rather than
+ prompting the user */
+ if ( ! (x->recursive || (x->remove_empty_directories && is_empty)))
+ {
+ write_protected = -1;
+ wp_errno = EISDIR;
+ }
+ break;
+ }
+
+ char const *quoted_name = quoteaf (full_name);
+
+ if (write_protected < 0)
+ {
+ error (0, wp_errno, _("cannot remove %s"), quoted_name);
+ return RM_ERROR;
+ }
/* Issue the prompt. */
- /* FIXME: use a variant of error (instead of fprintf) that doesn't
- append a newline. Then we won't have to declare program_name in
- this file. */
- if (S_ISDIR (sbuf->st_mode)
- && x->recursive
- && mode == PA_DESCEND_INTO_DIR
- && ((*is_empty = (is_empty_dir (fd_cwd, filename) ? T_YES : T_NO))
- == T_NO))
- fprintf (stderr,
- (write_protected
- ? _("%s: descend into write-protected directory %s? ")
- : _("%s: descend into directory %s? ")),
- program_name, quoted_name);
+ if (dirent_type == DT_DIR
+ && mode == PA_DESCEND_INTO_DIR
+ && !is_empty)
+ fprintf (stderr,
+ (write_protected
+ ? _("%s: descend into write-protected directory %s? ")
+ : _("%s: descend into directory %s? ")),
+ program_name, quoted_name);
else
- {
- /* TRANSLATORS: You may find it more convenient to translate
- the equivalent of _("%s: remove %s (write-protected) %s? ").
- It should avoid grammatical problems with the output
- of file_type. */
- fprintf (stderr,
- (write_protected
- ? _("%s: remove write-protected %s %s? ")
- : _("%s: remove %s %s? ")),
- program_name, file_type (sbuf), quoted_name);
- }
+ {
+ if (cache_fstatat (fd_cwd, filename, sbuf, AT_SYMLINK_NOFOLLOW) != 0)
+ {
+ error (0, errno, _("cannot remove %s"), quoted_name);
+ return RM_ERROR;
+ }
+
+ fprintf (stderr,
+ (write_protected
+ /* TRANSLATORS: In the next two strings the second %s is
+ replaced by the type of the file. To avoid grammatical
+ problems, it may be more convenient to translate these
+ strings instead as: "%1$s: %3$s is write-protected and
+ is of type '%2$s' -- remove it? ". */
+ ? _("%s: remove write-protected %s %s? ")
+ : _("%s: remove %s %s? ")),
+ program_name, file_type (sbuf), quoted_name);
+ }
if (!yesno ())
- return RM_USER_DECLINED;
+ return RM_USER_DECLINED;
}
return RM_OK;
}
-/* Return true if FILENAME is a directory (and not a symlink to a directory).
- Otherwise, including the case in which lstat fails, return false.
- *ST is FILENAME's tstatus.
- Do not modify errno. */
-static inline bool
-is_dir_lstat (char const *filename, struct stat *st)
-{
- int saved_errno = errno;
- bool is_dir =
- (cache_fstatat (AT_FDCWD, filename, st, AT_SYMLINK_NOFOLLOW) == 0
- && S_ISDIR (st->st_mode));
- errno = saved_errno;
- return is_dir;
-}
-
-#if HAVE_STRUCT_DIRENT_D_TYPE
-
-/* True if the type of the directory entry D is known. */
-# define DT_IS_KNOWN(d) ((d)->d_type != DT_UNKNOWN)
-
-/* True if the type of the directory entry D must be T. */
-# define DT_MUST_BE(d, t) ((d)->d_type == (t))
-
-#else
-# define DT_IS_KNOWN(d) false
-# define DT_MUST_BE(d, t) false
-#endif
-
-#define DO_UNLINK(Fd_cwd, Filename, X) \
- do \
- { \
- if (unlinkat (Fd_cwd, Filename, 0) == 0) \
- { \
- if ((X)->verbose) \
- printf (_("removed %s\n"), quote (full_filename (Filename))); \
- return RM_OK; \
- } \
- \
- if (ignorable_missing (X, errno)) \
- return RM_OK; \
- } \
- while (0)
-
-#define DO_RMDIR(Fd_cwd, Filename, X) \
- do \
- { \
- if (unlinkat (Fd_cwd, Filename, AT_REMOVEDIR) == 0) /* rmdir */ \
- { \
- if ((X)->verbose) \
- printf (_("removed directory: %s\n"), \
- quote (full_filename (Filename))); \
- return RM_OK; \
- } \
- \
- if (ignorable_missing (X, errno)) \
- return RM_OK; \
- \
- if (errno == ENOTEMPTY || errno == EEXIST) \
- return RM_NONEMPTY_DIR; \
- } \
- while (0)
-
/* When a function like unlink, rmdir, or fstatat fails with an errno
value of ERRNUM, return true if the specified file system object
is guaranteed not to exist; otherwise, return false. */
@@ -945,10 +308,18 @@ nonexistent_file_errno (int errnum)
exist, but be (in)accessible only via too long a symlink chain.
Likewise for ENAMETOOLONG, since rm -f ./././.../foo may fail
if the "..." part expands to a long enough sequence of "./"s,
- even though ./foo does indeed exist. */
+ even though ./foo does indeed exist.
+
+ Another case to consider is when a particular name is invalid for
+ a given file system. In 2011, smbfs returns EINVAL, but the next
+ revision of POSIX will require EILSEQ for that situation:
+ http://austingroupbugs.net/view.php?id=293
+ */
switch (errnum)
{
+ case EILSEQ:
+ case EINVAL:
case ENOENT:
case ENOTDIR:
return true;
@@ -964,602 +335,252 @@ ignorable_missing (struct rm_options const *x, int errnum)
return x->ignore_missing_files && nonexistent_file_errno (errnum);
}
-/* Remove the file or directory specified by FILENAME.
- Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED if not.
- But if FILENAME specifies a non-empty directory, return RM_NONEMPTY_DIR. */
-
-static enum RM_status
-remove_entry (int fd_cwd, Dirstack_state const *ds, char const *filename,
- struct stat *st,
- struct rm_options const *x, struct dirent const *dp)
+/* Tell fts not to traverse into the hierarchy at ENT. */
+static void
+fts_skip_tree (FTS *fts, FTSENT *ent)
{
- Ternary is_empty_directory;
- enum RM_status s = prompt (fd_cwd, ds, filename, st, x, PA_DESCEND_INTO_DIR,
- &is_empty_directory);
- bool known_to_be_dir = (cache_stat_ok (st) && S_ISDIR (st->st_mode));
-
- if (s != RM_OK)
- return s;
-
- /* Why bother with the following if/else block? Because on systems with
- an unlink function that *can* unlink directories, we must determine the
- type of each entry before removing it. Otherwise, we'd risk unlinking
- an entire directory tree simply by unlinking a single directory; then
- all the storage associated with that hierarchy would not be freed until
- the next fsck. Not nice. To avoid that, on such slightly losing
- systems, we need to call lstat to determine the type of each entry,
- and that represents extra overhead that -- it turns out -- we can
- avoid on non-losing systems, since there, unlink will never remove
- a directory. Also, on systems where unlink may unlink directories,
- we're forced to allow a race condition: we lstat a non-directory, then
- go to unlink it, but in the mean time, a malicious someone could have
- replaced it with a directory. */
-
- if (cannot_unlink_dir ())
- {
- if (known_to_be_dir && ! x->recursive)
- {
- error (0, EISDIR, _("cannot remove %s"),
- quote (full_filename (filename)));
- return RM_ERROR;
- }
-
- /* is_empty_directory is set iff it's ok to use rmdir.
- Note that it's set only in interactive mode -- in which case it's
- an optimization that arranges so that the user is asked just
- once whether to remove the directory. */
- if (is_empty_directory == T_YES)
- DO_RMDIR (fd_cwd, filename, x);
-
- /* If we happen to know that FILENAME is a directory, return now
- and let the caller remove it -- this saves the overhead of a failed
- unlink call. If FILENAME is a command-line argument, then dp is NULL,
- so we'll first try to unlink it. Using unlink here is ok, because it
- cannot remove a directory. */
- if ((dp && DT_MUST_BE (dp, DT_DIR)) || known_to_be_dir)
- return RM_NONEMPTY_DIR;
-
- DO_UNLINK (fd_cwd, filename, x);
-
- /* Upon a failed attempt to unlink a directory, most non-Linux systems
- set errno to the POSIX-required value EPERM. In that case, change
- errno to EISDIR so that we emit a better diagnostic. */
- if (! x->recursive && errno == EPERM && is_dir_lstat (filename, st))
- errno = EISDIR;
-
- if (! x->recursive
- || (cache_stat_ok (st) && !S_ISDIR (st->st_mode)))
- {
- if (ignorable_missing (x, errno))
- return RM_OK;
-
- /* Either --recursive is not in effect, or the file cannot be a
- directory. Report the unlink problem and fail. */
- error (0, errno, _("cannot remove %s"),
- quote (full_filename (filename)));
- return RM_ERROR;
- }
- assert (!cache_stat_ok (st) || S_ISDIR (st->st_mode));
- }
- else
- {
- /* If we don't already know whether FILENAME is a directory,
- find out now. Then, if it's a non-directory, we can use
- unlink on it. */
- bool is_dir;
-
- if (cache_statted (st))
- is_dir = known_to_be_dir;
- else
- {
- if (dp && DT_IS_KNOWN (dp))
- is_dir = DT_MUST_BE (dp, DT_DIR);
- else
- {
- if (fstatat (fd_cwd, filename, st, AT_SYMLINK_NOFOLLOW))
- {
- if (ignorable_missing (x, errno))
- return RM_OK;
-
- error (0, errno, _("cannot remove %s"),
- quote (full_filename (filename)));
- return RM_ERROR;
- }
-
- is_dir = !! S_ISDIR (st->st_mode);
- }
- }
-
- if (! is_dir)
- {
- /* At this point, barring race conditions, FILENAME is known
- to be a non-directory, so it's ok to try to unlink it. */
- DO_UNLINK (fd_cwd, filename, x);
-
- /* unlink failed with some other error code. report it. */
- error (0, errno, _("cannot remove %s"),
- quote (full_filename (filename)));
- return RM_ERROR;
- }
-
- if (! x->recursive)
- {
- error (0, EISDIR, _("cannot remove %s"),
- quote (full_filename (filename)));
- return RM_ERROR;
- }
-
- if (is_empty_directory == T_YES)
- {
- DO_RMDIR (fd_cwd, filename, x);
- /* Don't diagnose any failure here.
- It'll be detected when the caller tries another way. */
- }
- }
-
- return RM_NONEMPTY_DIR;
+ fts_set (fts, ent, FTS_SKIP);
+ /* Ensure that we do not process ENT a second time. */
+ ignore_value (fts_read (fts));
}
-/* Given FD_CWD, the file descriptor for an open directory,
- open its subdirectory F (F is already `known' to be a directory,
- so if it is no longer one, someone is playing games), return a DIR*
- pointer for F, and put F's `stat' data in *SUBDIR_SB.
- Upon failure give a diagnostic and return NULL.
- If PREV_ERRNO is nonzero, it is the errno value from a preceding failed
- unlink- or rmdir-like system call -- use that value instead of ENOTDIR
- if an opened file turns out not to be a directory. This is important
- when the preceding non-dir-unlink failed due to e.g., EPERM or EACCES.
- The caller must use a nonnnull CWD_ERRNO the first
- time this function is called for each command-line-specified directory.
- If CWD_ERRNO is not null, set *CWD_ERRNO to the appropriate error number
- if this function fails to restore the initial working directory.
- If it is null, report an error and exit if the working directory
- isn't restored. */
-static DIR *
-fd_to_subdirp (int fd_cwd, char const *f,
- struct rm_options const *x, int prev_errno,
- struct stat *subdir_sb,
- int *cwd_errno ATTRIBUTE_UNUSED)
+/* Upon unlink failure, or when the user declines to remove ENT, mark
+ each of its ancestor directories, so that we know not to prompt for
+ its removal. */
+static void
+mark_ancestor_dirs (FTSENT *ent)
{
- int open_flags = O_RDONLY | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK;
- int fd_sub = openat_permissive (fd_cwd, f, open_flags, 0, cwd_errno);
- int saved_errno;
-
- /* Record dev/ino of F. We may compare them against saved values
- to thwart any attempt to subvert the traversal. They are also used
- to detect directory cycles. */
- if (fd_sub < 0)
- return NULL;
- else if (fstat (fd_sub, subdir_sb) != 0)
- saved_errno = errno;
- else if (S_ISDIR (subdir_sb->st_mode))
+ FTSENT *p;
+ for (p = ent->fts_parent; FTS_ROOTLEVEL <= p->fts_level; p = p->fts_parent)
{
- DIR *subdir_dirp = fdopendir (fd_sub);
- if (subdir_dirp)
- return subdir_dirp;
- saved_errno = errno;
+ if (p->fts_number)
+ break;
+ p->fts_number = 1;
}
- else
- saved_errno = (prev_errno ? prev_errno : ENOTDIR);
-
- close (fd_sub);
- errno = saved_errno;
- return NULL;
}
-/* Remove entries in the directory open on DIRP
- Upon finding a directory that is both non-empty and that can be chdir'd
- into, return RM_OK and set *SUBDIR and fill in SUBDIR_SB, where
- SUBDIR is the malloc'd name of the subdirectory if the chdir succeeded,
- NULL otherwise (e.g., if opendir failed or if there was no subdirectory).
- Likewise, SUBDIR_SB is the result of calling lstat on SUBDIR.
- Return RM_OK if all entries are removed. Return RM_ERROR if any
- entry cannot be removed. Otherwise, return RM_USER_DECLINED if
- the user declines to remove at least one entry. Remove as much as
- possible, continuing even if we fail to remove some entries. */
+/* Remove the file system object specified by ENT. IS_DIR specifies
+ whether it is expected to be a directory or non-directory.
+ Return RM_OK upon success, else RM_ERROR. */
static enum RM_status
-remove_cwd_entries (DIR **dirp,
- Dirstack_state *ds, char **subdir, struct stat *subdir_sb,
- struct rm_options const *x)
+excise (FTS *fts, FTSENT *ent, struct rm_options const *x, bool is_dir)
{
- struct AD_ent *top = AD_stack_top (ds);
- enum RM_status status = top->status;
- size_t n_unlinked_since_opendir_or_last_rewind = 0;
-
- assert (VALID_STATUS (status));
- *subdir = NULL;
-
- while (1)
+ int flag = is_dir ? AT_REMOVEDIR : 0;
+ if (unlinkat (fts->fts_cwd_fd, ent->fts_accpath, flag) == 0)
{
- struct dirent const *dp;
- enum RM_status tmp_status;
- const char *f;
-
- /* Set errno to zero so we can distinguish between a readdir failure
- and when readdir simply finds that there are no more entries. */
- errno = 0;
- dp = readdir_ignoring_dot_and_dotdot (*dirp);
- if (dp == NULL)
- {
- if (errno)
- {
- /* fall through */
- }
- else if (NEED_REWIND (n_unlinked_since_opendir_or_last_rewind))
- {
- /* Call rewinddir if we've called unlink or rmdir so many times
- (since the opendir or the previous rewinddir) that this
- NULL-return may be the symptom of a buggy readdir. */
- rewinddir (*dirp);
- n_unlinked_since_opendir_or_last_rewind = 0;
- continue;
- }
- break;
- }
-
- f = dp->d_name;
-
- /* Skip files we've already tried/failed to remove. */
- if ( ! AD_is_removable (ds, f))
- continue;
-
- /* Pass dp->d_type info to remove_entry so the non-glibc
- case can decide whether to use unlink or chdir.
- Systems without the d_type member will have to endure
- the performance hit of first calling lstat F. */
- cache_stat_init (subdir_sb);
- tmp_status = remove_entry (dirfd (*dirp), ds, f, subdir_sb, x, dp);
- switch (tmp_status)
- {
- case RM_OK:
- /* Count how many files we've unlinked since the initial
- opendir or the last rewinddir. On buggy systems, if you
- remove too many, readdir returns NULL even though there
- remain unprocessed directory entries. */
- ++n_unlinked_since_opendir_or_last_rewind;
- break;
-
- case RM_ERROR:
- case RM_USER_DECLINED:
- AD_mark_as_unremovable (ds, f);
- UPDATE_STATUS (status, tmp_status);
- break;
-
- case RM_NONEMPTY_DIR:
- {
- DIR *subdir_dirp = fd_to_subdirp (dirfd (*dirp), f,
- x, errno, subdir_sb, NULL);
- if (subdir_dirp == NULL)
- {
- status = RM_ERROR;
-
- /* CAUTION: this test and diagnostic are identical to
- those following the other use of fd_to_subdirp. */
- if (ignorable_missing (x, errno))
- {
- /* With -f, don't report "file not found". */
- }
- else
- {
- /* Upon fd_to_subdirp failure, try to remove F directly,
- in case it's just an empty directory. */
- int saved_errno = errno;
- if (unlinkat (dirfd (*dirp), f, AT_REMOVEDIR) == 0)
- status = RM_OK;
- else
- error (0, saved_errno,
- _("cannot remove %s"), quote (full_filename (f)));
- }
-
- if (status == RM_ERROR)
- AD_mark_as_unremovable (ds, f);
- break;
- }
-
- *subdir = xstrdup (f);
- if (closedir (*dirp) != 0)
- {
- error (0, 0, _("failed to close directory %s"),
- quote (full_filename (".")));
- status = RM_ERROR;
- }
- *dirp = subdir_dirp;
-
- break;
- }
- }
-
- /* Record status for this directory. */
- UPDATE_STATUS (top->status, status);
-
- if (*subdir)
- break;
+ if (x->verbose)
+ {
+ printf ((is_dir
+ ? _("removed directory %s\n")
+ : _("removed %s\n")), quoteaf (ent->fts_path));
+ }
+ return RM_OK;
}
- /* Ensure that *dirp is not NULL and that its file descriptor is valid. */
- assert (*dirp != NULL);
- assert (0 <= fcntl (dirfd (*dirp), F_GETFD));
-
- return status;
-}
+ /* The unlinkat from kernels like linux-2.6.32 reports EROFS even for
+ nonexistent files. When the file is indeed missing, map that to ENOENT,
+ so that rm -f ignores it, as required. Even without -f, this is useful
+ because it makes rm print the more precise diagnostic. */
+ if (errno == EROFS)
+ {
+ struct stat st;
+ if ( ! (lstatat (fts->fts_cwd_fd, ent->fts_accpath, &st)
+ && errno == ENOENT))
+ errno = EROFS;
+ }
-/* Do this after each call to AD_push or AD_push_initial.
- Because the status = RM_OK bit is too remove-specific to
- go into the general-purpose AD_* package. */
-#define AD_INIT_OTHER_MEMBERS() \
- do \
- { \
- AD_stack_top(ds)->status = RM_OK; \
- } \
- while (0)
-
-/* Remove the hierarchy rooted at DIR.
- Do that by changing into DIR, then removing its contents, then
- returning to the original working directory and removing DIR itself.
- Don't use recursion. Be careful when using chdir ".." that we
- return to the same directory from which we came, if necessary.
- Return an RM_status value to indicate success or failure. */
+ if (ignorable_missing (x, errno))
+ return RM_OK;
+ /* When failing to rmdir an unreadable directory, we see errno values
+ like EISDIR or ENOTDIR (or, on Solaris 10, EEXIST), but they would be
+ meaningless in a diagnostic. When that happens and the errno value
+ from the failed open is EPERM or EACCES, use the earlier, more
+ descriptive errno value. */
+ if (ent->fts_info == FTS_DNR
+ && (errno == ENOTEMPTY || errno == EISDIR || errno == ENOTDIR
+ || errno == EEXIST)
+ && (ent->fts_errno == EPERM || ent->fts_errno == EACCES))
+ errno = ent->fts_errno;
+ error (0, errno, _("cannot remove %s"), quoteaf (ent->fts_path));
+ mark_ancestor_dirs (ent);
+ return RM_ERROR;
+}
+
+/* This function is called once for every file system object that fts
+ encounters. fts performs a depth-first traversal.
+ A directory is usually processed twice, first with fts_info == FTS_D,
+ and later, after all of its entries have been processed, with FTS_DP.
+ Return RM_ERROR upon error, RM_USER_DECLINED for a negative response
+ to an interactive prompt, and otherwise, RM_OK. */
static enum RM_status
-remove_dir (int fd_cwd, Dirstack_state *ds, char const *dir,
- struct stat *dir_st,
- struct rm_options const *x, int *cwd_errno)
+rm_fts (FTS *fts, FTSENT *ent, struct rm_options const *x)
{
- enum RM_status status;
- dev_t current_dev = dir_st->st_dev;
-
- /* There is a race condition in that an attacker could replace the nonempty
- directory, DIR, with a symlink between the preceding call to rmdir
- (unlinkat, in our caller) and fd_to_subdirp's openat call. But on most
- systems, even those without openat, this isn't a problem, since we ensure
- that opening a symlink will fail, when that is possible. Otherwise,
- fd_to_subdirp's fstat, along with the `fstat' and the dev/ino
- comparison in AD_push ensure that we detect it and fail. */
-
- DIR *dirp = fd_to_subdirp (fd_cwd, dir, x, 0, dir_st, cwd_errno);
-
- if (dirp == NULL)
+ switch (ent->fts_info)
{
- /* CAUTION: this test and diagnostic are identical to
- those following the other use of fd_to_subdirp. */
- if (ignorable_missing (x, errno))
- {
- /* With -f, don't report "file not found". */
- }
- else
- {
- /* Upon fd_to_subdirp failure, try to remove DIR directly,
- in case it's just an empty directory. */
- int saved_errno = errno;
- if (unlinkat (fd_cwd, dir, AT_REMOVEDIR) == 0)
- return RM_OK;
-
- error (0, saved_errno,
- _("cannot remove %s"), quote (full_filename (dir)));
- }
-
- return RM_ERROR;
- }
-
- if (ROOT_DEV_INO_CHECK (x->root_dev_ino, dir_st))
- {
- ROOT_DEV_INO_WARN (full_filename (dir));
- status = RM_ERROR;
- goto closedir_and_return;
- }
-
- AD_push (dirfd (dirp), ds, dir, dir_st);
- AD_INIT_OTHER_MEMBERS ();
-
- status = RM_OK;
+ case FTS_D: /* preorder directory */
+ if (! x->recursive
+ && !(x->remove_empty_directories
+ && is_empty_dir (fts->fts_cwd_fd, ent->fts_accpath)))
+ {
+ /* This is the first (pre-order) encounter with a directory
+ that we cannot delete.
+ Not recursive, and it's not an empty directory (if we're removing
+ them) so arrange to skip contents. */
+ int err = x->remove_empty_directories ? ENOTEMPTY : EISDIR;
+ error (0, err, _("cannot remove %s"), quoteaf (ent->fts_path));
+ mark_ancestor_dirs (ent);
+ fts_skip_tree (fts, ent);
+ return RM_ERROR;
+ }
+
+ /* Perform checks that can apply only for command-line arguments. */
+ if (ent->fts_level == FTS_ROOTLEVEL)
+ {
+ /* POSIX says:
+ If the basename of a command line argument is "." or "..",
+ diagnose it and do nothing more with that argument. */
+ if (dot_or_dotdot (last_component (ent->fts_accpath)))
+ {
+ error (0, 0,
+ _("refusing to remove %s or %s directory: skipping %s"),
+ quoteaf_n (0, "."), quoteaf_n (1, ".."),
+ quoteaf_n (2, ent->fts_path));
+ fts_skip_tree (fts, ent);
+ return RM_ERROR;
+ }
+
+ /* POSIX also says:
+ If a command line argument resolves to "/" (and --preserve-root
+ is in effect -- default) diagnose and skip it. */
+ if (ROOT_DEV_INO_CHECK (x->root_dev_ino, ent->fts_statp))
+ {
+ ROOT_DEV_INO_WARN (ent->fts_path);
+ fts_skip_tree (fts, ent);
+ return RM_ERROR;
+ }
+ }
- while (1)
- {
- char *subdir = NULL;
- struct stat subdir_sb;
- enum RM_status tmp_status;
-
- tmp_status = remove_cwd_entries (&dirp, ds, &subdir, &subdir_sb, x);
-
- if (tmp_status != RM_OK)
- {
- UPDATE_STATUS (status, tmp_status);
- AD_mark_current_as_unremovable (ds);
- }
- if (subdir)
- {
- if ( ! x->one_file_system
- || subdir_sb.st_dev == current_dev)
- {
- AD_push (dirfd (dirp), ds, subdir, &subdir_sb);
- AD_INIT_OTHER_MEMBERS ();
- free (subdir);
- continue;
- }
-
- /* Here, --one-file-system is in effect, and with remove_cwd_entries'
- traversal into the current directory, (known as SUBDIR, from ..),
- DIRP's device number is different from CURRENT_DEV. Arrange not
- to do anything more with this hierarchy. */
- error (0, 0, _("skipping %s, since it's on a different device"),
- quote (full_filename (subdir)));
- free (subdir);
- AD_mark_current_as_unremovable (ds);
- tmp_status = RM_ERROR;
- UPDATE_STATUS (status, tmp_status);
- }
-
- /* Execution reaches this point when we've removed the last
- removable entry from the current directory -- or, with
- --one-file-system, when the current directory is on a
- different file system. */
{
- int fd;
- /* The name of the directory that we have just processed,
- nominally removing all of its contents. */
- char *empty_dir = AD_pop_and_chdir (dirp, &fd, ds);
- dirp = NULL;
- assert (fd != AT_FDCWD || AD_stack_height (ds) == 1);
-
- /* Try to remove EMPTY_DIR only if remove_cwd_entries succeeded. */
- if (tmp_status == RM_OK)
- {
- /* This does a little more work than necessary when it actually
- prompts the user. E.g., we already know that D is a directory
- and that it's almost certainly empty, yet we lstat it.
- But that's no big deal since we're interactive. */
- struct stat empty_st;
- Ternary is_empty;
- enum RM_status s = prompt (fd, ds, empty_dir,
- cache_stat_init (&empty_st), x,
- PA_REMOVE_DIR, &is_empty);
-
- if (s != RM_OK)
- {
- free (empty_dir);
- status = s;
- if (fd != AT_FDCWD)
- close (fd);
- goto closedir_and_return;
- }
-
- if (unlinkat (fd, empty_dir, AT_REMOVEDIR) == 0)
- {
- if (x->verbose)
- printf (_("removed directory: %s\n"),
- quote (full_filename (empty_dir)));
- }
- else
- {
- error (0, errno, _("cannot remove directory %s"),
- quote (full_filename (empty_dir)));
- AD_mark_as_unremovable (ds, empty_dir);
- status = RM_ERROR;
- UPDATE_STATUS (AD_stack_top(ds)->status, status);
- }
- }
-
- free (empty_dir);
-
- if (fd == AT_FDCWD)
- break;
-
- dirp = fdopendir (fd);
- if (dirp == NULL)
- {
- error (0, errno, _("FATAL: cannot return to .. from %s"),
- quote (full_filename (".")));
- close (fd);
- longjmp (ds->current_arg_jumpbuf, 1);
- }
+ Ternary is_empty_directory;
+ enum RM_status s = prompt (fts, ent, true /*is_dir*/, x,
+ PA_DESCEND_INTO_DIR, &is_empty_directory);
+
+ if (s == RM_OK && is_empty_directory == T_YES)
+ {
+ /* When we know (from prompt when in interactive mode)
+ that this is an empty directory, don't prompt twice. */
+ s = excise (fts, ent, x, true);
+ fts_skip_tree (fts, ent);
+ }
+
+ if (s != RM_OK)
+ {
+ mark_ancestor_dirs (ent);
+ fts_skip_tree (fts, ent);
+ }
+
+ return s;
}
- }
-
- /* If the first/final hash table of unremovable entries was used,
- free it here. */
- AD_stack_pop (ds);
- closedir_and_return:;
- if (dirp != NULL && closedir (dirp) != 0)
- {
- error (0, 0, _("failed to close directory %s"),
- quote (full_filename (".")));
- status = RM_ERROR;
- }
-
- return status;
-}
-
-/* Remove the file or directory specified by FILENAME.
- Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED if not. */
+ case FTS_F: /* regular file */
+ case FTS_NS: /* stat(2) failed */
+ case FTS_SL: /* symbolic link */
+ case FTS_SLNONE: /* symbolic link without target */
+ case FTS_DP: /* postorder directory */
+ case FTS_DNR: /* unreadable directory */
+ case FTS_NSOK: /* e.g., dangling symlink */
+ case FTS_DEFAULT: /* none of the above */
+ {
+ /* With --one-file-system, do not attempt to remove a mount point.
+ fts' FTS_XDEV ensures that we don't process any entries under
+ the mount point. */
+ if (ent->fts_info == FTS_DP
+ && x->one_file_system
+ && FTS_ROOTLEVEL < ent->fts_level
+ && ent->fts_statp->st_dev != fts->fts_dev)
+ {
+ mark_ancestor_dirs (ent);
+ error (0, 0, _("skipping %s, since it's on a different device"),
+ quoteaf (ent->fts_path));
+ return RM_ERROR;
+ }
+
+ bool is_dir = ent->fts_info == FTS_DP || ent->fts_info == FTS_DNR;
+ enum RM_status s = prompt (fts, ent, is_dir, x, PA_REMOVE_DIR, NULL);
+ if (s != RM_OK)
+ return s;
+ return excise (fts, ent, x, is_dir);
+ }
-static enum RM_status
-rm_1 (Dirstack_state *ds, char const *filename,
- struct rm_options const *x, int *cwd_errno)
-{
- char const *base = last_component (filename);
- if (dot_or_dotdot (base))
- {
- error (0, 0, _(base == filename
- ? "cannot remove directory %s"
- : "cannot remove %s directory %s"),
- quote_n (0, base), quote_n (1, filename));
+ case FTS_DC: /* directory that causes cycles */
+ emit_cycle_warning (ent->fts_path);
+ fts_skip_tree (fts, ent);
return RM_ERROR;
- }
- struct stat st;
- cache_stat_init (&st);
- cycle_check_init (&ds->cycle_check_state);
- if (x->root_dev_ino)
- {
- if (cache_fstatat (AT_FDCWD, filename, &st, AT_SYMLINK_NOFOLLOW) != 0)
- {
- if (ignorable_missing (x, errno))
- return RM_OK;
- error (0, errno, _("cannot remove %s"), quote (filename));
- return RM_ERROR;
- }
- if (SAME_INODE (st, *(x->root_dev_ino)))
- {
- error (0, 0, _("cannot remove root directory %s"), quote (filename));
- return RM_ERROR;
- }
- }
-
- AD_push_initial (ds);
- AD_INIT_OTHER_MEMBERS ();
-
- enum RM_status status = remove_entry (AT_FDCWD, ds, filename, &st, x, NULL);
- if (status == RM_NONEMPTY_DIR)
- {
- /* In the event that remove_dir->remove_cwd_entries detects
- a directory cycle, arrange to fail, give up on this FILE, but
- continue on with any other arguments. */
- if (setjmp (ds->current_arg_jumpbuf))
- status = RM_ERROR;
- else
- status = remove_dir (AT_FDCWD, ds, filename, &st, x, cwd_errno);
+ case FTS_ERR:
+ /* Various failures, from opendir to ENOMEM, to failure to "return"
+ to preceding directory, can provoke this. */
+ error (0, ent->fts_errno, _("traversal failed: %s"),
+ quotef (ent->fts_path));
+ fts_skip_tree (fts, ent);
+ return RM_ERROR;
- AD_stack_clear (ds);
+ default:
+ error (0, 0, _("unexpected failure: fts_info=%d: %s\n"
+ "please report to %s"),
+ ent->fts_info,
+ quotef (ent->fts_path),
+ PACKAGE_BUGREPORT);
+ abort ();
}
-
- ds_clear (ds);
- return status;
}
-/* Remove all files and/or directories specified by N_FILES and FILE.
- Apply the options in X. */
-extern enum RM_status
-rm (size_t n_files, char const *const *file, struct rm_options const *x)
+/* Remove FILEs, honoring options specified via X.
+ Return RM_OK if successful. */
+enum RM_status
+rm (char *const *file, struct rm_options const *x)
{
- enum RM_status status = RM_OK;
- Dirstack_state *ds = ds_init ();
- int cwd_errno = 0;
- size_t i;
+ enum RM_status rm_status = RM_OK;
- for (i = 0; i < n_files; i++)
+ if (*file)
{
- if (cwd_errno && IS_RELATIVE_FILE_NAME (file[i]))
- {
- error (0, 0, _("cannot remove relative-named %s"), quote (file[i]));
- status = RM_ERROR;
- }
- else
- {
- enum RM_status s = rm_1 (ds, file[i], x, &cwd_errno);
- assert (VALID_STATUS (s));
- UPDATE_STATUS (status, s);
- }
+ int bit_flags = (FTS_CWDFD
+ | FTS_NOSTAT
+ | FTS_PHYSICAL);
+
+ if (x->one_file_system)
+ bit_flags |= FTS_XDEV;
+
+ FTS *fts = xfts_open (file, bit_flags, NULL);
+
+ while (1)
+ {
+ FTSENT *ent;
+
+ ent = fts_read (fts);
+ if (ent == NULL)
+ {
+ if (errno != 0)
+ {
+ error (0, errno, _("fts_read failed"));
+ rm_status = RM_ERROR;
+ }
+ break;
+ }
+
+ enum RM_status s = rm_fts (fts, ent, x);
+
+ assert (VALID_STATUS (s));
+ UPDATE_STATUS (rm_status, s);
+ }
+
+ if (fts_close (fts) != 0)
+ {
+ error (0, errno, _("fts_close failed"));
+ rm_status = RM_ERROR;
+ }
}
- if (x->require_restore_cwd && cwd_errno)
- {
- error (0, cwd_errno,
- _("cannot restore current working directory"));
- status = RM_ERROR;
- }
-
- ds_free (ds);
-
- return status;
+ return rm_status;
}
diff --git a/src/remove.h b/src/remove.h
index ae01e3c..ae2331f 100644
--- a/src/remove.h
+++ b/src/remove.h
@@ -1,12 +1,11 @@
/* Remove directory entries.
- Copyright (C) 1998, 2000, 2002, 2003, 2004, 2005, 2006, 2007 Free
- Software Foundation, Inc.
+ Copyright (C) 1998-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -14,8 +13,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#ifndef REMOVE_H
# define REMOVE_H
@@ -39,6 +37,7 @@ struct rm_options
/* If true, query the user about whether to remove each file. */
enum rm_interactive interactive;
+ // FIXME: remove
/* If true, do not traverse into (or remove) any directory that is
on a file system (i.e., that has a different device number) other
than that of the corresponding command line argument. Note that
@@ -50,8 +49,11 @@ struct rm_options
/* If true, recursively remove directories. */
bool recursive;
- /* Pointer to the device and inode numbers of `/', when --recursive
- and preserving `/'. Otherwise NULL. */
+ /* If true, remove empty directories. */
+ bool remove_empty_directories;
+
+ /* Pointer to the device and inode numbers of '/', when --recursive
+ and preserving '/'. Otherwise NULL. */
struct dev_ino *root_dev_ino;
/* If nonzero, stdin is a tty. */
@@ -85,12 +87,11 @@ enum RM_status
do \
{ \
if ((New_value) == RM_ERROR \
- || ((New_value) == RM_USER_DECLINED && (S) == RM_OK)) \
- (S) = (New_value); \
+ || ((New_value) == RM_USER_DECLINED && (S) == RM_OK)) \
+ (S) = (New_value); \
} \
while (0)
-enum RM_status rm (size_t n_files, char const *const *file,
- struct rm_options const *x);
+extern enum RM_status rm (char *const *file, struct rm_options const *x);
#endif
diff --git a/src/rm.c b/src/rm.c
index 81f81ec..13a5714 100644
--- a/src/rm.c
+++ b/src/rm.c
@@ -1,10 +1,10 @@
-/* `rm' file deletion utility for GNU.
- Copyright (C) 88, 90, 91, 1994-2007 Free Software Foundation, Inc.
+/* 'rm' file deletion utility for GNU.
+ Copyright (C) 1988-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,35 +12,11 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
-/* Written by Paul Rubin, David MacKenzie, and Richard Stallman.
- Reworked to use chdir and avoid recursion by Jim Meyering. */
-
-/* Implementation overview:
-
- In the `usual' case, RM saves no state for directories it is processing.
- When a removal fails (either due to an error or to an interactive `no'
- reply), the failure is noted (see description of `ht' in remove.c's
- remove_cwd_entries function) so that when/if the containing directory
- is reopened, RM doesn't try to remove the entry again.
-
- RM may delete arbitrarily deep hierarchies -- even ones in which file
- names (from root to leaf) are longer than the system-imposed maximum.
- It does this by using chdir to change to each directory in turn before
- removing the entries in that directory.
-
- RM detects directory cycles lazily. See lib/cycle-check.c.
-
- RM is careful to avoid forming full file names whenever possible.
- A full file name is formed only when it is about to be used -- e.g.
- in a diagnostic or in an interactive-mode prompt.
-
- RM minimizes the number of lstat system calls it makes. On systems
- that have valid d_type data in directory entries, RM makes only one
- lstat call per command line argument -- regardless of the depth of
- the hierarchy. */
+/* Initially written by Paul Rubin, David MacKenzie, and Richard Stallman.
+ Reworked to use chdir and avoid recursion, and later, rewritten
+ once again, to use fts, by Jim Meyering. */
#include <config.h>
#include <stdio.h>
@@ -51,21 +27,19 @@
#include "system.h"
#include "argmatch.h"
#include "error.h"
-#include "lstat.h"
-#include "quote.h"
-#include "quotearg.h"
#include "remove.h"
#include "root-dev-ino.h"
#include "yesno.h"
+#include "priv-set.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "rm"
#define AUTHORS \
- "Paul Rubin", "David MacKenzie, Richard Stallman", "Jim Meyering"
-
-/* Name this program was run with. */
-char *program_name;
+ proper_name ("Paul Rubin"), \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Richard M. Stallman"), \
+ proper_name ("Jim Meyering")
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
@@ -87,7 +61,6 @@ enum interactive_type
static struct option const long_opts[] =
{
- {"directory", no_argument, NULL, 'd'},
{"force", no_argument, NULL, 'f'},
{"interactive", optional_argument, NULL, INTERACTIVE_OPTION},
@@ -102,6 +75,7 @@ static struct option const long_opts[] =
{"-presume-input-tty", no_argument, NULL, PRESUME_INPUT_TTY_OPTION},
{"recursive", no_argument, NULL, 'r'},
+ {"dir", no_argument, NULL, 'd'},
{"verbose", no_argument, NULL, 'v'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
@@ -123,7 +97,7 @@ static enum interactive_type const interactive_types[] =
ARGMATCH_VERIFY (interactive_args, interactive_types);
/* Advise the user about invalid usages like "rm -foo" if the file
- "-foo" exists, assuming ARGC and ARGV are as with `main'. */
+ "-foo" exists, assuming ARGC and ARGV are as with 'main'. */
static void
diagnose_leading_hyphen (int argc, char **argv)
@@ -138,14 +112,14 @@ diagnose_leading_hyphen (int argc, char **argv)
struct stat st;
if (arg[0] == '-' && arg[1] && lstat (arg, &st) == 0)
- {
- fprintf (stderr,
- _("Try `%s ./%s' to remove the file %s.\n"),
- argv[0],
- quotearg_n_style (1, shell_quoting_style, arg),
- quote (arg));
- break;
- }
+ {
+ fprintf (stderr,
+ _("Try '%s ./%s' to remove the file %s.\n"),
+ argv[0],
+ quotearg_n_style (1, shell_escape_quoting_style, arg),
+ quoteaf (arg));
+ break;
+ }
}
}
@@ -153,23 +127,22 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
- printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
+ printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
fputs (_("\
Remove (unlink) the FILE(s).\n\
\n\
- -f, --force ignore nonexistent files, never prompt\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\
+ 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\
+ always (-i); without WHEN, prompt always\n\
"), stdout);
fputs (_("\
--one-file-system when removing a hierarchy recursively, skip any\n\
@@ -177,9 +150,10 @@ Remove (unlink) the FILE(s).\n\
that of the corresponding command line argument\n\
"), stdout);
fputs (_("\
- --no-preserve-root do not treat `/' specially\n\
- --preserve-root do not remove `/' (default)\n\
+ --no-preserve-root do not treat '/' specially\n\
+ --preserve-root do not remove '/' (default)\n\
-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);
@@ -191,20 +165,20 @@ option to remove each listed directory, too, along with all of its contents.\n\
"), stdout);
printf (_("\
\n\
-To remove a file whose name starts with a `-', for example `-foo',\n\
+To remove a file whose name starts with a '-', for example '-foo',\n\
use one of these commands:\n\
%s -- -foo\n\
\n\
%s ./-foo\n\
"),
- program_name, program_name);
+ program_name, program_name);
fputs (_("\
\n\
-Note that if you use rm to remove a file, it is usually possible to recover\n\
-the contents of that file. If you want more assurance that the contents are\n\
-truly unrecoverable, consider using shred.\n\
+Note that if you use rm to remove a file, it might be possible to recover\n\
+some of its contents, given sufficient expertise and/or time. For greater\n\
+assurance that the contents are truly unrecoverable, consider using shred.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -215,12 +189,13 @@ rm_option_init (struct rm_options *x)
x->ignore_missing_files = false;
x->interactive = RMI_SOMETIMES;
x->one_file_system = false;
+ x->remove_empty_directories = false;
x->recursive = false;
x->root_dev_ino = NULL;
x->stdin_tty = isatty (STDIN_FILENO);
x->verbose = false;
- /* Since this program exits immediately after calling `rm', rm need not
+ /* Since this program exits immediately after calling 'rm', rm need not
expend unnecessary effort to preserve the initial working directory. */
x->require_restore_cwd = false;
}
@@ -234,141 +209,146 @@ main (int argc, char **argv)
int c;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
- atexit (close_stdout);
+ atexit (close_stdin);
rm_option_init (&x);
+ /* Try to disable the ability to unlink a directory. */
+ priv_set_remove_linkdir ();
+
while ((c = getopt_long (argc, argv, "dfirvIR", long_opts, NULL)) != -1)
{
switch (c)
- {
- case 'd':
- /* Ignore this option, for backward compatibility with
- coreutils 5.92. FIXME: Some time after 2005, change this
- to report an error (or perhaps behave like FreeBSD does)
- instead of ignoring the option. */
- break;
-
- case 'f':
- x.interactive = RMI_NEVER;
- x.ignore_missing_files = true;
- prompt_once = false;
- break;
-
- case 'i':
- x.interactive = RMI_ALWAYS;
- x.ignore_missing_files = false;
- prompt_once = false;
- break;
-
- case 'I':
- x.interactive = RMI_NEVER;
- x.ignore_missing_files = false;
- prompt_once = true;
- break;
-
- case 'r':
- case 'R':
- x.recursive = true;
- break;
-
- case INTERACTIVE_OPTION:
- {
- int i;
- if (optarg)
- i = XARGMATCH ("--interactive", optarg, interactive_args,
- interactive_types);
- else
- i = interactive_always;
- switch (i)
- {
- case interactive_never:
- x.interactive = RMI_NEVER;
- prompt_once = false;
- break;
-
- case interactive_once:
- x.interactive = RMI_SOMETIMES;
- x.ignore_missing_files = false;
- prompt_once = true;
- break;
-
- case interactive_always:
- x.interactive = RMI_ALWAYS;
- x.ignore_missing_files = false;
- prompt_once = false;
- break;
- }
- break;
- }
-
- case ONE_FILE_SYSTEM:
- x.one_file_system = true;
- break;
-
- case NO_PRESERVE_ROOT:
- preserve_root = false;
- break;
-
- case PRESERVE_ROOT:
- preserve_root = true;
- break;
-
- case PRESUME_INPUT_TTY_OPTION:
- x.stdin_tty = true;
- break;
-
- case 'v':
- x.verbose = true;
- break;
-
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- diagnose_leading_hyphen (argc, argv);
- usage (EXIT_FAILURE);
- }
+ {
+ case 'd':
+ x.remove_empty_directories = true;
+ break;
+
+ case 'f':
+ x.interactive = RMI_NEVER;
+ x.ignore_missing_files = true;
+ prompt_once = false;
+ break;
+
+ case 'i':
+ x.interactive = RMI_ALWAYS;
+ x.ignore_missing_files = false;
+ prompt_once = false;
+ break;
+
+ case 'I':
+ x.interactive = RMI_SOMETIMES;
+ x.ignore_missing_files = false;
+ prompt_once = true;
+ break;
+
+ case 'r':
+ case 'R':
+ x.recursive = true;
+ break;
+
+ case INTERACTIVE_OPTION:
+ {
+ int i;
+ if (optarg)
+ i = XARGMATCH ("--interactive", optarg, interactive_args,
+ interactive_types);
+ else
+ i = interactive_always;
+ switch (i)
+ {
+ case interactive_never:
+ x.interactive = RMI_NEVER;
+ prompt_once = false;
+ break;
+
+ case interactive_once:
+ x.interactive = RMI_SOMETIMES;
+ x.ignore_missing_files = false;
+ prompt_once = true;
+ break;
+
+ case interactive_always:
+ x.interactive = RMI_ALWAYS;
+ x.ignore_missing_files = false;
+ prompt_once = false;
+ break;
+ }
+ break;
+ }
+
+ case ONE_FILE_SYSTEM:
+ x.one_file_system = true;
+ break;
+
+ case NO_PRESERVE_ROOT:
+ preserve_root = false;
+ break;
+
+ case PRESERVE_ROOT:
+ preserve_root = true;
+ break;
+
+ case PRESUME_INPUT_TTY_OPTION:
+ x.stdin_tty = true;
+ break;
+
+ case 'v':
+ x.verbose = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ diagnose_leading_hyphen (argc, argv);
+ usage (EXIT_FAILURE);
+ }
}
if (argc <= optind)
{
if (x.ignore_missing_files)
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
else
- {
- error (0, 0, _("missing operand"));
- usage (EXIT_FAILURE);
- }
+ {
+ error (0, 0, _("missing operand"));
+ usage (EXIT_FAILURE);
+ }
}
- if (x.recursive & preserve_root)
+ if (x.recursive && preserve_root)
{
static struct dev_ino dev_ino_buf;
x.root_dev_ino = get_root_dev_ino (&dev_ino_buf);
if (x.root_dev_ino == NULL)
- error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
- quote ("/"));
+ error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
+ quoteaf ("/"));
}
- size_t n_files = argc - optind;
- char const *const *file = (char const *const *) argv + optind;
+ uintmax_t n_files = argc - optind;
+ char **file = argv + optind;
if (prompt_once && (x.recursive || 3 < n_files))
{
fprintf (stderr,
- (x.recursive
- ? _("%s: remove all arguments recursively? ")
- : _("%s: remove all arguments? ")),
- program_name);
+ (x.recursive
+ ? ngettext ("%s: remove %"PRIuMAX" argument recursively? ",
+ "%s: remove %"PRIuMAX" arguments recursively? ",
+ select_plural (n_files))
+ : ngettext ("%s: remove %"PRIuMAX" argument? ",
+ "%s: remove %"PRIuMAX" arguments? ",
+ select_plural (n_files))),
+ program_name, n_files);
if (!yesno ())
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
- enum RM_status status = rm (n_files, file, &x);
+
+ enum RM_status status = rm (file, &x);
assert (VALID_STATUS (status));
- exit (status == RM_ERROR ? EXIT_FAILURE : EXIT_SUCCESS);
+ return status == RM_ERROR ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/rmdir.c b/src/rmdir.c
index 39063b4..d86ab7a 100644
--- a/src/rmdir.c
+++ b/src/rmdir.c
@@ -1,12 +1,11 @@
/* rmdir -- remove directories
- Copyright (C) 90, 91, 1995-2002, 2004, 2005, 2006 Free Software
- Foundation, Inc.
+ Copyright (C) 1990-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -14,13 +13,12 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Options:
-p, --parent Remove any parent dirs that are explicitly mentioned
- in an argument, if they become empty after the
- argument file is removed.
+ in an argument, if they become empty after the
+ argument file is removed.
David MacKenzie <djm@ai.mit.edu> */
@@ -31,15 +29,12 @@
#include "system.h"
#include "error.h"
-#include "quotearg.h"
+#include "prog-fprintf.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "rmdir"
-#define AUTHORS "David MacKenzie"
-
-/* The name this program was run with. */
-char *program_name;
+#define AUTHORS proper_name ("David MacKenzie")
/* If true, remove empty parent directories. */
static bool remove_empty_parents;
@@ -60,7 +55,7 @@ enum
static struct option const longopts[] =
{
- /* Don't name this `--force' because it's not close enough in meaning
+ /* 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, NULL,
IGNORE_FAIL_ON_NON_EMPTY_OPTION},
@@ -75,11 +70,39 @@ static struct option const longopts[] =
/* Return true if ERROR_NUMBER is one of the values associated
with a failed rmdir due to non-empty target directory. */
-
static bool
errno_rmdir_non_empty (int error_number)
{
- return (error_number == RMDIR_ERRNO_NOT_EMPTY);
+ return error_number == ENOTEMPTY || error_number == EEXIST;
+}
+
+/* Return true if when rmdir fails with errno == ERROR_NUMBER
+ the directory may be empty. */
+static bool
+errno_may_be_empty (int error_number)
+{
+ switch (error_number)
+ {
+ case EACCES:
+ case EPERM:
+ case EROFS:
+ case EEXIST:
+ case EBUSY:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* Return true if an rmdir failure with errno == error_number
+ for DIR is ignorable. */
+static bool
+ignorable_failure (int error_number, char const *dir)
+{
+ return (ignore_fail_on_non_empty
+ && (errno_rmdir_non_empty (error_number)
+ || (errno_may_be_empty (error_number)
+ && is_empty_dir (AT_FDCWD, dir))));
}
/* Remove any empty parent directories of DIR.
@@ -98,33 +121,34 @@ remove_parents (char *dir)
{
slash = strrchr (dir, '/');
if (slash == NULL)
- break;
+ break;
/* Remove any characters after the slash, skipping any extra
- slashes in a row. */
+ slashes in a row. */
while (slash > dir && *slash == '/')
- --slash;
+ --slash;
slash[1] = 0;
/* Give a diagnostic for each attempted removal if --verbose. */
if (verbose)
- error (0, 0, _("removing directory, %s"), dir);
+ prog_fprintf (stdout, _("removing directory, %s"), quoteaf (dir));
ok = (rmdir (dir) == 0);
if (!ok)
- {
- /* Stop quietly if --ignore-fail-on-non-empty. */
- if (ignore_fail_on_non_empty
- && errno_rmdir_non_empty (errno))
- {
- ok = true;
- }
- else
- {
- error (0, errno, "%s", quotearg_colon (dir));
- }
- break;
- }
+ {
+ /* Stop quietly if --ignore-fail-on-non-empty. */
+ if (ignorable_failure (errno, dir))
+ {
+ ok = true;
+ }
+ else
+ {
+ /* Barring race conditions, DIR is expected to be a directory. */
+ error (0, errno, _("failed to remove directory %s"),
+ quoteaf (dir));
+ }
+ break;
+ }
}
return ok;
}
@@ -133,8 +157,7 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("Usage: %s [OPTION]... DIRECTORY...\n"), program_name);
@@ -143,16 +166,17 @@ Remove the DIRECTORY(ies), if they are empty.\n\
\n\
--ignore-fail-on-non-empty\n\
ignore each failure that is solely because a directory\n\
- is non-empty\n\
+ is non-empty\n\
"), stdout);
fputs (_("\
- -p, --parents Remove DIRECTORY and its ancestors. E.g., `rmdir -p a/b/c' is\n\
- similar to `rmdir a/b/c a/b a'.\n\
+ -p, --parents remove DIRECTORY and its ancestors; e.g., 'rmdir -p a/b/c' is\
+\n\
+ similar to 'rmdir a/b/c a/b a'\n\
-v, --verbose output a diagnostic for every directory processed\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -164,7 +188,7 @@ main (int argc, char **argv)
int optc;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -176,21 +200,21 @@ main (int argc, char **argv)
while ((optc = getopt_long (argc, argv, "pv", longopts, NULL)) != -1)
{
switch (optc)
- {
- case 'p':
- remove_empty_parents = true;
- break;
- case IGNORE_FAIL_ON_NON_EMPTY_OPTION:
- ignore_fail_on_non_empty = true;
- break;
- case 'v':
- verbose = true;
- break;
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'p':
+ remove_empty_parents = true;
+ break;
+ case IGNORE_FAIL_ON_NON_EMPTY_OPTION:
+ ignore_fail_on_non_empty = true;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
}
if (optind == argc)
@@ -205,22 +229,23 @@ main (int argc, char **argv)
/* Give a diagnostic for each attempted removal if --verbose. */
if (verbose)
- error (0, 0, _("removing directory, %s"), dir);
+ prog_fprintf (stdout, _("removing directory, %s"), quoteaf (dir));
if (rmdir (dir) != 0)
- {
- if (ignore_fail_on_non_empty
- && errno_rmdir_non_empty (errno))
- continue;
-
- error (0, errno, "%s", quotearg_colon (dir));
- ok = false;
- }
+ {
+ if (ignorable_failure (errno, dir))
+ continue;
+
+ /* Here, the diagnostic is less precise, since we have no idea
+ whether DIR is a directory. */
+ error (0, errno, _("failed to remove %s"), quoteaf (dir));
+ ok = false;
+ }
else if (remove_empty_parents)
- {
- ok &= remove_parents (dir);
- }
+ {
+ ok &= remove_parents (dir);
+ }
}
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/runcon.c b/src/runcon.c
new file mode 100644
index 0000000..b25db04
--- /dev/null
+++ b/src/runcon.c
@@ -0,0 +1,263 @@
+/* runcon -- run command with specified security context
+ Copyright (C) 2005-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/*
+ * runcon [ context
+ * | ( [ -c ] [ -r role ] [-t type] [ -u user ] [ -l levelrange ] )
+ * command [arg1 [arg2 ...] ]
+ *
+ * attempt to run the specified command with the specified context.
+ *
+ * -r role : use the current context with the specified role
+ * -t type : use the current context with the specified type
+ * -u user : use the current context with the specified user
+ * -l level : use the current context with the specified level range
+ * -c : compute process transition context before modifying
+ *
+ * Contexts are interpreted as follows:
+ *
+ * Number of MLS
+ * components system?
+ *
+ * 1 - type
+ * 2 - role:type
+ * 3 Y role:type:range
+ * 3 N user:role:type
+ * 4 Y user:role:type:range
+ * 4 N error
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#include <sys/types.h>
+#include "system.h"
+#include "error.h"
+#include "quote.h"
+
+/* The official name of this program (e.g., no 'g' prefix). */
+#define PROGRAM_NAME "runcon"
+
+#define AUTHORS proper_name ("Russell Coker")
+
+static struct option const long_options[] =
+{
+ {"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},
+ {NULL, 0, NULL, 0}
+};
+
+void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ emit_try_help ();
+ else
+ {
+ printf (_("\
+Usage: %s CONTEXT COMMAND [args]\n\
+ or: %s [ -c ] [-u USER] [-r ROLE] [-t TYPE] [-l RANGE] COMMAND [args]\n\
+"), program_name, program_name);
+ fputs (_("\
+Run a program in a different SELinux security context.\n\
+With neither CONTEXT nor COMMAND, print the current security context.\n\
+"), stdout);
+
+ emit_mandatory_arg_note ();
+
+ fputs (_("\
+ CONTEXT Complete security context\n\
+ -c, --compute compute process transition context before modifying\n\
+ -t, --type=TYPE type (for same role as parent)\n\
+ -u, --user=USER user identity\n\
+ -r, --role=ROLE role\n\
+ -l, --range=RANGE levelrange\n\
+\n\
+"), stdout);
+ fputs (HELP_OPTION_DESCRIPTION, stdout);
+ fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ emit_ancillary_info (PROGRAM_NAME);
+ }
+ exit (status);
+}
+
+int
+main (int argc, char **argv)
+{
+ 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;
+
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ atexit (close_stdout);
+
+ while (1)
+ {
+ int option_index = 0;
+ int c = getopt_long (argc, argv, "+r:t:u:l:c", long_options,
+ &option_index);
+ if (c == -1)
+ break;
+ switch (c)
+ {
+ case 'r':
+ if (role)
+ error (EXIT_FAILURE, 0, _("multiple roles"));
+ role = optarg;
+ break;
+ case 't':
+ if (type)
+ error (EXIT_FAILURE, 0, _("multiple types"));
+ type = optarg;
+ break;
+ case 'u':
+ if (user)
+ error (EXIT_FAILURE, 0, _("multiple users"));
+ user = optarg;
+ break;
+ case 'l':
+ if (range)
+ error (EXIT_FAILURE, 0, _("multiple levelranges"));
+ range = optarg;
+ break;
+ case 'c':
+ compute_trans = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ break;
+ }
+ }
+
+ if (argc - optind == 0)
+ {
+ if (getcon (&cur_context) < 0)
+ error (EXIT_FAILURE, errno, _("failed to get current context"));
+ fputs (cur_context, stdout);
+ fputc ('\n', stdout);
+ return EXIT_SUCCESS;
+ }
+
+ if (!(user || role || type || range || compute_trans))
+ {
+ if (optind >= argc)
+ {
+ error (0, 0, _("you must specify -c, -t, -u, -l, -r, or context"));
+ usage (EXIT_FAILURE);
+ }
+ context = argv[optind++];
+ }
+
+ if (optind >= argc)
+ {
+ error (0, 0, _("no command specified"));
+ usage (EXIT_FAILURE);
+ }
+
+ if (is_selinux_enabled () != 1)
+ error (EXIT_FAILURE, 0, _("%s may be used only on a SELinux kernel"),
+ program_name);
+
+ if (context)
+ {
+ con = context_new (context);
+ if (!con)
+ error (EXIT_FAILURE, errno, _("failed to create security context: %s"),
+ quote (context));
+ }
+ else
+ {
+ if (getcon (&cur_context) < 0)
+ error (EXIT_FAILURE, errno, _("failed to get current context"));
+
+ /* We will generate context based on process transition */
+ if (compute_trans)
+ {
+ /* Get context of file to be executed */
+ if (getfilecon (argv[optind], &file_context) == -1)
+ error (EXIT_FAILURE, errno,
+ _("failed to get security context of %s"),
+ quoteaf (argv[optind]));
+ /* compute result of process transition */
+ if (security_compute_create (cur_context, file_context,
+ string_to_security_class ("process"),
+ &new_context) != 0)
+ error (EXIT_FAILURE, errno, _("failed to compute a new context"));
+ /* free contexts */
+ freecon (file_context);
+ freecon (cur_context);
+
+ /* set cur_context equal to new_context */
+ cur_context = new_context;
+ }
+
+ con = context_new (cur_context);
+ if (!con)
+ error (EXIT_FAILURE, errno, _("failed to create security context: %s"),
+ quote (cur_context));
+ if (user && context_user_set (con, user))
+ error (EXIT_FAILURE, errno, _("failed to set new user: %s"),
+ quote (user));
+ if (type && context_type_set (con, type))
+ error (EXIT_FAILURE, errno, _("failed to set new type: %s"),
+ quote (type));
+ if (range && context_range_set (con, range))
+ error (EXIT_FAILURE, errno, _("failed to set new range: %s"),
+ quote (range));
+ if (role && context_role_set (con, role))
+ error (EXIT_FAILURE, errno, _("failed to set new role: %s"),
+ quote (role));
+ }
+
+ if (security_check_context (context_str (con)) < 0)
+ error (EXIT_FAILURE, errno, _("invalid context: %s"),
+ quote (context_str (con)));
+
+ if (setexeccon (context_str (con)) != 0)
+ error (EXIT_FAILURE, errno, _("unable to set security context %s"),
+ quote (context_str (con)));
+ if (cur_context != NULL)
+ freecon (cur_context);
+
+ execvp (argv[optind], argv + optind);
+
+ int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
+ error (0, errno, "%s", quote (argv[optind]));
+ return exit_status;
+}
diff --git a/src/selinux.c b/src/selinux.c
new file mode 100644
index 0000000..4ad5690
--- /dev/null
+++ b/src/selinux.c
@@ -0,0 +1,340 @@
+/* selinux - core functions for maintaining SELinux labeling
+ Copyright (C) 2012-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Written by Daniel Walsh <dwalsh@redhat.com> */
+
+#include <config.h>
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#include <sys/types.h>
+
+#include "error.h"
+#include "system.h"
+#include "canonicalize.h"
+#include "dosname.h"
+#include "xfts.h"
+#include "selinux.h"
+
+#if HAVE_SELINUX_SELINUX_H
+
+# if ! HAVE_MODE_TO_SECURITY_CLASS
+/*
+ This function has been added to libselinux-2.1.12-5, but is here
+ for support with older versions of SELinux
+
+ Translates a mode into an Internal SELinux security_class definition.
+ Returns 0 on failure, with errno set to EINVAL.
+*/
+static security_class_t
+mode_to_security_class (mode_t m)
+{
+
+ if (S_ISREG (m))
+ return string_to_security_class ("file");
+ if (S_ISDIR (m))
+ return string_to_security_class ("dir");
+ if (S_ISCHR (m))
+ return string_to_security_class ("chr_file");
+ if (S_ISBLK (m))
+ return string_to_security_class ("blk_file");
+ if (S_ISFIFO (m))
+ return string_to_security_class ("fifo_file");
+ if (S_ISLNK (m))
+ return string_to_security_class ("lnk_file");
+ if (S_ISSOCK (m))
+ return string_to_security_class ("sock_file");
+
+ errno = EINVAL;
+ return 0;
+}
+# endif
+
+/*
+ This function takes a PATH and a MODE and then asks SELinux what the label
+ of the path object would be if the current process label created it.
+ It then returns the label.
+
+ Returns -1 on failure. errno will be set appropriately.
+*/
+
+static int
+computecon (char const *path, mode_t mode, char **con)
+{
+ char *scon = NULL;
+ char *tcon = NULL;
+ security_class_t tclass;
+ int rc = -1;
+
+ char *dir = dir_name (path);
+ if (!dir)
+ goto quit;
+ if (getcon (&scon) < 0)
+ goto quit;
+ if (getfilecon (dir, &tcon) < 0)
+ goto quit;
+ tclass = mode_to_security_class (mode);
+ if (!tclass)
+ goto quit;
+ rc = security_compute_create (scon, tcon, tclass, con);
+
+quit:
+ free (dir);
+ freecon (scon);
+ freecon (tcon);
+ return rc;
+}
+
+/*
+ This function takes a path and a mode, it calls computecon to get the
+ label of the path object if the current process created it, then it calls
+ matchpathcon to get the default type for the object. It substitutes the
+ default type into label. It tells the SELinux Kernel to label all new file
+ system objects created by the current process with this label.
+
+ Returns -1 on failure. errno will be set appropriately.
+*/
+int
+defaultcon (char const *path, mode_t mode)
+{
+ int rc = -1;
+ char *scon = NULL;
+ char *tcon = NULL;
+ context_t scontext = 0, tcontext = 0;
+ const char *contype;
+ char *constr;
+ char *newpath = NULL;
+
+ if (! IS_ABSOLUTE_FILE_NAME (path))
+ {
+ /* Generate absolute path as required by subsequent matchpathcon(),
+ with libselinux < 2.1.5 2011-0826. */
+ newpath = canonicalize_filename_mode (path, CAN_MISSING);
+ if (! newpath)
+ error (EXIT_FAILURE, errno, _("error canonicalizing %s"),
+ quoteaf (path));
+ path = newpath;
+ }
+
+ if (matchpathcon (path, mode, &scon) < 0)
+ {
+ /* "No such file or directory" is a confusing error,
+ when processing files, when in fact it was the
+ associated default context that was not found.
+ Therefore map the error to something more appropriate
+ to the context in which we're using matchpathcon(). */
+ if (errno == ENOENT)
+ errno = ENODATA;
+ goto quit;
+ }
+ if (computecon (path, mode, &tcon) < 0)
+ goto quit;
+ if (!(scontext = context_new (scon)))
+ goto quit;
+ if (!(tcontext = context_new (tcon)))
+ goto quit;
+
+ if (!(contype = context_type_get (scontext)))
+ goto quit;
+ if (context_type_set (tcontext, contype))
+ goto quit;
+ if (!(constr = context_str (tcontext)))
+ goto quit;
+
+ rc = setfscreatecon (constr);
+
+quit:
+ context_free (scontext);
+ context_free (tcontext);
+ freecon (scon);
+ freecon (tcon);
+ free (newpath);
+ return rc;
+}
+
+/*
+ This function takes a PATH of an existing file system object, and a LOCAL
+ boolean that indicates whether the function should set the object's label
+ to the default for the local process, or one using system wide settings.
+ If LOCAL == true, it will ask the SELinux Kernel what the default label
+ for all objects created should be and then sets the label on the object.
+ Otherwise it calls matchpathcon on the object to ask the system what the
+ default label should be, extracts the type field and then modifies the file
+ system object. Note only the type field is updated, thus preserving MLS
+ levels and user identity etc. of the PATH.
+
+ Returns -1 on failure. errno will be set appropriately.
+*/
+static int
+restorecon_private (char const *path, bool local)
+{
+ int rc = -1;
+ struct stat sb;
+ char *scon = NULL;
+ char *tcon = NULL;
+ context_t scontext = 0, tcontext = 0;
+ const char *contype;
+ char *constr;
+ int fd;
+
+ if (local)
+ {
+ if (getfscreatecon (&tcon) < 0)
+ return rc;
+ if (!tcon)
+ {
+ errno = ENODATA;
+ return rc;
+ }
+ rc = lsetfilecon (path, tcon);
+ freecon (tcon);
+ return rc;
+ }
+
+ fd = open (path, O_RDONLY | O_NOFOLLOW);
+ if (fd == -1 && (errno != ELOOP))
+ goto quit;
+
+ if (fd != -1)
+ {
+ if (fstat (fd, &sb) < 0)
+ goto quit;
+ }
+ else
+ {
+ if (lstat (path, &sb) < 0)
+ goto quit;
+ }
+
+ if (matchpathcon (path, sb.st_mode, &scon) < 0)
+ {
+ /* "No such file or directory" is a confusing error,
+ when processing files, when in fact it was the
+ associated default context that was not found.
+ Therefore map the error to something more appropriate
+ to the context in which we're using matchpathcon(). */
+ if (errno == ENOENT)
+ errno = ENODATA;
+ goto quit;
+ }
+ if (!(scontext = context_new (scon)))
+ goto quit;
+
+ if (fd != -1)
+ {
+ if (fgetfilecon (fd, &tcon) < 0)
+ goto quit;
+ }
+ else
+ {
+ if (lgetfilecon (path, &tcon) < 0)
+ goto quit;
+ }
+
+ if (!(tcontext = context_new (tcon)))
+ goto quit;
+
+ if (!(contype = context_type_get (scontext)))
+ goto quit;
+ if (context_type_set (tcontext, contype))
+ goto quit;
+ if (!(constr = context_str (tcontext)))
+ goto quit;
+
+ if (fd != -1)
+ rc = fsetfilecon (fd, constr);
+ else
+ rc = lsetfilecon (path, constr);
+
+quit:
+ if (fd != -1)
+ close (fd);
+ context_free (scontext);
+ context_free (tcontext);
+ freecon (scon);
+ freecon (tcon);
+ return rc;
+}
+
+/*
+ This function takes three parameters:
+
+ PATH of an existing file system object.
+
+ A RECURSE boolean which if the file system object is a directory, will
+ call restorecon_private on every file system object in the directory.
+
+ A LOCAL boolean that indicates whether the function should set object labels
+ to the default for the local process, or use system wide settings.
+
+ Returns false on failure. errno will be set appropriately.
+*/
+bool
+restorecon (char const *path, bool recurse, bool local)
+{
+ char *newpath = NULL;
+ FTS *fts;
+ bool ok = true;
+
+ if (! IS_ABSOLUTE_FILE_NAME (path) && ! local)
+ {
+ /* Generate absolute path as required by subsequent matchpathcon(),
+ with libselinux < 2.1.5 2011-0826. Also generating the absolute
+ path before the fts walk, will generate absolute paths in the
+ fts entries, which may be quicker to process in any case. */
+ newpath = canonicalize_filename_mode (path, CAN_MISSING);
+ if (! newpath)
+ error (EXIT_FAILURE, errno, _("error canonicalizing %s"),
+ quoteaf (path));
+ }
+
+ const char *ftspath[2] = { newpath ? newpath : path, NULL };
+
+ if (! recurse)
+ {
+ ok = restorecon_private (*ftspath, local) != -1;
+ free (newpath);
+ return ok;
+ }
+
+ fts = xfts_open ((char *const *) ftspath, FTS_PHYSICAL, NULL);
+ while (1)
+ {
+ FTSENT *ent;
+
+ ent = fts_read (fts);
+ if (ent == NULL)
+ {
+ if (errno != 0)
+ {
+ error (0, errno, _("fts_read failed"));
+ ok = false;
+ }
+ break;
+ }
+
+ ok &= restorecon_private (fts->fts_path, local) != -1;
+ }
+
+ if (fts_close (fts) != 0)
+ {
+ error (0, errno, _("fts_close failed"));
+ ok = false;
+ }
+
+ free (newpath);
+ return ok;
+}
+#endif
diff --git a/src/selinux.h b/src/selinux.h
new file mode 100644
index 0000000..4b84c65
--- /dev/null
+++ b/src/selinux.h
@@ -0,0 +1,49 @@
+/* selinux - core functions for maintaining SELinux labeling
+ Copyright (C) 2012-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Written by Daniel Walsh <dwalsh@redhat.com> */
+
+#ifndef COREUTILS_SELINUX_H
+# define COREUTILS_SELINUX_H
+
+/* Return true if ERR corresponds to an unsupported request,
+ or if there is no context or it's inaccessible. */
+static inline bool
+ignorable_ctx_err (int err)
+{
+ return err == ENOTSUP || err == ENODATA;
+}
+
+# if HAVE_SELINUX_SELINUX_H
+
+extern bool
+restorecon (char const *path, bool recurse, bool preserve);
+extern int
+defaultcon (char const *path, mode_t mode);
+
+# else
+
+static inline bool
+restorecon (char const *path, bool recurse, bool preserve)
+{ errno = ENOTSUP; return false; }
+
+static inline int
+defaultcon (char const *path, mode_t mode)
+{ errno = ENOTSUP; return -1; }
+
+# endif
+
+#endif
diff --git a/src/seq.c b/src/seq.c
index 59dd318..fbb94a0 100644
--- a/src/seq.c
+++ b/src/seq.c
@@ -1,10 +1,10 @@
/* seq - print sequence of numbers to standard output.
- Copyright (C) 1994-2006 Free Software Foundation, Inc.
+ Copyright (C) 1994-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Ulrich Drepper. */
@@ -34,23 +33,19 @@
# define isfinite(x) ((x) * 0 == 0)
#endif
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "seq"
-#define AUTHORS "Ulrich Drepper"
+#define AUTHORS proper_name ("Ulrich Drepper")
/* If true print all number with equal width. */
static bool equal_width;
-/* The name that this program was run with. */
-char *program_name;
-
/* The string used to separate two numbers. */
static char const *separator;
/* The string output after all numbers have been output.
Usually "\n" or "\0". */
-/* FIXME: make this an option. */
static char const terminator[] = "\n";
static struct option const long_options[] =
@@ -67,8 +62,7 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
@@ -78,7 +72,11 @@ Usage: %s [OPTION]... LAST\n\
"), program_name, program_name, program_name);
fputs (_("\
Print numbers from FIRST to LAST, in steps of INCREMENT.\n\
-\n\
+"), stdout);
+
+ 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\
@@ -89,16 +87,18 @@ Print numbers from FIRST to LAST, in steps of INCREMENT.\n\
\n\
If FIRST or INCREMENT is omitted, it defaults to 1. That is, an\n\
omitted INCREMENT defaults to 1 even when LAST is smaller than FIRST.\n\
+The sequence of numbers ends when the sum of the current number and\n\
+INCREMENT would become greater than LAST.\n\
FIRST, INCREMENT, and LAST are interpreted as floating point values.\n\
INCREMENT is usually positive if FIRST is smaller than LAST, and\n\
INCREMENT is usually negative if FIRST is greater than LAST.\n\
"), stdout);
fputs (_("\
-FORMAT must be suitable for printing one argument of type `double';\n\
+FORMAT must be suitable for printing one argument of type 'double';\n\
it defaults to %.PRECf if FIRST, INCREMENT, and LAST are all fixed point\n\
decimal numbers with maximum precision PREC, and to %g otherwise.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -121,6 +121,14 @@ struct operand
};
typedef struct operand operand;
+/* Description of what a number-generating format will generate. */
+struct layout
+{
+ /* Number of bytes before and after the number. */
+ size_t prefix_len;
+ size_t suffix_len;
+};
+
/* Read a long double value from the command line.
Return if the string is correct else signal error. */
@@ -131,47 +139,95 @@ scan_arg (const char *arg)
if (! xstrtold (arg, NULL, &ret.value, c_strtold))
{
- error (0, 0, _("invalid floating point argument: %s"), arg);
+ error (0, 0, _("invalid floating point argument: %s"), quote (arg));
usage (EXIT_FAILURE);
}
- ret.width = strlen (arg);
+ /* We don't output spaces or '+' so don't include in width */
+ while (isspace (to_uchar (*arg)) || *arg == '+')
+ arg++;
+
+ /* Default to auto width and precision. */
+ ret.width = 0;
ret.precision = INT_MAX;
- if (! arg[strcspn (arg, "eExX")] && isfinite (ret.value))
+ /* Use no precision (and possibly fast generation) for integers. */
+ char const *decimal_point = strchr (arg, '.');
+ if (! decimal_point && ! strchr (arg, 'p') /* not a hex float */)
+ ret.precision = 0;
+
+ /* auto set width and precision for decimal inputs. */
+ if (! arg[strcspn (arg, "xX")] && isfinite (ret.value))
{
- char const *decimal_point = strchr (arg, '.');
- if (! decimal_point)
- ret.precision = 0;
- else
- {
- size_t fraction_len = strlen (decimal_point + 1);
- if (fraction_len <= INT_MAX)
- ret.precision = fraction_len;
- ret.width += (fraction_len == 0
- ? -1
- : (decimal_point == arg
- || ! ISDIGIT (decimal_point[-1])));
- }
+ size_t fraction_len = 0;
+ ret.width = strlen (arg);
+
+ if (decimal_point)
+ {
+ fraction_len = strcspn (decimal_point + 1, "eE");
+ if (fraction_len <= INT_MAX)
+ ret.precision = fraction_len;
+ ret.width += (fraction_len == 0 /* #. -> # */
+ ? -1
+ : (decimal_point == arg /* .# -> 0.# */
+ || ! ISDIGIT (decimal_point[-1]))); /* -.# -> 0.# */
+ }
+ char const *e = strchr (arg, 'e');
+ if (! e)
+ e = strchr (arg, 'E');
+ if (e)
+ {
+ long exponent = strtol (e + 1, NULL, 10);
+ ret.precision += exponent < 0 ? -exponent
+ : - MIN (ret.precision, exponent);
+ /* Don't account for e.... in the width since this is not output. */
+ ret.width -= strlen (arg) - (e - arg);
+ /* Adjust the width as per the exponent. */
+ if (exponent < 0)
+ {
+ if (decimal_point)
+ {
+ if (e == decimal_point + 1) /* undo #. -> # above */
+ ret.width++;
+ }
+ else
+ ret.width++;
+ exponent = -exponent;
+ }
+ else
+ {
+ if (decimal_point && ret.precision == 0 && fraction_len)
+ ret.width--; /* discount space for '.' */
+ exponent -= MIN (fraction_len, exponent);
+ }
+ ret.width += exponent;
+ }
}
return ret;
}
/* If FORMAT is a valid printf format for a double argument, return
- its long double equivalent, possibly allocated from dynamic
- storage; otherwise, return NULL. */
+ its long double equivalent, allocated from dynamic storage, and
+ store into *LAYOUT a description of the output layout; otherwise,
+ report an error and exit. */
static char const *
-long_double_format (char const *fmt)
+long_double_format (char const *fmt, struct layout *layout)
{
size_t i;
- size_t prefix_len;
+ 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++)
- if (! fmt[i])
- return NULL;
+ for (i = 0; ! (fmt[i] == '%' && fmt[i + 1] != '%'); i += (fmt[i] == '%') + 1)
+ {
+ if (!fmt[i])
+ error (EXIT_FAILURE, 0,
+ _("format %s has no %% directive"), quote (fmt));
+ prefix_len++;
+ }
i++;
i += strspn (fmt + i, "-+#0 '");
@@ -182,47 +238,98 @@ long_double_format (char const *fmt)
i += strspn (fmt + i, "0123456789");
}
- prefix_len = i;
+ length_modifier_offset = i;
has_L = (fmt[i] == 'L');
i += has_L;
+ if (fmt[i] == '\0')
+ error (EXIT_FAILURE, 0, _("format %s ends in %%"), quote (fmt));
if (! strchr ("efgaEFGA", fmt[i]))
- return NULL;
-
- for (i++; ! (fmt[i] == '%' && fmt[i + 1] != '%'); i++)
- if (! fmt[i])
+ error (EXIT_FAILURE, 0,
+ _("format %s has unknown %%%c directive"), quote (fmt), fmt[i]);
+
+ for (i++; ; i += (fmt[i] == '%') + 1)
+ if (fmt[i] == '%' && fmt[i + 1] != '%')
+ error (EXIT_FAILURE, 0, _("format %s has too many %% directives"),
+ quote (fmt));
+ else if (fmt[i])
+ suffix_len++;
+ else
{
- size_t format_size = i + 1;
- char *ldfmt = xmalloc (format_size + 1);
- memcpy (ldfmt, fmt, prefix_len);
- ldfmt[prefix_len] = 'L';
- strcpy (ldfmt + prefix_len + 1, fmt + prefix_len + has_L);
- return ldfmt;
+ size_t format_size = i + 1;
+ char *ldfmt = xmalloc (format_size + 1);
+ memcpy (ldfmt, fmt, length_modifier_offset);
+ ldfmt[length_modifier_offset] = 'L';
+ strcpy (ldfmt + length_modifier_offset + 1,
+ fmt + length_modifier_offset + has_L);
+ layout->prefix_len = prefix_len;
+ layout->suffix_len = suffix_len;
+ return ldfmt;
}
-
- return NULL;
}
/* Actually print the sequence of numbers in the specified range, with the
given or default stepping and format. */
static void
-print_numbers (char const *fmt,
- long double first, long double step, long double last)
+print_numbers (char const *fmt, struct layout layout,
+ long double first, long double step, long double last)
{
- long double i;
+ bool out_of_range = (step < 0 ? first < last : last < first);
- for (i = 0; /* empty */; i++)
+ if (! out_of_range)
{
- long double x = first + i * step;
- if (step < 0 ? x < last : last < x)
- break;
- if (i)
- fputs (separator, stdout);
- printf (fmt, x);
+ long double x = first;
+ long double i;
+
+ for (i = 1; ; i++)
+ {
+ long double x0 = x;
+ printf (fmt, x);
+ if (out_of_range)
+ break;
+ x = first + i * step;
+ out_of_range = (step < 0 ? x < last : last < x);
+
+ if (out_of_range)
+ {
+ /* If the number just past LAST prints as a value equal
+ to LAST, and prints differently from the previous
+ number, then print the number. This avoids problems
+ with rounding. For example, with the x86 it causes
+ "seq 0 0.000001 0.000003" to print 0.000003 instead
+ of stopping at 0.000002. */
+
+ bool print_extra_number = false;
+ long double x_val;
+ char *x_str;
+ int x_strlen;
+ setlocale (LC_NUMERIC, "C");
+ x_strlen = asprintf (&x_str, fmt, x);
+ setlocale (LC_NUMERIC, "");
+ if (x_strlen < 0)
+ xalloc_die ();
+ x_str[x_strlen - layout.suffix_len] = '\0';
+
+ if (xstrtold (x_str + layout.prefix_len, NULL, &x_val, c_strtold)
+ && x_val == last)
+ {
+ char *x0_str = NULL;
+ if (asprintf (&x0_str, fmt, x0) < 0)
+ xalloc_die ();
+ print_extra_number = !STREQ (x0_str, x_str);
+ free (x0_str);
+ }
+
+ free (x_str);
+ if (! print_extra_number)
+ break;
+ }
+
+ fputs (separator, stdout);
+ }
+
+ fputs (terminator, stdout);
}
-
- if (i)
- fputs (terminator, stdout);
}
/* Return the default format given FIRST, STEP, and LAST. */
@@ -236,31 +343,183 @@ get_default_format (operand first, operand step, operand last)
if (prec != INT_MAX && last.precision != INT_MAX)
{
if (equal_width)
- {
- size_t first_width = first.width + (prec - first.precision);
- size_t last_width = last.width + (prec - last.precision);
- if (first.width <= first_width
- && (last.width < last_width) == (prec < last.precision))
- {
- size_t width = MAX (first_width, last_width);
- if (width <= INT_MAX)
- {
- int w = width;
- sprintf (format_buf, "%%0%d.%dLf", w, prec);
- return format_buf;
- }
- }
- }
+ {
+ /* increase first_width by any increased precision in step */
+ size_t first_width = first.width + (prec - first.precision);
+ /* adjust last_width to use precision from first/step */
+ size_t last_width = last.width + (prec - last.precision);
+ if (last.precision && prec == 0)
+ last_width--; /* don't include space for '.' */
+ if (last.precision == 0 && prec)
+ last_width++; /* include space for '.' */
+ if (first.precision == 0 && prec)
+ first_width++; /* include space for '.' */
+ size_t width = MAX (first_width, last_width);
+ if (width <= INT_MAX)
+ {
+ int w = width;
+ sprintf (format_buf, "%%0%d.%dLf", w, prec);
+ return format_buf;
+ }
+ }
else
- {
- sprintf (format_buf, "%%.%dLf", prec);
- return format_buf;
- }
+ {
+ sprintf (format_buf, "%%.%dLf", prec);
+ return format_buf;
+ }
}
return "%Lg";
}
+/* The NUL-terminated string S0 of length S_LEN represents a valid
+ non-negative decimal integer. Adjust the string and length so
+ that the pair describe the next-larger value. */
+static void
+incr (char **s0, size_t *s_len)
+{
+ char *s = *s0;
+ char *endp = s + *s_len - 1;
+
+ do
+ {
+ if ((*endp)++ < '9')
+ return;
+ *endp-- = '0';
+ }
+ while (endp >= s);
+ *--(*s0) = '1';
+ ++*s_len;
+}
+
+/* Compare A and B (each a NUL-terminated digit string), with lengths
+ given by A_LEN and B_LEN. Return +1 if A < B, -1 if B < A, else 0. */
+static int
+cmp (char const *a, size_t a_len, char const *b, size_t b_len)
+{
+ if (a_len < b_len)
+ return -1;
+ if (b_len < a_len)
+ return 1;
+ return (strcmp (a, b));
+}
+
+/* Trim leading 0's from S, but if S is all 0's, leave one.
+ Return a pointer to the trimmed string. */
+static char const * _GL_ATTRIBUTE_PURE
+trim_leading_zeros (char const *s)
+{
+ char const *p = s;
+ while (*s == '0')
+ ++s;
+
+ /* If there were only 0's, back up, to leave one. */
+ if (!*s && s != p)
+ --s;
+ return s;
+}
+
+/* Print all whole numbers from A to B, inclusive -- to stdout, each
+ followed by a newline. If B < A, return false and print nothing.
+ Otherwise, return true. */
+static bool
+seq_fast (char const *a, char const *b)
+{
+ bool inf = STREQ (b, "inf");
+
+ /* Skip past any leading 0's. Without this, our naive cmp
+ function would declare 000 to be larger than 99. */
+ a = trim_leading_zeros (a);
+ b = trim_leading_zeros (b);
+
+ size_t p_len = strlen (a);
+ size_t q_len = inf ? 0 : strlen (b);
+
+ /* Allow for at least 31 digits without realloc.
+ 1 more than p_len is needed for the inf case. */
+ size_t inc_size = MAX (MAX (p_len + 1, q_len), 31);
+
+ /* Copy input strings (incl NUL) to end of new buffers. */
+ char *p0 = xmalloc (inc_size + 1);
+ char *p = memcpy (p0 + inc_size - p_len, a, p_len + 1);
+ char *q;
+ char *q0;
+ if (! inf)
+ {
+ q0 = xmalloc (inc_size + 1);
+ q = memcpy (q0 + inc_size - q_len, b, q_len + 1);
+ }
+ else
+ q = q0 = NULL;
+
+ bool ok = inf || cmp (p, p_len, q, q_len) <= 0;
+ if (ok)
+ {
+ /* Reduce number of fwrite calls which is seen to
+ give a speed-up of more than 2x over the unbuffered code
+ when printing the first 10^9 integers. */
+ size_t buf_size = MAX (BUFSIZ, (inc_size + 1) * 2);
+ char *buf = xmalloc (buf_size);
+ char const *buf_end = buf + buf_size;
+
+ char *bufp = buf;
+
+ /* Write first number to buffer. */
+ bufp = mempcpy (bufp, p, p_len);
+
+ /* Append separator then number. */
+ while (inf || cmp (p, p_len, q, q_len) < 0)
+ {
+ *bufp++ = *separator;
+ incr (&p, &p_len);
+
+ /* Double up the buffers when needed for the inf case. */
+ if (p_len == inc_size)
+ {
+ inc_size *= 2;
+ p0 = xrealloc (p0, inc_size + 1);
+ p = memmove (p0 + p_len, p0, p_len + 1);
+
+ if (buf_size < (inc_size + 1) * 2)
+ {
+ size_t buf_offset = bufp - buf;
+ buf_size = (inc_size + 1) * 2;
+ buf = xrealloc (buf, buf_size);
+ buf_end = buf + buf_size;
+ bufp = buf + buf_offset;
+ }
+ }
+
+ bufp = mempcpy (bufp, p, p_len);
+ /* If no place for another separator + number then
+ output buffer so far, and reset to start of buffer. */
+ if (buf_end - (p_len + 1) < bufp)
+ {
+ fwrite (buf, bufp - buf, 1, stdout);
+ bufp = buf;
+ }
+ }
+
+ /* Write any remaining buffered output, and the terminator. */
+ *bufp++ = *terminator;
+ fwrite (buf, bufp - buf, 1, stdout);
+
+ IF_LINT (free (buf));
+ }
+
+ free (p0);
+ free (q0);
+ return ok;
+}
+
+/* Return true if S consists of at least one digit and no non-digits. */
+static bool _GL_ATTRIBUTE_PURE
+all_digits_p (char const *s)
+{
+ size_t n = strlen (s);
+ return ISDIGIT (s[0]) && n == strspn (s, "0123456789");
+}
+
int
main (int argc, char **argv)
{
@@ -268,12 +527,13 @@ main (int argc, char **argv)
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 = NULL;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -289,60 +549,80 @@ main (int argc, char **argv)
while (optind < argc)
{
if (argv[optind][0] == '-'
- && ((optc = argv[optind][1]) == '.' || ISDIGIT (optc)))
- {
- /* means negative number */
- break;
- }
+ && ((optc = argv[optind][1]) == '.' || ISDIGIT (optc)))
+ {
+ /* means negative number */
+ break;
+ }
optc = getopt_long (argc, argv, "+f:s:w", long_options, NULL);
if (optc == -1)
- break;
+ break;
switch (optc)
- {
- case 'f':
- format_str = optarg;
- break;
+ {
+ case 'f':
+ format_str = optarg;
+ break;
- case 's':
- separator = optarg;
- break;
+ case 's':
+ separator = optarg;
+ break;
- case 'w':
- equal_width = true;
- break;
+ case 'w':
+ equal_width = true;
+ break;
- case_GETOPT_HELP_CHAR;
+ case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- }
+ default:
+ usage (EXIT_FAILURE);
+ }
}
- if (argc - optind < 1)
+ unsigned int n_args = argc - optind;
+ if (n_args < 1)
{
error (0, 0, _("missing operand"));
usage (EXIT_FAILURE);
}
- if (3 < argc - optind)
+ if (3 < n_args)
{
error (0, 0, _("extra operand %s"), quote (argv[optind + 3]));
usage (EXIT_FAILURE);
}
if (format_str)
+ format_str = long_double_format (format_str, &layout);
+
+ if (format_str != NULL && equal_width)
{
- char const *f = long_double_format (format_str);
- if (! f)
- {
- error (0, 0, _("invalid format string: %s"), quote (format_str));
- usage (EXIT_FAILURE);
- }
- format_str = f;
+ error (0, 0, _("format string may not be specified"
+ " when printing equal width strings"));
+ usage (EXIT_FAILURE);
+ }
+
+ /* If the following hold:
+ - no format string, [FIXME: relax this, eventually]
+ - integer start (or no start)
+ - integer end
+ - increment == 1 or not specified [FIXME: relax this, eventually]
+ then use the much more efficient integer-only code. */
+ if (all_digits_p (argv[optind])
+ && (n_args == 1 || all_digits_p (argv[optind + 1]))
+ && (n_args < 3 || (STREQ ("1", argv[optind + 1])
+ && all_digits_p (argv[optind + 2])))
+ && !equal_width && !format_str && strlen (separator) == 1)
+ {
+ char const *s1 = n_args == 1 ? "1" : argv[optind];
+ char const *s2 = argv[optind + (n_args - 1)];
+ if (seq_fast (s1, s2))
+ return EXIT_SUCCESS;
+
+ /* Upon any failure, let the more general code deal with it. */
}
last = scan_arg (argv[optind++]);
@@ -353,23 +633,42 @@ main (int argc, char **argv)
last = scan_arg (argv[optind++]);
if (optind < argc)
- {
- step = last;
- last = scan_arg (argv[optind++]);
- }
+ {
+ step = last;
+ last = scan_arg (argv[optind++]);
+ }
}
- if (format_str != NULL && equal_width)
+ if ((isfinite (first.value) && first.precision == 0)
+ && step.precision == 0 && last.precision == 0
+ && 0 <= first.value && step.value == 1 && 0 <= last.value
+ && !equal_width && !format_str && strlen (separator) == 1)
{
- error (0, 0, _("\
-format string may not be specified when printing equal width strings"));
- usage (EXIT_FAILURE);
+ char *s1;
+ char *s2;
+ 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 ();
+
+ if (*s1 != '-' && *s2 != '-' && seq_fast (s1, s2))
+ {
+ IF_LINT (free (s1));
+ IF_LINT (free (s2));
+ return EXIT_SUCCESS;
+ }
+
+ free (s1);
+ free (s2);
+ /* Upon any failure, let the more general code deal with it. */
}
if (format_str == NULL)
format_str = get_default_format (first, step, last);
- print_numbers (format_str, first.value, step.value, last.value);
+ print_numbers (format_str, layout, first.value, step.value, last.value);
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/set-fields.c b/src/set-fields.c
new file mode 100644
index 0000000..9f05ae9
--- /dev/null
+++ b/src/set-fields.c
@@ -0,0 +1,322 @@
+/* set-fields.c -- common functions for parsing field list
+ Copyright (C) 2015-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Extracted from cut.c by Assaf Gordon */
+
+#include <config.h>
+
+#include "system.h"
+#include "error.h"
+#include "quote.h"
+#include "xstrndup.h"
+#include "set-fields.h"
+
+/* Array of `struct field_range_pair' holding all the finite ranges. */
+struct field_range_pair *frp;
+
+/* Number of finite ranges specified by the user. */
+size_t n_frp;
+
+/* Number of `struct field_range_pair's allocated. */
+static size_t n_frp_allocated;
+
+#define FATAL_ERROR(Message) \
+ do \
+ { \
+ error (0, 0, (Message)); \
+ usage (EXIT_FAILURE); \
+ } \
+ while (0)
+
+/* Append LOW, HIGH to the list RP of range pairs, allocating additional
+ space if necessary. Update global variable N_FRP. When allocating,
+ update global variable N_FRP_ALLOCATED. */
+static void
+add_range_pair (size_t lo, size_t hi)
+{
+ if (n_frp == n_frp_allocated)
+ frp = X2NREALLOC (frp, &n_frp_allocated);
+ frp[n_frp].lo = lo;
+ frp[n_frp].hi = hi;
+ ++n_frp;
+}
+
+
+/* Comparison function for qsort to order the list of
+ struct range_pairs. */
+static int
+compare_ranges (const void *a, const void *b)
+{
+ int a_start = ((const struct field_range_pair *) a)->lo;
+ int b_start = ((const struct field_range_pair *) b)->lo;
+ return a_start < b_start ? -1 : a_start > b_start;
+}
+
+/* Reallocate Range Pair entries, with corresponding
+ entries outside the range of each specified entry. */
+
+static void
+complement_rp (void)
+{
+ struct field_range_pair *c = frp;
+ size_t n = n_frp;
+ size_t i;
+
+ frp = NULL;
+ n_frp = 0;
+ n_frp_allocated = 0;
+
+ if (c[0].lo > 1)
+ add_range_pair (1, c[0].lo - 1);
+
+ for (i = 1; i < n; ++i)
+ {
+ if (c[i-1].hi + 1 == c[i].lo)
+ continue;
+
+ add_range_pair (c[i-1].hi + 1, c[i].lo - 1);
+ }
+
+ if (c[n-1].hi < SIZE_MAX)
+ add_range_pair (c[n-1].hi + 1, SIZE_MAX);
+
+ free (c);
+}
+
+/* Given the list of field or byte range specifications FIELDSTR,
+ allocate and initialize the FRP array. FIELDSTR should
+ be composed of one or more numbers or ranges of numbers, separated
+ by blanks or commas. Incomplete ranges may be given: '-m' means '1-m';
+ 'n-' means 'n' through end of line.
+ n=0 and n>=SIZE_MAX values will trigger an error.
+
+ if SETFLD_ALLOW_DASH option is used, a single '-' means all fields
+ (otherwise a single dash triggers an error).
+
+ if SETFLD_COMPLEMENT option is used, the specified field list
+ is complemented (e.g. '1-3' will result in fields '4-').
+
+ if SETFLD_ERRMSG_USE_POS option is used, error messages
+ will say 'position' (or 'byte/character positions')
+ instead of fields (used with cut -b/-c).
+
+ The function terminates on failure.
+
+ Upon return, the FRP array is initialized to contain
+ a non-overlapping, increasing list of field ranges.
+
+ N_FRP holds the number of field ranges in the FRP array.
+
+ The first field is stored as 1 (zero is not used).
+ An open-ended range (i.e., until the last field of the input line)
+ is indicated with hi = SIZE_MAX.
+
+ A sentinel of SIZE_MAX/SIZE_MAX is always added as the last
+ field range pair.
+
+ Examples:
+ given '1-2,4', frp = [ { .lo = 1, .hi = 2 },
+ { .lo = 4, .hi = 4 },
+ { .lo = SIZE_MAX, .hi = SIZE_MAX } ];
+
+ given '3-', frp = [ { .lo = 3, .hi = SIZE_MAX },
+ { .lo = SIZE_MAX, .hi = SIZE_MAX } ];
+*/
+void
+set_fields (const char *fieldstr, unsigned int options)
+{
+ size_t initial = 1; /* Value of first number in a range. */
+ size_t value = 0; /* If nonzero, a number being accumulated. */
+ bool lhs_specified = false;
+ bool rhs_specified = false;
+ bool dash_found = false; /* True if a '-' is found in this field. */
+
+ size_t i;
+ bool in_digits = false;
+
+ /* Collect and store in RP the range end points. */
+
+ /* Special case: '--field=-' means all fields, emulate '--field=1-' . */
+ if ((options & SETFLD_ALLOW_DASH) && STREQ (fieldstr,"-"))
+ {
+ value = 1;
+ lhs_specified = true;
+ dash_found = true;
+ fieldstr++;
+ }
+
+ while (true)
+ {
+ if (*fieldstr == '-')
+ {
+ in_digits = false;
+ /* Starting a range. */
+ if (dash_found)
+ FATAL_ERROR ( (options & SETFLD_ERRMSG_USE_POS)
+ ?_("invalid byte or character range")
+ :_("invalid field range"));
+
+ dash_found = true;
+ fieldstr++;
+
+ if (lhs_specified && !value)
+ FATAL_ERROR ( (options & SETFLD_ERRMSG_USE_POS)
+ ?_("byte/character positions are numbered from 1")
+ :_("fields are numbered from 1"));
+
+ initial = (lhs_specified ? value : 1);
+ value = 0;
+ }
+ else if (*fieldstr == ','
+ || isblank (to_uchar (*fieldstr)) || *fieldstr == '\0')
+ {
+ in_digits = false;
+ /* Ending the string, or this field/byte sublist. */
+ if (dash_found)
+ {
+ dash_found = false;
+
+ if (!lhs_specified && !rhs_specified)
+ {
+ /* if a lone dash is allowed, emulate '1-' for all fields */
+ if (options & SETFLD_ALLOW_DASH)
+ initial = 1;
+ else
+ FATAL_ERROR (_("invalid range with no endpoint: -"));
+ }
+
+ /* A range. Possibilities: -n, m-n, n-.
+ In any case, 'initial' contains the start of the range. */
+ if (!rhs_specified)
+ {
+ /* 'n-'. From 'initial' to end of line. */
+ add_range_pair (initial, SIZE_MAX);
+ }
+ else
+ {
+ /* 'm-n' or '-n' (1-n). */
+ if (value < initial)
+ FATAL_ERROR (_("invalid decreasing range"));
+
+ add_range_pair (initial, value);
+ }
+ value = 0;
+ }
+ else
+ {
+ /* A simple field number, not a range. */
+ if (value == 0)
+ FATAL_ERROR ( (options & SETFLD_ERRMSG_USE_POS)
+ ?_("byte/character positions are numbered from 1")
+ :_("fields are numbered from 1"));
+
+ add_range_pair (value, value);
+ value = 0;
+ }
+
+ if (*fieldstr == '\0')
+ break;
+
+ fieldstr++;
+ lhs_specified = false;
+ rhs_specified = false;
+ }
+ else if (ISDIGIT (*fieldstr))
+ {
+ /* Record beginning of digit string, in case we have to
+ complain about it. */
+ static char const *num_start;
+ if (!in_digits || !num_start)
+ num_start = fieldstr;
+ in_digits = true;
+
+ if (dash_found)
+ rhs_specified = 1;
+ else
+ lhs_specified = 1;
+
+ /* Detect overflow. */
+ if (!DECIMAL_DIGIT_ACCUMULATE (value, *fieldstr - '0', size_t)
+ || value == SIZE_MAX)
+ {
+ /* In case the user specified -c$(echo 2^64|bc),22,
+ complain only about the first number. */
+ /* Determine the length of the offending number. */
+ size_t len = strspn (num_start, "0123456789");
+ char *bad_num = xstrndup (num_start, len);
+ error (0, 0, (options & SETFLD_ERRMSG_USE_POS)
+ ?_("byte/character offset %s is too large")
+ :_("field number %s is too large"),
+ quote (bad_num));
+ free (bad_num);
+ usage (EXIT_FAILURE);
+ }
+
+ fieldstr++;
+ }
+ else
+ {
+ error (0, 0, (options & SETFLD_ERRMSG_USE_POS)
+ ?_("invalid byte/character position %s")
+ :_("invalid field value %s"),
+ quote (fieldstr));
+ usage (EXIT_FAILURE);
+ }
+ }
+
+ if (!n_frp)
+ FATAL_ERROR ( (options&SETFLD_ERRMSG_USE_POS)
+ ?_("missing list of byte/character positions")
+ :_("missing list of fields"));
+
+ qsort (frp, n_frp, sizeof (frp[0]), compare_ranges);
+
+ /* Merge range pairs (e.g. `2-5,3-4' becomes `2-5'). */
+ for (i = 0; i < n_frp; ++i)
+ {
+ for (size_t j = i + 1; j < n_frp; ++j)
+ {
+ if (frp[j].lo <= frp[i].hi)
+ {
+ frp[i].hi = MAX (frp[j].hi, frp[i].hi);
+ memmove (frp + j, frp + j + 1, (n_frp - j - 1) * sizeof *frp);
+ n_frp--;
+ j--;
+ }
+ else
+ break;
+ }
+ }
+
+ if (options & SETFLD_COMPLEMENT)
+ complement_rp ();
+
+ /* After merging, reallocate RP so we release memory to the system.
+ Also add a sentinel at the end of RP, to avoid out of bounds access
+ and for performance reasons. */
+ ++n_frp;
+ frp = xrealloc (frp, n_frp * sizeof (struct field_range_pair));
+ frp[n_frp - 1].lo = frp[n_frp - 1].hi = SIZE_MAX;
+}
+
+void
+reset_fields (void)
+{
+ n_frp = 0 ;
+ n_frp_allocated = 0;
+ free (frp);
+ frp = NULL;
+}
diff --git a/src/set-fields.h b/src/set-fields.h
new file mode 100644
index 0000000..c948947
--- /dev/null
+++ b/src/set-fields.h
@@ -0,0 +1,47 @@
+/* set-fields.h -- parse field list argument
+
+ Copyright (C) 2015-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+#ifndef SET_FIELDS_H
+# define SET_FIELDS_H
+
+struct field_range_pair
+ {
+ size_t lo;
+ size_t hi;
+ };
+
+/* Array of `struct range_pair' holding all the finite ranges. */
+extern struct field_range_pair *frp;
+
+/* Number of finite ranges specified by the user. */
+extern size_t n_frp;
+
+/* field list parsing options */
+enum
+{
+ SETFLD_ALLOW_DASH = 0x01, /* allow single dash meaning 'all fields' */
+ SETFLD_COMPLEMENT = 0x02, /* complement the field list */
+ SETFLD_ERRMSG_USE_POS = 0x04 /* when reporting errors, say 'position' instead
+ of 'field' (used with cut -b/-c) */
+};
+
+/* allocates and initializes the FRP array and N_FRP count */
+extern void set_fields (const char *fieldstr, unsigned int options);
+
+/* frees memory allocated by set_fields() */
+extern void reset_fields (void);
+
+#endif
diff --git a/src/setuidgid.c b/src/setuidgid.c
deleted file mode 100644
index 3a51225..0000000
--- a/src/setuidgid.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/* setuidgid - run a command with the UID and GID of a specified user
- Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
-
- This 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 2, 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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
-
-/* Written by Jim Meyering */
-
-#include <config.h>
-#include <getopt.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <pwd.h>
-#include <grp.h>
-
-#include "system.h"
-
-#include "error.h"
-#include "long-options.h"
-#include "quote.h"
-
-#define PROGRAM_NAME "setuidgid"
-
-/* I wrote this program from scratch, based on the description of
- D.J. Bernstein's program: http://cr.yp.to/daemontools/setuidgid.html. */
-#define AUTHORS "Jim Meyering"
-
-#define SETUIDGID_FAILURE 111
-
-char *program_name;
-
-void
-usage (int status)
-{
- if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
- else
- {
- printf (_("\
-Usage: %s USERNAME COMMAND [ARGUMENT]...\n\
- or: %s OPTION\n\
-"),
- program_name, program_name);
-
- fputs (_("\
-Drop any supplemental groups, assume the user-ID and group-ID of\n\
-the specified USERNAME, and run COMMAND with any specified ARGUMENTs.\n\
-Exit with status 111 if unable to assume the required user and group ID.\n\
-Otherwise, exit with the exit status of COMMAND.\n\
-This program is useful only when run by root (user ID zero).\n\
-\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
- }
- exit (status);
-}
-
-int
-main (int argc, char **argv)
-{
- char const *user_id;
- struct passwd *pwd;
-
- initialize_main (&argc, &argv);
- program_name = argv[0];
- setlocale (LC_ALL, "");
- bindtextdomain (PACKAGE, LOCALEDIR);
- textdomain (PACKAGE);
-
- initialize_exit_failure (SETUIDGID_FAILURE);
- atexit (close_stdout);
-
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
- if (getopt_long (argc, argv, "+", NULL, NULL) != -1)
- usage (SETUIDGID_FAILURE);
-
- if (argc <= optind + 1)
- {
- if (argc < optind + 1)
- error (0, 0, _("missing operand"));
- else
- error (0, 0, _("missing operand after %s"), quote (argv[optind]));
- usage (SETUIDGID_FAILURE);
- }
-
- user_id = argv[optind];
- pwd = getpwnam (user_id);
- if (pwd == NULL)
- error (SETUIDGID_FAILURE, errno,
- _("unknown user-ID: %s"), quote (user_id));
-
-#if HAVE_SETGROUPS
- if (setgroups (1, &pwd->pw_gid))
- error (SETUIDGID_FAILURE, errno, _("cannot set supplemental group"));
-#endif
-
- if (setgid (pwd->pw_gid))
- error (SETUIDGID_FAILURE, errno,
- _("cannot set group-ID to %lu"), (unsigned long int) pwd->pw_gid);
-
- if (setuid (pwd->pw_uid))
- error (SETUIDGID_FAILURE, errno,
- _("cannot set user-ID to %lu"), (unsigned long int) pwd->pw_uid);
-
- {
- char **cmd = argv + optind + 1;
- int exit_status;
- execvp (*cmd, cmd);
- exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
-
- error (0, errno, _("cannot run command %s"), quote (*cmd));
- exit (exit_status);
- }
-}
diff --git a/src/shred.c b/src/shred.c
index 23a4944..ba93f80 100644
--- a/src/shred.c
+++ b/src/shred.c
@@ -1,12 +1,12 @@
/* shred.c - overwrite files and devices to make it harder to recover data
- Copyright (C) 1999-2006 Free Software Foundation, Inc.
+ Copyright (C) 1999-2016 Free Software Foundation, Inc.
Copyright (C) 1997, 1998, 1999 Colin Plumb.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -14,22 +14,10 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
Written by Colin Plumb. */
-/* TODO:
- - use consistent non-capitalization in error messages
- - add standard GNU copyleft comment
-
- - Add -r/-R/--recursive
- - Add -i/--interactive
- - Reserve -d
- - Add -L
- - Add an unlink-all option to emulate rm.
- */
-
/*
* Do a more secure overwrite of given files or devices, to make it harder
* for even very expensive hardware probing to recover the data.
@@ -80,10 +68,10 @@
* drastically bad if told to attack a named pipe or socket?
*/
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "shred"
-#define AUTHORS "Colin Plumb"
+#define AUTHORS proper_name ("Colin Plumb")
#include <config.h>
@@ -92,21 +80,22 @@
#include <assert.h>
#include <setjmp.h>
#include <sys/types.h>
+#ifdef __linux__
+# include <sys/mtio.h>
+#endif
#include "system.h"
-#include "xstrtol.h"
+#include "argmatch.h"
+#include "xdectoint.h"
#include "error.h"
#include "fcntl--.h"
-#include "getpagesize.h"
#include "human.h"
-#include "inttostr.h"
-#include "quotearg.h" /* For quotearg_colon */
-#include "quote.h" /* For quotearg_colon */
#include "randint.h"
#include "randread.h"
+#include "stat-size.h"
/* Default number of times to overwrite. */
-enum { DEFAULT_PASSES = 25 };
+enum { DEFAULT_PASSES = 3 };
/* How many seconds to wait before checking whether to output another
verbose output line. */
@@ -118,12 +107,30 @@ enum { SECTOR_SIZE = 512 };
enum { SECTOR_MASK = SECTOR_SIZE - 1 };
verify (0 < SECTOR_SIZE && (SECTOR_SIZE & SECTOR_MASK) == 0);
+enum remove_method
+{
+ remove_none = 0, /* the default: only wipe data. */
+ remove_unlink, /* don't obfuscate name, just unlink. */
+ remove_wipe, /* obfuscate name before unlink. */
+ remove_wipesync /* obfuscate name, syncing each byte, before unlink. */
+};
+
+static char const *const remove_args[] =
+{
+ "unlink", "wipe", "wipesync", NULL
+};
+
+static enum remove_method const remove_methods[] =
+{
+ remove_unlink, remove_wipe, remove_wipesync
+};
+
struct Options
{
bool force; /* -f flag: chmod files if necessary */
size_t n_iterations; /* -n flag: Number of iterations */
off_t size; /* -s flag: size of file */
- bool remove_file; /* -u flag: remove file after shredding */
+ enum remove_method remove_file; /* -u flag: remove file after shredding */
bool verbose; /* -v flag: Print progress */
bool exact; /* -x flag: Do not round up file size */
bool zero_fill; /* -z flag: Add a final zero pass */
@@ -143,7 +150,7 @@ static struct option const long_opts[] =
{"iterations", required_argument, NULL, 'n'},
{"size", required_argument, NULL, 's'},
{"random-source", required_argument, NULL, RANDOM_SOURCE_OPTION},
- {"remove", no_argument, NULL, 'u'},
+ {"remove", optional_argument, NULL, 'u'},
{"verbose", no_argument, NULL, 'v'},
{"zero", no_argument, NULL, 'z'},
{GETOPT_HELP_OPTION_DECL},
@@ -151,34 +158,34 @@ static struct option const long_opts[] =
{NULL, 0, NULL, 0}
};
-/* Global variable for error printing purposes */
-char const *program_name; /* Initialized before any possible use */
-
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
- printf (_("Usage: %s [OPTIONS] FILE [...]\n"), program_name);
+ printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
fputs (_("\
Overwrite the specified FILE(s) repeatedly, in order to make it harder\n\
for even very expensive hardware probing to recover the data.\n\
-\n\
"), stdout);
fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
+\n\
+If FILE is -, shred standard output.\n\
"), stdout);
+
+ 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 (default /dev/urandom)\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\
"), DEFAULT_PASSES);
fputs (_("\
- -u, --remove truncate and remove file after overwriting\n\
+ -u truncate 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\
@@ -188,12 +195,14 @@ Mandatory arguments to long options are mandatory for short options too.\n\
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
\n\
-If FILE is -, shred standard output.\n\
-\n\
Delete FILE(s) if --remove (-u) is specified. The default is not to remove\n\
the files because it is common to operate on device files like /dev/hda,\n\
-and those files usually should not be removed. When operating on regular\n\
-files, most people use the --remove option.\n\
+and those files usually should not be removed.\n\
+The optional HOW parameter indicates how to remove a directory entry:\n\
+'unlink' => use a standard unlink call.\n\
+'wipe' => also first obfuscate bytes in the name.\n\
+'wipesync' => also sync each obfuscated byte to disk.\n\
+The default mode is 'wipesync', but note it can be expensive.\n\
\n\
"), stdout);
fputs (_("\
@@ -236,11 +245,30 @@ In addition, file system backups and remote mirrors may contain copies\n\
of the file that cannot be removed, and that will allow a shredded file\n\
to be recovered later.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
+/*
+ * Determine if pattern type is periodic or not.
+ */
+static bool
+periodic_pattern (int type)
+{
+ if (type <= 0)
+ return false;
+
+ unsigned char r[3];
+ unsigned int bits = type & 0xfff;
+
+ bits |= bits << 12;
+ r[0] = (bits >> 4) & 255;
+ r[1] = (bits >> 8) & 255;
+ r[2] = bits & 255;
+
+ return (r[0] != r[1]) || (r[0] != r[2]);
+}
/*
* Fill a buffer with a fixed pattern.
@@ -283,6 +311,17 @@ passname (unsigned char const *data, char name[PASS_NAME_SIZE])
memcpy (name, "random", PASS_NAME_SIZE);
}
+/* Return true when it's ok to ignore an fsync or fdatasync
+ failure that set errno to ERRNO_VAL. */
+static bool
+ignorable_sync_errno (int errno_val)
+{
+ return (errno_val == EINVAL
+ || errno_val == EBADF
+ /* HP-UX does this */
+ || errno_val == EISDIR);
+}
+
/* Request that all data for FD be transferred to the corresponding
storage device. QNAME is the file name (quoted for colons).
Report any errors found. Return 0 on success, -1
@@ -298,7 +337,7 @@ dosync (int fd, char const *qname)
if (fdatasync (fd) == 0)
return 0;
err = errno;
- if (err != EINVAL && err != EBADF)
+ if ( ! ignorable_sync_errno (err))
{
error (0, err, _("%s: fdatasync failed"), qname);
errno = err;
@@ -309,7 +348,7 @@ dosync (int fd, char const *qname)
if (fsync (fd) == 0)
return 0;
err = errno;
- if (err != EINVAL && err != EBADF)
+ if ( ! ignorable_sync_errno (err))
{
error (0, err, _("%s: fsync failed"), qname);
errno = err;
@@ -329,13 +368,13 @@ direct_mode (int fd, bool enable)
{
int fd_flags = fcntl (fd, F_GETFL);
if (0 < fd_flags)
- {
- int new_flags = (enable
- ? (fd_flags | O_DIRECT)
- : (fd_flags & ~O_DIRECT));
- if (new_flags != fd_flags)
- fcntl (fd, F_SETFL, new_flags);
- }
+ {
+ int new_flags = (enable
+ ? (fd_flags | O_DIRECT)
+ : (fd_flags & ~O_DIRECT));
+ if (new_flags != fd_flags)
+ fcntl (fd, F_SETFL, new_flags);
+ }
}
#if HAVE_DIRECTIO && defined DIRECTIO_ON && defined DIRECTIO_OFF
@@ -345,59 +384,101 @@ direct_mode (int fd, bool enable)
#endif
}
+/* Rewind FD; its status is ST. */
+static bool
+dorewind (int fd, struct stat const *st)
+{
+ if (S_ISCHR (st->st_mode))
+ {
+#ifdef __linux__
+ /* In the Linux kernel, lseek does not work on tape devices; it
+ returns a randomish value instead. Try the low-level tape
+ rewind operation first. */
+ struct mtop op;
+ op.mt_op = MTREW;
+ op.mt_count = 1;
+ if (ioctl (fd, MTIOCTOP, &op) == 0)
+ return true;
+#endif
+ }
+ off_t offset = lseek (fd, 0, SEEK_SET);
+ if (0 < offset)
+ errno = EINVAL;
+ return offset == 0;
+}
+
+/* By convention, negative sizes represent unknown values. */
+
+static bool
+known (off_t size)
+{
+ return 0 <= size;
+}
+
/*
- * Do pass number k of n, writing "size" bytes of the given pattern "type"
- * to the file descriptor fd. Qname, k and n are passed in only for verbose
- * progress message purposes. If n == 0, no progress messages are printed.
+ * Do pass number K of N, writing *SIZEP bytes of the given pattern TYPE
+ * to the file descriptor FD. K and N are passed in only for verbose
+ * progress message purposes. If N == 0, no progress messages are printed.
*
- * If *sizep == -1, the size is unknown, and it will be filled in as soon
- * as writing fails.
+ * If *SIZEP == -1, the size is unknown, and it will be filled in as soon
+ * as writing fails with ENOSPC.
*
* Return 1 on write error, -1 on other error, 0 on success.
*/
static int
-dopass (int fd, char const *qname, off_t *sizep, int type,
- struct randread_source *s, unsigned long int k, unsigned long int n)
+dopass (int fd, struct stat const *st, char const *qname, off_t *sizep,
+ int type, struct randread_source *s,
+ unsigned long int k, unsigned long int n)
{
off_t size = *sizep;
- off_t offset; /* Current file posiiton */
- time_t thresh IF_LINT (= 0); /* Time to maybe print next status update */
+ 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 */
- /* Fill pattern buffer. Aligning it to a 32-bit boundary speeds up randread
- in some cases. */
- typedef uint32_t fill_pattern_buffer[3 * 1024];
- union
- {
- fill_pattern_buffer buffer;
- char c[sizeof (fill_pattern_buffer)];
- unsigned char u[sizeof (fill_pattern_buffer)];
- } r;
-
- off_t sizeof_r = sizeof r;
+ /* Fill pattern buffer. Aligning it to a page so we can do direct I/O. */
+ size_t page_size = getpagesize ();
+#define PERIODIC_OUTPUT_SIZE (60 * 1024)
+#define NONPERIODIC_OUTPUT_SIZE (64 * 1024)
+ verify (PERIODIC_OUTPUT_SIZE % 3 == 0);
+ size_t output_size = periodic_pattern (type)
+ ? PERIODIC_OUTPUT_SIZE : NONPERIODIC_OUTPUT_SIZE;
+#define PAGE_ALIGN_SLOP (page_size - 1) /* So directio works */
+#define FILLPATTERN_SIZE (((output_size + 2) / 3) * 3) /* Multiple of 3 */
+#define PATTERNBUF_SIZE (PAGE_ALIGN_SLOP + FILLPATTERN_SIZE)
+ void *fill_pattern_mem = xmalloc (PATTERNBUF_SIZE);
+ unsigned char *pbuf = ptr_align (fill_pattern_mem, page_size);
+
char pass_string[PASS_NAME_SIZE]; /* Name of current pass */
bool write_error = false;
- bool first_write = true;
+ bool other_error = false;
/* Printable previous offset into the file */
char previous_offset_buf[LONGEST_HUMAN_READABLE + 1];
- char const *previous_human_offset IF_LINT (= 0);
+ char const *previous_human_offset IF_LINT ( = 0);
+
+ /* As a performance tweak, avoid direct I/O for small sizes,
+ as it's just a performance rather then 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)
+ direct_mode (fd, true);
- if (lseek (fd, 0, SEEK_SET) == -1)
+ if (! dorewind (fd, st))
{
error (0, errno, _("%s: cannot rewind"), qname);
- return -1;
+ other_error = true;
+ goto free_pattern_mem;
}
/* Constant fill patterns need only be set up once. */
if (type >= 0)
{
- lim = (0 <= size && size < sizeof_r ? size : sizeof r);
- fillpattern (type, r.u, lim);
- passname (r.u, pass_string);
+ lim = known (size) && size < FILLPATTERN_SIZE ? size : FILLPATTERN_SIZE;
+ fillpattern (type, pbuf, lim);
+ passname (pbuf, pass_string);
}
else
{
@@ -413,153 +494,169 @@ dopass (int fd, char const *qname, off_t *sizep, int type,
}
offset = 0;
- for (;;)
+ while (true)
{
/* How much to write this time? */
- lim = sizeof r;
- if (0 <= size && size - offset < sizeof_r)
- {
- if (size < offset)
- break;
- lim = size - offset;
- if (!lim)
- break;
- }
+ lim = output_size;
+ if (known (size) && size - offset < output_size)
+ {
+ if (size < offset)
+ break;
+ lim = size - offset;
+ if (!lim)
+ break;
+ }
if (type < 0)
- randread (s, &r, lim);
+ randread (s, pbuf, lim);
/* Loop to retry partial writes. */
- for (soff = 0; soff < lim; soff += ssize, first_write = false)
- {
- ssize = write (fd, r.c + soff, lim - soff);
- if (ssize <= 0)
- {
- if (size < 0 && (ssize == 0 || errno == ENOSPC))
- {
- /* Ah, we have found the end of the file */
- *sizep = size = offset + soff;
- break;
- }
- else
- {
- int errnum = errno;
- char buf[INT_BUFSIZE_BOUND (uintmax_t)];
-
- /* If the first write of the first pass for a given file
- has just failed with EINVAL, turn off direct mode I/O
- and try again. This works around a bug in linux-2.4
- whereby opening with O_DIRECT would succeed for some
- file system types (e.g., ext3), but any attempt to
- access a file through the resulting descriptor would
- fail with EINVAL. */
- if (k == 1 && first_write && errno == EINVAL)
- {
- direct_mode (fd, false);
- ssize = 0;
- continue;
- }
- error (0, errnum, _("%s: error writing at offset %s"),
- qname, umaxtostr (offset + soff, buf));
-
- /* 'shred' is often used on bad media, before throwing it
- out. Thus, it shouldn't give up on bad blocks. This
- code works because lim is always a multiple of
- SECTOR_SIZE, except at the end. */
- verify (sizeof r % SECTOR_SIZE == 0);
- if (errnum == EIO && 0 <= size && (soff | SECTOR_MASK) < lim)
- {
- size_t soff1 = (soff | SECTOR_MASK) + 1;
- if (lseek (fd, offset + soff1, SEEK_SET) != -1)
- {
- /* Arrange to skip this block. */
- ssize = soff1 - soff;
- write_error = true;
- continue;
- }
- error (0, errno, _("%s: lseek failed"), qname);
- }
- return -1;
- }
- }
- }
+ for (soff = 0; soff < lim; soff += ssize)
+ {
+ ssize = write (fd, pbuf + soff, lim - soff);
+ if (0 < ssize)
+ assume (ssize <= lim - soff);
+ else
+ {
+ if (! known (size) && (ssize == 0 || errno == ENOSPC))
+ {
+ /* We have found the end of the file. */
+ if (soff <= OFF_T_MAX - offset)
+ *sizep = size = offset + soff;
+ break;
+ }
+ else
+ {
+ int errnum = errno;
+ char buf[INT_BUFSIZE_BOUND (uintmax_t)];
+
+ /* Retry without direct I/O since this may not be supported
+ at all on some (file) systems, or with the current size.
+ I.e., a specified --size that is not aligned, or when
+ dealing with slop at the end of a file with --exact. */
+ if (! try_without_directio && errno == EINVAL)
+ {
+ direct_mode (fd, false);
+ ssize = 0;
+ try_without_directio = true;
+ continue;
+ }
+ error (0, errnum, _("%s: error writing at offset %s"),
+ qname, umaxtostr (offset + soff, buf));
+
+ /* 'shred' is often used on bad media, before throwing it
+ out. Thus, it shouldn't give up on bad blocks. This
+ code works because lim is always a multiple of
+ SECTOR_SIZE, except at the end. This size constraint
+ also enables direct I/O on some (file) systems. */
+ verify (PERIODIC_OUTPUT_SIZE % SECTOR_SIZE == 0);
+ verify (NONPERIODIC_OUTPUT_SIZE % SECTOR_SIZE == 0);
+ if (errnum == EIO && known (size)
+ && (soff | SECTOR_MASK) < lim)
+ {
+ size_t soff1 = (soff | SECTOR_MASK) + 1;
+ if (lseek (fd, offset + soff1, SEEK_SET) != -1)
+ {
+ /* Arrange to skip this block. */
+ ssize = soff1 - soff;
+ write_error = true;
+ continue;
+ }
+ error (0, errno, _("%s: lseek failed"), qname);
+ }
+ other_error = true;
+ goto free_pattern_mem;
+ }
+ }
+ }
/* Okay, we have written "soff" bytes. */
- if (offset + soff < offset)
- {
- error (0, 0, _("%s: file too large"), qname);
- return -1;
- }
+ if (OFF_T_MAX - offset < soff)
+ {
+ error (0, 0, _("%s: file too large"), qname);
+ other_error = true;
+ goto free_pattern_mem;
+ }
offset += soff;
+ bool done = offset == size;
+
/* Time to print progress? */
- if (n
- && ((offset == size && *previous_human_offset)
- || thresh <= (now = time (NULL))))
- {
- char offset_buf[LONGEST_HUMAN_READABLE + 1];
- char size_buf[LONGEST_HUMAN_READABLE + 1];
- int human_progress_opts = (human_autoscale | human_SI
- | human_base_1024 | human_B);
- char const *human_offset
- = human_readable (offset, offset_buf,
- human_floor | human_progress_opts, 1, 1);
-
- if (offset == size
- || !STREQ (previous_human_offset, human_offset))
- {
- if (size < 0)
- error (0, 0, _("%s: pass %lu/%lu (%s)...%s"),
- qname, k, n, pass_string, human_offset);
- else
- {
- uintmax_t off = offset;
- int percent = (size == 0
- ? 100
- : (off <= TYPE_MAXIMUM (uintmax_t) / 100
- ? off * 100 / size
- : off / (size / 100)));
- char const *human_size
- = human_readable (size, size_buf,
- human_ceiling | human_progress_opts,
- 1, 1);
- if (offset == size)
- human_offset = human_size;
- error (0, 0, _("%s: pass %lu/%lu (%s)...%s/%s %d%%"),
- qname, k, n, pass_string, human_offset, human_size,
- percent);
- }
-
- strcpy (previous_offset_buf, human_offset);
- previous_human_offset = previous_offset_buf;
- thresh = now + VERBOSE_UPDATE;
-
- /*
- * Force periodic syncs to keep displayed progress accurate
- * FIXME: Should these be present even if -v is not enabled,
- * to keep the buffer cache from filling with dirty pages?
- * It's a common problem with programs that do lots of writes,
- * like mkfs.
- */
- if (dosync (fd, qname) != 0)
- {
- if (errno != EIO)
- return -1;
- write_error = true;
- }
- }
- }
+ if (n && ((done && *previous_human_offset)
+ || thresh <= (now = time (NULL))))
+ {
+ char offset_buf[LONGEST_HUMAN_READABLE + 1];
+ char size_buf[LONGEST_HUMAN_READABLE + 1];
+ int human_progress_opts = (human_autoscale | human_SI
+ | human_base_1024 | human_B);
+ char const *human_offset
+ = human_readable (offset, offset_buf,
+ human_floor | human_progress_opts, 1, 1);
+
+ if (done || !STREQ (previous_human_offset, human_offset))
+ {
+ if (! known (size))
+ error (0, 0, _("%s: pass %lu/%lu (%s)...%s"),
+ qname, k, n, pass_string, human_offset);
+ else
+ {
+ uintmax_t off = offset;
+ int percent = (size == 0
+ ? 100
+ : (off <= TYPE_MAXIMUM (uintmax_t) / 100
+ ? off * 100 / size
+ : off / (size / 100)));
+ char const *human_size
+ = human_readable (size, size_buf,
+ human_ceiling | human_progress_opts,
+ 1, 1);
+ if (done)
+ human_offset = human_size;
+ error (0, 0, _("%s: pass %lu/%lu (%s)...%s/%s %d%%"),
+ qname, k, n, pass_string, human_offset, human_size,
+ percent);
+ }
+
+ strcpy (previous_offset_buf, human_offset);
+ previous_human_offset = previous_offset_buf;
+ thresh = now + VERBOSE_UPDATE;
+
+ /*
+ * Force periodic syncs to keep displayed progress accurate
+ * FIXME: Should these be present even if -v is not enabled,
+ * to keep the buffer cache from filling with dirty pages?
+ * It's a common problem with programs that do lots of writes,
+ * like mkfs.
+ */
+ if (dosync (fd, qname) != 0)
+ {
+ if (errno != EIO)
+ {
+ other_error = true;
+ goto free_pattern_mem;
+ }
+ write_error = true;
+ }
+ }
+ }
}
/* Force what we just wrote to hit the media. */
if (dosync (fd, qname) != 0)
{
if (errno != EIO)
- return -1;
+ {
+ other_error = true;
+ goto free_pattern_mem;
+ }
write_error = true;
}
- return write_error;
+free_pattern_mem:
+ memset (pbuf, 0, FILLPATTERN_SIZE);
+ free (fill_pattern_mem);
+
+ return other_error ? -1 : write_error;
}
/*
@@ -627,7 +724,7 @@ static int const
12, 0x111, 0x222, 0x333, 0x444, 0x666, 0x777,
0x888, 0x999, 0xBBB, 0xCCC, 0xDDD, 0xEEE, /* 4-bit */
-1, /* 1 random pass */
- /* The following patterns have the frst bit per block flipped */
+ /* The following patterns have the first bit per block flipped */
8, 0x1000, 0x1249, 0x1492, 0x16DB, 0x1924, 0x1B6D, 0x1DB6, 0x1FFF,
14, 0x1111, 0x1222, 0x1333, 0x1444, 0x1555, 0x1666, 0x1777,
0x1888, 0x1999, 0x1AAA, 0x1BBB, 0x1CCC, 0x1DDD, 0x1EEE,
@@ -660,51 +757,51 @@ genpattern (int *dest, size_t num, struct randint_source *s)
d = dest; /* Destination for generated pass list */
n = num; /* Passes remaining to fill */
- for (;;)
+ while (true)
{
k = *p++; /* Block descriptor word */
if (!k)
- { /* Loop back to the beginning */
- p = patterns;
- }
+ { /* Loop back to the beginning */
+ p = patterns;
+ }
else if (k < 0)
- { /* -k random passes */
- k = -k;
- if ((size_t) k >= n)
- {
- randpasses += n;
- n = 0;
- break;
- }
- randpasses += k;
- n -= k;
- }
+ { /* -k random passes */
+ k = -k;
+ if ((size_t) k >= n)
+ {
+ randpasses += n;
+ break;
+ }
+ randpasses += k;
+ n -= k;
+ }
else if ((size_t) k <= n)
- { /* Full block of patterns */
- memcpy (d, p, k * sizeof (int));
- p += k;
- d += k;
- n -= k;
- }
+ { /* Full block of patterns */
+ memcpy (d, p, k * sizeof (int));
+ p += k;
+ d += k;
+ n -= k;
+ }
else if (n < 2 || 3 * n < (size_t) k)
- { /* Finish with random */
- randpasses += n;
- break;
- }
+ { /* Finish with random */
+ randpasses += n;
+ break;
+ }
else
- { /* Pad out with k of the n available */
- do
- {
- if (n == (size_t) k || randint_choose (s, k) < n)
- {
- *d++ = *p;
- n--;
- }
- p++;
- }
- while (n);
- break;
- }
+ { /* Pad out with n of the k available */
+ do
+ {
+ if (n == (size_t) k || randint_choose (s, k) < n)
+ {
+ *d++ = *p;
+ n--;
+ }
+ p++;
+ k--;
+ }
+ while (n);
+ break;
+ }
}
top = num - randpasses; /* Top of initialized data */
/* assert (d == dest+top); */
@@ -734,18 +831,18 @@ genpattern (int *dest, size_t num, struct randint_source *s)
for (n = 0; n < num; n++)
{
if (accum <= randpasses)
- {
- accum += num - 1;
- dest[top++] = dest[n];
- dest[n] = -1;
- }
+ {
+ accum += num - 1;
+ dest[top++] = dest[n];
+ dest[n] = -1;
+ }
else
- {
- swap = n + randint_choose (s, top - n);
- k = dest[n];
- dest[n] = dest[swap];
- dest[swap] = k;
- }
+ {
+ swap = n + randint_choose (s, top - n);
+ k = dest[n];
+ dest[n] = dest[swap];
+ dest[swap] = k;
+ }
accum -= randpasses;
}
/* assert (top == num); */
@@ -757,17 +854,18 @@ genpattern (int *dest, size_t num, struct randint_source *s)
*/
static bool
do_wipefd (int fd, char const *qname, struct randint_source *s,
- struct Options const *flags)
+ struct Options const *flags)
{
size_t i;
struct stat st;
- off_t size; /* Size to write, size to read */
- unsigned long int n; /* Number of passes for printing purposes */
+ off_t size; /* Size to write, size to read */
+ off_t i_size = 0; /* For small files, initial size to overwrite inode */
+ unsigned long int n; /* Number of passes for printing purposes */
int *passarray;
bool ok = true;
struct randread_source *rs;
- n = 0; /* dopass takes n -- 0 to mean "don't print progress" */
+ n = 0; /* dopass takes n == 0 to mean "don't print progress" */
if (flags->verbose)
n = flags->n_iterations + flags->zero_fill;
@@ -778,7 +876,7 @@ do_wipefd (int fd, char const *qname, struct randint_source *s,
}
/* If we know that we can't possibly shred the file, give up now.
- Otherwise, we may go into a infinite loop writing data before we
+ Otherwise, we may go into an infinite loop writing data before we
find that we can't rewind the device. */
if ((S_ISCHR (st.st_mode) && isatty (fd))
|| S_ISFIFO (st.st_mode)
@@ -787,8 +885,11 @@ do_wipefd (int fd, char const *qname, struct randint_source *s,
error (0, 0, _("%s: invalid file type"), qname);
return false;
}
-
- direct_mode (fd, true);
+ else if (S_ISREG (st.st_mode) && st.st_size < 0)
+ {
+ error (0, 0, _("%s: file has negative size"), qname);
+ return false;
+ }
/* Allocate pass array */
passarray = xnmalloc (flags->n_iterations, sizeof *passarray);
@@ -796,91 +897,105 @@ do_wipefd (int fd, char const *qname, struct randint_source *s,
size = flags->size;
if (size == -1)
{
- /* Accept a length of zero only if it's a regular file.
- For any other type of file, try to get the size another way. */
if (S_ISREG (st.st_mode))
- {
- size = st.st_size;
- if (size < 0)
- {
- error (0, 0, _("%s: file has negative size"), qname);
- return false;
- }
- }
+ {
+ size = st.st_size;
+
+ if (! flags->exact)
+ {
+ /* Round up to the nearest block size to clear slack space. */
+ off_t remainder = size % ST_BLKSIZE (st);
+ if (size && size < ST_BLKSIZE (st))
+ i_size = size;
+ if (remainder != 0)
+ {
+ off_t size_incr = ST_BLKSIZE (st) - remainder;
+ size += MIN (size_incr, OFF_T_MAX - size);
+ }
+ }
+ }
else
- {
- size = lseek (fd, 0, SEEK_END);
- if (size <= 0)
- {
- /* We are unable to determine the length, up front.
- Let dopass do that as part of its first iteration. */
- size = -1;
- }
- }
-
- /* Allow `rounding up' only for regular files. */
- if (0 <= size && !(flags->exact) && S_ISREG (st.st_mode))
- {
- size += ST_BLKSIZE (st) - 1 - (size - 1) % ST_BLKSIZE (st);
-
- /* If in rounding up, we've just overflowed, use the maximum. */
- if (size < 0)
- size = TYPE_MAXIMUM (off_t);
- }
+ {
+ /* The behavior of lseek is unspecified, but in practice if
+ it returns a positive number that's the size of this
+ device. */
+ size = lseek (fd, 0, SEEK_END);
+ if (size <= 0)
+ {
+ /* We are unable to determine the length, up front.
+ Let dopass do that as part of its first iteration. */
+ size = -1;
+ }
+ }
}
+ else if (S_ISREG (st.st_mode)
+ && st.st_size < MIN (ST_BLKSIZE (st), size))
+ i_size = st.st_size;
/* Schedule the passes in random order. */
genpattern (passarray, flags->n_iterations, s);
rs = randint_get_source (s);
- /* Do the work */
- for (i = 0; i < flags->n_iterations; i++)
+ while (true)
{
- int err = dopass (fd, qname, &size, passarray[i], rs, i + 1, n);
- if (err)
- {
- if (err < 0)
- {
- memset (passarray, 0, flags->n_iterations * sizeof (int));
- free (passarray);
- return false;
- }
- ok = false;
- }
- }
-
- memset (passarray, 0, flags->n_iterations * sizeof (int));
- free (passarray);
-
- if (flags->zero_fill)
- {
- int err = dopass (fd, qname, &size, 0, rs, flags->n_iterations + 1, n);
- if (err)
- {
- if (err < 0)
- return false;
- ok = false;
- }
+ off_t pass_size;
+ unsigned long int pn = n;
+
+ if (i_size)
+ {
+ pass_size = i_size;
+ i_size = 0;
+ pn = 0;
+ }
+ else if (size)
+ {
+ pass_size = size;
+ size = 0;
+ }
+ /* TODO: consider handling tail packing by
+ writing the tail padding as a separate pass,
+ (that would not rewind). */
+ else
+ break;
+
+ for (i = 0; i < flags->n_iterations + flags->zero_fill; i++)
+ {
+ int err = 0;
+ int type = i < flags->n_iterations ? passarray[i] : 0;
+
+ err = dopass (fd, &st, qname, &pass_size, type, rs, i + 1, pn);
+
+ if (err)
+ {
+ ok = false;
+ if (err < 0)
+ goto wipefd_out;
+ }
+ }
}
- /* Okay, now deallocate the data. The effect of ftruncate on
+ /* Now deallocate the data. The effect of ftruncate on
non-regular files is unspecified, so don't worry about any
errors reported for them. */
if (flags->remove_file && ftruncate (fd, 0) != 0
&& S_ISREG (st.st_mode))
{
error (0, errno, _("%s: error truncating"), qname);
- return false;
+ ok = false;
+ goto wipefd_out;
}
+wipefd_out:
+ memset (passarray, 0, flags->n_iterations * sizeof (int));
+ free (passarray);
return ok;
}
/* A wrapper with a little more checking for fds on the command line */
static bool
wipefd (int fd, char const *qname, struct randint_source *s,
- struct Options const *flags)
+ struct Options const *flags)
{
int fd_flags = fcntl (fd, F_GETFL);
@@ -904,9 +1019,9 @@ static char const nameset[] =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_.";
/* Increment NAME (with LEN bytes). NAME must be a big-endian base N
- number with the digits taken from nameset. Return true if
- successful if not (because NAME already has the greatest possible
- value. */
+ number with the digits taken from nameset. Return true if successful.
+ Otherwise, (because NAME already has the greatest possible value)
+ return false. */
static bool
incname (char *name, size_t len)
@@ -915,12 +1030,16 @@ incname (char *name, size_t len)
{
char const *p = strchr (nameset, name[len]);
+ /* Given that NAME is composed of bytes from NAMESET,
+ P will never be NULL here. */
+ assert (p);
+
/* If this character has a successor, use it. */
if (p[1])
- {
- name[len] = p[1];
- return true;
- }
+ {
+ name[len] = p[1];
+ return true;
+ }
/* Otherwise, set this digit to 0 and increment the prefix. */
name[len] = nameset[0];
@@ -931,8 +1050,8 @@ incname (char *name, size_t len)
/*
* Repeatedly rename a file with shorter and shorter names,
- * to obliterate all traces of the file name on any system that
- * adds a trailing delimiter to on-disk file names and reuses
+ * to obliterate all traces of the file name (and length) on any system
+ * that adds a trailing delimiter to on-disk file names and reuses
* the same directory slot. Finally, unlink it.
* The passed-in filename is modified in place to the new filename.
* (Which is unlinked if this function succeeds, but is still present if
@@ -962,54 +1081,57 @@ wipename (char *oldname, char const *qoldname, struct Options const *flags)
char *base = last_component (newname);
size_t len = base_len (base);
char *dir = dir_name (newname);
- char *qdir = xstrdup (quotearg_colon (dir));
+ char *qdir = xstrdup (quotef (dir));
bool first = true;
bool ok = true;
+ int dir_fd = -1;
- int dir_fd = open (dir, O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
+ if (flags->remove_file == remove_wipesync)
+ dir_fd = open (dir, O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
if (flags->verbose)
error (0, 0, _("%s: removing"), qoldname);
- while (len)
+ while ((flags->remove_file != remove_unlink) && len)
{
memset (base, nameset[0], len);
base[len] = 0;
do
- {
- struct stat st;
- if (lstat (newname, &st) < 0)
- {
- if (rename (oldname, newname) == 0)
- {
- if (0 <= dir_fd && dosync (dir_fd, qdir) != 0)
- ok = false;
- if (flags->verbose)
- {
- /*
- * People seem to understand this better than talking
- * about renaming oldname. newname doesn't need
- * quoting because we picked it. oldname needs to
- * be quoted only the first time.
- */
- char const *old = (first ? qoldname : oldname);
- error (0, 0, _("%s: renamed to %s"), old, newname);
- first = false;
- }
- memcpy (oldname + (base - newname), base, len + 1);
- break;
- }
- else
- {
- /* The rename failed: give up on this length. */
- break;
- }
- }
- else
- {
- /* newname exists, so increment BASE so we use another */
- }
- }
+ {
+ struct stat st;
+ if (lstat (newname, &st) < 0)
+ {
+ if (rename (oldname, newname) == 0)
+ {
+ if (0 <= dir_fd && dosync (dir_fd, qdir) != 0)
+ ok = false;
+ if (flags->verbose)
+ {
+ /*
+ * People seem to understand this better than talking
+ * about renaming oldname. newname doesn't need
+ * quoting because we picked it. oldname needs to
+ * be quoted only the first time.
+ */
+ char const *old = (first ? qoldname : oldname);
+ error (0, 0, _("%s: renamed to %s"),
+ old, newname);
+ first = false;
+ }
+ memcpy (oldname + (base - newname), base, len + 1);
+ break;
+ }
+ else
+ {
+ /* The rename failed: give up on this length. */
+ break;
+ }
+ }
+ else
+ {
+ /* newname exists, so increment BASE so we use another */
+ }
+ }
while (incname (base, len));
len--;
}
@@ -1023,12 +1145,12 @@ wipename (char *oldname, char const *qoldname, struct Options const *flags)
if (0 <= dir_fd)
{
if (dosync (dir_fd, qdir) != 0)
- ok = false;
+ ok = false;
if (close (dir_fd) != 0)
- {
- error (0, errno, _("%s: failed to close"), qdir);
- ok = false;
- }
+ {
+ error (0, errno, _("%s: failed to close"), qdir);
+ ok = false;
+ }
}
free (newname);
free (dir);
@@ -1050,7 +1172,7 @@ wipename (char *oldname, char const *qoldname, struct Options const *flags)
*/
static bool
wipefile (char *name, char const *qname,
- struct randint_source *s, struct Options const *flags)
+ struct randint_source *s, struct Options const *flags)
{
bool ok;
int fd;
@@ -1103,7 +1225,7 @@ main (int argc, char **argv)
char const *random_source = NULL;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -1116,66 +1238,56 @@ main (int argc, char **argv)
while ((c = getopt_long (argc, argv, "fn:s:uvxz", long_opts, NULL)) != -1)
{
switch (c)
- {
- case 'f':
- flags.force = true;
- break;
-
- case 'n':
- {
- uintmax_t tmp;
- if (xstrtoumax (optarg, NULL, 10, &tmp, NULL) != LONGINT_OK
- || MIN (UINT32_MAX, SIZE_MAX / sizeof (int)) < tmp)
- {
- error (EXIT_FAILURE, 0, _("%s: invalid number of passes"),
- quotearg_colon (optarg));
- }
- flags.n_iterations = tmp;
- }
- break;
-
- case RANDOM_SOURCE_OPTION:
- if (random_source && !STREQ (random_source, optarg))
- error (EXIT_FAILURE, 0, _("multiple random sources specified"));
- random_source = optarg;
- break;
-
- case 'u':
- flags.remove_file = true;
- break;
-
- case 's':
- {
- uintmax_t tmp;
- if (xstrtoumax (optarg, NULL, 0, &tmp, "cbBkKMGTPEZY0")
- != LONGINT_OK)
- {
- error (EXIT_FAILURE, 0, _("%s: invalid file size"),
- quotearg_colon (optarg));
- }
- flags.size = tmp;
- }
- break;
-
- case 'v':
- flags.verbose = true;
- break;
-
- case 'x':
- flags.exact = true;
- break;
-
- case 'z':
- flags.zero_fill = true;
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'f':
+ flags.force = true;
+ break;
+
+ case 'n':
+ flags.n_iterations = xdectoumax (optarg, 0,
+ MIN (ULONG_MAX,
+ SIZE_MAX / sizeof (int)), "",
+ _("invalid number of passes"), 0);
+ break;
+
+ case RANDOM_SOURCE_OPTION:
+ if (random_source && !STREQ (random_source, optarg))
+ error (EXIT_FAILURE, 0, _("multiple random sources specified"));
+ random_source = optarg;
+ break;
+
+ case 'u':
+ if (optarg == NULL)
+ flags.remove_file = remove_wipesync;
+ else
+ flags.remove_file = XARGMATCH ("--remove", optarg,
+ remove_args, remove_methods);
+ break;
+
+ case 's':
+ flags.size = xnumtoumax (optarg, 0, 0, OFF_T_MAX, "cbBkKMGTPEZY0",
+ _("invalid file size"), 0);
+ break;
+
+ case 'v':
+ flags.verbose = true;
+ break;
+
+ case 'x':
+ flags.exact = true;
+ break;
+
+ case 'z':
+ flags.zero_fill = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
}
file = argv + optind;
@@ -1189,25 +1301,25 @@ main (int argc, char **argv)
randint_source = randint_all_new (random_source, SIZE_MAX);
if (! randint_source)
- error (EXIT_FAILURE, errno, "%s", quotearg_colon (random_source));
+ error (EXIT_FAILURE, errno, "%s", quotef (random_source));
atexit (clear_random_data);
for (i = 0; i < n_files; i++)
{
- char *qname = xstrdup (quotearg_colon (file[i]));
+ char *qname = xstrdup (quotef (file[i]));
if (STREQ (file[i], "-"))
- {
- ok &= wipefd (STDOUT_FILENO, qname, randint_source, &flags);
- }
+ {
+ ok &= wipefd (STDOUT_FILENO, qname, randint_source, &flags);
+ }
else
- {
- /* Plain filename - Note that this overwrites *argv! */
- ok &= wipefile (file[i], qname, randint_source, &flags);
- }
+ {
+ /* Plain filename - Note that this overwrites *argv! */
+ ok &= wipefile (file[i], qname, randint_source, &flags);
+ }
free (qname);
}
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
/*
* vim:sw=2:sts=2:
diff --git a/src/shuf.c b/src/shuf.c
index bfc9f30..1097493 100644
--- a/src/shuf.c
+++ b/src/shuf.c
@@ -1,11 +1,11 @@
/* Shuffle lines of text.
- Copyright (C) 2006, 2007 Free Software Foundation, Inc.
+ Copyright (C) 2006-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -13,39 +13,49 @@
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, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
Written by Paul Eggert. */
#include <config.h>
-#include <stdio.h>
#include <sys/types.h>
#include "system.h"
#include "error.h"
+#include "fadvise.h"
#include "getopt.h"
+#include "linebuffer.h"
#include "quote.h"
-#include "quotearg.h"
#include "randint.h"
#include "randperm.h"
+#include "read-file.h"
+#include "stdio--.h"
+#include "xdectoint.h"
#include "xstrtol.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "shuf"
-#define AUTHORS "Paul Eggert"
+#define AUTHORS proper_name ("Paul Eggert")
+
+/* For reservoir-sampling, allocate the reservoir lines in batches. */
+enum { RESERVOIR_LINES_INCREMENT = 1024 };
+
+/* reservoir-sampling introduces CPU overhead for small inputs.
+ So only enable it for inputs >= this limit.
+ This limit was determined using these commands:
+ $ for p in $(seq 7); do src/seq $((10**$p)) > 10p$p.in; done
+ $ for p in $(seq 7); do time shuf-nores -n10 10p$p.in >/dev/null; done
+ $ for p in $(seq 7); do time shuf -n10 10p$p.in >/dev/null; done .*/
+enum { RESERVOIR_MIN_INPUT = 8192 * 1024 };
-/* The name this program was run with. */
-char *program_name;
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
@@ -53,29 +63,28 @@ Usage: %s [OPTION]... [FILE]\n\
or: %s -e [OPTION]... [ARG]...\n\
or: %s -i LO-HI [OPTION]...\n\
"),
- program_name, program_name, program_name);
+ program_name, program_name, program_name);
fputs (_("\
Write a random permutation of the input lines to standard output.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
+
+ emit_stdin_note ();
+ emit_mandatory_arg_note ();
+
fputs (_("\
-e, --echo treat each ARG as an input line\n\
-i, --input-range=LO-HI treat each number LO through HI as an input line\n\
- -n, --head-lines=LINES output at most LINES lines\n\
+ -n, --head-count=COUNT output at most COUNT lines\n\
-o, --output=FILE write result to FILE instead of standard output\n\
- --random-source=FILE get random bytes from FILE (default /dev/urandom)\n\
- -z, --zero-terminated end lines with 0 byte, not newline\n\
+ --random-source=FILE get random bytes from FILE\n\
+ -r, --repeat output lines can be repeated\n\
"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
-\n\
-With no FILE, or when FILE is -, read standard input.\n\
+ -z, --zero-terminated line delimiter is NUL, not newline\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ fputs (HELP_OPTION_DESCRIPTION, stdout);
+ fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -95,18 +104,13 @@ static struct option const long_opts[] =
{"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},
};
-static bool
-input_numbers_option_used (size_t lo_input, size_t hi_input)
-{
- return ! (lo_input == SIZE_MAX && hi_input == 0);
-}
-
static void
input_from_argv (char **operand, int n_operands, char eolbyte)
{
@@ -139,6 +143,114 @@ next_line (char *line, char eolbyte, size_t n)
return p + 1;
}
+/* Return the size of the input if possible or OFF_T_MAX if not. */
+
+static off_t
+input_size (void)
+{
+ off_t file_size;
+
+ struct stat stat_buf;
+ if (fstat (STDIN_FILENO, &stat_buf) != 0)
+ return OFF_T_MAX;
+ if (usable_st_size (&stat_buf))
+ file_size = stat_buf.st_size;
+ else
+ return OFF_T_MAX;
+
+ off_t input_offset = lseek (STDIN_FILENO, 0, SEEK_CUR);
+ if (input_offset < 0)
+ return OFF_T_MAX;
+
+ file_size -= input_offset;
+
+ return file_size;
+}
+
+/* Read all lines and store up to K permuted lines in *OUT_RSRV.
+ Return the number of lines read, up to a maximum of K. */
+
+static size_t
+read_input_reservoir_sampling (FILE *in, char eolbyte, size_t k,
+ struct randint_source *s,
+ struct linebuffer **out_rsrv)
+{
+ randint n_lines = 0;
+ size_t n_alloc_lines = MIN (k, RESERVOIR_LINES_INCREMENT);
+ struct linebuffer *line = NULL;
+ struct linebuffer *rsrv;
+
+ rsrv = xcalloc (n_alloc_lines, sizeof (struct linebuffer));
+
+ /* Fill the first K lines, directly into the reservoir. */
+ while (n_lines < k
+ && (line =
+ readlinebuffer_delim (&rsrv[n_lines], in, eolbyte)) != NULL)
+ {
+ n_lines++;
+
+ /* Enlarge reservoir. */
+ if (n_lines >= n_alloc_lines)
+ {
+ n_alloc_lines += RESERVOIR_LINES_INCREMENT;
+ rsrv = xnrealloc (rsrv, n_alloc_lines, sizeof (struct linebuffer));
+ memset (&rsrv[n_lines], 0,
+ RESERVOIR_LINES_INCREMENT * sizeof (struct linebuffer));
+ }
+ }
+
+ /* last line wasn't NULL - so there may be more lines to read. */
+ if (line != NULL)
+ {
+ struct linebuffer dummy;
+ initbuffer (&dummy); /* space for lines not put in reservoir. */
+
+ /* Choose the fate of the next line, with decreasing probability (as
+ n_lines increases in size).
+
+ If the line will be used, store it directly in the reservoir.
+ Otherwise, store it in dummy space.
+
+ With 'struct linebuffer', storing into existing buffer will reduce
+ re-allocations (will only re-allocate if the new line is longer than
+ the currently allocated space). */
+ do
+ {
+ randint j = randint_choose (s, n_lines + 1); /* 0 .. n_lines. */
+ line = (j < k) ? (&rsrv[j]) : (&dummy);
+ }
+ while (readlinebuffer_delim (line, in, eolbyte) != NULL && n_lines++);
+
+ if (! n_lines)
+ error (EXIT_FAILURE, EOVERFLOW, _("too many input lines"));
+
+ freebuffer (&dummy);
+ }
+
+ /* no more input lines, or an input error. */
+ if (ferror (in))
+ error (EXIT_FAILURE, errno, _("read error"));
+
+ *out_rsrv = rsrv;
+ return MIN (k, n_lines);
+}
+
+static int
+write_permuted_output_reservoir (size_t n_lines, struct linebuffer *lines,
+ size_t const *permutation)
+{
+ size_t i;
+
+ for (i = 0; i < n_lines; i++)
+ {
+ const struct linebuffer *p = &lines[permutation[i]];
+ if (fwrite (p->buffer, sizeof (char), p->length, stdout) != p->length)
+ return -1;
+ }
+
+ return 0;
+}
+
/* Read data from file IN. Input lines are delimited by EOLBYTE;
silently append a trailing EOLBYTE if the file ends in some other
byte. Store a pointer to the resulting array of lines into *PLINE.
@@ -150,52 +262,23 @@ read_input (FILE *in, char eolbyte, char ***pline)
{
char *p;
char *buf = NULL;
+ size_t used;
char *lim;
- size_t alloc = 0;
- size_t used = 0;
- size_t next_alloc = (1 << 13) + 1;
- size_t bytes_to_read;
- size_t nread;
char **line;
size_t i;
size_t n_lines;
- int fread_errno;
- struct stat instat;
-
- if (fstat (fileno (in), &instat) == 0 && S_ISREG (instat.st_mode))
- {
- off_t file_size = instat.st_size;
- off_t current_offset = ftello (in);
- if (0 <= current_offset)
- {
- off_t remaining_size =
- (current_offset < file_size ? file_size - current_offset : 0);
- if (SIZE_MAX - 2 < remaining_size)
- xalloc_die ();
- next_alloc = remaining_size + 2;
- }
- }
-
- do
- {
- if (alloc <= used + 1)
- {
- if (alloc == SIZE_MAX)
- xalloc_die ();
- alloc = next_alloc;
- next_alloc = alloc * 2;
- if (next_alloc < alloc)
- next_alloc = SIZE_MAX;
- buf = xrealloc (buf, alloc);
- }
-
- bytes_to_read = alloc - used - 1;
- nread = fread (buf + used, sizeof (char), bytes_to_read, in);
- used += nread;
- }
- while (nread == bytes_to_read);
- fread_errno = errno;
+ /* TODO: We should limit the amount of data read here,
+ to less than RESERVOIR_MIN_INPUT. I.e., adjust fread_file() to support
+ taking a byte limit. We'd then need to ensure we handle a line spanning
+ this boundary. With that in place we could set use_reservoir_sampling
+ when used==RESERVOIR_MIN_INPUT, and have read_input_reservoir_sampling()
+ call a wrapper function to populate a linebuffer from the internal pline
+ or if none left, stdin. Doing that would give better performance by
+ avoiding the reservoir CPU overhead when reading < RESERVOIR_MIN_INPUT
+ from a pipe, and allow us to dispense with the input_size() function. */
+ if (!(buf = fread_file (in, &used)))
+ error (EXIT_FAILURE, errno, _("read error"));
if (used && buf[used - 1] != eolbyte)
buf[used++] = eolbyte;
@@ -212,31 +295,84 @@ read_input (FILE *in, char eolbyte, char ***pline)
for (i = 1; i <= n_lines; i++)
line[i] = p = next_line (p, eolbyte, lim - p);
- errno = fread_errno;
return n_lines;
}
+/* Output N_LINES lines to stdout from LINE array,
+ chosen by the indices in PERMUTATION.
+ PERMUTATION and LINE must have at least N_LINES elements.
+ Strings in LINE must include the line-terminator character. */
static int
-write_permuted_output (size_t n_lines, char * const *line, size_t lo_input,
- size_t const *permutation)
+write_permuted_lines (size_t n_lines, char *const *line,
+ size_t const *permutation)
{
size_t i;
- if (line)
- for (i = 0; i < n_lines; i++)
- {
- char * const *p = line + permutation[i];
- size_t len = p[1] - p[0];
- if (fwrite (p[0], sizeof *p[0], len, stdout) != len)
- return -1;
- }
- else
- for (i = 0; i < n_lines; i++)
- {
- unsigned long int n = lo_input + permutation[i];
- if (printf ("%lu\n", n) < 0)
- return -1;
- }
+ for (i = 0; i < n_lines; i++)
+ {
+ char *const *p = line + permutation[i];
+ size_t len = p[1] - p[0];
+ if (fwrite (p[0], sizeof *p[0], len, stdout) != len)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Output N_LINES of numbers to stdout, from PERMUTATION array.
+ PERMUTATION must have at least N_LINES elements. */
+static int
+write_permuted_numbers (size_t n_lines, size_t lo_input,
+ size_t const *permutation, char eolbyte)
+{
+ size_t i;
+
+ for (i = 0; i < n_lines; i++)
+ {
+ unsigned long int n = lo_input + permutation[i];
+ if (printf ("%lu%c", n, eolbyte) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Output COUNT numbers to stdout, chosen randomly from range
+ LO_INPUT through HI_INPUT. */
+static int
+write_random_numbers (struct randint_source *s, size_t count,
+ size_t lo_input, size_t hi_input, char eolbyte)
+{
+ size_t i;
+ const randint range = hi_input - lo_input + 1;
+
+ for (i = 0; i < count; i++)
+ {
+ unsigned long int j = lo_input + randint_choose (s, range);
+ if (printf ("%lu%c", j, eolbyte) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Output COUNT lines to stdout from LINES array.
+ LINES must have at least N_LINES elements in it.
+ Strings in LINES_ must include the line-terminator character. */
+static int
+write_random_lines (struct randint_source *s, size_t count,
+ char *const *lines, size_t n_lines)
+{
+ size_t i;
+
+ for (i = 0; i < count; i++)
+ {
+ const randint j = randint_choose (s, n_lines);
+ char *const *p = lines + j;
+ size_t len = p[1] - p[0];
+ if (fwrite (p[0], sizeof *p[0], len, stdout) != len)
+ return -1;
+ }
return 0;
}
@@ -245,6 +381,7 @@ int
main (int argc, char **argv)
{
bool echo = false;
+ bool input_range = false;
size_t lo_input = SIZE_MAX;
size_t hi_input = 0;
size_t head_lines = SIZE_MAX;
@@ -252,159 +389,213 @@ main (int argc, char **argv)
char *random_source = NULL;
char eolbyte = '\n';
char **input_lines = NULL;
+ bool use_reservoir_sampling = false;
+ bool repeat = false;
int optc;
int n_operands;
char **operand;
size_t n_lines;
- char **line;
+ char **line = NULL;
+ struct linebuffer *reservoir = NULL;
struct randint_source *randint_source;
- size_t *permutation;
+ size_t *permutation = NULL;
+ int i;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "ei:n:o:z", long_opts, NULL)) != -1)
+ while ((optc = getopt_long (argc, argv, "ei:n:o:rz", long_opts, NULL)) != -1)
switch (optc)
{
case 'e':
- echo = true;
- break;
+ echo = true;
+ break;
case 'i':
- {
- unsigned long int argval = 0;
- char *p = strchr (optarg, '-');
- char const *hi_optarg = optarg;
- bool invalid = !p;
-
- if (input_numbers_option_used (lo_input, hi_input))
- error (EXIT_FAILURE, 0, _("multiple -i options specified"));
-
- if (p)
- {
- *p = '\0';
- invalid = ((xstrtoul (optarg, NULL, 10, &argval, NULL)
- != LONGINT_OK)
- || SIZE_MAX < argval);
- *p = '-';
- lo_input = argval;
- hi_optarg = p + 1;
- }
-
- invalid |= ((xstrtoul (hi_optarg, NULL, 10, &argval, NULL)
- != LONGINT_OK)
- || SIZE_MAX < argval);
- hi_input = argval;
- n_lines = hi_input - lo_input + 1;
- invalid |= ((lo_input <= hi_input) == (n_lines == 0));
- if (invalid)
- error (EXIT_FAILURE, 0, _("invalid input range %s"),
- quote (optarg));
- }
- break;
+ {
+ char *p = strchr (optarg, '-');
+ char const *hi_optarg = optarg;
+ bool invalid = !p;
+
+ if (input_range)
+ error (EXIT_FAILURE, 0, _("multiple -i options specified"));
+ input_range = true;
+
+ if (p)
+ {
+ *p = '\0';
+ lo_input = xdectoumax (optarg, 0, SIZE_MAX, "",
+ _("invalid input range"), 0);
+ *p = '-';
+ hi_optarg = p + 1;
+ }
+
+ hi_input = xdectoumax (hi_optarg, 0, SIZE_MAX, "",
+ _("invalid input range"), 0);
+
+ n_lines = hi_input - lo_input + 1;
+ invalid |= ((lo_input <= hi_input) == (n_lines == 0));
+ if (invalid)
+ error (EXIT_FAILURE, errno, "%s: %s", _("invalid input range"),
+ quote (optarg));
+ }
+ break;
case 'n':
- {
- unsigned long int argval;
- strtol_error e = xstrtoul (optarg, NULL, 10, &argval, NULL);
-
- if (e == LONGINT_OK)
- head_lines = MIN (head_lines, argval);
- else if (e != LONGINT_OVERFLOW)
- error (EXIT_FAILURE, 0, _("invalid line count %s"),
- quote (optarg));
- }
- break;
+ {
+ unsigned long int argval;
+ strtol_error e = xstrtoul (optarg, NULL, 10, &argval, NULL);
+
+ if (e == LONGINT_OK)
+ head_lines = MIN (head_lines, argval);
+ else if (e != LONGINT_OVERFLOW)
+ error (EXIT_FAILURE, 0, _("invalid line count: %s"),
+ quote (optarg));
+ }
+ break;
case 'o':
- if (outfile && !STREQ (outfile, optarg))
- error (EXIT_FAILURE, 0, _("multiple output files specified"));
- outfile = optarg;
- break;
+ if (outfile && !STREQ (outfile, optarg))
+ error (EXIT_FAILURE, 0, _("multiple output files specified"));
+ outfile = optarg;
+ break;
case RANDOM_SOURCE_OPTION:
- if (random_source && !STREQ (random_source, optarg))
- error (EXIT_FAILURE, 0, _("multiple random sources specified"));
- random_source = optarg;
- break;
+ if (random_source && !STREQ (random_source, optarg))
+ error (EXIT_FAILURE, 0, _("multiple random sources specified"));
+ random_source = optarg;
+ break;
+
+ case 'r':
+ repeat = true;
+ break;
case 'z':
- eolbyte = '\0';
- break;
+ eolbyte = '\0';
+ break;
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
- usage (EXIT_FAILURE);
+ usage (EXIT_FAILURE);
}
n_operands = argc - optind;
operand = argv + optind;
+ /* Check invalid usage. */
+ if (echo && input_range)
+ {
+ error (0, 0, _("cannot combine -e and -i options"));
+ usage (EXIT_FAILURE);
+ }
+ if (input_range ? 0 < n_operands : !echo && 1 < n_operands)
+ {
+ error (0, 0, _("extra operand %s"), quote (operand[!input_range]));
+ usage (EXIT_FAILURE);
+ }
+
+ /* Prepare input. */
if (echo)
{
- if (input_numbers_option_used (lo_input, hi_input))
- error (EXIT_FAILURE, 0, _("cannot combine -e and -i options"));
input_from_argv (operand, n_operands, eolbyte);
n_lines = n_operands;
line = operand;
}
- else if (input_numbers_option_used (lo_input, hi_input))
+ else if (input_range)
{
- if (n_operands)
- {
- error (0, 0, _("extra operand %s\n"), quote (operand[0]));
- usage (EXIT_FAILURE);
- }
n_lines = hi_input - lo_input + 1;
line = NULL;
}
else
{
- switch (n_operands)
- {
- case 0:
- break;
-
- case 1:
- if (! (STREQ (operand[0], "-") || freopen (operand[0], "r", stdin)))
- error (EXIT_FAILURE, errno, "%s", operand[0]);
- break;
-
- default:
- error (0, 0, _("extra operand %s"), quote (operand[1]));
- usage (EXIT_FAILURE);
- }
-
- n_lines = read_input (stdin, eolbyte, &input_lines);
- line = input_lines;
+ /* If an input file is specified, re-open it as stdin. */
+ if (n_operands == 1)
+ if (! (STREQ (operand[0], "-") || ! head_lines
+ || freopen (operand[0], "r", stdin)))
+ error (EXIT_FAILURE, errno, "%s", quotef (operand[0]));
+
+ fadvise (stdin, FADVISE_SEQUENTIAL);
+
+ if (! repeat && head_lines != SIZE_MAX
+ && (! head_lines || input_size () > RESERVOIR_MIN_INPUT))
+ {
+ use_reservoir_sampling = true;
+ n_lines = SIZE_MAX; /* unknown number of input lines, for now. */
+ }
+ else
+ {
+ n_lines = read_input (stdin, eolbyte, &input_lines);
+ line = input_lines;
+ }
}
- head_lines = MIN (head_lines, n_lines);
+ if (! repeat)
+ head_lines = MIN (head_lines, n_lines);
randint_source = randint_all_new (random_source,
- randperm_bound (head_lines, n_lines));
+ (use_reservoir_sampling || repeat
+ ? SIZE_MAX
+ : randperm_bound (head_lines, n_lines)));
if (! randint_source)
- error (EXIT_FAILURE, errno, "%s", quotearg_colon (random_source));
+ error (EXIT_FAILURE, errno, "%s", quotef (random_source));
+
+ if (use_reservoir_sampling)
+ {
+ /* Instead of reading the entire file into 'line',
+ use reservoir-sampling to store just "head_lines" random lines. */
+ n_lines = read_input_reservoir_sampling (stdin, eolbyte, head_lines,
+ randint_source, &reservoir);
+ head_lines = n_lines;
+ }
/* Close stdin now, rather than earlier, so that randint_all_new
doesn't have to worry about opening something other than
stdin. */
- if (! (echo || input_numbers_option_used (lo_input, hi_input))
- && (ferror (stdin) || fclose (stdin) != 0))
+ if (! (echo || input_range)
+ && (fclose (stdin) != 0))
error (EXIT_FAILURE, errno, _("read error"));
- permutation = randperm_new (randint_source, head_lines, n_lines);
+ if (!repeat)
+ permutation = randperm_new (randint_source, head_lines, n_lines);
if (outfile && ! freopen (outfile, "w", stdout))
- error (EXIT_FAILURE, errno, "%s", quotearg_colon (outfile));
- if (write_permuted_output (head_lines, line, lo_input, permutation) != 0)
+ error (EXIT_FAILURE, errno, "%s", quotef (outfile));
+
+ /* Generate output according to requested method */
+ if (repeat)
+ {
+ if (head_lines == 0)
+ i = 0;
+ else
+ {
+ if (n_lines == 0)
+ error (EXIT_FAILURE, 0, _("no lines to repeat"));
+ if (input_range)
+ i = write_random_numbers (randint_source, head_lines,
+ lo_input, hi_input, eolbyte);
+ else
+ i = write_random_lines (randint_source, head_lines, line, n_lines);
+ }
+ }
+ else
+ {
+ if (use_reservoir_sampling)
+ i = write_permuted_output_reservoir (n_lines, reservoir, permutation);
+ else if (input_range)
+ i = write_permuted_numbers (head_lines, lo_input,
+ permutation, eolbyte);
+ else
+ i = write_permuted_lines (head_lines, line, permutation);
+ }
+
+ if (i != 0)
error (EXIT_FAILURE, errno, _("write error"));
#ifdef lint
@@ -415,6 +606,13 @@ main (int argc, char **argv)
free (input_lines[0]);
free (input_lines);
}
+ if (reservoir)
+ {
+ size_t j;
+ for (j = 0; j < n_lines; ++j)
+ freebuffer (&reservoir[j]);
+ free (reservoir);
+ }
#endif
return EXIT_SUCCESS;
diff --git a/src/single-binary.mk b/src/single-binary.mk
new file mode 100644
index 0000000..777352b
--- /dev/null
+++ b/src/single-binary.mk
@@ -0,0 +1,487 @@
+## Automatically generated by gen-single-binary.sh. DO NOT EDIT BY HAND!
+src_libsinglebin_dir_a_DEPENDENCIES = src/libsinglebin_ls.a
+src_libsinglebin_vdir_a_DEPENDENCIES = src/libsinglebin_ls.a
+src_libsinglebin_arch_a_DEPENDENCIES = src/libsinglebin_uname.a
+# Command arch
+noinst_LIBRARIES += src/libsinglebin_arch.a
+src_libsinglebin_arch_a_SOURCES = src/coreutils-arch.c
+src_libsinglebin_arch_a_ldadd = src/libsinglebin_uname.a
+src_libsinglebin_arch_a_CFLAGS = "-Dmain=single_binary_main_arch (int, char **); int single_binary_main_arch" -Dusage=_usage_arch $(src_coreutils_CFLAGS)
+# Command hostname
+noinst_LIBRARIES += src/libsinglebin_hostname.a
+src_libsinglebin_hostname_a_SOURCES = src/hostname.c
+src_libsinglebin_hostname_a_ldadd = $(GETHOSTNAME_LIB)
+src_libsinglebin_hostname_a_CFLAGS = "-Dmain=single_binary_main_hostname (int, char **); int single_binary_main_hostname" -Dusage=_usage_hostname $(src_coreutils_CFLAGS)
+# Command chroot
+noinst_LIBRARIES += src/libsinglebin_chroot.a
+src_libsinglebin_chroot_a_SOURCES = src/chroot.c
+src_libsinglebin_chroot_a_CFLAGS = "-Dmain=single_binary_main_chroot (int, char **); int single_binary_main_chroot" -Dusage=_usage_chroot $(src_coreutils_CFLAGS)
+# Command df
+noinst_LIBRARIES += src/libsinglebin_df.a
+src_libsinglebin_df_a_SOURCES = src/df.c src/find-mount-point.c
+src_libsinglebin_df_a_ldadd = $(LIBICONV)
+src_libsinglebin_df_a_CFLAGS = "-Dmain=single_binary_main_df (int, char **); int single_binary_main_df" -Dusage=_usage_df $(src_coreutils_CFLAGS)
+# Command hostid
+noinst_LIBRARIES += src/libsinglebin_hostid.a
+src_libsinglebin_hostid_a_SOURCES = src/hostid.c
+src_libsinglebin_hostid_a_CFLAGS = "-Dmain=single_binary_main_hostid (int, char **); int single_binary_main_hostid" -Dusage=_usage_hostid $(src_coreutils_CFLAGS)
+# Command nice
+noinst_LIBRARIES += src/libsinglebin_nice.a
+src_libsinglebin_nice_a_SOURCES = src/nice.c
+src_libsinglebin_nice_a_CFLAGS = "-Dmain=single_binary_main_nice (int, char **); int single_binary_main_nice" -Dusage=_usage_nice $(src_coreutils_CFLAGS)
+# Command pinky
+noinst_LIBRARIES += src/libsinglebin_pinky.a
+src_libsinglebin_pinky_a_SOURCES = src/pinky.c
+src_libsinglebin_pinky_a_ldadd = $(GETADDRINFO_LIB)
+src_libsinglebin_pinky_a_CFLAGS = "-Dmain=single_binary_main_pinky (int, char **); int single_binary_main_pinky" -Dusage=_usage_pinky $(src_coreutils_CFLAGS)
+# Command stdbuf
+noinst_LIBRARIES += src/libsinglebin_stdbuf.a
+src_libsinglebin_stdbuf_a_SOURCES = src/stdbuf.c
+src_libsinglebin_stdbuf_a_ldadd = $(LIBICONV)
+src_libsinglebin_stdbuf_a_CFLAGS = "-Dmain=single_binary_main_stdbuf (int, char **); int single_binary_main_stdbuf" -Dusage=_usage_stdbuf $(src_coreutils_CFLAGS)
+# Command stty
+noinst_LIBRARIES += src/libsinglebin_stty.a
+src_libsinglebin_stty_a_SOURCES = src/stty.c
+src_libsinglebin_stty_a_CFLAGS = "-Dmain=single_binary_main_stty (int, char **); int single_binary_main_stty" -Dusage=_usage_stty $(src_coreutils_CFLAGS)
+# Command uptime
+noinst_LIBRARIES += src/libsinglebin_uptime.a
+src_libsinglebin_uptime_a_SOURCES = src/uptime.c
+src_libsinglebin_uptime_a_ldadd = $(GETLOADAVG_LIBS)
+src_libsinglebin_uptime_a_CFLAGS = "-Dmain=single_binary_main_uptime (int, char **); int single_binary_main_uptime" -Dusage=_usage_uptime $(src_coreutils_CFLAGS)
+# Command users
+noinst_LIBRARIES += src/libsinglebin_users.a
+src_libsinglebin_users_a_SOURCES = src/users.c
+src_libsinglebin_users_a_CFLAGS = "-Dmain=single_binary_main_users (int, char **); int single_binary_main_users" -Dusage=_usage_users $(src_coreutils_CFLAGS)
+# Command who
+noinst_LIBRARIES += src/libsinglebin_who.a
+src_libsinglebin_who_a_SOURCES = src/who.c
+src_libsinglebin_who_a_ldadd = $(GETADDRINFO_LIB)
+src_libsinglebin_who_a_CFLAGS = "-Dmain=single_binary_main_who (int, char **); int single_binary_main_who" -Dusage=_usage_who $(src_coreutils_CFLAGS)
+# Command _
+noinst_LIBRARIES += src/libsinglebin__.a
+src_libsinglebin___a_SOURCES = src/lbracket.c
+src_libsinglebin___a_ldadd = $(src_test_LDADD)
+src_libsinglebin___a_CFLAGS = "-Dmain=single_binary_main__ (int, char **); int single_binary_main__" -Dusage=_usage__ $(src_coreutils_CFLAGS)
+# Command base64
+noinst_LIBRARIES += src/libsinglebin_base64.a
+src_libsinglebin_base64_a_SOURCES = src/base64.c
+src_libsinglebin_base64_a_CFLAGS = "-Dmain=single_binary_main_base64 (int, char **); int single_binary_main_base64" -Dusage=_usage_base64 $(src_coreutils_CFLAGS)
+src_libsinglebin_base64_a_CPPFLAGS = -DBASE_TYPE=64 $(AM_CPPFLAGS)
+# Command base32
+noinst_LIBRARIES += src/libsinglebin_base32.a
+src_libsinglebin_base32_a_SOURCES = src/base64.c
+src_libsinglebin_base32_a_CFLAGS = "-Dmain=single_binary_main_base32 (int, char **); int single_binary_main_base32" -Dusage=_usage_base32 $(src_coreutils_CFLAGS)
+src_libsinglebin_base32_a_CPPFLAGS = -DBASE_TYPE=32 $(AM_CPPFLAGS)
+# Command basename
+noinst_LIBRARIES += src/libsinglebin_basename.a
+src_libsinglebin_basename_a_SOURCES = src/basename.c
+src_libsinglebin_basename_a_CFLAGS = "-Dmain=single_binary_main_basename (int, char **); int single_binary_main_basename" -Dusage=_usage_basename $(src_coreutils_CFLAGS)
+# Command cat
+noinst_LIBRARIES += src/libsinglebin_cat.a
+src_libsinglebin_cat_a_SOURCES = src/cat.c
+src_libsinglebin_cat_a_ldadd = $(LIBICONV)
+src_libsinglebin_cat_a_CFLAGS = "-Dmain=single_binary_main_cat (int, char **); int single_binary_main_cat" -Dusage=_usage_cat $(src_coreutils_CFLAGS)
+# Command chcon
+noinst_LIBRARIES += src/libsinglebin_chcon.a
+src_libsinglebin_chcon_a_SOURCES = src/chcon.c
+src_libsinglebin_chcon_a_ldadd = $(LIB_SELINUX)
+src_libsinglebin_chcon_a_CFLAGS = "-Dmain=single_binary_main_chcon (int, char **); int single_binary_main_chcon" -Dusage=_usage_chcon $(src_coreutils_CFLAGS)
+# Command chgrp
+noinst_LIBRARIES += src/libsinglebin_chgrp.a
+src_libsinglebin_chgrp_a_SOURCES = src/chgrp.c src/chown-core.c
+src_libsinglebin_chgrp_a_CFLAGS = "-Dmain=single_binary_main_chgrp (int, char **); int single_binary_main_chgrp" -Dusage=_usage_chgrp $(src_coreutils_CFLAGS)
+# Command chmod
+noinst_LIBRARIES += src/libsinglebin_chmod.a
+src_libsinglebin_chmod_a_SOURCES = src/chmod.c
+src_libsinglebin_chmod_a_CFLAGS = "-Dmain=single_binary_main_chmod (int, char **); int single_binary_main_chmod" -Dusage=_usage_chmod $(src_coreutils_CFLAGS)
+# Command chown
+noinst_LIBRARIES += src/libsinglebin_chown.a
+src_libsinglebin_chown_a_SOURCES = src/chown.c src/chown-core.c
+src_libsinglebin_chown_a_CFLAGS = "-Dmain=single_binary_main_chown (int, char **); int single_binary_main_chown" -Dusage=_usage_chown $(src_coreutils_CFLAGS)
+# Command cksum
+noinst_LIBRARIES += src/libsinglebin_cksum.a
+src_libsinglebin_cksum_a_SOURCES = src/cksum.c
+src_libsinglebin_cksum_a_CFLAGS = "-Dmain=single_binary_main_cksum (int, char **); int single_binary_main_cksum" -Dusage=_usage_cksum $(src_coreutils_CFLAGS)
+# Command comm
+noinst_LIBRARIES += src/libsinglebin_comm.a
+src_libsinglebin_comm_a_SOURCES = src/comm.c
+src_libsinglebin_comm_a_CFLAGS = "-Dmain=single_binary_main_comm (int, char **); int single_binary_main_comm" -Dusage=_usage_comm $(src_coreutils_CFLAGS)
+# Command cp
+noinst_LIBRARIES += src/libsinglebin_cp.a
+src_libsinglebin_cp_a_SOURCES = src/cp.c $(copy_sources) $(selinux_sources)
+src_libsinglebin_cp_a_ldadd = $(copy_ldadd) $(LIBICONV)
+src_libsinglebin_cp_a_CFLAGS = "-Dmain=single_binary_main_cp (int, char **); int single_binary_main_cp" -Dusage=_usage_cp $(src_coreutils_CFLAGS)
+# Command csplit
+noinst_LIBRARIES += src/libsinglebin_csplit.a
+src_libsinglebin_csplit_a_SOURCES = src/csplit.c
+src_libsinglebin_csplit_a_CFLAGS = "-Dmain=single_binary_main_csplit (int, char **); int single_binary_main_csplit" -Dusage=_usage_csplit $(src_coreutils_CFLAGS)
+# Command cut
+noinst_LIBRARIES += src/libsinglebin_cut.a
+src_libsinglebin_cut_a_SOURCES = src/cut.c src/set-fields.c
+src_libsinglebin_cut_a_CFLAGS = "-Dmain=single_binary_main_cut (int, char **); int single_binary_main_cut" -Dusage=_usage_cut $(src_coreutils_CFLAGS)
+# Command date
+noinst_LIBRARIES += src/libsinglebin_date.a
+src_libsinglebin_date_a_SOURCES = src/date.c
+src_libsinglebin_date_a_ldadd = $(LIB_CLOCK_GETTIME)
+src_libsinglebin_date_a_CFLAGS = "-Dmain=single_binary_main_date (int, char **); int single_binary_main_date" -Dusage=_usage_date $(src_coreutils_CFLAGS)
+# Command dd
+noinst_LIBRARIES += src/libsinglebin_dd.a
+src_libsinglebin_dd_a_SOURCES = src/dd.c
+src_libsinglebin_dd_a_ldadd = $(LIB_GETHRXTIME) $(LIB_FDATASYNC)
+src_libsinglebin_dd_a_CFLAGS = "-Dmain=single_binary_main_dd (int, char **); int single_binary_main_dd" -Dusage=_usage_dd $(src_coreutils_CFLAGS)
+# Command dir
+noinst_LIBRARIES += src/libsinglebin_dir.a
+src_libsinglebin_dir_a_SOURCES = src/coreutils-dir.c
+src_libsinglebin_dir_a_ldadd = $(src_ls_LDADD) src/libsinglebin_ls.a
+src_libsinglebin_dir_a_CFLAGS = "-Dmain=single_binary_main_dir (int, char **); int single_binary_main_dir" -Dusage=_usage_dir $(src_coreutils_CFLAGS)
+# Command dircolors
+noinst_LIBRARIES += src/libsinglebin_dircolors.a
+src_libsinglebin_dircolors_a_SOURCES = src/dircolors.c
+src_libsinglebin_dircolors_a_CFLAGS = "-Dmain=single_binary_main_dircolors (int, char **); int single_binary_main_dircolors" -Dusage=_usage_dircolors $(src_coreutils_CFLAGS)
+# Command dirname
+noinst_LIBRARIES += src/libsinglebin_dirname.a
+src_libsinglebin_dirname_a_SOURCES = src/dirname.c
+src_libsinglebin_dirname_a_CFLAGS = "-Dmain=single_binary_main_dirname (int, char **); int single_binary_main_dirname" -Dusage=_usage_dirname $(src_coreutils_CFLAGS)
+# Command du
+noinst_LIBRARIES += src/libsinglebin_du.a
+src_libsinglebin_du_a_SOURCES = src/du.c
+src_libsinglebin_du_a_ldadd = $(LIBICONV)
+src_libsinglebin_du_a_CFLAGS = "-Dmain=single_binary_main_du (int, char **); int single_binary_main_du" -Dusage=_usage_du $(src_coreutils_CFLAGS)
+# Command echo
+noinst_LIBRARIES += src/libsinglebin_echo.a
+src_libsinglebin_echo_a_SOURCES = src/echo.c
+src_libsinglebin_echo_a_CFLAGS = "-Dmain=single_binary_main_echo (int, char **); int single_binary_main_echo" -Dusage=_usage_echo $(src_coreutils_CFLAGS)
+# Command env
+noinst_LIBRARIES += src/libsinglebin_env.a
+src_libsinglebin_env_a_SOURCES = src/env.c
+src_libsinglebin_env_a_CFLAGS = "-Dmain=single_binary_main_env (int, char **); int single_binary_main_env" -Dusage=_usage_env $(src_coreutils_CFLAGS)
+# Command expand
+noinst_LIBRARIES += src/libsinglebin_expand.a
+src_libsinglebin_expand_a_SOURCES = src/expand.c
+src_libsinglebin_expand_a_CFLAGS = "-Dmain=single_binary_main_expand (int, char **); int single_binary_main_expand" -Dusage=_usage_expand $(src_coreutils_CFLAGS)
+# Command expr
+noinst_LIBRARIES += src/libsinglebin_expr.a
+src_libsinglebin_expr_a_SOURCES = src/expr.c
+src_libsinglebin_expr_a_ldadd = $(LIB_GMP)
+src_libsinglebin_expr_a_CFLAGS = "-Dmain=single_binary_main_expr (int, char **); int single_binary_main_expr" -Dusage=_usage_expr $(src_coreutils_CFLAGS)
+# Command factor
+noinst_LIBRARIES += src/libsinglebin_factor.a
+src_libsinglebin_factor_a_SOURCES = src/factor.c
+src_libsinglebin_factor_a_ldadd = $(LIB_GMP) $(LIBICONV)
+src_libsinglebin_factor_a_CFLAGS = "-Dmain=single_binary_main_factor (int, char **); int single_binary_main_factor" -Dusage=_usage_factor $(src_coreutils_CFLAGS)
+# Command false
+noinst_LIBRARIES += src/libsinglebin_false.a
+src_libsinglebin_false_a_SOURCES = src/false.c
+src_libsinglebin_false_a_CFLAGS = "-Dmain=single_binary_main_false (int, char **); int single_binary_main_false" -Dusage=_usage_false $(src_coreutils_CFLAGS)
+# Command fmt
+noinst_LIBRARIES += src/libsinglebin_fmt.a
+src_libsinglebin_fmt_a_SOURCES = src/fmt.c
+src_libsinglebin_fmt_a_CFLAGS = "-Dmain=single_binary_main_fmt (int, char **); int single_binary_main_fmt" -Dusage=_usage_fmt $(src_coreutils_CFLAGS)
+# Command fold
+noinst_LIBRARIES += src/libsinglebin_fold.a
+src_libsinglebin_fold_a_SOURCES = src/fold.c
+src_libsinglebin_fold_a_CFLAGS = "-Dmain=single_binary_main_fold (int, char **); int single_binary_main_fold" -Dusage=_usage_fold $(src_coreutils_CFLAGS)
+# Command ginstall
+noinst_LIBRARIES += src/libsinglebin_ginstall.a
+src_libsinglebin_ginstall_a_SOURCES = src/install.c src/prog-fprintf.c $(copy_sources) $(selinux_sources)
+src_libsinglebin_ginstall_a_ldadd = $(copy_ldadd) $(LIB_SELINUX) $(LIB_CLOCK_GETTIME)
+src_libsinglebin_ginstall_a_CFLAGS = "-Dmain=single_binary_main_ginstall (int, char **); int single_binary_main_ginstall" -Dusage=_usage_ginstall $(src_coreutils_CFLAGS)
+src_libsinglebin_ginstall_a_CPPFLAGS = -DENABLE_MATCHPATHCON=1 $(AM_CPPFLAGS)
+# Command groups
+noinst_LIBRARIES += src/libsinglebin_groups.a
+src_libsinglebin_groups_a_SOURCES = src/groups.c src/group-list.c
+src_libsinglebin_groups_a_CFLAGS = "-Dmain=single_binary_main_groups (int, char **); int single_binary_main_groups" -Dusage=_usage_groups $(src_coreutils_CFLAGS)
+# Command head
+noinst_LIBRARIES += src/libsinglebin_head.a
+src_libsinglebin_head_a_SOURCES = src/head.c
+src_libsinglebin_head_a_CFLAGS = "-Dmain=single_binary_main_head (int, char **); int single_binary_main_head" -Dusage=_usage_head $(src_coreutils_CFLAGS)
+# Command id
+noinst_LIBRARIES += src/libsinglebin_id.a
+src_libsinglebin_id_a_SOURCES = src/id.c src/group-list.c
+src_libsinglebin_id_a_ldadd = $(LIB_SELINUX) $(LIB_SMACK)
+src_libsinglebin_id_a_CFLAGS = "-Dmain=single_binary_main_id (int, char **); int single_binary_main_id" -Dusage=_usage_id $(src_coreutils_CFLAGS)
+# Command join
+noinst_LIBRARIES += src/libsinglebin_join.a
+src_libsinglebin_join_a_SOURCES = src/join.c
+src_libsinglebin_join_a_CFLAGS = "-Dmain=single_binary_main_join (int, char **); int single_binary_main_join" -Dusage=_usage_join $(src_coreutils_CFLAGS)
+# Command kill
+noinst_LIBRARIES += src/libsinglebin_kill.a
+src_libsinglebin_kill_a_SOURCES = src/kill.c src/operand2sig.c
+src_libsinglebin_kill_a_ldadd = $(LIBTHREAD)
+src_libsinglebin_kill_a_CFLAGS = "-Dmain=single_binary_main_kill (int, char **); int single_binary_main_kill" -Dusage=_usage_kill $(src_coreutils_CFLAGS)
+# Command link
+noinst_LIBRARIES += src/libsinglebin_link.a
+src_libsinglebin_link_a_SOURCES = src/link.c
+src_libsinglebin_link_a_CFLAGS = "-Dmain=single_binary_main_link (int, char **); int single_binary_main_link" -Dusage=_usage_link $(src_coreutils_CFLAGS)
+# Command ln
+noinst_LIBRARIES += src/libsinglebin_ln.a
+src_libsinglebin_ln_a_SOURCES = src/ln.c src/relpath.c src/relpath.h
+src_libsinglebin_ln_a_CFLAGS = "-Dmain=single_binary_main_ln (int, char **); int single_binary_main_ln" -Dusage=_usage_ln $(src_coreutils_CFLAGS)
+# Command logname
+noinst_LIBRARIES += src/libsinglebin_logname.a
+src_libsinglebin_logname_a_SOURCES = src/logname.c
+src_libsinglebin_logname_a_CFLAGS = "-Dmain=single_binary_main_logname (int, char **); int single_binary_main_logname" -Dusage=_usage_logname $(src_coreutils_CFLAGS)
+# Command ls
+noinst_LIBRARIES += src/libsinglebin_ls.a
+src_libsinglebin_ls_a_SOURCES = src/ls.c src/ls-ls.c
+src_libsinglebin_ls_a_ldadd = $(LIB_SELINUX) $(LIB_SMACK) $(LIB_CLOCK_GETTIME) $(LIB_CAP) $(LIB_HAS_ACL)
+src_libsinglebin_ls_a_CFLAGS = "-Dmain=single_binary_main_ls (int, char **); int single_binary_main_ls" -Dusage=_usage_ls $(src_coreutils_CFLAGS)
+# Command md5sum
+noinst_LIBRARIES += src/libsinglebin_md5sum.a
+src_libsinglebin_md5sum_a_SOURCES = src/md5sum.c
+src_libsinglebin_md5sum_a_ldadd = $(LIB_CRYPTO)
+src_libsinglebin_md5sum_a_CFLAGS = "-Dmain=single_binary_main_md5sum (int, char **); int single_binary_main_md5sum" -Dusage=_usage_md5sum $(src_coreutils_CFLAGS)
+src_libsinglebin_md5sum_a_CPPFLAGS = -DHASH_ALGO_MD5=1 $(AM_CPPFLAGS)
+# Command mkdir
+noinst_LIBRARIES += src/libsinglebin_mkdir.a
+src_libsinglebin_mkdir_a_SOURCES = src/mkdir.c src/prog-fprintf.c $(selinux_sources)
+src_libsinglebin_mkdir_a_ldadd = $(LIB_SELINUX) $(LIB_SMACK)
+src_libsinglebin_mkdir_a_CFLAGS = "-Dmain=single_binary_main_mkdir (int, char **); int single_binary_main_mkdir" -Dusage=_usage_mkdir $(src_coreutils_CFLAGS)
+# Command mkfifo
+noinst_LIBRARIES += src/libsinglebin_mkfifo.a
+src_libsinglebin_mkfifo_a_SOURCES = src/mkfifo.c $(selinux_sources)
+src_libsinglebin_mkfifo_a_ldadd = $(LIB_SELINUX) $(LIB_SMACK)
+src_libsinglebin_mkfifo_a_CFLAGS = "-Dmain=single_binary_main_mkfifo (int, char **); int single_binary_main_mkfifo" -Dusage=_usage_mkfifo $(src_coreutils_CFLAGS)
+# Command mknod
+noinst_LIBRARIES += src/libsinglebin_mknod.a
+src_libsinglebin_mknod_a_SOURCES = src/mknod.c $(selinux_sources)
+src_libsinglebin_mknod_a_ldadd = $(LIB_SELINUX) $(LIB_SMACK)
+src_libsinglebin_mknod_a_CFLAGS = "-Dmain=single_binary_main_mknod (int, char **); int single_binary_main_mknod" -Dusage=_usage_mknod $(src_coreutils_CFLAGS)
+# Command mktemp
+noinst_LIBRARIES += src/libsinglebin_mktemp.a
+src_libsinglebin_mktemp_a_SOURCES = src/mktemp.c
+src_libsinglebin_mktemp_a_CFLAGS = "-Dmain=single_binary_main_mktemp (int, char **); int single_binary_main_mktemp" -Dusage=_usage_mktemp $(src_coreutils_CFLAGS)
+# Command mv
+noinst_LIBRARIES += src/libsinglebin_mv.a
+src_libsinglebin_mv_a_SOURCES = src/mv.c src/remove.c $(copy_sources) $(selinux_sources)
+src_libsinglebin_mv_a_ldadd = $(copy_ldadd) $(remove_ldadd)
+src_libsinglebin_mv_a_CFLAGS = "-Dmain=single_binary_main_mv (int, char **); int single_binary_main_mv" -Dusage=_usage_mv $(src_coreutils_CFLAGS)
+# Command nl
+noinst_LIBRARIES += src/libsinglebin_nl.a
+src_libsinglebin_nl_a_SOURCES = src/nl.c
+src_libsinglebin_nl_a_CFLAGS = "-Dmain=single_binary_main_nl (int, char **); int single_binary_main_nl" -Dusage=_usage_nl $(src_coreutils_CFLAGS)
+# Command nproc
+noinst_LIBRARIES += src/libsinglebin_nproc.a
+src_libsinglebin_nproc_a_SOURCES = src/nproc.c
+src_libsinglebin_nproc_a_CFLAGS = "-Dmain=single_binary_main_nproc (int, char **); int single_binary_main_nproc" -Dusage=_usage_nproc $(src_coreutils_CFLAGS)
+# Command nohup
+noinst_LIBRARIES += src/libsinglebin_nohup.a
+src_libsinglebin_nohup_a_SOURCES = src/nohup.c
+src_libsinglebin_nohup_a_CFLAGS = "-Dmain=single_binary_main_nohup (int, char **); int single_binary_main_nohup" -Dusage=_usage_nohup $(src_coreutils_CFLAGS)
+# Command numfmt
+noinst_LIBRARIES += src/libsinglebin_numfmt.a
+src_libsinglebin_numfmt_a_SOURCES = src/numfmt.c src/set-fields.c
+src_libsinglebin_numfmt_a_CFLAGS = "-Dmain=single_binary_main_numfmt (int, char **); int single_binary_main_numfmt" -Dusage=_usage_numfmt $(src_coreutils_CFLAGS)
+# Command od
+noinst_LIBRARIES += src/libsinglebin_od.a
+src_libsinglebin_od_a_SOURCES = src/od.c
+src_libsinglebin_od_a_CFLAGS = "-Dmain=single_binary_main_od (int, char **); int single_binary_main_od" -Dusage=_usage_od $(src_coreutils_CFLAGS)
+# Command paste
+noinst_LIBRARIES += src/libsinglebin_paste.a
+src_libsinglebin_paste_a_SOURCES = src/paste.c
+src_libsinglebin_paste_a_CFLAGS = "-Dmain=single_binary_main_paste (int, char **); int single_binary_main_paste" -Dusage=_usage_paste $(src_coreutils_CFLAGS)
+# Command pathchk
+noinst_LIBRARIES += src/libsinglebin_pathchk.a
+src_libsinglebin_pathchk_a_SOURCES = src/pathchk.c
+src_libsinglebin_pathchk_a_CFLAGS = "-Dmain=single_binary_main_pathchk (int, char **); int single_binary_main_pathchk" -Dusage=_usage_pathchk $(src_coreutils_CFLAGS)
+# Command pr
+noinst_LIBRARIES += src/libsinglebin_pr.a
+src_libsinglebin_pr_a_SOURCES = src/pr.c
+src_libsinglebin_pr_a_ldadd = $(LIB_CLOCK_GETTIME)
+src_libsinglebin_pr_a_CFLAGS = "-Dmain=single_binary_main_pr (int, char **); int single_binary_main_pr" -Dusage=_usage_pr $(src_coreutils_CFLAGS)
+# Command printenv
+noinst_LIBRARIES += src/libsinglebin_printenv.a
+src_libsinglebin_printenv_a_SOURCES = src/printenv.c
+src_libsinglebin_printenv_a_CFLAGS = "-Dmain=single_binary_main_printenv (int, char **); int single_binary_main_printenv" -Dusage=_usage_printenv $(src_coreutils_CFLAGS)
+# Command printf
+noinst_LIBRARIES += src/libsinglebin_printf.a
+src_libsinglebin_printf_a_SOURCES = src/printf.c
+src_libsinglebin_printf_a_ldadd = $(LIBICONV)
+src_libsinglebin_printf_a_CFLAGS = "-Dmain=single_binary_main_printf (int, char **); int single_binary_main_printf" -Dusage=_usage_printf $(src_coreutils_CFLAGS)
+# Command ptx
+noinst_LIBRARIES += src/libsinglebin_ptx.a
+src_libsinglebin_ptx_a_SOURCES = src/ptx.c
+src_libsinglebin_ptx_a_ldadd = $(LIBICONV)
+src_libsinglebin_ptx_a_CFLAGS = "-Dmain=single_binary_main_ptx (int, char **); int single_binary_main_ptx" -Dusage=_usage_ptx $(src_coreutils_CFLAGS)
+# Command pwd
+noinst_LIBRARIES += src/libsinglebin_pwd.a
+src_libsinglebin_pwd_a_SOURCES = src/pwd.c
+src_libsinglebin_pwd_a_CFLAGS = "-Dmain=single_binary_main_pwd (int, char **); int single_binary_main_pwd" -Dusage=_usage_pwd $(src_coreutils_CFLAGS)
+# Command readlink
+noinst_LIBRARIES += src/libsinglebin_readlink.a
+src_libsinglebin_readlink_a_SOURCES = src/readlink.c
+src_libsinglebin_readlink_a_CFLAGS = "-Dmain=single_binary_main_readlink (int, char **); int single_binary_main_readlink" -Dusage=_usage_readlink $(src_coreutils_CFLAGS)
+# Command realpath
+noinst_LIBRARIES += src/libsinglebin_realpath.a
+src_libsinglebin_realpath_a_SOURCES = src/realpath.c src/relpath.c src/relpath.h
+src_libsinglebin_realpath_a_ldadd = $(LIBICONV)
+src_libsinglebin_realpath_a_CFLAGS = "-Dmain=single_binary_main_realpath (int, char **); int single_binary_main_realpath" -Dusage=_usage_realpath $(src_coreutils_CFLAGS)
+# Command rm
+noinst_LIBRARIES += src/libsinglebin_rm.a
+src_libsinglebin_rm_a_SOURCES = src/rm.c src/remove.c
+src_libsinglebin_rm_a_ldadd = $(remove_ldadd)
+src_libsinglebin_rm_a_CFLAGS = "-Dmain=single_binary_main_rm (int, char **); int single_binary_main_rm" -Dusage=_usage_rm $(src_coreutils_CFLAGS)
+# Command rmdir
+noinst_LIBRARIES += src/libsinglebin_rmdir.a
+src_libsinglebin_rmdir_a_SOURCES = src/rmdir.c src/prog-fprintf.c
+src_libsinglebin_rmdir_a_CFLAGS = "-Dmain=single_binary_main_rmdir (int, char **); int single_binary_main_rmdir" -Dusage=_usage_rmdir $(src_coreutils_CFLAGS)
+# Command runcon
+noinst_LIBRARIES += src/libsinglebin_runcon.a
+src_libsinglebin_runcon_a_SOURCES = src/runcon.c
+src_libsinglebin_runcon_a_ldadd = $(LIB_SELINUX)
+src_libsinglebin_runcon_a_CFLAGS = "-Dmain=single_binary_main_runcon (int, char **); int single_binary_main_runcon" -Dusage=_usage_runcon $(src_coreutils_CFLAGS)
+# Command seq
+noinst_LIBRARIES += src/libsinglebin_seq.a
+src_libsinglebin_seq_a_SOURCES = src/seq.c
+src_libsinglebin_seq_a_CFLAGS = "-Dmain=single_binary_main_seq (int, char **); int single_binary_main_seq" -Dusage=_usage_seq $(src_coreutils_CFLAGS)
+# Command sha1sum
+noinst_LIBRARIES += src/libsinglebin_sha1sum.a
+src_libsinglebin_sha1sum_a_SOURCES = src/md5sum.c
+src_libsinglebin_sha1sum_a_ldadd = $(LIB_CRYPTO)
+src_libsinglebin_sha1sum_a_CFLAGS = "-Dmain=single_binary_main_sha1sum (int, char **); int single_binary_main_sha1sum" -Dusage=_usage_sha1sum $(src_coreutils_CFLAGS)
+src_libsinglebin_sha1sum_a_CPPFLAGS = -DHASH_ALGO_SHA1=1 $(AM_CPPFLAGS)
+# Command sha224sum
+noinst_LIBRARIES += src/libsinglebin_sha224sum.a
+src_libsinglebin_sha224sum_a_SOURCES = src/md5sum.c
+src_libsinglebin_sha224sum_a_ldadd = $(LIB_CRYPTO)
+src_libsinglebin_sha224sum_a_CFLAGS = "-Dmain=single_binary_main_sha224sum (int, char **); int single_binary_main_sha224sum" -Dusage=_usage_sha224sum $(src_coreutils_CFLAGS)
+src_libsinglebin_sha224sum_a_CPPFLAGS = -DHASH_ALGO_SHA224=1 $(AM_CPPFLAGS)
+# Command sha256sum
+noinst_LIBRARIES += src/libsinglebin_sha256sum.a
+src_libsinglebin_sha256sum_a_SOURCES = src/md5sum.c
+src_libsinglebin_sha256sum_a_ldadd = $(LIB_CRYPTO)
+src_libsinglebin_sha256sum_a_CFLAGS = "-Dmain=single_binary_main_sha256sum (int, char **); int single_binary_main_sha256sum" -Dusage=_usage_sha256sum $(src_coreutils_CFLAGS)
+src_libsinglebin_sha256sum_a_CPPFLAGS = -DHASH_ALGO_SHA256=1 $(AM_CPPFLAGS)
+# Command sha384sum
+noinst_LIBRARIES += src/libsinglebin_sha384sum.a
+src_libsinglebin_sha384sum_a_SOURCES = src/md5sum.c
+src_libsinglebin_sha384sum_a_ldadd = $(LIB_CRYPTO)
+src_libsinglebin_sha384sum_a_CFLAGS = "-Dmain=single_binary_main_sha384sum (int, char **); int single_binary_main_sha384sum" -Dusage=_usage_sha384sum $(src_coreutils_CFLAGS)
+src_libsinglebin_sha384sum_a_CPPFLAGS = -DHASH_ALGO_SHA384=1 $(AM_CPPFLAGS)
+# Command sha512sum
+noinst_LIBRARIES += src/libsinglebin_sha512sum.a
+src_libsinglebin_sha512sum_a_SOURCES = src/md5sum.c
+src_libsinglebin_sha512sum_a_ldadd = $(LIB_CRYPTO)
+src_libsinglebin_sha512sum_a_CFLAGS = "-Dmain=single_binary_main_sha512sum (int, char **); int single_binary_main_sha512sum" -Dusage=_usage_sha512sum $(src_coreutils_CFLAGS)
+src_libsinglebin_sha512sum_a_CPPFLAGS = -DHASH_ALGO_SHA512=1 $(AM_CPPFLAGS)
+# Command shred
+noinst_LIBRARIES += src/libsinglebin_shred.a
+src_libsinglebin_shred_a_SOURCES = src/shred.c
+src_libsinglebin_shred_a_ldadd = $(LIB_FDATASYNC)
+src_libsinglebin_shred_a_CFLAGS = "-Dmain=single_binary_main_shred (int, char **); int single_binary_main_shred" -Dusage=_usage_shred $(src_coreutils_CFLAGS)
+# Command shuf
+noinst_LIBRARIES += src/libsinglebin_shuf.a
+src_libsinglebin_shuf_a_SOURCES = src/shuf.c
+src_libsinglebin_shuf_a_CFLAGS = "-Dmain=single_binary_main_shuf (int, char **); int single_binary_main_shuf" -Dusage=_usage_shuf $(src_coreutils_CFLAGS)
+# Command sleep
+noinst_LIBRARIES += src/libsinglebin_sleep.a
+src_libsinglebin_sleep_a_SOURCES = src/sleep.c
+src_libsinglebin_sleep_a_ldadd = $(LIB_NANOSLEEP)
+src_libsinglebin_sleep_a_CFLAGS = "-Dmain=single_binary_main_sleep (int, char **); int single_binary_main_sleep" -Dusage=_usage_sleep $(src_coreutils_CFLAGS)
+# Command sort
+noinst_LIBRARIES += src/libsinglebin_sort.a
+src_libsinglebin_sort_a_SOURCES = src/sort.c
+src_libsinglebin_sort_a_ldadd = $(LIB_EACCESS) $(LIB_NANOSLEEP) $(LIB_CRYPTO) $(LIB_PTHREAD)
+src_libsinglebin_sort_a_CFLAGS = "-Dmain=single_binary_main_sort (int, char **); int single_binary_main_sort" -Dusage=_usage_sort $(src_coreutils_CFLAGS)
+# Command split
+noinst_LIBRARIES += src/libsinglebin_split.a
+src_libsinglebin_split_a_SOURCES = src/split.c
+src_libsinglebin_split_a_ldadd = $(LIBICONV)
+src_libsinglebin_split_a_CFLAGS = "-Dmain=single_binary_main_split (int, char **); int single_binary_main_split" -Dusage=_usage_split $(src_coreutils_CFLAGS)
+# Command stat
+noinst_LIBRARIES += src/libsinglebin_stat.a
+src_libsinglebin_stat_a_SOURCES = src/stat.c src/find-mount-point.c
+src_libsinglebin_stat_a_ldadd = $(LIB_SELINUX) $(LIB_NVPAIR)
+src_libsinglebin_stat_a_CFLAGS = "-Dmain=single_binary_main_stat (int, char **); int single_binary_main_stat" -Dusage=_usage_stat $(src_coreutils_CFLAGS)
+# Command sum
+noinst_LIBRARIES += src/libsinglebin_sum.a
+src_libsinglebin_sum_a_SOURCES = src/sum.c
+src_libsinglebin_sum_a_CFLAGS = "-Dmain=single_binary_main_sum (int, char **); int single_binary_main_sum" -Dusage=_usage_sum $(src_coreutils_CFLAGS)
+# Command sync
+noinst_LIBRARIES += src/libsinglebin_sync.a
+src_libsinglebin_sync_a_SOURCES = src/sync.c
+src_libsinglebin_sync_a_ldadd = $(LIB_FDATASYNC)
+src_libsinglebin_sync_a_CFLAGS = "-Dmain=single_binary_main_sync (int, char **); int single_binary_main_sync" -Dusage=_usage_sync $(src_coreutils_CFLAGS)
+# Command tac
+noinst_LIBRARIES += src/libsinglebin_tac.a
+src_libsinglebin_tac_a_SOURCES = src/tac.c
+src_libsinglebin_tac_a_CFLAGS = "-Dmain=single_binary_main_tac (int, char **); int single_binary_main_tac" -Dusage=_usage_tac $(src_coreutils_CFLAGS)
+# Command tail
+noinst_LIBRARIES += src/libsinglebin_tail.a
+src_libsinglebin_tail_a_SOURCES = src/tail.c
+src_libsinglebin_tail_a_ldadd = $(LIB_NANOSLEEP)
+src_libsinglebin_tail_a_CFLAGS = "-Dmain=single_binary_main_tail (int, char **); int single_binary_main_tail" -Dusage=_usage_tail $(src_coreutils_CFLAGS)
+# Command tee
+noinst_LIBRARIES += src/libsinglebin_tee.a
+src_libsinglebin_tee_a_SOURCES = src/tee.c
+src_libsinglebin_tee_a_CFLAGS = "-Dmain=single_binary_main_tee (int, char **); int single_binary_main_tee" -Dusage=_usage_tee $(src_coreutils_CFLAGS)
+# Command test
+noinst_LIBRARIES += src/libsinglebin_test.a
+src_libsinglebin_test_a_SOURCES = src/test.c
+src_libsinglebin_test_a_ldadd = $(LIB_EACCESS)
+src_libsinglebin_test_a_CFLAGS = "-Dmain=single_binary_main_test (int, char **); int single_binary_main_test" -Dusage=_usage_test $(src_coreutils_CFLAGS)
+# Command timeout
+noinst_LIBRARIES += src/libsinglebin_timeout.a
+src_libsinglebin_timeout_a_SOURCES = src/timeout.c src/operand2sig.c
+src_libsinglebin_timeout_a_ldadd = $(LIB_TIMER_TIME) $(LIBICONV)
+src_libsinglebin_timeout_a_CFLAGS = "-Dmain=single_binary_main_timeout (int, char **); int single_binary_main_timeout" -Dusage=_usage_timeout $(src_coreutils_CFLAGS)
+# Command touch
+noinst_LIBRARIES += src/libsinglebin_touch.a
+src_libsinglebin_touch_a_SOURCES = src/touch.c
+src_libsinglebin_touch_a_ldadd = $(LIB_CLOCK_GETTIME)
+src_libsinglebin_touch_a_CFLAGS = "-Dmain=single_binary_main_touch (int, char **); int single_binary_main_touch" -Dusage=_usage_touch $(src_coreutils_CFLAGS)
+# Command tr
+noinst_LIBRARIES += src/libsinglebin_tr.a
+src_libsinglebin_tr_a_SOURCES = src/tr.c
+src_libsinglebin_tr_a_CFLAGS = "-Dmain=single_binary_main_tr (int, char **); int single_binary_main_tr" -Dusage=_usage_tr $(src_coreutils_CFLAGS)
+# Command true
+noinst_LIBRARIES += src/libsinglebin_true.a
+src_libsinglebin_true_a_SOURCES = src/true.c
+src_libsinglebin_true_a_CFLAGS = "-Dmain=single_binary_main_true (int, char **); int single_binary_main_true" -Dusage=_usage_true $(src_coreutils_CFLAGS)
+# Command truncate
+noinst_LIBRARIES += src/libsinglebin_truncate.a
+src_libsinglebin_truncate_a_SOURCES = src/truncate.c
+src_libsinglebin_truncate_a_ldadd = $(LIBICONV)
+src_libsinglebin_truncate_a_CFLAGS = "-Dmain=single_binary_main_truncate (int, char **); int single_binary_main_truncate" -Dusage=_usage_truncate $(src_coreutils_CFLAGS)
+# Command tsort
+noinst_LIBRARIES += src/libsinglebin_tsort.a
+src_libsinglebin_tsort_a_SOURCES = src/tsort.c
+src_libsinglebin_tsort_a_CFLAGS = "-Dmain=single_binary_main_tsort (int, char **); int single_binary_main_tsort" -Dusage=_usage_tsort $(src_coreutils_CFLAGS)
+# Command tty
+noinst_LIBRARIES += src/libsinglebin_tty.a
+src_libsinglebin_tty_a_SOURCES = src/tty.c
+src_libsinglebin_tty_a_CFLAGS = "-Dmain=single_binary_main_tty (int, char **); int single_binary_main_tty" -Dusage=_usage_tty $(src_coreutils_CFLAGS)
+# Command uname
+noinst_LIBRARIES += src/libsinglebin_uname.a
+src_libsinglebin_uname_a_SOURCES = src/uname.c src/uname-uname.c
+src_libsinglebin_uname_a_ldadd = $(GETHOSTNAME_LIB)
+src_libsinglebin_uname_a_CFLAGS = "-Dmain=single_binary_main_uname (int, char **); int single_binary_main_uname" -Dusage=_usage_uname $(src_coreutils_CFLAGS)
+# Command unexpand
+noinst_LIBRARIES += src/libsinglebin_unexpand.a
+src_libsinglebin_unexpand_a_SOURCES = src/unexpand.c
+src_libsinglebin_unexpand_a_CFLAGS = "-Dmain=single_binary_main_unexpand (int, char **); int single_binary_main_unexpand" -Dusage=_usage_unexpand $(src_coreutils_CFLAGS)
+# Command uniq
+noinst_LIBRARIES += src/libsinglebin_uniq.a
+src_libsinglebin_uniq_a_SOURCES = src/uniq.c
+src_libsinglebin_uniq_a_CFLAGS = "-Dmain=single_binary_main_uniq (int, char **); int single_binary_main_uniq" -Dusage=_usage_uniq $(src_coreutils_CFLAGS)
+# Command unlink
+noinst_LIBRARIES += src/libsinglebin_unlink.a
+src_libsinglebin_unlink_a_SOURCES = src/unlink.c
+src_libsinglebin_unlink_a_CFLAGS = "-Dmain=single_binary_main_unlink (int, char **); int single_binary_main_unlink" -Dusage=_usage_unlink $(src_coreutils_CFLAGS)
+# Command vdir
+noinst_LIBRARIES += src/libsinglebin_vdir.a
+src_libsinglebin_vdir_a_SOURCES = src/coreutils-vdir.c
+src_libsinglebin_vdir_a_ldadd = $(src_ls_LDADD) src/libsinglebin_ls.a
+src_libsinglebin_vdir_a_CFLAGS = "-Dmain=single_binary_main_vdir (int, char **); int single_binary_main_vdir" -Dusage=_usage_vdir $(src_coreutils_CFLAGS)
+# Command wc
+noinst_LIBRARIES += src/libsinglebin_wc.a
+src_libsinglebin_wc_a_SOURCES = src/wc.c
+src_libsinglebin_wc_a_CFLAGS = "-Dmain=single_binary_main_wc (int, char **); int single_binary_main_wc" -Dusage=_usage_wc $(src_coreutils_CFLAGS)
+# Command whoami
+noinst_LIBRARIES += src/libsinglebin_whoami.a
+src_libsinglebin_whoami_a_SOURCES = src/whoami.c
+src_libsinglebin_whoami_a_CFLAGS = "-Dmain=single_binary_main_whoami (int, char **); int single_binary_main_whoami" -Dusage=_usage_whoami $(src_coreutils_CFLAGS)
+# Command yes
+noinst_LIBRARIES += src/libsinglebin_yes.a
+src_libsinglebin_yes_a_SOURCES = src/yes.c
+src_libsinglebin_yes_a_CFLAGS = "-Dmain=single_binary_main_yes (int, char **); int single_binary_main_yes" -Dusage=_usage_yes $(src_coreutils_CFLAGS)
diff --git a/src/sleep.c b/src/sleep.c
index 730b19d..a865c76 100644
--- a/src/sleep.c
+++ b/src/sleep.c
@@ -1,10 +1,10 @@
/* sleep - delay for a specified amount of time.
- Copyright (C) 84, 1991-1997, 1999-2005 Free Software Foundation, Inc.
+ Copyright (C) 1984-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#include <stdio.h>
@@ -28,44 +27,42 @@
#include "xnanosleep.h"
#include "xstrtod.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "sleep"
-#define AUTHORS "Jim Meyering", "Paul Eggert"
-
-/* The name by which this program was run. */
-char *program_name;
+#define AUTHORS \
+ proper_name ("Jim Meyering"), \
+ proper_name ("Paul Eggert")
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s NUMBER[SUFFIX]...\n\
or: %s OPTION\n\
-Pause for NUMBER seconds. SUFFIX may be `s' for seconds (the default),\n\
-`m' for minutes, `h' for hours or `d' for days. Unlike most implementations\n\
+Pause for NUMBER seconds. SUFFIX may be 's' for seconds (the default),\n\
+'m' for minutes, 'h' for hours or 'd' for days. Unlike most implementations\n\
that require NUMBER be an integer, here NUMBER may be an arbitrary floating\n\
point number. Given two or more arguments, pause for the amount of time\n\
specified by the sum of their values.\n\
\n\
"),
- program_name, program_name);
+ program_name, program_name);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
/* Given a floating point value *X, and a suffix character, SUFFIX_CHAR,
scale *X by the multiplier implied by SUFFIX_CHAR. SUFFIX_CHAR may
- be the NUL byte or `s' to denote seconds, `m' for minutes, `h' for
- hours, or `d' for days. If SUFFIX_CHAR is invalid, don't modify *X
+ be the NUL byte or 's' to denote seconds, 'm' for minutes, 'h' for
+ hours, or 'd' for days. If SUFFIX_CHAR is invalid, don't modify *X
and return false. Otherwise return true. */
static bool
@@ -105,15 +102,15 @@ main (int argc, char **argv)
bool ok = true;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
+ parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, Version,
+ usage, AUTHORS, (char const *) NULL);
if (getopt_long (argc, argv, "", NULL, NULL) != -1)
usage (EXIT_FAILURE);
@@ -128,16 +125,16 @@ main (int argc, char **argv)
double s;
const char *p;
if (! xstrtod (argv[i], &p, &s, c_strtod)
- /* Nonnegative interval. */
- || ! (0 <= s)
- /* No extra chars after the number and an optional s,m,h,d char. */
- || (*p && *(p+1))
- /* Check any suffix char and update S based on the suffix. */
- || ! apply_suffix (&s, *p))
- {
- error (0, 0, _("invalid time interval %s"), quote (argv[i]));
- ok = false;
- }
+ /* Nonnegative interval. */
+ || ! (0 <= s)
+ /* No extra chars after the number and an optional s,m,h,d char. */
+ || (*p && *(p+1))
+ /* Check any suffix char and update S based on the suffix. */
+ || ! apply_suffix (&s, *p))
+ {
+ error (0, 0, _("invalid time interval %s"), quote (argv[i]));
+ ok = false;
+ }
seconds += s;
}
@@ -148,5 +145,5 @@ main (int argc, char **argv)
if (xnanosleep (seconds))
error (EXIT_FAILURE, errno, _("cannot read realtime clock"));
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/sort.c b/src/sort.c
index 58ca66a..62acb62 100644
--- a/src/sort.c
+++ b/src/sort.c
@@ -1,10 +1,10 @@
/* sort - sort lines of text (with all kinds of options).
- Copyright (C) 1988, 1991-2007 Free Software Foundation, Inc.
+ Copyright (C) 1988-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
Written December 1988 by Mike Haertel.
The author may be reached (Email) at the address mike@gnu.ai.mit.edu,
@@ -24,39 +23,47 @@
#include <config.h>
#include <getopt.h>
+#include <pthread.h>
+#include <sys/resource.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
+#include <assert.h>
#include "system.h"
#include "argmatch.h"
#include "error.h"
+#include "fadvise.h"
+#include "filevercmp.h"
#include "hard-locale.h"
#include "hash.h"
-#include "inttostr.h"
+#include "heap.h"
+#include "ignore-value.h"
#include "md5.h"
+#include "mbswidth.h"
+#include "nproc.h"
#include "physmem.h"
#include "posixver.h"
#include "quote.h"
#include "randread.h"
+#include "readtokens0.h"
#include "stdio--.h"
#include "stdlib--.h"
#include "strnumcmp.h"
#include "xmemcoll.h"
-#include "xmemxfrm.h"
+#include "xnanosleep.h"
#include "xstrtol.h"
-#if HAVE_SYS_RESOURCE_H
-# include <sys/resource.h>
-#endif
#ifndef RLIMIT_DATA
struct rlimit { size_t rlim_cur; };
# define getrlimit(Resource, Rlp) (-1)
#endif
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "sort"
-#define AUTHORS "Mike Haertel", "Paul Eggert"
+#define AUTHORS \
+ proper_name ("Mike Haertel"), \
+ proper_name ("Paul Eggert")
#if HAVE_LANGINFO_CODESET
# include <langinfo.h>
@@ -74,16 +81,47 @@ struct rlimit { size_t rlim_cur; };
# endif
#endif
-#ifndef STDC_HEADERS
-double strtod ();
+#if !defined OPEN_MAX && defined NR_OPEN
+# define OPEN_MAX NR_OPEN
+#endif
+#if !defined OPEN_MAX
+# define OPEN_MAX 20
#endif
#define UCHAR_LIM (UCHAR_MAX + 1)
+#if HAVE_C99_STRTOLD
+# define long_double long double
+#else
+# define long_double double
+# undef strtold
+# define strtold strtod
+#endif
+
#ifndef DEFAULT_TMPDIR
# define DEFAULT_TMPDIR "/tmp"
#endif
+/* Maximum number of lines to merge every time a NODE is taken from
+ the merge queue. Node is at LEVEL in the binary merge tree,
+ and is responsible for merging TOTAL lines. */
+#define MAX_MERGE(total, level) (((total) >> (2 * ((level) + 1))) + 1)
+
+/* Heuristic value for the number of lines for which it is worth creating
+ a subthread, during an internal merge sort. I.e., it is a small number
+ of "average" lines for which sorting via two threads is faster than
+ sorting via one on an "average" system. On a dual-core 2.0 GHz i686
+ system with 3GB of RAM and 2MB of L2 cache, a file containing 128K
+ lines of gensort -a output is sorted slightly faster with --parallel=2
+ than with --parallel=1. By contrast, using --parallel=1 is about 10%
+ faster than using --parallel=2 with a 64K-line input. */
+enum { SUBTHREAD_LINES_HEURISTIC = 128 * 1024 };
+verify (4 <= SUBTHREAD_LINES_HEURISTIC);
+
+/* The number of threads after which there are
+ diminishing performance gains. */
+enum { DEFAULT_MAX_THREADS = 8 };
+
/* Exit statuses. */
enum
{
@@ -101,13 +139,22 @@ enum
/* The number of times we should try to fork a compression process
(we retry if the fork call fails). We don't _need_ to compress
temp files, this is just to reduce disk access, so this number
- can be small. */
- MAX_FORK_TRIES_COMPRESS = 2,
+ can be small. Each retry doubles in duration. */
+ MAX_FORK_TRIES_COMPRESS = 4,
/* The number of times we should try to fork a decompression process.
If we can't fork a decompression process, we can't sort, so this
- number should be big. */
- MAX_FORK_TRIES_DECOMPRESS = 8
+ number should be big. Each retry doubles in duration. */
+ MAX_FORK_TRIES_DECOMPRESS = 9
+ };
+
+enum
+ {
+ /* Level of the end-of-merge node, one level above the root. */
+ MERGE_END = 0,
+
+ /* Level of the root node in merge tree. */
+ MERGE_ROOT = 1
};
/* The representation of the decimal point in the current locale. */
@@ -143,10 +190,10 @@ struct line
struct buffer
{
char *buf; /* Dynamically allocated buffer,
- partitioned into 3 regions:
- - input data;
- - unused area;
- - an array of lines, in reverse order. */
+ partitioned into 3 regions:
+ - input data;
+ - unused area;
+ - an array of lines, in reverse order. */
size_t used; /* Number of bytes used for input data. */
size_t nlines; /* Number of lines in the line array. */
size_t alloc; /* Number of bytes allocated. */
@@ -155,24 +202,29 @@ struct buffer
bool eof; /* An EOF has been read. */
};
+/* Sort key. */
struct keyfield
{
size_t sword; /* Zero-origin 'word' to start at. */
size_t schar; /* Additional characters to skip. */
- size_t eword; /* Zero-origin first word after field. */
+ size_t eword; /* Zero-origin last 'word' of key. */
size_t echar; /* Additional characters in field. */
bool const *ignore; /* Boolean array of characters to ignore. */
char const *translate; /* Translation applied to characters. */
bool skipsblanks; /* Skip leading blanks when finding start. */
bool skipeblanks; /* Skip leading blanks when finding end. */
bool numeric; /* Flag for numeric comparison. Handle
- strings of digits with optional decimal
- point, but no exponential notation. */
+ strings of digits with optional decimal
+ point, but no exponential notation. */
bool random; /* Sort by random hash of key. */
bool general_numeric; /* Flag for general, numeric comparison.
- Handle numbers in exponential notation. */
+ Handle numbers in exponential notation. */
+ bool human_numeric; /* Flag for sorting by human readable
+ units with either SI or IEC prefixes. */
bool month; /* Flag for comparison by month name. */
bool reverse; /* Reverse the sense of comparison. */
+ bool version; /* sort by version number */
+ bool obsolete_used; /* obsolescent key option format is used. */
struct keyfield *next; /* Next keyfield to try. */
};
@@ -182,12 +234,39 @@ struct month
int val;
};
-/* The name this program was run with. */
-char *program_name;
+/* Binary merge tree node. */
+struct merge_node
+{
+ struct line *lo; /* Lines to merge from LO child node. */
+ struct line *hi; /* Lines to merge from HI child node. */
+ struct line *end_lo; /* End of available lines from LO. */
+ struct line *end_hi; /* End of available lines from HI. */
+ struct line **dest; /* Pointer to destination of merge. */
+ size_t nlo; /* Total Lines remaining from LO. */
+ size_t nhi; /* Total lines remaining from HI. */
+ struct merge_node *parent; /* Parent node. */
+ struct merge_node *lo_child; /* LO child node. */
+ struct merge_node *hi_child; /* HI child node. */
+ unsigned int level; /* Level in merge tree. */
+ bool queued; /* Node is already in heap. */
+ pthread_mutex_t lock; /* Lock for node operations. */
+};
+
+/* Priority queue of merge nodes. */
+struct merge_node_queue
+{
+ struct heap *priority_queue; /* Priority queue of merge tree nodes. */
+ pthread_mutex_t mutex; /* Lock for queue operations. */
+ pthread_cond_t cond; /* Conditional wait for empty queue to populate
+ when popping. */
+};
+
+/* Used to implement --unique (-u). */
+static struct line saved_line;
/* FIXME: None of these tables work with multibyte character sets.
Also, there are many other bugs when handling multibyte characters.
- One way to fix this is to rewrite `sort' to use wide characters
+ One way to fix this is to rewrite 'sort' to use wide characters
internally, but doing this with good performance is a bit
tricky. */
@@ -224,13 +303,13 @@ static struct month monthtab[] =
};
/* During the merge phase, the number of files to merge at once. */
-#define NMERGE 16
+#define NMERGE_DEFAULT 16
/* Minimum size for a merge or check buffer. */
#define MIN_MERGE_BUFFER_SIZE (2 + sizeof (struct line))
/* Minimum sort size; the code might not work with smaller sizes. */
-#define MIN_SORT_SIZE (NMERGE * MIN_MERGE_BUFFER_SIZE)
+#define MIN_SORT_SIZE (nmerge * MIN_MERGE_BUFFER_SIZE)
/* The number of bytes needed for a merge or check buffer, which can
function relatively efficiently even if it holds only one line. If
@@ -241,8 +320,12 @@ static size_t merge_buffer_size = MAX (MIN_MERGE_BUFFER_SIZE, 256 * 1024);
specified by the user. Zero if the user has not specified a size. */
static size_t sort_size;
-/* The guessed size for non-regular files. */
-#define INPUT_FILE_SIZE_GUESS (1024 * 1024)
+/* The initial allocation factor for non-regular files.
+ This is used, e.g., when reading from a pipe.
+ Don't make it too big, since it is multiplied by ~130 to
+ obtain the size of the actual buffer sort will allocate.
+ Also, there may be 8 threads all doing this at the same time. */
+#define INPUT_FILE_SIZE_GUESS (128 * 1024)
/* Array of directory names in which any temporary files are to be created. */
static char const **temp_dirs;
@@ -282,7 +365,40 @@ static struct keyfield *keylist;
/* Program used to (de)compress temp files. Must accept -d. */
static char const *compress_program;
-static void sortlines_temp (struct line *, size_t, struct line *);
+/* Annotate the output with extra info to aid the user. */
+static bool debug;
+
+/* Maximum number of files to merge in one go. If more than this
+ number are present, temp files will be used. */
+static unsigned int nmerge = NMERGE_DEFAULT;
+
+/* Output an error to stderr using async-signal-safe routines, and _exit().
+ This can be used safely from signal handlers,
+ and between fork() and exec() of multithreaded processes. */
+
+static void async_safe_die (int, const char *) ATTRIBUTE_NORETURN;
+static void
+async_safe_die (int errnum, const char *errstr)
+{
+ ignore_value (write (STDERR_FILENO, errstr, strlen (errstr)));
+
+ /* Even if defined HAVE_STRERROR_R, we can't use it,
+ as it may return a translated string etc. and even if not
+ may malloc() which is unsafe. We might improve this
+ by testing for sys_errlist and using that if available.
+ For now just report the error number. */
+ if (errnum)
+ {
+ char errbuf[INT_BUFSIZE_BOUND (errnum)];
+ char *p = inttostr (errnum, errbuf);
+ ignore_value (write (STDERR_FILENO, ": errno ", 8));
+ ignore_value (write (STDERR_FILENO, p, strlen (p)));
+ }
+
+ ignore_value (write (STDERR_FILENO, "\n", 1));
+
+ _exit (SORT_FAILURE);
+}
/* Report MESSAGE for FILE, then clean up and exit.
If FILE is null, it represents standard output. */
@@ -291,7 +407,8 @@ static void die (char const *, char const *) ATTRIBUTE_NORETURN;
static void
die (char const *message, char const *file)
{
- error (0, errno, "%s: %s", message, file ? file : _("standard output"));
+ error (0, errno, "%s: %s", message,
+ quotef (file ? file : _("standard output")));
exit (SORT_FAILURE);
}
@@ -299,88 +416,121 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
+ or: %s [OPTION]... --files0-from=F\n\
"),
- program_name);
+ program_name, program_name);
fputs (_("\
Write sorted concatenation of all FILE(s) to standard output.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
+
+ emit_stdin_note ();
+ emit_mandatory_arg_note ();
+
fputs (_("\
Ordering options:\n\
\n\
"), stdout);
fputs (_("\
-b, --ignore-leading-blanks ignore leading blanks\n\
- -d, --dictionary-order consider only blanks and alphanumeric characters\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\
+ -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\
- -R, --random-sort sort by random hash of keys\n\
- --random-source=FILE get random bytes from FILE (default /dev/urandom)\n\
+ -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\n\
+ -C, --check=quiet, --check=silent like -c, but do not report first bad line\
+\n\
--compress-program=PROG compress temporaries with PROG;\n\
decompress them with PROG -d\n\
- -k, --key=POS1[,POS2] start a key at POS1, end it at POS2 (origin 1)\n\
+"), stdout);
+ fputs (_("\
+ --debug annotate the part of the line used to sort,\n\
+ and warn about questionable usage to stderr\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, --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 with -c, check for strict ordering;\n\
- without -c, output only the first of an equal run\n\
+ without -c, output only the first of an equal run\
+\n\
"), DEFAULT_TMPDIR);
fputs (_("\
- -z, --zero-terminated end lines with 0 byte, not newline\n\
+ -z, --zero-terminated line delimiter is NUL, not newline\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
\n\
-POS is F[.C][OPTS], where F is the field number and C the character position\n\
-in the field; both are origin 1. If neither -t nor -b is in effect, characters\n\
-in a field are counted from the beginning of the preceding whitespace. OPTS is\n\
-one or more single-letter ordering options, which override global ordering\n\
-options for that key. If no key is given, use the entire line as the key.\n\
+KEYDEF is F[.C][OPTS][,F[.C][OPTS]] for start and stop position, where F is a\n\
+field number and C a character position in the field; both are origin 1, and\n\
+the stop position defaults to the line's end. If neither -t nor -b is in\n\
+effect, characters in a field are counted from the beginning of the preceding\n\
+whitespace. OPTS is one or more single-letter ordering options [bdfgiMhnRrV],\
+\n\
+which override global ordering options for that key. If no key is given, use\n\
+the entire line as the key. Use --debug to diagnose incorrect key usage.\n\
\n\
SIZE may be followed by the following multiplicative suffixes:\n\
"), stdout);
fputs (_("\
% 1% of memory, b 1, K 1024 (default), and so on for M, G, T, P, E, Z, Y.\n\
\n\
-With no FILE, or when FILE is -, read standard input.\n\
-\n\
*** WARNING ***\n\
The locale specified by the environment affects sort order.\n\
Set LC_ALL=C to get the traditional sort order that uses\n\
native byte values.\n\
"), stdout );
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -392,48 +542,89 @@ enum
{
CHECK_OPTION = CHAR_MAX + 1,
COMPRESS_PROGRAM_OPTION,
- RANDOM_SOURCE_OPTION
+ DEBUG_PROGRAM_OPTION,
+ FILES0_FROM_OPTION,
+ NMERGE_OPTION,
+ RANDOM_SOURCE_OPTION,
+ SORT_OPTION,
+ PARALLEL_OPTION
};
-static char const short_options[] = "-bcCdfgik:mMno:rRsS:t:T:uy:z";
+static char const short_options[] = "-bcCdfghik:mMno:rRsS:t:T:uVy:z";
static struct option const long_options[] =
{
{"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},
{NULL, 0, NULL, 0},
};
+#define CHECK_TABLE \
+ _ct_("quiet", 'C') \
+ _ct_("silent", 'C') \
+ _ct_("diagnose-first", 'c')
+
static char const *const check_args[] =
{
- "quiet", "silent", "diagnose-first", NULL
+#define _ct_(_s, _c) _s,
+ CHECK_TABLE NULL
+#undef _ct_
};
static char const check_types[] =
{
- 'C', 'C', 'c'
+#define _ct_(_s, _c) _c,
+ CHECK_TABLE
+#undef _ct_
+};
+
+#define SORT_TABLE \
+ _st_("general-numeric", 'g') \
+ _st_("human-numeric", 'h') \
+ _st_("month", 'M') \
+ _st_("numeric", 'n') \
+ _st_("random", 'R') \
+ _st_("version", 'V')
+
+static char const *const sort_args[] =
+{
+#define _st_(_s, _c) _s,
+ SORT_TABLE NULL
+#undef _st_
+};
+static char const sort_types[] =
+{
+#define _st_(_s, _c) _c,
+ SORT_TABLE
+#undef _st_
};
-ARGMATCH_VERIFY (check_args, check_types);
/* The set of signals that are caught. */
static sigset_t caught_signals;
@@ -465,178 +656,158 @@ cs_leave (struct cs_status status)
}
}
+/* Possible states for a temp file. If compressed, the file's status
+ is unreaped or reaped, depending on whether 'sort' has waited for
+ the subprocess to finish. */
+enum { UNCOMPRESSED, UNREAPED, REAPED };
+
/* The list of temporary files. */
struct tempnode
{
struct tempnode *volatile next;
- pid_t pid; /* If compressed, the pid of compressor, else zero */
+ pid_t pid; /* The subprocess PID; undefined if state == UNCOMPRESSED. */
+ char state;
char name[1]; /* Actual size is 1 + file name length. */
};
static struct tempnode *volatile temphead;
static struct tempnode *volatile *temptail = &temphead;
+/* A file to be sorted. */
struct sortfile
{
+ /* The file's name. */
char const *name;
- pid_t pid; /* If compressed, the pid of compressor, else zero */
+
+ /* Non-null if this is a temporary file, in which case NAME == TEMP->name. */
+ struct tempnode *temp;
};
-/* A table where we store compression process states. We clean up all
- processes in a timely manner so as not to exhaust system resources,
- so we store the info on whether the process is still running, or has
- been reaped here. */
+/* Map PIDs of unreaped subprocesses to their struct tempnode objects. */
static Hash_table *proctab;
enum { INIT_PROCTAB_SIZE = 47 };
-enum procstate { ALIVE, ZOMBIE };
-
-/* A proctab entry. The COUNT field is there in case we fork a new
- compression process that has the same PID as an old zombie process
- that is still in the table (because the process to decompress the
- temp file it was associated with hasn't started yet). */
-struct procnode
-{
- pid_t pid;
- enum procstate state;
- size_t count;
-};
-
static size_t
-proctab_hasher (const void *entry, size_t tabsize)
+proctab_hasher (void const *entry, size_t tabsize)
{
- const struct procnode *node = entry;
+ struct tempnode const *node = entry;
return node->pid % tabsize;
}
static bool
-proctab_comparator (const void *e1, const void *e2)
+proctab_comparator (void const *e1, void const *e2)
{
- const struct procnode *n1 = e1, *n2 = e2;
+ struct tempnode const *n1 = e1;
+ struct tempnode const *n2 = e2;
return n1->pid == n2->pid;
}
-/* The total number of forked processes (compressors and decompressors)
- that have not been reaped yet. */
-static size_t nprocs;
+/* The number of unreaped child processes. */
+static pid_t nprocs;
-/* The number of child processes we'll allow before we try to reap some. */
-enum { MAX_PROCS_BEFORE_REAP = 2 };
+static bool delete_proc (pid_t);
-/* If 0 < PID, wait for the child process with that PID to exit.
- If PID is -1, clean up a random child process which has finished and
- return the process ID of that child. If PID is -1 and no processes
- have quit yet, return 0 without waiting. */
+/* If PID is positive, wait for the child process with that PID to
+ exit, and assume that PID has already been removed from the process
+ table. If PID is 0 or -1, clean up some child that has exited (by
+ waiting for it, and removing it from the proc table) and return the
+ child's process ID. However, if PID is 0 and no children have
+ exited, return 0 without waiting. */
static pid_t
reap (pid_t pid)
{
int status;
- pid_t cpid = waitpid (pid, &status, pid < 0 ? WNOHANG : 0);
+ pid_t cpid = waitpid ((pid ? pid : -1), &status, (pid ? 0 : WNOHANG));
if (cpid < 0)
error (SORT_FAILURE, errno, _("waiting for %s [-d]"),
- compress_program);
- else if (0 < cpid)
+ quoteaf (compress_program));
+ else if (0 < cpid && (0 < pid || delete_proc (cpid)))
{
if (! WIFEXITED (status) || WEXITSTATUS (status))
- error (SORT_FAILURE, 0, _("%s [-d] terminated abnormally"),
- compress_program);
+ error (SORT_FAILURE, 0, _("%s [-d] terminated abnormally"),
+ quoteaf (compress_program));
--nprocs;
}
return cpid;
}
-/* Add the PID of a running compression process to proctab, or update
- the entry COUNT and STATE fields if it's already there. This also
- creates the table for us the first time it's called. */
+/* TEMP represents a new process; add it to the process table. Create
+ the process table the first time it's called. */
static void
-register_proc (pid_t pid)
+register_proc (struct tempnode *temp)
{
- struct procnode test, *node;
-
if (! proctab)
{
proctab = hash_initialize (INIT_PROCTAB_SIZE, NULL,
- proctab_hasher,
- proctab_comparator,
- free);
+ proctab_hasher,
+ proctab_comparator,
+ NULL);
if (! proctab)
- xalloc_die ();
+ xalloc_die ();
}
- test.pid = pid;
- node = hash_lookup (proctab, &test);
- if (node)
- {
- node->state = ALIVE;
- ++node->count;
- }
- else
- {
- node = xmalloc (sizeof *node);
- node->pid = pid;
- node->state = ALIVE;
- node->count = 1;
- hash_insert (proctab, node);
- }
+ temp->state = UNREAPED;
+
+ if (! hash_insert (proctab, temp))
+ xalloc_die ();
}
-/* This is called when we reap a random process. We don't know
- whether we have reaped a compression process or a decompression
- process until we look in the table. If there's an ALIVE entry for
- it, then we have reaped a compression process, so change the state
- to ZOMBIE. Otherwise, it's a decompression processes, so ignore it. */
+/* If PID is in the process table, remove it and return true.
+ Otherwise, return false. */
-static void
-update_proc (pid_t pid)
+static bool
+delete_proc (pid_t pid)
{
- struct procnode test, *node;
+ struct tempnode test;
test.pid = pid;
- node = hash_lookup (proctab, &test);
- if (node)
- node->state = ZOMBIE;
+ struct tempnode *node = hash_delete (proctab, &test);
+ if (! node)
+ return false;
+ node->state = REAPED;
+ return true;
}
-/* This is for when we need to wait for a compression process to exit.
- If it has a ZOMBIE entry in the table then it's already dead and has
- been reaped. Note that if there's an ALIVE entry for it, it still may
- already have died and been reaped if a second process was created with
- the same PID. This is probably exceedingly rare, but to be on the safe
- side we will have to wait for any compression process with this PID. */
+/* Remove PID from the process table, and wait for it to exit if it
+ hasn't already. */
static void
wait_proc (pid_t pid)
{
- struct procnode test, *node;
-
- test.pid = pid;
- node = hash_lookup (proctab, &test);
- if (node->state == ALIVE)
+ if (delete_proc (pid))
reap (pid);
+}
- node->state = ZOMBIE;
- if (! --node->count)
- {
- hash_delete (proctab, node);
- free (node);
- }
+/* Reap any exited children. Do not block; reap only those that have
+ already exited. */
+
+static void
+reap_exited (void)
+{
+ while (0 < nprocs && reap (0))
+ continue;
}
-/* Keep reaping finished children as long as there are more to reap.
- This doesn't block waiting for any of them, it only reaps those
- that are already dead. */
+/* Reap at least one exited child, waiting if necessary. */
static void
reap_some (void)
{
- pid_t pid;
+ reap (-1);
+ reap_exited ();
+}
+
+/* Reap all children, waiting if necessary. */
- while (0 < nprocs && (pid = reap (-1)))
- update_proc (pid);
+static void
+reap_all (void)
+{
+ while (0 < nprocs)
+ reap (-1);
}
/* Clean up any remaining temporary files. */
@@ -659,7 +830,7 @@ exit_cleanup (void)
if (temphead)
{
/* Clean up any remaining temporary files in a critical section so
- that a signal handler does not try to clean them too. */
+ that a signal handler does not try to clean them too. */
struct cs_status cs = cs_enter ();
cleanup ();
cs_leave (cs);
@@ -669,10 +840,13 @@ exit_cleanup (void)
}
/* Create a new temporary file, returning its newly allocated tempnode.
- Store into *PFD the file descriptor open for writing. */
+ Store into *PFD the file descriptor open for writing.
+ 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. */
static struct tempnode *
-create_temp_file (int *pfd)
+create_temp_file (int *pfd, bool survive_fd_exhaustion)
{
static char const slashbase[] = "/sortXXXXXX";
static size_t temp_dir_index;
@@ -688,7 +862,6 @@ create_temp_file (int *pfd)
memcpy (file, temp_dir, len);
memcpy (file + len, slashbase, sizeof slashbase);
node->next = NULL;
- node->pid = 0;
if (++temp_dir_index == temp_dir_count)
temp_dir_index = 0;
@@ -705,7 +878,13 @@ create_temp_file (int *pfd)
errno = saved_errno;
if (fd < 0)
- die (_("cannot create temporary file"), file);
+ {
+ if (! (survive_fd_exhaustion && errno == EMFILE))
+ error (SORT_FAILURE, errno, _("cannot create temporary file in %s"),
+ quoteaf (temp_dir));
+ free (node);
+ node = NULL;
+ }
*pfd = fd;
return node;
@@ -715,30 +894,90 @@ create_temp_file (int *pfd)
standard output; HOW should be "w". 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. */
+ opening an ordinary FILE. Return NULL if unsuccessful.
+
+ fadvise() is used to specify an access pattern for input files.
+ There are a few hints we could possibly provide,
+ and after careful testing it was decided that
+ specifying POSIX_FADV_SEQUENTIAL was not detrimental
+ to any cases. On Linux 2.6.31, this option doubles
+ the size of read ahead performed and thus was seen to
+ benefit these cases:
+ Merging
+ Sorting with a smaller internal buffer
+ Reading from faster flash devices
+
+ In _addition_ one could also specify other hints...
+
+ POSIX_FADV_WILLNEED was tested, but Linux 2.6.31
+ at least uses that to _synchronously_ prepopulate the cache
+ with the specified range. While sort does need to
+ read all of its input before outputting, a synchronous
+ read of the whole file up front precludes any processing
+ that sort could do in parallel with the system doing
+ read ahead of the data. This was seen to have negative effects
+ in a couple of cases:
+ Merging
+ Sorting with a smaller internal buffer
+ Note this option was seen to shorten the runtime for sort
+ on a multicore system with lots of RAM and other processes
+ competing for CPU. It could be argued that more explicit
+ scheduling hints with 'nice' et. al. are more appropriate
+ for this situation.
+
+ POSIX_FADV_NOREUSE is a possibility as it could lower
+ the priority of input data in the cache as sort will
+ only need to process it once. However its functionality
+ has changed over Linux kernel versions and as of 2.6.31
+ it does nothing and thus we can't depend on what it might
+ do in future.
+
+ POSIX_FADV_DONTNEED is not appropriate for user specified
+ input files, but for temp files we do want to drop the
+ cache immediately after processing. This is done implicitly
+ however when the files are unlinked. */
static FILE *
-xfopen (const char *file, const char *how)
+stream_open (char const *file, char const *how)
{
FILE *fp;
- if (!file)
- fp = stdout;
- else if (STREQ (file, "-") && *how == 'r')
+ if (*how == 'r')
{
- have_read_stdin = true;
- fp = stdin;
+ if (STREQ (file, "-"))
+ {
+ have_read_stdin = true;
+ fp = stdin;
+ }
+ else
+ fp = fopen (file, how);
+ fadvise (fp, FADVISE_SEQUENTIAL);
}
- else
+ else if (*how == 'w')
{
- fp = fopen (file, how);
- if (! fp)
- die (_("open failed"), file);
+ if (file && ftruncate (STDOUT_FILENO, 0) != 0)
+ error (SORT_FAILURE, errno, _("%s: error truncating"),
+ quotef (file));
+ fp = stdout;
}
+ else
+ assert (!"unexpected mode passed to stream_open");
return fp;
}
+/* Same as stream_open, except always return a non-null value; die on
+ failure. */
+
+static FILE *
+xfopen (char const *file, char const *how)
+{
+ FILE *fp = stream_open (file, how);
+ if (!fp)
+ die (_("open failed"), file);
+ return fp;
+}
+
/* Close FP, whose name is FILE, and report any errors. */
static void
@@ -749,32 +988,37 @@ xfclose (FILE *fp, char const *file)
case STDIN_FILENO:
/* Allow reading stdin from tty more than once. */
if (feof (fp))
- clearerr (fp);
+ clearerr (fp);
break;
case STDOUT_FILENO:
/* Don't close stdout just yet. close_stdout does that. */
if (fflush (fp) != 0)
- die (_("fflush failed"), file);
+ die (_("fflush failed"), file);
break;
default:
if (fclose (fp) != 0)
- die (_("close failed"), file);
+ die (_("close failed"), file);
break;
}
}
static void
-dup2_or_die (int oldfd, int newfd)
+move_fd_or_die (int oldfd, int newfd)
{
- if (dup2 (oldfd, newfd) < 0)
- error (SORT_FAILURE, errno, _("dup2 failed"));
+ if (oldfd != newfd)
+ {
+ /* This should never fail for our usage. */
+ dup2 (oldfd, newfd);
+ close (oldfd);
+ }
}
/* Fork a child process for piping to and do common cleanup. The
TRIES parameter tells us how many times to try to fork before
- giving up. Return the PID of the child or -1 if fork failed. */
+ giving up. Return the PID of the child, or -1 (setting errno)
+ on failure. */
static pid_t
pipe_fork (int pipefds[2], size_t tries)
@@ -782,17 +1026,27 @@ pipe_fork (int pipefds[2], size_t tries)
#if HAVE_WORKING_FORK
struct tempnode *saved_temphead;
int saved_errno;
- unsigned int wait_retry = 1;
- pid_t pid IF_LINT (= -1);
+ double wait_retry = 0.25;
+ pid_t pid IF_LINT ( = -1);
struct cs_status cs;
if (pipe (pipefds) < 0)
return -1;
+ /* At least NMERGE + 1 subprocesses are needed. More could be created, but
+ uncontrolled subprocess generation can hurt performance significantly.
+ Allow at most NMERGE + 2 subprocesses, on the theory that there
+ may be some useful parallelism by letting compression for the
+ previous merge finish (1 subprocess) in parallel with the current
+ merge (NMERGE + 1 subprocesses). */
+
+ if (nmerge + 1 < nprocs)
+ reap_some ();
+
while (tries--)
{
/* This is so the child process won't delete our temp files
- if it receives a signal before exec-ing. */
+ if it receives a signal before exec-ing. */
cs = cs_enter ();
saved_temphead = temphead;
temphead = NULL;
@@ -800,25 +1054,27 @@ pipe_fork (int pipefds[2], size_t tries)
pid = fork ();
saved_errno = errno;
if (pid)
- temphead = saved_temphead;
+ temphead = saved_temphead;
cs_leave (cs);
errno = saved_errno;
if (0 <= pid || errno != EAGAIN)
- break;
+ break;
else
- {
- sleep (wait_retry);
- wait_retry *= 2;
- reap_some ();
- }
+ {
+ xnanosleep (wait_retry);
+ wait_retry *= 2;
+ reap_exited ();
+ }
}
if (pid < 0)
{
+ saved_errno = errno;
close (pipefds[0]);
close (pipefds[1]);
+ errno = saved_errno;
}
else if (pid == 0)
{
@@ -835,16 +1091,21 @@ pipe_fork (int pipefds[2], size_t tries)
#endif
}
-/* Create a temporary file and start a compression program to filter output
- to that file. Set *PFP to the file handle and if *PPID is non-NULL,
- set it to the PID of the newly-created process. */
+/* 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 NULL if the failure is due to file descriptor
+ exhaustion and SURVIVE_FD_EXHAUSTION; otherwise, die. */
-static char *
-create_temp (FILE **pfp, pid_t *ppid)
+static struct tempnode *
+maybe_create_temp (FILE **pfp, bool survive_fd_exhaustion)
{
int tempfd;
- struct tempnode *node = create_temp_file (&tempfd);
- char *name = node->name;
+ struct tempnode *node = create_temp_file (&tempfd, survive_fd_exhaustion);
+ if (! node)
+ return NULL;
+
+ node->state = UNCOMPRESSED;
if (compress_program)
{
@@ -852,92 +1113,104 @@ create_temp (FILE **pfp, pid_t *ppid)
node->pid = pipe_fork (pipefds, MAX_FORK_TRIES_COMPRESS);
if (0 < node->pid)
- {
- close (tempfd);
- close (pipefds[0]);
- tempfd = pipefds[1];
+ {
+ close (tempfd);
+ close (pipefds[0]);
+ tempfd = pipefds[1];
- register_proc (node->pid);
- }
+ register_proc (node);
+ }
else if (node->pid == 0)
- {
- close (pipefds[1]);
- dup2_or_die (tempfd, STDOUT_FILENO);
- close (tempfd);
- dup2_or_die (pipefds[0], STDIN_FILENO);
- close (pipefds[0]);
-
- if (execlp (compress_program, compress_program, (char *) NULL) < 0)
- error (SORT_FAILURE, errno, _("couldn't execute %s"),
- compress_program);
- }
- else
- node->pid = 0;
+ {
+ /* Being the child of a multithreaded program before exec(),
+ we're restricted to calling async-signal-safe routines here. */
+ close (pipefds[1]);
+ move_fd_or_die (tempfd, STDOUT_FILENO);
+ move_fd_or_die (pipefds[0], STDIN_FILENO);
+
+ execlp (compress_program, compress_program, (char *) NULL);
+
+ async_safe_die (errno, "couldn't execute compress program");
+ }
}
*pfp = fdopen (tempfd, "w");
if (! *pfp)
- die (_("couldn't create temporary file"), name);
+ die (_("couldn't create temporary file"), node->name);
- if (ppid)
- *ppid = node->pid;
+ return node;
+}
+
+/* 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. Die on failure. */
- return name;
+static struct tempnode *
+create_temp (FILE **pfp)
+{
+ return maybe_create_temp (pfp, false);
}
/* Open a compressed temp file and start a decompression process through
- which to filter the input. PID must be the valid processes ID of the
- process used to compress the file. */
+ 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. */
static FILE *
-open_temp (const char *name, pid_t pid)
+open_temp (struct tempnode *temp)
{
int tempfd, pipefds[2];
- pid_t child_pid;
- FILE *fp;
+ FILE *fp = NULL;
- wait_proc (pid);
+ if (temp->state == UNREAPED)
+ wait_proc (temp->pid);
- tempfd = open (name, O_RDONLY);
+ tempfd = open (temp->name, O_RDONLY);
if (tempfd < 0)
- die (_("couldn't open temporary file"), name);
+ return NULL;
- child_pid = pipe_fork (pipefds, MAX_FORK_TRIES_DECOMPRESS);
- if (0 < child_pid)
+ pid_t child = pipe_fork (pipefds, MAX_FORK_TRIES_DECOMPRESS);
+
+ switch (child)
{
+ case -1:
+ if (errno != EMFILE)
+ error (SORT_FAILURE, errno, _("couldn't create process for %s -d"),
+ quoteaf (compress_program));
close (tempfd);
- close (pipefds[1]);
- }
- else if (child_pid == 0)
- {
+ errno = EMFILE;
+ break;
+
+ case 0:
+ /* Being the child of a multithreaded program before exec(),
+ we're restricted to calling async-signal-safe routines here. */
close (pipefds[0]);
- dup2_or_die (tempfd, STDIN_FILENO);
+ move_fd_or_die (tempfd, STDIN_FILENO);
+ move_fd_or_die (pipefds[1], STDOUT_FILENO);
+
+ execlp (compress_program, compress_program, "-d", (char *) NULL);
+
+ async_safe_die (errno, "couldn't execute compress program (with -d)");
+
+ default:
+ temp->pid = child;
+ register_proc (temp);
close (tempfd);
- dup2_or_die (pipefds[1], STDOUT_FILENO);
close (pipefds[1]);
- if (execlp (compress_program, compress_program, "-d", (char *) NULL) < 0)
- error (SORT_FAILURE, errno, _("couldn't execute %s -d"),
- compress_program);
+ fp = fdopen (pipefds[0], "r");
+ if (! fp)
+ {
+ int saved_errno = errno;
+ close (pipefds[0]);
+ errno = saved_errno;
+ }
+ break;
}
- else
- error (SORT_FAILURE, errno, _("couldn't create process for %s -d"),
- compress_program);
-
- fp = fdopen (pipefds[0], "r");
- if (! fp)
- die (_("couldn't create temporary file"), name);
return fp;
}
-static void
-write_bytes (const char *buf, size_t n_bytes, FILE *fp, const char *output_file)
-{
- if (fwrite (buf, 1, n_bytes, fp) != n_bytes)
- die (_("write failed"), output_file);
-}
-
/* Append DIR to the array of temporary directory names. */
static void
add_temp_dir (char const *dir)
@@ -951,7 +1224,7 @@ add_temp_dir (char const *dir)
/* Remove NAME from the list of temporary files. */
static void
-zaptemp (const char *name)
+zaptemp (char const *name)
{
struct tempnode *volatile *pnode;
struct tempnode *node;
@@ -963,6 +1236,9 @@ zaptemp (const char *name)
for (pnode = &temphead; (node = *pnode)->name != name; pnode = &node->next)
continue;
+ if (node->state == UNREAPED)
+ wait_proc (node->pid);
+
/* Unlink the temporary file in a critical section to avoid races. */
next = node->next;
cs = cs_enter ();
@@ -972,7 +1248,7 @@ zaptemp (const char *name)
cs_leave (cs);
if (unlink_status != 0)
- error (0, unlink_errno, _("warning: cannot remove: %s"), name);
+ error (0, unlink_errno, _("warning: cannot remove: %s"), quotef (name));
if (! next)
temptail = pnode;
free (node);
@@ -981,7 +1257,7 @@ zaptemp (const char *name)
#if HAVE_NL_LANGINFO
static int
-struct_month_cmp (const void *m1, const void *m2)
+struct_month_cmp (void const *m1, void const *m2)
{
struct month const *month1 = m1;
struct month const *month2 = m2;
@@ -999,9 +1275,9 @@ inittables (void)
for (i = 0; i < UCHAR_LIM; ++i)
{
- blanks[i] = !! isblank (i);
+ blanks[i] = field_sep (i);
nonprinting[i] = ! isprint (i);
- nondictionary[i] = ! isalnum (i) && ! isblank (i);
+ nondictionary[i] = ! isalnum (i) && ! field_sep (i);
fold_toupper[i] = toupper (i);
}
@@ -1010,30 +1286,86 @@ inittables (void)
if (hard_LC_TIME)
{
for (i = 0; i < MONTHS_PER_YEAR; i++)
- {
- char const *s;
- size_t s_len;
- size_t j;
- char *name;
-
- s = (char *) nl_langinfo (ABMON_1 + i);
- s_len = strlen (s);
- monthtab[i].name = name = xmalloc (s_len + 1);
- monthtab[i].val = i + 1;
-
- for (j = 0; j < s_len; j++)
- name[j] = fold_toupper[to_uchar (s[j])];
- name[j] = '\0';
- }
- qsort ((void *) monthtab, MONTHS_PER_YEAR,
- sizeof *monthtab, struct_month_cmp);
+ {
+ char const *s;
+ size_t s_len;
+ size_t j, k;
+ char *name;
+
+ s = nl_langinfo (ABMON_1 + i);
+ s_len = strlen (s);
+ monthtab[i].name = name = xmalloc (s_len + 1);
+ monthtab[i].val = i + 1;
+
+ for (j = k = 0; j < s_len; j++)
+ if (! isblank (to_uchar (s[j])))
+ name[k++] = fold_toupper[to_uchar (s[j])];
+ name[k] = '\0';
+ }
+ qsort (monthtab, MONTHS_PER_YEAR, sizeof *monthtab, struct_month_cmp);
}
#endif
}
+/* Specify how many inputs may be merged at once.
+ This may be set on the command-line with the
+ --batch-size option. */
+static void
+specify_nmerge (int oi, char c, char const *s)
+{
+ uintmax_t n;
+ struct rlimit rlimit;
+ enum strtol_error e = xstrtoumax (s, NULL, 10, &n, NULL);
+
+ /* Try to find out how many file descriptors we'll be able
+ to open. We need at least nmerge + 3 (STDIN_FILENO,
+ STDOUT_FILENO and STDERR_FILENO). */
+ unsigned int max_nmerge = ((getrlimit (RLIMIT_NOFILE, &rlimit) == 0
+ ? rlimit.rlim_cur
+ : OPEN_MAX)
+ - 3);
+
+ if (e == LONGINT_OK)
+ {
+ nmerge = n;
+ if (nmerge != n)
+ e = LONGINT_OVERFLOW;
+ else
+ {
+ if (nmerge < 2)
+ {
+ error (0, 0, _("invalid --%s argument %s"),
+ long_options[oi].name, quote (s));
+ error (SORT_FAILURE, 0,
+ _("minimum --%s argument is %s"),
+ long_options[oi].name, quote ("2"));
+ }
+ else if (max_nmerge < nmerge)
+ {
+ e = LONGINT_OVERFLOW;
+ }
+ else
+ return;
+ }
+ }
+
+ if (e == LONGINT_OVERFLOW)
+ {
+ char max_nmerge_buf[INT_BUFSIZE_BOUND (max_nmerge)];
+ error (0, 0, _("--%s argument %s too large"),
+ long_options[oi].name, quote (s));
+ error (SORT_FAILURE, 0,
+ _("maximum --%s argument with current rlimit is %s"),
+ long_options[oi].name,
+ uinttostr (max_nmerge, max_nmerge_buf));
+ }
+ else
+ xstrtol_fatal (e, oi, c, long_options, s);
+}
+
/* Specify the amount of main memory to use when sorting. */
static void
-specify_sort_size (char const *s)
+specify_sort_size (int oi, char c, char const *s)
{
uintmax_t n;
char *suffix;
@@ -1043,9 +1375,9 @@ specify_sort_size (char const *s)
if (e == LONGINT_OK && ISDIGIT (suffix[-1]))
{
if (n <= UINTMAX_MAX / 1024)
- n *= 1024;
+ n *= 1024;
else
- e = LONGINT_OVERFLOW;
+ e = LONGINT_OVERFLOW;
}
/* A 'b' suffix means bytes; a '%' suffix means percent of memory. */
@@ -1053,64 +1385,72 @@ specify_sort_size (char const *s)
switch (suffix[0])
{
case 'b':
- e = LONGINT_OK;
- break;
+ e = LONGINT_OK;
+ break;
case '%':
- {
- double mem = physmem_total () * n / 100;
-
- /* Use "<", not "<=", to avoid problems with rounding. */
- if (mem < UINTMAX_MAX)
- {
- n = mem;
- e = LONGINT_OK;
- }
- else
- e = LONGINT_OVERFLOW;
- }
- break;
+ {
+ double mem = physmem_total () * n / 100;
+
+ /* Use "<", not "<=", to avoid problems with rounding. */
+ if (mem < UINTMAX_MAX)
+ {
+ n = mem;
+ e = LONGINT_OK;
+ }
+ else
+ e = LONGINT_OVERFLOW;
+ }
+ break;
}
if (e == LONGINT_OK)
{
/* If multiple sort sizes are specified, take the maximum, so
- that option order does not matter. */
+ that option order does not matter. */
if (n < sort_size)
- return;
+ return;
sort_size = n;
if (sort_size == n)
- {
- sort_size = MAX (sort_size, MIN_SORT_SIZE);
- return;
- }
+ {
+ sort_size = MAX (sort_size, MIN_SORT_SIZE);
+ return;
+ }
e = LONGINT_OVERFLOW;
}
- STRTOL_FATAL_ERROR (s, _("sort size"), e);
+ xstrtol_fatal (e, oi, c, long_options, s);
+}
+
+/* Specify the number of threads to spawn during internal sort. */
+static size_t
+specify_nthreads (int oi, char c, char const *s)
+{
+ unsigned long int nthreads;
+ enum strtol_error e = xstrtoul (s, NULL, 10, &nthreads, "");
+ if (e == LONGINT_OVERFLOW)
+ return SIZE_MAX;
+ if (e != LONGINT_OK)
+ xstrtol_fatal (e, oi, c, long_options, s);
+ if (SIZE_MAX < nthreads)
+ nthreads = SIZE_MAX;
+ if (nthreads == 0)
+ error (SORT_FAILURE, 0, _("number in parallel must be nonzero"));
+ return nthreads;
}
/* Return the default sort size. */
static size_t
default_sort_size (void)
{
- /* Let MEM be available memory or 1/8 of total memory, whichever
- is greater. */
- double avail = physmem_available ();
- double total = physmem_total ();
- double mem = MAX (avail, total / 8);
- struct rlimit rlimit;
-
- /* Let SIZE be MEM, but no more than the maximum object size or
- system resource limits. Avoid the MIN macro here, as it is not
- quite right when only one argument is floating point. Don't
- bother to check for values like RLIM_INFINITY since in practice
- they are not much less than SIZE_MAX. */
+ /* Let SIZE be MEM, but no more than the maximum object size,
+ total memory, or system resource limits. Don't bother to check
+ for values like RLIM_INFINITY since in practice they are not much
+ less than SIZE_MAX. */
size_t size = SIZE_MAX;
- if (mem < size)
- size = mem;
+ struct rlimit rlimit;
if (getrlimit (RLIMIT_DATA, &rlimit) == 0 && rlimit.rlim_cur < size)
size = rlimit.rlim_cur;
#ifdef RLIMIT_AS
@@ -1129,7 +1469,21 @@ default_sort_size (void)
size = rlimit.rlim_cur / 16 * 15;
#endif
- /* Use no less than the minimum. */
+ /* Let MEM be available memory or 1/8 of total memory, whichever
+ is greater. */
+ double avail = physmem_available ();
+ double total = physmem_total ();
+ double mem = MAX (avail, total / 8);
+
+ /* Leave a 1/4 margin for physical memory. */
+ if (total * 0.75 < size)
+ size = total * 0.75;
+
+ /* Return the minimum of MEM and SIZE, but no less than
+ MIN_SORT_SIZE. Avoid the MIN macro here, as it is not quite
+ right when only one argument is floating point. */
+ if (mem < size)
+ size = mem;
return MAX (size, MIN_SORT_SIZE);
}
@@ -1142,8 +1496,8 @@ default_sort_size (void)
static size_t
sort_buffer_size (FILE *const *fps, size_t nfps,
- char *const *files, size_t nfiles,
- size_t line_bytes)
+ char *const *files, size_t nfiles,
+ size_t line_bytes)
{
/* A bound on the input size. If zero, the bound hasn't been
determined yet. */
@@ -1165,36 +1519,36 @@ sort_buffer_size (FILE *const *fps, size_t nfps,
size_t worst_case;
if ((i < nfps ? fstat (fileno (fps[i]), &st)
- : STREQ (files[i], "-") ? fstat (STDIN_FILENO, &st)
- : stat (files[i], &st))
- != 0)
- die (_("stat failed"), files[i]);
+ : STREQ (files[i], "-") ? fstat (STDIN_FILENO, &st)
+ : stat (files[i], &st))
+ != 0)
+ die (_("stat failed"), files[i]);
if (S_ISREG (st.st_mode))
- file_size = st.st_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;
- file_size = INPUT_FILE_SIZE_GUESS;
- }
+ {
+ /* 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;
+ file_size = INPUT_FILE_SIZE_GUESS;
+ }
if (! size_bound)
- {
- size_bound = sort_size;
- if (! size_bound)
- size_bound = default_sort_size ();
- }
+ {
+ 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. */
+ 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
- || size_bound - size <= worst_case)
- return size_bound;
+ || size_bound - size <= worst_case)
+ return size_bound;
size += worst_case;
}
@@ -1212,15 +1566,15 @@ initbuf (struct buffer *buf, size_t line_bytes, size_t alloc)
size cannot be allocated, repeatedly halve it until allocation
succeeds. The smaller allocation may hurt overall performance,
but that's better than failing. */
- for (;;)
+ while (true)
{
alloc += sizeof (struct line) - alloc % sizeof (struct line);
buf->buf = malloc (alloc);
if (buf->buf)
- break;
+ break;
alloc /= 2;
if (alloc <= line_bytes + 1)
- xalloc_die ();
+ xalloc_die ();
}
buf->line_bytes = line_bytes;
@@ -1234,19 +1588,19 @@ initbuf (struct buffer *buf, size_t line_bytes, size_t alloc)
static inline struct line *
buffer_linelim (struct buffer const *buf)
{
- return (struct line *) (buf->buf + buf->alloc);
+ void *linelim = buf->buf + buf->alloc;
+ return linelim;
}
/* Return a pointer to the first character of the field specified
by KEY in LINE. */
static char *
-begfield (const struct line *line, const struct keyfield *key)
+begfield (struct line const *line, struct keyfield const *key)
{
char *ptr = line->text, *lim = ptr + line->length - 1;
size_t sword = key->sword;
size_t schar = key->schar;
- size_t remaining_bytes;
/* The leading field separator itself is included in a field when -t
is absent. */
@@ -1254,30 +1608,28 @@ begfield (const struct line *line, const struct keyfield *key)
if (tab != TAB_DEFAULT)
while (ptr < lim && sword--)
{
- while (ptr < lim && *ptr != tab)
- ++ptr;
- if (ptr < lim)
- ++ptr;
+ while (ptr < lim && *ptr != tab)
+ ++ptr;
+ if (ptr < lim)
+ ++ptr;
}
else
while (ptr < lim && sword--)
{
- while (ptr < lim && blanks[to_uchar (*ptr)])
- ++ptr;
- while (ptr < lim && !blanks[to_uchar (*ptr)])
- ++ptr;
+ while (ptr < lim && blanks[to_uchar (*ptr)])
+ ++ptr;
+ while (ptr < lim && !blanks[to_uchar (*ptr)])
+ ++ptr;
}
+ /* If we're ignoring leading blanks when computing the Start
+ of the field, skip past them here. */
if (key->skipsblanks)
while (ptr < lim && blanks[to_uchar (*ptr)])
++ptr;
/* Advance PTR by SCHAR (if possible), but no further than LIM. */
- remaining_bytes = lim - ptr;
- if (schar < remaining_bytes)
- ptr += schar;
- else
- ptr = lim;
+ ptr = MIN (lim, ptr + schar);
return ptr;
}
@@ -1286,34 +1638,36 @@ begfield (const struct line *line, const struct keyfield *key)
in LINE specified by KEY. */
static char *
-limfield (const struct line *line, const struct keyfield *key)
+limfield (struct line const *line, struct keyfield const *key)
{
char *ptr = line->text, *lim = ptr + line->length - 1;
size_t eword = key->eword, echar = key->echar;
- size_t remaining_bytes;
+
+ if (echar == 0)
+ eword++; /* Skip all of end field. */
/* Move PTR past EWORD fields or to one past the last byte on LINE,
whichever comes first. If there are more than EWORD fields, leave
PTR pointing at the beginning of the field having zero-based index,
EWORD. If a delimiter character was specified (via -t), then that
- `beginning' is the first character following the delimiting TAB.
- Otherwise, leave PTR pointing at the first `blank' character after
+ 'beginning' is the first character following the delimiting TAB.
+ Otherwise, leave PTR pointing at the first 'blank' character after
the preceding field. */
if (tab != TAB_DEFAULT)
while (ptr < lim && eword--)
{
- while (ptr < lim && *ptr != tab)
- ++ptr;
- if (ptr < lim && (eword | echar))
- ++ptr;
+ while (ptr < lim && *ptr != tab)
+ ++ptr;
+ if (ptr < lim && (eword || echar))
+ ++ptr;
}
else
while (ptr < lim && eword--)
{
- while (ptr < lim && blanks[to_uchar (*ptr)])
- ++ptr;
- while (ptr < lim && !blanks[to_uchar (*ptr)])
- ++ptr;
+ while (ptr < lim && blanks[to_uchar (*ptr)])
+ ++ptr;
+ while (ptr < lim && !blanks[to_uchar (*ptr)])
+ ++ptr;
}
#ifdef POSIX_UNSPECIFIED
@@ -1326,7 +1680,7 @@ limfield (const struct line *line, const struct keyfield *key)
Date: Thu, 30 May 96 12:20:41 -0400
[Translated to POSIX 1003.1-2001 terminology by Paul Eggert.]
- [...]I believe I've found another bug in `sort'.
+ [...]I believe I've found another bug in 'sort'.
$ cat /tmp/sort.in
a b c 2 d
@@ -1353,33 +1707,31 @@ limfield (const struct line *line, const struct keyfield *key)
char *newlim;
newlim = memchr (ptr, tab, lim - ptr);
if (newlim)
- lim = newlim;
+ lim = newlim;
}
else
{
char *newlim;
newlim = ptr;
while (newlim < lim && blanks[to_uchar (*newlim)])
- ++newlim;
+ ++newlim;
while (newlim < lim && !blanks[to_uchar (*newlim)])
- ++newlim;
+ ++newlim;
lim = newlim;
}
#endif
- /* If we're ignoring leading blanks when computing the End
- of the field, don't start counting bytes until after skipping
- past any leading blanks. */
- if (key->skipeblanks)
- while (ptr < lim && blanks[to_uchar (*ptr)])
- ++ptr;
-
- /* Advance PTR by ECHAR (if possible), but no further than LIM. */
- remaining_bytes = lim - ptr;
- if (echar < remaining_bytes)
- ptr += echar;
- else
- ptr = lim;
+ if (echar != 0) /* We need to skip over a portion of the end field. */
+ {
+ /* If we're ignoring leading blanks when computing the End
+ of the field, skip past them here. */
+ if (key->skipeblanks)
+ while (ptr < lim && blanks[to_uchar (*ptr)])
+ ++ptr;
+
+ /* Advance PTR by ECHAR (if possible), but no further than LIM. */
+ ptr = MIN (lim, ptr + echar);
+ }
return ptr;
}
@@ -1408,7 +1760,7 @@ fillbuf (struct buffer *buf, FILE *fp, char const *file)
buf->nlines = 0;
}
- for (;;)
+ while (true)
{
char *ptr = buf->buf + buf->used;
struct line *linelim = buffer_linelim (buf);
@@ -1417,90 +1769,182 @@ fillbuf (struct buffer *buf, FILE *fp, char const *file)
char *line_start = buf->nlines ? line->text + line->length : buf->buf;
while (line_bytes + 1 < avail)
- {
- /* Read as many bytes as possible, but do not read so many
- bytes that there might not be enough room for the
- corresponding line array. The worst case is when the
- rest of the input file consists entirely of newlines,
- except that the last byte is not a newline. */
- size_t readsize = (avail - 1) / (line_bytes + 1);
- size_t bytes_read = fread (ptr, 1, readsize, fp);
- char *ptrlim = ptr + bytes_read;
- char *p;
- avail -= bytes_read;
-
- if (bytes_read != readsize)
- {
- if (ferror (fp))
- die (_("read failed"), file);
- if (feof (fp))
- {
- buf->eof = true;
- if (buf->buf == ptrlim)
- return false;
- if (ptrlim[-1] != eol)
- *ptrlim++ = eol;
- }
- }
-
- /* Find and record each line in the just-read input. */
- while ((p = memchr (ptr, eol, ptrlim - ptr)))
- {
- ptr = p + 1;
- line--;
- line->text = line_start;
- line->length = ptr - line_start;
- mergesize = MAX (mergesize, line->length);
- avail -= line_bytes;
-
- if (key)
- {
- /* Precompute the position of the first key for
- efficiency. */
- line->keylim = (key->eword == SIZE_MAX
- ? p
- : limfield (line, key));
-
- if (key->sword != SIZE_MAX)
- line->keybeg = begfield (line, key);
- else
- {
- if (key->skipsblanks)
- while (blanks[to_uchar (*line_start)])
- line_start++;
- line->keybeg = line_start;
- }
- }
-
- line_start = ptr;
- }
-
- ptr = ptrlim;
- if (buf->eof)
- break;
- }
+ {
+ /* Read as many bytes as possible, but do not read so many
+ bytes that there might not be enough room for the
+ corresponding line array. The worst case is when the
+ rest of the input file consists entirely of newlines,
+ except that the last byte is not a newline. */
+ size_t readsize = (avail - 1) / (line_bytes + 1);
+ size_t bytes_read = fread (ptr, 1, readsize, fp);
+ char *ptrlim = ptr + bytes_read;
+ char *p;
+ avail -= bytes_read;
+
+ if (bytes_read != readsize)
+ {
+ if (ferror (fp))
+ die (_("read failed"), file);
+ if (feof (fp))
+ {
+ buf->eof = true;
+ if (buf->buf == ptrlim)
+ return false;
+ if (line_start != ptrlim && ptrlim[-1] != eol)
+ *ptrlim++ = eol;
+ }
+ }
+
+ /* Find and record each line in the just-read input. */
+ while ((p = memchr (ptr, eol, ptrlim - ptr)))
+ {
+ /* Delimit the line with NUL. This eliminates the need to
+ temporarily replace the last byte with NUL when calling
+ xmemcoll(), which increases performance. */
+ *p = '\0';
+ ptr = p + 1;
+ line--;
+ line->text = line_start;
+ line->length = ptr - line_start;
+ mergesize = MAX (mergesize, line->length);
+ avail -= line_bytes;
+
+ if (key)
+ {
+ /* Precompute the position of the first key for
+ efficiency. */
+ line->keylim = (key->eword == SIZE_MAX
+ ? p
+ : limfield (line, key));
+
+ if (key->sword != SIZE_MAX)
+ line->keybeg = begfield (line, key);
+ else
+ {
+ if (key->skipsblanks)
+ while (blanks[to_uchar (*line_start)])
+ line_start++;
+ line->keybeg = line_start;
+ }
+ }
+
+ line_start = ptr;
+ }
+
+ ptr = ptrlim;
+ if (buf->eof)
+ break;
+ }
buf->used = ptr - buf->buf;
buf->nlines = buffer_linelim (buf) - line;
if (buf->nlines != 0)
- {
- buf->left = ptr - line_start;
- merge_buffer_size = mergesize + MIN_MERGE_BUFFER_SIZE;
- return true;
- }
+ {
+ buf->left = ptr - line_start;
+ merge_buffer_size = mergesize + MIN_MERGE_BUFFER_SIZE;
+ return true;
+ }
- /* The current input line is too long to fit in the buffer.
- Double the buffer size and try again. */
- buf->buf = X2REALLOC (buf->buf, &buf->alloc);
+ {
+ /* The current input line is too long to fit in the buffer.
+ Increase the buffer size and try again, keeping it properly
+ aligned. */
+ size_t line_alloc = buf->alloc / sizeof (struct line);
+ buf->buf = x2nrealloc (buf->buf, &line_alloc, sizeof (struct line));
+ buf->alloc = line_alloc * sizeof (struct line);
+ }
}
}
+/* Table that maps characters to order-of-magnitude values. */
+static char const unit_order[UCHAR_LIM] =
+ {
+#if ! ('K' == 75 && 'M' == 77 && 'G' == 71 && 'T' == 84 && 'P' == 80 \
+ && 'E' == 69 && 'Z' == 90 && 'Y' == 89 && 'k' == 107)
+ /* This initializer syntax works on all C99 hosts. For now, use
+ it only on non-ASCII hosts, to ease the pain of porting to
+ pre-C99 ASCII hosts. */
+ ['K']=1, ['M']=2, ['G']=3, ['T']=4, ['P']=5, ['E']=6, ['Z']=7, ['Y']=8,
+ ['k']=1,
+#else
+ /* Generate the following table with this command:
+ perl -e 'my %a=(k=>1, K=>1, M=>2, G=>3, T=>4, P=>5, E=>6, Z=>7, Y=>8);
+ foreach my $i (0..255) {my $c=chr($i); $a{$c} ||= 0;print "$a{$c}, "}'\
+ |fmt */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 3,
+ 0, 0, 0, 1, 0, 2, 0, 0, 5, 0, 0, 0, 4, 0, 0, 0, 0, 8, 7, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+#endif
+ };
+
+/* Return an integer that represents the order of magnitude of the
+ unit following the number. The number may contain thousands
+ separators and a decimal point, but it may not contain leading blanks.
+ Negative numbers get negative orders; zero numbers have a zero order. */
+
+static int _GL_ATTRIBUTE_PURE
+find_unit_order (char const *number)
+{
+ bool minus_sign = (*number == '-');
+ char const *p = number + minus_sign;
+ int nonzero = 0;
+ unsigned char ch;
+
+ /* Scan to end of number.
+ Decimals or separators not followed by digits stop the scan.
+ Numbers ending in decimals or separators are thus considered
+ to be lacking in units.
+ FIXME: add support for multibyte thousands_sep and decimal_point. */
+
+ do
+ {
+ while (ISDIGIT (ch = *p++))
+ nonzero |= ch - '0';
+ }
+ while (ch == thousands_sep);
+
+ if (ch == decimal_point)
+ while (ISDIGIT (ch = *p++))
+ nonzero |= ch - '0';
+
+ if (nonzero)
+ {
+ int order = unit_order[ch];
+ return (minus_sign ? -order : order);
+ }
+ else
+ return 0;
+}
+
+/* Compare numbers A and B ending in units with SI or IEC prefixes
+ <none/unknown> < K/k < M < G < T < P < E < Z < Y */
+
+static int
+human_numcompare (char const *a, char const *b)
+{
+ while (blanks[to_uchar (*a)])
+ a++;
+ while (blanks[to_uchar (*b)])
+ b++;
+
+ int diff = find_unit_order (a) - find_unit_order (b);
+ return (diff ? diff : strnumcmp (a, b, decimal_point, thousands_sep));
+}
+
/* Compare strings A and B as numbers without explicitly converting them to
machine numbers. Comparatively slow for short strings, but asymptotically
hideously fast. */
static int
-numcompare (const char *a, const char *b)
+numcompare (char const *a, char const *b)
{
while (blanks[to_uchar (*a)])
a++;
@@ -1510,17 +1954,34 @@ numcompare (const char *a, const char *b)
return strnumcmp (a, b, decimal_point, thousands_sep);
}
+/* Work around a problem whereby the long double value returned by glibc's
+ strtold ("NaN", ...) contains uninitialized bits: clear all bytes of
+ A and B before calling strtold. FIXME: remove this function once
+ gnulib guarantees that strtold's result is always well defined. */
static int
-general_numcompare (const char *sa, const char *sb)
+nan_compare (char const *sa, char const *sb)
+{
+ long_double a;
+ memset (&a, 0, sizeof a);
+ a = strtold (sa, NULL);
+
+ long_double b;
+ memset (&b, 0, sizeof b);
+ b = strtold (sb, NULL);
+
+ return memcmp (&a, &b, sizeof a);
+}
+
+static int
+general_numcompare (char const *sa, char const *sb)
{
- /* FIXME: add option to warn about failed conversions. */
/* FIXME: maybe add option to try expensive FP conversion
only if A and B can't be compared more cheaply/accurately. */
char *ea;
char *eb;
- double a = strtod (sa, &ea);
- double b = strtod (sb, &eb);
+ long_double a = strtold (sa, &ea);
+ long_double b = strtold (sb, &eb);
/* Put conversion errors at the start of the collating sequence. */
if (sa == ea)
@@ -1532,31 +1993,24 @@ general_numcompare (const char *sa, const char *sb)
conversion errors but before numbers; sort them by internal
bit-pattern, for lack of a more portable alternative. */
return (a < b ? -1
- : a > b ? 1
- : a == b ? 0
- : b == b ? -1
- : a == a ? 1
- : memcmp ((char *) &a, (char *) &b, sizeof a));
+ : a > b ? 1
+ : a == b ? 0
+ : b == b ? -1
+ : a == a ? 1
+ : nan_compare (sa, sb));
}
-/* Return an integer in 1..12 of the month name MONTH with length LEN.
+/* Return an integer in 1..12 of the month name MONTH.
Return 0 if the name in S is not recognized. */
static int
-getmonth (char const *month, size_t len)
+getmonth (char const *month, char **ea)
{
size_t lo = 0;
size_t hi = MONTHS_PER_YEAR;
- char const *monthlim = month + len;
- for (;;)
- {
- if (month == monthlim)
- return 0;
- if (!blanks[to_uchar (*month)])
- break;
- ++month;
- }
+ while (blanks[to_uchar (*month)])
+ month++;
do
{
@@ -1565,146 +2019,484 @@ getmonth (char const *month, size_t len)
char const *n = monthtab[ix].name;
for (;; m++, n++)
- {
- if (!*n)
- return monthtab[ix].val;
- if (m == monthlim || fold_toupper[to_uchar (*m)] < to_uchar (*n))
- {
- hi = ix;
- break;
- }
- else if (fold_toupper[to_uchar (*m)] > to_uchar (*n))
- {
- lo = ix + 1;
- break;
- }
- }
+ {
+ if (!*n)
+ {
+ if (ea)
+ *ea = (char *) m;
+ return monthtab[ix].val;
+ }
+ if (to_uchar (fold_toupper[to_uchar (*m)]) < to_uchar (*n))
+ {
+ hi = ix;
+ break;
+ }
+ else if (to_uchar (fold_toupper[to_uchar (*m)]) > to_uchar (*n))
+ {
+ lo = ix + 1;
+ break;
+ }
+ }
}
while (lo < hi);
return 0;
}
-/* A source of random data. */
-static struct randread_source *randread_source;
+/* A randomly chosen MD5 state, used for random comparison. */
+static struct md5_ctx random_md5_state;
-/* Return the Ith randomly-generated state. The caller must invoke
- random_state (H) for all H less than I before invoking random_state
- (I). */
+/* Initialize the randomly chosen MD5 state. */
-static struct md5_ctx
-random_state (size_t i)
+static void
+random_md5_state_init (char const *random_source)
{
- /* An array of states resulting from the random data, and counts of
- its used and allocated members. */
- static struct md5_ctx *state;
- static size_t used;
- static size_t allocated;
-
- struct md5_ctx *s = &state[i];
-
- if (used <= i)
- {
- unsigned char buf[MD5_DIGEST_SIZE];
+ unsigned char buf[MD5_DIGEST_SIZE];
+ struct randread_source *r = randread_new (random_source, sizeof buf);
+ if (! r)
+ die (_("open failed"), random_source);
+ randread (r, buf, sizeof buf);
+ if (randread_free (r) != 0)
+ die (_("close failed"), random_source);
+ md5_init_ctx (&random_md5_state);
+ md5_process_bytes (buf, sizeof buf, &random_md5_state);
+}
- used++;
+/* This is like strxfrm, except it reports any error and exits. */
- if (allocated <= i)
- {
- state = X2NREALLOC (state, &allocated);
- s = &state[i];
- }
+static size_t
+xstrxfrm (char *restrict dest, char const *restrict src, size_t destsize)
+{
+ errno = 0;
+ size_t translated_size = strxfrm (dest, src, destsize);
- randread (randread_source, buf, sizeof buf);
- md5_init_ctx (s);
- md5_process_bytes (buf, sizeof buf, s);
+ if (errno)
+ {
+ error (0, errno, _("string transformation failed"));
+ error (0, 0, _("set LC_ALL='C' to work around the problem"));
+ error (SORT_FAILURE, 0,
+ _("the untransformed string was %s"),
+ quotearg_n_style (0, locale_quoting_style, src));
}
- return *s;
+ return translated_size;
}
-/* Compare the hashes of TEXTA with length LENGTHA to those of TEXTB
- with length LENGTHB. Return negative if less, zero if equal,
- positive if greater. */
+/* Compare the keys TEXTA (of length LENA) and TEXTB (of length LENB)
+ using one or more random hash functions. TEXTA[LENA] and
+ TEXTB[LENB] must be zero. */
static int
-cmp_hashes (char const *texta, size_t lena,
- char const *textb, size_t lenb)
+compare_random (char *restrict texta, size_t lena,
+ char *restrict textb, size_t lenb)
{
- /* Try random hashes until a pair of hashes disagree. But if the
- first pair of random hashes agree, check whether the keys are
- identical and if so report no difference. */
- int diff;
- size_t i;
- for (i = 0; ; i++)
+ /* XFRM_DIFF records the equivalent of memcmp on the transformed
+ data. This is used to break ties if there is a checksum
+ collision, and this is good enough given the astronomically low
+ probability of a collision. */
+ int xfrm_diff = 0;
+
+ char stackbuf[4000];
+ char *buf = stackbuf;
+ size_t bufsize = sizeof stackbuf;
+ 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;
+
+ if (hard_LC_COLLATE)
{
- uint32_t dig[2][MD5_DIGEST_SIZE / sizeof (uint32_t)];
- struct md5_ctx s[2];
- s[0] = s[1] = random_state (i);
- md5_process_bytes (texta, lena, &s[0]); md5_finish_ctx (&s[0], dig[0]);
- md5_process_bytes (textb, lenb, &s[1]); md5_finish_ctx (&s[1], dig[1]);
- diff = memcmp (dig[0], dig[1], sizeof dig[0]);
- if (diff != 0)
- break;
- if (i == 0 && lena == lenb && memcmp (texta, textb, lena) == 0)
- break;
+ char const *lima = texta + lena;
+ char const *limb = textb + lenb;
+
+ while (true)
+ {
+ /* Transform the text into the basis of comparison, so that byte
+ strings that would otherwise considered to be equal are
+ considered equal here even if their bytes differ.
+
+ Each time through this loop, transform one
+ null-terminated string's worth from TEXTA or from TEXTB
+ or both. That way, there's no need to store the
+ transformation of the whole line, if it contains many
+ null-terminated strings. */
+
+ /* Store the transformed data into a big-enough buffer. */
+
+ /* A 3X size guess avoids the overhead of calling strxfrm
+ twice on typical implementations. Don't worry about
+ size_t overflow, as the guess need not be correct. */
+ size_t guess_bufsize = 3 * (lena + lenb) + 2;
+ if (bufsize < guess_bufsize)
+ {
+ bufsize = MAX (guess_bufsize, bufsize * 3 / 2);
+ free (allocated);
+ buf = allocated = malloc (bufsize);
+ if (! buf)
+ {
+ buf = stackbuf;
+ bufsize = sizeof stackbuf;
+ }
+ }
+
+ size_t sizea =
+ (texta < lima ? xstrxfrm (buf, texta, bufsize) + 1 : 0);
+ bool a_fits = sizea <= bufsize;
+ size_t sizeb =
+ (textb < limb
+ ? (xstrxfrm ((a_fits ? buf + sizea : NULL), textb,
+ (a_fits ? bufsize - sizea : 0))
+ + 1)
+ : 0);
+
+ if (! (a_fits && sizea + sizeb <= bufsize))
+ {
+ bufsize = sizea + sizeb;
+ if (bufsize < SIZE_MAX / 3)
+ bufsize = bufsize * 3 / 2;
+ free (allocated);
+ buf = allocated = xmalloc (bufsize);
+ if (texta < lima)
+ strxfrm (buf, texta, sizea);
+ if (textb < limb)
+ strxfrm (buf + sizea, textb, sizeb);
+ }
+
+ /* Advance past NULs to the next part of each input string,
+ exiting the loop if both strings are exhausted. When
+ exiting the loop, prepare to finish off the tiebreaker
+ comparison properly. */
+ if (texta < lima)
+ texta += strlen (texta) + 1;
+ if (textb < limb)
+ textb += strlen (textb) + 1;
+ if (! (texta < lima || textb < limb))
+ {
+ lena = sizea; texta = buf;
+ lenb = sizeb; textb = buf + sizea;
+ break;
+ }
+
+ /* Accumulate the transformed data in the corresponding
+ checksums. */
+ md5_process_bytes (buf, sizea, &s[0]);
+ md5_process_bytes (buf + sizea, sizeb, &s[1]);
+
+ /* Update the tiebreaker comparison of the transformed data. */
+ if (! xfrm_diff)
+ {
+ xfrm_diff = memcmp (buf, buf + sizea, MIN (sizea, sizeb));
+ if (! xfrm_diff)
+ xfrm_diff = (sizea > sizeb) - (sizea < sizeb);
+ }
+ }
+ }
+
+ /* Compute and compare the checksums. */
+ md5_process_bytes (texta, lena, &s[0]); md5_finish_ctx (&s[0], dig[0]);
+ md5_process_bytes (textb, lenb, &s[1]); md5_finish_ctx (&s[1], dig[1]);
+ int diff = memcmp (dig[0], dig[1], sizeof dig[0]);
+
+ /* Fall back on the tiebreaker if the checksums collide. */
+ if (! diff)
+ {
+ if (! xfrm_diff)
+ {
+ xfrm_diff = memcmp (texta, textb, MIN (lena, lenb));
+ if (! xfrm_diff)
+ xfrm_diff = (lena > lenb) - (lena < lenb);
+ }
+
+ diff = xfrm_diff;
}
+ free (allocated);
+
return diff;
}
-/* Compare the keys TEXTA (of length LENA) and TEXTB (of length LENB)
- using one or more random hash functions. */
+/* Return the printable width of the block of memory starting at
+ TEXT and ending just before LIM, counting each tab as one byte.
+ FIXME: Should we generally be counting non printable chars? */
-static int
-compare_random (char *restrict texta, size_t lena,
- char *restrict textb, size_t lenb)
+static size_t
+debug_width (char const *text, char const *lim)
{
- int diff;
+ size_t width = mbsnwidth (text, lim - text, 0);
+ while (text < lim)
+ width += (*text++ == '\t');
+ return width;
+}
- if (! hard_LC_COLLATE)
- diff = cmp_hashes (texta, lena, textb, lenb);
+/* For debug mode, "underline" a key at the
+ specified offset and screen width. */
+
+static void
+mark_key (size_t offset, size_t width)
+{
+ while (offset--)
+ putchar (' ');
+
+ if (!width)
+ printf (_("^ no match for key\n"));
else
{
- /* Transform the text into the basis of comparison, so that byte
- strings that would otherwise considered to be equal are
- considered equal here even if their bytes differ. */
+ do
+ putchar ('_');
+ while (--width);
+
+ putchar ('\n');
+ }
+}
- char *buf = NULL;
- char stackbuf[4000];
- size_t tlena = xmemxfrm (stackbuf, sizeof stackbuf, texta, lena);
- bool a_fits = tlena <= sizeof stackbuf;
- size_t tlenb = xmemxfrm ((a_fits ? stackbuf + tlena : NULL),
- (a_fits ? sizeof stackbuf - tlena : 0),
- textb, lenb);
+/* Return true if KEY is a numeric key. */
- if (a_fits && tlena + tlenb <= sizeof stackbuf)
- buf = stackbuf;
- else
- {
- /* Adding 1 to the buffer size lets xmemxfrm run a bit
- faster by avoiding the need for an extra buffer copy. */
- buf = xmalloc (tlena + tlenb + 1);
- xmemxfrm (buf, tlena + 1, texta, lena);
- xmemxfrm (buf + tlena, tlenb + 1, textb, lenb);
- }
+static inline bool
+key_numeric (struct keyfield const *key)
+{
+ return key->numeric || key->general_numeric || key->human_numeric;
+}
- diff = cmp_hashes (buf, tlena, buf + tlena, tlenb);
+/* For LINE, output a debugging line that underlines KEY in LINE.
+ If KEY is null, underline the whole line. */
- if (buf != stackbuf)
- free (buf);
+static void
+debug_key (struct line const *line, struct keyfield const *key)
+{
+ char *text = line->text;
+ char *beg = text;
+ char *lim = text + line->length - 1;
+
+ if (key)
+ {
+ if (key->sword != SIZE_MAX)
+ beg = begfield (line, key);
+ if (key->eword != SIZE_MAX)
+ lim = limfield (line, key);
+
+ if ((key->skipsblanks && key->sword == SIZE_MAX)
+ || key->month || key_numeric (key))
+ {
+ char saved = *lim;
+ *lim = '\0';
+
+ while (blanks[to_uchar (*beg)])
+ beg++;
+
+ char *tighter_lim = beg;
+
+ if (lim < beg)
+ tighter_lim = lim;
+ else if (key->month)
+ getmonth (beg, &tighter_lim);
+ else if (key->general_numeric)
+ ignore_value (strtold (beg, &tighter_lim));
+ else if (key->numeric || key->human_numeric)
+ {
+ char *p = beg + (beg < lim && *beg == '-');
+ bool found_digit = false;
+ unsigned char ch;
+
+ do
+ {
+ while (ISDIGIT (ch = *p++))
+ found_digit = true;
+ }
+ while (ch == thousands_sep);
+
+ if (ch == decimal_point)
+ while (ISDIGIT (ch = *p++))
+ found_digit = true;
+
+ if (found_digit)
+ tighter_lim = p - ! (key->human_numeric && unit_order[ch]);
+ }
+ else
+ tighter_lim = lim;
+
+ *lim = saved;
+ lim = tighter_lim;
+ }
}
- return diff;
+ size_t offset = debug_width (text, beg);
+ size_t width = debug_width (beg, lim);
+ mark_key (offset, width);
+}
+
+/* Debug LINE by underlining its keys. */
+
+static void
+debug_line (struct line const *line)
+{
+ struct keyfield const *key = keylist;
+
+ do
+ debug_key (line, key);
+ while (key && ((key = key->next) || ! (unique || stable)));
+}
+
+/* Return whether sorting options specified for key. */
+
+static bool
+default_key_compare (struct keyfield const *key)
+{
+ return ! (key->ignore
+ || key->translate
+ || key->skipsblanks
+ || key->skipeblanks
+ || key_numeric (key)
+ || key->month
+ || key->version
+ || key->random
+ /* || key->reverse */
+ );
+}
+
+/* Convert a key to the short options used to specify it. */
+
+static void
+key_to_opts (struct keyfield const *key, char *opts)
+{
+ if (key->skipsblanks || key->skipeblanks)
+ *opts++ = 'b';/* either disables global -b */
+ if (key->ignore == nondictionary)
+ *opts++ = 'd';
+ if (key->translate)
+ *opts++ = 'f';
+ if (key->general_numeric)
+ *opts++ = 'g';
+ if (key->human_numeric)
+ *opts++ = 'h';
+ if (key->ignore == nonprinting)
+ *opts++ = 'i';
+ if (key->month)
+ *opts++ = 'M';
+ if (key->numeric)
+ *opts++ = 'n';
+ if (key->random)
+ *opts++ = 'R';
+ if (key->reverse)
+ *opts++ = 'r';
+ if (key->version)
+ *opts++ = 'V';
+ *opts = '\0';
+}
+
+/* Output data independent key warnings to stderr. */
+
+static void
+key_warnings (struct keyfield const *gkey, bool gkey_only)
+{
+ struct keyfield const *key;
+ struct keyfield ugkey = *gkey;
+ unsigned long keynum = 1;
+
+ for (key = keylist; key; key = key->next, keynum++)
+ {
+ if (key->obsolete_used)
+ {
+ size_t sword = key->sword;
+ size_t eword = key->eword;
+ char tmp[INT_BUFSIZE_BOUND (uintmax_t)];
+ /* obsolescent syntax +A.x -B.y is equivalent to:
+ -k A+1.x+1,B.y (when y = 0)
+ -k A+1.x+1,B+1.y (when y > 0) */
+ char obuf[INT_BUFSIZE_BOUND (sword) * 2 + 4]; /* +# -# */
+ char nbuf[INT_BUFSIZE_BOUND (sword) * 2 + 5]; /* -k #,# */
+ char *po = obuf;
+ char *pn = nbuf;
+
+ if (sword == SIZE_MAX)
+ sword++;
+
+ po = stpcpy (stpcpy (po, "+"), umaxtostr (sword, tmp));
+ pn = stpcpy (stpcpy (pn, "-k "), umaxtostr (sword + 1, tmp));
+ if (key->eword != SIZE_MAX)
+ {
+ stpcpy (stpcpy (po, " -"), umaxtostr (eword + 1, tmp));
+ stpcpy (stpcpy (pn, ","),
+ umaxtostr (eword + 1
+ + (key->echar == SIZE_MAX), tmp));
+ }
+ error (0, 0, _("obsolescent key %s used; consider %s instead"),
+ quote_n (0, obuf), quote_n (1, nbuf));
+ }
+
+ /* Warn about field specs that will never match. */
+ if (key->sword != SIZE_MAX && key->eword < key->sword)
+ error (0, 0, _("key %lu has zero width and will be ignored"), keynum);
+
+ /* Warn about significant leading blanks. */
+ bool implicit_skip = key_numeric (key) || key->month;
+ bool maybe_space_aligned = !hard_LC_COLLATE && default_key_compare (key)
+ && !(key->schar || key->echar);
+ bool line_offset = key->eword == 0 && key->echar != 0; /* -k1.x,1.y */
+ if (!gkey_only && tab == TAB_DEFAULT && !line_offset
+ && ((!key->skipsblanks && !(implicit_skip || maybe_space_aligned))
+ || (!key->skipsblanks && key->schar)
+ || (!key->skipeblanks && key->echar)))
+ error (0, 0, _("leading blanks are significant in key %lu; "
+ "consider also specifying 'b'"), keynum);
+
+ /* Warn about numeric comparisons spanning fields,
+ as field delimiters could be interpreted as part
+ of the number (maybe only in other locales). */
+ if (!gkey_only && key_numeric (key))
+ {
+ size_t sword = key->sword + 1;
+ size_t eword = key->eword + 1;
+ if (!sword)
+ sword++;
+ if (!eword || sword < eword)
+ error (0, 0, _("key %lu is numeric and spans multiple fields"),
+ keynum);
+ }
+
+ /* Flag global options not copied or specified in any key. */
+ if (ugkey.ignore && (ugkey.ignore == key->ignore))
+ ugkey.ignore = NULL;
+ if (ugkey.translate && (ugkey.translate == key->translate))
+ ugkey.translate = NULL;
+ ugkey.skipsblanks &= !key->skipsblanks;
+ ugkey.skipeblanks &= !key->skipeblanks;
+ ugkey.month &= !key->month;
+ ugkey.numeric &= !key->numeric;
+ ugkey.general_numeric &= !key->general_numeric;
+ ugkey.human_numeric &= !key->human_numeric;
+ ugkey.random &= !key->random;
+ ugkey.version &= !key->version;
+ ugkey.reverse &= !key->reverse;
+ }
+
+ /* Warn about ignored global options flagged above.
+ Note if gkey is the only one in the list, all flags are cleared. */
+ if (!default_key_compare (&ugkey)
+ || (ugkey.reverse && (stable || unique) && keylist))
+ {
+ bool ugkey_reverse = ugkey.reverse;
+ if (!(stable || unique))
+ ugkey.reverse = false;
+ /* The following is too big, but guaranteed to be "big enough". */
+ char opts[sizeof short_options];
+ key_to_opts (&ugkey, opts);
+ error (0, 0,
+ ngettext ("option '-%s' is ignored",
+ "options '-%s' are ignored",
+ select_plural (strlen (opts))), opts);
+ ugkey.reverse = ugkey_reverse;
+ }
+ if (ugkey.reverse && !(stable || unique) && keylist)
+ error (0, 0, _("option '-r' only applies to last-resort comparison"));
}
/* Compare two lines A and B trying every key in sequence until there
are no more keys or a difference is found. */
static int
-keycompare (const struct line *a, const struct line *b)
+keycompare (struct line const *a, struct line const *b)
{
- struct keyfield const *key = keylist;
+ struct keyfield *key = keylist;
/* For the first iteration only, the key positions have been
precomputed for us. */
@@ -1715,156 +2507,184 @@ keycompare (const struct line *a, const struct line *b)
int diff;
- for (;;)
+ while (true)
{
char const *translate = key->translate;
bool const *ignore = key->ignore;
+ /* Treat field ends before field starts as empty fields. */
+ lima = MAX (texta, lima);
+ limb = MAX (textb, limb);
+
/* Find the lengths. */
- size_t lena = lima <= texta ? 0 : lima - texta;
- size_t lenb = limb <= textb ? 0 : limb - textb;
-
- /* Actually compare the fields. */
-
- if (key->random)
- diff = compare_random (texta, lena, textb, lenb);
- else if (key->numeric | key->general_numeric)
- {
- char savea = *lima, saveb = *limb;
-
- *lima = *limb = '\0';
- diff = ((key->numeric ? numcompare : general_numcompare)
- (texta, textb));
- *lima = savea, *limb = saveb;
- }
- else if (key->month)
- diff = getmonth (texta, lena) - getmonth (textb, lenb);
- /* Sorting like this may become slow, so in a simple locale the user
- can select a faster sort that is similar to ascii sort. */
- else if (hard_LC_COLLATE)
- {
- if (ignore || translate)
- {
- char buf[4000];
- size_t size = lena + 1 + lenb + 1;
- char *copy_a = (size <= sizeof buf ? buf : xmalloc (size));
- char *copy_b = copy_a + lena + 1;
- size_t new_len_a, new_len_b, i;
-
- /* Ignore and/or translate chars before comparing. */
- for (new_len_a = new_len_b = i = 0; i < MAX (lena, lenb); i++)
- {
- if (i < lena)
- {
- copy_a[new_len_a] = (translate
- ? translate[to_uchar (texta[i])]
- : texta[i]);
- if (!ignore || !ignore[to_uchar (texta[i])])
- ++new_len_a;
- }
- if (i < lenb)
- {
- copy_b[new_len_b] = (translate
- ? translate[to_uchar (textb[i])]
- : textb [i]);
- if (!ignore || !ignore[to_uchar (textb[i])])
- ++new_len_b;
- }
- }
-
- diff = xmemcoll (copy_a, new_len_a, copy_b, new_len_b);
-
- if (sizeof buf < size)
- free (copy_a);
- }
- else if (lena == 0)
- diff = - NONZERO (lenb);
- else if (lenb == 0)
- goto greater;
- else
- diff = xmemcoll (texta, lena, textb, lenb);
- }
+ size_t lena = lima - texta;
+ size_t lenb = limb - textb;
+
+ if (hard_LC_COLLATE || key_numeric (key)
+ || key->month || key->random || key->version)
+ {
+ char *ta;
+ char *tb;
+ size_t tlena;
+ size_t tlenb;
+
+ char enda IF_LINT (= 0);
+ char endb IF_LINT (= 0);
+ void *allocated IF_LINT (= NULL);
+ char stackbuf[4000];
+
+ if (ignore || translate)
+ {
+ /* Compute with copies of the keys, which are the result of
+ translating or ignoring characters, and which need their
+ own storage. */
+
+ size_t i;
+
+ /* Allocate space for copies. */
+ size_t size = lena + 1 + lenb + 1;
+ if (size <= sizeof stackbuf)
+ ta = stackbuf, allocated = NULL;
+ else
+ ta = allocated = xmalloc (size);
+ tb = ta + lena + 1;
+
+ /* Put into each copy a version of the key in which the
+ requested characters are ignored or translated. */
+ for (tlena = i = 0; i < lena; i++)
+ if (! (ignore && ignore[to_uchar (texta[i])]))
+ ta[tlena++] = (translate
+ ? translate[to_uchar (texta[i])]
+ : texta[i]);
+ ta[tlena] = '\0';
+
+ for (tlenb = i = 0; i < lenb; i++)
+ if (! (ignore && ignore[to_uchar (textb[i])]))
+ tb[tlenb++] = (translate
+ ? translate[to_uchar (textb[i])]
+ : textb[i]);
+ tb[tlenb] = '\0';
+ }
+ else
+ {
+ /* Use the keys in-place, temporarily null-terminated. */
+ ta = texta; tlena = lena; enda = ta[tlena]; ta[tlena] = '\0';
+ tb = textb; tlenb = lenb; endb = tb[tlenb]; tb[tlenb] = '\0';
+ }
+
+ if (key->numeric)
+ diff = numcompare (ta, tb);
+ else if (key->general_numeric)
+ diff = general_numcompare (ta, tb);
+ else if (key->human_numeric)
+ diff = human_numcompare (ta, tb);
+ else if (key->month)
+ diff = getmonth (ta, NULL) - getmonth (tb, NULL);
+ else if (key->random)
+ diff = compare_random (ta, tlena, tb, tlenb);
+ else if (key->version)
+ diff = filevercmp (ta, tb);
+ else
+ {
+ /* Locale-dependent string sorting. This is slower than
+ C-locale sorting, which is implemented below. */
+ if (tlena == 0)
+ diff = - NONZERO (tlenb);
+ else if (tlenb == 0)
+ diff = 1;
+ else
+ diff = xmemcoll0 (ta, tlena + 1, tb, tlenb + 1);
+ }
+
+ if (ignore || translate)
+ free (allocated);
+ else
+ {
+ ta[tlena] = enda;
+ tb[tlenb] = endb;
+ }
+ }
else if (ignore)
- {
+ {
#define CMP_WITH_IGNORE(A, B) \
do \
{ \
- for (;;) \
- { \
- while (texta < lima && ignore[to_uchar (*texta)]) \
- ++texta; \
- while (textb < limb && ignore[to_uchar (*textb)]) \
- ++textb; \
- if (! (texta < lima && textb < limb)) \
- break; \
- diff = to_uchar (A) - to_uchar (B); \
- if (diff) \
- goto not_equal; \
- ++texta; \
- ++textb; \
- } \
- \
- diff = (texta < lima) - (textb < limb); \
+ while (true) \
+ { \
+ while (texta < lima && ignore[to_uchar (*texta)]) \
+ ++texta; \
+ while (textb < limb && ignore[to_uchar (*textb)]) \
+ ++textb; \
+ if (! (texta < lima && textb < limb)) \
+ break; \
+ diff = to_uchar (A) - to_uchar (B); \
+ if (diff) \
+ goto not_equal; \
+ ++texta; \
+ ++textb; \
+ } \
+ \
+ diff = (texta < lima) - (textb < limb); \
} \
while (0)
- if (translate)
- CMP_WITH_IGNORE (translate[to_uchar (*texta)],
- translate[to_uchar (*textb)]);
- else
- CMP_WITH_IGNORE (*texta, *textb);
- }
+ if (translate)
+ CMP_WITH_IGNORE (translate[to_uchar (*texta)],
+ translate[to_uchar (*textb)]);
+ else
+ CMP_WITH_IGNORE (*texta, *textb);
+ }
else if (lena == 0)
- diff = - NONZERO (lenb);
+ diff = - NONZERO (lenb);
else if (lenb == 0)
- goto greater;
+ goto greater;
else
- {
- if (translate)
- {
- while (texta < lima && textb < limb)
- {
- diff = (to_uchar (translate[to_uchar (*texta++)])
- - to_uchar (translate[to_uchar (*textb++)]));
- if (diff)
- goto not_equal;
- }
- }
- else
- {
- diff = memcmp (texta, textb, MIN (lena, lenb));
- if (diff)
- goto not_equal;
- }
- diff = lena < lenb ? -1 : lena != lenb;
- }
+ {
+ if (translate)
+ {
+ while (texta < lima && textb < limb)
+ {
+ diff = (to_uchar (translate[to_uchar (*texta++)])
+ - to_uchar (translate[to_uchar (*textb++)]));
+ if (diff)
+ goto not_equal;
+ }
+ }
+ else
+ {
+ diff = memcmp (texta, textb, MIN (lena, lenb));
+ if (diff)
+ goto not_equal;
+ }
+ diff = lena < lenb ? -1 : lena != lenb;
+ }
if (diff)
- goto not_equal;
+ goto not_equal;
key = key->next;
if (! key)
- break;
+ break;
/* Find the beginning and limit of the next field. */
if (key->eword != SIZE_MAX)
- lima = limfield (a, key), limb = limfield (b, key);
+ lima = limfield (a, key), limb = limfield (b, key);
else
- lima = a->text + a->length - 1, limb = b->text + b->length - 1;
+ lima = a->text + a->length - 1, limb = b->text + b->length - 1;
if (key->sword != SIZE_MAX)
- texta = begfield (a, key), textb = begfield (b, key);
+ texta = begfield (a, key), textb = begfield (b, key);
else
- {
- texta = a->text, textb = b->text;
- if (key->skipsblanks)
- {
- while (texta < lima && blanks[to_uchar (*texta)])
- ++texta;
- while (textb < limb && blanks[to_uchar (*textb)])
- ++textb;
- }
- }
+ {
+ texta = a->text, textb = b->text;
+ if (key->skipsblanks)
+ {
+ while (texta < lima && blanks[to_uchar (*texta)])
+ ++texta;
+ while (textb < limb && blanks[to_uchar (*textb)])
+ ++textb;
+ }
+ }
}
return 0;
@@ -1879,7 +2699,7 @@ keycompare (const struct line *a, const struct line *b)
depending on whether A compares less than, equal to, or greater than B. */
static int
-compare (const struct line *a, const struct line *b)
+compare (struct line const *a, struct line const *b)
{
int diff;
size_t alen, blen;
@@ -1890,8 +2710,8 @@ compare (const struct line *a, const struct line *b)
if (keylist)
{
diff = keycompare (a, b);
- if (diff | unique | stable)
- return diff;
+ if (diff || unique || stable)
+ return diff;
}
/* If the keys all compare equal (or no keys were specified)
@@ -1903,13 +2723,58 @@ compare (const struct line *a, const struct line *b)
else if (blen == 0)
diff = 1;
else if (hard_LC_COLLATE)
- diff = xmemcoll (a->text, alen, b->text, blen);
+ {
+ /* Note xmemcoll0 is a performance enhancement as
+ it will not unconditionally write '\0' after the
+ passed in buffers, which was seen to give around
+ a 3% increase in performance for short lines. */
+ diff = xmemcoll0 (a->text, alen + 1, b->text, blen + 1);
+ }
else if (! (diff = memcmp (a->text, b->text, MIN (alen, blen))))
diff = alen < blen ? -1 : alen != blen;
return reverse ? -diff : diff;
}
+/* Write LINE to output stream FP; the output file's name is
+ OUTPUT_FILE if OUTPUT_FILE is non-null, and is the standard output
+ otherwise. If debugging is enabled and FP is standard output,
+ append some debugging information. */
+
+static void
+write_line (struct line const *line, FILE *fp, char const *output_file)
+{
+ char *buf = line->text;
+ size_t n_bytes = line->length;
+ char *ebuf = buf + n_bytes;
+
+ if (!output_file && debug)
+ {
+ /* Convert TAB to '>' and EOL to \n, and then output debugging info. */
+ char const *c = buf;
+
+ while (c < ebuf)
+ {
+ char wc = *c++;
+ if (wc == '\t')
+ wc = '>';
+ else if (c == ebuf)
+ wc = '\n';
+ if (fputc (wc, fp) == EOF)
+ die (_("write failed"), output_file);
+ }
+
+ debug_line (line);
+ }
+ else
+ {
+ ebuf[-1] = eolchar;
+ if (fwrite (buf, 1, n_bytes, fp) != n_bytes)
+ die (_("write failed"), output_file);
+ ebuf[-1] = '\0';
+ }
+}
+
/* Check that the lines read from FILE_NAME come in order. Return
true if they are in order. If CHECKONLY == 'c', also print a
diagnostic (FILE_NAME, line number, contents of line) to stderr if
@@ -1928,7 +2793,7 @@ check (char const *file_name, char checkonly)
bool ordered = true;
initbuf (&buf, sizeof (struct line),
- MAX (merge_buffer_size, sort_size));
+ MAX (merge_buffer_size, sort_size));
temp.text = NULL;
while (fillbuf (&buf, fp, file_name))
@@ -1937,59 +2802,59 @@ check (char const *file_name, char checkonly)
struct line const *linebase = line - buf.nlines;
/* Make sure the line saved from the old buffer contents is
- less than or equal to the first line of the new buffer. */
+ less than or equal to the first line of the new buffer. */
if (alloc && nonunique <= compare (&temp, line - 1))
- {
- found_disorder:
- {
- if (checkonly == 'c')
- {
- struct line const *disorder_line = line - 1;
- uintmax_t disorder_line_number =
- buffer_linelim (&buf) - disorder_line + line_number;
- char hr_buf[INT_BUFSIZE_BOUND (uintmax_t)];
- fprintf (stderr, _("%s: %s:%s: disorder: "),
- program_name, file_name,
- umaxtostr (disorder_line_number, hr_buf));
- write_bytes (disorder_line->text, disorder_line->length,
- stderr, _("standard error"));
- }
-
- ordered = false;
- break;
- }
- }
+ {
+ found_disorder:
+ {
+ if (checkonly == 'c')
+ {
+ struct line const *disorder_line = line - 1;
+ uintmax_t disorder_line_number =
+ buffer_linelim (&buf) - disorder_line + line_number;
+ char hr_buf[INT_BUFSIZE_BOUND (disorder_line_number)];
+ fprintf (stderr, _("%s: %s:%s: disorder: "),
+ program_name, file_name,
+ umaxtostr (disorder_line_number, hr_buf));
+ write_line (disorder_line, stderr, _("standard error"));
+ }
+
+ ordered = false;
+ break;
+ }
+ }
/* Compare each line in the buffer with its successor. */
while (linebase < --line)
- if (nonunique <= compare (line, line - 1))
- goto found_disorder;
+ if (nonunique <= compare (line, line - 1))
+ goto found_disorder;
line_number += buf.nlines;
/* Save the last line of the buffer. */
if (alloc < line->length)
- {
- do
- {
- alloc *= 2;
- if (! alloc)
- {
- alloc = line->length;
- break;
- }
- }
- while (alloc < line->length);
-
- temp.text = xrealloc (temp.text, alloc);
- }
+ {
+ do
+ {
+ alloc *= 2;
+ if (! alloc)
+ {
+ alloc = line->length;
+ break;
+ }
+ }
+ while (alloc < line->length);
+
+ free (temp.text);
+ temp.text = xmalloc (alloc);
+ }
memcpy (temp.text, line->text, line->length);
temp.length = line->length;
if (key)
- {
- temp.keybeg = temp.text + (line->keybeg - line->text);
- temp.keylim = temp.text + (line->keylim - line->text);
- }
+ {
+ temp.keybeg = temp.text + (line->keybeg - line->text);
+ temp.keylim = temp.text + (line->keylim - line->text);
+ }
}
xfclose (fp, file_name);
@@ -1998,29 +2863,56 @@ check (char const *file_name, char checkonly)
return ordered;
}
+/* Open FILES (there are NFILES of them) and store the resulting array
+ of stream pointers into (*PFPS). Allocate the array. Return the
+ number of successfully opened files, setting errno if this value is
+ less than NFILES. */
+
+static size_t
+open_input_files (struct sortfile *files, size_t nfiles, FILE ***pfps)
+{
+ FILE **fps = *pfps = xnmalloc (nfiles, sizeof *fps);
+ int i;
+
+ /* Open as many input files as we can. */
+ for (i = 0; i < nfiles; i++)
+ {
+ fps[i] = (files[i].temp && files[i].temp->state != UNCOMPRESSED
+ ? open_temp (files[i].temp)
+ : stream_open (files[i].name, "r"));
+ if (!fps[i])
+ break;
+ }
+
+ return i;
+}
+
/* Merge lines from FILES onto OFP. NTEMPS is the number of temporary
files (all of which are at the start of the FILES array), and
NFILES is the number of files; 0 <= NTEMPS <= NFILES <= NMERGE.
- Close input and output files before returning.
+ FPS is the vector of open stream corresponding to the files.
+ Close input and output streams before returning.
OUTPUT_FILE gives the name of the output file. If it is NULL,
- the output file is standard output. If OFP is NULL, the output
- file has not been opened yet (or written to, if standard output). */
+ the output file is standard output. */
static void
mergefps (struct sortfile *files, size_t ntemps, size_t nfiles,
- FILE *ofp, char const *output_file)
+ FILE *ofp, char const *output_file, FILE **fps)
{
- FILE *fps[NMERGE]; /* Input streams for each file. */
- struct buffer buffer[NMERGE]; /* Input buffers for each file. */
+ 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 = NULL;
- /* &saved if there is a saved line. */
+ /* &saved if there is a saved line. */
size_t savealloc = 0; /* Size allocated for the saved line. */
- struct line const *cur[NMERGE]; /* Current line in each line table. */
- struct line const *base[NMERGE]; /* Base of each line table. */
- size_t ord[NMERGE]; /* Table representing a permutation of fps,
- such that cur[ord[0]] is the smallest line
- and will be next output. */
+ struct line const **cur = xnmalloc (nfiles, sizeof *cur);
+ /* Current line in each line table. */
+ struct line const **base = xnmalloc (nfiles, sizeof *base);
+ /* Base of each line table. */
+ size_t *ord = xnmalloc (nfiles, sizeof *ord);
+ /* 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;
@@ -2030,36 +2922,33 @@ mergefps (struct sortfile *files, size_t ntemps, size_t nfiles,
/* Read initial lines from each input file. */
for (i = 0; i < nfiles; )
{
- fps[i] = (files[i].pid
- ? open_temp (files[i].name, files[i].pid)
- : xfopen (files[i].name, "r"));
initbuf (&buffer[i], sizeof (struct line),
- MAX (merge_buffer_size, sort_size / nfiles));
+ MAX (merge_buffer_size, sort_size / nfiles));
if (fillbuf (&buffer[i], fps[i], files[i].name))
- {
- struct line const *linelim = buffer_linelim (&buffer[i]);
- cur[i] = linelim - 1;
- base[i] = linelim - buffer[i].nlines;
- i++;
- }
+ {
+ struct line const *linelim = buffer_linelim (&buffer[i]);
+ cur[i] = linelim - 1;
+ base[i] = linelim - buffer[i].nlines;
+ i++;
+ }
else
- {
- /* fps[i] is empty; eliminate it from future consideration. */
- xfclose (fps[i], files[i].name);
- if (i < ntemps)
- {
- ntemps--;
- zaptemp (files[i].name);
- }
- free (buffer[i].buf);
- --nfiles;
- for (j = i; j < nfiles; ++j)
- files[j] = files[j + 1];
- }
- }
-
- if (! ofp)
- ofp = xfopen (output_file, "w");
+ {
+ /* fps[i] is empty; eliminate it from future consideration. */
+ xfclose (fps[i], files[i].name);
+ if (i < ntemps)
+ {
+ ntemps--;
+ zaptemp (files[i].name);
+ }
+ free (buffer[i].buf);
+ --nfiles;
+ for (j = i; j < nfiles; ++j)
+ {
+ files[j] = files[j + 1];
+ fps[j] = fps[j + 1];
+ }
+ }
+ }
/* Set up the ord table according to comparisons among input lines.
Since this only reorders two items if one is strictly greater than
@@ -2076,161 +2965,190 @@ mergefps (struct sortfile *files, size_t ntemps, size_t nfiles,
struct line const *smallest = cur[ord[0]];
/* If uniquified output is turned on, output only the first of
- an identical series of lines. */
+ an identical series of lines. */
if (unique)
- {
- if (savedline && compare (savedline, smallest))
- {
- savedline = NULL;
- write_bytes (saved.text, saved.length, ofp, output_file);
- }
- if (!savedline)
- {
- savedline = &saved;
- if (savealloc < smallest->length)
- {
- do
- if (! savealloc)
- {
- savealloc = smallest->length;
- break;
- }
- while ((savealloc *= 2) < smallest->length);
-
- saved.text = xrealloc (saved.text, savealloc);
- }
- saved.length = smallest->length;
- memcpy (saved.text, smallest->text, saved.length);
- if (key)
- {
- saved.keybeg =
- saved.text + (smallest->keybeg - smallest->text);
- saved.keylim =
- saved.text + (smallest->keylim - smallest->text);
- }
- }
- }
+ {
+ if (savedline && compare (savedline, smallest))
+ {
+ savedline = NULL;
+ write_line (&saved, ofp, output_file);
+ }
+ if (!savedline)
+ {
+ savedline = &saved;
+ if (savealloc < smallest->length)
+ {
+ do
+ if (! savealloc)
+ {
+ savealloc = smallest->length;
+ break;
+ }
+ while ((savealloc *= 2) < smallest->length);
+
+ free (saved.text);
+ saved.text = xmalloc (savealloc);
+ }
+ saved.length = smallest->length;
+ memcpy (saved.text, smallest->text, saved.length);
+ if (key)
+ {
+ saved.keybeg =
+ saved.text + (smallest->keybeg - smallest->text);
+ saved.keylim =
+ saved.text + (smallest->keylim - smallest->text);
+ }
+ }
+ }
else
- write_bytes (smallest->text, smallest->length, ofp, output_file);
+ write_line (smallest, ofp, output_file);
/* Check if we need to read more lines into core. */
if (base[ord[0]] < smallest)
- cur[ord[0]] = smallest - 1;
+ cur[ord[0]] = smallest - 1;
else
- {
- if (fillbuf (&buffer[ord[0]], fps[ord[0]], files[ord[0]].name))
- {
- struct line const *linelim = buffer_linelim (&buffer[ord[0]]);
- cur[ord[0]] = linelim - 1;
- base[ord[0]] = linelim - buffer[ord[0]].nlines;
- }
- else
- {
- /* We reached EOF on fps[ord[0]]. */
- for (i = 1; i < nfiles; ++i)
- if (ord[i] > ord[0])
- --ord[i];
- --nfiles;
- xfclose (fps[ord[0]], files[ord[0]].name);
- if (ord[0] < ntemps)
- {
- ntemps--;
- zaptemp (files[ord[0]].name);
- }
- free (buffer[ord[0]].buf);
- for (i = ord[0]; i < nfiles; ++i)
- {
- fps[i] = fps[i + 1];
- files[i] = files[i + 1];
- buffer[i] = buffer[i + 1];
- cur[i] = cur[i + 1];
- base[i] = base[i + 1];
- }
- for (i = 0; i < nfiles; ++i)
- ord[i] = ord[i + 1];
- continue;
- }
- }
+ {
+ if (fillbuf (&buffer[ord[0]], fps[ord[0]], files[ord[0]].name))
+ {
+ struct line const *linelim = buffer_linelim (&buffer[ord[0]]);
+ cur[ord[0]] = linelim - 1;
+ base[ord[0]] = linelim - buffer[ord[0]].nlines;
+ }
+ else
+ {
+ /* We reached EOF on fps[ord[0]]. */
+ for (i = 1; i < nfiles; ++i)
+ if (ord[i] > ord[0])
+ --ord[i];
+ --nfiles;
+ xfclose (fps[ord[0]], files[ord[0]].name);
+ if (ord[0] < ntemps)
+ {
+ ntemps--;
+ zaptemp (files[ord[0]].name);
+ }
+ free (buffer[ord[0]].buf);
+ for (i = ord[0]; i < nfiles; ++i)
+ {
+ fps[i] = fps[i + 1];
+ files[i] = files[i + 1];
+ buffer[i] = buffer[i + 1];
+ cur[i] = cur[i + 1];
+ base[i] = base[i + 1];
+ }
+ for (i = 0; i < nfiles; ++i)
+ ord[i] = ord[i + 1];
+ continue;
+ }
+ }
/* The new line just read in may be larger than other lines
- already in main memory; push it back in the queue until we
- encounter a line larger than it. Optimize for the common
- case where the new line is smallest. */
+ already in main memory; push it back in the queue until we
+ encounter a line larger than it. Optimize for the common
+ case where the new line is smallest. */
{
- size_t lo = 1;
- size_t hi = nfiles;
- size_t probe = lo;
- size_t ord0 = ord[0];
- size_t count_of_smaller_lines;
-
- while (lo < hi)
- {
- int cmp = compare (cur[ord0], cur[ord[probe]]);
- if (cmp < 0 || (cmp == 0 && ord0 < ord[probe]))
- hi = probe;
- else
- lo = probe + 1;
- probe = (lo + hi) / 2;
- }
-
- count_of_smaller_lines = lo - 1;
- for (j = 0; j < count_of_smaller_lines; j++)
- ord[j] = ord[j + 1];
- ord[count_of_smaller_lines] = ord0;
+ size_t lo = 1;
+ size_t hi = nfiles;
+ size_t probe = lo;
+ size_t ord0 = ord[0];
+ size_t count_of_smaller_lines;
+
+ while (lo < hi)
+ {
+ int cmp = compare (cur[ord0], cur[ord[probe]]);
+ if (cmp < 0 || (cmp == 0 && ord0 < ord[probe]))
+ hi = probe;
+ else
+ lo = probe + 1;
+ probe = (lo + hi) / 2;
+ }
+
+ count_of_smaller_lines = lo - 1;
+ for (j = 0; j < count_of_smaller_lines; j++)
+ ord[j] = ord[j + 1];
+ ord[count_of_smaller_lines] = ord0;
}
-
- /* Free up some resources every once in a while. */
- if (MAX_PROCS_BEFORE_REAP < nprocs)
- reap_some ();
}
if (unique && savedline)
{
- write_bytes (saved.text, saved.length, ofp, output_file);
+ write_line (&saved, ofp, output_file);
free (saved.text);
}
xfclose (ofp, output_file);
+ free (fps);
+ free (buffer);
+ free (ord);
+ free (base);
+ free (cur);
}
-/* Merge into T the two sorted arrays of lines LO (with NLO members)
- and HI (with NHI members). T, LO, and HI point just past their
- respective arrays, and the arrays are in reverse order. NLO and
- NHI must be positive, and HI - NHI must equal T - (NLO + NHI). */
+/* Merge lines from FILES onto OFP. NTEMPS is the number of temporary
+ files (all of which are at the start of the FILES array), and
+ NFILES is the number of files; 0 <= NTEMPS <= NFILES <= NMERGE.
+ Close input and output files before returning.
+ OUTPUT_FILE gives the name of the output file.
+
+ Return the number of files successfully merged. This number can be
+ less than NFILES if we ran low on file descriptors, but in this
+ case it is never less than 2. */
-static inline void
-mergelines (struct line *t,
- struct line const *lo, size_t nlo,
- struct line const *hi, size_t nhi)
+static size_t
+mergefiles (struct sortfile *files, size_t ntemps, size_t nfiles,
+ FILE *ofp, char const *output_file)
+{
+ FILE **fps;
+ size_t nopened = open_input_files (files, nfiles, &fps);
+ if (nopened < nfiles && nopened < 2)
+ die (_("open failed"), files[nopened].name);
+ mergefps (files, ntemps, nopened, ofp, output_file, fps);
+ return nopened;
+}
+
+/* Merge into T (of size NLINES) the two sorted arrays of lines
+ LO (with NLINES / 2 members), and
+ T - (NLINES / 2) (with NLINES - NLINES / 2 members).
+ T and LO point just past their respective arrays, and the arrays
+ are in reverse order. NLINES must be at least 2. */
+
+static void
+mergelines (struct line *restrict t, size_t nlines,
+ struct line const *restrict lo)
{
- for (;;)
+ size_t nlo = nlines / 2;
+ size_t nhi = nlines - nlo;
+ struct line *hi = t - nlo;
+
+ while (true)
if (compare (lo - 1, hi - 1) <= 0)
{
- *--t = *--lo;
- if (! --nlo)
- {
- /* HI - NHI equalled T - (NLO + NHI) when this function
- began. Therefore HI must equal T now, and there is no
- need to copy from HI to T. */
- return;
- }
+ *--t = *--lo;
+ if (! --nlo)
+ {
+ /* HI must equal T now, and there is no need to copy from
+ HI to T. */
+ return;
+ }
}
else
{
- *--t = *--hi;
- if (! --nhi)
- {
- do
- *--t = *--lo;
- while (--nlo);
-
- return;
- }
+ *--t = *--hi;
+ if (! --nhi)
+ {
+ do
+ *--t = *--lo;
+ while (--nlo);
+
+ return;
+ }
}
}
/* Sort the array LINES with NLINES members, using TEMP for temporary space.
- NLINES must be at least 2.
+ Do this all within one thread. NLINES must be at least 2.
+ If TO_TEMP, put the sorted output into TEMP, and TEMP is as large as LINES.
+ Otherwise the sort is in-place and TEMP is half-sized.
The input and output arrays are in reverse order, and LINES and
TEMP point just past the end of their respective arrays.
@@ -2242,16 +3160,26 @@ mergelines (struct line *t,
D. A. Bell, Comp J. 1 (1958), 75. */
static void
-sortlines (struct line *lines, size_t nlines, struct line *temp)
+sequential_sort (struct line *restrict lines, size_t nlines,
+ struct line *restrict temp, bool to_temp)
{
if (nlines == 2)
{
- if (0 < compare (&lines[-1], &lines[-2]))
- {
- struct line tmp = lines[-1];
- lines[-1] = lines[-2];
- lines[-2] = tmp;
- }
+ /* Declare 'swap' as int, not bool, to work around a bug
+ <http://lists.gnu.org/archive/html/bug-coreutils/2005-10/msg00086.html>
+ in the IBM xlc 6.0.0.0 compiler in 64-bit mode. */
+ int swap = (0 < compare (&lines[-1], &lines[-2]));
+ if (to_temp)
+ {
+ temp[-1] = lines[-1 - swap];
+ temp[-2] = lines[-2 + swap];
+ }
+ else if (swap)
+ {
+ temp[-1] = lines[-1];
+ lines[-1] = lines[-2];
+ lines[-2] = temp[-1];
+ }
}
else
{
@@ -2259,55 +3187,476 @@ sortlines (struct line *lines, size_t nlines, struct line *temp)
size_t nhi = nlines - nlo;
struct line *lo = lines;
struct line *hi = lines - nlo;
- struct line *sorted_lo = temp;
- sortlines (hi, nhi, temp);
+ sequential_sort (hi, nhi, temp - (to_temp ? nlo : 0), to_temp);
if (1 < nlo)
- sortlines_temp (lo, nlo, sorted_lo);
+ sequential_sort (lo, nlo, temp, !to_temp);
+ else if (!to_temp)
+ temp[-1] = lo[-1];
+
+ struct line *dest;
+ struct line const *sorted_lo;
+ if (to_temp)
+ {
+ dest = temp;
+ sorted_lo = lines;
+ }
else
- sorted_lo[-1] = lo[-1];
+ {
+ dest = lines;
+ sorted_lo = temp;
+ }
+ mergelines (dest, nlines, sorted_lo);
+ }
+}
+
+static struct merge_node *init_node (struct merge_node *restrict,
+ struct merge_node *restrict,
+ struct line *, size_t, size_t, bool);
+
+
+/* Create and return a merge tree for NTHREADS threads, sorting NLINES
+ lines, with destination DEST. */
+static struct merge_node *
+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 = NULL;
+ root->dest = NULL;
+ root->nlo = root->nhi = nlines;
+ root->parent = NULL;
+ root->level = MERGE_END;
+ root->queued = false;
+ pthread_mutex_init (&root->lock, NULL);
+
+ init_node (root, root + 1, dest, nthreads, nlines, false);
+ return merge_tree;
+}
+
+/* Destroy the merge tree. */
+static void
+merge_tree_destroy (size_t nthreads, struct merge_node *merge_tree)
+{
+ size_t n_nodes = nthreads * 2;
+ struct merge_node *node = merge_tree;
+
+ while (n_nodes--)
+ {
+ pthread_mutex_destroy (&node->lock);
+ node++;
+ }
+
+ free (merge_tree);
+}
+
+/* Initialize a merge tree node and its descendants. The node's
+ parent is PARENT. The node and its descendants are taken from the
+ array of nodes NODE_POOL. Their destination starts at DEST; they
+ will consume NTHREADS threads. The total number of sort lines is
+ TOTAL_LINES. IS_LO_CHILD is true if the node is the low child of
+ its parent. */
+
+static struct merge_node *
+init_node (struct merge_node *restrict parent,
+ struct merge_node *restrict node_pool,
+ struct line *dest, size_t nthreads,
+ size_t total_lines, bool is_lo_child)
+{
+ size_t nlines = (is_lo_child ? parent->nlo : parent->nhi);
+ size_t nlo = nlines / 2;
+ size_t nhi = nlines - nlo;
+ struct line *lo = dest - total_lines;
+ struct line *hi = lo - nlo;
+ struct line **parent_end = (is_lo_child ? &parent->end_lo : &parent->end_hi);
+
+ struct merge_node *node = node_pool++;
+ node->lo = node->end_lo = lo;
+ node->hi = node->end_hi = hi;
+ node->dest = parent_end;
+ node->nlo = nlo;
+ node->nhi = nhi;
+ node->parent = parent;
+ node->level = parent->level + 1;
+ node->queued = false;
+ pthread_mutex_init (&node->lock, NULL);
+
+ if (nthreads > 1)
+ {
+ size_t lo_threads = nthreads / 2;
+ size_t hi_threads = nthreads - lo_threads;
+ node->lo_child = node_pool;
+ node_pool = init_node (node, node_pool, lo, lo_threads,
+ total_lines, true);
+ node->hi_child = node_pool;
+ node_pool = init_node (node, node_pool, hi, hi_threads,
+ total_lines, false);
+ }
+ else
+ {
+ node->lo_child = NULL;
+ node->hi_child = NULL;
+ }
+ return node_pool;
+}
+
+
+/* Compare two merge nodes A and B for priority. */
+
+static int
+compare_nodes (void const *a, void const *b)
+{
+ struct merge_node const *nodea = a;
+ struct merge_node const *nodeb = b;
+ if (nodea->level == nodeb->level)
+ return (nodea->nlo + nodea->nhi) < (nodeb->nlo + nodeb->nhi);
+ return nodea->level < nodeb->level;
+}
+
+/* Lock a merge tree NODE. */
+
+static inline void
+lock_node (struct merge_node *node)
+{
+ pthread_mutex_lock (&node->lock);
+}
+
+/* Unlock a merge tree NODE. */
+
+static inline void
+unlock_node (struct merge_node *node)
+{
+ pthread_mutex_unlock (&node->lock);
+}
- mergelines (lines, sorted_lo, nlo, hi, nhi);
+/* Destroy merge QUEUE. */
+
+static void
+queue_destroy (struct merge_node_queue *queue)
+{
+ heap_free (queue->priority_queue);
+ pthread_cond_destroy (&queue->cond);
+ pthread_mutex_destroy (&queue->mutex);
+}
+
+/* Initialize merge QUEUE, allocating space suitable for a maximum of
+ NTHREADS threads. */
+
+static void
+queue_init (struct merge_node_queue *queue, size_t nthreads)
+{
+ /* Though it's highly unlikely all nodes are in the heap at the same
+ 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, NULL);
+ pthread_cond_init (&queue->cond, NULL);
+}
+
+/* Insert NODE into QUEUE. The caller either holds a lock on NODE, or
+ does not need to lock NODE. */
+
+static void
+queue_insert (struct merge_node_queue *queue, struct merge_node *node)
+{
+ pthread_mutex_lock (&queue->mutex);
+ heap_insert (queue->priority_queue, node);
+ node->queued = true;
+ pthread_cond_signal (&queue->cond);
+ pthread_mutex_unlock (&queue->mutex);
+}
+
+/* Pop the top node off the priority QUEUE, lock the node, return it. */
+
+static struct merge_node *
+queue_pop (struct merge_node_queue *queue)
+{
+ struct merge_node *node;
+ pthread_mutex_lock (&queue->mutex);
+ while (! (node = heap_remove_top (queue->priority_queue)))
+ pthread_cond_wait (&queue->cond, &queue->mutex);
+ pthread_mutex_unlock (&queue->mutex);
+ lock_node (node);
+ node->queued = false;
+ return node;
+}
+
+/* Output LINE to TFP, unless -u is specified and the line compares
+ equal to the previous line. TEMP_OUTPUT is the name of TFP, or
+ is null if TFP is standard output.
+
+ This function does not save the line for comparison later, so it is
+ appropriate only for internal sort. */
+
+static void
+write_unique (struct line const *line, FILE *tfp, char const *temp_output)
+{
+ if (unique)
+ {
+ if (saved_line.text && ! compare (line, &saved_line))
+ return;
+ saved_line = *line;
}
+
+ write_line (line, tfp, temp_output);
}
-/* Like sortlines (LINES, NLINES, TEMP), except output into TEMP
- rather than sorting in place. */
+/* Merge the lines currently available to a NODE in the binary
+ merge tree. Merge a number of lines appropriate for this merge
+ level, assuming TOTAL_LINES is the total number of lines.
+
+ If merging at the top level, send output to TFP. TEMP_OUTPUT is
+ the name of TFP, or is null if TFP is standard output. */
static void
-sortlines_temp (struct line *lines, size_t nlines, struct line *temp)
+mergelines_node (struct merge_node *restrict node, size_t total_lines,
+ FILE *tfp, char const *temp_output)
{
- if (nlines == 2)
+ struct line *lo_orig = node->lo;
+ struct line *hi_orig = node->hi;
+ size_t to_merge = MAX_MERGE (total_lines, node->level);
+ size_t merged_lo;
+ size_t merged_hi;
+
+ if (node->level > MERGE_ROOT)
{
- /* Declare `swap' as int, not bool, to work around a bug
- <http://lists.gnu.org/archive/html/bug-coreutils/2005-10/msg00086.html>
- in the IBM xlc 6.0.0.0 compiler in 64-bit mode. */
- int swap = (0 < compare (&lines[-1], &lines[-2]));
- temp[-1] = lines[-1 - swap];
- temp[-2] = lines[-2 + swap];
+ /* Merge to destination buffer. */
+ struct line *dest = *node->dest;
+ while (node->lo != node->end_lo && node->hi != node->end_hi && to_merge--)
+ if (compare (node->lo - 1, node->hi - 1) <= 0)
+ *--dest = *--node->lo;
+ else
+ *--dest = *--node->hi;
+
+ merged_lo = lo_orig - node->lo;
+ merged_hi = hi_orig - node->hi;
+
+ if (node->nhi == merged_hi)
+ while (node->lo != node->end_lo && to_merge--)
+ *--dest = *--node->lo;
+ else if (node->nlo == merged_lo)
+ while (node->hi != node->end_hi && to_merge--)
+ *--dest = *--node->hi;
+ *node->dest = dest;
}
else
{
- size_t nlo = nlines / 2;
- size_t nhi = nlines - nlo;
- struct line *lo = lines;
- struct line *hi = lines - nlo;
- struct line *sorted_hi = temp - nlo;
+ /* Merge directly to output. */
+ while (node->lo != node->end_lo && node->hi != node->end_hi && to_merge--)
+ {
+ if (compare (node->lo - 1, node->hi - 1) <= 0)
+ write_unique (--node->lo, tfp, temp_output);
+ else
+ write_unique (--node->hi, tfp, temp_output);
+ }
+
+ merged_lo = lo_orig - node->lo;
+ merged_hi = hi_orig - node->hi;
+
+ if (node->nhi == merged_hi)
+ {
+ while (node->lo != node->end_lo && to_merge--)
+ write_unique (--node->lo, tfp, temp_output);
+ }
+ else if (node->nlo == merged_lo)
+ {
+ while (node->hi != node->end_hi && to_merge--)
+ write_unique (--node->hi, tfp, temp_output);
+ }
+ }
+
+ /* Update NODE. */
+ merged_lo = lo_orig - node->lo;
+ merged_hi = hi_orig - node->hi;
+ node->nlo -= merged_lo;
+ node->nhi -= merged_hi;
+}
+
+/* Into QUEUE, insert NODE if it is not already queued, and if one of
+ NODE's children has available lines and the other either has
+ available lines or has exhausted its lines. */
+
+static void
+queue_check_insert (struct merge_node_queue *queue, struct merge_node *node)
+{
+ if (! node->queued)
+ {
+ bool lo_avail = (node->lo - node->end_lo) != 0;
+ bool hi_avail = (node->hi - node->end_hi) != 0;
+ if (lo_avail ? hi_avail || ! node->nhi : hi_avail && ! node->nlo)
+ queue_insert (queue, node);
+ }
+}
+
+/* Into QUEUE, insert NODE's parent if the parent can now be worked on. */
+
+static void
+queue_check_insert_parent (struct merge_node_queue *queue,
+ struct merge_node *node)
+{
+ if (node->level > MERGE_ROOT)
+ {
+ lock_node (node->parent);
+ queue_check_insert (queue, node->parent);
+ unlock_node (node->parent);
+ }
+ else if (node->nlo + node->nhi == 0)
+ {
+ /* If the MERGE_ROOT NODE has finished merging, insert the
+ MERGE_END node. */
+ queue_insert (queue, node->parent);
+ }
+}
+
+/* Repeatedly pop QUEUE for a node with lines to merge, and merge at least
+ some of those lines, until the MERGE_END node is popped.
+ TOTAL_LINES is the total number of lines. If merging at the top
+ level, send output to TFP. TEMP_OUTPUT is the name of TFP, or is
+ null if TFP is standard output. */
+
+static void
+merge_loop (struct merge_node_queue *queue,
+ size_t total_lines, FILE *tfp, char const *temp_output)
+{
+ while (1)
+ {
+ struct merge_node *node = queue_pop (queue);
+
+ if (node->level == MERGE_END)
+ {
+ unlock_node (node);
+ /* Reinsert so other threads can pop it. */
+ queue_insert (queue, node);
+ break;
+ }
+ mergelines_node (node, total_lines, tfp, temp_output);
+ queue_check_insert (queue, node);
+ queue_check_insert_parent (queue, node);
+
+ unlock_node (node);
+ }
+}
+
+
+static void sortlines (struct line *restrict, size_t, size_t,
+ struct merge_node *, struct merge_node_queue *,
+ FILE *, char const *);
+
+/* Thread arguments for sortlines_thread. */
+
+struct thread_args
+{
+ /* Source, i.e., the array of lines to sort. This points just past
+ the end of the array. */
+ struct line *lines;
+
+ /* Number of threads to use. If 0 or 1, sort single-threaded. */
+ size_t nthreads;
+
+ /* Number of lines in LINES and DEST. */
+ size_t const total_lines;
+
+ /* Merge node. Lines from this node and this node's sibling will merged
+ to this node's parent. */
+ struct merge_node *const node;
- sortlines_temp (hi, nhi, sorted_hi);
+ /* The priority queue controlling available work for the entire
+ internal sort. */
+ struct merge_node_queue *const queue;
+
+ /* If at the top level, the file to output to, and the file's name.
+ If the file is standard output, the file's name is null. */
+ FILE *tfp;
+ char const *output_temp;
+};
+
+/* Like sortlines, except with a signature acceptable to pthread_create. */
+
+static void *
+sortlines_thread (void *data)
+{
+ struct thread_args const *args = data;
+ sortlines (args->lines, args->nthreads, args->total_lines,
+ args->node, args->queue, args->tfp,
+ args->output_temp);
+ return NULL;
+}
+
+/* Sort lines, possibly in parallel. The arguments are as in struct
+ thread_args above.
+
+ The algorithm has three phases: node creation, sequential sort,
+ and binary merge.
+
+ During node creation, sortlines recursively visits each node in the
+ binary merge tree and creates a NODE structure corresponding to all the
+ future line merging NODE is responsible for. For each call to
+ sortlines, half the available threads are assigned to each recursive
+ call, until a leaf node having only 1 available thread is reached.
+
+ Each leaf node then performs two sequential sorts, one on each half of
+ the lines it is responsible for. It records in its NODE structure that
+ there are two sorted sublists available to merge from, and inserts its
+ NODE into the priority queue.
+
+ The binary merge phase then begins. Each thread drops into a loop
+ where the thread retrieves a NODE from the priority queue, merges lines
+ available to that NODE, and potentially insert NODE or its parent back
+ into the queue if there are sufficient available lines for them to
+ merge. This continues until all lines at all nodes of the merge tree
+ have been merged. */
+
+static void
+sortlines (struct line *restrict lines, size_t nthreads,
+ size_t total_lines, struct merge_node *node,
+ struct merge_node_queue *queue, FILE *tfp, char const *temp_output)
+{
+ size_t nlines = node->nlo + node->nhi;
+
+ /* Calculate thread arguments. */
+ size_t lo_threads = nthreads / 2;
+ size_t hi_threads = nthreads - lo_threads;
+ pthread_t thread;
+ struct thread_args args = {lines, lo_threads, total_lines,
+ node->lo_child, queue, tfp, temp_output};
+
+ if (nthreads > 1 && SUBTHREAD_LINES_HEURISTIC <= nlines
+ && 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, NULL);
+ }
+ else
+ {
+ /* Nthreads = 1, this is a leaf NODE, or pthread_create failed.
+ Sort with 1 thread. */
+ size_t nlo = node->nlo;
+ size_t nhi = node->nhi;
+ struct line *temp = lines - total_lines;
+ if (1 < nhi)
+ sequential_sort (lines - nlo, nhi, temp - nlo / 2, false);
if (1 < nlo)
- sortlines (lo, nlo, temp);
+ sequential_sort (lines, nlo, temp, false);
+
+ /* Update merge NODE. No need to lock yet. */
+ node->lo = lines;
+ node->hi = lines - nlo;
+ node->end_lo = lines - nlo;
+ node->end_hi = lines - nlo - nhi;
- mergelines (temp, lo, nlo, sorted_hi, nhi);
+ queue_insert (queue, node);
+ merge_loop (queue, total_lines, tfp, temp_output);
}
}
-/* Scan through FILES[NTEMPS .. NFILES-1] looking for a file that is
- the same as OUTFILE. If found, merge the found instances (and perhaps
- some other files) into a temporary file so that it can in turn be
- merged into OUTFILE without destroying OUTFILE before it is completely
- read. Return the new value of NFILES, which differs from the old if
- some merging occurred.
+/* Scan through FILES[NTEMPS .. NFILES-1] looking for files that are
+ the same as OUTFILE. If found, replace each with the same
+ temporary copy that can be merged into OUTFILE without destroying
+ OUTFILE before it is completely read. This temporary copy does not
+ count as a merge temp, so don't worry about incrementing NTEMPS in
+ the caller; final cleanup will remove it, not zaptemp.
This test ensures that an otherwise-erroneous use like
"sort -m -o FILE ... FILE ..." copies FILE before writing to it.
@@ -2319,13 +3668,14 @@ sortlines_temp (struct line *lines, size_t nlines, struct line *temp)
Catching these obscure cases would slow down performance in
common cases. */
-static size_t
+static void
avoid_trashing_input (struct sortfile *files, size_t ntemps,
- size_t nfiles, char const *outfile)
+ size_t nfiles, char const *outfile)
{
size_t i;
bool got_outstat = false;
struct stat outstat;
+ struct tempnode *tempcopy = NULL;
for (i = ntemps; i < nfiles; i++)
{
@@ -2334,39 +3684,75 @@ avoid_trashing_input (struct sortfile *files, size_t ntemps,
struct stat instat;
if (outfile && STREQ (outfile, files[i].name) && !is_stdin)
- same = true;
+ same = true;
else
- {
- if (! got_outstat)
- {
- if ((outfile
- ? stat (outfile, &outstat)
- : fstat (STDOUT_FILENO, &outstat))
- != 0)
- break;
- got_outstat = true;
- }
-
- same = (((is_stdin
- ? fstat (STDIN_FILENO, &instat)
- : stat (files[i].name, &instat))
- == 0)
- && SAME_INODE (instat, outstat));
- }
+ {
+ if (! got_outstat)
+ {
+ if (fstat (STDOUT_FILENO, &outstat) != 0)
+ break;
+ got_outstat = true;
+ }
+
+ same = (((is_stdin
+ ? fstat (STDIN_FILENO, &instat)
+ : stat (files[i].name, &instat))
+ == 0)
+ && SAME_INODE (instat, outstat));
+ }
if (same)
- {
- FILE *tftp;
- pid_t pid;
- char *temp = create_temp (&tftp, &pid);
- mergefps (&files[i],0, nfiles - i, tftp, temp);
- files[i].name = temp;
- files[i].pid = pid;
- return i + 1;
- }
+ {
+ if (! tempcopy)
+ {
+ FILE *tftp;
+ tempcopy = create_temp (&tftp);
+ mergefiles (&files[i], 0, 1, tftp, tempcopy->name);
+ }
+
+ files[i].name = tempcopy->name;
+ files[i].temp = tempcopy;
+ }
}
+}
+
+/* Scan the input files to ensure all are accessible.
+ Otherwise exit with a diagnostic.
+
+ Note this will catch common issues with permissions etc.
+ but will fail to notice issues where you can open() but not read(),
+ like when a directory is specified on some systems.
+ Catching these obscure cases could slow down performance in
+ common cases. */
- return nfiles;
+static void
+check_inputs (char *const *files, size_t nfiles)
+{
+ size_t i;
+ for (i = 0; i < nfiles; i++)
+ {
+ if (STREQ (files[i], "-"))
+ continue;
+
+ if (euidaccess (files[i], R_OK) != 0)
+ die (_("cannot read"), files[i]);
+ }
+}
+
+/* Ensure a specified output file can be created or written to,
+ and point stdout to it. Do not truncate the file.
+ Exit with a diagnostic on failure. */
+
+static void
+check_output (char const *outfile)
+{
+ if (outfile)
+ {
+ int outfd = open (outfile, O_WRONLY | O_CREAT | O_BINARY, MODE_RW_UGO);
+ if (outfd < 0)
+ die (_("open failed"), outfile);
+ move_fd_or_die (outfd, STDOUT_FILENO);
+ }
}
/* Merge the input FILES. NTEMPS is the number of files at the
@@ -2378,7 +3764,7 @@ static void
merge (struct sortfile *files, size_t ntemps, size_t nfiles,
char const *output_file)
{
- while (NMERGE < nfiles)
+ while (nmerge < nfiles)
{
/* Number of input files processed so far. */
size_t in;
@@ -2387,62 +3773,115 @@ merge (struct sortfile *files, size_t ntemps, size_t nfiles,
size_t out;
/* nfiles % NMERGE; this counts input files that are left over
- after all full-sized merges have been done. */
+ after all full-sized merges have been done. */
size_t remainder;
/* Number of easily-available slots at the next loop iteration. */
size_t cheap_slots;
- /* Do as many NMERGE-size merges as possible. */
- for (out = in = 0; out < nfiles / NMERGE; out++, in += NMERGE)
- {
- FILE *tfp;
- pid_t pid;
- char *temp = create_temp (&tfp, &pid);
- size_t nt = MIN (ntemps, NMERGE);
- ntemps -= nt;
- mergefps (&files[in], nt, NMERGE, tfp, temp);
- files[out].name = temp;
- files[out].pid = pid;
- }
+ /* Do as many NMERGE-size merges as possible. In the case that
+ nmerge is bogus, increment by the maximum number of file
+ descriptors allowed. */
+ for (out = in = 0; nmerge <= nfiles - in; out++)
+ {
+ FILE *tfp;
+ struct tempnode *temp = create_temp (&tfp);
+ size_t num_merged = mergefiles (&files[in], MIN (ntemps, nmerge),
+ nmerge, tfp, temp->name);
+ ntemps -= MIN (ntemps, num_merged);
+ files[out].name = temp->name;
+ files[out].temp = temp;
+ in += num_merged;
+ }
remainder = nfiles - in;
- cheap_slots = NMERGE - out % NMERGE;
+ cheap_slots = nmerge - out % nmerge;
if (cheap_slots < remainder)
- {
- /* So many files remain that they can't all be put into the last
- NMERGE-sized output window. Do one more merge. Merge as few
- files as possible, to avoid needless I/O. */
- size_t nshortmerge = remainder - cheap_slots + 1;
- FILE *tfp;
- pid_t pid;
- char *temp = create_temp (&tfp, &pid);
- size_t nt = MIN (ntemps, nshortmerge);
- ntemps -= nt;
- mergefps (&files[in], nt, nshortmerge, tfp, temp);
- files[out].name = temp;
- files[out++].pid = pid;
- in += nshortmerge;
- }
+ {
+ /* So many files remain that they can't all be put into the last
+ NMERGE-sized output window. Do one more merge. Merge as few
+ files as possible, to avoid needless I/O. */
+ size_t nshortmerge = remainder - cheap_slots + 1;
+ FILE *tfp;
+ struct tempnode *temp = create_temp (&tfp);
+ size_t num_merged = mergefiles (&files[in], MIN (ntemps, nshortmerge),
+ nshortmerge, tfp, temp->name);
+ ntemps -= MIN (ntemps, num_merged);
+ files[out].name = temp->name;
+ files[out++].temp = temp;
+ in += num_merged;
+ }
/* Put the remaining input files into the last NMERGE-sized output
- window, so they will be merged in the next pass. */
- memmove(&files[out], &files[in], (nfiles - in) * sizeof *files);
+ window, so they will be merged in the next pass. */
+ memmove (&files[out], &files[in], (nfiles - in) * sizeof *files);
ntemps += out;
nfiles -= in - out;
}
- nfiles = avoid_trashing_input (files, ntemps, nfiles, output_file);
- mergefps (files, ntemps, nfiles, NULL, output_file);
+ avoid_trashing_input (files, ntemps, nfiles, output_file);
+
+ /* We aren't guaranteed that this final mergefiles will work, therefore we
+ try to merge into the output, and then merge as much as we can into a
+ temp file if we can't. Repeat. */
+
+ while (true)
+ {
+ /* Merge directly into the output file if possible. */
+ FILE **fps;
+ size_t nopened = open_input_files (files, nfiles, &fps);
+
+ if (nopened == nfiles)
+ {
+ FILE *ofp = stream_open (output_file, "w");
+ if (ofp)
+ {
+ mergefps (files, ntemps, nfiles, ofp, output_file, fps);
+ break;
+ }
+ if (errno != EMFILE || nopened <= 2)
+ die (_("open failed"), output_file);
+ }
+ else if (nopened <= 2)
+ die (_("open failed"), files[nopened].name);
+
+ /* We ran out of file descriptors. Close one of the input
+ files, to gain a file descriptor. Then create a temporary
+ file with our spare file descriptor. Retry if that failed
+ (e.g., some other process could open a file between the time
+ we closed and tried to create). */
+ FILE *tfp;
+ struct tempnode *temp;
+ do
+ {
+ nopened--;
+ xfclose (fps[nopened], files[nopened].name);
+ temp = maybe_create_temp (&tfp, ! (nopened <= 2));
+ }
+ while (!temp);
+
+ /* Merge into the newly allocated temporary. */
+ mergefps (&files[0], MIN (ntemps, nopened), nopened, tfp, temp->name,
+ fps);
+ ntemps -= MIN (ntemps, nopened);
+ files[0].name = temp->name;
+ files[0].temp = temp;
+
+ memmove (&files[1], &files[nopened], (nfiles - nopened) * sizeof *files);
+ ntemps++;
+ nfiles -= nopened - 1;
+ }
}
-/* Sort NFILES FILES onto OUTPUT_FILE. */
+/* Sort NFILES FILES onto OUTPUT_FILE. Use at most NTHREADS threads. */
static void
-sort (char * const *files, size_t nfiles, char const *output_file)
+sort (char *const *files, size_t nfiles, char const *output_file,
+ size_t nthreads)
{
struct buffer buf;
+ IF_LINT (buf.buf = NULL);
size_t ntemps = 0;
bool output_file_created = false;
@@ -2454,68 +3893,82 @@ sort (char * const *files, size_t nfiles, char const *output_file)
char const *file = *files;
FILE *fp = xfopen (file, "r");
FILE *tfp;
- size_t bytes_per_line = (2 * sizeof (struct line)
- - sizeof (struct line) / 2);
+
+ size_t bytes_per_line;
+ if (nthreads > 1)
+ {
+ /* Get log P. */
+ size_t tmp = 1;
+ size_t mult = 1;
+ while (tmp < nthreads)
+ {
+ tmp *= 2;
+ mult++;
+ }
+ bytes_per_line = (mult * sizeof (struct line));
+ }
+ else
+ 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));
+ initbuf (&buf, bytes_per_line,
+ sort_buffer_size (&fp, 1, files, nfiles, bytes_per_line));
buf.eof = false;
files++;
nfiles--;
while (fillbuf (&buf, fp, file))
- {
- struct line *line;
- struct line *linebase;
-
- if (buf.eof && nfiles
- && (bytes_per_line + 1
- < (buf.alloc - buf.used - bytes_per_line * buf.nlines)))
- {
- /* End of file, but there is more input and buffer room.
- Concatenate the next input file; this is faster in
- the usual case. */
- buf.left = buf.used;
- break;
- }
-
- line = buffer_linelim (&buf);
- linebase = line - buf.nlines;
- if (1 < buf.nlines)
- sortlines (line, buf.nlines, linebase);
- if (buf.eof && !nfiles && !ntemps && !buf.left)
- {
- xfclose (fp, file);
- tfp = xfopen (output_file, "w");
- temp_output = output_file;
- output_file_created = true;
- }
- else
- {
- ++ntemps;
- temp_output = create_temp (&tfp, NULL);
- }
-
- do
- {
- line--;
- write_bytes (line->text, line->length, tfp, temp_output);
- if (unique)
- while (linebase < line && compare (line, line - 1) == 0)
- line--;
- }
- while (linebase < line);
-
- xfclose (tfp, temp_output);
-
- /* Free up some resources every once in a while. */
- if (MAX_PROCS_BEFORE_REAP < nprocs)
- reap_some ();
-
- if (output_file_created)
- goto finish;
- }
+ {
+ struct line *line;
+
+ if (buf.eof && nfiles
+ && (bytes_per_line + 1
+ < (buf.alloc - buf.used - bytes_per_line * buf.nlines)))
+ {
+ /* End of file, but there is more input and buffer room.
+ Concatenate the next input file; this is faster in
+ the usual case. */
+ buf.left = buf.used;
+ break;
+ }
+
+ saved_line.text = NULL;
+ line = buffer_linelim (&buf);
+ if (buf.eof && !nfiles && !ntemps && !buf.left)
+ {
+ xfclose (fp, file);
+ tfp = xfopen (output_file, "w");
+ temp_output = output_file;
+ output_file_created = true;
+ }
+ else
+ {
+ ++ntemps;
+ temp_output = create_temp (&tfp)->name;
+ }
+ 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);
+
+ sortlines (line, nthreads, buf.nlines, merge_tree + 1,
+ &queue, tfp, temp_output);
+
+#ifdef lint
+ merge_tree_destroy (nthreads, merge_tree);
+ queue_destroy (&queue);
+#endif
+ }
+ else
+ write_unique (line - 1, tfp, temp_output);
+
+ xfclose (tfp, temp_output);
+
+ if (output_file_created)
+ goto finish;
+ }
xfclose (fp, file);
}
@@ -2528,14 +3981,16 @@ sort (char * const *files, size_t nfiles, char const *output_file)
struct tempnode *node = temphead;
struct sortfile *tempfiles = xnmalloc (ntemps, sizeof *tempfiles);
for (i = 0; node; i++)
- {
- tempfiles[i].name = node->name;
- tempfiles[i].pid = node->pid;
- node = node->next;
- }
+ {
+ tempfiles[i].name = node->name;
+ tempfiles[i].temp = node;
+ node = node->next;
+ }
merge (tempfiles, ntemps, ntemps, output_file);
free (tempfiles);
}
+
+ reap_all ();
}
/* Insert a malloc'd copy of key KEY_ARG at the end of the key list. */
@@ -2560,7 +4015,7 @@ static void
badfieldspec (char const *spec, char const *msgid)
{
error (SORT_FAILURE, 0, _("%s: invalid field specification %s"),
- _(msgid), quote (spec));
+ _(msgid), quote (spec));
abort ();
}
@@ -2570,7 +4025,7 @@ static void incompatible_options (char const *) ATTRIBUTE_NORETURN;
static void
incompatible_options (char const *opts)
{
- error (SORT_FAILURE, 0, _("options `-%s' are incompatible"), opts);
+ error (SORT_FAILURE, 0, _("options '-%s' are incompatible"), (opts));
abort ();
}
@@ -2579,31 +4034,18 @@ incompatible_options (char const *opts)
static void
check_ordering_compatibility (void)
{
- struct keyfield const *key;
+ struct keyfield *key;
for (key = keylist; key; key = key->next)
- if ((1 < (key->random + key->numeric + key->general_numeric + key->month
- + !!key->ignore))
- || (key->random && key->translate))
+ if (1 < (key->numeric + key->general_numeric + key->human_numeric
+ + key->month + (key->version | key->random | !!key->ignore)))
{
- char opts[7];
- char *p = opts;
- if (key->ignore == nondictionary)
- *p++ = 'd';
- if (key->translate)
- *p++ = 'f';
- if (key->general_numeric)
- *p++ = 'g';
- if (key->ignore == nonprinting)
- *p++ = 'i';
- if (key->month)
- *p++ = 'M';
- if (key->numeric)
- *p++ = 'n';
- if (key->random)
- *p++ = 'R';
- *p = '\0';
- incompatible_options (opts);
+ /* The following is too big, but guaranteed to be "big enough". */
+ char opts[sizeof short_options];
+ /* Clear flags we're not interested in. */
+ key->skipsblanks = key->skipeblanks = key->reverse = false;
+ key_to_opts (key, opts);
+ incompatible_options (opts);
}
}
@@ -2625,7 +4067,7 @@ parse_field_count (char const *string, size_t *val, char const *msgid)
case LONGINT_INVALID_SUFFIX_CHAR:
*val = n;
if (*val == n)
- break;
+ break;
/* Fall through. */
case LONGINT_OVERFLOW:
case LONGINT_OVERFLOW | LONGINT_INVALID_SUFFIX_CHAR:
@@ -2634,8 +4076,8 @@ parse_field_count (char const *string, size_t *val, char const *msgid)
case LONGINT_INVALID:
if (msgid)
- error (SORT_FAILURE, 0, _("%s: invalid count at start of %s"),
- _(msgid), quote (string));
+ error (SORT_FAILURE, 0, _("%s: invalid count at start of %s"),
+ _(msgid), quote (string));
return NULL;
}
@@ -2662,53 +4104,61 @@ sighandler (int sig)
BLANKTYPE is the kind of blanks that 'b' should skip. */
static char *
-set_ordering (const char *s, struct keyfield *key, enum blanktype blanktype)
+set_ordering (char const *s, struct keyfield *key, enum blanktype blanktype)
{
while (*s)
{
switch (*s)
- {
- case 'b':
- if (blanktype == bl_start || blanktype == bl_both)
- key->skipsblanks = true;
- if (blanktype == bl_end || blanktype == bl_both)
- key->skipeblanks = true;
- break;
- case 'd':
- key->ignore = nondictionary;
- break;
- case 'f':
- key->translate = fold_toupper;
- break;
- case 'g':
- key->general_numeric = true;
- break;
- case 'i':
- /* Option order should not matter, so don't let -i override
- -d. -d implies -i, but -i does not imply -d. */
- if (! key->ignore)
- key->ignore = nonprinting;
- break;
- case 'M':
- key->month = true;
- break;
- case 'n':
- key->numeric = true;
- break;
- case 'R':
- key->random = true;
- break;
- case 'r':
- key->reverse = true;
- break;
- default:
- return (char *) s;
- }
+ {
+ case 'b':
+ if (blanktype == bl_start || blanktype == bl_both)
+ key->skipsblanks = true;
+ if (blanktype == bl_end || blanktype == bl_both)
+ key->skipeblanks = true;
+ break;
+ case 'd':
+ key->ignore = nondictionary;
+ break;
+ case 'f':
+ key->translate = fold_toupper;
+ break;
+ case 'g':
+ key->general_numeric = true;
+ break;
+ case 'h':
+ key->human_numeric = true;
+ break;
+ case 'i':
+ /* Option order should not matter, so don't let -i override
+ -d. -d implies -i, but -i does not imply -d. */
+ if (! key->ignore)
+ key->ignore = nonprinting;
+ break;
+ case 'M':
+ key->month = true;
+ break;
+ case 'n':
+ key->numeric = true;
+ break;
+ case 'R':
+ key->random = true;
+ break;
+ case 'r':
+ key->reverse = true;
+ break;
+ case 'V':
+ key->version = true;
+ break;
+ default:
+ return (char *) s;
+ }
++s;
}
return (char *) s;
}
+/* Initialize KEY. */
+
static struct keyfield *
key_init (struct keyfield *key)
{
@@ -2723,21 +4173,26 @@ main (int argc, char **argv)
struct keyfield *key;
struct keyfield key_buf;
struct keyfield gkey;
+ bool gkey_only = false;
char const *s;
int c = 0;
char checkonly = 0;
bool mergeonly = false;
char *random_source = NULL;
bool need_random = false;
+ size_t nthreads = 0;
size_t nfiles = 0;
bool posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
bool obsolete_usage = (posix2_version () < 200112);
char **files;
+ char *files_from = NULL;
+ struct Tokens tok;
char const *outfile = NULL;
+ bool locale_ok;
initialize_main (&argc, &argv);
- program_name = argv[0];
- setlocale (LC_ALL, "");
+ set_program_name (argv[0]);
+ locale_ok = !! setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -2772,25 +4227,25 @@ main (int argc, char **argv)
size_t i;
static int const sig[] =
{
- /* The usual suspects. */
- SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM,
+ /* The usual suspects. */
+ SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM,
#ifdef SIGPOLL
- SIGPOLL,
+ SIGPOLL,
#endif
#ifdef SIGPROF
- SIGPROF,
+ SIGPROF,
#endif
#ifdef SIGVTALRM
- SIGVTALRM,
+ SIGVTALRM,
#endif
#ifdef SIGXCPU
- SIGXCPU,
+ SIGXCPU,
#endif
#ifdef SIGXFSZ
- SIGXFSZ,
+ SIGXFSZ,
#endif
};
- enum { nsigs = sizeof sig / sizeof sig[0] };
+ enum { nsigs = ARRAY_CARDINALITY (sig) };
#if SA_NOCLDSTOP
struct sigaction act;
@@ -2798,9 +4253,9 @@ main (int argc, char **argv)
sigemptyset (&caught_signals);
for (i = 0; i < nsigs; i++)
{
- sigaction (sig[i], NULL, &act);
- if (act.sa_handler != SIG_IGN)
- sigaddset (&caught_signals, sig[i]);
+ sigaction (sig[i], NULL, &act);
+ if (act.sa_handler != SIG_IGN)
+ sigaddset (&caught_signals, sig[i]);
}
act.sa_handler = sighandler;
@@ -2809,284 +4264,367 @@ main (int argc, char **argv)
for (i = 0; i < nsigs; i++)
if (sigismember (&caught_signals, sig[i]))
- sigaction (sig[i], &act, NULL);
+ sigaction (sig[i], &act, NULL);
#else
for (i = 0; i < nsigs; i++)
if (signal (sig[i], SIG_IGN) != SIG_IGN)
- {
- signal (sig[i], sighandler);
- siginterrupt (sig[i], 1);
- }
+ {
+ signal (sig[i], sighandler);
+ siginterrupt (sig[i], 1);
+ }
#endif
}
+ signal (SIGCHLD, SIG_DFL); /* Don't inherit CHLD handling from parent. */
/* The signal mask is known, so it is safe to invoke exit_cleanup. */
atexit (exit_cleanup);
- gkey.sword = gkey.eword = SIZE_MAX;
- gkey.ignore = NULL;
- gkey.translate = NULL;
- gkey.numeric = gkey.general_numeric = gkey.random = false;
- gkey.month = gkey.reverse = false;
- gkey.skipsblanks = gkey.skipeblanks = false;
+ key_init (&gkey);
+ gkey.sword = SIZE_MAX;
files = xnmalloc (argc, sizeof *files);
- for (;;)
+ while (true)
{
/* Parse an operand as a file after "--" was seen; or if
- pedantic and a file was seen, unless the POSIX version
- predates 1003.1-2001 and -c was not seen and the operand is
- "-o FILE" or "-oFILE". */
+ pedantic and a file was seen, unless the POSIX version
+ predates 1003.1-2001 and -c was not seen and the operand is
+ "-o FILE" or "-oFILE". */
+ int oi = -1;
if (c == -1
- || (posixly_correct && nfiles != 0
- && ! (obsolete_usage
- && ! checkonly
- && optind != argc
- && argv[optind][0] == '-' && argv[optind][1] == 'o'
- && (argv[optind][2] || optind + 1 != argc)))
- || ((c = getopt_long (argc, argv, short_options,
- long_options, NULL))
- == -1))
- {
- if (argc <= optind)
- break;
- files[nfiles++] = argv[optind++];
- }
+ || (posixly_correct && nfiles != 0
+ && ! (obsolete_usage
+ && ! checkonly
+ && optind != argc
+ && argv[optind][0] == '-' && argv[optind][1] == 'o'
+ && (argv[optind][2] || optind + 1 != argc)))
+ || ((c = getopt_long (argc, argv, short_options,
+ long_options, &oi))
+ == -1))
+ {
+ if (argc <= optind)
+ break;
+ files[nfiles++] = argv[optind++];
+ }
else switch (c)
- {
- case 1:
- key = NULL;
- if (optarg[0] == '+')
- {
- bool minus_pos_usage = (optind != argc && argv[optind][0] == '-'
- && ISDIGIT (argv[optind][1]));
- obsolete_usage |= minus_pos_usage & ~posixly_correct;
- if (obsolete_usage)
- {
- /* 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, NULL);
- if (s && *s == '.')
- 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))
- {
- free (key);
- key = NULL;
- }
- else
- {
- if (minus_pos_usage)
- {
- char const *optarg1 = argv[optind++];
- s = parse_field_count (optarg1 + 1, &key->eword,
- N_("invalid number after `-'"));
- if (*s == '.')
- s = parse_field_count (s + 1, &key->echar,
- N_("invalid number after `.'"));
- if (*set_ordering (s, key, bl_end))
- badfieldspec (optarg1,
- N_("stray character in field spec"));
- }
- insertkey (key);
- }
- }
- }
- if (! key)
- files[nfiles++] = optarg;
- break;
-
- case 'b':
- case 'd':
- case 'f':
- case 'g':
- case 'i':
- case 'M':
- case 'n':
- case 'r':
- case 'R':
- {
- char str[2];
- str[0] = c;
- str[1] = '\0';
- set_ordering (str, &gkey, bl_both);
- }
- break;
-
- case CHECK_OPTION:
- c = (optarg
- ? XARGMATCH ("--check", optarg, check_args, check_types)
- : 'c');
- /* Fall through. */
- case 'c':
- case 'C':
- if (checkonly && checkonly != c)
- incompatible_options ("cC");
- checkonly = c;
- break;
-
- case COMPRESS_PROGRAM_OPTION:
- if (compress_program && strcmp (compress_program, optarg) != 0)
- error (SORT_FAILURE, 0, _("multiple compress programs specified"));
- compress_program = optarg;
- break;
-
- case 'k':
- key = key_init (&key_buf);
-
- /* Get POS1. */
- s = parse_field_count (optarg, &key->sword,
- N_("invalid number at field start"));
- if (! key->sword--)
- {
- /* Provoke with `sort -k0' */
- badfieldspec (optarg, N_("field number is zero"));
- }
- if (*s == '.')
- {
- s = parse_field_count (s + 1, &key->schar,
- N_("invalid number after `.'"));
- if (! key->schar--)
- {
- /* Provoke with `sort -k1.0' */
- badfieldspec (optarg, N_("character offset is zero"));
- }
- }
- if (! (key->sword | key->schar))
- key->sword = SIZE_MAX;
- s = set_ordering (s, key, bl_start);
- if (*s != ',')
- {
- key->eword = SIZE_MAX;
- key->echar = 0;
- }
- else
- {
- /* Get POS2. */
- s = parse_field_count (s + 1, &key->eword,
- N_("invalid number after `,'"));
- if (! key->eword--)
- {
- /* Provoke with `sort -k1,0' */
- badfieldspec (optarg, N_("field number is zero"));
- }
- if (*s == '.')
- s = parse_field_count (s + 1, &key->echar,
- N_("invalid number after `.'"));
- else
- {
- /* `-k 2,3' is equivalent to `+1 -3'. */
- key->eword++;
- }
- s = set_ordering (s, key, bl_end);
- }
- if (*s)
- badfieldspec (optarg, N_("stray character in field spec"));
- insertkey (key);
- break;
-
- case 'm':
- mergeonly = true;
- break;
-
- case 'o':
- if (outfile && !STREQ (outfile, optarg))
- error (SORT_FAILURE, 0, _("multiple output files specified"));
- outfile = optarg;
- break;
-
- case RANDOM_SOURCE_OPTION:
- if (random_source && !STREQ (random_source, optarg))
- error (SORT_FAILURE, 0, _("multiple random sources specified"));
- random_source = optarg;
- break;
-
- case 's':
- stable = true;
- break;
-
- case 'S':
- specify_sort_size (optarg);
- break;
-
- case 't':
- {
- char newtab = optarg[0];
- if (! newtab)
- error (SORT_FAILURE, 0, _("empty tab"));
- if (optarg[1])
- {
- if (STREQ (optarg, "\\0"))
- newtab = '\0';
- else
- {
- /* Provoke with `sort -txx'. Complain about
- "multi-character tab" instead of "multibyte tab", so
- that the diagnostic's wording does not need to be
- changed once multibyte characters are supported. */
- error (SORT_FAILURE, 0, _("multi-character tab %s"),
- quote (optarg));
- }
- }
- if (tab != TAB_DEFAULT && tab != newtab)
- error (SORT_FAILURE, 0, _("incompatible tabs"));
- tab = newtab;
- }
- break;
-
- case 'T':
- add_temp_dir (optarg);
- break;
-
- case 'u':
- unique = true;
- break;
-
- case 'y':
- /* Accept and ignore e.g. -y0 for compatibility with Solaris 2.x
- through Solaris 7. It is also accepted by many non-Solaris
- "sort" implementations, e.g., AIX 5.2, HP-UX 11i v2, IRIX 6.5.
- -y is marked as obsolete starting with Solaris 8 (1999), but is
- still accepted as of Solaris 10 prerelease (2004).
-
- Solaris 2.5.1 "sort -y 100" reads the input file "100", but
- emulate Solaris 8 and 9 "sort -y 100" which ignores the "100",
- and which in general ignores the argument after "-y" if it
- consists entirely of digits (it can even be empty). */
- if (optarg == argv[optind - 1])
- {
- char const *p;
- for (p = optarg; ISDIGIT (*p); p++)
- continue;
- optind -= (*p != '\0');
- }
- break;
-
- case 'z':
- eolchar = 0;
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (SORT_FAILURE);
- }
+ {
+ case 1:
+ key = NULL;
+ if (optarg[0] == '+')
+ {
+ bool minus_pos_usage = (optind != argc && argv[optind][0] == '-'
+ && ISDIGIT (argv[optind][1]));
+ obsolete_usage |= minus_pos_usage && !posixly_correct;
+ if (obsolete_usage)
+ {
+ /* 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, NULL);
+ if (s && *s == '.')
+ 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 = NULL;
+ else
+ {
+ if (minus_pos_usage)
+ {
+ char const *optarg1 = argv[optind++];
+ s = parse_field_count (optarg1 + 1, &key->eword,
+ N_("invalid number after '-'"));
+ /* When called with a non-NULL message ID,
+ parse_field_count cannot return NULL. Tell static
+ analysis tools that dereferencing S is safe. */
+ assert (s);
+ if (*s == '.')
+ s = parse_field_count (s + 1, &key->echar,
+ N_("invalid number after '.'"));
+ if (!key->echar && key->eword)
+ {
+ /* obsolescent syntax +A.x -B.y is equivalent to:
+ -k A+1.x+1,B.y (when y = 0)
+ -k A+1.x+1,B+1.y (when y > 0)
+ So eword is decremented as in the -k case
+ only when the end field (B) is specified and
+ echar (y) is 0. */
+ key->eword--;
+ }
+ if (*set_ordering (s, key, bl_end))
+ badfieldspec (optarg1,
+ N_("stray character in field spec"));
+ }
+ key->obsolete_used = true;
+ insertkey (key);
+ }
+ }
+ }
+ if (! key)
+ files[nfiles++] = optarg;
+ break;
+
+ case SORT_OPTION:
+ c = XARGMATCH ("--sort", optarg, sort_args, sort_types);
+ /* Fall through. */
+ case 'b':
+ case 'd':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'M':
+ case 'n':
+ case 'r':
+ case 'R':
+ case 'V':
+ {
+ char str[2];
+ str[0] = c;
+ str[1] = '\0';
+ set_ordering (str, &gkey, bl_both);
+ }
+ break;
+
+ case CHECK_OPTION:
+ c = (optarg
+ ? XARGMATCH ("--check", optarg, check_args, check_types)
+ : 'c');
+ /* Fall through. */
+ case 'c':
+ case 'C':
+ if (checkonly && checkonly != c)
+ incompatible_options ("cC");
+ checkonly = c;
+ break;
+
+ case COMPRESS_PROGRAM_OPTION:
+ if (compress_program && !STREQ (compress_program, optarg))
+ error (SORT_FAILURE, 0, _("multiple compress programs specified"));
+ compress_program = optarg;
+ break;
+
+ case DEBUG_PROGRAM_OPTION:
+ debug = true;
+ break;
+
+ case FILES0_FROM_OPTION:
+ files_from = optarg;
+ break;
+
+ case 'k':
+ key = key_init (&key_buf);
+
+ /* Get POS1. */
+ s = parse_field_count (optarg, &key->sword,
+ N_("invalid number at field start"));
+ if (! key->sword--)
+ {
+ /* Provoke with 'sort -k0' */
+ badfieldspec (optarg, N_("field number is zero"));
+ }
+ if (*s == '.')
+ {
+ s = parse_field_count (s + 1, &key->schar,
+ N_("invalid number after '.'"));
+ if (! key->schar--)
+ {
+ /* Provoke with 'sort -k1.0' */
+ badfieldspec (optarg, N_("character offset is zero"));
+ }
+ }
+ if (! (key->sword || key->schar))
+ key->sword = SIZE_MAX;
+ s = set_ordering (s, key, bl_start);
+ if (*s != ',')
+ {
+ key->eword = SIZE_MAX;
+ key->echar = 0;
+ }
+ else
+ {
+ /* Get POS2. */
+ s = parse_field_count (s + 1, &key->eword,
+ N_("invalid number after ','"));
+ if (! key->eword--)
+ {
+ /* Provoke with 'sort -k1,0' */
+ badfieldspec (optarg, N_("field number is zero"));
+ }
+ if (*s == '.')
+ {
+ s = parse_field_count (s + 1, &key->echar,
+ N_("invalid number after '.'"));
+ }
+ s = set_ordering (s, key, bl_end);
+ }
+ if (*s)
+ badfieldspec (optarg, N_("stray character in field spec"));
+ insertkey (key);
+ break;
+
+ case 'm':
+ mergeonly = true;
+ break;
+
+ case NMERGE_OPTION:
+ specify_nmerge (oi, c, optarg);
+ break;
+
+ case 'o':
+ if (outfile && !STREQ (outfile, optarg))
+ error (SORT_FAILURE, 0, _("multiple output files specified"));
+ outfile = optarg;
+ break;
+
+ case RANDOM_SOURCE_OPTION:
+ if (random_source && !STREQ (random_source, optarg))
+ error (SORT_FAILURE, 0, _("multiple random sources specified"));
+ random_source = optarg;
+ break;
+
+ case 's':
+ stable = true;
+ break;
+
+ case 'S':
+ specify_sort_size (oi, c, optarg);
+ break;
+
+ case 't':
+ {
+ char newtab = optarg[0];
+ if (! newtab)
+ error (SORT_FAILURE, 0, _("empty tab"));
+ if (optarg[1])
+ {
+ if (STREQ (optarg, "\\0"))
+ newtab = '\0';
+ else
+ {
+ /* Provoke with 'sort -txx'. Complain about
+ "multi-character tab" instead of "multibyte tab", so
+ that the diagnostic's wording does not need to be
+ changed once multibyte characters are supported. */
+ error (SORT_FAILURE, 0, _("multi-character tab %s"),
+ quote (optarg));
+ }
+ }
+ if (tab != TAB_DEFAULT && tab != newtab)
+ error (SORT_FAILURE, 0, _("incompatible tabs"));
+ tab = newtab;
+ }
+ break;
+
+ case 'T':
+ add_temp_dir (optarg);
+ break;
+
+ case PARALLEL_OPTION:
+ nthreads = specify_nthreads (oi, c, optarg);
+ break;
+
+ case 'u':
+ unique = true;
+ break;
+
+ case 'y':
+ /* Accept and ignore e.g. -y0 for compatibility with Solaris 2.x
+ through Solaris 7. It is also accepted by many non-Solaris
+ "sort" implementations, e.g., AIX 5.2, HP-UX 11i v2, IRIX 6.5.
+ -y is marked as obsolete starting with Solaris 8 (1999), but is
+ still accepted as of Solaris 10 prerelease (2004).
+
+ Solaris 2.5.1 "sort -y 100" reads the input file "100", but
+ emulate Solaris 8 and 9 "sort -y 100" which ignores the "100",
+ and which in general ignores the argument after "-y" if it
+ consists entirely of digits (it can even be empty). */
+ if (optarg == argv[optind - 1])
+ {
+ char const *p;
+ for (p = optarg; ISDIGIT (*p); p++)
+ continue;
+ optind -= (*p != '\0');
+ }
+ break;
+
+ case 'z':
+ eolchar = 0;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (SORT_FAILURE);
+ }
+ }
+
+ if (files_from)
+ {
+ FILE *stream;
+
+ /* When using --files0-from=F, you may not specify any files
+ on the command-line. */
+ if (nfiles)
+ {
+ error (0, 0, _("extra operand %s"), quoteaf (files[0]));
+ fprintf (stderr, "%s\n",
+ _("file operands cannot be combined with --files0-from"));
+ usage (SORT_FAILURE);
+ }
+
+ if (STREQ (files_from, "-"))
+ stream = stdin;
+ else
+ {
+ stream = fopen (files_from, "r");
+ if (stream == NULL)
+ error (SORT_FAILURE, errno, _("cannot open %s for reading"),
+ quoteaf (files_from));
+ }
+
+ readtokens0_init (&tok);
+
+ if (! readtokens0 (stream, &tok) || fclose (stream) != 0)
+ error (SORT_FAILURE, 0, _("cannot read file names from %s"),
+ quoteaf (files_from));
+
+ if (tok.n_tok)
+ {
+ size_t i;
+ free (files);
+ files = tok.tok;
+ nfiles = tok.n_tok;
+ for (i = 0; i < nfiles; i++)
+ {
+ if (STREQ (files[i], "-"))
+ error (SORT_FAILURE, 0, _("when reading file names from stdin, "
+ "no file name of %s allowed"),
+ quoteaf (files[i]));
+ else if (files[i][0] == '\0')
+ {
+ /* Using the standard 'filename:line-number:' prefix here is
+ not totally appropriate, since NUL is the separator,
+ not NL, but it might be better than nothing. */
+ unsigned long int file_number = i + 1;
+ error (SORT_FAILURE, 0,
+ _("%s:%lu: invalid zero-length file name"),
+ quotef (files_from), file_number);
+ }
+ }
+ }
+ else
+ error (SORT_FAILURE, 0, _("no input from %s"),
+ quoteaf (files_from));
}
/* Inheritance of global options to individual keys. */
for (key = keylist; key; key = key->next)
{
- if (! (key->ignore || key->translate
- || (key->skipsblanks | key->reverse
- | key->skipeblanks | key->month | key->numeric
- | key->general_numeric
- | key->random)))
+ if (default_key_compare (key) && !key->reverse)
{
key->ignore = gkey.ignore;
key->translate = gkey.translate;
@@ -3095,6 +4633,8 @@ main (int argc, char **argv)
key->month = gkey.month;
key->numeric = gkey.numeric;
key->general_numeric = gkey.general_numeric;
+ key->human_numeric = gkey.human_numeric;
+ key->version = gkey.version;
key->random = gkey.random;
key->reverse = gkey.reverse;
}
@@ -3102,25 +4642,45 @@ main (int argc, char **argv)
need_random |= key->random;
}
- if (!keylist && (gkey.ignore || gkey.translate
- || (gkey.skipsblanks | gkey.skipeblanks | gkey.month
- | gkey.numeric | gkey.general_numeric
- | gkey.random)))
+ if (!keylist && !default_key_compare (&gkey))
{
+ gkey_only = true;
insertkey (&gkey);
need_random |= gkey.random;
}
check_ordering_compatibility ();
+ if (debug)
+ {
+ if (checkonly || outfile)
+ {
+ static char opts[] = "X --debug";
+ opts[0] = (checkonly ? checkonly : 'o');
+ incompatible_options (opts);
+ }
+
+ /* Always output the locale in debug mode, since this
+ is such a common source of confusion. */
+ if (hard_LC_COLLATE)
+ error (0, 0, _("using %s sorting rules"),
+ quote (setlocale (LC_COLLATE, NULL)));
+ else
+ {
+ /* OpenBSD can only set some categories with LC_ALL above,
+ so set LC_COLLATE explicitly to flag errors. */
+ if (locale_ok)
+ locale_ok = !! setlocale (LC_COLLATE, "");
+ error (0, 0, "%s%s", locale_ok ? "" : _("failed to set locale; "),
+ _("using simple byte comparison"));
+ }
+ key_warnings (&gkey, gkey_only);
+ }
+
reverse = gkey.reverse;
if (need_random)
- {
- randread_source = randread_new (random_source, MD5_DIGEST_SIZE);
- if (! randread_source)
- die (_("open failed"), random_source);
- }
+ random_md5_state_init (random_source);
if (temp_dir_count == 0)
{
@@ -3130,45 +4690,76 @@ main (int argc, char **argv)
if (nfiles == 0)
{
- static char *minus = "-";
nfiles = 1;
free (files);
- files = &minus;
+ files = xmalloc (sizeof *files);
+ *files = (char *) "-";
}
+ /* Need to re-check that we meet the minimum requirement for memory
+ usage with the final value for NMERGE. */
+ if (0 < sort_size)
+ sort_size = MAX (sort_size, MIN_SORT_SIZE);
+
if (checkonly)
{
if (nfiles > 1)
- error (SORT_FAILURE, 0, _("extra operand %s not allowed with -%c"),
- quote (files[1]), checkonly);
+ error (SORT_FAILURE, 0, _("extra operand %s not allowed with -%c"),
+ quoteaf (files[1]), checkonly);
if (outfile)
- {
- static char opts[] = {0, 'o', 0};
- opts[0] = checkonly;
- incompatible_options (opts);
- }
+ {
+ static char opts[] = {0, 'o', 0};
+ opts[0] = checkonly;
+ incompatible_options (opts);
+ }
/* POSIX requires that sort return 1 IFF invoked with -c or -C and the
- input is not properly sorted. */
- exit (check (files[0], checkonly) ? EXIT_SUCCESS : SORT_OUT_OF_ORDER);
+ input is not properly sorted. */
+ return check (files[0], checkonly) ? EXIT_SUCCESS : SORT_OUT_OF_ORDER;
}
+ /* Check all inputs are accessible, or exit immediately. */
+ check_inputs (files, nfiles);
+
+ /* Check output is writable, or exit immediately. */
+ check_output (outfile);
+
if (mergeonly)
{
struct sortfile *sortfiles = xcalloc (nfiles, sizeof *sortfiles);
size_t i;
for (i = 0; i < nfiles; ++i)
- sortfiles[i].name = files[i];
+ sortfiles[i].name = files[i];
merge (sortfiles, 0, nfiles, outfile);
+ IF_LINT (free (sortfiles));
+ }
+ else
+ {
+ if (!nthreads)
+ {
+ unsigned long int np = num_processors (NPROC_CURRENT_OVERRIDABLE);
+ nthreads = MIN (np, DEFAULT_MAX_THREADS);
+ }
+
+ /* Avoid integer overflow later. */
+ size_t nthreads_max = SIZE_MAX / (2 * sizeof (struct merge_node));
+ nthreads = MIN (nthreads, nthreads_max);
+
+ sort (files, nfiles, outfile, nthreads);
}
+
+#ifdef lint
+ if (files_from)
+ readtokens0_free (&tok);
else
- sort (files, nfiles, outfile);
+ free (files);
+#endif
if (have_read_stdin && fclose (stdin) == EOF)
die (_("close failed"), "-");
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/split.c b/src/split.c
index 1f0f3d7..510af13 100644
--- a/src/split.c
+++ b/src/split.c
@@ -1,10 +1,10 @@
/* split.c -- split a file into pieces.
- Copyright (C) 1988, 1991, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1988-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,42 +12,56 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
-
-/* By tege@sics.se, with rms.
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
- To do:
- * Implement -t CHAR or -t REGEX to specify break characters other
- than newline. */
+/* By tege@sics.se, with rms.
+ TODO:
+ * support -p REGEX as in BSD's split.
+ * support --suppress-matched as in csplit. */
#include <config.h>
+#include <assert.h>
#include <stdio.h>
#include <getopt.h>
+#include <signal.h>
#include <sys/types.h>
+#include <sys/wait.h>
#include "system.h"
#include "error.h"
#include "fd-reopen.h"
#include "fcntl--.h"
-#include "getpagesize.h"
-#include "full-read.h"
#include "full-write.h"
-#include "inttostr.h"
+#include "ioblksize.h"
#include "quote.h"
#include "safe-read.h"
+#include "sig2str.h"
+#include "xfreopen.h"
+#include "xdectoint.h"
#include "xstrtol.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "split"
-#define AUTHORS "Torbjorn Granlund", "Richard M. Stallman"
+#define AUTHORS \
+ proper_name_utf8 ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \
+ proper_name ("Richard M. Stallman")
-#define DEFAULT_SUFFIX_LENGTH 2
+/* Shell command to filter through, instead of creating files. */
+static char const *filter_command;
-/* The name this program was run with. */
-char *program_name;
+/* Process ID of the filter. */
+static int filter_pid;
+
+/* Array of open pipes. */
+static int *open_pipes;
+static size_t open_pipes_alloc;
+static size_t n_open_pipes;
+
+/* Blocked signals. */
+static sigset_t oldblocked;
+static sigset_t newblocked;
/* Base name of output files. */
static char const *outbase;
@@ -59,27 +73,59 @@ static char *outfile;
Suffixes are inserted here. */
static char *outfile_mid;
+/* Generate new suffix when suffixes are exhausted. */
+static bool suffix_auto = true;
+
/* Length of OUTFILE's suffix. */
-static size_t suffix_length = DEFAULT_SUFFIX_LENGTH;
+static size_t suffix_length;
/* Alphabet of characters to use in suffix. */
static char const *suffix_alphabet = "abcdefghijklmnopqrstuvwxyz";
+/* Numerical suffix start value. */
+static const char *numeric_suffix_start;
+
+/* Additional suffix to append to output file names. */
+static char const *additional_suffix;
+
/* Name of input file. May be "-". */
static char *infile;
+/* stat buf for input file. */
+static struct stat in_stat_buf;
+
/* Descriptor on which output file is open. */
-static int output_desc;
+static int output_desc = -1;
/* If true, print a diagnostic on standard error just before each
output file is opened. */
static bool verbose;
+/* If true, don't generate zero length output files. */
+static bool elide_empty_files;
+
+/* If true, in round robin mode, immediately copy
+ input to output, which is much slower, so disabled by default. */
+static bool unbuffered;
+
+/* The character marking end of line. Defaults to \n below. */
+static int eolchar = -1;
+
+/* The split mode to use. */
+enum Split_type
+{
+ type_undef, type_bytes, type_byteslines, type_lines, type_digits,
+ type_chunk_bytes, type_chunk_lines, type_rr
+};
+
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
{
- VERBOSE_OPTION = CHAR_MAX + 1
+ VERBOSE_OPTION = CHAR_MAX + 1,
+ FILTER_OPTION,
+ IO_BLKSIZE_OPTION,
+ ADDITIONAL_SUFFIX_OPTION
};
static struct option const longopts[] =
@@ -87,92 +133,259 @@ static struct option const longopts[] =
{"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'},
- {"numeric-suffixes", no_argument, NULL, 'd'},
+ {"additional-suffix", required_argument, NULL,
+ ADDITIONAL_SUFFIX_OPTION},
+ {"numeric-suffixes", optional_argument, NULL, 'd'},
+ {"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},
{NULL, 0, NULL, 0}
};
+/* Return true if the errno value, ERR, is ignorable. */
+static inline bool
+ignorable (int err)
+{
+ return filter_command && err == EPIPE;
+}
+
+static void
+set_suffix_length (uintmax_t n_units, enum Split_type split_type)
+{
+#define DEFAULT_SUFFIX_LENGTH 2
+
+ uintmax_t suffix_needed = 0;
+
+ /* The suffix auto length feature is incompatible with
+ a user specified start value as the generated suffixes
+ are not all consecutive. */
+ if (numeric_suffix_start)
+ suffix_auto = false;
+
+ /* Auto-calculate the suffix length if the number of files is given. */
+ if (split_type == type_chunk_bytes || split_type == type_chunk_lines
+ || split_type == type_rr)
+ {
+ uintmax_t n_units_end = n_units;
+ if (numeric_suffix_start)
+ {
+ uintmax_t n_start;
+ strtol_error e = xstrtoumax (numeric_suffix_start, NULL, 10,
+ &n_start, "");
+ if (e == LONGINT_OK && n_start <= UINTMAX_MAX - n_units)
+ {
+ /* Restrict auto adjustment so we don't keep
+ incrementing a suffix size arbitrarily,
+ as that would break sort order for files
+ generated from multiple split runs. */
+ if (n_start < n_units)
+ n_units_end += n_start;
+ }
+
+ }
+ size_t alphabet_len = strlen (suffix_alphabet);
+ bool alphabet_slop = (n_units_end % alphabet_len) != 0;
+ while (n_units_end /= alphabet_len)
+ suffix_needed++;
+ suffix_needed += alphabet_slop;
+ suffix_auto = false;
+ }
+
+ if (suffix_length) /* set by user */
+ {
+ if (suffix_length < suffix_needed)
+ {
+ error (EXIT_FAILURE, 0,
+ _("the suffix length needs to be at least %"PRIuMAX),
+ suffix_needed);
+ }
+ suffix_auto = false;
+ return;
+ }
+ else
+ suffix_length = MAX (DEFAULT_SUFFIX_LENGTH, suffix_needed);
+}
+
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
-Usage: %s [OPTION] [INPUT [PREFIX]]\n\
+Usage: %s [OPTION]... [FILE [PREFIX]]\n\
"),
- program_name);
- fputs (_("\
-Output fixed-size pieces of INPUT to PREFIXaa, PREFIXab, ...; default\n\
-size is 1000 lines, and default PREFIX is `x'. With no INPUT, or when INPUT\n\
-is -, read standard input.\n\
-\n\
-"), stdout);
+ program_name);
fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
+Output pieces of FILE to PREFIXaa, PREFIXab, ...;\n\
+default size is 1000 lines, and default PREFIX is 'x'.\n\
"), stdout);
+
+ emit_stdin_note ();
+ emit_mandatory_arg_note ();
+
fprintf (stdout, _("\
- -a, --suffix-length=N use suffixes of length N (default %d)\n\
+ -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 lines per output file\n\
- -d, --numeric-suffixes use numeric suffixes instead of alphabetic\n\
- -l, --lines=NUMBER put NUMBER lines 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\
+ -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\
"), DEFAULT_SUFFIX_LENGTH);
fputs (_("\
- --verbose print a diagnostic to standard error just\n\
- before each output file is opened\n\
+ --verbose print a diagnostic just before each\n\
+ output file is opened\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- fputs (_("\
-\n\
-SIZE may have a multiplier suffix: b for 512, k for 1K, m for 1 Meg.\n\
+ emit_size_note ();
+ fputs (_("\n\
+CHUNKS may be:\n\
+ N split into N files based on size of input\n\
+ K/N output Kth of N to stdout\n\
+ l/N split into N files without splitting lines/records\n\
+ l/K/N output Kth of N to stdout without splitting lines/records\n\
+ r/N like 'l' but use round robin distribution\n\
+ r/K/N likewise but only output Kth of N to stdout\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
+/* Return the number of bytes that can be read from FD, a file with
+ apparent size SIZE. Actually read the data into BUF (of size
+ BUFSIZE) if the file appears to be smaller than BUFSIZE, as this
+ works better on proc-like file systems. If the returned value is
+ less than BUFSIZE, store all the file's data into BUF; otherwise,
+ restore the input file's position so that the file can be reread if
+ needed. */
+
+static off_t
+input_file_size (int fd, off_t size, char *buf, size_t bufsize)
+{
+ if (size < bufsize)
+ {
+ size = 0;
+ while (true)
+ {
+ size_t save = size < bufsize ? size : 0;
+ size_t n_read = safe_read (fd, buf + save, bufsize - save);
+ if (n_read == 0)
+ break;
+ if (n_read == SAFE_READ_ERROR)
+ error (EXIT_FAILURE, errno, "%s", quotef (infile));
+ size += n_read;
+ }
+ if (bufsize <= size && lseek (fd, - size, SEEK_CUR) < 0)
+ error (EXIT_FAILURE, errno, "%s", quotef (infile));
+ }
+
+ return size;
+}
+
/* Compute the next sequential output file name and store it into the
- string `outfile'. */
+ string 'outfile'. */
static void
next_file_name (void)
{
/* Index in suffix_alphabet of each character in the suffix. */
static size_t *sufindex;
+ static size_t outbase_length;
+ static size_t outfile_length;
+ static size_t addsuf_length;
if (! outfile)
{
- /* Allocate and initialize the first file name. */
+ bool widen;
+
+new_name:
+ widen = !! outfile_length;
+
+ if (! widen)
+ {
+ /* Allocate and initialize the first file name. */
+
+ outbase_length = strlen (outbase);
+ addsuf_length = additional_suffix ? strlen (additional_suffix) : 0;
+ outfile_length = outbase_length + suffix_length + addsuf_length;
+ }
+ else
+ {
+ /* Reallocate and initialize a new wider file name.
+ We do this by subsuming the unchanging part of
+ the generated suffix into the prefix (base), and
+ reinitializing the now one longer suffix. */
+
+ outfile_length += 2;
+ suffix_length++;
+ }
- size_t outbase_length = strlen (outbase);
- size_t outfile_length = outbase_length + suffix_length;
if (outfile_length + 1 < outbase_length)
- xalloc_die ();
- outfile = xmalloc (outfile_length + 1);
+ xalloc_die ();
+ outfile = xrealloc (outfile, outfile_length + 1);
+
+ if (! widen)
+ memcpy (outfile, outbase, outbase_length);
+ else
+ {
+ /* Append the last alphabet character to the file name prefix. */
+ outfile[outbase_length] = suffix_alphabet[sufindex[0]];
+ outbase_length++;
+ }
+
outfile_mid = outfile + outbase_length;
- memcpy (outfile, outbase, outbase_length);
memset (outfile_mid, suffix_alphabet[0], suffix_length);
+ if (additional_suffix)
+ memcpy (outfile_mid + suffix_length, additional_suffix, addsuf_length);
outfile[outfile_length] = 0;
+
+ free (sufindex);
sufindex = xcalloc (suffix_length, sizeof *sufindex);
+ if (numeric_suffix_start)
+ {
+ assert (! widen);
+
+ /* Update the output file name. */
+ size_t i = strlen (numeric_suffix_start);
+ memcpy (outfile_mid + suffix_length - i, numeric_suffix_start, i);
+
+ /* Update the suffix index. */
+ size_t *sufindex_end = sufindex + suffix_length;
+ while (i-- != 0)
+ *--sufindex_end = numeric_suffix_start[i] - '0';
+ }
+
#if ! _POSIX_NO_TRUNC && HAVE_PATHCONF && defined _PC_NAME_MAX
/* POSIX requires that if the output file name is too long for
- its directory, `split' must fail without creating any files.
- This must be checked for explicitly on operating systems that
- silently truncate file names. */
+ its directory, 'split' must fail without creating any files.
+ This must be checked for explicitly on operating systems that
+ silently truncate file names. */
{
- char *dir = dir_name (outfile);
- long name_max = pathconf (dir, _PC_NAME_MAX);
- if (0 <= name_max && name_max < base_len (last_component (outfile)))
- error (EXIT_FAILURE, ENAMETOOLONG, "%s", outfile);
- free (dir);
+ char *dir = dir_name (outfile);
+ long name_max = pathconf (dir, _PC_NAME_MAX);
+ if (0 <= name_max && name_max < base_len (last_component (outfile)))
+ error (EXIT_FAILURE, ENAMETOOLONG, "%s", quotef (outfile));
+ free (dir);
}
#endif
}
@@ -182,15 +395,153 @@ next_file_name (void)
size_t i = suffix_length;
while (i-- != 0)
- {
- sufindex[i]++;
- outfile_mid[i] = suffix_alphabet[sufindex[i]];
- if (outfile_mid[i])
- return;
- sufindex[i] = 0;
- outfile_mid[i] = suffix_alphabet[sufindex[i]];
- }
- error (EXIT_FAILURE, 0, _("Output file suffixes exhausted"));
+ {
+ sufindex[i]++;
+ if (suffix_auto && i == 0 && ! suffix_alphabet[sufindex[0] + 1])
+ goto new_name;
+ outfile_mid[i] = suffix_alphabet[sufindex[i]];
+ if (outfile_mid[i])
+ return;
+ sufindex[i] = 0;
+ outfile_mid[i] = suffix_alphabet[sufindex[i]];
+ }
+ error (EXIT_FAILURE, 0, _("output file suffixes exhausted"));
+ }
+}
+
+/* Create or truncate a file. */
+
+static int
+create (const char *name)
+{
+ if (!filter_command)
+ {
+ if (verbose)
+ fprintf (stdout, _("creating file %s\n"), quoteaf (name));
+
+ int fd = open (name, O_WRONLY | O_CREAT | O_BINARY, MODE_RW_UGO);
+ if (fd < 0)
+ return fd;
+ struct stat out_stat_buf;
+ if (fstat (fd, &out_stat_buf) != 0)
+ error (EXIT_FAILURE, errno, _("failed to stat %s"), quoteaf (name));
+ if (SAME_INODE (in_stat_buf, out_stat_buf))
+ error (EXIT_FAILURE, 0, _("%s would overwrite input; aborting"),
+ quoteaf (name));
+ if (ftruncate (fd, 0) != 0)
+ error (EXIT_FAILURE, errno, _("%s: error truncating"), quotef (name));
+
+ return fd;
+ }
+ else
+ {
+ int fd_pair[2];
+ pid_t child_pid;
+ char const *shell_prog = getenv ("SHELL");
+ if (shell_prog == NULL)
+ shell_prog = "/bin/sh";
+ if (setenv ("FILE", name, 1) != 0)
+ error (EXIT_FAILURE, errno,
+ _("failed to set FILE environment variable"));
+ if (verbose)
+ fprintf (stdout, _("executing with FILE=%s\n"), quotef (name));
+ if (pipe (fd_pair) != 0)
+ error (EXIT_FAILURE, errno, _("failed to create pipe"));
+ child_pid = fork ();
+ if (child_pid == 0)
+ {
+ /* This is the child process. If an error occurs here, the
+ parent will eventually learn about it after doing a wait,
+ at which time it will emit its own error message. */
+ int j;
+ /* We have to close any pipes that were opened during an
+ earlier call, otherwise this process will be holding a
+ write-pipe that will prevent the earlier process from
+ reading an EOF on the corresponding read-pipe. */
+ for (j = 0; j < n_open_pipes; ++j)
+ if (close (open_pipes[j]) != 0)
+ error (EXIT_FAILURE, errno, _("closing prior pipe"));
+ if (close (fd_pair[1]))
+ error (EXIT_FAILURE, errno, _("closing output pipe"));
+ if (fd_pair[0] != STDIN_FILENO)
+ {
+ if (dup2 (fd_pair[0], STDIN_FILENO) != STDIN_FILENO)
+ error (EXIT_FAILURE, errno, _("moving input pipe"));
+ if (close (fd_pair[0]) != 0)
+ error (EXIT_FAILURE, errno, _("closing input pipe"));
+ }
+ sigprocmask (SIG_SETMASK, &oldblocked, NULL);
+ execl (shell_prog, last_component (shell_prog), "-c",
+ filter_command, (char *) NULL);
+ error (EXIT_FAILURE, errno, _("failed to run command: \"%s -c %s\""),
+ shell_prog, filter_command);
+ }
+ if (child_pid == -1)
+ error (EXIT_FAILURE, errno, _("fork system call failed"));
+ if (close (fd_pair[0]) != 0)
+ error (EXIT_FAILURE, errno, _("failed to close input pipe"));
+ filter_pid = child_pid;
+ if (n_open_pipes == open_pipes_alloc)
+ open_pipes = x2nrealloc (open_pipes, &open_pipes_alloc,
+ sizeof *open_pipes);
+ open_pipes[n_open_pipes++] = fd_pair[1];
+ return fd_pair[1];
+ }
+}
+
+/* Close the output file, and do any associated cleanup.
+ If FP and FD are both specified, they refer to the same open file;
+ in this case FP is closed, but FD is still used in cleanup. */
+static void
+closeout (FILE *fp, int fd, pid_t pid, char const *name)
+{
+ if (fp != NULL && fclose (fp) != 0 && ! ignorable (errno))
+ error (EXIT_FAILURE, errno, "%s", quotef (name));
+ if (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)
+ {
+ if (open_pipes[j] == fd)
+ {
+ open_pipes[j] = open_pipes[--n_open_pipes];
+ break;
+ }
+ }
+ }
+ if (pid > 0)
+ {
+ int wstatus = 0;
+ if (waitpid (pid, &wstatus, 0) == -1 && errno != ECHILD)
+ error (EXIT_FAILURE, errno, _("waiting for child process"));
+ if (WIFSIGNALED (wstatus))
+ {
+ int sig = WTERMSIG (wstatus);
+ if (sig != SIGPIPE)
+ {
+ char signame[MAX (SIG2STR_MAX, INT_BUFSIZE_BOUND (int))];
+ if (sig2str (sig, signame) != 0)
+ sprintf (signame, "%d", sig);
+ error (sig + 128, 0,
+ _("with FILE=%s, signal %s from command: %s"),
+ quotef (name), signame, filter_command);
+ }
+ }
+ else if (WIFEXITED (wstatus))
+ {
+ int ex = WEXITSTATUS (wstatus);
+ if (ex != 0)
+ error (ex, 0, _("with FILE=%s, exit %d from command: %s"),
+ quotef (name), ex, filter_command);
+ }
+ else
+ {
+ /* shouldn't happen. */
+ error (EXIT_FAILURE, 0,
+ _("unknown status from command (0x%X)"), wstatus + 0u);
+ }
}
}
@@ -203,66 +554,86 @@ cwrite (bool new_file_flag, const char *bp, size_t bytes)
{
if (new_file_flag)
{
- if (output_desc >= 0 && close (output_desc) < 0)
- error (EXIT_FAILURE, errno, "%s", outfile);
-
+ if (!bp && bytes == 0 && elide_empty_files)
+ return;
+ closeout (NULL, output_desc, filter_pid, outfile);
next_file_name ();
- if (verbose)
- fprintf (stderr, _("creating file %s\n"), quote (outfile));
- output_desc = open (outfile,
- O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
- (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
- | S_IROTH | S_IWOTH));
- if (output_desc < 0)
- error (EXIT_FAILURE, errno, "%s", outfile);
- }
- if (full_write (output_desc, bp, bytes) != bytes)
- error (EXIT_FAILURE, errno, "%s", outfile);
+ if ((output_desc = create (outfile)) < 0)
+ error (EXIT_FAILURE, errno, "%s", quotef (outfile));
+ }
+ if (full_write (output_desc, bp, bytes) != bytes && ! ignorable (errno))
+ error (EXIT_FAILURE, errno, "%s", quotef (outfile));
}
/* Split into pieces of exactly N_BYTES bytes.
- Use buffer BUF, whose size is BUFSIZE. */
+ Use buffer BUF, whose size is BUFSIZE.
+ If INITIAL_READ != SIZE_MAX, the entire input file has already been
+ partly read into BUF and BUF contains INITIAL_READ input bytes. */
static void
-bytes_split (uintmax_t n_bytes, char *buf, size_t bufsize)
+bytes_split (uintmax_t n_bytes, char *buf, size_t bufsize, size_t initial_read,
+ uintmax_t max_files)
{
size_t n_read;
bool new_file_flag = true;
size_t to_read;
uintmax_t to_write = n_bytes;
char *bp_out;
+ uintmax_t opened = 0;
do
{
- n_read = full_read (STDIN_FILENO, buf, bufsize);
- if (n_read == SAFE_READ_ERROR)
- error (EXIT_FAILURE, errno, "%s", infile);
+ if (initial_read != SIZE_MAX)
+ {
+ n_read = initial_read;
+ initial_read = SIZE_MAX;
+ }
+ else
+ {
+ n_read = safe_read (STDIN_FILENO, buf, bufsize);
+ if (n_read == SAFE_READ_ERROR)
+ error (EXIT_FAILURE, errno, "%s", quotef (infile));
+ }
bp_out = buf;
to_read = n_read;
- for (;;)
- {
- if (to_read < to_write)
- {
- if (to_read) /* do not write 0 bytes! */
- {
- cwrite (new_file_flag, bp_out, to_read);
- to_write -= to_read;
- new_file_flag = false;
- }
- break;
- }
- else
- {
- size_t w = to_write;
- cwrite (new_file_flag, bp_out, w);
- bp_out += w;
- to_read -= w;
- new_file_flag = true;
- to_write = n_bytes;
- }
- }
- }
- while (n_read == bufsize);
+ while (true)
+ {
+ if (to_read < to_write)
+ {
+ if (to_read) /* do not write 0 bytes! */
+ {
+ cwrite (new_file_flag, bp_out, to_read);
+ opened += new_file_flag;
+ to_write -= to_read;
+ new_file_flag = false;
+ }
+ break;
+ }
+ else
+ {
+ size_t w = to_write;
+ cwrite (new_file_flag, bp_out, w);
+ opened += new_file_flag;
+ new_file_flag = !max_files || (opened < max_files);
+ if (!new_file_flag && ignorable (errno))
+ {
+ /* If filter no longer accepting input, stop reading. */
+ n_read = 0;
+ break;
+ }
+ bp_out += w;
+ to_read -= w;
+ to_write = n_bytes;
+ }
+ }
+ }
+ while (n_read);
+
+ /* Ensure NUMBER files are created, which truncates
+ any existing files or notifies any consumers on fifos.
+ FIXME: Should we do this before EXIT_FAILURE? */
+ while (opened++ < max_files)
+ cwrite (true, NULL, 0);
}
/* Split into pieces of exactly N_LINES lines.
@@ -278,90 +649,555 @@ lines_split (uintmax_t n_lines, char *buf, size_t bufsize)
do
{
- n_read = full_read (STDIN_FILENO, buf, bufsize);
+ n_read = safe_read (STDIN_FILENO, buf, bufsize);
if (n_read == SAFE_READ_ERROR)
- error (EXIT_FAILURE, errno, "%s", infile);
+ error (EXIT_FAILURE, errno, "%s", quotef (infile));
bp = bp_out = buf;
eob = bp + n_read;
- *eob = '\n';
- for (;;)
- {
- bp = memchr (bp, '\n', eob - bp + 1);
- if (bp == eob)
- {
- if (eob != bp_out) /* do not write 0 bytes! */
- {
- size_t len = eob - bp_out;
- cwrite (new_file_flag, bp_out, len);
- new_file_flag = false;
- }
- break;
- }
-
- ++bp;
- if (++n >= n_lines)
- {
- cwrite (new_file_flag, bp_out, bp - bp_out);
- bp_out = bp;
- new_file_flag = true;
- n = 0;
- }
- }
- }
- while (n_read == bufsize);
+ *eob = eolchar;
+ while (true)
+ {
+ bp = memchr (bp, eolchar, eob - bp + 1);
+ if (bp == eob)
+ {
+ if (eob != bp_out) /* do not write 0 bytes! */
+ {
+ size_t len = eob - bp_out;
+ cwrite (new_file_flag, bp_out, len);
+ new_file_flag = false;
+ }
+ break;
+ }
+
+ ++bp;
+ if (++n >= n_lines)
+ {
+ cwrite (new_file_flag, bp_out, bp - bp_out);
+ bp_out = bp;
+ new_file_flag = true;
+ n = 0;
+ }
+ }
+ }
+ while (n_read);
}
-
+
/* Split into pieces that are as large as possible while still not more
than N_BYTES bytes, and are split on line boundaries except
- where lines longer than N_BYTES bytes occur.
- FIXME: Allow N_BYTES to be any uintmax_t value, and don't require a
- buffer of size N_BYTES, in case N_BYTES is very large. */
+ where lines longer than N_BYTES bytes occur. */
static void
-line_bytes_split (size_t n_bytes)
+line_bytes_split (uintmax_t n_bytes, char *buf, size_t bufsize)
{
size_t n_read;
- char *bp;
- bool eof = false;
- size_t n_buffered = 0;
- char *buf = xmalloc (n_bytes);
+ uintmax_t n_out = 0; /* for each split. */
+ size_t n_hold = 0;
+ char *hold = NULL; /* for lines > bufsize. */
+ size_t hold_size = 0;
+ bool split_line = false; /* Whether a \n was output in a split. */
do
{
- /* Fill up the full buffer size from the input file. */
+ n_read = safe_read (STDIN_FILENO, buf, bufsize);
+ if (n_read == SAFE_READ_ERROR)
+ error (EXIT_FAILURE, errno, "%s", quotef (infile));
+ size_t n_left = n_read;
+ char *sob = buf;
+ while (n_left)
+ {
+ size_t split_rest = 0;
+ char *eoc = NULL;
+ char *eol;
+
+ /* Determine End Of Chunk and/or End of Line,
+ which are used below to select what to write or buffer. */
+ if (n_bytes - n_out - n_hold <= n_left)
+ {
+ /* Have enough for split. */
+ split_rest = n_bytes - n_out - n_hold;
+ eoc = sob + split_rest - 1;
+ eol = memrchr (sob, eolchar, split_rest);
+ }
+ else
+ eol = memrchr (sob, eolchar, n_left);
+
+ /* Output hold space if possible. */
+ if (n_hold && !(!eol && n_out))
+ {
+ cwrite (n_out == 0, hold, n_hold);
+ n_out += n_hold;
+ if (n_hold > bufsize)
+ hold = xrealloc (hold, bufsize);
+ n_hold = 0;
+ hold_size = bufsize;
+ }
+
+ /* Output to eol if present. */
+ if (eol)
+ {
+ split_line = true;
+ size_t n_write = eol - sob + 1;
+ cwrite (n_out == 0, sob, n_write);
+ n_out += n_write;
+ n_left -= n_write;
+ sob += n_write;
+ if (eoc)
+ split_rest -= n_write;
+ }
+
+ /* Output to eoc or eob if possible. */
+ if (n_left && !split_line)
+ {
+ size_t n_write = eoc ? split_rest : n_left;
+ cwrite (n_out == 0, sob, n_write);
+ n_out += n_write;
+ n_left -= n_write;
+ sob += n_write;
+ if (eoc)
+ split_rest -= n_write;
+ }
+
+ /* Update hold if needed. */
+ if ((eoc && split_rest) || (!eoc && n_left))
+ {
+ size_t n_buf = eoc ? split_rest : n_left;
+ if (hold_size - n_hold < n_buf)
+ {
+ if (hold_size <= SIZE_MAX - bufsize)
+ hold_size += bufsize;
+ else
+ xalloc_die ();
+ hold = xrealloc (hold, hold_size);
+ }
+ memcpy (hold + n_hold, sob, n_buf);
+ n_hold += n_buf;
+ n_left -= n_buf;
+ sob += n_buf;
+ }
+
+ /* Reset for new split. */
+ if (eoc)
+ {
+ n_out = 0;
+ split_line = false;
+ }
+ }
+ }
+ while (n_read);
+
+ /* Handle no eol at end of file. */
+ if (n_hold)
+ cwrite (n_out == 0, hold, n_hold);
+
+ free (hold);
+}
+
+/* -n l/[K/]N: Write lines to files of approximately file size / N.
+ The file is partitioned into file size / N sized portions, with the
+ last assigned any excess. If a line _starts_ within a partition
+ it is written completely to the corresponding file. Since lines
+ 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 is so long as to completely overlap the partition. */
+
+static void
+lines_chunk_split (uintmax_t k, uintmax_t n, char *buf, size_t bufsize,
+ size_t initial_read, off_t file_size)
+{
+ assert (n && k <= n && n <= file_size);
+
+ const off_t chunk_size = file_size / n;
+ uintmax_t chunk_no = 1;
+ off_t chunk_end = chunk_size - 1;
+ off_t n_written = 0;
+ bool new_file_flag = true;
+ bool chunk_truncated = false;
+
+ if (k > 1)
+ {
+ /* Start reading 1 byte before kth chunk of file. */
+ off_t start = (k - 1) * chunk_size - 1;
+ if (initial_read != SIZE_MAX)
+ {
+ memmove (buf, buf + start, initial_read - start);
+ initial_read -= start;
+ }
+ else if (lseek (STDIN_FILENO, start, SEEK_CUR) < 0)
+ error (EXIT_FAILURE, errno, "%s", quotef (infile));
+ n_written = start;
+ chunk_no = k - 1;
+ chunk_end = chunk_no * chunk_size - 1;
+ }
+
+ while (n_written < file_size)
+ {
+ char *bp = buf, *eob;
+ size_t n_read;
+ if (initial_read != SIZE_MAX)
+ {
+ n_read = initial_read;
+ initial_read = SIZE_MAX;
+ }
+ else
+ {
+ n_read = safe_read (STDIN_FILENO, buf, bufsize);
+ if (n_read == SAFE_READ_ERROR)
+ error (EXIT_FAILURE, errno, "%s", quotef (infile));
+ }
+ if (n_read == 0)
+ break; /* eof. */
+ n_read = MIN (n_read, file_size - n_written);
+ chunk_truncated = false;
+ eob = buf + n_read;
+
+ while (bp != eob)
+ {
+ size_t to_write;
+ bool next = false;
+
+ /* Begin looking for '\n' at last byte of chunk. */
+ off_t skip = MIN (n_read, MAX (0, chunk_end - n_written));
+ char *bp_out = memchr (bp + skip, eolchar, n_read - skip);
+ if (bp_out++)
+ next = true;
+ else
+ bp_out = eob;
+ to_write = bp_out - bp;
+
+ if (k == chunk_no)
+ {
+ /* We don't use the stdout buffer here since we're writing
+ large chunks from an existing file, so it's more efficient
+ to write out directly. */
+ if (full_write (STDOUT_FILENO, bp, to_write) != to_write)
+ error (EXIT_FAILURE, errno, "%s", _("write error"));
+ }
+ else if (! k)
+ cwrite (new_file_flag, bp, to_write);
+ n_written += to_write;
+ bp += to_write;
+ n_read -= to_write;
+ new_file_flag = next;
+
+ /* A line could have been so long that it skipped
+ entire chunks. So create empty files in that case. */
+ while (next || chunk_end <= n_written - 1)
+ {
+ if (!next && bp == eob)
+ {
+ /* replenish buf, before going to next chunk. */
+ chunk_truncated = true;
+ break;
+ }
+ chunk_no++;
+ if (k && chunk_no > k)
+ return;
+ if (chunk_no == n)
+ chunk_end = file_size - 1; /* >= chunk_size. */
+ else
+ chunk_end += chunk_size;
+ if (chunk_end <= n_written - 1)
+ {
+ if (! k)
+ cwrite (true, NULL, 0);
+ }
+ else
+ next = false;
+ }
+ }
+ }
+
+ if (chunk_truncated)
+ chunk_no++;
+
+ /* Ensure NUMBER files are created, which truncates
+ any existing files or notifies any consumers on fifos.
+ FIXME: Should we do this before EXIT_FAILURE? */
+ while (!k && chunk_no++ <= n)
+ cwrite (true, NULL, 0);
+}
+
+/* -n K/N: Extract Kth of N chunks. */
+
+static void
+bytes_chunk_extract (uintmax_t k, uintmax_t n, char *buf, size_t bufsize,
+ size_t initial_read, off_t file_size)
+{
+ off_t start;
+ off_t end;
+
+ assert (k && n && k <= n && n <= file_size);
+
+ start = (k - 1) * (file_size / n);
+ end = (k == n) ? file_size : k * (file_size / n);
+
+ if (initial_read != SIZE_MAX)
+ {
+ memmove (buf, buf + start, initial_read - start);
+ initial_read -= start;
+ }
+ else if (lseek (STDIN_FILENO, start, SEEK_CUR) < 0)
+ error (EXIT_FAILURE, errno, "%s", quotef (infile));
+
+ while (start < end)
+ {
+ size_t n_read;
+ if (initial_read != SIZE_MAX)
+ {
+ n_read = initial_read;
+ initial_read = SIZE_MAX;
+ }
+ else
+ {
+ n_read = safe_read (STDIN_FILENO, buf, bufsize);
+ if (n_read == SAFE_READ_ERROR)
+ error (EXIT_FAILURE, errno, "%s", quotef (infile));
+ }
+ if (n_read == 0)
+ break; /* eof. */
+ n_read = MIN (n_read, end - start);
+ if (full_write (STDOUT_FILENO, buf, n_read) != n_read
+ && ! ignorable (errno))
+ error (EXIT_FAILURE, errno, "%s", quotef ("-"));
+ start += n_read;
+ }
+}
+
+typedef struct of_info
+{
+ char *of_name;
+ int ofd;
+ FILE *ofile;
+ int opid;
+} of_t;
+
+enum
+{
+ OFD_NEW = -1,
+ OFD_APPEND = -2
+};
+
+/* Rotate file descriptors when we're writing to more output files than we
+ have available file descriptors.
+ Return whether we came under file resource pressure.
+ If so, it's probably best to close each file when finished with it. */
+
+static bool
+ofile_open (of_t *files, size_t i_check, size_t nfiles)
+{
+ bool file_limit = false;
+
+ if (files[i_check].ofd <= OFD_NEW)
+ {
+ int fd;
+ size_t i_reopen = i_check ? i_check - 1 : nfiles - 1;
+
+ /* Another process could have opened a file in between the calls to
+ close and open, so we should keep trying until open succeeds or
+ we've closed all of our files. */
+ while (true)
+ {
+ if (files[i_check].ofd == OFD_NEW)
+ fd = create (files[i_check].of_name);
+ else /* OFD_APPEND */
+ {
+ /* Attempt to append to previously opened file.
+ We use O_NONBLOCK to support writing to fifos,
+ where the other end has closed because of our
+ previous close. In that case we'll immediately
+ get an error, rather than waiting indefinitely.
+ In specialised cases the consumer can keep reading
+ from the fifo, terminating on conditions in the data
+ itself, or perhaps never in the case of 'tail -f'.
+ I.e., for fifos it is valid to attempt this reopen.
+
+ We don't handle the filter_command case here, as create()
+ will exit if there are not enough files in that case.
+ I.e., we don't support restarting filters, as that would
+ put too much burden on users specifying --filter commands. */
+ fd = open (files[i_check].of_name,
+ O_WRONLY | O_BINARY | O_APPEND | O_NONBLOCK);
+ }
+
+ if (-1 < fd)
+ break;
+
+ if (!(errno == EMFILE || errno == ENFILE))
+ error (EXIT_FAILURE, errno, "%s", quotef (files[i_check].of_name));
+
+ file_limit = true;
+
+ /* Search backwards for an open file to close. */
+ while (files[i_reopen].ofd < 0)
+ {
+ i_reopen = i_reopen ? i_reopen - 1 : nfiles - 1;
+ /* No more open files to close, exit with E[NM]FILE. */
+ if (i_reopen == i_check)
+ error (EXIT_FAILURE, errno, "%s",
+ quotef (files[i_check].of_name));
+ }
+
+ if (fclose (files[i_reopen].ofile) != 0)
+ error (EXIT_FAILURE, errno, "%s", quotef (files[i_reopen].of_name));
+ files[i_reopen].ofile = NULL;
+ files[i_reopen].ofd = OFD_APPEND;
+ }
+
+ files[i_check].ofd = fd;
+ if (!(files[i_check].ofile = fdopen (fd, "a")))
+ error (EXIT_FAILURE, errno, "%s", quotef (files[i_check].of_name));
+ files[i_check].opid = filter_pid;
+ filter_pid = 0;
+ }
+
+ return file_limit;
+}
- n_read = full_read (STDIN_FILENO, buf + n_buffered, n_bytes - n_buffered);
+/* -n r/[K/]N: Divide file into N chunks in round robin fashion.
+ When K == 0, we try to keep the files open in parallel.
+ If we run out of file resources, then we revert
+ to opening and closing each file for each line. */
+
+static void
+lines_rr (uintmax_t k, uintmax_t n, char *buf, size_t bufsize)
+{
+ bool wrapped = false;
+ bool wrote = false;
+ bool file_limit;
+ size_t i_file;
+ of_t *files IF_LINT (= NULL);
+ uintmax_t line_no;
+
+ if (k)
+ line_no = 1;
+ else
+ {
+ if (SIZE_MAX < n)
+ xalloc_die ();
+ files = xnmalloc (n, sizeof *files);
+
+ /* Generate output file names. */
+ for (i_file = 0; i_file < n; i_file++)
+ {
+ next_file_name ();
+ files[i_file].of_name = xstrdup (outfile);
+ files[i_file].ofd = OFD_NEW;
+ files[i_file].ofile = NULL;
+ files[i_file].opid = 0;
+ }
+ i_file = 0;
+ file_limit = false;
+ }
+
+ while (true)
+ {
+ char *bp = buf, *eob;
+ size_t n_read = safe_read (STDIN_FILENO, buf, bufsize);
if (n_read == SAFE_READ_ERROR)
- error (EXIT_FAILURE, errno, "%s", infile);
-
- n_buffered += n_read;
- if (n_buffered != n_bytes)
- eof = true;
-
- /* Find where to end this chunk. */
- bp = buf + n_buffered;
- if (n_buffered == n_bytes)
- {
- while (bp > buf && bp[-1] != '\n')
- bp--;
- }
-
- /* If chunk has no newlines, use all the chunk. */
- if (bp == buf)
- bp = buf + n_buffered;
-
- /* Output the chars as one output file. */
- cwrite (true, buf, bp - buf);
-
- /* Discard the chars we just output; move rest of chunk
- down to be the start of the next chunk. Source and
- destination probably overlap. */
- n_buffered -= bp - buf;
- if (n_buffered > 0)
- memmove (buf, bp, n_buffered);
- }
- while (!eof);
- free (buf);
+ error (EXIT_FAILURE, errno, "%s", quotef (infile));
+ else if (n_read == 0)
+ break; /* eof. */
+ eob = buf + n_read;
+
+ while (bp != eob)
+ {
+ size_t to_write;
+ bool next = false;
+
+ /* Find end of line. */
+ char *bp_out = memchr (bp, eolchar, eob - bp);
+ if (bp_out)
+ {
+ bp_out++;
+ next = true;
+ }
+ else
+ bp_out = eob;
+ to_write = bp_out - bp;
+
+ if (k)
+ {
+ if (line_no == k && unbuffered)
+ {
+ if (full_write (STDOUT_FILENO, bp, to_write) != to_write)
+ error (EXIT_FAILURE, errno, "%s", _("write error"));
+ }
+ else if (line_no == k && fwrite (bp, to_write, 1, stdout) != 1)
+ {
+ clearerr (stdout); /* To silence close_stdout(). */
+ error (EXIT_FAILURE, errno, "%s", _("write error"));
+ }
+ if (next)
+ line_no = (line_no == n) ? 1 : line_no + 1;
+ }
+ else
+ {
+ /* Secure file descriptor. */
+ file_limit |= ofile_open (files, i_file, n);
+ if (unbuffered)
+ {
+ /* Note writing to fd, rather than flushing the FILE gives
+ an 8% performance benefit, due to reduced data copying. */
+ if (full_write (files[i_file].ofd, bp, to_write) != to_write
+ && ! ignorable (errno))
+ {
+ error (EXIT_FAILURE, errno, "%s",
+ quotef (files[i_file].of_name));
+ }
+ }
+ else if (fwrite (bp, to_write, 1, files[i_file].ofile) != 1
+ && ! ignorable (errno))
+ {
+ error (EXIT_FAILURE, errno, "%s",
+ quotef (files[i_file].of_name));
+ }
+ if (! ignorable (errno))
+ wrote = true;
+
+ if (file_limit)
+ {
+ if (fclose (files[i_file].ofile) != 0)
+ {
+ error (EXIT_FAILURE, errno, "%s",
+ quotef (files[i_file].of_name));
+ }
+ files[i_file].ofile = NULL;
+ files[i_file].ofd = OFD_APPEND;
+ }
+ if (next && ++i_file == n)
+ {
+ wrapped = true;
+ /* If no filters are accepting input, stop reading. */
+ if (! wrote)
+ goto no_filters;
+ wrote = false;
+ i_file = 0;
+ }
+ }
+
+ bp = bp_out;
+ }
+ }
+
+no_filters:
+ /* Ensure all files created, so that any existing files are truncated,
+ and to signal any waiting fifo consumers.
+ Also, close any open file descriptors.
+ FIXME: Should we do this before EXIT_FAILURE? */
+ if (!k)
+ {
+ int ceiling = (wrapped ? n : i_file);
+ for (i_file = 0; i_file < n; i_file++)
+ {
+ if (i_file >= ceiling && !elide_empty_files)
+ file_limit |= ofile_open (files, i_file, n);
+ if (files[i_file].ofd >= 0)
+ closeout (files[i_file].ofile, files[i_file].ofd,
+ files[i_file].opid, files[i_file].of_name);
+ files[i_file].ofd = OFD_APPEND;
+ }
+ }
+ IF_LINT (free (files));
}
#define FAIL_ONLY_ONE_WAY() \
@@ -372,23 +1208,39 @@ line_bytes_split (size_t n_bytes)
} \
while (0)
+
+/* Parse K/N syntax of chunk options. */
+
+static void
+parse_chunk (uintmax_t *k_units, uintmax_t *n_units, char *slash)
+{
+ *n_units = xdectoumax (slash + 1, 1, UINTMAX_MAX, "",
+ _("invalid number of chunks"), 0);
+ if (slash != optarg) /* a leading number is specified. */
+ {
+ *slash = '\0';
+ *k_units = xdectoumax (optarg, 1, *n_units, "",
+ _("invalid chunk number"), 0);
+ }
+}
+
+
int
main (int argc, char **argv)
{
- struct stat stat_buf;
- enum
- {
- type_undef, type_bytes, type_byteslines, type_lines, type_digits
- } split_type = type_undef;
- size_t in_blk_size; /* optimal block size of input file device */
- char *buf; /* file i/o buffer */
+ enum Split_type split_type = type_undef;
+ size_t in_blk_size = 0; /* optimal block size of input file device */
size_t page_size = getpagesize ();
- uintmax_t n_units;
+ uintmax_t k_units = 0;
+ uintmax_t n_units = 0;
+
+ static char const multipliers[] = "bEGKkMmPTYZ0";
int c;
int digits_optind = 0;
+ off_t file_size IF_LINT (= 0);
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -397,113 +1249,204 @@ main (int argc, char **argv)
/* Parse command line options. */
- infile = "-";
- outbase = "x";
+ infile = bad_cast ("-");
+ outbase = bad_cast ("x");
- while (1)
+ while (true)
{
/* This is the argv-index of the option we will read next. */
int this_optind = optind ? optind : 1;
+ char *slash;
- c = getopt_long (argc, argv, "0123456789C:a:b:dl:", longopts, NULL);
+ c = getopt_long (argc, argv, "0123456789C:a:b:del:n:t:u",
+ longopts, NULL);
if (c == -1)
- break;
+ break;
switch (c)
- {
- case 'a':
- {
- unsigned long tmp;
- if (xstrtoul (optarg, NULL, 10, &tmp, "") != LONGINT_OK
- || SIZE_MAX / sizeof (size_t) < tmp)
- {
- error (0, 0, _("%s: invalid suffix length"), optarg);
- usage (EXIT_FAILURE);
- }
- suffix_length = tmp;
- }
- break;
-
- case 'b':
- if (split_type != type_undef)
- FAIL_ONLY_ONE_WAY ();
- split_type = type_bytes;
- if (xstrtoumax (optarg, NULL, 10, &n_units, "bkm") != LONGINT_OK
- || n_units == 0)
- {
- error (0, 0, _("%s: invalid number of bytes"), optarg);
- usage (EXIT_FAILURE);
- }
- break;
-
- case 'l':
- if (split_type != type_undef)
- FAIL_ONLY_ONE_WAY ();
- split_type = type_lines;
- if (xstrtoumax (optarg, NULL, 10, &n_units, "") != LONGINT_OK
- || n_units == 0)
- {
- error (0, 0, _("%s: invalid number of lines"), optarg);
- usage (EXIT_FAILURE);
- }
- break;
-
- case 'C':
- if (split_type != type_undef)
- FAIL_ONLY_ONE_WAY ();
- split_type = type_byteslines;
- if (xstrtoumax (optarg, NULL, 10, &n_units, "bkm") != LONGINT_OK
- || n_units == 0 || SIZE_MAX < n_units)
- {
- error (0, 0, _("%s: invalid number of bytes"), optarg);
- usage (EXIT_FAILURE);
- }
- break;
-
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (split_type == type_undef)
- {
- split_type = type_digits;
- n_units = 0;
- }
- if (split_type != type_undef && split_type != type_digits)
- FAIL_ONLY_ONE_WAY ();
- if (digits_optind != 0 && digits_optind != this_optind)
- n_units = 0; /* More than one number given; ignore other. */
- digits_optind = this_optind;
- if (!DECIMAL_DIGIT_ACCUMULATE (n_units, c - '0', uintmax_t))
- {
- char buffer[INT_BUFSIZE_BOUND (uintmax_t)];
- error (EXIT_FAILURE, 0,
- _("line count option -%s%c... is too large"),
- umaxtostr (n_units, buffer), c);
- }
- break;
-
- case 'd':
- suffix_alphabet = "0123456789";
- break;
-
- case VERBOSE_OPTION:
- verbose = true;
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'a':
+ suffix_length = xdectoumax (optarg, 0, SIZE_MAX / sizeof (size_t),
+ "", _("invalid suffix length"), 0);
+ break;
+
+ case ADDITIONAL_SUFFIX_OPTION:
+ if (last_component (optarg) != optarg)
+ {
+ error (0, 0,
+ _("invalid suffix %s, contains directory separator"),
+ quote (optarg));
+ usage (EXIT_FAILURE);
+ }
+ additional_suffix = optarg;
+ break;
+
+ case 'b':
+ if (split_type != type_undef)
+ FAIL_ONLY_ONE_WAY ();
+ split_type = type_bytes;
+ /* Limit to OFF_T_MAX, because if input is a pipe, we could get more
+ data than is possible to write to a single file, so indicate that
+ immediately rather than having possibly future invocations fail. */
+ n_units = xdectoumax (optarg, 1, OFF_T_MAX, multipliers,
+ _("invalid number of bytes"), 0);
+ break;
+
+ case 'l':
+ if (split_type != type_undef)
+ FAIL_ONLY_ONE_WAY ();
+ split_type = type_lines;
+ n_units = xdectoumax (optarg, 1, UINTMAX_MAX, "",
+ _("invalid number of lines"), 0);
+ break;
+
+ case 'C':
+ if (split_type != type_undef)
+ FAIL_ONLY_ONE_WAY ();
+ split_type = type_byteslines;
+ n_units = xdectoumax (optarg, 1, MIN (SIZE_MAX, OFF_T_MAX),
+ multipliers, _("invalid number of bytes"), 0);
+ break;
+
+ case 'n':
+ if (split_type != type_undef)
+ FAIL_ONLY_ONE_WAY ();
+ /* skip any whitespace */
+ while (isspace (to_uchar (*optarg)))
+ optarg++;
+ if (STRNCMP_LIT (optarg, "r/") == 0)
+ {
+ split_type = type_rr;
+ optarg += 2;
+ }
+ else if (STRNCMP_LIT (optarg, "l/") == 0)
+ {
+ split_type = type_chunk_lines;
+ optarg += 2;
+ }
+ else
+ split_type = type_chunk_bytes;
+ if ((slash = strchr (optarg, '/')))
+ parse_chunk (&k_units, &n_units, slash);
+ else
+ n_units = xdectoumax (optarg, 1, UINTMAX_MAX, "",
+ _("invalid number of chunks"), 0);
+ break;
+
+ case 'u':
+ unbuffered = true;
+ break;
+
+ case 't':
+ {
+ char neweol = optarg[0];
+ if (! neweol)
+ error (EXIT_FAILURE, 0, _("empty record separator"));
+ if (optarg[1])
+ {
+ if (STREQ (optarg, "\\0"))
+ neweol = '\0';
+ else
+ {
+ /* Provoke with 'split -txx'. Complain about
+ "multi-character tab" instead of "multibyte tab", so
+ that the diagnostic's wording does not need to be
+ changed once multibyte characters are supported. */
+ error (EXIT_FAILURE, 0, _("multi-character separator %s"),
+ quote (optarg));
+ }
+ }
+ /* Make it explicit we don't support multiple separators. */
+ if (0 <= eolchar && neweol != eolchar)
+ {
+ error (EXIT_FAILURE, 0,
+ _("multiple separator characters specified"));
+ }
+
+ eolchar = neweol;
+ }
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (split_type == type_undef)
+ {
+ split_type = type_digits;
+ n_units = 0;
+ }
+ if (split_type != type_undef && split_type != type_digits)
+ FAIL_ONLY_ONE_WAY ();
+ if (digits_optind != 0 && digits_optind != this_optind)
+ n_units = 0; /* More than one number given; ignore other. */
+ digits_optind = this_optind;
+ if (!DECIMAL_DIGIT_ACCUMULATE (n_units, c - '0', uintmax_t))
+ {
+ char buffer[INT_BUFSIZE_BOUND (uintmax_t)];
+ error (EXIT_FAILURE, 0,
+ _("line count option -%s%c... is too large"),
+ umaxtostr (n_units, buffer), c);
+ }
+ break;
+
+ case 'd':
+ suffix_alphabet = "0123456789";
+ if (optarg)
+ {
+ if (strlen (optarg) != strspn (optarg, suffix_alphabet))
+ {
+ error (0, 0,
+ _("%s: invalid start value for numerical suffix"),
+ quote (optarg));
+ usage (EXIT_FAILURE);
+ }
+ else
+ {
+ /* Skip any leading zero. */
+ while (*optarg == '0' && *(optarg + 1) != '\0')
+ optarg++;
+ numeric_suffix_start = optarg;
+ }
+ }
+ break;
+
+ case 'e':
+ elide_empty_files = true;
+ break;
+
+ case FILTER_OPTION:
+ filter_command = optarg;
+ break;
+
+ case IO_BLKSIZE_OPTION:
+ in_blk_size = xdectoumax (optarg, 1, SIZE_MAX - page_size,
+ multipliers, _("invalid IO block size"), 0);
+ break;
+
+ case VERBOSE_OPTION:
+ verbose = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
+ }
+
+ if (k_units != 0 && filter_command)
+ {
+ error (0, 0, _("--filter does not process a chunk extracted to stdout"));
+ usage (EXIT_FAILURE);
}
/* Handle default case. */
@@ -515,10 +1458,15 @@ main (int argc, char **argv)
if (n_units == 0)
{
- error (0, 0, _("invalid number of lines: 0"));
+ error (0, 0, "%s: %s", _("invalid number of lines"), quote ("0"));
usage (EXIT_FAILURE);
}
+ if (eolchar < 0)
+ eolchar = '\n';
+
+ set_suffix_length (n_units, split_type);
+
/* Get out the filename arguments. */
if (optind < argc)
@@ -533,26 +1481,87 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
+ /* Check that the suffix length is large enough for the numerical
+ suffix start value. */
+ if (numeric_suffix_start && strlen (numeric_suffix_start) > suffix_length)
+ {
+ error (0, 0, _("numerical suffix start value is too large "
+ "for the suffix length"));
+ usage (EXIT_FAILURE);
+ }
+
/* Open the input file. */
if (! STREQ (infile, "-")
&& fd_reopen (STDIN_FILENO, infile, O_RDONLY, 0) < 0)
error (EXIT_FAILURE, errno, _("cannot open %s for reading"),
- quote (infile));
+ quoteaf (infile));
- /* Binary I/O is safer when bytecounts are used. */
+ /* Binary I/O is safer when byte counts are used. */
if (O_BINARY && ! isatty (STDIN_FILENO))
- freopen (NULL, "rb", stdin);
-
- /* No output file is open now. */
- output_desc = -1;
+ xfreopen (NULL, "rb", stdin);
/* Get the optimal block size of input device and make a buffer. */
- if (fstat (STDIN_FILENO, &stat_buf) != 0)
- error (EXIT_FAILURE, errno, "%s", infile);
- in_blk_size = ST_BLKSIZE (stat_buf);
+ if (fstat (STDIN_FILENO, &in_stat_buf) != 0)
+ error (EXIT_FAILURE, errno, "%s", quotef (infile));
+
+ bool specified_buf_size = !! in_blk_size;
+ if (! specified_buf_size)
+ in_blk_size = io_blksize (in_stat_buf);
+
+ void *b = xmalloc (in_blk_size + 1 + page_size - 1);
+ char *buf = ptr_align (b, page_size);
+ size_t initial_read = SIZE_MAX;
- buf = ptr_align (xmalloc (in_blk_size + 1 + page_size - 1), page_size);
+ if (split_type == type_chunk_bytes || split_type == type_chunk_lines)
+ {
+ off_t input_offset = lseek (STDIN_FILENO, 0, SEEK_CUR);
+ if (0 <= input_offset)
+ {
+ if (usable_st_size (&in_stat_buf) && ! specified_buf_size)
+ {
+ assert (ST_BLKSIZE (in_stat_buf) <= in_blk_size);
+ file_size = input_file_size (STDIN_FILENO, in_stat_buf.st_size,
+ buf, in_blk_size);
+ if (file_size < in_blk_size)
+ initial_read = file_size;
+ }
+ else
+ {
+ file_size = lseek (STDIN_FILENO, 0, SEEK_END);
+ input_offset = (file_size < 0
+ ? file_size
+ : lseek (STDIN_FILENO, input_offset, SEEK_SET));
+ file_size -= input_offset;
+ }
+ }
+ if (input_offset < 0)
+ error (EXIT_FAILURE, 0, _("%s: cannot determine file size"),
+ quotef (infile));
+ /* Overflow, and sanity checking. */
+ if (OFF_T_MAX < n_units)
+ {
+ char buffer[INT_BUFSIZE_BOUND (uintmax_t)];
+ error (EXIT_FAILURE, EOVERFLOW, "%s: %s",
+ _("invalid number of chunks"),
+ quote (umaxtostr (n_units, buffer)));
+ }
+ /* increase file_size to n_units here, so that we still process
+ any input data, and create empty files for the rest. */
+ file_size = MAX (file_size, n_units);
+ }
+
+ /* When filtering, closure of one pipe must not terminate the process,
+ as there may still be other streams expecting input from us. */
+ if (filter_command)
+ {
+ struct sigaction act;
+ sigemptyset (&newblocked);
+ sigaction (SIGPIPE, NULL, &act);
+ if (act.sa_handler != SIG_IGN)
+ sigaddset (&newblocked, SIGPIPE);
+ sigprocmask (SIG_BLOCK, &newblocked, &oldblocked);
+ }
switch (split_type)
{
@@ -562,21 +1571,42 @@ main (int argc, char **argv)
break;
case type_bytes:
- bytes_split (n_units, buf, in_blk_size);
+ bytes_split (n_units, buf, in_blk_size, SIZE_MAX, 0);
break;
case type_byteslines:
- line_bytes_split (n_units);
+ line_bytes_split (n_units, buf, in_blk_size);
+ break;
+
+ case type_chunk_bytes:
+ if (k_units == 0)
+ bytes_split (file_size / n_units, buf, in_blk_size, initial_read,
+ n_units);
+ else
+ bytes_chunk_extract (k_units, n_units, buf, in_blk_size, initial_read,
+ file_size);
+ break;
+
+ case type_chunk_lines:
+ lines_chunk_split (k_units, n_units, buf, in_blk_size, initial_read,
+ file_size);
+ break;
+
+ case type_rr:
+ /* Note, this is like 'sed -n ${k}~${n}p' when k > 0,
+ but the functionality is provided for symmetry. */
+ lines_rr (k_units, n_units, buf, in_blk_size);
break;
default:
abort ();
}
+ IF_LINT (free (b));
+
if (close (STDIN_FILENO) != 0)
- error (EXIT_FAILURE, errno, "%s", infile);
- if (output_desc >= 0 && close (output_desc) < 0)
- error (EXIT_FAILURE, errno, "%s", outfile);
+ error (EXIT_FAILURE, errno, "%s", quotef (infile));
+ closeout (NULL, output_desc, filter_pid, outfile);
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/stat.c b/src/stat.c
index ca84236..e11e431 100644
--- a/src/stat.c
+++ b/src/stat.c
@@ -1,10 +1,10 @@
/* stat.c -- display file or file system status
- Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation.
+ Copyright (C) 2001-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
Written by Michael Meskes. */
@@ -21,9 +20,9 @@
/* Keep this conditional in sync with the similar conditional in
../m4/stat-prog.m4. */
-#if (STAT_STATVFS \
+#if ((STAT_STATVFS || STAT_STATVFS64) \
&& (HAVE_STRUCT_STATVFS_F_BASETYPE || HAVE_STRUCT_STATVFS_F_FSTYPENAME \
- || (! HAVE_STRUCT_STATFS_F_FSTYPENAME && HAVE_STRUCT_STATVFS_F_TYPE)))
+ || (! HAVE_STRUCT_STATFS_F_FSTYPENAME && HAVE_STRUCT_STATVFS_F_TYPE)))
# define USE_STATVFS 1
#else
# define USE_STATVFS 0
@@ -31,6 +30,7 @@
#include <stddef.h>
#include <stdio.h>
+#include <stdalign.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
@@ -45,7 +45,7 @@
/* NetBSD 1.5.2 needs these, for the declaration of struct statfs. */
# include <sys/param.h>
# include <sys/mount.h>
-# if HAVE_NETINET_IN_H && HAVE_NFS_NFS_CLNT_H && HAVE_NFS_VFS_H
+# if HAVE_NFS_NFS_CLNT_H && HAVE_NFS_VFS_H
/* Ultrix 4.4 needs these for the declaration of struct statfs. */
# include <netinet/in.h>
# include <nfs/nfs_clnt.h>
@@ -54,31 +54,37 @@
#elif HAVE_OS_H /* BeOS */
# include <fs_info.h>
#endif
+#include <selinux/selinux.h>
#include "system.h"
+#include "areadlink.h"
#include "error.h"
-#include "filemode.h"
#include "file-type.h"
+#include "filemode.h"
#include "fs.h"
#include "getopt.h"
-#include "inttostr.h"
+#include "mountlist.h"
#include "quote.h"
-#include "quotearg.h"
+#include "stat-size.h"
#include "stat-time.h"
#include "strftime.h"
-#include "xreadlink.h"
-
-#define alignof(type) offsetof (struct { char c; type x; }, x)
+#include "find-mount-point.h"
+#include "xvasprintf.h"
#if USE_STATVFS
-# define STRUCT_STATVFS struct statvfs
# define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATVFS_F_FSID_IS_INTEGER
# define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATVFS_F_TYPE
# if HAVE_STRUCT_STATVFS_F_NAMEMAX
# define SB_F_NAMEMAX(S) ((S)->f_namemax)
# endif
-# define STATFS statvfs
+# if ! STAT_STATVFS && STAT_STATVFS64
+# define STRUCT_STATVFS struct statvfs64
+# define STATFS statvfs64
+# else
+# define STRUCT_STATVFS struct statvfs
+# define STATFS statvfs
+# endif
# define STATFS_FRSIZE(S) ((S)->f_frsize)
#else
# define HAVE_STRUCT_STATXFS_F_TYPE HAVE_STRUCT_STATFS_F_TYPE
@@ -90,18 +96,18 @@
/* BeOS has a statvfs function, but it does not return sensible values
for f_files, f_ffree and f_favail, and lacks f_type, f_basetype and
f_fstypename. Use 'struct fs_info' instead. */
-static int
+static int ATTRIBUTE_WARN_UNUSED_RESULT
statfs (char const *filename, struct fs_info *buf)
{
dev_t device = dev_for_path (filename);
if (device < 0)
{
errno = (device == B_ENTRY_NOT_FOUND ? ENOENT
- : device == B_BAD_VALUE ? EINVAL
- : device == B_NAME_TOO_LONG ? ENAMETOOLONG
- : device == B_NO_MEMORY ? ENOMEM
- : device == B_FILE_ERROR ? EIO
- : 0);
+ : device == B_BAD_VALUE ? EINVAL
+ : device == B_NAME_TOO_LONG ? ENAMETOOLONG
+ : device == B_NO_MEMORY ? ENOMEM
+ : device == B_FILE_ERROR ? EIO
+ : 0);
return -1;
}
/* If successful, buf->dev will be == device. */
@@ -120,7 +126,11 @@ statfs (char const *filename, struct fs_info *buf)
# else
# define STRUCT_STATVFS struct statfs
# define STRUCT_STATXFS_F_FSID_IS_INTEGER STRUCT_STATFS_F_FSID_IS_INTEGER
-# define STATFS_FRSIZE(S) 0
+# if HAVE_STRUCT_STATFS_F_FRSIZE
+# define STATFS_FRSIZE(S) ((S)->f_frsize)
+# else
+# define STATFS_FRSIZE(S) 0
+# endif
# endif
#endif
@@ -142,25 +152,40 @@ statfs (char const *filename, struct fs_info *buf)
# endif
#endif
+#if HAVE_GETATTRAT
+# include <attr.h>
+# include <sys/nvpair.h>
+#endif
+
/* FIXME: these are used by printf.c, too */
#define isodigit(c) ('0' <= (c) && (c) <= '7')
#define octtobin(c) ((c) - '0')
#define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
- (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
+ (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
+
+static char const digits[] = "0123456789";
+
+/* Flags that are portable for use in printf, for at least one
+ conversion specifier; make_format removes unportable flags as
+ needed for particular specifiers. The glibc 2.2 extension "I" is
+ listed here; it is removed by make_format because it has undefined
+ behavior elsewhere and because it is incompatible with
+ out_epoch_sec. */
+static char const printf_flags[] = "'-+ #0I";
#define PROGRAM_NAME "stat"
-#define AUTHORS "Michael Meskes"
+#define AUTHORS proper_name ("Michael Meskes")
enum
{
PRINTF_OPTION = CHAR_MAX + 1
};
-static struct option const long_options[] = {
+static struct option const long_options[] =
+{
{"dereference", no_argument, NULL, 'L'},
{"file-system", no_argument, NULL, 'f'},
- {"filesystem", no_argument, NULL, 'f'}, /* obsolete and undocumented alias */
{"format", required_argument, NULL, 'c'},
{"printf", required_argument, NULL, PRINTF_OPTION},
{"terse", no_argument, NULL, 't'},
@@ -169,7 +194,8 @@ static struct option const long_options[] = {
{NULL, 0, NULL, 0}
};
-char *program_name;
+/* Whether to follow symbolic links; True for --dereference (-L). */
+static bool follow_links;
/* Whether to interpret backslash-escape sequences.
True for --printf=FMT, not for --format=FMT (-c). */
@@ -179,13 +205,17 @@ static bool interpret_backslash_escapes;
"" for --printf=FMT, "\n" for --format=FMT (-c). */
static char const *trailing_delim = "";
+/* The representation of the decimal point in the current locale. */
+static char const *decimal_point;
+static size_t decimal_point_len;
+
/* Return the type of the specified file system.
Some systems have statfvs.f_basetype[FSTYPSZ] (AIX, HP-UX, and Solaris).
Others have statvfs.f_fstypename[_VFS_NAMELEN] (NetBSD 3.0).
Others have statfs.f_fstypename[MFSNAMELEN] (NetBSD 1.5.2).
- Still others have neither and have to get by with f_type (Linux).
+ Still others have neither and have to get by with f_type (GNU/Linux).
But f_type may only exist in statfs (Cygwin). */
-static char const *
+static char const * ATTRIBUTE_WARN_UNUSED_RESULT
human_fstype (STRUCT_STATVFS const *statfsbuf)
{
#ifdef STATXFS_FILE_SYSTEM_TYPE_MEMBER_NAME
@@ -195,81 +225,258 @@ human_fstype (STRUCT_STATVFS const *statfsbuf)
{
# if defined __linux__
- /* IMPORTANT NOTE: Each of the following `case S_MAGIC_...:'
- statements must be followed by a hexadecimal constant in
- a comment. The S_MAGIC_... name and constant are automatically
- combined to produce the #define directives in fs.h. */
-
- case S_MAGIC_AFFS: /* 0xADFF */
+ /* Compare with what's in libc:
+ f=/a/libc/sysdeps/unix/sysv/linux/linux_fsinfo.h
+ sed -n '/ADFS_SUPER_MAGIC/,/SYSFS_MAGIC/p' $f \
+ | perl -n -e '/#define (.*?)_(?:SUPER_)MAGIC\s+0x(\S+)/' \
+ -e 'and print "case S_MAGIC_$1: /\* 0x" . uc($2) . " *\/\n"' \
+ | sort > sym_libc
+ perl -ne '/^\s+(case S_MAGIC_.*?): \/\* 0x(\S+) \*\//' \
+ -e 'and do { $v=uc$2; print "$1: /\* 0x$v *\/\n"}' stat.c \
+ | sort > sym_stat
+ diff -u sym_stat sym_libc
+ */
+
+ /* Also compare with the list in "man 2 statfs" using the
+ fs-magic-compare make target. */
+
+ /* IMPORTANT NOTE: Each of the following 'case S_MAGIC_...:'
+ statements must be followed by a hexadecimal constant in
+ a comment. The S_MAGIC_... name and constant are automatically
+ combined to produce the #define directives in fs.h. */
+
+ case S_MAGIC_ACFS: /* 0x61636673 remote */
+ return "acfs";
+ case S_MAGIC_ADFS: /* 0xADF5 local */
+ return "adfs";
+ case S_MAGIC_AFFS: /* 0xADFF local */
return "affs";
- case S_MAGIC_DEVPTS: /* 0x1CD1 */
+ case S_MAGIC_AFS: /* 0x5346414F remote */
+ return "afs";
+ case S_MAGIC_ANON_INODE_FS: /* 0x09041934 local */
+ return "anon-inode FS";
+ case S_MAGIC_AUFS: /* 0x61756673 remote */
+ /* FIXME: change syntax or add an optional attribute like "inotify:no".
+ The above is labeled as "remote" so that tail always uses polling,
+ but this isn't really a remote file system type. */
+ return "aufs";
+ case S_MAGIC_AUTOFS: /* 0x0187 local */
+ return "autofs";
+ case S_MAGIC_BEFS: /* 0x42465331 local */
+ return "befs";
+ case S_MAGIC_BDEVFS: /* 0x62646576 local */
+ return "bdevfs";
+ case S_MAGIC_BFS: /* 0x1BADFACE local */
+ return "bfs";
+ case S_MAGIC_BPF_FS: /* 0xCAFE4A11 local */
+ return "bpf_fs";
+ case S_MAGIC_BINFMTFS: /* 0x42494E4D local */
+ return "binfmt_misc";
+ case S_MAGIC_BTRFS: /* 0x9123683E local */
+ return "btrfs";
+ case S_MAGIC_BTRFS_TEST: /* 0x73727279 local */
+ return "btrfs_test";
+ case S_MAGIC_CEPH: /* 0x00C36400 remote */
+ return "ceph";
+ case S_MAGIC_CGROUP: /* 0x0027E0EB local */
+ return "cgroupfs";
+ case S_MAGIC_CIFS: /* 0xFF534D42 remote */
+ return "cifs";
+ case S_MAGIC_CODA: /* 0x73757245 remote */
+ return "coda";
+ case S_MAGIC_COH: /* 0x012FF7B7 local */
+ return "coh";
+ case S_MAGIC_CONFIGFS: /* 0x62656570 local */
+ return "configfs";
+ case S_MAGIC_CRAMFS: /* 0x28CD3D45 local */
+ return "cramfs";
+ case S_MAGIC_CRAMFS_WEND: /* 0x453DCD28 local */
+ return "cramfs-wend";
+ case S_MAGIC_DEBUGFS: /* 0x64626720 local */
+ return "debugfs";
+ case S_MAGIC_DEVFS: /* 0x1373 local */
+ return "devfs";
+ case S_MAGIC_DEVPTS: /* 0x1CD1 local */
return "devpts";
- case S_MAGIC_EXT: /* 0x137D */
+ case S_MAGIC_ECRYPTFS: /* 0xF15F local */
+ return "ecryptfs";
+ case S_MAGIC_EFIVARFS: /* 0xDE5E81E4 local */
+ return "efivarfs";
+ case S_MAGIC_EFS: /* 0x00414A53 local */
+ return "efs";
+ case S_MAGIC_EXOFS: /* 0x5DF5 local */
+ return "exofs";
+ case S_MAGIC_EXT: /* 0x137D local */
return "ext";
- case S_MAGIC_EXT2_OLD: /* 0xEF51 */
- return "ext2";
- case S_MAGIC_EXT2: /* 0xEF53 */
+ case S_MAGIC_EXT2: /* 0xEF53 local */
return "ext2/ext3";
- case S_MAGIC_JFS: /* 0x3153464a */
- return "jfs";
- case S_MAGIC_XFS: /* 0x58465342 */
- return "xfs";
- case S_MAGIC_HPFS: /* 0xF995E849 */
+ case S_MAGIC_EXT2_OLD: /* 0xEF51 local */
+ return "ext2";
+ case S_MAGIC_F2FS: /* 0xF2F52010 local */
+ return "f2fs";
+ case S_MAGIC_FAT: /* 0x4006 local */
+ return "fat";
+ case S_MAGIC_FHGFS: /* 0x19830326 remote */
+ return "fhgfs";
+ case S_MAGIC_FUSEBLK: /* 0x65735546 remote */
+ return "fuseblk";
+ case S_MAGIC_FUSECTL: /* 0x65735543 remote */
+ return "fusectl";
+ case S_MAGIC_FUTEXFS: /* 0x0BAD1DEA local */
+ return "futexfs";
+ case S_MAGIC_GFS: /* 0x01161970 remote */
+ return "gfs/gfs2";
+ case S_MAGIC_GPFS: /* 0x47504653 remote */
+ return "gpfs";
+ case S_MAGIC_HFS: /* 0x4244 local */
+ return "hfs";
+ case S_MAGIC_HFS_PLUS: /* 0x482B local */
+ return "hfs+";
+ case S_MAGIC_HFS_X: /* 0x4858 local */
+ return "hfsx";
+ case S_MAGIC_HOSTFS: /* 0x00C0FFEE local */
+ return "hostfs";
+ case S_MAGIC_HPFS: /* 0xF995E849 local */
return "hpfs";
- case S_MAGIC_ISOFS: /* 0x9660 */
+ case S_MAGIC_HUGETLBFS: /* 0x958458F6 local */
+ return "hugetlbfs";
+ case S_MAGIC_MTD_INODE_FS: /* 0x11307854 local */
+ return "inodefs";
+ case S_MAGIC_IBRIX: /* 0x013111A8 remote */
+ return "ibrix";
+ case S_MAGIC_INOTIFYFS: /* 0x2BAD1DEA local */
+ return "inotifyfs";
+ case S_MAGIC_ISOFS: /* 0x9660 local */
return "isofs";
- case S_MAGIC_ISOFS_WIN: /* 0x4000 */
+ case S_MAGIC_ISOFS_R_WIN: /* 0x4004 local */
return "isofs";
- case S_MAGIC_ISOFS_R_WIN: /* 0x4004 */
+ case S_MAGIC_ISOFS_WIN: /* 0x4000 local */
return "isofs";
- case S_MAGIC_MINIX: /* 0x137F */
+ case S_MAGIC_JFFS: /* 0x07C0 local */
+ return "jffs";
+ case S_MAGIC_JFFS2: /* 0x72B6 local */
+ return "jffs2";
+ case S_MAGIC_JFS: /* 0x3153464A local */
+ return "jfs";
+ case S_MAGIC_KAFS: /* 0x6B414653 remote */
+ return "k-afs";
+ case S_MAGIC_LOGFS: /* 0xC97E8168 local */
+ return "logfs";
+ case S_MAGIC_LUSTRE: /* 0x0BD00BD0 remote */
+ return "lustre";
+ case S_MAGIC_MINIX: /* 0x137F local */
return "minix";
- case S_MAGIC_MINIX_30: /* 0x138F */
+ case S_MAGIC_MINIX_30: /* 0x138F local */
return "minix (30 char.)";
- case S_MAGIC_MINIX_V2: /* 0x2468 */
+ case S_MAGIC_MINIX_V2: /* 0x2468 local */
return "minix v2";
- case S_MAGIC_MINIX_V2_30: /* 0x2478 */
+ case S_MAGIC_MINIX_V2_30: /* 0x2478 local */
return "minix v2 (30 char.)";
- case S_MAGIC_MSDOS: /* 0x4d44 */
+ case S_MAGIC_MINIX_V3: /* 0x4D5A local */
+ return "minix3";
+ case S_MAGIC_MQUEUE: /* 0x19800202 local */
+ return "mqueue";
+ case S_MAGIC_MSDOS: /* 0x4D44 local */
return "msdos";
- case S_MAGIC_FAT: /* 0x4006 */
- return "fat";
- case S_MAGIC_NCP: /* 0x564c */
+ case S_MAGIC_NCP: /* 0x564C remote */
return "novell";
- case S_MAGIC_NFS: /* 0x6969 */
+ case S_MAGIC_NFS: /* 0x6969 remote */
return "nfs";
- case S_MAGIC_PROC: /* 0x9fa0 */
- return "proc";
- case S_MAGIC_SMB: /* 0x517B */
- return "smb";
- case S_MAGIC_XENIX: /* 0x012FF7B4 */
- return "xenix";
- case S_MAGIC_SYSV4: /* 0x012FF7B5 */
- return "sysv4";
- case S_MAGIC_SYSV2: /* 0x012FF7B6 */
- return "sysv2";
- case S_MAGIC_COH: /* 0x012FF7B7 */
- return "coh";
- case S_MAGIC_UFS: /* 0x00011954 */
- return "ufs";
- case S_MAGIC_XIAFS: /* 0x012FD16D */
- return "xia";
- case S_MAGIC_NTFS: /* 0x5346544e */
+ case S_MAGIC_NFSD: /* 0x6E667364 remote */
+ return "nfsd";
+ case S_MAGIC_NILFS: /* 0x3434 local */
+ return "nilfs";
+ case S_MAGIC_NSFS: /* 0x6E736673 local */
+ return "nsfs";
+ case S_MAGIC_NTFS: /* 0x5346544E local */
return "ntfs";
- case S_MAGIC_TMPFS: /* 0x1021994 */
- return "tmpfs";
- case S_MAGIC_REISERFS: /* 0x52654973 */
+ case S_MAGIC_OPENPROM: /* 0x9FA1 local */
+ return "openprom";
+ case S_MAGIC_OCFS2: /* 0x7461636F remote */
+ return "ocfs2";
+ case S_MAGIC_OVERLAYFS: /* 0x794C7630 remote */
+ /* This may overlay remote file systems.
+ Also there have been issues reported with inotify and overlayfs,
+ so mark as "remote" so that polling is used. */
+ return "overlayfs";
+ case S_MAGIC_PANFS: /* 0xAAD7AAEA remote */
+ return "panfs";
+ case S_MAGIC_PIPEFS: /* 0x50495045 remote */
+ /* FIXME: change syntax or add an optional attribute like "inotify:no".
+ The above is labeled as "remote" so that tail always uses polling,
+ but this isn't really a remote file system type. */
+ return "pipefs";
+ case S_MAGIC_PROC: /* 0x9FA0 local */
+ return "proc";
+ case S_MAGIC_PSTOREFS: /* 0x6165676C local */
+ return "pstorefs";
+ case S_MAGIC_QNX4: /* 0x002F local */
+ return "qnx4";
+ case S_MAGIC_QNX6: /* 0x68191122 local */
+ return "qnx6";
+ case S_MAGIC_RAMFS: /* 0x858458F6 local */
+ return "ramfs";
+ case S_MAGIC_REISERFS: /* 0x52654973 local */
return "reiserfs";
- case S_MAGIC_CRAMFS: /* 0x28cd3d45 */
- return "cramfs";
- case S_MAGIC_ROMFS: /* 0x7275 */
+ case S_MAGIC_ROMFS: /* 0x7275 local */
return "romfs";
- case S_MAGIC_RAMFS: /* 0x858458f6 */
- return "ramfs";
- case S_MAGIC_SQUASHFS: /* 0x73717368 */
+ case S_MAGIC_RPC_PIPEFS: /* 0x67596969 local */
+ return "rpc_pipefs";
+ case S_MAGIC_SECURITYFS: /* 0x73636673 local */
+ return "securityfs";
+ case S_MAGIC_SELINUX: /* 0xF97CFF8C local */
+ return "selinux";
+ case S_MAGIC_SMACK: /* 0x43415D53 local */
+ return "smackfs";
+ case S_MAGIC_SMB: /* 0x517B remote */
+ return "smb";
+ case S_MAGIC_SNFS: /* 0xBEEFDEAD remote */
+ return "snfs";
+ case S_MAGIC_SOCKFS: /* 0x534F434B local */
+ return "sockfs";
+ case S_MAGIC_SQUASHFS: /* 0x73717368 local */
return "squashfs";
- case S_MAGIC_SYSFS: /* 0x62656572 */
+ case S_MAGIC_SYSFS: /* 0x62656572 local */
return "sysfs";
+ case S_MAGIC_SYSV2: /* 0x012FF7B6 local */
+ return "sysv2";
+ case S_MAGIC_SYSV4: /* 0x012FF7B5 local */
+ return "sysv4";
+ case S_MAGIC_TMPFS: /* 0x01021994 local */
+ return "tmpfs";
+ case S_MAGIC_TRACEFS: /* 0x74726163 local */
+ return "tracefs";
+ case S_MAGIC_UBIFS: /* 0x24051905 local */
+ return "ubifs";
+ case S_MAGIC_UDF: /* 0x15013346 local */
+ return "udf";
+ case S_MAGIC_UFS: /* 0x00011954 local */
+ return "ufs";
+ case S_MAGIC_UFS_BYTESWAPPED: /* 0x54190100 local */
+ return "ufs";
+ case S_MAGIC_USBDEVFS: /* 0x9FA2 local */
+ return "usbdevfs";
+ case S_MAGIC_V9FS: /* 0x01021997 local */
+ return "v9fs";
+ case S_MAGIC_VMHGFS: /* 0xBACBACBC remote */
+ return "vmhgfs";
+ case S_MAGIC_VXFS: /* 0xA501FCF5 remote */
+ /* Veritas File System can run in single instance or clustered mode,
+ so mark as remote to cater for the latter case. */
+ return "vxfs";
+ case S_MAGIC_VZFS: /* 0x565A4653 local */
+ return "vzfs";
+ case S_MAGIC_XENFS: /* 0xABBA1974 local */
+ return "xenfs";
+ case S_MAGIC_XENIX: /* 0x012FF7B4 local */
+ return "xenix";
+ case S_MAGIC_XFS: /* 0x58465342 local */
+ return "xfs";
+ case S_MAGIC_XIAFS: /* 0x012FD16D local */
+ return "xia";
+ case S_MAGIC_ZFS: /* 0x2FC12FC1 local */
+ return "zfs";
+
# elif __GNU__
case FSTYPE_UFS:
return "ufs";
@@ -328,17 +535,17 @@ human_fstype (STRUCT_STATVFS const *statfsbuf)
# endif
default:
{
- unsigned long int type = statfsbuf->f_type;
- static char buf[sizeof "UNKNOWN (0x%lx)" - 3
- + (sizeof type * CHAR_BIT + 3) / 4];
- sprintf (buf, "UNKNOWN (0x%lx)", type);
- return buf;
+ unsigned long int type = statfsbuf->f_type;
+ static char buf[sizeof "UNKNOWN (0x%lx)" - 3
+ + (sizeof type * CHAR_BIT + 3) / 4];
+ sprintf (buf, "UNKNOWN (0x%lx)", type);
+ return buf;
}
}
#endif
}
-static char *
+static char * ATTRIBUTE_WARN_UNUSED_RESULT
human_access (struct stat const *statbuf)
{
static char modebuf[12];
@@ -347,59 +554,219 @@ human_access (struct stat const *statbuf)
return modebuf;
}
-static char *
+static char * ATTRIBUTE_WARN_UNUSED_RESULT
human_time (struct timespec t)
{
static char str[MAX (INT_BUFSIZE_BOUND (intmax_t),
- (INT_STRLEN_BOUND (int) /* YYYY */
- + 1 /* because YYYY might equal INT_MAX + 1900 */
- + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
+ (INT_STRLEN_BOUND (int) /* YYYY */
+ + 1 /* because YYYY might equal INT_MAX + 1900 */
+ + sizeof "-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ"))];
+ static timezone_t tz;
+ if (!tz)
+ tz = tzalloc (getenv ("TZ"));
struct tm const *tm = localtime (&t.tv_sec);
if (tm == NULL)
- return (TYPE_SIGNED (time_t)
- ? imaxtostr (t.tv_sec, str)
- : umaxtostr (t.tv_sec, str));
- nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, 0, t.tv_nsec);
+ return timetostr (t.tv_sec, str);
+ nstrftime (str, sizeof str, "%Y-%m-%d %H:%M:%S.%N %z", tm, tz, t.tv_nsec);
return str;
}
+/* PFORMAT points to a '%' followed by a prefix of a format, all of
+ size PREFIX_LEN. The flags allowed for this format are
+ ALLOWED_FLAGS; remove other printf flags from the prefix, then
+ append SUFFIX. */
+static void
+make_format (char *pformat, size_t prefix_len, char const *allowed_flags,
+ char const *suffix)
+{
+ char *dst = pformat + 1;
+ char const *src;
+ char const *srclim = pformat + prefix_len;
+ for (src = dst; src < srclim && strchr (printf_flags, *src); src++)
+ if (strchr (allowed_flags, *src))
+ *dst++ = *src;
+ while (src < srclim)
+ *dst++ = *src++;
+ strcpy (dst, suffix);
+}
+
static void
out_string (char *pformat, size_t prefix_len, char const *arg)
{
- strcpy (pformat + prefix_len, "s");
+ make_format (pformat, prefix_len, "-", "s");
printf (pformat, arg);
}
-static void
+static int
out_int (char *pformat, size_t prefix_len, intmax_t arg)
{
- strcpy (pformat + prefix_len, PRIdMAX);
- printf (pformat, arg);
+ make_format (pformat, prefix_len, "'-+ 0", PRIdMAX);
+ return printf (pformat, arg);
}
-static void
+static int
out_uint (char *pformat, size_t prefix_len, uintmax_t arg)
{
- strcpy (pformat + prefix_len, PRIuMAX);
- printf (pformat, arg);
+ make_format (pformat, prefix_len, "'-0", PRIuMAX);
+ return printf (pformat, arg);
}
static void
out_uint_o (char *pformat, size_t prefix_len, uintmax_t arg)
{
- strcpy (pformat + prefix_len, PRIoMAX);
+ make_format (pformat, prefix_len, "-#0", PRIoMAX);
printf (pformat, arg);
}
static void
out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg)
{
- strcpy (pformat + prefix_len, PRIxMAX);
+ make_format (pformat, prefix_len, "-#0", PRIxMAX);
printf (pformat, arg);
}
+static int
+out_minus_zero (char *pformat, size_t prefix_len)
+{
+ make_format (pformat, prefix_len, "'-+ 0", ".0f");
+ return printf (pformat, -0.25);
+}
-/* print statfs info */
+/* Output the number of seconds since the Epoch, using a format that
+ acts like printf's %f format. */
static void
-print_statfs (char *pformat, size_t prefix_len, char m, char const *filename,
- void const *data)
+out_epoch_sec (char *pformat, size_t prefix_len,
+ struct stat const *statbuf _GL_UNUSED,
+ struct timespec arg)
+{
+ char *dot = memchr (pformat, '.', prefix_len);
+ size_t sec_prefix_len = prefix_len;
+ int width = 0;
+ int precision = 0;
+ bool frac_left_adjust = false;
+
+ if (dot)
+ {
+ sec_prefix_len = dot - pformat;
+ pformat[prefix_len] = '\0';
+
+ if (ISDIGIT (dot[1]))
+ {
+ long int lprec = strtol (dot + 1, NULL, 10);
+ precision = (lprec <= INT_MAX ? lprec : INT_MAX);
+ }
+ else
+ {
+ precision = 9;
+ }
+
+ if (precision && ISDIGIT (dot[-1]))
+ {
+ /* If a nontrivial width is given, subtract the width of the
+ decimal point and PRECISION digits that will be output
+ later. */
+ char *p = dot;
+ *dot = '\0';
+
+ do
+ --p;
+ while (ISDIGIT (p[-1]));
+
+ long int lwidth = strtol (p, NULL, 10);
+ width = (lwidth <= INT_MAX ? lwidth : INT_MAX);
+ if (1 < width)
+ {
+ p += (*p == '0');
+ sec_prefix_len = p - pformat;
+ int w_d = (decimal_point_len < width
+ ? width - decimal_point_len
+ : 0);
+ if (1 < w_d)
+ {
+ int w = w_d - precision;
+ if (1 < w)
+ {
+ char *dst = pformat;
+ for (char const *src = dst; src < p; src++)
+ {
+ if (*src == '-')
+ frac_left_adjust = true;
+ else
+ *dst++ = *src;
+ }
+ sec_prefix_len =
+ (dst - pformat
+ + (frac_left_adjust ? 0 : sprintf (dst, "%d", w)));
+ }
+ }
+ }
+ }
+ }
+
+ int divisor = 1;
+ for (int i = precision; i < 9; i++)
+ divisor *= 10;
+ int frac_sec = arg.tv_nsec / divisor;
+ int int_len;
+
+ if (TYPE_SIGNED (time_t))
+ {
+ bool minus_zero = false;
+ if (arg.tv_sec < 0 && arg.tv_nsec != 0)
+ {
+ int frac_sec_modulus = 1000000000 / divisor;
+ frac_sec = (frac_sec_modulus - frac_sec
+ - (arg.tv_nsec % divisor != 0));
+ arg.tv_sec += (frac_sec != 0);
+ minus_zero = (arg.tv_sec == 0);
+ }
+ int_len = (minus_zero
+ ? out_minus_zero (pformat, sec_prefix_len)
+ : out_int (pformat, sec_prefix_len, arg.tv_sec));
+ }
+ else
+ int_len = out_uint (pformat, sec_prefix_len, arg.tv_sec);
+
+ if (precision)
+ {
+ int prec = (precision < 9 ? precision : 9);
+ int trailing_prec = precision - prec;
+ int ilen = (int_len < 0 ? 0 : int_len);
+ int trailing_width = (ilen < width && decimal_point_len < width - ilen
+ ? width - ilen - decimal_point_len - prec
+ : 0);
+ printf ("%s%.*d%-*.*d", decimal_point, prec, frac_sec,
+ trailing_width, trailing_prec, 0);
+ }
+}
+
+/* Print the context information of FILENAME, and return true iff the
+ context could not be obtained. */
+static bool ATTRIBUTE_WARN_UNUSED_RESULT
+out_file_context (char *pformat, size_t prefix_len, char const *filename)
+{
+ char *scontext;
+ bool fail = false;
+
+ if ((follow_links
+ ? getfilecon (filename, &scontext)
+ : lgetfilecon (filename, &scontext)) < 0)
+ {
+ error (0, errno, _("failed to get security context of %s"),
+ quoteaf (filename));
+ scontext = NULL;
+ fail = true;
+ }
+ strcpy (pformat + prefix_len, "s");
+ printf (pformat, (scontext ? scontext : "?"));
+ if (scontext)
+ freecon (scontext);
+ return fail;
+}
+
+/* Print statfs info. Return zero upon success, nonzero upon failure. */
+static bool ATTRIBUTE_WARN_UNUSED_RESULT
+print_statfs (char *pformat, size_t prefix_len, unsigned int m,
+ int fd, char const *filename,
+ void const *data)
{
STRUCT_STATVFS const *statfsbuf = data;
+ bool fail = false;
switch (m)
{
@@ -410,26 +777,26 @@ print_statfs (char *pformat, size_t prefix_len, char m, char const *filename,
case 'i':
{
#if STRUCT_STATXFS_F_FSID_IS_INTEGER
- uintmax_t fsid = statfsbuf->f_fsid;
+ uintmax_t fsid = statfsbuf->f_fsid;
#else
- typedef unsigned int fsid_word;
- verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
- verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
- verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
- fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
-
- /* Assume a little-endian word order, as that is compatible
- with glibc's statvfs implementation. */
- uintmax_t fsid = 0;
- int words = sizeof statfsbuf->f_fsid / sizeof *p;
- int i;
- for (i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
- {
- uintmax_t u = p[words - 1 - i];
- fsid |= u << (i * CHAR_BIT * sizeof *p);
- }
+ typedef unsigned int fsid_word;
+ verify (alignof (STRUCT_STATVFS) % alignof (fsid_word) == 0);
+ verify (offsetof (STRUCT_STATVFS, f_fsid) % alignof (fsid_word) == 0);
+ verify (sizeof statfsbuf->f_fsid % alignof (fsid_word) == 0);
+ fsid_word const *p = (fsid_word *) &statfsbuf->f_fsid;
+
+ /* Assume a little-endian word order, as that is compatible
+ with glibc's statvfs implementation. */
+ uintmax_t fsid = 0;
+ int words = sizeof statfsbuf->f_fsid / sizeof *p;
+ int i;
+ for (i = 0; i < words && i * sizeof *p < sizeof fsid; i++)
+ {
+ uintmax_t u = p[words - 1 - i];
+ fsid |= u << (i * CHAR_BIT * sizeof *p);
+ }
#endif
- out_uint_x (pformat, prefix_len, fsid);
+ out_uint_x (pformat, prefix_len, fsid);
}
break;
@@ -460,33 +827,166 @@ print_statfs (char *pformat, size_t prefix_len, char m, char const *filename,
break;
case 'S':
{
- uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
- if (! frsize)
- frsize = statfsbuf->f_bsize;
- out_uint (pformat, prefix_len, frsize);
+ uintmax_t frsize = STATFS_FRSIZE (statfsbuf);
+ if (! frsize)
+ frsize = statfsbuf->f_bsize;
+ out_uint (pformat, prefix_len, frsize);
}
break;
case 'c':
- out_int (pformat, prefix_len, statfsbuf->f_files);
+ out_uint (pformat, prefix_len, statfsbuf->f_files);
break;
case 'd':
out_int (pformat, prefix_len, statfsbuf->f_ffree);
break;
-
default:
fputc ('?', stdout);
break;
}
+ return fail;
}
-/* print stat info */
-static void
-print_stat (char *pformat, size_t prefix_len, char m,
- char const *filename, void const *data)
+/* Return any bind mounted source for a path.
+ The caller should not free the returned buffer.
+ Return NULL if no bind mount found. */
+static char const * ATTRIBUTE_WARN_UNUSED_RESULT
+find_bind_mount (char const * name)
+{
+ char const * bind_mount = NULL;
+
+ static struct mount_entry *mount_list;
+ static bool tried_mount_list = false;
+ if (!tried_mount_list) /* attempt/warn once per process. */
+ {
+ if (!(mount_list = read_file_system_list (false)))
+ error (0, errno, "%s", _("cannot read table of mounted file systems"));
+ tried_mount_list = true;
+ }
+
+ struct stat name_stats;
+ if (stat (name, &name_stats) != 0)
+ return NULL;
+
+ struct mount_entry *me;
+ for (me = mount_list; me; me = me->me_next)
+ {
+ if (me->me_dummy && me->me_devname[0] == '/'
+ && STREQ (me->me_mountdir, name))
+ {
+ struct stat dev_stats;
+
+ if (stat (me->me_devname, &dev_stats) == 0
+ && SAME_INODE (name_stats, dev_stats))
+ {
+ bind_mount = me->me_devname;
+ break;
+ }
+ }
+ }
+
+ return bind_mount;
+}
+
+/* Print mount point. Return zero upon success, nonzero upon failure. */
+static bool ATTRIBUTE_WARN_UNUSED_RESULT
+out_mount_point (char const *filename, char *pformat, size_t prefix_len,
+ const struct stat *statp)
+{
+
+ char const *np = "?", *bp = NULL;
+ char *mp = NULL;
+ bool fail = true;
+
+ /* Look for bind mounts first. Note we output the immediate alias,
+ rather than further resolving to a base device mount point. */
+ if (follow_links || !S_ISLNK (statp->st_mode))
+ {
+ char *resolved = canonicalize_file_name (filename);
+ if (!resolved)
+ {
+ error (0, errno, _("failed to canonicalize %s"), quoteaf (filename));
+ goto print_mount_point;
+ }
+ bp = find_bind_mount (resolved);
+ free (resolved);
+ if (bp)
+ {
+ fail = false;
+ goto print_mount_point;
+ }
+ }
+
+ /* If there is no direct bind mount, then navigate
+ back up the tree looking for a device change.
+ Note we don't detect if any of the directory components
+ are bind mounted to the same device, but that's OK
+ since we've not directly queried them. */
+ if ((mp = find_mount_point (filename, statp)))
+ {
+ /* This dir might be bind mounted to another device,
+ so we resolve the bound source in that case also. */
+ bp = find_bind_mount (mp);
+ fail = false;
+ }
+
+print_mount_point:
+
+ out_string (pformat, prefix_len, bp ? bp : mp ? mp : np);
+ free (mp);
+ return fail;
+}
+
+static struct timespec
+get_birthtime (int fd, char const *filename, struct stat const *st)
+{
+ struct timespec ts = get_stat_birthtime (st);
+
+#if HAVE_GETATTRAT
+ if (ts.tv_nsec < 0)
+ {
+ nvlist_t *response;
+ if ((fd < 0
+ ? getattrat (AT_FDCWD, XATTR_VIEW_READWRITE, filename, &response)
+ : fgetattr (fd, XATTR_VIEW_READWRITE, &response))
+ == 0)
+ {
+ uint64_t *val;
+ uint_t n;
+ if (nvlist_lookup_uint64_array (response, A_CRTIME, &val, &n) == 0
+ && 2 <= n
+ && val[0] <= TYPE_MAXIMUM (time_t)
+ && val[1] < 1000000000 * 2 /* for leap seconds */)
+ {
+ ts.tv_sec = val[0];
+ ts.tv_nsec = val[1];
+ }
+ nvlist_free (response);
+ }
+ }
+#endif
+
+ return ts;
+}
+
+/* Map a TS with negative TS.tv_nsec to {0,0}. */
+static inline struct timespec
+neg_to_zero (struct timespec ts)
+{
+ if (0 <= ts.tv_nsec)
+ return ts;
+ struct timespec z = {0, 0};
+ return z;
+}
+
+/* Print stat info. Return zero upon success, nonzero upon failure. */
+static bool
+print_stat (char *pformat, size_t prefix_len, unsigned int m,
+ int fd, char const *filename, void const *data)
{
struct stat *statbuf = (struct stat *) data;
struct passwd *pw_ent;
struct group *gw_ent;
+ bool fail = false;
switch (m)
{
@@ -494,19 +994,20 @@ print_stat (char *pformat, size_t prefix_len, char m,
out_string (pformat, prefix_len, filename);
break;
case 'N':
- out_string (pformat, prefix_len, quote (filename));
+ out_string (pformat, prefix_len, quoteaf (filename));
if (S_ISLNK (statbuf->st_mode))
- {
- char *linkname = xreadlink_with_size (filename, statbuf->st_size);
- if (linkname == NULL)
- {
- error (0, errno, _("cannot read symbolic link %s"),
- quote (filename));
- return;
- }
- printf (" -> ");
- out_string (pformat, prefix_len, quote (linkname));
- }
+ {
+ char *linkname = areadlink_with_size (filename, statbuf->st_size);
+ if (linkname == NULL)
+ {
+ error (0, errno, _("cannot read symbolic link %s"),
+ quoteaf (filename));
+ return true;
+ }
+ printf (" -> ");
+ out_string (pformat, prefix_len, quoteaf (linkname));
+ free (linkname);
+ }
break;
case 'd':
out_uint (pformat, prefix_len, statbuf->st_dev);
@@ -536,28 +1037,29 @@ print_stat (char *pformat, size_t prefix_len, char m,
out_uint (pformat, prefix_len, statbuf->st_uid);
break;
case 'U':
- setpwent ();
pw_ent = getpwuid (statbuf->st_uid);
out_string (pformat, prefix_len,
- pw_ent ? pw_ent->pw_name : "UNKNOWN");
+ pw_ent ? pw_ent->pw_name : "UNKNOWN");
break;
case 'g':
out_uint (pformat, prefix_len, statbuf->st_gid);
break;
case 'G':
- setgrent ();
gw_ent = getgrgid (statbuf->st_gid);
out_string (pformat, prefix_len,
- gw_ent ? gw_ent->gr_name : "UNKNOWN");
+ gw_ent ? gw_ent->gr_name : "UNKNOWN");
break;
case 't':
out_uint_x (pformat, prefix_len, major (statbuf->st_rdev));
break;
+ case 'm':
+ fail |= out_mount_point (filename, pformat, prefix_len, statbuf);
+ break;
case 'T':
out_uint_x (pformat, prefix_len, minor (statbuf->st_rdev));
break;
case 's':
- out_uint (pformat, prefix_len, statbuf->st_size);
+ out_int (pformat, prefix_len, statbuf->st_size);
break;
case 'B':
out_uint (pformat, prefix_len, ST_NBLOCKSIZE);
@@ -566,39 +1068,47 @@ print_stat (char *pformat, size_t prefix_len, char m,
out_uint (pformat, prefix_len, ST_NBLOCKS (*statbuf));
break;
case 'o':
- out_uint (pformat, prefix_len, statbuf->st_blksize);
+ out_uint (pformat, prefix_len, ST_BLKSIZE (*statbuf));
+ break;
+ case 'w':
+ {
+ struct timespec t = get_birthtime (fd, filename, statbuf);
+ if (t.tv_nsec < 0)
+ out_string (pformat, prefix_len, "-");
+ else
+ out_string (pformat, prefix_len, human_time (t));
+ }
+ break;
+ case 'W':
+ out_epoch_sec (pformat, prefix_len, statbuf,
+ neg_to_zero (get_birthtime (fd, filename, statbuf)));
break;
case 'x':
out_string (pformat, prefix_len, human_time (get_stat_atime (statbuf)));
break;
case 'X':
- if (TYPE_SIGNED (time_t))
- out_int (pformat, prefix_len, statbuf->st_atime);
- else
- out_uint (pformat, prefix_len, statbuf->st_atime);
+ out_epoch_sec (pformat, prefix_len, statbuf, get_stat_atime (statbuf));
break;
case 'y':
out_string (pformat, prefix_len, human_time (get_stat_mtime (statbuf)));
break;
case 'Y':
- if (TYPE_SIGNED (time_t))
- out_int (pformat, prefix_len, statbuf->st_mtime);
- else
- out_uint (pformat, prefix_len, statbuf->st_mtime);
+ out_epoch_sec (pformat, prefix_len, statbuf, get_stat_mtime (statbuf));
break;
case 'z':
out_string (pformat, prefix_len, human_time (get_stat_ctime (statbuf)));
break;
case 'Z':
- if (TYPE_SIGNED (time_t))
- out_int (pformat, prefix_len, statbuf->st_ctime);
- else
- out_uint (pformat, prefix_len, statbuf->st_ctime);
+ out_epoch_sec (pformat, prefix_len, statbuf, get_stat_ctime (statbuf));
+ break;
+ case 'C':
+ fail |= out_file_context (pformat, prefix_len, filename);
break;
default:
fputc ('?', stdout);
break;
}
+ return fail;
}
/* Output a single-character \ escape. */
@@ -614,6 +1124,9 @@ print_esc_char (char c)
case 'b': /* Backspace. */
c ='\b';
break;
+ case 'e': /* Escape. */
+ c ='\x1B';
+ break;
case 'f': /* Form feed. */
c ='\f';
break;
@@ -633,25 +1146,31 @@ print_esc_char (char c)
case '\\':
break;
default:
- error (0, 0, _("warning: unrecognized escape `\\%c'"), c);
+ error (0, 0, _("warning: unrecognized escape '\\%c'"), c);
break;
}
putchar (c);
}
-static void
-print_it (char const *format, char const *filename,
- void (*print_func) (char *, size_t, char, char const *, void const *),
- void const *data)
+/* Print the information specified by the format string, FORMAT,
+ calling PRINT_FUNC for each %-directive encountered.
+ Return zero upon success, nonzero upon failure. */
+static bool ATTRIBUTE_WARN_UNUSED_RESULT
+print_it (char const *format, int fd, char const *filename,
+ bool (*print_func) (char *, size_t, unsigned int,
+ int, char const *, void const *),
+ void const *data)
{
- /* Add 2 to accommodate our conversion of the stat `%s' format string
- to the longer printf `%llu' one. */
+ bool fail = false;
+
+ /* Add 2 to accommodate our conversion of the stat '%s' format string
+ to the longer printf '%llu' one. */
enum
{
MAX_ADDITIONAL_BYTES =
- (MAX (sizeof PRIdMAX,
- MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
- - 1)
+ (MAX (sizeof PRIdMAX,
+ MAX (sizeof PRIoMAX, MAX (sizeof PRIuMAX, sizeof PRIxMAX)))
+ - 1)
};
size_t n_alloc = strlen (format) + MAX_ADDITIONAL_BYTES + 1;
char *dest = xmalloc (n_alloc);
@@ -659,179 +1178,264 @@ print_it (char const *format, char const *filename,
for (b = format; *b; b++)
{
switch (*b)
- {
- case '%':
- {
- size_t len = strspn (b + 1, "#-+.I 0123456789");
- char const *fmt_char = b + len + 1;
- memcpy (dest, b, len + 1);
-
- b = fmt_char;
- switch (*fmt_char)
- {
- case '\0':
- --b;
- /* fall through */
- case '%':
- if (0 < len)
- {
- dest[len + 1] = *fmt_char;
- dest[len + 2] = '\0';
- error (EXIT_FAILURE, 0, _("%s: invalid directive"),
- quotearg_colon (dest));
- }
- putchar ('%');
- break;
- default:
- print_func (dest, len + 1, *fmt_char, filename, data);
- break;
- }
- break;
- }
-
- case '\\':
- if ( ! interpret_backslash_escapes)
- {
- putchar ('\\');
- break;
- }
- ++b;
- if (isodigit (*b))
- {
- int esc_value = octtobin (*b);
- int esc_length = 1; /* number of octal digits */
- for (++b; esc_length < 3 && isodigit (*b);
- ++esc_length, ++b)
- {
- esc_value = esc_value * 8 + octtobin (*b);
- }
- putchar (esc_value);
- --b;
- }
- else if (*b == 'x' && isxdigit (to_uchar (b[1])))
- {
- int esc_value = hextobin (b[1]); /* Value of \xhh escape. */
- /* A hexadecimal \xhh escape sequence must have
- 1 or 2 hex. digits. */
- ++b;
- if (isxdigit (to_uchar (b[1])))
- {
- ++b;
- esc_value = esc_value * 16 + hextobin (*b);
- }
- putchar (esc_value);
- }
- else if (*b == '\0')
- {
- error (0, 0, _("warning: backslash at end of format"));
- putchar ('\\');
- /* Arrange to exit the loop. */
- --b;
- }
- else
- {
- print_esc_char (*b);
- }
- break;
-
- default:
- putchar (*b);
- break;
- }
+ {
+ case '%':
+ {
+ size_t len = strspn (b + 1, printf_flags);
+ char const *fmt_char = b + len + 1;
+ fmt_char += strspn (fmt_char, digits);
+ if (*fmt_char == '.')
+ fmt_char += 1 + strspn (fmt_char + 1, digits);
+ len = fmt_char - (b + 1);
+ unsigned int fmt_code = *fmt_char;
+ memcpy (dest, b, len + 1);
+
+ b = fmt_char;
+ switch (fmt_code)
+ {
+ case '\0':
+ --b;
+ /* fall through */
+ case '%':
+ if (0 < len)
+ {
+ dest[len + 1] = *fmt_char;
+ dest[len + 2] = '\0';
+ error (EXIT_FAILURE, 0, _("%s: invalid directive"),
+ quote (dest));
+ }
+ putchar ('%');
+ break;
+ default:
+ fail |= print_func (dest, len + 1, fmt_code,
+ fd, filename, data);
+ break;
+ }
+ break;
+ }
+
+ case '\\':
+ if ( ! interpret_backslash_escapes)
+ {
+ putchar ('\\');
+ break;
+ }
+ ++b;
+ if (isodigit (*b))
+ {
+ int esc_value = octtobin (*b);
+ int esc_length = 1; /* number of octal digits */
+ for (++b; esc_length < 3 && isodigit (*b);
+ ++esc_length, ++b)
+ {
+ esc_value = esc_value * 8 + octtobin (*b);
+ }
+ putchar (esc_value);
+ --b;
+ }
+ else if (*b == 'x' && isxdigit (to_uchar (b[1])))
+ {
+ int esc_value = hextobin (b[1]); /* Value of \xhh escape. */
+ /* A hexadecimal \xhh escape sequence must have
+ 1 or 2 hex. digits. */
+ ++b;
+ if (isxdigit (to_uchar (b[1])))
+ {
+ ++b;
+ esc_value = esc_value * 16 + hextobin (*b);
+ }
+ putchar (esc_value);
+ }
+ else if (*b == '\0')
+ {
+ error (0, 0, _("warning: backslash at end of format"));
+ putchar ('\\');
+ /* Arrange to exit the loop. */
+ --b;
+ }
+ else
+ {
+ print_esc_char (*b);
+ }
+ break;
+
+ default:
+ putchar (*b);
+ break;
+ }
}
free (dest);
fputs (trailing_delim, stdout);
+
+ return fail;
}
/* Stat the file system and print what we find. */
-static bool
-do_statfs (char const *filename, bool terse, char const *format)
+static bool ATTRIBUTE_WARN_UNUSED_RESULT
+do_statfs (char const *filename, char const *format)
{
STRUCT_STATVFS statfsbuf;
- if (STATFS (filename, &statfsbuf) != 0)
+ if (STREQ (filename, "-"))
{
- error (0, errno, _("cannot read file system information for %s"),
- quote (filename));
+ error (0, 0, _("using %s to denote standard input does not work"
+ " in file system mode"), quoteaf (filename));
return false;
}
- if (format == NULL)
+ if (STATFS (filename, &statfsbuf) != 0)
{
- format = (terse
- ? "%n %i %l %t %s %S %b %f %a %c %d\n"
- : " File: \"%n\"\n"
- " ID: %-8i Namelen: %-7l Type: %T\n"
- "Block size: %-10s Fundamental block size: %S\n"
- "Blocks: Total: %-10b Free: %-10f Available: %a\n"
- "Inodes: Total: %-10c Free: %d\n");
+ error (0, errno, _("cannot read file system information for %s"),
+ quoteaf (filename));
+ return false;
}
- print_it (format, filename, print_statfs, &statfsbuf);
- return true;
+ bool fail = print_it (format, -1, filename, print_statfs, &statfsbuf);
+ return ! fail;
}
/* stat the file and print what we find */
-static bool
-do_stat (char const *filename, bool follow_links, bool terse,
- char const *format)
+static bool ATTRIBUTE_WARN_UNUSED_RESULT
+do_stat (char const *filename, char const *format,
+ char const *format2)
{
+ int fd = STREQ (filename, "-") ? 0 : -1;
struct stat statbuf;
- if ((follow_links ? stat : lstat) (filename, &statbuf) != 0)
+ if (0 <= fd)
+ {
+ if (fstat (fd, &statbuf) != 0)
+ {
+ error (0, errno, _("cannot stat standard input"));
+ return false;
+ }
+ }
+ /* We can't use the shorter
+ (follow_links?stat:lstat) (filename, &statbug)
+ since stat might be a function-like macro. */
+ else if ((follow_links
+ ? stat (filename, &statbuf)
+ : lstat (filename, &statbuf)) != 0)
{
- error (0, errno, _("cannot stat %s"), quote (filename));
+ error (0, errno, _("cannot stat %s"), quoteaf (filename));
return false;
}
- if (format == NULL)
+ if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
+ format = format2;
+
+ bool fail = print_it (format, fd, filename, print_stat, &statbuf);
+ return ! fail;
+}
+
+/* Return an allocated format string in static storage that
+ corresponds to whether FS and TERSE options were declared. */
+static char *
+default_format (bool fs, bool terse, bool device)
+{
+ char *format;
+ if (fs)
+ {
+ if (terse)
+ format = xstrdup ("%n %i %l %t %s %S %b %f %a %c %d\n");
+ else
+ {
+ /* TRANSLATORS: This string uses format specifiers from
+ 'stat --help' with --file-system, and NOT from printf. */
+ format = xstrdup (_(" File: \"%n\"\n"
+ " ID: %-8i Namelen: %-7l Type: %T\n"
+ "Block size: %-10s Fundamental block size: %S\n"
+ "Blocks: Total: %-10b Free: %-10f Available: %a\n"
+ "Inodes: Total: %-10c Free: %d\n"));
+ }
+ }
+ else /* ! fs */
{
if (terse)
- {
- format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n";
- }
+ {
+ if (0 < is_selinux_enabled ())
+ format = xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
+ " %X %Y %Z %W %o %C\n");
+ else
+ format = xstrdup ("%n %s %b %f %u %g %D %i %h %t %T"
+ " %X %Y %Z %W %o\n");
+ }
else
- {
- /* Temporary hack to match original output until conditional
- implemented. */
- if (S_ISBLK (statbuf.st_mode) || S_ISCHR (statbuf.st_mode))
- {
- format =
- " File: %N\n"
- " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
- "Device: %Dh/%dd\tInode: %-10i Links: %-5h"
- " Device type: %t,%T\n"
- "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
- "Access: %x\n" "Modify: %y\n" "Change: %z\n";
- }
- else
- {
- format =
- " File: %N\n"
- " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n"
- "Device: %Dh/%dd\tInode: %-10i Links: %h\n"
- "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n"
- "Access: %x\n" "Modify: %y\n" "Change: %z\n";
- }
- }
+ {
+ char *temp;
+ /* TRANSLATORS: This string uses format specifiers from
+ 'stat --help' without --file-system, and NOT from printf. */
+ format = xstrdup (_("\
+ File: %N\n\
+ Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n\
+"));
+
+ temp = format;
+ if (device)
+ {
+ /* TRANSLATORS: This string uses format specifiers from
+ 'stat --help' without --file-system, and NOT from printf. */
+ format = xasprintf ("%s%s", format, _("\
+" "Device: %Dh/%dd\tInode: %-10i Links: %-5h Device type: %t,%T\n\
+"));
+ }
+ else
+ {
+ /* TRANSLATORS: This string uses format specifiers from
+ 'stat --help' without --file-system, and NOT from printf. */
+ format = xasprintf ("%s%s", format, _("\
+" "Device: %Dh/%dd\tInode: %-10i Links: %h\n\
+"));
+ }
+ free (temp);
+
+ temp = format;
+ /* TRANSLATORS: This string uses format specifiers from
+ 'stat --help' without --file-system, and NOT from printf. */
+ format = xasprintf ("%s%s", format, _("\
+" "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n\
+"));
+ free (temp);
+
+ if (0 < is_selinux_enabled ())
+ {
+ temp = format;
+ /* TRANSLATORS: This string uses format specifiers from
+ 'stat --help' without --file-system, and NOT from printf. */
+ format = xasprintf ("%s%s", format, _("Context: %C\n"));
+ free (temp);
+ }
+
+ temp = format;
+ /* TRANSLATORS: This string uses format specifiers from
+ 'stat --help' without --file-system, and NOT from printf. */
+ format = xasprintf ("%s%s", format,
+ _("Access: %x\n"
+ "Modify: %y\n"
+ "Change: %z\n"
+ " Birth: %w\n"));
+ free (temp);
+ }
}
- print_it (format, filename, print_stat, &statbuf);
- return true;
+ return format;
}
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
- printf (_("Usage: %s [OPTION] FILE...\n"), program_name);
+ printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
fputs (_("\
Display file or file system status.\n\
-\n\
+"), stdout);
+
+ emit_mandatory_arg_note ();
+
+ fputs (_("\
-L, --dereference follow links\n\
-f, --file-system display file system status instead of file status\n\
"), stdout);
@@ -839,8 +1443,8 @@ Display file or file system status.\n\
-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\
+ 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);
@@ -849,61 +1453,65 @@ Display file or file system status.\n\
fputs (_("\n\
The valid format sequences for files (without --file-system):\n\
\n\
- %a Access rights in octal\n\
- %A Access rights in human readable form\n\
- %b Number of blocks allocated (see %B)\n\
- %B The size in bytes of each block reported by %b\n\
+ %a access rights in octal (note '#' and '0' printf flags)\n\
+ %A access rights in human readable form\n\
+ %b number of blocks allocated (see %B)\n\
+ %B the size in bytes of each block reported by %b\n\
+ %C SELinux security context string\n\
"), stdout);
fputs (_("\
- %d Device number in decimal\n\
- %D Device number in hex\n\
- %f Raw mode in hex\n\
- %F File type\n\
- %g Group ID of owner\n\
- %G Group name of owner\n\
+ %d device number in decimal\n\
+ %D device number in hex\n\
+ %f raw mode in hex\n\
+ %F file type\n\
+ %g group ID of owner\n\
+ %G group name of owner\n\
"), stdout);
fputs (_("\
- %h Number of hard links\n\
- %i Inode number\n\
- %n File name\n\
- %N Quoted file name with dereference if symbolic link\n\
- %o I/O block size\n\
- %s Total size, in bytes\n\
- %t Major device type in hex\n\
- %T Minor device type in hex\n\
+ %h number of hard links\n\
+ %i inode number\n\
+ %m mount point\n\
+ %n file name\n\
+ %N quoted file name with dereference if symbolic link\n\
+ %o optimal I/O transfer size hint\n\
+ %s total size, in bytes\n\
+ %t major device type in hex, for character/block device special files\n\
+ %T minor device type in hex, for character/block device special files\n\
"), stdout);
fputs (_("\
- %u User ID of owner\n\
- %U User name of owner\n\
- %x Time of last access\n\
- %X Time of last access as seconds since Epoch\n\
- %y Time of last modification\n\
- %Y Time of last modification as seconds since Epoch\n\
- %z Time of last change\n\
- %Z Time of last change as seconds since Epoch\n\
+ %u user ID of owner\n\
+ %U user name of owner\n\
+ %w time of file birth, human-readable; - if unknown\n\
+ %W time of file birth, seconds since Epoch; 0 if unknown\n\
+ %x time of last access, human-readable\n\
+ %X time of last access, seconds since Epoch\n\
+ %y time of last data modification, human-readable\n\
+ %Y time of last data modification, seconds since Epoch\n\
+ %z time of last status change, human-readable\n\
+ %Z time of last status change, seconds since Epoch\n\
\n\
"), stdout);
fputs (_("\
Valid format sequences for file systems:\n\
\n\
- %a Free blocks available to non-superuser\n\
- %b Total data blocks in file system\n\
- %c Total file nodes in file system\n\
- %d Free file nodes in file system\n\
- %f Free blocks in file system\n\
+ %a free blocks available to non-superuser\n\
+ %b total data blocks in file system\n\
+ %c total file nodes in file system\n\
+ %d free file nodes in file system\n\
+ %f free blocks in file system\n\
"), stdout);
fputs (_("\
- %i File System ID in hex\n\
- %l Maximum length of filenames\n\
- %n File name\n\
- %s Block size (for faster transfers)\n\
- %S Fundamental block size (for block counts)\n\
- %t Type in hex\n\
- %T Type in human readable form\n\
+ %i file system ID in hex\n\
+ %l maximum length of filenames\n\
+ %n file name\n\
+ %s block size (for faster transfers)\n\
+ %S fundamental block size (for block counts)\n\
+ %t file system type in hex\n\
+ %T file system type in human readable form\n\
"), stdout);
printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -913,55 +1521,59 @@ main (int argc, char *argv[])
{
int c;
int i;
- bool follow_links = false;
bool fs = false;
bool terse = false;
char *format = NULL;
+ char *format2;
bool ok = true;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
+ struct lconv const *locale = localeconv ();
+ decimal_point = (locale->decimal_point[0] ? locale->decimal_point : ".");
+ decimal_point_len = strlen (decimal_point);
+
atexit (close_stdout);
while ((c = getopt_long (argc, argv, "c:fLt", long_options, NULL)) != -1)
{
switch (c)
- {
- case PRINTF_OPTION:
- format = optarg;
- interpret_backslash_escapes = true;
- trailing_delim = "";
- break;
-
- case 'c':
- format = optarg;
- interpret_backslash_escapes = false;
- trailing_delim = "\n";
- break;
-
- case 'L':
- follow_links = true;
- break;
-
- case 'f':
- fs = true;
- break;
-
- case 't':
- terse = true;
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case PRINTF_OPTION:
+ format = optarg;
+ interpret_backslash_escapes = true;
+ trailing_delim = "";
+ break;
+
+ case 'c':
+ format = optarg;
+ interpret_backslash_escapes = false;
+ trailing_delim = "\n";
+ break;
+
+ case 'L':
+ follow_links = true;
+ break;
+
+ case 'f':
+ fs = true;
+ break;
+
+ case 't':
+ terse = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
}
if (argc == optind)
@@ -970,10 +1582,18 @@ main (int argc, char *argv[])
usage (EXIT_FAILURE);
}
+ if (format)
+ format2 = format;
+ else
+ {
+ format = default_format (fs, terse, false);
+ format2 = default_format (fs, terse, true);
+ }
+
for (i = optind; i < argc; i++)
ok &= (fs
- ? do_statfs (argv[i], terse, format)
- : do_stat (argv[i], follow_links, terse, format));
+ ? do_statfs (argv[i], format)
+ : do_stat (argv[i], format, format2));
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/stdbuf.c b/src/stdbuf.c
new file mode 100644
index 0000000..7d2dc5c
--- /dev/null
+++ b/src/stdbuf.c
@@ -0,0 +1,393 @@
+/* stdbuf -- setup the standard streams for a command
+ Copyright (C) 2009-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Written by Pádraig Brady. */
+
+#include <config.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <assert.h>
+
+#include "system.h"
+#include "error.h"
+#include "filenamecat.h"
+#include "quote.h"
+#include "xreadlink.h"
+#include "xstrtol.h"
+#include "c-ctype.h"
+
+/* The official name of this program (e.g., no 'g' prefix). */
+#define PROGRAM_NAME "stdbuf"
+#define LIB_NAME "libstdbuf.so" /* FIXME: don't hardcode */
+
+#define AUTHORS proper_name_utf8 ("Padraig Brady", "P\303\241draig Brady")
+
+static char *program_path;
+
+static struct
+{
+ size_t size;
+ int optc;
+ char *optarg;
+} stdbuf[3];
+
+static struct option const longopts[] =
+{
+ {"input", required_argument, NULL, 'i'},
+ {"output", required_argument, NULL, 'o'},
+ {"error", required_argument, NULL, 'e'},
+ {GETOPT_HELP_OPTION_DECL},
+ {GETOPT_VERSION_OPTION_DECL},
+ {NULL, 0, NULL, 0}
+};
+
+/* Set size to the value of STR, interpreted as a decimal integer,
+ optionally multiplied by various values.
+ Return -1 on error, 0 on success.
+
+ This supports dd BLOCK size suffixes.
+ Note we don't support dd's b=512, c=1, w=2 or 21x512MiB formats. */
+static int
+parse_size (char const *str, size_t *size)
+{
+ uintmax_t tmp_size;
+ enum strtol_error e = xstrtoumax (str, NULL, 10, &tmp_size, "EGkKMPTYZ0");
+ if (e == LONGINT_OK && SIZE_MAX < tmp_size)
+ e = LONGINT_OVERFLOW;
+
+ if (e == LONGINT_OK)
+ {
+ errno = 0;
+ *size = tmp_size;
+ return 0;
+ }
+
+ errno = (e == LONGINT_OVERFLOW ? EOVERFLOW : errno);
+ return -1;
+}
+
+void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ emit_try_help ();
+ else
+ {
+ printf (_("Usage: %s OPTION... COMMAND\n"), program_name);
+ fputs (_("\
+Run COMMAND, with modified buffering operations for its standard streams.\n\
+"), stdout);
+
+ emit_mandatory_arg_note ();
+
+ fputs (_("\
+ -i, --input=MODE adjust standard input stream buffering\n\
+ -o, --output=MODE adjust standard output stream buffering\n\
+ -e, --error=MODE adjust standard error stream buffering\n\
+"), stdout);
+ fputs (HELP_OPTION_DESCRIPTION, stdout);
+ fputs (VERSION_OPTION_DESCRIPTION, stdout);
+ fputs (_("\n\
+If MODE is 'L' the corresponding stream will be line buffered.\n\
+This option is invalid with standard input.\n"), stdout);
+ fputs (_("\n\
+If MODE is '0' the corresponding stream will be unbuffered.\n\
+"), stdout);
+ fputs (_("\n\
+Otherwise MODE is a number which may be followed by one of the following:\n\
+KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n\
+In this case the corresponding stream will be fully buffered with the buffer\n\
+size set to MODE bytes.\n\
+"), stdout);
+ fputs (_("\n\
+NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does\n\
+for example) then that will override corresponding changes by 'stdbuf'.\n\
+Also some filters (like 'dd' and 'cat' etc.) don't use streams for I/O,\n\
+and are thus unaffected by 'stdbuf' settings.\n\
+"), stdout);
+ emit_ancillary_info (PROGRAM_NAME);
+ }
+ exit (status);
+}
+
+/* argv[0] can be anything really, but generally it contains
+ the path to the executable or just a name if it was executed
+ using $PATH. In the latter case to get the path we can:
+ search getenv("PATH"), readlink("/prof/self/exe"), getenv("_"),
+ dladdr(), pstat_getpathname(), etc. */
+
+static void
+set_program_path (const char *arg)
+{
+ if (strchr (arg, '/')) /* Use absolute or relative paths directly. */
+ {
+ program_path = dir_name (arg);
+ }
+ else
+ {
+ char *path = xreadlink ("/proc/self/exe");
+ if (path)
+ program_path = dir_name (path);
+ else if ((path = getenv ("PATH")))
+ {
+ char *dir;
+ path = xstrdup (path);
+ for (dir = strtok (path, ":"); dir != NULL; dir = strtok (NULL, ":"))
+ {
+ char *candidate = file_name_concat (dir, arg, NULL);
+ if (access (candidate, X_OK) == 0)
+ {
+ program_path = dir_name (candidate);
+ free (candidate);
+ break;
+ }
+ free (candidate);
+ }
+ }
+ free (path);
+ }
+}
+
+static int
+optc_to_fileno (int c)
+{
+ int ret = -1;
+
+ switch (c)
+ {
+ case 'e':
+ ret = STDERR_FILENO;
+ break;
+ case 'i':
+ ret = STDIN_FILENO;
+ break;
+ case 'o':
+ ret = STDOUT_FILENO;
+ break;
+ }
+
+ return ret;
+}
+
+static void
+set_LD_PRELOAD (void)
+{
+ int ret;
+#ifdef __APPLE__
+ char const *preload_env = "DYLD_INSERT_LIBRARIES";
+#else
+ char const *preload_env = "LD_PRELOAD";
+#endif
+ char *old_libs = getenv (preload_env);
+ char *LD_PRELOAD;
+
+ /* Note this would auto add the appropriate search path for "libstdbuf.so":
+ gcc stdbuf.c -Wl,-rpath,'$ORIGIN' -Wl,-rpath,$PKGLIBEXECDIR
+ However we want the lookup done for the exec'd command not stdbuf.
+
+ Since we don't link against libstdbuf.so add it to PKGLIBEXECDIR
+ rather than to LIBDIR.
+
+ Note we could add "" as the penultimate item in the following list
+ to enable searching for libstdbuf.so in the default system lib paths.
+ However that would not indicate an error if libstdbuf.so was not found.
+ Also while this could support auto selecting the right arch in a multilib
+ environment, what we really want is to auto select based on the arch of the
+ command being run, rather than that of stdbuf itself. This is currently
+ not supported due to the unusual need for controlling the stdio buffering
+ of programs that are a different architecture to the default on the
+ system (and that of stdbuf itself). */
+ char const *const search_path[] = {
+ program_path,
+ PKGLIBEXECDIR,
+ NULL
+ };
+
+ char const *const *path = search_path;
+ char *libstdbuf;
+
+ while (true)
+ {
+ struct stat sb;
+
+ if (!**path) /* system default */
+ {
+ libstdbuf = xstrdup (LIB_NAME);
+ break;
+ }
+ ret = asprintf (&libstdbuf, "%s/%s", *path, LIB_NAME);
+ if (ret < 0)
+ xalloc_die ();
+ if (stat (libstdbuf, &sb) == 0) /* file_exists */
+ break;
+ free (libstdbuf);
+
+ ++path;
+ if ( ! *path)
+ error (EXIT_CANCELED, 0, _("failed to find %s"), quote (LIB_NAME));
+ }
+
+ /* FIXME: Do we need to support libstdbuf.dll, c:, '\' separators etc? */
+
+ if (old_libs)
+ ret = asprintf (&LD_PRELOAD, "%s=%s:%s", preload_env, old_libs, libstdbuf);
+ else
+ ret = asprintf (&LD_PRELOAD, "%s=%s", preload_env, libstdbuf);
+
+ if (ret < 0)
+ xalloc_die ();
+
+ free (libstdbuf);
+
+ ret = putenv (LD_PRELOAD);
+#ifdef __APPLE__
+ if (ret == 0)
+ ret = setenv ("DYLD_FORCE_FLAT_NAMESPACE", "y", 1);
+#endif
+
+ if (ret != 0)
+ {
+ error (EXIT_CANCELED, errno,
+ _("failed to update the environment with %s"),
+ quote (LD_PRELOAD));
+ }
+}
+
+/* Populate environ with _STDBUF_I=$MODE _STDBUF_O=$MODE _STDBUF_E=$MODE.
+ Return TRUE if any environment variables set. */
+
+static bool
+set_libstdbuf_options (void)
+{
+ bool env_set = false;
+ size_t i;
+
+ for (i = 0; i < ARRAY_CARDINALITY (stdbuf); i++)
+ {
+ if (stdbuf[i].optarg)
+ {
+ char *var;
+ int ret;
+
+ if (*stdbuf[i].optarg == 'L')
+ ret = asprintf (&var, "%s%c=L", "_STDBUF_",
+ toupper (stdbuf[i].optc));
+ else
+ ret = asprintf (&var, "%s%c=%" PRIuMAX, "_STDBUF_",
+ toupper (stdbuf[i].optc),
+ (uintmax_t) stdbuf[i].size);
+ if (ret < 0)
+ xalloc_die ();
+
+ if (putenv (var) != 0)
+ {
+ error (EXIT_CANCELED, errno,
+ _("failed to update the environment with %s"),
+ quote (var));
+ }
+
+ env_set = true;
+ }
+ }
+
+ return env_set;
+}
+
+int
+main (int argc, char **argv)
+{
+ int c;
+
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ initialize_exit_failure (EXIT_CANCELED);
+ atexit (close_stdout);
+
+ while ((c = getopt_long (argc, argv, "+i:o:e:", longopts, NULL)) != -1)
+ {
+ int opt_fileno;
+
+ switch (c)
+ {
+ /* Old McDonald had a farm ei... */
+ case 'e':
+ case 'i':
+ case 'o':
+ opt_fileno = optc_to_fileno (c);
+ assert (0 <= opt_fileno && opt_fileno < ARRAY_CARDINALITY (stdbuf));
+ stdbuf[opt_fileno].optc = c;
+ while (c_isspace (*optarg))
+ optarg++;
+ stdbuf[opt_fileno].optarg = optarg;
+ if (c == 'i' && *optarg == 'L')
+ {
+ /* -oL will be by far the most common use of this utility,
+ but one could easily think -iL might have the same affect,
+ so disallow it as it could be confusing. */
+ error (0, 0, _("line buffering stdin is meaningless"));
+ usage (EXIT_CANCELED);
+ }
+
+ if (!STREQ (optarg, "L")
+ && parse_size (optarg, &stdbuf[opt_fileno].size) == -1)
+ error (EXIT_CANCELED, errno, _("invalid mode %s"), quote (optarg));
+
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_CANCELED);
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ /* must specify at least 1 command. */
+ if (argc < 1)
+ {
+ error (0, 0, _("missing operand"));
+ usage (EXIT_CANCELED);
+ }
+
+ if (! set_libstdbuf_options ())
+ {
+ error (0, 0, _("you must specify a buffering mode option"));
+ usage (EXIT_CANCELED);
+ }
+
+ /* Try to preload libstdbuf first from the same path as
+ stdbuf is running from. */
+ set_program_path (program_name);
+ if (!program_path)
+ program_path = xstrdup (PKGLIBDIR); /* Need to init to non-NULL. */
+ set_LD_PRELOAD ();
+ free (program_path);
+
+ execvp (*argv, argv);
+
+ int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
+ error (0, errno, _("failed to run command %s"), quote (argv[0]));
+ return exit_status;
+}
diff --git a/src/stty.c b/src/stty.c
index 33a821d..30053cc 100644
--- a/src/stty.c
+++ b/src/stty.c
@@ -1,10 +1,10 @@
/* stty -- change and print terminal line settings
- Copyright (C) 1990-2005 Free Software Foundation, Inc.
+ Copyright (C) 1990-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Usage: stty [-ag] [--all] [--save] [-F device] [--file=device] [setting...]
@@ -37,15 +36,11 @@
#include <stdio.h>
#include <sys/types.h>
-#if HAVE_TERMIOS_H
-# include <termios.h>
-#endif
+#include <termios.h>
#if HAVE_STROPTS_H
# include <stropts.h>
#endif
-#ifdef HAVE_SYS_IOCTL_H
-# include <sys/ioctl.h>
-#endif
+#include <sys/ioctl.h>
#ifdef WINSIZE_IN_PTEM
# include <sys/stream.h>
@@ -57,18 +52,19 @@
#endif
#include <getopt.h>
#include <stdarg.h>
+#include <assert.h>
#include "system.h"
#include "error.h"
#include "fd-reopen.h"
#include "quote.h"
-#include "vasprintf.h"
+#include "xdectoint.h"
#include "xstrtol.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "stty"
-#define AUTHORS "David MacKenzie"
+#define AUTHORS proper_name ("David MacKenzie")
#ifndef _POSIX_VDISABLE
# define _POSIX_VDISABLE 0
@@ -122,8 +118,8 @@
# define CSWTCH _POSIX_VDISABLE
#endif
-/* SunOS 5.3 loses (^Z doesn't work) if `swtch' is the same as `susp'.
- So the default is to disable `swtch.' */
+/* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
+ So the default is to disable 'swtch.' */
#if defined __sparc__ && defined __svr4__
# undef CSWTCH
# define CSWTCH _POSIX_VDISABLE
@@ -184,17 +180,18 @@ enum output_type
changed, all, recoverable /* Default, -a, -g. */
};
-/* Which member(s) of `struct termios' a mode uses. */
+/* Which member(s) of 'struct termios' a mode uses. */
enum mode_type
{
control, input, output, local, combination
};
-/* Flags for `struct mode_info'. */
-#define SANE_SET 1 /* Set in `sane' mode. */
-#define SANE_UNSET 2 /* Unset in `sane' mode. */
-#define REV 4 /* Can be turned off by prepending `-'. */
+/* Flags for 'struct mode_info'. */
+#define SANE_SET 1 /* Set in 'sane' mode. */
+#define SANE_UNSET 2 /* Unset in 'sane' mode. */
+#define REV 4 /* Can be turned off by prepending '-'. */
#define OMIT 8 /* Don't display value. */
+#define NO_SETATTR 16 /* tcsetattr not used to set mode bits. */
/* Each mode. */
struct mode_info
@@ -206,10 +203,13 @@ struct mode_info
unsigned long mask; /* Other bits to turn off for this mode. */
};
-static struct mode_info mode_info[] =
+static struct mode_info const mode_info[] =
{
{"parenb", control, REV, PARENB, 0},
{"parodd", control, REV, PARODD, 0},
+#ifdef CMSPAR
+ {"cmspar", control, REV, CMSPAR, 0},
+#endif
{"cs5", control, 0, CS5, CSIZE},
{"cs6", control, 0, CS6, CSIZE},
{"cs7", control, 0, CS7, CSIZE},
@@ -222,6 +222,9 @@ static struct mode_info mode_info[] =
#ifdef CRTSCTS
{"crtscts", control, REV, CRTSCTS, 0},
#endif
+#ifdef CDTRDSR
+ {"cdtrdsr", control, REV, CDTRDSR, 0},
+#endif
{"ignbrk", input, SANE_UNSET | REV, IGNBRK, 0},
{"brkint", input, SANE_SET | REV, BRKINT, 0},
@@ -281,10 +284,18 @@ static struct mode_info mode_info[] =
{"cr0", output, SANE_SET, CR0, CRDLY},
#endif
#ifdef TABDLY
+# ifdef TAB3
{"tab3", output, SANE_UNSET, TAB3, TABDLY},
+# endif
+# ifdef TAB2
{"tab2", output, SANE_UNSET, TAB2, TABDLY},
+# endif
+# ifdef TAB1
{"tab1", output, SANE_UNSET, TAB1, TABDLY},
+# endif
+# ifdef TAB0
{"tab0", output, SANE_SET, TAB0, TABDLY},
+# endif
#else
# ifdef OXTABS
{"tab3", output, SANE_UNSET, OXTABS, 0},
@@ -332,6 +343,14 @@ static struct mode_info mode_info[] =
{"echoke", local, SANE_SET | REV, ECHOKE, 0},
{"crtkill", local, REV | OMIT, ECHOKE, 0},
#endif
+#ifdef FLUSHO
+ {"flusho", local, SANE_UNSET | REV, FLUSHO, 0},
+#endif
+#if defined TIOCEXT
+ {"extproc", local, SANE_UNSET | REV | NO_SETATTR, EXTPROC, 0},
+#elif defined EXTPROC
+ {"extproc", local, SANE_UNSET | REV, EXTPROC, 0},
+#endif
{"evenp", combination, REV | OMIT, 0, 0},
{"parity", combination, REV | OMIT, 0, 0},
@@ -364,13 +383,13 @@ static struct mode_info mode_info[] =
struct control_info
{
const char *name; /* Name given on command line. */
- cc_t saneval; /* Value to set for `stty sane'. */
+ cc_t saneval; /* Value to set for 'stty sane'. */
size_t offset; /* Offset in c_cc. */
};
/* Control characters. */
-static struct control_info control_info[] =
+static struct control_info const control_info[] =
{
{"intr", CINTR, VINTR},
{"quit", CQUIT, VQUIT},
@@ -404,7 +423,8 @@ static struct control_info control_info[] =
{"lnext", CLNEXT, VLNEXT},
#endif
#ifdef VFLUSHO
- {"flush", CFLUSHO, VFLUSHO},
+ {"flush", CFLUSHO, VFLUSHO}, /* deprecated compat option. */
+ {"discard", CFLUSHO, VFLUSHO},
#endif
#ifdef VSTATUS
{"status", CSTATUS, VSTATUS},
@@ -420,8 +440,8 @@ static char const *visible (cc_t ch);
static unsigned long int baud_to_value (speed_t speed);
static bool recover_mode (char const *arg, struct termios *mode);
static int screen_columns (void);
-static bool set_mode (struct mode_info *info, bool reversed,
- struct termios *mode);
+static bool set_mode (struct mode_info const *info, bool reversed,
+ struct termios *mode);
static unsigned long int integer_arg (const char *s, unsigned long int max);
static speed_t string_to_baud (const char *arg);
static tcflag_t *mode_type_flag (enum mode_type type, struct termios *mode);
@@ -429,16 +449,16 @@ static void display_all (struct termios *mode, char const *device_name);
static void display_changed (struct termios *mode);
static void display_recoverable (struct termios *mode);
static void display_settings (enum output_type output_type,
- struct termios *mode,
- const char *device_name);
+ struct termios *mode,
+ const char *device_name);
static void display_speed (struct termios *mode, bool fancy);
static void display_window_size (bool fancy, char const *device_name);
static void sane_mode (struct termios *mode);
-static void set_control_char (struct control_info *info,
- const char *arg,
- struct termios *mode);
+static void set_control_char (struct control_info const *info,
+ const char *arg,
+ struct termios *mode);
static void set_speed (enum speed_setting type, const char *arg,
- struct termios *mode);
+ struct termios *mode);
static void set_window_size (int rows, int cols, char const *device_name);
/* The width of the screen, for output wrapping. */
@@ -447,7 +467,10 @@ static int max_col;
/* Current position, to know when to wrap. */
static int current_col;
-static struct option longopts[] =
+/* Default "drain" mode for tcsetattr. */
+static int tcsetattr_options = TCSADRAIN;
+
+static struct option const longopts[] =
{
{"all", no_argument, NULL, 'a'},
{"save", no_argument, NULL, 'g'},
@@ -457,9 +480,6 @@ static struct option longopts[] =
{NULL, 0, NULL, 0}
};
-/* The name this program was run with. */
-char *program_name;
-
static void wrapf (const char *message, ...)
__attribute__ ((__format__ (__printf__, 1, 2)));
@@ -484,15 +504,15 @@ wrapf (const char *message,...)
if (0 < current_col)
{
if (max_col - current_col < buflen)
- {
- putchar ('\n');
- current_col = 0;
- }
+ {
+ putchar ('\n');
+ current_col = 0;
+ }
else
- {
- putchar (' ');
- current_col++;
- }
+ {
+ putchar (' ');
+ current_col++;
+ }
}
fputs (buf, stdout);
@@ -504,19 +524,22 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
-Usage: %s [-F DEVICE] [--file=DEVICE] [SETTING]...\n\
- or: %s [-F DEVICE] [--file=DEVICE] [-a|--all]\n\
- or: %s [-F DEVICE] [--file=DEVICE] [-g|--save]\n\
+Usage: %s [-F DEVICE | --file=DEVICE] [SETTING]...\n\
+ or: %s [-F DEVICE | --file=DEVICE] [-a|--all]\n\
+ or: %s [-F DEVICE | --file=DEVICE] [-g|--save]\n\
"),
- program_name, program_name, program_name);
+ program_name, program_name, program_name);
fputs (_("\
Print or change terminal characteristics.\n\
-\n\
+"), stdout);
+
+ emit_mandatory_arg_note ();
+
+ fputs (_("\
-a, --all print all current settings in human-readable form\n\
-g, --save print all current settings in a stty-readable form\n\
-F, --file=DEVICE open and use the specified DEVICE instead of stdin\n\
@@ -530,45 +553,97 @@ settings. The underlying system defines which settings are available.\n\
"), stdout);
fputs (_("\
\n\
-Special characters:\n\
+Special characters:\n"), stdout);
+#ifdef VFLUSHO
+ fputs (_("\
+ * discard CHAR CHAR will toggle discarding of output\n\
+"), stdout);
+#endif
+#ifdef VDSUSP
+ fputs (_("\
* dsusp CHAR CHAR will send a terminal stop signal once input flushed\n\
+"), stdout);
+#endif
+ fputs (_("\
eof CHAR CHAR will send an end of file (terminate the input)\n\
eol CHAR CHAR will end the line\n\
"), stdout);
+#ifdef VEOL2
fputs (_("\
* eol2 CHAR alternate CHAR for ending the line\n\
+"), stdout);
+#endif
+ fputs (_("\
erase CHAR CHAR will erase the last character typed\n\
intr CHAR CHAR will send an interrupt signal\n\
kill CHAR CHAR will erase the current line\n\
"), stdout);
+#ifdef VLNEXT
fputs (_("\
* lnext CHAR CHAR will enter the next character quoted\n\
+"), stdout);
+#endif
+#ifdef VSTATUS
+ fputs (_("\
+ * status CHAR CHAR will send an info signal\n\
+"), stdout);
+#endif
+ fputs (_("\
quit CHAR CHAR will send a quit signal\n\
+"), stdout);
+#if defined CREPRINT || defined VREPRINT
+ fputs (_("\
* rprnt CHAR CHAR will redraw the current line\n\
- start CHAR CHAR will restart the output after stopping it\n\
"), stdout);
+#endif
fputs (_("\
+ start CHAR CHAR will restart the output after stopping it\n\
stop CHAR CHAR will stop the output\n\
susp CHAR CHAR will send a terminal stop signal\n\
+"), stdout);
+#ifdef VSWTCH
+ fputs (_("\
* swtch CHAR CHAR will switch to a different shell layer\n\
+"), stdout);
+#endif
+#ifdef VWERASE
+ fputs (_("\
* werase CHAR CHAR will erase the last word typed\n\
"), stdout);
+#endif
fputs (_("\
\n\
Special settings:\n\
- N set the input and output speeds to N bauds\n\
+ N set the input and output speeds to N bauds\n\
+"), stdout);
+#ifdef TIOCGWINSZ
+ fputs (_("\
* cols N tell the kernel that the terminal has N columns\n\
* columns N same as cols N\n\
"), stdout);
+#endif
+ printf (_("\
+ * [-]drain wait for transmission before applying settings (%s by default)\
+\n"), tcsetattr_options == TCSADRAIN ? _("on") : _("off"));
fputs (_("\
ispeed N set the input speed to N\n\
+"), stdout);
+#ifdef HAVE_C_LINE
+ fputs (_("\
* line N use line discipline N\n\
+"), stdout);
+#endif
+ fputs (_("\
min N with -icanon, set N characters minimum for a completed read\n\
ospeed N set the output speed to N\n\
"), stdout);
+#ifdef TIOCGWINSZ
fputs (_("\
* rows N tell the kernel that the terminal has N rows\n\
* size print the number of rows and columns according to the kernel\n\
+"), stdout);
+#endif
+ fputs (_("\
speed print the terminal speed\n\
time N with -icanon, set read timeout of N tenths of a second\n\
"), stdout);
@@ -577,16 +652,32 @@ Special settings:\n\
Control settings:\n\
[-]clocal disable modem control signals\n\
[-]cread allow input to be received\n\
+"), stdout);
+#ifdef CRTSCTS
+ fputs (_("\
* [-]crtscts enable RTS/CTS handshaking\n\
+"), stdout);
+#endif
+#ifdef CDTRDSR
+ fputs (_("\
+ * [-]cdtrdsr enable DTR/DSR handshaking\n\
+"), stdout);
+#endif
+ fputs (_("\
csN set character size to N bits, N in [5..8]\n\
"), stdout);
fputs (_("\
- [-]cstopb use two stop bits per character (one with `-')\n\
+ [-]cstopb use two stop bits per character (one with '-')\n\
[-]hup send a hangup signal when the last process closes the tty\n\
[-]hupcl same as [-]hup\n\
[-]parenb generate parity bit in output and expect parity bit in input\n\
- [-]parodd set odd parity (even with `-')\n\
+ [-]parodd set odd parity (or even parity with '-')\n\
+"), stdout);
+#ifdef CMSPAR
+ fputs (_("\
+ * [-]cmspar use \"stick\" (mark/space) parity\n\
"), stdout);
+#endif
fputs (_("\
\n\
Input settings:\n\
@@ -594,20 +685,34 @@ Input settings:\n\
[-]icrnl translate carriage return to newline\n\
[-]ignbrk ignore break characters\n\
[-]igncr ignore carriage return\n\
+ [-]ignpar ignore characters with parity errors\n\
"), stdout);
+#ifdef IMAXBEL
fputs (_("\
- [-]ignpar ignore characters with parity errors\n\
* [-]imaxbel beep and do not flush a full input buffer on a character\n\
+"), stdout);
+#endif
+ fputs (_("\
[-]inlcr translate newline to carriage return\n\
[-]inpck enable input parity checking\n\
[-]istrip clear high (8th) bit of input characters\n\
"), stdout);
+#ifdef IUTF8
fputs (_("\
* [-]iutf8 assume input characters are UTF-8 encoded\n\
"), stdout);
+#endif
+#ifdef IUCLC
fputs (_("\
* [-]iuclc translate uppercase characters to lowercase\n\
+"), stdout);
+#endif
+#ifdef IXANY
+ fputs (_("\
* [-]ixany let any character restart output, not only start character\n\
+"), stdout);
+#endif
+ fputs (_("\
[-]ixoff enable sending of start/stop characters\n\
[-]ixon enable XON/XOFF flow control\n\
[-]parmrk mark parity errors (with a 255-0-character sequence)\n\
@@ -616,59 +721,168 @@ Input settings:\n\
fputs (_("\
\n\
Output settings:\n\
+"), stdout);
+#ifdef BSDLY
+ fputs (_("\
* bsN backspace delay style, N in [0..1]\n\
+"), stdout);
+#endif
+#ifdef CRDLY
+ fputs (_("\
* crN carriage return delay style, N in [0..3]\n\
+"), stdout);
+#endif
+#ifdef FFDLY
+ fputs (_("\
* ffN form feed delay style, N in [0..1]\n\
+"), stdout);
+#endif
+#ifdef NLDLY
+ fputs (_("\
* nlN newline delay style, N in [0..1]\n\
"), stdout);
+#endif
+#ifdef OCRNL
fputs (_("\
* [-]ocrnl translate carriage return to newline\n\
- * [-]ofdel use delete characters for fill instead of null characters\n\
+"), stdout);
+#endif
+#ifdef OFDEL
+ fputs (_("\
+ * [-]ofdel use delete characters for fill instead of NUL characters\n\
+"), stdout);
+#endif
+#ifdef OFILL
+ fputs (_("\
* [-]ofill use fill (padding) characters instead of timing for delays\n\
+"), stdout);
+#endif
+#ifdef OLCUC
+ fputs (_("\
* [-]olcuc translate lowercase characters to uppercase\n\
+"), stdout);
+#endif
+#ifdef ONLCR
+ fputs (_("\
* [-]onlcr translate newline to carriage return-newline\n\
+"), stdout);
+#endif
+#ifdef ONLRET
+ fputs (_("\
* [-]onlret newline performs a carriage return\n\
"), stdout);
+#endif
+#ifdef ONOCR
fputs (_("\
* [-]onocr do not print carriage returns in the first column\n\
+"), stdout);
+#endif
+ fputs (_("\
[-]opost postprocess output\n\
+"), stdout);
+#if defined TABDLY || defined OXTABS
+ fputs (_("\
* tabN horizontal tab delay style, N in [0..3]\n\
* tabs same as tab0\n\
* -tabs same as tab3\n\
+"), stdout);
+#endif
+#ifdef VTDLY
+ fputs (_("\
* vtN vertical tab delay style, N in [0..1]\n\
"), stdout);
+#endif
fputs (_("\
\n\
Local settings:\n\
[-]crterase echo erase characters as backspace-space-backspace\n\
+"), stdout);
+#ifdef ECHOKE
+ fputs (_("\
* crtkill kill all line by obeying the echoprt and echoe settings\n\
* -crtkill kill all line by obeying the echoctl and echok settings\n\
"), stdout);
+#endif
+#ifdef ECHOCTL
+ fputs (_("\
+ * [-]ctlecho echo control characters in hat notation ('^c')\n\
+"), stdout);
+#endif
fputs (_("\
- * [-]ctlecho echo control characters in hat notation (`^c')\n\
[-]echo echo input characters\n\
+"), stdout);
+#ifdef ECHOCTL
+ fputs (_("\
* [-]echoctl same as [-]ctlecho\n\
+"), stdout);
+#endif
+ fputs (_("\
[-]echoe same as [-]crterase\n\
[-]echok echo a newline after a kill character\n\
"), stdout);
+#ifdef ECHOKE
fputs (_("\
* [-]echoke same as [-]crtkill\n\
+"), stdout);
+#endif
+ fputs (_("\
[-]echonl echo newline even if not echoing other characters\n\
- * [-]echoprt echo erased characters backward, between `\\' and '/'\n\
- [-]icanon enable erase, kill, werase, and rprnt special characters\n\
- [-]iexten enable non-POSIX special characters\n\
"), stdout);
+#ifdef ECHOPRT
+ fputs (_("\
+ * [-]echoprt echo erased characters backward, between '\\' and '/'\n\
+"), stdout);
+#endif
+#if defined EXTPROC || defined TIOCEXT
+ fputs (_("\
+ * [-]extproc enable \"LINEMODE\"; useful with high latency links\n\
+"), stdout);
+#endif
+#if defined FLUSHO
+ fputs (_("\
+ * [-]flusho discard output\n\
+"), stdout);
+#endif
+ printf (_("\
+ [-]icanon enable special characters: %s\n\
+ [-]iexten enable non-POSIX special characters\n\
+"), "erase, kill"
+#ifdef VWERASE
+ ", werase"
+#endif
+#if defined CREPRINT || defined VREPRINT
+ ", rprnt"
+#endif
+);
fputs (_("\
[-]isig enable interrupt, quit, and suspend special characters\n\
[-]noflsh disable flushing after interrupt and quit special characters\n\
+"), stdout);
+#ifdef ECHOPRT
+ fputs (_("\
* [-]prterase same as [-]echoprt\n\
+"), stdout);
+#endif
+#ifdef TOSTOP
+ fputs (_("\
* [-]tostop stop background jobs that try to write to the terminal\n\
- * [-]xcase with icanon, escape with `\\' for uppercase characters\n\
"), stdout);
+#endif
+#ifdef XCASE
+ fputs (_("\
+ * [-]xcase with icanon, escape with '\\' for uppercase characters\n\
+"), stdout);
+#endif
fputs (_("\
\n\
Combination settings:\n\
+"), stdout);
+#if defined XCASE && defined IUCLC && defined OLCUC
+ fputs (_("\
* [-]LCASE same as [-]lcase\n\
+"), stdout);
+#endif
+ fputs (_("\
cbreak same as -icanon\n\
-cbreak same as icanon\n\
"), stdout);
@@ -676,22 +890,69 @@ Combination settings:\n\
cooked same as brkint ignpar istrip icrnl ixon opost isig\n\
icanon, eof and eol characters to their default values\n\
-cooked same as raw\n\
- crt same as echoe echoctl echoke\n\
"), stdout);
- fputs (_("\
- dec same as echoe echoctl echoke -ixany intr ^c erase 0177\n\
+ printf (_("\
+ crt same as %s\n\
+"), "echoe"
+#ifdef ECHOCTL
+ " echoctl"
+#endif
+#ifdef ECHOKE
+ " echoke"
+#endif
+);
+ printf (_("\
+ dec same as %s intr ^c erase 0177\n\
kill ^u\n\
+"), "echoe"
+#ifdef ECHOCTL
+ " echoctl"
+#endif
+#ifdef ECHOKE
+ " echoke"
+#endif
+#ifdef IXANY
+ " -ixany"
+#endif
+);
+#ifdef IXANY
+ fputs (_("\
* [-]decctlq same as [-]ixany\n\
+"), stdout);
+#endif
+ fputs (_("\
ek erase and kill characters to their default values\n\
evenp same as parenb -parodd cs7\n\
+ -evenp same as -parenb cs8\n\
"), stdout);
+#if defined XCASE && defined IUCLC && defined OLCUC
fputs (_("\
- -evenp same as -parenb cs8\n\
* [-]lcase same as xcase iuclc olcuc\n\
+"), stdout);
+#endif
+ fputs (_("\
litout same as -parenb -istrip -opost cs8\n\
-litout same as parenb istrip opost cs7\n\
- nl same as -icrnl -onlcr\n\
- -nl same as icrnl -inlcr -igncr onlcr -ocrnl -onlret\n\
+"), stdout);
+ printf (_("\
+ nl same as %s\n\
+ -nl same as %s\n\
+"), "-icrnl"
+#ifdef ONLCR
+ " -onlcr"
+#endif
+ , "icrnl -inlcr -igncr"
+#ifdef ONLCR
+ " onlcr"
+#endif
+#ifdef OCRNL
+ " -ocrnl"
+#endif
+#ifdef ONLRET
+ " -onlret"
+#endif
+);
+ fputs (_("\
"), stdout);
fputs (_("\
oddp same as parenb parodd cs7\n\
@@ -700,20 +961,111 @@ Combination settings:\n\
pass8 same as -parenb -istrip cs8\n\
-pass8 same as parenb istrip cs7\n\
"), stdout);
- fputs (_("\
+ printf (_("\
raw same as -ignbrk -brkint -ignpar -parmrk -inpck -istrip\n\
- -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany\n\
- -imaxbel -opost -isig -icanon -xcase min 1 time 0\n\
+ -inlcr -igncr -icrnl -ixon -ixoff -icanon -opost\n\
+ -isig%s min 1 time 0\n\
-raw same as cooked\n\
-"), stdout);
- fputs (_("\
- sane same as cread -ignbrk brkint -inlcr -igncr icrnl -iutf8\n\
- -ixoff -iuclc -ixany imaxbel opost -olcuc -ocrnl onlcr\n\
- -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0\n\
- isig icanon iexten echo echoe echok -echonl -noflsh\n\
- -xcase -tostop -echoprt echoctl echoke, all special\n\
- characters to their default values.\n\
-"), stdout);
+"),
+#ifdef IUCLC
+ " -iuclc"
+#endif
+#ifdef IXANY
+ " -ixany"
+#endif
+#ifdef IMAXBEL
+ " -imaxbel"
+#endif
+#ifdef XCASE
+ " -xcase"
+#endif
+);
+ printf (_("\
+ sane same as cread -ignbrk brkint -inlcr -igncr icrnl\n\
+ icanon iexten echo echoe echok -echonl -noflsh\n\
+ %s\n\
+ %s\n\
+ %s,\n\
+ all special characters to their default values\n\
+"),
+ "-ixoff"
+#ifdef IUTF8
+ " -iutf8"
+#endif
+#ifdef IUCLC
+ " -iuclc"
+#endif
+#ifdef IXANY
+ " -ixany"
+#endif
+#ifdef IMAXBEL
+ " imaxbel"
+#endif
+#ifdef XCASE
+ " -xcase"
+#endif
+#ifdef OLCUC
+ " -olcuc"
+#endif
+#ifdef OCRNL
+ " -ocrnl"
+#endif
+
+ , "opost"
+#ifdef OFILL
+ " -ofill"
+#endif
+#ifdef ONLCR
+ " onlcr"
+#endif
+#ifdef ONOCR
+ " -onocr"
+#endif
+#ifdef ONLRET
+ " -onlret"
+#endif
+#ifdef NLDLY
+ " nl0"
+#endif
+#ifdef CRDLY
+ " cr0"
+#endif
+#ifdef TAB0
+ " tab0"
+#endif
+#ifdef BSDLY
+ " bs0"
+#endif
+#ifdef VTDLY
+ " vt0"
+#endif
+#ifdef FFDLY
+ " ff0"
+#endif
+
+ , "isig"
+#ifdef TOSTOP
+ " -tostop"
+#endif
+#ifdef OFDEL
+ " -ofdel"
+#endif
+#ifdef ECHOPRT
+ " -echoprt"
+#endif
+#ifdef ECHOCTL
+ " echoctl"
+#endif
+#ifdef ECHOKE
+ " echoke"
+#endif
+#ifdef EXTPROC
+ " -extproc"
+#endif
+#ifdef FLUSHO
+ " -flusho"
+#endif
+);
fputs (_("\
\n\
Handle the tty line connected to standard input. Without arguments,\n\
@@ -721,7 +1073,7 @@ prints baud rate, line discipline, and deviations from stty sane. In\n\
settings, CHAR is taken literally, or coded as in ^c, 0x37, 0177 or\n\
127; special values ^- or undef used to disable special characters.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -731,14 +1083,14 @@ main (int argc, char **argv)
{
/* Initialize to all zeroes so there is no risk memcmp will report a
spurious difference in an uninitialized portion of the structure. */
- struct termios mode = { 0, };
+ static struct termios mode;
enum output_type output_type;
int optc;
int argi = 0;
int opti = 1;
bool require_set_attr;
- bool speed_was_set;
+ bool speed_was_set _GL_UNUSED;
bool verbose_output;
bool recoverable_output;
int k;
@@ -747,7 +1099,7 @@ main (int argc, char **argv)
const char *device_name;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -769,60 +1121,64 @@ main (int argc, char **argv)
short and long options, --, POSIXLY_CORRECT, etc. */
while ((optc = getopt_long (argc - argi, argv + argi, "-agF:",
- longopts, NULL))
- != -1)
+ longopts, NULL))
+ != -1)
{
switch (optc)
- {
- case 'a':
- verbose_output = true;
- output_type = all;
- break;
+ {
+ case 'a':
+ verbose_output = true;
+ output_type = all;
+ break;
- case 'g':
- recoverable_output = true;
- output_type = recoverable;
- break;
+ case 'g':
+ recoverable_output = true;
+ output_type = recoverable;
+ break;
- case 'F':
- if (file_name)
- error (EXIT_FAILURE, 0, _("only one device may be specified"));
- file_name = optarg;
- break;
+ case 'F':
+ if (file_name)
+ error (EXIT_FAILURE, 0, _("only one device may be specified"));
+ file_name = optarg;
+ break;
- case_GETOPT_HELP_CHAR;
+ case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- noargs = false;
+ default:
+ /* Consider "drain" as an option rather than a setting,
+ to support: alias stty='stty -drain' etc. */
+ if (! STREQ (argv[argi + opti], "-drain")
+ && ! STREQ (argv[argi + opti], "drain"))
+ noargs = false;
- /* Skip the argument containing this unrecognized option;
- the 2nd pass will analyze it. */
- argi += opti;
+ /* Skip the argument containing this unrecognized option;
+ the 2nd pass will analyze it. */
+ argi += opti;
- /* Restart getopt_long from the first unskipped argument. */
- opti = 1;
- optind = 0;
+ /* Restart getopt_long from the first unskipped argument. */
+ opti = 1;
+ optind = 0;
- break;
- }
+ break;
+ }
/* Clear fully-parsed arguments, so they don't confuse the 2nd pass. */
while (opti < optind)
- argv[argi + opti++] = NULL;
+ argv[argi + opti++] = NULL;
}
/* Specifying both -a and -g gets an error. */
- if (verbose_output & recoverable_output)
+ if (verbose_output && recoverable_output)
error (EXIT_FAILURE, 0,
- _("the options for verbose and stty-readable output styles are\n"
- "mutually exclusive"));
+ _("the options for verbose and stty-readable output styles are\n"
+ "mutually exclusive"));
/* Specifying any other arguments with -a or -g gets an error. */
- if (!noargs & (verbose_output | recoverable_output))
+ if (!noargs && (verbose_output || recoverable_output))
error (EXIT_FAILURE, 0,
- _("when specifying an output style, modes may not be set"));
+ _("when specifying an output style, modes may not be set"));
/* FIXME: it'd be better not to open the file until we've verified
that all arguments are valid. Otherwise, we could end up doing
@@ -834,24 +1190,24 @@ main (int argc, char **argv)
int fdflags;
device_name = file_name;
if (fd_reopen (STDIN_FILENO, device_name, O_RDONLY | O_NONBLOCK, 0) < 0)
- error (EXIT_FAILURE, errno, "%s", device_name);
+ error (EXIT_FAILURE, errno, "%s", quotef (device_name));
if ((fdflags = fcntl (STDIN_FILENO, F_GETFL)) == -1
- || fcntl (STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
- error (EXIT_FAILURE, errno, _("%s: couldn't reset non-blocking mode"),
- device_name);
+ || fcntl (STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
+ error (EXIT_FAILURE, errno, _("%s: couldn't reset non-blocking mode"),
+ quotef (device_name));
}
else
device_name = _("standard input");
if (tcgetattr (STDIN_FILENO, &mode))
- error (EXIT_FAILURE, errno, "%s", device_name);
+ error (EXIT_FAILURE, errno, "%s", quotef (device_name));
- if (verbose_output | recoverable_output | noargs)
+ if (verbose_output || recoverable_output || noargs)
{
max_col = screen_columns ();
current_col = 0;
display_settings (output_type, &mode, device_name);
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
speed_was_set = false;
@@ -860,213 +1216,238 @@ main (int argc, char **argv)
{
char const *arg = argv[k];
bool match_found = false;
+ bool not_set_attr = false;
bool reversed = false;
int i;
if (! arg)
- continue;
+ continue;
if (arg[0] == '-')
- {
- ++arg;
- reversed = true;
- }
+ {
+ ++arg;
+ reversed = true;
+ }
+ if (STREQ (arg, "drain"))
+ {
+ tcsetattr_options = reversed ? TCSANOW : TCSADRAIN;
+ continue;
+ }
for (i = 0; mode_info[i].name != NULL; ++i)
- {
- if (STREQ (arg, mode_info[i].name))
- {
- match_found = set_mode (&mode_info[i], reversed, &mode);
- require_set_attr = true;
- break;
- }
- }
- if (!match_found & reversed)
- {
- error (0, 0, _("invalid argument %s"), quote (arg - 1));
- usage (EXIT_FAILURE);
- }
- if (!match_found)
- {
- for (i = 0; control_info[i].name != NULL; ++i)
- {
- if (STREQ (arg, control_info[i].name))
- {
- if (k == argc - 1)
- {
- error (0, 0, _("missing argument to %s"), quote (arg));
- usage (EXIT_FAILURE);
- }
- match_found = true;
- ++k;
- set_control_char (&control_info[i], argv[k], &mode);
- require_set_attr = true;
- break;
- }
- }
- }
+ {
+ if (STREQ (arg, mode_info[i].name))
+ {
+ if ((mode_info[i].flags & NO_SETATTR) == 0)
+ {
+ match_found = set_mode (&mode_info[i], reversed, &mode);
+ require_set_attr = true;
+ }
+ else
+ match_found = not_set_attr = true;
+ break;
+ }
+ }
+ if (!match_found && reversed)
+ {
+ error (0, 0, _("invalid argument %s"), quote (arg - 1));
+ usage (EXIT_FAILURE);
+ }
if (!match_found)
- {
- if (STREQ (arg, "ispeed"))
- {
- if (k == argc - 1)
- {
- error (0, 0, _("missing argument to %s"), quote (arg));
- usage (EXIT_FAILURE);
- }
- ++k;
- set_speed (input_speed, argv[k], &mode);
- speed_was_set = true;
- require_set_attr = true;
- }
- else if (STREQ (arg, "ospeed"))
- {
- if (k == argc - 1)
- {
- error (0, 0, _("missing argument to %s"), quote (arg));
- usage (EXIT_FAILURE);
- }
- ++k;
- set_speed (output_speed, argv[k], &mode);
- speed_was_set = true;
- require_set_attr = true;
- }
+ {
+ for (i = 0; control_info[i].name != NULL; ++i)
+ {
+ if (STREQ (arg, control_info[i].name))
+ {
+ if (k == argc - 1)
+ {
+ error (0, 0, _("missing argument to %s"), quote (arg));
+ usage (EXIT_FAILURE);
+ }
+ match_found = true;
+ ++k;
+ set_control_char (&control_info[i], argv[k], &mode);
+ require_set_attr = true;
+ break;
+ }
+ }
+ }
+ if (!match_found || not_set_attr)
+ {
+ if (STREQ (arg, "ispeed"))
+ {
+ if (k == argc - 1)
+ {
+ error (0, 0, _("missing argument to %s"), quote (arg));
+ usage (EXIT_FAILURE);
+ }
+ ++k;
+ set_speed (input_speed, argv[k], &mode);
+ speed_was_set = true;
+ require_set_attr = true;
+ }
+ else if (STREQ (arg, "ospeed"))
+ {
+ if (k == argc - 1)
+ {
+ error (0, 0, _("missing argument to %s"), quote (arg));
+ usage (EXIT_FAILURE);
+ }
+ ++k;
+ set_speed (output_speed, argv[k], &mode);
+ speed_was_set = true;
+ require_set_attr = true;
+ }
+#ifdef TIOCEXT
+ /* This is the BSD interface to "extproc".
+ Even though it's an lflag, an ioctl is used to set it. */
+ else if (STREQ (arg, "extproc"))
+ {
+ int val = ! reversed;
+
+ if (ioctl (STDIN_FILENO, TIOCEXT, &val) != 0)
+ {
+ error (EXIT_FAILURE, errno, _("%s: error setting %s"),
+ quotef_n (0, device_name), quote_n (1, arg));
+ }
+ }
+#endif
#ifdef TIOCGWINSZ
- else if (STREQ (arg, "rows"))
- {
- if (k == argc - 1)
- {
- error (0, 0, _("missing argument to %s"), quote (arg));
- usage (EXIT_FAILURE);
- }
- ++k;
- set_window_size (integer_arg (argv[k], INT_MAX), -1,
- device_name);
- }
- else if (STREQ (arg, "cols")
- || STREQ (arg, "columns"))
- {
- if (k == argc - 1)
- {
- error (0, 0, _("missing argument to %s"), quote (arg));
- usage (EXIT_FAILURE);
- }
- ++k;
- set_window_size (-1, integer_arg (argv[k], INT_MAX),
- device_name);
- }
- else if (STREQ (arg, "size"))
- {
- max_col = screen_columns ();
- current_col = 0;
- display_window_size (false, device_name);
- }
+ else if (STREQ (arg, "rows"))
+ {
+ if (k == argc - 1)
+ {
+ error (0, 0, _("missing argument to %s"), quote (arg));
+ usage (EXIT_FAILURE);
+ }
+ ++k;
+ set_window_size (integer_arg (argv[k], INT_MAX), -1,
+ device_name);
+ }
+ else if (STREQ (arg, "cols")
+ || STREQ (arg, "columns"))
+ {
+ if (k == argc - 1)
+ {
+ error (0, 0, _("missing argument to %s"), quote (arg));
+ usage (EXIT_FAILURE);
+ }
+ ++k;
+ set_window_size (-1, integer_arg (argv[k], INT_MAX),
+ device_name);
+ }
+ else if (STREQ (arg, "size"))
+ {
+ max_col = screen_columns ();
+ current_col = 0;
+ display_window_size (false, device_name);
+ }
#endif
#ifdef HAVE_C_LINE
- else if (STREQ (arg, "line"))
- {
- unsigned long int value;
- if (k == argc - 1)
- {
- error (0, 0, _("missing argument to %s"), quote (arg));
- usage (EXIT_FAILURE);
- }
- ++k;
- mode.c_line = value = integer_arg (argv[k], ULONG_MAX);
- if (mode.c_line != value)
- error (0, 0, _("invalid line discipline %s"), quote (argv[k]));
- require_set_attr = true;
- }
-#endif
- else if (STREQ (arg, "speed"))
- {
- max_col = screen_columns ();
- display_speed (&mode, false);
- }
- else if (string_to_baud (arg) != (speed_t) -1)
- {
- set_speed (both_speeds, arg, &mode);
- speed_was_set = true;
- require_set_attr = true;
- }
- else
- {
- if (! recover_mode (arg, &mode))
- {
- error (0, 0, _("invalid argument %s"), quote (arg));
- usage (EXIT_FAILURE);
- }
- require_set_attr = true;
- }
- }
+ else if (STREQ (arg, "line"))
+ {
+ unsigned long int value;
+ if (k == argc - 1)
+ {
+ error (0, 0, _("missing argument to %s"), quote (arg));
+ usage (EXIT_FAILURE);
+ }
+ ++k;
+ mode.c_line = value = integer_arg (argv[k], ULONG_MAX);
+ if (mode.c_line != value)
+ error (0, 0, _("invalid line discipline %s"), quote (argv[k]));
+ require_set_attr = true;
+ }
+#endif
+ else if (STREQ (arg, "speed"))
+ {
+ max_col = screen_columns ();
+ display_speed (&mode, false);
+ }
+ else if (string_to_baud (arg) != (speed_t) -1)
+ {
+ set_speed (both_speeds, arg, &mode);
+ speed_was_set = true;
+ require_set_attr = true;
+ }
+ else
+ {
+ if (! recover_mode (arg, &mode))
+ {
+ error (0, 0, _("invalid argument %s"), quote (arg));
+ usage (EXIT_FAILURE);
+ }
+ require_set_attr = true;
+ }
+ }
}
if (require_set_attr)
{
/* Initialize to all zeroes so there is no risk memcmp will report a
- spurious difference in an uninitialized portion of the structure. */
- struct termios new_mode = { 0, };
+ spurious difference in an uninitialized portion of the structure. */
+ static struct termios new_mode;
- if (tcsetattr (STDIN_FILENO, TCSADRAIN, &mode))
- error (EXIT_FAILURE, errno, "%s", device_name);
+ if (tcsetattr (STDIN_FILENO, tcsetattr_options, &mode))
+ error (EXIT_FAILURE, errno, "%s", quotef (device_name));
/* POSIX (according to Zlotnick's book) tcsetattr returns zero if
- it performs *any* of the requested operations. This means it
- can report `success' when it has actually failed to perform
- some proper subset of the requested operations. To detect
- this partial failure, get the current terminal attributes and
- compare them to the requested ones. */
+ it performs *any* of the requested operations. This means it
+ can report 'success' when it has actually failed to perform
+ some proper subset of the requested operations. To detect
+ this partial failure, get the current terminal attributes and
+ compare them to the requested ones. */
if (tcgetattr (STDIN_FILENO, &new_mode))
- error (EXIT_FAILURE, errno, "%s", device_name);
+ error (EXIT_FAILURE, errno, "%s", quotef (device_name));
/* Normally, one shouldn't use memcmp to compare structures that
- may have `holes' containing uninitialized data, but we have been
- careful to initialize the storage of these two variables to all
- zeroes. One might think it more efficient simply to compare the
- modified fields, but that would require enumerating those fields --
- and not all systems have the same fields in this structure. */
+ may have 'holes' containing uninitialized data, but we have been
+ careful to initialize the storage of these two variables to all
+ zeroes. One might think it more efficient simply to compare the
+ modified fields, but that would require enumerating those fields --
+ and not all systems have the same fields in this structure. */
if (memcmp (&mode, &new_mode, sizeof (mode)) != 0)
- {
+ {
#ifdef CIBAUD
- /* SunOS 4.1.3 (at least) has the problem that after this sequence,
- tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
- sometimes (m1 != m2). The only difference is in the four bits
- of the c_cflag field corresponding to the baud rate. To save
- Sun users a little confusion, don't report an error if this
- happens. But suppress the error only if we haven't tried to
- set the baud rate explicitly -- otherwise we'd never give an
- error for a true failure to set the baud rate. */
-
- new_mode.c_cflag &= (~CIBAUD);
- if (speed_was_set || memcmp (&mode, &new_mode, sizeof (mode)) != 0)
-#endif
- {
- error (EXIT_FAILURE, 0,
- _("%s: unable to perform all requested operations"),
- device_name);
+ /* SunOS 4.1.3 (at least) has the problem that after this sequence,
+ tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
+ sometimes (m1 != m2). The only difference is in the four bits
+ of the c_cflag field corresponding to the baud rate. To save
+ Sun users a little confusion, don't report an error if this
+ happens. But suppress the error only if we haven't tried to
+ set the baud rate explicitly -- otherwise we'd never give an
+ error for a true failure to set the baud rate. */
+
+ new_mode.c_cflag &= (~CIBAUD);
+ if (speed_was_set || memcmp (&mode, &new_mode, sizeof (mode)) != 0)
+#endif
+ {
+ error (EXIT_FAILURE, 0,
+ _("%s: unable to perform all requested operations"),
+ quotef (device_name));
#ifdef TESTING
- {
- size_t i;
- printf (_("new_mode: mode\n"));
- for (i = 0; i < sizeof (new_mode); i++)
- printf ("0x%02x: 0x%02x\n",
- *(((unsigned char *) &new_mode) + i),
- *(((unsigned char *) &mode) + i));
- }
-#endif
- }
- }
+ {
+ size_t i;
+ printf ("new_mode: mode\n");
+ for (i = 0; i < sizeof (new_mode); i++)
+ printf ("0x%02x: 0x%02x\n",
+ *(((unsigned char *) &new_mode) + i),
+ *(((unsigned char *) &mode) + i));
+ }
+#endif
+ }
+ }
}
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
/* Return false if not applied because not reversible; otherwise
return true. */
static bool
-set_mode (struct mode_info *info, bool reversed, struct termios *mode)
+set_mode (struct mode_info const *info, bool reversed, struct termios *mode)
{
tcflag_t *bitsp;
@@ -1079,189 +1460,189 @@ set_mode (struct mode_info *info, bool reversed, struct termios *mode)
{
/* Combination mode. */
if (STREQ (info->name, "evenp") || STREQ (info->name, "parity"))
- {
- if (reversed)
- mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
- else
- mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
- }
+ {
+ if (reversed)
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ else
+ mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
+ }
else if (STREQ (info->name, "oddp"))
- {
- if (reversed)
- mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
- else
- mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
- }
+ {
+ if (reversed)
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ else
+ mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
+ }
else if (STREQ (info->name, "nl"))
- {
- if (reversed)
- {
- mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
- mode->c_oflag = (mode->c_oflag
+ {
+ if (reversed)
+ {
+ mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
+ mode->c_oflag = (mode->c_oflag
#ifdef ONLCR
- | ONLCR
+ | ONLCR
#endif
- )
+ )
#ifdef OCRNL
- & ~OCRNL
+ & ~OCRNL
#endif
#ifdef ONLRET
- & ~ONLRET
+ & ~ONLRET
#endif
- ;
- }
- else
- {
- mode->c_iflag = mode->c_iflag & ~ICRNL;
+ ;
+ }
+ else
+ {
+ mode->c_iflag = mode->c_iflag & ~ICRNL;
#ifdef ONLCR
- mode->c_oflag = mode->c_oflag & ~ONLCR;
+ mode->c_oflag = mode->c_oflag & ~ONLCR;
#endif
- }
- }
+ }
+ }
else if (STREQ (info->name, "ek"))
- {
- mode->c_cc[VERASE] = CERASE;
- mode->c_cc[VKILL] = CKILL;
- }
+ {
+ mode->c_cc[VERASE] = CERASE;
+ mode->c_cc[VKILL] = CKILL;
+ }
else if (STREQ (info->name, "sane"))
- sane_mode (mode);
+ sane_mode (mode);
else if (STREQ (info->name, "cbreak"))
- {
- if (reversed)
- mode->c_lflag |= ICANON;
- else
- mode->c_lflag &= ~ICANON;
- }
+ {
+ if (reversed)
+ mode->c_lflag |= ICANON;
+ else
+ mode->c_lflag &= ~ICANON;
+ }
else if (STREQ (info->name, "pass8"))
- {
- if (reversed)
- {
- mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
- mode->c_iflag |= ISTRIP;
- }
- else
- {
- mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
- mode->c_iflag &= ~ISTRIP;
- }
- }
+ {
+ if (reversed)
+ {
+ mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
+ mode->c_iflag |= ISTRIP;
+ }
+ else
+ {
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ mode->c_iflag &= ~ISTRIP;
+ }
+ }
else if (STREQ (info->name, "litout"))
- {
- if (reversed)
- {
- mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
- mode->c_iflag |= ISTRIP;
- mode->c_oflag |= OPOST;
- }
- else
- {
- mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
- mode->c_iflag &= ~ISTRIP;
- mode->c_oflag &= ~OPOST;
- }
- }
+ {
+ if (reversed)
+ {
+ mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
+ mode->c_iflag |= ISTRIP;
+ mode->c_oflag |= OPOST;
+ }
+ else
+ {
+ mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+ mode->c_iflag &= ~ISTRIP;
+ mode->c_oflag &= ~OPOST;
+ }
+ }
else if (STREQ (info->name, "raw") || STREQ (info->name, "cooked"))
- {
- if ((info->name[0] == 'r' && reversed)
- || (info->name[0] == 'c' && !reversed))
- {
- /* Cooked mode. */
- mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
- mode->c_oflag |= OPOST;
- mode->c_lflag |= ISIG | ICANON;
+ {
+ if ((info->name[0] == 'r' && reversed)
+ || (info->name[0] == 'c' && !reversed))
+ {
+ /* Cooked mode. */
+ mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
+ mode->c_oflag |= OPOST;
+ mode->c_lflag |= ISIG | ICANON;
#if VMIN == VEOF
- mode->c_cc[VEOF] = CEOF;
+ mode->c_cc[VEOF] = CEOF;
#endif
#if VTIME == VEOL
- mode->c_cc[VEOL] = CEOL;
-#endif
- }
- else
- {
- /* Raw mode. */
- mode->c_iflag = 0;
- mode->c_oflag &= ~OPOST;
- mode->c_lflag &= ~(ISIG | ICANON
+ mode->c_cc[VEOL] = CEOL;
+#endif
+ }
+ else
+ {
+ /* Raw mode. */
+ mode->c_iflag = 0;
+ mode->c_oflag &= ~OPOST;
+ mode->c_lflag &= ~(ISIG | ICANON
#ifdef XCASE
- | XCASE
+ | XCASE
#endif
- );
- mode->c_cc[VMIN] = 1;
- mode->c_cc[VTIME] = 0;
- }
- }
+ );
+ mode->c_cc[VMIN] = 1;
+ mode->c_cc[VTIME] = 0;
+ }
+ }
#ifdef IXANY
else if (STREQ (info->name, "decctlq"))
- {
- if (reversed)
- mode->c_iflag |= IXANY;
- else
- mode->c_iflag &= ~IXANY;
- }
+ {
+ if (reversed)
+ mode->c_iflag |= IXANY;
+ else
+ mode->c_iflag &= ~IXANY;
+ }
#endif
#ifdef TABDLY
else if (STREQ (info->name, "tabs"))
- {
- if (reversed)
- mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
- else
- mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
- }
+ {
+ if (reversed)
+ mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
+ else
+ mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
+ }
#else
# ifdef OXTABS
else if (STREQ (info->name, "tabs"))
- {
- if (reversed)
- mode->c_oflag = mode->c_oflag | OXTABS;
- else
- mode->c_oflag = mode->c_oflag & ~OXTABS;
- }
+ {
+ if (reversed)
+ mode->c_oflag = mode->c_oflag | OXTABS;
+ else
+ mode->c_oflag = mode->c_oflag & ~OXTABS;
+ }
# endif
#endif
#if defined XCASE && defined IUCLC && defined OLCUC
else if (STREQ (info->name, "lcase")
- || STREQ (info->name, "LCASE"))
- {
- if (reversed)
- {
- mode->c_lflag &= ~XCASE;
- mode->c_iflag &= ~IUCLC;
- mode->c_oflag &= ~OLCUC;
- }
- else
- {
- mode->c_lflag |= XCASE;
- mode->c_iflag |= IUCLC;
- mode->c_oflag |= OLCUC;
- }
- }
+ || STREQ (info->name, "LCASE"))
+ {
+ if (reversed)
+ {
+ mode->c_lflag &= ~XCASE;
+ mode->c_iflag &= ~IUCLC;
+ mode->c_oflag &= ~OLCUC;
+ }
+ else
+ {
+ mode->c_lflag |= XCASE;
+ mode->c_iflag |= IUCLC;
+ mode->c_oflag |= OLCUC;
+ }
+ }
#endif
else if (STREQ (info->name, "crt"))
- mode->c_lflag |= ECHOE
+ mode->c_lflag |= ECHOE
#ifdef ECHOCTL
- | ECHOCTL
+ | ECHOCTL
#endif
#ifdef ECHOKE
- | ECHOKE
+ | ECHOKE
#endif
- ;
+ ;
else if (STREQ (info->name, "dec"))
- {
- mode->c_cc[VINTR] = 3; /* ^C */
- mode->c_cc[VERASE] = 127; /* DEL */
- mode->c_cc[VKILL] = 21; /* ^U */
- mode->c_lflag |= ECHOE
+ {
+ mode->c_cc[VINTR] = 3; /* ^C */
+ mode->c_cc[VERASE] = 127; /* DEL */
+ mode->c_cc[VKILL] = 21; /* ^U */
+ mode->c_lflag |= ECHOE
#ifdef ECHOCTL
- | ECHOCTL
+ | ECHOCTL
#endif
#ifdef ECHOKE
- | ECHOKE
+ | ECHOKE
#endif
- ;
+ ;
#ifdef IXANY
- mode->c_iflag &= ~IXANY;
+ mode->c_iflag &= ~IXANY;
#endif
- }
+ }
}
else if (reversed)
*bitsp = *bitsp & ~info->mask & ~info->bits;
@@ -1272,8 +1653,8 @@ set_mode (struct mode_info *info, bool reversed, struct termios *mode)
}
static void
-set_control_char (struct control_info *info, const char *arg,
- struct termios *mode)
+set_control_char (struct control_info const *info, const char *arg,
+ struct termios *mode)
{
unsigned long int value;
@@ -1286,9 +1667,9 @@ set_control_char (struct control_info *info, const char *arg,
else if (arg[0] == '^' && arg[1] != '\0') /* Ignore any trailing junk. */
{
if (arg[1] == '?')
- value = 127;
+ value = 127;
else
- value = to_uchar (arg[1]) & ~0140; /* Non-letters get weird results. */
+ value = to_uchar (arg[1]) & ~0140; /* Non-letters get weird results. */
}
else
value = integer_arg (arg, TYPE_MAXIMUM (cc_t));
@@ -1324,7 +1705,7 @@ set_window_size (int rows, int cols, char const *device_name)
if (get_win_size (STDIN_FILENO, &win))
{
if (errno != EINVAL)
- error (EXIT_FAILURE, errno, "%s", device_name);
+ error (EXIT_FAILURE, errno, "%s", quotef (device_name));
memset (&win, 0, sizeof (win));
}
@@ -1346,8 +1727,8 @@ set_window_size (int rows, int cols, char const *device_name)
it's almost certainly a "struct winsize" instead.
At any rate, the bug manifests itself when ws_row == 0; the symptom is
- that ws_row is set to ws_col, and ws_col is set to (ws_xpixel<<16) +
- ws_ypixel. Since GNU stty sets rows and columns separately, this bug
+ that ws_row is set to ws_col, and ws_col is set to (ws_xpixel<<16)
+ + ws_ypixel. Since GNU stty sets rows and columns separately, this bug
caused "stty rows 0 cols 0" to set rows to cols and cols to 0, while
"stty cols 0 rows 0" would do the right thing. On a little-endian
machine like the sun386i, the problem is the same, but for ws_col == 0.
@@ -1366,16 +1747,16 @@ set_window_size (int rows, int cols, char const *device_name)
win.ws_col = 1;
if (ioctl (STDIN_FILENO, TIOCSWINSZ, (char *) &win))
- error (EXIT_FAILURE, errno, "%s", device_name);
+ error (EXIT_FAILURE, errno, "%s", quotef (device_name));
if (ioctl (STDIN_FILENO, TIOCSSIZE, (char *) &ttysz))
- error (EXIT_FAILURE, errno, "%s", device_name);
+ error (EXIT_FAILURE, errno, "%s", quotef (device_name));
return;
}
# endif
if (ioctl (STDIN_FILENO, TIOCSWINSZ, (char *) &win))
- error (EXIT_FAILURE, errno, "%s", device_name);
+ error (EXIT_FAILURE, errno, "%s", quotef (device_name));
}
static void
@@ -1386,17 +1767,18 @@ display_window_size (bool fancy, char const *device_name)
if (get_win_size (STDIN_FILENO, &win))
{
if (errno != EINVAL)
- error (EXIT_FAILURE, errno, "%s", device_name);
+ error (EXIT_FAILURE, errno, "%s", quotef (device_name));
if (!fancy)
- error (EXIT_FAILURE, 0,
- _("%s: no size information for this device"), device_name);
+ error (EXIT_FAILURE, 0,
+ _("%s: no size information for this device"),
+ quotef (device_name));
}
else
{
wrapf (fancy ? "rows %d; columns %d;" : "%d %d\n",
- win.ws_row, win.ws_col);
+ win.ws_row, win.ws_col);
if (!fancy)
- current_col = 0;
+ current_col = 0;
}
}
#endif
@@ -1421,15 +1803,15 @@ screen_columns (void)
char *col_string = getenv ("COLUMNS");
long int n_columns;
if (!(col_string != NULL
- && xstrtol (col_string, NULL, 0, &n_columns, "") == LONGINT_OK
- && 0 < n_columns
- && n_columns <= INT_MAX))
+ && xstrtol (col_string, NULL, 0, &n_columns, "") == LONGINT_OK
+ && 0 < n_columns
+ && n_columns <= INT_MAX))
n_columns = 80;
return n_columns;
}
}
-static tcflag_t *
+static tcflag_t * _GL_ATTRIBUTE_PURE
mode_type_flag (enum mode_type type, struct termios *mode)
{
switch (type)
@@ -1456,7 +1838,7 @@ mode_type_flag (enum mode_type type, struct termios *mode)
static void
display_settings (enum output_type output_type, struct termios *mode,
- char const *device_name)
+ char const *device_name)
{
switch (output_type)
{
@@ -1494,29 +1876,35 @@ display_changed (struct termios *mode)
for (i = 0; !STREQ (control_info[i].name, "min"); ++i)
{
if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
- continue;
+ continue;
+
+#ifdef VFLUSHO
+ /* 'flush' is the deprecated equivalent of 'discard'. */
+ if (STREQ (control_info[i].name, "flush"))
+ continue;
+#endif
/* If swtch is the same as susp, don't print both. */
#if VSWTCH == VSUSP
if (STREQ (control_info[i].name, "swtch"))
- continue;
+ continue;
#endif
/* If eof uses the same slot as min, only print whichever applies. */
#if VEOF == VMIN
if ((mode->c_lflag & ICANON) == 0
- && (STREQ (control_info[i].name, "eof")
- || STREQ (control_info[i].name, "eol")))
- continue;
+ && (STREQ (control_info[i].name, "eof")
+ || STREQ (control_info[i].name, "eol")))
+ continue;
#endif
empty_line = false;
wrapf ("%s = %s;", control_info[i].name,
- visible (mode->c_cc[control_info[i].offset]));
+ visible (mode->c_cc[control_info[i].offset]));
}
if ((mode->c_lflag & ICANON) == 0)
{
wrapf ("min = %lu; time = %lu;\n",
- (unsigned long int) mode->c_cc[VMIN],
- (unsigned long int) mode->c_cc[VTIME]);
+ (unsigned long int) mode->c_cc[VMIN],
+ (unsigned long int) mode->c_cc[VTIME]);
}
else if (!empty_line)
putchar ('\n');
@@ -1526,33 +1914,39 @@ display_changed (struct termios *mode)
for (i = 0; mode_info[i].name != NULL; ++i)
{
if (mode_info[i].flags & OMIT)
- continue;
+ continue;
if (mode_info[i].type != prev_type)
- {
- if (!empty_line)
- {
- putchar ('\n');
- current_col = 0;
- empty_line = true;
- }
- prev_type = mode_info[i].type;
- }
+ {
+ if (!empty_line)
+ {
+ putchar ('\n');
+ current_col = 0;
+ empty_line = true;
+ }
+ prev_type = mode_info[i].type;
+ }
bitsp = mode_type_flag (mode_info[i].type, mode);
mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
+
+ /* bitsp would be NULL only for "combination" modes, yet those
+ are filtered out above via the OMIT flag. Tell static analysis
+ tools that it's ok to dereference bitsp here. */
+ assert (bitsp);
+
if ((*bitsp & mask) == mode_info[i].bits)
- {
- if (mode_info[i].flags & SANE_UNSET)
- {
- wrapf ("%s", mode_info[i].name);
- empty_line = false;
- }
- }
+ {
+ if (mode_info[i].flags & SANE_UNSET)
+ {
+ wrapf ("%s", mode_info[i].name);
+ empty_line = false;
+ }
+ }
else if ((mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
- {
- wrapf ("-%s", mode_info[i].name);
- empty_line = false;
- }
+ {
+ wrapf ("-%s", mode_info[i].name);
+ empty_line = false;
+ }
}
if (!empty_line)
putchar ('\n');
@@ -1579,27 +1973,32 @@ display_all (struct termios *mode, char const *device_name)
for (i = 0; ! STREQ (control_info[i].name, "min"); ++i)
{
+#ifdef VFLUSHO
+ /* 'flush' is the deprecated equivalent of 'discard'. */
+ if (STREQ (control_info[i].name, "flush"))
+ continue;
+#endif
/* If swtch is the same as susp, don't print both. */
#if VSWTCH == VSUSP
if (STREQ (control_info[i].name, "swtch"))
- continue;
+ continue;
#endif
/* If eof uses the same slot as min, only print whichever applies. */
#if VEOF == VMIN
if ((mode->c_lflag & ICANON) == 0
- && (STREQ (control_info[i].name, "eof")
- || STREQ (control_info[i].name, "eol")))
- continue;
+ && (STREQ (control_info[i].name, "eof")
+ || STREQ (control_info[i].name, "eol")))
+ continue;
#endif
wrapf ("%s = %s;", control_info[i].name,
- visible (mode->c_cc[control_info[i].offset]));
+ visible (mode->c_cc[control_info[i].offset]));
}
#if VEOF == VMIN
if ((mode->c_lflag & ICANON) == 0)
#endif
wrapf ("min = %lu; time = %lu;",
- (unsigned long int) mode->c_cc[VMIN],
- (unsigned long int) mode->c_cc[VTIME]);
+ (unsigned long int) mode->c_cc[VMIN],
+ (unsigned long int) mode->c_cc[VTIME]);
if (current_col != 0)
putchar ('\n');
current_col = 0;
@@ -1607,20 +2006,21 @@ display_all (struct termios *mode, char const *device_name)
for (i = 0; mode_info[i].name != NULL; ++i)
{
if (mode_info[i].flags & OMIT)
- continue;
+ continue;
if (mode_info[i].type != prev_type)
- {
- putchar ('\n');
- current_col = 0;
- prev_type = mode_info[i].type;
- }
+ {
+ putchar ('\n');
+ current_col = 0;
+ prev_type = mode_info[i].type;
+ }
bitsp = mode_type_flag (mode_info[i].type, mode);
mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
+ assert (bitsp); /* See the identical assertion and comment above. */
if ((*bitsp & mask) == mode_info[i].bits)
- wrapf ("%s", mode_info[i].name);
+ wrapf ("%s", mode_info[i].name);
else if (mode_info[i].flags & REV)
- wrapf ("-%s", mode_info[i].name);
+ wrapf ("-%s", mode_info[i].name);
}
putchar ('\n');
current_col = 0;
@@ -1631,11 +2031,11 @@ display_speed (struct termios *mode, bool fancy)
{
if (cfgetispeed (mode) == 0 || cfgetispeed (mode) == cfgetospeed (mode))
wrapf (fancy ? "speed %lu baud;" : "%lu\n",
- baud_to_value (cfgetospeed (mode)));
+ baud_to_value (cfgetospeed (mode)));
else
wrapf (fancy ? "ispeed %lu baud; ospeed %lu baud;" : "%lu %lu\n",
- baud_to_value (cfgetispeed (mode)),
- baud_to_value (cfgetospeed (mode)));
+ baud_to_value (cfgetispeed (mode)),
+ baud_to_value (cfgetospeed (mode)));
if (!fancy)
current_col = 0;
}
@@ -1646,51 +2046,70 @@ display_recoverable (struct termios *mode)
size_t i;
printf ("%lx:%lx:%lx:%lx",
- (unsigned long int) mode->c_iflag,
- (unsigned long int) mode->c_oflag,
- (unsigned long int) mode->c_cflag,
- (unsigned long int) mode->c_lflag);
+ (unsigned long int) mode->c_iflag,
+ (unsigned long int) mode->c_oflag,
+ (unsigned long int) mode->c_cflag,
+ (unsigned long int) mode->c_lflag);
for (i = 0; i < NCCS; ++i)
printf (":%lx", (unsigned long int) mode->c_cc[i]);
putchar ('\n');
}
+/* NOTE: identical to below, modulo use of tcflag_t */
+static int
+strtoul_tcflag_t (char const *s, int base, char **p, tcflag_t *result,
+ char delim)
+{
+ unsigned long ul;
+ errno = 0;
+ ul = strtoul (s, p, base);
+ if (errno || **p != delim || *p == s || (tcflag_t) ul != ul)
+ return -1;
+ *result = ul;
+ return 0;
+}
+
+/* NOTE: identical to above, modulo use of cc_t */
+static int
+strtoul_cc_t (char const *s, int base, char **p, cc_t *result, char delim)
+{
+ unsigned long ul;
+ errno = 0;
+ ul = strtoul (s, p, base);
+ if (errno || **p != delim || *p == s || (cc_t) ul != ul)
+ return -1;
+ *result = ul;
+ return 0;
+}
+
+/* Parse the output of display_recoverable.
+ Return false if any part of it is invalid. */
static bool
recover_mode (char const *arg, struct termios *mode)
{
+ tcflag_t flag[4];
+ char const *s = arg;
size_t i;
- int n;
- unsigned long int chr;
- unsigned long int iflag, oflag, cflag, lflag;
-
- /* Scan into temporaries since it is too much trouble to figure out
- the right format for `tcflag_t'. */
- if (sscanf (arg, "%lx:%lx:%lx:%lx%n",
- &iflag, &oflag, &cflag, &lflag, &n) != 4)
- return false;
- mode->c_iflag = iflag;
- mode->c_oflag = oflag;
- mode->c_cflag = cflag;
- mode->c_lflag = lflag;
- if (mode->c_iflag != iflag
- || mode->c_oflag != oflag
- || mode->c_cflag != cflag
- || mode->c_lflag != lflag)
- return false;
- arg += n;
- for (i = 0; i < NCCS; ++i)
+ for (i = 0; i < 4; i++)
{
- if (sscanf (arg, ":%lx%n", &chr, &n) != 1)
- return false;
- mode->c_cc[i] = chr;
- if (mode->c_cc[i] != chr)
- return false;
- arg += n;
+ char *p;
+ if (strtoul_tcflag_t (s, 16, &p, flag + i, ':') != 0)
+ return false;
+ s = p + 1;
}
+ mode->c_iflag = flag[0];
+ mode->c_oflag = flag[1];
+ mode->c_cflag = flag[2];
+ mode->c_lflag = flag[3];
- /* Fail if there are too many fields. */
- if (*arg != '\0')
- return false;
+ for (i = 0; i < NCCS; ++i)
+ {
+ char *p;
+ char delim = i < NCCS - 1 ? ':' : '\0';
+ if (strtoul_cc_t (s, 16, &p, mode->c_cc + i, delim) != 0)
+ return false;
+ s = p + 1;
+ }
return true;
}
@@ -1702,7 +2121,7 @@ struct speed_map
unsigned long int value; /* Numeric value. */
};
-static struct speed_map speeds[] =
+static struct speed_map const speeds[] =
{
{"0", B0, 0},
{"50", B50, 50},
@@ -1771,7 +2190,7 @@ static struct speed_map speeds[] =
{NULL, 0, 0}
};
-static speed_t
+static speed_t _GL_ATTRIBUTE_PURE
string_to_baud (const char *arg)
{
int i;
@@ -1782,7 +2201,7 @@ string_to_baud (const char *arg)
return (speed_t) -1;
}
-static unsigned long int
+static unsigned long int _GL_ATTRIBUTE_PURE
baud_to_value (speed_t speed)
{
int i;
@@ -1803,28 +2222,31 @@ sane_mode (struct termios *mode)
{
#if VMIN == VEOF
if (STREQ (control_info[i].name, "min"))
- break;
+ break;
#endif
mode->c_cc[control_info[i].offset] = control_info[i].saneval;
}
for (i = 0; mode_info[i].name != NULL; ++i)
{
+ if (mode_info[i].flags & NO_SETATTR)
+ continue;
+
if (mode_info[i].flags & SANE_SET)
- {
- bitsp = mode_type_flag (mode_info[i].type, mode);
- *bitsp = (*bitsp & ~mode_info[i].mask) | mode_info[i].bits;
- }
+ {
+ bitsp = mode_type_flag (mode_info[i].type, mode);
+ *bitsp = (*bitsp & ~mode_info[i].mask) | mode_info[i].bits;
+ }
else if (mode_info[i].flags & SANE_UNSET)
- {
- bitsp = mode_type_flag (mode_info[i].type, mode);
- *bitsp = *bitsp & ~mode_info[i].mask & ~mode_info[i].bits;
- }
+ {
+ bitsp = mode_type_flag (mode_info[i].type, mode);
+ *bitsp = *bitsp & ~mode_info[i].mask & ~mode_info[i].bits;
+ }
}
}
/* Return a string that is the printable representation of character CH. */
-/* Adapted from `cat' by Torbjorn Granlund. */
+/* Adapted from 'cat' by Torbjorn Granlund. */
static const char *
visible (cc_t ch)
@@ -1838,32 +2260,32 @@ visible (cc_t ch)
if (ch >= 32)
{
if (ch < 127)
- *bpout++ = ch;
+ *bpout++ = ch;
else if (ch == 127)
- {
- *bpout++ = '^';
- *bpout++ = '?';
- }
+ {
+ *bpout++ = '^';
+ *bpout++ = '?';
+ }
else
- {
- *bpout++ = 'M',
- *bpout++ = '-';
- if (ch >= 128 + 32)
- {
- if (ch < 128 + 127)
- *bpout++ = ch - 128;
- else
- {
- *bpout++ = '^';
- *bpout++ = '?';
- }
- }
- else
- {
- *bpout++ = '^';
- *bpout++ = ch - 128 + 64;
- }
- }
+ {
+ *bpout++ = 'M';
+ *bpout++ = '-';
+ if (ch >= 128 + 32)
+ {
+ if (ch < 128 + 127)
+ *bpout++ = ch - 128;
+ else
+ {
+ *bpout++ = '^';
+ *bpout++ = '?';
+ }
+ }
+ else
+ {
+ *bpout++ = '^';
+ *bpout++ = ch - 128 + 64;
+ }
+ }
}
else
{
@@ -1881,12 +2303,5 @@ visible (cc_t ch)
static unsigned long int
integer_arg (const char *s, unsigned long int maxval)
{
- unsigned long int value;
- if (xstrtoul (s, NULL, 0, &value, "bB") != LONGINT_OK
- || maxval < value)
- {
- error (0, 0, _("invalid integer argument %s"), quote (s));
- usage (EXIT_FAILURE);
- }
- return value;
+ return xnumtoumax (s, 0, 0, maxval, "bB", _("invalid integer argument"), 0);
}
diff --git a/src/su.c b/src/su.c
deleted file mode 100644
index 70828b8..0000000
--- a/src/su.c
+++ /dev/null
@@ -1,526 +0,0 @@
-/* su for GNU. Run a shell with substitute user and group IDs.
- Copyright (C) 1992-2006 Free Software Foundation, Inc.
-
- This 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 2, 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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
-
-/* Run a shell with the real and effective UID and GID and groups
- of USER, default `root'.
-
- The shell run is taken from USER's password entry, /bin/sh if
- none is specified there. If the account has a password, su
- prompts for a password unless run by a user with real UID 0.
-
- Does not change the current directory.
- Sets `HOME' and `SHELL' from the password entry for USER, and if
- USER is not root, sets `USER' and `LOGNAME' to USER.
- The subshell is not a login shell.
-
- If one or more ARGs are given, they are passed as additional
- arguments to the subshell.
-
- Does not handle /bin/sh or other shells specially
- (setting argv[0] to "-su", passing -c only to certain shells, etc.).
- I don't see the point in doing that, and it's ugly.
-
- This program intentionally does not support a "wheel group" that
- restricts who can su to UID 0 accounts. RMS considers that to
- be fascist.
-
- Compile-time options:
- -DSYSLOG_SUCCESS Log successful su's (by default, to root) with syslog.
- -DSYSLOG_FAILURE Log failed su's (by default, to root) with syslog.
-
- -DSYSLOG_NON_ROOT Log all su's, not just those to root (UID 0).
- Never logs attempted su's to nonexistent accounts.
-
- Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
-
-#include <config.h>
-#include <stdio.h>
-#include <getopt.h>
-#include <sys/types.h>
-#include <pwd.h>
-#include <grp.h>
-
-/* Hide any system prototype for getusershell.
- This is necessary because some Cray systems have a conflicting
- prototype (returning `int') in <unistd.h>. */
-#define getusershell _getusershell_sys_proto_
-
-#include "system.h"
-#include "getpass.h"
-
-#undef getusershell
-
-#if HAVE_SYSLOG_H && HAVE_SYSLOG
-# include <syslog.h>
-#else
-# undef SYSLOG_SUCCESS
-# undef SYSLOG_FAILURE
-# undef SYSLOG_NON_ROOT
-#endif
-
-#if HAVE_SYS_PARAM_H
-# include <sys/param.h>
-#endif
-
-#ifndef HAVE_ENDGRENT
-# define endgrent() ((void) 0)
-#endif
-
-#ifndef HAVE_ENDPWENT
-# define endpwent() ((void) 0)
-#endif
-
-#if HAVE_SHADOW_H
-# include <shadow.h>
-#endif
-
-#include "error.h"
-
-/* The official name of this program (e.g., no `g' prefix). */
-#define PROGRAM_NAME "su"
-
-#define AUTHORS "David MacKenzie"
-
-#if HAVE_PATHS_H
-# include <paths.h>
-#endif
-
-/* The default PATH for simulated logins to non-superuser accounts. */
-#ifdef _PATH_DEFPATH
-# define DEFAULT_LOGIN_PATH _PATH_DEFPATH
-#else
-# define DEFAULT_LOGIN_PATH ":/usr/ucb:/bin:/usr/bin"
-#endif
-
-/* The default PATH for simulated logins to superuser accounts. */
-#ifdef _PATH_DEFPATH_ROOT
-# define DEFAULT_ROOT_LOGIN_PATH _PATH_DEFPATH_ROOT
-#else
-# define DEFAULT_ROOT_LOGIN_PATH "/usr/ucb:/bin:/usr/bin:/etc"
-#endif
-
-/* The shell to run if none is given in the user's passwd entry. */
-#define DEFAULT_SHELL "/bin/sh"
-
-/* The user to become if none is specified. */
-#define DEFAULT_USER "root"
-
-char *crypt ();
-char *getusershell ();
-void endusershell ();
-void setusershell ();
-
-extern char **environ;
-
-static void run_shell (char const *, char const *, char **, size_t)
- ATTRIBUTE_NORETURN;
-
-/* The name this program was run with. */
-char *program_name;
-
-/* If true, pass the `-f' option to the subshell. */
-static bool fast_startup;
-
-/* If true, simulate a login instead of just starting a shell. */
-static bool simulate_login;
-
-/* If true, change some environment vars to indicate the user su'd to. */
-static bool change_environment;
-
-static struct option const longopts[] =
-{
- {"command", required_argument, NULL, 'c'},
- {"fast", no_argument, NULL, 'f'},
- {"login", no_argument, NULL, 'l'},
- {"preserve-environment", no_argument, NULL, 'p'},
- {"shell", required_argument, NULL, 's'},
- {GETOPT_HELP_OPTION_DECL},
- {GETOPT_VERSION_OPTION_DECL},
- {NULL, 0, NULL, 0}
-};
-
-/* Add NAME=VAL to the environment, checking for out of memory errors. */
-
-static void
-xsetenv (char const *name, char const *val)
-{
- size_t namelen = strlen (name);
- size_t vallen = strlen (val);
- char *string = xmalloc (namelen + 1 + vallen + 1);
- strcpy (string, name);
- string[namelen] = '=';
- strcpy (string + namelen + 1, val);
- if (putenv (string) != 0)
- xalloc_die ();
-}
-
-#if defined SYSLOG_SUCCESS || defined SYSLOG_FAILURE
-/* Log the fact that someone has run su to the user given by PW;
- if SUCCESSFUL is true, they gave the correct password, etc. */
-
-static void
-log_su (struct passwd const *pw, bool successful)
-{
- const char *new_user, *old_user, *tty;
-
-# ifndef SYSLOG_NON_ROOT
- if (pw->pw_uid)
- return;
-# endif
- new_user = pw->pw_name;
- /* The utmp entry (via getlogin) is probably the best way to identify
- the user, especially if someone su's from a su-shell. */
- old_user = getlogin ();
- if (!old_user)
- {
- /* getlogin can fail -- usually due to lack of utmp entry.
- Resort to getpwuid. */
- struct passwd *pwd = getpwuid (getuid ());
- old_user = (pwd ? pwd->pw_name : "");
- }
- tty = ttyname (STDERR_FILENO);
- if (!tty)
- tty = "none";
- /* 4.2BSD openlog doesn't have the third parameter. */
- openlog (last_component (program_name), 0
-# ifdef LOG_AUTH
- , LOG_AUTH
-# endif
- );
- syslog (LOG_NOTICE,
-# ifdef SYSLOG_NON_ROOT
- "%s(to %s) %s on %s",
-# else
- "%s%s on %s",
-# endif
- successful ? "" : "FAILED SU ",
-# ifdef SYSLOG_NON_ROOT
- new_user,
-# endif
- old_user, tty);
- closelog ();
-}
-#endif
-
-/* Ask the user for a password.
- Return true if the user gives the correct password for entry PW,
- false if not. Return true without asking for a password if run by UID 0
- or if PW has an empty password. */
-
-static bool
-correct_password (const struct passwd *pw)
-{
- char *unencrypted, *encrypted, *correct;
-#if HAVE_GETSPNAM && HAVE_STRUCT_SPWD_SP_PWDP
- /* Shadow passwd stuff for SVR3 and maybe other systems. */
- struct spwd *sp = getspnam (pw->pw_name);
-
- endspent ();
- if (sp)
- correct = sp->sp_pwdp;
- else
-#endif
- correct = pw->pw_passwd;
-
- if (getuid () == 0 || !correct || correct[0] == '\0')
- return true;
-
- unencrypted = getpass (_("Password:"));
- if (!unencrypted)
- {
- error (0, 0, _("getpass: cannot open /dev/tty"));
- return false;
- }
- encrypted = crypt (unencrypted, correct);
- memset (unencrypted, 0, strlen (unencrypted));
- return STREQ (encrypted, correct);
-}
-
-/* Update `environ' for the new shell based on PW, with SHELL being
- the value for the SHELL environment variable. */
-
-static void
-modify_environment (const struct passwd *pw, const char *shell)
-{
- if (simulate_login)
- {
- /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH.
- Unset all other environment variables. */
- char const *term = getenv ("TERM");
- if (term)
- term = xstrdup (term);
- environ = xmalloc ((6 + !!term) * sizeof (char *));
- environ[0] = NULL;
- if (term)
- xsetenv ("TERM", term);
- xsetenv ("HOME", pw->pw_dir);
- xsetenv ("SHELL", shell);
- xsetenv ("USER", pw->pw_name);
- xsetenv ("LOGNAME", pw->pw_name);
- xsetenv ("PATH", (pw->pw_uid
- ? DEFAULT_LOGIN_PATH
- : DEFAULT_ROOT_LOGIN_PATH));
- }
- else
- {
- /* Set HOME, SHELL, and if not becoming a super-user,
- USER and LOGNAME. */
- if (change_environment)
- {
- xsetenv ("HOME", pw->pw_dir);
- xsetenv ("SHELL", shell);
- if (pw->pw_uid)
- {
- xsetenv ("USER", pw->pw_name);
- xsetenv ("LOGNAME", pw->pw_name);
- }
- }
- }
-}
-
-/* Become the user and group(s) specified by PW. */
-
-static void
-change_identity (const struct passwd *pw)
-{
-#ifdef HAVE_INITGROUPS
- errno = 0;
- if (initgroups (pw->pw_name, pw->pw_gid) == -1)
- error (EXIT_FAIL, errno, _("cannot set groups"));
- endgrent ();
-#endif
- if (setgid (pw->pw_gid))
- error (EXIT_FAIL, errno, _("cannot set group id"));
- if (setuid (pw->pw_uid))
- error (EXIT_FAIL, errno, _("cannot set user id"));
-}
-
-/* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
- If COMMAND is nonzero, pass it to the shell with the -c option.
- Pass ADDITIONAL_ARGS to the shell as more arguments; there
- are N_ADDITIONAL_ARGS extra arguments. */
-
-static void
-run_shell (char const *shell, char const *command, char **additional_args,
- size_t n_additional_args)
-{
- size_t n_args = 1 + fast_startup + 2 * !!command + n_additional_args + 1;
- char const **args = xnmalloc (n_args, sizeof *args);
- size_t argno = 1;
-
- if (simulate_login)
- {
- char *arg0;
- char *shell_basename;
-
- shell_basename = last_component (shell);
- arg0 = xmalloc (strlen (shell_basename) + 2);
- arg0[0] = '-';
- strcpy (arg0 + 1, shell_basename);
- args[0] = arg0;
- }
- else
- args[0] = last_component (shell);
- if (fast_startup)
- args[argno++] = "-f";
- if (command)
- {
- args[argno++] = "-c";
- args[argno++] = command;
- }
- memcpy (args + argno, additional_args, n_additional_args * sizeof *args);
- args[argno + n_additional_args] = NULL;
- execv (shell, (char **) args);
-
- {
- int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
- error (0, errno, "%s", shell);
- exit (exit_status);
- }
-}
-
-/* Return true if SHELL is a restricted shell (one not returned by
- getusershell), else false, meaning it is a standard shell. */
-
-static bool
-restricted_shell (const char *shell)
-{
- char *line;
-
- setusershell ();
- while ((line = getusershell ()) != NULL)
- {
- if (*line != '#' && STREQ (line, shell))
- {
- endusershell ();
- return false;
- }
- }
- endusershell ();
- return true;
-}
-
-void
-usage (int status)
-{
- if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
- else
- {
- printf (_("Usage: %s [OPTION]... [-] [USER [ARG]...]\n"), program_name);
- fputs (_("\
-Change the effective user id and group id to that of USER.\n\
-\n\
- -, -l, --login make the shell a login shell\n\
- -c, --command=COMMAND pass a single COMMAND to the shell with -c\n\
- -f, --fast pass -f to the shell (for csh or tcsh)\n\
- -m, --preserve-environment do not reset environment variables\n\
- -p same as -m\n\
- -s, --shell=SHELL run SHELL if /etc/shells allows it\n\
-"), stdout);
- fputs (HELP_OPTION_DESCRIPTION, stdout);
- fputs (VERSION_OPTION_DESCRIPTION, stdout);
- fputs (_("\
-\n\
-A mere - implies -l. If USER not given, assume root.\n\
-"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
- }
- exit (status);
-}
-
-int
-main (int argc, char **argv)
-{
- int optc;
- const char *new_user = DEFAULT_USER;
- char *command = NULL;
- char *shell = NULL;
- struct passwd *pw;
- struct passwd pw_copy;
-
- initialize_main (&argc, &argv);
- program_name = argv[0];
- setlocale (LC_ALL, "");
- bindtextdomain (PACKAGE, LOCALEDIR);
- textdomain (PACKAGE);
-
- initialize_exit_failure (EXIT_FAIL);
- atexit (close_stdout);
-
- fast_startup = false;
- simulate_login = false;
- change_environment = true;
-
- while ((optc = getopt_long (argc, argv, "c:flmps:", longopts, NULL)) != -1)
- {
- switch (optc)
- {
- case 'c':
- command = optarg;
- break;
-
- case 'f':
- fast_startup = true;
- break;
-
- case 'l':
- simulate_login = true;
- break;
-
- case 'm':
- case 'p':
- change_environment = false;
- break;
-
- case 's':
- shell = optarg;
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (EXIT_FAIL);
- }
- }
-
- if (optind < argc && STREQ (argv[optind], "-"))
- {
- simulate_login = true;
- ++optind;
- }
- if (optind < argc)
- new_user = argv[optind++];
-
- pw = getpwnam (new_user);
- if (! (pw && pw->pw_name && pw->pw_name[0] && pw->pw_dir && pw->pw_dir[0]
- && pw->pw_passwd))
- error (EXIT_FAIL, 0, _("user %s does not exist"), new_user);
-
- /* Make a copy of the password information and point pw at the local
- copy instead. Otherwise, some systems (e.g. Linux) would clobber
- the static data through the getlogin call from log_su.
- Also, make sure pw->pw_shell is a nonempty string.
- It may be NULL when NEW_USER is a username that is retrieved via NIS (YP),
- but that doesn't have a default shell listed. */
- pw_copy = *pw;
- pw = &pw_copy;
- pw->pw_name = xstrdup (pw->pw_name);
- pw->pw_passwd = xstrdup (pw->pw_passwd);
- pw->pw_dir = xstrdup (pw->pw_dir);
- pw->pw_shell = xstrdup (pw->pw_shell && pw->pw_shell[0]
- ? pw->pw_shell
- : DEFAULT_SHELL);
- endpwent ();
-
- if (!correct_password (pw))
- {
-#ifdef SYSLOG_FAILURE
- log_su (pw, false);
-#endif
- error (EXIT_FAIL, 0, _("incorrect password"));
- }
-#ifdef SYSLOG_SUCCESS
- else
- {
- log_su (pw, true);
- }
-#endif
-
- if (!shell && !change_environment)
- shell = getenv ("SHELL");
- if (shell && getuid () != 0 && restricted_shell (pw->pw_shell))
- {
- /* The user being su'd to has a nonstandard shell, and so is
- probably a uucp account or has restricted access. Don't
- compromise the account by allowing access with a standard
- shell. */
- error (0, 0, _("using restricted shell %s"), pw->pw_shell);
- shell = NULL;
- }
- shell = xstrdup (shell ? shell : pw->pw_shell);
- modify_environment (pw, shell);
-
- change_identity (pw);
- if (simulate_login && chdir (pw->pw_dir) != 0)
- error (0, errno, _("warning: cannot change directory to %s"), pw->pw_dir);
-
- run_shell (shell, command, argv + optind, MAX (0, argc - optind));
-}
diff --git a/src/sum.c b/src/sum.c
index 92e4126..04e3931 100644
--- a/src/sum.c
+++ b/src/sum.c
@@ -1,10 +1,10 @@
/* sum -- checksum and count the blocks in a file
- Copyright (C) 86, 89, 91, 1995-2002, 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 1986-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Like BSD sum or SysV sum -r, except like SysV sum if -s option is given. */
@@ -26,16 +25,17 @@
#include <getopt.h>
#include "system.h"
#include "error.h"
+#include "fadvise.h"
#include "human.h"
#include "safe-read.h"
+#include "xfreopen.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "sum"
-#define AUTHORS "Kayvan Aghaiepour", "David MacKenzie"
-
-/* The name this program was run with. */
-char *program_name;
+#define AUTHORS \
+ proper_name ("Kayvan Aghaiepour"), \
+ proper_name ("David MacKenzie")
/* True if any of the files read were the standard input. */
static bool have_read_stdin;
@@ -52,27 +52,27 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
- program_name);
+ program_name);
fputs (_("\
Print checksum and block counts for each FILE.\n\
+"), stdout);
+
+ emit_stdin_note ();
+
+ fputs (_("\
\n\
- -r defeat -s, use BSD sum algorithm, use 1K blocks\n\
+ -r use BSD sum algorithm, use 1K blocks\n\
-s, --sysv use System V sum algorithm, use 512 bytes blocks\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- fputs (_("\
-\n\
-With no FILE, or when FILE is -, read standard input.\n\
-"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -98,18 +98,20 @@ bsd_sum_file (const char *file, int print_name)
fp = stdin;
have_read_stdin = true;
if (O_BINARY && ! isatty (STDIN_FILENO))
- freopen (NULL, "rb", stdin);
+ xfreopen (NULL, "rb", stdin);
}
else
{
fp = fopen (file, (O_BINARY ? "rb" : "r"));
if (fp == NULL)
- {
- error (0, errno, "%s", file);
- return false;
- }
+ {
+ error (0, errno, "%s", quotef (file));
+ return false;
+ }
}
+ fadvise (fp, FADVISE_SEQUENTIAL);
+
while ((ch = getc (fp)) != EOF)
{
total_bytes++;
@@ -120,20 +122,20 @@ bsd_sum_file (const char *file, int print_name)
if (ferror (fp))
{
- error (0, errno, "%s", file);
+ error (0, errno, "%s", quotef (file));
if (!is_stdin)
- fclose (fp);
+ fclose (fp);
return false;
}
if (!is_stdin && fclose (fp) != 0)
{
- error (0, errno, "%s", file);
+ error (0, errno, "%s", quotef (file));
return false;
}
printf ("%05d %5s", checksum,
- human_readable (total_bytes, hbuf, human_ceiling, 1, 1024));
+ human_readable (total_bytes, hbuf, human_ceiling, 1, 1024));
if (print_name > 1)
printf (" %s", file);
putchar ('\n');
@@ -166,16 +168,16 @@ sysv_sum_file (const char *file, int print_name)
fd = STDIN_FILENO;
have_read_stdin = true;
if (O_BINARY && ! isatty (STDIN_FILENO))
- freopen (NULL, "rb", stdin);
+ xfreopen (NULL, "rb", stdin);
}
else
{
fd = open (file, O_RDONLY | O_BINARY);
if (fd == -1)
- {
- error (0, errno, "%s", file);
- return false;
- }
+ {
+ error (0, errno, "%s", quotef (file));
+ return false;
+ }
}
while (1)
@@ -184,24 +186,24 @@ sysv_sum_file (const char *file, int print_name)
size_t bytes_read = safe_read (fd, buf, sizeof buf);
if (bytes_read == 0)
- break;
+ break;
if (bytes_read == SAFE_READ_ERROR)
- {
- error (0, errno, "%s", file);
- if (!is_stdin)
- close (fd);
- return false;
- }
+ {
+ error (0, errno, "%s", quotef (file));
+ if (!is_stdin)
+ close (fd);
+ return false;
+ }
for (i = 0; i < bytes_read; i++)
- s += buf[i];
+ s += buf[i];
total_bytes += bytes_read;
}
if (!is_stdin && close (fd) != 0)
{
- error (0, errno, "%s", file);
+ error (0, errno, "%s", quotef (file));
return false;
}
@@ -209,7 +211,7 @@ sysv_sum_file (const char *file, int print_name)
checksum = (r & 0xffff) + (r >> 16);
printf ("%d %s", checksum,
- human_readable (total_bytes, hbuf, human_ceiling, 1, 512));
+ human_readable (total_bytes, hbuf, human_ceiling, 1, 512));
if (print_name)
printf (" %s", file);
putchar ('\n');
@@ -226,34 +228,38 @@ main (int argc, char **argv)
bool (*sum_func) (const char *, int) = bsd_sum_file;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ 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);
+
have_read_stdin = false;
while ((optc = getopt_long (argc, argv, "rs", longopts, NULL)) != -1)
{
switch (optc)
- {
- case 'r': /* For SysV compatibility. */
- sum_func = bsd_sum_file;
- break;
+ {
+ case 'r': /* For SysV compatibility. */
+ sum_func = bsd_sum_file;
+ break;
- case 's':
- sum_func = sysv_sum_file;
- break;
+ case 's':
+ sum_func = sysv_sum_file;
+ break;
- case_GETOPT_HELP_CHAR;
+ case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- }
+ default:
+ usage (EXIT_FAILURE);
+ }
}
files_given = argc - optind;
@@ -264,6 +270,6 @@ main (int argc, char **argv)
ok &= sum_func (argv[optind], files_given);
if (have_read_stdin && fclose (stdin) == EOF)
- error (EXIT_FAILURE, errno, "-");
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ error (EXIT_FAILURE, errno, "%s", quotef ("-"));
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/sync.c b/src/sync.c
index 5e94afb..faf5ff8 100644
--- a/src/sync.c
+++ b/src/sync.c
@@ -1,10 +1,10 @@
/* sync - update the super block
- Copyright (C) 1994-2004 Free Software Foundation, Inc.
+ Copyright (C) 1994-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,67 +12,225 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Jim Meyering */
#include <config.h>
+#include <assert.h>
#include <getopt.h>
#include <stdio.h>
#include <sys/types.h>
#include "system.h"
#include "error.h"
-#include "long-options.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "sync"
-#define AUTHORS "Jim Meyering"
+#define AUTHORS \
+ proper_name ("Jim Meyering"), \
+ proper_name ("Giuseppe Scrivano")
-/* The name this program was run with. */
-char *program_name;
+#ifndef HAVE_SYNCFS
+# define HAVE_SYNCFS 0
+#endif
+
+enum sync_mode
+{
+ MODE_FILE,
+ MODE_DATA,
+ MODE_FILE_SYSTEM,
+ MODE_SYNC
+};
+
+static struct option const long_options[] =
+{
+ {"data", no_argument, NULL, 'd'},
+ {"file-system", no_argument, NULL, 'f'},
+ {GETOPT_HELP_OPTION_DECL},
+ {GETOPT_VERSION_OPTION_DECL},
+ {NULL, 0, NULL, 0}
+};
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
- printf (_("Usage: %s [OPTION]\n"), program_name);
+ printf (_("Usage: %s [OPTION] [FILE]...\n"), program_name);
fputs (_("\
-Force changed blocks to disk, update the super block.\n\
+Synchronize cached writes to persistent storage\n\
+\n\
+If one or more files are specified, sync only them,\n\
+or their containing file systems.\n\
\n\
"), stdout);
+
+ fputs (_("\
+ -d, --data sync only file data, no unneeded metadata\n\
+"), stdout);
+ fputs (_("\
+ -f, --file-system sync the file systems that contain the files\n\
+"), stdout);
+
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
+/* Sync the specified FILE, or file systems associated with FILE.
+ Return 1 on success. */
+
+static bool
+sync_arg (enum sync_mode mode, char const *file)
+{
+ bool ret = true;
+ int open_flags = O_RDONLY | O_NONBLOCK;
+ int fd;
+
+#ifdef _AIX
+ /* AIX 7.1 fsync requires write access to file. */
+ if (mode == MODE_FILE)
+ open_flags = O_WRONLY | O_NONBLOCK;
+#endif
+
+ /* Note O_PATH might be supported with syncfs(),
+ though as of Linux 3.18 is not. */
+ fd = open (file, open_flags);
+ if (fd < 0)
+ {
+ /* Use the O_RDONLY errno, which is significant
+ with directories for example. */
+ int rd_errno = errno;
+ if (open_flags != (O_WRONLY | O_NONBLOCK))
+ fd = open (file, O_WRONLY | O_NONBLOCK);
+ if (fd < 0)
+ error (0, rd_errno, _("error opening %s"), quoteaf (file));
+ return false;
+ }
+
+ /* We used O_NONBLOCK above to not hang with fifos,
+ so reset that here. */
+ int fdflags = fcntl (fd, F_GETFL);
+ if (fdflags == -1
+ || fcntl (fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
+ {
+ error (0, errno, _("couldn't reset non-blocking mode %s"),
+ quoteaf (file));
+ ret = false;
+ }
+
+ if (ret == true)
+ {
+ int sync_status = -1;
+
+ switch (mode)
+ {
+ case MODE_DATA:
+ sync_status = fdatasync (fd);
+ break;
+
+ case MODE_FILE:
+ sync_status = fsync (fd);
+ break;
+
+#if HAVE_SYNCFS
+ case MODE_FILE_SYSTEM:
+ sync_status = syncfs (fd);
+ break;
+#endif
+
+ default:
+ assert ("invalid sync_mode");
+ }
+
+ if (sync_status < 0)
+ {
+ error (0, errno, _("error syncing %s"), quoteaf (file));
+ ret = false;
+ }
+ }
+
+ if (close (fd) < 0)
+ {
+ error (0, errno, _("failed to close %s"), quoteaf (file));
+ ret = false;
+ }
+
+ return ret;
+}
+
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);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
- if (getopt_long (argc, argv, "", NULL, NULL) != -1)
- usage (EXIT_FAILURE);
+ while ((c = getopt_long (argc, argv, "df", long_options, NULL))
+ != -1)
+ {
+ switch (c)
+ {
+ case 'd':
+ arg_data = true;
+ break;
+
+ case 'f':
+ arg_file_system = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- if (optind < argc)
- error (0, 0, _("ignoring all arguments"));
+ default:
+ usage (EXIT_FAILURE);
+ }
+ }
+
+ args_specified = optind < argc;
+
+ if (arg_data && arg_file_system)
+ {
+ error (EXIT_FAILURE, 0,
+ _("cannot specify both --data and --file-system"));
+ }
+
+ if (!args_specified && arg_data)
+ error (EXIT_FAILURE, 0, _("--data needs at least one argument"));
+
+ if (! args_specified || (arg_file_system && ! HAVE_SYNCFS))
+ mode = MODE_SYNC;
+ else if (arg_file_system)
+ mode = MODE_FILE_SYSTEM;
+ else if (! arg_data)
+ mode = MODE_FILE;
+ else
+ mode = MODE_DATA;
+
+ if (mode == MODE_SYNC)
+ sync ();
+ else
+ {
+ for (; optind < argc; optind++)
+ ok &= sync_arg (mode, argv[optind]);
+ }
- sync ();
- exit (EXIT_SUCCESS);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/system.h b/src/system.h
index 763909b..9898bc7 100644
--- a/src/system.h
+++ b/src/system.h
@@ -1,10 +1,10 @@
/* system-dependent definitions for coreutils
- Copyright (C) 1989, 1991-2007 Free Software Foundation, Inc.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,21 +12,17 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
-#include <alloca.h>
-
-/* Include sys/types.h before this file. */
+/* Include this file _after_ system headers if possible. */
-#if 2 <= __GLIBC__ && 2 <= __GLIBC_MINOR__
-# if ! defined _SYS_TYPES_H
-you must include <sys/types.h> before including this file
-# endif
-#endif
+#include <alloca.h>
#include <sys/stat.h>
+/* Commonly used file permission combination. */
+#define MODE_RW_UGO (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
+
#if !defined HAVE_MKFIFO
# define mkfifo(name, mode) mknod (name, (mode) | S_IFIFO, 0)
#endif
@@ -37,39 +33,19 @@ you must include <sys/types.h> before including this file
#include <unistd.h>
-#ifndef STDIN_FILENO
-# define STDIN_FILENO 0
-#endif
-
-#ifndef STDOUT_FILENO
-# define STDOUT_FILENO 1
-#endif
-
-#ifndef STDERR_FILENO
-# define STDERR_FILENO 2
-#endif
-
-
-/* limits.h must come before pathmax.h because limits.h on some systems
- undefs PATH_MAX, whereas pathmax.h sets PATH_MAX. */
#include <limits.h>
#include "pathmax.h"
+#ifndef PATH_MAX
+# define PATH_MAX 8192
+#endif
#include "configmake.h"
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-# include <sys/time.h>
-# else
-# include <time.h>
-# endif
-#endif
+#include <sys/time.h>
+#include <time.h>
-/* Since major is a function on SVR4, we can't use `ifndef major'. */
+/* Since major is a function on SVR4, we can't use 'ifndef major'. */
#if MAJOR_IN_MKDEV
# include <sys/mkdev.h>
# define HAVE_MAJOR
@@ -93,34 +69,27 @@ you must include <sys/types.h> before including this file
# define makedev(maj, min) mkdev (maj, min)
#endif
-/* Don't use bcopy! Use memmove if source and destination may overlap,
- memcpy otherwise. */
-
#include <string.h>
-
#include <errno.h>
-/* Some systems don't define the following symbols. */
-#ifndef EDQUOT
-# define EDQUOT (-1)
-#endif
-#ifndef EISDIR
-# define EISDIR (-1)
-#endif
-#ifndef ENOSYS
-# define ENOSYS (-1)
+/* Some systems don't define this; POSIX mentions it but says it is
+ obsolete. gnulib defines it, but only on native Windows systems,
+ and there only because MSVC 10 does. */
+#ifndef ENODATA
+# define ENODATA (-1)
#endif
#include <stdbool.h>
#include <stdlib.h>
+#include "version.h"
-/* Exit statuses for programs like 'env' that exec other programs.
- EXIT_FAILURE might not be 1, so use EXIT_FAIL in such programs. */
+/* Exit statuses for programs like 'env' that exec other programs. */
enum
{
- EXIT_FAIL = 1,
- EXIT_CANNOT_INVOKE = 126,
- EXIT_ENOENT = 127
+ EXIT_TIMEDOUT = 124, /* Time expired before child completed. */
+ EXIT_CANCELED = 125, /* Internal error prior to exec attempt. */
+ EXIT_CANNOT_INVOKE = 126, /* Program located, but not usable. */
+ EXIT_ENOENT = 127 /* Could not find program to exec. */
};
#include "exitfail.h"
@@ -135,13 +104,6 @@ initialize_exit_failure (int status)
#include <fcntl.h>
-#ifndef F_OK
-# define F_OK 0
-# define X_OK 1
-# define W_OK 2
-# define R_OK 4
-#endif
-
#include <dirent.h>
#ifndef _D_EXACT_NAMLEN
# define _D_EXACT_NAMLEN(dp) strlen ((dp)->d_name)
@@ -159,102 +121,33 @@ enum
# define D_INO(dp) NOT_AN_INODE_NUMBER
#endif
-/* Get or fake the disk device blocksize.
- Usually defined by sys/param.h (if at all). */
-#if !defined DEV_BSIZE && defined BSIZE
-# define DEV_BSIZE BSIZE
-#endif
-#if !defined DEV_BSIZE && defined BBSIZE /* SGI */
-# define DEV_BSIZE BBSIZE
-#endif
-#ifndef DEV_BSIZE
-# define DEV_BSIZE 4096
-#endif
-
-/* Extract or fake data from a `struct stat'.
- ST_BLKSIZE: Preferred I/O blocksize for the file, in bytes.
- ST_NBLOCKS: Number of blocks in the file, including indirect blocks.
- ST_NBLOCKSIZE: Size of blocks used when calculating ST_NBLOCKS. */
-#ifndef HAVE_STRUCT_STAT_ST_BLOCKS
-# define ST_BLKSIZE(statbuf) DEV_BSIZE
-# if defined _POSIX_SOURCE || !defined BSIZE /* fileblocks.c uses BSIZE. */
-# define ST_NBLOCKS(statbuf) \
- ((statbuf).st_size / ST_NBLOCKSIZE + ((statbuf).st_size % ST_NBLOCKSIZE != 0))
-# else /* !_POSIX_SOURCE && BSIZE */
-# define ST_NBLOCKS(statbuf) \
- (S_ISREG ((statbuf).st_mode) \
- || S_ISDIR ((statbuf).st_mode) \
- ? st_blocks ((statbuf).st_size) : 0)
-# endif /* !_POSIX_SOURCE && BSIZE */
-#else /* HAVE_STRUCT_STAT_ST_BLOCKS */
-/* Some systems, like Sequents, return st_blksize of 0 on pipes.
- Also, when running `rsh hpux11-system cat any-file', cat would
- determine that the output stream had an st_blksize of 2147421096.
- Conversely st_blksize can be 2 GiB (or maybe even larger) with XFS
- on 64-bit hosts. Somewhat arbitrarily, limit the `optimal' block
- size to SIZE_MAX / 8 + 1. (Dividing SIZE_MAX by only 4 wouldn't
- suffice, since "cat" sometimes multiplies the result by 4.) If
- anyone knows of a system for which this limit is too small, please
- report it as a bug in this code. */
-# define ST_BLKSIZE(statbuf) ((0 < (statbuf).st_blksize \
- && (statbuf).st_blksize <= SIZE_MAX / 8 + 1) \
- ? (statbuf).st_blksize : DEV_BSIZE)
-# if defined hpux || defined __hpux__ || defined __hpux
-/* HP-UX counts st_blocks in 1024-byte units.
- This loses when mixing HP-UX and BSD file systems with NFS. */
-# define ST_NBLOCKSIZE 1024
-# else /* !hpux */
-# if defined _AIX && defined _I386
-/* AIX PS/2 counts st_blocks in 4K units. */
-# define ST_NBLOCKSIZE (4 * 1024)
-# else /* not AIX PS/2 */
-# if defined _CRAY
-# define ST_NBLOCKS(statbuf) \
- (S_ISREG ((statbuf).st_mode) \
- || S_ISDIR ((statbuf).st_mode) \
- ? (statbuf).st_blocks * ST_BLKSIZE(statbuf)/ST_NBLOCKSIZE : 0)
-# endif /* _CRAY */
-# endif /* not AIX PS/2 */
-# endif /* !hpux */
-#endif /* HAVE_STRUCT_STAT_ST_BLOCKS */
-
-#ifndef ST_NBLOCKS
-# define ST_NBLOCKS(statbuf) ((statbuf).st_blocks)
-#endif
-
-#ifndef ST_NBLOCKSIZE
-# ifdef S_BLKSIZE
-# define ST_NBLOCKSIZE S_BLKSIZE
-# else
-# define ST_NBLOCKSIZE 512
-# endif
-#endif
+/* include here for SIZE_MAX. */
+#include <inttypes.h>
/* Redirection and wildcarding when done by the utility itself.
- Generally a noop, but used in particular for native VMS. */
+ Generally a noop, but used in particular for OS/2. */
#ifndef initialize_main
-# define initialize_main(ac, av)
+# ifndef __OS2__
+# define initialize_main(ac, av)
+# else
+# define initialize_main(ac, av) \
+ do { _wildcard (ac, av); _response (ac, av); } while (0)
+# endif
#endif
#include "stat-macros.h"
#include "timespec.h"
-#include <inttypes.h>
-
#include <ctype.h>
-#if ! (defined isblank || HAVE_DECL_ISBLANK)
-# define isblank(c) ((c) == ' ' || (c) == '\t')
-#endif
-
/* ISDIGIT differs from isdigit, as follows:
- Its arg may be any int or unsigned int; it need not be an unsigned char
or EOF.
- It's typically faster.
POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
isdigit unless it's important to use the locale's definition
- of `digit' even when the host does not conform to POSIX. */
+ of 'digit' even when the host does not conform to POSIX. */
#define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
/* Convert a possibly-signed character to an unsigned character. This is
@@ -262,6 +155,13 @@ enum
errors that the cast doesn't. */
static inline unsigned char to_uchar (char ch) { return ch; }
+/* '\n' is considered a field separator with --zero-terminated. */
+static inline bool
+field_sep (unsigned char ch)
+{
+ return isblank (ch) || ch == '\n';
+}
+
#include <locale.h>
/* Take care of NLS matters. */
@@ -289,30 +189,12 @@ select_plural (uintmax_t n)
}
#define STREQ(a, b) (strcmp (a, b) == 0)
+#define STREQ_LEN(a, b, n) (strncmp (a, b, n) == 0)
+#define STRPREFIX(a, b) (strncmp (a, b, strlen (b)) == 0)
-#if !HAVE_DECL_FREE
-void free ();
-#endif
-
-#if !HAVE_DECL_MALLOC
-char *malloc ();
-#endif
-
-#if !HAVE_DECL_MEMCHR
-char *memchr ();
-#endif
-
-#if !HAVE_DECL_REALLOC
-char *realloc ();
-#endif
-
-#if !HAVE_DECL_GETENV
-char *getenv ();
-#endif
-
-#if !HAVE_DECL_LSEEK
-off_t lseek ();
-#endif
+/* Just like strncmp, but the second argument must be a literal string
+ and you don't specify the length; that comes from the literal. */
+#define STRNCMP_LIT(s, lit) strncmp (s, "" lit "", sizeof (lit) - 1)
#if !HAVE_DECL_GETLOGIN
char *getlogin ();
@@ -334,6 +216,24 @@ struct passwd *getpwuid ();
struct group *getgrgid ();
#endif
+/* Interix has replacements for getgr{gid,nam,ent}, that don't
+ query the domain controller for group members when not required.
+ This speeds up the calls tremendously (<1 ms vs. >3 s). */
+/* To protect any system that could provide _nomembers functions
+ other than interix, check for HAVE_SETGROUPS, as interix is
+ one of the very few (the only?) platform that lacks it */
+#if ! HAVE_SETGROUPS
+# if HAVE_GETGRGID_NOMEMBERS
+# define getgrgid(gid) getgrgid_nomembers(gid)
+# endif
+# if HAVE_GETGRNAM_NOMEMBERS
+# define getgrnam(nam) getgrnam_nomembers(nam)
+# endif
+# if HAVE_GETGRENT_NOMEMBERS
+# define getgrent() getgrent_nomembers()
+# endif
+#endif
+
#if !HAVE_DECL_GETUID
uid_t getuid ();
#endif
@@ -342,11 +242,11 @@ uid_t getuid ();
#include "verify.h"
/* This is simply a shorthand for the common case in which
- the third argument to x2nrealloc would be `sizeof *(P)'.
+ the third argument to x2nrealloc would be 'sizeof *(P)'.
Ensure that sizeof *(P) is *not* 1. In that case, it'd be
better to use X2REALLOC, although not strictly necessary. */
#define X2NREALLOC(P, PN) ((void) verify_true (sizeof *(P) != 1), \
- x2nrealloc (P, PN, sizeof *(P)))
+ x2nrealloc (P, PN, sizeof *(P)))
/* Using x2realloc (when appropriate) usually makes your code more
readable than using x2nrealloc, but it also makes it so your
@@ -359,6 +259,7 @@ uid_t getuid ();
#include "same-inode.h"
#include "dirname.h"
+#include "openat.h"
static inline bool
dot_or_dotdot (char const *file_name)
@@ -372,7 +273,7 @@ dot_or_dotdot (char const *file_name)
return false;
}
-/* A wrapper for readdir so that callers don't see entries for `.' or `..'. */
+/* A wrapper for readdir so that callers don't see entries for '.' or '..'. */
static inline struct dirent const *
readdir_ignoring_dot_and_dotdot (DIR *dirp)
{
@@ -380,8 +281,38 @@ readdir_ignoring_dot_and_dotdot (DIR *dirp)
{
struct dirent const *dp = readdir (dirp);
if (dp == NULL || ! dot_or_dotdot (dp->d_name))
- return dp;
+ return dp;
+ }
+}
+
+/* Return true if DIR is determined to be an empty directory. */
+static inline bool
+is_empty_dir (int fd_cwd, char const *dir)
+{
+ DIR *dirp;
+ struct dirent const *dp;
+ int saved_errno;
+ int fd = openat (fd_cwd, dir,
+ (O_RDONLY | O_DIRECTORY
+ | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK));
+
+ if (fd < 0)
+ return false;
+
+ dirp = fdopendir (fd);
+ if (dirp == NULL)
+ {
+ close (fd);
+ return false;
}
+
+ errno = 0;
+ dp = readdir_ignoring_dot_and_dotdot (dirp);
+ saved_errno = errno;
+ closedir (dirp);
+ if (dp != NULL)
+ return false;
+ return saved_errno == 0 ? true : false;
}
/* Factor out some of the common --help and --version processing code. */
@@ -399,6 +330,8 @@ enum
"help", no_argument, NULL, GETOPT_HELP_CHAR
#define GETOPT_VERSION_OPTION_DECL \
"version", no_argument, NULL, GETOPT_VERSION_CHAR
+#define GETOPT_SELINUX_CONTEXT_OPTION_DECL \
+ "context", optional_argument, NULL, 'Z'
#define case_GETOPT_HELP_CHAR \
case GETOPT_HELP_CHAR: \
@@ -418,12 +351,26 @@ enum
#define VERSION_OPTION_DESCRIPTION \
_(" --version output version information and exit\n")
+#include "closein.h"
#include "closeout.h"
+
+#define emit_bug_reporting_address unused__emit_bug_reporting_address
#include "version-etc.h"
+#undef emit_bug_reporting_address
+
+#include "propername.h"
+/* Define away proper_name (leaving proper_name_utf8, which affects far
+ fewer programs), since it's not worth the cost of adding ~17KB to
+ the x86_64 text size of every single program. This avoids a 40%
+ (almost ~2MB) increase in the on-disk space utilization for the set
+ of the 100 binaries. */
+#define proper_name(x) (x)
+
+#include "progname.h"
#define case_GETOPT_VERSION_CHAR(Program_name, Authors) \
case GETOPT_VERSION_CHAR: \
- version_etc (stdout, Program_name, GNU_PACKAGE, VERSION, Authors, \
+ version_etc (stdout, Program_name, PACKAGE_NAME, Version, Authors, \
(char *) NULL); \
exit (EXIT_SUCCESS); \
break;
@@ -462,7 +409,7 @@ enum
# define PID_T_MAX TYPE_MAXIMUM (pid_t)
#endif
-/* Use this to suppress gcc's `...may be used before initialized' warnings. */
+/* Use this to suppress gcc's '...may be used before initialized' warnings. */
#ifdef lint
# define IF_LINT(Code) Code
#else
@@ -470,7 +417,7 @@ enum
#endif
#ifndef __attribute__
-# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8)
# define __attribute__(x) /* empty */
# endif
#endif
@@ -479,10 +426,23 @@ enum
# define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__))
#endif
-#ifndef ATTRIBUTE_UNUSED
-# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+/* The warn_unused_result attribute appeared first in gcc-3.4.0 */
+#undef ATTRIBUTE_WARN_UNUSED_RESULT
+#if __GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
+# define ATTRIBUTE_WARN_UNUSED_RESULT /* empty */
+#else
+# define ATTRIBUTE_WARN_UNUSED_RESULT __attribute__ ((__warn_unused_result__))
#endif
+#ifdef __GNUC__
+# define LIKELY(cond) __builtin_expect ((cond), 1)
+# define UNLIKELY(cond) __builtin_expect ((cond), 0)
+#else
+# define LIKELY(cond) (cond)
+# define UNLIKELY(cond) (cond)
+#endif
+
+
#if defined strdupa
# define ASSIGN_STRDUPA(DEST, S) \
do { DEST = strdupa (S); } while (0)
@@ -498,25 +458,6 @@ enum
while (0)
#endif
-#ifndef EOVERFLOW
-# define EOVERFLOW EINVAL
-#endif
-
-#if ! HAVE_FSEEKO
-# if ! defined fseeko
-# define fseeko(s, o, w) ((o) == (long int) (o) \
- ? fseek (s, o, w) \
- : (errno = EOVERFLOW, -1))
-# endif
-# if ! defined ftello
-static inline off_t ftello (FILE *stream)
-{
- verify (sizeof (long int) <= sizeof (off_t));
- return ftell (stream);
-}
-# endif
-#endif
-
#if ! HAVE_SYNC
# define sync() /* empty */
#endif
@@ -524,7 +465,7 @@ static inline off_t ftello (FILE *stream)
/* Compute the greatest common divisor of U and V using Euclid's
algorithm. U and V must be nonzero. */
-static inline size_t
+static inline size_t _GL_ATTRIBUTE_CONST
gcd (size_t u, size_t v)
{
do
@@ -542,7 +483,7 @@ gcd (size_t u, size_t v)
nonzero. There is no overflow checking, so callers should not
specify outlandish sizes. */
-static inline size_t
+static inline size_t _GL_ATTRIBUTE_CONST
lcm (size_t u, size_t v)
{
return u * (v / gcd (u, v));
@@ -561,6 +502,57 @@ ptr_align (void const *ptr, size_t alignment)
return (void *) (p1 - (size_t) p1 % alignment);
}
+/* Return whether the buffer consists entirely of NULs.
+ Based on memeqzero in CCAN by Rusty Russell under CC0 (Public domain). */
+
+static inline bool _GL_ATTRIBUTE_PURE
+is_nul (void const *buf, size_t length)
+{
+ const unsigned char *p = buf;
+/* Using possibly unaligned access for the first 16 bytes
+ saves about 30-40 cycles, though it is strictly undefined behavior
+ and so would need __attribute__ ((__no_sanitize_undefined__))
+ to avoid -fsanitize=undefined warnings.
+ Considering coreutils is mainly concerned with relatively
+ large buffers, we'll just use the defined behavior. */
+#if 0 && _STRING_ARCH_unaligned
+ unsigned long word;
+#else
+ unsigned char word;
+#endif
+
+ if (! length)
+ return true;
+
+ /* Check len bytes not aligned on a word. */
+ while (UNLIKELY (length & (sizeof word - 1)))
+ {
+ if (*p)
+ return false;
+ p++;
+ length--;
+ if (! length)
+ return true;
+ }
+
+ /* Check up to 16 bytes a word at a time. */
+ for (;;)
+ {
+ memcpy (&word, p, sizeof word);
+ if (word)
+ return false;
+ p += sizeof word;
+ length -= sizeof word;
+ if (! length)
+ return true;
+ if (UNLIKELY (length & 15) == 0)
+ break;
+ }
+
+ /* Now we know first 16 bytes are NUL, memcmp with self. */
+ return memcmp (buf, p, length) == 0;
+}
+
/* If 10*Accum + Digit_val is larger than the maximum value for Type,
then don't update Accum and return false to indicate it would
overflow. Otherwise, set Accum to that new value and return true.
@@ -581,3 +573,187 @@ ptr_align (void const *ptr, size_t alignment)
|| (Type) ((Accum) * 10 + (Digit_val)) < (Accum)) \
? false : (((Accum) = (Accum) * 10 + (Digit_val)), true)) \
)
+
+static inline void
+emit_stdin_note (void)
+{
+ fputs (_("\n\
+With no FILE, or when FILE is -, read standard input.\n\
+"), stdout);
+}
+static inline void
+emit_mandatory_arg_note (void)
+{
+ fputs (_("\n\
+Mandatory arguments to long options are mandatory for short options too.\n\
+"), stdout);
+}
+
+static inline void
+emit_size_note (void)
+{
+ fputs (_("\n\
+The SIZE argument is an integer and optional unit (example: 10K is 10*1024).\n\
+Units are K,M,G,T,P,E,Z,Y (powers of 1024) or KB,MB,... (powers of 1000).\n\
+"), stdout);
+}
+
+static inline void
+emit_blocksize_note (char const *program)
+{
+ printf (_("\n\
+Display values are in units of the first available SIZE from --block-size,\n\
+and the %s_BLOCK_SIZE, BLOCK_SIZE and BLOCKSIZE environment variables.\n\
+Otherwise, units default to 1024 bytes (or 512 if POSIXLY_CORRECT is set).\n\
+"), program);
+}
+
+static inline void
+emit_ancillary_info (char const *program)
+{
+ struct infomap { char const *program; char const *node; } const infomap[] = {
+ { "[", "test invocation" },
+ { "coreutils", "Multi-call invocation" },
+ { "sha224sum", "sha2 utilities" },
+ { "sha256sum", "sha2 utilities" },
+ { "sha384sum", "sha2 utilities" },
+ { "sha512sum", "sha2 utilities" },
+ { NULL, NULL }
+ };
+
+ char const *node = program;
+ struct infomap const *map_prog = infomap;
+
+ while (map_prog->program && ! STREQ (program, map_prog->program))
+ map_prog++;
+
+ if (map_prog->node)
+ node = map_prog->node;
+
+ printf (_("\n%s online help: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
+
+ /* Don't output this redundant message for English locales.
+ Note we still output for 'C' so that it gets included in the man page. */
+ const char *lc_messages = setlocale (LC_MESSAGES, NULL);
+ if (lc_messages && STRNCMP_LIT (lc_messages, "en_"))
+ {
+ /* TRANSLATORS: Replace LANG_CODE in this URL with your language code
+ <http://translationproject.org/team/LANG_CODE.html> to form one of
+ the URLs at http://translationproject.org/team/. Otherwise, replace
+ the entire URL with your translation team's email address. */
+ printf (_("Report %s translation bugs to "
+ "<http://translationproject.org/team/>\n"), program);
+ }
+ printf (_("Full documentation at: <%s%s>\n"),
+ PACKAGE_URL, program);
+ printf (_("or available locally via: info '(coreutils) %s%s'\n"),
+ node, node == program ? " invocation" : "");
+}
+
+static inline void
+emit_try_help (void)
+{
+ fprintf (stderr, _("Try '%s --help' for more information.\n"), program_name);
+}
+
+#include "inttostr.h"
+
+static inline char *
+timetostr (time_t t, char *buf)
+{
+ return (TYPE_SIGNED (time_t)
+ ? imaxtostr (t, buf)
+ : umaxtostr (t, buf));
+}
+
+static inline char *
+bad_cast (char const *s)
+{
+ return (char *) s;
+}
+
+/* Return a boolean indicating whether SB->st_size is defined. */
+static inline bool
+usable_st_size (struct stat const *sb)
+{
+ return (S_ISREG (sb->st_mode) || S_ISLNK (sb->st_mode)
+ || S_TYPEISSHM (sb) || S_TYPEISTMO (sb));
+}
+
+void usage (int status) ATTRIBUTE_NORETURN;
+
+/* Like error(0, 0, ...), but without an implicit newline.
+ Also a noop unless the global DEV_DEBUG is set. */
+#define devmsg(...) \
+ do \
+ { \
+ if (dev_debug) \
+ fprintf (stderr, __VA_ARGS__); \
+ } \
+ while (0)
+
+#define emit_cycle_warning(file_name) \
+ do \
+ { \
+ error (0, 0, _("\
+WARNING: Circular directory structure.\n\
+This almost certainly means that you have a corrupted file system.\n\
+NOTIFY YOUR SYSTEM MANAGER.\n\
+The following directory is part of the cycle:\n %s\n"), \
+ quotef (file_name)); \
+ } \
+ while (0)
+
+/* Like stpncpy, but do ensure that the result is NUL-terminated,
+ and do not NUL-pad out to LEN. I.e., when strnlen (src, len) == len,
+ this function writes a NUL byte into dest[len]. Thus, the length
+ of the destination buffer must be at least LEN + 1.
+ The DEST and SRC buffers must not overlap. */
+static inline char *
+stzncpy (char *restrict dest, char const *restrict src, size_t len)
+{
+ char const *src_end = src + len;
+ while (src < src_end && *src)
+ *dest++ = *src++;
+ *dest = 0;
+ return dest;
+}
+
+#ifndef ARRAY_CARDINALITY
+# define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array))
+#endif
+
+/* Avoid const warnings by casting to more portable type.
+ This is to cater for the incorrect const function declarations
+ in selinux.h before libselinux-2.3 (May 2014).
+ When version >= 2.3 is ubiquitous remove this function. */
+static inline char * se_const (char const * sctx) { return (char *) sctx; }
+
+/* Return true if ERR is ENOTSUP or EOPNOTSUPP, otherwise false.
+ This wrapper function avoids the redundant 'or'd comparison on
+ systems like Linux for which they have the same value. It also
+ avoids the gcc warning to that effect. */
+static inline bool
+is_ENOTSUP (int err)
+{
+ return err == EOPNOTSUPP || (ENOTSUP != EOPNOTSUPP && err == ENOTSUP);
+}
+
+
+/* How coreutils quotes filenames, to minimize use of outer quotes,
+ but also provide better support for copy and paste when used. */
+#include "quotearg.h"
+
+/* Use these to shell quote only when necessary,
+ when the quoted item is already delimited with colons. */
+#define quotef(arg) \
+ quotearg_n_style_colon (0, shell_escape_quoting_style, arg)
+#define quotef_n(n, arg) \
+ quotearg_n_style_colon (n, shell_escape_quoting_style, arg)
+
+/* Use these when there are spaces around the file name,
+ in the error message. */
+#define quoteaf(arg) \
+ quotearg_style (shell_escape_always_quoting_style, arg)
+#define quoteaf_n(n, arg) \
+ quotearg_n_style (n, shell_escape_always_quoting_style, arg)
diff --git a/src/tac-pipe.c b/src/tac-pipe.c
index d2c6f67..976570a 100644
--- a/src/tac-pipe.c
+++ b/src/tac-pipe.c
@@ -1,11 +1,11 @@
/* tac from a pipe.
- Copyright (C) 1997, 1998, 2002, 2004 Free Software Foundation, Inc.
+ Copyright (C) 1997-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -13,8 +13,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* FIXME */
#include <assert.h>
@@ -63,52 +62,52 @@ buf_init_from_stdin (Buf *x, char eol_byte)
size_t bytes_read;
if (buf == NULL)
- {
- /* Fall back on the code that relies on a temporary file.
- Write all buffers to that file and free them. */
- /* FIXME */
- ok = false;
- break;
- }
+ {
+ /* Fall back on the code that relies on a temporary file.
+ Write all buffers to that file and free them. */
+ /* FIXME */
+ ok = false;
+ break;
+ }
bytes_read = full_read (STDIN_FILENO, buf, BUFFER_SIZE);
if (bytes_read != buffer_size && errno != 0)
- error (EXIT_FAILURE, errno, _("read error"));
+ error (EXIT_FAILURE, errno, _("read error"));
{
- struct B_pair bp;
- bp.start = buf;
- bp.one_past_end = buf + bytes_read;
- obstack_grow (OBS, &bp, sizeof (bp));
+ struct B_pair bp;
+ bp.start = buf;
+ bp.one_past_end = buf + bytes_read;
+ obstack_grow (OBS, &bp, sizeof (bp));
}
if (bytes_read != 0)
- last_byte_is_eol_byte = (buf[bytes_read - 1] == eol_byte);
+ last_byte_is_eol_byte = (buf[bytes_read - 1] == eol_byte);
if (bytes_read < BUFFER_SIZE)
- break;
+ break;
}
if (ok)
{
/* If the file was non-empty and lacked an EOL_BYTE at its end,
- then add a buffer containing just that one byte. */
+ then add a buffer containing just that one byte. */
if (!last_byte_is_eol_byte)
- {
- char *buf = malloc (1);
- if (buf == NULL)
- {
- /* FIXME: just like above */
- ok = false;
- }
- else
- {
- struct B_pair bp;
- *buf = eol_byte;
- bp.start = buf;
- bp.one_past_end = buf + 1;
- obstack_grow (OBS, &bp, sizeof (bp));
- }
- }
+ {
+ char *buf = malloc (1);
+ if (buf == NULL)
+ {
+ /* FIXME: just like above */
+ ok = false;
+ }
+ else
+ {
+ struct B_pair bp;
+ *buf = eol_byte;
+ bp.start = buf;
+ bp.one_past_end = buf + 1;
+ obstack_grow (OBS, &bp, sizeof (bp));
+ }
+ }
}
x->n_bufs = obstack_object_size (OBS) / sizeof (x->p[0]);
@@ -173,7 +172,7 @@ line_ptr_increment (const Buf *x, const Line_ptr *lp)
static bool
find_bol (const Buf *x,
- const Line_ptr *last_bol, Line_ptr *new_bol, char eol_byte)
+ const Line_ptr *last_bol, Line_ptr *new_bol, char eol_byte)
{
size_t i;
Line_ptr tmp;
@@ -189,16 +188,16 @@ find_bol (const Buf *x,
{
char *nl = memrchr (x->p[i].start, last_bol_ptr, eol_byte);
if (nl)
- {
- Line_ptr nl_pos;
- nl_pos.i = i;
- nl_pos.ptr = nl;
- *new_bol = line_ptr_increment (x, &nl_pos);
- return true;
- }
+ {
+ Line_ptr nl_pos;
+ nl_pos.i = i;
+ nl_pos.ptr = nl;
+ *new_bol = line_ptr_increment (x, &nl_pos);
+ return true;
+ }
if (i == 0)
- break;
+ break;
--i;
last_bol_ptr = ONE_PAST_END (x, i);
@@ -219,7 +218,7 @@ find_bol (const Buf *x,
static void
print_line (FILE *out_stream, const Buf *x,
- const Line_ptr *bol, const Line_ptr *bol_next)
+ const Line_ptr *bol, const Line_ptr *bol_next)
{
size_t i;
for (i = bol->i; i <= bol_next->i; i++)
@@ -255,7 +254,7 @@ tac_mem ()
{
Line_ptr new_bol;
if (! find_bol (&x, &bol, &new_bol, eol_byte))
- break;
+ break;
print_line (stdout, &x, &new_bol, &bol);
bol = new_bol;
}
diff --git a/src/tac.c b/src/tac.c
index dc166aa..4681f3a 100644
--- a/src/tac.c
+++ b/src/tac.c
@@ -1,10 +1,10 @@
/* tac - concatenate and print files in reverse
- Copyright (C) 1988-1991, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1988-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Jay Lepreau (lepreau@cs.utah.edu).
GNU enhancements by David MacKenzie (djm@gnu.ai.mit.edu). */
@@ -27,7 +26,7 @@
Options:
-b, --before The separator is attached to the beginning
- of the record that it precedes in the file.
+ of the record that it precedes in the file.
-r, --regex The separator is a regular expression.
-s, --separator=separator Use SEPARATOR as the record separator.
@@ -45,15 +44,17 @@ tac -r -s '.\|
#include <regex.h>
#include "error.h"
-#include "quote.h"
-#include "quotearg.h"
+#include "filenamecat.h"
#include "safe-read.h"
#include "stdlib--.h"
+#include "xfreopen.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "tac"
-#define AUTHORS "Jay Lepreau", "David MacKenzie"
+#define AUTHORS \
+ proper_name ("Jay Lepreau"), \
+ proper_name ("David MacKenzie")
#if defined __MSDOS__ || defined _WIN32
/* Define this to non-zero on systems for which the regular mechanism
@@ -74,43 +75,41 @@ tac -r -s '.\|
/* The number of bytes per atomic write. */
#define WRITESIZE 8192
-/* The name this program was run with. */
-char *program_name;
-
/* The string that separates the records of the file. */
static char const *separator;
/* True if we have ever read standard input. */
static bool have_read_stdin = false;
-/* If true, print `separator' along with the record preceding it
+/* If true, print 'separator' along with the record preceding it
in the file; otherwise with the record following it. */
static bool separator_ends_record;
-/* 0 if `separator' is to be matched as a regular expression;
- otherwise, the length of `separator', used as a sentinel to
+/* 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;
-/* The length of a match with `separator'. If `sentinel_length' is 0,
- `match_length' is computed every time a match succeeds;
- otherwise, it is simply the length of `separator'. */
+/* The length of a match with 'separator'. If 'sentinel_length' is 0,
+ 'match_length' is computed every time a match succeeds;
+ otherwise, it is simply the length of 'separator'. */
static size_t match_length;
/* The input buffer. */
static char *G_buffer;
-/* The number of bytes to read at once into `buffer'. */
+/* The number of bytes to read at once into 'buffer'. */
static size_t read_size;
-/* The size of `buffer'. This is read_size * 2 + sentinel_length + 2.
- The extra 2 bytes allow `past_end' to have a value beyond the
- end of `G_buffer' and `match_start' to run off the front of `G_buffer'. */
+/* The size of 'buffer'. This is read_size * 2 + sentinel_length + 2.
+ The extra 2 bytes allow 'past_end' to have a value beyond the
+ end of 'G_buffer' and 'match_start' to run off the front of 'G_buffer'. */
static size_t G_buffer_size;
-/* The compiled regular expression representing `separator'. */
+/* The compiled regular expression representing 'separator'. */
static struct re_pattern_buffer compiled_separator;
static char compiled_separator_fastmap[UCHAR_MAX + 1];
+static struct re_registers regs;
static struct option const longopts[] =
{
@@ -126,22 +125,20 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
- program_name);
+ program_name);
fputs (_("\
Write each FILE to standard output, last line first.\n\
-With no FILE, or when FILE is -, read standard input.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
+
+ emit_stdin_note ();
+ emit_mandatory_arg_note ();
+
fputs (_("\
-b, --before attach the separator before instead of after\n\
-r, --regex interpret the separator as a regular expression\n\
@@ -149,7 +146,7 @@ Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -188,56 +185,69 @@ output (const char *start, const char *past_end)
}
/* Print in reverse the file open on descriptor FD for reading FILE.
+ The file is already positioned at FILE_POS, which should be near its end.
Return true if successful. */
static bool
-tac_seekable (int input_fd, const char *file)
+tac_seekable (int input_fd, const char *file, off_t file_pos)
{
- /* Pointer to the location in `G_buffer' where the search for
+ /* Pointer to the location in 'G_buffer' where the search for
the next separator will begin. */
char *match_start;
- /* Pointer to one past the rightmost character in `G_buffer' that
+ /* Pointer to one past the rightmost character in 'G_buffer' that
has not been printed yet. */
char *past_end;
- /* Length of the record growing in `G_buffer'. */
+ /* Length of the record growing in 'G_buffer'. */
size_t saved_record_size;
- /* Offset in the file of the next read. */
- off_t file_pos;
-
- /* True if `output' has not been called yet for any file.
+ /* True if 'output' has not been called yet for any file.
Only used when the separator is attached to the preceding record. */
bool first_time = true;
char first_char = *separator; /* Speed optimization, non-regexp. */
char const *separator1 = separator + 1; /* Speed optimization, non-regexp. */
size_t match_length1 = match_length - 1; /* Speed optimization, non-regexp. */
- struct re_registers regs;
-
- /* Find the size of the input file. */
- file_pos = lseek (input_fd, (off_t) 0, SEEK_END);
- if (file_pos < 1)
- return true; /* It's an empty file. */
/* Arrange for the first read to lop off enough to leave the rest of the
- file a multiple of `read_size'. Since `read_size' can change, this may
+ file a multiple of 'read_size'. Since 'read_size' can change, this may
not always hold during the program run, but since it usually will, leave
it here for i/o efficiency (page/sector boundaries and all that).
Note: the efficiency gain has not been verified. */
- saved_record_size = file_pos % read_size;
- if (saved_record_size == 0)
- saved_record_size = read_size;
- file_pos -= saved_record_size;
- /* `file_pos' now points to the start of the last (probably partial) block
- in the input file. */
+ size_t remainder = file_pos % read_size;
+ if (remainder != 0)
+ {
+ file_pos -= remainder;
+ if (lseek (input_fd, file_pos, SEEK_SET) < 0)
+ error (0, errno, _("%s: seek failed"), quotef (file));
+ }
- if (lseek (input_fd, file_pos, SEEK_SET) < 0)
- error (0, errno, _("%s: seek failed"), quotearg_colon (file));
+ /* Scan backward, looking for end of file. This caters to proc-like
+ file systems where the file size is just an estimate. */
+ while ((saved_record_size = safe_read (input_fd, G_buffer, read_size)) == 0
+ && file_pos != 0)
+ {
+ off_t rsize = read_size;
+ if (lseek (input_fd, -rsize, SEEK_CUR) < 0)
+ error (0, errno, _("%s: seek failed"), quotef (file));
+ file_pos -= read_size;
+ }
- if (safe_read (input_fd, G_buffer, saved_record_size) != saved_record_size)
+ /* Now scan forward, looking for end of file. */
+ while (saved_record_size == read_size)
{
- error (0, errno, _("%s: read error"), quotearg_colon (file));
+ size_t nread = safe_read (input_fd, G_buffer, read_size);
+ if (nread == 0)
+ break;
+ saved_record_size = nread;
+ if (saved_record_size == SAFE_READ_ERROR)
+ break;
+ file_pos += nread;
+ }
+
+ if (saved_record_size == SAFE_READ_ERROR)
+ {
+ error (0, errno, _("%s: read error"), quotef (file));
return false;
}
@@ -246,136 +256,131 @@ tac_seekable (int input_fd, const char *file)
if (sentinel_length)
match_start -= match_length1;
- for (;;)
+ while (true)
{
- /* Search backward from `match_start' - 1 to `G_buffer' for a match
- with `separator'; for speed, use strncmp if `separator' contains no
- metacharacters.
- If the match succeeds, set `match_start' to point to the start of
- the match and `match_length' to the length of the match.
- Otherwise, make `match_start' < `G_buffer'. */
+ /* Search backward from 'match_start' - 1 to 'G_buffer' for a match
+ with 'separator'; for speed, use strncmp if 'separator' contains no
+ metacharacters.
+ If the match succeeds, set 'match_start' to point to the start of
+ the match and 'match_length' to the length of the match.
+ Otherwise, make 'match_start' < 'G_buffer'. */
if (sentinel_length == 0)
- {
- size_t i = match_start - G_buffer;
- regoff_t ri = i;
- regoff_t range = 1 - ri;
- regoff_t ret;
-
- if (1 < range)
- error (EXIT_FAILURE, 0, _("record too large"));
-
- if (range == 1
- || ((ret = re_search (&compiled_separator, G_buffer,
- i, i - 1, range, &regs))
- == -1))
- match_start = G_buffer - 1;
- else if (ret == -2)
- {
- error (EXIT_FAILURE, 0,
- _("error in regular expression search"));
- }
- else
- {
- match_start = G_buffer + regs.start[0];
- match_length = regs.end[0] - regs.start[0];
- }
- }
+ {
+ size_t i = match_start - G_buffer;
+ regoff_t ri = i;
+ regoff_t range = 1 - ri;
+ regoff_t ret;
+
+ if (1 < range)
+ error (EXIT_FAILURE, 0, _("record too large"));
+
+ if (range == 1
+ || ((ret = re_search (&compiled_separator, G_buffer,
+ i, i - 1, range, &regs))
+ == -1))
+ match_start = G_buffer - 1;
+ else if (ret == -2)
+ {
+ error (EXIT_FAILURE, 0,
+ _("error in regular expression search"));
+ }
+ else
+ {
+ match_start = G_buffer + regs.start[0];
+ match_length = regs.end[0] - regs.start[0];
+ }
+ }
else
- {
- /* `match_length' is constant for non-regexp boundaries. */
- while (*--match_start != first_char
- || (match_length1 && strncmp (match_start + 1, separator1,
- match_length1)))
- /* Do nothing. */ ;
- }
-
- /* Check whether we backed off the front of `G_buffer' without finding
- a match for `separator'. */
+ {
+ /* 'match_length' is constant for non-regexp boundaries. */
+ while (*--match_start != first_char
+ || (match_length1 && !STREQ_LEN (match_start + 1, separator1,
+ match_length1)))
+ /* Do nothing. */ ;
+ }
+
+ /* Check whether we backed off the front of 'G_buffer' without finding
+ a match for 'separator'. */
if (match_start < G_buffer)
- {
- if (file_pos == 0)
- {
- /* Hit the beginning of the file; print the remaining record. */
- output (G_buffer, past_end);
- return true;
- }
-
- saved_record_size = past_end - G_buffer;
- if (saved_record_size > read_size)
- {
- /* `G_buffer_size' is about twice `read_size', so since
- we want to read in another `read_size' bytes before
- the data already in `G_buffer', we need to increase
- `G_buffer_size'. */
- char *newbuffer;
- size_t offset = sentinel_length ? sentinel_length : 1;
- ptrdiff_t match_start_offset = match_start - G_buffer;
- ptrdiff_t past_end_offset = past_end - G_buffer;
- size_t old_G_buffer_size = G_buffer_size;
-
- read_size *= 2;
- G_buffer_size = read_size * 2 + sentinel_length + 2;
- if (G_buffer_size < old_G_buffer_size)
- xalloc_die ();
- newbuffer = xrealloc (G_buffer - offset, G_buffer_size);
- newbuffer += offset;
- /* Adjust the pointers for the new buffer location. */
- match_start = newbuffer + match_start_offset;
- past_end = newbuffer + past_end_offset;
- G_buffer = newbuffer;
- }
-
- /* Back up to the start of the next bufferfull of the file. */
- if (file_pos >= read_size)
- file_pos -= read_size;
- else
- {
- read_size = file_pos;
- file_pos = 0;
- }
- if (lseek (input_fd, file_pos, SEEK_SET) < 0)
- error (0, errno, _("%s: seek failed"), quotearg_colon (file));
-
- /* Shift the pending record data right to make room for the new.
- The source and destination regions probably overlap. */
- memmove (G_buffer + read_size, G_buffer, saved_record_size);
- past_end = G_buffer + read_size + saved_record_size;
- /* For non-regexp searches, avoid unneccessary scanning. */
- if (sentinel_length)
- match_start = G_buffer + read_size;
- else
- match_start = past_end;
-
- if (safe_read (input_fd, G_buffer, read_size) != read_size)
- {
- error (0, errno, _("%s: read error"), quotearg_colon (file));
- return false;
- }
- }
+ {
+ if (file_pos == 0)
+ {
+ /* Hit the beginning of the file; print the remaining record. */
+ output (G_buffer, past_end);
+ return true;
+ }
+
+ saved_record_size = past_end - G_buffer;
+ if (saved_record_size > read_size)
+ {
+ /* 'G_buffer_size' is about twice 'read_size', so since
+ we want to read in another 'read_size' bytes before
+ the data already in 'G_buffer', we need to increase
+ 'G_buffer_size'. */
+ char *newbuffer;
+ size_t offset = sentinel_length ? sentinel_length : 1;
+ size_t old_G_buffer_size = G_buffer_size;
+
+ read_size *= 2;
+ G_buffer_size = read_size * 2 + sentinel_length + 2;
+ if (G_buffer_size < old_G_buffer_size)
+ xalloc_die ();
+ newbuffer = xrealloc (G_buffer - offset, G_buffer_size);
+ newbuffer += offset;
+ G_buffer = newbuffer;
+ }
+
+ /* Back up to the start of the next bufferfull of the file. */
+ if (file_pos >= read_size)
+ file_pos -= read_size;
+ else
+ {
+ read_size = file_pos;
+ file_pos = 0;
+ }
+ if (lseek (input_fd, file_pos, SEEK_SET) < 0)
+ error (0, errno, _("%s: seek failed"), quotef (file));
+
+ /* Shift the pending record data right to make room for the new.
+ The source and destination regions probably overlap. */
+ memmove (G_buffer + read_size, G_buffer, saved_record_size);
+ past_end = G_buffer + read_size + saved_record_size;
+ /* For non-regexp searches, avoid unnecessary scanning. */
+ if (sentinel_length)
+ match_start = G_buffer + read_size;
+ else
+ match_start = past_end;
+
+ if (safe_read (input_fd, G_buffer, read_size) != read_size)
+ {
+ error (0, errno, _("%s: read error"), quotef (file));
+ return false;
+ }
+ }
else
- {
- /* Found a match of `separator'. */
- if (separator_ends_record)
- {
- char *match_end = match_start + match_length;
-
- /* If this match of `separator' isn't at the end of the
- file, print the record. */
- if (!first_time || match_end != past_end)
- output (match_end, past_end);
- past_end = match_end;
- first_time = false;
- }
- else
- {
- output (match_start, past_end);
- past_end = match_start;
- }
-
- /* For non-regex matching, we can back up. */
- if (sentinel_length > 0)
- match_start -= match_length - 1;
- }
+ {
+ /* Found a match of 'separator'. */
+ if (separator_ends_record)
+ {
+ char *match_end = match_start + match_length;
+
+ /* If this match of 'separator' isn't at the end of the
+ file, print the record. */
+ if (!first_time || match_end != past_end)
+ output (match_end, past_end);
+ past_end = match_end;
+ first_time = false;
+ }
+ else
+ {
+ output (match_start, past_end);
+ past_end = match_start;
+ }
+
+ /* For non-regex matching, we can back up. */
+ if (sentinel_length > 0)
+ match_start -= match_length - 1;
+ }
}
}
@@ -411,97 +416,129 @@ record_or_unlink_tempfile (char const *fn, FILE *fp)
#else
static void
-record_or_unlink_tempfile (char const *fn, FILE *fp ATTRIBUTE_UNUSED)
+record_or_unlink_tempfile (char const *fn, FILE *fp _GL_UNUSED)
{
unlink (fn);
}
#endif
-/* Copy from file descriptor INPUT_FD (corresponding to the named FILE) to
- a temporary file, and set *G_TMP and *G_TEMPFILE to the resulting stream
- and file name. Return true if successful. */
-
+/* A wrapper around mkstemp that gives us both an open stream pointer,
+ FP, and the corresponding FILE_NAME. Always return the same FP/name
+ pair, rewinding/truncating it upon each reuse. */
static bool
-copy_to_temp (FILE **g_tmp, char **g_tempfile, int input_fd, char const *file)
+temp_stream (FILE **fp, char **file_name)
{
- static char *template = NULL;
- static char const *tempdir;
- char *tempfile;
- FILE *tmp;
- int fd;
-
- if (template == NULL)
+ static char *tempfile = NULL;
+ static FILE *tmp_fp;
+ if (tempfile == NULL)
{
- char const * const Template = "%s/tacXXXXXX";
- tempdir = getenv ("TMPDIR");
+ char const *t = getenv ("TMPDIR");
+ char const *tempdir = t ? t : DEFAULT_TMPDIR;
+ tempfile = mfile_name_concat (tempdir, "tacXXXXXX", NULL);
if (tempdir == NULL)
- tempdir = DEFAULT_TMPDIR;
-
- /* Subtract 2 for `%s' and add 1 for the trailing NUL byte. */
- template = xmalloc (strlen (tempdir) + strlen (Template) - 2 + 1);
- sprintf (template, Template, tempdir);
+ {
+ error (0, 0, _("memory exhausted"));
+ return false;
+ }
+
+ /* FIXME: there's a small window between a successful mkstemp call
+ and the unlink that's performed by record_or_unlink_tempfile.
+ If we're interrupted in that interval, this code fails to remove
+ the temporary file. On systems that define DONT_UNLINK_WHILE_OPEN,
+ the window is much larger -- it extends to the atexit-called
+ unlink_tempfile.
+ FIXME: clean up upon fatal signal. Don't block them, in case
+ $TMPFILE is a remote file system. */
+
+ int fd = mkstemp (tempfile);
+ if (fd < 0)
+ {
+ error (0, errno, _("failed to create temporary file in %s"),
+ quoteaf (tempdir));
+ goto Reset;
+ }
+
+ tmp_fp = fdopen (fd, (O_BINARY ? "w+b" : "w+"));
+ if (! tmp_fp)
+ {
+ error (0, errno, _("failed to open %s for writing"),
+ quoteaf (tempfile));
+ close (fd);
+ unlink (tempfile);
+ Reset:
+ free (tempfile);
+ tempfile = NULL;
+ return false;
+ }
+
+ record_or_unlink_tempfile (tempfile, tmp_fp);
}
-
- /* FIXME: there's a small window between a successful mkstemp call
- and the unlink that's performed by record_or_unlink_tempfile.
- If we're interrupted in that interval, this code fails to remove
- the temporary file. On systems that define DONT_UNLINK_WHILE_OPEN,
- the window is much larger -- it extends to the atexit-called
- unlink_tempfile.
- FIXME: clean up upon fatal signal. Don't block them, in case
- $TMPFILE is a remote file system. */
-
- tempfile = template;
- fd = mkstemp (template);
- if (fd < 0)
+ else
{
- error (0, errno, _("cannot create temporary file %s"), quote (tempfile));
- return false;
+ if (fseeko (tmp_fp, 0, SEEK_SET) < 0
+ || ftruncate (fileno (tmp_fp), 0) < 0)
+ {
+ error (0, errno, _("failed to rewind stream for %s"),
+ quoteaf (tempfile));
+ return false;
+ }
}
- tmp = fdopen (fd, (O_BINARY ? "w+b" : "w+"));
- if (! tmp)
- {
- error (0, errno, _("cannot open %s for writing"), quote (tempfile));
- close (fd);
- unlink (tempfile);
- return false;
- }
+ *fp = tmp_fp;
+ *file_name = tempfile;
+ return true;
+}
- record_or_unlink_tempfile (tempfile, tmp);
+/* Copy from file descriptor INPUT_FD (corresponding to the named FILE) to
+ a temporary file, and set *G_TMP and *G_TEMPFILE to the resulting stream
+ and file name. Return the number of bytes copied, or -1 on error. */
+
+static off_t
+copy_to_temp (FILE **g_tmp, char **g_tempfile, int input_fd, char const *file)
+{
+ FILE *fp;
+ char *file_name;
+ uintmax_t bytes_copied = 0;
+ if (!temp_stream (&fp, &file_name))
+ return -1;
while (1)
{
size_t bytes_read = safe_read (input_fd, G_buffer, read_size);
if (bytes_read == 0)
- break;
+ break;
if (bytes_read == SAFE_READ_ERROR)
- {
- error (0, errno, _("%s: read error"), quotearg_colon (file));
- goto Fail;
- }
-
- if (fwrite (G_buffer, 1, bytes_read, tmp) != bytes_read)
- {
- error (0, errno, _("%s: write error"), quotearg_colon (tempfile));
- goto Fail;
- }
+ {
+ error (0, errno, _("%s: read error"), quotef (file));
+ goto Fail;
+ }
+
+ if (fwrite (G_buffer, 1, bytes_read, fp) != bytes_read)
+ {
+ error (0, errno, _("%s: write error"), quotef (file_name));
+ goto Fail;
+ }
+
+ /* Implicitly <= OFF_T_MAX due to preceding fwrite(),
+ but unsigned type used to avoid compiler warnings
+ not aware of this fact. */
+ bytes_copied += bytes_read;
}
- if (fflush (tmp) != 0)
+ if (fflush (fp) != 0)
{
- error (0, errno, _("%s: write error"), quotearg_colon (tempfile));
+ error (0, errno, _("%s: write error"), quotef (file_name));
goto Fail;
}
- *g_tmp = tmp;
- *g_tempfile = tempfile;
- return true;
+ *g_tmp = fp;
+ *g_tempfile = file_name;
+ return bytes_copied;
Fail:
- fclose (tmp);
- return false;
+ fclose (fp);
+ return -1;
}
/* Copy INPUT_FD to a temporary, then tac that file.
@@ -512,8 +549,12 @@ tac_nonseekable (int input_fd, const char *file)
{
FILE *tmp_stream;
char *tmp_file;
- return (copy_to_temp (&tmp_stream, &tmp_file, input_fd, file)
- && tac_seekable (fileno (tmp_stream), tmp_file));
+ off_t bytes_copied = copy_to_temp (&tmp_stream, &tmp_file, input_fd, file);
+ if (bytes_copied < 0)
+ return false;
+
+ bool ok = tac_seekable (fileno (tmp_stream), tmp_file, bytes_copied);
+ return ok;
}
/* Print FILE in reverse, copying it to a temporary
@@ -534,27 +575,28 @@ tac_file (const char *filename)
fd = STDIN_FILENO;
filename = _("standard input");
if (O_BINARY && ! isatty (STDIN_FILENO))
- freopen (NULL, "rb", stdin);
+ xfreopen (NULL, "rb", stdin);
}
else
{
fd = open (filename, O_RDONLY | O_BINARY);
if (fd < 0)
- {
- error (0, errno, _("cannot open %s for reading"), quote (filename));
- return false;
- }
+ {
+ error (0, errno, _("failed to open %s for reading"),
+ quoteaf (filename));
+ return false;
+ }
}
- file_size = lseek (fd, (off_t) 0, SEEK_END);
+ file_size = lseek (fd, 0, SEEK_END);
ok = (file_size < 0 || isatty (fd)
- ? tac_nonseekable (fd, filename)
- : tac_seekable (fd, filename));
+ ? tac_nonseekable (fd, filename)
+ : tac_seekable (fd, filename, file_size));
if (!is_stdin && close (fd) != 0)
{
- error (0, errno, _("%s: read error"), quotearg_colon (filename));
+ error (0, errno, _("%s: read error"), quotef (filename));
ok = false;
}
return ok;
@@ -574,7 +616,7 @@ main (int argc, char **argv)
char const *const *file;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -588,44 +630,45 @@ main (int argc, char **argv)
while ((optc = getopt_long (argc, argv, "brs:", longopts, NULL)) != -1)
{
switch (optc)
- {
- case 'b':
- separator_ends_record = false;
- break;
- case 'r':
- sentinel_length = 0;
- break;
- case 's':
- separator = optarg;
- if (*separator == 0)
- error (EXIT_FAILURE, 0, _("separator cannot be empty"));
- break;
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'b':
+ separator_ends_record = false;
+ break;
+ case 'r':
+ sentinel_length = 0;
+ break;
+ case 's':
+ separator = optarg;
+ break;
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ usage (EXIT_FAILURE);
+ }
}
if (sentinel_length == 0)
{
+ if (*separator == 0)
+ error (EXIT_FAILURE, 0, _("separator cannot be empty"));
+
compiled_separator.buffer = NULL;
compiled_separator.allocated = 0;
compiled_separator.fastmap = compiled_separator_fastmap;
compiled_separator.translate = NULL;
error_message = re_compile_pattern (separator, strlen (separator),
- &compiled_separator);
+ &compiled_separator);
if (error_message)
- error (EXIT_FAILURE, 0, "%s", error_message);
+ error (EXIT_FAILURE, 0, "%s", (error_message));
}
else
- match_length = sentinel_length = strlen (separator);
+ match_length = sentinel_length = *separator ? strlen (separator) : 1;
read_size = INITIAL_READSIZE;
while (sentinel_length >= read_size / 2)
{
if (SIZE_MAX / 2 < read_size)
- xalloc_die ();
+ xalloc_die ();
read_size *= 2;
}
half_buffer_size = read_size + sentinel_length + 1;
@@ -635,7 +678,7 @@ main (int argc, char **argv)
G_buffer = xmalloc (G_buffer_size);
if (sentinel_length)
{
- strcpy (G_buffer, separator);
+ memcpy (G_buffer, separator, sentinel_length + 1);
G_buffer += sentinel_length;
}
else
@@ -644,11 +687,11 @@ main (int argc, char **argv)
}
file = (optind < argc
- ? (char const *const *) &argv[optind]
- : default_file_list);
+ ? (char const *const *) &argv[optind]
+ : default_file_list);
if (O_BINARY && ! isatty (STDOUT_FILENO))
- freopen (NULL, "wb", stdout);
+ xfreopen (NULL, "wb", stdout);
{
size_t i;
@@ -661,6 +704,15 @@ main (int argc, char **argv)
output ((char *) NULL, (char *) NULL);
if (have_read_stdin && close (STDIN_FILENO) < 0)
- error (EXIT_FAILURE, errno, "-");
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ {
+ error (0, errno, "-");
+ ok = false;
+ }
+
+#ifdef lint
+ size_t offset = sentinel_length ? sentinel_length : 1;
+ free (G_buffer - offset);
+#endif
+
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/tail.c b/src/tail.c
index 2582b9d..2a72a93 100644
--- a/src/tail.c
+++ b/src/tail.c
@@ -1,10 +1,10 @@
/* tail -- output the last part of file(s)
- Copyright (C) 1989, 90, 91, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Can display any amount of data, unlike the Unix version, which uses
a fixed size buffer and therefore can only deliver a limited number
@@ -21,7 +20,8 @@
Original version by Paul Rubin <phr@ocf.berkeley.edu>.
Extensions by David MacKenzie <djm@gnu.ai.mit.edu>.
- tail -f for multiple files by Ian Lance Taylor <ian@airs.com>. */
+ tail -f for multiple files by Ian Lance Taylor <ian@airs.com>.
+ inotify back-end by Giuseppe Scrivano <gscrivano@gnu.org>. */
#include <config.h>
@@ -36,21 +36,42 @@
#include "c-strtod.h"
#include "error.h"
#include "fcntl--.h"
-#include "inttostr.h"
#include "isapipe.h"
#include "posixver.h"
#include "quote.h"
#include "safe-read.h"
+#include "stat-size.h"
#include "stat-time.h"
+#include "xfreopen.h"
#include "xnanosleep.h"
+#include "xdectoint.h"
#include "xstrtol.h"
#include "xstrtod.h"
-/* The official name of this program (e.g., no `g' prefix). */
+#if HAVE_INOTIFY
+# include "hash.h"
+# include <sys/inotify.h>
+/* 'select' is used by tail_forever_inotify. */
+# include <sys/select.h>
+
+/* inotify needs to know if a file is local. */
+# include "fs.h"
+# include "fs-is-local.h"
+# if HAVE_SYS_STATFS_H
+# include <sys/statfs.h>
+# elif HAVE_SYS_VFS_H
+# include <sys/vfs.h>
+# endif
+#endif
+
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "tail"
#define AUTHORS \
- "Paul Rubin", "David MacKenzie, Ian Lance Taylor", "Jim Meyering"
+ proper_name ("Paul Rubin"), \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Ian Lance Taylor"), \
+ proper_name ("Jim Meyering")
/* Number of items to tail. */
#define DEFAULT_N_LINES 10
@@ -94,9 +115,6 @@ struct File_spec
/* The actual file name, or "-" for stdin. */
char *name;
- /* File descriptor on which the file is open; -1 if it's not open. */
- int fd;
-
/* Attributes of the file the last time we checked. */
off_t size;
struct timespec mtime;
@@ -104,24 +122,41 @@ struct File_spec
ino_t ino;
mode_t mode;
- /* 1 if O_NONBLOCK is clear, 0 if set, -1 if not known. */
- int blocking;
-
/* The specified name initially referred to a directory or some other
type for which tail isn't meaningful. Unlike for a permission problem
(tailable, below) once this is set, the name is not checked ever again. */
bool ignore;
- /* See description of DEFAULT_MAX_N_... below. */
- uintmax_t n_unchanged_stats;
+ /* See the description of fremote. */
+ bool remote;
/* A file is tailable if it exists, is readable, and is of type
IS_TAILABLE_FILE_TYPE. */
bool tailable;
+ /* File descriptor on which the file is open; -1 if it's not open. */
+ int fd;
+
/* The value of errno seen last time we checked this file. */
int errnum;
+ /* 1 if O_NONBLOCK is clear, 0 if set, -1 if not known. */
+ int blocking;
+
+#if HAVE_INOTIFY
+ /* The watch descriptor used by inotify. */
+ int wd;
+
+ /* The parent directory watch descriptor. It is used only
+ * when Follow_name is used. */
+ int parent_wd;
+
+ /* Offset in NAME of the basename part. */
+ size_t basename_start;
+#endif
+
+ /* See description of DEFAULT_MAX_N_... below. */
+ uintmax_t n_unchanged_stats;
};
/* Keep trying to open a file even if it is inaccessible when tail starts
@@ -145,6 +180,9 @@ static bool from_start;
/* If true, print filename headers. */
static bool print_headers;
+/* Character to split lines by. */
+static char line_end;
+
/* When to print the filename banners. */
enum header_mode
{
@@ -160,9 +198,6 @@ enum header_mode
static uintmax_t max_n_unchanged_stats_between_opens =
DEFAULT_MAX_N_UNCHANGED_STATS_BETWEEN_OPENS;
-/* The name this program was run with. */
-char *program_name;
-
/* The process ID of the process (presumably on the current host)
that is writing to all followed files. */
static pid_t pid;
@@ -175,6 +210,9 @@ static bool have_read_stdin;
more expensive) code unconditionally. Intended solely for testing. */
static bool presume_input_pipe;
+/* If nonzero then don't use inotify even if available. */
+static bool disable_inotify;
+
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
@@ -183,7 +221,8 @@ enum
MAX_UNCHANGED_STATS_OPTION,
PID_OPTION,
PRESUME_INPUT_PIPE_OPTION,
- LONG_FOLLOW_OPTION
+ LONG_FOLLOW_OPTION,
+ DISABLE_INOTIFY_OPTION
};
static struct option const long_options[] =
@@ -192,6 +231,8 @@ static struct option const long_options[] =
{"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, NULL,
+ DISABLE_INOTIFY_OPTION}, /* do not document */
{"pid", required_argument, NULL, PID_OPTION},
{"-presume-input-pipe", no_argument, NULL,
PRESUME_INPUT_PIPE_OPTION}, /* do not document */
@@ -200,6 +241,7 @@ static struct option const long_options[] =
{"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},
{NULL, 0, NULL, 0}
@@ -209,80 +251,77 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
- program_name);
+ program_name);
printf (_("\
Print the last %d lines of each FILE to standard output.\n\
With more than one FILE, precede each with a header giving the file name.\n\
-With no FILE, or when FILE is -, read standard input.\n\
-\n\
"), DEFAULT_N_LINES);
+
+ emit_stdin_note ();
+ emit_mandatory_arg_note ();
+
fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
-"), stdout);
- fputs (_("\
- --retry keep trying to open a file even if it is\n\
- inaccessible when tail starts or if it becomes\n\
- inaccessible later; useful when following by name,\n\
- i.e., with --follow=name\n\
- -c, --bytes=N output the last N bytes; alternatively, use +N to\n\
- output bytes starting with the Nth of each file\n\
+ -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 (_("\
-f, --follow[={name|descriptor}]\n\
output appended data as the file grows;\n\
- -f, --follow, and --follow=descriptor are\n\
- equivalent\n\
+ an absent option argument means 'descriptor'\n\
-F same as --follow=name --retry\n\
"), stdout);
printf (_("\
- -n, --lines=N output the last N lines, instead of the last %d;\n\
- or use +N to output lines starting with the Nth\n\
+ -n, --lines=[+]NUM output the last NUM lines, instead of the last %d;\n\
+ or use -n +NUM to output starting with line NUM\n\
--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\
+ 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_N_LINES,
- DEFAULT_MAX_N_UNCHANGED_STATS_BETWEEN_OPENS
- );
+ DEFAULT_N_LINES,
+ DEFAULT_MAX_N_UNCHANGED_STATS_BETWEEN_OPENS
+ );
fputs (_("\
--pid=PID with -f, terminate after process ID, PID dies\n\
-q, --quiet, --silent never output headers giving file names\n\
- -s, --sleep-interval=S with -f, sleep for approximately S seconds\n\
- (default 1.0) between iterations.\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);
fputs (_("\
\n\
-If the first character of N (the number of bytes or lines) is a `+',\n\
-print beginning with the Nth item from the start of each file, otherwise,\n\
-print the last N items in the file. N may have a multiplier suffix:\n\
-b 512, k 1024, m 1024*1024.\n\
+NUM may have a multiplier suffix:\n\
+b 512, kB 1000, K 1024, MB 1000*1000, M 1024*1024,\n\
+GB 1000*1000*1000, G 1024*1024*1024, and so on for T, P, E, Z, Y.\n\
\n\
"), stdout);
fputs (_("\
With --follow (-f), tail defaults to following the file descriptor, which\n\
means that even if a tail'ed file is renamed, tail will continue to track\n\
-its end. \
-"), stdout);
- fputs (_("\
-This default behavior is not desirable when you really want to\n\
+its end. This default behavior is not desirable when you really want to\n\
track the actual name of the file, not the file descriptor (e.g., log\n\
rotation). Use --follow=name in that case. That causes tail to track the\n\
-named file by reopening it periodically to see if it has been removed and\n\
-recreated by some other program.\n\
+named file in a way that accommodates renaming, removal and creation.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -297,14 +336,7 @@ valid_file_spec (struct File_spec const *f)
static char const *
pretty_name (struct File_spec const *f)
{
- return (STREQ (f->name, "-") ? "standard input" : f->name);
-}
-
-static void
-xwrite_stdout (char const *buffer, size_t n_bytes)
-{
- if (n_bytes > 0 && fwrite (buffer, 1, n_bytes, stdout) == 0)
- error (EXIT_FAILURE, errno, _("write error"));
+ return (STREQ (f->name, "-") ? _("standard input") : f->name);
}
/* Record a file F with descriptor FD, size SIZE, status ST, and
@@ -312,8 +344,8 @@ xwrite_stdout (char const *buffer, size_t n_bytes)
static void
record_open_fd (struct File_spec *f, int fd,
- off_t size, struct stat const *st,
- int blocking)
+ off_t size, struct stat const *st,
+ int blocking)
{
f->fd = fd;
f->size = size;
@@ -323,7 +355,7 @@ record_open_fd (struct File_spec *f, int fd,
f->mode = st->st_mode;
f->blocking = blocking;
f->n_unchanged_stats = 0;
- f->ignore = 0;
+ f->ignore = false;
}
/* Close the file with descriptor FD and name FILENAME. */
@@ -333,7 +365,7 @@ close_fd (int fd, const char *filename)
{
if (fd != -1 && fd != STDIN_FILENO && close (fd))
{
- error (0, errno, _("closing %s (fd=%d)"), filename, fd);
+ error (0, errno, _("closing %s (fd=%d)"), quoteaf (filename), fd);
}
}
@@ -346,6 +378,20 @@ write_header (const char *pretty_filename)
first_file = false;
}
+/* Write N_BYTES from BUFFER to stdout.
+ Exit immediately on error with a single diagnostic. */
+
+static void
+xwrite_stdout (char const *buffer, size_t n_bytes)
+{
+ if (n_bytes > 0 && fwrite (buffer, 1, n_bytes, stdout) < n_bytes)
+ {
+ clearerr (stdout); /* To avoid redundant close_stdout diagnostic. */
+ error (EXIT_FAILURE, errno, _("error writing %s"),
+ quoteaf ("standard output"));
+ }
+}
+
/* Read and output N_BYTES of file PRETTY_FILENAME starting at the current
position in FD. If N_BYTES is COPY_TO_EOF, then copy until end of file.
If N_BYTES is COPY_A_BUFFER, then copy at most one buffer's worth.
@@ -364,22 +410,22 @@ dump_remainder (const char *pretty_filename, int fd, uintmax_t n_bytes)
size_t n = MIN (n_remaining, BUFSIZ);
size_t bytes_read = safe_read (fd, buffer, n);
if (bytes_read == SAFE_READ_ERROR)
- {
- if (errno != EAGAIN)
- error (EXIT_FAILURE, errno, _("error reading %s"),
- quote (pretty_filename));
- break;
- }
+ {
+ if (errno != EAGAIN)
+ error (EXIT_FAILURE, errno, _("error reading %s"),
+ quoteaf (pretty_filename));
+ break;
+ }
if (bytes_read == 0)
- break;
+ break;
xwrite_stdout (buffer, bytes_read);
n_written += bytes_read;
if (n_bytes != COPY_TO_EOF)
- {
- n_remaining -= bytes_read;
- if (n_remaining == 0 || n_bytes == COPY_A_BUFFER)
- break;
- }
+ {
+ n_remaining -= bytes_read;
+ if (n_remaining == 0 || n_bytes == COPY_A_BUFFER)
+ break;
+ }
}
return n_written;
@@ -394,7 +440,7 @@ static off_t
xlseek (int fd, off_t offset, int whence, char const *filename)
{
off_t new_offset = lseek (fd, offset, whence);
- char buf[INT_BUFSIZE_BOUND (off_t)];
+ char buf[INT_BUFSIZE_BOUND (offset)];
char *s;
if (0 <= new_offset)
@@ -405,15 +451,15 @@ xlseek (int fd, off_t offset, int whence, char const *filename)
{
case SEEK_SET:
error (0, errno, _("%s: cannot seek to offset %s"),
- filename, s);
+ quotef (filename), s);
break;
case SEEK_CUR:
error (0, errno, _("%s: cannot seek to relative offset %s"),
- filename, s);
+ quotef (filename), s);
break;
case SEEK_END:
error (0, errno, _("%s: cannot seek to end-relative offset %s"),
- filename, s);
+ quotef (filename), s);
break;
default:
abort ();
@@ -423,7 +469,7 @@ xlseek (int fd, off_t offset, int whence, char const *filename)
}
/* Print the last N_LINES lines from the end of file FD.
- Go backward through the file, reading `BUFSIZ' bytes at a time (except
+ Go backward through the file, reading 'BUFSIZ' bytes at a time (except
probably the first), until we hit the start of the file or have
read NUMBER newlines.
START_POS is the starting position of the read pointer for the file
@@ -433,7 +479,7 @@ xlseek (int fd, off_t offset, int whence, char const *filename)
static bool
file_lines (const char *pretty_filename, int fd, uintmax_t n_lines,
- off_t start_pos, off_t end_pos, uintmax_t *read_pos)
+ off_t start_pos, off_t end_pos, uintmax_t *read_pos)
{
char buffer[BUFSIZ];
size_t bytes_read;
@@ -442,25 +488,25 @@ file_lines (const char *pretty_filename, int fd, uintmax_t n_lines,
if (n_lines == 0)
return true;
- /* Set `bytes_read' to the size of the last, probably partial, buffer;
- 0 < `bytes_read' <= `BUFSIZ'. */
+ /* Set 'bytes_read' to the size of the last, probably partial, buffer;
+ 0 < 'bytes_read' <= 'BUFSIZ'. */
bytes_read = (pos - start_pos) % BUFSIZ;
if (bytes_read == 0)
bytes_read = BUFSIZ;
- /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
+ /* Make 'pos' a multiple of 'BUFSIZ' (0 if the file is short), so that all
reads will be on block boundaries, which might increase efficiency. */
pos -= bytes_read;
xlseek (fd, pos, SEEK_SET, pretty_filename);
bytes_read = safe_read (fd, buffer, bytes_read);
if (bytes_read == SAFE_READ_ERROR)
{
- error (0, errno, _("error reading %s"), quote (pretty_filename));
+ error (0, errno, _("error reading %s"), quoteaf (pretty_filename));
return false;
}
*read_pos = pos + bytes_read;
/* Count the incomplete line on files that don't end with a newline. */
- if (bytes_read && buffer[bytes_read - 1] != '\n')
+ if (bytes_read && buffer[bytes_read - 1] != line_end)
--n_lines;
do
@@ -469,43 +515,43 @@ file_lines (const char *pretty_filename, int fd, uintmax_t n_lines,
size_t n = bytes_read;
while (n)
- {
- char const *nl;
- nl = memrchr (buffer, '\n', n);
- if (nl == NULL)
- break;
- n = nl - buffer;
- if (n_lines-- == 0)
- {
- /* If this newline isn't the last character in the buffer,
- output the part that is after it. */
- if (n != bytes_read - 1)
- xwrite_stdout (nl + 1, bytes_read - (n + 1));
- *read_pos += dump_remainder (pretty_filename, fd,
- end_pos - (pos + bytes_read));
- return true;
- }
- }
+ {
+ char const *nl;
+ nl = memrchr (buffer, line_end, n);
+ if (nl == NULL)
+ break;
+ n = nl - buffer;
+ if (n_lines-- == 0)
+ {
+ /* If this newline isn't the last character in the buffer,
+ output the part that is after it. */
+ if (n != bytes_read - 1)
+ xwrite_stdout (nl + 1, bytes_read - (n + 1));
+ *read_pos += dump_remainder (pretty_filename, fd,
+ end_pos - (pos + bytes_read));
+ return true;
+ }
+ }
/* Not enough newlines in that bufferfull. */
if (pos == start_pos)
- {
- /* Not enough lines in the file; print everything from
- start_pos to the end. */
- xlseek (fd, start_pos, SEEK_SET, pretty_filename);
- *read_pos = start_pos + dump_remainder (pretty_filename, fd,
- end_pos);
- return true;
- }
+ {
+ /* Not enough lines in the file; print everything from
+ start_pos to the end. */
+ xlseek (fd, start_pos, SEEK_SET, pretty_filename);
+ *read_pos = start_pos + dump_remainder (pretty_filename, fd,
+ end_pos);
+ return true;
+ }
pos -= BUFSIZ;
xlseek (fd, pos, SEEK_SET, pretty_filename);
bytes_read = safe_read (fd, buffer, BUFSIZ);
if (bytes_read == SAFE_READ_ERROR)
- {
- error (0, errno, _("error reading %s"), quote (pretty_filename));
- return false;
- }
+ {
+ error (0, errno, _("error reading %s"), quoteaf (pretty_filename));
+ return false;
+ }
*read_pos = pos + bytes_read;
}
@@ -521,7 +567,7 @@ file_lines (const char *pretty_filename, int fd, uintmax_t n_lines,
static bool
pipe_lines (const char *pretty_filename, int fd, uintmax_t n_lines,
- uintmax_t *read_pos)
+ uintmax_t *read_pos)
{
struct linebuffer
{
@@ -546,7 +592,7 @@ pipe_lines (const char *pretty_filename, int fd, uintmax_t n_lines,
{
n_read = safe_read (fd, tmp->buffer, BUFSIZ);
if (n_read == 0 || n_read == SAFE_READ_ERROR)
- break;
+ break;
tmp->nbytes = n_read;
*read_pos += n_read;
tmp->nlines = 0;
@@ -554,49 +600,49 @@ pipe_lines (const char *pretty_filename, int fd, uintmax_t n_lines,
/* Count the number of newlines just read. */
{
- char const *buffer_end = tmp->buffer + n_read;
- char const *p = tmp->buffer;
- while ((p = memchr (p, '\n', buffer_end - p)))
- {
- ++p;
- ++tmp->nlines;
- }
+ char const *buffer_end = tmp->buffer + n_read;
+ char const *p = tmp->buffer;
+ while ((p = memchr (p, line_end, buffer_end - p)))
+ {
+ ++p;
+ ++tmp->nlines;
+ }
}
total_lines += tmp->nlines;
/* If there is enough room in the last buffer read, just append the new
- one to it. This is because when reading from a pipe, `n_read' can
+ one to it. This is because when reading from a pipe, 'n_read' can
often be very small. */
if (tmp->nbytes + last->nbytes < BUFSIZ)
- {
- memcpy (&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
- last->nbytes += tmp->nbytes;
- last->nlines += tmp->nlines;
- }
+ {
+ memcpy (&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
+ last->nbytes += tmp->nbytes;
+ last->nlines += tmp->nlines;
+ }
else
- {
- /* If there's not enough room, link the new buffer onto the end of
- the list, then either free up the oldest buffer for the next
- read if that would leave enough lines, or else malloc a new one.
- Some compaction mechanism is possible but probably not
- worthwhile. */
- last = last->next = tmp;
- if (total_lines - first->nlines > n_lines)
- {
- tmp = first;
- total_lines -= first->nlines;
- first = first->next;
- }
- else
- tmp = xmalloc (sizeof (LBUFFER));
- }
+ {
+ /* If there's not enough room, link the new buffer onto the end of
+ the list, then either free up the oldest buffer for the next
+ read if that would leave enough lines, or else malloc a new one.
+ Some compaction mechanism is possible but probably not
+ worthwhile. */
+ last = last->next = tmp;
+ if (total_lines - first->nlines > n_lines)
+ {
+ tmp = first;
+ total_lines -= first->nlines;
+ first = first->next;
+ }
+ else
+ tmp = xmalloc (sizeof (LBUFFER));
+ }
}
free (tmp);
if (n_read == SAFE_READ_ERROR)
{
- error (0, errno, _("error reading %s"), quote (pretty_filename));
+ error (0, errno, _("error reading %s"), quoteaf (pretty_filename));
ok = false;
goto free_lbuffers;
}
@@ -610,7 +656,7 @@ pipe_lines (const char *pretty_filename, int fd, uintmax_t n_lines,
goto free_lbuffers;
/* Count the incomplete line on files that don't end with a newline. */
- if (last->buffer[last->nbytes - 1] != '\n')
+ if (last->buffer[last->nbytes - 1] != line_end)
{
++last->nlines;
++total_lines;
@@ -627,15 +673,15 @@ pipe_lines (const char *pretty_filename, int fd, uintmax_t n_lines,
char const *buffer_end = tmp->buffer + tmp->nbytes;
if (total_lines > n_lines)
{
- /* Skip `total_lines' - `n_lines' newlines. We made sure that
- `total_lines' - `n_lines' <= `tmp->nlines'. */
- size_t j;
- for (j = total_lines - n_lines; j; --j)
- {
- beg = memchr (beg, '\n', buffer_end - beg);
- assert (beg);
- ++beg;
- }
+ /* Skip 'total_lines' - 'n_lines' newlines. We made sure that
+ 'total_lines' - 'n_lines' <= 'tmp->nlines'. */
+ size_t j;
+ for (j = total_lines - n_lines; j; --j)
+ {
+ beg = memchr (beg, line_end, buffer_end - beg);
+ assert (beg);
+ ++beg;
+ }
}
xwrite_stdout (beg, buffer_end - beg);
@@ -660,7 +706,7 @@ free_lbuffers:
static bool
pipe_bytes (const char *pretty_filename, int fd, uintmax_t n_bytes,
- uintmax_t *read_pos)
+ uintmax_t *read_pos)
{
struct charbuffer
{
@@ -685,46 +731,46 @@ pipe_bytes (const char *pretty_filename, int fd, uintmax_t n_bytes,
{
n_read = safe_read (fd, tmp->buffer, BUFSIZ);
if (n_read == 0 || n_read == SAFE_READ_ERROR)
- break;
+ break;
*read_pos += n_read;
tmp->nbytes = n_read;
tmp->next = NULL;
total_bytes += tmp->nbytes;
/* If there is enough room in the last buffer read, just append the new
- one to it. This is because when reading from a pipe, `nbytes' can
+ one to it. This is because when reading from a pipe, 'nbytes' can
often be very small. */
if (tmp->nbytes + last->nbytes < BUFSIZ)
- {
- memcpy (&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
- last->nbytes += tmp->nbytes;
- }
+ {
+ memcpy (&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
+ last->nbytes += tmp->nbytes;
+ }
else
- {
- /* If there's not enough room, link the new buffer onto the end of
- the list, then either free up the oldest buffer for the next
- read if that would leave enough characters, or else malloc a new
- one. Some compaction mechanism is possible but probably not
- worthwhile. */
- last = last->next = tmp;
- if (total_bytes - first->nbytes > n_bytes)
- {
- tmp = first;
- total_bytes -= first->nbytes;
- first = first->next;
- }
- else
- {
- tmp = xmalloc (sizeof (CBUFFER));
- }
- }
+ {
+ /* If there's not enough room, link the new buffer onto the end of
+ the list, then either free up the oldest buffer for the next
+ read if that would leave enough characters, or else malloc a new
+ one. Some compaction mechanism is possible but probably not
+ worthwhile. */
+ last = last->next = tmp;
+ if (total_bytes - first->nbytes > n_bytes)
+ {
+ tmp = first;
+ total_bytes -= first->nbytes;
+ first = first->next;
+ }
+ else
+ {
+ tmp = xmalloc (sizeof (CBUFFER));
+ }
+ }
}
free (tmp);
if (n_read == SAFE_READ_ERROR)
{
- error (0, errno, _("error reading %s"), quote (pretty_filename));
+ error (0, errno, _("error reading %s"), quoteaf (pretty_filename));
ok = false;
goto free_cbuffers;
}
@@ -735,7 +781,7 @@ pipe_bytes (const char *pretty_filename, int fd, uintmax_t n_bytes,
total_bytes -= tmp->nbytes;
/* Find the correct beginning, then print the rest of the file.
- We made sure that `total_bytes' - `n_bytes' <= `tmp->nbytes'. */
+ We made sure that 'total_bytes' - 'n_bytes' <= 'tmp->nbytes'. */
if (total_bytes > n_bytes)
i = total_bytes - n_bytes;
else
@@ -761,7 +807,7 @@ free_cbuffers:
static int
start_bytes (const char *pretty_filename, int fd, uintmax_t n_bytes,
- uintmax_t *read_pos)
+ uintmax_t *read_pos)
{
char buffer[BUFSIZ];
@@ -769,22 +815,22 @@ start_bytes (const char *pretty_filename, int fd, uintmax_t n_bytes,
{
size_t bytes_read = safe_read (fd, buffer, BUFSIZ);
if (bytes_read == 0)
- return -1;
+ return -1;
if (bytes_read == SAFE_READ_ERROR)
- {
- error (0, errno, _("error reading %s"), quote (pretty_filename));
- return 1;
- }
- read_pos += bytes_read;
+ {
+ error (0, errno, _("error reading %s"), quoteaf (pretty_filename));
+ return 1;
+ }
+ *read_pos += bytes_read;
if (bytes_read <= n_bytes)
- n_bytes -= bytes_read;
+ n_bytes -= bytes_read;
else
- {
- size_t n_remaining = bytes_read - n_bytes;
- if (n_remaining)
- xwrite_stdout (&buffer[n_bytes], n_remaining);
- break;
- }
+ {
+ size_t n_remaining = bytes_read - n_bytes;
+ if (n_remaining)
+ xwrite_stdout (&buffer[n_bytes], n_remaining);
+ break;
+ }
}
return 0;
@@ -796,7 +842,7 @@ start_bytes (const char *pretty_filename, int fd, uintmax_t n_bytes,
static int
start_lines (const char *pretty_filename, int fd, uintmax_t n_lines,
- uintmax_t *read_pos)
+ uintmax_t *read_pos)
{
if (n_lines == 0)
return 0;
@@ -804,38 +850,86 @@ start_lines (const char *pretty_filename, int fd, uintmax_t n_lines,
while (1)
{
char buffer[BUFSIZ];
- char *p = buffer;
size_t bytes_read = safe_read (fd, buffer, BUFSIZ);
- char *buffer_end = buffer + bytes_read;
if (bytes_read == 0) /* EOF */
- return -1;
+ return -1;
if (bytes_read == SAFE_READ_ERROR) /* error */
- {
- error (0, errno, _("error reading %s"), quote (pretty_filename));
- return 1;
- }
+ {
+ error (0, errno, _("error reading %s"), quoteaf (pretty_filename));
+ return 1;
+ }
+
+ char *buffer_end = buffer + bytes_read;
*read_pos += bytes_read;
- while ((p = memchr (p, '\n', buffer_end - p)))
- {
- ++p;
- if (--n_lines == 0)
- {
- if (p < buffer_end)
- xwrite_stdout (p, buffer_end - p);
- return 0;
- }
- }
+ char *p = buffer;
+ while ((p = memchr (p, line_end, buffer_end - p)))
+ {
+ ++p;
+ if (--n_lines == 0)
+ {
+ if (p < buffer_end)
+ xwrite_stdout (p, buffer_end - p);
+ return 0;
+ }
+ }
}
}
-/* FIXME: describe */
+#if HAVE_INOTIFY
+/* Without inotify support, always return false. Otherwise, return false
+ when FD is open on a file known to reside on a local file system.
+ If fstatfs fails, give a diagnostic and return true.
+ If fstatfs cannot be called, return true. */
+static bool
+fremote (int fd, const char *name)
+{
+ bool remote = true; /* be conservative (poll by default). */
+
+# if HAVE_FSTATFS && HAVE_STRUCT_STATFS_F_TYPE && defined __linux__
+ struct statfs buf;
+ int err = fstatfs (fd, &buf);
+ if (err != 0)
+ {
+ /* On at least linux-2.6.38, fstatfs fails with ENOSYS when FD
+ is open on a pipe. Treat that like a remote file. */
+ if (errno != ENOSYS)
+ error (0, errno, _("cannot determine location of %s. "
+ "reverting to polling"), quoteaf (name));
+ }
+ else
+ {
+ switch (is_local_fs_type (buf.f_type))
+ {
+ case 0:
+ break;
+ case -1:
+ /* Treat unrecognized file systems as "remote", so caller polls.
+ Note README-release has instructions for syncing the internal
+ list with the latest Linux kernel file system constants. */
+ break;
+ case 1:
+ remote = false;
+ break;
+ default:
+ assert (!"unexpected return value from is_local_fs_type");
+ }
+ }
+# endif
+
+ return remote;
+}
+#else
+/* Without inotify support, whether a file is remote is irrelevant.
+ Always return "false" in that case. */
+# define fremote(fd, name) false
+#endif
+/* open/fstat F->name and handle changes. */
static void
recheck (struct File_spec *f, bool blocking)
{
- /* open/fstat the file and announce if dev/ino have changed */
struct stat new_stats;
bool ok = true;
bool is_stdin = (STREQ (f->name, "-"));
@@ -843,8 +937,8 @@ recheck (struct File_spec *f, bool blocking)
int prev_errnum = f->errnum;
bool new_file;
int fd = (is_stdin
- ? STDIN_FILENO
- : open (f->name, O_RDONLY | (blocking ? 0 : O_NONBLOCK)));
+ ? STDIN_FILENO
+ : open (f->name, O_RDONLY | (blocking ? 0 : O_NONBLOCK)));
assert (valid_file_spec (f));
@@ -852,30 +946,41 @@ recheck (struct File_spec *f, bool blocking)
then mark the file as not tailable. */
f->tailable = !(reopen_inaccessible_files && fd == -1);
- if (fd == -1 || fstat (fd, &new_stats) < 0)
+ if (! disable_inotify && ! lstat (f->name, &new_stats)
+ && S_ISLNK (new_stats.st_mode))
+ {
+ /* Diagnose the edge case where a regular file is changed
+ to a symlink. We avoid inotify with symlinks since
+ it's awkward to match between symlink name and target. */
+ ok = false;
+ f->errnum = -1;
+ f->ignore = true;
+
+ error (0, 0, _("%s has been replaced with a symbolic link. "
+ "giving up on this name"), quoteaf (pretty_name (f)));
+ }
+ else if (fd == -1 || fstat (fd, &new_stats) < 0)
{
ok = false;
f->errnum = errno;
if (!f->tailable)
- {
- if (was_tailable)
- {
- /* FIXME-maybe: detect the case in which the file first becomes
- unreadable (perms), and later becomes readable again and can
- be seen to be the same file (dev/ino). Otherwise, tail prints
- the entire contents of the file when it becomes readable. */
- error (0, f->errnum, _("%s has become inaccessible"),
- quote (pretty_name (f)));
- }
- else
- {
- /* say nothing... it's still not tailable */
- }
- }
+ {
+ if (was_tailable)
+ {
+ /* FIXME-maybe: detect the case in which the file first becomes
+ unreadable (perms), and later becomes readable again and can
+ be seen to be the same file (dev/ino). Otherwise, tail prints
+ the entire contents of the file when it becomes readable. */
+ error (0, f->errnum, _("%s has become inaccessible"),
+ quoteaf (pretty_name (f)));
+ }
+ else
+ {
+ /* say nothing... it's still not tailable */
+ }
+ }
else if (prev_errnum != errno)
- {
- error (0, errno, "%s", pretty_name (f));
- }
+ error (0, errno, "%s", quotef (pretty_name (f)));
}
else if (!IS_TAILABLE_FILE_TYPE (new_stats.st_mode))
{
@@ -883,9 +988,18 @@ recheck (struct File_spec *f, bool blocking)
f->errnum = -1;
error (0, 0, _("%s has been replaced with an untailable file;\
giving up on this name"),
- quote (pretty_name (f)));
+ quoteaf (pretty_name (f)));
f->ignore = true;
}
+ else if (!disable_inotify && fremote (fd, pretty_name (f)))
+ {
+ ok = false;
+ f->errnum = -1;
+ error (0, 0, _("%s has been replaced with a remote file. "
+ "giving up on this name"), quoteaf (pretty_name (f)));
+ f->ignore = true;
+ f->remote = true;
+ }
else
{
f->errnum = 0;
@@ -902,44 +1016,47 @@ recheck (struct File_spec *f, bool blocking)
{
new_file = true;
assert (f->fd == -1);
- error (0, 0, _("%s has become accessible"), quote (pretty_name (f)));
+ error (0, 0, _("%s has become accessible"), quoteaf (pretty_name (f)));
+ }
+ else if (f->fd == -1)
+ {
+ /* A new file even when inodes haven't changed as <dev,inode>
+ pairs can be reused, and we know the file was missing
+ on the previous iteration. Note this also means the file
+ is redisplayed in --follow=name mode if renamed away from
+ and back to a monitored name. */
+ new_file = true;
+
+ error (0, 0,
+ _("%s has appeared; following new file"),
+ quoteaf (pretty_name (f)));
}
else if (f->ino != new_stats.st_ino || f->dev != new_stats.st_dev)
{
+ /* File has been replaced (e.g., via log rotation) --
+ tail the new one. */
new_file = true;
- if (f->fd == -1)
- {
- error (0, 0,
- _("%s has appeared; following end of new file"),
- quote (pretty_name (f)));
- }
- else
- {
- /* Close the old one. */
- close_fd (f->fd, pretty_name (f));
-
- /* File has been replaced (e.g., via log rotation) --
- tail the new one. */
- error (0, 0,
- _("%s has been replaced; following end of new file"),
- quote (pretty_name (f)));
- }
+
+ error (0, 0,
+ _("%s has been replaced; following new file"),
+ quoteaf (pretty_name (f)));
+
+ /* Close the old one. */
+ close_fd (f->fd, pretty_name (f));
+
}
else
{
- if (f->fd == -1)
- {
- /* This happens when one iteration finds the file missing,
- then the preceding <dev,inode> pair is reused as the
- file is recreated. */
- new_file = true;
- }
- else
- {
- close_fd (fd, pretty_name (f));
- }
+ /* No changes detected, so close new fd. */
+ close_fd (fd, pretty_name (f));
}
+ /* FIXME: When a log is rotated, daemons tend to log to the
+ old file descriptor until the new file is present and
+ the daemon is sent a signal. Therefore tail may miss entries
+ being written to the old file. Perhaps we should keep
+ the older file open and continue to monitor it until
+ data is written to a new file. */
if (new_file)
{
/* Start at the beginning of the file. */
@@ -949,20 +1066,36 @@ recheck (struct File_spec *f, bool blocking)
}
/* Return true if any of the N_FILES files in F are live, i.e., have
- open file descriptors. */
+ open file descriptors, or should be checked again (see --retry).
+ When following descriptors, checking should only continue when any
+ of the files is not yet ignored. */
static bool
-any_live_files (const struct File_spec *f, int n_files)
+any_live_files (const struct File_spec *f, size_t n_files)
{
- int i;
+ size_t i;
+
+ if (reopen_inaccessible_files && follow_mode == Follow_name)
+ return true; /* continue following for -F option */
for (i = 0; i < n_files; i++)
- if (0 <= f[i].fd)
- return true;
+ {
+ if (0 <= f[i].fd)
+ {
+ return true;
+ }
+ else
+ {
+ if (reopen_inaccessible_files && follow_mode == Follow_descriptor)
+ if (! f[i].ignore)
+ return true;
+ }
+ }
+
return false;
}
-/* Tail NFILES files forever, or until killed.
+/* Tail N_FILES files forever, or until killed.
The pertinent information for each file is stored in an entry of F.
Loop over each of them, doing an fstat to see if they have changed size,
and an occasional open/fstat to see if any dev/ino pair has changed.
@@ -970,214 +1103,690 @@ any_live_files (const struct File_spec *f, int n_files)
while and try again. Continue until the user interrupts us. */
static void
-tail_forever (struct File_spec *f, int nfiles, double sleep_interval)
+tail_forever (struct File_spec *f, size_t n_files, double sleep_interval)
{
/* Use blocking I/O as an optimization, when it's easy. */
bool blocking = (pid == 0 && follow_mode == Follow_descriptor
- && nfiles == 1 && ! S_ISREG (f[0].mode));
- int last;
+ && n_files == 1 && ! S_ISREG (f[0].mode));
+ size_t last;
bool writer_is_dead = false;
- last = nfiles - 1;
+ last = n_files - 1;
while (1)
{
- int i;
+ size_t i;
bool any_input = false;
- for (i = 0; i < nfiles; i++)
- {
- int fd;
- char const *name;
- mode_t mode;
- struct stat stats;
- uintmax_t bytes_read;
-
- if (f[i].ignore)
- continue;
-
- if (f[i].fd < 0)
- {
- recheck (&f[i], blocking);
- continue;
- }
-
- fd = f[i].fd;
- name = pretty_name (&f[i]);
- mode = f[i].mode;
-
- if (f[i].blocking != blocking)
- {
- int old_flags = fcntl (fd, F_GETFL);
- int new_flags = old_flags | (blocking ? 0 : O_NONBLOCK);
- if (old_flags < 0
- || (new_flags != old_flags
- && fcntl (fd, F_SETFL, new_flags) == -1))
- {
- /* Don't update f[i].blocking if fcntl fails. */
- if (S_ISREG (f[i].mode) && errno == EPERM)
- {
- /* This happens when using tail -f on a file with
- the append-only attribute. */
- }
- else
- error (EXIT_FAILURE, errno,
- _("%s: cannot change nonblocking mode"), name);
- }
- else
- f[i].blocking = blocking;
- }
-
- if (!f[i].blocking)
- {
- if (fstat (fd, &stats) != 0)
- {
- f[i].fd = -1;
- f[i].errnum = errno;
- error (0, errno, "%s", name);
- continue;
- }
-
- if (f[i].mode == stats.st_mode
- && (! S_ISREG (stats.st_mode) || f[i].size == stats.st_size)
- && timespec_cmp (f[i].mtime, get_stat_mtime (&stats)) == 0)
- {
- if ((max_n_unchanged_stats_between_opens
- <= f[i].n_unchanged_stats++)
- && follow_mode == Follow_name)
- {
- recheck (&f[i], f[i].blocking);
- f[i].n_unchanged_stats = 0;
- }
- continue;
- }
-
- /* This file has changed. Print out what we can, and
- then keep looping. */
-
- f[i].mtime = get_stat_mtime (&stats);
- f[i].mode = stats.st_mode;
-
- /* reset counter */
- f[i].n_unchanged_stats = 0;
-
- if (S_ISREG (mode) && stats.st_size < f[i].size)
- {
- error (0, 0, _("%s: file truncated"), name);
- last = i;
- xlseek (fd, stats.st_size, SEEK_SET, name);
- f[i].size = stats.st_size;
- continue;
- }
-
- if (i != last)
- {
- if (print_headers)
- write_header (name);
- last = i;
- }
- }
-
- bytes_read = dump_remainder (name, fd,
- (f[i].blocking
- ? COPY_A_BUFFER : COPY_TO_EOF));
- any_input |= (bytes_read != 0);
- f[i].size += bytes_read;
- }
-
- if (! any_live_files (f, nfiles) && ! reopen_inaccessible_files)
- {
- error (0, 0, _("no files remaining"));
- break;
- }
-
- if ((!any_input | blocking) && fflush (stdout) != 0)
- error (EXIT_FAILURE, errno, _("write error"));
+ for (i = 0; i < n_files; i++)
+ {
+ int fd;
+ char const *name;
+ mode_t mode;
+ struct stat stats;
+ uintmax_t bytes_read;
+
+ if (f[i].ignore)
+ continue;
+
+ if (f[i].fd < 0)
+ {
+ recheck (&f[i], blocking);
+ continue;
+ }
+
+ fd = f[i].fd;
+ name = pretty_name (&f[i]);
+ mode = f[i].mode;
+
+ if (f[i].blocking != blocking)
+ {
+ int old_flags = fcntl (fd, F_GETFL);
+ int new_flags = old_flags | (blocking ? 0 : O_NONBLOCK);
+ if (old_flags < 0
+ || (new_flags != old_flags
+ && fcntl (fd, F_SETFL, new_flags) == -1))
+ {
+ /* Don't update f[i].blocking if fcntl fails. */
+ if (S_ISREG (f[i].mode) && errno == EPERM)
+ {
+ /* This happens when using tail -f on a file with
+ the append-only attribute. */
+ }
+ else
+ error (EXIT_FAILURE, errno,
+ _("%s: cannot change nonblocking mode"),
+ quotef (name));
+ }
+ else
+ f[i].blocking = blocking;
+ }
+
+ if (!f[i].blocking)
+ {
+ if (fstat (fd, &stats) != 0)
+ {
+ f[i].fd = -1;
+ f[i].errnum = errno;
+ error (0, errno, "%s", quotef (name));
+ close (fd); /* ignore failure */
+ continue;
+ }
+
+ if (f[i].mode == stats.st_mode
+ && (! S_ISREG (stats.st_mode) || f[i].size == stats.st_size)
+ && timespec_cmp (f[i].mtime, get_stat_mtime (&stats)) == 0)
+ {
+ if ((max_n_unchanged_stats_between_opens
+ <= f[i].n_unchanged_stats++)
+ && follow_mode == Follow_name)
+ {
+ recheck (&f[i], f[i].blocking);
+ f[i].n_unchanged_stats = 0;
+ }
+ continue;
+ }
+
+ /* This file has changed. Print out what we can, and
+ then keep looping. */
+
+ f[i].mtime = get_stat_mtime (&stats);
+ f[i].mode = stats.st_mode;
+
+ /* reset counter */
+ f[i].n_unchanged_stats = 0;
+
+ /* XXX: This is only a heuristic, as the file may have also
+ been truncated and written to if st_size >= size
+ (in which case we ignore new data <= size). */
+ if (S_ISREG (mode) && stats.st_size < f[i].size)
+ {
+ error (0, 0, _("%s: file truncated"), quotef (name));
+ /* Assume the file was truncated to 0,
+ and therefore output all "new" data. */
+ xlseek (fd, 0, SEEK_SET, name);
+ f[i].size = 0;
+ }
+
+ if (i != last)
+ {
+ if (print_headers)
+ write_header (name);
+ last = i;
+ }
+ }
+
+ bytes_read = dump_remainder (name, fd,
+ (f[i].blocking
+ ? COPY_A_BUFFER : COPY_TO_EOF));
+ any_input |= (bytes_read != 0);
+ f[i].size += bytes_read;
+ }
+
+ if (! any_live_files (f, n_files))
+ {
+ error (0, 0, _("no files remaining"));
+ break;
+ }
+
+ if ((!any_input || blocking) && fflush (stdout) != 0)
+ error (EXIT_FAILURE, errno, _("write error"));
/* If nothing was read, sleep and/or check for dead writers. */
if (!any_input)
- {
- if (writer_is_dead)
- break;
-
- if (xnanosleep (sleep_interval))
- error (EXIT_FAILURE, errno, _("cannot read realtime clock"));
-
- /* Once the writer is dead, read the files once more to
- avoid a race condition. */
- writer_is_dead = (pid != 0
- && kill (pid, 0) != 0
- /* Handle the case in which you cannot send a
- signal to the writer, so kill fails and sets
- errno to EPERM. */
- && errno != EPERM);
- }
+ {
+ if (writer_is_dead)
+ break;
+
+ /* Once the writer is dead, read the files once more to
+ avoid a race condition. */
+ writer_is_dead = (pid != 0
+ && kill (pid, 0) != 0
+ /* Handle the case in which you cannot send a
+ signal to the writer, so kill fails and sets
+ errno to EPERM. */
+ && errno != EPERM);
+
+ if (!writer_is_dead && xnanosleep (sleep_interval))
+ error (EXIT_FAILURE, errno, _("cannot read realtime clock"));
+
+ }
}
}
+#if HAVE_INOTIFY
+
+/* Return true if any of the N_FILES files in F is remote, i.e., has
+ an open file descriptor and is on a network file system. */
+
+static bool
+any_remote_file (const struct File_spec *f, size_t n_files)
+{
+ size_t i;
+
+ for (i = 0; i < n_files; i++)
+ if (0 <= f[i].fd && f[i].remote)
+ return true;
+ return false;
+}
+
+/* Return true if any of the N_FILES files in F is non remote, i.e., has
+ an open file descriptor and is not on a network file system. */
+
+static bool
+any_non_remote_file (const struct File_spec *f, size_t n_files)
+{
+ size_t i;
+
+ for (i = 0; i < n_files; i++)
+ if (0 <= f[i].fd && ! f[i].remote)
+ return true;
+ return false;
+}
+
+/* Return true if any of the N_FILES files in F is a symlink.
+ Note we don't worry about the edge case where "-" exists,
+ since that will have the same consequences for inotify,
+ which is the only context this function is currently used. */
+
+static bool
+any_symlinks (const struct File_spec *f, size_t n_files)
+{
+ size_t i;
+
+ struct stat st;
+ for (i = 0; i < n_files; i++)
+ if (lstat (f[i].name, &st) == 0 && S_ISLNK (st.st_mode))
+ return true;
+ return false;
+}
+
+/* Return true if any of the N_FILES files in F represents
+ stdin and is tailable. */
+
+static bool
+tailable_stdin (const struct File_spec *f, size_t n_files)
+{
+ size_t i;
+
+ for (i = 0; i < n_files; i++)
+ if (!f[i].ignore && STREQ (f[i].name, "-"))
+ return true;
+ return false;
+}
+
+static size_t
+wd_hasher (const void *entry, size_t tabsize)
+{
+ const struct File_spec *spec = entry;
+ return spec->wd % tabsize;
+}
+
+static bool
+wd_comparator (const void *e1, const void *e2)
+{
+ const struct File_spec *spec1 = e1;
+ const struct File_spec *spec2 = e2;
+ return spec1->wd == spec2->wd;
+}
+
+/* Output (new) data for FSPEC->fd. */
+static void
+check_fspec (struct File_spec *fspec, struct File_spec **prev_fspec)
+{
+ struct stat stats;
+ char const *name;
+
+ if (fspec->fd == -1)
+ return;
+
+ name = pretty_name (fspec);
+
+ if (fstat (fspec->fd, &stats) != 0)
+ {
+ fspec->errnum = errno;
+ close_fd (fspec->fd, name);
+ fspec->fd = -1;
+ return;
+ }
+
+ /* XXX: This is only a heuristic, as the file may have also
+ been truncated and written to if st_size >= size
+ (in which case we ignore new data <= size).
+ Though in the inotify case it's more likely we'll get
+ separate events for truncate() and write(). */
+ if (S_ISREG (fspec->mode) && stats.st_size < fspec->size)
+ {
+ error (0, 0, _("%s: file truncated"), quotef (name));
+ xlseek (fspec->fd, 0, SEEK_SET, name);
+ fspec->size = 0;
+ }
+ else if (S_ISREG (fspec->mode) && stats.st_size == fspec->size
+ && timespec_cmp (fspec->mtime, get_stat_mtime (&stats)) == 0)
+ return;
+
+ if (fspec != *prev_fspec)
+ {
+ if (print_headers)
+ write_header (name);
+ *prev_fspec = fspec;
+ }
+
+ uintmax_t bytes_read = dump_remainder (name, fspec->fd, COPY_TO_EOF);
+ fspec->size += bytes_read;
+
+ if (fflush (stdout) != 0)
+ error (EXIT_FAILURE, errno, _("write error"));
+}
+
+/* Attempt to tail N_FILES files forever, or until killed.
+ Check modifications using the inotify events system.
+ Return false on error, or true to revert to polling. */
+static bool
+tail_forever_inotify (int wd, struct File_spec *f, size_t n_files,
+ double sleep_interval)
+{
+# if TAIL_TEST_SLEEP
+ /* Delay between open() and inotify_add_watch()
+ to help trigger different cases. */
+ xnanosleep (1000000);
+# endif
+ unsigned int max_realloc = 3;
+
+ /* Map an inotify watch descriptor to the name of the file it's watching. */
+ Hash_table *wd_to_name;
+
+ bool found_watchable_file = false;
+ bool tailed_but_unwatchable = false;
+ bool found_unwatchable_dir = false;
+ bool no_inotify_resources = false;
+ bool writer_is_dead = false;
+ struct File_spec *prev_fspec;
+ size_t evlen = 0;
+ char *evbuf;
+ size_t evbuf_off = 0;
+ size_t len = 0;
+
+ wd_to_name = hash_initialize (n_files, NULL, wd_hasher, wd_comparator, NULL);
+ if (! wd_to_name)
+ xalloc_die ();
+
+ /* The events mask used with inotify on files (not directories). */
+ uint32_t inotify_wd_mask = IN_MODIFY;
+ /* TODO: Perhaps monitor these events in Follow_descriptor mode also,
+ to tag reported file names with "deleted", "moved" etc. */
+ if (follow_mode == Follow_name)
+ inotify_wd_mask |= (IN_ATTRIB | IN_DELETE_SELF | IN_MOVE_SELF);
+
+ /* Add an inotify watch for each watched file. If -F is specified then watch
+ its parent directory too, in this way when they re-appear we can add them
+ again to the watch list. */
+ size_t i;
+ for (i = 0; i < n_files; i++)
+ {
+ if (!f[i].ignore)
+ {
+ size_t fnlen = strlen (f[i].name);
+ if (evlen < fnlen)
+ evlen = fnlen;
+
+ f[i].wd = -1;
+
+ if (follow_mode == Follow_name)
+ {
+ size_t dirlen = dir_len (f[i].name);
+ char prev = f[i].name[dirlen];
+ f[i].basename_start = last_component (f[i].name) - f[i].name;
+
+ f[i].name[dirlen] = '\0';
+
+ /* It's fine to add the same directory more than once.
+ In that case the same watch descriptor is returned. */
+ f[i].parent_wd = inotify_add_watch (wd, dirlen ? f[i].name : ".",
+ (IN_CREATE | IN_DELETE
+ | IN_MOVED_TO | IN_ATTRIB));
+
+ f[i].name[dirlen] = prev;
+
+ if (f[i].parent_wd < 0)
+ {
+ if (errno != ENOSPC) /* suppress confusing error. */
+ error (0, errno, _("cannot watch parent directory of %s"),
+ quoteaf (f[i].name));
+ else
+ error (0, 0, _("inotify resources exhausted"));
+ found_unwatchable_dir = true;
+ /* We revert to polling below. Note invalid uses
+ of the inotify API will still be diagnosed. */
+ break;
+ }
+ }
+
+ f[i].wd = inotify_add_watch (wd, f[i].name, inotify_wd_mask);
+
+ if (f[i].wd < 0)
+ {
+ if (f[i].fd != -1) /* already tailed. */
+ tailed_but_unwatchable = true;
+ if (errno == ENOSPC || errno == ENOMEM)
+ {
+ no_inotify_resources = true;
+ error (0, 0, _("inotify resources exhausted"));
+ break;
+ }
+ else if (errno != f[i].errnum)
+ error (0, errno, _("cannot watch %s"), quoteaf (f[i].name));
+ continue;
+ }
+
+ if (hash_insert (wd_to_name, &(f[i])) == NULL)
+ xalloc_die ();
+
+ found_watchable_file = true;
+ }
+ }
+
+ /* Linux kernel 2.6.24 at least has a bug where eventually, ENOSPC is always
+ returned by inotify_add_watch. In any case we should revert to polling
+ when there are no inotify resources. Also a specified directory may not
+ be currently present or accessible, so revert to polling. Also an already
+ tailed but unwatchable due rename/unlink race, should also revert. */
+ if (no_inotify_resources || found_unwatchable_dir
+ || (follow_mode == Follow_descriptor && tailed_but_unwatchable))
+ {
+ hash_free (wd_to_name);
+
+ errno = 0;
+ return true;
+ }
+ if (follow_mode == Follow_descriptor && !found_watchable_file)
+ return false;
+
+ prev_fspec = &(f[n_files - 1]);
+
+ /* Check files again. New files or data can be available since last time we
+ checked and before they are watched by inotify. */
+ for (i = 0; i < n_files; i++)
+ {
+ if (! f[i].ignore)
+ {
+ /* check for new files. */
+ if (follow_mode == Follow_name)
+ recheck (&(f[i]), false);
+ else if (f[i].fd != -1)
+ {
+ /* If the file was replaced in the small window since we tailed,
+ then assume the watch is on the wrong item (different to
+ that we've already produced output for), and so revert to
+ polling the original descriptor. */
+ struct stat stats;
+
+ if (stat (f[i].name, &stats) == 0
+ && (f[i].dev != stats.st_dev || f[i].ino != stats.st_ino))
+ {
+ error (0, errno, _("%s was replaced"),
+ quoteaf (pretty_name (&(f[i]))));
+ hash_free (wd_to_name);
+
+ errno = 0;
+ return true;
+ }
+ }
+
+ /* check for new data. */
+ check_fspec (&f[i], &prev_fspec);
+ }
+ }
+
+ evlen += sizeof (struct inotify_event) + 1;
+ evbuf = xmalloc (evlen);
+
+ /* Wait for inotify events and handle them. Events on directories
+ ensure that watched files can be re-added when following by name.
+ This loop blocks on the 'safe_read' call until a new event is notified.
+ But when --pid=P is specified, tail usually waits via the select. */
+ while (1)
+ {
+ struct File_spec *fspec;
+ struct inotify_event *ev;
+ void *void_ev;
+
+ /* When following by name without --retry, and the last file has
+ been unlinked or renamed-away, diagnose it and return. */
+ if (follow_mode == Follow_name
+ && ! reopen_inaccessible_files
+ && hash_get_n_entries (wd_to_name) == 0)
+ {
+ error (0, 0, _("no files remaining"));
+ return false;
+ }
+
+ /* When watching a PID, ensure that a read from WD will not block
+ indefinitely. */
+ if (pid)
+ {
+ if (writer_is_dead)
+ exit (EXIT_SUCCESS);
+
+ writer_is_dead = (kill (pid, 0) != 0 && errno != EPERM);
+
+ struct timeval delay; /* how long to wait for file changes. */
+ if (writer_is_dead)
+ delay.tv_sec = delay.tv_usec = 0;
+ else
+ {
+ delay.tv_sec = (time_t) sleep_interval;
+ delay.tv_usec = 1000000 * (sleep_interval - delay.tv_sec);
+ }
+
+ fd_set rfd;
+ FD_ZERO (&rfd);
+ FD_SET (wd, &rfd);
+
+ int file_change = select (wd + 1, &rfd, NULL, NULL, &delay);
+
+ if (file_change == 0)
+ continue;
+ else if (file_change == -1)
+ error (EXIT_FAILURE, errno, _("error monitoring inotify event"));
+ }
+
+ if (len <= evbuf_off)
+ {
+ len = safe_read (wd, evbuf, evlen);
+ evbuf_off = 0;
+
+ /* For kernels prior to 2.6.21, read returns 0 when the buffer
+ is too small. */
+ if ((len == 0 || (len == SAFE_READ_ERROR && errno == EINVAL))
+ && max_realloc--)
+ {
+ len = 0;
+ evlen *= 2;
+ evbuf = xrealloc (evbuf, evlen);
+ continue;
+ }
+
+ if (len == 0 || len == SAFE_READ_ERROR)
+ error (EXIT_FAILURE, errno, _("error reading inotify event"));
+ }
+
+ void_ev = evbuf + evbuf_off;
+ ev = void_ev;
+ evbuf_off += sizeof (*ev) + ev->len;
+
+ if (ev->len) /* event on ev->name in watched directory. */
+ {
+ size_t j;
+ for (j = 0; j < n_files; j++)
+ {
+ /* With N=hundreds of frequently-changing files, this O(N^2)
+ process might be a problem. FIXME: use a hash table? */
+ if (f[j].parent_wd == ev->wd
+ && STREQ (ev->name, f[j].name + f[j].basename_start))
+ break;
+ }
+
+ /* It is not a watched file. */
+ if (j == n_files)
+ continue;
+
+ fspec = &(f[j]);
+
+ int new_wd = -1;
+ bool deleting = !! (ev->mask & IN_DELETE);
+
+ if (! deleting)
+ {
+ /* Adding the same inode again will look up any existing wd. */
+ new_wd = inotify_add_watch (wd, f[j].name, inotify_wd_mask);
+ }
+
+ if (! deleting && new_wd < 0)
+ {
+ if (errno == ENOSPC || errno == ENOMEM)
+ {
+ error (0, 0, _("inotify resources exhausted"));
+ hash_free (wd_to_name);
+ errno = 0;
+ return true; /* revert to polling. */
+ }
+ else
+ {
+ /* Can get ENOENT for a dangling symlink for example. */
+ error (0, errno, _("cannot watch %s"), quoteaf (f[j].name));
+ }
+ /* We'll continue below after removing the existing watch. */
+ }
+
+ /* This will be false if only attributes of file change. */
+ bool new_watch;
+ new_watch = (! deleting) && (fspec->wd < 0 || new_wd != fspec->wd);
+
+ if (new_watch)
+ {
+ if (0 <= fspec->wd)
+ {
+ inotify_rm_watch (wd, fspec->wd);
+ hash_delete (wd_to_name, fspec);
+ }
+
+ fspec->wd = new_wd;
+
+ if (new_wd == -1)
+ continue;
+
+ /* If the file was moved then inotify will use the source file wd
+ for the destination file. Make sure the key is not present in
+ the table. */
+ struct File_spec *prev = hash_delete (wd_to_name, fspec);
+ if (prev && prev != fspec)
+ {
+ if (follow_mode == Follow_name)
+ recheck (prev, false);
+ prev->wd = -1;
+ close_fd (prev->fd, pretty_name (prev));
+ }
+
+ if (hash_insert (wd_to_name, fspec) == NULL)
+ xalloc_die ();
+ }
+
+ if (follow_mode == Follow_name)
+ recheck (fspec, false);
+ }
+ else
+ {
+ struct File_spec key;
+ key.wd = ev->wd;
+ fspec = hash_lookup (wd_to_name, &key);
+ }
+
+ if (! fspec)
+ continue;
+
+ if (ev->mask & (IN_ATTRIB | IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF))
+ {
+ /* Note for IN_MOVE_SELF (the file we're watching has
+ been clobbered via a rename) we leave the watch
+ in place since it may still be part of the set
+ of watched names. */
+ if (ev->mask & IN_DELETE_SELF)
+ {
+ inotify_rm_watch (wd, fspec->wd);
+ hash_delete (wd_to_name, fspec);
+ }
+
+ /* Note we get IN_ATTRIB for unlink() as st_nlink decrements.
+ The usual path is a close() done in recheck() triggers
+ an IN_DELETE_SELF event as the inode is removed.
+ However sometimes open() will succeed as even though
+ st_nlink is decremented, the dentry (cache) is not updated.
+ Thus we depend on the IN_DELETE event on the directory
+ to trigger processing for the removed file. */
+
+ recheck (fspec, false);
+
+ continue;
+ }
+ check_fspec (fspec, &prev_fspec);
+ }
+}
+#endif
+
/* Output the last N_BYTES bytes of file FILENAME open for reading in FD.
Return true if successful. */
static bool
tail_bytes (const char *pretty_filename, int fd, uintmax_t n_bytes,
- uintmax_t *read_pos)
+ uintmax_t *read_pos)
{
struct stat stats;
if (fstat (fd, &stats))
{
- error (0, errno, _("cannot fstat %s"), quote (pretty_filename));
+ error (0, errno, _("cannot fstat %s"), quoteaf (pretty_filename));
return false;
}
if (from_start)
{
if ( ! presume_input_pipe
- && S_ISREG (stats.st_mode) && n_bytes <= OFF_T_MAX)
- {
- xlseek (fd, n_bytes, SEEK_CUR, pretty_filename);
- *read_pos += n_bytes;
- }
+ && S_ISREG (stats.st_mode) && n_bytes <= OFF_T_MAX)
+ {
+ xlseek (fd, n_bytes, SEEK_CUR, pretty_filename);
+ *read_pos += n_bytes;
+ }
else
- {
- int t = start_bytes (pretty_filename, fd, n_bytes, read_pos);
- if (t)
- return t < 0;
- }
- *read_pos += dump_remainder (pretty_filename, fd, COPY_TO_EOF);
+ {
+ int t = start_bytes (pretty_filename, fd, n_bytes, read_pos);
+ if (t)
+ return t < 0;
+ }
+ n_bytes = COPY_TO_EOF;
}
else
{
- if ( ! presume_input_pipe
- && S_ISREG (stats.st_mode) && n_bytes <= OFF_T_MAX)
- {
- off_t current_pos = xlseek (fd, 0, SEEK_CUR, pretty_filename);
- off_t end_pos = xlseek (fd, 0, SEEK_END, pretty_filename);
- off_t diff = end_pos - current_pos;
- /* Be careful here. The current position may actually be
- beyond the end of the file. */
- off_t bytes_remaining = (diff = end_pos - current_pos) < 0 ? 0 : diff;
- off_t nb = n_bytes;
-
- if (bytes_remaining <= nb)
- {
- /* From the current position to end of file, there are no
- more bytes than have been requested. So reposition the
- file pointer to the incoming current position and print
- everything after that. */
- *read_pos = xlseek (fd, current_pos, SEEK_SET, pretty_filename);
- }
- else
- {
- /* There are more bytes remaining than were requested.
- Back up. */
- *read_pos = xlseek (fd, -nb, SEEK_END, pretty_filename);
- }
- *read_pos += dump_remainder (pretty_filename, fd, n_bytes);
- }
- else
- return pipe_bytes (pretty_filename, fd, n_bytes, read_pos);
+ off_t end_pos = ((! presume_input_pipe && usable_st_size (&stats)
+ && n_bytes <= OFF_T_MAX)
+ ? stats.st_size : -1);
+ if (end_pos <= ST_BLKSIZE (stats))
+ return pipe_bytes (pretty_filename, fd, n_bytes, read_pos);
+ off_t current_pos = xlseek (fd, 0, SEEK_CUR, pretty_filename);
+ if (current_pos < end_pos)
+ {
+ off_t bytes_remaining = end_pos - current_pos;
+
+ if (n_bytes < bytes_remaining)
+ {
+ current_pos = end_pos - n_bytes;
+ xlseek (fd, current_pos, SEEK_SET, pretty_filename);
+ }
+ }
+ *read_pos = current_pos;
}
+
+ *read_pos += dump_remainder (pretty_filename, fd, n_bytes);
return true;
}
@@ -1186,13 +1795,13 @@ tail_bytes (const char *pretty_filename, int fd, uintmax_t n_bytes,
static bool
tail_lines (const char *pretty_filename, int fd, uintmax_t n_lines,
- uintmax_t *read_pos)
+ uintmax_t *read_pos)
{
struct stat stats;
if (fstat (fd, &stats))
{
- error (0, errno, _("cannot fstat %s"), quote (pretty_filename));
+ error (0, errno, _("cannot fstat %s"), quoteaf (pretty_filename));
return false;
}
@@ -1200,7 +1809,7 @@ tail_lines (const char *pretty_filename, int fd, uintmax_t n_lines,
{
int t = start_lines (pretty_filename, fd, n_lines, read_pos);
if (t)
- return t < 0;
+ return t < 0;
*read_pos += dump_remainder (pretty_filename, fd, COPY_TO_EOF);
}
else
@@ -1209,29 +1818,29 @@ tail_lines (const char *pretty_filename, int fd, uintmax_t n_lines,
off_t end_pos;
/* Use file_lines only if FD refers to a regular file for
- which lseek (... SEEK_END) works. */
+ which lseek (... SEEK_END) works. */
if ( ! presume_input_pipe
- && S_ISREG (stats.st_mode)
- && (start_pos = lseek (fd, 0, SEEK_CUR)) != -1
- && start_pos < (end_pos = lseek (fd, 0, SEEK_END)))
- {
- *read_pos = end_pos;
- if (end_pos != 0
- && ! file_lines (pretty_filename, fd, n_lines,
- start_pos, end_pos, read_pos))
- return false;
- }
+ && S_ISREG (stats.st_mode)
+ && (start_pos = lseek (fd, 0, SEEK_CUR)) != -1
+ && start_pos < (end_pos = lseek (fd, 0, SEEK_END)))
+ {
+ *read_pos = end_pos;
+ if (end_pos != 0
+ && ! file_lines (pretty_filename, fd, n_lines,
+ start_pos, end_pos, read_pos))
+ return false;
+ }
else
- {
- /* Under very unlikely circumstances, it is possible to reach
- this point after positioning the file pointer to end of file
- via the `lseek (...SEEK_END)' above. In that case, reposition
- the file pointer back to start_pos before calling pipe_lines. */
- if (start_pos != -1)
- xlseek (fd, start_pos, SEEK_SET, pretty_filename);
-
- return pipe_lines (pretty_filename, fd, n_lines, read_pos);
- }
+ {
+ /* Under very unlikely circumstances, it is possible to reach
+ this point after positioning the file pointer to end of file
+ via the 'lseek (...SEEK_END)' above. In that case, reposition
+ the file pointer back to start_pos before calling pipe_lines. */
+ if (start_pos != -1)
+ xlseek (fd, start_pos, SEEK_SET, pretty_filename);
+
+ return pipe_lines (pretty_filename, fd, n_lines, read_pos);
+ }
}
return true;
}
@@ -1273,7 +1882,7 @@ tail_file (struct File_spec *f, uintmax_t n_units)
have_read_stdin = true;
fd = STDIN_FILENO;
if (O_BINARY && ! isatty (STDIN_FILENO))
- freopen (NULL, "rb", stdin);
+ xfreopen (NULL, "rb", stdin);
}
else
fd = open (f->name, O_RDONLY | O_BINARY);
@@ -1283,15 +1892,15 @@ tail_file (struct File_spec *f, uintmax_t n_units)
if (fd == -1)
{
if (forever)
- {
- f->fd = -1;
- f->errnum = errno;
- f->ignore = false;
- f->ino = 0;
- f->dev = 0;
- }
+ {
+ f->fd = -1;
+ f->errnum = errno;
+ f->ignore = false;
+ f->ino = 0;
+ f->dev = 0;
+ }
error (0, errno, _("cannot open %s for reading"),
- quote (pretty_name (f)));
+ quoteaf (pretty_name (f)));
ok = false;
}
else
@@ -1299,56 +1908,59 @@ tail_file (struct File_spec *f, uintmax_t n_units)
uintmax_t read_pos;
if (print_headers)
- write_header (pretty_name (f));
+ write_header (pretty_name (f));
ok = tail (pretty_name (f), fd, n_units, &read_pos);
if (forever)
- {
- struct stat stats;
-
-#if TEST_RACE_BETWEEN_FINAL_READ_AND_INITIAL_FSTAT
- /* Before the tail function provided `read_pos', there was
- a race condition described in the URL below. This sleep
- call made the window big enough to exercise the problem. */
- sleep (1);
+ {
+ struct stat stats;
+
+#if TAIL_TEST_SLEEP
+ /* Before the tail function provided 'read_pos', there was
+ a race condition described in the URL below. This sleep
+ call made the window big enough to exercise the problem. */
+ xnanosleep (1);
#endif
- f->errnum = ok - 1;
- if (fstat (fd, &stats) < 0)
- {
- ok = false;
- f->errnum = errno;
- error (0, errno, _("error reading %s"), quote (pretty_name (f)));
- }
- else if (!IS_TAILABLE_FILE_TYPE (stats.st_mode))
- {
- error (0, 0, _("%s: cannot follow end of this type of file;\
+ f->errnum = ok - 1;
+ if (fstat (fd, &stats) < 0)
+ {
+ ok = false;
+ f->errnum = errno;
+ error (0, errno, _("error reading %s"),
+ quoteaf (pretty_name (f)));
+ }
+ else if (!IS_TAILABLE_FILE_TYPE (stats.st_mode))
+ {
+ error (0, 0, _("%s: cannot follow end of this type of file;\
giving up on this name"),
- pretty_name (f));
- ok = false;
- f->errnum = -1;
- f->ignore = true;
- }
-
- if (!ok)
- {
- close_fd (fd, pretty_name (f));
- f->fd = -1;
- }
- else
- {
- /* Note: we must use read_pos here, not stats.st_size,
- to avoid a race condition described by Ken Raeburn:
- http://mail.gnu.org/archive/html/bug-textutils/2003-05/msg00007.html */
- record_open_fd (f, fd, read_pos, &stats, (is_stdin ? -1 : 1));
- }
- }
+ quotef (pretty_name (f)));
+ ok = false;
+ f->errnum = -1;
+ f->ignore = true;
+ }
+
+ if (!ok)
+ {
+ close_fd (fd, pretty_name (f));
+ f->fd = -1;
+ }
+ else
+ {
+ /* Note: we must use read_pos here, not stats.st_size,
+ to avoid a race condition described by Ken Raeburn:
+ http://mail.gnu.org/archive/html/bug-textutils/2003-05/msg00007.html */
+ record_open_fd (f, fd, read_pos, &stats, (is_stdin ? -1 : 1));
+ f->remote = fremote (fd, pretty_name (f));
+ }
+ }
else
- {
- if (!is_stdin && close (fd))
- {
- error (0, errno, _("error reading %s"), quote (pretty_name (f)));
- ok = false;
- }
- }
+ {
+ if (!is_stdin && close (fd))
+ {
+ error (0, errno, _("error reading %s"),
+ quoteaf (pretty_name (f)));
+ ok = false;
+ }
+ }
}
return ok;
@@ -1378,8 +1990,8 @@ parse_obsolete_option (int argc, char * const *argv, uintmax_t *n_units)
/* With the obsolete form, there is one option string and at most
one file argument. Watch out for "-" and "--", though. */
if (! (argc == 2
- || (argc == 3 && ! (argv[2][0] == '-' && argv[2][1]))
- || (3 <= argc && argc <= 4 && STREQ (argv[2], "--"))))
+ || (argc == 3 && ! (argv[2][0] == '-' && argv[2][1]))
+ || (3 <= argc && argc <= 4 && STREQ (argv[2], "--"))))
return false;
obsolete_usage = (posix2_version () < 200112);
@@ -1393,18 +2005,18 @@ parse_obsolete_option (int argc, char * const *argv, uintmax_t *n_units)
case '+':
/* Leading "+" is a file name in the non-obsolete form. */
if (!obsolete_usage)
- return false;
+ return false;
t_from_start = true;
break;
case '-':
/* In the non-obsolete form, "-" is standard input and "-c"
- requires an option-argument. The obsolete multidigit options
- are supported as a GNU extension even when conforming to
- POSIX 1003.1-2001, so don't complain about them. */
+ requires an option-argument. The obsolete multidigit options
+ are supported as a GNU extension even when conforming to
+ POSIX 1003.1-2001, so don't complain about them. */
if (!obsolete_usage && !p[p[0] == 'c'])
- return false;
+ return false;
t_from_start = false;
break;
@@ -1434,9 +2046,12 @@ parse_obsolete_option (int argc, char * const *argv, uintmax_t *n_units)
if (n_string == n_string_end)
*n_units = default_count;
else if ((xstrtoumax (n_string, NULL, 10, n_units, "b")
- & ~LONGINT_INVALID_SUFFIX_CHAR)
- != LONGINT_OK)
- error (EXIT_FAILURE, 0, _("number in %s is too large"), quote (argv[1]));
+ & ~LONGINT_INVALID_SUFFIX_CHAR)
+ != LONGINT_OK)
+ {
+ error (EXIT_FAILURE, errno, "%s: %s", _("invalid number"),
+ quote (argv[1]));
+ }
/* Set globals. */
from_start = t_from_start;
@@ -1448,126 +2063,121 @@ parse_obsolete_option (int argc, char * const *argv, uintmax_t *n_units)
static void
parse_options (int argc, char **argv,
- uintmax_t *n_units, enum header_mode *header_mode,
- double *sleep_interval)
+ uintmax_t *n_units, enum header_mode *header_mode,
+ double *sleep_interval)
{
int c;
- while ((c = getopt_long (argc, argv, "c:n:fFqs:v0123456789",
- long_options, NULL))
- != -1)
+ while ((c = getopt_long (argc, argv, "c:n:fFqs:vz0123456789",
+ long_options, NULL))
+ != -1)
{
switch (c)
- {
- case 'F':
- forever = true;
- follow_mode = Follow_name;
- reopen_inaccessible_files = true;
- break;
-
- case 'c':
- case 'n':
- count_lines = (c == 'n');
- if (*optarg == '+')
- from_start = true;
- else if (*optarg == '-')
- ++optarg;
-
- {
- strtol_error s_err;
- s_err = xstrtoumax (optarg, NULL, 10, n_units, "bkm");
- if (s_err != LONGINT_OK)
- {
- error (EXIT_FAILURE, 0, "%s: %s", optarg,
- (c == 'n'
- ? _("invalid number of lines")
- : _("invalid number of bytes")));
- }
- }
- break;
-
- case 'f':
- case LONG_FOLLOW_OPTION:
- forever = true;
- if (optarg == NULL)
- follow_mode = DEFAULT_FOLLOW_MODE;
- else
- follow_mode = XARGMATCH ("--follow", optarg,
- follow_mode_string, follow_mode_map);
- break;
-
- case RETRY_OPTION:
- reopen_inaccessible_files = true;
- break;
-
- case MAX_UNCHANGED_STATS_OPTION:
- /* --max-unchanged-stats=N */
- if (xstrtoumax (optarg, NULL, 10,
- &max_n_unchanged_stats_between_opens,
- "")
- != LONGINT_OK)
- {
- error (EXIT_FAILURE, 0,
- _("%s: invalid maximum number of unchanged stats between opens"),
- optarg);
- }
- break;
-
- case PID_OPTION:
- {
- strtol_error s_err;
- unsigned long int tmp_ulong;
- s_err = xstrtoul (optarg, NULL, 10, &tmp_ulong, "");
- if (s_err != LONGINT_OK || tmp_ulong > PID_T_MAX)
- {
- error (EXIT_FAILURE, 0, _("%s: invalid PID"), optarg);
- }
- pid = tmp_ulong;
- }
- break;
-
- case PRESUME_INPUT_PIPE_OPTION:
- presume_input_pipe = true;
- break;
-
- case 'q':
- *header_mode = never;
- break;
-
- case 's':
- {
- double s;
- if (! (xstrtod (optarg, NULL, &s, c_strtod) && 0 <= s))
- error (EXIT_FAILURE, 0,
- _("%s: invalid number of seconds"), optarg);
- *sleep_interval = s;
- }
- break;
-
- case 'v':
- *header_mode = always;
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- error (EXIT_FAILURE, 0,
- _("option used in invalid context -- %c"), c);
-
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'F':
+ forever = true;
+ follow_mode = Follow_name;
+ reopen_inaccessible_files = true;
+ break;
+
+ case 'c':
+ case 'n':
+ count_lines = (c == 'n');
+ if (*optarg == '+')
+ from_start = true;
+ else if (*optarg == '-')
+ ++optarg;
+
+ *n_units = xdectoumax (optarg, 0, UINTMAX_MAX, "bkKmMGTPEZY0",
+ count_lines
+ ? _("invalid number of lines")
+ : _("invalid number of bytes"), 0);
+ break;
+
+ case 'f':
+ case LONG_FOLLOW_OPTION:
+ forever = true;
+ if (optarg == NULL)
+ follow_mode = DEFAULT_FOLLOW_MODE;
+ else
+ follow_mode = XARGMATCH ("--follow", optarg,
+ follow_mode_string, follow_mode_map);
+ break;
+
+ case RETRY_OPTION:
+ reopen_inaccessible_files = true;
+ break;
+
+ case MAX_UNCHANGED_STATS_OPTION:
+ /* --max-unchanged-stats=N */
+ max_n_unchanged_stats_between_opens =
+ xdectoumax (optarg, 0, UINTMAX_MAX, "",
+ _("invalid maximum number of unchanged stats between opens"), 0);
+ break;
+
+ case DISABLE_INOTIFY_OPTION:
+ disable_inotify = true;
+ break;
+
+ case PID_OPTION:
+ pid = xdectoumax (optarg, 0, PID_T_MAX, "", _("invalid PID"), 0);
+ break;
+
+ case PRESUME_INPUT_PIPE_OPTION:
+ presume_input_pipe = true;
+ break;
+
+ case 'q':
+ *header_mode = never;
+ break;
+
+ case 's':
+ {
+ double s;
+ if (! (xstrtod (optarg, NULL, &s, c_strtod) && 0 <= s))
+ error (EXIT_FAILURE, 0,
+ _("invalid number of seconds: %s"), quote (optarg));
+ *sleep_interval = s;
+ }
+ break;
+
+ case 'v':
+ *header_mode = always;
+ break;
+
+ case 'z':
+ line_end = '\0';
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ error (EXIT_FAILURE, 0,
+ _("option used in invalid context -- %c"), c);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
}
- if (reopen_inaccessible_files && follow_mode != Follow_name)
- error (0, 0, _("warning: --retry is useful mainly when following by name"));
+ if (reopen_inaccessible_files)
+ {
+ if (!forever)
+ {
+ reopen_inaccessible_files = false;
+ error (0, 0, _("warning: --retry ignored; --retry is useful"
+ " only when following"));
+ }
+ else if (follow_mode == Follow_descriptor)
+ error (0, 0, _("warning: --retry only effective for the initial open"));
+ }
if (pid && !forever)
error (0, 0,
- _("warning: PID ignored; --pid=PID is useful only when following"));
+ _("warning: PID ignored; --pid=PID is useful only when following"));
else if (pid && kill (pid, 0) != 0 && errno == ENOSYS)
{
error (0, 0, _("warning: --pid=PID is not supported on this system"));
@@ -1575,6 +2185,35 @@ parse_options (int argc, char **argv,
}
}
+/* Mark as '.ignore'd each member of F that corresponds to a
+ pipe or fifo, and return the number of non-ignored members. */
+static size_t
+ignore_fifo_and_pipe (struct File_spec *f, size_t n_files)
+{
+ /* When there is no FILE operand and stdin is a pipe or FIFO
+ POSIX requires that tail ignore the -f option.
+ Since we allow multiple FILE operands, we extend that to say: with -f,
+ ignore any "-" operand that corresponds to a pipe or FIFO. */
+ size_t n_viable = 0;
+
+ size_t i;
+ for (i = 0; i < n_files; i++)
+ {
+ bool is_a_fifo_or_pipe =
+ (STREQ (f[i].name, "-")
+ && !f[i].ignore
+ && 0 <= f[i].fd
+ && (S_ISFIFO (f[i].mode)
+ || (HAVE_FIFO_PIPES != 1 && isapipe (f[i].fd))));
+ if (is_a_fifo_or_pipe)
+ f[i].ignore = true;
+ else
+ ++n_viable;
+ }
+
+ return n_viable;
+}
+
int
main (int argc, char **argv)
{
@@ -1584,10 +2223,10 @@ main (int argc, char **argv)
the number of items at the end of the file to print. Although the type
is signed, the value is never negative. */
uintmax_t n_units = DEFAULT_N_LINES;
- int n_files;
+ size_t n_files;
char **file;
struct File_spec *F;
- int i;
+ size_t i;
bool obsolete_option;
/* The number of seconds to sleep between iterations.
@@ -1596,7 +2235,7 @@ main (int argc, char **argv)
double sleep_interval = 1.0;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -1607,20 +2246,23 @@ main (int argc, char **argv)
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;
parse_options (argc, argv, &n_units, &header_mode, &sleep_interval);
/* To start printing with item N_UNITS from the start of the file, skip
- N_UNITS - 1 items. `tail -n +0' is actually meaningless, but for Unix
- compatibility it's treated the same as `tail -n +1'. */
+ N_UNITS - 1 items. 'tail -n +0' is actually meaningless, but for Unix
+ compatibility it's treated the same as 'tail -n +1'. */
if (from_start)
{
if (n_units)
- --n_units;
+ --n_units;
}
+ IF_LINT (assert (0 <= argc));
+
if (optind < argc)
{
n_files = argc - optind;
@@ -1628,31 +2270,9 @@ main (int argc, char **argv)
}
else
{
- static char *dummy_stdin = "-";
+ static char *dummy_stdin = (char *) "-";
n_files = 1;
file = &dummy_stdin;
-
- /* POSIX says that -f is ignored if no file operand is specified
- and standard input is a pipe. However, the GNU coding
- standards say that program behavior should not depend on
- device type, because device independence is an important
- principle of the system's design.
-
- Follow the POSIX requirement only if POSIXLY_CORRECT is set. */
-
- if (forever && getenv ("POSIXLY_CORRECT"))
- {
- struct stat st;
- int is_a_fifo_or_pipe =
- (fstat (STDIN_FILENO, &st) != 0 ? -1
- : S_ISFIFO (st.st_mode) ? 1
- : HAVE_FIFO_PIPES == 1 ? 0
- : isapipe (STDIN_FILENO));
- if (is_a_fifo_or_pipe < 0)
- error (EXIT_FAILURE, errno, _("standard input"));
- if (is_a_fifo_or_pipe)
- forever = false;
- }
}
{
@@ -1660,20 +2280,24 @@ main (int argc, char **argv)
for (i = 0; i < n_files; i++)
if (STREQ (file[i], "-"))
- found_hyphen = true;
+ found_hyphen = true;
/* When following by name, there must be a name. */
if (found_hyphen && follow_mode == Follow_name)
- error (EXIT_FAILURE, 0, _("cannot follow %s by name"), quote ("-"));
+ error (EXIT_FAILURE, 0, _("cannot follow %s by name"), quoteaf ("-"));
- /* When following forever, warn if any file is `-'.
+ /* When following forever, warn if any file is '-'.
This is only a warning, since tail's output (before a failing seek,
and that from any non-stdin files) might still be useful. */
if (forever && found_hyphen && isatty (STDIN_FILENO))
error (0, 0, _("warning: following standard input"
- " indefinitely is ineffective"));
+ " indefinitely is ineffective"));
}
+ /* Don't read anything if we'll never output anything. */
+ if (! n_units && ! forever && ! from_start)
+ return EXIT_SUCCESS;
+
F = xnmalloc (n_files, sizeof *F);
for (i = 0; i < n_files; i++)
F[i].name = file[i];
@@ -1683,15 +2307,99 @@ main (int argc, char **argv)
print_headers = true;
if (O_BINARY && ! isatty (STDOUT_FILENO))
- freopen (NULL, "wb", stdout);
+ xfreopen (NULL, "wb", stdout);
for (i = 0; i < n_files; i++)
ok &= tail_file (&F[i], n_units);
- if (forever)
- tail_forever (F, n_files, sleep_interval);
+ if (forever && ignore_fifo_and_pipe (F, n_files))
+ {
+#if HAVE_INOTIFY
+ /* tailable_stdin() checks if the user specifies stdin via "-",
+ or implicitly by providing no arguments. If so, we won't use inotify.
+ Technically, on systems with a working /dev/stdin, we *could*,
+ but would it be worth it? Verifying that it's a real device
+ and hooked up to stdin is not trivial, while reverting to
+ non-inotify-based tail_forever is easy and portable.
+
+ any_remote_file() checks if the user has specified any
+ files that reside on remote file systems. inotify is not used
+ in this case because it would miss any updates to the file
+ that were not initiated from the local system.
+
+ any_non_remote_file() checks if the user has specified any
+ files that don't reside on remote file systems. inotify is not used
+ if there are no open files, as we can't determine if those file
+ will be on a remote file system.
+
+ any_symlinks() checks if the user has specified any symbolic links.
+ inotify is not used in this case because it returns updated _targets_
+ which would not match the specified names. If we tried to always
+ use the target names, then we would miss changes to the symlink itself.
+
+ ok is false when one of the files specified could not be opened for
+ reading. In this case and when following by descriptor,
+ tail_forever_inotify() cannot be used (in its current implementation).
+
+ FIXME: inotify doesn't give any notification when a new
+ (remote) file or directory is mounted on top a watched file.
+ When follow_mode == Follow_name we would ideally like to detect that.
+ Note if there is a change to the original file then we'll
+ recheck it and follow the new file, or ignore it if the
+ file has changed to being remote.
+
+ FIXME: when using inotify, and a directory for a watched file
+ is recreated, then we don't recheck any new file when
+ follow_mode == Follow_name.
+
+ FIXME-maybe: inotify has a watch descriptor per inode, and hence with
+ our current hash implementation will only --follow data for one
+ of the names when multiple hardlinked files are specified, or
+ for one name when a name is specified multiple times. */
+ if (!disable_inotify && (tailable_stdin (F, n_files)
+ || any_remote_file (F, n_files)
+ || ! any_non_remote_file (F, n_files)
+ || any_symlinks (F, n_files)
+ || (!ok && follow_mode == Follow_descriptor)))
+ disable_inotify = true;
+
+ if (!disable_inotify)
+ {
+ int wd = inotify_init ();
+ if (0 <= wd)
+ {
+ /* Flush any output from tail_file, now, since
+ tail_forever_inotify flushes only after writing,
+ not before reading. */
+ if (fflush (stdout) != 0)
+ error (EXIT_FAILURE, errno, _("write error"));
+
+ if (! tail_forever_inotify (wd, F, n_files, sleep_interval))
+ return EXIT_FAILURE;
+ }
+ error (0, errno, _("inotify cannot be used, reverting to polling"));
+
+ /* Free resources as this process can be long lived,
+ and we may have exhausted system resources above. */
+
+ for (i = 0; i < n_files; i++)
+ {
+ /* It's OK to remove the same watch multiple times,
+ ignoring the EINVAL from redundant calls. */
+ if (F[i].wd != -1)
+ inotify_rm_watch (wd, F[i].wd);
+ if (F[i].parent_wd != -1)
+ inotify_rm_watch (wd, F[i].parent_wd);
+ }
+ }
+#endif
+ disable_inotify = true;
+ tail_forever (F, n_files, sleep_interval);
+ }
+
+ IF_LINT (free (F));
if (have_read_stdin && close (STDIN_FILENO) < 0)
error (EXIT_FAILURE, errno, "-");
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/tee.c b/src/tee.c
index d21edbc..d8ae6a4 100644
--- a/src/tee.c
+++ b/src/tee.c
@@ -1,10 +1,10 @@
/* tee - read from standard input and write to standard output and files.
- Copyright (C) 85,1990-2006 Free Software Foundation, Inc.
+ Copyright (C) 1985-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Mike Parker, Richard M. Stallman, and David MacKenzie */
@@ -23,15 +22,21 @@
#include <getopt.h>
#include "system.h"
+#include "argmatch.h"
#include "error.h"
+#include "fadvise.h"
#include "stdio--.h"
+#include "xfreopen.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "tee"
-#define AUTHORS "Mike Parker", "Richard M. Stallman", "David MacKenzie"
+#define AUTHORS \
+ proper_name ("Mike Parker"), \
+ proper_name ("Richard M. Stallman"), \
+ proper_name ("David MacKenzie")
-static bool tee_files (int nfiles, const char **files);
+static bool tee_files (int nfiles, char **files);
/* If true, append to output files rather than truncating them. */
static bool append;
@@ -39,24 +44,43 @@ static bool append;
/* If true, ignore interrupts. */
static bool ignore_interrupts;
-/* The name that this program was run with. */
-char *program_name;
+enum output_error
+ {
+ output_error_sigpipe, /* traditional behavior, sigpipe enabled. */
+ output_error_warn, /* warn on EPIPE, but continue. */
+ output_error_warn_nopipe, /* ignore EPIPE, continue. */
+ output_error_exit, /* exit on any output error. */
+ output_error_exit_nopipe /* exit on any output error except EPIPE. */
+ };
+
+static enum output_error output_error;
static struct option const long_options[] =
{
{"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},
{NULL, 0, NULL, 0}
};
+static char const *const output_error_args[] =
+{
+ "warn", "warn-nopipe", "exit", "exit-nopipe", NULL
+};
+static enum output_error const output_error_types[] =
+{
+ output_error_warn, output_error_warn_nopipe,
+ output_error_exit, output_error_exit_nopipe
+};
+ARGMATCH_VERIFY (output_error_args, output_error_types);
+
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
@@ -66,13 +90,25 @@ Copy standard input to each FILE, and also to standard output.\n\
-a, --append append to the given FILEs, do not overwrite\n\
-i, --ignore-interrupts ignore interrupt signals\n\
"), stdout);
+ fputs (_("\
+ -p diagnose errors writing to non 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);
fputs (_("\
\n\
-If a FILE is -, copy again to standard output.\n\
+MODE determines behavior with write errors on the outputs:\n\
+ 'warn' diagnose errors writing to any output\n\
+ 'warn-nopipe' diagnose errors writing to any output not a pipe\n\
+ 'exit' exit on error writing to any output\n\
+ 'exit-nopipe' exit on error writing to any output not a pipe\n\
+The default MODE for the -p option is 'warn-nopipe'.\n\
+The default operation when --output-error is not specified, is to\n\
+exit immediately on error writing to a pipe, and diagnose errors\n\
+writing to non pipe outputs.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -84,7 +120,7 @@ main (int argc, char **argv)
int optc;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -94,50 +130,62 @@ main (int argc, char **argv)
append = false;
ignore_interrupts = false;
- while ((optc = getopt_long (argc, argv, "ai", long_options, NULL)) != -1)
+ while ((optc = getopt_long (argc, argv, "aip", long_options, NULL)) != -1)
{
switch (optc)
- {
- case 'a':
- append = true;
- break;
-
- case 'i':
- ignore_interrupts = true;
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'a':
+ append = true;
+ break;
+
+ case 'i':
+ ignore_interrupts = true;
+ break;
+
+ case 'p':
+ if (optarg)
+ output_error = XARGMATCH ("--output-error", optarg,
+ output_error_args, output_error_types);
+ else
+ output_error = output_error_warn_nopipe;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
}
if (ignore_interrupts)
signal (SIGINT, SIG_IGN);
+ if (output_error != output_error_sigpipe)
+ signal (SIGPIPE, SIG_IGN);
+
/* Do *not* warn if tee is given no file arguments.
POSIX requires that it work when given no arguments. */
- ok = tee_files (argc - optind, (const char **) &argv[optind]);
+ ok = tee_files (argc - optind, &argv[optind]);
if (close (STDIN_FILENO) != 0)
- error (EXIT_FAILURE, errno, _("standard input"));
+ error (EXIT_FAILURE, errno, "%s", _("standard input"));
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
/* Copy the standard input into each of the NFILES files in FILES
- and into the standard output.
+ and into the standard output. As a side effect, modify FILES[-1].
Return true if successful. */
static bool
-tee_files (int nfiles, const char **files)
+tee_files (int nfiles, char **files)
{
+ size_t n_outputs = 0;
FILE **descriptors;
char buffer[BUFSIZ];
- ssize_t bytes_read;
+ ssize_t bytes_read = 0;
int i;
bool ok = true;
char const *mode_string =
@@ -145,58 +193,71 @@ tee_files (int nfiles, const char **files)
? (append ? "ab" : "wb")
: (append ? "a" : "w"));
- descriptors = xnmalloc (nfiles + 1, sizeof *descriptors);
-
- /* Move all the names `up' one in the argv array to make room for
- the entry for standard output. This writes into argv[argc]. */
- for (i = nfiles; i >= 1; i--)
- files[i] = files[i - 1];
-
if (O_BINARY && ! isatty (STDIN_FILENO))
- freopen (NULL, "rb", stdin);
+ xfreopen (NULL, "rb", stdin);
if (O_BINARY && ! isatty (STDOUT_FILENO))
- freopen (NULL, "wb", stdout);
+ xfreopen (NULL, "wb", stdout);
+
+ fadvise (stdin, FADVISE_SEQUENTIAL);
- /* In the array of NFILES + 1 descriptors, make
- the first one correspond to standard output. */
+ /* Set up FILES[0 .. NFILES] and DESCRIPTORS[0 .. NFILES].
+ In both arrays, entry 0 corresponds to standard output. */
+
+ descriptors = xnmalloc (nfiles + 1, sizeof *descriptors);
+ files--;
descriptors[0] = stdout;
- files[0] = _("standard output");
+ files[0] = bad_cast (_("standard output"));
setvbuf (stdout, NULL, _IONBF, 0);
+ n_outputs++;
for (i = 1; i <= nfiles; i++)
{
- descriptors[i] = (STREQ (files[i], "-")
- ? stdout
- : fopen (files[i], mode_string));
+ /* Do not treat "-" specially - as mandated by POSIX. */
+ descriptors[i] = fopen (files[i], mode_string);
if (descriptors[i] == NULL)
- {
- error (0, errno, "%s", files[i]);
- ok = false;
- }
+ {
+ error (output_error == output_error_exit
+ || output_error == output_error_exit_nopipe,
+ errno, "%s", quotef (files[i]));
+ ok = false;
+ }
else
- setvbuf (descriptors[i], NULL, _IONBF, 0);
+ {
+ setvbuf (descriptors[i], NULL, _IONBF, 0);
+ n_outputs++;
+ }
}
- while (1)
+ while (n_outputs)
{
bytes_read = read (0, buffer, sizeof buffer);
-#ifdef EINTR
if (bytes_read < 0 && errno == EINTR)
continue;
-#endif
if (bytes_read <= 0)
- break;
+ break;
/* Write to all NFILES + 1 descriptors.
- Standard output is the first one. */
+ Standard output is the first one. */
for (i = 0; i <= nfiles; i++)
- if (descriptors[i]
- && fwrite (buffer, 1, bytes_read, descriptors[i]) != bytes_read)
- {
- error (0, errno, "%s", files[i]);
- descriptors[i] = NULL;
- ok = false;
- }
+ if (descriptors[i]
+ && fwrite (buffer, bytes_read, 1, descriptors[i]) != 1)
+ {
+ 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] = NULL;
+ if (fail)
+ ok = false;
+ n_outputs--;
+ }
}
if (bytes_read == -1)
@@ -207,11 +268,10 @@ tee_files (int nfiles, const char **files)
/* Close the files, but not standard output. */
for (i = 1; i <= nfiles; i++)
- if (!STREQ (files[i], "-")
- && descriptors[i] && fclose (descriptors[i]) != 0)
+ if (descriptors[i] && fclose (descriptors[i]) != 0)
{
- error (0, errno, "%s", files[i]);
- ok = false;
+ error (0, errno, "%s", quotef (files[i]));
+ ok = false;
}
free (descriptors);
diff --git a/src/test.c b/src/test.c
index b25436b..36817d2 100644
--- a/src/test.c
+++ b/src/test.c
@@ -2,27 +2,30 @@
/* Modified to run with the GNU shell by bfox. */
-/* Copyright (C) 1987-2005 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2016 Free Software Foundation, Inc.
- This file is part of GNU Bash, the Bourne Again SHell.
+ This 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.
- Bash 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 2, or (at your option) any later
- version.
-
- Bash 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.
+ 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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Define TEST_STANDALONE to get the /bin/test version. Otherwise, you get
the shell builtin version. */
+/* Without this pragma, gcc 4.6.2 20111027 mistakenly suggests that
+ the advance function might be candidate for attribute 'pure'. */
+#if (__GNUC__ == 4 && 6 <= __GNUC_MINOR__) || 4 < __GNUC__
+# pragma GCC diagnostic ignored "-Wsuggest-attribute=pure"
+#endif
+
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
@@ -33,7 +36,7 @@
# define LBRACKET 0
#endif
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#if LBRACKET
# define PROGRAM_NAME "["
#else
@@ -41,28 +44,28 @@
#endif
#include "system.h"
-#include "error.h"
-#include "euidaccess.h"
-#include "inttostr.h"
#include "quote.h"
#include "stat-time.h"
#include "strnumcmp.h"
+#include <stdarg.h>
+#include "verror.h"
+
#if HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif
-char *program_name;
-
/* Exit status for syntax errors, etc. */
enum { TEST_TRUE, TEST_FALSE, TEST_FAILURE };
#if defined TEST_STANDALONE
# define test_exit(val) exit (val)
+# define test_main_return(val) return val
#else
static jmp_buf test_exit_buf;
static int test_error_return = 0;
# define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1)
+# define test_main_return(val) test_exit (val)
#endif /* !TEST_STANDALONE */
static int pos; /* The offset of the current argument in ARGV. */
@@ -81,25 +84,24 @@ static bool term (void);
static bool and (void);
static bool or (void);
-static void test_syntax_error (char const *format, char const *arg)
+static void test_syntax_error (char const *format, ...)
ATTRIBUTE_NORETURN;
static void beyond (void) ATTRIBUTE_NORETURN;
static void
-test_syntax_error (char const *format, char const *arg)
+test_syntax_error (char const *format, ...)
{
- fprintf (stderr, "%s: ", argv[0]);
- fprintf (stderr, format, arg);
- fputc ('\n', stderr);
- fflush (stderr);
+ va_list ap;
+ va_start (ap, format);
+ verror (0, 0, format, ap);
test_exit (TEST_FAILURE);
}
/* Increment our position in the argument list. Check that we're not
- past the end of the argument list. This check is supressed if the
+ past the end of the argument list. This check is suppressed if the
argument is false. */
-static inline void
+static void
advance (bool f)
{
++pos;
@@ -108,7 +110,7 @@ advance (bool f)
beyond ();
}
-static inline void
+static void
unary_advance (void)
{
advance (true);
@@ -151,11 +153,11 @@ find_int (char const *string)
if (ISDIGIT (*p++))
{
while (ISDIGIT (*p))
- p++;
+ p++;
while (isblank (to_uchar (*p)))
- p++;
+ p++;
if (!*p)
- return number_start;
+ return number_start;
}
test_syntax_error (_("invalid integer %s"), quote (string));
@@ -181,10 +183,11 @@ get_mtime (char const *filename, struct timespec *mtime)
static bool
binop (char const *s)
{
- return ((STREQ (s, "=")) || (STREQ (s, "!=")) || (STREQ (s, "-nt")) ||
- (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
- (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
- (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
+ return ((STREQ (s, "=")) || (STREQ (s, "!=")) || (STREQ (s, "==")) ||
+ (STREQ (s, "-nt")) ||
+ (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
+ (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
+ (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
}
/*
@@ -211,7 +214,7 @@ term (void)
bool value;
bool negated = false;
- /* Deal with leading `not's. */
+ /* Deal with leading 'not's. */
while (pos < argc && argv[pos][0] == '!' && argv[pos][1] == '\0')
{
advance (true);
@@ -229,20 +232,21 @@ term (void)
advance (true);
for (nargs = 1;
- pos + nargs < argc && ! STREQ (argv[pos + nargs], ")");
- nargs++)
- if (nargs == 4)
- {
- nargs = argc - pos;
- break;
- }
+ pos + nargs < argc && ! STREQ (argv[pos + nargs], ")");
+ nargs++)
+ if (nargs == 4)
+ {
+ nargs = argc - pos;
+ break;
+ }
value = posixtest (nargs);
if (argv[pos] == 0)
- test_syntax_error (_("')' expected"), NULL);
+ test_syntax_error (_("%s expected"), quote (")"));
else
if (argv[pos][0] != ')' || argv[pos][1])
- test_syntax_error (_("')' expected, found %s"), argv[pos]);
+ test_syntax_error (_("%s expected, found %s"),
+ quote_n (0, ")"), quote_n (1, argv[pos]));
advance (false);
}
@@ -256,9 +260,9 @@ term (void)
else if (argv[pos][0] == '-' && argv[pos][1] && argv[pos][2] == '\0')
{
if (test_unop (argv[pos]))
- value = unary_operator ();
+ value = unary_operator ();
else
- test_syntax_error (_("%s: unary operator expected"), argv[pos]);
+ test_syntax_error (_("%s: unary operator expected"), quote (argv[pos]));
}
else
{
@@ -293,82 +297,83 @@ binary_operator (bool l_is_l)
{
/* check for eq, nt, and stuff */
if ((((argv[op][1] == 'l' || argv[op][1] == 'g')
- && (argv[op][2] == 'e' || argv[op][2] == 't'))
- || (argv[op][1] == 'e' && argv[op][2] == 'q')
- || (argv[op][1] == 'n' && argv[op][2] == 'e'))
- && !argv[op][3])
- {
- char lbuf[INT_BUFSIZE_BOUND (uintmax_t)];
- char rbuf[INT_BUFSIZE_BOUND (uintmax_t)];
- char const *l = (l_is_l
- ? umaxtostr (strlen (argv[op - 1]), lbuf)
- : find_int (argv[op - 1]));
- char const *r = (r_is_l
- ? umaxtostr (strlen (argv[op + 2]), rbuf)
- : find_int (argv[op + 1]));
- int cmp = strintcmp (l, r);
- bool xe_operator = (argv[op][2] == 'e');
- pos += 3;
- return (argv[op][1] == 'l' ? cmp < xe_operator
- : argv[op][1] == 'g' ? cmp > - xe_operator
- : (cmp != 0) == xe_operator);
- }
+ && (argv[op][2] == 'e' || argv[op][2] == 't'))
+ || (argv[op][1] == 'e' && argv[op][2] == 'q')
+ || (argv[op][1] == 'n' && argv[op][2] == 'e'))
+ && !argv[op][3])
+ {
+ char lbuf[INT_BUFSIZE_BOUND (uintmax_t)];
+ char rbuf[INT_BUFSIZE_BOUND (uintmax_t)];
+ char const *l = (l_is_l
+ ? umaxtostr (strlen (argv[op - 1]), lbuf)
+ : find_int (argv[op - 1]));
+ char const *r = (r_is_l
+ ? umaxtostr (strlen (argv[op + 2]), rbuf)
+ : find_int (argv[op + 1]));
+ int cmp = strintcmp (l, r);
+ bool xe_operator = (argv[op][2] == 'e');
+ pos += 3;
+ return (argv[op][1] == 'l' ? cmp < xe_operator
+ : argv[op][1] == 'g' ? cmp > - xe_operator
+ : (cmp != 0) == xe_operator);
+ }
switch (argv[op][1])
- {
- default:
- break;
-
- case 'n':
- if (argv[op][2] == 't' && !argv[op][3])
- {
- /* nt - newer than */
- struct timespec lt, rt;
- bool le, re;
- pos += 3;
- if (l_is_l | r_is_l)
- test_syntax_error (_("-nt does not accept -l"), NULL);
- le = get_mtime (argv[op - 1], &lt);
- re = get_mtime (argv[op + 1], &rt);
- return le && (!re || timespec_cmp (lt, rt) > 0);
- }
- break;
-
- case 'e':
- if (argv[op][2] == 'f' && !argv[op][3])
- {
- /* ef - hard link? */
- pos += 3;
- if (l_is_l | r_is_l)
- test_syntax_error (_("-ef does not accept -l"), NULL);
- return (stat (argv[op - 1], &stat_buf) == 0
- && stat (argv[op + 1], &stat_spare) == 0
- && stat_buf.st_dev == stat_spare.st_dev
- && stat_buf.st_ino == stat_spare.st_ino);
- }
- break;
-
- case 'o':
- if ('t' == argv[op][2] && '\000' == argv[op][3])
- {
- /* ot - older than */
- struct timespec lt, rt;
- bool le, re;
- pos += 3;
- if (l_is_l | r_is_l)
- test_syntax_error (_("-ot does not accept -l"), NULL);
- le = get_mtime (argv[op - 1], &lt);
- re = get_mtime (argv[op + 1], &rt);
- return re && (!le || timespec_cmp (lt, rt) < 0);
- }
- break;
- }
+ {
+ default:
+ break;
+
+ case 'n':
+ if (argv[op][2] == 't' && !argv[op][3])
+ {
+ /* nt - newer than */
+ struct timespec lt, rt;
+ bool le, re;
+ pos += 3;
+ if (l_is_l || r_is_l)
+ test_syntax_error (_("-nt does not accept -l"), NULL);
+ le = get_mtime (argv[op - 1], &lt);
+ re = get_mtime (argv[op + 1], &rt);
+ return le && (!re || timespec_cmp (lt, rt) > 0);
+ }
+ break;
+
+ case 'e':
+ if (argv[op][2] == 'f' && !argv[op][3])
+ {
+ /* ef - hard link? */
+ pos += 3;
+ if (l_is_l || r_is_l)
+ test_syntax_error (_("-ef does not accept -l"), NULL);
+ return (stat (argv[op - 1], &stat_buf) == 0
+ && stat (argv[op + 1], &stat_spare) == 0
+ && stat_buf.st_dev == stat_spare.st_dev
+ && stat_buf.st_ino == stat_spare.st_ino);
+ }
+ break;
+
+ case 'o':
+ if ('t' == argv[op][2] && '\000' == argv[op][3])
+ {
+ /* ot - older than */
+ struct timespec lt, rt;
+ bool le, re;
+ pos += 3;
+ if (l_is_l || r_is_l)
+ test_syntax_error (_("-ot does not accept -l"), NULL);
+ le = get_mtime (argv[op - 1], &lt);
+ re = get_mtime (argv[op + 1], &rt);
+ return re && (!le || timespec_cmp (lt, rt) < 0);
+ }
+ break;
+ }
/* FIXME: is this dead code? */
- test_syntax_error (_("unknown binary operator"), argv[op]);
+ test_syntax_error (_("%s: unknown binary operator"), quote (argv[op]));
}
- if (argv[op][0] == '=' && !argv[op][1])
+ if (argv[op][0] == '='
+ && (!argv[op][1] || ((argv[op][1] == '=') && !argv[op][2])))
{
bool value = STREQ (argv[pos], argv[pos + 2]);
pos += 3;
@@ -397,9 +402,9 @@ unary_operator (void)
return false;
/* All of the following unary operators use unary_advance (), which
- checks to make sure that there is an argument, and then advances
- pos right past it. This means that pos - 1 is the location of the
- argument. */
+ checks to make sure that there is an argument, and then advances
+ pos right past it. This means that pos - 1 is the location of the
+ argument. */
case 'a': /* file exists in the file system? */
case 'e':
@@ -419,51 +424,63 @@ unary_operator (void)
return euidaccess (argv[pos - 1], X_OK) == 0;
case 'O': /* File is owned by you? */
- unary_advance ();
- return (stat (argv[pos - 1], &stat_buf) == 0
- && (geteuid () == stat_buf.st_uid));
+ {
+ unary_advance ();
+ if (stat (argv[pos - 1], &stat_buf) != 0)
+ return false;
+ errno = 0;
+ uid_t euid = geteuid ();
+ uid_t NO_UID = -1;
+ return ! (euid == NO_UID && errno) && euid == stat_buf.st_uid;
+ }
case 'G': /* File is owned by your group? */
- unary_advance ();
- return (stat (argv[pos - 1], &stat_buf) == 0
- && (getegid () == stat_buf.st_gid));
+ {
+ unary_advance ();
+ if (stat (argv[pos - 1], &stat_buf) != 0)
+ return false;
+ errno = 0;
+ gid_t egid = getegid ();
+ gid_t NO_GID = -1;
+ return ! (egid == NO_GID && errno) && egid == stat_buf.st_gid;
+ }
case 'f': /* File is a file? */
unary_advance ();
/* Under POSIX, -f is true if the given file exists
- and is a regular file. */
+ and is a regular file. */
return (stat (argv[pos - 1], &stat_buf) == 0
- && S_ISREG (stat_buf.st_mode));
+ && S_ISREG (stat_buf.st_mode));
case 'd': /* File is a directory? */
unary_advance ();
return (stat (argv[pos - 1], &stat_buf) == 0
- && S_ISDIR (stat_buf.st_mode));
+ && S_ISDIR (stat_buf.st_mode));
case 's': /* File has something in it? */
unary_advance ();
return (stat (argv[pos - 1], &stat_buf) == 0
- && 0 < stat_buf.st_size);
+ && 0 < stat_buf.st_size);
case 'S': /* File is a socket? */
unary_advance ();
return (stat (argv[pos - 1], &stat_buf) == 0
- && S_ISSOCK (stat_buf.st_mode));
+ && S_ISSOCK (stat_buf.st_mode));
case 'c': /* File is character special? */
unary_advance ();
return (stat (argv[pos - 1], &stat_buf) == 0
- && S_ISCHR (stat_buf.st_mode));
+ && S_ISCHR (stat_buf.st_mode));
case 'b': /* File is block special? */
unary_advance ();
return (stat (argv[pos - 1], &stat_buf) == 0
- && S_ISBLK (stat_buf.st_mode));
+ && S_ISBLK (stat_buf.st_mode));
case 'p': /* File is a named pipe? */
unary_advance ();
return (stat (argv[pos - 1], &stat_buf) == 0
- && S_ISFIFO (stat_buf.st_mode));
+ && S_ISFIFO (stat_buf.st_mode));
case 'L': /* Same as -h */
/*FALLTHROUGH*/
@@ -471,32 +488,32 @@ unary_operator (void)
case 'h': /* File is a symbolic link? */
unary_advance ();
return (lstat (argv[pos - 1], &stat_buf) == 0
- && S_ISLNK (stat_buf.st_mode));
+ && S_ISLNK (stat_buf.st_mode));
case 'u': /* File is setuid? */
unary_advance ();
return (stat (argv[pos - 1], &stat_buf) == 0
- && (stat_buf.st_mode & S_ISUID));
+ && (stat_buf.st_mode & S_ISUID));
case 'g': /* File is setgid? */
unary_advance ();
return (stat (argv[pos - 1], &stat_buf) == 0
- && (stat_buf.st_mode & S_ISGID));
+ && (stat_buf.st_mode & S_ISGID));
case 'k': /* File has sticky bit set? */
unary_advance ();
return (stat (argv[pos - 1], &stat_buf) == 0
- && (stat_buf.st_mode & S_ISVTX));
+ && (stat_buf.st_mode & S_ISVTX));
case 't': /* File (fd) is a terminal? */
{
- long int fd;
- char const *arg;
- unary_advance ();
- arg = find_int (argv[pos - 1]);
- errno = 0;
- fd = strtol (arg, NULL, 10);
- return (errno != ERANGE && 0 <= fd && fd <= INT_MAX && isatty (fd));
+ long int fd;
+ char const *arg;
+ unary_advance ();
+ arg = find_int (argv[pos - 1]);
+ errno = 0;
+ fd = strtol (arg, NULL, 10);
+ return (errno != ERANGE && 0 <= fd && fd <= INT_MAX && isatty (fd));
}
case 'n': /* True if arg has some length. */
@@ -519,11 +536,11 @@ and (void)
{
bool value = true;
- for (;;)
+ while (true)
{
value &= term ();
if (! (pos < argc && STREQ (argv[pos], "-a")))
- return value;
+ return value;
advance (false);
}
}
@@ -538,11 +555,11 @@ or (void)
{
bool value = false;
- for (;;)
+ while (true)
{
value |= and ();
if (! (pos < argc && STREQ (argv[pos], "-o")))
- return value;
+ return value;
advance (false);
}
}
@@ -575,9 +592,9 @@ test_unop (char const *op)
case 'u': case 'w': case 'x': case 'z':
case 'G': case 'L': case 'O': case 'S': case 'N':
return true;
+ default:
+ return false;
}
-
- return false;
}
static bool
@@ -597,13 +614,13 @@ two_arguments (void)
value = ! one_argument ();
}
else if (argv[pos][0] == '-'
- && argv[pos][1] != '\0'
- && argv[pos][2] == '\0')
+ && argv[pos][1] != '\0'
+ && argv[pos][2] == '\0')
{
if (test_unop (argv[pos]))
- value = unary_operator ();
+ value = unary_operator ();
else
- test_syntax_error (_("%s: unary operator expected"), argv[pos]);
+ test_syntax_error (_("%s: unary operator expected"), quote (argv[pos]));
}
else
beyond ();
@@ -631,7 +648,7 @@ three_arguments (void)
else if (STREQ (argv[pos + 1], "-a") || STREQ (argv[pos + 1], "-o"))
value = expr ();
else
- test_syntax_error (_("%s: binary operator expected"), argv[pos+1]);
+ test_syntax_error (_("%s: binary operator expected"), quote (argv[pos+1]));
return (value);
}
@@ -644,51 +661,49 @@ posixtest (int nargs)
switch (nargs)
{
case 1:
- value = one_argument ();
- break;
+ value = one_argument ();
+ break;
case 2:
- value = two_arguments ();
- break;
+ value = two_arguments ();
+ break;
case 3:
- value = three_arguments ();
- break;
+ value = three_arguments ();
+ break;
case 4:
- if (STREQ (argv[pos], "!"))
- {
- advance (true);
- value = !three_arguments ();
- break;
- }
- if (STREQ (argv[pos], "(") && STREQ (argv[pos + 3], ")"))
- {
- advance (false);
- value = two_arguments ();
- advance (false);
- break;
- }
- /* FALLTHROUGH */
+ if (STREQ (argv[pos], "!"))
+ {
+ advance (true);
+ value = !three_arguments ();
+ break;
+ }
+ if (STREQ (argv[pos], "(") && STREQ (argv[pos + 3], ")"))
+ {
+ advance (false);
+ value = two_arguments ();
+ advance (false);
+ break;
+ }
+ /* FALLTHROUGH */
case 5:
default:
- if (nargs <= 0)
- abort ();
- value = expr ();
+ if (nargs <= 0)
+ abort ();
+ value = expr ();
}
return (value);
}
#if defined TEST_STANDALONE
-# include "long-options.h"
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
fputs (_("\
@@ -773,8 +788,13 @@ Except for -h and -L, all FILE-related tests dereference symbolic links.\n\
Beware that parentheses need to be escaped (e.g., by backslashes) for shells.\n\
INTEGER may also be -l STRING, which evaluates to the length of STRING.\n\
"), stdout);
+ fputs (_("\
+\n\
+NOTE: [ honors the --help and --version options, but test does not.\n\
+test treats each of those as it treats any other nonempty STRING.\n\
+"), stdout);
printf (USAGE_BUILTIN_WARNING, _("test and/or ["));
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -784,7 +804,9 @@ INTEGER may also be -l STRING, which evaluates to the length of STRING.\n\
# define main test_command
#endif
-#define AUTHORS "Kevin Braunsdorf", "Matthew Bradburn"
+#define AUTHORS \
+ proper_name ("Kevin Braunsdorf"), \
+ proper_name ("Matthew Bradburn")
/*
* [:
@@ -806,7 +828,7 @@ main (int margc, char **margv)
return (test_error_return);
#else /* TEST_STANDALONE */
initialize_main (&margc, &margv);
- program_name = margv[0];
+ set_program_name (margv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -820,16 +842,25 @@ main (int margc, char **margv)
if (LBRACKET)
{
/* Recognize --help or --version, but only when invoked in the
- "[" form, and when the last argument is not "]". POSIX
- allows "[ --help" and "[ --version" to have the usual GNU
- behavior, but it requires "test --help" and "test --version"
- to exit silently with status 0. */
+ "[" form, when the last argument is not "]". Use direct
+ parsing, rather than parse_long_options, to avoid accepting
+ abbreviations. POSIX allows "[ --help" and "[ --version" to
+ have the usual GNU behavior, but it requires "test --help"
+ and "test --version" to exit silently with status 0. */
+ if (margc == 2)
+ {
+ if (STREQ (margv[1], "--help"))
+ usage (EXIT_SUCCESS);
+
+ if (STREQ (margv[1], "--version"))
+ {
+ version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version, AUTHORS,
+ (char *) NULL);
+ test_main_return (EXIT_SUCCESS);
+ }
+ }
if (margc < 2 || !STREQ (margv[margc - 1], "]"))
- {
- parse_long_options (margc, margv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
- test_syntax_error (_("missing `]'"), NULL);
- }
+ test_syntax_error (_("missing %s"), quote ("]"));
--margc;
}
@@ -838,12 +869,12 @@ main (int margc, char **margv)
pos = 1;
if (pos >= argc)
- test_exit (TEST_FALSE);
+ test_main_return (TEST_FALSE);
value = posixtest (argc - 1);
if (pos != argc)
test_syntax_error (_("extra argument %s"), quote (argv[pos]));
- test_exit (value ? TEST_TRUE : TEST_FALSE);
+ test_main_return (value ? TEST_TRUE : TEST_FALSE);
}
diff --git a/src/timeout.c b/src/timeout.c
new file mode 100644
index 0000000..9c31df5
--- /dev/null
+++ b/src/timeout.c
@@ -0,0 +1,506 @@
+/* timeout -- run a command with bounded time
+ Copyright (C) 2008-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+
+/* timeout - Start a command, and kill it if the specified timeout expires
+
+ We try to behave like a shell starting a single (foreground) job,
+ and will kill the job if we receive the alarm signal we setup.
+ The exit status of the job is returned, or one of these errors:
+ EXIT_TIMEDOUT 124 job timed out
+ EXIT_CANCELED 125 internal error
+ EXIT_CANNOT_INVOKE 126 error executing job
+ EXIT_ENOENT 127 couldn't find job to exec
+
+ Caveats:
+ If user specifies the KILL (9) signal is to be sent on timeout,
+ the monitor is killed and so exits with 128+9 rather than 124.
+
+ If you start a command in the background, which reads from the tty
+ and so is immediately sent SIGTTIN to stop, then the timeout
+ process will ignore this so it can timeout the command as expected.
+ This can be seen with 'timeout 10 dd&' for example.
+ However if one brings this group to the foreground with the 'fg'
+ command before the timer expires, the command will remain
+ in the stop state as the shell doesn't send a SIGCONT
+ because the timeout process (group leader) is already running.
+ To get the command running again one can Ctrl-Z, and do fg again.
+ Note one can Ctrl-C the whole job when in this state.
+ I think this could be fixed but I'm not sure the extra
+ complication is justified for this scenario.
+
+ Written by Pádraig Brady. */
+
+#include <config.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+#if HAVE_PRCTL
+# include <sys/prctl.h>
+#endif
+#include <sys/wait.h>
+
+#include "system.h"
+#include "c-strtod.h"
+#include "xstrtod.h"
+#include "sig2str.h"
+#include "operand2sig.h"
+#include "error.h"
+#include "quote.h"
+
+#if HAVE_SETRLIMIT
+/* FreeBSD 5.0 at least needs <sys/types.h> and <sys/time.h> included
+ before <sys/resource.h>. Currently "system.h" includes <sys/time.h>. */
+# include <sys/resource.h>
+#endif
+
+/* NonStop circa 2011 lacks both SA_RESTART and siginterrupt. */
+#ifndef SA_RESTART
+# define SA_RESTART 0
+#endif
+
+#define PROGRAM_NAME "timeout"
+
+#define AUTHORS proper_name_utf8 ("Padraig Brady", "P\303\241draig Brady")
+
+static int timed_out;
+static int term_signal = SIGTERM; /* same default as kill command. */
+static int monitored_pid;
+static double kill_after;
+static bool foreground; /* whether to use another program group. */
+static bool preserve_status; /* whether to use a timeout status or not. */
+
+/* for long options with no corresponding short option, use enum */
+enum
+{
+ FOREGROUND_OPTION = CHAR_MAX + 1,
+ PRESERVE_STATUS_OPTION
+};
+
+static struct option const long_options[] =
+{
+ {"kill-after", required_argument, NULL, 'k'},
+ {"signal", required_argument, NULL, 's'},
+ {"foreground", no_argument, NULL, FOREGROUND_OPTION},
+ {"preserve-status", no_argument, NULL, PRESERVE_STATUS_OPTION},
+ {GETOPT_HELP_OPTION_DECL},
+ {GETOPT_VERSION_OPTION_DECL},
+ {NULL, 0, NULL, 0}
+};
+
+static void
+unblock_signal (int sig)
+{
+ sigset_t unblock_set;
+ sigemptyset (&unblock_set);
+ sigaddset (&unblock_set, sig);
+ if (sigprocmask (SIG_UNBLOCK, &unblock_set, NULL) != 0)
+ error (0, errno, _("warning: sigprocmask"));
+}
+
+/* Start the timeout after which we'll receive a SIGALRM.
+ Round DURATION up to the next representable value.
+ Treat out-of-range values as if they were maximal,
+ as that's more useful in practice than reporting an error.
+ '0' means don't timeout. */
+static void
+settimeout (double duration, bool warn)
+{
+
+ /* We configure timers below so that SIGALRM is sent on expiry.
+ Therefore ensure we don't inherit a mask blocking SIGALRM. */
+ unblock_signal (SIGALRM);
+
+/* timer_settime() provides potentially nanosecond resolution.
+ setitimer() is more portable (to Darwin for example),
+ but only provides microsecond resolution and thus is
+ a little more awkward to use with timespecs, as well as being
+ deprecated by POSIX. Instead we fallback to single second
+ resolution provided by alarm(). */
+
+#if HAVE_TIMER_SETTIME
+ struct timespec ts = dtotimespec (duration);
+ struct itimerspec its = { {0, 0}, ts };
+ timer_t timerid;
+ if (timer_create (CLOCK_REALTIME, NULL, &timerid) == 0)
+ {
+ if (timer_settime (timerid, 0, &its, NULL) == 0)
+ return;
+ else
+ {
+ if (warn)
+ error (0, errno, _("warning: timer_settime"));
+ timer_delete (timerid);
+ }
+ }
+ else if (warn && errno != ENOSYS)
+ error (0, errno, _("warning: timer_create"));
+#endif
+
+ unsigned int timeint;
+ if (UINT_MAX <= duration)
+ timeint = UINT_MAX;
+ else
+ {
+ unsigned int duration_floor = duration;
+ timeint = duration_floor + (duration_floor < duration);
+ }
+ alarm (timeint);
+}
+
+/* send SIG avoiding the current process. */
+
+static int
+send_sig (int where, int sig)
+{
+ /* If sending to the group, then ignore the signal,
+ so we don't go into a signal loop. Note that this will ignore any of the
+ signals registered in install_signal_handlers(), that are sent after we
+ propagate the first one, which hopefully won't be an issue. Note this
+ process can be implicitly multithreaded due to some timer_settime()
+ implementations, therefore a signal sent to the group, can be sent
+ multiple times to this process. */
+ if (where == 0)
+ signal (sig, SIG_IGN);
+ return kill (where, sig);
+}
+
+static void
+cleanup (int sig)
+{
+ if (sig == SIGALRM)
+ {
+ timed_out = 1;
+ sig = term_signal;
+ }
+ if (monitored_pid)
+ {
+ if (kill_after)
+ {
+ int saved_errno = errno; /* settimeout may reset. */
+ /* Start a new timeout after which we'll send SIGKILL. */
+ term_signal = SIGKILL;
+ settimeout (kill_after, false);
+ kill_after = 0; /* Don't let later signals reset kill alarm. */
+ errno = saved_errno;
+ }
+
+ /* Send the signal directly to the monitored child,
+ in case it has itself become group leader,
+ or is not running in a separate group. */
+ send_sig (monitored_pid, sig);
+
+ /* The normal case is the job has remained in our
+ newly created process group, so send to all processes in that. */
+ if (!foreground)
+ {
+ send_sig (0, sig);
+ if (sig != SIGKILL && sig != SIGCONT)
+ {
+ send_sig (monitored_pid, SIGCONT);
+ send_sig (0, SIGCONT);
+ }
+ }
+ }
+ else /* we're the child or the child is not exec'd yet. */
+ _exit (128 + sig);
+}
+
+void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ emit_try_help ();
+ else
+ {
+ printf (_("\
+Usage: %s [OPTION] DURATION COMMAND [ARG]...\n\
+ or: %s [OPTION]\n"), program_name, program_name);
+
+ fputs (_("\
+Start COMMAND, and kill it if still running after DURATION.\n\
+"), stdout);
+
+ emit_mandatory_arg_note ();
+
+ fputs (_("\
+ --preserve-status\n\
+ exit with the same status as COMMAND, even when the\n\
+ command times out\n\
+ --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\
+ -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\
+ -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 (HELP_OPTION_DESCRIPTION, stdout);
+ fputs (VERSION_OPTION_DESCRIPTION, stdout);
+
+ fputs (_("\n\
+DURATION is a floating point number with an optional suffix:\n\
+'s' for seconds (the default), 'm' for minutes, 'h' for hours \
+or 'd' for days.\n"), stdout);
+
+ fputs (_("\n\
+If the command times out, and --preserve-status is not set, then exit with\n\
+status 124. Otherwise, exit with the status of COMMAND. If no signal\n\
+is specified, send the TERM signal upon timeout. The TERM signal kills\n\
+any process that does not block or catch that signal. It may be necessary\n\
+to use the KILL (9) signal, since this signal cannot be caught, in which\n\
+case the exit status is 128+9 rather than 124.\n"), stdout);
+ emit_ancillary_info (PROGRAM_NAME);
+ }
+ exit (status);
+}
+
+/* Given a floating point value *X, and a suffix character, SUFFIX_CHAR,
+ scale *X by the multiplier implied by SUFFIX_CHAR. SUFFIX_CHAR may
+ be the NUL byte or 's' to denote seconds, 'm' for minutes, 'h' for
+ hours, or 'd' for days. If SUFFIX_CHAR is invalid, don't modify *X
+ and return false. Otherwise return true. */
+
+static bool
+apply_time_suffix (double *x, char suffix_char)
+{
+ int multiplier;
+
+ switch (suffix_char)
+ {
+ case 0:
+ case 's':
+ multiplier = 1;
+ break;
+ case 'm':
+ multiplier = 60;
+ break;
+ case 'h':
+ multiplier = 60 * 60;
+ break;
+ case 'd':
+ multiplier = 60 * 60 * 24;
+ break;
+ default:
+ return false;
+ }
+
+ *x *= multiplier;
+
+ return true;
+}
+
+static double
+parse_duration (const char* str)
+{
+ double duration;
+ const char *ep;
+
+ if (!xstrtod (str, &ep, &duration, c_strtod)
+ /* Nonnegative interval. */
+ || ! (0 <= duration)
+ /* No extra chars after the number and an optional s,m,h,d char. */
+ || (*ep && *(ep + 1))
+ /* Check any suffix char and update timeout based on the suffix. */
+ || !apply_time_suffix (&duration, *ep))
+ {
+ error (0, 0, _("invalid time interval %s"), quote (str));
+ usage (EXIT_CANCELED);
+ }
+
+ return duration;
+}
+
+static void
+install_signal_handlers (int sigterm)
+{
+ struct sigaction sa;
+ sigemptyset (&sa.sa_mask); /* Allow concurrent calls to handler */
+ sa.sa_handler = cleanup;
+ sa.sa_flags = SA_RESTART; /* Restart syscalls if possible, as that's
+ more likely to work cleanly. */
+
+ sigaction (SIGALRM, &sa, NULL); /* our timeout. */
+ sigaction (SIGINT, &sa, NULL); /* Ctrl-C at terminal for example. */
+ sigaction (SIGQUIT, &sa, NULL); /* Ctrl-\ at terminal for example. */
+ sigaction (SIGHUP, &sa, NULL); /* terminal closed for example. */
+ sigaction (SIGTERM, &sa, NULL); /* if we're killed, stop monitored proc. */
+ sigaction (sigterm, &sa, NULL); /* user specified termination signal. */
+}
+
+/* Try to disable core dumps for this process.
+ Return TRUE if successful, FALSE otherwise. */
+static bool
+disable_core_dumps (void)
+{
+#if HAVE_PRCTL && defined PR_SET_DUMPABLE
+ if (prctl (PR_SET_DUMPABLE, 0) == 0)
+ return true;
+
+#elif HAVE_SETRLIMIT && defined RLIMIT_CORE
+ /* Note this doesn't disable processing by a filter in
+ /proc/sys/kernel/core_pattern on Linux. */
+ if (setrlimit (RLIMIT_CORE, &(struct rlimit) {0,0}) == 0)
+ return true;
+
+#else
+ return false;
+#endif
+
+ error (0, errno, _("warning: disabling core dumps failed"));
+ return false;
+}
+
+int
+main (int argc, char **argv)
+{
+ double timeout;
+ char signame[SIG2STR_MAX];
+ int c;
+
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ initialize_exit_failure (EXIT_CANCELED);
+ atexit (close_stdout);
+
+ while ((c = getopt_long (argc, argv, "+k:s:", long_options, NULL)) != -1)
+ {
+ switch (c)
+ {
+ case 'k':
+ kill_after = parse_duration (optarg);
+ break;
+
+ case 's':
+ term_signal = operand2sig (optarg, signame);
+ if (term_signal == -1)
+ usage (EXIT_CANCELED);
+ break;
+
+ case FOREGROUND_OPTION:
+ foreground = true;
+ break;
+
+ case PRESERVE_STATUS_OPTION:
+ preserve_status = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_CANCELED);
+ break;
+ }
+ }
+
+ if (argc - optind < 2)
+ usage (EXIT_CANCELED);
+
+ timeout = parse_duration (argv[optind++]);
+
+ argv += optind;
+
+ /* Ensure we're in our own group so all subprocesses can be killed.
+ Note we don't just put the child in a separate group as
+ then we would need to worry about foreground and background groups
+ and propagating signals between them. */
+ if (!foreground)
+ setpgid (0, 0);
+
+ /* Setup handlers before fork() so that we
+ handle any signals caused by child, without races. */
+ install_signal_handlers (term_signal);
+ signal (SIGTTIN, SIG_IGN); /* Don't stop if background child needs tty. */
+ signal (SIGTTOU, SIG_IGN); /* Don't stop if background child needs tty. */
+ signal (SIGCHLD, SIG_DFL); /* Don't inherit CHLD handling from parent. */
+
+ monitored_pid = fork ();
+ if (monitored_pid == -1)
+ {
+ error (0, errno, _("fork system call failed"));
+ return EXIT_CANCELED;
+ }
+ else if (monitored_pid == 0)
+ { /* child */
+ /* exec doesn't reset SIG_IGN -> SIG_DFL. */
+ signal (SIGTTIN, SIG_DFL);
+ signal (SIGTTOU, SIG_DFL);
+
+ execvp (argv[0], argv); /* FIXME: should we use "sh -c" ... here? */
+
+ /* exit like sh, env, nohup, ... */
+ int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
+ error (0, errno, _("failed to run command %s"), quote (argv[0]));
+ return exit_status;
+ }
+ else
+ {
+ pid_t wait_result;
+ int status;
+
+ settimeout (timeout, true);
+
+ while ((wait_result = waitpid (monitored_pid, &status, 0)) < 0
+ && errno == EINTR)
+ continue;
+
+ if (wait_result < 0)
+ {
+ /* shouldn't happen. */
+ error (0, errno, _("error waiting for command"));
+ status = EXIT_CANCELED;
+ }
+ else
+ {
+ if (WIFEXITED (status))
+ status = WEXITSTATUS (status);
+ else if (WIFSIGNALED (status))
+ {
+ int sig = WTERMSIG (status);
+ if (WCOREDUMP (status))
+ error (0, 0, _("the monitored command dumped core"));
+ if (!timed_out && disable_core_dumps ())
+ {
+ /* exit with the signal flag set. */
+ signal (sig, SIG_DFL);
+ raise (sig);
+ }
+ status = sig + 128; /* what sh returns for signaled processes. */
+ }
+ else
+ {
+ /* shouldn't happen. */
+ error (0, 0, _("unknown status from command (%d)"), status);
+ status = EXIT_FAILURE;
+ }
+ }
+
+ if (timed_out && !preserve_status)
+ status = EXIT_TIMEDOUT;
+ return status;
+ }
+}
diff --git a/src/touch.c b/src/touch.c
index a79c26d..b851b6f 100644
--- a/src/touch.c
+++ b/src/touch.c
@@ -1,10 +1,10 @@
/* touch -- change modification and access times of files
- Copyright (C) 87, 1989-1991, 1995-2005 Free Software Foundation, Inc.
+ Copyright (C) 1987-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Paul Rubin, Arnold Robbins, Jim Kingdon, David MacKenzie,
and Randy Smith. */
@@ -22,32 +21,33 @@
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
+#include <assert.h>
#include "system.h"
#include "argmatch.h"
#include "error.h"
#include "fd-reopen.h"
-#include "getdate.h"
+#include "parse-datetime.h"
#include "posixtm.h"
#include "posixver.h"
#include "quote.h"
-#include "safe-read.h"
#include "stat-time.h"
#include "utimens.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "touch"
#define AUTHORS \
-"Paul Rubin", "Arnold Robbins, Jim Kingdon, David MacKenzie", "Randy Smith"
+ proper_name ("Paul Rubin"), \
+ proper_name ("Arnold Robbins"), \
+ proper_name ("Jim Kingdon"), \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Randy Smith")
-/* Bitmasks for `change_times'. */
+/* Bitmasks for 'change_times'. */
#define CH_ATIME 1
#define CH_MTIME 2
-/* The name by which this program was run. */
-char *program_name;
-
/* Which timestamps to change. */
static int change_times;
@@ -57,6 +57,9 @@ static bool no_create;
/* (-r) If true, use times from a reference file. */
static bool use_ref;
+/* (-h) If true, change the times of an existing symlink, if possible. */
+static bool no_dereference;
+
/* If true, the only thing we have to do is change both the
modification and access time to the current time, so we don't
have to own the file, just be able to read and write it.
@@ -82,20 +85,20 @@ static struct option const longopts[] =
{"time", required_argument, NULL, TIME_OPTION},
{"no-create", no_argument, NULL, 'c'},
{"date", required_argument, NULL, 'd'},
- {"file", required_argument, NULL, 'r'}, /* FIXME: remove --file in 2006 */
{"reference", required_argument, NULL, 'r'},
+ {"no-dereference", no_argument, NULL, 'h'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
};
-/* Valid arguments to the `--time' option. */
+/* Valid arguments to the '--time' option. */
static char const* const time_args[] =
{
"atime", "access", "use", "mtime", "modify", NULL
};
-/* The bits in `change_times' that those arguments set. */
+/* The bits in 'change_times' that those arguments set. */
static int const time_masks[] =
{
CH_ATIME, CH_ATIME, CH_ATIME, CH_MTIME, CH_MTIME
@@ -106,9 +109,9 @@ static int const time_masks[] =
static void
get_reldate (struct timespec *result,
- char const *flex_date, struct timespec const *now)
+ char const *flex_date, struct timespec const *now)
{
- if (! get_date (result, flex_date, now))
+ if (! parse_datetime (result, flex_date, now))
error (EXIT_FAILURE, 0, _("invalid date format %s"), quote (flex_date));
}
@@ -119,103 +122,80 @@ static bool
touch (const char *file)
{
bool ok;
- struct stat sbuf;
int fd = -1;
int open_errno = 0;
- struct timespec timespec[2];
- struct timespec const *t;
+ struct timespec const *t = newtime;
if (STREQ (file, "-"))
fd = STDOUT_FILENO;
- else if (! no_create)
+ else if (! (no_create || no_dereference))
{
/* Try to open FILE, creating it if necessary. */
fd = fd_reopen (STDIN_FILENO, file,
- O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+ O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY, MODE_RW_UGO);
/* Don't save a copy of errno if it's EISDIR, since that would lead
- touch to give a bogus diagnostic for e.g., `touch /' (assuming
- we don't own / or have write access to it). On Solaris 5.6,
- and probably other systems, it is EINVAL. On SunOS4, it's EPERM. */
+ touch to give a bogus diagnostic for e.g., 'touch /' (assuming
+ we don't own / or have write access to it). On Solaris 5.6,
+ and probably other systems, it is EINVAL. On SunOS4, it's EPERM. */
if (fd == -1 && errno != EISDIR && errno != EINVAL && errno != EPERM)
- open_errno = errno;
+ open_errno = errno;
}
if (change_times != (CH_ATIME | CH_MTIME))
{
- /* We're setting only one of the time values. stat the target to get
- the other one. If we have the file descriptor already, use fstat.
- Otherwise, either we're in no-create mode (and hence didn't call open)
- or FILE is inaccessible or a directory, so we have to use stat. */
- if (fd != -1 ? fstat (fd, &sbuf) : stat (file, &sbuf))
- {
- if (open_errno)
- error (0, open_errno, _("creating %s"), quote (file));
- else
- {
- if (no_create && (errno == ENOENT || errno == EBADF))
- return true;
- error (0, errno, _("failed to get attributes of %s"),
- quote (file));
- }
- if (fd == STDIN_FILENO)
- close (fd);
- return false;
- }
+ /* We're setting only one of the time values. */
+ if (change_times == CH_MTIME)
+ newtime[0].tv_nsec = UTIME_OMIT;
+ else
+ {
+ assert (change_times == CH_ATIME);
+ newtime[1].tv_nsec = UTIME_OMIT;
+ }
}
if (amtime_now)
{
/* Pass NULL to futimens so it will not fail if we have
- write access to the file, but don't own it. */
+ write access to the file, but don't own it. */
t = NULL;
}
- else
- {
- timespec[0] = (change_times & CH_ATIME
- ? newtime[0]
- : get_stat_atime (&sbuf));
- timespec[1] = (change_times & CH_MTIME
- ? newtime[1]
- : get_stat_mtime (&sbuf));
- t = timespec;
- }
- ok = (futimens (fd, (fd == STDOUT_FILENO ? NULL : file), t) == 0);
+ ok = (fdutimensat (fd, AT_FDCWD, (fd == STDOUT_FILENO ? NULL : file), t,
+ (no_dereference && fd == -1) ? AT_SYMLINK_NOFOLLOW : 0)
+ == 0);
if (fd == STDIN_FILENO)
{
if (close (STDIN_FILENO) != 0)
- {
- error (0, errno, _("closing %s"), quote (file));
- return false;
- }
+ {
+ error (0, errno, _("failed to close %s"), quoteaf (file));
+ return false;
+ }
}
else if (fd == STDOUT_FILENO)
{
/* Do not diagnose "touch -c - >&-". */
- if (!ok && errno == EBADF && no_create
- && change_times == (CH_ATIME | CH_MTIME))
- return true;
+ if (!ok && errno == EBADF && no_create)
+ return true;
}
if (!ok)
{
if (open_errno)
- {
- /* The wording of this diagnostic should cover at least two cases:
- - the file does not exist, but the parent directory is unwritable
- - the file exists, but it isn't writable
- I think it's not worth trying to distinguish them. */
- error (0, open_errno, _("cannot touch %s"), quote (file));
- }
+ {
+ /* The wording of this diagnostic should cover at least two cases:
+ - the file does not exist, but the parent directory is unwritable
+ - the file exists, but it isn't writable
+ I think it's not worth trying to distinguish them. */
+ error (0, open_errno, _("cannot touch %s"), quoteaf (file));
+ }
else
- {
- if (no_create && errno == ENOENT)
- return true;
- error (0, errno, _("setting times of %s"), quote (file));
- }
+ {
+ if (no_create && errno == ENOENT)
+ return true;
+ error (0, errno, _("setting times of %s"), quoteaf (file));
+ }
return false;
}
@@ -226,29 +206,38 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
fputs (_("\
Update the access and modification times of each FILE to the current time.\n\
\n\
+A FILE argument that does not exist is created empty, unless -c or -h\n\
+is supplied.\n\
+\n\
+A FILE argument string of - is handled specially and causes touch to\n\
+change the times of the file associated with standard output.\n\
"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
-"), stdout);
+
+ 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 STAMP use [[CC]YY]MMDDhhmm[.ss] instead of current time\n\
- --time=WORD change the specified time:\n\
+ --time=WORD change the specified time:\n\
WORD is access, atime, or use: equivalent to -a\n\
WORD is modify or mtime: equivalent to -m\n\
"), stdout);
@@ -257,10 +246,8 @@ Mandatory arguments to long options are mandatory for short options too.\n\
fputs (_("\
\n\
Note that the -d and -t options accept different time-date formats.\n\
-\n\
-If a FILE is -, touch standard output.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -274,7 +261,7 @@ main (int argc, char **argv)
char const *flex_date = NULL;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -284,56 +271,60 @@ main (int argc, char **argv)
change_times = 0;
no_create = use_ref = false;
- while ((c = getopt_long (argc, argv, "acd:fmr:t:", longopts, NULL)) != -1)
+ while ((c = getopt_long (argc, argv, "acd:fhmr:t:", longopts, NULL)) != -1)
{
switch (c)
- {
- case 'a':
- change_times |= CH_ATIME;
- break;
-
- case 'c':
- no_create = true;
- break;
-
- case 'd':
- flex_date = optarg;
- break;
-
- case 'f':
- break;
-
- case 'm':
- change_times |= CH_MTIME;
- break;
-
- case 'r':
- use_ref = true;
- ref_file = optarg;
- break;
-
- case 't':
- if (! posixtime (&newtime[0].tv_sec, optarg,
- PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS))
- error (EXIT_FAILURE, 0, _("invalid date format %s"),
- quote (optarg));
- newtime[0].tv_nsec = 0;
- newtime[1] = newtime[0];
- date_set = true;
- break;
-
- case TIME_OPTION: /* --time */
- change_times |= XARGMATCH ("--time", optarg,
- time_args, time_masks);
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'a':
+ change_times |= CH_ATIME;
+ break;
+
+ case 'c':
+ no_create = true;
+ break;
+
+ case 'd':
+ flex_date = optarg;
+ break;
+
+ case 'f':
+ break;
+
+ case 'h':
+ no_dereference = true;
+ break;
+
+ case 'm':
+ change_times |= CH_MTIME;
+ break;
+
+ case 'r':
+ use_ref = true;
+ ref_file = optarg;
+ break;
+
+ case 't':
+ if (! posixtime (&newtime[0].tv_sec, optarg,
+ PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS))
+ error (EXIT_FAILURE, 0, _("invalid date format %s"),
+ quote (optarg));
+ newtime[0].tv_nsec = 0;
+ newtime[1] = newtime[0];
+ date_set = true;
+ break;
+
+ case TIME_OPTION: /* --time */
+ change_times |= XARGMATCH ("--time", optarg,
+ time_args, time_masks);
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
}
if (change_times == 0)
@@ -348,50 +339,79 @@ main (int argc, char **argv)
if (use_ref)
{
struct stat ref_stats;
- if (stat (ref_file, &ref_stats))
- error (EXIT_FAILURE, errno,
- _("failed to get attributes of %s"), quote (ref_file));
+ /* Don't use (no_dereference?lstat:stat) (args), since stat
+ might be an object-like macro. */
+ if (no_dereference ? lstat (ref_file, &ref_stats)
+ : stat (ref_file, &ref_stats))
+ error (EXIT_FAILURE, errno,
+ _("failed to get attributes of %s"), quoteaf (ref_file));
newtime[0] = get_stat_atime (&ref_stats);
newtime[1] = get_stat_mtime (&ref_stats);
date_set = true;
if (flex_date)
- {
- if (change_times & CH_ATIME)
- get_reldate (&newtime[0], flex_date, &newtime[0]);
- if (change_times & CH_MTIME)
- get_reldate (&newtime[1], flex_date, &newtime[1]);
- }
+ {
+ if (change_times & CH_ATIME)
+ get_reldate (&newtime[0], flex_date, &newtime[0]);
+ if (change_times & CH_MTIME)
+ get_reldate (&newtime[1], flex_date, &newtime[1]);
+ }
}
else
{
if (flex_date)
- {
- get_reldate (&newtime[0], flex_date, NULL);
- newtime[1] = newtime[0];
- date_set = true;
- }
+ {
+ struct timespec now;
+ gettime (&now);
+ get_reldate (&newtime[0], flex_date, &now);
+ newtime[1] = newtime[0];
+ date_set = true;
+
+ /* If neither -a nor -m is specified, treat "-d now" as if
+ 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)
+ {
+ /* Check that it really was "-d now", and not a time
+ stamp that just happens to be the current time. */
+ struct timespec notnow, notnow1;
+ notnow.tv_sec = now.tv_sec ^ 1;
+ notnow.tv_nsec = now.tv_nsec;
+ get_reldate (&notnow1, flex_date, &notnow);
+ if (notnow1.tv_sec == notnow.tv_sec
+ && notnow1.tv_nsec == notnow.tv_nsec)
+ date_set = false;
+ }
+ }
}
- /* The obsolete `MMDDhhmm[YY]' form is valid IFF there are
+ /* The obsolete 'MMDDhhmm[YY]' form is valid IFF there are
two or more non-option arguments. */
if (!date_set && 2 <= argc - optind && posix2_version () < 200112
&& posixtime (&newtime[0].tv_sec, argv[optind],
- PDS_TRAILING_YEAR | PDS_PRE_2000))
+ PDS_TRAILING_YEAR | PDS_PRE_2000))
{
newtime[0].tv_nsec = 0;
newtime[1] = newtime[0];
date_set = true;
if (! getenv ("POSIXLY_CORRECT"))
- {
- struct tm const *tm = localtime (&newtime[0].tv_sec);
- error (0, 0,
- _("warning: `touch %s' is obsolete; use "
- "`touch -t %04ld%02d%02d%02d%02d.%02d'"),
- argv[optind],
- tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
- }
+ {
+ struct tm const *tm = localtime (&newtime[0].tv_sec);
+
+ /* Technically, it appears that even a deliberate attempt to cause
+ 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)
+ error (0, 0,
+ _("warning: 'touch %s' is obsolete; use "
+ "'touch -t %04ld%02d%02d%02d%02d.%02d'"),
+ argv[optind],
+ tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ }
optind++;
}
@@ -399,12 +419,9 @@ main (int argc, char **argv)
if (!date_set)
{
if (change_times == (CH_ATIME | CH_MTIME))
- amtime_now = true;
+ amtime_now = true;
else
- {
- gettime (&newtime[0]);
- newtime[1] = newtime[0];
- }
+ newtime[1].tv_nsec = newtime[0].tv_nsec = UTIME_NOW;
}
if (optind == argc)
@@ -416,5 +433,5 @@ main (int argc, char **argv)
for (; optind < argc; ++optind)
ok &= touch (argv[optind]);
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/tr.c b/src/tr.c
index 214eb2b..c6a1540 100644
--- a/src/tr.c
+++ b/src/tr.c
@@ -1,10 +1,10 @@
/* tr -- a filter to translate characters
- Copyright (C) 91, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1991-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Jim Meyering */
@@ -26,14 +25,16 @@
#include "system.h"
#include "error.h"
+#include "fadvise.h"
#include "quote.h"
#include "safe-read.h"
+#include "xfreopen.h"
#include "xstrtol.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "tr"
-#define AUTHORS "Jim Meyering"
+#define AUTHORS proper_name ("Jim Meyering")
enum { N_CHARS = UCHAR_MAX + 1 };
@@ -100,28 +101,28 @@ enum Range_element_type
For example, consider the POSIX version of the classic tr command:
tr -cs 'a-zA-Z_' '[\n*]'
String1 has 3 constructs, two of which are ranges (a-z and A-Z),
- and a single normal character, `_'. String2 has one construct. */
+ and a single normal character, '_'. String2 has one construct. */
struct List_element
{
enum Range_element_type type;
struct List_element *next;
union
{
- unsigned char normal_char;
- struct /* unnamed */
- {
- unsigned char first_char;
- unsigned char last_char;
- }
- range;
- enum Char_class char_class;
- unsigned char equiv_code;
- struct /* unnamed */
- {
- unsigned char the_repeated_char;
- count repeat_count;
- }
- repeated_char;
+ unsigned char normal_char;
+ struct /* unnamed */
+ {
+ unsigned char first_char;
+ unsigned char last_char;
+ }
+ range;
+ enum Char_class char_class;
+ unsigned char equiv_code;
+ struct /* unnamed */
+ {
+ unsigned char the_repeated_char;
+ count repeat_count;
+ }
+ repeated_char;
}
u;
};
@@ -132,9 +133,9 @@ struct List_element
the corresponding argument string. The attributes are used mainly
to verify that the strings are valid in the context of any options
specified (like -s, -d, or -c). The main exception is the member
- `tail', which is first used to construct the list. After construction,
+ 'tail', which is first used to construct the list. After construction,
it is used by get_next to save its state when traversing the list.
- The member `state' serves a similar function. */
+ The member 'state' serves a similar function. */
struct Spec_list
{
/* Points to the head of the list of range elements.
@@ -194,9 +195,6 @@ es_match (struct E_string const *es, size_t i, char c)
return es->s[i] == c && !es->escaped[i];
}
-/* The name by which this program was run. */
-char *program_name;
-
/* When true, each sequence in the input of a repeated character
(call it c) is replaced (in the output) by a single occurrence of c
for every c in the squeeze set. */
@@ -249,15 +247,14 @@ static char const *const char_class_name[] =
"alnum", "alpha", "blank", "cntrl", "digit", "graph",
"lower", "print", "punct", "space", "upper", "xdigit"
};
-enum { N_CHAR_CLASSES = sizeof char_class_name / sizeof char_class_name[0] };
-/* Array of boolean values. A character `c' is a member of the
+/* Array of boolean values. A character 'c' is a member of the
squeeze set if and only if in_squeeze_set[c] is true. The squeeze
set is defined by the last (possibly, the only) string argument
on the command line when the squeeze option is given. */
static bool in_squeeze_set[N_CHARS];
-/* Array of boolean values. A character `c' is a member of the
+/* Array of boolean values. A character 'c' is a member of the
delete set if and only if in_delete_set[c] is true. The delete
set is defined by the first (or only) string argument on the
command line when the delete option is given. */
@@ -278,28 +275,27 @@ static struct option const long_options[] =
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
};
-
+
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... SET1 [SET2]\n\
"),
- program_name);
+ program_name);
fputs (_("\
Translate, squeeze, and/or delete characters from standard input,\n\
writing to standard output.\n\
\n\
- -c, -C, --complement first complement SET1\n\
+ -c, -C, --complement use the complement of SET1\n\
-d, --delete delete characters in SET1, do not translate\n\
- -s, --squeeze-repeats replace each input sequence of a repeated character\n\
- that is listed in SET1 with a single occurrence\n\
- of that character\n\
+ -s, --squeeze-repeats replace each sequence of a repeated character\n\
+ that is listed in the last specified SET,\n\
+ with a single occurrence of that character\n\
-t, --truncate-set1 first truncate SET1 to length of SET2\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
@@ -343,20 +339,13 @@ Interpreted sequences are:\n\
\n\
Translation occurs if -d is not given and both SET1 and SET2 appear.\n\
-t may be used only when translating. SET2 is extended to length of\n\
-SET1 by repeating its last character as necessary. \
-"), stdout);
- fputs (_("\
-Excess characters\n\
+SET1 by repeating its last character as necessary. Excess characters\n\
of SET2 are ignored. Only [:lower:] and [:upper:] are guaranteed to\n\
expand in ascending order; used in SET2 while translating, they may\n\
-only be used in pairs to specify case conversion. \
+only be used in pairs to specify case conversion. -s uses the last\n\
+specified SET, and occurs after translation or deletion.\n\
"), stdout);
- fputs (_("\
--s uses SET1 if not\n\
-translating nor deleting; else squeezing uses SET2 and occurs after\n\
-translation or deletion.\n\
-"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -373,7 +362,7 @@ is_equiv_class_member (unsigned char equiv_class, unsigned char c)
/* Return true if the character C is a member of the
character class CHAR_CLASS. */
-static bool
+static bool _GL_ATTRIBUTE_PURE
is_char_class_member (enum Char_class char_class, unsigned char c)
{
int result;
@@ -455,93 +444,94 @@ unquote (char const *s, struct E_string *es)
int oct_digit;
switch (s[i])
- {
- case '\\':
- es->escaped[j] = true;
- switch (s[i + 1])
- {
- case '\\':
- c = '\\';
- break;
- case 'a':
- c = '\a';
- break;
- case 'b':
- c = '\b';
- break;
- case 'f':
- c = '\f';
- break;
- case 'n':
- c = '\n';
- break;
- case 'r':
- c = '\r';
- break;
- case 't':
- c = '\t';
- break;
- case 'v':
- c = '\v';
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- c = s[i + 1] - '0';
- oct_digit = s[i + 2] - '0';
- if (0 <= oct_digit && oct_digit <= 7)
- {
- c = 8 * c + oct_digit;
- ++i;
- oct_digit = s[i + 2] - '0';
- if (0 <= oct_digit && oct_digit <= 7)
- {
- if (8 * c + oct_digit < N_CHARS)
- {
- c = 8 * c + oct_digit;
- ++i;
- }
- else
- {
- /* A 3-digit octal number larger than \377 won't
- fit in 8 bits. So we stop when adding the
- next digit would put us over the limit and
- give a warning about the ambiguity. POSIX
- isn't clear on this, and we interpret this
- lack of clarity as meaning the resulting behavior
- is undefined, which means we're allowed to issue
- a warning. */
- error (0, 0, _("warning: the ambiguous octal escape \
-\\%c%c%c is being\n\tinterpreted as the 2-byte sequence \\0%c%c, %c"),
- s[i], s[i + 1], s[i + 2],
- s[i], s[i + 1], s[i + 2]);
- }
- }
- }
- break;
- case '\0':
- /* POSIX seems to require that a trailing backslash must
- stand for itself. Weird. */
- es->escaped[j] = false;
- i--;
- c = '\\';
- break;
- default:
- c = s[i + 1];
- break;
- }
- ++i;
- es->s[j++] = c;
- break;
- default:
- es->s[j++] = s[i];
- break;
- }
+ {
+ case '\\':
+ es->escaped[j] = true;
+ switch (s[i + 1])
+ {
+ case '\\':
+ c = '\\';
+ break;
+ case 'a':
+ c = '\a';
+ break;
+ case 'b':
+ c = '\b';
+ break;
+ case 'f':
+ c = '\f';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case 'v':
+ c = '\v';
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ c = s[i + 1] - '0';
+ oct_digit = s[i + 2] - '0';
+ if (0 <= oct_digit && oct_digit <= 7)
+ {
+ c = 8 * c + oct_digit;
+ ++i;
+ oct_digit = s[i + 2] - '0';
+ if (0 <= oct_digit && oct_digit <= 7)
+ {
+ if (8 * c + oct_digit < N_CHARS)
+ {
+ c = 8 * c + oct_digit;
+ ++i;
+ }
+ else
+ {
+ /* A 3-digit octal number larger than \377 won't
+ fit in 8 bits. So we stop when adding the
+ next digit would put us over the limit and
+ give a warning about the ambiguity. POSIX
+ isn't clear on this, and we interpret this
+ lack of clarity as meaning the resulting behavior
+ is undefined, which means we're allowed to issue
+ a warning. */
+ error (0, 0, _("warning: the ambiguous octal escape\
+ \\%c%c%c is being\n\tinterpreted as the 2-byte sequence \\0%c%c, %c"),
+ s[i], s[i + 1], s[i + 2],
+ s[i], s[i + 1], s[i + 2]);
+ }
+ }
+ }
+ break;
+ case '\0':
+ error (0, 0, _("warning: an unescaped backslash "
+ "at end of string is not portable"));
+ /* POSIX is not clear about this. */
+ es->escaped[j] = false;
+ i--;
+ c = '\\';
+ break;
+ default:
+ c = s[i + 1];
+ break;
+ }
+ ++i;
+ es->s[j++] = c;
+ break;
+ default:
+ es->s[j++] = s[i];
+ break;
+ }
}
es->len = j;
return true;
@@ -550,14 +540,14 @@ unquote (char const *s, struct E_string *es)
/* If CLASS_STR is a valid character class string, return its index
in the global char_class_name array. Otherwise, return CC_NO_CLASS. */
-static enum Char_class
+static enum Char_class _GL_ATTRIBUTE_PURE
look_up_char_class (char const *class_str, size_t len)
{
enum Char_class i;
- for (i = 0; i < N_CHAR_CLASSES; i++)
- if (strncmp (class_str, char_class_name[i], len) == 0
- && strlen (char_class_name[i]) == len)
+ for (i = 0; i < ARRAY_CARDINALITY (char_class_name); i++)
+ if (STREQ_LEN (class_str, char_class_name[i], len)
+ && strlen (char_class_name[i]) == len)
return i;
return CC_NO_CLASS;
}
@@ -605,42 +595,42 @@ make_printable_str (char const *s, size_t len)
unsigned char c = s[i];
switch (c)
- {
- case '\\':
- tmp = "\\";
- break;
- case '\a':
- tmp = "\\a";
- break;
- case '\b':
- tmp = "\\b";
- break;
- case '\f':
- tmp = "\\f";
- break;
- case '\n':
- tmp = "\\n";
- break;
- case '\r':
- tmp = "\\r";
- break;
- case '\t':
- tmp = "\\t";
- break;
- case '\v':
- tmp = "\\v";
- break;
- default:
- if (isprint (c))
- {
- buf[0] = c;
- buf[1] = '\0';
- }
- else
- sprintf (buf, "\\%03o", c);
- tmp = buf;
- break;
- }
+ {
+ case '\\':
+ tmp = "\\";
+ break;
+ case '\a':
+ tmp = "\\a";
+ break;
+ case '\b':
+ tmp = "\\b";
+ break;
+ case '\f':
+ tmp = "\\f";
+ break;
+ case '\n':
+ tmp = "\\n";
+ break;
+ case '\r':
+ tmp = "\\r";
+ break;
+ case '\t':
+ tmp = "\\t";
+ break;
+ case '\v':
+ tmp = "\\v";
+ break;
+ default:
+ if (isprint (c))
+ {
+ buf[0] = c;
+ buf[1] = '\0';
+ }
+ else
+ sprintf (buf, "\\%03o", c);
+ tmp = buf;
+ break;
+ }
p = stpcpy (p, tmp);
}
return printable_buf;
@@ -679,8 +669,8 @@ append_range (struct Spec_list *list, unsigned char first, unsigned char last)
char *tmp2 = make_printable_char (last);
error (0, 0,
- _("range-endpoints of `%s-%s' are in reverse collating sequence order"),
- tmp1, tmp2);
+ _("range-endpoints of '%s-%s' are in reverse collating sequence order"),
+ tmp1, tmp2);
free (tmp1);
free (tmp2);
return false;
@@ -703,7 +693,7 @@ append_range (struct Spec_list *list, unsigned char first, unsigned char last)
static bool
append_char_class (struct Spec_list *list,
- char const *char_class_str, size_t len)
+ char const *char_class_str, size_t len)
{
enum Char_class char_class;
struct List_element *new;
@@ -728,7 +718,7 @@ append_char_class (struct Spec_list *list,
static void
append_repeated_char (struct Spec_list *list, unsigned char the_char,
- count repeat_count)
+ count repeat_count)
{
struct List_element *new;
@@ -750,7 +740,7 @@ append_repeated_char (struct Spec_list *list, unsigned char the_char,
static bool
append_equiv_class (struct Spec_list *list,
- char const *equiv_class_str, size_t len)
+ char const *equiv_class_str, size_t len)
{
struct List_element *new;
@@ -774,16 +764,16 @@ append_equiv_class (struct Spec_list *list,
static bool
find_closing_delim (const struct E_string *es, size_t start_idx,
- char pre_bracket_char, size_t *result_idx)
+ char pre_bracket_char, size_t *result_idx)
{
size_t i;
for (i = start_idx; i < es->len - 1; i++)
if (es->s[i] == pre_bracket_char && es->s[i + 1] == ']'
- && !es->escaped[i] && !es->escaped[i + 1])
+ && !es->escaped[i] && !es->escaped[i + 1])
{
- *result_idx = i;
- return true;
+ *result_idx = i;
+ return true;
}
return false;
}
@@ -792,16 +782,16 @@ find_closing_delim (const struct E_string *es, size_t start_idx,
beginning with P[ START_IDX ] comprise a valid [c*n] construct,
then set *CHAR_TO_REPEAT, *REPEAT_COUNT, and *CLOSING_BRACKET_IDX
and return zero. If the second character following
- the opening bracket is not `*' or if no closing bracket can be
+ the opening bracket is not '*' or if no closing bracket can be
found, return -1. If a closing bracket is found and the
- second char is `*', but the string between the `*' and `]' isn't
+ second char is '*', but the string between the '*' and ']' isn't
empty, an octal number, or a decimal number, print an error message
and return -2. */
static int
find_bracketed_repeat (const struct E_string *es, size_t start_idx,
- unsigned char *char_to_repeat, count *repeat_count,
- size_t *closing_bracket_idx)
+ unsigned char *char_to_repeat, count *repeat_count,
+ size_t *closing_bracket_idx)
{
size_t i;
@@ -812,47 +802,47 @@ find_bracketed_repeat (const struct E_string *es, size_t start_idx,
for (i = start_idx + 2; i < es->len && !es->escaped[i]; i++)
{
if (es->s[i] == ']')
- {
- size_t digit_str_len = i - start_idx - 2;
-
- *char_to_repeat = es->s[start_idx];
- if (digit_str_len == 0)
- {
- /* We've matched [c*] -- no explicit repeat count. */
- *repeat_count = 0;
- }
- else
- {
- /* Here, we have found [c*s] where s should be a string
- of octal (if it starts with `0') or decimal digits. */
- 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, NULL)
- != LONGINT_OK)
- || REPEAT_COUNT_MAXIMUM < *repeat_count
- || digit_str + digit_str_len != d_end)
- {
- char *tmp = make_printable_str (digit_str, digit_str_len);
- error (0, 0,
- _("invalid repeat count %s in [c*n] construct"),
- quote (tmp));
- free (tmp);
- return -2;
- }
- }
- *closing_bracket_idx = i;
- return 0;
- }
+ {
+ size_t digit_str_len = i - start_idx - 2;
+
+ *char_to_repeat = es->s[start_idx];
+ if (digit_str_len == 0)
+ {
+ /* We've matched [c*] -- no explicit repeat count. */
+ *repeat_count = 0;
+ }
+ else
+ {
+ /* Here, we have found [c*s] where s should be a string
+ of octal (if it starts with '0') or decimal digits. */
+ 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, NULL)
+ != LONGINT_OK)
+ || REPEAT_COUNT_MAXIMUM < *repeat_count
+ || digit_str + digit_str_len != d_end)
+ {
+ char *tmp = make_printable_str (digit_str, digit_str_len);
+ error (0, 0,
+ _("invalid repeat count %s in [c*n] construct"),
+ quote (tmp));
+ free (tmp);
+ return -2;
+ }
+ }
+ *closing_bracket_idx = i;
+ return 0;
+ }
}
return -1; /* No bracket found. */
}
/* Return true if the string at ES->s[IDX] matches the regular
- expression `\*[0-9]*\]', false otherwise. The string does not
+ expression '\*[0-9]*\]', false otherwise. The string does not
match if any of its characters are escaped. */
-static bool
+static bool _GL_ATTRIBUTE_PURE
star_digits_closebracket (const struct E_string *es, size_t idx)
{
size_t i;
@@ -869,13 +859,13 @@ star_digits_closebracket (const struct E_string *es, size_t idx)
/* Convert string UNESCAPED_STRING (which has been preprocessed to
convert backslash-escape sequences) of length LEN characters into
a linked list of the following 5 types of constructs:
- - [:str:] Character class where `str' is one of the 12 valid strings.
- - [=c=] Equivalence class where `c' is any single character.
- - [c*n] Repeat the single character `c' `n' times. n may be omitted.
- However, if `n' is present, it must be a non-negative octal or
- decimal integer.
- - r-s Range of characters from `r' to `s'. The second endpoint must
- not precede the first in the current collating sequence.
+ - [:str:] Character class where 'str' is one of the 12 valid strings.
+ - [=c=] Equivalence class where 'c' is any single character.
+ - [c*n] Repeat the single character 'c' 'n' times. n may be omitted.
+ However, if 'n' is present, it must be a non-negative octal or
+ decimal integer.
+ - r-s Range of characters from 'r' to 's'. The second endpoint must
+ not precede the first in the current collating sequence.
- c Any other character is interpreted as itself. */
static bool
@@ -888,7 +878,7 @@ build_spec_list (const struct E_string *es, struct Spec_list *result)
/* The main for-loop below recognizes the 4 multi-character constructs.
A character that matches (in its context) none of the multi-character
- constructs is classified as `normal'. Since all multi-character
+ constructs is classified as 'normal'. Since all multi-character
constructs have at least 3 characters, any strings of length 2 or
less are composed solely of normal characters. Hence, the index of
the outer for-loop runs only as far as LEN-2. */
@@ -896,120 +886,120 @@ build_spec_list (const struct E_string *es, struct Spec_list *result)
for (i = 0; i + 2 < es->len; /* empty */)
{
if (es_match (es, i, '['))
- {
- bool matched_multi_char_construct;
- size_t closing_bracket_idx;
- unsigned char char_to_repeat;
- count repeat_count;
- int err;
-
- matched_multi_char_construct = true;
- if (es_match (es, i + 1, ':') || es_match (es, i + 1, '='))
- {
- size_t closing_delim_idx;
-
- if (find_closing_delim (es, i + 2, p[i + 1], &closing_delim_idx))
- {
- size_t opnd_str_len = closing_delim_idx - 1 - (i + 2) + 1;
- char const *opnd_str = p + i + 2;
-
- if (opnd_str_len == 0)
- {
- if (p[i + 1] == ':')
- error (0, 0, _("missing character class name `[::]'"));
- else
- error (0, 0,
- _("missing equivalence class character `[==]'"));
- return false;
- }
-
- if (p[i + 1] == ':')
- {
- /* FIXME: big comment. */
- if (!append_char_class (result, opnd_str, opnd_str_len))
- {
- if (star_digits_closebracket (es, i + 2))
- goto try_bracketed_repeat;
- else
- {
- char *tmp = make_printable_str (opnd_str,
- opnd_str_len);
- error (0, 0, _("invalid character class %s"),
- quote (tmp));
- free (tmp);
- return false;
- }
- }
- }
- else
- {
- /* FIXME: big comment. */
- if (!append_equiv_class (result, opnd_str, opnd_str_len))
- {
- if (star_digits_closebracket (es, i + 2))
- goto try_bracketed_repeat;
- else
- {
- char *tmp = make_printable_str (opnd_str,
- opnd_str_len);
- error (0, 0,
- _("%s: equivalence class operand must be a single character"),
- tmp);
- free (tmp);
- return false;
- }
- }
- }
-
- i = closing_delim_idx + 2;
- continue;
- }
- /* Else fall through. This could be [:*] or [=*]. */
- }
-
- try_bracketed_repeat:
-
- /* Determine whether this is a bracketed repeat range
- matching the RE \[.\*(dec_or_oct_number)?\]. */
- err = find_bracketed_repeat (es, i + 1, &char_to_repeat,
- &repeat_count,
- &closing_bracket_idx);
- if (err == 0)
- {
- append_repeated_char (result, char_to_repeat, repeat_count);
- i = closing_bracket_idx + 1;
- }
- else if (err == -1)
- {
- matched_multi_char_construct = false;
- }
- else
- {
- /* Found a string that looked like [c*n] but the
- numeric part was invalid. */
- return false;
- }
-
- if (matched_multi_char_construct)
- continue;
-
- /* We reach this point if P does not match [:str:], [=c=],
- [c*n], or [c*]. Now, see if P looks like a range `[-c'
- (from `[' to `c'). */
- }
+ {
+ bool matched_multi_char_construct;
+ size_t closing_bracket_idx;
+ unsigned char char_to_repeat;
+ count repeat_count;
+ int err;
+
+ matched_multi_char_construct = true;
+ if (es_match (es, i + 1, ':') || es_match (es, i + 1, '='))
+ {
+ size_t closing_delim_idx;
+
+ if (find_closing_delim (es, i + 2, p[i + 1], &closing_delim_idx))
+ {
+ size_t opnd_str_len = closing_delim_idx - 1 - (i + 2) + 1;
+ char const *opnd_str = p + i + 2;
+
+ if (opnd_str_len == 0)
+ {
+ if (p[i + 1] == ':')
+ error (0, 0, _("missing character class name '[::]'"));
+ else
+ error (0, 0,
+ _("missing equivalence class character '[==]'"));
+ return false;
+ }
+
+ if (p[i + 1] == ':')
+ {
+ /* FIXME: big comment. */
+ if (!append_char_class (result, opnd_str, opnd_str_len))
+ {
+ if (star_digits_closebracket (es, i + 2))
+ goto try_bracketed_repeat;
+ else
+ {
+ char *tmp = make_printable_str (opnd_str,
+ opnd_str_len);
+ error (0, 0, _("invalid character class %s"),
+ quote (tmp));
+ free (tmp);
+ return false;
+ }
+ }
+ }
+ else
+ {
+ /* FIXME: big comment. */
+ if (!append_equiv_class (result, opnd_str, opnd_str_len))
+ {
+ if (star_digits_closebracket (es, i + 2))
+ goto try_bracketed_repeat;
+ else
+ {
+ char *tmp = make_printable_str (opnd_str,
+ opnd_str_len);
+ error (0, 0,
+ _("%s: equivalence class operand must be a single character"),
+ tmp);
+ free (tmp);
+ return false;
+ }
+ }
+ }
+
+ i = closing_delim_idx + 2;
+ continue;
+ }
+ /* Else fall through. This could be [:*] or [=*]. */
+ }
+
+ try_bracketed_repeat:
+
+ /* Determine whether this is a bracketed repeat range
+ matching the RE \[.\*(dec_or_oct_number)?\]. */
+ err = find_bracketed_repeat (es, i + 1, &char_to_repeat,
+ &repeat_count,
+ &closing_bracket_idx);
+ if (err == 0)
+ {
+ append_repeated_char (result, char_to_repeat, repeat_count);
+ i = closing_bracket_idx + 1;
+ }
+ else if (err == -1)
+ {
+ matched_multi_char_construct = false;
+ }
+ else
+ {
+ /* Found a string that looked like [c*n] but the
+ numeric part was invalid. */
+ return false;
+ }
+
+ if (matched_multi_char_construct)
+ continue;
+
+ /* We reach this point if P does not match [:str:], [=c=],
+ [c*n], or [c*]. Now, see if P looks like a range '[-c'
+ (from '[' to 'c'). */
+ }
/* Look ahead one char for ranges like a-z. */
if (es_match (es, i + 1, '-'))
- {
- if (!append_range (result, p[i], p[i + 2]))
- return false;
- i += 3;
- }
+ {
+ if (!append_range (result, p[i], p[i + 2]))
+ return false;
+ i += 3;
+ }
else
- {
- append_normal_char (result, p[i]);
- ++i;
- }
+ {
+ append_normal_char (result, p[i]);
+ ++i;
+ }
}
/* Now handle the (2 or fewer) remaining characters p[i]..p[es->len - 1]. */
@@ -1019,8 +1009,17 @@ build_spec_list (const struct E_string *es, struct Spec_list *result)
return true;
}
+/* Advance past the current construct.
+ S->tail must be non-NULL. */
+static void
+skip_construct (struct Spec_list *s)
+{
+ s->tail = s->tail->next;
+ s->state = NEW_ELEMENT;
+}
+
/* Given a Spec_list S (with its saved state implicit in the values
- of its members `tail' and `state'), return the next single character
+ of its members 'tail' and 'state'), return the next single character
in the expansion of S's constructs. If the last character of S was
returned on the previous call or if S was empty, this function
returns -1. For example, successive calls to get_next where S
@@ -1063,65 +1062,53 @@ get_next (struct Spec_list *s, enum Upper_Lower_class *class)
case RE_RANGE:
if (s->state == NEW_ELEMENT)
- s->state = p->u.range.first_char;
+ s->state = p->u.range.first_char;
else
- ++(s->state);
+ ++(s->state);
return_val = s->state;
if (s->state == p->u.range.last_char)
- {
- s->tail = p->next;
- s->state = NEW_ELEMENT;
- }
+ {
+ s->tail = p->next;
+ s->state = NEW_ELEMENT;
+ }
break;
case RE_CHAR_CLASS:
if (class)
- {
- bool upper_or_lower;
- switch (p->u.char_class)
- {
- case CC_LOWER:
- *class = UL_LOWER;
- upper_or_lower = true;
- break;
- case CC_UPPER:
- *class = UL_UPPER;
- upper_or_lower = true;
- break;
- default:
- upper_or_lower = false;
- break;
- }
-
- if (upper_or_lower)
- {
- s->tail = p->next;
- s->state = NEW_ELEMENT;
- return_val = 0;
- break;
- }
- }
+ {
+ switch (p->u.char_class)
+ {
+ case CC_LOWER:
+ *class = UL_LOWER;
+ break;
+ case CC_UPPER:
+ *class = UL_UPPER;
+ break;
+ default:
+ break;
+ }
+ }
if (s->state == NEW_ELEMENT)
- {
- for (i = 0; i < N_CHARS; i++)
- if (is_char_class_member (p->u.char_class, i))
- break;
- assert (i < N_CHARS);
- s->state = i;
- }
+ {
+ for (i = 0; i < N_CHARS; i++)
+ if (is_char_class_member (p->u.char_class, i))
+ break;
+ assert (i < N_CHARS);
+ s->state = i;
+ }
assert (is_char_class_member (p->u.char_class, s->state));
return_val = s->state;
for (i = s->state + 1; i < N_CHARS; i++)
- if (is_char_class_member (p->u.char_class, i))
- break;
+ if (is_char_class_member (p->u.char_class, i))
+ break;
if (i < N_CHARS)
- s->state = i;
+ s->state = i;
else
- {
- s->tail = p->next;
- s->state = NEW_ELEMENT;
- }
+ {
+ s->tail = p->next;
+ s->state = NEW_ELEMENT;
+ }
break;
case RE_EQUIV_CLASS:
@@ -1138,25 +1125,25 @@ get_next (struct Spec_list *s, enum Upper_Lower_class *class)
case RE_REPEATED_CHAR:
/* Here, a repeat count of n == 0 means don't repeat at all. */
if (p->u.repeated_char.repeat_count == 0)
- {
- s->tail = p->next;
- s->state = NEW_ELEMENT;
- return_val = get_next (s, class);
- }
+ {
+ s->tail = p->next;
+ s->state = NEW_ELEMENT;
+ return_val = get_next (s, class);
+ }
else
- {
- if (s->state == NEW_ELEMENT)
- {
- s->state = 0;
- }
- ++(s->state);
- return_val = p->u.repeated_char.the_repeated_char;
- if (s->state == p->u.repeated_char.repeat_count)
- {
- s->tail = p->next;
- s->state = NEW_ELEMENT;
- }
- }
+ {
+ if (s->state == NEW_ELEMENT)
+ {
+ s->state = 0;
+ }
+ ++(s->state);
+ return_val = p->u.repeated_char.the_repeated_char;
+ if (s->state == p->u.repeated_char.repeat_count)
+ {
+ s->tail = p->next;
+ s->state = NEW_ELEMENT;
+ }
+ }
break;
default:
@@ -1188,6 +1175,78 @@ card_of_complement (struct Spec_list *s)
return cardinality;
}
+/* Discard the lengths associated with a case conversion,
+ as using the actual number of upper or lower case characters
+ is problematic when they don't match in some locales.
+ Also ensure the case conversion classes in string2 are
+ aligned correctly with those in string1.
+ Note POSIX says the behavior of 'tr "[:upper:]" "[:upper:]"'
+ is undefined. Therefore we allow it (unlike Solaris)
+ and treat it as a no-op. */
+
+static void
+validate_case_classes (struct Spec_list *s1, struct Spec_list *s2)
+{
+ size_t n_upper = 0;
+ size_t n_lower = 0;
+ unsigned int i;
+ int c1 = 0;
+ int c2 = 0;
+ count old_s1_len = s1->length;
+ count old_s2_len = s2->length;
+ struct List_element *s1_tail = s1->tail;
+ struct List_element *s2_tail = s2->tail;
+ bool s1_new_element = true;
+ bool s2_new_element = true;
+
+ if (!s2->has_char_class)
+ return;
+
+ for (i = 0; i < N_CHARS; i++)
+ {
+ if (isupper (i))
+ n_upper++;
+ if (islower (i))
+ n_lower++;
+ }
+
+ s1->state = BEGIN_STATE;
+ s2->state = BEGIN_STATE;
+
+ while (c1 != -1 && c2 != -1)
+ {
+ enum Upper_Lower_class class_s1, class_s2;
+
+ c1 = get_next (s1, &class_s1);
+ c2 = get_next (s2, &class_s2);
+
+ /* If c2 transitions to a new case class, then
+ c1 must also transition at the same time. */
+ if (s2_new_element && class_s2 != UL_NONE
+ && !(s1_new_element && class_s1 != UL_NONE))
+ error (EXIT_FAILURE, 0,
+ _("misaligned [:upper:] and/or [:lower:] construct"));
+
+ /* If case converting, quickly skip over the elements. */
+ if (class_s2 != UL_NONE)
+ {
+ skip_construct (s1);
+ skip_construct (s2);
+ /* Discount insignificant/problematic lengths. */
+ s1->length -= (class_s1 == UL_UPPER ? n_upper : n_lower) - 1;
+ s2->length -= (class_s2 == UL_UPPER ? n_upper : n_lower) - 1;
+ }
+
+ s1_new_element = s1->state == NEW_ELEMENT; /* Next element is new. */
+ s2_new_element = s2->state == NEW_ELEMENT; /* Next element is new. */
+ }
+
+ assert (old_s1_len >= s1->length && old_s2_len >= s2->length);
+
+ s1->tail = s1_tail;
+ s2->tail = s2_tail;
+}
+
/* Gather statistics about the spec-list S in preparation for the tests
in validate that determine the consistency of the specs. This function
is called at most twice; once for string1, and again for any string2.
@@ -1218,61 +1277,61 @@ get_spec_stats (struct Spec_list *s)
count new_length;
switch (p->type)
- {
- case RE_NORMAL_CHAR:
- len = 1;
- break;
-
- case RE_RANGE:
- assert (p->u.range.last_char >= p->u.range.first_char);
- len = p->u.range.last_char - p->u.range.first_char + 1;
- break;
-
- case RE_CHAR_CLASS:
- s->has_char_class = true;
- for (i = 0; i < N_CHARS; i++)
- if (is_char_class_member (p->u.char_class, i))
- ++len;
- switch (p->u.char_class)
- {
- case CC_UPPER:
- case CC_LOWER:
- break;
- default:
- s->has_restricted_char_class = true;
- break;
- }
- break;
-
- case RE_EQUIV_CLASS:
- for (i = 0; i < N_CHARS; i++)
- if (is_equiv_class_member (p->u.equiv_code, i))
- ++len;
- s->has_equiv_class = true;
- break;
-
- case RE_REPEATED_CHAR:
- if (p->u.repeated_char.repeat_count > 0)
- len = p->u.repeated_char.repeat_count;
- else
- {
- s->indefinite_repeat_element = p;
- ++(s->n_indefinite_repeats);
- }
- break;
-
- default:
- abort ();
- break;
- }
+ {
+ case RE_NORMAL_CHAR:
+ len = 1;
+ break;
+
+ case RE_RANGE:
+ assert (p->u.range.last_char >= p->u.range.first_char);
+ len = p->u.range.last_char - p->u.range.first_char + 1;
+ break;
+
+ case RE_CHAR_CLASS:
+ s->has_char_class = true;
+ for (i = 0; i < N_CHARS; i++)
+ if (is_char_class_member (p->u.char_class, i))
+ ++len;
+ switch (p->u.char_class)
+ {
+ case CC_UPPER:
+ case CC_LOWER:
+ break;
+ default:
+ s->has_restricted_char_class = true;
+ break;
+ }
+ break;
+
+ case RE_EQUIV_CLASS:
+ for (i = 0; i < N_CHARS; i++)
+ if (is_equiv_class_member (p->u.equiv_code, i))
+ ++len;
+ s->has_equiv_class = true;
+ break;
+
+ case RE_REPEATED_CHAR:
+ if (p->u.repeated_char.repeat_count > 0)
+ len = p->u.repeated_char.repeat_count;
+ else
+ {
+ s->indefinite_repeat_element = p;
+ ++(s->n_indefinite_repeats);
+ }
+ break;
+
+ default:
+ abort ();
+ break;
+ }
/* Check for arithmetic overflow in computing length. Also, reject
- any length greater than the maximum repeat count, in case the
- length is later used to compute the repeat count for an
- indefinite element. */
+ any length greater than the maximum repeat count, in case the
+ length is later used to compute the repeat count for an
+ indefinite element. */
new_length = length + len;
if (! (length <= new_length && new_length <= REPEAT_COUNT_MAXIMUM))
- error (EXIT_FAILURE, 0, _("too many characters in set"));
+ error (EXIT_FAILURE, 0, _("too many characters in set"));
length = new_length;
}
@@ -1294,7 +1353,7 @@ get_s2_spec_stats (struct Spec_list *s2, count len_s1)
if (len_s1 >= s2->length && s2->n_indefinite_repeats == 1)
{
s2->indefinite_repeat_element->u.repeated_char.repeat_count =
- len_s1 - s2->length;
+ len_s1 - s2->length;
s2->length = len_s1;
}
}
@@ -1329,20 +1388,14 @@ parse_str (char const *s, struct Spec_list *spec_list)
Upon successful completion, S2->length is set to S1->length. The only
way this function can fail to make S2 as long as S1 is when S2 has
zero-length, since in that case, there is no last character to repeat.
- So S2->length is required to be at least 1.
+ So S2->length is required to be at least 1. */
- Providing this functionality allows the user to do some pretty
- non-BSD (and non-portable) things: For example, the command
- tr -cs '[:upper:]0-9' '[:lower:]'
- is almost guaranteed to give results that depend on your collating
- sequence. */
static void
string2_extend (const struct Spec_list *s1, struct Spec_list *s2)
{
struct List_element *p;
unsigned char char_to_repeat;
- int i;
assert (translating);
assert (s1->length > s2->length);
@@ -1358,11 +1411,14 @@ string2_extend (const struct Spec_list *s1, struct Spec_list *s2)
char_to_repeat = p->u.range.last_char;
break;
case RE_CHAR_CLASS:
- for (i = N_CHARS - 1; i >= 0; i--)
- if (is_char_class_member (p->u.char_class, i))
- break;
- assert (i >= 0);
- char_to_repeat = i;
+ /* Note BSD allows extending of classes in string2. For example:
+ tr '[:upper:]0-9' '[:lower:]'
+ That's not portable however, contradicts POSIX and is dependent
+ on your collating sequence. */
+ error (EXIT_FAILURE, 0,
+ _("when translating with string1 longer than string2,\nthe\
+ latter string must not end with a character class"));
+ abort (); /* inform gcc that the above use of error never returns. */
break;
case RE_REPEATED_CHAR:
@@ -1420,7 +1476,7 @@ validate (struct Spec_list *s1, struct Spec_list *s2)
if (s1->n_indefinite_repeats > 0)
{
error (EXIT_FAILURE, 0,
- _("the [c*] repeat construct may not appear in string1"));
+ _("the [c*] repeat construct may not appear in string1"));
}
if (s2)
@@ -1428,57 +1484,59 @@ validate (struct Spec_list *s1, struct Spec_list *s2)
get_s2_spec_stats (s2, s1->length);
if (s2->n_indefinite_repeats > 1)
- {
- error (EXIT_FAILURE, 0,
- _("only one [c*] repeat construct may appear in string2"));
- }
+ {
+ error (EXIT_FAILURE, 0,
+ _("only one [c*] repeat construct may appear in string2"));
+ }
if (translating)
- {
- if (s2->has_equiv_class)
- {
- error (EXIT_FAILURE, 0,
- _("[=c=] expressions may not appear in string2 \
-when translating"));
- }
-
- if (s1->length > s2->length)
- {
- if (!truncate_set1)
- {
- /* string2 must be non-empty unless --truncate-set1 is
- given or string1 is empty. */
-
- if (s2->length == 0)
- error (EXIT_FAILURE, 0,
- _("when not truncating set1, string2 must be non-empty"));
- string2_extend (s1, s2);
- }
- }
-
- if (complement && s1->has_char_class
- && ! (s2->length == s1->length && homogeneous_spec_list (s2)))
- {
- error (EXIT_FAILURE, 0,
- _("when translating with complemented character classes,\
+ {
+ if (s2->has_equiv_class)
+ {
+ error (EXIT_FAILURE, 0,
+ _("[=c=] expressions may not appear in string2\
+ when translating"));
+ }
+
+ if (s2->has_restricted_char_class)
+ {
+ error (EXIT_FAILURE, 0,
+ _("when translating, the only character classes that may\
+ appear in\nstring2 are 'upper' and 'lower'"));
+ }
+
+ validate_case_classes (s1, s2);
+
+ if (s1->length > s2->length)
+ {
+ if (!truncate_set1)
+ {
+ /* string2 must be non-empty unless --truncate-set1 is
+ given or string1 is empty. */
+
+ if (s2->length == 0)
+ error (EXIT_FAILURE, 0,
+ _("when not truncating set1, string2 must be non-empty"));
+ string2_extend (s1, s2);
+ }
+ }
+
+ if (complement && s1->has_char_class
+ && ! (s2->length == s1->length && homogeneous_spec_list (s2)))
+ {
+ error (EXIT_FAILURE, 0,
+ _("when translating with complemented character classes,\
\nstring2 must map all characters in the domain to one"));
- }
-
- if (s2->has_restricted_char_class)
- {
- error (EXIT_FAILURE, 0,
- _("when translating, the only character classes that may \
-appear in\nstring2 are `upper' and `lower'"));
- }
- }
+ }
+ }
else
- /* Not translating. */
- {
- if (s2->n_indefinite_repeats > 0)
- error (EXIT_FAILURE, 0,
- _("the [c*] construct may appear in string2 only \
-when translating"));
- }
+ /* Not translating. */
+ {
+ if (s2->n_indefinite_repeats > 0)
+ error (EXIT_FAILURE, 0,
+ _("the [c*] construct may appear in string2 only\
+ when translating"));
+ }
}
}
@@ -1495,85 +1553,85 @@ squeeze_filter (char *buf, size_t size, size_t (*reader) (char *, size_t))
{
/* A value distinct from any character that may have been stored in a
buffer as the result of a block-read in the function squeeze_filter. */
- enum { NOT_A_CHAR = CHAR_MAX + 1 };
+ const int NOT_A_CHAR = INT_MAX;
int char_to_squeeze = NOT_A_CHAR;
size_t i = 0;
size_t nr = 0;
- for (;;)
+ while (true)
{
size_t begin;
if (i >= nr)
- {
- nr = reader (buf, size);
- if (nr == 0)
- break;
- i = 0;
- }
+ {
+ nr = reader (buf, size);
+ if (nr == 0)
+ break;
+ i = 0;
+ }
begin = i;
if (char_to_squeeze == NOT_A_CHAR)
- {
- size_t out_len;
- /* Here, by being a little tricky, we can get a significant
- performance increase in most cases when the input is
- reasonably large. Since tr will modify the input only
- if two consecutive (and identical) input characters are
- in the squeeze set, we can step by two through the data
- when searching for a character in the squeeze set. This
- means there may be a little more work in a few cases and
- perhaps twice as much work in the worst cases where most
- of the input is removed by squeezing repeats. But most
- uses of this functionality seem to remove less than 20-30%
- of the input. */
- for (; i < nr && !in_squeeze_set[to_uchar (buf[i])]; i += 2)
- continue;
-
- /* There is a special case when i == nr and we've just
- skipped a character (the last one in buf) that is in
- the squeeze set. */
- if (i == nr && in_squeeze_set[to_uchar (buf[i - 1])])
- --i;
-
- if (i >= nr)
- out_len = nr - begin;
- else
- {
- char_to_squeeze = buf[i];
- /* We're about to output buf[begin..i]. */
- out_len = i - begin + 1;
-
- /* But since we stepped by 2 in the loop above,
- out_len may be one too large. */
- if (i > 0 && buf[i - 1] == char_to_squeeze)
- --out_len;
-
- /* Advance i to the index of first character to be
- considered when looking for a char different from
- char_to_squeeze. */
- ++i;
- }
- if (out_len > 0
- && fwrite (&buf[begin], 1, out_len, stdout) != out_len)
- error (EXIT_FAILURE, errno, _("write error"));
- }
+ {
+ size_t out_len;
+ /* Here, by being a little tricky, we can get a significant
+ performance increase in most cases when the input is
+ reasonably large. Since tr will modify the input only
+ if two consecutive (and identical) input characters are
+ in the squeeze set, we can step by two through the data
+ when searching for a character in the squeeze set. This
+ means there may be a little more work in a few cases and
+ perhaps twice as much work in the worst cases where most
+ of the input is removed by squeezing repeats. But most
+ uses of this functionality seem to remove less than 20-30%
+ of the input. */
+ for (; i < nr && !in_squeeze_set[to_uchar (buf[i])]; i += 2)
+ continue;
+
+ /* There is a special case when i == nr and we've just
+ skipped a character (the last one in buf) that is in
+ the squeeze set. */
+ if (i == nr && in_squeeze_set[to_uchar (buf[i - 1])])
+ --i;
+
+ if (i >= nr)
+ out_len = nr - begin;
+ else
+ {
+ char_to_squeeze = buf[i];
+ /* We're about to output buf[begin..i]. */
+ out_len = i - begin + 1;
+
+ /* But since we stepped by 2 in the loop above,
+ out_len may be one too large. */
+ if (i > 0 && buf[i - 1] == char_to_squeeze)
+ --out_len;
+
+ /* Advance i to the index of first character to be
+ considered when looking for a char different from
+ char_to_squeeze. */
+ ++i;
+ }
+ if (out_len > 0
+ && fwrite (&buf[begin], 1, out_len, stdout) != out_len)
+ error (EXIT_FAILURE, errno, _("write error"));
+ }
if (char_to_squeeze != NOT_A_CHAR)
- {
- /* Advance i to index of first char != char_to_squeeze
- (or to nr if all the rest of the characters in this
- buffer are the same as char_to_squeeze). */
- for (; i < nr && buf[i] == char_to_squeeze; i++)
- continue;
- if (i < nr)
- char_to_squeeze = NOT_A_CHAR;
- /* If (i >= nr) we've squeezed the last character in this buffer.
- So now we have to read a new buffer and continue comparing
- characters against char_to_squeeze. */
- }
+ {
+ /* Advance i to index of first char != char_to_squeeze
+ (or to nr if all the rest of the characters in this
+ buffer are the same as char_to_squeeze). */
+ for (; i < nr && buf[i] == char_to_squeeze; i++)
+ continue;
+ if (i < nr)
+ char_to_squeeze = NOT_A_CHAR;
+ /* If (i >= nr) we've squeezed the last character in this buffer.
+ So now we have to read a new buffer and continue comparing
+ characters against char_to_squeeze. */
+ }
}
}
@@ -1606,7 +1664,7 @@ read_and_delete (char *buf, size_t size)
size_t nr = plain_read (buf, size);
if (nr == 0)
- return 0;
+ return 0;
/* This first loop may be a waste of code, but gives much
better performance when no characters are deleted in
@@ -1614,12 +1672,12 @@ read_and_delete (char *buf, size_t size)
of buf[i] into buf[n_saved] when it would be a NOP. */
for (i = 0; i < nr && !in_delete_set[to_uchar (buf[i])]; i++)
- continue;
+ continue;
n_saved = i;
for (++i; i < nr; i++)
- if (!in_delete_set[to_uchar (buf[i])])
- buf[n_saved++] = buf[i];
+ if (!in_delete_set[to_uchar (buf[i])])
+ buf[n_saved++] = buf[i];
}
while (n_saved == 0);
@@ -1628,7 +1686,7 @@ read_and_delete (char *buf, size_t size)
/* Read at most SIZE bytes from stdin into the array BUF. Then
perform the in-place and one-to-one mapping specified by the global
- array `xlate'. Return the number of characters read, or 0 upon EOF. */
+ array 'xlate'. Return the number of characters read, or 0 upon EOF. */
static size_t
read_and_xlate (char *buf, size_t size)
@@ -1644,7 +1702,7 @@ read_and_xlate (char *buf, size_t size)
/* Initialize a boolean membership set, IN_SET, with the character
values obtained by traversing the linked list of constructs S
- using the function `get_next'. IN_SET is expected to have been
+ using the function 'get_next'. IN_SET is expected to have been
initialized to all zeros by the caller. If COMPLEMENT_THIS_SET
is true the resulting set is complemented. */
@@ -1674,7 +1732,7 @@ main (int argc, char **argv)
struct Spec_list *s2 = &buf2;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -1684,32 +1742,32 @@ main (int argc, char **argv)
while ((c = getopt_long (argc, argv, "+cCdst", long_options, NULL)) != -1)
{
switch (c)
- {
- case 'c':
- case 'C':
- complement = true;
- break;
+ {
+ case 'c':
+ case 'C':
+ complement = true;
+ break;
- case 'd':
- delete = true;
- break;
+ case 'd':
+ delete = true;
+ break;
- case 's':
- squeeze_repeats = true;
- break;
+ case 's':
+ squeeze_repeats = true;
+ break;
- case 't':
- truncate_set1 = true;
- break;
+ case 't':
+ truncate_set1 = true;
+ break;
- case_GETOPT_HELP_CHAR;
+ case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (EXIT_FAILURE);
- break;
- }
+ default:
+ usage (EXIT_FAILURE);
+ break;
+ }
}
non_option_args = argc - optind;
@@ -1720,16 +1778,16 @@ main (int argc, char **argv)
if (non_option_args < min_operands)
{
if (non_option_args == 0)
- error (0, 0, _("missing operand"));
+ error (0, 0, _("missing operand"));
else
- {
- error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
- fprintf (stderr, "%s\n",
- _(squeeze_repeats
- ? ("Two strings must be given when "
- "both deleting and squeezing repeats.")
- : "Two strings must be given when translating."));
- }
+ {
+ error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
+ fprintf (stderr, "%s\n",
+ _(squeeze_repeats
+ ? N_("Two strings must be given when "
+ "both deleting and squeezing repeats.")
+ : N_("Two strings must be given when translating.")));
+ }
usage (EXIT_FAILURE);
}
@@ -1737,34 +1795,36 @@ main (int argc, char **argv)
{
error (0, 0, _("extra operand %s"), quote (argv[optind + max_operands]));
if (non_option_args == 2)
- fprintf (stderr, "%s\n",
- _("Only one string may be given when "
- "deleting without squeezing repeats."));
+ fprintf (stderr, "%s\n",
+ _("Only one string may be given when "
+ "deleting without squeezing repeats."));
usage (EXIT_FAILURE);
}
spec_init (s1);
if (!parse_str (argv[optind], s1))
- exit (EXIT_FAILURE);
+ return EXIT_FAILURE;
if (non_option_args == 2)
{
spec_init (s2);
if (!parse_str (argv[optind + 1], s2))
- exit (EXIT_FAILURE);
+ return EXIT_FAILURE;
}
else
s2 = NULL;
validate (s1, s2);
- /* Use binary I/O, since `tr' is sometimes used to transliterate
+ /* Use binary I/O, since 'tr' is sometimes used to transliterate
non-printable characters, or characters which are stripped away
by text-mode reads (like CR and ^Z). */
if (O_BINARY && ! isatty (STDIN_FILENO))
- freopen (NULL, "rb", stdin);
+ xfreopen (NULL, "rb", stdin);
if (O_BINARY && ! isatty (STDOUT_FILENO))
- freopen (NULL, "wb", stdout);
+ xfreopen (NULL, "wb", stdout);
+
+ fadvise (stdin, FADVISE_SEQUENTIAL);
if (squeeze_repeats && non_option_args == 1)
{
@@ -1775,14 +1835,14 @@ main (int argc, char **argv)
{
set_initialize (s1, complement, in_delete_set);
- for (;;)
- {
- size_t nr = read_and_delete (io_buf, sizeof io_buf);
- if (nr == 0)
- break;
- if (fwrite (io_buf, 1, nr, stdout) != nr)
- error (EXIT_FAILURE, errno, _("write error"));
- }
+ while (true)
+ {
+ size_t nr = read_and_delete (io_buf, sizeof io_buf);
+ if (nr == 0)
+ break;
+ if (fwrite (io_buf, 1, nr, stdout) != nr)
+ error (EXIT_FAILURE, errno, _("write error"));
+ }
}
else if (squeeze_repeats && delete && non_option_args == 2)
{
@@ -1793,104 +1853,95 @@ main (int argc, char **argv)
else if (translating)
{
if (complement)
- {
- int i;
- bool *in_s1 = in_delete_set;
-
- set_initialize (s1, false, in_s1);
- s2->state = BEGIN_STATE;
- for (i = 0; i < N_CHARS; i++)
- xlate[i] = i;
- for (i = 0; i < N_CHARS; i++)
- {
- if (!in_s1[i])
- {
- int ch = get_next (s2, NULL);
- assert (ch != -1 || truncate_set1);
- if (ch == -1)
- {
- /* This will happen when tr is invoked like e.g.
- tr -cs A-Za-z0-9 '\012'. */
- break;
- }
- xlate[i] = ch;
- }
- }
- assert (get_next (s2, NULL) == -1 || truncate_set1);
- }
+ {
+ int i;
+ bool *in_s1 = in_delete_set;
+
+ set_initialize (s1, false, in_s1);
+ s2->state = BEGIN_STATE;
+ for (i = 0; i < N_CHARS; i++)
+ xlate[i] = i;
+ for (i = 0; i < N_CHARS; i++)
+ {
+ if (!in_s1[i])
+ {
+ int ch = get_next (s2, NULL);
+ assert (ch != -1 || truncate_set1);
+ if (ch == -1)
+ {
+ /* This will happen when tr is invoked like e.g.
+ tr -cs A-Za-z0-9 '\012'. */
+ break;
+ }
+ xlate[i] = ch;
+ }
+ }
+ }
else
- {
- int c1, c2;
- int i;
- enum Upper_Lower_class class_s1;
- enum Upper_Lower_class class_s2;
-
- for (i = 0; i < N_CHARS; i++)
- xlate[i] = i;
- s1->state = BEGIN_STATE;
- s2->state = BEGIN_STATE;
- for (;;)
- {
- c1 = get_next (s1, &class_s1);
- c2 = get_next (s2, &class_s2);
-
- /* When constructing the translation array, either one of the
- values returned by paired calls to get_next must be from
- [:upper:] and the other is [:lower:], or neither can be from
- upper or lower. */
-
- if ((class_s1 == UL_NONE) != (class_s2 == UL_NONE))
- error (EXIT_FAILURE, 0,
- _("misaligned [:upper:] and/or [:lower:] construct"));
-
- if (class_s1 == UL_LOWER && class_s2 == UL_UPPER)
- {
- for (i = 0; i < N_CHARS; i++)
- if (islower (i))
- xlate[i] = toupper (i);
- }
- else if (class_s1 == UL_UPPER && class_s2 == UL_LOWER)
- {
- for (i = 0; i < N_CHARS; i++)
- if (isupper (i))
- xlate[i] = tolower (i);
- }
- else if ((class_s1 == UL_LOWER && class_s2 == UL_LOWER)
- || (class_s1 == UL_UPPER && class_s2 == UL_UPPER))
- {
- /* POSIX says the behavior of `tr "[:upper:]" "[:upper:]"'
- is undefined. Treat it as a no-op. */
- }
- else
- {
- /* The following should have been checked by validate... */
- if (c1 == -1 || c2 == -1)
- break;
- xlate[c1] = c2;
- }
- }
- assert (c1 == -1 || truncate_set1);
- }
+ {
+ int c1, c2;
+ int i;
+ enum Upper_Lower_class class_s1;
+ enum Upper_Lower_class class_s2;
+
+ for (i = 0; i < N_CHARS; i++)
+ xlate[i] = i;
+ s1->state = BEGIN_STATE;
+ s2->state = BEGIN_STATE;
+ while (true)
+ {
+ c1 = get_next (s1, &class_s1);
+ c2 = get_next (s2, &class_s2);
+
+ if (class_s1 == UL_LOWER && class_s2 == UL_UPPER)
+ {
+ for (i = 0; i < N_CHARS; i++)
+ if (islower (i))
+ xlate[i] = toupper (i);
+ }
+ else if (class_s1 == UL_UPPER && class_s2 == UL_LOWER)
+ {
+ for (i = 0; i < N_CHARS; i++)
+ if (isupper (i))
+ xlate[i] = tolower (i);
+ }
+ else
+ {
+ /* The following should have been checked by validate... */
+ if (c1 == -1 || c2 == -1)
+ break;
+ xlate[c1] = c2;
+ }
+
+ /* When case-converting, skip the elements as an optimization. */
+ if (class_s2 != UL_NONE)
+ {
+ skip_construct (s1);
+ skip_construct (s2);
+ }
+ }
+ assert (c1 == -1 || truncate_set1);
+ }
if (squeeze_repeats)
- {
- set_initialize (s2, false, in_squeeze_set);
- squeeze_filter (io_buf, sizeof io_buf, read_and_xlate);
- }
+ {
+ set_initialize (s2, false, in_squeeze_set);
+ squeeze_filter (io_buf, sizeof io_buf, read_and_xlate);
+ }
else
- {
- for (;;)
- {
- size_t bytes_read = read_and_xlate (io_buf, sizeof io_buf);
- if (bytes_read == 0)
- break;
- if (fwrite (io_buf, 1, bytes_read, stdout) != bytes_read)
- error (EXIT_FAILURE, errno, _("write error"));
- }
- }
+ {
+ while (true)
+ {
+ size_t bytes_read = read_and_xlate (io_buf, sizeof io_buf);
+ if (bytes_read == 0)
+ break;
+ if (fwrite (io_buf, 1, bytes_read, stdout) != bytes_read)
+ error (EXIT_FAILURE, errno, _("write error"));
+ }
+ }
}
if (close (STDIN_FILENO) != 0)
error (EXIT_FAILURE, errno, _("standard input"));
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/true.c b/src/true.c
index 55490f9..f9ba9ff 100644
--- a/src/true.c
+++ b/src/true.c
@@ -1,10 +1,10 @@
/* Exit with a status code indicating success.
- Copyright (C) 1999-2003, 2005 Free Software Foundation, Inc.
+ Copyright (C) 1999-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#include <stdio.h>
@@ -31,10 +30,7 @@
# define PROGRAM_NAME "false"
#endif
-#define AUTHORS "Jim Meyering"
-
-/* The name this program was run with. */
-char *program_name;
+#define AUTHORS proper_name ("Jim Meyering")
void
usage (int status)
@@ -43,40 +39,42 @@ usage (int status)
Usage: %s [ignored command line arguments]\n\
or: %s OPTION\n\
"),
- program_name, program_name);
+ program_name, program_name);
printf ("%s\n\n",
- _(EXIT_STATUS == EXIT_SUCCESS
- ? "Exit with a status code indicating success."
- : "Exit with a status code indicating failure."));
+ _(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);
printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
exit (status);
}
int
main (int argc, char **argv)
{
- initialize_main (&argc, &argv);
- program_name = argv[0];
- setlocale (LC_ALL, "");
- bindtextdomain (PACKAGE, LOCALEDIR);
- textdomain (PACKAGE);
-
- atexit (close_stdout);
-
/* Recognize --help or --version only if it's the only command-line
argument. */
if (argc == 2)
{
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ /* Note true(1) will return EXIT_FAILURE in the
+ edge case where writes fail with GNU specific options. */
+ atexit (close_stdout);
+
if (STREQ (argv[1], "--help"))
- usage (EXIT_STATUS);
+ usage (EXIT_STATUS);
if (STREQ (argv[1], "--version"))
- version_etc (stdout, PROGRAM_NAME, GNU_PACKAGE, VERSION, AUTHORS,
- (char *) NULL);
+ version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version, AUTHORS,
+ (char *) NULL);
}
- exit (EXIT_STATUS);
+ return EXIT_STATUS;
}
diff --git a/src/truncate.c b/src/truncate.c
new file mode 100644
index 0000000..0dff840
--- /dev/null
+++ b/src/truncate.c
@@ -0,0 +1,398 @@
+/* truncate -- truncate or extend the length of files.
+ Copyright (C) 2008-2016 Free Software Foundation, Inc.
+
+ This 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/>. */
+
+/* Written by Pádraig Brady
+
+ This is backwards compatible with the FreeBSD utility, but is more
+ flexible wrt the size specifications and the use of long options,
+ to better fit the "GNU" environment. */
+
+#include <config.h> /* sets _FILE_OFFSET_BITS=64 etc. */
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+
+#include "system.h"
+#include "error.h"
+#include "quote.h"
+#include "stat-size.h"
+#include "xdectoint.h"
+
+/* The official name of this program (e.g., no 'g' prefix). */
+#define PROGRAM_NAME "truncate"
+
+#define AUTHORS proper_name_utf8 ("Padraig Brady", "P\303\241draig Brady")
+
+/* (-c) If true, don't create if not already there */
+static bool no_create;
+
+/* (-o) If true, --size refers to blocks not bytes */
+static bool block_mode;
+
+/* (-r) Reference file to use size from */
+static char const *ref_file;
+
+static struct option const longopts[] =
+{
+ {"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},
+ {NULL, 0, NULL, 0}
+};
+
+typedef enum
+{ rm_abs = 0, rm_rel, rm_min, rm_max, rm_rdn, rm_rup } rel_mode_t;
+
+void
+usage (int status)
+{
+ if (status != EXIT_SUCCESS)
+ emit_try_help ();
+ else
+ {
+ printf (_("Usage: %s OPTION... FILE...\n"), program_name);
+ fputs (_("\
+Shrink or extend the size of each FILE to the specified size\n\
+\n\
+A FILE argument that does not exist is created.\n\
+\n\
+If a FILE is larger than the specified size, the extra data is lost.\n\
+If a FILE is shorter, it is extended and the extended part (hole)\n\
+reads as zero bytes.\n\
+"), stdout);
+
+ emit_mandatory_arg_note ();
+
+ fputs (_("\
+ -c, --no-create do not create any files\n\
+"), stdout);
+ fputs (_("\
+ -o, --io-blocks treat SIZE as number of IO blocks instead of bytes\n\
+"), stdout);
+ fputs (_("\
+ -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);
+ emit_size_note ();
+ fputs (_("\n\
+SIZE may also be prefixed by one of the following modifying characters:\n\
+'+' extend by, '-' reduce by, '<' at most, '>' at least,\n\
+'/' round down to multiple of, '%' round up to multiple of.\n"), stdout);
+ emit_ancillary_info (PROGRAM_NAME);
+ }
+ exit (status);
+}
+
+/* return true on success, false on error. */
+static bool
+do_ftruncate (int fd, char const *fname, off_t ssize, off_t rsize,
+ rel_mode_t rel_mode)
+{
+ struct stat sb;
+ off_t nsize;
+
+ if ((block_mode || (rel_mode && rsize < 0)) && fstat (fd, &sb) != 0)
+ {
+ error (0, errno, _("cannot fstat %s"), quoteaf (fname));
+ return false;
+ }
+ if (block_mode)
+ {
+ off_t const blksize = ST_BLKSIZE (sb);
+ if (ssize < OFF_T_MIN / blksize || ssize > OFF_T_MAX / blksize)
+ {
+ error (0, 0,
+ _("overflow in %" PRIdMAX
+ " * %" PRIdMAX " byte blocks for file %s"),
+ (intmax_t) ssize, (intmax_t) blksize,
+ quoteaf (fname));
+ return false;
+ }
+ ssize *= blksize;
+ }
+ if (rel_mode)
+ {
+ uintmax_t fsize;
+
+ if (0 <= rsize)
+ fsize = rsize;
+ else
+ {
+ off_t file_size;
+ if (usable_st_size (&sb))
+ {
+ file_size = sb.st_size;
+ if (file_size < 0)
+ {
+ /* Sanity check. Overflow is the only reason I can think
+ this would ever go negative. */
+ error (0, 0, _("%s has unusable, apparently negative size"),
+ quoteaf (fname));
+ return false;
+ }
+ }
+ else
+ {
+ file_size = lseek (fd, 0, SEEK_END);
+ if (file_size < 0)
+ {
+ error (0, errno, _("cannot get the size of %s"),
+ quoteaf (fname));
+ return false;
+ }
+ }
+ fsize = file_size;
+ }
+
+ if (rel_mode == rm_min)
+ nsize = MAX (fsize, (uintmax_t) ssize);
+ else if (rel_mode == rm_max)
+ nsize = MIN (fsize, (uintmax_t) ssize);
+ else if (rel_mode == rm_rdn)
+ /* 0..ssize-1 -> 0 */
+ nsize = (fsize / ssize) * ssize;
+ else if (rel_mode == rm_rup)
+ /* 1..ssize -> ssize */
+ {
+ /* Here ssize>=1 && fsize>=0 */
+ uintmax_t const overflow = ((fsize + ssize - 1) / ssize) * ssize;
+ if (overflow > OFF_T_MAX)
+ {
+ error (0, 0, _("overflow rounding up size of file %s"),
+ quoteaf (fname));
+ return false;
+ }
+ nsize = overflow;
+ }
+ else
+ {
+ if (ssize > OFF_T_MAX - (off_t)fsize)
+ {
+ error (0, 0, _("overflow extending size of file %s"),
+ quoteaf (fname));
+ return false;
+ }
+ nsize = fsize + ssize;
+ }
+ }
+ else
+ nsize = ssize;
+ if (nsize < 0)
+ nsize = 0;
+
+ if (ftruncate (fd, nsize) == -1) /* note updates mtime & ctime */
+ {
+ error (0, errno,
+ _("failed to truncate %s at %" PRIdMAX " bytes"), quoteaf (fname),
+ (intmax_t) nsize);
+ return false;
+ }
+
+ return true;
+}
+
+int
+main (int argc, char **argv)
+{
+ bool got_size = false;
+ bool errors = false;
+ off_t size IF_LINT ( = 0);
+ off_t rsize = -1;
+ rel_mode_t rel_mode = rm_abs;
+ int c, fd = -1, oflags;
+ char const *fname;
+
+ initialize_main (&argc, &argv);
+ set_program_name (argv[0]);
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ atexit (close_stdout);
+
+ while ((c = getopt_long (argc, argv, "cor:s:", longopts, NULL)) != -1)
+ {
+ switch (c)
+ {
+ case 'c':
+ no_create = true;
+ break;
+
+ case 'o':
+ block_mode = true;
+ break;
+
+ case 'r':
+ ref_file = optarg;
+ break;
+
+ case 's':
+ /* skip any whitespace */
+ while (isspace (to_uchar (*optarg)))
+ optarg++;
+ switch (*optarg)
+ {
+ case '<':
+ rel_mode = rm_max;
+ optarg++;
+ break;
+ case '>':
+ rel_mode = rm_min;
+ optarg++;
+ break;
+ case '/':
+ rel_mode = rm_rdn;
+ optarg++;
+ break;
+ case '%':
+ rel_mode = rm_rup;
+ optarg++;
+ break;
+ }
+ /* skip any whitespace */
+ while (isspace (to_uchar (*optarg)))
+ optarg++;
+ if (*optarg == '+' || *optarg == '-')
+ {
+ if (rel_mode)
+ {
+ error (0, 0, _("multiple relative modifiers specified"));
+ /* Note other combinations are flagged as invalid numbers */
+ usage (EXIT_FAILURE);
+ }
+ rel_mode = rm_rel;
+ }
+ /* Support dd BLOCK size suffixes + lowercase g,t,m for bsd compat.
+ Note we don't support dd's b=512, c=1, w=2 or 21x512MiB formats. */
+ size = xdectoimax (optarg, OFF_T_MIN, OFF_T_MAX, "EgGkKmMPtTYZ0",
+ _("Invalid number"), 0);
+ /* Rounding to multiple of 0 is nonsensical */
+ if ((rel_mode == rm_rup || rel_mode == rm_rdn) && size == 0)
+ error (EXIT_FAILURE, 0, _("division by zero"));
+ got_size = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ /* must specify either size or reference file */
+ if (!ref_file && !got_size)
+ {
+ error (0, 0, _("you must specify either %s or %s"),
+ quote_n (0, "--size"), quote_n (1, "--reference"));
+ usage (EXIT_FAILURE);
+ }
+ /* must specify a relative size with a reference file */
+ if (ref_file && got_size && !rel_mode)
+ {
+ error (0, 0, _("you must specify a relative %s with %s"),
+ quote_n (0, "--size"), quote_n (1, "--reference"));
+ usage (EXIT_FAILURE);
+ }
+ /* block_mode without size is not valid */
+ if (block_mode && !got_size)
+ {
+ error (0, 0, _("%s was specified but %s was not"),
+ quote_n (0, "--io-blocks"), quote_n (1, "--size"));
+ usage (EXIT_FAILURE);
+ }
+ /* must specify at least 1 file */
+ if (argc < 1)
+ {
+ error (0, 0, _("missing file operand"));
+ usage (EXIT_FAILURE);
+ }
+
+ if (ref_file)
+ {
+ struct stat sb;
+ off_t file_size = -1;
+ if (stat (ref_file, &sb) != 0)
+ error (EXIT_FAILURE, errno, _("cannot stat %s"), quoteaf (ref_file));
+ if (usable_st_size (&sb))
+ file_size = sb.st_size;
+ else
+ {
+ int ref_fd = open (ref_file, O_RDONLY);
+ if (0 <= ref_fd)
+ {
+ off_t file_end = lseek (ref_fd, 0, SEEK_END);
+ int saved_errno = errno;
+ close (ref_fd); /* ignore failure */
+ if (0 <= file_end)
+ file_size = file_end;
+ else
+ {
+ /* restore, in case close clobbered it. */
+ errno = saved_errno;
+ }
+ }
+ }
+ if (file_size < 0)
+ error (EXIT_FAILURE, errno, _("cannot get the size of %s"),
+ quoteaf (ref_file));
+ if (!got_size)
+ size = file_size;
+ else
+ rsize = file_size;
+ }
+
+ oflags = O_WRONLY | (no_create ? 0 : O_CREAT) | O_NONBLOCK;
+
+ while ((fname = *argv++) != NULL)
+ {
+ if ((fd = open (fname, oflags, MODE_RW_UGO)) == -1)
+ {
+ /* 'truncate -s0 -c no-such-file' shouldn't gen error
+ 'truncate -s0 no-such-dir/file' should gen ENOENT error
+ 'truncate -s0 no-such-dir/' should gen EISDIR error
+ 'truncate -s0 .' should gen EISDIR error */
+ if (!(no_create && errno == ENOENT))
+ {
+ error (0, errno, _("cannot open %s for writing"),
+ quoteaf (fname));
+ errors = true;
+ }
+ continue;
+ }
+
+
+ if (fd != -1)
+ {
+ errors |= !do_ftruncate (fd, fname, size, rsize, rel_mode);
+ if (close (fd) != 0)
+ {
+ error (0, errno, _("failed to close %s"), quoteaf (fname));
+ errors = true;
+ }
+ }
+ }
+
+ return errors ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/tsort.c b/src/tsort.c
index 9393232..980abcd 100644
--- a/src/tsort.c
+++ b/src/tsort.c
@@ -1,10 +1,10 @@
/* tsort - topological sort.
- Copyright (C) 1998-2005 Free Software Foundation, Inc.
+ Copyright (C) 1998-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Mark Kettenis <kettenis@phys.uva.nl>. */
@@ -23,7 +22,6 @@
#include <config.h>
-#include <stdio.h>
#include <assert.h>
#include <getopt.h>
#include <sys/types.h>
@@ -31,13 +29,15 @@
#include "system.h"
#include "long-options.h"
#include "error.h"
-#include "quote.h"
+#include "fadvise.h"
#include "readtokens.h"
+#include "stdio--.h"
+#include "quote.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "tsort"
-#define AUTHORS "Mark Kettenis"
+#define AUTHORS proper_name ("Mark Kettenis")
/* Token delimiters when reading from a file. */
#define DELIM " \t\n"
@@ -60,13 +60,10 @@ struct item
struct successor *top;
};
-/* The name this program was run with. */
-char *program_name;
-
/* The head of the sorted list. */
static struct item *head = NULL;
-/* The tail of the list of `zeros', strings that have no predecessors. */
+/* The tail of the list of 'zeros', strings that have no predecessors. */
static struct item *zeros = NULL;
/* Used for loop detection. */
@@ -74,24 +71,27 @@ static struct item *loop = NULL;
/* The number of strings to sort. */
static size_t n_strings = 0;
-
+
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION] [FILE]\n\
Write totally ordered list consistent with the partial ordering in FILE.\n\
-With no FILE, or when FILE is -, read standard input.\n\
-\n\
"), program_name);
+
+ emit_stdin_note ();
+
+ fputs (_("\
+\n\
+"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
@@ -140,125 +140,125 @@ search_item (struct item *root, const char *str)
t = root;
s = p = root->right;
- for (;;)
+ while (true)
{
/* A2. Compare. */
a = strcmp (str, p->str);
if (a == 0)
- return p;
+ return p;
/* A3 & A4. Move left & right. */
if (a < 0)
- q = p->left;
+ q = p->left;
else
- q = p->right;
+ q = p->right;
if (q == NULL)
- {
- /* A5. Insert. */
- q = new_item (str);
-
- /* A3 & A4. (continued). */
- if (a < 0)
- p->left = q;
- else
- p->right = q;
-
- /* A6. Adjust balance factors. */
- assert (!STREQ (str, s->str));
- if (strcmp (str, s->str) < 0)
- {
- r = p = s->left;
- a = -1;
- }
- else
- {
- r = p = s->right;
- a = 1;
- }
-
- while (p != q)
- {
- assert (!STREQ (str, p->str));
- if (strcmp (str, p->str) < 0)
- {
- p->balance = -1;
- p = p->left;
- }
- else
- {
- p->balance = 1;
- p = p->right;
- }
- }
-
- /* A7. Balancing act. */
- if (s->balance == 0 || s->balance == -a)
- {
- s->balance += a;
- return q;
- }
-
- if (r->balance == a)
- {
- /* A8. Single Rotation. */
- p = r;
- if (a < 0)
- {
- s->left = r->right;
- r->right = s;
- }
- else
- {
- s->right = r->left;
- r->left = s;
- }
- s->balance = r->balance = 0;
- }
- else
- {
- /* A9. Double rotation. */
- if (a < 0)
- {
- p = r->right;
- r->right = p->left;
- p->left = r;
- s->left = p->right;
- p->right = s;
- }
- else
- {
- p = r->left;
- r->left = p->right;
- p->right = r;
- s->right = p->left;
- p->left = s;
- }
-
- s->balance = 0;
- r->balance = 0;
- if (p->balance == a)
- s->balance = -a;
- else if (p->balance == -a)
- r->balance = a;
- p->balance = 0;
- }
-
- /* A10. Finishing touch. */
- if (s == t->right)
- t->right = p;
- else
- t->left = p;
-
- return q;
- }
+ {
+ /* A5. Insert. */
+ q = new_item (str);
+
+ /* A3 & A4. (continued). */
+ if (a < 0)
+ p->left = q;
+ else
+ p->right = q;
+
+ /* A6. Adjust balance factors. */
+ assert (!STREQ (str, s->str));
+ if (strcmp (str, s->str) < 0)
+ {
+ r = p = s->left;
+ a = -1;
+ }
+ else
+ {
+ r = p = s->right;
+ a = 1;
+ }
+
+ while (p != q)
+ {
+ assert (!STREQ (str, p->str));
+ if (strcmp (str, p->str) < 0)
+ {
+ p->balance = -1;
+ p = p->left;
+ }
+ else
+ {
+ p->balance = 1;
+ p = p->right;
+ }
+ }
+
+ /* A7. Balancing act. */
+ if (s->balance == 0 || s->balance == -a)
+ {
+ s->balance += a;
+ return q;
+ }
+
+ if (r->balance == a)
+ {
+ /* A8. Single Rotation. */
+ p = r;
+ if (a < 0)
+ {
+ s->left = r->right;
+ r->right = s;
+ }
+ else
+ {
+ s->right = r->left;
+ r->left = s;
+ }
+ s->balance = r->balance = 0;
+ }
+ else
+ {
+ /* A9. Double rotation. */
+ if (a < 0)
+ {
+ p = r->right;
+ r->right = p->left;
+ p->left = r;
+ s->left = p->right;
+ p->right = s;
+ }
+ else
+ {
+ p = r->left;
+ r->left = p->right;
+ p->right = r;
+ s->right = p->left;
+ p->left = s;
+ }
+
+ s->balance = 0;
+ r->balance = 0;
+ if (p->balance == a)
+ s->balance = -a;
+ else if (p->balance == -a)
+ r->balance = a;
+ p->balance = 0;
+ }
+
+ /* A10. Finishing touch. */
+ if (s == t->right)
+ t->right = p;
+ else
+ t->left = p;
+
+ return q;
+ }
/* A3 & A4. (continued). */
if (q->balance)
- {
- t = p;
- s = q;
- }
+ {
+ t = p;
+ s = q;
+ }
p = q;
}
@@ -284,7 +284,7 @@ record_relation (struct item *j, struct item *k)
}
static bool
-count_items (struct item *unused ATTRIBUTE_UNUSED)
+count_items (struct item *unused _GL_UNUSED)
{
n_strings++;
return false;
@@ -297,9 +297,9 @@ scan_zeros (struct item *k)
if (k->count == 0 && k->str)
{
if (head == NULL)
- head = k;
+ head = k;
else
- zeros->qlink = k;
+ zeros->qlink = k;
zeros = k;
}
@@ -331,67 +331,66 @@ detect_loop (struct item *k)
if (k->count > 0)
{
/* K does not have to be part of a cycle. It is however part of
- a graph that contains a cycle. */
+ a graph that contains a cycle. */
if (loop == NULL)
- /* Start traversing the graph at K. */
- loop = k;
+ /* Start traversing the graph at K. */
+ loop = k;
else
- {
- struct successor **p = &k->top;
-
- while (*p)
- {
- if ((*p)->suc == loop)
- {
- if (k->qlink)
- {
- /* We have found a loop. Retrace our steps. */
- while (loop)
- {
- struct item *tmp = loop->qlink;
-
- fprintf (stderr, "%s: %s\n", program_name,
- loop->str);
-
- /* Until we encounter K again. */
- if (loop == k)
- {
- /* Remove relation. */
- (*p)->suc->count--;
- *p = (*p)->next;
- break;
- }
-
- /* Tidy things up since we might have to
+ {
+ struct successor **p = &k->top;
+
+ while (*p)
+ {
+ if ((*p)->suc == loop)
+ {
+ if (k->qlink)
+ {
+ /* We have found a loop. Retrace our steps. */
+ while (loop)
+ {
+ struct item *tmp = loop->qlink;
+
+ error (0, 0, "%s", (loop->str));
+
+ /* Until we encounter K again. */
+ if (loop == k)
+ {
+ /* Remove relation. */
+ (*p)->suc->count--;
+ *p = (*p)->next;
+ break;
+ }
+
+ /* Tidy things up since we might have to
detect another loop. */
- loop->qlink = NULL;
- loop = tmp;
- }
+ loop->qlink = NULL;
+ loop = tmp;
+ }
- while (loop)
- {
- struct item *tmp = loop->qlink;
+ while (loop)
+ {
+ struct item *tmp = loop->qlink;
- loop->qlink = NULL;
- loop = tmp;
- }
+ loop->qlink = NULL;
+ loop = tmp;
+ }
- /* Since we have found the loop, stop walking
+ /* Since we have found the loop, stop walking
the tree. */
- return true;
- }
- else
- {
- k->qlink = loop;
- loop = k;
- break;
- }
- }
-
- p = &(*p)->next;
- }
- }
+ return true;
+ }
+ else
+ {
+ k->qlink = loop;
+ loop = k;
+ break;
+ }
+ }
+
+ p = &(*p)->next;
+ }
+ }
}
return false;
@@ -408,13 +407,13 @@ recurse_tree (struct item *root, bool (*action) (struct item *))
else
{
if (root->left != NULL)
- if (recurse_tree (root->left, action))
- return true;
+ if (recurse_tree (root->left, action))
+ return true;
if ((*action) (root))
- return true;
+ return true;
if (root->right != NULL)
- if (recurse_tree (root->right, action))
- return true;
+ if (recurse_tree (root->right, action))
+ return true;
}
return false;
@@ -446,7 +445,9 @@ tsort (const char *file)
root = new_item (NULL);
if (!is_stdin && ! freopen (file, "r", stdin))
- error (EXIT_FAILURE, errno, "%s", file);
+ error (EXIT_FAILURE, errno, "%s", quotef (file));
+
+ fadvise (stdin, FADVISE_SEQUENTIAL);
init_tokenbuffer (&tokenbuffer);
@@ -455,24 +456,24 @@ tsort (const char *file)
/* T2. Next Relation. */
size_t len = readtoken (stdin, DELIM, sizeof (DELIM) - 1, &tokenbuffer);
if (len == (size_t) -1)
- break;
+ break;
assert (len != 0);
k = search_item (root, tokenbuffer.buffer);
if (j)
- {
- /* T3. Record the relation. */
- record_relation (j, k);
- k = NULL;
- }
+ {
+ /* T3. Record the relation. */
+ record_relation (j, k);
+ k = NULL;
+ }
j = k;
}
if (k != NULL)
error (EXIT_FAILURE, 0, _("%s: input contains an odd number of tokens"),
- file);
+ quotef (file));
/* T1. Initialize (N <- n). */
walk_tree (root, count_items);
@@ -483,48 +484,55 @@ tsort (const char *file)
walk_tree (root, scan_zeros);
while (head)
- {
- struct successor *p = head->top;
-
- /* T5. Output front of queue. */
- printf ("%s\n", head->str);
- head->str = NULL; /* Avoid printing the same string twice. */
- n_strings--;
-
- /* T6. Erase relations. */
- while (p)
- {
- p->suc->count--;
- if (p->suc->count == 0)
- {
- zeros->qlink = p->suc;
- zeros = p->suc;
- }
-
- p = p->next;
- }
-
- /* T7. Remove from queue. */
- head = head->qlink;
- }
+ {
+ struct successor *p = head->top;
+
+ /* T5. Output front of queue. */
+ puts (head->str);
+#ifdef lint
+ /* suppress valgrind "definitely lost" warnings. */
+ void *head_str = (void *) head->str;
+ free (head_str);
+#endif
+ head->str = NULL; /* Avoid printing the same string twice. */
+ n_strings--;
+
+ /* T6. Erase relations. */
+ while (p)
+ {
+ p->suc->count--;
+ if (p->suc->count == 0)
+ {
+ zeros->qlink = p->suc;
+ zeros = p->suc;
+ }
+
+ p = p->next;
+ }
+
+ /* T7. Remove from queue. */
+ head = head->qlink;
+ }
/* T8. End of process. */
if (n_strings > 0)
- {
- /* The input contains a loop. */
- error (0, 0, _("%s: input contains a loop:"), file);
- ok = false;
-
- /* Print the loop and remove a relation to break it. */
- do
- walk_tree (root, detect_loop);
- while (loop);
- }
+ {
+ /* The input contains a loop. */
+ error (0, 0, _("%s: input contains a loop:"), quotef (file));
+ ok = false;
+
+ /* Print the loop and remove a relation to break it. */
+ do
+ walk_tree (root, detect_loop);
+ while (loop);
+ }
}
+ IF_LINT (free (root));
+
if (fclose (stdin) != 0)
error (EXIT_FAILURE, errno, "%s",
- is_stdin ? _("standard input") : quote (file));
+ is_stdin ? _("standard input") : quotef (file));
return ok;
}
@@ -535,15 +543,15 @@ main (int argc, char **argv)
bool ok;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
+ parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE, Version,
+ usage, AUTHORS, (char const *) NULL);
if (getopt_long (argc, argv, "", NULL, NULL) != -1)
usage (EXIT_FAILURE);
@@ -555,5 +563,5 @@ main (int argc, char **argv)
ok = tsort (optind == argc ? "-" : argv[optind]);
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/tty.c b/src/tty.c
index 5228e7a..af88a96 100644
--- a/src/tty.c
+++ b/src/tty.c
@@ -1,10 +1,10 @@
/* tty -- print the name of the terminal connected to standard input
- Copyright (C) 1990-2005 Free Software Foundation, Inc.
+ Copyright (C) 1990-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Displays "not a tty" if stdin is not a terminal.
Displays nothing if -s option is given.
@@ -38,13 +37,10 @@ enum
TTY_WRITE_ERROR = 3
};
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "tty"
-#define AUTHORS "David MacKenzie"
-
-/* The name under which this program was run. */
-char *program_name;
+#define AUTHORS proper_name ("David MacKenzie")
/* If true, return an exit status but produce no output. */
static bool silent;
@@ -62,8 +58,7 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("Usage: %s [OPTION]...\n"), program_name);
@@ -74,7 +69,7 @@ Print the file name of the terminal connected to standard input.\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -86,7 +81,7 @@ main (int argc, char **argv)
int optc;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -99,18 +94,18 @@ main (int argc, char **argv)
while ((optc = getopt_long (argc, argv, "s", longopts, NULL)) != -1)
{
switch (optc)
- {
- case 's':
- silent = true;
- break;
+ {
+ case 's':
+ silent = true;
+ break;
- case_GETOPT_HELP_CHAR;
+ case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- usage (TTY_FAILURE);
- }
+ default:
+ usage (TTY_FAILURE);
+ }
}
if (optind < argc)
@@ -120,10 +115,10 @@ main (int argc, char **argv)
if (!silent)
{
if (tty)
- puts (tty);
+ puts (tty);
else
- puts (_("not a tty"));
+ puts (_("not a tty"));
}
- exit (isatty (STDIN_FILENO) ? EXIT_SUCCESS : EXIT_FAIL);
+ return isatty (STDIN_FILENO) ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/uname-arch.c b/src/uname-arch.c
new file mode 100644
index 0000000..eb42942
--- /dev/null
+++ b/src/uname-arch.c
@@ -0,0 +1,2 @@
+#include "uname.h"
+int uname_mode = UNAME_ARCH;
diff --git a/src/uname-uname.c b/src/uname-uname.c
new file mode 100644
index 0000000..450245d
--- /dev/null
+++ b/src/uname-uname.c
@@ -0,0 +1,2 @@
+#include "uname.h"
+int uname_mode = UNAME_UNAME;
diff --git a/src/uname.c b/src/uname.c
index 0715e07..962a6cf 100644
--- a/src/uname.c
+++ b/src/uname.c
@@ -1,12 +1,11 @@
/* uname -- print system information
- Copyright (C) 1989, 1992, 1993, 1996, 1997, 1999, 2000, 2001, 2002,
- 2003, 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -14,8 +13,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by David MacKenzie <djm@gnu.ai.mit.edu> */
@@ -54,13 +52,15 @@
#include "system.h"
#include "error.h"
#include "quote.h"
+#include "uname.h"
-/* The official name of this program (e.g., no `g' prefix). */
-#define PROGRAM_NAME "uname"
+/* The official name of this program (e.g., no 'g' prefix). */
+#define PROGRAM_NAME (uname_mode == UNAME_UNAME ? "uname" : "arch")
-#define AUTHORS "David MacKenzie"
+#define AUTHORS proper_name ("David MacKenzie")
+#define ARCH_AUTHORS "David MacKenzie", "Karel Zak"
-/* Values that are bitwise or'd into `toprint'. */
+/* Values that are bitwise or'd into 'toprint'. */
/* Kernel name. */
#define PRINT_KERNEL_NAME 1
@@ -85,10 +85,7 @@
/* Operating system. */
#define PRINT_OPERATING_SYSTEM 128
-/* The name this program was run with, for error messages. */
-char *program_name;
-
-static struct option const long_options[] =
+static struct option const uname_long_options[] =
{
{"all", no_argument, NULL, 'a'},
{"kernel-name", no_argument, NULL, 's'},
@@ -106,16 +103,25 @@ static struct option const long_options[] =
{NULL, 0, NULL, 0}
};
+static struct option const arch_long_options[] =
+{
+ {GETOPT_HELP_OPTION_DECL},
+ {GETOPT_VERSION_OPTION_DECL},
+ {NULL, 0, NULL, 0}
+};
+
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("Usage: %s [OPTION]...\n"), program_name);
- fputs (_("\
+
+ if (uname_mode == UNAME_UNAME)
+ {
+ fputs (_("\
Print certain system information. With no OPTION, same as -s.\n\
\n\
-a, --all print all information, in the following order,\n\
@@ -124,16 +130,25 @@ Print certain system information. With no OPTION, same as -s.\n\
-n, --nodename print the network node hostname\n\
-r, --kernel-release print the kernel release\n\
"), stdout);
- fputs (_("\
+ fputs (_("\
-v, --kernel-version print the kernel version\n\
-m, --machine print the machine hardware name\n\
- -p, --processor print the processor type or \"unknown\"\n\
- -i, --hardware-platform print the hardware platform or \"unknown\"\n\
+ -p, --processor print the processor type (non-portable)\n\
+ -i, --hardware-platform print the hardware platform (non-portable)\n\
-o, --operating-system print the operating system\n\
"), stdout);
+ }
+ else
+ {
+ fputs (_("\
+Print machine architecture.\n\
+\n\
+"), stdout);
+ }
+
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -151,100 +166,135 @@ print_element (char const *element)
fputs (element, stdout);
}
+
+/* Set all the option flags according to the switches specified.
+ Return the mask indicating which elements to print. */
+
+static int
+decode_switches (int argc, char **argv)
+{
+ int c;
+ unsigned int toprint = 0;
+
+ if (uname_mode == UNAME_ARCH)
+ {
+ while ((c = getopt_long (argc, argv, "",
+ arch_long_options, NULL)) != -1)
+ {
+ switch (c)
+ {
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, ARCH_AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
+ }
+ toprint = PRINT_MACHINE;
+ }
+ else
+ {
+ while ((c = getopt_long (argc, argv, "asnrvmpio",
+ uname_long_options, NULL)) != -1)
+ {
+ switch (c)
+ {
+ case 'a':
+ toprint = UINT_MAX;
+ break;
+
+ case 's':
+ toprint |= PRINT_KERNEL_NAME;
+ break;
+
+ case 'n':
+ toprint |= PRINT_NODENAME;
+ break;
+
+ case 'r':
+ toprint |= PRINT_KERNEL_RELEASE;
+ break;
+
+ case 'v':
+ toprint |= PRINT_KERNEL_VERSION;
+ break;
+
+ case 'm':
+ toprint |= PRINT_MACHINE;
+ break;
+
+ case 'p':
+ toprint |= PRINT_PROCESSOR;
+ break;
+
+ case 'i':
+ toprint |= PRINT_HARDWARE_PLATFORM;
+ break;
+
+ case 'o':
+ toprint |= PRINT_OPERATING_SYSTEM;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
+ }
+ }
+
+ if (argc != optind)
+ {
+ error (0, 0, _("extra operand %s"), quote (argv[optind]));
+ usage (EXIT_FAILURE);
+ }
+
+ return toprint;
+}
+
int
main (int argc, char **argv)
{
- int c;
static char const unknown[] = "unknown";
/* Mask indicating which elements to print. */
unsigned int toprint = 0;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- while ((c = getopt_long (argc, argv, "asnrvmpio", long_options, NULL)) != -1)
- {
- switch (c)
- {
- case 'a':
- toprint = UINT_MAX;
- break;
-
- case 's':
- toprint |= PRINT_KERNEL_NAME;
- break;
-
- case 'n':
- toprint |= PRINT_NODENAME;
- break;
-
- case 'r':
- toprint |= PRINT_KERNEL_RELEASE;
- break;
-
- case 'v':
- toprint |= PRINT_KERNEL_VERSION;
- break;
-
- case 'm':
- toprint |= PRINT_MACHINE;
- break;
-
- case 'p':
- toprint |= PRINT_PROCESSOR;
- break;
-
- case 'i':
- toprint |= PRINT_HARDWARE_PLATFORM;
- break;
-
- case 'o':
- toprint |= PRINT_OPERATING_SYSTEM;
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (EXIT_FAILURE);
- }
- }
-
- if (argc != optind)
- {
- error (0, 0, _("extra operand %s"), quote (argv[optind]));
- usage (EXIT_FAILURE);
- }
+ toprint = decode_switches (argc, argv);
if (toprint == 0)
toprint = PRINT_KERNEL_NAME;
if (toprint
& (PRINT_KERNEL_NAME | PRINT_NODENAME | PRINT_KERNEL_RELEASE
- | PRINT_KERNEL_VERSION | PRINT_MACHINE))
+ | PRINT_KERNEL_VERSION | PRINT_MACHINE))
{
struct utsname name;
if (uname (&name) == -1)
- error (EXIT_FAILURE, errno, _("cannot get system name"));
+ error (EXIT_FAILURE, errno, _("cannot get system name"));
if (toprint & PRINT_KERNEL_NAME)
- print_element (name.sysname);
+ print_element (name.sysname);
if (toprint & PRINT_NODENAME)
- print_element (name.nodename);
+ print_element (name.nodename);
if (toprint & PRINT_KERNEL_RELEASE)
- print_element (name.release);
+ print_element (name.release);
if (toprint & PRINT_KERNEL_VERSION)
- print_element (name.version);
+ print_element (name.version);
if (toprint & PRINT_MACHINE)
- print_element (name.machine);
+ print_element (name.machine);
}
if (toprint & PRINT_PROCESSOR)
@@ -252,43 +302,43 @@ main (int argc, char **argv)
char const *element = unknown;
#if HAVE_SYSINFO && defined SI_ARCHITECTURE
{
- static char processor[257];
- if (0 <= sysinfo (SI_ARCHITECTURE, processor, sizeof processor))
- element = processor;
+ static char processor[257];
+ if (0 <= sysinfo (SI_ARCHITECTURE, processor, sizeof processor))
+ element = processor;
}
#endif
#ifdef UNAME_PROCESSOR
if (element == unknown)
- {
- static char processor[257];
- size_t s = sizeof processor;
- static int mib[] = { CTL_HW, UNAME_PROCESSOR };
- if (sysctl (mib, 2, processor, &s, 0, 0) >= 0)
- element = processor;
+ {
+ static char processor[257];
+ size_t s = sizeof processor;
+ static int mib[] = { CTL_HW, UNAME_PROCESSOR };
+ if (sysctl (mib, 2, processor, &s, 0, 0) >= 0)
+ element = processor;
# ifdef __APPLE__
- /* This kludge works around a bug in Mac OS X. */
- if (element == unknown)
- {
- cpu_type_t cputype;
- size_t s = sizeof cputype;
- NXArchInfo const *ai;
- if (sysctlbyname ("hw.cputype", &cputype, &s, NULL, 0) == 0
- && (ai = NXGetArchInfoFromCpuType (cputype,
- CPU_SUBTYPE_MULTIPLE))
- != NULL)
- element = ai->name;
-
- /* Hack "safely" around the ppc vs. powerpc return value. */
- if (cputype == CPU_TYPE_POWERPC
- && strncmp (element, "ppc", 3) == 0)
- element = "powerpc";
- }
+ /* This kludge works around a bug in Mac OS X. */
+ if (element == unknown)
+ {
+ cpu_type_t cputype;
+ size_t cs = sizeof cputype;
+ NXArchInfo const *ai;
+ if (sysctlbyname ("hw.cputype", &cputype, &cs, NULL, 0) == 0
+ && (ai = NXGetArchInfoFromCpuType (cputype,
+ CPU_SUBTYPE_MULTIPLE))
+ != NULL)
+ element = ai->name;
+
+ /* Hack "safely" around the ppc vs. powerpc return value. */
+ if (cputype == CPU_TYPE_POWERPC
+ && STRNCMP_LIT (element, "ppc") == 0)
+ element = "powerpc";
+ }
# endif
- }
+ }
#endif
if (! (toprint == UINT_MAX && element == unknown))
- print_element (element);
+ print_element (element);
}
if (toprint & PRINT_HARDWARE_PLATFORM)
@@ -296,24 +346,24 @@ main (int argc, char **argv)
char const *element = unknown;
#if HAVE_SYSINFO && defined SI_PLATFORM
{
- static char hardware_platform[257];
- if (0 <= sysinfo (SI_PLATFORM,
- hardware_platform, sizeof hardware_platform))
- element = hardware_platform;
+ static char hardware_platform[257];
+ if (0 <= sysinfo (SI_PLATFORM,
+ hardware_platform, sizeof hardware_platform))
+ element = hardware_platform;
}
#endif
#ifdef UNAME_HARDWARE_PLATFORM
if (element == unknown)
- {
- static char hardware_platform[257];
- size_t s = sizeof hardware_platform;
- static int mib[] = { CTL_HW, UNAME_HARDWARE_PLATFORM };
- if (sysctl (mib, 2, hardware_platform, &s, 0, 0) >= 0)
- element = hardware_platform;
- }
+ {
+ static char hardware_platform[257];
+ size_t s = sizeof hardware_platform;
+ static int mib[] = { CTL_HW, UNAME_HARDWARE_PLATFORM };
+ if (sysctl (mib, 2, hardware_platform, &s, 0, 0) >= 0)
+ element = hardware_platform;
+ }
#endif
if (! (toprint == UINT_MAX && element == unknown))
- print_element (element);
+ print_element (element);
}
if (toprint & PRINT_OPERATING_SYSTEM)
@@ -321,5 +371,5 @@ main (int argc, char **argv)
putchar ('\n');
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/uname.h b/src/uname.h
new file mode 100644
index 0000000..f4da9b0
--- /dev/null
+++ b/src/uname.h
@@ -0,0 +1,7 @@
+/* This is for the 'uname' program. */
+#define UNAME_UNAME 1
+
+/* This is for the 'arch' program. */
+#define UNAME_ARCH 2
+
+extern int uname_mode;
diff --git a/src/unexpand.c b/src/unexpand.c
index cbceca0..a758756 100644
--- a/src/unexpand.c
+++ b/src/unexpand.c
@@ -1,10 +1,10 @@
/* unexpand - convert blanks to tabs
- Copyright (C) 89, 91, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* By default, convert only maximal strings of initial blanks and tabs
into tabs.
@@ -25,12 +24,12 @@
--tabs=tab1[,tab2[,...]]
-t tab1[,tab2[,...]]
-tab1[,tab2[,...]] If only one tab stop is given, set the tabs tab1
- columns apart instead of the default 8. Otherwise,
- set the tabs at columns tab1, tab2, etc. (numbered from
- 0); preserve any blanks beyond the tab stops given.
+ columns apart instead of the default 8. Otherwise,
+ set the tabs at columns tab1, tab2, etc. (numbered from
+ 0); preserve any blanks beyond the tab stops given.
--all
-a Use tabs wherever they would replace 2 or more blanks,
- not just at the beginnings of lines.
+ not just at the beginnings of lines.
David MacKenzie <djm@gnu.ai.mit.edu> */
@@ -41,50 +40,44 @@
#include <sys/types.h>
#include "system.h"
#include "error.h"
+#include "fadvise.h"
#include "quote.h"
#include "xstrndup.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "unexpand"
-#define AUTHORS "David MacKenzie"
-
-/* The number of bytes added at a time to the amount of memory
- allocated for the output line. */
-#define OUTPUT_BLOCK 256
-
-/* The name this program was run with. */
-char *program_name;
+#define AUTHORS proper_name ("David MacKenzie")
/* If true, convert blanks even after nonblank characters have been
read on the line. */
static bool convert_entire_line;
-/* If nonzero, the size of all tab stops. If zero, use `tab_list' instead. */
+/* If nonzero, the size of all tab stops. If zero, use 'tab_list' instead. */
static size_t tab_size;
/* The maximum distance between tab stops. */
static size_t max_column_width;
/* Array of the explicit column numbers of the tab stops;
- after `tab_list' is exhausted, the rest of the line is printed
+ after 'tab_list' is exhausted, the rest of the line is printed
unchanged. The first column is column 0. */
static uintmax_t *tab_list;
-/* The number of allocated entries in `tab_list'. */
+/* The number of allocated entries in 'tab_list'. */
static size_t n_tabs_allocated;
-/* The index of the first invalid element of `tab_list',
+/* The index of the first invalid element of 'tab_list',
where the next element can be added. */
static size_t first_free_tab;
/* Null-terminated array of input filenames. */
static char **file_list;
-/* Default for `file_list' if no files are given on the command line. */
+/* Default for 'file_list' if no files are given on the command line. */
static char *stdin_argv[] =
{
- "-", NULL
+ (char *) "-", NULL
};
/* True if we have ever read standard input. */
@@ -114,22 +107,20 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
- program_name);
+ program_name);
fputs (_("\
Convert blanks in each FILE to tabs, writing to standard output.\n\
-With no FILE, or when FILE is -, read standard input.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
+
+ 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\
@@ -138,12 +129,12 @@ Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
-/* Add tab stop TABVAL to the end of `tab_list'. */
+/* Add tab stop TABVAL to the end of 'tab_list'. */
static void
add_tab_stop (uintmax_t tabval)
@@ -158,7 +149,7 @@ add_tab_stop (uintmax_t tabval)
if (max_column_width < column_width)
{
if (SIZE_MAX < column_width)
- error (EXIT_FAILURE, 0, _("tabs are too far apart"));
+ error (EXIT_FAILURE, 0, _("tabs are too far apart"));
max_column_width = column_width;
}
}
@@ -170,45 +161,45 @@ static void
parse_tab_stops (char const *stops)
{
bool have_tabval = false;
- uintmax_t tabval IF_LINT (= 0);
- char const *num_start IF_LINT (= NULL);
+ uintmax_t tabval IF_LINT ( = 0);
+ char const *num_start IF_LINT ( = NULL);
bool ok = true;
for (; *stops; stops++)
{
if (*stops == ',' || isblank (to_uchar (*stops)))
- {
- if (have_tabval)
- add_tab_stop (tabval);
- have_tabval = false;
- }
+ {
+ if (have_tabval)
+ add_tab_stop (tabval);
+ have_tabval = false;
+ }
else if (ISDIGIT (*stops))
- {
- if (!have_tabval)
- {
- tabval = 0;
- have_tabval = true;
- num_start = stops;
- }
-
- /* Detect overflow. */
- if (!DECIMAL_DIGIT_ACCUMULATE (tabval, *stops - '0', uintmax_t))
- {
- size_t len = strspn (num_start, "0123456789");
- char *bad_num = xstrndup (num_start, len);
- error (0, 0, _("tab stop is too large %s"), quote (bad_num));
- free (bad_num);
- ok = false;
- stops = num_start + len - 1;
- }
- }
+ {
+ if (!have_tabval)
+ {
+ tabval = 0;
+ have_tabval = true;
+ num_start = stops;
+ }
+
+ /* Detect overflow. */
+ if (!DECIMAL_DIGIT_ACCUMULATE (tabval, *stops - '0', uintmax_t))
+ {
+ size_t len = strspn (num_start, "0123456789");
+ char *bad_num = xstrndup (num_start, len);
+ error (0, 0, _("tab stop is too large %s"), quote (bad_num));
+ free (bad_num);
+ ok = false;
+ stops = num_start + len - 1;
+ }
+ }
else
- {
- error (0, 0, _("tab size contains invalid character(s): %s"),
- quote (stops));
- ok = false;
- break;
- }
+ {
+ error (0, 0, _("tab size contains invalid character(s): %s"),
+ quote (stops));
+ ok = false;
+ break;
+ }
}
if (!ok)
@@ -230,16 +221,16 @@ validate_tab_stops (uintmax_t const *tabs, size_t entries)
for (i = 0; i < entries; i++)
{
if (tabs[i] == 0)
- error (EXIT_FAILURE, 0, _("tab size cannot be 0"));
+ error (EXIT_FAILURE, 0, _("tab size cannot be 0"));
if (tabs[i] <= prev_tab)
- error (EXIT_FAILURE, 0, _("tab sizes must be ascending"));
+ error (EXIT_FAILURE, 0, _("tab sizes must be ascending"));
prev_tab = tabs[i];
}
}
/* 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.
+ Open a filename of '-' as the standard input.
Return NULL if there are no more input files. */
static FILE *
@@ -251,41 +242,42 @@ next_file (FILE *fp)
if (fp)
{
if (ferror (fp))
- {
- error (0, errno, "%s", prev_file);
- exit_status = EXIT_FAILURE;
- }
+ {
+ error (0, errno, "%s", quotef (prev_file));
+ exit_status = EXIT_FAILURE;
+ }
if (STREQ (prev_file, "-"))
- clearerr (fp); /* Also clear EOF. */
+ clearerr (fp); /* Also clear EOF. */
else if (fclose (fp) != 0)
- {
- error (0, errno, "%s", prev_file);
- exit_status = EXIT_FAILURE;
- }
+ {
+ error (0, errno, "%s", quotef (prev_file));
+ exit_status = EXIT_FAILURE;
+ }
}
while ((file = *file_list++) != NULL)
{
if (STREQ (file, "-"))
- {
- have_read_stdin = true;
- prev_file = file;
- return stdin;
- }
- fp = fopen (file, "r");
+ {
+ have_read_stdin = true;
+ fp = stdin;
+ }
+ else
+ fp = fopen (file, "r");
if (fp)
- {
- prev_file = file;
- return fp;
- }
- error (0, errno, "%s", file);
+ {
+ prev_file = file;
+ fadvise (fp, FADVISE_SEQUENTIAL);
+ return fp;
+ }
+ error (0, errno, "%s", quotef (file));
exit_status = EXIT_FAILURE;
}
return NULL;
}
/* Change blanks to tabs, writing to stdout.
- Read each file in `file_list', in order. */
+ Read each file in 'file_list', in order. */
static void
unexpand (void)
@@ -306,7 +298,7 @@ unexpand (void)
allocate MAX_COLUMN_WIDTH bytes to store the blanks. */
pending_blank = xmalloc (max_column_width);
- for (;;)
+ while (true)
{
/* Input character, or EOF. */
int c;
@@ -316,7 +308,7 @@ unexpand (void)
/* The following variables have valid values only when CONVERT
- is true: */
+ is true: */
/* Column of next input character. */
uintmax_t column = 0;
@@ -331,8 +323,8 @@ unexpand (void)
bool one_blank_before_tab_stop = false;
/* If true, the previous input character was a blank. This is
- initially true, since initial strings of blanks are treated
- as if the line was preceded by a blank. */
+ initially true, since initial strings of blanks are treated
+ as if the line was preceded by a blank. */
bool prev_blank = true;
/* Number of pending columns of blanks. */
@@ -342,113 +334,113 @@ unexpand (void)
/* Convert a line of text. */
do
- {
- while ((c = getc (fp)) < 0 && (fp = next_file (fp)))
- continue;
-
- if (convert)
- {
- bool blank = !! isblank (c);
-
- if (blank)
- {
- if (next_tab_column <= column)
- {
- if (tab_size)
- next_tab_column =
- column + (tab_size - column % tab_size);
- else
- for (;;)
- if (tab_index == first_free_tab)
- {
- convert = false;
- break;
- }
- else
- {
- uintmax_t tab = tab_list[tab_index++];
- if (column < tab)
- {
- next_tab_column = tab;
- break;
- }
- }
- }
-
- if (convert)
- {
- if (next_tab_column < column)
- error (EXIT_FAILURE, 0, _("input line is too long"));
-
- if (c == '\t')
- {
- column = next_tab_column;
-
- /* Discard pending blanks, unless it was a single
- blank just before the previous tab stop. */
- if (! (pending == 1 && one_blank_before_tab_stop))
- {
- pending = 0;
- one_blank_before_tab_stop = false;
- }
- }
- else
- {
- column++;
-
- if (! (prev_blank && column == next_tab_column))
- {
- /* It is not yet known whether the pending blanks
- will be replaced by tabs. */
- if (column == next_tab_column)
- one_blank_before_tab_stop = true;
- pending_blank[pending++] = c;
- prev_blank = true;
- continue;
- }
-
- /* Replace the pending blanks by a tab or two. */
- pending_blank[0] = c = '\t';
- pending = one_blank_before_tab_stop;
- }
- }
- }
- else if (c == '\b')
- {
- /* Go back one column, and force recalculation of the
- next tab stop. */
- column -= !!column;
- next_tab_column = column;
- tab_index -= !!tab_index;
- }
- else
- {
- column++;
- if (!column)
- error (EXIT_FAILURE, 0, _("input line is too long"));
- }
-
- if (pending)
- {
- if (fwrite (pending_blank, 1, pending, stdout) != pending)
- error (EXIT_FAILURE, errno, _("write error"));
- pending = 0;
- one_blank_before_tab_stop = false;
- }
-
- prev_blank = blank;
- convert &= convert_entire_line | blank;
- }
-
- if (c < 0)
- {
- free (pending_blank);
- return;
- }
-
- if (putchar (c) < 0)
- error (EXIT_FAILURE, errno, _("write error"));
- }
+ {
+ while ((c = getc (fp)) < 0 && (fp = next_file (fp)))
+ continue;
+
+ if (convert)
+ {
+ bool blank = !! isblank (c);
+
+ if (blank)
+ {
+ if (next_tab_column <= column)
+ {
+ if (tab_size)
+ next_tab_column =
+ column + (tab_size - column % tab_size);
+ else
+ while (true)
+ if (tab_index == first_free_tab)
+ {
+ convert = false;
+ break;
+ }
+ else
+ {
+ uintmax_t tab = tab_list[tab_index++];
+ if (column < tab)
+ {
+ next_tab_column = tab;
+ break;
+ }
+ }
+ }
+
+ if (convert)
+ {
+ if (next_tab_column < column)
+ error (EXIT_FAILURE, 0, _("input line is too long"));
+
+ if (c == '\t')
+ {
+ column = next_tab_column;
+
+ if (pending)
+ pending_blank[0] = '\t';
+ }
+ else
+ {
+ column++;
+
+ if (! (prev_blank && column == next_tab_column))
+ {
+ /* It is not yet known whether the pending blanks
+ will be replaced by tabs. */
+ if (column == next_tab_column)
+ one_blank_before_tab_stop = true;
+ pending_blank[pending++] = c;
+ prev_blank = true;
+ continue;
+ }
+
+ /* Replace the pending blanks by a tab or two. */
+ pending_blank[0] = c = '\t';
+ }
+
+ /* Discard pending blanks, unless it was a single
+ blank just before the previous tab stop. */
+ pending = one_blank_before_tab_stop;
+ }
+ }
+ else if (c == '\b')
+ {
+ /* Go back one column, and force recalculation of the
+ next tab stop. */
+ column -= !!column;
+ next_tab_column = column;
+ tab_index -= !!tab_index;
+ }
+ else
+ {
+ column++;
+ if (!column)
+ error (EXIT_FAILURE, 0, _("input line is too long"));
+ }
+
+ if (pending)
+ {
+ if (pending > 1 && one_blank_before_tab_stop)
+ pending_blank[0] = '\t';
+ if (fwrite (pending_blank, 1, pending, stdout) != pending)
+ error (EXIT_FAILURE, errno, _("write error"));
+ pending = 0;
+ one_blank_before_tab_stop = false;
+ }
+
+ prev_blank = blank;
+ convert &= convert_entire_line || blank;
+ }
+
+ if (c < 0)
+ {
+ free (pending_blank);
+ return;
+ }
+
+ if (putchar (c) < 0)
+ error (EXIT_FAILURE, errno, _("write error"));
+ }
while (c != '\n');
}
}
@@ -457,7 +449,7 @@ int
main (int argc, char **argv)
{
bool have_tabval = false;
- uintmax_t tabval IF_LINT (= 0);
+ uintmax_t tabval IF_LINT ( = 0);
int c;
/* If true, cancel the effect of any -a (explicit or implicit in -t),
@@ -465,7 +457,7 @@ main (int argc, char **argv)
bool convert_first_only = false;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -479,39 +471,39 @@ main (int argc, char **argv)
first_free_tab = 0;
while ((c = getopt_long (argc, argv, ",0123456789at:", longopts, NULL))
- != -1)
+ != -1)
{
switch (c)
- {
- case '?':
- usage (EXIT_FAILURE);
- case 'a':
- convert_entire_line = true;
- break;
- case 't':
- convert_entire_line = true;
- parse_tab_stops (optarg);
- break;
- case CONVERT_FIRST_ONLY_OPTION:
- convert_first_only = true;
- break;
- case ',':
- if (have_tabval)
- add_tab_stop (tabval);
- have_tabval = false;
- break;
- case_GETOPT_HELP_CHAR;
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
- default:
- if (!have_tabval)
- {
- tabval = 0;
- have_tabval = true;
- }
- if (!DECIMAL_DIGIT_ACCUMULATE (tabval, c - '0', uintmax_t))
- error (EXIT_FAILURE, 0, _("tab stop value is too large"));
- break;
- }
+ {
+ case '?':
+ usage (EXIT_FAILURE);
+ case 'a':
+ convert_entire_line = true;
+ break;
+ case 't':
+ convert_entire_line = true;
+ parse_tab_stops (optarg);
+ break;
+ case CONVERT_FIRST_ONLY_OPTION:
+ convert_first_only = true;
+ break;
+ case ',':
+ if (have_tabval)
+ add_tab_stop (tabval);
+ have_tabval = false;
+ break;
+ case_GETOPT_HELP_CHAR;
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+ default:
+ if (!have_tabval)
+ {
+ tabval = 0;
+ have_tabval = true;
+ }
+ if (!DECIMAL_DIGIT_ACCUMULATE (tabval, c - '0', uintmax_t))
+ error (EXIT_FAILURE, 0, _("tab stop value is too large"));
+ break;
+ }
}
if (convert_first_only)
@@ -536,5 +528,5 @@ main (int argc, char **argv)
if (have_read_stdin && fclose (stdin) != 0)
error (EXIT_FAILURE, errno, "-");
- exit (exit_status);
+ return exit_status;
}
diff --git a/src/uniq.c b/src/uniq.c
index 6c38ed8..0e118da 100644
--- a/src/uniq.c
+++ b/src/uniq.c
@@ -1,10 +1,10 @@
/* uniq -- remove duplicate lines from a sorted file
- Copyright (C) 86, 91, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1986-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,14 +12,12 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Written by Richard M. Stallman and David MacKenzie. */
-/* Written by Richard Stallman and David MacKenzie. */
-
#include <config.h>
-#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
@@ -27,17 +25,21 @@
#include "argmatch.h"
#include "linebuffer.h"
#include "error.h"
+#include "fadvise.h"
#include "hard-locale.h"
#include "posixver.h"
-#include "quote.h"
+#include "stdio--.h"
#include "xmemcoll.h"
#include "xstrtol.h"
#include "memcasecmp.h"
+#include "quote.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "uniq"
-#define AUTHORS "Richard Stallman", "David MacKenzie"
+#define AUTHORS \
+ proper_name ("Richard M. Stallman"), \
+ proper_name ("David MacKenzie")
#define SWAP_LINES(A, B) \
do \
@@ -49,9 +51,6 @@
} \
while (0)
-/* The name this program was run with. */
-char *program_name;
-
/* True if the LC_COLLATE locale is hard. */
static bool hard_LC_COLLATE;
@@ -109,16 +108,53 @@ static enum delimit_method const delimit_method_map[] =
/* Select whether/how to delimit groups of duplicate lines. */
static enum delimit_method delimit_groups;
+enum grouping_method
+{
+ /* No grouping, when "--group" isn't used */
+ GM_NONE,
+
+ /* Delimiter preceges all groups. --group=prepend */
+ GM_PREPEND,
+
+ /* Delimiter follows all groups. --group=append */
+ GM_APPEND,
+
+ /* Delimiter between groups. --group[=separate] */
+ GM_SEPARATE,
+
+ /* Delimiter before and after each group. --group=both */
+ GM_BOTH
+};
+
+static char const *const grouping_method_string[] =
+{
+ "prepend", "append", "separate", "both", NULL
+};
+
+static enum grouping_method const grouping_method_map[] =
+{
+ GM_PREPEND, GM_APPEND, GM_SEPARATE, GM_BOTH
+};
+
+static enum grouping_method grouping = GM_NONE;
+
+enum
+{
+ GROUP_OPTION = CHAR_MAX + 1
+};
+
static struct option const longopts[] =
{
{"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},
{NULL, 0, NULL, 0}
@@ -128,35 +164,47 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... [INPUT [OUTPUT]]\n\
"),
- program_name);
+ program_name);
fputs (_("\
-Discard all but one of successive identical lines from INPUT (or\n\
-standard input), writing to OUTPUT (or standard output).\n\
+Filter adjacent matching lines from INPUT (or standard input),\n\
+writing to OUTPUT (or standard output).\n\
\n\
+With no options, matching lines are merged to the first occurrence.\n\
"), stdout);
+
+ emit_mandatory_arg_note ();
+
fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
+ -c, --count prefix lines by the number of occurrences\n\
+ -d, --repeated only print duplicate lines, one for each group\n\
"), stdout);
fputs (_("\
- -c, --count prefix lines by the number of occurrences\n\
- -d, --repeated only print duplicate lines\n\
+ -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 (_("\
- -D, --all-repeated[=delimit-method] print all duplicate lines\n\
- delimit-method={none(default),prepend,separate}\n\
- Delimiting is done with blank lines.\n\
-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);
@@ -164,10 +212,16 @@ Mandatory arguments to long options are mandatory for short options too.\n\
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
\n\
-A field is a run of whitespace, then non-whitespace characters.\n\
-Fields are skipped before chars.\n\
+A field is a run of blanks (usually spaces and/or TABs), then non-blank\n\
+characters. Fields are skipped before chars.\n\
+"), stdout);
+ fputs (_("\
+\n\
+Note: 'uniq' does not detect repeated lines unless they are adjacent.\n\
+You may want to sort the input first, or use 'sort -u' without 'uniq'.\n\
+Also, comparisons honor the rules specified by 'LC_COLLATE'.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -197,26 +251,25 @@ size_opt (char const *opt, char const *msgid)
/* Given a linebuffer LINE,
return a pointer to the beginning of the line's field to be compared. */
-static char *
-find_field (const struct linebuffer *line)
+static char * _GL_ATTRIBUTE_PURE
+find_field (struct linebuffer const *line)
{
size_t count;
- char *lp = line->buffer;
+ char const *lp = line->buffer;
size_t size = line->length - 1;
size_t i = 0;
for (count = 0; count < skip_fields && i < size; count++)
{
- while (i < size && isblank (lp[i]))
- i++;
- while (i < size && !isblank (lp[i]))
- i++;
+ while (i < size && field_sep (lp[i]))
+ i++;
+ while (i < size && !field_sep (lp[i]))
+ i++;
}
- for (count = 0; count < skip_chars && i < size; count++)
- i++;
+ i += MIN (skip_chars, size - i);
- return lp + i;
+ return line->buffer + i;
}
/* Return false if two strings OLD and NEW match, true if not.
@@ -251,11 +304,11 @@ different (char *old, char *new, size_t oldlen, size_t newlen)
static void
writeline (struct linebuffer const *line,
- bool match, uintmax_t linecount)
+ bool match, uintmax_t linecount)
{
if (! (linecount == 0 ? output_unique
- : !match ? output_first_repeated
- : output_later_repeated))
+ : !match ? output_first_repeated
+ : output_later_repeated))
return;
if (countmode == count_occurrences)
@@ -268,15 +321,17 @@ writeline (struct linebuffer const *line,
If either is "-", use the standard I/O stream for it instead. */
static void
-check_file (const char *infile, const char *outfile)
+check_file (const char *infile, const char *outfile, char delimiter)
{
struct linebuffer lb1, lb2;
struct linebuffer *thisline, *prevline;
if (! (STREQ (infile, "-") || freopen (infile, "r", stdin)))
- error (EXIT_FAILURE, errno, "%s", infile);
+ error (EXIT_FAILURE, errno, "%s", quotef (infile));
if (! (STREQ (outfile, "-") || freopen (outfile, "w", stdout)))
- error (EXIT_FAILURE, errno, "%s", outfile);
+ error (EXIT_FAILURE, errno, "%s", quotef (outfile));
+
+ fadvise (stdin, FADVISE_SEQUENTIAL);
thisline = &lb1;
prevline = &lb2;
@@ -284,37 +339,61 @@ check_file (const char *infile, const char *outfile)
initbuffer (thisline);
initbuffer (prevline);
- /* The duplication in the following `if' and `else' blocks is an
- optimization to distinguish the common case (in which none of
- the following options has been specified: --count, -repeated,
- --all-repeated, --unique) from the others. In the common case,
- this optimization lets uniq output each different line right away,
- without waiting to see if the next one is different. */
+ /* The duplication in the following 'if' and 'else' blocks is an
+ optimization to distinguish between when we can print input
+ lines immediately (1. & 2.) or not.
+ 1. --group => all input lines are printed.
+ checking for unique/duplicated lines is used only for printing
+ group separators.
+
+ 2. The default case in which none of these options has been specified:
+ --count, --repeated, --all-repeated, --unique
+ In the default case, this optimization lets uniq output each different
+ line right away, without waiting to see if the next one is different.
+
+ 3. All other cases.
+ */
if (output_unique && output_first_repeated && countmode == count_none)
{
- char *prevfield IF_LINT (= NULL);
- size_t prevlen IF_LINT (= 0);
+ char *prevfield IF_LINT ( = NULL);
+ size_t prevlen IF_LINT ( = 0);
+ bool first_group_printed = false;
while (!feof (stdin))
- {
- char *thisfield;
- size_t thislen;
- if (readlinebuffer (thisline, stdin) == 0)
- break;
- thisfield = find_field (thisline);
- thislen = thisline->length - 1 - (thisfield - thisline->buffer);
- if (prevline->length == 0
- || different (thisfield, prevfield, thislen, prevlen))
- {
- fwrite (thisline->buffer, sizeof (char),
- thisline->length, stdout);
-
- SWAP_LINES (prevline, thisline);
- prevfield = thisfield;
- prevlen = thislen;
- }
- }
+ {
+ char *thisfield;
+ size_t thislen;
+ bool new_group;
+
+ if (readlinebuffer_delim (thisline, stdin, delimiter) == 0)
+ break;
+
+ thisfield = find_field (thisline);
+ thislen = thisline->length - 1 - (thisfield - thisline->buffer);
+
+ new_group = (prevline->length == 0
+ || different (thisfield, prevfield, thislen, prevlen));
+
+ if (new_group && grouping != GM_NONE
+ && (grouping == GM_PREPEND || grouping == GM_BOTH
+ || (first_group_printed && (grouping == GM_APPEND
+ || grouping == GM_SEPARATE))))
+ putchar (delimiter);
+
+ if (new_group || grouping != GM_NONE)
+ {
+ fwrite (thisline->buffer, sizeof (char),
+ thisline->length, stdout);
+
+ SWAP_LINES (prevline, thisline);
+ prevfield = thisfield;
+ prevlen = thislen;
+ first_group_printed = true;
+ }
+ }
+ if ((grouping == GM_BOTH || grouping == GM_APPEND) && first_group_printed)
+ putchar (delimiter);
}
else
{
@@ -323,67 +402,67 @@ check_file (const char *infile, const char *outfile)
uintmax_t match_count = 0;
bool first_delimiter = true;
- if (readlinebuffer (prevline, stdin) == 0)
- goto closefiles;
+ if (readlinebuffer_delim (prevline, stdin, delimiter) == 0)
+ goto closefiles;
prevfield = find_field (prevline);
prevlen = prevline->length - 1 - (prevfield - prevline->buffer);
while (!feof (stdin))
- {
- bool match;
- char *thisfield;
- size_t thislen;
- if (readlinebuffer (thisline, stdin) == 0)
- {
- if (ferror (stdin))
- goto closefiles;
- break;
- }
- thisfield = find_field (thisline);
- thislen = thisline->length - 1 - (thisfield - thisline->buffer);
- match = !different (thisfield, prevfield, thislen, prevlen);
- match_count += match;
-
- if (match_count == UINTMAX_MAX)
- {
- if (count_occurrences)
- error (EXIT_FAILURE, 0, _("too many repeated lines"));
- match_count--;
- }
+ {
+ bool match;
+ char *thisfield;
+ size_t thislen;
+ if (readlinebuffer_delim (thisline, stdin, delimiter) == 0)
+ {
+ if (ferror (stdin))
+ goto closefiles;
+ break;
+ }
+ thisfield = find_field (thisline);
+ thislen = thisline->length - 1 - (thisfield - thisline->buffer);
+ match = !different (thisfield, prevfield, thislen, prevlen);
+ match_count += match;
+
+ if (match_count == UINTMAX_MAX)
+ {
+ if (count_occurrences)
+ error (EXIT_FAILURE, 0, _("too many repeated lines"));
+ match_count--;
+ }
if (delimit_groups != DM_NONE)
- {
- if (!match)
- {
- if (match_count) /* a previous match */
- first_delimiter = false; /* Only used when DM_SEPARATE */
- }
- else if (match_count == 1)
- {
- if ((delimit_groups == DM_PREPEND)
- || (delimit_groups == DM_SEPARATE
- && !first_delimiter))
- putchar ('\n');
- }
- }
-
- if (!match || output_later_repeated)
- {
- writeline (prevline, match, match_count);
- SWAP_LINES (prevline, thisline);
- prevfield = thisfield;
- prevlen = thislen;
- if (!match)
- match_count = 0;
- }
- }
+ {
+ if (!match)
+ {
+ if (match_count) /* a previous match */
+ first_delimiter = false; /* Only used when DM_SEPARATE */
+ }
+ else if (match_count == 1)
+ {
+ if ((delimit_groups == DM_PREPEND)
+ || (delimit_groups == DM_SEPARATE
+ && !first_delimiter))
+ putchar (delimiter);
+ }
+ }
+
+ if (!match || output_later_repeated)
+ {
+ writeline (prevline, match, match_count);
+ SWAP_LINES (prevline, thisline);
+ prevfield = thisfield;
+ prevlen = thislen;
+ if (!match)
+ match_count = 0;
+ }
+ }
writeline (prevline, false, match_count);
}
closefiles:
if (ferror (stdin) || fclose (stdin) != 0)
- error (EXIT_FAILURE, 0, _("error reading %s"), infile);
+ error (EXIT_FAILURE, 0, _("error reading %s"), quoteaf (infile));
/* stdout is handled via the atexit-invoked close_stdout function. */
@@ -404,12 +483,14 @@ main (int argc, char **argv)
int optc = 0;
bool posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
enum Skip_field_option_type skip_field_option_type = SFO_NONE;
- int nfiles = 0;
+ unsigned int nfiles = 0;
char const *file[2];
+ char delimiter = '\n'; /* change with --zero-terminated, -z */
+ bool output_option_used = false; /* if true, one of -u/-d/-D/-c was used */
file[0] = file[1] = "-";
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -425,128 +506,162 @@ main (int argc, char **argv)
countmode = count_none;
delimit_groups = DM_NONE;
- for (;;)
+ while (true)
{
/* Parse an operand with leading "+" as a file after "--" was
seen; or if pedantic and a file was seen; or if not
obsolete. */
if (optc == -1
- || (posixly_correct && nfiles != 0)
- || ((optc = getopt_long (argc, argv,
- "-0123456789Dcdf:is:uw:", longopts, NULL))
- == -1))
- {
- if (argc <= optind)
- break;
- if (nfiles == 2)
- {
- error (0, 0, _("extra operand %s"), quote (argv[optind]));
- usage (EXIT_FAILURE);
- }
- file[nfiles++] = argv[optind++];
- }
+ || (posixly_correct && nfiles != 0)
+ || ((optc = getopt_long (argc, argv,
+ "-0123456789Dcdf:is:uw:z", longopts, NULL))
+ == -1))
+ {
+ if (argc <= optind)
+ break;
+ if (nfiles == 2)
+ {
+ error (0, 0, _("extra operand %s"), quote (argv[optind]));
+ usage (EXIT_FAILURE);
+ }
+ file[nfiles++] = argv[optind++];
+ }
else switch (optc)
- {
- case 1:
- {
- unsigned long int size;
- if (optarg[0] == '+'
- && posix2_version () < 200112
- && xstrtoul (optarg, NULL, 10, &size, "") == LONGINT_OK
- && size <= SIZE_MAX)
- skip_chars = size;
- else if (nfiles == 2)
- {
- error (0, 0, _("extra operand %s"), quote (optarg));
- usage (EXIT_FAILURE);
- }
- else
- file[nfiles++] = optarg;
- }
- break;
-
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- if (skip_field_option_type == SFO_NEW)
- skip_fields = 0;
-
- if (!DECIMAL_DIGIT_ACCUMULATE (skip_fields, optc - '0', size_t))
- skip_fields = SIZE_MAX;
-
- skip_field_option_type = SFO_OBSOLETE;
- }
- break;
-
- case 'c':
- countmode = count_occurrences;
- break;
-
- case 'd':
- output_unique = false;
- break;
-
- case 'D':
- output_unique = false;
- output_later_repeated = true;
- if (optarg == NULL)
- delimit_groups = DM_NONE;
- else
- delimit_groups = XARGMATCH ("--all-repeated", optarg,
- delimit_method_string,
- delimit_method_map);
- break;
-
- case 'f':
- skip_field_option_type = SFO_NEW;
- skip_fields = size_opt (optarg,
- N_("invalid number of fields to skip"));
- break;
-
- case 'i':
- ignore_case = true;
- break;
-
- case 's':
- skip_chars = size_opt (optarg,
- N_("invalid number of bytes to skip"));
- break;
-
- case 'u':
- output_first_repeated = false;
- break;
-
- case 'w':
- check_chars = size_opt (optarg,
- N_("invalid number of bytes to compare"));
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 1:
+ {
+ unsigned long int size;
+ if (optarg[0] == '+'
+ && posix2_version () < 200112
+ && xstrtoul (optarg, NULL, 10, &size, "") == LONGINT_OK
+ && size <= SIZE_MAX)
+ skip_chars = size;
+ else if (nfiles == 2)
+ {
+ error (0, 0, _("extra operand %s"), quote (optarg));
+ usage (EXIT_FAILURE);
+ }
+ else
+ file[nfiles++] = optarg;
+ }
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ if (skip_field_option_type == SFO_NEW)
+ skip_fields = 0;
+
+ if (!DECIMAL_DIGIT_ACCUMULATE (skip_fields, optc - '0', size_t))
+ skip_fields = SIZE_MAX;
+
+ skip_field_option_type = SFO_OBSOLETE;
+ }
+ break;
+
+ case 'c':
+ countmode = count_occurrences;
+ output_option_used = true;
+ break;
+
+ case 'd':
+ output_unique = false;
+ output_option_used = true;
+ break;
+
+ case 'D':
+ output_unique = false;
+ output_later_repeated = true;
+ if (optarg == NULL)
+ delimit_groups = DM_NONE;
+ else
+ delimit_groups = XARGMATCH ("--all-repeated", optarg,
+ delimit_method_string,
+ delimit_method_map);
+ output_option_used = true;
+ break;
+
+ case GROUP_OPTION:
+ if (optarg == NULL)
+ grouping = GM_SEPARATE;
+ else
+ grouping = XARGMATCH ("--group", optarg,
+ grouping_method_string,
+ grouping_method_map);
+ break;
+
+ case 'f':
+ skip_field_option_type = SFO_NEW;
+ skip_fields = size_opt (optarg,
+ N_("invalid number of fields to skip"));
+ break;
+
+ case 'i':
+ ignore_case = true;
+ break;
+
+ case 's':
+ skip_chars = size_opt (optarg,
+ N_("invalid number of bytes to skip"));
+ break;
+
+ case 'u':
+ output_first_repeated = false;
+ output_option_used = true;
+ break;
+
+ case 'w':
+ check_chars = size_opt (optarg,
+ N_("invalid number of bytes to compare"));
+ break;
+
+ case 'z':
+ delimiter = '\0';
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
+ }
+
+ /* Note we could allow --group with -D at least, and that would
+ avoid the need to specify a grouping method to --all-repeated.
+ It was thought best to avoid deprecating those parameters though
+ and keep --group separate to other options. */
+ if (grouping != GM_NONE && output_option_used)
+ {
+ error (0, 0, _("--group is mutually exclusive with -c/-d/-D/-u"));
+ usage (EXIT_FAILURE);
+ }
+
+ if (grouping != GM_NONE && countmode != count_none)
+ {
+ error (0, 0,
+ _("grouping and printing repeat counts is meaningless"));
+ usage (EXIT_FAILURE);
}
if (countmode == count_occurrences && output_later_repeated)
{
error (0, 0,
- _("printing all duplicated lines and repeat counts is meaningless"));
+ _("printing all duplicated lines and repeat counts is meaningless"));
usage (EXIT_FAILURE);
}
- check_file (file[0], file[1]);
+ check_file (file[0], file[1], delimiter);
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/unlink.c b/src/unlink.c
index 7255076..51a26b1 100644
--- a/src/unlink.c
+++ b/src/unlink.c
@@ -1,10 +1,10 @@
/* unlink utility for GNU.
- Copyright (C) 2001, 2002, 2004 Free Software Foundation, Inc.
+ Copyright (C) 2001-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Michael Stone */
@@ -31,30 +30,26 @@
#include "long-options.h"
#include "quote.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "unlink"
-#define AUTHORS "Michael Stone"
-
-/* Name this program was run with. */
-char *program_name;
+#define AUTHORS proper_name ("Michael Stone")
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
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);
+ stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -63,15 +58,15 @@ int
main (int argc, char **argv)
{
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
+ parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, Version,
+ usage, AUTHORS, (char const *) NULL);
if (getopt_long (argc, argv, "", NULL, NULL) != -1)
usage (EXIT_FAILURE);
@@ -88,7 +83,7 @@ main (int argc, char **argv)
}
if (unlink (argv[optind]) != 0)
- error (EXIT_FAILURE, errno, _("cannot unlink %s"), quote (argv[optind]));
+ error (EXIT_FAILURE, errno, _("cannot unlink %s"), quoteaf (argv[optind]));
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/uptime.c b/src/uptime.c
index 6b2a724..74ea87d 100644
--- a/src/uptime.c
+++ b/src/uptime.c
@@ -1,10 +1,10 @@
/* GNU's uptime.
- Copyright (C) 1992-2002, 2004, 2005, 2006 Free Software Foundation, Inc.
+ Copyright (C) 1992-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Created by hacking who.c by Kaveh Ghazi ghazi@caip.rutgers.edu. */
@@ -37,16 +36,15 @@
#include "long-options.h"
#include "quote.h"
#include "readutmp.h"
+#include "fprintftime.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "uptime"
-#define AUTHORS "Joseph Arceneaux", "David MacKenzie", "Kaveh Ghazi"
-
-int getloadavg ();
-
-/* The name this program was run with. */
-char *program_name;
+#define AUTHORS \
+ proper_name ("Joseph Arceneaux"), \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Kaveh Ghazi")
static void
print_uptime (size_t n, const STRUCT_UTMP *this)
@@ -70,13 +68,13 @@ print_uptime (size_t n, const STRUCT_UTMP *this)
char buf[BUFSIZ];
char *b = fgets (buf, BUFSIZ, fp);
if (b == buf)
- {
- char *end_ptr;
- double upsecs = c_strtod (buf, &end_ptr);
- if (buf != end_ptr)
- uptime = (0 <= upsecs && upsecs < TYPE_MAXIMUM (time_t)
- ? upsecs : -1);
- }
+ {
+ char *end_ptr;
+ double upsecs = c_strtod (buf, &end_ptr);
+ if (buf != end_ptr)
+ uptime = (0 <= upsecs && upsecs < TYPE_MAXIMUM (time_t)
+ ? upsecs : -1);
+ }
fclose (fp);
}
@@ -110,61 +108,63 @@ print_uptime (size_t n, const STRUCT_UTMP *this)
{
entries += IS_USER_PROCESS (this);
if (UT_TYPE_BOOT_TIME (this))
- boot_time = UT_TIME_MEMBER (this);
+ boot_time = UT_TIME_MEMBER (this);
++this;
}
+#else
+ (void) n;
+ (void) this;
#endif
+
time_now = time (NULL);
#if defined HAVE_PROC_UPTIME
if (uptime == 0)
#endif
{
if (boot_time == 0)
- error (EXIT_FAILURE, errno, _("couldn't get boot time"));
+ error (EXIT_FAILURE, errno, _("couldn't get boot time"));
uptime = time_now - boot_time;
}
updays = uptime / 86400;
uphours = (uptime - (updays * 86400)) / 3600;
upmins = (uptime - (updays * 86400) - (uphours * 3600)) / 60;
tmn = localtime (&time_now);
+ /* procps' version of uptime also prints the seconds field, but
+ previous versions of coreutils don't. */
if (tmn)
- printf (_(" %2d:%02d%s up "),
- ((tmn->tm_hour % 12) == 0 ? 12 : tmn->tm_hour % 12),
- /* FIXME: use strftime, not am, pm. Uli reports that
- the german translation is meaningless. */
- tmn->tm_min, (tmn->tm_hour < 12 ? _("am") : _("pm")));
+ /* TRANSLATORS: This prints the current clock time. */
+ fprintftime (stdout, _(" %H:%M%P "), tmn, 0, 0);
else
- printf (_(" ??:???? up "));
+ printf (_(" ??:???? "));
if (uptime == (time_t) -1)
- printf (_("???? days ??:??, "));
+ printf (_("up ???? days ??:??, "));
else
{
if (0 < updays)
- printf (ngettext ("%ld day", "%ld days", select_plural (updays)),
- updays);
- printf (" %2d:%02d, ", uphours, upmins);
+ printf (ngettext ("up %ld day %2d:%02d, ",
+ "up %ld days %2d:%02d, ",
+ select_plural (updays)),
+ updays, uphours, upmins);
+ else
+ printf (_("up %2d:%02d, "), uphours, upmins);
}
- printf (ngettext ("%lu user", "%lu users", entries),
- (unsigned long int) entries);
+ printf (ngettext ("%lu user", "%lu users", select_plural (entries)),
+ (unsigned long int) entries);
-#if defined HAVE_GETLOADAVG || defined C_GETLOADAVG
loads = getloadavg (avg, 3);
-#else
- loads = -1;
-#endif
if (loads == -1)
putchar ('\n');
else
{
if (loads > 0)
- printf (_(", load average: %.2f"), avg[0]);
+ printf (_(", load average: %.2f"), avg[0]);
if (loads > 1)
- printf (", %.2f", avg[1]);
+ printf (", %.2f", avg[1]);
if (loads > 2)
- printf (", %.2f", avg[2]);
+ printf (", %.2f", avg[2]);
if (loads > 0)
- putchar ('\n');
+ putchar ('\n');
}
}
@@ -176,36 +176,48 @@ static void
uptime (const char *filename, int options)
{
size_t n_users;
- STRUCT_UTMP *utmp_buf;
+ STRUCT_UTMP *utmp_buf = NULL;
#if HAVE_UTMPX_H || HAVE_UTMP_H
if (read_utmp (filename, &n_users, &utmp_buf, options) != 0)
- error (EXIT_FAILURE, errno, "%s", filename);
+ error (EXIT_FAILURE, errno, "%s", quotef (filename));
#endif
print_uptime (n_users, utmp_buf);
+
+ IF_LINT (free (utmp_buf));
}
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
- printf (_("Usage: %s [OPTION]... [ FILE ]\n"), program_name);
+ printf (_("Usage: %s [OPTION]... [FILE]\n"), program_name);
printf (_("\
Print the current time, the length of time the system has been up,\n\
the number of users on the system, and the average number of jobs\n\
-in the run queue over the last 1, 5 and 15 minutes.\n\
+in the run queue over the last 1, 5 and 15 minutes."));
+#ifdef __linux__
+ /* It would be better to introduce a configure test for this,
+ but such a test is hard to write. For the moment then, we
+ have a hack which depends on the preprocessor used at compile
+ time to tell us what the running kernel is. Ugh. */
+ printf (_(" \
+Processes in\n\
+an uninterruptible sleep state also contribute to the load average.\n"));
+#else
+ printf (_("\n"));
+#endif
+ printf (_("\
If FILE is not specified, use %s. %s as FILE is common.\n\
-\n\
-"),
- UTMP_FILE, WTMP_FILE);
+\n"),
+ UTMP_FILE, WTMP_FILE);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -214,15 +226,15 @@ int
main (int argc, char **argv)
{
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
+ parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, Version,
+ usage, AUTHORS, (char const *) NULL);
if (getopt_long (argc, argv, "", NULL, NULL) != -1)
usage (EXIT_FAILURE);
@@ -241,5 +253,5 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/users.c b/src/users.c
index dba4701..d5c86f3 100644
--- a/src/users.c
+++ b/src/users.c
@@ -1,10 +1,10 @@
/* GNU's users.
- Copyright (C) 1992-2005 Free Software Foundation, Inc.
+ Copyright (C) 1992-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by jla; revised by djm */
@@ -29,13 +28,12 @@
#include "quote.h"
#include "readutmp.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "users"
-#define AUTHORS "Joseph Arceneaux", "David MacKenzie"
-
-/* The name this program was run with. */
-char *program_name;
+#define AUTHORS \
+ proper_name ("Joseph Arceneaux"), \
+ proper_name ("David MacKenzie")
static int
userid_compare (const void *v_a, const void *v_b)
@@ -55,14 +53,14 @@ list_entries_users (size_t n, const STRUCT_UTMP *this)
while (n--)
{
if (IS_USER_PROCESS (this))
- {
- char *trimmed_name;
+ {
+ char *trimmed_name;
- trimmed_name = extract_trimmed_name (this);
+ trimmed_name = extract_trimmed_name (this);
- u[n_entries] = trimmed_name;
- ++n_entries;
- }
+ u[n_entries] = trimmed_name;
+ ++n_entries;
+ }
this++;
}
@@ -90,7 +88,7 @@ users (const char *filename, int options)
STRUCT_UTMP *utmp_buf;
if (read_utmp (filename, &n_users, &utmp_buf, options) != 0)
- error (EXIT_FAILURE, errno, "%s", filename);
+ error (EXIT_FAILURE, errno, "%s", quotef (filename));
list_entries_users (n_users, utmp_buf);
@@ -101,20 +99,19 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
- printf (_("Usage: %s [OPTION]... [ FILE ]\n"), program_name);
+ printf (_("Usage: %s [OPTION]... [FILE]\n"), program_name);
printf (_("\
Output who is currently logged in according to FILE.\n\
If FILE is not specified, use %s. %s as FILE is common.\n\
\n\
"),
- UTMP_FILE, WTMP_FILE);
+ UTMP_FILE, WTMP_FILE);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -123,15 +120,15 @@ int
main (int argc, char **argv)
{
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
+ parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, Version,
+ usage, AUTHORS, (char const *) NULL);
if (getopt_long (argc, argv, "", NULL, NULL) != -1)
usage (EXIT_FAILURE);
@@ -150,5 +147,5 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/wc.c b/src/wc.c
index 332f32d..94cbaff 100644
--- a/src/wc.c
+++ b/src/wc.c
@@ -1,10 +1,10 @@
/* wc - print the number of lines, words, and bytes in files
- Copyright (C) 85, 91, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1985-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,42 +12,46 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by Paul Rubin, phr@ocf.berkeley.edu
and David MacKenzie, djm@gnu.ai.mit.edu. */
-
+
#include <config.h>
#include <stdio.h>
+#include <assert.h>
#include <getopt.h>
#include <sys/types.h>
+#include <wchar.h>
+#include <wctype.h>
#include "system.h"
+#include "argv-iter.h"
#include "error.h"
-#include "inttostr.h"
-#include "quote.h"
+#include "fadvise.h"
+#include "mbchar.h"
+#include "physmem.h"
#include "readtokens0.h"
#include "safe-read.h"
-#include "wcwidth.h"
+#include "stat-size.h"
+#include "xfreopen.h"
#if !defined iswspace && !HAVE_ISWSPACE
# define iswspace(wc) \
((wc) == to_uchar (wc) && isspace (to_uchar (wc)))
#endif
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "wc"
-#define AUTHORS "Paul Rubin", "David MacKenzie"
+#define AUTHORS \
+ proper_name ("Paul Rubin"), \
+ proper_name ("David MacKenzie")
/* Size of atomic reads. */
#define BUFFER_SIZE (16 * 1024)
-/* The name this program was run with. */
-char *program_name;
-
/* Cumulative number of lines, words, chars and bytes in all files so far.
max_line_length is the maximum over all files processed so far. */
static uintmax_t total_lines;
@@ -101,32 +105,40 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
or: %s [OPTION]... --files0-from=F\n\
"),
- program_name, program_name);
+ program_name, program_name);
fputs (_("\
Print newline, word, and byte counts for each FILE, and a total line if\n\
-more than one FILE is specified. With no FILE, or when FILE is -,\n\
-read standard input.\n\
+more than one FILE is specified. A word is a non-zero-length sequence of\n\
+characters delimited by white space.\n\
+"), stdout);
+
+ emit_stdin_note ();
+
+ fputs (_("\
+\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\
- -L, --max-line-length print the length of the longest line\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 (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -135,11 +147,11 @@ read standard input.\n\
associated with the specified counters. */
static void
write_counts (uintmax_t lines,
- uintmax_t words,
- uintmax_t chars,
- uintmax_t bytes,
- uintmax_t linelength,
- const char *file)
+ uintmax_t words,
+ uintmax_t chars,
+ uintmax_t bytes,
+ uintmax_t linelength,
+ const char *file)
{
static char const format_sp_int[] = " %*s";
char const *format_int = format_sp_int + 1;
@@ -170,15 +182,16 @@ write_counts (uintmax_t lines,
printf (format_int, number_width, umaxtostr (linelength, buf));
}
if (file)
- printf (" %s", file);
+ printf (" %s", strchr (file, '\n') ? quotef (file) : file);
putchar ('\n');
}
/* Count words. FILE_X is the name of the file (or NULL for standard
input) that is open on descriptor FD. *FSTATUS is its status.
+ CURRENT_POS is the current file offset if known, negative if unknown.
Return true if successful. */
static bool
-wc (int fd, char const *file_x, struct fstatus *fstatus)
+wc (int fd, char const *file_x, struct fstatus *fstatus, off_t current_pos)
{
bool ok = true;
char buf[BUFFER_SIZE + 1];
@@ -191,7 +204,7 @@ wc (int fd, char const *file_x, struct fstatus *fstatus)
/* If in the current locale, chars are equivalent to bytes, we prefer
counting bytes, because that's easier. */
-#if HAVE_MBRTOWC && (MB_LEN_MAX > 1)
+#if MB_LEN_MAX > 1
if (MB_CUR_MAX > 1)
{
count_bytes = print_bytes;
@@ -200,207 +213,242 @@ wc (int fd, char const *file_x, struct fstatus *fstatus)
else
#endif
{
- count_bytes = print_bytes | print_chars;
+ count_bytes = print_bytes || print_chars;
count_chars = false;
}
- count_complicated = print_words | print_linelength;
+ count_complicated = print_words || print_linelength;
+
+ /* Advise the kernel of our access pattern only if we will read(). */
+ if (!count_bytes || count_chars || print_lines || count_complicated)
+ fdadvise (fd, 0, 0, FADVISE_SEQUENTIAL);
/* When counting only bytes, save some line- and word-counting
- overhead. If FD is a `regular' Unix file, using lseek is enough
- to get its `size' in bytes. Otherwise, read blocks of BUFFER_SIZE
- bytes at a time until EOF. Note that the `size' (number of bytes)
+ overhead. If FD is a 'regular' Unix file, using lseek is enough
+ to get its 'size' in bytes. Otherwise, read blocks of BUFFER_SIZE
+ bytes at a time until EOF. Note that the 'size' (number of bytes)
that wc reports is smaller than stats.st_size when the file is not
positioned at its beginning. That's why the lseek calls below are
necessary. For example the command
- `(dd ibs=99k skip=1 count=0; ./wc -c) < /etc/group'
- should make wc report `0' bytes. */
+ '(dd ibs=99k skip=1 count=0; ./wc -c) < /etc/group'
+ should make wc report '0' bytes. */
- if (count_bytes & !count_chars & !print_lines & !count_complicated)
+ if (count_bytes && !count_chars && !print_lines && !count_complicated)
{
- off_t current_pos, end_pos;
-
if (0 < fstatus->failed)
- fstatus->failed = fstat (fd, &fstatus->st);
-
- if (! fstatus->failed && S_ISREG (fstatus->st.st_mode)
- && (current_pos = lseek (fd, (off_t) 0, SEEK_CUR)) != -1
- && (end_pos = lseek (fd, (off_t) 0, SEEK_END)) != -1)
- {
- /* Be careful here. The current position may actually be
- beyond the end of the file. As in the example above. */
- bytes = end_pos < current_pos ? 0 : end_pos - current_pos;
- }
- else
- {
- while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
- {
- if (bytes_read == SAFE_READ_ERROR)
- {
- error (0, errno, "%s", file);
- ok = false;
- break;
- }
- bytes += bytes_read;
- }
- }
+ fstatus->failed = fstat (fd, &fstatus->st);
+
+ /* For sized files, seek to one st_blksize before EOF rather than to EOF.
+ This works better for files in proc-like file systems where
+ the size is only approximate. */
+ if (! fstatus->failed && usable_st_size (&fstatus->st)
+ && 0 <= fstatus->st.st_size)
+ {
+ size_t end_pos = fstatus->st.st_size;
+ off_t hi_pos = end_pos - end_pos % (ST_BLKSIZE (fstatus->st) + 1);
+ if (current_pos < 0)
+ current_pos = lseek (fd, 0, SEEK_CUR);
+ if (0 <= current_pos && current_pos < hi_pos
+ && 0 <= lseek (fd, hi_pos, SEEK_CUR))
+ bytes = hi_pos - current_pos;
+ }
+
+ fdadvise (fd, 0, 0, FADVISE_SEQUENTIAL);
+ while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
+ {
+ if (bytes_read == SAFE_READ_ERROR)
+ {
+ error (0, errno, "%s", quotef (file));
+ ok = false;
+ break;
+ }
+ bytes += bytes_read;
+ }
}
- else if (!count_chars & !count_complicated)
+ else if (!count_chars && !count_complicated)
{
/* Use a separate loop when counting only lines or lines and bytes --
- but not chars or words. */
+ but not chars or words. */
+ bool long_lines = false;
while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
- {
- char *p = buf;
-
- if (bytes_read == SAFE_READ_ERROR)
- {
- error (0, errno, "%s", file);
- ok = false;
- break;
- }
-
- while ((p = memchr (p, '\n', (buf + bytes_read) - p)))
- {
- ++p;
- ++lines;
- }
- bytes += bytes_read;
- }
+ {
+ if (bytes_read == SAFE_READ_ERROR)
+ {
+ error (0, errno, "%s", quotef (file));
+ ok = false;
+ break;
+ }
+
+ bytes += bytes_read;
+
+ char *p = buf;
+ char *end = p + bytes_read;
+ uintmax_t plines = lines;
+
+ if (! long_lines)
+ {
+ /* Avoid function call overhead for shorter lines. */
+ while (p != end)
+ lines += *p++ == '\n';
+ }
+ else
+ {
+ /* memchr is more efficient with longer lines. */
+ while ((p = memchr (p, '\n', end - p)))
+ {
+ ++p;
+ ++lines;
+ }
+ }
+
+ /* If the average line length in the block is >= 15, then use
+ memchr for the next block, where system specific optimizations
+ may outweigh function call overhead.
+ FIXME: This line length was determined in 2015, on both
+ x86_64 and ppc64, but it's worth re-evaluating in future with
+ newer compilers, CPUs, or memchr() implementations etc. */
+ if (lines - plines <= bytes_read / 15)
+ long_lines = true;
+ else
+ long_lines = false;
+ }
}
-#if HAVE_MBRTOWC && (MB_LEN_MAX > 1)
+#if MB_LEN_MAX > 1
# define SUPPORT_OLD_MBRTOWC 1
else if (MB_CUR_MAX > 1)
{
bool in_word = false;
uintmax_t linepos = 0;
mbstate_t state = { 0, };
- uintmax_t last_error_line = 0;
- int last_error_errno = 0;
+ bool in_shift = false;
# if SUPPORT_OLD_MBRTOWC
/* Back-up the state before each multibyte character conversion and
- move the last incomplete character of the buffer to the front
- of the buffer. This is needed because we don't know whether
- the `mbrtowc' function updates the state when it returns -2, -
- this is the ISO C 99 and glibc-2.2 behaviour - or not - amended
- ANSI C, glibc-2.1 and Solaris 5.7 behaviour. We don't have an
- autoconf test for this, yet. */
+ move the last incomplete character of the buffer to the front
+ of the buffer. This is needed because we don't know whether
+ the 'mbrtowc' function updates the state when it returns -2, --
+ this is the ISO C 99 and glibc-2.2 behaviour - or not - amended
+ ANSI C, glibc-2.1 and Solaris 5.7 behaviour. We don't have an
+ autoconf test for this, yet. */
size_t prev = 0; /* number of bytes carried over from previous round */
# else
const size_t prev = 0;
# endif
while ((bytes_read = safe_read (fd, buf + prev, BUFFER_SIZE - prev)) > 0)
- {
- const char *p;
+ {
+ const char *p;
# if SUPPORT_OLD_MBRTOWC
- mbstate_t backup_state;
+ mbstate_t backup_state;
# endif
- if (bytes_read == SAFE_READ_ERROR)
- {
- error (0, errno, "%s", file);
- ok = false;
- break;
- }
-
- bytes += bytes_read;
- p = buf;
- bytes_read += prev;
- do
- {
- wchar_t wide_char;
- size_t n;
-
+ if (bytes_read == SAFE_READ_ERROR)
+ {
+ error (0, errno, "%s", quotef (file));
+ ok = false;
+ break;
+ }
+
+ bytes += bytes_read;
+ p = buf;
+ bytes_read += prev;
+ do
+ {
+ wchar_t wide_char;
+ size_t n;
+
+ if (!in_shift && is_basic (*p))
+ {
+ /* Handle most ASCII characters quickly, without calling
+ mbrtowc(). */
+ n = 1;
+ wide_char = *p;
+ }
+ else
+ {
+ in_shift = true;
# if SUPPORT_OLD_MBRTOWC
- backup_state = state;
+ backup_state = state;
# endif
- n = mbrtowc (&wide_char, p, bytes_read, &state);
- if (n == (size_t) -2)
- {
+ n = mbrtowc (&wide_char, p, bytes_read, &state);
+ if (n == (size_t) -2)
+ {
# if SUPPORT_OLD_MBRTOWC
- state = backup_state;
+ state = backup_state;
# endif
- break;
- }
- if (n == (size_t) -1)
- {
- /* Signal repeated errors only once per line. */
- if (!(lines + 1 == last_error_line
- && errno == last_error_errno))
- {
- char line_number_buf[INT_BUFSIZE_BOUND (uintmax_t)];
- last_error_line = lines + 1;
- last_error_errno = errno;
- error (0, errno, "%s:%s", file,
- umaxtostr (last_error_line, line_number_buf));
- ok = false;
- }
- p++;
- bytes_read--;
- }
- else
- {
- if (n == 0)
- {
- wide_char = 0;
- n = 1;
- }
- p += n;
- bytes_read -= n;
- chars++;
- switch (wide_char)
- {
- case '\n':
- lines++;
- /* Fall through. */
- case '\r':
- case '\f':
- if (linepos > linelength)
- linelength = linepos;
- linepos = 0;
- goto mb_word_separator;
- case '\t':
- linepos += 8 - (linepos % 8);
- goto mb_word_separator;
- case ' ':
- linepos++;
- /* Fall through. */
- case '\v':
- mb_word_separator:
- words += in_word;
- in_word = false;
- break;
- default:
- if (iswprint (wide_char))
- {
- int width = wcwidth (wide_char);
- if (width > 0)
- linepos += width;
- if (iswspace (wide_char))
- goto mb_word_separator;
- in_word = true;
- }
- break;
- }
- }
- }
- while (bytes_read > 0);
+ break;
+ }
+ if (n == (size_t) -1)
+ {
+ /* Remember that we read a byte, but don't complain
+ about the error. Because of the decoding error,
+ this is a considered to be byte but not a
+ character (that is, chars is not incremented). */
+ p++;
+ bytes_read--;
+ continue;
+ }
+ if (mbsinit (&state))
+ in_shift = false;
+ if (n == 0)
+ {
+ wide_char = 0;
+ n = 1;
+ }
+ }
+ p += n;
+ bytes_read -= n;
+ chars++;
+ switch (wide_char)
+ {
+ case '\n':
+ lines++;
+ /* Fall through. */
+ case '\r':
+ case '\f':
+ if (linepos > linelength)
+ linelength = linepos;
+ linepos = 0;
+ goto mb_word_separator;
+ case '\t':
+ linepos += 8 - (linepos % 8);
+ goto mb_word_separator;
+ case ' ':
+ linepos++;
+ /* Fall through. */
+ case '\v':
+ mb_word_separator:
+ words += in_word;
+ in_word = false;
+ break;
+ default:
+ if (iswprint (wide_char))
+ {
+ int width = wcwidth (wide_char);
+ if (width > 0)
+ linepos += width;
+ if (iswspace (wide_char))
+ goto mb_word_separator;
+ in_word = true;
+ }
+ break;
+ }
+ }
+ while (bytes_read > 0);
# if SUPPORT_OLD_MBRTOWC
- if (bytes_read > 0)
- {
- if (bytes_read == BUFFER_SIZE)
- {
- /* Encountered a very long redundant shift sequence. */
- p++;
- bytes_read--;
- }
- memmove (buf, p, bytes_read);
- }
- prev = bytes_read;
+ if (bytes_read > 0)
+ {
+ if (bytes_read == BUFFER_SIZE)
+ {
+ /* Encountered a very long redundant shift sequence. */
+ p++;
+ bytes_read--;
+ }
+ memmove (buf, p, bytes_read);
+ }
+ prev = bytes_read;
# endif
- }
+ }
if (linepos > linelength)
- linelength = linepos;
+ linelength = linepos;
words += in_word;
}
#endif
@@ -410,55 +458,55 @@ wc (int fd, char const *file_x, struct fstatus *fstatus)
uintmax_t linepos = 0;
while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
- {
- const char *p = buf;
- if (bytes_read == SAFE_READ_ERROR)
- {
- error (0, errno, "%s", file);
- ok = false;
- break;
- }
-
- bytes += bytes_read;
- do
- {
- switch (*p++)
- {
- case '\n':
- lines++;
- /* Fall through. */
- case '\r':
- case '\f':
- if (linepos > linelength)
- linelength = linepos;
- linepos = 0;
- goto word_separator;
- case '\t':
- linepos += 8 - (linepos % 8);
- goto word_separator;
- case ' ':
- linepos++;
- /* Fall through. */
- case '\v':
- word_separator:
- words += in_word;
- in_word = false;
- break;
- default:
- if (isprint (to_uchar (p[-1])))
- {
- linepos++;
- if (isspace (to_uchar (p[-1])))
- goto word_separator;
- in_word = true;
- }
- break;
- }
- }
- while (--bytes_read);
- }
+ {
+ const char *p = buf;
+ if (bytes_read == SAFE_READ_ERROR)
+ {
+ error (0, errno, "%s", quotef (file));
+ ok = false;
+ break;
+ }
+
+ bytes += bytes_read;
+ do
+ {
+ switch (*p++)
+ {
+ case '\n':
+ lines++;
+ /* Fall through. */
+ case '\r':
+ case '\f':
+ if (linepos > linelength)
+ linelength = linepos;
+ linepos = 0;
+ goto word_separator;
+ case '\t':
+ linepos += 8 - (linepos % 8);
+ goto word_separator;
+ case ' ':
+ linepos++;
+ /* Fall through. */
+ case '\v':
+ word_separator:
+ words += in_word;
+ in_word = false;
+ break;
+ default:
+ if (isprint (to_uchar (p[-1])))
+ {
+ linepos++;
+ if (isspace (to_uchar (p[-1])))
+ goto word_separator;
+ in_word = true;
+ }
+ break;
+ }
+ }
+ while (--bytes_read);
+ }
if (linepos > linelength)
- linelength = linepos;
+ linelength = linepos;
words += in_word;
}
@@ -483,53 +531,55 @@ wc_file (char const *file, struct fstatus *fstatus)
{
have_read_stdin = true;
if (O_BINARY && ! isatty (STDIN_FILENO))
- freopen (NULL, "rb", stdin);
- return wc (STDIN_FILENO, file, fstatus);
+ xfreopen (NULL, "rb", stdin);
+ return wc (STDIN_FILENO, file, fstatus, -1);
}
else
{
int fd = open (file, O_RDONLY | O_BINARY);
if (fd == -1)
- {
- error (0, errno, "%s", file);
- return false;
- }
+ {
+ error (0, errno, "%s", quotef (file));
+ return false;
+ }
else
- {
- bool ok = wc (fd, file, fstatus);
- if (close (fd) != 0)
- {
- error (0, errno, "%s", file);
- return false;
- }
- return ok;
- }
+ {
+ bool ok = wc (fd, file, fstatus, 0);
+ if (close (fd) != 0)
+ {
+ error (0, errno, "%s", quotef (file));
+ return false;
+ }
+ return ok;
+ }
}
}
/* Return the file status for the NFILES files addressed by FILE.
Optimize the case where only one number is printed, for just one
file; in that case we can use a print width of 1, so we don't need
- to stat the file. */
+ to stat the file. Handle the case of (nfiles == 0) in the same way;
+ that happens when we don't know how long the list of file names will be. */
static struct fstatus *
-get_input_fstatus (int nfiles, char * const *file)
+get_input_fstatus (size_t nfiles, char *const *file)
{
- struct fstatus *fstatus = xnmalloc (nfiles, sizeof *fstatus);
+ struct fstatus *fstatus = xnmalloc (nfiles ? nfiles : 1, sizeof *fstatus);
- if (nfiles == 1
- && ((print_lines + print_words + print_chars
- + print_bytes + print_linelength)
- == 1))
+ if (nfiles == 0
+ || (nfiles == 1
+ && ((print_lines + print_words + print_chars
+ + print_bytes + print_linelength)
+ == 1)))
fstatus[0].failed = 1;
else
{
- int i;
+ size_t i;
for (i = 0; i < nfiles; i++)
- fstatus[i].failed = (! file[i] || STREQ (file[i], "-")
- ? fstat (STDIN_FILENO, &fstatus[i].st)
- : stat (file[i], &fstatus[i].st));
+ fstatus[i].failed = (! file[i] || STREQ (file[i], "-")
+ ? fstat (STDIN_FILENO, &fstatus[i].st)
+ : stat (file[i], &fstatus[i].st));
}
return fstatus;
@@ -539,8 +589,8 @@ get_input_fstatus (int nfiles, char * const *file)
recorded in FSTATUS. Optimize the same special case that
get_input_fstatus optimizes. */
-static int
-compute_number_width (int nfiles, struct fstatus const *fstatus)
+static int _GL_ATTRIBUTE_PURE
+compute_number_width (size_t nfiles, struct fstatus const *fstatus)
{
int width = 1;
@@ -548,21 +598,21 @@ compute_number_width (int nfiles, struct fstatus const *fstatus)
{
int minimum_width = 1;
uintmax_t regular_total = 0;
- int i;
+ size_t i;
for (i = 0; i < nfiles; i++)
- if (! fstatus[i].failed)
- {
- if (S_ISREG (fstatus[i].st.st_mode))
- regular_total += fstatus[i].st.st_size;
- else
- minimum_width = 7;
- }
+ if (! fstatus[i].failed)
+ {
+ if (S_ISREG (fstatus[i].st.st_mode))
+ regular_total += fstatus[i].st.st_size;
+ else
+ minimum_width = 7;
+ }
for (; 10 <= regular_total; regular_total /= 10)
- width++;
+ width++;
if (width < minimum_width)
- width = minimum_width;
+ width = minimum_width;
}
return width;
@@ -572,23 +622,26 @@ compute_number_width (int nfiles, struct fstatus const *fstatus)
int
main (int argc, char **argv)
{
- int i;
bool ok;
int optc;
- int nfiles;
+ size_t nfiles;
char **files;
char *files_from = NULL;
struct fstatus *fstatus;
struct Tokens tok;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ 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);
+
print_lines = print_words = print_chars = print_bytes = false;
print_linelength = false;
total_lines = total_words = total_chars = total_bytes = max_line_length = 0;
@@ -597,108 +650,184 @@ main (int argc, char **argv)
switch (optc)
{
case 'c':
- print_bytes = true;
- break;
+ print_bytes = true;
+ break;
case 'm':
- print_chars = true;
- break;
+ print_chars = true;
+ break;
case 'l':
- print_lines = true;
- break;
+ print_lines = true;
+ break;
case 'w':
- print_words = true;
- break;
+ print_words = true;
+ break;
case 'L':
- print_linelength = true;
- break;
+ print_linelength = true;
+ break;
case FILES0_FROM_OPTION:
- files_from = optarg;
- break;
+ files_from = optarg;
+ break;
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
- usage (EXIT_FAILURE);
+ usage (EXIT_FAILURE);
}
- if (! (print_lines | print_words | print_chars | print_bytes
- | print_linelength))
+ if (! (print_lines || print_words || print_chars || print_bytes
+ || print_linelength))
print_lines = print_words = print_bytes = true;
+ bool read_tokens = false;
+ struct argv_iterator *ai;
if (files_from)
{
FILE *stream;
/* When using --files0-from=F, you may not specify any files
- on the command-line. */
+ on the command-line. */
if (optind < argc)
- {
- error (0, 0, _("extra operand %s"), quote (argv[optind]));
- fprintf (stderr, "%s\n",
- _("File operands cannot be combined with --files0-from."));
- usage (EXIT_FAILURE);
- }
+ {
+ error (0, 0, _("extra operand %s"), quoteaf (argv[optind]));
+ fprintf (stderr, "%s\n",
+ _("file operands cannot be combined with --files0-from"));
+ usage (EXIT_FAILURE);
+ }
if (STREQ (files_from, "-"))
- stream = stdin;
+ stream = stdin;
else
- {
- stream = fopen (files_from, "r");
- if (stream == NULL)
- error (EXIT_FAILURE, errno, _("cannot open %s for reading"),
- quote (files_from));
- }
-
- readtokens0_init (&tok);
-
- if (! readtokens0 (stream, &tok) || fclose (stream) != 0)
- error (EXIT_FAILURE, 0, _("cannot read file names from %s"),
- quote (files_from));
-
- files = tok.tok;
- nfiles = tok.n_tok;
+ {
+ stream = fopen (files_from, "r");
+ if (stream == NULL)
+ error (EXIT_FAILURE, errno, _("cannot open %s for reading"),
+ quoteaf (files_from));
+ }
+
+ /* Read the file list into RAM if we can detect its size and that
+ size is reasonable. Otherwise, we'll read a name at a time. */
+ struct stat st;
+ if (fstat (fileno (stream), &st) == 0
+ && S_ISREG (st.st_mode)
+ && st.st_size <= MIN (10 * 1024 * 1024, physmem_available () / 2))
+ {
+ read_tokens = true;
+ readtokens0_init (&tok);
+ if (! readtokens0 (stream, &tok) || fclose (stream) != 0)
+ error (EXIT_FAILURE, 0, _("cannot read file names from %s"),
+ quoteaf (files_from));
+ files = tok.tok;
+ nfiles = tok.n_tok;
+ ai = argv_iter_init_argv (files);
+ }
+ else
+ {
+ files = NULL;
+ nfiles = 0;
+ ai = argv_iter_init_stream (stream);
+ }
}
else
{
- static char *stdin_only[2];
+ static char *stdin_only[] = { NULL };
files = (optind < argc ? argv + optind : stdin_only);
nfiles = (optind < argc ? argc - optind : 1);
- stdin_only[0] = NULL;
+ ai = argv_iter_init_argv (files);
}
+ if (!ai)
+ xalloc_die ();
+
fstatus = get_input_fstatus (nfiles, files);
number_width = compute_number_width (nfiles, fstatus);
+ int i;
ok = true;
- for (i = 0; i < nfiles; i++)
+ for (i = 0; /* */; i++)
{
- if (files_from && STREQ (files_from, "-") && STREQ (files[i], "-"))
- {
- ok = false;
- error (0, 0,
- _("when reading file names from stdin, "
- "no file name of %s allowed"),
- quote ("-"));
- continue;
- }
- ok &= wc_file (files[i], &fstatus[i]);
+ bool skip_file = false;
+ enum argv_iter_err ai_err;
+ char *file_name = argv_iter (ai, &ai_err);
+ if (!file_name)
+ {
+ switch (ai_err)
+ {
+ case AI_ERR_EOF:
+ goto argv_iter_done;
+ case AI_ERR_READ:
+ error (0, errno, _("%s: read error"),
+ quotef (files_from));
+ ok = false;
+ goto argv_iter_done;
+ case AI_ERR_MEM:
+ xalloc_die ();
+ default:
+ assert (!"unexpected error code from argv_iter");
+ }
+ }
+ if (files_from && STREQ (files_from, "-") && STREQ (file_name, "-"))
+ {
+ /* Give a better diagnostic in an unusual case:
+ printf - | wc --files0-from=- */
+ error (0, 0, _("when reading file names from stdin, "
+ "no file name of %s allowed"),
+ quoteaf (file_name));
+ skip_file = true;
+ }
+
+ if (!file_name[0])
+ {
+ /* Diagnose a zero-length file name. When it's one
+ 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 == NULL)
+ error (0, 0, "%s", _("invalid zero-length file name"));
+ else
+ {
+ /* Using the standard 'filename:line-number:' prefix here is
+ not totally appropriate, since NUL is the separator, not NL,
+ but it might be better than nothing. */
+ unsigned long int file_number = argv_iter_n_args (ai);
+ error (0, 0, "%s:%lu: %s", quotef (files_from),
+ file_number, _("invalid zero-length file name"));
+ }
+ skip_file = true;
+ }
+
+ if (skip_file)
+ ok = false;
+ else
+ ok &= wc_file (file_name, &fstatus[nfiles ? i : 0]);
}
+ argv_iter_done:
+
+ /* No arguments on the command line is fine. That means read from stdin.
+ 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 (NULL, &fstatus[0]);
- if (1 < nfiles)
+ if (read_tokens)
+ readtokens0_free (&tok);
+
+ if (1 < argv_iter_n_args (ai))
write_counts (total_lines, total_words, total_chars, total_bytes,
- max_line_length, _("total"));
+ max_line_length, _("total"));
+
+ argv_iter_free (ai);
free (fstatus);
if (have_read_stdin && close (STDIN_FILENO) != 0)
error (EXIT_FAILURE, errno, "-");
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/src/wheel-gen.pl b/src/wheel-gen.pl
deleted file mode 100755
index a225830..0000000
--- a/src/wheel-gen.pl
+++ /dev/null
@@ -1,115 +0,0 @@
-#!/usr/bin/perl -w
-# Generate the spokes of a wheel, for wheel factorization.
-
-# Copyright (C) 2001, 2005 Free Software Foundation, Inc.
-
-# This 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 2, 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, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
- if 0;
-
-use strict;
-(my $ME = $0) =~ s|.*/||;
-
-# A global destructor to close standard output with error checking.
-sub END
-{
- defined fileno STDOUT
- or return;
- close STDOUT
- and return;
- warn "$ME: closing standard output: $!\n";
- $? ||= 1;
-}
-
-sub is_prime ($)
-{
- my ($n) = @_;
- use integer;
-
- $n == 2
- and return 1;
-
- my $d = 2;
- my $w = 1;
- while (1)
- {
- my $q = $n / $d;
- $n == $q * $d
- and return 0;
- $d += $w;
- $q < $d
- and last;
- $w = 2;
- }
- return 1;
-}
-
-{
- @ARGV == 1
- or die "$ME: missing argument\n";
-
- my $wheel_size = $ARGV[0];
-
- my @primes = (2);
- my $product = $primes[0];
- my $n_primes = 1;
- for (my $i = 3; ; $i += 2)
- {
- if (is_prime $i)
- {
- push @primes, $i;
- $product *= $i;
- ++$n_primes == $wheel_size
- and last;
- }
- }
-
- my $ws_m1 = $wheel_size - 1;
- print <<EOF;
-/* The first $ws_m1 elements correspond to the incremental offsets of the
- first $wheel_size primes (@primes). The $wheel_size(th) element is the
- difference between that last prime and the next largest integer
- that is not a multiple of those primes. The remaining numbers
- define the wheel. For more information, see
- http://www.utm.edu/research/primes/glossary/WheelFactorization.html. */
-EOF
-
- my @increments;
- my $prev = 2;
- for (my $i = 3; ; $i += 2)
- {
- my $rel_prime = 1;
- foreach my $divisor (@primes)
- {
- $i != $divisor && $i % $divisor == 0
- and $rel_prime = 0;
- }
-
- if ($rel_prime)
- {
- #warn $i, ' ', $i - $prev, "\n";
- push @increments, $i - $prev;
- $prev = $i;
-
- $product + 1 < $i
- and last;
- }
- }
-
- print join (",\n", @increments), "\n";
-
- exit 0;
-}
diff --git a/src/wheel-size.h b/src/wheel-size.h
deleted file mode 100644
index 1f2d609..0000000
--- a/src/wheel-size.h
+++ /dev/null
@@ -1 +0,0 @@
-#define WHEEL_SIZE 5
diff --git a/src/wheel.h b/src/wheel.h
deleted file mode 100644
index 1c04d59..0000000
--- a/src/wheel.h
+++ /dev/null
@@ -1,491 +0,0 @@
-/* The first 4 elements correspond to the incremental offsets of the
- first 5 primes (2 3 5 7 11). The 5(th) element is the
- difference between that last prime and the next largest integer
- that is not a multiple of those primes. The remaining numbers
- define the wheel. For more information, see
- http://www.utm.edu/research/primes/glossary/WheelFactorization.html. */
-1,
-2,
-2,
-4,
-2,
-4,
-2,
-4,
-6,
-2,
-6,
-4,
-2,
-4,
-6,
-6,
-2,
-6,
-4,
-2,
-6,
-4,
-6,
-8,
-4,
-2,
-4,
-2,
-4,
-14,
-4,
-6,
-2,
-10,
-2,
-6,
-6,
-4,
-2,
-4,
-6,
-2,
-10,
-2,
-4,
-2,
-12,
-10,
-2,
-4,
-2,
-4,
-6,
-2,
-6,
-4,
-6,
-6,
-6,
-2,
-6,
-4,
-2,
-6,
-4,
-6,
-8,
-4,
-2,
-4,
-6,
-8,
-6,
-10,
-2,
-4,
-6,
-2,
-6,
-6,
-4,
-2,
-4,
-6,
-2,
-6,
-4,
-2,
-6,
-10,
-2,
-10,
-2,
-4,
-2,
-4,
-6,
-8,
-4,
-2,
-4,
-12,
-2,
-6,
-4,
-2,
-6,
-4,
-6,
-12,
-2,
-4,
-2,
-4,
-8,
-6,
-4,
-6,
-2,
-4,
-6,
-2,
-6,
-10,
-2,
-4,
-6,
-2,
-6,
-4,
-2,
-4,
-2,
-10,
-2,
-10,
-2,
-4,
-6,
-6,
-2,
-6,
-6,
-4,
-6,
-6,
-2,
-6,
-4,
-2,
-6,
-4,
-6,
-8,
-4,
-2,
-6,
-4,
-8,
-6,
-4,
-6,
-2,
-4,
-6,
-8,
-6,
-4,
-2,
-10,
-2,
-6,
-4,
-2,
-4,
-2,
-10,
-2,
-10,
-2,
-4,
-2,
-4,
-8,
-6,
-4,
-2,
-4,
-6,
-6,
-2,
-6,
-4,
-8,
-4,
-6,
-8,
-4,
-2,
-4,
-2,
-4,
-8,
-6,
-4,
-6,
-6,
-6,
-2,
-6,
-6,
-4,
-2,
-4,
-6,
-2,
-6,
-4,
-2,
-4,
-2,
-10,
-2,
-10,
-2,
-6,
-4,
-6,
-2,
-6,
-4,
-2,
-4,
-6,
-6,
-8,
-4,
-2,
-6,
-10,
-8,
-4,
-2,
-4,
-2,
-4,
-8,
-10,
-6,
-2,
-4,
-8,
-6,
-6,
-4,
-2,
-4,
-6,
-2,
-6,
-4,
-6,
-2,
-10,
-2,
-10,
-2,
-4,
-2,
-4,
-6,
-2,
-6,
-4,
-2,
-4,
-6,
-6,
-2,
-6,
-6,
-6,
-4,
-6,
-8,
-4,
-2,
-4,
-2,
-4,
-8,
-6,
-4,
-8,
-4,
-6,
-2,
-6,
-6,
-4,
-2,
-4,
-6,
-8,
-4,
-2,
-4,
-2,
-10,
-2,
-10,
-2,
-4,
-2,
-4,
-6,
-2,
-10,
-2,
-4,
-6,
-8,
-6,
-4,
-2,
-6,
-4,
-6,
-8,
-4,
-6,
-2,
-4,
-8,
-6,
-4,
-6,
-2,
-4,
-6,
-2,
-6,
-6,
-4,
-6,
-6,
-2,
-6,
-6,
-4,
-2,
-10,
-2,
-10,
-2,
-4,
-2,
-4,
-6,
-2,
-6,
-4,
-2,
-10,
-6,
-2,
-6,
-4,
-2,
-6,
-4,
-6,
-8,
-4,
-2,
-4,
-2,
-12,
-6,
-4,
-6,
-2,
-4,
-6,
-2,
-12,
-4,
-2,
-4,
-8,
-6,
-4,
-2,
-4,
-2,
-10,
-2,
-10,
-6,
-2,
-4,
-6,
-2,
-6,
-4,
-2,
-4,
-6,
-6,
-2,
-6,
-4,
-2,
-10,
-6,
-8,
-6,
-4,
-2,
-4,
-8,
-6,
-4,
-6,
-2,
-4,
-6,
-2,
-6,
-6,
-6,
-4,
-6,
-2,
-6,
-4,
-2,
-4,
-2,
-10,
-12,
-2,
-4,
-2,
-10,
-2,
-6,
-4,
-2,
-4,
-6,
-6,
-2,
-10,
-2,
-6,
-4,
-14,
-4,
-2,
-4,
-2,
-4,
-8,
-6,
-4,
-6,
-2,
-4,
-6,
-2,
-6,
-6,
-4,
-2,
-4,
-6,
-2,
-6,
-4,
-2,
-4,
-12,
-2,
-12
diff --git a/src/who.c b/src/who.c
index db3af6e..c6fc4dc 100644
--- a/src/who.c
+++ b/src/who.c
@@ -1,10 +1,10 @@
/* GNU's who.
- Copyright (C) 1992-2006 Free Software Foundation, Inc.
+ Copyright (C) 1992-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Written by jla; revised by djm; revised again by mstone */
@@ -31,22 +30,24 @@
#include <sys/types.h>
#include "system.h"
+#include "c-ctype.h"
#include "canon-host.h"
#include "readutmp.h"
#include "error.h"
#include "hard-locale.h"
-#include "inttostr.h"
#include "quote.h"
-#include "vasprintf.h"
-/* The official name of this program (e.g., no `g' prefix). */
+#ifdef TTY_GROUP_NAME
+# include <grp.h>
+#endif
+
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "who"
-#define AUTHORS "Joseph Arceneaux", "David MacKenzie", "Michael Stone"
-
-#ifndef MAXHOSTNAMELEN
-# define MAXHOSTNAMELEN 64
-#endif
+#define AUTHORS \
+ proper_name ("Joseph Arceneaux"), \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Michael Stone")
#ifdef RUN_LVL
# define UT_TYPE_RUN_LVL(U) UT_TYPE_EQ (U, RUN_LVL)
@@ -95,17 +96,14 @@
# define UT_ID(U) "??"
#endif
-char *ttyname ();
-
-/* The name this program was run with. */
-char *program_name;
+char *ttyname (int);
/* If true, attempt to canonicalize hostnames via a DNS lookup. */
static bool do_lookup;
/* If true, display only a list of usernames and count of
the users logged on.
- Ignored for `who am i'. */
+ Ignored for 'who am i'. */
static bool short_list;
/* If true, display only name, line, and time fields. */
@@ -119,8 +117,8 @@ static bool include_idle;
/* If true, display a line at the top describing each field. */
static bool include_heading;
-/* If true, display a `+' for each user if mesg y, a `-' if mesg n,
- or a `?' if their tty cannot be statted. */
+/* If true, display a '+' for each user if mesg y, a '-' if mesg n,
+ or a '?' if their tty cannot be statted. */
static bool include_mesg;
/* If true, display process termination & exit status. */
@@ -161,13 +159,13 @@ enum
LOOKUP_OPTION = CHAR_MAX + 1
};
-static struct option const longopts[] = {
+static struct option const longopts[] =
+{
{"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'},
- {"idle", no_argument, NULL, 'i'}, /* FIXME: deprecated: remove in late 2006 */
{"login", no_argument, NULL, 'l'},
{"lookup", no_argument, NULL, LOOKUP_OPTION},
{"message", no_argument, NULL, 'T'},
@@ -198,15 +196,15 @@ idle_string (time_t when, time_t boottime)
{
int seconds_idle = now - when;
if (seconds_idle < 60)
- return " . ";
+ return " . ";
else
- {
- static char idle_hhmm[IDLESTR_LEN];
- sprintf (idle_hhmm, "%02d:%02d",
- seconds_idle / (60 * 60),
- (seconds_idle % (60 * 60)) / 60);
- return idle_hhmm;
- }
+ {
+ static char idle_hhmm[IDLESTR_LEN];
+ sprintf (idle_hhmm, "%02d:%02d",
+ seconds_idle / (60 * 60),
+ (seconds_idle % (60 * 60)) / 60);
+ return idle_hhmm;
+ }
}
return _(" old ");
@@ -220,10 +218,10 @@ time_string (const STRUCT_UTMP *utmp_ent)
/* Don't take the address of UT_TIME_MEMBER directly.
Ulrich Drepper wrote:
- ``... GNU libc (and perhaps other libcs as well) have extended
+ "... GNU libc (and perhaps other libcs as well) have extended
utmp file formats which do not use a simple time_t ut_time field.
In glibc, ut_time is a macro which selects for backward compatibility
- the tv_sec member of a struct timeval value.'' */
+ the tv_sec member of a struct timeval value." */
time_t t = UT_TIME_MEMBER (utmp_ent);
struct tm *tmp = localtime (&t);
@@ -233,7 +231,7 @@ time_string (const STRUCT_UTMP *utmp_ent)
return buf;
}
else
- return TYPE_SIGNED (time_t) ? imaxtostr (t, buf) : umaxtostr (t, buf);
+ return timetostr (t, buf);
}
/* Print formatted output line. Uses mostly arbitrary field sizes, probably
@@ -241,9 +239,9 @@ time_string (const STRUCT_UTMP *utmp_ent)
pids, etc. */
static void
print_line (int userlen, const char *user, const char state,
- int linelen, const char *line,
- const char *time_str, const char *idle, const char *pid,
- const char *comment, const char *exitstr)
+ int linelen, const char *line,
+ const char *time_str, const char *idle, const char *pid,
+ const char *comment, const char *exitstr)
{
static char mesg[3] = { ' ', 'x', '\0' };
char *buf;
@@ -271,29 +269,29 @@ print_line (int userlen, const char *user, const char state,
*x_exitstr = '\0';
err = asprintf (&buf,
- "%-8.*s"
- "%s"
- " %-12.*s"
- " %-*s"
- "%s"
- "%s"
- " %-8s"
- "%s"
- ,
- userlen, user ? user : " .",
- include_mesg ? mesg : "",
- linelen, 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
- );
+ "%-8.*s"
+ "%s"
+ " %-12.*s"
+ " %-*s"
+ "%s"
+ "%s"
+ " %-8s"
+ "%s"
+ ,
+ userlen, user ? user : " .",
+ include_mesg ? mesg : "",
+ linelen, 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 ();
@@ -310,6 +308,22 @@ print_line (int userlen, const char *user, const char state,
free (x_exitstr);
}
+/* Return true if a terminal device given as PSTAT allows other users
+ to send messages to; false otherwise */
+static bool
+is_tty_writable (struct stat const *pstat)
+{
+#ifdef TTY_GROUP_NAME
+ /* Ensure the group of the TTY device matches TTY_GROUP_NAME, more info at
+ https://bugzilla.redhat.com/454261 */
+ struct group *ttygr = getgrnam (TTY_GROUP_NAME);
+ if (!ttygr || (pstat->st_gid != ttygr->gr_gid))
+ return false;
+#endif
+
+ return pstat->st_mode & S_IWGRP;
+}
+
/* Send properly parsed USER_PROCESS info to print_line. The most
recent boot time is BOOTTIME. */
static void
@@ -328,27 +342,19 @@ print_user (const STRUCT_UTMP *utmp_ent, time_t boottime)
#define DEV_DIR_LEN (sizeof (DEV_DIR_WITH_TRAILING_SLASH) - 1)
char line[sizeof (utmp_ent->ut_line) + DEV_DIR_LEN + 1];
+ char *p = line;
PIDSTR_DECL_AND_INIT (pidstr, utmp_ent);
- /* Copy ut_line into LINE, prepending `/dev/' if ut_line is not
+ /* Copy ut_line into LINE, prepending '/dev/' if ut_line is not
already an absolute file name. Some systems may put the full,
absolute file name in ut_line. */
- if (utmp_ent->ut_line[0] == '/')
- {
- strncpy (line, utmp_ent->ut_line, sizeof (utmp_ent->ut_line));
- line[sizeof (utmp_ent->ut_line)] = '\0';
- }
- else
- {
- strcpy (line, DEV_DIR_WITH_TRAILING_SLASH);
- strncpy (line + DEV_DIR_LEN, utmp_ent->ut_line,
- sizeof (utmp_ent->ut_line));
- line[DEV_DIR_LEN + sizeof (utmp_ent->ut_line)] = '\0';
- }
+ if ( ! IS_ABSOLUTE_FILE_NAME (utmp_ent->ut_line))
+ p = stpcpy (p, DEV_DIR_WITH_TRAILING_SLASH);
+ stzncpy (p, utmp_ent->ut_line, sizeof (utmp_ent->ut_line));
if (stat (line, &stats) == 0)
{
- mesg = (stats.st_mode & S_IWGRP) ? '+' : '-';
+ mesg = is_tty_writable (&stats) ? '+' : '-';
last_change = stats.st_atime;
}
else
@@ -370,67 +376,69 @@ print_user (const STRUCT_UTMP *utmp_ent, time_t boottime)
char *display = NULL;
/* Copy the host name into UT_HOST, and ensure it's nul terminated. */
- strncpy (ut_host, utmp_ent->ut_host, sizeof (utmp_ent->ut_host));
- ut_host[sizeof (utmp_ent->ut_host)] = '\0';
+ stzncpy (ut_host, utmp_ent->ut_host, sizeof (utmp_ent->ut_host));
/* Look for an X display. */
display = strchr (ut_host, ':');
if (display)
- *display++ = '\0';
+ *display++ = '\0';
if (*ut_host && do_lookup)
- {
- /* See if we can canonicalize it. */
- host = canon_host (ut_host);
- }
+ {
+ /* See if we can canonicalize it. */
+ host = canon_host (ut_host);
+ }
if (! host)
- host = ut_host;
+ host = ut_host;
if (display)
- {
- if (hostlen < strlen (host) + strlen (display) + 4)
- {
- hostlen = strlen (host) + strlen (display) + 4;
- hoststr = xrealloc (hoststr, hostlen);
- }
- sprintf (hoststr, "(%s:%s)", host, display);
- }
+ {
+ if (hostlen < strlen (host) + strlen (display) + 4)
+ {
+ hostlen = strlen (host) + strlen (display) + 4;
+ free (hoststr);
+ hoststr = xmalloc (hostlen);
+ }
+ sprintf (hoststr, "(%s:%s)", host, display);
+ }
else
- {
- if (hostlen < strlen (host) + 3)
- {
- hostlen = strlen (host) + 3;
- hoststr = xrealloc (hoststr, hostlen);
- }
- sprintf (hoststr, "(%s)", host);
- }
+ {
+ if (hostlen < strlen (host) + 3)
+ {
+ hostlen = strlen (host) + 3;
+ free (hoststr);
+ hoststr = xmalloc (hostlen);
+ }
+ sprintf (hoststr, "(%s)", host);
+ }
if (host != ut_host)
- free (host);
+ free (host);
}
else
{
if (hostlen < 1)
- {
- hostlen = 1;
- hoststr = xrealloc (hoststr, hostlen);
- }
+ {
+ hostlen = 1;
+ free (hoststr);
+ hoststr = xmalloc (hostlen);
+ }
*hoststr = '\0';
}
#endif
print_line (sizeof UT_USER (utmp_ent), UT_USER (utmp_ent), mesg,
- sizeof utmp_ent->ut_line, utmp_ent->ut_line,
- time_string (utmp_ent), idlestr, pidstr,
- hoststr ? hoststr : "", "");
+ sizeof utmp_ent->ut_line, utmp_ent->ut_line,
+ time_string (utmp_ent), idlestr, pidstr,
+ hoststr ? hoststr : "", "");
}
static void
print_boottime (const STRUCT_UTMP *utmp_ent)
{
- print_line (-1, "", ' ', -1, "system boot",
- time_string (utmp_ent), "", "", "", "");
+ print_line (-1, "", ' ', -1, _("system boot"),
+ time_string (utmp_ent), "", "", "", "");
}
static char *
@@ -452,17 +460,17 @@ print_deadprocs (const STRUCT_UTMP *utmp_ent)
if (!exitstr)
exitstr = xmalloc (strlen (_("term="))
- + INT_STRLEN_BOUND (UT_EXIT_E_TERMINATION (utmp_ent)) + 1
- + strlen (_("exit="))
- + INT_STRLEN_BOUND (UT_EXIT_E_EXIT (utmp_ent))
- + 1);
+ + INT_STRLEN_BOUND (UT_EXIT_E_TERMINATION (utmp_ent)) + 1
+ + strlen (_("exit="))
+ + INT_STRLEN_BOUND (UT_EXIT_E_EXIT (utmp_ent))
+ + 1);
sprintf (exitstr, "%s%d %s%d", _("term="), UT_EXIT_E_TERMINATION (utmp_ent),
- _("exit="), UT_EXIT_E_EXIT (utmp_ent));
+ _("exit="), UT_EXIT_E_EXIT (utmp_ent));
/* FIXME: add idle time? */
print_line (-1, "", ' ', sizeof utmp_ent->ut_line, utmp_ent->ut_line,
- time_string (utmp_ent), "", pidstr, comment, exitstr);
+ time_string (utmp_ent), "", pidstr, comment, exitstr);
free (comment);
}
@@ -474,8 +482,8 @@ print_login (const STRUCT_UTMP *utmp_ent)
/* FIXME: add idle time? */
- print_line (-1, "LOGIN", ' ', sizeof utmp_ent->ut_line, utmp_ent->ut_line,
- time_string (utmp_ent), "", pidstr, comment, "");
+ print_line (-1, _("LOGIN"), ' ', sizeof utmp_ent->ut_line, utmp_ent->ut_line,
+ time_string (utmp_ent), "", pidstr, comment, "");
free (comment);
}
@@ -486,7 +494,7 @@ print_initspawn (const STRUCT_UTMP *utmp_ent)
PIDSTR_DECL_AND_INIT (pidstr, utmp_ent);
print_line (-1, "", ' ', sizeof utmp_ent->ut_line, utmp_ent->ut_line,
- time_string (utmp_ent), "", pidstr, comment, "");
+ time_string (utmp_ent), "", pidstr, comment, "");
free (comment);
}
@@ -495,7 +503,7 @@ print_clockchange (const STRUCT_UTMP *utmp_ent)
{
/* FIXME: handle NEW_TIME & OLD_TIME both */
print_line (-1, "", ' ', -1, _("clock change"),
- time_string (utmp_ent), "", "", "", "");
+ time_string (utmp_ent), "", "", "", "");
}
static void
@@ -514,7 +522,7 @@ print_runlevel (const STRUCT_UTMP *utmp_ent)
sprintf (comment, "%s%c", _("last="), (last == 'N') ? 'S' : last);
print_line (-1, "", ' ', -1, runlevline, time_string (utmp_ent),
- "", "", comment, "");
+ "", "", c_isprint (last) ? comment : "", "");
return;
}
@@ -530,16 +538,16 @@ list_entries_who (size_t n, const STRUCT_UTMP *utmp_buf)
while (n--)
{
if (IS_USER_PROCESS (utmp_buf))
- {
- char *trimmed_name;
+ {
+ char *trimmed_name;
- trimmed_name = extract_trimmed_name (utmp_buf);
+ trimmed_name = extract_trimmed_name (utmp_buf);
- printf ("%s%s", separator, trimmed_name);
- free (trimmed_name);
- separator = " ";
- entries++;
- }
+ printf ("%s%s", separator, trimmed_name);
+ free (trimmed_name);
+ separator = " ";
+ entries++;
+ }
utmp_buf++;
}
printf (_("\n# users=%lu\n"), entries);
@@ -549,7 +557,7 @@ static void
print_heading (void)
{
print_line (-1, _("NAME"), ' ', -1, _("LINE"), _("TIME"), _("IDLE"),
- _("PID"), _("COMMENT"), _("EXIT"));
+ _("PID"), _("COMMENT"), _("EXIT"));
}
/* Display UTMP_BUF, which should have N entries. */
@@ -566,38 +574,38 @@ scan_entries (size_t n, const STRUCT_UTMP *utmp_buf)
{
ttyname_b = ttyname (STDIN_FILENO);
if (!ttyname_b)
- return;
- if (strncmp (ttyname_b, DEV_DIR_WITH_TRAILING_SLASH, DEV_DIR_LEN) == 0)
- ttyname_b += DEV_DIR_LEN; /* Discard /dev/ prefix. */
+ return;
+ if (STRNCMP_LIT (ttyname_b, DEV_DIR_WITH_TRAILING_SLASH) == 0)
+ ttyname_b += DEV_DIR_LEN; /* Discard /dev/ prefix. */
}
while (n--)
{
- if (!my_line_only ||
- strncmp (ttyname_b, utmp_buf->ut_line,
- sizeof (utmp_buf->ut_line)) == 0)
- {
- if (need_users && IS_USER_PROCESS (utmp_buf))
- print_user (utmp_buf, boottime);
- else if (need_runlevel && UT_TYPE_RUN_LVL (utmp_buf))
- print_runlevel (utmp_buf);
- else if (need_boottime && UT_TYPE_BOOT_TIME (utmp_buf))
- print_boottime (utmp_buf);
- /* I've never seen one of these, so I don't know what it should
- look like :^)
- FIXME: handle OLD_TIME also, perhaps show the delta? */
- else if (need_clockchange && UT_TYPE_NEW_TIME (utmp_buf))
- print_clockchange (utmp_buf);
- else if (need_initspawn && UT_TYPE_INIT_PROCESS (utmp_buf))
- print_initspawn (utmp_buf);
- else if (need_login && UT_TYPE_LOGIN_PROCESS (utmp_buf))
- print_login (utmp_buf);
- else if (need_deadprocs && UT_TYPE_DEAD_PROCESS (utmp_buf))
- print_deadprocs (utmp_buf);
- }
+ if (!my_line_only
+ || STREQ_LEN (ttyname_b, utmp_buf->ut_line,
+ sizeof (utmp_buf->ut_line)))
+ {
+ if (need_users && IS_USER_PROCESS (utmp_buf))
+ print_user (utmp_buf, boottime);
+ else if (need_runlevel && UT_TYPE_RUN_LVL (utmp_buf))
+ print_runlevel (utmp_buf);
+ else if (need_boottime && UT_TYPE_BOOT_TIME (utmp_buf))
+ print_boottime (utmp_buf);
+ /* I've never seen one of these, so I don't know what it should
+ look like :^)
+ FIXME: handle OLD_TIME also, perhaps show the delta? */
+ else if (need_clockchange && UT_TYPE_NEW_TIME (utmp_buf))
+ print_clockchange (utmp_buf);
+ else if (need_initspawn && UT_TYPE_INIT_PROCESS (utmp_buf))
+ print_initspawn (utmp_buf);
+ else if (need_login && UT_TYPE_LOGIN_PROCESS (utmp_buf))
+ print_login (utmp_buf);
+ else if (need_deadprocs && UT_TYPE_DEAD_PROCESS (utmp_buf))
+ print_deadprocs (utmp_buf);
+ }
if (UT_TYPE_BOOT_TIME (utmp_buf))
- boottime = UT_TIME_MEMBER (utmp_buf);
+ boottime = UT_TIME_MEMBER (utmp_buf);
utmp_buf++;
}
@@ -612,7 +620,7 @@ who (const char *filename, int options)
STRUCT_UTMP *utmp_buf;
if (read_utmp (filename, &n_users, &utmp_buf, options) != 0)
- error (EXIT_FAILURE, errno, "%s", filename);
+ error (EXIT_FAILURE, errno, "%s", quotef (filename));
if (short_list)
list_entries_who (n_users, utmp_buf);
@@ -626,12 +634,14 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("Usage: %s [OPTION]... [ FILE | ARG1 ARG2 ]\n"), program_name);
fputs (_("\
+Print information about users who are currently logged in.\n\
+"), stdout);
+ fputs (_("\
\n\
-a, --all same as -b -d --login -p -r -t -T -u\n\
-b, --boot time of last system boot\n\
@@ -663,9 +673,9 @@ usage (int status)
printf (_("\
\n\
If FILE is not specified, use %s. %s as FILE is common.\n\
-If ARG1 ARG2 given, -m presumed: `am i' or `mom likes' are usual.\n\
+If ARG1 ARG2 given, -m presumed: 'am i' or 'mom likes' are usual.\n\
"), UTMP_FILE, WTMP_FILE);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -677,109 +687,104 @@ main (int argc, char **argv)
bool assumptions = true;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- while ((optc = getopt_long (argc, argv, "abdilmpqrstuwHT", longopts, NULL))
- != -1)
+ while ((optc = getopt_long (argc, argv, "abdlmpqrstuwHT", longopts, NULL))
+ != -1)
{
switch (optc)
- {
- case 'a':
- need_boottime = true;
- need_deadprocs = true;
- need_login = true;
- need_initspawn = true;
- need_runlevel = true;
- need_clockchange = true;
- need_users = true;
- include_mesg = true;
- include_idle = true;
- include_exit = true;
- assumptions = false;
- break;
-
- case 'b':
- need_boottime = true;
- assumptions = false;
- break;
-
- case 'd':
- need_deadprocs = true;
- include_idle = true;
- include_exit = true;
- assumptions = false;
- break;
-
- case 'H':
- include_heading = true;
- break;
-
- case 'l':
- need_login = true;
- include_idle = true;
- assumptions = false;
- break;
-
- case 'm':
- my_line_only = true;
- break;
-
- case 'p':
- need_initspawn = true;
- assumptions = false;
- break;
-
- case 'q':
- short_list = true;
- break;
-
- case 'r':
- need_runlevel = true;
- include_idle = true;
- assumptions = false;
- break;
-
- case 's':
- short_output = true;
- break;
-
- case 't':
- need_clockchange = true;
- assumptions = false;
- break;
-
- case 'T':
- case 'w':
- include_mesg = true;
- break;
-
- case 'i':
- error (0, 0,
- _("Warning: -i will be removed in a future release; \
- use -u instead"));
- /* Fall through. */
- case 'u':
- need_users = true;
- include_idle = true;
- assumptions = false;
- break;
-
- case LOOKUP_OPTION:
- do_lookup = true;
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- usage (EXIT_FAILURE);
- }
+ {
+ case 'a':
+ need_boottime = true;
+ need_deadprocs = true;
+ need_login = true;
+ need_initspawn = true;
+ need_runlevel = true;
+ need_clockchange = true;
+ need_users = true;
+ include_mesg = true;
+ include_idle = true;
+ include_exit = true;
+ assumptions = false;
+ break;
+
+ case 'b':
+ need_boottime = true;
+ assumptions = false;
+ break;
+
+ case 'd':
+ need_deadprocs = true;
+ include_idle = true;
+ include_exit = true;
+ assumptions = false;
+ break;
+
+ case 'H':
+ include_heading = true;
+ break;
+
+ case 'l':
+ need_login = true;
+ include_idle = true;
+ assumptions = false;
+ break;
+
+ case 'm':
+ my_line_only = true;
+ break;
+
+ case 'p':
+ need_initspawn = true;
+ assumptions = false;
+ break;
+
+ case 'q':
+ short_list = true;
+ break;
+
+ case 'r':
+ need_runlevel = true;
+ include_idle = true;
+ assumptions = false;
+ break;
+
+ case 's':
+ short_output = true;
+ break;
+
+ case 't':
+ need_clockchange = true;
+ assumptions = false;
+ break;
+
+ case 'T':
+ case 'w':
+ include_mesg = true;
+ break;
+
+ case 'u':
+ need_users = true;
+ include_idle = true;
+ assumptions = false;
+ break;
+
+ case LOOKUP_OPTION:
+ do_lookup = true;
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ usage (EXIT_FAILURE);
+ }
}
if (assumptions)
@@ -823,5 +828,5 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
- exit (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
diff --git a/src/whoami.c b/src/whoami.c
index c4a2b5e..e58c575 100644
--- a/src/whoami.c
+++ b/src/whoami.c
@@ -1,12 +1,11 @@
/* whoami -- print effective userid
- Copyright (C) 89,90, 1991-1997, 1999-2002, 2004, 2005 Free Software
- Foundation, Inc.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -14,10 +13,9 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
-/* Equivalent to `id -un'. */
+/* Equivalent to 'id -un'. */
/* Written by Richard Mlynarik. */
#include <config.h>
@@ -31,20 +29,16 @@
#include "long-options.h"
#include "quote.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "whoami"
-#define AUTHORS "Richard Mlynarik"
-
-/* The name this program was run with. */
-char *program_name;
+#define AUTHORS proper_name ("Richard Mlynarik")
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("Usage: %s [OPTION]...\n"), program_name);
@@ -55,7 +49,7 @@ Same as id -un.\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -65,17 +59,18 @@ main (int argc, char **argv)
{
struct passwd *pw;
uid_t uid;
+ uid_t NO_UID = -1;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
+ parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, Version,
+ usage, AUTHORS, (char const *) NULL);
if (getopt_long (argc, argv, "", NULL, NULL) != -1)
usage (EXIT_FAILURE);
@@ -85,14 +80,12 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
+ errno = 0;
uid = geteuid ();
- pw = getpwuid (uid);
- if (pw)
- {
- puts (pw->pw_name);
- exit (EXIT_SUCCESS);
- }
- fprintf (stderr, _("%s: cannot find name for user ID %lu\n"),
- program_name, (unsigned long int) uid);
- exit (EXIT_FAILURE);
+ pw = (uid == NO_UID && errno ? NULL : getpwuid (uid));
+ if (!pw)
+ error (EXIT_FAILURE, errno, _("cannot find name for user ID %lu"),
+ (unsigned long int) uid);
+ puts (pw->pw_name);
+ return EXIT_SUCCESS;
}
diff --git a/src/yes.c b/src/yes.c
index 4f09f68..31424cf 100644
--- a/src/yes.c
+++ b/src/yes.c
@@ -1,10 +1,10 @@
/* yes - output a string repeatedly until killed
- Copyright (C) 1991-1997, 1999-2004 Free Software Foundation, Inc.
+ Copyright (C) 1991-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This 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 2, or (at your option)
- any later version.
+ 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
@@ -12,8 +12,7 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* David MacKenzie <djm@gnu.ai.mit.edu> */
@@ -27,35 +26,31 @@
#include "error.h"
#include "long-options.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "yes"
-#define AUTHORS "David MacKenzie"
-
-/* The name this program was run with. */
-char *program_name;
+#define AUTHORS proper_name ("David MacKenzie")
void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [STRING]...\n\
or: %s OPTION\n\
"),
- program_name, program_name);
+ program_name, program_name);
fputs (_("\
-Repeatedly output a line with all specified STRING(s), or `y'.\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);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -63,34 +58,77 @@ Repeatedly output a line with all specified STRING(s), or `y'.\n\
int
main (int argc, char **argv)
{
+ char buf[BUFSIZ];
+ char *pbuf = buf;
+ int i;
+
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
- parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- usage, AUTHORS, (char const *) NULL);
+ parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, Version,
+ usage, AUTHORS, (char const *) NULL);
if (getopt_long (argc, argv, "+", NULL, NULL) != -1)
usage (EXIT_FAILURE);
if (argc <= optind)
{
optind = argc;
- argv[argc++] = "y";
+ argv[argc++] = bad_cast ("y");
+ }
+
+ /* Buffer data locally once, rather than having the
+ large overhead of stdio buffering each item. */
+ for (i = optind; i < argc; i++)
+ {
+ size_t len = strlen (argv[i]);
+ if (BUFSIZ < len || BUFSIZ - len <= pbuf - buf)
+ break;
+ memcpy (pbuf, argv[i], len);
+ pbuf += len;
+ *pbuf++ = i == argc - 1 ? '\n' : ' ';
+ }
+ if (i == argc)
+ {
+ size_t line_len = pbuf - buf;
+ size_t lines = BUFSIZ / line_len;
+ while (--lines)
+ {
+ memcpy (pbuf, pbuf - line_len, line_len);
+ pbuf += line_len;
+ }
+ }
+
+ /* The normal case is to continuously output the local buffer. */
+ while (i == argc)
+ {
+ if (write (STDOUT_FILENO, buf, pbuf - buf) == -1)
+ {
+ error (0, errno, _("standard output"));
+ return EXIT_FAILURE;
+ }
}
- for (;;)
+ /* If the data doesn't fit in BUFSIZ then output
+ what we've buffered, and iterate over the remaining items. */
+ while (true /* i != argc */)
{
- int i;
- for (i = optind; i < argc; i++)
- if (fputs (argv[i], stdout) == EOF
- || putchar (i == argc - 1 ? '\n' : ' ') == EOF)
- {
- error (0, errno, _("standard output"));
- exit (EXIT_FAILURE);
- }
+ int j;
+ if ((pbuf - buf) && fwrite (buf, pbuf - buf, 1, stdout) != 1)
+ {
+ error (0, errno, _("standard output"));
+ return EXIT_FAILURE;
+ }
+ for (j = i; j < argc; j++)
+ if (fputs (argv[j], stdout) == EOF
+ || putchar (j == argc - 1 ? '\n' : ' ') == EOF)
+ {
+ error (0, errno, _("standard output"));
+ return EXIT_FAILURE;
+ }
}
}